diff --git a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp index 980fc0282..9b2654f21 100644 --- a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp @@ -34,8 +34,7 @@ void DecalMaterialShader::Bind(BindParameters& params) ASSERT_LOW_LAYER(cb.Length() >= sizeof(DecalMaterialShaderData)); auto materialData = reinterpret_cast(cb.Get()); cb = Span(cb.Get() + sizeof(DecalMaterialShaderData), cb.Length() - sizeof(DecalMaterialShaderData)); - int32 srv = 0; - const bool isCameraInside = OrientedBoundingBox(Vector3::Half, params.FirstDrawCall->World).Contains(view.Position) == ContainmentType::Contains; + const bool isCameraInside = OrientedBoundingBox(Vector3::Half, drawCall.World).Contains(view.Position) == ContainmentType::Contains; // Setup parameters MaterialParameter::BindMeta bindMeta; diff --git a/Source/Engine/Level/Actors/Decal.cpp b/Source/Engine/Level/Actors/Decal.cpp index 0d08bc933..dbec91b57 100644 --- a/Source/Engine/Level/Actors/Decal.cpp +++ b/Source/Engine/Level/Actors/Decal.cpp @@ -69,20 +69,26 @@ void Decal::OnLayerChanged() void Decal::Draw(RenderContext& renderContext) { + MaterialBase* material = Material; if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Decals) && EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) && - Material && - Material->IsLoaded() && - Material->IsDecal()) + material && + material->IsLoaded() && + material->IsDecal()) { + // Check if decal is being culled const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(_sphere.Center - renderContext.View.Origin, (float)_sphere.Radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt; - - // Check if decal is being culled if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared) return; - renderContext.List->Decals.Add(this); + RenderDecalData data; + Transform transform = GetTransform(); + transform.Scale *= _size; + renderContext.View.GetWorldMatrix(transform, data.World); + data.SortOrder = SortOrder; + data.Material = material; + renderContext.List->Decals.Add(data); } } diff --git a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp index 2df397df5..1d7534485 100644 --- a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp +++ b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp @@ -130,17 +130,14 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon MaterialBase::BindParameters bindParams(context, renderContext, drawCall); bindParams.BindViewData(); drawCall.WorldDeterminantSign = 1.0f; + drawCall.PerInstanceRandom = 0.0f; context->SetRenderTarget(lightBuffer); for (int32 i = 0; i < decals.Count(); i++) { - const auto decal = decals[i]; - ASSERT(decal && decal->Material); - Transform transform = decal->GetTransform(); - transform.Scale *= decal->GetSize(); - renderContext.View.GetWorldMatrix(transform, drawCall.World); + const RenderDecalData& decal = decals.Get()[i]; + drawCall.World = decal.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.Material = decal->Material; - drawCall.PerInstanceRandom = decal->GetPerInstanceRandom(); + drawCall.Material = decal.Material; decalsWrapper.Bind(bindParams); boxModel->Render(context); } diff --git a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp index 20e06550a..2674c66bd 100644 --- a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp +++ b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp @@ -49,7 +49,8 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context, context->BindUA(1, overdrawTexture->View()); context->BindUA(2, liveCountTexture->View()); DrawCall drawCall; - drawCall.PerInstanceRandom = 1.0f; + drawCall.WorldDeterminantSign = 1.0f; + drawCall.PerInstanceRandom = 0.0f; MaterialBase::BindParameters bindParams(context, renderContext, drawCall); bindParams.BindViewData(); renderContext.View.Pass = DrawPass::QuadOverdraw; @@ -62,13 +63,8 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context, // Draw decals for (int32 i = 0; i < renderContext.List->Decals.Count(); i++) { - const auto decal = renderContext.List->Decals[i]; - ASSERT(decal && decal->Material); - Transform transform = decal->GetTransform(); - transform.Scale *= decal->GetSize(); - renderContext.View.GetWorldMatrix(transform, drawCall.World); - drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.PerInstanceRandom = decal->GetPerInstanceRandom(); + const RenderDecalData& decal = renderContext.List->Decals.Get()[i]; + drawCall.World = decal.World; defaultMaterial->Bind(bindParams); boxModel->Render(context); } diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index f196a4ee9..589401892 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -59,13 +59,8 @@ bool GBufferPass::Init() bool GBufferPass::setupResources() { - ASSERT(_gBufferShader); - - // Check if shader has not been loaded - if (!_gBufferShader->IsLoaded()) - { + if (!_gBufferShader || !_gBufferShader->IsLoaded()) return true; - } auto gbuffer = _gBufferShader->GetShader(); // Validate shader constant buffers sizes @@ -228,9 +223,13 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTexture* lightBuffer) context->ResetRenderTarget(); } -bool SortDecal(Decal* const& a, Decal* const& b) +bool SortDecal(RenderDecalData const& a, RenderDecalData const& b) { - return a->SortOrder < b->SortOrder; + if (a.SortOrder == b.SortOrder) + { + return (uintptr)a.Material < (uintptr)b.Material; + } + return a.SortOrder < b.SortOrder; } void GBufferPass::RenderDebug(RenderContext& renderContext) @@ -428,96 +427,89 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context) void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer) { - // Skip if no decals to render auto& decals = renderContext.List->Decals; - if (decals.IsEmpty() || _boxModel == nullptr || !_boxModel->CanBeRendered() || EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::Decals)) + const auto boxModel = _boxModel.Get(); + if (decals.IsEmpty() || boxModel == nullptr || !boxModel->CanBeRendered() || EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::Decals)) return; - PROFILE_GPU_CPU("Decals"); - - // Cache data - auto device = GPUDevice::Instance; - auto context = device->GetMainContext(); - auto model = _boxModel.Get(); + auto context = GPUDevice::Instance->GetMainContext(); auto buffers = renderContext.Buffers; // Sort decals from the lowest order to the highest order - Sorting::QuickSort(decals.Get(), (int32)decals.Count(), &SortDecal); - - // TODO: batch decals using the same material - - // TODO: sort decals by the blending mode within the same order + Sorting::QuickSort(decals.Get(), decals.Count(), &SortDecal); // Prepare DrawCall drawCall; MaterialBase::BindParameters bindParams(context, renderContext, drawCall); bindParams.BindViewData(); - drawCall.Material = nullptr; - drawCall.WorldDeterminantSign = 1.0f; + MaterialDecalBlendingMode decalBlendingMode = (MaterialDecalBlendingMode)-1; + MaterialUsageFlags usageFlags = (MaterialUsageFlags)-1; + boxModel->LODs.Get()->Meshes.Get()->GetDrawCallGeometry(drawCall); + context->BindVB(ToSpan(drawCall.Geometry.VertexBuffers, 3)); + context->BindIB(drawCall.Geometry.IndexBuffer); + context->ResetRenderTarget(); // Draw all decals for (int32 i = 0; i < decals.Count(); i++) { - const auto decal = decals[i]; - ASSERT(decal && decal->Material); - Transform transform = decal->GetTransform(); - transform.Scale *= decal->GetSize(); - renderContext.View.GetWorldMatrix(transform, drawCall.World); - drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.ObjectRadius = decal->GetSphere().Radius; + const RenderDecalData& decal = decals.Get()[i]; - context->ResetRenderTarget(); - - // Bind output - const MaterialInfo& info = decal->Material->GetInfo(); - switch (info.DecalBlendingMode) + // Bind output (skip if won't change in-between decals) + const MaterialInfo& info = decal.Material->GetInfo(); + const MaterialUsageFlags infoUsageFlags = info.UsageFlags & (MaterialUsageFlags::UseEmissive | MaterialUsageFlags::UseNormal); + if (decalBlendingMode != info.DecalBlendingMode || usageFlags != infoUsageFlags) { - case MaterialDecalBlendingMode::Translucent: - { - GPUTextureView* targetBuffers[4]; - int32 count = 2; - targetBuffers[0] = buffers->GBuffer0->View(); - targetBuffers[1] = buffers->GBuffer2->View(); - if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive)) + decalBlendingMode = info.DecalBlendingMode; + usageFlags = infoUsageFlags; + switch (decalBlendingMode) { - count++; - targetBuffers[2] = lightBuffer; - - if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseNormal)) + case MaterialDecalBlendingMode::Translucent: + { + GPUTextureView* targetBuffers[4]; + int32 count = 2; + targetBuffers[0] = buffers->GBuffer0->View(); + targetBuffers[1] = buffers->GBuffer2->View(); + if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseEmissive)) { count++; - targetBuffers[3] = buffers->GBuffer1->View(); + targetBuffers[2] = lightBuffer; + if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseNormal)) + { + count++; + targetBuffers[3] = buffers->GBuffer1->View(); + } } + else if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseNormal)) + { + count++; + targetBuffers[2] = buffers->GBuffer1->View(); + } + context->SetRenderTarget(nullptr, ToSpan(targetBuffers, count)); + break; } - else if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseNormal)) + case MaterialDecalBlendingMode::Stain: { - count++; - targetBuffers[2] = buffers->GBuffer1->View(); + context->SetRenderTarget(buffers->GBuffer0->View()); + break; + } + case MaterialDecalBlendingMode::Normal: + { + context->SetRenderTarget(buffers->GBuffer1->View()); + break; + } + case MaterialDecalBlendingMode::Emissive: + { + context->SetRenderTarget(lightBuffer); + break; + } } - context->SetRenderTarget(nullptr, ToSpan(targetBuffers, count)); - break; - } - case MaterialDecalBlendingMode::Stain: - { - context->SetRenderTarget(buffers->GBuffer0->View()); - break; - } - case MaterialDecalBlendingMode::Normal: - { - context->SetRenderTarget(buffers->GBuffer1->View()); - break; - } - case MaterialDecalBlendingMode::Emissive: - { - context->SetRenderTarget(lightBuffer); - break; - } } // Draw decal - drawCall.PerInstanceRandom = decal->GetPerInstanceRandom(); - decal->Material->Bind(bindParams); - model->Render(context); + drawCall.World = decal.World; + decal.Material->Bind(bindParams); + // TODO: use hardware instancing + context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, 1, 0, 0, 0); } context->ResetSR(); diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 4dabe669d..5c0c8e39e 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -106,6 +106,13 @@ struct RenderSkyLightData : RenderLightData void SetupLightData(ShaderLightData* data, bool useShadow) const; }; +struct RenderDecalData +{ + Matrix World; + MaterialBase* Material; + int32 SortOrder; +}; + /// /// The draw calls list types. /// @@ -288,7 +295,7 @@ public: /// /// Decals registered for the rendering. /// - Array Decals; + Array Decals; /// /// Local volumetric fog particles registered for the rendering.