diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 44b3b4ea0..26a705970 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -89,6 +89,7 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor draw.Flags = StaticFlags::Transform; draw.DrawModes = DrawPass::Forward; draw.PerInstanceRandom = 0; + draw.StencilValue = 0; draw.LODBias = 0; draw.ForcedLOD = -1; draw.SortOrder = 0; diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 8a0e75a6e..ddc3468f7 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -323,6 +323,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, draw.Bounds = sphere; draw.PerInstanceRandom = instance.Random; draw.DrawModes = type._drawModes; + draw.SetStencilValue(_layer); type.Model->Draw(renderContext, draw); //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); @@ -1182,6 +1183,7 @@ void Foliage::Draw(RenderContext& renderContext) draw.Bounds = instance.Bounds; draw.PerInstanceRandom = instance.Random; draw.DrawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode); + draw.SetStencilValue(_layer); type.Model->Draw(renderContext, draw); return; } diff --git a/Source/Engine/Graphics/Config.h b/Source/Engine/Graphics/Config.h index 542669f03..43fb319bf 100644 --- a/Source/Engine/Graphics/Config.h +++ b/Source/Engine/Graphics/Config.h @@ -70,7 +70,7 @@ // Default depth buffer pixel format #ifndef GPU_DEPTH_BUFFER_PIXEL_FORMAT -#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D32_Float +#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D24_UNorm_S8_UInt #endif // Enable/disable gpu resources naming diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 5f0abad33..bbcdc8207 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -85,13 +85,10 @@ void DeferredMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant) { // Invert culling when scale is negative - if (cullMode == CullMode::Normal) - cullMode = CullMode::Inverted; - else - cullMode = CullMode::Normal; + cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal; } ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes const auto cache = params.Instanced ? &_cacheInstanced : &_cache; @@ -101,6 +98,7 @@ void DeferredMaterialShader::Bind(BindParameters& params) // Bind pipeline context->SetState(state); + context->SetStencilRef(drawCall.StencilValue); } void DeferredMaterialShader::Unload() @@ -137,6 +135,10 @@ bool DeferredMaterialShader::Load() } #endif + psDesc.StencilEnable = true; + psDesc.StencilReadMask = 0; + psDesc.StencilPassOp = StencilOperation::Replace; + // GBuffer Pass psDesc.VS = _shader->GetVS("VS"); failed |= psDesc.VS == nullptr; @@ -160,6 +162,9 @@ bool DeferredMaterialShader::Load() psDesc.PS = _shader->GetPS("PS_GBuffer"); _cache.DefaultSkinned.Init(psDesc); + psDesc.StencilEnable = false; + psDesc.StencilPassOp = StencilOperation::Keep; + #if USE_EDITOR if (_shader->HasShader("PS_QuadOverdraw")) { diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp index 3efd9a2bc..6260163f4 100644 --- a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp @@ -62,7 +62,7 @@ void DeformableMaterialShader::Bind(BindParameters& params) { Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix); - materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; + materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f; materialData->Segment = drawCall.Deformable.Segment; materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment; materialData->MeshMinZ = drawCall.Deformable.MeshMinZ; @@ -84,13 +84,10 @@ void DeformableMaterialShader::Bind(BindParameters& params) // Select pipeline state based on current pass and render mode const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != MaterialFeaturesFlags::None || view.Mode == ViewMode::Wireframe; CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode; - if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant) { // Invert culling when scale is negative - if (cullMode == CullMode::Normal) - cullMode = CullMode::Inverted; - else - cullMode = CullMode::Normal; + cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal; } PipelineStateCache* psCache = _cache.GetPS(view.Pass); ASSERT(psCache); @@ -98,6 +95,7 @@ void DeformableMaterialShader::Bind(BindParameters& params) // Bind pipeline context->SetState(state); + context->SetStencilRef(drawCall.StencilValue); } void DeformableMaterialShader::Unload() @@ -139,10 +137,17 @@ bool DeformableMaterialShader::Load() { _drawModes |= DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas; + psDesc.StencilEnable = true; + psDesc.StencilReadMask = 0; + psDesc.StencilPassOp = StencilOperation::Replace; + // GBuffer Pass psDesc.VS = _shader->GetVS("VS_SplineModel"); psDesc.PS = _shader->GetPS("PS_GBuffer"); _cache.Default.Init(psDesc); + + psDesc.StencilEnable = false; + psDesc.StencilPassOp = StencilOperation::Keep; } else { diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index a966507d8..72ec3c7bd 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -80,13 +80,10 @@ void ForwardMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant) { // Invert culling when scale is negative - if (cullMode == CullMode::Normal) - cullMode = CullMode::Inverted; - else - cullMode = CullMode::Normal; + cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal; } ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes const auto cacheObj = params.Instanced ? &_cacheInstanced : &_cache; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index d99e0f5d0..b6b455f20 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -76,7 +76,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); - materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; + materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; materialData->CurrentLOD = drawCall.Terrain.CurrentLOD; materialData->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD; @@ -109,13 +109,10 @@ void TerrainMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant) { // Invert culling when scale is negative - if (cullMode == CullMode::Normal) - cullMode = CullMode::Inverted; - else - cullMode = CullMode::Normal; + cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal; } const PipelineStateCache* psCache = _cache.GetPS(view.Pass, useLightmap); ASSERT(psCache); @@ -123,6 +120,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) // Bind pipeline context->SetState(state); + context->SetStencilRef(drawCall.StencilValue); } void TerrainMaterialShader::Unload() @@ -139,6 +137,10 @@ bool TerrainMaterialShader::Load() psDesc.DepthEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == MaterialFeaturesFlags::None; psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None; + psDesc.StencilEnable = true; + psDesc.StencilReadMask = 0; + psDesc.StencilPassOp = StencilOperation::Replace; + #if GPU_ALLOW_TESSELLATION_SHADERS // Check if use tessellation (both material and runtime supports it) const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 32df730e8..c2f340645 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -218,7 +218,7 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* return Init(vertices, triangles, vbData, ib, use16BitIndexBuffer, vbLayout); } -void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const +void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const { if (!material || !material->IsSurface() || !IsInitialized()) return; @@ -240,8 +240,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue(); drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = world; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = perInstanceRandom; + drawCall.StencilValue = stencilValue; #if USE_EDITOR const ViewMode viewMode = renderContext.View.Mode; if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview) @@ -307,8 +307,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty; drawCall.Surface.LODDitherFactor = lodDitherFactor; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = info.PerInstanceRandom; + drawCall.StencilValue = info.StencilValue; #if USE_EDITOR const ViewMode viewMode = renderContext.View.Mode; if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview) @@ -370,8 +370,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty; drawCall.Surface.LODDitherFactor = lodDitherFactor; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = info.PerInstanceRandom; + drawCall.StencilValue = info.StencilValue; #if USE_EDITOR const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode; if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview) diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 63b069f9a..8e273a3d5 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -142,7 +142,8 @@ public: /// The draw passes to use for rendering this object. /// The random per-instance value (normalized to range 0-1). /// Object sorting key. - API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const; + /// Object stencil value. + API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0, uint8 stencilValue = 0) const; /// /// Draws the mesh. diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 6a61ec3a5..1c76df166 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -404,6 +404,11 @@ public: /// float PerInstanceRandom; + /// + /// The 8-bit stencil value to write into Depth-Stencil Buffer. + /// + uint8 StencilValue; + /// /// The LOD bias value. /// @@ -422,6 +427,12 @@ public: #if USE_EDITOR float LightmapScale = -1.0f; #endif + + // Packs object layer into the stencil bits. + FORCE_INLINE void SetStencilValue(int32 layer) + { + StencilValue = uint8(layer & 0x1f); + } }; /// diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 8c1b98ebf..66b3e5701 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -314,8 +314,8 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Skinning = info.Skinning; drawCall.Surface.LODDitherFactor = lodDitherFactor; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = info.PerInstanceRandom; + drawCall.StencilValue = info.StencilValue; // Push draw call to the render list renderContext.List->AddDrawCall(renderContext, drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals, info.SortOrder); @@ -355,8 +355,8 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Skinning = info.Skinning; drawCall.Surface.LODDitherFactor = lodDitherFactor; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = info.PerInstanceRandom; + drawCall.StencilValue = info.StencilValue; // Push draw call to the render lists const auto shadowsMode = entry.ShadowsMode & slot.ShadowsMode; diff --git a/Source/Engine/Graphics/RenderBuffers.h b/Source/Engine/Graphics/RenderBuffers.h index 647d28c4c..dadd06175 100644 --- a/Source/Engine/Graphics/RenderBuffers.h +++ b/Source/Engine/Graphics/RenderBuffers.h @@ -13,6 +13,17 @@ #define GBUFFER2_FORMAT PixelFormat::R8G8B8A8_UNorm #define GBUFFER3_FORMAT PixelFormat::R8G8B8A8_UNorm +// Stencil bits usage (must match GBuffer.hlsl) +// [0] | Object Layer +// [1] | Object Layer +// [2] | Object Layer +// [3] | Object Layer +// [4] | Object Layer +// [5] | +// [6] | +// [7] | +#define STENCIL_BUFFER_OBJECT_LAYER(value) uint8(value & 0x1f) + /// /// The scene rendering buffers container. /// diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index 85ac4ae0b..184f50c89 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -437,6 +437,12 @@ public: /// The view to the depth-stencil resource descriptor as read-only depth. API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0; + /// + /// Gets the view to the texture as stencil buffer. + /// + /// The view to the stencil resource descriptor. + API_FUNCTION() virtual GPUTextureView* ViewStencil() const = 0; + /// /// Implicit conversion to the first surface (only for 2D textures). /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 2a50fbfd8..a0ec80bb1 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -11,6 +11,7 @@ #include "GPUSamplerDX11.h" #include "GPUVertexLayoutDX11.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Core/Math/Viewport.h" #include "Engine/Core/Math/Rectangle.h" #include "Engine/Profiler/RenderStats.h" @@ -216,7 +217,10 @@ void GPUContextDX11::ClearDepth(GPUTextureView* depthBuffer, float depthValue, u if (depthBufferDX11) { ASSERT(depthBufferDX11->DSV()); - _context->ClearDepthStencilView(depthBufferDX11->DSV(), D3D11_CLEAR_DEPTH, depthValue, stencilValue); + UINT clearFlags = D3D11_CLEAR_DEPTH; + if (PixelFormatExtensions::HasStencil(depthBufferDX11->GetFormat())) + clearFlags |= D3D11_CLEAR_STENCIL; + _context->ClearDepthStencilView(depthBufferDX11->DSV(), clearFlags, depthValue, stencilValue); } } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp index e858f7e3e..a05585696 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp @@ -150,6 +150,7 @@ void GPUTextureDX11::OnReleaseGPU() _handleArray.Release(); _handleVolume.Release(); _handleReadOnlyDepth.Release(); + _handleStencil.Release(); DX_SAFE_RELEASE_CHECK(_resource, 0); // Base @@ -547,6 +548,41 @@ void GPUTextureDX11::initHandles() } _handleReadOnlyDepth.Init(this, rtView, srView, dsView, nullptr, format, msaa); } + + // Stencil view + if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) + { + PixelFormat stencilFormat; + switch (_dxgiFormatDSV) + { + case PixelFormat::D24_UNorm_S8_UInt: + srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; + stencilFormat = PixelFormat::X24_Typeless_G8_UInt; + break; + case PixelFormat::D32_Float_S8X24_UInt: + srDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; + stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt; + break; + } + if (isCubeMap) + { + srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + srDesc.TextureCube.MostDetailedMip = 0; + srDesc.TextureCube.MipLevels = mipLevels; + } + else if (isMsaa) + { + srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } + else + { + srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srDesc.Texture2D.MostDetailedMip = 0; + srDesc.Texture2D.MipLevels = mipLevels; + } + VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView)); + _handleStencil.Init(this, nullptr, srView, nullptr, nullptr, stencilFormat, msaa); + } } bool GPUTextureDX11::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h index d33c56158..35101e79b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h @@ -158,6 +158,7 @@ private: GPUTextureViewDX11 _handleArray; GPUTextureViewDX11 _handleVolume; GPUTextureViewDX11 _handleReadOnlyDepth; + GPUTextureViewDX11 _handleStencil; Array _handlesPerSlice; // [slice] Array> _handlesPerMip; // [slice][mip] @@ -225,6 +226,11 @@ public: ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); return (GPUTextureView*)&_handleReadOnlyDepth; } + GPUTextureView* ViewStencil() const override + { + ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil); + return (GPUTextureView*)&_handleStencil; + } void* GetNativePtr() const override { return static_cast(_resource); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index c68e7f262..64c384447 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -36,6 +36,7 @@ #include "CommandSignatureDX12.h" #include "Engine/Profiler/RenderStats.h" #include "Engine/Graphics/GPUResourceAccess.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Threading/Threading.h" @@ -823,7 +824,10 @@ void GPUContextDX12::ClearDepth(GPUTextureView* depthBuffer, float depthValue, u SetResourceState(depthBufferDX12->GetResourceOwner(), D3D12_RESOURCE_STATE_DEPTH_WRITE, depthBufferDX12->SubresourceIndex); flushRBs(); - _commandList->ClearDepthStencilView(depthBufferDX12->DSV(), D3D12_CLEAR_FLAG_DEPTH, depthValue, stencilValue, 0, nullptr); + D3D12_CLEAR_FLAGS clearFlags = D3D12_CLEAR_FLAG_DEPTH; + if (PixelFormatExtensions::HasStencil(depthBufferDX12->GetFormat())) + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + _commandList->ClearDepthStencilView(depthBufferDX12->DSV(), clearFlags, depthValue, stencilValue, 0, nullptr); } } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index 5913468ee..abecfd776 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -227,6 +227,8 @@ void GPUTextureDX12::OnReleaseGPU() _handlesPerSlice.Resize(0, false); _handleArray.Release(); _handleVolume.Release(); + _handleReadOnlyDepth.Release(); + _handleStencil.Release(); _srv.Release(); _uav.Release(); releaseResource(); @@ -721,6 +723,45 @@ void GPUTextureDX12::initHandles() _handleReadOnlyDepth.SetSRV(srDesc); } } + + // Stencil view + if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) + { + PixelFormat stencilFormat; + switch (_dxgiFormatDSV) + { + case PixelFormat::D24_UNorm_S8_UInt: + srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; + stencilFormat = PixelFormat::X24_Typeless_G8_UInt; + break; + case PixelFormat::D32_Float_S8X24_UInt: + srDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; + stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt; + break; + } + if (isCubeMap) + { + srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + srDesc.TextureCube.MostDetailedMip = 0; + srDesc.TextureCube.MipLevels = mipLevels; + srDesc.TextureCube.ResourceMinLODClamp = 0; + } + else if (isMsaa) + { + srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS; + } + else + { + srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srDesc.Texture2D.MostDetailedMip = 0; + srDesc.Texture2D.MipLevels = mipLevels; + srDesc.Texture2D.ResourceMinLODClamp = 0; + srDesc.Texture2D.PlaneSlice = 1; + } + _handleStencil.Init(this, _device, this, stencilFormat, msaa); + _handleStencil.ReadOnlyDepthView = true; + _handleStencil.SetSRV(srDesc); + } } #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h index e6427ab6d..b513b5b4f 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h @@ -91,7 +91,7 @@ public: // [IShaderResourceDX12] bool IsDepthStencilResource() const override { - return _dsv.IsValid(); + return ReadOnlyDepthView || _dsv.IsValid(); } D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override { @@ -117,6 +117,7 @@ private: GPUTextureViewDX12 _handleArray; GPUTextureViewDX12 _handleVolume; GPUTextureViewDX12 _handleReadOnlyDepth; + GPUTextureViewDX12 _handleStencil; Array _handlesPerSlice; // [slice] Array> _handlesPerMip; // [slice][mip] @@ -165,6 +166,11 @@ public: ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); return (GPUTextureView*)&_handleReadOnlyDepth; } + GPUTextureView* ViewStencil() const override + { + ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil); + return (GPUTextureView*)&_handleStencil; + } void* GetNativePtr() const override { return (void*)_resource; diff --git a/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h b/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h index 25b204184..23e7d2b54 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h +++ b/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h @@ -33,6 +33,10 @@ public: { return nullptr; } + GPUTextureView* ViewStencil() const override + { + return nullptr; + } void* GetNativePtr() const override { return nullptr; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp index 326c87e8d..52f347959 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp @@ -11,7 +11,7 @@ #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Scripting/Enums.h" -void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels, int32 firstMipIndex, int32 arraySize, int32 firstArraySlice, bool readOnlyDepth) +void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels, int32 firstMipIndex, int32 arraySize, int32 firstArraySlice, bool readOnlyDepth, bool stencilView) { ASSERT(View == VK_NULL_HANDLE); @@ -57,7 +57,13 @@ void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* ow SubresourceIndex = RenderTools::CalcSubresourceIndex(firstMipIndex, firstArraySlice, totalMipLevels); } - if (PixelFormatExtensions::IsDepthStencil(format)) + if (stencilView) + { + range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + LayoutRTV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + LayoutSRV = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + } + else if (PixelFormatExtensions::IsDepthStencil(format)) { range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; #if 0 @@ -157,13 +163,23 @@ void GPUTextureViewVulkan::DescriptorAsImage(GPUContextVulkan* context, VkImageV imageView = View; layout = LayoutSRV; const VkImageAspectFlags aspectMask = Info.subresourceRange.aspectMask; - if (aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + if (aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - // Transition depth-only when binding depth buffer with stencil + // Transition depth-only when binding depth buffer with stencil (or stencil-only without depth) if (ViewSRV == VK_NULL_HANDLE) { VkImageViewCreateInfo createInfo = Info; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) + { + // Stencil + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + createInfo.components.g = VK_COMPONENT_SWIZZLE_R; // Map .g component in shader to .r of stencil plane + } + else + { + // Depth + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewSRV)); } imageView = ViewSRV; @@ -434,6 +450,26 @@ void GPUTextureVulkan::initHandles() { _handleReadOnlyDepth.Init(_device, this, _image, mipLevels, format, msaa, extent, VK_IMAGE_VIEW_TYPE_2D, mipLevels, 0, 1, 0, true); } + + // Stencil view + if (IsDepthStencil() && IsShaderResource() && PixelFormatExtensions::HasStencil(format)) + { + PixelFormat stencilFormat; + switch (format) + { + case PixelFormat::D24_UNorm_S8_UInt: + case PixelFormat::R24_UNorm_X8_Typeless: + case PixelFormat::R24G8_Typeless: + stencilFormat = PixelFormat::X24_Typeless_G8_UInt; + break; + case PixelFormat::D32_Float_S8X24_UInt: + case PixelFormat::R32_Float_X8X24_Typeless: + case PixelFormat::R32G8X24_Typeless: + stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt; + break; + } + _handleStencil.Init(_device, this, _image, mipLevels, stencilFormat, msaa, extent, VK_IMAGE_VIEW_TYPE_2D, mipLevels, 0, 1, 0, true, true); + } } void GPUTextureVulkan::OnResidentMipsChanged() @@ -457,6 +493,7 @@ void GPUTextureVulkan::OnReleaseGPU() _handleVolume.Release(); _handleUAV.Release(); _handleReadOnlyDepth.Release(); + _handleStencil.Release(); for (int32 i = 0; i < _handlesPerMip.Count(); i++) { for (int32 j = 0; j < _handlesPerMip[i].Count(); j++) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h index 999e0f602..178938b82 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h @@ -45,7 +45,7 @@ public: #endif public: - void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false); + void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false, bool stencilView = false); VkImageView GetFramebufferView(); @@ -79,6 +79,7 @@ private: GPUTextureViewVulkan _handleVolume; GPUTextureViewVulkan _handleUAV; GPUTextureViewVulkan _handleReadOnlyDepth; + GPUTextureViewVulkan _handleStencil; Array _handlesPerSlice; // [slice] Array> _handlesPerMip; // [slice][mip] @@ -140,6 +141,11 @@ public: ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); return (GPUTextureView*)&_handleReadOnlyDepth; } + GPUTextureView* ViewStencil() const override + { + ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil); + return (GPUTextureView*)&_handleStencil; + } void* GetNativePtr() const override { return (void*)_image; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 18342dbbb..ee66c6c77 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -1027,6 +1027,7 @@ void AnimatedModel::Draw(RenderContext& renderContext) draw.LODBias = LODBias; draw.ForcedLOD = ForcedLOD; draw.SortOrder = SortOrder; + draw.SetStencilValue(_layer); SkinnedModel->Draw(renderContext, draw); } @@ -1068,6 +1069,7 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch) draw.LODBias = LODBias; draw.ForcedLOD = ForcedLOD; draw.SortOrder = SortOrder; + draw.SetStencilValue(_layer); PRAGMA_DISABLE_DEPRECATION_WARNINGS if (ShadowsMode != ShadowsCastingMode::All) diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 490f9d786..4a7d2aa75 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -374,6 +374,7 @@ void Camera::Draw(RenderContext& renderContext) BoundingSphere::FromBox(_previewModelBox, draw.Bounds); draw.Bounds.Center -= renderContext.View.Origin; draw.PerInstanceRandom = GetPerInstanceRandom(); + draw.StencilValue = 0; draw.LODBias = 0; draw.ForcedLOD = -1; draw.SortOrder = 0; diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp index e08d8a61a..dbda65d3f 100644 --- a/Source/Engine/Level/Actors/Skybox.cpp +++ b/Source/Engine/Level/Actors/Skybox.cpp @@ -107,7 +107,6 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M drawCall.ObjectPosition = drawCall.World.GetTranslation(); drawCall.ObjectRadius = (float)_sphere.Radius; drawCall.Surface.GeometrySize = _box.GetSize(); - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = GetPerInstanceRandom(); MaterialBase::BindParameters bindParams(context, renderContext, drawCall); bindParams.BindViewData(); diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 8680fb8d5..42b2dc620 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -407,12 +407,12 @@ void SplineModel::Draw(RenderContext& renderContext) drawCall.Deformable.MeshMaxZ = _meshMaxZ; drawCall.Deformable.GeometrySize = _box.GetSize(); drawCall.PerInstanceRandom = GetPerInstanceRandom(); + drawCall.SetStencilValue(_layer); _preTransform.GetWorld(drawCall.Deformable.LocalMatrix); const Transform splineTransform = GetTransform(); renderContext.View.GetWorldMatrix(splineTransform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); drawCall.ObjectRadius = (float)_sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline - const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); for (int32 segment = 0; segment < _instances.Count(); segment++) { auto& instance = _instances[segment]; @@ -468,7 +468,6 @@ void SplineModel::Draw(RenderContext& renderContext) // Submit draw call mesh->GetDrawCallGeometry(drawCall); drawCall.Material = material; - drawCall.WorldDeterminantSign = Math::FloatSelect(worldDeterminantSign * instance.RotDeterminant, 1, -1); renderContext.List->AddDrawCall(renderContext, drawModes, _staticFlags, drawCall, entry.ReceiveDecals); } } diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 912009b3b..f41e4a805 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -361,6 +361,7 @@ void StaticModel::Draw(RenderContext& renderContext) draw.ForcedLOD = _forcedLod; draw.SortOrder = _sortOrder; draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr; + draw.SetStencilValue(_layer); #if USE_EDITOR if (HasStaticFlag(StaticFlags::Lightmap)) draw.LightmapScale = _scaleInLightmap; @@ -397,6 +398,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch) draw.ForcedLOD = _forcedLod; draw.SortOrder = _sortOrder; draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr; + draw.SetStencilValue(_layer); #if USE_EDITOR if (HasStaticFlag(StaticFlags::Lightmap)) draw.LightmapScale = _scaleInLightmap; diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 487bc4b03..03da3e324 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1168,9 +1168,6 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf Matrix worlds[2]; Matrix::Translation(-viewOrigin, worlds[0]); // World renderContextBatch.GetMainContext().View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local - float worldDeterminantSigns[2]; - worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1); - worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1); const StaticFlags staticFlags = effect->GetStaticFlags(); const int8 sortOrder = effect->SortOrder; @@ -1194,6 +1191,7 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf // Setup a draw call common data DrawCall drawCall; drawCall.PerInstanceRandom = effect->GetPerInstanceRandom(); + drawCall.SetStencilValue(effect->GetLayer()); drawCall.ObjectPosition = bounds.Center; drawCall.ObjectRadius = (float)bounds.Radius; @@ -1209,7 +1207,6 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf continue; drawCall.World = worlds[(int32)emitter->SimulationSpace]; - drawCall.WorldDeterminantSign = worldDeterminantSigns[(int32)emitter->SimulationSpace]; drawCall.Particle.Particles = buffer; // Check if need to render any module diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 1a8d23285..b71b64874 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -265,16 +265,23 @@ struct DrawCall /// float ObjectRadius; - /// - /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling). - /// - float WorldDeterminantSign; - /// /// The random per-instance value (normalized to range 0-1). /// float PerInstanceRandom; + /// + /// The 8-bit stencil value to write into Depth-Stencil Buffer. + /// + uint8 StencilValue; + + /// + /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling). + /// 0 - sign is positive + /// 1 - sign is negative (flips object surfaces) + /// + uint8 WorldDeterminant : 1; + /// /// The sorting key for the draw call calculate by RenderList. /// @@ -287,6 +294,12 @@ struct DrawCall { Platform::MemoryClear(this, sizeof(DrawCall)); } + + // Packs object layer into the stencil bits. + FORCE_INLINE void SetStencilValue(int32 layer) + { + StencilValue = uint8(layer & 0x1f); + } }; template<> diff --git a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp index 41283f63e..eb4afc26c 100644 --- a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp +++ b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp @@ -129,8 +129,6 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon DrawCall drawCall; 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++) { diff --git a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp index c6804f300..3c38d11a4 100644 --- a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp +++ b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp @@ -50,8 +50,6 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context, context->BindUA(1, overdrawTexture->View()); context->BindUA(2, liveCountTexture->View()); DrawCall drawCall; - drawCall.WorldDeterminantSign = 1.0f; - drawCall.PerInstanceRandom = 0.0f; MaterialBase::BindParameters bindParams(context, renderContext, drawCall); bindParams.BindViewData(); renderContext.View.Pass = DrawPass::QuadOverdraw; @@ -83,7 +81,6 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context, m1 *= m2; drawCall.World = m1; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); skyMaterial->Bind(bindParams); skyModel->Render(context); } diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 72b9a4e20..bb580aaf3 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -564,7 +564,8 @@ FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall& IMaterial::InstancingHandler handler; if (drawCall.Material->CanUseInstancing(handler)) handler.GetHash(drawCall, batchKey); - uint32 drawKey = (uint32)(471 * drawCall.WorldDeterminantSign); + batchKey = (batchKey * 397) ^ drawCall.StencilValue; + uint32 drawKey = (uint32)(471 * drawCall.WorldDeterminant); drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[0]); drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]); drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]); @@ -586,8 +587,11 @@ void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawMo ASSERT_LOW_LAYER(drawModes != DrawPass::None && ((uint32)drawModes & ~(uint32)materialDrawModes) == 0); #endif - // Append draw call data + // Finalize draw call initialization + drawCall.WorldDeterminant = drawCall.World.RotDeterminant() < 0 ? 1 : 0; CalculateSortKey(renderContext, drawCall, sortOrder); + + // Append draw call data const int32 index = DrawCalls.Add(drawCall); // Add draw call to proper draw lists @@ -625,8 +629,11 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP #endif const RenderContext& mainRenderContext = renderContextBatch.Contexts.Get()[0]; - // Append draw call data + // Finalize draw call initialization + drawCall.WorldDeterminant = drawCall.World.RotDeterminant() < 0 ? 1 : 0; CalculateSortKey(mainRenderContext, drawCall, sortOrder); + + // Append draw call data const int32 index = DrawCalls.Add(drawCall); // Add draw call to proper draw lists @@ -777,7 +784,8 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD other.InstanceCount != 0 && drawCallHandler.CanBatch == otherHandler.CanBatch && drawCallHandler.CanBatch(drawCall, other, pass) && - drawCall.WorldDeterminantSign * other.WorldDeterminantSign > 0; + drawCall.WorldDeterminant == other.WorldDeterminant && + drawCall.StencilValue == other.StencilValue; if (!canBatch) break; batchSize++; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 24262162d..fc11b26cc 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -651,13 +651,15 @@ GPU_CB_STRUCT(ShaderObjectData FORCE_INLINE void Store(const DrawCall& drawCall) { - Store(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor); + Store(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminant ? -1.0f : 1.0f, drawCall.Surface.LODDitherFactor); } FORCE_INLINE void Load(DrawCall& drawCall) const { - Load(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor); + float worldDeterminantSign; + Load(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, worldDeterminantSign, drawCall.Surface.LODDitherFactor); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.WorldDeterminant = worldDeterminantSign < 0 ? 1 : 0; } }); diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 513f6af97..d1bcb7842 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -122,8 +122,8 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const drawCall.Terrain.Lightmap = nullptr; drawCall.Terrain.LightmapUVsArea = Rectangle::Empty; } - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = _perInstanceRandom; + drawCall.SetStencilValue(_patch->_terrain->GetLayer()); #if USE_EDITOR if (renderContext.View.Mode == ViewMode::LightmapUVsDensity) drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader @@ -183,8 +183,8 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi drawCall.Terrain.Lightmap = nullptr; drawCall.Terrain.LightmapUVsArea = Rectangle::Empty; } - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = _perInstanceRandom; + drawCall.SetStencilValue(_patch->_terrain->GetLayer()); #if USE_EDITOR if (renderContext.View.Mode == ViewMode::LightmapUVsDensity) drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 52132b972..c2f6789c7 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -390,8 +390,8 @@ void TextRender::Draw(RenderContext& renderContext) drawCall.ObjectRadius = (float)_sphere.Radius; drawCall.Surface.GeometrySize = _localBox.GetSize(); drawCall.Surface.PrevWorld = _drawState.PrevWorld; - drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World); drawCall.PerInstanceRandom = GetPerInstanceRandom(); + drawCall.SetStencilValue(_layer); drawCall.Geometry.IndexBuffer = _ib.GetBuffer(); drawCall.Geometry.VertexBuffers[0] = _vb.GetBuffer(); drawCall.InstanceCount = 1; diff --git a/Source/Shaders/Stencil.hlsl b/Source/Shaders/Stencil.hlsl new file mode 100644 index 000000000..7cb72f024 --- /dev/null +++ b/Source/Shaders/Stencil.hlsl @@ -0,0 +1,23 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#ifndef __STENCIL__ +#define __STENCIL__ + +#include "./Flax/Common.hlsl" + +// Stencil bits usage (must match RenderBuffers.h) +// [0] | Object Layer +// [1] | Object Layer +// [2] | Object Layer +// [3] | Object Layer +// [4] | Object Layer +// [5] | +// [6] | +// [7] | +#define STENCIL_BUFFER_OBJECT_LAYER(stencil) uint(stencil & 0x1f) +#ifndef STENCIL_BUFFER_SWIZZLE +#define STENCIL_BUFFER_SWIZZLE .g +#endif +#define STENCIL_BUFFER_LOAD(rt, pos) rt.Load(int3(pos, 0)) STENCIL_BUFFER_SWIZZLE + +#endif