Files
UnrealEngine/Engine/Source/Developer/ShaderCompilerCommon/Private/SpirvReflectCommon.cpp
2025-05-18 13:04:45 +08:00

219 lines
5.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SpirvReflectCommon.h"
void FSpirvReflectBindings::AddDescriptorBinding(SpvReflectDescriptorBinding* InBinding)
{
check(InBinding != nullptr);
switch (InBinding->resource_type)
{
case SPV_REFLECT_RESOURCE_FLAG_SAMPLER:
// Gather sampler states (i.e. SamplerState or SamplerComparisonState)
check(InBinding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER);
if (InBinding->accessed)
{
Samplers.Add(InBinding);
}
break;
case SPV_REFLECT_RESOURCE_FLAG_CBV:
// Gather constant buffers (i.e. cbuffer or ConstantBuffer<T>).
check(InBinding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
if (InBinding->accessed)
{
UniformBuffers.Add(InBinding);
}
break;
case SPV_REFLECT_RESOURCE_FLAG_SRV:
// Gather SRV resources (e.g. Buffer, StructuredBuffer, Texture2D etc.)
switch (InBinding->descriptor_type)
{
/*case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
{
if (InBinding->accessed)
{
Samplers.Add(InBinding);
}
break;
}*/
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
if (InBinding->accessed)
{
TextureSRVs.Add(InBinding);
}
break;
case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
if (InBinding->accessed)
{
TBufferSRVs.Add(InBinding);
}
break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER:
if (InBinding->accessed)
{
SBufferSRVs.Add(InBinding);
}
break;
case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
if (InBinding->accessed)
{
AccelerationStructures.Add(InBinding);
}
break;
default:
// check(false);
break;
}
break;
case SPV_REFLECT_RESOURCE_FLAG_UAV:
if (InBinding->uav_counter_binding)
{
AtomicCounters.Add(InBinding->uav_counter_binding);
}
switch (InBinding->descriptor_type)
{
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE:
TextureUAVs.Add(InBinding);
break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
TBufferUAVs.Add(InBinding);
break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER:
if (!AtomicCounters.Contains(InBinding) || InBinding->accessed)
{
SBufferUAVs.Add(InBinding);
}
break;
default:
break;
}
break;
default:
// Gather input attachments (e.g. subpass inputs)
switch (InBinding->descriptor_type)
{
case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
if (InBinding->accessed)
{
InputAttachments.Add(InBinding);
}
break;
default:
break;
}
break;
} // switch
}
void FSpirvReflectBindings::GatherDescriptorBindings(const spv_reflect::ShaderModule& SpirvReflection)
{
// Enumerate all binding points
uint32 NumBindings = 0;
SpvReflectResult SpvResult = SpirvReflection.EnumerateDescriptorBindings(&NumBindings, nullptr);
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
if (NumBindings > 0)
{
TArray<SpvReflectDescriptorBinding*> Bindings;
Bindings.SetNum(NumBindings);
SpvResult = SpirvReflection.EnumerateDescriptorBindings(&NumBindings, Bindings.GetData());
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
// Extract all the bindings first so that we process them in order - this lets us assign UAVs before other resources
// Which is necessary to match the D3D binding scheme.
for (SpvReflectDescriptorBinding* BindingEntry : Bindings)
{
AddDescriptorBinding(BindingEntry);
}
}
}
void FSpirvReflectBindings::GatherInputAttributes(const spv_reflect::ShaderModule& SpirvReflection)
{
// Enumerate all input attributes
uint32 NumInputAttributes = 0;
SpvReflectResult SpvResult = SpirvReflection.EnumerateEntryPointInputVariables(SpirvReflection.GetEntryPointName(), &NumInputAttributes, nullptr);
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
if (NumInputAttributes > 0)
{
InputAttributes.SetNum(NumInputAttributes);
SpvResult = SpirvReflection.EnumerateEntryPointInputVariables(SpirvReflection.GetEntryPointName(), &NumInputAttributes, InputAttributes.GetData());
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
}
}
void FSpirvReflectBindings::GatherOutputAttributes(const spv_reflect::ShaderModule & SpirvReflection)
{
// Enumerate all output attributes
uint32 NumOutputAttributes = 0;
SpvReflectResult SpvResult = SpirvReflection.EnumerateEntryPointOutputVariables(SpirvReflection.GetEntryPointName(), &NumOutputAttributes, nullptr);
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
if (NumOutputAttributes > 0)
{
OutputAttributes.SetNum(NumOutputAttributes);
SpvResult = SpirvReflection.EnumerateEntryPointOutputVariables(SpirvReflection.GetEntryPointName(), &NumOutputAttributes, OutputAttributes.GetData());
check(SpvResult == SPV_REFLECT_RESULT_SUCCESS);
}
}
// Parse the index from a semantic name, e.g. "ATTRIBUTE14" returns 14.
static bool ParseSemanticIndex(const ANSICHAR* InName, int32& OutSemanticIndex)
{
if (!InName)
{
return false;
}
for (int32 NameLen = FCStringAnsi::Strlen(InName), Index = NameLen; Index > 0; --Index)
{
if (!FChar::IsDigit(InName[Index - 1]))
{
if (Index == NameLen)
{
// Semantic name does not end with digits
return false;
}
else
{
// Return suffix numeric starting at previous string position
OutSemanticIndex = FCStringAnsi::Atoi(InName + Index);
return true;
}
}
}
return false;
}
void FSpirvReflectBindings::AssignInputAttributeLocationsBySemanticIndex(spv_reflect::ShaderModule& SpirvReflection, const ANSICHAR* SemanticName)
{
const int32 SemanticNameLen = FCStringAnsi::Strlen(SemanticName);
for (SpvReflectInterfaceVariable* InterfaceVar : InputAttributes)
{
if (InterfaceVar->built_in == -1 && InterfaceVar->name && FCStringAnsi::Strncmp(InterfaceVar->name, SemanticName, SemanticNameLen) == 0)
{
int32 Location = 0;
if (ParseSemanticIndex(InterfaceVar->name, Location))
{
SpirvReflection.ChangeInputVariableLocation(InterfaceVar, static_cast<uint32>(Location));
}
}
}
}