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

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