// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Math/Half.h"
#include "Engine/Graphics/PostProcessSettings.h"
#include "Engine/Graphics/DynamicBuffer.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "DrawCall.h"
#include "RenderListBuffer.h"
#include "RendererAllocation.h"
#include "RenderSetup.h"
enum class StaticFlags;
class RenderBuffers;
class PostProcessEffect;
class SceneRendering;
class LightWithShadow;
class IPostFxSettingsProvider;
class CubeTexture;
struct RenderContext;
struct RenderContextBatch;
struct RenderLightData
{
Guid ID;
Float3 Position;
float MinRoughness;
Float3 Color;
float ShadowsStrength;
Float3 Direction;
float ShadowsFadeDistance;
float ShadowsNormalOffsetScale;
float ShadowsDepthBias;
float ShadowsSharpness;
float ShadowsDistance;
StaticFlags StaticFlags;
ShadowsCastingMode ShadowsMode;
float IndirectLightingIntensity;
uint8 HasShadow : 1;
uint8 CastVolumetricShadow : 1;
uint8 UseInverseSquaredFalloff : 1;
uint8 IsDirectionalLight : 1;
uint8 IsPointLight : 1;
uint8 IsSpotLight : 1;
uint8 IsSkyLight : 1;
float VolumetricScatteringIntensity;
float ContactShadowsLength;
float ScreenSize;
uint32 ShadowsBufferAddress;
float ShadowsUpdateRate;
float ShadowsUpdateRateAtDistance;
uint32 ShadowFrame;
int32 ShadowsResolution;
bool CanRenderShadow(const RenderView& view) const;
};
struct RenderDirectionalLightData : RenderLightData
{
float Cascade1Spacing;
float Cascade2Spacing;
float Cascade3Spacing;
float Cascade4Spacing;
PartitionMode PartitionMode;
int32 CascadeCount;
RenderDirectionalLightData()
{
Platform::MemoryClear(this, sizeof(RenderDirectionalLightData));
IsDirectionalLight = 1;
}
POD_COPYABLE(RenderDirectionalLightData);
void SetShaderData(ShaderLightData& data, bool useShadow) const;
};
struct RenderLocalLightData : RenderLightData
{
GPUTexture* IESTexture;
float Radius;
float SourceRadius;
bool CanRenderShadow(const RenderView& view) const;
};
struct RenderSpotLightData : RenderLocalLightData
{
Float3 UpVector;
float OuterConeAngle;
float CosOuterCone;
float InvCosConeDifference;
float FallOffExponent;
RenderSpotLightData()
{
Platform::MemoryClear(this, sizeof(RenderSpotLightData));
IsSpotLight = 1;
}
POD_COPYABLE(RenderSpotLightData);
void SetShaderData(ShaderLightData& data, bool useShadow) const;
};
struct RenderPointLightData : RenderLocalLightData
{
float FallOffExponent;
float SourceLength;
RenderPointLightData()
{
Platform::MemoryClear(this, sizeof(RenderPointLightData));
IsPointLight = 1;
}
POD_COPYABLE(RenderPointLightData);
void SetShaderData(ShaderLightData& data, bool useShadow) const;
};
struct RenderSkyLightData : RenderLightData
{
Float3 AdditiveColor;
float Radius;
CubeTexture* Image;
RenderSkyLightData()
{
Platform::MemoryClear(this, sizeof(RenderSkyLightData));
IsSkyLight = 1;
}
POD_COPYABLE(RenderSkyLightData);
void SetShaderData(ShaderLightData& data, bool useShadow) const;
};
struct RenderEnvironmentProbeData
{
GPUTexture* Texture;
Float3 Position;
float Radius;
float Brightness;
int32 SortOrder;
uint32 HashID;
void SetShaderData(ShaderEnvProbeData& data) const;
};
struct RenderDecalData
{
Matrix World;
MaterialBase* Material;
int32 SortOrder;
};
///
/// The draw calls list types.
///
API_ENUM() enum class DrawCallsListType
{
///
/// Hardware depth rendering.
///
Depth,
///
/// GBuffer rendering.
///
GBuffer,
///
/// GBuffer rendering after decals.
///
GBufferNoDecals,
///
/// Transparency rendering.
///
Forward,
///
/// Distortion accumulation rendering.
///
Distortion,
///
/// Motion vectors rendering.
///
MotionVectors,
MAX,
};
///
/// Represents a patch of draw calls that can be submitted to rendering.
///
struct DrawBatch
{
///
/// Draw calls sorting key (shared by the all draw calls withing a patch).
///
uint64 SortKey;
///
/// The first draw call index.
///
uint16 StartIndex;
///
/// A number of draw calls to be submitted at once.
///
uint16 BatchSize;
///
/// The total amount of instances (sum from all draw calls in this batch).
///
uint32 InstanceCount;
bool operator<(const DrawBatch& other) const
{
return SortKey < other.SortKey;
}
};
struct BatchedDrawCall
{
DrawCall DrawCall;
uint16 ObjectsStartIndex = 0; // Index of the instances start in the ObjectsBuffer (set internally).
Array Instances;
};
///
/// Represents a list of draw calls.
///
struct DrawCallsList
{
///
/// The list of draw calls indices to render.
///
RenderListBuffer Indices;
///
/// The list of external draw calls indices to render.
///
RenderListBuffer PreBatchedDrawCalls;
///
/// The draw calls batches (for instancing).
///
Array Batches;
///
/// True if draw calls batches list can be rendered using hardware instancing, otherwise false.
///
bool CanUseInstancing;
void Clear();
bool IsEmpty() const;
};
///
/// Rendering cache container object for the draw calls collecting, sorting and executing.
///
API_CLASS(Sealed) class FLAXENGINE_API RenderList : public ScriptingObject
{
DECLARE_SCRIPTING_TYPE(RenderList);
///
/// Allocates the new renderer list object or reuses already allocated one.
///
/// The cache object.
API_FUNCTION() static RenderList* GetFromPool();
///
/// Frees the list back to the pool.
///
/// The cache.
API_FUNCTION() static void ReturnToPool(RenderList* cache);
///
/// Cleanups the static data cache used to accelerate draw calls sorting. Use it to reduce memory pressure.
///
static void CleanupCache();
public:
///
/// All scenes for rendering.
///
Array Scenes;
///
/// Draw calls list (for all draw passes).
///
RenderListBuffer DrawCalls;
///
/// Draw calls list with pre-batched instances (for all draw passes).
///
RenderListBuffer BatchedDrawCalls;
///
/// The draw calls lists. Each for the separate draw pass.
///
DrawCallsList DrawCallsLists[(int32)DrawCallsListType::MAX];
///
/// The additional draw calls list for Depth drawing into Shadow Projections that use DrawCalls from main render context. This assumes that RenderContextBatch contains main context and shadow projections only.
///
DrawCallsList ShadowDepthDrawCallsList;
///
/// Light pass members - directional lights
///
Array DirectionalLights;
///
/// Light pass members - point lights
///
Array PointLights;
///
/// Light pass members - spot lights
///
Array SpotLights;
///
/// Light pass members - sky lights
///
Array SkyLights;
///
/// Environment probes to use for rendering reflections
///
Array EnvironmentProbes;
///
/// Decals registered for the rendering.
///
Array Decals;
///
/// Local volumetric fog particles registered for the rendering.
///
Array VolumetricFogParticles;
///
/// Sky/skybox renderer proxy to use (only one per frame)
///
ISkyRenderer* Sky;
///
/// Atmospheric fog renderer proxy to use (only one per frame)
///
IAtmosphericFogRenderer* AtmosphericFog;
///
/// Fog renderer proxy to use (only one per frame)
///
IFogRenderer* Fog;
///
/// Post effects to render.
///
Array PostFx;
///
/// The renderer setup for the frame drawing.
///
RenderSetup Setup;
///
/// The post process settings.
///
PostProcessSettings Settings;
struct FLAXENGINE_API BlendableSettings
{
IPostFxSettingsProvider* Provider;
float Weight;
int32 Priority;
float VolumeSizeSqr;
bool operator<(const BlendableSettings& other) const;
};
///
/// The blendable postFx volumes collected during frame draw calls gather pass.
///
Array Blendable;
void AddSettingsBlend(IPostFxSettingsProvider* provider, float weight, int32 priority, float volumeSizeSqr);
///
/// Camera frustum corners in World Space
///
Float3 FrustumCornersWs[8];
///
/// Camera frustum corners in View Space
///
Float3 FrustumCornersVs[8];
///
/// Objects buffer that contains ShaderObjectData for each DrawCall.
///
DynamicTypedBuffer ObjectBuffer;
///
/// Temporary objects buffer that contains ShaderObjectData for each DrawCall reused during scene rendering (eg. by skybox).
///
DynamicTypedBuffer TempObjectBuffer;
private:
DynamicVertexBuffer _instanceBuffer;
public:
///
/// Blends the postprocessing settings into the final options.
///
void BlendSettings();
///
/// Runs the post fx materials pass. Uses input/output buffer to render all materials. Uses temporary render target as a ping pong buffer if required (the same format and description).
///
/// The context.
/// The rendering context.
/// The material postFx location.
/// The custom postFx location.
/// The input and output texture.
void RunPostFxPass(GPUContext* context, RenderContext& renderContext, MaterialPostFxLocation locationA, PostProcessEffectLocation locationB, GPUTexture*& inputOutput);
///
/// Runs the material post fx pass. Uses input and output buffers as a ping pong to render all materials.
///
/// The context.
/// The rendering context.
/// The material postFx location.
/// The input texture.
/// The output texture.
void RunMaterialPostFxPass(GPUContext* context, RenderContext& renderContext, MaterialPostFxLocation location, GPUTexture*& input, GPUTexture*& output);
///
/// Runs the custom post fx pass. Uses input and output buffers as a ping pong to render all effects.
///
/// The context.
/// The rendering context.
/// The custom postFx location.
/// The input texture.
/// The output texture.
void RunCustomPostFxPass(GPUContext* context, RenderContext& renderContext, PostProcessEffectLocation location, GPUTexture*& input, GPUTexture*& output);
///
/// Determines whether any Custom PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module.
///
/// The rendering context.
/// The PostFx location to check (for scripts).
/// True if render any postFx of the given type, otherwise false.
bool HasAnyPostFx(const RenderContext& renderContext, PostProcessEffectLocation postProcess) const;
///
/// Determines whether any Material PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module.
///
/// The rendering context.
/// The PostFx location to check (for materials).
/// True if render any postFx of the given type, otherwise false.
bool HasAnyPostFx(const RenderContext& renderContext, MaterialPostFxLocation materialPostFx) const;
///
/// Determines whether any Custom PostFx or Material PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module.
///
/// The rendering context.
/// The PostFx location to check (for scripts).
/// The PostFx location to check (for materials).
/// True if render any postFx of the given type, otherwise false.
bool HasAnyPostFx(const RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const
{
return HasAnyPostFx(renderContext, postProcess) || HasAnyPostFx(renderContext, materialPostFx);
}
public:
///
/// Init cache for given task
///
/// The rendering context.
void Init(RenderContext& renderContext);
///
/// Clear cached data
///
void Clear();
public:
///
/// Adds the draw call to the draw lists.
///
/// The rendering context.
/// The object draw modes.
/// The object static flags.
/// The draw call data.
/// True if the rendered mesh can receive decals.
/// Object sorting key.
void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals = true, int8 sortOrder = 0);
///
/// Adds the draw call to the draw lists and references it in other render contexts. Performs additional per-context frustum culling.
///
/// The rendering context batch. This assumes that RenderContextBatch contains main context and shadow projections only.
/// The object draw modes.
/// The object static flags.
/// The object shadows casting mode.
/// The object bounds.
/// The draw call data.
/// True if the rendered mesh can receive decals.
/// Object sorting key.
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals = true, int8 sortOrder = 0);
///
/// Writes all draw calls into large objects buffer (used for random-access object data access on a GPU). Can be executed in async.
///
void BuildObjectsBuffer();
///
/// Sorts the collected draw calls list.
///
/// The rendering context.
/// If set to true reverse draw call distance to the view. Results in back to front sorting.
/// The collected draw calls list type.
/// The draw pass (optional).
API_FUNCTION() FORCE_INLINE void SortDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, bool reverseDistance, DrawCallsListType listType, DrawPass pass = DrawPass::All)
{
const bool stable = listType == DrawCallsListType::Forward;
SortDrawCalls(renderContext, reverseDistance, DrawCallsLists[(int32)listType], DrawCalls, pass, stable);
}
///
/// Sorts the collected draw calls list.
///
/// The rendering context.
/// If set to true reverse draw call distance to the view. Results in back to front sorting.
/// The collected draw calls indices list.
/// The collected draw calls list.
/// The draw pass (optional).
/// If set to true draw batches will be additionally sorted to prevent any flickering, otherwise Depth Buffer will smooth out any non-stability in sorting.
void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const RenderListBuffer& drawCalls, DrawPass pass = DrawPass::All, bool stable = false);
///
/// Executes the collected draw calls.
///
/// The rendering context.
/// The collected draw calls list type.
/// The input scene color. It's optional and used in forward/postFx rendering.
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType, GPUTextureView* input = nullptr)
{
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType], this, input);
}
///
/// Executes the collected draw calls.
///
/// The rendering context.
/// The collected draw calls indices list.
/// The input scene color. It's optional and used in forward/postFx rendering.
FORCE_INLINE void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input = nullptr)
{
ExecuteDrawCalls(renderContext, list, this, input);
}
///
/// Executes the collected draw calls.
///
/// The rendering context.
/// The collected draw calls indices list.
/// The collected draw calls list owner.
/// The input scene color. It's optional and used in forward/postFx rendering.
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, RenderList* drawCallsList, GPUTextureView* input);
};
///
/// Represents a single object information for GPU rendering.
///
GPU_CB_STRUCT(ShaderObjectData
{
Float4 Raw[8];
void FLAXENGINE_API Store(const Matrix& worldMatrix, const Matrix& prevWorldMatrix, const Rectangle& lightmapUVsArea, const Float3& geometrySize, float perInstanceRandom = 0.0f, float worldDeterminantSign = 1.0f, float lodDitherFactor = 0.0f);
void FLAXENGINE_API Load(Matrix& worldMatrix, Matrix& prevWorldMatrix, Rectangle& lightmapUVsArea, Float3& geometrySize, float& perInstanceRandom, float& worldDeterminantSign, float& lodDitherFactor) const;
FORCE_INLINE void Store(const DrawCall& drawCall)
{
Store(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor);
}
FORCE_INLINE void Load(DrawCall& drawCall) const
{
Load(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
}
});
///
/// Represents data passed to Vertex Shader used for instanced rendering (per-instance element).
///
PACK_STRUCT(struct ShaderObjectDrawInstanceData
{
uint32 ObjectIndex;
});
struct SurfaceDrawCallHandler
{
static void GetHash(const DrawCall& drawCall, uint32& batchKey);
static bool CanBatch(const DrawCall& a, const DrawCall& b, DrawPass pass);
};