diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index ac7e62abe..9c7110692 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -31,11 +31,11 @@ #define STREAM_TASK_BASE MainThreadTask #endif -#define CHECK_INVALID_BUFFER(buffer) \ - if (buffer->IsValidFor(this) == false) \ +#define CHECK_INVALID_BUFFER(model, buffer) \ + if (buffer->IsValidFor(model) == false) \ { \ - LOG(Warning, "Invalid Model Instance Buffer size {0} for Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), ToString(), MaterialSlots.Count()); \ - buffer->Setup(this); \ + LOG(Warning, "Invalid Model Instance Buffer size {0} for Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), model->ToString(), model->MaterialSlots.Count()); \ + buffer->Setup(model); \ } REGISTER_BINARY_ASSET_ABSTRACT(ModelBase, "FlaxEngine.ModelBase"); @@ -206,14 +206,15 @@ void Model::Draw(const RenderContext& renderContext, MaterialBase* material, con LODs[lodIndex].Draw(renderContext, material, world, flags, receiveDecals); } -void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) +template +FORCE_INLINE void ModelDraw(Model* model, const RenderContext& renderContext, const ContextType& context, const Mesh::DrawInfo& info) { ASSERT(info.Buffer); - if (!CanBeRendered()) + if (!model->CanBeRendered()) return; const auto frame = Engine::FrameCount; const auto modelFrame = info.DrawState->PrevFrame + 1; - CHECK_INVALID_BUFFER(info.Buffer); + CHECK_INVALID_BUFFER(model, info.Buffer); // Select a proper LOD index (model may be culled) int32 lodIndex; @@ -223,7 +224,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) } else { - lodIndex = RenderTools::ComputeModelLOD(this, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); + lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); if (lodIndex == -1) { // Handling model fade-out transition @@ -244,9 +245,9 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) } else { - const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD); + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[prevLOD].Draw(renderContext, info, normalizedProgress); + model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress); } } @@ -254,7 +255,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) } } lodIndex += info.LODBias + renderContext.View.ModelLODBias; - lodIndex = ClampLODIndex(lodIndex); + lodIndex = model->ClampLODIndex(lodIndex); if (renderContext.View.IsSingleFrame) { @@ -287,22 +288,32 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) // Draw if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame) { - LODs[lodIndex].Draw(renderContext, info, 0.0f); + model->LODs.Get()[lodIndex].Draw(context, info, 0.0f); } else if (info.DrawState->PrevLOD == -1) { const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[lodIndex].Draw(renderContext, info, 1.0f - normalizedProgress); + model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress); } else { - const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD); + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[prevLOD].Draw(renderContext, info, normalizedProgress); - LODs[lodIndex].Draw(renderContext, info, normalizedProgress - 1.0f); + model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress); + model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f); } } +void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) +{ + ModelDraw(this, renderContext, renderContext, info); +} + +void Model::Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info) +{ + ModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info); +} + bool Model::SetupLODs(const Span& meshesCountPerLod) { ScopeLock lock(Locker); diff --git a/Source/Engine/Content/Assets/Model.h b/Source/Engine/Content/Assets/Model.h index 9a030f07f..3dd33532f 100644 --- a/Source/Engine/Content/Assets/Model.h +++ b/Source/Engine/Content/Assets/Model.h @@ -190,6 +190,13 @@ public: /// The packed drawing info data. void Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info); + /// + /// Draws the model. + /// + /// The rendering context batch. + /// The packed drawing info data. + void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info); + public: /// /// Setups the model LODs collection including meshes creation. diff --git a/Source/Engine/Content/Assets/ModelBase.h b/Source/Engine/Content/Assets/ModelBase.h index 75b0d83a1..7f7db7893 100644 --- a/Source/Engine/Content/Assets/ModelBase.h +++ b/Source/Engine/Content/Assets/ModelBase.h @@ -18,6 +18,7 @@ #define MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1) class MeshBase; +struct RenderContextBatch; /// /// Base class for asset types that can contain a model resource. diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 05922433d..6369eb0be 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -17,11 +17,11 @@ #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" #include "Engine/Renderer/DrawCall.h" -#define CHECK_INVALID_BUFFER(buffer) \ - if (buffer->IsValidFor(this) == false) \ +#define CHECK_INVALID_BUFFER(model, buffer) \ + if (buffer->IsValidFor(model) == false) \ { \ - LOG(Warning, "Invalid Skinned Model Instance Buffer size {0} for Skinned Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), ToString(), MaterialSlots.Count()); \ - buffer->Setup(this); \ + LOG(Warning, "Invalid Skinned Model Instance Buffer size {0} for Skinned Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), model->ToString(), model->MaterialSlots.Count()); \ + buffer->Setup(model); \ } /// @@ -172,24 +172,25 @@ BoundingBox SkinnedModel::GetBox(int32 lodIndex) const return LODs[lodIndex].GetBox(); } -void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInfo& info) +template +FORCE_INLINE void SkinnedModelDraw(SkinnedModel* model, const RenderContext& renderContext, const ContextType& context, const SkinnedMesh::DrawInfo& info) { ASSERT(info.Buffer); - if (!CanBeRendered()) + if (!model->CanBeRendered()) return; const auto frame = Engine::FrameCount; const auto modelFrame = info.DrawState->PrevFrame + 1; - CHECK_INVALID_BUFFER(info.Buffer); + CHECK_INVALID_BUFFER(model, info.Buffer); // Select a proper LOD index (model may be culled) int32 lodIndex; if (info.ForcedLOD != -1) { - lodIndex = (int32)info.ForcedLOD; + lodIndex = info.ForcedLOD; } else { - lodIndex = RenderTools::ComputeSkinnedModelLOD(this, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); + lodIndex = RenderTools::ComputeSkinnedModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); if (lodIndex == -1) { // Handling model fade-out transition @@ -210,9 +211,9 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf } else { - const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD); + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[prevLOD].Draw(renderContext, info, normalizedProgress); + model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress); } } @@ -220,7 +221,7 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf } } lodIndex += info.LODBias + renderContext.View.ModelLODBias; - lodIndex = ClampLODIndex(lodIndex); + lodIndex = model->ClampLODIndex(lodIndex); if (renderContext.View.IsSingleFrame) { @@ -253,22 +254,32 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf // Draw if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame) { - LODs[lodIndex].Draw(renderContext, info, 0.0f); + model->LODs.Get()[lodIndex].Draw(context, info, 0.0f); } else if (info.DrawState->PrevLOD == -1) { const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[lodIndex].Draw(renderContext, info, 1.0f - normalizedProgress); + model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress); } else { - const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD); + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - LODs[prevLOD].Draw(renderContext, info, normalizedProgress); - LODs[lodIndex].Draw(renderContext, info, normalizedProgress - 1.0f); + model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress); + model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f); } } +void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info) +{ + SkinnedModelDraw(this, renderContext, renderContext, info); +} + +void SkinnedModel::Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info) +{ + SkinnedModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info); +} + bool SkinnedModel::SetupLODs(const Span& meshesCountPerLod) { ScopeLock lock(Locker); diff --git a/Source/Engine/Content/Assets/SkinnedModel.h b/Source/Engine/Content/Assets/SkinnedModel.h index a0cc060c8..9eff5b014 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.h +++ b/Source/Engine/Content/Assets/SkinnedModel.h @@ -210,7 +210,14 @@ public: /// /// The rendering context. /// The packed drawing info data. - void Draw(RenderContext& renderContext, const SkinnedMesh::DrawInfo& info); + void Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info); + + /// + /// Draws the model. + /// + /// The rendering context batch. + /// The packed drawing info data. + void Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info); public: /// diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index ac33b76a9..939c7ff57 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -407,6 +407,9 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons { if (!material || !material->IsSurface() || !IsInitialized()) return; + drawModes &= material->GetDrawModes(); + if (drawModes == DrawPass::None) + return; // Submit draw call DrawCall drawCall; @@ -436,18 +439,70 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const { - // Cache data const auto& entry = info.Buffer->At(_materialSlotIndex); if (!entry.Visible || !IsInitialized()) return; const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex]; + // Select material + MaterialBase* material; + if (entry.Material && entry.Material->IsLoaded()) + material = entry.Material; + else if (slot.Material && slot.Material->IsLoaded()) + material = slot.Material; + else + material = GPUDevice::Instance->GetDefaultMaterial(); + if (!material || !material->IsSurface()) + return; + // Check if skip rendering - const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); - const auto drawModes = static_cast(info.DrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)); + const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode); + const auto drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes()); if (drawModes == DrawPass::None) return; + // Submit draw call + DrawCall drawCall; + drawCall.Geometry.IndexBuffer = _indexBuffer; + drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0]; + drawCall.Geometry.VertexBuffers[1] = _vertexBuffers[1]; + drawCall.Geometry.VertexBuffers[2] = _vertexBuffers[2]; + drawCall.Geometry.VertexBuffersOffsets[0] = 0; + drawCall.Geometry.VertexBuffersOffsets[1] = 0; + drawCall.Geometry.VertexBuffersOffsets[2] = 0; + if (info.VertexColors && info.VertexColors[_lodIndex]) + { + // TODO: cache vertexOffset within the model LOD per-mesh + uint32 vertexOffset = 0; + for (int32 meshIndex = 0; meshIndex < _index; meshIndex++) + vertexOffset += ((Model*)_model)->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount(); + drawCall.Geometry.VertexBuffers[2] = info.VertexColors[_lodIndex]; + drawCall.Geometry.VertexBuffersOffsets[2] = vertexOffset * sizeof(VB2ElementType); + } + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; + drawCall.InstanceCount = 1; + drawCall.Material = material; + drawCall.World = *info.World; + drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; + drawCall.Surface.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr; + drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty; + drawCall.Surface.Skinning = nullptr; + drawCall.Surface.LODDitherFactor = lodDitherFactor; + drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); + drawCall.PerInstanceRandom = info.PerInstanceRandom; + renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals); +} + +void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const +{ + const auto& entry = info.Buffer->At(_materialSlotIndex); + if (!entry.Visible || !IsInitialized()) + return; + const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex]; + // Select material MaterialBase* material; if (entry.Material && entry.Material->IsLoaded()) @@ -491,7 +546,18 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.Surface.LODDitherFactor = lodDitherFactor; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = info.PerInstanceRandom; - renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals); + + // Push draw call to every context + const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode); + const auto materialDrawModes = material->GetDrawModes(); + for (const RenderContext& renderContext : renderContextBatch.Contexts) + { + const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes); + if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds)) + { + renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals); + } + } } bool Mesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 5be9ea018..f65083d97 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -367,6 +367,14 @@ public: /// The LOD transition dither factor. void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const; + /// + /// Draws the mesh. + /// + /// The rendering context batch. + /// The packed drawing info data. + /// The LOD transition dither factor. + void Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const; + public: // [MeshBase] bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override; diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 1816118d3..e8b46d2ad 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -10,6 +10,7 @@ class Task; class ModelBase; +struct RenderContextBatch; /// /// Base class for model resources meshes. diff --git a/Source/Engine/Graphics/Models/ModelLOD.h b/Source/Engine/Graphics/Models/ModelLOD.h index fb3363ecc..1f44ae39f 100644 --- a/Source/Engine/Graphics/Models/ModelLOD.h +++ b/Source/Engine/Graphics/Models/ModelLOD.h @@ -116,7 +116,7 @@ public: { for (int32 i = 0; i < Meshes.Count(); i++) { - Meshes[i].Render(context); + Meshes.Get()[i].Render(context); } } @@ -134,7 +134,7 @@ public: { for (int32 i = 0; i < Meshes.Count(); i++) { - Meshes[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom); + Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom); } } @@ -148,7 +148,21 @@ public: { for (int32 i = 0; i < Meshes.Count(); i++) { - Meshes[i].Draw(renderContext, info, lodDitherFactor); + Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor); + } + } + + /// + /// Draws all the meshes from the model LOD. + /// + /// The rendering context batch. + /// The packed drawing info data. + /// The LOD transition dither factor. + FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info, float lodDitherFactor) const + { + for (int32 i = 0; i < Meshes.Count(); i++) + { + Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor); } } }; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 330c92147..6c1d704c2 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -152,18 +152,75 @@ void SkinnedMesh::Render(GPUContext* context) const void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const { - // Cache data const auto& entry = info.Buffer->At(_materialSlotIndex); if (!entry.Visible || !IsInitialized()) return; const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex]; + // Select material + MaterialBase* material; + if (entry.Material && entry.Material->IsLoaded()) + material = entry.Material; + else if (slot.Material && slot.Material->IsLoaded()) + material = slot.Material; + else + material = GPUDevice::Instance->GetDefaultMaterial(); + if (!material || !material->IsSurface()) + return; + // Check if skip rendering - const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); - const auto drawModes = static_cast(info.DrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)); + const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode); + const auto drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes()); if (drawModes == DrawPass::None) return; + // Submit draw call + DrawCall drawCall; + drawCall.Geometry.IndexBuffer = _indexBuffer; + BlendShapesInstance::MeshInstance* blendShapeMeshInstance; + if (info.BlendShapes && info.BlendShapes->Meshes.TryGet(this, blendShapeMeshInstance) && blendShapeMeshInstance->IsUsed) + { + // Use modified vertex buffer from the blend shapes + if (blendShapeMeshInstance->IsDirty) + { + blendShapeMeshInstance->VertexBuffer.Flush(); + blendShapeMeshInstance->IsDirty = false; + } + drawCall.Geometry.VertexBuffers[0] = blendShapeMeshInstance->VertexBuffer.GetBuffer(); + } + else + { + drawCall.Geometry.VertexBuffers[0] = _vertexBuffer; + } + drawCall.Geometry.VertexBuffers[1] = nullptr; + drawCall.Geometry.VertexBuffers[2] = nullptr; + drawCall.Geometry.VertexBuffersOffsets[0] = 0; + drawCall.Geometry.VertexBuffersOffsets[1] = 0; + drawCall.Geometry.VertexBuffersOffsets[2] = 0; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; + drawCall.InstanceCount = 1; + drawCall.Material = material; + drawCall.World = *info.World; + drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; + drawCall.Surface.Lightmap = nullptr; + drawCall.Surface.LightmapUVsArea = Rectangle::Empty; + drawCall.Surface.Skinning = info.Skinning; + drawCall.Surface.LODDitherFactor = lodDitherFactor; + drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); + drawCall.PerInstanceRandom = info.PerInstanceRandom; + renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals); +} + +void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const +{ + const auto& entry = info.Buffer->At(_materialSlotIndex); + if (!entry.Visible || !IsInitialized()) + return; + const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex]; + // Select material MaterialBase* material; if (entry.Material && entry.Material->IsLoaded()) @@ -212,7 +269,18 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Surface.LODDitherFactor = lodDitherFactor; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = info.PerInstanceRandom; - renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals); + + // Push draw call to every context + const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode); + const auto materialDrawModes = material->GetDrawModes(); + for (const RenderContext& renderContext : renderContextBatch.Contexts) + { + const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes); + if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds)) + { + renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals); + } + } } bool SkinnedMesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index 874bdb48a..c876dd310 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -240,6 +240,14 @@ public: /// The LOD transition dither factor. void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const; + /// + /// Draws the mesh. + /// + /// The rendering context batch. + /// The packed drawing info data. + /// The LOD transition dither factor. + void Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const; + public: // [MeshBase] bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override; diff --git a/Source/Engine/Graphics/Models/SkinnedModelLOD.h b/Source/Engine/Graphics/Models/SkinnedModelLOD.h index 32d2f2ee5..0cdae7f50 100644 --- a/Source/Engine/Graphics/Models/SkinnedModelLOD.h +++ b/Source/Engine/Graphics/Models/SkinnedModelLOD.h @@ -102,7 +102,7 @@ public: { for (int32 i = 0; i < Meshes.Count(); i++) { - Meshes[i].Render(context); + Meshes.Get()[i].Render(context); } } @@ -116,7 +116,21 @@ public: { for (int32 i = 0; i < Meshes.Count(); i++) { - Meshes[i].Draw(renderContext, info, lodDitherFactor); + Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor); + } + } + + /// + /// Draws all the meshes from the model LOD. + /// + /// The rendering context batch. + /// The packed drawing info data. + /// The LOD transition dither factor. + FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info, float lodDitherFactor) const + { + for (int32 i = 0; i < Meshes.Count(); i++) + { + Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor); } } }; diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index df35f809f..6428a3504 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -285,7 +285,7 @@ void AddActorToSceneRendering(SceneRendering* s, Actor* a) } } -void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext, byte category) +void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch, byte category) { // Draw actors (collect draw calls) if ((ActorsSource & ActorsSources::CustomActors) != 0) @@ -296,15 +296,16 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext, byte cate _customActorsScene->Clear(); for (Actor* a : CustomActors) AddActorToSceneRendering(_customActorsScene, a); - _customActorsScene->Draw(renderContext, (SceneRendering::DrawCategory)category); + _customActorsScene->Draw(renderContextBatch, (SceneRendering::DrawCategory)category); } if ((ActorsSource & ActorsSources::Scenes) != 0) { - Level::DrawActors(renderContext, category); + Level::DrawActors(renderContextBatch, category); } // External drawing event - CollectDrawCalls(renderContext); + for (RenderContext& renderContext : renderContextBatch.Contexts) + CollectDrawCalls(renderContext); } void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderContext) @@ -313,14 +314,16 @@ void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderCont // Collect initial draw calls renderContext.View.Pass = DrawPass::GBuffer; - OnCollectDrawCalls(renderContext, SceneRendering::PreRender); + RenderContextBatch renderContextBatch(renderContext); + OnCollectDrawCalls(renderContextBatch, SceneRendering::PreRender); } void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderContext) { // Collect final draw calls renderContext.View.Pass = DrawPass::GBuffer; - OnCollectDrawCalls(renderContext, SceneRendering::PostRender); + RenderContextBatch renderContextBatch(renderContext); + OnCollectDrawCalls(renderContextBatch, SceneRendering::PostRender); PostRender(context, renderContext); } @@ -478,9 +481,22 @@ void MainRenderTask::OnBegin(GPUContext* context) SceneRenderTask::OnBegin(context); } -RenderContext::RenderContext(SceneRenderTask* task) +RenderContext::RenderContext(SceneRenderTask* task) noexcept { Buffers = task->Buffers; Task = task; View = task->View; } + +RenderContextBatch::RenderContextBatch(SceneRenderTask* task) +{ + Buffers = task->Buffers; + Task = task; +} + +RenderContextBatch::RenderContextBatch(const RenderContext& context) +{ + Buffers = context.Buffers; + Task = context.Task; + Contexts.Add(context); +} diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 0c25aca05..9a677e613 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -310,9 +310,9 @@ public: /// /// Calls drawing scene objects. /// - /// The rendering context. + /// The rendering context batch. /// The actors category to draw (see SceneRendering::DrawCategory). - virtual void OnCollectDrawCalls(RenderContext& renderContext, byte category = 0); + virtual void OnCollectDrawCalls(RenderContextBatch& renderContextBatch, byte category = 0); /// /// The action called after scene rendering. Can be used to perform custom pre-rendering or to modify the render view. @@ -424,9 +424,43 @@ API_STRUCT(NoDefault) struct RenderContext /// API_FIELD() RenderView View; - RenderContext() + RenderContext() = default; + RenderContext(SceneRenderTask* task) noexcept; +}; + +/// +/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once). +/// +API_STRUCT(NoDefault) struct RenderContextBatch +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch); + + /// + /// The render buffers. + /// + API_FIELD() RenderBuffers* Buffers = nullptr; + + /// + /// The scene rendering task that is a source of renderable objects (optional). + /// + API_FIELD() SceneRenderTask* Task = nullptr; + + /// + /// The all render views collection for the current rendering (main view, shadow projections, etc.). + /// + API_FIELD() Array Contexts; + + RenderContextBatch() = default; + RenderContextBatch(SceneRenderTask* task); + RenderContextBatch(const RenderContext& context); + + FORCE_INLINE RenderContext& GetMainContext() { + return Contexts.Get()[0]; } - RenderContext(SceneRenderTask* task); + FORCE_INLINE const RenderContext& GetMainContext() const + { + return Contexts.Get()[0]; + } }; diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index e127e6656..777b7595d 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -60,7 +60,7 @@ void RenderView::Prepare(RenderContext& renderContext) PrepareCache(renderContext, width, height, taaJitter); } -void RenderView::PrepareCache(RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, RenderView* mainView) +void RenderView::PrepareCache(const RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, const RenderView* mainView) { // The same format used by the Flax common shaders and postFx materials ViewInfo = Float4(1.0f / Projection.M11, 1.0f / Projection.M22, Far / (Far - Near), (-Far * Near) / (Far - Near) / Far); diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index 69de869e1..d40538089 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -12,7 +12,9 @@ namespace FlaxEngine MaxShadowsQuality = Quality.Ultra; ModelLODDistanceFactor = 1.0f; ModelLODDistanceFactorSqrt = 1.0f; +#pragma warning disable 0612 ShadowModelLODDistanceFactor = 1.0f; +#pragma warning restore 0612 Flags = ViewFlags.DefaultGame; Mode = ViewMode.Default; } diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index bd7a7f331..e9b862219 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -10,6 +10,7 @@ #include "Enums.h" struct RenderContext; +struct RenderContextBatch; struct Viewport; class Camera; class RenderList; @@ -148,13 +149,15 @@ public: /// /// The model LOD bias. Default is 0. Applied to all the objects in the shadow maps render views. Can be used to improve shadows rendering performance or increase quality. + /// [Deprecated on 26.10.2022, expires on 26.10.2024] /// - API_FIELD() int32 ShadowModelLODBias = 0; + API_FIELD() DEPRECATED int32 ShadowModelLODBias = 0; /// /// The model LOD distance scale factor. Default is 1. Applied to all the objects in the shadow maps render views. Higher values increase LODs quality. Can be used to improve shadows rendering performance or increase quality. + /// [Deprecated on 26.10.2022, expires on 26.10.2024] /// - API_FIELD() float ShadowModelLODDistanceFactor = 1.0f; + API_FIELD() DEPRECATED float ShadowModelLODDistanceFactor = 1.0f; /// /// The Temporal Anti-Aliasing jitter frame index. @@ -231,7 +234,7 @@ public: /// The rendering height. /// The temporal jitter for this frame. /// The main rendering viewport. Use null if it's top level view; pass pointer to main view for sub-passes like shadow depths. - void PrepareCache(RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, RenderView* mainView = nullptr); + void PrepareCache(const RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, const RenderView* mainView = nullptr); /// /// Determines whether view is perspective projection or orthographic. diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index c09aa2a99..7d21f96b8 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1218,6 +1218,13 @@ void Actor::Draw(RenderContext& renderContext) { } +void Actor::Draw(RenderContextBatch& renderContextBatch) +{ + // Default impl calls single-context + for (RenderContext& renderContext : renderContextBatch.Contexts) + Draw(renderContext); +} + #if USE_EDITOR void Actor::OnDebugDraw() diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index e6229c0a3..2d78dad69 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -12,6 +12,7 @@ struct RenderView; struct RenderContext; +struct RenderContextBatch; class GPUContext; class MemoryWriteStream; class PhysicsScene; @@ -675,6 +676,12 @@ public: /// The rendering context. virtual void Draw(RenderContext& renderContext); + /// + /// Draws this actor. Called by Scene Rendering service. This call is more optimized than generic Draw (eg. geometry is rendered during all pass types but other actors are drawn only during GBufferFill pass). + /// + /// The rendering context batch (eg, main view and shadow projections). + virtual void Draw(RenderContextBatch& renderContextBatch); + #if USE_EDITOR /// diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 086b85e26..55b19d054 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -707,8 +707,7 @@ void AnimatedModel::Draw(RenderContext& renderContext) renderContext.View.GetWorldMatrix(_transform, world); GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); - const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode)); - if (SkinnedModel && SkinnedModel->IsLoaded() && drawModes != DrawPass::None) + if (SkinnedModel && SkinnedModel->IsLoaded()) { _lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin)); @@ -728,7 +727,9 @@ void AnimatedModel::Draw(RenderContext& renderContext) draw.BlendShapes = &_blendShapes; draw.World = &world; draw.DrawState = &_drawState; - draw.DrawModes = drawModes; + PRAGMA_DISABLE_DEPRECATION_WARNINGS + draw.DrawModes = (DrawPass)(DrawModes & renderContext.View.GetShadowsDrawPassMask(ShadowsMode)); + PRAGMA_ENABLE_DEPRECATION_WARNINGS draw.Bounds = _sphere; draw.Bounds.Center -= renderContext.View.Origin; draw.PerInstanceRandom = GetPerInstanceRandom(); @@ -742,6 +743,58 @@ void AnimatedModel::Draw(RenderContext& renderContext) GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world); } +void AnimatedModel::Draw(RenderContextBatch& renderContextBatch) +{ + if (!SkinnedModel || !SkinnedModel->IsLoaded()) + return; + const RenderContext& renderContext = renderContextBatch.GetMainContext(); + Matrix world; + renderContext.View.GetWorldMatrix(_transform, world); + GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); + _lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin)); + if (_skinningData.IsReady()) + { +#if USE_EDITOR + // Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving + if (!Editor::IsPlayMode) + _drawState.PrevWorld = world; +#endif + + _skinningData.Flush(GPUDevice::Instance->GetMainContext()); + + SkinnedMesh::DrawInfo draw; + draw.Buffer = &Entries; + draw.Skinning = &_skinningData; + draw.BlendShapes = &_blendShapes; + draw.World = &world; + draw.DrawState = &_drawState; + draw.DrawModes = DrawModes; + draw.Bounds = _sphere; + draw.Bounds.Center -= renderContext.View.Origin; + draw.PerInstanceRandom = GetPerInstanceRandom(); + draw.LODBias = LODBias; + draw.ForcedLOD = ForcedLOD; + + PRAGMA_DISABLE_DEPRECATION_WARNINGS + if (ShadowsMode != ShadowsCastingMode::All) + { + // To handle old ShadowsMode option for all meshes we need to call per-context drawing (no batching opportunity) + // TODO: maybe deserialize ShadowsMode into ModelInstanceBuffer entries options? + for (auto& e : renderContextBatch.Contexts) + { + draw.DrawModes = (DrawPass)(DrawModes & e.View.GetShadowsDrawPassMask(ShadowsMode)); + SkinnedModel->Draw(e, draw); + } + } + else + { + SkinnedModel->Draw(renderContextBatch, draw); + } + PRAGMA_ENABLE_DEPRECATION_WARNINGS + } + GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world); +} + #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" @@ -795,7 +848,9 @@ void AnimatedModel::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE(LODBias); SERIALIZE(ForcedLOD); SERIALIZE(DrawModes); + PRAGMA_DISABLE_DEPRECATION_WARNINGS SERIALIZE(ShadowsMode); + PRAGMA_ENABLE_DEPRECATION_WARNINGS SERIALIZE(RootMotionTarget); stream.JKEY("Buffer"); @@ -819,7 +874,9 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m DESERIALIZE(LODBias); DESERIALIZE(ForcedLOD); DESERIALIZE(DrawModes); + PRAGMA_DISABLE_DEPRECATION_WARNINGS DESERIALIZE(ShadowsMode); + PRAGMA_ENABLE_DEPRECATION_WARNINGS DESERIALIZE(RootMotionTarget); Entries.DeserializeIfExists(stream, "Buffer", modifier); @@ -889,7 +946,7 @@ void AnimatedModel::OnDeleteObject() { // Ensure this object is no longer referenced for anim update Animations::RemoveFromUpdate(this); - + ModelInstanceActor::OnDeleteObject(); } diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 16f2740dd..7230ace0f 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -139,9 +139,10 @@ public: /// /// The shadows casting mode. + /// [Deprecated on 26.10.2022, expires on 26.10.2024] /// API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")") - ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; + DEPRECATED ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; /// /// The animation root motion apply target. If not specified the animated model will apply it itself. @@ -358,6 +359,7 @@ public: // [ModelInstanceActor] bool HasContentLoaded() const override; void Draw(RenderContext& renderContext) override; + void Draw(RenderContextBatch& renderContextBatch) override; #if USE_EDITOR void OnDebugDrawSelected() override; BoundingBox GetEditorBox() const override; diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 01e261ff5..e1e529631 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -410,12 +410,6 @@ void SplineModel::Draw(RenderContext& renderContext) continue; const MaterialSlot& slot = model->MaterialSlots[mesh->GetMaterialSlotIndex()]; - // Check if skip rendering - const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); - const auto drawModes = static_cast(actorDrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)); - if (drawModes == DrawPass::None) - continue; - // Select material MaterialBase* material = nullptr; if (entry.Material && entry.Material->IsLoaded()) @@ -427,6 +421,12 @@ void SplineModel::Draw(RenderContext& renderContext) if (!material || !material->IsDeformable()) continue; + // Check if skip rendering + const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); + const auto drawModes = static_cast(actorDrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes()); + if (drawModes == DrawPass::None) + continue; + // Submit draw call mesh->GetDrawCallGeometry(drawCall); drawCall.Material = material; diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 26eb1fc44..4c9cb2cce 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -222,6 +222,31 @@ void StaticModel::UpdateBounds() GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); } +void StaticModel::FlushVertexColors() +{ + for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++) + { + auto& vertexColorsData = _vertexColorsData[lodIndex]; + auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex]; + if (vertexColorsData.HasItems()) + { + const uint32 size = vertexColorsData.Count() * sizeof(Color32); + if (!vertexColorsBuffer) + vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors")); + if (vertexColorsBuffer->GetSize() != size) + { + if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count()))) + return; + } + GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size); + } + else + { + SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer); + } + } +} + bool StaticModel::HasContentLoaded() const { return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded(); @@ -229,8 +254,7 @@ bool StaticModel::HasContentLoaded() const void StaticModel::Draw(RenderContext& renderContext) { - const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass); - if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None) + if (!Model || !Model->IsLoaded() || !Model->CanBeRendered()) return; if (renderContext.View.Pass == DrawPass::GlobalSDF) { @@ -246,32 +270,8 @@ void StaticModel::Draw(RenderContext& renderContext) renderContext.View.GetWorldMatrix(_transform, world); GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); - // Flush vertex colors if need to if (_vertexColorsDirty) - { - for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++) - { - auto& vertexColorsData = _vertexColorsData[lodIndex]; - auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex]; - if (vertexColorsData.HasItems()) - { - const uint32 size = vertexColorsData.Count() * sizeof(Color32); - if (!vertexColorsBuffer) - vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors")); - if (vertexColorsBuffer->GetSize() != size) - { - if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count()))) - return; - } - GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size); - } - else - { - SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer); - } - } - _vertexColorsDirty = false; - } + FlushVertexColors(); #if USE_EDITOR // Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving @@ -286,7 +286,7 @@ void StaticModel::Draw(RenderContext& renderContext) draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); draw.LightmapUVs = &Lightmap.UVsArea; draw.Flags = _staticFlags; - draw.DrawModes = drawModes; + draw.DrawModes = DrawModes; draw.Bounds = _sphere; draw.Bounds.Center -= renderContext.View.Origin; draw.PerInstanceRandom = GetPerInstanceRandom(); @@ -299,6 +299,44 @@ void StaticModel::Draw(RenderContext& renderContext) GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world); } +void StaticModel::Draw(RenderContextBatch& renderContextBatch) +{ + if (!Model || !Model->IsLoaded()) + return; + const RenderContext& renderContext = renderContextBatch.GetMainContext(); + Matrix world; + renderContext.View.GetWorldMatrix(_transform, world); + GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); + + if (_vertexColorsDirty) + FlushVertexColors(); + +#if USE_EDITOR + // Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving + if (!Editor::IsPlayMode) + _drawState.PrevWorld = world; +#endif + + Mesh::DrawInfo draw; + draw.Buffer = &Entries; + draw.World = &world; + draw.DrawState = &_drawState; + draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + draw.LightmapUVs = &Lightmap.UVsArea; + draw.Flags = _staticFlags; + draw.DrawModes = DrawModes; + draw.Bounds = _sphere; + draw.Bounds.Center -= renderContext.View.Origin; + draw.PerInstanceRandom = GetPerInstanceRandom(); + draw.LODBias = _lodBias; + draw.ForcedLOD = _forcedLod; + draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr; + + Model->Draw(renderContextBatch, draw); + + GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world); +} + bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) { bool result = false; diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 7ab5263df..b05a7df9b 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -171,11 +171,13 @@ private: void OnModelLoaded(); void OnModelResidencyChanged(); void UpdateBounds(); + void FlushVertexColors(); public: // [ModelInstanceActor] bool HasContentLoaded() const override; void Draw(RenderContext& renderContext) override; + void Draw(RenderContextBatch& renderContextBatch) override; bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 745f753bf..b5001a730 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -389,7 +389,7 @@ void Level::CallBeginPlay(Actor* obj) } } -void Level::DrawActors(RenderContext& renderContext, byte category) +void Level::DrawActors(RenderContextBatch& renderContextBatch, byte category) { PROFILE_CPU(); @@ -398,7 +398,7 @@ void Level::DrawActors(RenderContext& renderContext, byte category) for (Scene* scene : Scenes) { if (scene->IsActiveInHierarchy()) - scene->Rendering.Draw(renderContext, (SceneRendering::DrawCategory)category); + scene->Rendering.Draw(renderContextBatch, (SceneRendering::DrawCategory)category); } } diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index 561d07150..96202c830 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -15,6 +15,7 @@ class JsonWriter; class Engine; struct RenderView; struct RenderContext; +struct RenderContextBatch; /// /// The scene manager that contains the loaded scenes collection and spawns/deleted actors. @@ -163,9 +164,9 @@ public: /// /// Draws all the actors. /// - /// The rendering context. + /// The rendering context batch. /// The actors category to draw (see SceneRendering::DrawCategory). - static void DrawActors(RenderContext& renderContext, byte category = 0); + static void DrawActors(RenderContextBatch& renderContextBatch, byte category = 0); /// /// Collects all the post fx volumes. diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index 830375753..f0d23a1ff 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -11,6 +11,16 @@ #include "Engine/Profiler/ProfilerCPU.h" #endif +FORCE_INLINE bool FrustumsListCull(const BoundingSphere& bounds, const BoundingFrustum* frustums, int32 frustumsCount) +{ + for (int32 i = 0; i < frustumsCount; i++) + { + if (frustums[i].Intersects(bounds)) + return true; + } + return false; +} + ISceneRenderingListener::~ISceneRenderingListener() { for (SceneRendering* scene : _scenes) @@ -28,60 +38,90 @@ void ISceneRenderingListener::ListenSceneRendering(SceneRendering* scene) } } -void SceneRendering::Draw(RenderContext& renderContext, DrawCategory category) +void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory category) { ScopeLock lock(Locker); - auto& view = renderContext.View; - const BoundingFrustum frustum = view.CullingFrustum; + auto& view = renderContextBatch.GetMainContext().View; const Vector3 origin = view.Origin; - renderContext.List->Scenes.Add(this); + for (auto& renderContext : renderContextBatch.Contexts) + renderContext.List->Scenes.Add(this); auto& list = Actors[(int32)category]; + // Setup frustum data + Array frustumsData; + BoundingFrustum* frustums = &view.CullingFrustum; + int32 frustumsCount = renderContextBatch.Contexts.Count(); + if (frustumsCount != 1) + { + frustumsData.Resize(frustumsCount); + frustums = frustumsData.Get(); + for (int32 i = 0; i < frustumsCount; i++) + frustums[i] = renderContextBatch.Contexts[i].View.CullingFrustum; + } + +#define CHECK_ACTOR ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || FrustumsListCull(e.Bounds, frustums, frustumsCount))) +#define CHECK_ACTOR_SINGLE_FRUSTUM ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || frustums->Intersects(e.Bounds))) +#if SCENE_RENDERING_USE_PROFILER +#define DRAW_ACTOR(mode) PROFILE_CPU_ACTOR(e.Actor); e.Actor->Draw(mode) +#else +#define DRAW_ACTOR(mode) e.Actor->Draw(mode) +#endif + // Draw all visual components if (view.IsOfflinePass) { + // Offline pass with additional static flags culling for (int32 i = 0; i < list.Count(); i++) { auto e = list.Get()[i]; e.Bounds.Center -= origin; - if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)) && e.Actor->GetStaticFlags() & view.StaticFlagsMask) + if (CHECK_ACTOR && e.Actor->GetStaticFlags() & view.StaticFlagsMask) { -#if SCENE_RENDERING_USE_PROFILER - PROFILE_CPU_ACTOR(e.Actor); -#endif - e.Actor->Draw(renderContext); + DRAW_ACTOR(renderContextBatch); + } + } + } + else if (origin.IsZero() && frustumsCount == 1) + { + // Fast path for no origin shifting with a single context + auto& renderContext = renderContextBatch.Contexts[0]; + for (int32 i = 0; i < list.Count(); i++) + { + auto e = list.Get()[i]; + if (CHECK_ACTOR_SINGLE_FRUSTUM) + { + DRAW_ACTOR(renderContext); } } } else if (origin.IsZero()) { + // Fast path for no origin shifting for (int32 i = 0; i < list.Count(); i++) { auto e = list.Get()[i]; - if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds))) + if (CHECK_ACTOR) { -#if SCENE_RENDERING_USE_PROFILER - PROFILE_CPU_ACTOR(e.Actor); -#endif - e.Actor->Draw(renderContext); + DRAW_ACTOR(renderContextBatch); } } } else { + // Generic case for (int32 i = 0; i < list.Count(); i++) { auto e = list.Get()[i]; e.Bounds.Center -= origin; - if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds))) + if (CHECK_ACTOR) { -#if SCENE_RENDERING_USE_PROFILER - PROFILE_CPU_ACTOR(e.Actor); -#endif - e.Actor->Draw(renderContext); + DRAW_ACTOR(renderContextBatch); } } } + +#undef CHECK_ACTOR +#undef DRAW_ACTOR #if USE_EDITOR if (view.Pass & DrawPass::GBuffer && category == SceneDraw) { diff --git a/Source/Engine/Level/Scene/SceneRendering.h b/Source/Engine/Level/Scene/SceneRendering.h index ffa22c5c3..c94149b6f 100644 --- a/Source/Engine/Level/Scene/SceneRendering.h +++ b/Source/Engine/Level/Scene/SceneRendering.h @@ -12,6 +12,7 @@ class SceneRenderTask; class SceneRendering; struct PostProcessSettings; struct RenderContext; +struct RenderContextBatch; struct RenderView; /// @@ -103,9 +104,9 @@ public: /// /// Draws the scene. Performs the optimized actors culling and draw calls submission for the current render pass (defined by the render view). /// - /// The rendering context. + /// The rendering context batch. /// The actors category to draw. - void Draw(RenderContext& renderContext, DrawCategory category = DrawCategory::SceneDraw); + void Draw(RenderContextBatch& renderContextBatch, DrawCategory category = SceneDraw); /// /// Collects the post fx volumes for the given rendering view. diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 5a7e776bc..3c8cdcb5f 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -408,12 +408,15 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa { const auto material = (MaterialBase*)module->Assets[0].Get(); const auto moduleDrawModes = module->Values.Count() > 3 ? (DrawPass)module->Values[3].AsInt : DrawPass::Default; + auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes()); + if (dp == DrawPass::None) + break; drawCall.Material = material; // Submit draw call SpriteRenderer.SetupDrawCall(drawCall); drawCall.InstanceCount = buffer->CPU.Count; - renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); + renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false); break; } @@ -423,6 +426,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa const auto model = (Model*)module->Assets[0].Get(); const auto material = (MaterialBase*)module->Assets[1].Get(); const auto moduleDrawModes = module->Values.Count() > 4 ? (DrawPass)module->Values[4].AsInt : DrawPass::Default; + auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes()); + if (dp == DrawPass::None) + break; drawCall.Material = material; // TODO: model LOD picking for particles? @@ -438,7 +444,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa // Submit draw call mesh.GetDrawCallGeometry(drawCall); drawCall.InstanceCount = buffer->CPU.Count; - renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); + renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false); } break; @@ -450,6 +456,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa break; const auto material = (MaterialBase*)module->Assets[0].Get(); const auto moduleDrawModes = module->Values.Count() > 6 ? (DrawPass)module->Values[6].AsInt : DrawPass::Default; + auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes()); + if (dp == DrawPass::None) + break; drawCall.Material = material; // Node properties @@ -495,7 +504,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex]; drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex]; drawCall.InstanceCount = 1; - renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); + renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false); ribbonModuleIndex++; @@ -810,6 +819,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa { const auto material = (MaterialBase*)module->Assets[0].Get(); const auto moduleDrawModes = module->Values.Count() > 3 ? (DrawPass)module->Values[3].AsInt : DrawPass::Default; + auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes()); drawCall.Material = material; // Submit draw call @@ -817,7 +827,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa drawCall.InstanceCount = 0; drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); - renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); + if (dp != DrawPass::None) + renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false); indirectDrawCallIndex++; break; @@ -828,6 +839,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa const auto model = (Model*)module->Assets[0].Get(); const auto material = (MaterialBase*)module->Assets[1].Get(); const auto moduleDrawModes = module->Values.Count() > 4 ? (DrawPass)module->Values[4].AsInt : DrawPass::Default; + auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes()); drawCall.Material = material; // TODO: model LOD picking for particles? @@ -845,7 +857,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa drawCall.InstanceCount = 0; drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); - renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); + if (dp != DrawPass::None) + renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false); indirectDrawCallIndex++; } diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp index fa79932a4..08ca199f2 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp @@ -63,12 +63,12 @@ void TAA::Dispose() _shader = nullptr; } -bool TAA::NeedMotionVectors(RenderContext& renderContext) +bool TAA::NeedMotionVectors(const RenderContext& renderContext) { return renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing; } -void TAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output) +void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output) { auto context = GPUDevice::Instance->GetMainContext(); diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.h b/Source/Engine/Renderer/AntiAliasing/TAA.h index c90cfbb8a..06951a638 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.h +++ b/Source/Engine/Renderer/AntiAliasing/TAA.h @@ -22,7 +22,7 @@ public: /// /// The rendering context. /// True if need to render motion vectors, otherwise false. - static bool NeedMotionVectors(RenderContext& renderContext); + static bool NeedMotionVectors(const RenderContext& renderContext); /// /// Performs AA pass rendering for the input task. @@ -30,7 +30,7 @@ public: /// The rendering context. /// The input render target. /// The output render target. - void Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output); + void Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output); private: diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 9b541509c..8fb0c891c 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -518,7 +518,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co renderContextTiles.View.Pass = DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas; renderContextTiles.View.Mode = ViewMode::Default; renderContextTiles.View.ModelLODBias += 100000; - renderContextTiles.View.ShadowModelLODBias += 100000; renderContextTiles.View.IsSingleFrame = true; renderContextTiles.View.IsCullingDisabled = true; renderContextTiles.View.Near = 0.0f; diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp index 2d9760387..3565a60b5 100644 --- a/Source/Engine/Renderer/LightPass.cpp +++ b/Source/Engine/Renderer/LightPass.cpp @@ -150,7 +150,7 @@ void LightPass::Dispose() _sphereModel = nullptr; } -void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightBuffer) +void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer) { const float sphereModelScale = 3.0f; @@ -166,6 +166,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB // Cache data auto device = GPUDevice::Instance; auto context = device->GetMainContext(); + auto& renderContext = renderContextBatch.Contexts[0]; auto& view = renderContext.View; auto mainCache = renderContext.List; const auto lightShader = _shader->GetShader(); @@ -219,12 +220,6 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB auto cb1 = lightShader->GetCB(1); context->UpdateCB(cb1, &perFrame); - // Prepare shadows rendering (is will be used) - if (useShadows) - { - ShadowsPass::Instance()->Prepare(renderContext, context); - } - // Bind inputs context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); @@ -250,7 +245,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB auto& light = mainCache->PointLights[lightIndex]; float lightRadius = light.Radius; Float3 lightPosition = light.Position; - const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light); + const bool renderShadow = useShadows && light.ShadowDataIndex != -1; bool useIES = light.IESTexture != nullptr; // Get distance from view center to light center less radius (check if view is inside a sphere) @@ -268,7 +263,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB if (renderShadow) { GET_SHADOW_MASK(); - ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); + ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView); // Bind output context->SetRenderTarget(depthBufferRTV, lightBuffer); @@ -307,7 +302,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB auto& light = mainCache->SpotLights[lightIndex]; float lightRadius = light.Radius; Float3 lightPosition = light.Position; - const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light); + const bool renderShadow = useShadows && light.ShadowDataIndex != -1; bool useIES = light.IESTexture != nullptr; // Get distance from view center to light center less radius (check if view is inside a sphere) @@ -325,7 +320,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB if (renderShadow) { GET_SHADOW_MASK(); - ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); + ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView); // Bind output context->SetRenderTarget(depthBufferRTV, lightBuffer); @@ -362,13 +357,13 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB // Cache data auto& light = mainCache->DirectionalLights[lightIndex]; - const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light); + const bool renderShadow = useShadows && light.ShadowDataIndex != -1; // Check if render shadow if (renderShadow) { GET_SHADOW_MASK(); - ShadowsPass::Instance()->RenderShadow(renderContext, light, lightIndex, shadowMaskView); + ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, lightIndex, shadowMaskView); // Bind output context->SetRenderTarget(depthBufferRTV, lightBuffer); diff --git a/Source/Engine/Renderer/LightPass.h b/Source/Engine/Renderer/LightPass.h index 9ecc52634..e39cb2cc4 100644 --- a/Source/Engine/Renderer/LightPass.h +++ b/Source/Engine/Renderer/LightPass.h @@ -27,13 +27,12 @@ private: PixelFormat _shadowMaskFormat; public: - /// /// Performs the lighting rendering for the input task. /// - /// The rendering context. + /// The rendering context batch. /// The light accumulation buffer (input and output). - void RenderLight(RenderContext& renderContext, GPUTextureView* lightBuffer); + void RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer); private: diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index eb66ef98d..d37451bec 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -416,36 +416,37 @@ void RenderList::Clear() void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals) { - // Mix object mask with material mask - const auto mask = (DrawPass)(drawModes & drawCall.Material->GetDrawModes()); - if (mask == DrawPass::None) - return; +#if ENABLE_ASSERTION_LOW_LAYERS + // Ensure that draw modes are non-empty and in conjunction with material draw modes + auto materialDrawModes = drawCall.Material->GetDrawModes(); + ASSERT_LOW_LAYER(drawModes != DrawPass::None && ((uint32)drawModes & ~(uint32)materialDrawModes) == 0); +#endif // Append draw call data const int32 index = DrawCalls.Count(); DrawCalls.Add(drawCall); // Add draw call to proper draw lists - if (mask & DrawPass::Depth) + if (drawModes & DrawPass::Depth) { DrawCallsLists[(int32)DrawCallsListType::Depth].Indices.Add(index); } - if (mask & (DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas)) + if (drawModes & (DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas)) { if (receivesDecals) DrawCallsLists[(int32)DrawCallsListType::GBuffer].Indices.Add(index); else DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals].Indices.Add(index); } - if (mask & DrawPass::Forward) + if (drawModes & DrawPass::Forward) { DrawCallsLists[(int32)DrawCallsListType::Forward].Indices.Add(index); } - if (mask & DrawPass::Distortion) + if (drawModes & DrawPass::Distortion) { DrawCallsLists[(int32)DrawCallsListType::Distortion].Indices.Add(index); } - if (mask & DrawPass::MotionVectors && (staticFlags & StaticFlags::Transform) == 0) + if (drawModes & DrawPass::MotionVectors && (staticFlags & StaticFlags::Transform) == 0) { DrawCallsLists[(int32)DrawCallsListType::MotionVectors].Indices.Add(index); } @@ -477,7 +478,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD PROFILE_CPU(); const int32 listSize = (int32)list.Indices.Count(); - const Float3 planeNormal = renderContext.View.Direction; + const Float3 planeNormal = renderContext.View.Direction; const float planePoint = -Float3::Dot(planeNormal, renderContext.View.Position); // Peek shared memory diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 7c81eacf4..cb5b78640 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -36,6 +36,7 @@ struct RendererDirectionalLightData StaticFlags StaticFlags; float IndirectLightingIntensity; + int16 ShadowDataIndex = -1; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; @@ -80,6 +81,7 @@ struct RendererSpotLightData ShadowsCastingMode ShadowsMode; StaticFlags StaticFlags; + int16 ShadowDataIndex = -1; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; int8 UseInverseSquaredFalloff : 1; @@ -117,6 +119,7 @@ struct RendererPointLightData ShadowsCastingMode ShadowsMode; StaticFlags StaticFlags; + int16 ShadowDataIndex = -1; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; int8 UseInverseSquaredFalloff : 1; @@ -508,7 +511,7 @@ public: /// The rendering context. /// The PostFx location to check (for scripts). /// True if render any postFx of the given type, otherwise false. - bool HasAnyPostFx(RenderContext& renderContext, PostProcessEffectLocation postProcess) const; + 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. @@ -516,7 +519,7 @@ public: /// The rendering context. /// The PostFx location to check (for materials). /// True if render any postFx of the given type, otherwise false. - bool HasAnyPostFx(RenderContext& renderContext, MaterialPostFxLocation materialPostFx) const; + 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. @@ -525,7 +528,7 @@ public: /// 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(RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const + bool HasAnyPostFx(const RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const { return HasAnyPostFx(renderContext, postProcess) || HasAnyPostFx(renderContext, materialPostFx); } diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 30ff7675a..61dca918a 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -60,7 +60,7 @@ public: RendererService RendererServiceInstance; -void RenderInner(SceneRenderTask* task, RenderContext& renderContext); +void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderContextBatch& renderContextBatch); bool RendererService::Init() { @@ -165,15 +165,18 @@ void Renderer::Render(SceneRenderTask* task) { PROFILE_GPU_CPU_NAMED("Render Frame"); + // Prepare GPU context auto context = GPUDevice::Instance->GetMainContext(); context->ClearState(); context->FlushState(); const Viewport viewport = task->GetViewport(); context->SetViewportAndScissors(viewport); - // Prepare + // Prepare render context RenderContext renderContext(task); renderContext.List = RenderList::GetFromPool(); + RenderContextBatch renderContextBatch(task); + renderContextBatch.Contexts.Add(renderContext); #if USE_EDITOR // Turn on low quality rendering during baking lightmaps (leave more GPU power for baking) @@ -204,7 +207,7 @@ void Renderer::Render(SceneRenderTask* task) // Perform the actual rendering task->OnPreRender(context, renderContext); - RenderInner(task, renderContext); + RenderInner(task, renderContext, renderContextBatch); task->OnPostRender(context, renderContext); #if USE_EDITOR @@ -216,10 +219,11 @@ void Renderer::Render(SceneRenderTask* task) task->View = renderContext.View; // Cleanup - RenderList::ReturnToPool(renderContext.List); + for (const auto& e : renderContextBatch.Contexts) + RenderList::ReturnToPool(e.List); } -bool Renderer::NeedMotionVectors(RenderContext& renderContext) +bool Renderer::NeedMotionVectors(const RenderContext& renderContext) { const int32 screenWidth = renderContext.Buffers->GetWidth(); const int32 screenHeight = renderContext.Buffers->GetHeight(); @@ -255,7 +259,8 @@ void Renderer::DrawSceneDepth(GPUContext* context, SceneRenderTask* task, GPUTex else { // Draw scene actors - Level::DrawActors(renderContext); + RenderContextBatch renderContextBatch(renderContext); + Level::DrawActors(renderContextBatch); } // Sort draw calls @@ -290,7 +295,7 @@ void Renderer::DrawPostFxMaterial(GPUContext* context, const RenderContext& rend context->ResetRenderTarget(); } -void RenderInner(SceneRenderTask* task, RenderContext& renderContext) +void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderContextBatch& renderContextBatch) { auto context = GPUDevice::Instance->GetMainContext(); auto* graphicsSettings = GraphicsSettings::Get(); @@ -316,17 +321,47 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) renderContext.List->PostFx.Add(&postFx); } - // Collect renderable objects and construct draw call list - view.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion; - if (Renderer::NeedMotionVectors(renderContext)) - view.Pass |= DrawPass::MotionVectors; - task->OnCollectDrawCalls(renderContext); + // Build batch of render contexts (main view and shadow projections) + const bool isGBufferDebug = GBufferPass::IsDebugView(renderContext.View.Mode); + { + PROFILE_CPU_NAMED("Collect Draw Calls"); + + view.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion; + if (Renderer::NeedMotionVectors(renderContext)) + view.Pass |= DrawPass::MotionVectors; + renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value + + bool drawShadows = !isGBufferDebug && ((view.Flags & ViewFlags::Shadows) != 0) && ShadowsPass::Instance()->IsReady(); + switch (renderContext.View.Mode) + { + case ViewMode::QuadOverdraw: + case ViewMode::Emissive: + case ViewMode::LightmapUVsDensity: + case ViewMode::GlobalSurfaceAtlas: + case ViewMode::GlobalSDF: + case ViewMode::MaterialComplexity: + drawShadows = false; + break; + } + if (drawShadows) + ShadowsPass::Instance()->SetupShadows(renderContext, renderContextBatch); + + task->OnCollectDrawCalls(renderContextBatch); + } // Sort draw calls - renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBuffer); - renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBufferNoDecals); - renderContext.List->SortDrawCalls(renderContext, true, DrawCallsListType::Forward); - renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Distortion); + { + PROFILE_CPU_NAMED("Sort Draw Calls"); + renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBuffer); + renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBufferNoDecals); + renderContext.List->SortDrawCalls(renderContext, true, DrawCallsListType::Forward); + renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Distortion); + for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++) + { + auto& shadowContext = renderContextBatch.Contexts[i]; + shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth); + } + } // Get the light accumulation buffer auto outputFormat = renderContext.Buffers->GetOutputFormat(); @@ -389,7 +424,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) AmbientOcclusionPass::Instance()->Render(renderContext); // Check if use custom view mode - if (GBufferPass::IsDebugView(renderContext.View.Mode)) + if (isGBufferDebug) { context->ResetRenderTarget(); context->SetRenderTarget(task->GetOutputView()); @@ -400,7 +435,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) } // Render lighting - LightPass::Instance()->RenderLight(renderContext, *lightBuffer); + renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value + LightPass::Instance()->RenderLight(renderContextBatch, *lightBuffer); if (renderContext.View.Flags & ViewFlags::GI) { switch (renderContext.List->Settings.GlobalIllumination.Mode) diff --git a/Source/Engine/Renderer/Renderer.h b/Source/Engine/Renderer/Renderer.h index 2596b26e1..8f98f5335 100644 --- a/Source/Engine/Renderer/Renderer.h +++ b/Source/Engine/Renderer/Renderer.h @@ -38,7 +38,7 @@ public: /// /// The rendering context. /// True if need to render motion vectors, otherwise false. - static bool NeedMotionVectors(RenderContext& renderContext); + static bool NeedMotionVectors(const RenderContext& renderContext); public: diff --git a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp index 4d192a8a0..f809707b2 100644 --- a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp +++ b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp @@ -54,7 +54,7 @@ PACK_STRUCT(struct Data GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas; }); -bool ScreenSpaceReflectionsPass::NeedMotionVectors(RenderContext& renderContext) +bool ScreenSpaceReflectionsPass::NeedMotionVectors(const RenderContext& renderContext) { auto& settings = renderContext.List->Settings.ScreenSpaceReflections; return settings.TemporalEffect && renderContext.View.Flags & ViewFlags::SSR; diff --git a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.h b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.h index 24abf2b62..8d38fd40d 100644 --- a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.h +++ b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.h @@ -41,7 +41,7 @@ public: /// /// The rendering context. /// True if need to render motion vectors, otherwise false. - static bool NeedMotionVectors(RenderContext& renderContext); + static bool NeedMotionVectors(const RenderContext& renderContext); /// /// Perform SSR rendering for the input task (blends reflections to given texture using alpha blending). diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 1a7024d24..2bbd9b2b4 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -189,298 +189,37 @@ void ShadowsPass::updateShadowMapSize() } } -void ShadowsPass::Dispose() +void ShadowsPass::SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext) { - // Base - RendererPass::Dispose(); - - // Cleanup - _psShadowDir.Delete(); - _psShadowPoint.Delete(); - _psShadowSpot.Delete(); - _shader = nullptr; - _sphereModel = nullptr; - SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM); - SAFE_DELETE_GPU_RESOURCE(_shadowMapCube); -} - -bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPointLightData& light) -{ - const Float3 lightPosition = light.Position; - const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position); - - // Fade shadow on distance - const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - - return fade > ZeroTolerance && _supportsShadows; -} - -bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light) -{ - const Float3 lightPosition = light.Position; - const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position); - - // Fade shadow on distance - const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - - return fade > ZeroTolerance && _supportsShadows; -} - -bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light) -{ - return _supportsShadows; -} - -void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context) -{ - ASSERT(IsReady()); - auto& view = renderContext.View; - const auto shader = _shader->GetShader(); - - const auto shadowMapsQuality = Graphics::ShadowMapsQuality; - if (shadowMapsQuality != _currentShadowMapsQuality) - updateShadowMapSize(); - auto shadowsQuality = Graphics::ShadowsQuality; - maxShadowsQuality = Math::Clamp(Math::Min(static_cast(shadowsQuality), static_cast(view.MaxShadowsQuality)), 0, static_cast(Quality::MAX) - 1); + const auto& view = renderContext.View; // Use the current render view to sync model LODs with the shadow maps rendering stage - _shadowContext.LodProxyView = &renderContext.View; + shadowContext.LodProxyView = &renderContext.View; // Prepare properties - auto& shadowView = _shadowContext.View; + auto& shadowView = shadowContext.View; shadowView.Flags = view.Flags; shadowView.StaticFlagsMask = view.StaticFlagsMask; shadowView.RenderLayersMask = view.RenderLayersMask; shadowView.IsOfflinePass = view.IsOfflinePass; - shadowView.ModelLODBias = view.ModelLODBias + view.ShadowModelLODBias; - shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor * view.ShadowModelLODDistanceFactor; + shadowView.ModelLODBias = view.ModelLODBias; + shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor; shadowView.Pass = DrawPass::Depth; shadowView.Origin = view.Origin; - _shadowContext.List = &_shadowCache; + shadowContext.List = RenderList::GetFromPool(); + shadowContext.Buffers = renderContext.Buffers; + shadowContext.Task = renderContext.Task; } -void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererPointLightData& light, GPUTextureView* shadowMask) +void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light) { - const float sphereModelScale = 3.0f; - - PROFILE_GPU_CPU("Shadow"); - - // Cache data - auto device = GPUDevice::Instance; - auto context = device->GetMainContext(); - auto& view = renderContext.View; - auto shader = _shader->GetShader(); - Data sperLight; - float lightRadius = light.Radius; - Float3 lightPosition = light.Position; - Float3 lightDirection = light.Direction; - float dstLightToView = Float3::Distance(lightPosition, view.Position); - - // TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality - int32 shadowQuality = maxShadowsQuality; - - // Fade shadow on distance - const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - - // Set up GPU context and render view - const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; - context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube); - _shadowContext.View.SetUpCube(PointLight_NearPlane, lightRadius, lightPosition); - _shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view); - - // Render depth to all 6 faces of the cube map - for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) - { - // Set up view - _shadowCache.Clear(); - _shadowContext.View.SetFace(faceIndex); - Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[faceIndex]); - - // Set render target - auto rt = _shadowMapCube->View(faceIndex); - context->ResetSR(); - context->SetRenderTarget(rt, static_cast(nullptr)); - context->ClearDepth(rt); - - // Render actors to the shadow map - renderContext.Task->OnCollectDrawCalls(_shadowContext); - _shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth); - _shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth); - } - - // Restore GPU context - context->ResetSR(); - context->ResetRenderTarget(); - const Viewport viewport = renderContext.Task->GetViewport(); - GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); - context->SetViewportAndScissors(viewport); - context->BindSR(0, renderContext.Buffers->GBuffer0); - context->BindSR(1, renderContext.Buffers->GBuffer1); - context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBufferSRV); - context->BindSR(4, renderContext.Buffers->GBuffer3); - - // Setup shader data - GBufferPass::SetInputs(view, sperLight.GBuffer); - light.SetupLightData(&sperLight.Light, true); - sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube; - sperLight.LightShadow.Sharpness = light.ShadowsSharpness; - sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade); - sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube); - sperLight.LightShadow.Bias = light.ShadowsDepthBias; - sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - sperLight.LightShadow.NumCascades = 1; - sperLight.LightShadow.CascadeSplits = Float4::Zero; - Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix); - sperLight.ContactShadowsDistance = light.ShadowsDistance; - sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f; - - // Calculate world view projection matrix for the light sphere - Matrix world, wvp, matrix; - Matrix::Scaling(lightRadius * sphereModelScale, wvp); - Matrix::Translation(lightPosition, matrix); - Matrix::Multiply(wvp, matrix, world); - Matrix::Multiply(world, view.ViewProjection(), wvp); - Matrix::Transpose(wvp, sperLight.WVP); - - // Render shadow in screen space - context->UpdateCB(shader->GetCB(0), &sperLight); - context->BindCB(0, shader->GetCB(0)); - context->BindCB(1, shader->GetCB(1)); - context->BindSR(5, _shadowMapCube->ViewArray()); - context->SetRenderTarget(shadowMask); - context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0))); - _sphereModel->Render(context); - - // Cleanup - context->ResetRenderTarget(); - context->UnBindSR(5); - - // Render volumetric light with shadow - VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow); -} - -void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererSpotLightData& light, GPUTextureView* shadowMask) -{ - const float sphereModelScale = 3.0f; - - PROFILE_GPU_CPU("Shadow"); - - // Cache data - auto device = GPUDevice::Instance; - auto context = device->GetMainContext(); - auto& view = renderContext.View; - auto shader = _shader->GetShader(); - Data sperLight; - float lightRadius = light.Radius; - Float3 lightPosition = light.Position; - Float3 lightDirection = light.Direction; - float dstLightToView = Float3::Distance(lightPosition, view.Position); - - // TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality - int32 shadowQuality = maxShadowsQuality; - - // Fade shadow on distance - const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - - // Set up GPU context and render view - const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; - context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube); - _shadowContext.View.SetProjector(SpotLight_NearPlane, lightRadius, lightPosition, lightDirection, light.UpVector, light.OuterConeAngle * 2.0f); - _shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view); - - // Render depth to all 1 face of the cube map - const int32 cubeFaceIndex = 0; - { - // Set up view - _shadowCache.Clear(); - Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[cubeFaceIndex]); - - // Set render target - auto rt = _shadowMapCube->View(cubeFaceIndex); - context->ResetSR(); - context->SetRenderTarget(rt, static_cast(nullptr)); - context->ClearDepth(rt); - - // Render actors to the shadow map - renderContext.Task->OnCollectDrawCalls(_shadowContext); - _shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth); - _shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth); - } - - // Restore GPU context - context->ResetSR(); - context->ResetRenderTarget(); - const Viewport viewport = renderContext.Task->GetViewport(); - GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); - context->SetViewportAndScissors(viewport); - context->BindSR(0, renderContext.Buffers->GBuffer0); - context->BindSR(1, renderContext.Buffers->GBuffer1); - context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBufferSRV); - context->BindSR(4, renderContext.Buffers->GBuffer3); - - // Setup shader data - GBufferPass::SetInputs(view, sperLight.GBuffer); - light.SetupLightData(&sperLight.Light, true); - sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube; - sperLight.LightShadow.Sharpness = light.ShadowsSharpness; - sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade); - sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube); - sperLight.LightShadow.Bias = light.ShadowsDepthBias; - sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - sperLight.LightShadow.NumCascades = 1; - sperLight.LightShadow.CascadeSplits = Float4::Zero; - Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix); - sperLight.ContactShadowsDistance = light.ShadowsDistance; - sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f; - - // Calculate world view projection matrix for the light sphere - Matrix world, wvp, matrix; - Matrix::Scaling(lightRadius * sphereModelScale, wvp); - Matrix::Translation(lightPosition, matrix); - Matrix::Multiply(wvp, matrix, world); - Matrix::Multiply(world, view.ViewProjection(), wvp); - Matrix::Transpose(wvp, sperLight.WVP); - - // Render shadow in screen space - context->UpdateCB(shader->GetCB(0), &sperLight); - context->BindCB(0, shader->GetCB(0)); - context->BindCB(1, shader->GetCB(1)); - context->BindSR(5, _shadowMapCube->View(cubeFaceIndex)); - context->SetRenderTarget(shadowMask); - context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0))); - _sphereModel->Render(context); - - // Cleanup - context->ResetRenderTarget(); - context->UnBindSR(5); - - // Render volumetric light with shadow - VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(cubeFaceIndex), sperLight.LightShadow); -} - -void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask) -{ - PROFILE_GPU_CPU("Shadow"); - - // Cache data - auto device = GPUDevice::Instance; - auto context = device->GetMainContext(); - auto& view = renderContext.View; + const RenderView& view = renderContext.View; auto mainCache = renderContext.List; - auto shader = _shader->GetShader(); - Data sperLight; Float3 lightDirection = light.Direction; float shadowsDistance = Math::Min(view.Far, light.ShadowsDistance); int32 csmCount = Math::Clamp(light.CascadeCount, 0, MAX_CSM_CASCADES); bool blendCSM = Graphics::AllowCSMBlending; + const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM; #if USE_EDITOR if (IsRunningRadiancePass) blendCSM = false; @@ -561,9 +300,7 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional // Convert distance splits to ratios cascade in the range [0, 1] for (int32 i = 0; i < MAX_CSM_CASCADES; i++) - { cascadeSplits[i] = (cascadeSplits[i] - cameraNear) / cameraRange; - } } // Select best Up vector @@ -585,10 +322,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional Float3 frustumCorners[8]; Matrix shadowView, shadowProjection, shadowVP; - // Set up GPU context and render view - const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM; - context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM); - _shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view); + // Init shadow data + light.ShadowDataIndex = _shadowData.Count(); + auto& shadowData = _shadowData.AddOne(); + shadowData.ContextIndex = renderContextBatch.Contexts.Count(); + shadowData.ContextCount = csmCount; + shadowData.BlendCSM = blendCSM; + renderContextBatch.Contexts.AddDefault(shadowData.ContextCount); // Create the different view and projection matrices for each split float splitMinRatio = 0; @@ -684,26 +424,213 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional 0.5f, 0.5f, 0.0f, 1.0f); Matrix m; Matrix::Multiply(shadowVP, T, m); - Matrix::Transpose(m, sperLight.LightShadow.ShadowVP[cascadeIndex]); + Matrix::Transpose(m, shadowData.Constants.ShadowVP[cascadeIndex]); } - // Set up view and cache - _shadowCache.Clear(); - _shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position; - _shadowContext.View.Direction = lightDirection; - _shadowContext.View.SetUp(shadowView, shadowProjection); - _shadowContext.View.CullingFrustum.SetMatrix(cullingVP); + // Setup context for cascade + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex]; + SetupRenderContext(renderContext, shadowContext); + shadowContext.List->Clear(); + shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position; + shadowContext.View.Direction = lightDirection; + shadowContext.View.SetUp(shadowView, shadowProjection); + shadowContext.View.CullingFrustum.SetMatrix(cullingVP); + shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view); + } - // Set render target - const auto rt = _shadowMapCSM->View(cascadeIndex); + // Setup constant buffer data + shadowData.Constants.ShadowMapSize = shadowMapsSizeCSM; + shadowData.Constants.Sharpness = light.ShadowsSharpness; + shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength); + shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM); + shadowData.Constants.Bias = light.ShadowsDepthBias; + shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + shadowData.Constants.NumCascades = csmCount; + shadowData.Constants.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange; +} + +void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererPointLightData& light) +{ + // Init shadow data + light.ShadowDataIndex = _shadowData.Count(); + auto& shadowData = _shadowData.AddOne(); + shadowData.ContextIndex = renderContextBatch.Contexts.Count(); + shadowData.ContextCount = 6; + renderContextBatch.Contexts.AddDefault(shadowData.ContextCount); + + const auto& view = renderContext.View; + const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; + + // Fade shadow on distance + const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + const float dstLightToView = Float3::Distance(light.Position, view.Position); + const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); + + // Render depth to all 6 faces of the cube map + for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) + { + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex]; + SetupRenderContext(renderContext, shadowContext); + shadowContext.List->Clear(); + shadowContext.View.SetUpCube(PointLight_NearPlane, light.Radius, light.Position); + shadowContext.View.SetFace(faceIndex); + shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view); + Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]); + } + + // Setup constant buffer data + shadowData.Constants.ShadowMapSize = shadowMapsSizeCube; + shadowData.Constants.Sharpness = light.ShadowsSharpness; + shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade); + shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube); + shadowData.Constants.Bias = light.ShadowsDepthBias; + shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + shadowData.Constants.NumCascades = 1; + shadowData.Constants.CascadeSplits = Float4::Zero; +} + +void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererSpotLightData& light) +{ + // Init shadow data + light.ShadowDataIndex = _shadowData.Count(); + auto& shadowData = _shadowData.AddOne(); + shadowData.ContextIndex = renderContextBatch.Contexts.Count(); + shadowData.ContextCount = 1; + renderContextBatch.Contexts.AddDefault(shadowData.ContextCount); + + const auto& view = renderContext.View; + const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; + + // Fade shadow on distance + const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + const float dstLightToView = Float3::Distance(light.Position, view.Position); + const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); + + // Render depth to all 1 face of the cube map + constexpr int32 faceIndex = 0; + { + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex]; + SetupRenderContext(renderContext, shadowContext); + shadowContext.List->Clear(); + shadowContext.View.SetProjector(SpotLight_NearPlane, light.Radius, light.Position, light.Direction, light.UpVector, light.OuterConeAngle * 2.0f); + shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view); + Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]); + } + + // Setup constant buffer data + shadowData.Constants.ShadowMapSize = shadowMapsSizeCube; + shadowData.Constants.Sharpness = light.ShadowsSharpness; + shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade); + shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube); + shadowData.Constants.Bias = light.ShadowsDepthBias; + shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + shadowData.Constants.NumCascades = 1; + shadowData.Constants.CascadeSplits = Float4::Zero; +} + +void ShadowsPass::Dispose() +{ + // Base + RendererPass::Dispose(); + + // Cleanup + _psShadowDir.Delete(); + _psShadowPoint.Delete(); + _psShadowSpot.Delete(); + _shader = nullptr; + _sphereModel = nullptr; + SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM); + SAFE_DELETE_GPU_RESOURCE(_shadowMapCube); +} + +void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch) +{ + PROFILE_CPU(); + _shadowData.Clear(); + auto& view = renderContext.View; + + // Update shadow map + const auto shadowMapsQuality = Graphics::ShadowMapsQuality; + if (shadowMapsQuality != _currentShadowMapsQuality) + updateShadowMapSize(); + auto shadowsQuality = Graphics::ShadowsQuality; + maxShadowsQuality = Math::Clamp(Math::Min(static_cast(shadowsQuality), static_cast(view.MaxShadowsQuality)), 0, static_cast(Quality::MAX) - 1); + + // Create shadow projections for lights + for (auto& light : renderContext.List->DirectionalLights) + { + if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light)) + SetupLight(renderContext, renderContextBatch, light); + } + for (auto& light : renderContext.List->PointLights) + { + if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light)) + SetupLight(renderContext, renderContextBatch, light); + } + for (auto& light : renderContext.List->SpotLights) + { + if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light)) + SetupLight(renderContext, renderContextBatch, light); + } +} + +bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererPointLightData& light) +{ + const Float3 lightPosition = light.Position; + const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position); + + // Fade shadow on distance + const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); + + return fade > ZeroTolerance && _supportsShadows; +} + +bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererSpotLightData& light) +{ + const Float3 lightPosition = light.Position; + const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position); + + // Fade shadow on distance + const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); + const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); + + return fade > ZeroTolerance && _supportsShadows; +} + +bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererDirectionalLightData& light) +{ + return _supportsShadows; +} + +void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererPointLightData& light, GPUTextureView* shadowMask) +{ + if (light.ShadowDataIndex == -1) + return; + PROFILE_GPU_CPU("Shadow"); + GPUContext* context = GPUDevice::Instance->GetMainContext(); + RenderContext& renderContext = renderContextBatch.Contexts[0]; + ShadowData& shadowData = _shadowData[light.ShadowDataIndex]; + const float sphereModelScale = 3.0f; + auto& view = renderContext.View; + auto shader = _shader->GetShader(); + + // TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality + int32 shadowQuality = maxShadowsQuality; + + // Set up GPU context and render view + const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; + context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube); + + // Render depth to all 6 faces of the cube map + for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) + { + auto rt = _shadowMapCube->View(faceIndex); context->ResetSR(); context->SetRenderTarget(rt, static_cast(nullptr)); context->ClearDepth(rt); - - // Render actors to the shadow map - renderContext.Task->OnCollectDrawCalls(_shadowContext); - _shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth); - _shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth); + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex]; + shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth); } // Restore GPU context @@ -720,27 +647,168 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data + Data sperLight; GBufferPass::SetInputs(view, sperLight.GBuffer); light.SetupLightData(&sperLight.Light, true); - sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCSM; - sperLight.LightShadow.Sharpness = light.ShadowsSharpness; - sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength); - sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM); - sperLight.LightShadow.Bias = light.ShadowsDepthBias; - sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); - sperLight.LightShadow.NumCascades = csmCount; - sperLight.LightShadow.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange; + sperLight.LightShadow = shadowData.Constants; Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix); sperLight.ContactShadowsDistance = light.ShadowsDistance; sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f; + // Calculate world view projection matrix for the light sphere + Matrix world, wvp, matrix; + Matrix::Scaling(light.Radius * sphereModelScale, wvp); + Matrix::Translation(light.Position, matrix); + Matrix::Multiply(wvp, matrix, world); + Matrix::Multiply(world, view.ViewProjection(), wvp); + Matrix::Transpose(wvp, sperLight.WVP); + // Render shadow in screen space context->UpdateCB(shader->GetCB(0), &sperLight); context->BindCB(0, shader->GetCB(0)); context->BindCB(1, shader->GetCB(1)); + context->BindSR(5, _shadowMapCube->ViewArray()); + context->SetRenderTarget(shadowMask); + context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0))); + _sphereModel->Render(context); + + // Cleanup + context->ResetRenderTarget(); + context->UnBindSR(5); + + // Render volumetric light with shadow + VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow); +} + +void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererSpotLightData& light, GPUTextureView* shadowMask) +{ + if (light.ShadowDataIndex == -1) + return; + PROFILE_GPU_CPU("Shadow"); + GPUContext* context = GPUDevice::Instance->GetMainContext(); + RenderContext& renderContext = renderContextBatch.Contexts[0]; + ShadowData& shadowData = _shadowData[light.ShadowDataIndex]; + const float sphereModelScale = 3.0f; + auto& view = renderContext.View; + auto shader = _shader->GetShader(); + + // TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality + int32 shadowQuality = maxShadowsQuality; + + // Set up GPU context and render view + const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube; + context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube); + + // Render depth to all 1 face of the cube map + constexpr int32 faceIndex = 0; + { + auto rt = _shadowMapCube->View(faceIndex); + context->ResetSR(); + context->SetRenderTarget(rt, static_cast(nullptr)); + context->ClearDepth(rt); + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex]; + shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth); + } + + // Restore GPU context + context->ResetSR(); + context->ResetRenderTarget(); + const Viewport viewport = renderContext.Task->GetViewport(); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + context->SetViewportAndScissors(viewport); + context->BindSR(0, renderContext.Buffers->GBuffer0); + context->BindSR(1, renderContext.Buffers->GBuffer1); + context->BindSR(2, renderContext.Buffers->GBuffer2); + context->BindSR(3, depthBufferSRV); + context->BindSR(4, renderContext.Buffers->GBuffer3); + + // Setup shader data + Data sperLight; + GBufferPass::SetInputs(view, sperLight.GBuffer); + light.SetupLightData(&sperLight.Light, true); + sperLight.LightShadow = shadowData.Constants; + Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix); + sperLight.ContactShadowsDistance = light.ShadowsDistance; + sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f; + + // Calculate world view projection matrix for the light sphere + Matrix world, wvp, matrix; + Matrix::Scaling(light.Radius * sphereModelScale, wvp); + Matrix::Translation(light.Position, matrix); + Matrix::Multiply(wvp, matrix, world); + Matrix::Multiply(world, view.ViewProjection(), wvp); + Matrix::Transpose(wvp, sperLight.WVP); + + // Render shadow in screen space + context->UpdateCB(shader->GetCB(0), &sperLight); + context->BindCB(0, shader->GetCB(0)); + context->BindCB(1, shader->GetCB(1)); + context->BindSR(5, _shadowMapCube->View(faceIndex)); + context->SetRenderTarget(shadowMask); + context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0))); + _sphereModel->Render(context); + + // Cleanup + context->ResetRenderTarget(); + context->UnBindSR(5); + + // Render volumetric light with shadow + VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(faceIndex), sperLight.LightShadow); +} + +void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask) +{ + if (light.ShadowDataIndex == -1) + return; + PROFILE_GPU_CPU("Shadow"); + GPUContext* context = GPUDevice::Instance->GetMainContext(); + RenderContext& renderContext = renderContextBatch.Contexts[0]; + ShadowData& shadowData = _shadowData[light.ShadowDataIndex]; + const float shadowMapsSizeCSM = (float)_shadowMapsSizeCSM; + context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM); + + // Render shadow map for each projection + for (int32 cascadeIndex = 0; cascadeIndex < shadowData.ContextCount; cascadeIndex++) + { + const auto rt = _shadowMapCSM->View(cascadeIndex); + context->ResetSR(); + context->SetRenderTarget(rt, static_cast(nullptr)); + context->ClearDepth(rt); + auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex]; + shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth); + } + + // Restore GPU context + context->ResetSR(); + context->ResetRenderTarget(); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + context->SetViewportAndScissors(renderContext.Task->GetViewport()); + context->BindSR(0, renderContext.Buffers->GBuffer0); + context->BindSR(1, renderContext.Buffers->GBuffer1); + context->BindSR(2, renderContext.Buffers->GBuffer2); + context->BindSR(3, depthBufferSRV); + context->BindSR(4, renderContext.Buffers->GBuffer3); + + // Setup shader data + Data sperLight; + auto& view = renderContext.View; + GBufferPass::SetInputs(view, sperLight.GBuffer); + light.SetupLightData(&sperLight.Light, true); + sperLight.LightShadow = shadowData.Constants; + Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix); + sperLight.ContactShadowsDistance = light.ShadowsDistance; + sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f; + + // Render shadow in screen space + auto shader = _shader->GetShader(); + context->UpdateCB(shader->GetCB(0), &sperLight); + context->BindCB(0, shader->GetCB(0)); + context->BindCB(1, shader->GetCB(1)); context->BindSR(5, _shadowMapCSM->ViewArray()); context->SetRenderTarget(shadowMask); - context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast(Quality::MAX) * blendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0))); + context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast(Quality::MAX) * shadowData.BlendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0))); context->DrawFullscreenTriangle(); // Cleanup diff --git a/Source/Engine/Renderer/ShadowsPass.h b/Source/Engine/Renderer/ShadowsPass.h index c17757fb4..3506583b4 100644 --- a/Source/Engine/Renderer/ShadowsPass.h +++ b/Source/Engine/Renderer/ShadowsPass.h @@ -15,7 +15,7 @@ #define SHADOWS_PASS_SS_RR_FORMAT PixelFormat::R11G11B10_Float template -bool CanRenderShadow(RenderView& view, const T& light) +bool CanRenderShadow(const RenderView& view, const T& light) { bool result = false; switch ((ShadowsCastingMode)light.ShadowsMode) @@ -42,6 +42,14 @@ class ShadowsPass : public RendererPass { private: + struct ShadowData + { + int32 ContextIndex; + int32 ContextCount; + bool BlendCSM; + LightShadowData Constants; + }; + // Shader stuff AssetReference _shader; GPUPipelineStatePermutationsPs(Quality::MAX) * 2 * 2> _psShadowDir; @@ -57,9 +65,8 @@ private: Quality _currentShadowMapsQuality; // Shadow map rendering stuff - RenderContext _shadowContext; - RenderList _shadowCache; AssetReference _sphereModel; + Array _shadowData; // Cached state for the current frame rendering (setup via Prepare) int32 maxShadowsQuality; @@ -87,6 +94,10 @@ public: LightShadowData LastDirLight; public: + /// + /// Setups the shadows rendering for batched scene drawing. Checks which lights will cast a shadow. + /// + void SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch); /// /// Determines whether can render shadow for the specified light. @@ -94,7 +105,7 @@ public: /// The rendering context. /// The light. /// true if can render shadow for the specified light; otherwise, false. - bool CanRenderShadow(RenderContext& renderContext, const RendererPointLightData& light); + bool CanRenderShadow(const RenderContext& renderContext, const RendererPointLightData& light); /// /// Determines whether can render shadow for the specified light. @@ -102,7 +113,7 @@ public: /// The rendering context. /// The light. /// true if can render shadow for the specified light; otherwise, false. - bool CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light); + bool CanRenderShadow(const RenderContext& renderContext, const RendererSpotLightData& light); /// /// Determines whether can render shadow for the specified light. @@ -110,43 +121,40 @@ public: /// The rendering context. /// The light. /// true if can render shadow for the specified light; otherwise, false. - bool CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light); - - /// - /// Prepares the shadows rendering. Called by the light pass once per frame. - /// - /// The rendering context. - /// The GPU command context. - void Prepare(RenderContext& renderContext, GPUContext* context); + bool CanRenderShadow(const RenderContext& renderContext, const RendererDirectionalLightData& light); /// /// Renders the shadow mask for the given light. /// - /// The rendering context. + /// The rendering context batch. /// The light. /// The shadow mask (output). - void RenderShadow(RenderContext& renderContext, RendererPointLightData& light, GPUTextureView* shadowMask); + void RenderShadow(RenderContextBatch& renderContextBatch, RendererPointLightData& light, GPUTextureView* shadowMask); /// /// Renders the shadow mask for the given light. /// - /// The rendering context. + /// The rendering context batch. /// The light. /// The shadow mask (output). - void RenderShadow(RenderContext& renderContext, RendererSpotLightData& light, GPUTextureView* shadowMask); + void RenderShadow(RenderContextBatch& renderContextBatch, RendererSpotLightData& light, GPUTextureView* shadowMask); /// /// Renders the shadow mask for the given light. /// - /// The rendering context. + /// The rendering context batch. /// The light. /// The light index. /// The shadow mask (output). - void RenderShadow(RenderContext& renderContext, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask); + void RenderShadow(RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask); private: void updateShadowMapSize(); + void SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext); + void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light); + void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererPointLightData& light); + void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererSpotLightData& light); #if COMPILE_WITH_DEV_ENV void OnShaderReloading(Asset* obj) diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index b1e1fb2c6..ac4674d3f 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -121,8 +121,9 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const //drawCall.TerrainData.HeightmapUVScaleBias.W += halfTexelOffset; // Submit draw call - auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass); - renderContext.List->AddDrawCall(drawModes, flags, drawCall, true); + auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass & (uint32)drawCall.Material->GetDrawModes()); + if (drawModes != DrawPass::None) + renderContext.List->AddDrawCall(drawModes, flags, drawCall, true); } void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* material, int32 lodIndex) const @@ -176,8 +177,9 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi //drawCall.TerrainData.HeightmapUVScaleBias.W += halfTexelOffset; // Submit draw call - auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass); - renderContext.List->AddDrawCall(drawModes, flags, drawCall, true); + auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass & (uint32)drawCall.Material->GetDrawModes()); + if (drawModes != DrawPass::None) + renderContext.List->AddDrawCall(drawModes, flags, drawCall, true); } bool TerrainChunk::Intersects(const Ray& ray, Real& distance) diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 02ccb3f07..e35542dae 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -344,14 +344,12 @@ void TextRender::Draw(RenderContext& renderContext) if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas) return; // TODO: Text rendering to Global Surface Atlas if (_isDirty) - { UpdateLayout(); - } Matrix world; renderContext.View.GetWorldMatrix(_transform, world); GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); - const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode)); + const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode)); if (_vb0.Data.Count() > 0 && drawModes != DrawPass::None) { #if USE_EDITOR @@ -394,6 +392,8 @@ void TextRender::Draw(RenderContext& renderContext) // Submit draw calls for (const auto& e : _drawChunks) { + if ((drawModes & e.Material->GetDrawModes()) == 0) + continue; drawCall.Draw.IndicesCount = e.IndicesCount; drawCall.Draw.StartIndex = e.StartIndex; drawCall.Material = e.Material;