Flatten user spec constants when debugging shaders. Closes #3803

This commit is contained in:
baldurk
2026-03-02 15:05:57 +00:00
parent 4287c87561
commit 4151720bdc
3 changed files with 73 additions and 15 deletions
@@ -1094,6 +1094,63 @@ rdcpair<Id, Id> Editor::AddBufferVariable(rdcarray<Id> &addedGlobals, Id varType
return ret;
}
void Editor::FlattenSpecConstants(const rdcarray<SpecConstant> &userSpec)
{
// patch all bindings up trivially to account for the extra reservation
for(Iter it = Begin(Section::Annotations), end = End(Section::Annotations); it < end; ++it)
{
if(it.opcode() == Op::Decorate)
{
OpDecorate dec(it);
if(dec.decoration == Decoration::SpecId)
{
for(const SpecConstant &s : userSpec)
{
if(s.specID == dec.decoration.specId)
{
Iter target = GetID(dec.target);
if(target.opcode() == Op::SpecConstantTrue || target.opcode() == Op::SpecConstantFalse)
{
RDCCOMPILE_ASSERT(
OpConstantTrue::FixedWordSize == OpSpecConstantTrue::FixedWordSize &&
OpConstantTrue::FixedWordSize == OpSpecConstantFalse::FixedWordSize &&
OpConstantTrue::FixedWordSize == OpConstantFalse::FixedWordSize,
"OpConstantTrue and False should be interchangeable and equal to Spec versions");
OpSpecConstantTrue orig(target);
if(s.value != 0)
target = OpConstantTrue(orig.resultType, orig.result);
else
target = OpConstantFalse(orig.resultType, orig.result);
}
else if(target.opcode() == Op::SpecConstant)
{
rdcarray<uint32_t> data;
data.assign(target.words() + 1, target.size() - 1);
// first two IDs are type and result
size_t constSize = data.byteSize() - 2 * sizeof(Id);
RDCASSERTEQUAL(s.dataSize, constSize);
memcpy(&data[2], &s.value, RDCMIN(s.dataSize, constSize));
target = Operation(Op::Constant, data);
}
break;
}
}
// remove the spec id whether we found one or not. If we didn't find one, the default should be used
it.nopRemove();
}
}
}
}
#define TYPETABLE(StructType, variable) \
template <> \
std::map<StructType, Id> &Editor::GetTable<StructType>() \
@@ -140,6 +140,8 @@ public:
Id AddConstant(const Operation &op);
void AddFunction(const OperationList &ops);
void FlattenSpecConstants(const rdcarray<SpecConstant> &userSpec);
Iter GetID(Id id);
// the entry point has 'two' opcodes, the entrypoint declaration and the function.
// This returns the first, GetID returns the second.
+14 -15
View File
@@ -3767,7 +3767,7 @@ static rdcspv::Id AddQuadSwizzleHelper(rdcspv::Editor &editor, uint32_t count)
return func;
}
static void CreateInputFetcher(rdcarray<uint32_t> &spv,
static void CreateInputFetcher(rdcarray<uint32_t> &spv, const rdcarray<SpecConstant> &userSpec,
VulkanCreationInfo::ShaderModuleReflection &shadRefl,
BufferStorageMode storageMode, bool usePrimitiveID, bool useSampleID,
bool useViewIndex, SubgroupCapability subgroupCapability,
@@ -3815,6 +3815,8 @@ static void CreateInputFetcher(rdcarray<uint32_t> &spv,
editor.Prepare();
editor.SetBufferStorageMode(storageMode);
editor.FlattenSpecConstants(userSpec);
// remove any OpSource
{
// remove any OpName that refers to deleted IDs - functions or results
@@ -5471,7 +5473,7 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u
VkSpecializationInfo patchedSpecInfo = MakeSpecInfo(specData, specMaps);
auto patchCallback = [this, &shadRefl, &patchedSpecInfo, useViewIndex, subgroupCapability,
auto patchCallback = [this, &spec, &shadRefl, &patchedSpecInfo, useViewIndex, subgroupCapability,
maxSubgroupSize](const AddedDescriptorData &patchedBufferdata,
VkShaderStageFlagBits stage, const char *entryName,
const rdcarray<uint32_t> &origSpirv,
@@ -5485,14 +5487,13 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/debug_vsinput_before.spv", modSpirv);
CreateInputFetcher(modSpirv, shadRefl, m_StorageMode, false, false, useViewIndex,
CreateInputFetcher(modSpirv, spec, shadRefl, m_StorageMode, false, false, useViewIndex,
subgroupCapability, maxSubgroupSize);
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/debug_vsinput_after.spv", modSpirv);
// overwrite user's specialisation info, assuming that the old specialisation info is not
// relevant for codegen (the only thing it would be used for)
// overwrite user's specialisation info. We flattened the user's spec constants when patching the SPIR-V above.
specInfo = &patchedSpecInfo;
return true;
@@ -6039,7 +6040,7 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_
VkSpecializationInfo patchedSpecInfo = MakeSpecInfo(specData, specMaps);
auto patchCallback = [this, &shadRefl, &patchedSpecInfo, usePrimitiveID, useSampleID,
auto patchCallback = [this, &spec, &shadRefl, &patchedSpecInfo, usePrimitiveID, useSampleID,
useViewIndex, subgroupCapability, maxSubgroupSize](
const AddedDescriptorData &patchedBufferdata, VkShaderStageFlagBits stage,
const char *entryName, const rdcarray<uint32_t> &origSpirv,
@@ -6052,14 +6053,13 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/debug_psinput_before.spv", modSpirv);
CreateInputFetcher(modSpirv, shadRefl, m_StorageMode, usePrimitiveID, useSampleID, useViewIndex,
subgroupCapability, maxSubgroupSize);
CreateInputFetcher(modSpirv, spec, shadRefl, m_StorageMode, usePrimitiveID, useSampleID,
useViewIndex, subgroupCapability, maxSubgroupSize);
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/debug_psinput_after.spv", modSpirv);
// overwrite user's specialisation info, assuming that the old specialisation info is not
// relevant for codegen (the only thing it would be used for)
// overwrite user's specialisation info. We flattened the user's spec constants when patching the SPIR-V above.
specInfo = &patchedSpecInfo;
return true;
@@ -6575,7 +6575,7 @@ ShaderDebugTrace *VulkanReplay::DebugComputeCommon(ShaderStage stage, uint32_t e
VkSpecializationInfo patchedSpecInfo = MakeSpecInfo(specData, specMaps);
auto patchCallback = [this, stageBit, &shadRefl, &patchedSpecInfo, subgroupCapability,
auto patchCallback = [this, stageBit, &spec, &shadRefl, &patchedSpecInfo, subgroupCapability,
maxSubgroupSize](const AddedDescriptorData &patchedBufferdata,
VkShaderStageFlagBits stage, const char *entryName,
const rdcarray<uint32_t> &origSpirv,
@@ -6597,14 +6597,13 @@ ShaderDebugTrace *VulkanReplay::DebugComputeCommon(ShaderStage stage, uint32_t e
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/before_" + filename[idx], modSpirv);
CreateInputFetcher(modSpirv, shadRefl, m_StorageMode, false, false, false, subgroupCapability,
maxSubgroupSize);
CreateInputFetcher(modSpirv, spec, shadRefl, m_StorageMode, false, false, false,
subgroupCapability, maxSubgroupSize);
if(!Vulkan_Debug_PSDebugDumpDirPath().empty())
FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/after_" + filename[idx], modSpirv);
// overwrite user's specialisation info, assuming that the old specialisation info is not
// relevant for codegen (the only thing it would be used for)
// overwrite user's specialisation info. We flattened the user's spec constants when patching the SPIR-V above.
specInfo = &patchedSpecInfo;
return true;