Only force baking of tiles where the geometry changed

This commit is contained in:
Magnus Norddahl 2025-05-13 01:51:41 +02:00
commit 76fb5901d7
8 changed files with 106 additions and 45 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -142,7 +142,7 @@ void VkLightmapper::SelectTiles(const TArray<LightmapTile*>& 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<LightmapTile*>& tiles)
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(result->X + tile->AtlasLocation.Width));
bakeImage.maxY = std::max<uint16_t>(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;

View file

@ -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)

View file

@ -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() : "<nullptr>");
@ -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;
}

View file

@ -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)

View file

@ -157,7 +157,13 @@ struct HWDrawInfo
TArray<HWDecal *> Decals[2]; // the second slot is for mirrors which get rendered in a separate pass.
TArray<HUDSprite> hudsprites; // These may just be stored by value.
TArray<Fogball> Fogballs;
TArray<LightmapTile*> VisibleTiles;
struct
{
TArray<LightmapTile*> Geometry;
TArray<LightmapTile*> ReceivedNewLight;
TArray<LightmapTile*> Background;
TArray<LightmapTile*> 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);
}
}