Add vertex shader input layout reading via shader compiler reflection to handle missing vertex elements binding when explicit layout got deprecated

This commit is contained in:
Wojtek Figat
2025-01-07 23:26:06 +01:00
parent 7f0d852f49
commit 7aa240e5eb
20 changed files with 350 additions and 232 deletions

View File

@@ -177,22 +177,28 @@ GPUShaderProgram* GPUShader::GetShader(ShaderStage stage, const StringAnsiView&
return shader;
}
GPUVertexLayout* GPUShader::ReadVertexLayout(MemoryReadStream& stream)
void GPUShader::ReadVertexLayout(MemoryReadStream& stream, GPUVertexLayout*& inputLayout, GPUVertexLayout*& vertexLayout)
{
inputLayout = vertexLayout = nullptr;
// Read input layout (based on shader reflection)
GPUVertexLayout::Elements elements;
stream.Read(elements);
inputLayout = GPUVertexLayout::Get(elements);
// [Deprecated in v1.10]
byte inputLayoutSize;
stream.ReadByte(&inputLayoutSize);
if (inputLayoutSize == 0)
return nullptr;
return;
void* elementsData = stream.Move(sizeof(VertexElement) * inputLayoutSize);
if (inputLayoutSize > GPU_MAX_VS_ELEMENTS)
{
LOG(Error, "Incorrect input layout size.");
return nullptr;
return;
}
GPUVertexLayout::Elements elements;
elements.Set((VertexElement*)elementsData, inputLayoutSize);
return GPUVertexLayout::Get(elements);
vertexLayout = GPUVertexLayout::Get(elements);
}
GPUResourceType GPUShader::GetResourceType() const

View File

@@ -12,7 +12,7 @@ class GPUShaderProgram;
/// <summary>
/// The runtime version of the shaders cache supported by the all graphics back-ends. The same for all the shader cache formats (easier to sync and validate).
/// </summary>
#define GPU_SHADER_CACHE_VERSION 11
#define GPU_SHADER_CACHE_VERSION 12
/// <summary>
/// The GPU resource with shader programs that can run on the GPU and are able to perform rendering calculation using textures, vertices and other resources.
@@ -135,7 +135,7 @@ public:
protected:
GPUShaderProgram* GetShader(ShaderStage stage, const StringAnsiView& name, int32 permutationIndex) const;
virtual GPUShaderProgram* CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, Span<byte> bytecode, MemoryReadStream& stream) = 0;
static GPUVertexLayout* ReadVertexLayout(MemoryReadStream& stream);
static void ReadVertexLayout(MemoryReadStream& stream, GPUVertexLayout*& inputLayout, GPUVertexLayout*& vertexLayout);
public:
// [GPUResource]

View File

@@ -136,6 +136,9 @@ public:
// [Deprecated in v1.10]
GPUVertexLayout* Layout = nullptr;
// Vertex shader inputs layout. Used to ensure that binded vertex buffers provide all required elements.
GPUVertexLayout* InputLayout = nullptr;
public:
// [GPUShaderProgram]
ShaderStage GetStage() const override

View File

@@ -26,7 +26,7 @@ ID3D11InputLayout* GPUShaderProgramVSDX11::GetInputLayout(GPUVertexLayoutDX11* v
{
if (vertexLayout && vertexLayout->InputElementsCount)
{
auto mergedVertexLayout = (GPUVertexLayoutDX11*)GPUVertexLayout::Merge(vertexLayout, Layout);
auto mergedVertexLayout = (GPUVertexLayoutDX11*)GPUVertexLayout::Merge(vertexLayout, Layout ? Layout : InputLayout);
LOG_DIRECTX_RESULT(vertexLayout->GetDevice()->GetDevice()->CreateInputLayout(mergedVertexLayout->InputElements, mergedVertexLayout->InputElementsCount, Bytecode.Get(), Bytecode.Length(), &inputLayout));
}
_cache.Add(vertexLayout, inputLayout);
@@ -42,42 +42,30 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
{
case ShaderStage::Vertex:
{
// Load Input Layout
GPUVertexLayout* vertexLayout = ReadVertexLayout(stream);
// Create shader
GPUVertexLayout* inputLayout, *vertexLayout;
ReadVertexLayout(stream, inputLayout, vertexLayout);
ID3D11VertexShader* buffer = nullptr;
result = _device->GetDevice()->CreateVertexShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramVSDX11>(initializer, buffer, vertexLayout, bytecode);
shader = New<GPUShaderProgramVSDX11>(initializer, buffer, inputLayout, vertexLayout, bytecode);
break;
}
#if GPU_ALLOW_TESSELLATION_SHADERS
case ShaderStage::Hull:
{
// Read control points
int32 controlPointsCount;
stream.ReadInt32(&controlPointsCount);
// Create shader
ID3D11HullShader* buffer = nullptr;
result = _device->GetDevice()->CreateHullShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramHSDX11>(initializer, buffer, controlPointsCount);
break;
}
case ShaderStage::Domain:
{
// Create shader
ID3D11DomainShader* buffer = nullptr;
result = _device->GetDevice()->CreateDomainShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramDSDX11>(initializer, buffer);
break;
}
@@ -92,35 +80,26 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
#if GPU_ALLOW_GEOMETRY_SHADERS
case ShaderStage::Geometry:
{
// Create shader
ID3D11GeometryShader* buffer = nullptr;
result = _device->GetDevice()->CreateGeometryShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramGSDX11>(initializer, buffer);
break;
}
#endif
case ShaderStage::Pixel:
{
// Create shader
ID3D11PixelShader* buffer = nullptr;
result = _device->GetDevice()->CreatePixelShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramPSDX11>(initializer, buffer);
break;
}
case ShaderStage::Compute:
{
// Create shader
ID3D11ComputeShader* buffer = nullptr;
result = _device->GetDevice()->CreateComputeShader(bytecode.Get(), bytecode.Length(), nullptr, &buffer);
LOG_DIRECTX_RESULT_WITH_RETURN(result, nullptr);
// Create object
shader = New<GPUShaderProgramCSDX11>(initializer, buffer);
break;
}

View File

@@ -58,9 +58,10 @@ private:
Dictionary<class GPUVertexLayoutDX11*, ID3D11InputLayout*> _cache;
public:
GPUShaderProgramVSDX11(const GPUShaderProgramInitializer& initializer, ID3D11VertexShader* buffer, GPUVertexLayout* vertexLayout, Span<byte> bytecode)
GPUShaderProgramVSDX11(const GPUShaderProgramInitializer& initializer, ID3D11VertexShader* buffer, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout, Span<byte> bytecode)
: GPUShaderProgramDX11(initializer, buffer)
{
InputLayout = inputLayout;
Layout = vertexLayout;
Bytecode.Copy(bytecode);
}

View File

@@ -124,7 +124,7 @@ void DescriptorHeapWithSlotsDX12::ReleaseSlot(uint32 index)
{
uint32& value = _usage[index / 32];
const uint32 mask = 1 << (index & 31);
ASSERT_LOW_LAYER((value & mask) == mask);
//ASSERT_LOW_LAYER((value & mask) == mask);
value &= ~mask;
}

View File

@@ -180,7 +180,7 @@ bool GPUPipelineStateDX12::Init(const Description& desc)
INIT_SHADER_STAGE(PS, GPUShaderProgramPSDX12);
// Input Assembly
VertexLayout = desc.VS ? (GPUVertexLayoutDX12*)desc.VS->Layout : nullptr;
VertexLayout = desc.VS ? (GPUVertexLayoutDX12*)(desc.VS->Layout ? desc.VS->Layout : desc.VS->InputLayout) : nullptr;
const D3D12_PRIMITIVE_TOPOLOGY_TYPE primTypes1[] =
{
D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED,

View File

@@ -19,8 +19,9 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
{
case ShaderStage::Vertex:
{
GPUVertexLayout* vertexLayout = ReadVertexLayout(stream);
shader = New<GPUShaderProgramVSDX12>(initializer, header, bytecode, vertexLayout);
GPUVertexLayout* inputLayout, *vertexLayout;
ReadVertexLayout(stream, inputLayout, vertexLayout);
shader = New<GPUShaderProgramVSDX12>(initializer, header, bytecode, inputLayout, vertexLayout);
break;
}
#if GPU_ALLOW_TESSELLATION_SHADERS

View File

@@ -46,9 +46,10 @@ public:
class GPUShaderProgramVSDX12 : public GPUShaderProgramDX12<GPUShaderProgramVS>
{
public:
GPUShaderProgramVSDX12(const GPUShaderProgramInitializer& initializer, const DxShaderHeader* header, Span<byte> bytecode, GPUVertexLayout* vertexLayout)
GPUShaderProgramVSDX12(const GPUShaderProgramInitializer& initializer, const DxShaderHeader* header, Span<byte> bytecode, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout)
: GPUShaderProgramDX12(initializer, header, bytecode)
{
InputLayout = inputLayout;
Layout = vertexLayout;
}
};

View File

@@ -320,7 +320,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_desc.pStages = _shaderStages;
// Input Assembly
VertexShaderLayout = desc.VS ? (GPUVertexLayoutVulkan*)desc.VS->Layout : nullptr;
VertexShaderLayout = desc.VS ? (GPUVertexLayoutVulkan*)(desc.VS->Layout ? desc.VS->Layout : desc.VS->InputLayout) : nullptr;
RenderToolsVulkan::ZeroStruct(_descInputAssembly, VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO);;
switch (desc.PrimitiveTopology)
{

View File

@@ -66,9 +66,10 @@ public:
class GPUShaderProgramVSVulkan : public GPUShaderProgramVulkan<GPUShaderProgramVS>
{
public:
GPUShaderProgramVSVulkan(GPUDeviceVulkan* device, const GPUShaderProgramInitializer& initializer, const SpirvShaderDescriptorInfo& descriptorInfo, VkShaderModule shaderModule, GPUVertexLayout* vertexLayout)
GPUShaderProgramVSVulkan(GPUDeviceVulkan* device, const GPUShaderProgramInitializer& initializer, const SpirvShaderDescriptorInfo& descriptorInfo, VkShaderModule shaderModule, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout)
: GPUShaderProgramVulkan(device, initializer, descriptorInfo, shaderModule)
{
InputLayout = inputLayout;
Layout = vertexLayout;
}
};

View File

@@ -138,8 +138,9 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
{
case ShaderStage::Vertex:
{
GPUVertexLayout* vertexLayout = ReadVertexLayout(stream);
shader = New<GPUShaderProgramVSVulkan>(_device, initializer, header->DescriptorInfo, shaderModule, vertexLayout);
GPUVertexLayout* inputLayout, *vertexLayout;
ReadVertexLayout(stream, inputLayout, vertexLayout);
shader = New<GPUShaderProgramVSVulkan>(_device, initializer, header->DescriptorInfo, shaderModule, inputLayout, vertexLayout);
break;
}
#if GPU_ALLOW_TESSELLATION_SHADERS

View File

@@ -16,11 +16,9 @@
class IncludeD3D : public ID3DInclude
{
private:
ShaderCompilationContext* _context;
public:
IncludeD3D(ShaderCompilationContext* context)
{
_context = context;
@@ -84,14 +82,9 @@ namespace
// Extract constant buffers usage information
for (uint32 a = 0; a < desc.ConstantBuffers; a++)
{
// Get CB
auto cb = reflector->GetConstantBufferByIndex(a);
// Get CB description
D3D11_SHADER_BUFFER_DESC cbDesc;
cb->GetDesc(&cbDesc);
// Check buffer type
if (cbDesc.Type == D3D_CT_CBUFFER)
{
// Find CB slot index
@@ -132,22 +125,20 @@ namespace
// Extract resources usage
for (uint32 i = 0; i < desc.BoundResources; i++)
{
// Get resource description
D3D11_SHADER_INPUT_BIND_DESC resDesc;
reflector->GetResourceBindingDesc(i, &resDesc);
switch (resDesc.Type)
{
// Sampler
// Sampler
case D3D_SIT_SAMPLER:
break;
// Constant Buffer
// Constant Buffer
case D3D_SIT_CBUFFER:
case D3D_SIT_TBUFFER:
break;
// Shader Resource
// Shader Resource
case D3D_SIT_TEXTURE:
case D3D_SIT_STRUCTURED:
case D3D_SIT_BYTEADDRESS:
@@ -155,7 +146,7 @@ namespace
bindings.UsedSRsMask |= 1 << (resDesc.BindPoint + shift);
break;
// Unordered Access
// Unordered Access
case D3D_SIT_UAV_RWTYPED:
case D3D_SIT_UAV_RWSTRUCTURED:
case D3D_SIT_UAV_RWBYTEADDRESS:
@@ -212,7 +203,6 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
else
{
profileName += "_4_0";
if (type == ShaderStage::Domain || type == ShaderStage::Hull)
{
_context->OnError("Tessellation is not supported on DirectX 10");
@@ -221,6 +211,7 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
}
// Compile all shader function permutations
AdditionalDataVS additionalDataVS;
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
{
_macros.Clear();
@@ -253,8 +244,6 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
0,
&shader,
&errors);
// Check compilation result
if (FAILED(result))
{
const auto msg = static_cast<const char*>(errors->GetBufferPointer());
@@ -279,24 +268,68 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
reflector->GetDesc(&desc);
// Process shader reflection data
void* additionalData = nullptr;
if (type == ShaderStage::Vertex)
{
additionalData = &additionalDataVS;
additionalDataVS.Inputs.Clear();
for (UINT inputIdx = 0; inputIdx < desc.InputParameters; inputIdx++)
{
D3D11_SIGNATURE_PARAMETER_DESC inputDesc;
reflector->GetInputParameterDesc(inputIdx, &inputDesc);
if (inputDesc.ReadWriteMask == 0 || inputDesc.SystemValueType != D3D10_NAME_UNDEFINED)
continue;
auto format = PixelFormat::Unknown;
switch (inputDesc.ComponentType)
{
case D3D_REGISTER_COMPONENT_UINT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_UInt;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_UInt;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_UInt;
else
format = PixelFormat::R32_UInt;
break;
case D3D_REGISTER_COMPONENT_SINT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_SInt;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_SInt;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_SInt;
else
format = PixelFormat::R32_SInt;
break;
case D3D_REGISTER_COMPONENT_FLOAT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_Float;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_Float;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_Float;
else
format = PixelFormat::R32_Float;
break;
}
additionalDataVS.Inputs.Add({ ParseVertexElementType(inputDesc.SemanticName, inputDesc.SemanticIndex), 0, 0, 0, format });
}
}
ShaderBindings bindings = { desc.InstructionCount, 0, 0, 0 };
if (ProcessShader(_context, _constantBuffers, reflector.Get(), desc, bindings))
return true;
#ifdef GPU_USE_SHADERS_DEBUG_LAYER
// Generate debug information
if (ProcessDebugInfo(_context, meta, permutationIndex, shaderBuffer, shaderBufferSize))
return true;
#endif
// Strip shader bytecode for an optimization
ComPtr<ID3DBlob> shaderStripped;
if (!options->GenerateDebugData)
{
//auto prevShaderBufferSize = shaderBufferSize;
// Strip shader bytes
result = D3DStripShader(
shaderBuffer,
@@ -312,15 +345,12 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
// Set new buffer
shaderBuffer = shaderStripped->GetBufferPointer();
shaderBufferSize = static_cast<int32>(shaderStripped->GetBufferSize());
//auto strippedBytes = prevShaderBufferSize - shaderBufferSize;
//auto strippedBytesPercentage = Math::FloorToInt(strippedBytes * 100.0f / prevShaderBufferSize);
}
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, shaderBuffer, shaderBufferSize))
return true;
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros, additionalData))
return true;
}

View File

@@ -12,12 +12,10 @@
class ShaderCompilerD3D : public ShaderCompiler
{
private:
Array<char> _funcNameDefineBuffer;
uint32 _flags;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderCompilerD3D"/> class.
/// </summary>
@@ -25,7 +23,6 @@ public:
ShaderCompilerD3D(ShaderProfile profile);
protected:
// [ShaderCompiler]
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
bool OnCompileBegin() override;

View File

@@ -4,7 +4,6 @@
#include "ShaderCompilerDX.h"
#include "Engine/Core/Log.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Graphics/Config.h"
#include "Engine/GraphicsDevice/DirectX/DX12/Types.h"
@@ -22,12 +21,10 @@
class IncludeDX : public IDxcIncludeHandler
{
private:
ShaderCompilationContext* _context;
IDxcLibrary* _library;
public:
IncludeDX(ShaderCompilationContext* context, IDxcLibrary* library)
{
_context = context;
@@ -185,6 +182,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
}
// Compile all shader function permutations
AdditionalDataVS additionalDataVS;
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
{
_macros.Clear();
@@ -320,6 +318,54 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
shaderReflection->GetDesc(&desc);
// Process shader reflection data
void* additionalData = nullptr;
if (type == ShaderStage::Vertex)
{
additionalData = &additionalDataVS;
additionalDataVS.Inputs.Clear();
for (UINT inputIdx = 0; inputIdx < desc.InputParameters; inputIdx++)
{
D3D12_SIGNATURE_PARAMETER_DESC inputDesc;
shaderReflection->GetInputParameterDesc(inputIdx, &inputDesc);
if (inputDesc.ReadWriteMask == 0 || inputDesc.SystemValueType != D3D10_NAME_UNDEFINED)
continue;
auto format = PixelFormat::Unknown;
switch (inputDesc.ComponentType)
{
case D3D_REGISTER_COMPONENT_UINT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_UInt;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_UInt;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_UInt;
else
format = PixelFormat::R32_UInt;
break;
case D3D_REGISTER_COMPONENT_SINT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_SInt;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_SInt;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_SInt;
else
format = PixelFormat::R32_SInt;
break;
case D3D_REGISTER_COMPONENT_FLOAT32:
if (inputDesc.Mask >= 0b1111)
format = PixelFormat::R32G32B32A32_Float;
else if (inputDesc.Mask >= 0b111)
format = PixelFormat::R32G32B32_Float;
else if (inputDesc.Mask >= 0b11)
format = PixelFormat::R32G32_Float;
else
format = PixelFormat::R32_Float;
break;
}
additionalDataVS.Inputs.Add({ ParseVertexElementType(inputDesc.SemanticName, inputDesc.SemanticIndex), 0, 0, 0, format });
}
}
DxShaderHeader header;
Platform::MemoryClear(&header, sizeof(header));
ShaderBindings bindings = { desc.InstructionCount, 0, 0, 0 };
@@ -370,16 +416,16 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
shaderReflection->GetResourceBindingDesc(i, &resDesc);
switch (resDesc.Type)
{
// Sampler
// Sampler
case D3D_SIT_SAMPLER:
break;
// Constant Buffer
// Constant Buffer
case D3D_SIT_CBUFFER:
case D3D_SIT_TBUFFER:
break;
// Shader Resource
// Shader Resource
case D3D_SIT_TEXTURE:
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
{
@@ -396,7 +442,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
}
break;
// Unordered Access
// Unordered Access
case D3D_SIT_UAV_RWTYPED:
case D3D_SIT_UAV_RWSTRUCTURED:
case D3D_SIT_UAV_RWBYTEADDRESS:
@@ -415,7 +461,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize()))
return true;
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros, additionalData))
return true;
}

View File

@@ -12,14 +12,12 @@
class ShaderCompilerDX : public ShaderCompiler
{
private:
Array<char> _funcNameDefineBuffer;
void* _compiler;
void* _library;
void* _containerReflection;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderCompilerDX"/> class.
/// </summary>
@@ -32,7 +30,6 @@ public:
~ShaderCompilerDX();
protected:
// [ShaderCompiler]
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
bool OnCompileBegin() override;

View File

@@ -271,7 +271,6 @@ bool ShaderCompiler::WriteShaderFunctionBegin(ShaderCompilationContext* context,
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize)
{
auto output = context->Output;
output->Write((uint32)(cacheSize + headerSize));
output->WriteBytes(header, headerSize);
output->WriteBytes(cache, cacheSize);
@@ -293,18 +292,22 @@ bool ShaderCompiler::WriteShaderFunctionEnd(ShaderCompilationContext* context, S
return false;
}
bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros)
bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData)
{
// [Deprecated in v1.10]
auto output = context->Output;
// Write vertex shader inputs (based on compiled shader reflection) to bind any missing vertex buffer streaming at runtime (during drawing - see usage of GPUVertexLayout::Merge)
if (auto* additionalDataVS = (AdditionalDataVS*)additionalData)
output->Write(additionalDataVS->Inputs);
else
output->WriteInt32(0);
// [Deprecated in v1.10]
auto& metaVS = *(VertexShaderMeta*)&meta;
auto& layout = metaVS.InputLayout;
#if FLAXENGINE_VERSION_MAJOR > 2 || (FLAXENGINE_VERSION_MAJOR == 2 && FLAXENGINE_VERSION_MINOR >= 1)
if (layout.HasItems())
LOG(Warning, "Vertex Shader '{}' (asset '{}') uses explicit vertex layout via 'META_VS_IN_ELEMENT' macros which has been deprecated. Remove this code and migrate to GPUVertexLayout with VertexElement array in code (assigned to vertex buffer).", String(meta.Name), context->Options->TargetName);
#elif FLAXENGINE_VERSION_MAJOR == 1 && FLAXENGINE_VERSION_MINOR >= 11
if (layout.HasItems())
LOG(Warning, "Vertex Shader '{}' (asset '{}') uses explicit vertex layout via 'META_VS_IN_ELEMENT' macros which has been deprecated. Remove this code and migrate to GPUVertexLayout with VertexElement array in code (assigned to vertex buffer).", String(meta.Name), context->Options->TargetName);
#endif
// Get visible entries (based on `visible` flag switch)
@@ -443,7 +446,7 @@ bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, Shader
return false;
}
bool ShaderCompiler::WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros)
bool ShaderCompiler::WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData)
{
auto output = context->Output;
auto& metaHS = *(HullShaderMeta*)&meta;
@@ -457,7 +460,7 @@ bool ShaderCompiler::WriteCustomDataHS(ShaderCompilationContext* context, Shader
void ShaderCompiler::GetDefineForFunction(ShaderFunctionMeta& meta, Array<ShaderMacro>& macros)
{
auto& functionName = meta.Name;
const int32 functionNameLength = static_cast<int32>(functionName.Length());
const int32 functionNameLength = functionName.Length();
_funcNameDefineBuffer.Clear();
_funcNameDefineBuffer.EnsureCapacity(functionNameLength + 2);
_funcNameDefineBuffer.Add('_');
@@ -466,4 +469,35 @@ void ShaderCompiler::GetDefineForFunction(ShaderFunctionMeta& meta, Array<Shader
macros.Add({ _funcNameDefineBuffer.Get(), "1" });
}
VertexElement::Types ShaderCompiler::ParseVertexElementType(StringAnsiView semantic, uint32 index)
{
if (semantic.HasChars() && StringUtils::IsDigit(semantic[semantic.Length() - 1]))
{
// Get index from end of the name
index = semantic[semantic.Length() - 1] - '0';
semantic = StringAnsiView(semantic.Get(), semantic.Length() - 1);
}
if (semantic == "POSITION")
return VertexElement::Types::Position;
if (semantic == "COLOR")
return VertexElement::Types::Color;
if (semantic == "NORMAL")
return VertexElement::Types::Normal;
if (semantic == "TANGENT")
return VertexElement::Types::Tangent;
if (semantic == "BLENDINDICES")
return VertexElement::Types::BlendIndices;
if (semantic == "BLENDWEIGHTS" ||
semantic == "BLENDWEIGHT") // [Deprecated in v1.10]
return VertexElement::Types::BlendWeights;
if (semantic == "TEXCOORD" && index < 8)
return (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + index);
if (semantic == "ATTRIBUTE" && index < 4)
return (VertexElement::Types)((int32)VertexElement::Types::Attribute0 + index);
LOG(Warning, "Unsupported vertex shader input element semantic {}{}", semantic.ToString(), index);
return VertexElement::Types::Unknown;;
}
#endif

View File

@@ -7,6 +7,7 @@
#include "ShaderCompilationContext.h"
#include "Parser/ShaderMeta.h"
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
/// <summary>
/// Base class for the objects that can compile shaders source code.
@@ -14,7 +15,6 @@
class ShaderCompiler
{
public:
struct ShaderResourceBuffer
{
byte Slot;
@@ -23,11 +23,9 @@ public:
};
private:
Array<char> _funcNameDefineBuffer;
protected:
ShaderProfile _profile;
ShaderCompilationContext* _context = nullptr;
Array<ShaderMacro> _globalMacros;
@@ -35,7 +33,6 @@ protected:
Array<ShaderResourceBuffer> _constantBuffers;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderCompiler"/> class.
/// </summary>
@@ -51,7 +48,6 @@ public:
virtual ~ShaderCompiler() = default;
public:
/// <summary>
/// Gets shader profile supported by this compiler.
/// </summary>
@@ -85,8 +81,13 @@ public:
static void DisposeIncludedFilesCache();
protected:
// Input elements read from reflection after shader compilation. Rough approx or attributes without exact format nor bind slot (only semantics and value dimensions match).
struct AdditionalDataVS
{
GPUVertexLayout::Elements Inputs;
};
typedef bool (*WritePermutationData)(ShaderCompilationContext*, ShaderFunctionMeta&, int32, const Array<ShaderMacro>&);
typedef bool (*WritePermutationData)(ShaderCompilationContext*, ShaderFunctionMeta&, int32, const Array<ShaderMacro>&, void*);
virtual bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) = 0;
@@ -99,9 +100,11 @@ protected:
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize);
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize);
static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros);
static bool WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros);
static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData);
static bool WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData);
void GetDefineForFunction(ShaderFunctionMeta& meta, Array<ShaderMacro>& macros);
static VertexElement::Types ParseVertexElementType(StringAnsiView semantic, uint32 index = 0);
};
#endif

View File

@@ -234,14 +234,14 @@ SpirvShaderResourceType GetTextureType(const glslang::TSampler& sampler)
}
}
PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
PixelFormat GetResourceFormat(glslang::TBasicType basicType, uint32 vectorSize)
{
switch (sampler.type)
switch (basicType)
{
case glslang::EbtVoid:
return PixelFormat::Unknown;
case glslang::EbtFloat:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R32_Float;
@@ -254,7 +254,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtFloat16:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R16_Float;
@@ -265,7 +265,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtUint:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R32_UInt;
@@ -278,7 +278,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtInt:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R32_SInt;
@@ -291,7 +291,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtUint8:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R8_UInt;
@@ -302,7 +302,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtInt8:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R8_SInt;
@@ -313,7 +313,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtUint16:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R16_UInt;
@@ -324,7 +324,7 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
}
break;
case glslang::EbtInt16:
switch (sampler.vectorSize)
switch (vectorSize)
{
case 1:
return PixelFormat::R16_SInt;
@@ -340,6 +340,16 @@ PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
return PixelFormat::Unknown;
}
PixelFormat GetResourceFormat(const glslang::TSampler& sampler)
{
return GetResourceFormat(sampler.type, sampler.vectorSize);
}
PixelFormat GetResourceFormat(const glslang::TType& type)
{
return GetResourceFormat(type.getBasicType(), type.getVectorSize());
}
bool IsUavType(const glslang::TType& type)
{
if (type.getQualifier().isReadOnly())
@@ -611,6 +621,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
EShMessages messages = (EShMessages)(EShMsgReadHlsl | EShMsgSpvRules | EShMsgVulkanRules);
// Compile all shader function permutations
AdditionalDataVS additionalDataVS;
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
{
#if PRINT_DESCRIPTORS
@@ -721,157 +732,167 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
}
// Process shader reflection data
void* additionalData = nullptr;
SpirvShaderHeader header;
Platform::MemoryClear(&header, sizeof(header));
ShaderBindings bindings = { 0, 0, 0, 0 };
if (type == ShaderStage::Vertex)
{
// Extract constant buffers usage information
for (int blockIndex = 0; blockIndex < program.getNumLiveUniformBlocks(); blockIndex++)
additionalData = &additionalDataVS;
additionalDataVS.Inputs.Clear();
for (int inputIndex = 0; inputIndex < program.getNumPipeInputs(); inputIndex++)
{
auto size = program.getUniformBlockSize(blockIndex);
auto uniform = program.getUniformBlockTType(blockIndex);
auto& qualifier = uniform->getQualifier();
auto binding = (int32)qualifier.layoutBinding;
if (!qualifier.hasBinding())
{
// Each uniform must have a valid binding
//LOG(Warning, "Found a uniform block \'{0}\' without a binding qualifier. Each uniform block must have an explicitly defined binding number.", String(uniform->getTypeName().c_str()));
const glslang::TObjectReflection& input = program.getPipeInput(inputIndex);
if (!input.getType() || input.getType()->containsBuiltIn())
continue;
}
additionalDataVS.Inputs.Add({ ParseVertexElementType(input.getType()->getQualifier().semanticName), 0, 0, 0, GetResourceFormat(*input.getType()) });
}
}
for (int blockIndex = 0; blockIndex < program.getNumLiveUniformBlocks(); blockIndex++)
{
auto size = program.getUniformBlockSize(blockIndex);
auto uniform = program.getUniformBlockTType(blockIndex);
auto& qualifier = uniform->getQualifier();
auto binding = (int32)qualifier.layoutBinding;
// Shared storage buffer
if (qualifier.storage == glslang::EvqBuffer)
if (!qualifier.hasBinding())
{
// Each uniform must have a valid binding
//LOG(Warning, "Found a uniform block \'{0}\' without a binding qualifier. Each uniform block must have an explicitly defined binding number.", String(uniform->getTypeName().c_str()));
continue;
}
// Shared storage buffer
if (qualifier.storage == glslang::EvqBuffer)
{
// RWBuffer
}
else
{
// Uniform buffer
bool found = false;
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
{
// RWBuffer
auto& descriptor = descriptorsCollector.Descriptors[i];
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB && descriptor.Binding == binding)
{
found = true;
descriptor.Size = size;
break;
}
}
else
if (!found)
{
// Uniform buffer
bool found = false;
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
{
auto& descriptor = descriptorsCollector.Descriptors[i];
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB && descriptor.Binding == binding)
{
found = true;
descriptor.Size = size;
break;
}
}
if (!found)
{
LOG(Warning, "Failed to find descriptor for the uniform block \'{0}\' of size {1} (bytes), binding: {2}.", String(uniform->getTypeName().c_str()), size, binding);
}
LOG(Warning, "Failed to find descriptor for the uniform block \'{0}\' of size {1} (bytes), binding: {2}.", String(uniform->getTypeName().c_str()), size, binding);
}
}
}
#if PRINT_UNIFORMS
// Debug printing all uniforms
for (int32 index = 0; index < program.getNumLiveUniformVariables(); index++)
{
auto uniform = program.getUniformTType(index);
auto qualifier = uniform->getQualifier();
if (!uniform->isArray())
LOG(Warning, "Shader {0}:{1} - uniform: {2} {3} at binding {4}",
_context->TargetNameAnsi,
String(meta.Name),
uniform->getCompleteString().c_str(),
program.getUniformName(index),
qualifier.layoutBinding
);
}
// Debug printing all uniforms
for (int32 index = 0; index < program.getNumLiveUniformVariables(); index++)
{
auto uniform = program.getUniformTType(index);
auto qualifier = uniform->getQualifier();
if (!uniform->isArray())
LOG(Warning, "Shader {0}:{1} - uniform: {2} {3} at binding {4}",
_context->TargetNameAnsi,
String(meta.Name),
uniform->getCompleteString().c_str(),
program.getUniformName(index),
qualifier.layoutBinding
);
}
#endif
// Process all descriptors
header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images;
header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers;
header.DescriptorInfo.TexelBufferViewsCount = descriptorsCollector.TexelBuffers;
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
// Process all descriptors
header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images;
header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers;
header.DescriptorInfo.TexelBufferViewsCount = descriptorsCollector.TexelBuffers;
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
{
auto& descriptor = descriptorsCollector.Descriptors[i];
// Skip cases (eg. AppendStructuredBuffer counter buffer)
if (descriptor.Slot == MAX_uint16)
continue;
auto& d = header.DescriptorInfo.DescriptorTypes[header.DescriptorInfo.DescriptorTypesCount++];
d.Binding = descriptor.Binding;
d.Set = stageSet;
d.Slot = descriptor.Slot;
d.BindingType = descriptor.BindingType;
d.DescriptorType = descriptor.DescriptorType;
d.ResourceType = descriptor.ResourceType;
d.ResourceFormat = descriptor.ResourceFormat;
d.Count = descriptor.Count;
switch (descriptor.BindingType)
{
auto& descriptor = descriptorsCollector.Descriptors[i];
case SpirvShaderResourceBindingType::CB:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_CB_BINDED);
bindings.UsedCBsMask |= 1 << descriptor.Slot;
break;
case SpirvShaderResourceBindingType::SRV:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_SR_BINDED);
bindings.UsedSRsMask |= 1 << descriptor.Slot;
break;
case SpirvShaderResourceBindingType::UAV:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_UA_BINDED);
bindings.UsedUAsMask |= 1 << descriptor.Slot;
break;
}
// Skip cases (eg. AppendStructuredBuffer counter buffer)
if (descriptor.Slot == MAX_uint16)
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB)
{
if (descriptor.Size == -1)
{
// Skip unused constant buffers
continue;
auto& d = header.DescriptorInfo.DescriptorTypes[header.DescriptorInfo.DescriptorTypesCount++];
d.Binding = descriptor.Binding;
d.Set = stageSet;
d.Slot = descriptor.Slot;
d.BindingType = descriptor.BindingType;
d.DescriptorType = descriptor.DescriptorType;
d.ResourceType = descriptor.ResourceType;
d.ResourceFormat = descriptor.ResourceFormat;
d.Count = descriptor.Count;
switch (descriptor.BindingType)
}
if (descriptor.Size == 0)
{
case SpirvShaderResourceBindingType::CB:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_CB_BINDED);
bindings.UsedCBsMask |= 1 << descriptor.Slot;
break;
case SpirvShaderResourceBindingType::SRV:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_SR_BINDED);
bindings.UsedSRsMask |= 1 << descriptor.Slot;
break;
case SpirvShaderResourceBindingType::UAV:
ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_UA_BINDED);
bindings.UsedUAsMask |= 1 << descriptor.Slot;
break;
LOG(Warning, "Found constant buffer \'{1}\' at slot {0} but it's not used or has no valid size.", descriptor.Slot, String(descriptor.Name.c_str()));
continue;
}
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB)
for (int32 b = 0; b < _constantBuffers.Count(); b++)
{
if (descriptor.Size == -1)
auto& cc = _constantBuffers[b];
if (cc.Slot == descriptor.Slot)
{
// Skip unused constant buffers
continue;
}
if (descriptor.Size == 0)
{
LOG(Warning, "Found constant buffer \'{1}\' at slot {0} but it's not used or has no valid size.", descriptor.Slot, String(descriptor.Name.c_str()));
continue;
}
for (int32 b = 0; b < _constantBuffers.Count(); b++)
{
auto& cc = _constantBuffers[b];
if (cc.Slot == descriptor.Slot)
{
// Mark as used and cache some data
cc.IsUsed = true;
cc.Size = descriptor.Size;
break;
}
// Mark as used and cache some data
cc.IsUsed = true;
cc.Size = descriptor.Size;
break;
}
}
}
#if PRINT_DESCRIPTORS
String type;
switch (descriptor.BindingType)
{
case SpirvShaderResourceBindingType::INVALID:
type = TEXT("INVALID");
break;
case SpirvShaderResourceBindingType::CB:
type = TEXT("CB");
break;
case SpirvShaderResourceBindingType::SAMPLER:
type = TEXT("SAMPLER");
break;
case SpirvShaderResourceBindingType::SRV:
type = TEXT("SRV");
break;
case SpirvShaderResourceBindingType::UAV:
type = TEXT("UAV");
break;
default:
type = TEXT("?");
}
LOG(Warning, "VULKAN SHADER RESOURCE: slot: {1}, binding: {2}, name: {0}, type: {3}", String(descriptor.Name.c_str()), descriptor.Slot, descriptor.Binding, type);
#endif
String type;
switch (descriptor.BindingType)
{
case SpirvShaderResourceBindingType::INVALID:
type = TEXT("INVALID");
break;
case SpirvShaderResourceBindingType::CB:
type = TEXT("CB");
break;
case SpirvShaderResourceBindingType::SAMPLER:
type = TEXT("SAMPLER");
break;
case SpirvShaderResourceBindingType::SRV:
type = TEXT("SRV");
break;
case SpirvShaderResourceBindingType::UAV:
type = TEXT("UAV");
break;
default:
type = TEXT("?");
}
LOG(Warning, "VULKAN SHADER RESOURCE: slot: {1}, binding: {2}, name: {0}, type: {3}", String(descriptor.Name.c_str()), descriptor.Slot, descriptor.Binding, type);
#endif
}
// Generate SPIR-V (optimize it at the same time)
@@ -916,7 +937,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), &spirv[0], spirvBytesCount))
return true;
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros, additionalData))
return true;
}

View File

@@ -12,11 +12,9 @@
class ShaderCompilerVulkan : public ShaderCompiler
{
private:
Array<char> _funcNameDefineBuffer;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderCompilerVulkan"/> class.
/// </summary>
@@ -29,7 +27,6 @@ public:
~ShaderCompilerVulkan();
protected:
// [ShaderCompiler]
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
bool OnCompileBegin() override;