Add stencil buffer usage with object layer information

#3080 #967
This commit is contained in:
Wojtek Figat
2025-10-07 18:07:23 +02:00
parent 823ed247d2
commit 361fc3ecfb
36 changed files with 291 additions and 65 deletions

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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"))
{

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -142,7 +142,8 @@ public:
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
/// <param name="sortOrder">Object sorting key.</param>
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;
/// <param name="stencilValue">Object stencil value.</param>
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;
/// <summary>
/// Draws the mesh.

View File

@@ -404,6 +404,11 @@ public:
/// </summary>
float PerInstanceRandom;
/// <summary>
/// The 8-bit stencil value to write into Depth-Stencil Buffer.
/// </summary>
uint8 StencilValue;
/// <summary>
/// The LOD bias value.
/// </summary>
@@ -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);
}
};
/// <summary>

View File

@@ -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;

View File

@@ -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] | <unsued>
// [6] | <unsued>
// [7] | <unsued>
#define STENCIL_BUFFER_OBJECT_LAYER(value) uint8(value & 0x1f)
/// <summary>
/// The scene rendering buffers container.
/// </summary>

View File

@@ -437,6 +437,12 @@ public:
/// <returns>The view to the depth-stencil resource descriptor as read-only depth.</returns>
API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0;
/// <summary>
/// Gets the view to the texture as stencil buffer.
/// </summary>
/// <returns>The view to the stencil resource descriptor.</returns>
API_FUNCTION() virtual GPUTextureView* ViewStencil() const = 0;
/// <summary>
/// Implicit conversion to the first surface (only for 2D textures).
/// </summary>

View File

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

View File

@@ -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)

View File

@@ -158,6 +158,7 @@ private:
GPUTextureViewDX11 _handleArray;
GPUTextureViewDX11 _handleVolume;
GPUTextureViewDX11 _handleReadOnlyDepth;
GPUTextureViewDX11 _handleStencil;
Array<GPUTextureViewDX11> _handlesPerSlice; // [slice]
Array<Array<GPUTextureViewDX11>> _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<void*>(_resource);

View File

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

View File

@@ -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

View File

@@ -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<GPUTextureViewDX12> _handlesPerSlice; // [slice]
Array<Array<GPUTextureViewDX12>> _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;

View File

@@ -33,6 +33,10 @@ public:
{
return nullptr;
}
GPUTextureView* ViewStencil() const override
{
return nullptr;
}
void* GetNativePtr() const override
{
return nullptr;

View File

@@ -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++)

View File

@@ -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<GPUTextureViewVulkan> _handlesPerSlice; // [slice]
Array<Array<GPUTextureViewVulkan>> _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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

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

View File

@@ -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;

View File

@@ -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

View File

@@ -265,16 +265,23 @@ struct DrawCall
/// </summary>
float ObjectRadius;
/// <summary>
/// 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).
/// </summary>
float WorldDeterminantSign;
/// <summary>
/// The random per-instance value (normalized to range 0-1).
/// </summary>
float PerInstanceRandom;
/// <summary>
/// The 8-bit stencil value to write into Depth-Stencil Buffer.
/// </summary>
uint8 StencilValue;
/// <summary>
/// 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)
/// </summary>
uint8 WorldDeterminant : 1;
/// <summary>
/// The sorting key for the draw call calculate by RenderList.
/// </summary>
@@ -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<>

View File

@@ -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++)
{

View File

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

View File

@@ -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++;

View File

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

View File

@@ -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

View File

@@ -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;

View File

@@ -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] | <unsued>
// [6] | <unsued>
// [7] | <unsued>
#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