Add recycling to CullingId for occlusion culling
This commit is contained in:
@@ -289,6 +289,7 @@ void RenderBuffers::Release()
|
||||
if (auto* culling = FromInterface(OcclusionCulling))
|
||||
Delete(culling);
|
||||
OcclusionCulling = nullptr;
|
||||
_statelessCulling = true;
|
||||
|
||||
RenderTargetPool::Release(VolumetricFog);
|
||||
VolumetricFog = nullptr;
|
||||
@@ -335,6 +336,7 @@ void RenderBuffers::OnRendering(const RenderContext& renderContext)
|
||||
{
|
||||
Delete(culling);
|
||||
OcclusionCulling = nullptr;
|
||||
_statelessCulling = true;
|
||||
}
|
||||
}
|
||||
if (!OcclusionCulling && occlusionCullingTypeName.HasChars() && enableCulling)
|
||||
@@ -349,6 +351,7 @@ void RenderBuffers::OnRendering(const RenderContext& renderContext)
|
||||
LOG(Error, "Occlusion Culling system '{}' is unsupported", occlusionCullingTypeName.ToString());
|
||||
return;
|
||||
}
|
||||
_statelessCulling = OcclusionCulling->IsStateless();
|
||||
|
||||
if (_usedCulling)
|
||||
{
|
||||
@@ -466,16 +469,36 @@ void RenderBuffers::OnSceneRenderingUpdateActor(SceneRendering* scene, int32 key
|
||||
|
||||
void RenderBuffers::OnSceneRenderingRemoveActor(SceneRendering* scene, int32 key, Actor* a)
|
||||
{
|
||||
// Skip actors that don't have nested sub-objects
|
||||
if (!_cullingIdsOwnerTypes.Contains(a->GetTypeHandle()))
|
||||
bool isCullingIdOwner = _cullingIdsOwnerTypes.Contains(a->GetTypeHandle());
|
||||
if (_statelessCulling && !isCullingIdOwner)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
|
||||
if (auto* sceneData = Scenes.TryGet(scene))
|
||||
{
|
||||
for (auto it = sceneData->CullingIds.Begin(); it.IsNotEnd(); ++it)
|
||||
// Check actors that have nested sub-objects
|
||||
if (isCullingIdOwner)
|
||||
{
|
||||
if (it->Key.First == a)
|
||||
for (auto it = sceneData->CullingIds.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
sceneData->CullingIds.Remove(it);
|
||||
if (it->Key.First == a)
|
||||
{
|
||||
if (!_statelessCulling && it->Value)
|
||||
OcclusionCulling->FreeObject(it->Value);
|
||||
sceneData->CullingIds.Remove(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that actor
|
||||
auto& list = sceneData->Geo[a->_drawCategory];
|
||||
if (list.IsValidIndex(key) && !_statelessCulling)
|
||||
{
|
||||
auto& item = list.Get()[key];
|
||||
if (item.CullingId)
|
||||
{
|
||||
OcclusionCulling->FreeObject(item.CullingId);
|
||||
item.CullingId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,5 +506,32 @@ void RenderBuffers::OnSceneRenderingRemoveActor(SceneRendering* scene, int32 key
|
||||
|
||||
void RenderBuffers::OnSceneRenderingClear(SceneRendering* scene)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
if (!_statelessCulling)
|
||||
{
|
||||
// Free culling ids
|
||||
if (auto* sceneData = Scenes.TryGet(scene))
|
||||
{
|
||||
for (auto& e : sceneData->CullingIds)
|
||||
{
|
||||
if (e.Value)
|
||||
OcclusionCulling->FreeObject(e.Value);
|
||||
}
|
||||
for (auto& list : sceneData->Geo)
|
||||
{
|
||||
for (auto& item : list)
|
||||
{
|
||||
if (item.CullingId)
|
||||
{
|
||||
OcclusionCulling->FreeObject(item.CullingId);
|
||||
item.CullingId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
Scenes.Remove(scene);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ protected:
|
||||
bool _useAlpha = false;
|
||||
bool _useNull = false;
|
||||
bool _usedCulling = false;
|
||||
bool _statelessCulling = true;
|
||||
Viewport _viewport;
|
||||
Array<GPUTexture*, FixedAllocation<32>> _resources;
|
||||
CriticalSection _cullingLocker;
|
||||
|
||||
@@ -38,6 +38,11 @@ bool HZBOcclusionCulling::IsSupported()
|
||||
return limits.HasCompute;
|
||||
}
|
||||
|
||||
bool HZBOcclusionCulling::IsStateless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void HZBOcclusionCulling::BeginFrame(const RenderContext& renderContext)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -229,6 +234,11 @@ void HZBOcclusionCulling::Submit(const RenderContext& renderContext)
|
||||
context->SetViewportAndScissors(renderContext.Buffers->GetViewport());
|
||||
}
|
||||
|
||||
void HZBOcclusionCulling::FreeObject(uint32 cullingId)
|
||||
{
|
||||
_items.FreeObject(cullingId);
|
||||
}
|
||||
|
||||
bool HZBOcclusionCulling::IsVisible(const BoundingBox& bounds, uint32& cullingId)
|
||||
{
|
||||
return IsVisible(bounds, cullingId, nullptr);
|
||||
|
||||
@@ -50,9 +50,11 @@ private:
|
||||
public:
|
||||
// [IOcclusionCulling]
|
||||
bool IsSupported() override;
|
||||
bool IsStateless() override;
|
||||
void BeginFrame(const RenderContext& renderContext) override;
|
||||
void EndFrame(const RenderContext& renderContext) override;
|
||||
void Submit(const RenderContext& renderContext) override;
|
||||
void FreeObject(uint32 cullingId) override;
|
||||
bool IsVisible(const BoundingBox& bounds, uint32& cullingId) override;
|
||||
bool IsVisible(const BoundingBox& bounds, GeometryDrawState& drawState) override;
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ HardwareOcclusionCulling::~HardwareOcclusionCulling()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HardwareOcclusionCulling::IsStateless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void HardwareOcclusionCulling::BeginFrame(const RenderContext& renderContext)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -214,6 +219,11 @@ void HardwareOcclusionCulling::Submit(const RenderContext& renderContext)
|
||||
#endif
|
||||
}
|
||||
|
||||
void HardwareOcclusionCulling::FreeObject(uint32 cullingId)
|
||||
{
|
||||
_items.FreeObject(cullingId);
|
||||
}
|
||||
|
||||
bool HardwareOcclusionCulling::IsVisible(const BoundingBox& bounds, uint32& cullingId)
|
||||
{
|
||||
return IsVisible(bounds, cullingId, nullptr);
|
||||
|
||||
@@ -49,9 +49,11 @@ private:
|
||||
|
||||
public:
|
||||
// [IOcclusionCulling]
|
||||
bool IsStateless() override;
|
||||
void BeginFrame(const RenderContext& renderContext) override;
|
||||
void EndFrame(const RenderContext& renderContext) override;
|
||||
void Submit(const RenderContext& renderContext) override;
|
||||
void FreeObject(uint32 cullingId) override;
|
||||
bool IsVisible(const BoundingBox& bounds, uint32& cullingId) override;
|
||||
bool IsVisible(const BoundingBox& bounds, GeometryDrawState& drawState) override;
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ API_INTERFACE() class FLAXENGINE_API IOcclusionCulling
|
||||
/// </summary>
|
||||
virtual bool IsSupported() { return true; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the culling system doesn't contain any state (eg. visibility resolved on GPU) related to culled objects. Otherwise, the culling system may maintain some state between frames (eg. visibility results from previous frames) to perform the culling operations thus will receive FreeObject.
|
||||
/// </summary>
|
||||
virtual bool IsStateless() { return true; }
|
||||
|
||||
/// <summary>
|
||||
/// Frame begin event. Called before the drawing to prepare the culling system for the new frame.
|
||||
/// </summary>
|
||||
@@ -38,6 +43,12 @@ API_INTERFACE() class FLAXENGINE_API IOcclusionCulling
|
||||
/// </summary>
|
||||
virtual void EndFrame(const RenderContext& renderContext) {}
|
||||
|
||||
/// <summary>
|
||||
/// Frees specific object from the culling system. Called when the object is removed from the scene or destroyed. Can be used to free any state related to the object (eg. visibility results from previous frames).
|
||||
/// </summary>
|
||||
/// <remarks>Used only when IsStateless returns true (as optimization).</remarks>
|
||||
virtual void FreeObject(uint32 cullingId) {}
|
||||
|
||||
/// <summary>
|
||||
/// Object bounds visibility check. Returns true if the object is visible (not occluded by other geometry).
|
||||
/// Works only for CPU-side culling (or with delayed GPU-readback).
|
||||
|
||||
@@ -24,6 +24,7 @@ private:
|
||||
volatile int64 _freeItemsCount = 0;
|
||||
volatile int64 _newItemsCount = 0;
|
||||
Array<uint32> _freeItems;
|
||||
Array<uint32> _freedItems;
|
||||
|
||||
public:
|
||||
void BeginFrame()
|
||||
@@ -31,23 +32,9 @@ public:
|
||||
// Remove used free items
|
||||
_freeItems.Resize(Math::Max((int32)_freeItemsCount, 0));
|
||||
|
||||
#if 0 // TODO: find a different way as there might be some invisible object with CullingId assigned and drawing it later will overlap with reused IDs
|
||||
// Trim history
|
||||
constexpr int32 frameTTL = 20;
|
||||
if (_frameCounter % 10 == 0 && _frameCounter > frameTTL)
|
||||
{
|
||||
const int32 lastFrame = _frameCounter - frameTTL;
|
||||
for (int32 i = 0; i < this->Count(); i++)
|
||||
{
|
||||
auto& item = this->Get()[i];
|
||||
if (item.LastUsedFrame && item.LastUsedFrame < lastFrame)
|
||||
{
|
||||
Platform::MemoryClear(&item, sizeof(item));
|
||||
_freeItems.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Put back freed items to the free list
|
||||
_freeItems.Add(_freedItems);
|
||||
_freedItems.Clear();
|
||||
|
||||
// Allocate new items (as requested during the previous frame)
|
||||
if (_newItemsCount > 0)
|
||||
@@ -88,4 +75,12 @@ public:
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FreeObject(uint32 cullingId)
|
||||
{
|
||||
ASSERT(cullingId > 0 && cullingId < (uint32)this->Count());
|
||||
_freedItems.Add(cullingId);
|
||||
auto& item = this->Get()[cullingId];
|
||||
Platform::MemoryClear(&item, sizeof(item));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user