Improve Global Surface Atlas objects dirtying limit to pick the largers objects first
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
#define GLOBAL_SURFACE_ATLAS_TILE_SIZE_MAX 192 // The maximum size of the tile
|
#define GLOBAL_SURFACE_ATLAS_TILE_SIZE_MAX 192 // The maximum size of the tile
|
||||||
#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
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_LIGHTS 0 // Forces to redraw all lights every frame
|
||||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations)
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations)
|
||||||
#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
|
||||||
@@ -157,6 +158,13 @@ struct GlobalSurfaceAtlasLight
|
|||||||
uint64 LastFrameUpdated = 0;
|
uint64 LastFrameUpdated = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GlobalSurfaceAtlasPendingDirtyObject
|
||||||
|
{
|
||||||
|
void* ActorObject;
|
||||||
|
GlobalSurfaceAtlasObject* Object;
|
||||||
|
float Priority;
|
||||||
|
};
|
||||||
|
|
||||||
class GlobalSurfaceAtlasCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener
|
class GlobalSurfaceAtlasCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -190,6 +198,7 @@ public:
|
|||||||
|
|
||||||
// Cached data to be reused during RasterizeActor
|
// Cached data to be reused during RasterizeActor
|
||||||
Array<void*> DirtyObjectsBuffer;
|
Array<void*> DirtyObjectsBuffer;
|
||||||
|
Array<GlobalSurfaceAtlasPendingDirtyObject> PendingDirtyObjectsBuffer;
|
||||||
Vector4 CullingPosDistance;
|
Vector4 CullingPosDistance;
|
||||||
uint64 CurrentFrame;
|
uint64 CurrentFrame;
|
||||||
Float3 ViewPosition;
|
Float3 ViewPosition;
|
||||||
@@ -495,22 +504,39 @@ public:
|
|||||||
void WriteObjects()
|
void WriteObjects()
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Write Objects");
|
PROFILE_CPU_NAMED("Write Objects");
|
||||||
DirtyObjectsBuffer.Clear();
|
PendingDirtyObjectsBuffer.Clear();
|
||||||
ObjectsBuffer.Data.EnsureCapacity(Objects.Count() * sizeof(Float4) * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + 2 * GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE));
|
ObjectsBuffer.Data.EnsureCapacity(Objects.Count() * sizeof(Float4) * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + 2 * GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE));
|
||||||
ObjectsListBuffer.Data.Resize(Objects.Count() * sizeof(uint32));
|
ObjectsListBuffer.Data.Resize(Objects.Count() * sizeof(uint32));
|
||||||
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;
|
||||||
if (object.Dirty && dirtyObjectsLimitLeft-- > 0)
|
|
||||||
|
// Collect dirty objects
|
||||||
|
if (object.Dirty)
|
||||||
{
|
{
|
||||||
// Collect dirty objects
|
float priority = object.Radius;
|
||||||
object.LastFrameUpdated = CurrentFrame;
|
GlobalSurfaceAtlasPendingDirtyObject pending = { e.Key, &object, priority };
|
||||||
object.LightingUpdateFrame = CurrentFrame;
|
if (dirtyObjectsLimitLeft-- > 0)
|
||||||
DirtyObjectsBuffer.Add(e.Key);
|
{
|
||||||
|
PendingDirtyObjectsBuffer.Add(pending);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not enough space to update all objects that have bigger radius (more visible)
|
||||||
|
// TODO: maybe use screen-size as better heuristic for which objects to update first
|
||||||
|
for (int32 i = 0; i < PendingDirtyObjectsBuffer.Count(); i++)
|
||||||
|
{
|
||||||
|
if (PendingDirtyObjectsBuffer[i].Priority < priority)
|
||||||
|
{
|
||||||
|
// TODO: this is wrong, we should collect all pending objects and sort them to pick top ones
|
||||||
|
PendingDirtyObjectsBuffer[i] = pending;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!object.ObjectDataDirty)
|
if (!object.ObjectDataDirty)
|
||||||
@@ -624,6 +650,19 @@ public:
|
|||||||
}
|
}
|
||||||
ZoneValue(dirtyTiles);
|
ZoneValue(dirtyTiles);
|
||||||
|
|
||||||
|
// Move pending dirty objects to be actually in a dirty buffer to redraw
|
||||||
|
DirtyObjectsBuffer.Clear();
|
||||||
|
DirtyObjectsBuffer.Resize(PendingDirtyObjectsBuffer.Count());
|
||||||
|
auto* dirtyObjectsBufferPtr = DirtyObjectsBuffer.Get();
|
||||||
|
const auto* pendingDirtyObjectsBufferPtr = PendingDirtyObjectsBuffer.Get();
|
||||||
|
for (int32 i = 0; i < DirtyObjectsBuffer.Count(); i++)
|
||||||
|
{
|
||||||
|
auto pending = pendingDirtyObjectsBufferPtr[i];
|
||||||
|
pending.Object->LastFrameUpdated = CurrentFrame;
|
||||||
|
pending.Object->LightingUpdateFrame = CurrentFrame;
|
||||||
|
dirtyObjectsBufferPtr[i] = pending.ActorObject;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Debug print objects buffer usage
|
// Debug print objects buffer usage
|
||||||
uint32 freeObjectsBufferSlotsCount = 0;
|
uint32 freeObjectsBufferSlotsCount = 0;
|
||||||
@@ -1377,7 +1416,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
data.GlobalSurfaceAtlas = result.Constants;
|
data.GlobalSurfaceAtlas = result.Constants;
|
||||||
|
|
||||||
// Collect objects to update lighting this frame (dirty objects and dirty lights)
|
// Collect objects to update lighting this frame (dirty objects and dirty lights)
|
||||||
bool allLightingDirty = false;
|
bool allLightingDirty = GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_LIGHTS;
|
||||||
for (auto& light : renderContext.List->DirectionalLights)
|
for (auto& light : renderContext.List->DirectionalLights)
|
||||||
{
|
{
|
||||||
GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[light.ID];
|
GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[light.ID];
|
||||||
|
|||||||
Reference in New Issue
Block a user