From 76fb5901d7b207c56fc2456e9e850a7a9c23f877 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 13 May 2025 01:51:41 +0200 Subject: [PATCH] Only force baking of tiles where the geometry changed --- src/commandlets/lightmapcmd.cpp | 2 +- .../hwrenderer/data/hw_levelmesh.cpp | 4 +- .../hwrenderer/data/hw_lightmaptile.h | 10 +++- .../rendering/vulkan/vk_lightmapper.cpp | 8 ++- src/maploader/maploader.cpp | 8 ++- src/rendering/hwrenderer/doom_levelmesh.cpp | 44 +++++++------- .../hwrenderer/scene/hw_drawinfo.cpp | 57 +++++++++++++++---- src/rendering/hwrenderer/scene/hw_drawinfo.h | 18 +++++- 8 files changed, 106 insertions(+), 45 deletions(-) diff --git a/src/commandlets/lightmapcmd.cpp b/src/commandlets/lightmapcmd.cpp index 5f3593799..1fece868b 100644 --- a/src/commandlets/lightmapcmd.cpp +++ b/src/commandlets/lightmapcmd.cpp @@ -72,7 +72,7 @@ void LightmapBuildCmdlet::OnCommand(FArgs args) tiles.Clear(); for (auto& e : level.levelMesh->Lightmap.Tiles) { - if (e.NeedsUpdate) + if (e.NeedsInitialBake) { tiles.Push(&e); if (tiles.Size() == 1001) diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp index d91423e36..1477bdb46 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp @@ -137,7 +137,7 @@ LevelMeshTileStats LevelMesh::GatherTilePixelStats() stats.pixels.total += area; - if (tile.NeedsUpdate) + if (tile.NeedsInitialBake) { stats.tiles.dirty++; stats.pixels.dirty += area; @@ -160,7 +160,7 @@ void LevelMesh::PackLightmapAtlas() tile.AddedThisFrame = false; - if (tile.NeedsUpdate) // false for tiles loaded from lump + if (tile.NeedsInitialBake || tile.GeometryUpdate) // NeedsInitialBake is false for tiles loaded from lump tile.SetupTileTransform(Lightmap.TextureSize); sortedTiles.push_back(&tile); diff --git a/src/common/rendering/hwrenderer/data/hw_lightmaptile.h b/src/common/rendering/hwrenderer/data/hw_lightmaptile.h index 86574faf9..bf6e52302 100644 --- a/src/common/rendering/hwrenderer/data/hw_lightmaptile.h +++ b/src/common/rendering/hwrenderer/data/hw_lightmaptile.h @@ -57,8 +57,14 @@ struct LightmapTile uint16_t SampleDimension = 0; FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f); - // True if the tile needs to be rendered into the lightmap texture before it can be used - bool NeedsUpdate = true; + // Initial tile for surfaces that can be baked in the background + bool NeedsInitialBake = false; + + // This tile MUST be baked before it can be rendered + bool GeometryUpdate = true; + + // The light for this tile changed since last bake + bool ReceivedNewLight = true; // Used to track if tile has already been added to the VisibleTiles list for this scene int LastSeen = 0; diff --git a/src/common/rendering/vulkan/vk_lightmapper.cpp b/src/common/rendering/vulkan/vk_lightmapper.cpp index 1029ac139..e48e873f0 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.cpp +++ b/src/common/rendering/vulkan/vk_lightmapper.cpp @@ -142,7 +142,7 @@ void VkLightmapper::SelectTiles(const TArray& tiles) { LightmapTile* tile = tiles[i]; - if (!tile->NeedsUpdate) + if (!tile->ReceivedNewLight) continue; // Only grab surfaces until our bake texture is full @@ -158,7 +158,9 @@ void VkLightmapper::SelectTiles(const TArray& tiles) bakeImage.maxX = std::max(bakeImage.maxX, uint16_t(result->X + tile->AtlasLocation.Width)); bakeImage.maxY = std::max(bakeImage.maxY, uint16_t(result->Y + tile->AtlasLocation.Height)); - tile->NeedsUpdate = false; + tile->ReceivedNewLight = false; + tile->NeedsInitialBake = false; + tile->GeometryUpdate = false; } } @@ -248,7 +250,7 @@ void VkLightmapper::Render() { while (i < count) { - selectedTiles[i].Tile->NeedsUpdate = true; + selectedTiles[i].Tile->ReceivedNewLight = true; i++; } break; diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index c33aa21a2..9f09ddd1d 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3173,7 +3173,9 @@ bool MapLoader::LoadLightmap(MapData* map) tile->Transform.TranslateWorldToLocal = entry.translateWorldToLocal; tile->Transform.ProjLocalToU = entry.projLocalToU; tile->Transform.ProjLocalToV = entry.projLocalToV; - tile->NeedsUpdate = false; + tile->NeedsInitialBake = false; + tile->GeometryUpdate = false; + tile->ReceivedNewLight = false; foundBindings.Push({ &entry, tile }); } @@ -3210,7 +3212,9 @@ bool MapLoader::LoadLightmap(MapData* map) memcpy(dstline, srcline, width * 3 * sizeof(uint16_t)); } - tile->NeedsUpdate = false; + tile->NeedsInitialBake = false; + tile->GeometryUpdate = false; + tile->ReceivedNewLight = false; } if (errors > 0) diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index a257ec36e..2a635b8e8 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -48,9 +48,9 @@ static int InvalidateLightmap() for (auto& tile : level.levelMesh->Lightmap.Tiles) { - if (!tile.NeedsUpdate) + if (!tile.NeedsInitialBake) ++count; - tile.NeedsUpdate = true; + tile.NeedsInitialBake = true; } return count; @@ -126,12 +126,7 @@ CCMD(invalidatelightmap) if (!RequireLightmap()) return; int count = InvalidateLightmap(); - for (auto& tile : level.levelMesh->Lightmap.Tiles) - { - if (!tile.NeedsUpdate) - ++count; - tile.NeedsUpdate = true; - } + Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->Lightmap.Tiles.Size()); } @@ -160,7 +155,9 @@ void DoomLevelMesh::PrintSurfaceInfo(const LevelMeshSurface* surface) Printf(" Atlas page: %d, x:%d, y:%d\n", tile->AtlasLocation.ArrayIndex, tile->AtlasLocation.X, tile->AtlasLocation.Y); Printf(" Pixels: %dx%d (area: %d)\n", tile->AtlasLocation.Width, tile->AtlasLocation.Height, tile->AtlasLocation.Area()); Printf(" Sample dimension: %d\n", tile->SampleDimension); - Printf(" Needs update?: %d\n", tile->NeedsUpdate); + Printf(" Background update?: %d\n", (int)tile->NeedsInitialBake); + Printf(" Geometry update?: %d\n", (int)tile->GeometryUpdate); + Printf(" Light update?: %d\n", (int)tile->ReceivedNewLight); } Printf(" Sector group: %d\n", surface->SectorGroup); Printf(" Texture: '%s'\n", gameTexture ? gameTexture->GetName().GetChars() : ""); @@ -238,9 +235,16 @@ DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap) // This is a bit of a hack. Lights aren't available until BeginFrame is called. // Unfortunately we need a surface list already at this point for our Mesh.MaxNodes calculation for (unsigned int i = 0; i < Sides.Size(); i++) - UpdateSide(i, SurfaceUpdateType::Full); + UpdateSide(i, SurfaceUpdateType::LightList); for (unsigned int i = 0; i < Flats.Size(); i++) - UpdateFlat(i, SurfaceUpdateType::Full); + UpdateFlat(i, SurfaceUpdateType::LightList); + + // Initial tiles are black and should be background updated + for (auto& tile : Lightmap.Tiles) + { + tile.GeometryUpdate = false; + tile.NeedsInitialBake = true; + } CreateCollision(); UploadPortals(); @@ -845,7 +849,7 @@ void DoomLevelMesh::UpdateSideLightList(FLevelLocals& doomMap, unsigned int side int tile = Mesh.Surfaces[surf].LightmapTileIndex; if (tile != -1) { - Lightmap.Tiles[tile].NeedsUpdate = true; + Lightmap.Tiles[tile].ReceivedNewLight = true; } surf = DoomSurfaceInfos[surf].NextSurface; } @@ -853,14 +857,14 @@ void DoomLevelMesh::UpdateSideLightList(FLevelLocals& doomMap, unsigned int side void DoomLevelMesh::UpdateFlatLightList(FLevelLocals& doomMap, unsigned int sectorIndex) { + for (auto& lightlist : Flats[sectorIndex].Lights) + FreeLightList(lightlist.Start, lightlist.Count); + Flats[sectorIndex].Lights.Clear(); + sector_t* sector = &doomMap.sectors[sectorIndex]; - int lightListSection = 0; for (FSection& section : doomMap.sections.SectionsForSector(sectorIndex)) { - auto& lightlist = Flats[sectorIndex].Lights[lightListSection]; - FreeLightList(lightlist.Start, lightlist.Count); - lightlist = CreateLightList(section.lighthead, section.sector->PortalGroup); - lightListSection++; + Flats[sectorIndex].Lights.Push(CreateLightList(section.lighthead, section.sector->PortalGroup)); } int surf = Flats[sectorIndex].FirstSurface; @@ -874,7 +878,7 @@ void DoomLevelMesh::UpdateFlatLightList(FLevelLocals& doomMap, unsigned int sect int tile = Mesh.Surfaces[surf].LightmapTileIndex; if (tile != -1) { - Lightmap.Tiles[tile].NeedsUpdate = true; + Lightmap.Tiles[tile].ReceivedNewLight = true; } surf = DoomSurfaceInfos[surf].NextSurface; } @@ -888,7 +892,7 @@ void DoomLevelMesh::UpdateSideShadows(FLevelLocals& doomMap, unsigned int sideIn int tile = Mesh.Surfaces[surf].LightmapTileIndex; if (tile != -1) { - Lightmap.Tiles[tile].NeedsUpdate = true; + Lightmap.Tiles[tile].ReceivedNewLight = true; } surf = DoomSurfaceInfos[surf].NextSurface; } @@ -902,7 +906,7 @@ void DoomLevelMesh::UpdateFlatShadows(FLevelLocals& doomMap, unsigned int sector int tile = Mesh.Surfaces[surf].LightmapTileIndex; if (tile != -1) { - Lightmap.Tiles[tile].NeedsUpdate = true; + Lightmap.Tiles[tile].ReceivedNewLight = true; } surf = DoomSurfaceInfos[surf].NextSurface; } diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index aa25970c6..7a17a52e8 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -720,26 +720,59 @@ void HWDrawInfo::UpdateLightmaps() { if (outer) outer->UpdateLightmaps(); - /* - if (VisibleTiles.size() > (size_t)lm_max_updates) - VisibleTiles.resize(lm_max_updates); - */ - if (VisibleTiles.size() < (size_t)lm_background_updates) + VisibleTiles.Result.Clear(); + + size_t max_updates = (size_t)lm_max_updates; + + // We always must bake tiles that received new geometry + for (auto tile : VisibleTiles.Geometry) + VisibleTiles.Result.Push(tile); + + if (VisibleTiles.Result.size() < max_updates) { - for (auto& e : level.levelMesh->Lightmap.Tiles) + // We got room for more. Include some visible tiles that are being background updated + for (auto tile : VisibleTiles.Background) + VisibleTiles.Result.Push(tile); + + if (VisibleTiles.Result.size() < max_updates) { - if (e.NeedsUpdate && e.LastSeen != TileSeenCounter) + // We still have room. Add tiles that received new light + for (auto tile : VisibleTiles.ReceivedNewLight) + VisibleTiles.Result.Push(tile); + + if (VisibleTiles.Result.size() < max_updates) { - VisibleTiles.Push(&e); - if (VisibleTiles.size() >= (size_t)lm_background_updates) - break; + max_updates = VisibleTiles.Result.size() + (size_t)lm_background_updates; + + // Look for more background updates + for (auto& e : level.levelMesh->Lightmap.Tiles) + { + if (e.NeedsInitialBake && e.LastSeen != TileSeenCounter) + { + VisibleTiles.Result.Push(&e); + if (VisibleTiles.Result.size() >= max_updates) + break; + } + } } + else + { + VisibleTiles.Result.resize(max_updates); + } + } + else + { + VisibleTiles.Result.resize(max_updates); } } - screen->UpdateLightmaps(VisibleTiles); - VisibleTiles.Clear(); + screen->UpdateLightmaps(VisibleTiles.Result); + + VisibleTiles.Geometry.Clear(); + VisibleTiles.Background.Clear(); + VisibleTiles.ReceivedNewLight.Clear(); + VisibleTiles.Result.Clear(); } void HWDrawInfo::RenderScene(FRenderState &state) diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index f92bb3cf5..483eeea83 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -157,7 +157,13 @@ struct HWDrawInfo TArray Decals[2]; // the second slot is for mirrors which get rendered in a separate pass. TArray hudsprites; // These may just be stored by value. TArray Fogballs; - TArray VisibleTiles; + struct + { + TArray Geometry; + TArray ReceivedNewLight; + TArray Background; + TArray Result; + } VisibleTiles; int TileSeenCounter = 0; uint64_t LastFrameTime = 0; @@ -241,10 +247,16 @@ public: } LightmapTile* tile = &Level->levelMesh->Lightmap.Tiles[tileIndex]; - if (tile->NeedsUpdate && tile->LastSeen != TileSeenCounter) + if (tile->LastSeen != TileSeenCounter) { tile->LastSeen = TileSeenCounter; - VisibleTiles.Push(tile); + + if (tile->GeometryUpdate) + VisibleTiles.Geometry.Push(tile); + else if (tile->NeedsInitialBake) + VisibleTiles.Background.Push(tile); + else if (tile->ReceivedNewLight) + VisibleTiles.ReceivedNewLight.Push(tile); } }