Refactor Global Surface Atlas objects buffer into separate tiles buffer to reduce memory usage and increase cache hit ratio
This commit is contained in:
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
@@ -18,7 +18,8 @@
|
|||||||
#include "Engine/Utilities/RectPack.h"
|
#include "Engine/Utilities/RectPack.h"
|
||||||
|
|
||||||
// This must match HLSL
|
// This must match HLSL
|
||||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
#define GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE 6 // Amount of float4s per-object
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE 5 // Amount of float4s per-tile
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles
|
#define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
||||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES 0 // Forces to redraw all object tiles every frame
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES 0 // Forces to redraw all object tiles every frame
|
||||||
@@ -55,6 +56,7 @@ struct GlobalSurfaceAtlasTile : RectPack<GlobalSurfaceAtlasTile, uint16>
|
|||||||
Vector3 ViewPosition;
|
Vector3 ViewPosition;
|
||||||
Vector3 ViewBoundsSize;
|
Vector3 ViewBoundsSize;
|
||||||
Matrix ViewMatrix;
|
Matrix ViewMatrix;
|
||||||
|
uint16 TileIndex;
|
||||||
|
|
||||||
GlobalSurfaceAtlasTile(uint16 x, uint16 y, uint16 width, uint16 height)
|
GlobalSurfaceAtlasTile(uint16 x, uint16 y, uint16 width, uint16 height)
|
||||||
: RectPack<GlobalSurfaceAtlasTile, uint16>(x, y, width, height)
|
: RectPack<GlobalSurfaceAtlasTile, uint16>(x, y, width, height)
|
||||||
@@ -118,13 +120,16 @@ public:
|
|||||||
GPUTexture* AtlasGBuffer2 = nullptr;
|
GPUTexture* AtlasGBuffer2 = nullptr;
|
||||||
GPUTexture* AtlasDirectLight = nullptr;
|
GPUTexture* AtlasDirectLight = nullptr;
|
||||||
DynamicTypedBuffer ObjectsBuffer;
|
DynamicTypedBuffer ObjectsBuffer;
|
||||||
|
DynamicTypedBuffer TilesBuffer;
|
||||||
uint32 ObjectIndexCounter;
|
uint32 ObjectIndexCounter;
|
||||||
|
uint16 TileIndexCounter;
|
||||||
GlobalSurfaceAtlasPass::BindingData Result;
|
GlobalSurfaceAtlasPass::BindingData Result;
|
||||||
GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles
|
GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles
|
||||||
Dictionary<Actor*, GlobalSurfaceAtlasObject> Objects;
|
Dictionary<Actor*, GlobalSurfaceAtlasObject> Objects;
|
||||||
|
|
||||||
GlobalSurfaceAtlasCustomBuffer()
|
GlobalSurfaceAtlasCustomBuffer()
|
||||||
: ObjectsBuffer(256 * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE, PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer"))
|
: ObjectsBuffer(256 * GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE, PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer"))
|
||||||
|
, TilesBuffer(256 * GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE * 3 / 4, PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.TilesBuffer"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +138,7 @@ public:
|
|||||||
LastFrameAtlasDefragmentation = Engine::FrameCount;
|
LastFrameAtlasDefragmentation = Engine::FrameCount;
|
||||||
SAFE_DELETE(AtlasTiles);
|
SAFE_DELETE(AtlasTiles);
|
||||||
ObjectsBuffer.Clear();
|
ObjectsBuffer.Clear();
|
||||||
|
TilesBuffer.Clear();
|
||||||
Objects.Clear();
|
Objects.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,20 +350,28 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
||||||
Vector2 minUV(0, 0), maxUV(1, 1); \
|
Vector2 minUV(0, 0), maxUV(1, 1); \
|
||||||
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
||||||
quad[0] = { { max }, { maxUV }, (uint16)object.Index, (uint16)tileIndex }; \
|
quad[0] = { { max }, { maxUV }, (uint16)object.Index, tile->TileIndex }; \
|
||||||
quad[1] = { { min.X, max.Y }, { minUV.X, maxUV.Y }, (uint16)object.Index, (uint16)tileIndex }; \
|
quad[1] = { { min.X, max.Y }, { minUV.X, maxUV.Y }, (uint16)object.Index, tile->TileIndex }; \
|
||||||
quad[2] = { { min }, { minUV }, (uint16)object.Index, (uint16)tileIndex }; \
|
quad[2] = { { min }, { minUV }, (uint16)object.Index, tile->TileIndex }; \
|
||||||
quad[3] = quad[2]; \
|
quad[3] = quad[2]; \
|
||||||
quad[4] = { { max.X, min.Y }, { maxUV.X, minUV.Y }, (uint16)object.Index, (uint16)tileIndex }; \
|
quad[4] = { { max.X, min.Y }, { maxUV.X, minUV.Y }, (uint16)object.Index, tile->TileIndex }; \
|
||||||
quad[5] = quad[0]
|
quad[5] = quad[0]
|
||||||
#define VB_DRAW() \
|
#define VB_DRAW() \
|
||||||
_vertexBuffer->Flush(context); \
|
_vertexBuffer->Flush(context); \
|
||||||
auto vb = _vertexBuffer->GetBuffer(); \
|
auto vb = _vertexBuffer->GetBuffer(); \
|
||||||
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);
|
||||||
|
|
||||||
// Add objects into the atlas
|
// Add objects into the atlas
|
||||||
surfaceAtlasData.ObjectsBuffer.Clear();
|
surfaceAtlasData.ObjectsBuffer.Clear();
|
||||||
|
surfaceAtlasData.TilesBuffer.Clear();
|
||||||
surfaceAtlasData.ObjectIndexCounter = 0;
|
surfaceAtlasData.ObjectIndexCounter = 0;
|
||||||
|
{
|
||||||
|
// Tile at index 0 is invalid
|
||||||
|
surfaceAtlasData.TileIndexCounter = 1;
|
||||||
|
auto* tileData = surfaceAtlasData.TilesBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE);
|
||||||
|
Platform::MemoryClear(tileData, sizeof(Vector4) * GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE);
|
||||||
|
}
|
||||||
_dirtyObjectsBuffer.Clear();
|
_dirtyObjectsBuffer.Clear();
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Draw");
|
PROFILE_CPU_NAMED("Draw");
|
||||||
@@ -468,23 +482,22 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
|
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
|
||||||
// TODO: cache data for static objects to optimize CPU perf (move ObjectsBuffer into surfaceAtlasData)
|
// TODO: cache data for static objects to optimize CPU perf (move ObjectsBuffer into surfaceAtlasData)
|
||||||
object->Index = surfaceAtlasData.ObjectIndexCounter++;
|
object->Index = surfaceAtlasData.ObjectIndexCounter++;
|
||||||
auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_OBJECT_SIZE);
|
auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE);
|
||||||
objectData[0] = *(Vector4*)&e.Bounds;
|
objectData[0] = *(Vector4*)&e.Bounds;
|
||||||
objectData[1] = Vector4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
objectData[1] = Vector4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
||||||
objectData[2] = Vector4(worldToLocalBounds.M21, worldToLocalBounds.M22, worldToLocalBounds.M23, worldToLocalBounds.M42);
|
objectData[2] = Vector4(worldToLocalBounds.M21, worldToLocalBounds.M22, worldToLocalBounds.M23, worldToLocalBounds.M42);
|
||||||
objectData[3] = Vector4(worldToLocalBounds.M31, worldToLocalBounds.M32, worldToLocalBounds.M33, worldToLocalBounds.M43);
|
objectData[3] = Vector4(worldToLocalBounds.M31, worldToLocalBounds.M32, worldToLocalBounds.M33, worldToLocalBounds.M43);
|
||||||
objectData[4] = Vector4(object->Bounds.Extents, 0.0f);
|
objectData[4] = Vector4(object->Bounds.Extents, 0.0f); // w unused
|
||||||
|
objectData[5] = Vector4::Zero; // w unused
|
||||||
|
auto tileIndices = reinterpret_cast<uint16*>(&objectData[5]); // xyz used for tile indices packed into uint16
|
||||||
// TODO: try to optimize memory footprint (eg. merge scale into extents and use rotation+offset but reconstruct rotation from two axes with sign)
|
// TODO: try to optimize memory footprint (eg. merge scale into extents and use rotation+offset but reconstruct rotation from two axes with sign)
|
||||||
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
{
|
{
|
||||||
auto* tile = object->Tiles[tileIndex];
|
auto* tile = object->Tiles[tileIndex];
|
||||||
const int32 tileStart = 5 + tileIndex * 5;
|
|
||||||
if (!tile)
|
if (!tile)
|
||||||
{
|
|
||||||
// Disable tile
|
|
||||||
objectData[tileStart + 4] = Vector4::Zero;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
tile->TileIndex = surfaceAtlasData.TileIndexCounter++;
|
||||||
|
tileIndices[tileIndex] = tile->TileIndex;
|
||||||
|
|
||||||
// Setup view to render object from the side
|
// Setup view to render object from the side
|
||||||
Vector3 xAxis, yAxis, zAxis = Vector3::Zero;
|
Vector3 xAxis, yAxis, zAxis = Vector3::Zero;
|
||||||
@@ -517,11 +530,12 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
// 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;
|
||||||
objectData[tileStart + 0] = Vector4(tile->X, tile->Y, tileWidth, tileHeight) * resolutionInv;
|
auto* tileData = surfaceAtlasData.TilesBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE);
|
||||||
objectData[tileStart + 1] = Vector4(tile->ViewMatrix.M11, tile->ViewMatrix.M12, tile->ViewMatrix.M13, tile->ViewMatrix.M41);
|
tileData[0] = Vector4(tile->X, tile->Y, tileWidth, tileHeight) * resolutionInv;
|
||||||
objectData[tileStart + 2] = Vector4(tile->ViewMatrix.M21, tile->ViewMatrix.M22, tile->ViewMatrix.M23, tile->ViewMatrix.M42);
|
tileData[1] = Vector4(tile->ViewMatrix.M11, tile->ViewMatrix.M12, tile->ViewMatrix.M13, tile->ViewMatrix.M41);
|
||||||
objectData[tileStart + 3] = Vector4(tile->ViewMatrix.M31, tile->ViewMatrix.M32, tile->ViewMatrix.M33, tile->ViewMatrix.M43);
|
tileData[2] = Vector4(tile->ViewMatrix.M21, tile->ViewMatrix.M22, tile->ViewMatrix.M23, tile->ViewMatrix.M42);
|
||||||
objectData[tileStart + 4] = Vector4(tile->ViewBoundsSize, 1.0f);
|
tileData[3] = Vector4(tile->ViewMatrix.M31, tile->ViewMatrix.M32, tile->ViewMatrix.M33, tile->ViewMatrix.M43);
|
||||||
|
tileData[4] = Vector4(tile->ViewBoundsSize, 0.0f); // w unused
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -548,6 +562,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
{
|
{
|
||||||
PROFILE_GPU_CPU("Update Objects");
|
PROFILE_GPU_CPU("Update Objects");
|
||||||
surfaceAtlasData.ObjectsBuffer.Flush(context);
|
surfaceAtlasData.ObjectsBuffer.Flush(context);
|
||||||
|
surfaceAtlasData.TilesBuffer.Flush(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rasterize world geometry material properties into Global Surface Atlas
|
// Rasterize world geometry material properties into Global Surface Atlas
|
||||||
@@ -670,6 +685,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
result.Atlas[3] = surfaceAtlasData.AtlasGBuffer2;
|
result.Atlas[3] = surfaceAtlasData.AtlasGBuffer2;
|
||||||
result.Atlas[4] = surfaceAtlasData.AtlasDirectLight;
|
result.Atlas[4] = surfaceAtlasData.AtlasDirectLight;
|
||||||
result.Objects = surfaceAtlasData.ObjectsBuffer.GetBuffer();
|
result.Objects = surfaceAtlasData.ObjectsBuffer.GetBuffer();
|
||||||
|
result.Tiles = surfaceAtlasData.TilesBuffer.GetBuffer();
|
||||||
result.GlobalSurfaceAtlas.Resolution = (float)resolution;
|
result.GlobalSurfaceAtlas.Resolution = (float)resolution;
|
||||||
result.GlobalSurfaceAtlas.ObjectsCount = surfaceAtlasData.Objects.Count();
|
result.GlobalSurfaceAtlas.ObjectsCount = surfaceAtlasData.Objects.Count();
|
||||||
surfaceAtlasData.Result = result;
|
surfaceAtlasData.Result = result;
|
||||||
@@ -693,10 +709,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View());
|
context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View());
|
||||||
context->BindSR(3, surfaceAtlasData.AtlasDepth->View());
|
context->BindSR(3, surfaceAtlasData.AtlasDepth->View());
|
||||||
context->BindSR(4, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View());
|
context->BindSR(4, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View());
|
||||||
|
context->BindSR(5, surfaceAtlasData.TilesBuffer.GetBuffer()->View());
|
||||||
for (int32 i = 0; i < 4; i++)
|
for (int32 i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
context->BindSR(i + 5, bindingDataSDF.Cascades[i]->ViewVolume());
|
context->BindSR(i + 6, bindingDataSDF.Cascades[i]->ViewVolume());
|
||||||
context->BindSR(i + 9, bindingDataSDF.CascadeMips[i]->ViewVolume());
|
context->BindSR(i + 10, bindingDataSDF.CascadeMips[i]->ViewVolume());
|
||||||
}
|
}
|
||||||
context->BindCB(0, _cb0);
|
context->BindCB(0, _cb0);
|
||||||
Data0 data;
|
Data0 data;
|
||||||
@@ -827,13 +844,14 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
|||||||
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume());
|
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume());
|
||||||
}
|
}
|
||||||
context->BindSR(8, bindingData.Objects ? bindingData.Objects->View() : nullptr);
|
context->BindSR(8, bindingData.Objects ? bindingData.Objects->View() : nullptr);
|
||||||
context->BindSR(9, bindingData.Atlas[0]->View());
|
context->BindSR(9, bindingData.Tiles ? bindingData.Tiles->View() : nullptr);
|
||||||
|
context->BindSR(10, bindingData.Atlas[0]->View());
|
||||||
{
|
{
|
||||||
//GPUTexture* tex = bindingData.Atlas[1]; // Preview diffuse
|
//GPUTexture* tex = bindingData.Atlas[1]; // Preview diffuse
|
||||||
//GPUTexture* tex = bindingData.Atlas[2]; // Preview normals
|
//GPUTexture* tex = bindingData.Atlas[2]; // Preview normals
|
||||||
//GPUTexture* tex = bindingData.Atlas[3]; // Preview roughness/metalness/ao
|
//GPUTexture* tex = bindingData.Atlas[3]; // Preview roughness/metalness/ao
|
||||||
GPUTexture* tex = bindingData.Atlas[4]; // Preview direct light
|
GPUTexture* tex = bindingData.Atlas[4]; // Preview direct light
|
||||||
context->BindSR(10, tex->View());
|
context->BindSR(11, tex->View());
|
||||||
}
|
}
|
||||||
context->SetState(_psDebug);
|
context->SetState(_psDebug);
|
||||||
context->SetRenderTarget(output->View());
|
context->SetRenderTarget(output->View());
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public:
|
|||||||
{
|
{
|
||||||
GPUTexture* Atlas[5];
|
GPUTexture* Atlas[5];
|
||||||
GPUBuffer* Objects;
|
GPUBuffer* Objects;
|
||||||
|
GPUBuffer* Tiles;
|
||||||
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include "./Flax/Collisions.hlsl"
|
#include "./Flax/Collisions.hlsl"
|
||||||
|
|
||||||
// This must match C++
|
// This must match C++
|
||||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
#define GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE 6 // Amount of float4s per-object
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE 5 // Amount of float4s per-tile
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.1f // Cut-off value for tiles transitions blending during sampling
|
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.1f // Cut-off value for tiles transitions blending during sampling
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
||||||
|
|
||||||
@@ -13,7 +14,6 @@ struct GlobalSurfaceTile
|
|||||||
float4 AtlasRectUV;
|
float4 AtlasRectUV;
|
||||||
float4x4 WorldToLocal;
|
float4x4 WorldToLocal;
|
||||||
float3 ViewBoundsSize;
|
float3 ViewBoundsSize;
|
||||||
bool Enabled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GlobalSurfaceObject
|
struct GlobalSurfaceObject
|
||||||
@@ -22,24 +22,26 @@ struct GlobalSurfaceObject
|
|||||||
float BoundsRadius;
|
float BoundsRadius;
|
||||||
float4x4 WorldToLocal;
|
float4x4 WorldToLocal;
|
||||||
float3 Extent;
|
float3 Extent;
|
||||||
|
uint TileIndices[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
float4 LoadGlobalSurfaceAtlasObjectBounds(Buffer<float4> objects, uint objectIndex)
|
float4 LoadGlobalSurfaceAtlasObjectBounds(Buffer<float4> objects, uint objectIndex)
|
||||||
{
|
{
|
||||||
// This must match C++
|
// This must match C++
|
||||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE;
|
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE;
|
||||||
return objects.Load(objectStart);
|
return objects.Load(objectStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint objectIndex)
|
GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint objectIndex)
|
||||||
{
|
{
|
||||||
// This must match C++
|
// This must match C++
|
||||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE;
|
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_BUFFER_STRIDE;
|
||||||
float4 vector0 = objects.Load(objectStart + 0);
|
float4 vector0 = objects.Load(objectStart + 0);
|
||||||
float4 vector1 = objects.Load(objectStart + 1);
|
float4 vector1 = objects.Load(objectStart + 1);
|
||||||
float4 vector2 = objects.Load(objectStart + 2);
|
float4 vector2 = objects.Load(objectStart + 2);
|
||||||
float4 vector3 = objects.Load(objectStart + 3);
|
float4 vector3 = objects.Load(objectStart + 3);
|
||||||
float4 vector4 = objects.Load(objectStart + 4); // w unused
|
float4 vector4 = objects.Load(objectStart + 4); // w unused
|
||||||
|
float4 vector5 = objects.Load(objectStart + 5); // w unused
|
||||||
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
|
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
|
||||||
object.BoundsPosition = vector0.xyz;
|
object.BoundsPosition = vector0.xyz;
|
||||||
object.BoundsRadius = vector0.w;
|
object.BoundsRadius = vector0.w;
|
||||||
@@ -48,19 +50,27 @@ GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint ob
|
|||||||
object.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
|
object.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
|
||||||
object.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
|
object.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
|
||||||
object.Extent = vector4.xyz;
|
object.Extent = vector4.xyz;
|
||||||
|
uint vector5x = asuint(vector5.x);
|
||||||
|
uint vector5y = asuint(vector5.y);
|
||||||
|
uint vector5z = asuint(vector5.z);
|
||||||
|
object.TileIndices[0] = vector5x & 0xffff; // Limitation on max 65k active tiles
|
||||||
|
object.TileIndices[1] = vector5x >> 16;
|
||||||
|
object.TileIndices[2] = vector5y & 0xffff;
|
||||||
|
object.TileIndices[3] = vector5y >> 16;
|
||||||
|
object.TileIndices[4] = vector5z & 0xffff;
|
||||||
|
object.TileIndices[5] = vector5z >> 16;
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalSurfaceTile LoadGlobalSurfaceAtlasTile(Buffer<float4> objects, uint objectIndex, uint tileIndex)
|
GlobalSurfaceTile LoadGlobalSurfaceAtlasTile(Buffer<float4> objects, uint tileIndex)
|
||||||
{
|
{
|
||||||
// This must match C++
|
// This must match C++
|
||||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE;
|
const uint tileStart = tileIndex * GLOBAL_SURFACE_ATLAS_TILE_BUFFER_STRIDE;
|
||||||
const uint tileStart = objectStart + 5 + tileIndex * 5;
|
|
||||||
float4 vector0 = objects.Load(tileStart + 0);
|
float4 vector0 = objects.Load(tileStart + 0);
|
||||||
float4 vector1 = objects.Load(tileStart + 1);
|
float4 vector1 = objects.Load(tileStart + 1);
|
||||||
float4 vector2 = objects.Load(tileStart + 2);
|
float4 vector2 = objects.Load(tileStart + 2);
|
||||||
float4 vector3 = objects.Load(tileStart + 3);
|
float4 vector3 = objects.Load(tileStart + 3);
|
||||||
float4 vector4 = objects.Load(tileStart + 4);
|
float4 vector4 = objects.Load(tileStart + 4); // w unused
|
||||||
GlobalSurfaceTile tile = (GlobalSurfaceTile)0;
|
GlobalSurfaceTile tile = (GlobalSurfaceTile)0;
|
||||||
tile.AtlasRectUV = vector0.xyzw;
|
tile.AtlasRectUV = vector0.xyzw;
|
||||||
tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f);
|
tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f);
|
||||||
@@ -68,7 +78,6 @@ GlobalSurfaceTile LoadGlobalSurfaceAtlasTile(Buffer<float4> objects, uint object
|
|||||||
tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
|
tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
|
||||||
tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
|
tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
|
||||||
tile.ViewBoundsSize = vector4.xyz;
|
tile.ViewBoundsSize = vector4.xyz;
|
||||||
tile.Enabled = vector4.w > 0;
|
|
||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +147,7 @@ float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSur
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Samples the Global Surface Atlas and returns the lighting (with opacity) at the given world location (and direction).
|
// Samples the Global Surface Atlas and returns the lighting (with opacity) at the given world location (and direction).
|
||||||
float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Buffer<float4> objects, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal)
|
float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Buffer<float4> objects, Buffer<float4> tiles, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal)
|
||||||
{
|
{
|
||||||
float4 result = float4(0, 0, 0, 0);
|
float4 result = float4(0, 0, 0, 0);
|
||||||
float surfaceThreshold = 20.0f; // Additional threshold between object or tile size compared with input data (error due to SDF or LOD incorrect appearance)
|
float surfaceThreshold = 20.0f; // Additional threshold between object or tile size compared with input data (error due to SDF or LOD incorrect appearance)
|
||||||
@@ -157,25 +166,24 @@ float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Buffer<float4
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Sample tiles based on the directionality
|
// Sample tiles based on the directionality
|
||||||
// TODO: place enabled tiles mask in object data to skip reading disabled tiles
|
|
||||||
float3 localNormal = normalize(mul(worldNormal, (float3x3)object.WorldToLocal));
|
float3 localNormal = normalize(mul(worldNormal, (float3x3)object.WorldToLocal));
|
||||||
float3 localNormalSq = localNormal * localNormal;
|
float3 localNormalSq = localNormal * localNormal;
|
||||||
if (localNormalSq.x > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD)
|
uint tileIndex = object.TileIndices[localNormal.x > 0.0f ? 0 : 1];
|
||||||
|
if (localNormalSq.x > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileIndex != 0)
|
||||||
{
|
{
|
||||||
uint tileIndex = localNormal.x > 0.0f ? 0 : 1;
|
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(tiles, tileIndex);
|
||||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectIndex, tileIndex);
|
|
||||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||||
}
|
}
|
||||||
if (localNormalSq.y > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD)
|
tileIndex = object.TileIndices[localNormal.y > 0.0f ? 2 : 3];
|
||||||
|
if (localNormalSq.y > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileIndex != 0)
|
||||||
{
|
{
|
||||||
uint tileIndex = localNormal.y > 0.0f ? 2 : 3;
|
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(tiles, tileIndex);
|
||||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectIndex, tileIndex);
|
|
||||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||||
}
|
}
|
||||||
if (localNormalSq.z > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD)
|
tileIndex = object.TileIndices[localNormal.z > 0.0f ? 4 : 5];
|
||||||
|
if (localNormalSq.z > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileIndex != 0)
|
||||||
{
|
{
|
||||||
uint tileIndex = localNormal.z > 0.0f ? 4 : 5;
|
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(tiles, tileIndex);
|
||||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectIndex, tileIndex);
|
|
||||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,9 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl
|
|||||||
|
|
||||||
// GBuffer+Depth at 0-3 slots
|
// GBuffer+Depth at 0-3 slots
|
||||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||||
Texture3D<float> GlobalSDFTex[4] : register(t5);
|
Buffer<float4> GlobalSurfaceAtlasTiles : register(t5);
|
||||||
Texture3D<float> GlobalSDFMip[4] : register(t9);
|
Texture3D<float> GlobalSDFTex[4] : register(t6);
|
||||||
|
Texture3D<float> GlobalSDFMip[4] : register(t10);
|
||||||
|
|
||||||
// Pixel shader for Global Surface Atlas shading with direct light contribution
|
// Pixel shader for Global Surface Atlas shading with direct light contribution
|
||||||
META_PS(true, FEATURE_LEVEL_SM5)
|
META_PS(true, FEATURE_LEVEL_SM5)
|
||||||
@@ -78,7 +79,7 @@ float4 PS_DirectLighting(AtlasVertexOutput input) : SV_Target
|
|||||||
{
|
{
|
||||||
// Load current tile info
|
// Load current tile info
|
||||||
//GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(GlobalSurfaceAtlasObjects, input.Index.x);
|
//GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(GlobalSurfaceAtlasObjects, input.Index.x);
|
||||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(GlobalSurfaceAtlasObjects, input.Index.x, input.Index.y);
|
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(GlobalSurfaceAtlasTiles, input.Index.y);
|
||||||
float2 atlasUV = input.TileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy;
|
float2 atlasUV = input.TileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy;
|
||||||
|
|
||||||
// Load GBuffer sample from atlas
|
// Load GBuffer sample from atlas
|
||||||
@@ -161,8 +162,9 @@ float4 PS_DirectLighting(AtlasVertexOutput input) : SV_Target
|
|||||||
Texture3D<float> GlobalSDFTex[4] : register(t0);
|
Texture3D<float> GlobalSDFTex[4] : register(t0);
|
||||||
Texture3D<float> GlobalSDFMip[4] : register(t4);
|
Texture3D<float> GlobalSDFMip[4] : register(t4);
|
||||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t8);
|
Buffer<float4> GlobalSurfaceAtlasObjects : register(t8);
|
||||||
Texture2D GlobalSurfaceAtlasDepth : register(t9);
|
Buffer<float4> GlobalSurfaceAtlasTiles : register(t9);
|
||||||
Texture2D GlobalSurfaceAtlasTex : register(t10);
|
Texture2D GlobalSurfaceAtlasDepth : register(t10);
|
||||||
|
Texture2D GlobalSurfaceAtlasTex : register(t11);
|
||||||
|
|
||||||
// Pixel shader for Global Surface Atlas debug drawing
|
// Pixel shader for Global Surface Atlas debug drawing
|
||||||
META_PS(true, FEATURE_LEVEL_SM5)
|
META_PS(true, FEATURE_LEVEL_SM5)
|
||||||
@@ -185,7 +187,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target
|
|||||||
//return float4(hit.HitNormal * 0.5f + 0.5f, 1);
|
//return float4(hit.HitNormal * 0.5f + 0.5f, 1);
|
||||||
|
|
||||||
// Sample Global Surface Atlas at the hit location
|
// Sample Global Surface Atlas at the hit location
|
||||||
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay);
|
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasTiles, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay);
|
||||||
return float4(surfaceColor.rgb, 1);
|
return float4(surfaceColor.rgb, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user