Optimize Global Surface Atlas defragmentation with copying old atlas to avoid redrawing all objects
This commit is contained in:
Binary file not shown.
@@ -44,6 +44,7 @@
|
|||||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS 0 // Debug draws culled chunks bounds (non-empty)
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS 0 // Debug draws culled chunks bounds (non-empty)
|
||||||
#define GLOBAL_SURFACE_ATLAS_MAX_NEW_OBJECTS_PER_FRAME 500 // Limits the amount of newly added objects to atlas per-frame to reduce hitches on 1st frame or camera-cut
|
#define GLOBAL_SURFACE_ATLAS_MAX_NEW_OBJECTS_PER_FRAME 500 // Limits the amount of newly added objects to atlas per-frame to reduce hitches on 1st frame or camera-cut
|
||||||
#define GLOBAL_SURFACE_ATLAS_DIRTY_FRAMES(flags) (EnumHasAnyFlags(flags, StaticFlags::Lightmap) ? 200 : 10) // Amount of frames after which update object (less frequent updates for static scenes)
|
#define GLOBAL_SURFACE_ATLAS_DIRTY_FRAMES(flags) (EnumHasAnyFlags(flags, StaticFlags::Lightmap) ? 200 : 10) // Amount of frames after which update object (less frequent updates for static scenes)
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_DEFRAG_COPY 1 // Copies existing atlas contents after defragmentation to avoid dirtying objects
|
||||||
|
|
||||||
#if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS || GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS
|
#if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS || GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS
|
||||||
#include "Engine/Debug/DebugDraw.h"
|
#include "Engine/Debug/DebugDraw.h"
|
||||||
@@ -138,6 +139,18 @@ struct GlobalSurfaceAtlasObject
|
|||||||
POD_COPYABLE(GlobalSurfaceAtlasObject);
|
POD_COPYABLE(GlobalSurfaceAtlasObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GlobalSurfaceAtlasObjectDefragmentation
|
||||||
|
{
|
||||||
|
void* ActorObject;
|
||||||
|
struct Tile
|
||||||
|
{
|
||||||
|
uint16 X;
|
||||||
|
uint16 Y;
|
||||||
|
uint16 Width;
|
||||||
|
uint16 Height;
|
||||||
|
} Tiles[6];
|
||||||
|
};
|
||||||
|
|
||||||
struct GlobalSurfaceAtlasLight
|
struct GlobalSurfaceAtlasLight
|
||||||
{
|
{
|
||||||
uint64 LastFrameUsed = 0;
|
uint64 LastFrameUsed = 0;
|
||||||
@@ -170,6 +183,10 @@ public:
|
|||||||
Dictionary<Guid, GlobalSurfaceAtlasLight> Lights;
|
Dictionary<Guid, GlobalSurfaceAtlasLight> Lights;
|
||||||
SamplesBuffer<uint32, 30> CulledObjectsUsageHistory;
|
SamplesBuffer<uint32, 30> CulledObjectsUsageHistory;
|
||||||
Array<GlobalSurfaceAtlasFreeShaderSlot> FreeObjectsBufferSlots[6]; // Bin for each tile count for quick reusage
|
Array<GlobalSurfaceAtlasFreeShaderSlot> FreeObjectsBufferSlots[6]; // Bin for each tile count for quick reusage
|
||||||
|
#if GLOBAL_SURFACE_ATLAS_DEFRAG_COPY
|
||||||
|
Array<GlobalSurfaceAtlasObjectDefragmentation> ObjectsDefragmentation;
|
||||||
|
int32 ObjectsDefragmentationTilesCount = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cached data to be reused during RasterizeActor
|
// Cached data to be reused during RasterizeActor
|
||||||
Array<void*> DirtyObjectsBuffer;
|
Array<void*> DirtyObjectsBuffer;
|
||||||
@@ -292,10 +309,35 @@ public:
|
|||||||
(float)AtlasPixelsUsed / AtlasPixelsTotal < maxUsageToDefrag)
|
(float)AtlasPixelsUsed / AtlasPixelsTotal < maxUsageToDefrag)
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Defragment Atlas");
|
PROFILE_CPU_NAMED("Defragment Atlas");
|
||||||
|
|
||||||
|
// Destroy atlas to recreate it without fragmentation but cache existing atlas structure to copy existing tiles back to the new atlas to reduce amount of dirty tiles for redraw
|
||||||
LastFrameAtlasDefragmentation = Engine::FrameCount;
|
LastFrameAtlasDefragmentation = Engine::FrameCount;
|
||||||
|
#if GLOBAL_SURFACE_ATLAS_DEFRAG_COPY
|
||||||
|
ObjectsDefragmentation.Clear();
|
||||||
|
ObjectsDefragmentation.Resize(Objects.Count());
|
||||||
|
ObjectsDefragmentationTilesCount = 0;
|
||||||
|
#endif
|
||||||
|
int32 i = 0;
|
||||||
for (auto& e : Objects)
|
for (auto& e : Objects)
|
||||||
{
|
{
|
||||||
auto& object = e.Value;
|
auto& object = e.Value;
|
||||||
|
|
||||||
|
#if GLOBAL_SURFACE_ATLAS_DEFRAG_COPY
|
||||||
|
// Cache object tiles positions in atlas to copy them back after defragmentation to avoid dirtying all objects
|
||||||
|
auto& defrag = ObjectsDefragmentation[i++];
|
||||||
|
defrag.ActorObject = e.Key;
|
||||||
|
Platform::MemoryClear(defrag.Tiles, sizeof(defrag.Tiles));
|
||||||
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
|
{
|
||||||
|
if (object.Tiles[tileIndex])
|
||||||
|
{
|
||||||
|
Platform::MemoryCopy(&defrag.Tiles[tileIndex].X, &object.Tiles[tileIndex]->X, sizeof(GlobalSurfaceAtlasTile::Size) * 4);
|
||||||
|
ObjectsDefragmentationTilesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Free atlas tiles and data slot
|
||||||
Platform::MemoryClear(object.Tiles, sizeof(object.Tiles));
|
Platform::MemoryClear(object.Tiles, sizeof(object.Tiles));
|
||||||
if (object.ObjectDataAddress.TilesCount != 0)
|
if (object.ObjectDataAddress.TilesCount != 0)
|
||||||
{
|
{
|
||||||
@@ -459,6 +501,7 @@ public:
|
|||||||
auto objectsListData = (uint32*)ObjectsListBuffer.Data.Get();
|
auto objectsListData = (uint32*)ObjectsListBuffer.Data.Get();
|
||||||
int32 dirtyTiles = 0, objectIndex = 0;
|
int32 dirtyTiles = 0, objectIndex = 0;
|
||||||
int32 dirtyObjectsLimitLeft = 50; // TODO: expose as scalability parameter
|
int32 dirtyObjectsLimitLeft = 50; // TODO: expose as scalability parameter
|
||||||
|
// TODO: sort dirt objects by size to collect biggest ones first
|
||||||
for (auto& e : Objects)
|
for (auto& e : Objects)
|
||||||
{
|
{
|
||||||
auto& object = e.Value;
|
auto& object = e.Value;
|
||||||
@@ -699,13 +742,20 @@ bool GlobalSurfaceAtlasPass::setupResources()
|
|||||||
if (_psDebug1->Init(psDesc))
|
if (_psDebug1->Init(psDesc))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
psDesc.DepthEnable = true;
|
||||||
|
psDesc.DepthWriteEnable = true;
|
||||||
|
psDesc.DepthFunc = ComparisonFunc::Always;
|
||||||
|
psDesc.VS = shader->GetVS("VS_Atlas");
|
||||||
|
if (!_psCopy)
|
||||||
|
{
|
||||||
|
_psCopy = device->CreatePipelineState();
|
||||||
|
psDesc.PS = shader->GetPS("PS_Copy");
|
||||||
|
if (_psCopy->Init(psDesc))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!_psClear)
|
if (!_psClear)
|
||||||
{
|
{
|
||||||
_psClear = device->CreatePipelineState();
|
_psClear = device->CreatePipelineState();
|
||||||
psDesc.DepthEnable = true;
|
|
||||||
psDesc.DepthWriteEnable = true;
|
|
||||||
psDesc.DepthFunc = ComparisonFunc::Always;
|
|
||||||
psDesc.VS = shader->GetVS("VS_Atlas");
|
|
||||||
psDesc.PS = shader->GetPS("PS_Clear");
|
psDesc.PS = shader->GetPS("PS_Clear");
|
||||||
if (_psClear->Init(psDesc))
|
if (_psClear->Init(psDesc))
|
||||||
return true;
|
return true;
|
||||||
@@ -716,7 +766,6 @@ bool GlobalSurfaceAtlasPass::setupResources()
|
|||||||
if (!_psClearLighting)
|
if (!_psClearLighting)
|
||||||
{
|
{
|
||||||
_psClearLighting = device->CreatePipelineState();
|
_psClearLighting = device->CreatePipelineState();
|
||||||
psDesc.VS = shader->GetVS("VS_Atlas");
|
|
||||||
psDesc.PS = shader->GetPS("PS_ClearLighting");
|
psDesc.PS = shader->GetPS("PS_ClearLighting");
|
||||||
if (_psClearLighting->Init(psDesc))
|
if (_psClearLighting->Init(psDesc))
|
||||||
return true;
|
return true;
|
||||||
@@ -746,6 +795,7 @@ bool GlobalSurfaceAtlasPass::setupResources()
|
|||||||
|
|
||||||
void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj)
|
void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj)
|
||||||
{
|
{
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psCopy);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClearLighting);
|
SAFE_DELETE_GPU_RESOURCE(_psClearLighting);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
||||||
@@ -767,6 +817,7 @@ void GlobalSurfaceAtlasPass::Dispose()
|
|||||||
SAFE_DELETE_GPU_RESOURCE(_culledObjectsSizeBuffer);
|
SAFE_DELETE_GPU_RESOURCE(_culledObjectsSizeBuffer);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClearLighting);
|
SAFE_DELETE_GPU_RESOURCE(_psClearLighting);
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psCopy);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1);
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting);
|
SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting);
|
||||||
@@ -837,8 +888,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
const float resolutionInv = 1.0f / (float)resolution;
|
const float resolutionInv = 1.0f / (float)resolution;
|
||||||
|
|
||||||
// Initialize buffers
|
// Initialize buffers
|
||||||
bool noCache = surfaceAtlasData.Resolution != resolution;
|
bool atlasResized = surfaceAtlasData.Resolution != resolution;
|
||||||
if (noCache)
|
if (atlasResized)
|
||||||
{
|
{
|
||||||
surfaceAtlasData.Reset();
|
surfaceAtlasData.Reset();
|
||||||
surfaceAtlasData.Atlas.Init(resolution, resolution);
|
surfaceAtlasData.Atlas.Init(resolution, resolution);
|
||||||
@@ -869,6 +920,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
memUsage += surfaceAtlasData.ChunksBuffer->GetMemoryUsage();
|
memUsage += surfaceAtlasData.ChunksBuffer->GetMemoryUsage();
|
||||||
}
|
}
|
||||||
LOG(Info, "Global Surface Atlas resolution: {0}, memory usage: {1} MB", resolution, memUsage / (1024 * 1024));
|
LOG(Info, "Global Surface Atlas resolution: {0}, memory usage: {1} MB", resolution, memUsage / (1024 * 1024));
|
||||||
|
|
||||||
|
context->Clear(surfaceAtlasData.AtlasLighting->View(), Color::Transparent);
|
||||||
}
|
}
|
||||||
for (SceneRendering* scene : renderContext.List->Scenes)
|
for (SceneRendering* scene : renderContext.List->Scenes)
|
||||||
surfaceAtlasData.ListenSceneRendering(scene);
|
surfaceAtlasData.ListenSceneRendering(scene);
|
||||||
@@ -894,12 +947,12 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
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), (float)(tile->Y + tile->Height)); \
|
||||||
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
||||||
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
||||||
quad[0].Position = max; \
|
quad[0] = { { max }, Half2::Zero, 0 }; \
|
||||||
quad[1].Position = { min.X, max.Y }; \
|
quad[1] = { { min.X, max.Y }, Half2::Zero, 0 }; \
|
||||||
quad[2].Position = min; \
|
quad[2] = { { min }, Half2::Zero, 0 }; \
|
||||||
quad[3].Position = quad[2].Position; \
|
quad[3] = quad[2]; \
|
||||||
quad[4].Position = { max.X, min.Y }; \
|
quad[4] = { { max.X, min.Y }, Half2::Zero, 0 }; \
|
||||||
quad[5].Position = quad[0].Position
|
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), (float)(tile->Y + tile->Height)); \
|
||||||
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
||||||
@@ -917,11 +970,102 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
context->BindVB(ToSpan(&vb, 1)); \
|
context->BindVB(ToSpan(&vb, 1)); \
|
||||||
context->DrawInstanced(_vertexBuffer->Data.Count() / sizeof(AtlasTileVertex), 1);
|
context->DrawInstanced(_vertexBuffer->Data.Count() / sizeof(AtlasTileVertex), 1);
|
||||||
|
|
||||||
|
#if GLOBAL_SURFACE_ATLAS_DEFRAG_COPY
|
||||||
|
// When performing atlas defragmentation, preserve the previous atlas to copy tiles from it (to avoid redrawing them)
|
||||||
|
bool atlasDefrag = !atlasResized && surfaceAtlasData.LastFrameAtlasDefragmentation == currentFrame && surfaceAtlasData.ObjectsDefragmentation.HasItems();
|
||||||
|
if (atlasDefrag)
|
||||||
|
{
|
||||||
|
PROFILE_GPU_CPU_NAMED("Defragment");
|
||||||
|
// TODO: atlas copy maybe could be done in separate pass for each surface if they could alias the same memory chunk (eg. in Vulkan/D3D12)
|
||||||
|
|
||||||
|
// Allocate a new atlas textures to copy data from the old ones
|
||||||
|
#define INIT_ATLAS_TEXTURE(texture) GPUTexture* defrag##texture = surfaceAtlasData.texture; surfaceAtlasData.texture = RenderTargetPool::Get(surfaceAtlasData.texture->GetDescription()); RENDER_TARGET_POOL_SET_NAME(surfaceAtlasData.texture, "GlobalSurfaceAtlas." #texture);
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasDepth);
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasEmissive);
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasGBuffer0);
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasGBuffer1);
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasLighting);
|
||||||
|
#undef INIT_ATLAS_TEXTURE
|
||||||
|
|
||||||
|
// Bind and clear outputs
|
||||||
|
// TODO: convert into GPUDrawPass maybe
|
||||||
|
GPUTextureView* depthBuffer = surfaceAtlasData.AtlasDepth->View();
|
||||||
|
GPUTextureView* targetBuffers[4] =
|
||||||
|
{
|
||||||
|
surfaceAtlasData.AtlasEmissive->View(),
|
||||||
|
surfaceAtlasData.AtlasGBuffer0->View(),
|
||||||
|
surfaceAtlasData.AtlasGBuffer1->View(),
|
||||||
|
surfaceAtlasData.AtlasLighting->View(),
|
||||||
|
};
|
||||||
|
context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
||||||
|
context->ClearDepth(depthBuffer);
|
||||||
|
context->Clear(targetBuffers[0], Color::Transparent);
|
||||||
|
context->Clear(targetBuffers[1], Color::Transparent);
|
||||||
|
context->Clear(targetBuffers[2], Color::Transparent);
|
||||||
|
context->Clear(targetBuffers[3], Color::Transparent);
|
||||||
|
|
||||||
|
// Copy old atlas tiles into the new atlas tiles
|
||||||
|
_vertexBuffer->Clear();
|
||||||
|
_vertexBuffer->Data.EnsureCapacity(surfaceAtlasData.ObjectsDefragmentationTilesCount * sizeof(AtlasTileVertex));
|
||||||
|
for (auto& e : surfaceAtlasData.ObjectsDefragmentation)
|
||||||
|
{
|
||||||
|
const GlobalSurfaceAtlasObject* objectPtr = surfaceAtlasData.Objects.TryGet(e.ActorObject);
|
||||||
|
if (!objectPtr)
|
||||||
|
continue;
|
||||||
|
const GlobalSurfaceAtlasObject& object = *objectPtr;
|
||||||
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
|
{
|
||||||
|
auto* tile = object.Tiles[tileIndex];
|
||||||
|
if (!tile)
|
||||||
|
continue;
|
||||||
|
VB_WRITE_TILE_POS_ONLY(tile);
|
||||||
|
|
||||||
|
// Write old atlas UVs of this tile
|
||||||
|
auto defragTile = e.Tiles[tileIndex];
|
||||||
|
Half2 minUV(defragTile.X * resolutionInv, defragTile.Y * resolutionInv);
|
||||||
|
Half2 maxUV((defragTile.X + defragTile.Width) * resolutionInv, (defragTile.Y + defragTile.Height) * resolutionInv);
|
||||||
|
quad[0].TileUV = maxUV;
|
||||||
|
quad[1].TileUV = Half2(minUV.X, maxUV.Y);
|
||||||
|
quad[2].TileUV = minUV;
|
||||||
|
quad[3].TileUV = minUV;
|
||||||
|
quad[4].TileUV = Half2(maxUV.X, minUV.Y);
|
||||||
|
quad[5].TileUV = maxUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't redraw this object if all tiles were restored from old atlas
|
||||||
|
object.Dirty = false;
|
||||||
|
surfaceAtlasData.DirtyObjectsBuffer.Remove(e.ActorObject);
|
||||||
|
}
|
||||||
|
surfaceAtlasData.ObjectsDefragmentation.Clear();
|
||||||
|
context->BindSR(0, defragAtlasDepth->View());
|
||||||
|
context->BindSR(1, defragAtlasEmissive->View());
|
||||||
|
context->BindSR(2, defragAtlasGBuffer0->View());
|
||||||
|
context->BindSR(3, defragAtlasGBuffer1->View());
|
||||||
|
context->BindSR(4, defragAtlasLighting->View());
|
||||||
|
context->SetState(_psCopy);
|
||||||
|
context->SetViewportAndScissors(Viewport(0, 0, (float)resolution, (float)resolution));
|
||||||
|
VB_DRAW();
|
||||||
|
|
||||||
|
// Free old atlas textures
|
||||||
|
context->ResetRenderTarget();
|
||||||
|
context->ResetSR();
|
||||||
|
context->FlushState();
|
||||||
|
RenderTargetPool::Release(defragAtlasLighting);
|
||||||
|
RenderTargetPool::Release(defragAtlasGBuffer1);
|
||||||
|
RenderTargetPool::Release(defragAtlasGBuffer0);
|
||||||
|
RenderTargetPool::Release(defragAtlasEmissive);
|
||||||
|
RenderTargetPool::Release(defragAtlasDepth);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
constexpr bool atlasDefrag = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Rasterize world geometry material properties into Global Surface Atlas
|
// Rasterize world geometry material properties into Global Surface Atlas
|
||||||
if (surfaceAtlasData.DirtyObjectsBuffer.Count() != 0)
|
if (surfaceAtlasData.DirtyObjectsBuffer.HasItems())
|
||||||
{
|
{
|
||||||
PROFILE_GPU_CPU_NAMED("Rasterize Tiles");
|
PROFILE_GPU_CPU_NAMED("Rasterize Tiles");
|
||||||
|
|
||||||
|
// Init rendering context
|
||||||
RenderContext renderContextTiles = renderContext;
|
RenderContext renderContextTiles = renderContext;
|
||||||
renderContextTiles.List = RenderList::GetFromPool();
|
renderContextTiles.List = RenderList::GetFromPool();
|
||||||
renderContextTiles.View.Pass = DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
|
renderContextTiles.View.Pass = DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
|
||||||
@@ -932,6 +1076,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
renderContextTiles.View.Near = 0.0f;
|
renderContextTiles.View.Near = 0.0f;
|
||||||
renderContextTiles.View.Prepare(renderContextTiles);
|
renderContextTiles.View.Prepare(renderContextTiles);
|
||||||
|
|
||||||
|
// Bind render targets for materials and clear them (whole or just dirty tiles)
|
||||||
GPUTextureView* depthBuffer = surfaceAtlasData.AtlasDepth->View();
|
GPUTextureView* depthBuffer = surfaceAtlasData.AtlasDepth->View();
|
||||||
GPUTextureView* targetBuffers[3] =
|
GPUTextureView* targetBuffers[3] =
|
||||||
{
|
{
|
||||||
@@ -940,9 +1085,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
surfaceAtlasData.AtlasGBuffer1->View(),
|
surfaceAtlasData.AtlasGBuffer1->View(),
|
||||||
};
|
};
|
||||||
context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
||||||
|
if (!atlasDefrag)
|
||||||
{
|
{
|
||||||
PROFILE_GPU_CPU_NAMED("Clear");
|
PROFILE_GPU_CPU_NAMED("Clear");
|
||||||
if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES || surfaceAtlasData.LastFrameAtlasDefragmentation == currentFrame)
|
if (atlasResized || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES)
|
||||||
{
|
{
|
||||||
// Full-atlas hardware clear
|
// Full-atlas hardware clear
|
||||||
context->ClearDepth(depthBuffer);
|
context->ClearDepth(depthBuffer);
|
||||||
@@ -974,6 +1120,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
VB_DRAW();
|
VB_DRAW();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw all dirty tiles
|
||||||
auto& drawCallsListGBuffer = renderContextTiles.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
|
auto& drawCallsListGBuffer = renderContextTiles.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
|
||||||
auto& drawCallsListGBufferNoDecals = renderContextTiles.List->DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals];
|
auto& drawCallsListGBufferNoDecals = renderContextTiles.List->DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals];
|
||||||
drawCallsListGBuffer.CanUseInstancing = false;
|
drawCallsListGBuffer.CanUseInstancing = false;
|
||||||
@@ -1036,6 +1184,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ZoneValue(tilesDrawn);
|
ZoneValue(tilesDrawn);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
context->ResetRenderTarget();
|
context->ResetRenderTarget();
|
||||||
RenderList::ReturnToPool(renderContextTiles.List);
|
RenderList::ReturnToPool(renderContextTiles.List);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool _supported = false;
|
bool _supported = false;
|
||||||
AssetReference<Shader> _shader;
|
AssetReference<Shader> _shader;
|
||||||
|
GPUPipelineState* _psCopy = nullptr;
|
||||||
GPUPipelineState* _psClear = nullptr;
|
GPUPipelineState* _psClear = nullptr;
|
||||||
GPUPipelineState* _psClearLighting = nullptr;
|
GPUPipelineState* _psClearLighting = nullptr;
|
||||||
GPUPipelineState* _psDirectLighting0 = nullptr;
|
GPUPipelineState* _psDirectLighting0 = nullptr;
|
||||||
|
|||||||
@@ -49,6 +49,28 @@ AtlasVertexOutput VS_Atlas(AtlasVertexInput input)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _PS_Copy
|
||||||
|
|
||||||
|
Texture2D DepthTexture : register(t0);
|
||||||
|
Texture2D EmissiveTexture : register(t1);
|
||||||
|
Texture2D GBuffer0Texture : register(t2);
|
||||||
|
Texture2D GBuffer1Texture : register(t3);
|
||||||
|
Texture2D LightingTexture : register(t4);
|
||||||
|
|
||||||
|
// Pixel shader for Global Surface Atlas copying (eg. after defragmentation)
|
||||||
|
META_PS(true, FEATURE_LEVEL_SM5)
|
||||||
|
void PS_Copy(AtlasVertexOutput input, out float Depth : SV_Depth, out float4 Emissive : SV_Target0, out float4 GBuffer0 : SV_Target1, out float4 GBuffer1 : SV_Target2, out float4 Lighting : SV_Target3)
|
||||||
|
{
|
||||||
|
float2 atlasUV = input.TileUV; // Old-atlas UVs computed on CPU
|
||||||
|
Depth = SAMPLE_RT_DEPTH(DepthTexture, atlasUV);
|
||||||
|
Emissive = SAMPLE_RT(EmissiveTexture, atlasUV);
|
||||||
|
GBuffer0 = SAMPLE_RT(GBuffer0Texture, atlasUV);
|
||||||
|
GBuffer1 = SAMPLE_RT(GBuffer1Texture, atlasUV);
|
||||||
|
Lighting = SAMPLE_RT(LightingTexture, atlasUV);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Pixel shader for Global Surface Atlas software clearing
|
// Pixel shader for Global Surface Atlas software clearing
|
||||||
META_PS(true, FEATURE_LEVEL_SM5)
|
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)
|
void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out float4 RT1 : SV_Target2, out float4 RT2 : SV_Target3)
|
||||||
@@ -64,7 +86,7 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl
|
|||||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||||
Texture2D Texture : register(t7);
|
Texture2D Texture : register(t7);
|
||||||
|
|
||||||
// Pixel shader for Global Surface Atlas clearing
|
// Pixel shader for Global Surface Atlas clearing (copies emissive texture)
|
||||||
META_PS(true, FEATURE_LEVEL_SM5)
|
META_PS(true, FEATURE_LEVEL_SM5)
|
||||||
float4 PS_ClearLighting(AtlasVertexOutput input) : SV_Target
|
float4 PS_ClearLighting(AtlasVertexOutput input) : SV_Target
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user