From 1791167fe56c07a2a4d52392a11534dd2d815f25 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 30 May 2025 18:52:44 +0200 Subject: [PATCH] Draw sorted by pipeline --- .../hwrenderer/data/hw_levelmesh.cpp | 91 ------------------- .../rendering/hwrenderer/data/hw_levelmesh.h | 20 ---- .../hwrenderer/data/hw_renderstate.h | 2 +- .../rendering/vulkan/vk_renderstate.cpp | 25 +++-- src/common/rendering/vulkan/vk_renderstate.h | 6 +- src/rendering/hwrenderer/doom_levelmesh.cpp | 25 +++-- src/rendering/hwrenderer/doom_levelmesh.h | 46 +++++++++- .../hwrenderer/scene/hw_drawinfo.cpp | 54 ++++++----- src/rendering/hwrenderer/scene/hw_drawinfo.h | 4 +- 9 files changed, 103 insertions(+), 170 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp index 8ef4a863e..cd4f8916e 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp @@ -369,94 +369,3 @@ void MeshBufferUploads::Add(int position, int count) auto rightPos = right - Ranges.begin(); Ranges.Delete(leftPos, rightPos - leftPos); } - -///////////////////////////////////////////////////////////////////////////// - -void LevelMeshDrawList::Add(int position, int count) -{ - if (count <= 0) - return; - - MeshBufferRange range = { position, position + count }; - - // First element? - if (Ranges.Size() == 0) - { - Ranges.push_back(range); - return; - } - - // Find start position in ranges - auto right = std::lower_bound(Ranges.begin(), Ranges.end(), range, [](const auto& a, const auto& b) { return a.Start < b.Start; }); - bool leftExists = right != Ranges.begin(); - bool rightExists = right != Ranges.end(); - auto left = right; - if (leftExists) - --left; - - // Is this a gap between two ranges? - if ((!leftExists || left->End < range.Start) && (!rightExists || right->Start > range.End)) - { - Ranges.Insert(right - Ranges.begin(), range); - return; - } - - // Are we extending the left or the right range? - if (leftExists && range.Start <= left->End) - { - left->End = std::max(left->End, range.End); - right = left; - } - else // if (rightExists && right->Start <= range.End) - { - right->Start = range.Start; - right->End = std::max(right->End, range.End); - left = right; - } - - // Merge overlaps to the right - while (true) - { - ++right; - if (right == Ranges.end() || right->Start > range.End) - break; - left->End = std::max(right->End, range.End); - } - - // Remove ranges now covered by the extended range - //ranges.erase(++left, right); - ++left; - auto leftPos = left - Ranges.begin(); - auto rightPos = right - Ranges.begin(); - Ranges.Delete(leftPos, rightPos - leftPos); -} - -void LevelMeshDrawList::Remove(int position, int count) -{ - if (count <= 0) - return; - - MeshBufferRange range = { position, position + count }; - - auto entry = std::lower_bound(Ranges.begin(), Ranges.end(), range, [](const auto& a, const auto& b) { return a.End < b.End; }); - if (entry->Start == range.Start && entry->End == range.End) - { - Ranges.Delete(entry - Ranges.begin()); - } - else if (entry->Start == range.Start) - { - entry->Start = range.End; - } - else if (entry->End == range.End) - { - entry->End = range.Start; - } - else - { - MeshBufferRange split; - split.Start = entry->Start; - split.End = range.Start; - entry->Start = range.End; - Ranges.Insert(entry - Ranges.begin(), split); - } -} diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index f819c48fd..812c3c9e5 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -92,17 +92,6 @@ private: TArray Ranges; }; -class LevelMeshDrawList -{ -public: - void Add(int position, int count); - void Remove(int position, int count); - const TArray& GetRanges() const { return Ranges; } - -private: - TArray Ranges; -}; - struct LevelMeshLimits { int MaxVertices = 0; @@ -111,15 +100,6 @@ struct LevelMeshLimits int MaxIndexes = 0; }; -enum class LevelMeshDrawType -{ - Opaque, - Masked, - Portal, - Translucent, - NumDrawTypes -}; - class LevelMesh { public: diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index a9c2e0ae1..89a2d13d8 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -828,7 +828,7 @@ public: // Draw level mesh virtual void ApplyLevelMesh() { } - virtual void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) {} + virtual void DrawLevelMeshList(const TArray>& drawList, bool masked, bool noFragmentShader) {} virtual void DispatchLightTiles(const VSMatrix& worldToView, float m5) { } virtual int GetNextQueryIndex() { return 0; } virtual void BeginQuery() { } diff --git a/src/common/rendering/vulkan/vk_renderstate.cpp b/src/common/rendering/vulkan/vk_renderstate.cpp index 5aa41a3b0..d6d2d62bc 100644 --- a/src/common/rendering/vulkan/vk_renderstate.cpp +++ b/src/common/rendering/vulkan/vk_renderstate.cpp @@ -919,7 +919,6 @@ void VkRenderState::ApplyLevelMesh() ApplyStencilRef(); ApplyDepthBias(); mNeedApply = true; - mLevelMeshPipelineID = -1; VkBuffer vertexBuffers[2] = { fb->GetLevelMesh()->GetVertexBuffer()->buffer, fb->GetLevelMesh()->GetUniformIndexBuffer()->buffer }; VkDeviceSize vertexBufferOffsets[] = { 0, 0 }; @@ -1061,16 +1060,22 @@ void VkRenderState::DispatchLightTiles(const VSMatrix& worldToView, float m5) fb->GetCommands()->PopGroup(cmdbuffer); } -void VkRenderState::DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) +void VkRenderState::DrawLevelMeshList(const TArray>& drawList, bool masked, bool noFragmentShader) { - if (pipelineID != mLevelMeshPipelineID) + int pipelineID = 0; + for (const TArray& ranges : drawList) { - const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID); - ApplyLevelMeshPipeline(mCommandBuffer, key, drawType, noFragmentShader); - mLevelMeshPipelineID = pipelineID; + if (ranges.size() != 0) + { + const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID); + ApplyLevelMeshPipeline(mCommandBuffer, key, masked, noFragmentShader); + for (const MeshBufferRange& range : ranges) + { + mCommandBuffer->drawIndexed(range.Count(), 1, range.Start, 0, 0); + } + } + pipelineID++; } - - mCommandBuffer->drawIndexed(indexCount, 1, firstIndex, 0, 0); } void VkRenderState::BeginQuery() @@ -1107,9 +1112,9 @@ void VkRenderState::GetQueryResults(int queryStart, int queryCount, TArray } } -void VkRenderState::ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, LevelMeshDrawType drawType, bool noFragmentShader) +void VkRenderState::ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, bool masked, bool noFragmentShader) { - if (drawType == LevelMeshDrawType::Masked && noFragmentShader) + if (masked && noFragmentShader) { // We unfortunately have to run the fragment shader to know which pixels are masked. Use a simplified version to reduce the cost. noFragmentShader = false; diff --git a/src/common/rendering/vulkan/vk_renderstate.h b/src/common/rendering/vulkan/vk_renderstate.h index 4ef315cf0..67cbe5517 100644 --- a/src/common/rendering/vulkan/vk_renderstate.h +++ b/src/common/rendering/vulkan/vk_renderstate.h @@ -58,7 +58,7 @@ public: // Draw level mesh void ApplyLevelMesh() override; - void DrawLevelMeshRange(int firstIndex, int indexCount, int pipelineID, LevelMeshDrawType drawType, bool noFragmentShader) override; + void DrawLevelMeshList(const TArray>& drawList, bool masked, bool noFragmentShader) override; void DispatchLightTiles(const VSMatrix& worldToView, float m5) override; int GetNextQueryIndex() override; void BeginQuery() override; @@ -92,7 +92,7 @@ protected: void WaitForStreamBuffers(); void RunZMinMaxPass(); - void ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, LevelMeshDrawType drawType, bool noFragmentShader); + void ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPipelineKey pipelineKey, bool masked, bool noFragmentShader); VulkanRenderDevice* fb = nullptr; @@ -107,8 +107,6 @@ protected: bool mNeedApply = true; bool mDrawLine = false; - int mLevelMeshPipelineID = -1; - int mScissorX = 0, mScissorY = 0, mScissorWidth = -1, mScissorHeight = -1; int mViewportX = 0, mViewportY = 0, mViewportWidth = -1, mViewportHeight = -1; float mViewportDepthMin = 0.0f, mViewportDepthMax = 1.0f; diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 9e2810549..7f0ac962d 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -318,24 +318,24 @@ void DoomLevelMesh::SetLimits(FLevelLocals& doomMap) Reset(limits); } -void DoomLevelMesh::DrawSectors(FRenderState& renderstate, const TArray& sectors, LevelMeshDrawType drawType, bool noFragmentShader) +void DoomLevelMesh::AddSectorsToDrawLists(const TArray& sectors, LevelMeshDrawLists& lists) { for (int sectorIndex : sectors) { - for (const DrawRangeInfo& di : Flats[sectorIndex].DrawRanges[(int)drawType]) + for (const DrawRangeInfo& di : Flats[sectorIndex].DrawRanges) { - renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader); + lists.Add(di.DrawType, di.PipelineID, { di.IndexStart, di.IndexStart + di.IndexCount }); } } } -void DoomLevelMesh::DrawSides(FRenderState& renderstate, const TArray& sides, LevelMeshDrawType drawType, bool noFragmentShader) +void DoomLevelMesh::AddSidesToDrawLists(const TArray& sides, LevelMeshDrawLists& lists) { for (int sideIndex : sides) { - for (const DrawRangeInfo& di : Sides[sideIndex].DrawRanges[(int)drawType]) + for (const DrawRangeInfo& di : Sides[sideIndex].DrawRanges) { - renderstate.DrawLevelMeshRange(di.IndexStart, di.IndexCount, di.PipelineID, drawType, noFragmentShader); + lists.Add(di.DrawType, di.PipelineID, { di.IndexStart, di.IndexStart + di.IndexCount }); } } } @@ -653,8 +653,7 @@ void DoomLevelMesh::FreeSide(FLevelLocals& doomMap, unsigned int sideIndex) FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount); Sides[sideIndex].Geometries.Clear(); - for (auto& list : Sides[sideIndex].DrawRanges) - list.Clear(); + Sides[sideIndex].DrawRanges.Clear(); for (auto& uni : Sides[sideIndex].Uniforms) FreeUniforms(uni.Start, uni.Count); @@ -696,8 +695,7 @@ void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex) FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount); Flats[sectorIndex].Geometries.Clear(); - for (auto& list : Flats[sectorIndex].DrawRanges) - list.Clear(); + Flats[sectorIndex].DrawRanges.Clear(); for (auto& uni : Flats[sectorIndex].Uniforms) FreeUniforms(uni.Start, uni.Count); @@ -1379,13 +1377,14 @@ void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, Mesh Sides[sideIndex].Geometries.Push(ginfo); Sides[sideIndex].Uniforms.Push(uinfo); - AddToDrawList(Sides[sideIndex].DrawRanges[(int)drawType], pipelineID, ginfo.IndexStart, ginfo.IndexCount); + AddToDrawList(Sides[sideIndex].DrawRanges, drawType, pipelineID, ginfo.IndexStart, ginfo.IndexCount); } } -void DoomLevelMesh::AddToDrawList(TArray& drawRanges, int pipelineID, int indexStart, int indexCount) +void DoomLevelMesh::AddToDrawList(TArray& drawRanges, LevelMeshDrawType drawType, int pipelineID, int indexStart, int indexCount) { DrawRangeInfo info; + info.DrawType = drawType; info.IndexStart = indexStart; info.IndexCount = indexCount; info.PipelineID = pipelineID; @@ -1663,7 +1662,7 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state SetSubsectorLightmap(sinfo.Index); } - AddToDrawList(Flats[sectorIndex].DrawRanges[(int)drawType], pipelineID, ginfo.IndexStart, ginfo.IndexCount); + AddToDrawList(Flats[sectorIndex].DrawRanges, drawType, pipelineID, ginfo.IndexStart, ginfo.IndexCount); } } diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index 96338466a..7a10b7f7a 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -52,6 +52,7 @@ struct GeometryFreeInfo struct DrawRangeInfo { + LevelMeshDrawType DrawType = {}; int PipelineID = 0; int IndexStart = 0; int IndexCount = 0; @@ -75,7 +76,7 @@ struct SideSurfaceBlock TArray Decals; bool InSidePortalsList = false; bool InSideDecalsList = false; - TArray DrawRanges[(int)LevelMeshDrawType::NumDrawTypes]; + TArray DrawRanges; SurfaceUpdateType UpdateType = SurfaceUpdateType::None; LightListAllocInfo Lights; }; @@ -85,11 +86,45 @@ struct FlatSurfaceBlock int FirstSurface = -1; TArray Geometries; TArray Uniforms; - TArray DrawRanges[(int)LevelMeshDrawType::NumDrawTypes]; + TArray DrawRanges; SurfaceUpdateType UpdateType = SurfaceUpdateType::None; TArray Lights; }; +enum class LevelMeshDrawType +{ + Opaque, + Masked, + Portal, + Translucent, + NumDrawTypes +}; + +class LevelMeshDrawLists +{ +public: + TArray> List[static_cast(LevelMeshDrawType::NumDrawTypes)]; + + void Clear() + { + for (auto& l : List) + { + for (auto& p : l) + { + p.Clear(); + } + } + } + + void Add(LevelMeshDrawType drawType, int pipelineID, const MeshBufferRange& range) + { + int listIndex = static_cast(drawType); + if (pipelineID >= (int)List[listIndex].Size()) + List[listIndex].Resize(pipelineID + 1); + List[listIndex][pipelineID].Push(range); + } +}; + class DoomLevelMesh : public LevelMesh, public UpdateLevelMesh { public: @@ -106,8 +141,9 @@ public: void ProcessDecals(HWDrawInfo* drawinfo, FRenderState& state); - void DrawSectors(FRenderState& renderstate, const TArray& sectors, LevelMeshDrawType drawType, bool noFragmentShader); - void DrawSides(FRenderState& renderstate, const TArray& sides, LevelMeshDrawType drawType, bool noFragmentShader); + void AddSectorsToDrawLists(const TArray& sectors, LevelMeshDrawLists& lists); + void AddSidesToDrawLists(const TArray& sides, LevelMeshDrawLists& lists); + TArray& GetSidePortals(int sideIndex); TArray SideDecals; @@ -196,7 +232,7 @@ private: void UpdateLight(FDynamicLight* light); void CopyToMeshLight(FDynamicLight* light, LevelMeshLight& meshlight, int portalgroup); - void AddToDrawList(TArray& drawRanges, int pipelineID, int indexStart, int indexCount); + void AddToDrawList(TArray& drawRanges, LevelMeshDrawType drawType, int pipelineID, int indexStart, int indexCount); void UploadDynLights(FLevelLocals& doomMap); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 899096b21..8b0835b59 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -559,6 +559,11 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state) } } + SeenFlatsDrawLists.Clear(); + SeenSidesDrawLists.Clear(); + level.levelMesh->AddSectorsToDrawLists(SeenSectors.Get(), SeenFlatsDrawLists); + level.levelMesh->AddSidesToDrawLists(SeenSides.Get(), SeenSidesDrawLists); + // And now the crappy hacks that have to be done to avoid rendering anomalies. // These cannot be multithreaded when the time comes because all these depend // on the global 'validcount' variable. @@ -779,30 +784,42 @@ void HWDrawInfo::RenderScene(FRenderState &state) state.EnableTexture(gl_texture); state.EnableBrightmap(true); - // To do: replace this is classic light lists + // To do: replace this with classic light lists state.ApplyLevelMesh(); - DrawSeenSides(state, LevelMeshDrawType::Opaque, true); - DrawSeenFlats(state, LevelMeshDrawType::Opaque, true); + state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast(LevelMeshDrawType::Opaque)], false, true); + state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast(LevelMeshDrawType::Opaque)], false, true); if (uselevelmesh) state.DispatchLightTiles(VPUniforms.mViewMatrix, VPUniforms.mProjectionMatrix.get()[5]); + RenderWall.Clock(); state.ApplyLevelMesh(); - DrawSeenSides(state, LevelMeshDrawType::Opaque, false); + state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast(LevelMeshDrawType::Opaque)], false, false); + RenderWall.Unclock(); + drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false); + RenderFlat.Clock(); state.ApplyLevelMesh(); - DrawSeenFlats(state, LevelMeshDrawType::Opaque, false); + state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast(LevelMeshDrawType::Opaque)], false, false); + RenderFlat.Unclock(); + drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false); // Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show state.AlphaFunc(Alpha_GEqual, gl_mask_threshold); + RenderWall.Clock(); state.ApplyLevelMesh(); - DrawSeenSides(state, LevelMeshDrawType::Masked, false); + state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast(LevelMeshDrawType::Masked)], true, false); + RenderWall.Unclock(); + drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false); + RenderFlat.Clock(); state.ApplyLevelMesh(); - DrawSeenFlats(state, LevelMeshDrawType::Masked, false); + state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast(LevelMeshDrawType::Masked)], true, false); + RenderFlat.Unclock(); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false); // Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use. @@ -826,20 +843,6 @@ void HWDrawInfo::RenderScene(FRenderState &state) RenderAll.Unclock(); } -void HWDrawInfo::DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader) -{ - RenderWall.Clock(); - level.levelMesh->DrawSides(state, SeenSides.Get(), drawType, noFragmentShader); - RenderWall.Unclock(); -} - -void HWDrawInfo::DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader) -{ - RenderFlat.Clock(); - level.levelMesh->DrawSectors(state, SeenSectors.Get(), drawType, noFragmentShader); - RenderFlat.Unclock(); -} - //----------------------------------------------------------------------------- // // RenderTranslucent @@ -859,9 +862,14 @@ void HWDrawInfo::RenderTranslucent(FRenderState &state) state.SetDepthMask(false); // To do: this needs to be sorted + RenderWall.Clock(); state.ApplyLevelMesh(); - DrawSeenSides(state, LevelMeshDrawType::Translucent, false); - DrawSeenFlats(state, LevelMeshDrawType::Translucent, false); + state.DrawLevelMeshList(SeenSidesDrawLists.List[static_cast(LevelMeshDrawType::Translucent)], false, false); + RenderWall.Unclock(); + RenderFlat.Clock(); + state.ApplyLevelMesh(); + state.DrawLevelMeshList(SeenFlatsDrawLists.List[static_cast(LevelMeshDrawType::Translucent)], false, false); + RenderFlat.Unclock(); drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state); state.EnableBrightmap(false); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index b65b4c7e4..b1f146b43 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -227,6 +227,7 @@ struct HWDrawInfo }; VisList SeenSectors, SeenSides, SeenSubsectors, SeenHackedSubsectors; + LevelMeshDrawLists SeenFlatsDrawLists, SeenSidesDrawLists; TArray QueryResultsBuffer; @@ -427,9 +428,6 @@ private: void AddSpecialPortalLines(subsector_t* sub, sector_t* sector, linebase_t* line, FRenderState& state); void UpdateLightmaps(); - - void DrawSeenSides(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader); - void DrawSeenFlats(FRenderState& state, LevelMeshDrawType drawType, bool noFragmentShader); }; void CleanSWDrawer();