Add **Hardware Occlusion Culling** to models, foliage, particles, terrain and local light shadows
This commit is contained in:
@@ -1193,15 +1193,20 @@ API_ENUM(Attributes="Flags") enum class ViewFlags : uint64
|
||||
/// </summary>
|
||||
Particles = 1 << 28,
|
||||
|
||||
/// <summary>
|
||||
/// Shows/hides occlusion culling.
|
||||
/// </summary>
|
||||
OcclusionCulling = 1 << 29,
|
||||
|
||||
/// <summary>
|
||||
/// Default flags for Game.
|
||||
/// </summary>
|
||||
DefaultGame = Reflections | DepthOfField | Fog | Decals | MotionBlur | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | ContactShadows | GlobalSDF | Sky | Particles,
|
||||
DefaultGame = Reflections | DepthOfField | Fog | Decals | MotionBlur | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | ContactShadows | GlobalSDF | Sky | Particles | OcclusionCulling,
|
||||
|
||||
/// <summary>
|
||||
/// Default flags for Editor.
|
||||
/// </summary>
|
||||
DefaultEditor = Reflections | Fog | Decals | DebugDraw | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | EditorSprites | ContactShadows | GlobalSDF | Sky | Particles,
|
||||
DefaultEditor = Reflections | Fog | Decals | DebugDraw | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | EditorSprites | ContactShadows | GlobalSDF | Sky | Particles | OcclusionCulling,
|
||||
|
||||
/// <summary>
|
||||
/// Default flags for materials/models previews generating.
|
||||
@@ -1211,7 +1216,7 @@ API_ENUM(Attributes="Flags") enum class ViewFlags : uint64
|
||||
/// <summary>
|
||||
/// All flags enabled.
|
||||
/// </summary>
|
||||
All = None | DebugDraw | EditorSprites | Reflections | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | Decals | DepthOfField | PhysicsDebug | Fog | MotionBlur | ContactShadows | GlobalSDF | Sky | LightsDebug | Particles,
|
||||
All = None | DebugDraw | EditorSprites | Reflections | SSR | AO | GI | DirectionalLights | PointLights | SpotLights | SkyLights | Shadows | SpecularLight | AntiAliasing | CustomPostProcess | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | Decals | DepthOfField | PhysicsDebug | Fog | MotionBlur | ContactShadows | GlobalSDF | Sky | LightsDebug | Particles | OcclusionCulling,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(ViewFlags);
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "RenderBuffers.h"
|
||||
#include "Engine/Core/Config/GraphicsSettings.h"
|
||||
#include "RenderContext.h"
|
||||
#include "RenderTools.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Renderer/Utils/MultiScaler.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#include "Engine/Core/Config/GraphicsSettings.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
|
||||
// How many frames keep cached buffers for temporal or optional effects?
|
||||
#define LAZY_FRAMES_COUNT 4
|
||||
bool UnsupportedOcclusionCulling = false;
|
||||
|
||||
RenderBuffers::RenderBuffers(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
@@ -275,6 +279,10 @@ void RenderBuffers::Release()
|
||||
for (int32 i = 0; i < _resources.Count(); i++)
|
||||
_resources[i]->ReleaseGPU();
|
||||
|
||||
if (auto* culling = FromInterface(OcclusionCulling))
|
||||
Delete(culling);
|
||||
OcclusionCulling = nullptr;
|
||||
|
||||
RenderTargetPool::Release(VolumetricFog);
|
||||
VolumetricFog = nullptr;
|
||||
RenderTargetPool::Release(VolumetricFogHistory);
|
||||
@@ -305,6 +313,60 @@ RenderBuffers::ReadOnlyDepthBuffer RenderBuffers::GetReadOnlyDepthBuffer() const
|
||||
return { depthBufferRTV, depthBufferSRV };
|
||||
}
|
||||
|
||||
void RenderBuffers::OnRendering(const RenderContext& renderContext)
|
||||
{
|
||||
// Initialize occlusion culling
|
||||
if (UnsupportedOcclusionCulling)
|
||||
return;
|
||||
bool enableCulling = EnumHasAllFlags(renderContext.View.Flags, ViewFlags::OcclusionCulling) && !renderContext.View.IsCullingDisabled && !renderContext.View.IsSingleFrame;
|
||||
const StringAnsi& occlusionCullingTypeName = GraphicsSettings::Get()->OcclusionCulling;
|
||||
if (auto* culling = FromInterface(OcclusionCulling))
|
||||
{
|
||||
// Check if type still matches and effect is active
|
||||
if (culling->GetType().Fullname != occlusionCullingTypeName || !enableCulling)
|
||||
{
|
||||
Delete(culling);
|
||||
OcclusionCulling = nullptr;
|
||||
}
|
||||
}
|
||||
if (!OcclusionCulling && occlusionCullingTypeName.HasChars() && enableCulling)
|
||||
{
|
||||
const ScriptingTypeHandle occlusionCullingType = Scripting::FindScriptingType(occlusionCullingTypeName);
|
||||
if (occlusionCullingType && occlusionCullingType.GetType().GetInterface(IOcclusionCulling::TypeInitializer))
|
||||
{
|
||||
OcclusionCulling = ToInterface<IOcclusionCulling>(NewObject(occlusionCullingType));
|
||||
if (!OcclusionCulling->IsSupported())
|
||||
{
|
||||
UnsupportedOcclusionCulling = true;
|
||||
LOG(Error, "Occlusion Culling system '{}' is unsupported", occlusionCullingTypeName.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_usedCulling)
|
||||
{
|
||||
// Reset existing state to use a fresh CullingIds
|
||||
_cullingLocker.Lock();
|
||||
for (auto& e : Scenes)
|
||||
{
|
||||
for (auto& q : e.Value.Geo)
|
||||
{
|
||||
for (auto& geo : q)
|
||||
{
|
||||
geo.CullingId = 0;
|
||||
}
|
||||
}
|
||||
e.Value.CullingIds.Clear();
|
||||
}
|
||||
_cullingLocker.Unlock();
|
||||
}
|
||||
else
|
||||
_usedCulling = true;
|
||||
}
|
||||
}
|
||||
if (OcclusionCulling)
|
||||
OcclusionCulling->BeginFrame(renderContext);
|
||||
}
|
||||
|
||||
void RenderBuffers::OnSceneRendering(SceneRendering* scene)
|
||||
{
|
||||
if (!Scenes.ContainsKey(scene))
|
||||
@@ -342,6 +404,41 @@ GeometryDrawState* RenderBuffers::GetGeometryDrawState(SceneRendering* scene, in
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RenderBuffers::TestOcclusionCulling(const Actor* actor, uint32& cullingId) const
|
||||
{
|
||||
return TestOcclusionCulling(actor->GetSceneRendering(), actor, actor->GetBox(), cullingId);
|
||||
}
|
||||
|
||||
bool RenderBuffers::TestOcclusionCulling(SceneRendering* scene, const Actor* actor, const BoundingBox& objectBounds, uint32& cullingId, const void* object) const
|
||||
{
|
||||
cullingId = 0;
|
||||
if (!OcclusionCulling)
|
||||
return true;
|
||||
bool result = true;
|
||||
if (auto* sceneData = Scenes.TryGet(scene))
|
||||
{
|
||||
// Get stable CullingId
|
||||
const Pair<const Actor*, const void*> key(actor, object);
|
||||
_cullingLocker.Lock();
|
||||
_cullingIdsOwnerTypes.Add(actor->GetTypeHandle());
|
||||
sceneData->CullingIds.TryGet(key, cullingId);
|
||||
_cullingLocker.Unlock();
|
||||
|
||||
// Cull
|
||||
uint32 cullingIdPrev = cullingId;
|
||||
result = OcclusionCulling->IsVisible(objectBounds, cullingId);
|
||||
|
||||
// Update CullingId if got changed
|
||||
if (cullingIdPrev != cullingId)
|
||||
{
|
||||
_cullingLocker.Lock();
|
||||
sceneData->CullingIds[key] = cullingId;
|
||||
_cullingLocker.Unlock();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RenderBuffers::OnSceneRenderingAddActor(SceneRendering* scene, int32 key, Actor* a)
|
||||
{
|
||||
// Init geo state of that object
|
||||
@@ -361,6 +458,19 @@ 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()))
|
||||
return;
|
||||
if (auto* sceneData = Scenes.TryGet(scene))
|
||||
{
|
||||
for (auto it = sceneData->CullingIds.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
if (it->Key.First == a)
|
||||
{
|
||||
sceneData->CullingIds.Remove(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderBuffers::OnSceneRenderingClear(SceneRendering* scene)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Core/Math/Viewport.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/HashSet.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
class Actor;
|
||||
class SceneRendering;
|
||||
class IOcclusionCulling;
|
||||
|
||||
/// <summary>
|
||||
/// The scene rendering buffers container.
|
||||
@@ -64,14 +66,27 @@ private:
|
||||
uint64 LastFrameHalfResDepth = 0;
|
||||
uint64 LastFrameHiZ = 0;
|
||||
|
||||
// Scene drawing cache with the per-object state (eg. LOD transitions, motion-vectors movement)
|
||||
struct SceneData
|
||||
{
|
||||
// Per-object drawing state (eg. LOD transition). Indexing matches actor/object key of object registered in SceneRendering.
|
||||
Array<GeometryDrawState> Geo[SceneRendering::DrawCategory::MAX];
|
||||
// Scene culling cache with per-object (pair of actor and subobject) CullingId used by the IOcclusionCulling. Allows for stable visibility testing of custom non-actor objects (eg. terrain chunks or foliage patches).
|
||||
Dictionary<Pair<const Actor*, const void*>, uint32> CullingIds;
|
||||
};
|
||||
Dictionary<SceneRendering*, SceneData> Scenes;
|
||||
|
||||
protected:
|
||||
int32 _width = 0;
|
||||
int32 _height = 0;
|
||||
float _aspectRatio = 0.0f;
|
||||
bool _useAlpha = false;
|
||||
bool _useNull = false;
|
||||
bool _usedCulling = false;
|
||||
Viewport _viewport;
|
||||
Array<GPUTexture*, FixedAllocation<32>> _resources;
|
||||
CriticalSection _cullingLocker;
|
||||
mutable HashSet<ScriptingTypeHandle> _cullingIdsOwnerTypes;
|
||||
|
||||
public:
|
||||
union
|
||||
@@ -120,14 +135,6 @@ public:
|
||||
// Maps the custom buffer type into the object that holds the state.
|
||||
Array<CustomBuffer*, HeapAllocation> CustomBuffers;
|
||||
|
||||
// Scene drawing cache with the per-object state (eg. LOD transitions, motion-vectors movement)
|
||||
struct SceneData
|
||||
{
|
||||
// Per-object drawing state (eg. LOD transition). Indexing matches actor/object key of object registered in SceneRendering.
|
||||
Array<GeometryDrawState> Geo[SceneRendering::DrawCategory::MAX];
|
||||
};
|
||||
Dictionary<SceneRendering*, SceneData> Scenes;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="RenderBuffers"/> class.
|
||||
@@ -264,6 +271,11 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD() RenderBuffers* LinkedCustomBuffers = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// Occlusion culling implementation (optional). Can skip drawing occluded objects. Maintains a state synchronized with scene rendering with container RenderBuffers.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) IOcclusionCulling* OcclusionCulling = nullptr;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Allocates the buffers.
|
||||
@@ -283,6 +295,8 @@ public:
|
||||
/// </summary>
|
||||
ReadOnlyDepthBuffer GetReadOnlyDepthBuffer() const;
|
||||
|
||||
// Internal event called by Renderer to initiate drawing.
|
||||
void OnRendering(const RenderContext& renderContext);
|
||||
// Internal event called by SceneRendering to initiate drawing.
|
||||
void OnSceneRendering(SceneRendering* scene);
|
||||
|
||||
@@ -291,6 +305,25 @@ public:
|
||||
/// </summary>
|
||||
GeometryDrawState* GetGeometryDrawState(SceneRendering* scene, int32 key, const Actor* actor) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the occlusion culling test for a specific sub-object of the actor (eg. terrain chunk or foliage patch) and returns the assigned CullingId (for draw call).
|
||||
/// </summary>
|
||||
/// <param name="actor">The owning actor.</param>
|
||||
/// <param name="cullingId">Result CullingId to use for drawing this actor.</param>
|
||||
/// <returns>True if actor can be rendered (is visible or visibility will be calculated on GPU), otherwise false.</returns>
|
||||
bool TestOcclusionCulling(const Actor* actor, uint32& cullingId) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the occlusion culling test for a specific sub-object of the actor (eg. terrain chunk or foliage patch) and returns the assigned CullingId (for draw call).
|
||||
/// </summary>
|
||||
/// <param name="scene">The scene owning this actor.</param>
|
||||
/// <param name="actor">The owning actor.</param>
|
||||
/// <param name="objectBounds">The world-space bounds of the actor (or sub-object).</param>
|
||||
/// <param name="cullingId">Result CullingId to use for drawing this actor (or sub-object).</param>
|
||||
/// <param name="object">The pointer to the sub-actor object. Null for actor-only culling.</param>
|
||||
/// <returns>True if object can be rendered (is visible or visibility will be calculated on GPU), otherwise false.</returns>
|
||||
bool TestOcclusionCulling(SceneRendering* scene, const Actor* actor, const BoundingBox& objectBounds, uint32& cullingId, const void* object = nullptr) const;
|
||||
|
||||
public:
|
||||
// [ISceneRenderingListener]
|
||||
void OnSceneRenderingAddActor(SceneRendering* scene, int32 key, Actor* a) override;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#if USE_EDITOR
|
||||
@@ -345,7 +346,11 @@ void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderCon
|
||||
PostRender(context, renderContext);
|
||||
|
||||
if (Buffers)
|
||||
{
|
||||
if (Buffers->OcclusionCulling)
|
||||
Buffers->OcclusionCulling->EndFrame(renderContext);
|
||||
Buffers->ReleaseUnusedMemory();
|
||||
}
|
||||
}
|
||||
|
||||
Viewport SceneRenderTask::GetViewport() const
|
||||
|
||||
Reference in New Issue
Block a user