Fix Global Surface Atlas tiles padding and position precision to have proper stability

This commit is contained in:
2026-05-12 17:10:02 +02:00
parent fc8a9b69d2
commit b039e3779d
@@ -67,8 +67,8 @@ GPU_CB_STRUCT(Data0 {
PACK_STRUCT(struct AtlasTileVertex PACK_STRUCT(struct AtlasTileVertex
{ {
Half2 Position; Float2 Position;
Half2 TileUV; Float2 TileUV;
uint32 TileAddress; uint32 TileAddress;
}); });
@@ -649,8 +649,8 @@ public:
tile->ViewBoundsSize = viewExtent.GetAbsolute() * 2.0f; tile->ViewBoundsSize = viewExtent.GetAbsolute() * 2.0f;
// Per-tile data // Per-tile data
const float tileWidth = (float)tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING; const float tileWidth = (float)(tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING);
const float tileHeight = (float)tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING; const float tileHeight = (float)(tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING);
tileData[0] = Float4(tile->X, tile->Y, tileWidth, tileHeight) * ResolutionInv; tileData[0] = Float4(tile->X, tile->Y, tileWidth, tileHeight) * ResolutionInv;
tileData[1] = Float4(tile->ViewMatrix.M11, tile->ViewMatrix.M12, tile->ViewMatrix.M13, tile->ViewMatrix.M41); tileData[1] = Float4(tile->ViewMatrix.M11, tile->ViewMatrix.M12, tile->ViewMatrix.M13, tile->ViewMatrix.M41);
tileData[2] = Float4(tile->ViewMatrix.M21, tile->ViewMatrix.M22, tile->ViewMatrix.M23, tile->ViewMatrix.M42); tileData[2] = Float4(tile->ViewMatrix.M21, tile->ViewMatrix.M22, tile->ViewMatrix.M23, tile->ViewMatrix.M42);
@@ -979,8 +979,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
if (!_vertexBuffer) if (!_vertexBuffer)
{ {
auto layout = GPUVertexLayout::Get({ auto layout = GPUVertexLayout::Get({
{ VertexElement::Types::Position, 0, 0, 0, PixelFormat::R16G16_Float }, { VertexElement::Types::Position, 0, 0, 0, PixelFormat::R32G32_Float },
{ VertexElement::Types::TexCoord0, 0, 0, 0, PixelFormat::R16G16_Float }, { VertexElement::Types::TexCoord0, 0, 0, 0, PixelFormat::R32G32_Float },
{ VertexElement::Types::TexCoord1, 0, 0, 0, PixelFormat::R32_UInt }, { VertexElement::Types::TexCoord1, 0, 0, 0, PixelFormat::R32_UInt },
}); });
_vertexBuffer = New<DynamicVertexBuffer>(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer"), layout); _vertexBuffer = New<DynamicVertexBuffer>(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer"), layout);
@@ -994,26 +994,15 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
// Utility for writing into tiles vertex buffer // Utility for writing into tiles vertex buffer
const Float2 posToClipMul(2.0f * resolutionInv, -2.0f * resolutionInv); const Float2 posToClipMul(2.0f * resolutionInv, -2.0f * resolutionInv);
const Float2 posToClipAdd(-1.0f, 1.0f); const Float2 posToClipAdd(-1.0f, 1.0f);
#define VB_WRITE_TILE_POS_ONLY(tile) \
Float2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width), (float)(tile->Y + tile->Height)); \
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
quad[0] = { { max }, Half2::Zero, 0 }; \
quad[1] = { { min.X, max.Y }, Half2::Zero, 0 }; \
quad[2] = { { min }, Half2::Zero, 0 }; \
quad[3] = quad[2]; \
quad[4] = { { max.X, min.Y }, Half2::Zero, 0 }; \
quad[5] = quad[0]
#define VB_WRITE_TILE(tile) \ #define VB_WRITE_TILE(tile) \
Float2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width), (float)(tile->Y + tile->Height)); \ Float2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING), (float)(tile->Y + tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING)); \
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \ Float2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
Float2 minUV(0, 0), maxUV(1, 1); \
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \ auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
quad[0] = { { max }, { maxUV }, tile->Address }; \ quad[0] = { max, Float2::One, tile->Address }; \
quad[1] = { { min.X, max.Y }, { minUV.X, maxUV.Y }, tile->Address }; \ quad[1] = { { min.X, max.Y }, Float2::UnitY, tile->Address }; \
quad[2] = { { min }, { minUV }, tile->Address }; \ quad[2] = { min, Float2::Zero, tile->Address }; \
quad[3] = quad[2]; \ quad[3] = quad[2]; \
quad[4] = { { max.X, min.Y }, { maxUV.X, minUV.Y }, tile->Address }; \ quad[4] = { { max.X, min.Y }, Float2::UnitX, tile->Address }; \
quad[5] = quad[0] quad[5] = quad[0]
#define VB_DRAW() \ #define VB_DRAW() \
_vertexBuffer->Flush(context); \ _vertexBuffer->Flush(context); \
@@ -1069,17 +1058,17 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
auto* tile = object.Tiles[tileIndex]; auto* tile = object.Tiles[tileIndex];
if (!tile) if (!tile)
continue; continue;
VB_WRITE_TILE_POS_ONLY(tile); VB_WRITE_TILE(tile);
// Write old atlas UVs of this tile // Write old atlas UVs of this tile
auto defragTile = e.Tiles[tileIndex]; auto defragTile = e.Tiles[tileIndex];
Half2 minUV(defragTile.X * resolutionInv, defragTile.Y * resolutionInv); Float2 minUV((float)defragTile.X * resolutionInv, (float)defragTile.Y * resolutionInv);
Half2 maxUV((defragTile.X + defragTile.Width) * resolutionInv, (defragTile.Y + defragTile.Height) * resolutionInv); Float2 maxUV((float)(defragTile.X + defragTile.Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING) * resolutionInv, (float)(defragTile.Y + defragTile.Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING) * resolutionInv);
quad[0].TileUV = maxUV; quad[0].TileUV = maxUV;
quad[1].TileUV = Half2(minUV.X, maxUV.Y); quad[1].TileUV = Float2(minUV.X, maxUV.Y);
quad[2].TileUV = minUV; quad[2].TileUV = minUV;
quad[3].TileUV = minUV; quad[3].TileUV = minUV;
quad[4].TileUV = Half2(maxUV.X, minUV.Y); quad[4].TileUV = Float2(maxUV.X, minUV.Y);
quad[5].TileUV = maxUV; quad[5].TileUV = maxUV;
} }
@@ -1163,7 +1152,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
auto* tile = object.Tiles[tileIndex]; auto* tile = object.Tiles[tileIndex];
if (!tile) if (!tile)
continue; continue;
VB_WRITE_TILE_POS_ONLY(tile); VB_WRITE_TILE(tile);
} }
} }
context->SetState(_psClear); context->SetState(_psClear);
@@ -1211,8 +1200,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
auto* tile = object.Tiles[tileIndex]; auto* tile = object.Tiles[tileIndex];
if (!tile) if (!tile)
continue; continue;
const float tileWidth = (float)tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING;
const float tileHeight = (float)tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING;
// Setup projection to capture object from the side // Setup projection to capture object from the side
renderContextTiles.View.Position = tile->ViewPosition; renderContextTiles.View.Position = tile->ViewPosition;
@@ -1228,6 +1215,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
#endif #endif
// Draw // Draw
const float tileWidth = (float)(tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING);
const float tileHeight = (float)(tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING);
context->SetViewportAndScissors(Viewport(tile->X, tile->Y, tileWidth, tileHeight)); context->SetViewportAndScissors(Viewport(tile->X, tile->Y, tileWidth, tileHeight));
renderContextTiles.List->ExecuteDrawCalls(renderContextTiles, drawCallsListGBuffer); renderContextTiles.List->ExecuteDrawCalls(renderContextTiles, drawCallsListGBuffer);
renderContextTiles.List->ExecuteDrawCalls(renderContextTiles, drawCallsListGBufferNoDecals); renderContextTiles.List->ExecuteDrawCalls(renderContextTiles, drawCallsListGBufferNoDecals);
@@ -1511,7 +1500,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
} }
// Copy emissive light into the final direct lighting atlas // Copy emissive light into the final direct lighting atlas
if (allLightingDirty) if (allLightingDirty && surfaceAtlasData.Objects.Count() > 100) // Batch copy only in high usage
{ {
PROFILE_GPU("Copy Emissive"); PROFILE_GPU("Copy Emissive");
context->Draw(surfaceAtlasData.AtlasEmissive); context->Draw(surfaceAtlasData.AtlasEmissive);