From 18db0b49a73c979930d818072772c737971688bb Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 25 Aug 2024 01:06:44 +0200 Subject: [PATCH] Update level mesh on sector changes --- .../rendering/hwrenderer/data/hw_levelmesh.h | 49 +++-- .../rendering/vulkan/vk_renderstate.cpp | 18 +- src/rendering/hwrenderer/doom_levelmesh.cpp | 192 ++++++++---------- src/rendering/hwrenderer/doom_levelmesh.h | 26 ++- 4 files changed, 161 insertions(+), 124 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index 8361a274d..8928606ef 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -19,13 +19,6 @@ typedef dp::rect_pack::RectPacker RectPacker; struct LevelMeshTileStats; -struct LevelSubmeshDrawRange -{ - int PipelineID; - int Start; - int Count; -}; - struct GeometryAllocInfo { FFlatVertex* Vertices = nullptr; @@ -103,7 +96,9 @@ public: AddRange(UploadRanges.Vertex, { info.VertexStart, info.VertexStart + info.VertexCount }); AddRange(UploadRanges.UniformIndexes, { info.VertexStart, info.VertexStart + info.VertexCount }); AddRange(UploadRanges.Index, { info.IndexStart, info.IndexStart + info.IndexCount }); - AddRange(UploadRanges.SurfaceIndex, { info.IndexStart / 3, (info.IndexStart + info.IndexCount) / 3 + 1 }); + AddRange(UploadRanges.SurfaceIndex, { info.IndexStart / 3, (info.IndexStart + info.IndexCount) / 3 }); + + Mesh.IndexCount = std::max(Mesh.IndexCount, info.IndexStart + info.IndexCount); return info; } @@ -145,14 +140,14 @@ public: void FreeUniforms(int start, int count) { - AddRange(FreeLists.Uniforms, { start, count }); + AddRange(FreeLists.Uniforms, { start, start + count }); } void FreeSurface(unsigned int surfaceIndex) { // To do: remove the surface from the surface tile, if attached - AddRange(FreeLists.Surface, { (int)surfaceIndex, 1 }); + AddRange(FreeLists.Surface, { (int)surfaceIndex, (int)surfaceIndex + 1 }); } // Sets the sizes of all the GPU buffers and empties the mesh @@ -229,9 +224,9 @@ public: // Data structure for doing mesh traces on the CPU std::unique_ptr Collision; - // Draw index ranges for rendering the level mesh - TArray DrawList; - TArray PortalList; + // Draw index ranges for rendering the level mesh, grouped by pipeline + std::unordered_map> DrawList; + std::unordered_map> PortalList; // Lightmap atlas int LMTextureCount = 0; @@ -283,6 +278,34 @@ public: I_FatalError("Could not find space in level mesh buffer"); } + void RemoveRange(TArray& ranges, MeshBufferRange range) + { + if (range.Start == range.End) + return; + + 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); + } + } + void AddRange(TArray& ranges, MeshBufferRange range) { // Empty range? diff --git a/src/common/rendering/vulkan/vk_renderstate.cpp b/src/common/rendering/vulkan/vk_renderstate.cpp index 3d75d3205..2df40a53b 100644 --- a/src/common/rendering/vulkan/vk_renderstate.cpp +++ b/src/common/rendering/vulkan/vk_renderstate.cpp @@ -850,9 +850,14 @@ void VkRenderState::DrawLevelMeshSurfaces(bool noFragmentShader) ApplyLevelMesh(); auto mesh = fb->GetLevelMesh()->GetMesh(); - for (LevelSubmeshDrawRange& range : mesh->DrawList) + for (auto& it : mesh->DrawList) { - DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader); + int pipelineID = it.first; + const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID); + for (MeshBufferRange& range : it.second) + { + DrawLevelMeshRange(mCommandBuffer, key, range.Start, range.End - range.Start, noFragmentShader); + } } } @@ -861,9 +866,14 @@ void VkRenderState::DrawLevelMeshPortals(bool noFragmentShader) ApplyLevelMesh(); auto mesh = fb->GetLevelMesh()->GetMesh(); - for (LevelSubmeshDrawRange& range : mesh->PortalList) + for (auto& it : mesh->PortalList) { - DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader); + int pipelineID = it.first; + const VkPipelineKey& key = fb->GetLevelMeshPipelineKey(pipelineID); + for (MeshBufferRange& range : it.second) + { + DrawLevelMeshRange(mCommandBuffer, key, range.Start, range.End - range.Start, noFragmentShader); + } } } diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index dfd588940..4983ab22d 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -166,7 +166,6 @@ DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap) CreatePortals(doomMap); CreateSurfaces(doomMap); - SortIndexes(); BuildTileSurfaceLists(); UpdateCollision(); @@ -372,7 +371,19 @@ void DoomLevelMesh::FreeSide(FLevelLocals& doomMap, unsigned int sideIndex) } Sides[sideIndex].FirstSurface = -1; - // To do: call FreeGeometry and FreeUniforms + for (auto& geo : Sides[sideIndex].Geometries) + { + FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount); + if (!geo.IsPortal) + RemoveRange(DrawList[geo.PipelineID], { geo.IndexStart, geo.IndexStart + geo.IndexCount }); + else + RemoveRange(PortalList[geo.PipelineID], { geo.IndexStart, geo.IndexStart + geo.IndexCount }); + } + Sides[sideIndex].Geometries.Clear(); + + for (auto& uni : Sides[sideIndex].Uniforms) + FreeUniforms(uni.Start, uni.Count); + Sides[sideIndex].Uniforms.Clear(); } void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex) @@ -389,17 +400,43 @@ void DoomLevelMesh::FreeFlat(FLevelLocals& doomMap, unsigned int sectorIndex) } Flats[sectorIndex].FirstSurface = -1; - // To do: call FreeGeometry and FreeUniforms + for (auto& geo : Flats[sectorIndex].Geometries) + { + FreeGeometry(geo.VertexStart, geo.VertexCount, geo.IndexStart, geo.IndexCount); + if (!geo.IsPortal) + RemoveRange(DrawList[geo.PipelineID], { geo.IndexStart, geo.IndexStart + geo.IndexCount }); + else + RemoveRange(PortalList[geo.PipelineID], { geo.IndexStart, geo.IndexStart + geo.IndexCount }); + } + Flats[sectorIndex].Geometries.Clear(); + + for (auto& uni : Flats[sectorIndex].Uniforms) + FreeUniforms(uni.Start, uni.Count); + Flats[sectorIndex].Uniforms.Clear(); } void DoomLevelMesh::FloorHeightChanged(struct sector_t* sector) { - // UpdateFlat(level, sector->Index()); + UpdateFlat(level, sector->Index()); + for (line_t* line : sector->Lines) + { + if (line->sidedef[0]) + UpdateSide(level, line->sidedef[0]->Index()); + if (line->sidedef[1]) + UpdateSide(level, line->sidedef[1]->Index()); + } } void DoomLevelMesh::CeilingHeightChanged(struct sector_t* sector) { - // UpdateFlat(level, sector->Index()); + UpdateFlat(level, sector->Index()); + for (line_t* line : sector->Lines) + { + if (line->sidedef[0]) + UpdateSide(level, line->sidedef[0]->Index()); + if (line->sidedef[1]) + UpdateSide(level, line->sidedef[1]->Index()); + } } void DoomLevelMesh::MidTex3DHeightChanged(struct sector_t* sector) @@ -409,22 +446,22 @@ void DoomLevelMesh::MidTex3DHeightChanged(struct sector_t* sector) void DoomLevelMesh::FloorTextureChanged(struct sector_t* sector) { - // UpdateFlat(level, sector->Index()); + UpdateFlat(level, sector->Index()); } void DoomLevelMesh::CeilingTextureChanged(struct sector_t* sector) { - // UpdateFlat(level, sector->Index()); + UpdateFlat(level, sector->Index()); } void DoomLevelMesh::SectorChangedOther(struct sector_t* sector) { - // UpdateFlat(level, sector->Index()); + UpdateFlat(level, sector->Index()); } void DoomLevelMesh::SideTextureChanged(struct side_t* side, int section) { - // UpdateSide(level, side->Index()); + UpdateSide(level, side->Index()); } void DoomLevelMesh::SectorLightChanged(struct sector_t* sector) @@ -633,11 +670,9 @@ void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, Mesh info.ControlSector = wallpart.LevelMeshInfo.ControlSector; info.TypeIndex = side->Index(); info.Side = side; - if (sideIndex < Sides.Size()) - { - info.NextSurface = Sides[sideIndex].FirstSurface; - Sides[sideIndex].FirstSurface = sinfo.Index; - } + + info.NextSurface = Sides[sideIndex].FirstSurface; + Sides[sideIndex].FirstSurface = sinfo.Index; sinfo.Surface->PipelineID = pipelineID; sinfo.Surface->SectorGroup = sectorGroup[side->sector->Index()]; @@ -654,6 +689,21 @@ void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, Mesh sinfo.Surface->LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(info, *sinfo.Surface, sampleDimension, !!(side->sector->Flags & SECF_LM_DYNAMIC)) : -1; SetSideLightmap(sinfo.Index); + + for (int i = ginfo.IndexStart / 3, end = (ginfo.IndexStart + ginfo.IndexCount) / 3; i < end; i++) + Mesh.SurfaceIndexes[i] = sinfo.Index; + + Sides[sideIndex].Geometries.Push({ ginfo, pipelineID, sinfo.Surface->IsSky }); + Sides[sideIndex].Uniforms.Push(uinfo); + + if (!sinfo.Surface->IsSky) + { + AddRange(DrawList[pipelineID], { ginfo.IndexStart, ginfo.IndexStart + ginfo.IndexCount }); + } + else + { + AddRange(PortalList[pipelineID], { ginfo.IndexStart, ginfo.IndexStart + ginfo.IndexCount }); + } } } @@ -789,6 +839,20 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state GeometryAllocInfo ginfo = AllocGeometry(numVertices, numIndexes); UniformsAllocInfo uinfo = AllocUniforms(1); + Flats[sectorIndex].Geometries.Push({ ginfo, pipelineID, isSky }); + Flats[sectorIndex].Uniforms.Push(uinfo); + + if (!isSky) + { + AddRange(DrawList[pipelineID], { ginfo.IndexStart, ginfo.IndexStart + ginfo.IndexCount }); + } + else + { + AddRange(PortalList[pipelineID], { ginfo.IndexStart, ginfo.IndexStart + ginfo.IndexCount }); + } + + int* surfaceIndexes = &Mesh.SurfaceIndexes[ginfo.IndexStart / 3]; + *uinfo.Uniforms = *uniforms; *uinfo.Materials = *material; @@ -857,6 +921,8 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state *(ginfo.UniformIndexes++) = uniformsIndex; } + SurfaceAllocInfo sinfo = AllocSurface(); + if (flatpart.ceiling) { for (int i = 2, count = sub->numlines; i < count; i++) @@ -864,6 +930,7 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state *(ginfo.Indexes++) = startVertIndex; *(ginfo.Indexes++) = startVertIndex + i - 1; *(ginfo.Indexes++) = startVertIndex + i; + *(surfaceIndexes++) = sinfo.Index; } } else @@ -873,6 +940,7 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state *(ginfo.Indexes++) = startVertIndex + i; *(ginfo.Indexes++) = startVertIndex + i - 1; *(ginfo.Indexes++) = startVertIndex; + *(surfaceIndexes++) = sinfo.Index; } } @@ -885,106 +953,20 @@ void DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state surf.Bounds = GetBoundsFromSurface(surf); surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(info, surf, sampleDimension, !!(flatpart.sector->Flags & SECF_LM_DYNAMIC)) : -1; - SurfaceAllocInfo sinfo = AllocSurface(); - - if (sectorIndex < Flats.Size()) - { - info.NextSurface = Flats[sectorIndex].FirstSurface; - Flats[sectorIndex].FirstSurface = sinfo.Index; - } + info.NextSurface = Flats[sectorIndex].FirstSurface; + Flats[sectorIndex].FirstSurface = sinfo.Index; *sinfo.Surface = surf; DoomSurfaceInfos[sinfo.Index] = info; + for (int i = ginfo.IndexStart / 3, end = (ginfo.IndexStart + ginfo.IndexCount) / 3; i < end; i++) + Mesh.SurfaceIndexes[i] = sinfo.Index; + SetSubsectorLightmap(sinfo.Index); } } } -void DoomLevelMesh::SortIndexes() -{ - DrawList.Clear(); - PortalList.Clear(); - - // Order surfaces by pipeline - std::unordered_map> pipelineSurfaces; - for (const SideSurfaceBlock& side : Sides) - { - int index = side.FirstSurface; - while (index != -1) - { - LevelMeshSurface* s = &Mesh.Surfaces[index]; - pipelineSurfaces[(int64_t(s->PipelineID) << 32) | int64_t(s->IsSky)].Push(index); - - index = DoomSurfaceInfos[index].NextSurface; - } - } - for (const FlatSurfaceBlock& flat : Flats) - { - int index = flat.FirstSurface; - while (index != -1) - { - LevelMeshSurface* s = &Mesh.Surfaces[index]; - pipelineSurfaces[(int64_t(s->PipelineID) << 32) | int64_t(s->IsSky)].Push(index); - - index = DoomSurfaceInfos[index].NextSurface; - } - } - - // Reorder surface indexes by pipeline and create a draw range for each - TArray sortedIndexes; - TArray sortedSurfaceIndexes; - for (const auto& it : pipelineSurfaces) - { - LevelSubmeshDrawRange range; - range.PipelineID = it.first >> 32; - range.Start = sortedIndexes.Size(); - - // Move indexes to new array - for (unsigned int i : it.second) - { - LevelMeshSurface& s = Mesh.Surfaces[i]; - - unsigned int start = s.MeshLocation.StartElementIndex; - unsigned int count = s.MeshLocation.NumElements; - - s.MeshLocation.StartElementIndex = sortedIndexes.Size(); - - for (unsigned int j = 0; j < count; j++) - { - sortedIndexes.Push(Mesh.Indexes[start + j]); - } - - for (unsigned int j = 0; j < count; j += 3) - { - sortedSurfaceIndexes.Push((int)i); - } - } - - range.Count = sortedIndexes.Size() - range.Start; - - if ((it.first & 1) == 0) - DrawList.Push(range); - else - PortalList.Push(range); - } - - // Place result at front of buffers and upload - { - int count = sortedIndexes.Size(); - memcpy(Mesh.Indexes.Data(), sortedIndexes.Data(), count * sizeof(uint32_t)); - UploadRanges.Index.Clear(); - UploadRanges.Index.Push({ 0, count }); - Mesh.IndexCount = count; - } - { - int count = sortedSurfaceIndexes.Size(); - memcpy(Mesh.SurfaceIndexes.Data(), sortedSurfaceIndexes.Data(), count * sizeof(int)); - UploadRanges.SurfaceIndex.Clear(); - UploadRanges.SurfaceIndex.Push({ 0, count }); - } -} - void DoomLevelMesh::SetSubsectorLightmap(int surfaceIndex) { int lightmapTileIndex = Mesh.Surfaces[surfaceIndex].LightmapTileIndex; diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index f7024d5a7..cab491162 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -30,14 +30,38 @@ struct DoomSurfaceInfo int NextSurface = -1; }; +struct GeometryFreeInfo +{ + GeometryFreeInfo(const GeometryAllocInfo& ginfo, int pipelineID, bool isPortal) + { + VertexStart = ginfo.VertexStart; + VertexCount = ginfo.VertexCount; + IndexStart = ginfo.IndexStart; + IndexCount = ginfo.IndexCount; + PipelineID = pipelineID; + IsPortal = isPortal; + } + + int VertexStart = 0; + int VertexCount = 0; + int IndexStart = 0; + int IndexCount = 0; + int PipelineID = 0; + bool IsPortal = false; +}; + struct SideSurfaceBlock { int FirstSurface = -1; + TArray Geometries; + TArray Uniforms; }; struct FlatSurfaceBlock { int FirstSurface = -1; + TArray Geometries; + TArray Uniforms; }; class DoomLevelMesh : public LevelMesh, public UpdateLevelMesh @@ -91,8 +115,6 @@ private: void SetSubsectorLightmap(int surfaceIndex); void SetSideLightmap(int surfaceIndex); - void SortIndexes(); - void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray& list, bool isPortal, bool translucent, unsigned int sectorIndex); void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray& list, bool isSky, bool translucent, unsigned int sectorIndex);