Add **Hardware Occlusion Culling** to models, foliage, particles, terrain and local light shadows
This commit is contained in:
BIN
Binary file not shown.
Binary file not shown.
@@ -406,6 +406,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Reflections/SSR"));
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Shadows/Shadows"));
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Utils/BitonicSort"));
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Utils/Culling"));
|
||||
//data.AddRootEngineAsset(TEXT("Shaders/Utils/DebugDraw")); // TODO: debug draw in dev-builds
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Utils/GlobalSignDistanceField"));
|
||||
data.AddRootEngineAsset(TEXT("Shaders/Utils/GPUParticlesSorting"));
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"General\", \"Use V-Sync\")")
|
||||
bool UseVSync = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Anti Aliasing quality setting.
|
||||
/// </summary>
|
||||
@@ -96,6 +97,26 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(1320), EditorDisplay(\"Quality\", \"Allow CSM Blending\")")
|
||||
bool AllowCSMBlending = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The type of the occlusion culling (implements IOcclusionCulling) that will be used to test visibility of the objects and skip rendering occluded ones. Can be left empty to use frustum-culling only.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(1450), EditorDisplay(\"Culling\"), TypeReference(typeof(IOcclusionCulling)), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")")
|
||||
StringAnsi OcclusionCulling;
|
||||
|
||||
/// <summary>
|
||||
/// The number of buffered frames for the visibility query results readback from GPU (to avoid stalls). The higher value the more latency but less CPU stalls (to wait for GPU results). Higher values increase object popping artifacts.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(1460), EditorDisplay(\"Culling\"), Limit(1, 4)")
|
||||
int32 OcclusionBufferedFrames = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The object bounds scale for occlusion culling to reduce popping artifacts caused by latency of visibility readback from GPU to CPU. Higher values inflate bounds which improves visual stability but lowers occlusion culling efficiency.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(1461), EditorDisplay(\"Culling\"), Limit(1, 1.5f)")
|
||||
float OcclusionBoundsScale = 1.1f;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Default probes cubemap resolution (use for Environment Probes, can be overriden per-actor).
|
||||
/// </summary>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
@@ -19,8 +21,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
#include "Engine/Renderer/Utils/GlobalSignDistanceFieldPass.h"
|
||||
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
@@ -174,6 +175,12 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal
|
||||
return;
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Main-view occlusion culling
|
||||
uint32 cullingId = 0;
|
||||
bool isMain = context.RenderContext.View.Pass != DrawPass::Depth;
|
||||
if (isMain && !context.RenderContext.Buffers->TestOcclusionCulling(context.Scene, this, cluster->TotalBounds, cullingId, cluster))
|
||||
return;
|
||||
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
{
|
||||
@@ -487,6 +494,7 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me
|
||||
type.Model->HighestResidentLODIndex(),
|
||||
type.Model->GetLODsCount() - 1,
|
||||
renderContext.View.CullingFrustum,
|
||||
GetSceneRendering(),
|
||||
};
|
||||
_cachedDrawWorldOrigin = renderContext.View.Origin;
|
||||
if (context.RenderContext.View.Pass != DrawPass::Depth)
|
||||
|
||||
@@ -172,6 +172,7 @@ private:
|
||||
Float3 LodViewPosition;
|
||||
int32 MinLOD, MaxLOD;
|
||||
BoundingFrustum CullingFrustum;
|
||||
SceneRendering* Scene;
|
||||
|
||||
FORCE_INLINE int32 ClampLODIndex(int32 index) const
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/SceneObjectsFactory.h"
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
@@ -1262,6 +1263,15 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
const Float3 translation = _transform.Translation - renderContext.View.Origin;
|
||||
Matrix::Transformation(_transform.Scale, _transform.Orientation, translation, world);
|
||||
auto drawState = renderContext.Buffers->GetGeometryDrawState(&GetScene()->Rendering, _sceneRenderingKey, this);
|
||||
SkinnedMesh::DrawInfo draw;
|
||||
draw.DrawModes = DrawModes;
|
||||
if (drawState && renderContextBatch.Buffers->OcclusionCulling && !renderContextBatch.Buffers->OcclusionCulling->IsVisible(_box, *drawState))
|
||||
{
|
||||
// Draw shadows-only (or cull)
|
||||
draw.DrawModes &= DrawPass::Depth;
|
||||
if (renderContextBatch.Contexts.Count() == 1 || draw.DrawModes == DrawPass::None)
|
||||
return;
|
||||
}
|
||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(drawState, world);
|
||||
|
||||
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.WorldPosition));
|
||||
@@ -1271,7 +1281,6 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
if (_bones.IsDirty)
|
||||
_bones.Flush();
|
||||
|
||||
SkinnedMesh::DrawInfo draw;
|
||||
draw.Buffer = &Entries;
|
||||
draw.SkinningBones = RenderListExtension.GlobalBuffer;
|
||||
draw.SkinningBonesOffset = _bones.GlobalBufferOffset / sizeof(Matrix3x4);
|
||||
@@ -1289,7 +1298,6 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
draw.World = &world;
|
||||
draw.DrawState = drawState;
|
||||
draw.Deformation = _deformation;
|
||||
draw.DrawModes = DrawModes;
|
||||
draw.Bounds = _sphere;
|
||||
draw.Bounds.Center -= renderContext.View.Origin;
|
||||
draw.PerInstanceRandom = GetPerInstanceRandom();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "PointLight.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
@@ -123,6 +124,12 @@ void PointLight::Draw(RenderContextBatch& renderContextBatch)
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View)));
|
||||
uint32 cullingId = 0;
|
||||
if (data.CanRenderShadow(renderContext.View) && !renderContext.Buffers->TestOcclusionCulling(this, cullingId))
|
||||
{
|
||||
// Occlusion cull dynamic shadow
|
||||
data.ShadowsMode = ShadowsCastingMode::None;
|
||||
}
|
||||
renderContext.List->PointLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUBuffer.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/Scene/SceneRendering.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
@@ -392,6 +395,17 @@ void SplineModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
return; // TODO: Spline Model rendering to Global SDF
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
|
||||
return; // TODO: Spline Model rendering to Global Surface Atlas
|
||||
auto drawState = renderContext.Buffers->GetGeometryDrawState(&GetScene()->Rendering, _sceneRenderingKey, this);
|
||||
ACTOR_GET_WORLD_MATRIX(this, view, world);
|
||||
DrawPass drawModes = DrawModes;
|
||||
if (drawState && renderContextBatch.Buffers->OcclusionCulling && !renderContextBatch.Buffers->OcclusionCulling->IsVisible(_box, *drawState))
|
||||
{
|
||||
// Draw shadows-only (or cull)
|
||||
drawModes &= DrawPass::Depth;
|
||||
if (renderContextBatch.Contexts.Count() == 1 || drawModes == DrawPass::None)
|
||||
return;
|
||||
}
|
||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(drawState, world);
|
||||
if (!Entries.IsValidFor(model))
|
||||
Entries.Setup(model);
|
||||
|
||||
@@ -466,16 +480,18 @@ void SplineModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
|
||||
// Check if skip rendering
|
||||
const auto shadowsMode = entry.ShadowsMode & slot.ShadowsMode;
|
||||
const auto drawModes = DrawModes & material->GetDrawModes();
|
||||
if (drawModes == DrawPass::None)
|
||||
const auto meshDrawModes = drawModes & material->GetDrawModes();
|
||||
if (meshDrawModes == DrawPass::None)
|
||||
continue;
|
||||
|
||||
// Submit draw call
|
||||
mesh->GetDrawCallGeometry(drawCall);
|
||||
drawCall.Material = material;
|
||||
renderContext.List->AddDrawCall(renderContextBatch, drawModes, _staticFlags, shadowsMode, instanceSphere, drawCall, entry.ReceiveDecals);
|
||||
renderContext.List->AddDrawCall(renderContextBatch, meshDrawModes, _staticFlags, shadowsMode, instanceSphere, drawCall, entry.ReceiveDecals);
|
||||
}
|
||||
}
|
||||
|
||||
GEOMETRY_DRAW_STATE_EVENT_END(drawState, world);
|
||||
}
|
||||
|
||||
bool SplineModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "SpotLight.h"
|
||||
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Content/Assets/IESProfile.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Level/Scene/SceneRendering.h"
|
||||
|
||||
@@ -175,6 +175,12 @@ void SpotLight::Draw(RenderContextBatch& renderContextBatch)
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View)));
|
||||
uint32 cullingId = 0;
|
||||
if (data.CanRenderShadow(renderContext.View) && !renderContext.Buffers->TestOcclusionCulling(this, cullingId))
|
||||
{
|
||||
// Occlusion cull dynamic shadow
|
||||
data.ShadowsMode = ShadowsCastingMode::None;
|
||||
}
|
||||
renderContext.List->SpotLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Renderer/Utils/GlobalSignDistanceFieldPass.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
|
||||
#include "Engine/Utilities/Encryption.h"
|
||||
#if USE_EDITOR
|
||||
@@ -386,11 +387,19 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
}
|
||||
auto drawState = renderContext.Buffers->GetGeometryDrawState(&GetScene()->Rendering, _sceneRenderingKey, this);
|
||||
ACTOR_GET_WORLD_MATRIX(this, view, world);
|
||||
Mesh::DrawInfo draw;
|
||||
draw.DrawModes = _drawModes;
|
||||
if (drawState && renderContextBatch.Buffers->OcclusionCulling && !renderContextBatch.Buffers->OcclusionCulling->IsVisible(_box, *drawState))
|
||||
{
|
||||
// Draw shadows-only (or cull)
|
||||
draw.DrawModes &= DrawPass::Depth;
|
||||
if (renderContextBatch.Contexts.Count() == 1 || draw.DrawModes == DrawPass::None)
|
||||
return;
|
||||
}
|
||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(drawState, world);
|
||||
if (_vertexColorsDirty)
|
||||
FlushVertexColors();
|
||||
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Buffer = &Entries;
|
||||
draw.World = &world;
|
||||
draw.DrawState = drawState;
|
||||
@@ -398,7 +407,6 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
draw.Lightmap = _scene && Lightmap.TextureIndex != -1 ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
|
||||
draw.LightmapUVs = &Lightmap.UVsArea;
|
||||
draw.Flags = _staticFlags;
|
||||
draw.DrawModes = _drawModes;
|
||||
draw.Bounds = _sphere;
|
||||
draw.Bounds.Center -= renderContext.View.Origin;
|
||||
draw.PerInstanceRandom = GetPerInstanceRandom();
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -613,7 +615,11 @@ void ParticleEffect::Draw(RenderContextBatch& renderContextBatch)
|
||||
mainView.Pass == DrawPass::GlobalSurfaceAtlas ||
|
||||
EnumHasNoneFlags(mainView.Flags, ViewFlags::Particles))
|
||||
return;
|
||||
Particles::DrawParticles(renderContextBatch, this);
|
||||
auto drawState = renderContext.Buffers->GetGeometryDrawState(&GetScene()->Rendering, _sceneRenderingKey, this);
|
||||
DrawPass drawModes = DrawModes;
|
||||
if (drawState && renderContextBatch.Buffers->OcclusionCulling && !renderContextBatch.Buffers->OcclusionCulling->IsVisible(_box, *drawState))
|
||||
drawModes &= DrawPass::Depth;
|
||||
Particles::DrawParticles(renderContextBatch, this, drawModes);
|
||||
|
||||
// Cull again against the main context (if using multiple ones) to skip caching draw distance from shadow projections
|
||||
const BoundingSphere bounds(_sphere.Center - mainView.Origin, _sphere.Radius);
|
||||
|
||||
@@ -1137,7 +1137,7 @@ void DrawEmitterGPU(RenderContextBatch& renderContextBatch, ParticleBuffer* buff
|
||||
|
||||
#endif
|
||||
|
||||
void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect)
|
||||
void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect, DrawPass drawModes)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Particles);
|
||||
@@ -1153,7 +1153,7 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf
|
||||
for (int32 i = 0; i < renderContextBatch.Contexts.Count(); i++)
|
||||
{
|
||||
const RenderView& view = renderContextBatch.Contexts.Get()[i].View;
|
||||
const bool visible = (view.Pass & effect->DrawModes) != DrawPass::None && (view.IsCullingDisabled || view.CullingFrustum.Intersects(bounds));
|
||||
const bool visible = (view.Pass & drawModes) != DrawPass::None && (view.IsCullingDisabled || view.CullingFrustum.Intersects(bounds));
|
||||
if (visible)
|
||||
{
|
||||
drawAnyView = true;
|
||||
@@ -1163,7 +1163,7 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf
|
||||
}
|
||||
if (drawAnyView == false)
|
||||
return;
|
||||
viewsDrawModes &= effect->DrawModes;
|
||||
viewsDrawModes &= drawModes;
|
||||
|
||||
// Setup
|
||||
ScopeReadLock systemScope(SystemLocker);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Graphics/Enums.h"
|
||||
|
||||
class TaskGraphSystem;
|
||||
struct RenderContextBatch;
|
||||
@@ -49,7 +50,8 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="renderContextBatch">The rendering context.</param>
|
||||
/// <param name="effect">The owning actor.</param>
|
||||
static void DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect);
|
||||
/// <param name="drawModes">The drawing modes.</param>
|
||||
static void DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect, DrawPass drawModes);
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "HardwareOcclusionCulling.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Assets/Shader.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUPass.h"
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerGPU.h"
|
||||
#include "Engine/Core/Config/GraphicsSettings.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
HardwareOcclusionCulling::HardwareOcclusionCulling(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
, _vertexBuffer(0, sizeof(Float3), TEXT("HardwareOcclusionCulling.VB"), GPUVertexLayout::Get({ { VertexElement::Types::Position, 0, 0, 0, PixelFormat::R32G32B32_Float } }))
|
||||
, _shader(Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Utils/Culling")))
|
||||
{
|
||||
}
|
||||
|
||||
HardwareOcclusionCulling::~HardwareOcclusionCulling()
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||
}
|
||||
|
||||
void HardwareOcclusionCulling::BeginFrame(const RenderContext& renderContext)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Read settings
|
||||
auto settings = GraphicsSettings::Get();
|
||||
auto framesCount = Math::Clamp(settings->OcclusionBufferedFrames, 1, MaxFrames);
|
||||
if (_framesCount != framesCount)
|
||||
{
|
||||
// Reset state (graphics backend recycles stale queries)
|
||||
_framesCount = framesCount;
|
||||
for (auto& e : _items)
|
||||
{
|
||||
Platform::MemoryClear(e.Queries, sizeof(e.Queries) + sizeof(e.Frames));
|
||||
}
|
||||
}
|
||||
_boundsScale = Math::Max(settings->OcclusionBoundsScale, 1.01f);
|
||||
|
||||
// Handle origin-relative rendering
|
||||
_forceUpdateBounds = renderContext.View.Origin != _origin;
|
||||
_origin = renderContext.View.Origin;
|
||||
_viewPos = renderContext.View.WorldPosition;
|
||||
|
||||
// Skip reading occlusion results on camera cuts (but issue queries for the next frame)
|
||||
bool forceVisible = renderContext.Task->IsCameraCut;
|
||||
|
||||
// Skip reading when view was not rendered for some time (queries might expire)
|
||||
uint64 engineFrame = Engine::FrameCount;
|
||||
forceVisible |= (int32)(engineFrame - _lastEngineFrameUsed) >= framesCount;
|
||||
_lastEngineFrameUsed = engineFrame;
|
||||
|
||||
if (forceVisible)
|
||||
{
|
||||
// Reset visibility
|
||||
for (auto& item : _items)
|
||||
{
|
||||
item.Occluded = false;
|
||||
Platform::MemoryClear(item.Queries, sizeof(item.Queries) + sizeof(item.Frames));
|
||||
}
|
||||
}
|
||||
else if (_frameCounter > 0)
|
||||
{
|
||||
// Resolve the last buffered frame results (with wait)
|
||||
PROFILE_CPU_NAMED("Wait for Occlusion Queries");
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
int32 frame = _frameCounter, bufferedFrame = _frameCounter % framesCount, itemsUsed = 0;
|
||||
auto device = GPUDevice::Instance;
|
||||
for (auto& item : _items)
|
||||
{
|
||||
uint64 query = item.Queries[bufferedFrame];
|
||||
int32 lag = frame - item.Frames[bufferedFrame];
|
||||
if (query && lag <= framesCount)
|
||||
{
|
||||
// Clear query
|
||||
item.Queries[bufferedFrame] = 0;
|
||||
|
||||
// Read result (occluded object didn't pass any depth test, assume visible if query failed)
|
||||
uint64 result = 1;
|
||||
device->GetQueryResult(query, result, true);
|
||||
item.Occluded = result == 0;
|
||||
itemsUsed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Maintain state if no new query has been issued (eg. object goes outside frustum or gets hidden)
|
||||
}
|
||||
}
|
||||
ZoneValue(itemsUsed);
|
||||
}
|
||||
|
||||
// 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 < _items.Count(); i++)
|
||||
{
|
||||
auto& item = _items.Get()[i];
|
||||
if (item.LastUsedFrame && item.LastUsedFrame < lastFrame)
|
||||
{
|
||||
Platform::MemoryClear(&item, sizeof(item));
|
||||
_freeItems.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocate new items (as requested during the previous frame)
|
||||
if (_newItemsCount > 0)
|
||||
{
|
||||
int32 itemsStart = _items.Count(), count = (int32)_newItemsCount, freeStart = _freeItems.Count();
|
||||
if (itemsStart == 0)
|
||||
count++; // 0 is invalid for cullingId
|
||||
_items.AddZeroed(count);
|
||||
_freeItems.AddUninitialized(count);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
_freeItems.Get()[freeStart + i] = itemsStart + i;
|
||||
if (itemsStart == 0)
|
||||
_freeItems.RemoveAt(0); // 0 is invalid for cullingId
|
||||
_newItemsCount = 0;
|
||||
}
|
||||
_freeItemsCount = _freeItems.Count();
|
||||
|
||||
// Prepare vertex buffer to build geometry bound meshes in async during drawing
|
||||
_vertexBuffer.Data.Resize(_items.Count() * 8 * sizeof(Float3), true);
|
||||
_dirtyBounds = 0;
|
||||
}
|
||||
|
||||
void HardwareOcclusionCulling::EndFrame(const RenderContext& renderContext)
|
||||
{
|
||||
// Move to the next frame
|
||||
_frameCounter++;
|
||||
}
|
||||
|
||||
void HardwareOcclusionCulling::Submit(const RenderContext& renderContext)
|
||||
{
|
||||
if (_items.IsEmpty() || !_shader || !_shader->IsLoaded())
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
PROFILE_GPU("Occlusion Culling");
|
||||
GPUContext* context = GPUDevice::Instance->GetMainContext();
|
||||
|
||||
// Setup vertex and index buffers
|
||||
if (!_indexBuffer)
|
||||
{
|
||||
const uint16 cubeIndices[12 * 3] =
|
||||
{
|
||||
0, 2, 3,
|
||||
0, 3, 1,
|
||||
4, 5, 7,
|
||||
4, 7, 6,
|
||||
0, 1, 5,
|
||||
0, 5, 4,
|
||||
2, 6, 7,
|
||||
2, 7, 3,
|
||||
0, 4, 6,
|
||||
0, 6, 2,
|
||||
1, 3, 7,
|
||||
1, 7, 5,
|
||||
};
|
||||
auto desc = GPUBufferDescription::Index(sizeof(uint16), ARRAY_COUNT(cubeIndices), cubeIndices);
|
||||
_indexBuffer = GPUDevice::Instance->CreateBuffer(TEXT("HardwareOcclusionCulling.IB"));
|
||||
_indexBuffer->Init(desc);
|
||||
}
|
||||
if (_dirtyBounds)
|
||||
_vertexBuffer.Flush(context);
|
||||
auto vb = _vertexBuffer.GetBuffer();
|
||||
|
||||
// Use depth-only for testing visibility
|
||||
GPUDrawPass pass(context, *renderContext.Buffers->DepthBuffer, GPUDrawPassAction::Load, Span<GPUTextureView*>(), Span<GPUDrawPassAction>());
|
||||
if (!_pso)
|
||||
{
|
||||
_pso = GPUDevice::Instance->CreatePipelineState();
|
||||
auto desc = GPUPipelineState::Description::Default;
|
||||
desc.DepthWriteEnable = false;
|
||||
desc.DepthClipEnable = false;
|
||||
desc.DepthFunc = ComparisonFunc::DefaultEqual;
|
||||
desc.StencilEnable = false;
|
||||
desc.CullMode = CullMode::Inverted;
|
||||
desc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None;
|
||||
desc.VS = _shader->GPU->GetVS("VS_HardwareOcclusionCulling");
|
||||
if (_pso->Init(desc))
|
||||
return;
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
_shader->Reloading.Bind<HardwareOcclusionCulling, &HardwareOcclusionCulling::OnShaderReloading>(this);
|
||||
#endif
|
||||
}
|
||||
auto cb = _shader->GPU->GetCB(0);
|
||||
{
|
||||
Matrix viewProjectionMatrix;
|
||||
Matrix::Transpose(renderContext.View.ViewProjection(), viewProjectionMatrix);
|
||||
context->UpdateCB(cb, &viewProjectionMatrix);
|
||||
}
|
||||
context->BindCB(0, cb);
|
||||
context->BindIB(_indexBuffer);
|
||||
context->BindVB(Span<GPUBuffer*>(&vb, 1));
|
||||
context->SetState(_pso);
|
||||
#if COMPILE_WITH_PROFILER
|
||||
auto stats = RenderStatsData::Counter;
|
||||
#endif
|
||||
|
||||
// Issue occlusion queries
|
||||
int32 frame = _frameCounter, bufferedFrame = _frameCounter % _framesCount, itemsCount = _items.Count(), itemsUsed = 0;
|
||||
auto frustum = renderContext.View.Frustum;
|
||||
auto* items = _items.Get();
|
||||
for (int32 i = 0; i < itemsCount; i++)
|
||||
{
|
||||
auto& item = items[i];
|
||||
if (item.LastUsedFrame != frame || frustum.Contains(item.Bounds) == ContainmentType::Disjoint)
|
||||
continue;
|
||||
itemsUsed++;
|
||||
|
||||
// Begin occlusion query for this object index
|
||||
uint64 query = context->BeginQuery(GPUQueryType::BinaryOcclusion);
|
||||
item.Queries[bufferedFrame] = query;
|
||||
item.Frames[bufferedFrame] = frame;
|
||||
|
||||
// Draw the low-poly bounds of that object
|
||||
context->DrawIndexed(12 * 3, i * 8);
|
||||
|
||||
// End occlusion query
|
||||
context->EndQuery(query);
|
||||
}
|
||||
ZoneValue(itemsUsed);
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Cancel-out any draw stats from profiler (hidden draws)
|
||||
RenderStatsData::Counter = stats;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HardwareOcclusionCulling::IsVisible(const BoundingBox& bounds, uint32& cullingId)
|
||||
{
|
||||
return IsVisible(bounds, cullingId, nullptr);
|
||||
}
|
||||
|
||||
bool HardwareOcclusionCulling::IsVisible(const BoundingBox& bounds, GeometryDrawState& drawState)
|
||||
{
|
||||
return IsVisible(bounds, drawState.CullingId, &drawState);
|
||||
}
|
||||
|
||||
bool HardwareOcclusionCulling::IsVisible(BoundingBox bounds, uint32& cullingId, GeometryDrawState* drawState)
|
||||
{
|
||||
// Enlarge bounds to reduce popping
|
||||
bounds = BoundingBox::MakeScaled(bounds, _boundsScale);
|
||||
// TODO: use camera motion to enlarge bounds
|
||||
// TODO: use object motion (from prev frame world matrix) to enlarge bounds in the direction of movement to reduce popping
|
||||
|
||||
// Assume visible when view is right inside the bounds
|
||||
if (bounds.Contains(_viewPos) == ContainmentType::Contains)
|
||||
return true;
|
||||
|
||||
// Check if object doesn't have ID assigned yet
|
||||
if (cullingId == 0 || cullingId >= (uint32)_items.Count())
|
||||
{
|
||||
int64 freeIndex = Platform::InterlockedDecrement(&_freeItemsCount);
|
||||
if (freeIndex >= 0)
|
||||
{
|
||||
// Use the ID from the free list
|
||||
ASSERT_LOW_LAYER(freeIndex < _freeItems.Count());
|
||||
cullingId = _freeItems.Get()[freeIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count space needed to contain all objects (for the next frame)
|
||||
Platform::InterlockedIncrement(&_newItemsCount);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update item bounds
|
||||
auto& item = _items.Get()[cullingId];
|
||||
if (item.Bounds != bounds || _forceUpdateBounds)
|
||||
{
|
||||
// Update bounds
|
||||
item.Bounds = bounds;
|
||||
|
||||
// Write to the vertex buffer
|
||||
Float3 boxMin = bounds.Minimum - _origin;
|
||||
Float3 boxMax = bounds.Maximum - _origin;
|
||||
ASSERT_LOW_LAYER(_vertexBuffer.Data.Count() >= (8 * sizeof(Float3)) * (cullingId + 1));
|
||||
Float3* vertices = (Float3*)(_vertexBuffer.Data.Get() + (8 * sizeof(Float3)) * cullingId);
|
||||
vertices[0] = boxMin;
|
||||
vertices[1] = Float3(boxMin.X, boxMin.Y, boxMax.Z);
|
||||
vertices[2] = Float3(boxMin.X, boxMax.Y, boxMin.Z);
|
||||
vertices[3] = Float3(boxMin.X, boxMax.Y, boxMax.Z);
|
||||
vertices[4] = Float3(boxMax.X, boxMin.Y, boxMin.Z);
|
||||
vertices[5] = Float3(boxMax.X, boxMin.Y, boxMax.Z);
|
||||
vertices[6] = Float3(boxMax.X, boxMax.Y, boxMin.Z);
|
||||
vertices[7] = boxMax;
|
||||
Platform::InterlockedIncrement(&_dirtyBounds);
|
||||
}
|
||||
|
||||
// Force visible when object was not rendered last frame (eg. outside the frustum)
|
||||
if (_frameCounter - item.LastUsedFrame > 1)
|
||||
{
|
||||
item.Occluded = false;
|
||||
}
|
||||
item.LastUsedFrame = _frameCounter;
|
||||
|
||||
// Read occlusion result
|
||||
return !item.Occluded;
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
|
||||
void HardwareOcclusionCulling::OnShaderReloading(Asset* obj)
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(_pso);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IOcclusionCulling.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Graphics/DynamicBuffer.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
|
||||
class GPUPipelineState;
|
||||
|
||||
/// <summary>
|
||||
/// Occlusion culling system based on hardware occlusion queries.
|
||||
/// Uses GPU query to determine if the object is visible (not occluded by other geometry) and can be drawn.
|
||||
/// Results are readback with a few frames latency (objects can pop in fast motion).
|
||||
/// </summary>
|
||||
API_CLASS(Sealed) class FLAXENGINE_API HardwareOcclusionCulling : public ScriptingObject, public IOcclusionCulling
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(HardwareOcclusionCulling);
|
||||
~HardwareOcclusionCulling();
|
||||
|
||||
// Maximum number of frames to delay the visibility results readback from GPU (to avoid stalls). The higher value the more latency but less GPU stalls.
|
||||
constexpr static int32 MaxFrames = 4;
|
||||
|
||||
private:
|
||||
struct alignas(sizeof(uint64)) Item
|
||||
{
|
||||
BoundingBox Bounds; // Object bounds (world-space)
|
||||
uint64 Queries[MaxFrames]; // Buffered frames (ring-buffer)
|
||||
int32 Frames[MaxFrames]; // Frame counter for each query
|
||||
int32 LastUsedFrame; // Last frame object was drawn (incl. not frustum-culled or hidden)
|
||||
bool Occluded; // Result from the last frame
|
||||
};
|
||||
|
||||
int32 _framesCount = 2, _frameCounter = 0;
|
||||
volatile int64 _freeItemsCount = 0;
|
||||
volatile int64 _newItemsCount = 0;
|
||||
volatile int64 _dirtyBounds = 0;
|
||||
uint64 _lastEngineFrameUsed = 0;
|
||||
float _boundsScale = 1.0f;
|
||||
Array<Item> _items;
|
||||
Array<uint32> _freeItems;
|
||||
GPUPipelineState* _pso = nullptr;
|
||||
DynamicVertexBuffer _vertexBuffer;
|
||||
GPUBuffer* _indexBuffer = nullptr;
|
||||
Vector3 _origin = Vector3::Zero;
|
||||
Vector3 _viewPos = Vector3::Zero;
|
||||
bool _forceUpdateBounds = true;
|
||||
AssetReference<class Shader> _shader;
|
||||
|
||||
public:
|
||||
// [IOcclusionCulling]
|
||||
void BeginFrame(const RenderContext& renderContext) override;
|
||||
void EndFrame(const RenderContext& renderContext) override;
|
||||
void Submit(const RenderContext& renderContext) override;
|
||||
bool IsVisible(const BoundingBox& bounds, uint32& cullingId) override;
|
||||
bool IsVisible(const BoundingBox& bounds, GeometryDrawState& drawState) override;
|
||||
|
||||
private:
|
||||
bool IsVisible(BoundingBox bounds, uint32& cullingId, GeometryDrawState* drawState);
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
|
||||
struct GeometryDrawState;
|
||||
struct RenderContext;
|
||||
struct RenderContextBatch;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for occlusion querying and culling systems. Performs visibility checks for the scene objects (incl. meshes and lights).
|
||||
/// Implementations can use hardware occlusion queries, software rasterization, Hi-Z tests, or any other method to determine if an object is visible in the current view frustum and not occluded by other geometry.
|
||||
/// </summary>
|
||||
/// <remarks>Can be implemented only in the native code (C++) but also used in scripting for custom objects culling.</remarks>
|
||||
API_INTERFACE() class FLAXENGINE_API IOcclusionCulling
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(IOcclusionCulling);
|
||||
virtual ~IOcclusionCulling() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the culling system is supported on the current platform and hardware.
|
||||
/// </summary>
|
||||
virtual bool IsSupported() { return true; }
|
||||
|
||||
/// <summary>
|
||||
/// Frame begin event. Called before the drawing to prepare the culling system for the new frame.
|
||||
/// </summary>
|
||||
virtual void BeginFrame(const RenderContext& renderContext) {}
|
||||
|
||||
/// <summary>
|
||||
/// Submits occlusion queries or performs the culling operations. Called after occluders depth drawing (depth from GBufferPass or DepthPrePass). Can be used to submit occlusion queries for the scene objects (eg., using GPU occlusion queries) or generate a Hi-Z buffer.
|
||||
/// </summary>
|
||||
virtual void Submit(const RenderContext& renderContext) {}
|
||||
|
||||
/// <summary>
|
||||
/// Frame end event. Called after the drawing to prepare the culling system for the new frame.
|
||||
/// </summary>
|
||||
virtual void EndFrame(const RenderContext& renderContext) {}
|
||||
|
||||
/// <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).
|
||||
/// GPU-based culling uses indirect draw arguments to handle conditional drawing.
|
||||
/// Usually called from multiple threads at once (async) when rendering scene.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The bounds of the object to check.</param>
|
||||
/// <param name="cullingId">The unique identifier of the visibility query - stable for the same object. Set to 0 by default (as invalid ID), will be assigned internally by the culling system.</param>
|
||||
/// <returns>True if object can be rendered (is visible or visibility will be calculated on GPU), otherwise false.</returns>
|
||||
API_FUNCTION(Sealed) virtual bool IsVisible(const BoundingBox& bounds, API_PARAM(Ref) uint32& cullingId) { return true; }
|
||||
|
||||
/// <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).
|
||||
/// GPU-based culling uses indirect draw arguments to handle conditional drawing.
|
||||
/// Usually called from multiple threads at once (async) when rendering scene.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The bounds of the object to check.</param>
|
||||
/// <param name="drawState">The geometry drawing state - allocated within RenderBuffers for a single model actor. Contains CullingId field that will be assigned and used internally by the culling system.</param>
|
||||
/// <returns>True if object can be rendered (is visible or visibility will be calculated on GPU), otherwise false.</returns>
|
||||
virtual bool IsVisible(const BoundingBox& bounds, GeometryDrawState& drawState) { return true; }
|
||||
};
|
||||
@@ -334,6 +334,11 @@ struct GeometryDrawState
|
||||
/// </summary>
|
||||
uint64 PrevFrame = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier of the object culling state. Assigned and managed by the occlusion culling system (IOcclusionCulling).
|
||||
/// </summary>
|
||||
uint32 CullingId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The previous frame model LOD index used. It's locked during LOD transition to cache the transition start LOD.
|
||||
/// </summary>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
|
||||
void CheckDrawListQuadOverdraw(RenderList* list, DrawCallsListType type)
|
||||
{
|
||||
@@ -127,6 +127,10 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context,
|
||||
context->ResetUA();
|
||||
context->ResetSR();
|
||||
|
||||
// Submit occlusion queries
|
||||
if (renderContext.Buffers->OcclusionCulling)
|
||||
renderContext.Buffers->OcclusionCulling->Submit(renderContext);
|
||||
|
||||
// Convert stats into debug colors
|
||||
context->BindSR(0, overdrawTexture->View());
|
||||
context->SetRenderTarget(lightBuffer);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "GBufferPass.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Culling/IOcclusionCulling.h"
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
#include "Engine/Renderer/Editor/VertexColors.h"
|
||||
#include "Engine/Renderer/Editor/LightmapUVsDensity.h"
|
||||
@@ -221,6 +222,10 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTexture* lightBuffer)
|
||||
renderContext.List->ExecuteDrawCalls(renderContext, DrawCallsListType::GBuffer);
|
||||
}
|
||||
|
||||
// Submit occlusion queries
|
||||
if (renderContext.Buffers->OcclusionCulling)
|
||||
renderContext.Buffers->OcclusionCulling->Submit(renderContext);
|
||||
|
||||
// Draw decals
|
||||
DrawDecals(renderContext, lightBuffer->View());
|
||||
|
||||
|
||||
@@ -476,17 +476,17 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
e->PreRender(context, renderContext);
|
||||
|
||||
// Final render view preparations
|
||||
renderContext.View.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion;
|
||||
if (setup.UseMotionVectors)
|
||||
renderContext.View.Pass |= DrawPass::MotionVectors;
|
||||
renderContext.View.Prepare(renderContext);
|
||||
renderContext.Buffers->OnRendering(renderContext);
|
||||
}
|
||||
|
||||
static void CollectDrawCalls(GPUContext* context, RenderContext& renderContext, RenderContextBatch& renderContextBatch)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Collect Draw Calls");
|
||||
RenderSetup& setup = renderContext.List->Setup;
|
||||
|
||||
renderContext.View.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion;
|
||||
if (setup.UseMotionVectors)
|
||||
renderContext.View.Pass |= DrawPass::MotionVectors;
|
||||
renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value
|
||||
renderContext.List->PreDraw(context, renderContextBatch);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Physics/PhysicalMaterial.h"
|
||||
#include "Engine/Physics/PhysicsBackend.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Graphics/RenderContext.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
@@ -529,15 +530,15 @@ void Terrain::RemovePatch(const Int2& patchCoord)
|
||||
void Terrain::Draw(RenderContextBatch& renderContextBatch)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
if (DrawSetup(renderContextBatch.GetMainContext()))
|
||||
RenderContext& mainContext = renderContextBatch.GetMainContext();
|
||||
if (DrawSetup(mainContext))
|
||||
return;
|
||||
HashSet<TerrainChunk*, RendererAllocation> drawnChunks;
|
||||
bool isMain = true;
|
||||
for (RenderContext& renderContext : renderContextBatch.Contexts)
|
||||
{
|
||||
const DrawPass drawModes = DrawModes & renderContext.View.Pass;
|
||||
if (drawModes == DrawPass::None)
|
||||
continue;
|
||||
DrawImpl(renderContext, drawnChunks);
|
||||
DrawImpl(renderContext, DrawModes & renderContext.View.Pass, drawnChunks, isMain);
|
||||
isMain = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,14 +603,18 @@ bool Terrain::DrawSetup(RenderContext& renderContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Terrain::DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, RendererAllocation>& drawnChunks)
|
||||
void Terrain::DrawImpl(RenderContext& renderContext, DrawPass drawModes, HashSet<TerrainChunk*, RendererAllocation>& drawnChunks, bool isMain)
|
||||
{
|
||||
if (drawModes == DrawPass::None)
|
||||
return;
|
||||
|
||||
// Collect chunks to render and calculate LOD/material for them (required to be done before to gather NeighborLOD)
|
||||
Array<TerrainChunk*, RendererAllocation> drawChunks;
|
||||
|
||||
// Frustum vs Box culling for patches
|
||||
const BoundingFrustum frustum = renderContext.View.CullingFrustum;
|
||||
const Vector3 origin = renderContext.View.Origin;
|
||||
auto scene = _scene ? &_scene->Rendering : nullptr;
|
||||
for (int32 patchIndex = 0; patchIndex < _patches.Count(); patchIndex++)
|
||||
{
|
||||
const auto patch = _patches[patchIndex];
|
||||
@@ -623,16 +628,22 @@ void Terrain::DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, Rend
|
||||
// Frustum vs Box culling for chunks
|
||||
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
|
||||
{
|
||||
auto chunk = &patch->Chunks[chunkIndex];
|
||||
TerrainChunk* chunk = &patch->Chunks[chunkIndex];
|
||||
bounds = BoundingBox(chunk->_bounds.Minimum - origin, chunk->_bounds.Maximum - origin);
|
||||
if (renderContext.View.IsCullingDisabled || frustum.Intersects(bounds))
|
||||
{
|
||||
// Init chunks (once)
|
||||
if (!drawnChunks.Contains(chunk) && !chunk->PrepareDraw(renderContext))
|
||||
continue;
|
||||
drawnChunks.Add(chunk);
|
||||
|
||||
// Main-view occlusion culling
|
||||
uint32 cullingId = 0;
|
||||
if (isMain && !renderContext.Buffers->TestOcclusionCulling(scene, this, chunk->_bounds, cullingId, chunk))
|
||||
continue;
|
||||
|
||||
// Add chunk for drawing
|
||||
drawChunks.Add(chunk);
|
||||
drawnChunks.Add(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,7 +652,7 @@ void Terrain::DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, Rend
|
||||
// Draw all visible chunks
|
||||
for (int32 i = 0; i < drawChunks.Count(); i++)
|
||||
{
|
||||
drawChunks.Get()[i]->Draw(renderContext);
|
||||
drawChunks.Get()[i]->Draw(renderContext, drawModes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -457,7 +457,7 @@ public:
|
||||
private:
|
||||
ImplementPhysicsDebug;
|
||||
bool DrawSetup(RenderContext& renderContext);
|
||||
void DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, class RendererAllocation>& drawnChunks);
|
||||
void DrawImpl(RenderContext& renderContext, DrawPass drawModes, HashSet<TerrainChunk*, class RendererAllocation>& drawnChunks, bool isMain);
|
||||
|
||||
public:
|
||||
// [PhysicsColliderActor]
|
||||
|
||||
@@ -83,7 +83,7 @@ bool TerrainChunk::PrepareDraw(const RenderContext& renderContext)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TerrainChunk::Draw(const RenderContext& renderContext) const
|
||||
void TerrainChunk::Draw(const RenderContext& renderContext, DrawPass drawModes) const
|
||||
{
|
||||
const int32 lod = _cachedDrawLOD;
|
||||
const int32 minLod = Math::Max(lod + 1, 0);
|
||||
@@ -140,7 +140,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
|
||||
//drawCall.TerrainData.HeightmapUVScaleBias.W += halfTexelOffset;
|
||||
|
||||
// Submit draw call
|
||||
const DrawPass drawModes = _patch->_terrain->DrawModes & renderContext.View.Pass & drawCall.Material->GetDrawModes();
|
||||
drawModes &= renderContext.View.Pass & drawCall.Material->GetDrawModes();
|
||||
if (drawModes != DrawPass::None)
|
||||
renderContext.List->AddDrawCall(renderContext, drawModes, flags, drawCall, true);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,8 @@ public:
|
||||
/// Draws the chunk (adds the draw call). Must be called after PrepareDraw.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
void Draw(const RenderContext& renderContext) const;
|
||||
/// <param name="drawModes">The drawing modes.</param>
|
||||
void Draw(const RenderContext& renderContext, DrawPass drawModes =DrawPass::Default) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the terrain chunk.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "Engine/Render2D/FontManager.h"
|
||||
#include "Engine/Render2D/FontTextureAtlas.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/Culling/IOcclusionCulling.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
@@ -376,6 +377,14 @@ void TextRender::Draw(RenderContextBatch& renderContextBatch)
|
||||
Matrix world;
|
||||
renderContext.View.GetWorldMatrix(_transform, world);
|
||||
auto drawState = renderContext.Buffers->GetGeometryDrawState(&GetScene()->Rendering, _sceneRenderingKey, this);
|
||||
DrawPass drawModes = DrawModes;
|
||||
if (drawState && renderContextBatch.Buffers->OcclusionCulling && !renderContextBatch.Buffers->OcclusionCulling->IsVisible(_box, *drawState))
|
||||
{
|
||||
// Draw shadows-only (or cull)
|
||||
drawModes &= DrawPass::Depth;
|
||||
if (renderContextBatch.Contexts.Count() == 1 || drawModes == DrawPass::None)
|
||||
return;
|
||||
}
|
||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(drawState, world);
|
||||
|
||||
// Flush buffers
|
||||
@@ -400,7 +409,6 @@ void TextRender::Draw(RenderContextBatch& renderContextBatch)
|
||||
drawCall.InstanceCount = 1;
|
||||
|
||||
// Submit draw calls
|
||||
const DrawPass drawModes = DrawModes & renderContext.View.GetShadowsDrawPassMask(ShadowsMode);
|
||||
for (const auto& e : _drawChunks)
|
||||
{
|
||||
const DrawPass chunkDrawModes = drawModes & e.Material->GetDrawModes();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
|
||||
META_CB_BEGIN(0, OcclusionCullingData)
|
||||
float4x4 ViewProjectionMatrix;
|
||||
META_CB_END
|
||||
|
||||
// Vertex Shader function for Hardware Occlusion Culling queries bounds projection
|
||||
META_VS(true, FEATURE_LEVEL_ES2)
|
||||
float4 VS_HardwareOcclusionCulling(float3 Position : POSITION0) : SV_Position
|
||||
{
|
||||
return mul(float4(Position, 1), ViewProjectionMatrix);
|
||||
}
|
||||
Reference in New Issue
Block a user