diff --git a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp index fbdf85a02..c718abfe6 100644 --- a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.cpp @@ -20,7 +20,7 @@ #define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (1) #define GLOBAL_SURFACE_ATLAS_OBJECT_STRIDE (16 * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE) #define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles -#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW 1 // Forces to redraw all object tiles every frame +#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW 0 // Forces to redraw all object tiles every frame #define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations) #if GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS @@ -38,6 +38,11 @@ PACK_STRUCT(struct Data0 GlobalSurfaceAtlasPass::GlobalSurfaceAtlasData GlobalSurfaceAtlas; }); +PACK_STRUCT(struct AtlasTileVertex + { + Half2 Position; + }); + struct GlobalSurfaceAtlasTile : RectPack { GlobalSurfaceAtlasTile(uint16 x, uint16 y, uint16 width, uint16 height) @@ -138,6 +143,17 @@ bool GlobalSurfaceAtlasPass::setupResources() if (_psDebug->Init(psDesc)) return true; } + if (!_psClear) + { + _psClear = device->CreatePipelineState(); + psDesc.DepthTestEnable = true; + psDesc.DepthWriteEnable = true; + psDesc.DepthFunc = ComparisonFunc::Always; + psDesc.VS = shader->GetVS("VS_Clear"); + psDesc.PS = shader->GetPS("PS_Clear"); + if (_psClear->Init(psDesc)) + return true; + } return false; } @@ -146,6 +162,7 @@ bool GlobalSurfaceAtlasPass::setupResources() void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj) { + SAFE_DELETE_GPU_RESOURCE(_psClear); SAFE_DELETE_GPU_RESOURCE(_psDebug); invalidateResources(); } @@ -157,7 +174,9 @@ void GlobalSurfaceAtlasPass::Dispose() RendererPass::Dispose(); // Cleanup + SAFE_DELETE(_vertexBuffer); SAFE_DELETE(_objectsBuffer); + SAFE_DELETE_GPU_RESOURCE(_psClear); SAFE_DELETE_GPU_RESOURCE(_psDebug); _cb0 = nullptr; _shader = nullptr; @@ -208,6 +227,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co surfaceAtlasData.Resolution = resolution; LOG(Info, "Global Surface Atlas resolution: {0}, memory usage: {1} MB", resolution, memUsage / 1024 / 1024); } + if (!_vertexBuffer) + _vertexBuffer = New(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer")); // Add objects into the atlas if (_objectsBuffer) @@ -223,7 +244,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co const uint16 maxTileResolution = 128; // Maximum size (in texels) of the tile in atlas const uint16 tileResolutionAlignment = 8; // Alignment to snap (down) tiles resolution which allows to reuse atlas slots once object gets resizes/replaced by other object const float minObjectRadius = 20.0f; // Skip too small objects - const float tileTexelsPerWorldUnit = 1.0f / 4.0f; // Scales the tiles resolution + const float tileTexelsPerWorldUnit = 1.0f / 10.0f; // Scales the tiles resolution const float distanceScalingStart = 2000.0f; // Distance from camera at which the tiles resolution starts to be scaled down const float distanceScalingEnd = 5000.0f; // Distance from camera at which the tiles resolution end to be scaled down const float distanceScaling = 0.1f; // The scale for tiles at distanceScalingEnd and further away @@ -376,7 +397,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co PROFILE_GPU_CPU("Clear"); if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW) { - /// Full-atlas hardware clear + // Full-atlas hardware clear context->ClearDepth(depthBuffer); context->Clear(targetBuffers[0], Color::Transparent); context->Clear(targetBuffers[1], Color::Transparent); @@ -385,7 +406,36 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co } else { - // TODO: clear all dirt tiles in a single draw call (software) + // Per-tile clear (with a single draw call) + _vertexBuffer->Clear(); + _vertexBuffer->Data.EnsureCapacity(_dirtyObjectsBuffer.Count() * 6 * sizeof(AtlasTileVertex)); + const Vector2 posToClipMul(2.0f / resolution, -2.0f / resolution); + const Vector2 posToClipAdd(-1.0f, 1.0f); + for (const auto& e : _dirtyObjectsBuffer) + { + const auto& object = *e.Second; + for (int32 tileIndex = 0; tileIndex < 6; tileIndex++) + { + auto* tile = object.Tiles[tileIndex]; + if (!tile) + continue; + Vector2 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(6); + quad[0] = { { max } }; + quad[1] = { { min.X, max.Y } }; + quad[2] = { { min } }; + quad[3] = quad[2]; + quad[4] = { { max.X, min.Y } }; + quad[5] = quad[0]; + } + } + _vertexBuffer->Flush(context); + auto vb = _vertexBuffer->GetBuffer(); + context->SetState(_psClear); + context->SetViewportAndScissors(Viewport(0, 0, resolution, resolution)); + context->BindVB(ToSpan(&vb, 1)); + context->DrawInstanced(_vertexBuffer->Data.Count() / sizeof(AtlasTileVertex), 1); } } renderContextTiles.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer].CanUseInstancing = false; diff --git a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.h b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.h index 45db66b2b..4966913d9 100644 --- a/Source/Engine/Renderer/GlobalSurfaceAtlasPass.h +++ b/Source/Engine/Renderer/GlobalSurfaceAtlasPass.h @@ -27,10 +27,12 @@ public: private: bool _supported = false; AssetReference _shader; + GPUPipelineState* _psClear = nullptr; GPUPipelineState* _psDebug = nullptr; GPUConstantBuffer* _cb0 = nullptr; // Rasterization cache + class DynamicVertexBuffer* _vertexBuffer = nullptr; class DynamicTypedBuffer* _objectsBuffer = nullptr; Array> _dirtyObjectsBuffer; diff --git a/Source/Shaders/GlobalSurfaceAtlas.shader b/Source/Shaders/GlobalSurfaceAtlas.shader index 4b0f00b16..1f8101f6c 100644 --- a/Source/Shaders/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GlobalSurfaceAtlas.shader @@ -15,6 +15,24 @@ GlobalSDFData GlobalSDF; GlobalSurfaceAtlasData GlobalSurfaceAtlas; META_CB_END +// Vertex shader for Global Surface Atlas software clearing +META_VS(true, FEATURE_LEVEL_SM5) +META_VS_IN_ELEMENT(POSITION, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) +float4 VS_Clear(float2 Position : POSITION0) : SV_Position +{ + return float4(Position, 1, 1); +} + +// Pixel shader for Global Surface Atlas software clearing +META_PS(true, FEATURE_LEVEL_SM5) +void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out float4 RT1 : SV_Target2, out float4 RT2 : SV_Target3) +{ + Light = float4(0, 0, 0, 0); + RT0 = float4(0, 0, 0, 0); + RT1 = float4(0, 0, 0, 0); + RT2 = float4(1, 0, 0, 0); +} + #ifdef _PS_Debug Texture3D GlobalSDFTex[4] : register(t0);