// 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); };