Change level mesh draw code to draw ranges in the mesh rather than the entire mesh

This commit is contained in:
Magnus Norddahl 2025-05-25 18:18:29 +02:00
commit f302f03287
10 changed files with 73 additions and 119 deletions

View file

@ -46,13 +46,10 @@ void LevelMesh::Reset(const LevelMeshLimits& limits)
Mesh.Indexes.Resize(limits.MaxIndexes);
Mesh.SurfaceIndexes.Resize(limits.MaxIndexes / 3 + 1);
Mesh.DrawIndexes.Resize(limits.MaxIndexes);
FreeLists.Vertex.Reset(limits.MaxVertices);
FreeLists.Index.Reset(limits.MaxIndexes);
FreeLists.Uniforms.Reset(limits.MaxUniforms);
FreeLists.Surface.Reset(limits.MaxSurfaces);
FreeLists.DrawIndex.Reset(limits.MaxIndexes);
FreeLists.LightIndex.Reset(limits.MaxSurfaces * 10);
FreeLists.Light.Reset(maxLights);
}

View file

@ -180,9 +180,6 @@ public:
TArray<int> SurfaceIndexes;
int IndexCount = 0; // Index range filled with data
// Indexes sorted by pipeline
TArray<uint32_t> DrawIndexes;
// Acceleration structure nodes for when the GPU doesn't support rayquery
TArray<CollisionNode> Nodes;
int RootNode = 0;
@ -203,7 +200,6 @@ public:
MeshBufferUploads Light;
MeshBufferUploads LightIndex;
MeshBufferUploads DynLight;
MeshBufferUploads DrawIndex;
} UploadRanges;
// Ranges in mesh currently not in use
@ -215,15 +211,11 @@ public:
MeshBufferAllocator Surface;
MeshBufferAllocator Light;
MeshBufferAllocator LightIndex;
MeshBufferAllocator DrawIndex;
} FreeLists;
// Data structure for doing mesh traces on the CPU
std::unique_ptr<CPUAccelStruct> Collision;
// Draw index ranges for rendering the level mesh, grouped by pipeline
std::unordered_map<int, LevelMeshDrawList> DrawList[(int)LevelMeshDrawType::NumDrawTypes];
// Lightmap tiles and their locations in the texture atlas
struct
{

View file

@ -827,8 +827,8 @@ public:
}
// Draw level mesh
virtual void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) {}
virtual void DispatchLightTiles(const VSMatrix& worldToView, float m5) { }
virtual void DrawLevelMesh(LevelMeshDrawType drawType, bool noFragmentShader) { }
virtual int GetNextQueryIndex() { return 0; }
virtual void BeginQuery() { }
virtual void EndQuery() { }

View file

@ -58,7 +58,6 @@ void VkLevelMesh::Reset()
deletelist->Add(std::move(PortalBuffer));
deletelist->Add(std::move(LightBuffer));
deletelist->Add(std::move(LightIndexBuffer));
deletelist->Add(std::move(DrawIndexBuffer));
for (BLAS& blas : DynamicBLAS)
{
deletelist->Add(std::move(blas.ScratchBuffer));
@ -257,12 +256,6 @@ void VkLevelMesh::CreateBuffers()
.DebugName("IndexBuffer")
.Create(fb->GetDevice());
DrawIndexBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size((size_t)Mesh->Mesh.DrawIndexes.Size() * sizeof(uint32_t))
.DebugName("DrawIndexBuffer")
.Create(fb->GetDevice());
NodeBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(CollisionNodeBufferHeader) + Mesh->Mesh.Nodes.Size() * sizeof(CollisionNode))
@ -704,7 +697,6 @@ void VkLevelMeshUploader::Upload()
UploadRanges(Mesh->Mesh->UploadRanges.Index, Mesh->Mesh->Mesh.Indexes.Data(), Mesh->IndexBuffer.get(), "IndexBuffer");
UploadRanges(Mesh->Mesh->UploadRanges.SurfaceIndex, Mesh->Mesh->Mesh.SurfaceIndexes.Data(), Mesh->SurfaceIndexBuffer.get(), "SurfaceIndexBuffer");
UploadRanges(Mesh->Mesh->UploadRanges.LightIndex, Mesh->Mesh->Mesh.LightIndexes.Data(), Mesh->LightIndexBuffer.get(), "LightIndexBuffer");
UploadRanges(Mesh->Mesh->UploadRanges.DrawIndex, Mesh->Mesh->Mesh.DrawIndexes.Data(), Mesh->DrawIndexBuffer.get(), "DrawIndexBuffer");
UploadRanges(Mesh->Mesh->UploadRanges.LightUniforms, Mesh->Mesh->Mesh.LightUniforms.Data(), Mesh->LightUniformsBuffer.get(), "LightUniformsBuffer");
UploadRanges(Mesh->Mesh->UploadRanges.DynLight, Mesh->Mesh->Mesh.DynLights.Data(), Mesh->DynLightBuffer.get(), "DynLightBuffer");
UploadSurfaces();
@ -746,7 +738,6 @@ void VkLevelMeshUploader::ClearRanges()
Mesh->Mesh->UploadRanges.Light.Clear();
Mesh->Mesh->UploadRanges.LightIndex.Clear();
Mesh->Mesh->UploadRanges.DynLight.Clear();
Mesh->Mesh->UploadRanges.DrawIndex.Clear();
}
void VkLevelMeshUploader::BeginTransfer(size_t transferBufferSize)
@ -948,6 +939,5 @@ size_t VkLevelMeshUploader::GetTransferSize()
for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.LightIndex.GetRanges()) transferBufferSize += range.Count() * sizeof(int32_t);
for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Light.GetRanges()) transferBufferSize += range.Count() * sizeof(LightInfo);
for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DynLight.GetRanges()) transferBufferSize += range.Count();
for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DrawIndex.GetRanges()) transferBufferSize += range.Count() * sizeof(uint32_t);
return transferBufferSize;
}

View file

@ -89,7 +89,6 @@ public:
VulkanBuffer* GetPortalBuffer() { return PortalBuffer.get(); }
VulkanBuffer* GetLightBuffer() { return LightBuffer.get(); }
VulkanBuffer* GetLightIndexBuffer() { return LightIndexBuffer.get(); }
VulkanBuffer* GetDrawIndexBuffer() { return DrawIndexBuffer.get(); }
LevelMesh* GetMesh() { return Mesh; }
@ -138,7 +137,6 @@ private:
std::unique_ptr<VulkanBuffer> LightBuffer;
std::unique_ptr<VulkanBuffer> LightIndexBuffer;
std::unique_ptr<VulkanBuffer> DynLightBuffer;
std::unique_ptr<VulkanBuffer> DrawIndexBuffer;
std::unique_ptr<VulkanBuffer> NodeBuffer;

View file

@ -923,7 +923,7 @@ void VkRenderState::ApplyLevelMesh()
VkBuffer vertexBuffers[2] = { fb->GetLevelMesh()->GetVertexBuffer()->buffer, fb->GetLevelMesh()->GetUniformIndexBuffer()->buffer };
VkDeviceSize vertexBufferOffsets[] = { 0, 0 };
mCommandBuffer->bindVertexBuffers(0, 2, vertexBuffers, vertexBufferOffsets);
mCommandBuffer->bindIndexBuffer(fb->GetLevelMesh()->GetDrawIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
mCommandBuffer->bindIndexBuffer(fb->GetLevelMesh()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
}
void VkRenderState::RunZMinMaxPass()
@ -1055,23 +1055,14 @@ void VkRenderState::DispatchLightTiles(const VSMatrix& worldToView, float m5)
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkRenderState::DrawLevelMesh(LevelMeshDrawType drawType, bool noFragmentShader)
void VkRenderState::DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader)
{
ApplyLevelMesh();
auto mesh = fb->GetLevelMesh()->GetMesh();
for (auto& it : mesh->DrawList[(int)drawType])
{
int pipelineID = it.first;
const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID);
const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID);
ApplyLevelMeshPipeline(mCommandBuffer, key, drawType, noFragmentShader);
ApplyLevelMeshPipeline(mCommandBuffer, key, drawType, noFragmentShader);
for (const MeshBufferRange& range : it.second.GetRanges())
{
mCommandBuffer->drawIndexed(range.End - range.Start, 1, range.Start, 0, 0);
}
}
mCommandBuffer->drawIndexed(indexCount, 1, firstIndex, 0, 0);
}
void VkRenderState::BeginQuery()

View file

@ -57,8 +57,8 @@ public:
void ResetVertices() override;
// Draw level mesh
void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) override;
void DispatchLightTiles(const VSMatrix& worldToView, float m5) override;
void DrawLevelMesh(LevelMeshDrawType drawType, bool noFragmentShader) override;
int GetNextQueryIndex() override;
void BeginQuery() override;
void EndQuery() override;

View file

@ -248,7 +248,6 @@ DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap)
CreateCollision();
UploadPortals();
SortDrawLists();
r_viewpoint.extralight = oldextralight;
r_viewpoint.camera = oldcamera;
@ -319,6 +318,28 @@ void DoomLevelMesh::SetLimits(FLevelLocals& doomMap)
Reset(limits);
}
void DoomLevelMesh::DrawSector(FRenderState& renderstate, int sectorIndex, LevelMeshDrawType drawType, bool noFragmentShader)
{
for (const DrawRangeInfo& di : Flats[sectorIndex].DrawRanges)
{
if (di.DrawType == drawType)
{
renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader);
}
}
}
void DoomLevelMesh::DrawSide(FRenderState& renderstate, int sideIndex, LevelMeshDrawType drawType, bool noFragmentShader)
{
for (const DrawRangeInfo& di : Sides[sideIndex].DrawRanges)
{
if (di.DrawType == drawType)
{
renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader);
}
}
}
void DoomLevelMesh::BeginFrame(FLevelLocals& doomMap)
{
LastFrameStats = CurFrameStats;
@ -640,7 +661,6 @@ void DoomLevelMesh::FreeSide(FLevelLocals& doomMap, unsigned int sideIndex)
FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount);
Sides[sideIndex].Geometries.Clear();
RemoveFromDrawList(Sides[sideIndex].DrawRanges);
Sides[sideIndex].DrawRanges.Clear();
for (auto& uni : Sides[sideIndex].Uniforms)
@ -683,7 +703,6 @@ void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex)
FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount);
Flats[sectorIndex].Geometries.Clear();
RemoveFromDrawList(Flats[sectorIndex].DrawRanges);
Flats[sectorIndex].DrawRanges.Clear();
for (auto& uni : Flats[sectorIndex].Uniforms)
@ -1372,77 +1391,12 @@ void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, Mesh
void DoomLevelMesh::AddToDrawList(TArray<DrawRangeInfo>& drawRanges, int pipelineID, int indexStart, int indexCount, LevelMeshDrawType drawType)
{
// Remember the location if we have to remove it again
DrawRangeInfo info;
info.DrawIndexStart = FreeLists.DrawIndex.Alloc(indexCount);
info.DrawIndexCount = indexCount;
info.IndexStart = indexStart;
info.IndexCount = indexCount;
info.DrawType = drawType;
info.PipelineID = pipelineID;
drawRanges.Push(info);
// Copy the indexes over from the unsorted index list
memcpy(&Mesh.DrawIndexes[info.DrawIndexStart], &Mesh.Indexes[indexStart], indexCount * sizeof(uint32_t));
UploadRanges.DrawIndex.Add(info.DrawIndexStart, indexCount);
// Add to the draw lists
DrawList[(int)drawType][pipelineID].Add(info.DrawIndexStart, indexCount);
}
void DoomLevelMesh::RemoveFromDrawList(const TArray<DrawRangeInfo>& drawRanges)
{
for (const DrawRangeInfo& info : drawRanges)
{
DrawList[(int)info.DrawType][info.PipelineID].Remove(info.DrawIndexStart, info.DrawIndexCount);
FreeLists.DrawIndex.Free(info.DrawIndexStart, info.DrawIndexCount);
}
}
void DoomLevelMesh::SortDrawLists()
{
std::unordered_map<int, TArray<DrawRangeInfo*>> sortedDrawList[(int)LevelMeshDrawType::NumDrawTypes];
for (auto& side : Sides)
{
for (auto& range : side.DrawRanges)
{
sortedDrawList[(int)range.DrawType][range.PipelineID].Push(&range);
}
}
for (auto& flat : Flats)
{
for (auto& range : flat.DrawRanges)
{
sortedDrawList[(int)range.DrawType][range.PipelineID].Push(&range);
}
}
TArray<uint32_t> indexes;
for (int drawType = 0; drawType < (int)LevelMeshDrawType::NumDrawTypes; drawType++)
{
DrawList[drawType].clear();
for (auto& it : sortedDrawList[drawType])
{
auto& list = DrawList[drawType][it.first];
int listStart = indexes.Size();
for (DrawRangeInfo* range : it.second)
{
int sortedStart = indexes.Size();
int start = range->DrawIndexStart;
int count = range->DrawIndexCount;
for (int i = 0; i < count; i++)
{
indexes.Push(Mesh.DrawIndexes[start + i]);
}
range->DrawIndexStart = sortedStart;
}
int listEnd = indexes.Size();
list.Add(listStart, listEnd - listStart);
}
}
memcpy(Mesh.DrawIndexes.Data(), indexes.Data(), indexes.Size() * sizeof(uint32_t));
}
int DoomLevelMesh::AddSurfaceToTile(const DoomSurfaceInfo& info, const LevelMeshSurface& surf, uint16_t sampleDimension, uint8_t alwaysUpdate)

View file

@ -54,8 +54,8 @@ struct DrawRangeInfo
{
int PipelineID = 0;
LevelMeshDrawType DrawType = {};
int DrawIndexStart = 0;
int DrawIndexCount = 0;
int IndexStart = 0;
int IndexCount = 0;
};
enum SurfaceUpdateType
@ -107,6 +107,9 @@ public:
void ProcessDecals(HWDrawInfo* drawinfo, FRenderState& state);
void DrawSector(FRenderState& renderstate, int sectorIndex, LevelMeshDrawType drawType, bool noFragmentShader);
void DrawSide(FRenderState& renderstate, int sideIndex, LevelMeshDrawType drawType, bool noFragmentShader);
TArray<int> SideDecals;
TArray<int> SidePortals;
TArray<HWWall*> WallPortals;
@ -197,8 +200,6 @@ private:
void CopyToMeshLight(FDynamicLight* light, LevelMeshLight& meshlight, int portalgroup);
void AddToDrawList(TArray<DrawRangeInfo>& drawRanges, int pipelineID, int indexStart, int indexCount, LevelMeshDrawType drawType);
void RemoveFromDrawList(const TArray<DrawRangeInfo>& drawRanges);
void SortDrawLists();
void UploadDynLights(FLevelLocals& doomMap);

View file

@ -477,12 +477,29 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
// draw level into depth buffer
state.SetColorMask(false);
state.SetCulling(Cull_CW);
state.DrawLevelMesh(LevelMeshDrawType::Opaque, true);
state.DrawLevelMesh(LevelMeshDrawType::Masked, true);
int numSectors = level.sectors.Size();
int numSides = level.sides.Size();
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Opaque, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Opaque, true);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Masked, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Masked, true);
if (gl_portals)
{
state.SetDepthBias(1, 128);
state.DrawLevelMesh(LevelMeshDrawType::Portal, true);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Portal, true);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Portal, true);
state.SetDepthBias(0, 0);
}
@ -518,12 +535,26 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
state.DispatchLightTiles(VPUniforms.mViewMatrix, VPUniforms.mProjectionMatrix.get()[5]);
// draw opaque level so the GPU has something to do while we examine the query results
state.DrawLevelMesh(LevelMeshDrawType::Opaque, false);
state.DrawLevelMesh(LevelMeshDrawType::Masked, false);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Opaque, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Opaque, false);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Masked, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Masked, false);
if (!gl_portals)
{
state.SetDepthBias(1, 128);
state.DrawLevelMesh(LevelMeshDrawType::Portal, false);
for (int i = 0; i < numSectors; i++)
level.levelMesh->DrawSector(state, i, LevelMeshDrawType::Portal, false);
for (int i = 0; i < numSides; i++)
level.levelMesh->DrawSide(state, i, LevelMeshDrawType::Portal, false);
state.SetDepthBias(0, 0);
}
state.SetCulling(Cull_None);