[Fix] (ShaderTranspiler): deduplicate image types after Vulkan image rewrites

This commit is contained in:
2026-10-05 00:08:32 -04:00
parent 2f1a673513
commit 4d6904184d
2 changed files with 59 additions and 5 deletions
@@ -123,10 +123,12 @@ TEST_F(ProgramUtilTest, UnformattedFloatStorageImagesKeepIntegerAtomicImagesType
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(rgba16, binding = 0) uniform image2D floatImage;
layout(r32ui, binding = 1) uniform uimage2D atomicImage;
layout(rgba32f, binding = 2) uniform image2D otherFloatImages[2];
void main() {
ivec2 coordinate = ivec2(gl_GlobalInvocationID.xy);
imageStore(floatImage, coordinate, imageLoad(floatImage, coordinate));
imageStore(otherFloatImages[1], coordinate, imageLoad(otherFloatImages[0], coordinate));
imageAtomicAdd(atomicImage, coordinate, 1u);
}
)";
@@ -174,6 +176,10 @@ void main() {
<< outputText;
EXPECT_EQ(countOccurrences(outputText, "OpCapability StorageImageWriteWithoutFormat"), 1u)
<< outputText;
EXPECT_EQ(countOccurrences(outputText, "2D 0 0 0 2 Unknown"), 1u) << outputText;
EXPECT_NE(outputText.find("Binding 0"), String::npos) << outputText;
EXPECT_NE(outputText.find("Binding 1"), String::npos) << outputText;
EXPECT_NE(outputText.find("Binding 2"), String::npos) << outputText;
EXPECT_TRUE(tools.Validate(outputBinary));
Vector<Uint32> secondOutputBinary;
@@ -181,6 +187,49 @@ void main() {
EXPECT_EQ(secondOutputBinary, outputBinary);
}
TEST_F(ProgramUtilTest, LowerRectImagesMergesWithExisting2DImageTypes) {
using namespace MG_Util::ShaderTranspiler;
const String spirvText = R"(
OpCapability Shader
OpCapability ImageRect
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %rect DescriptorSet 0
OpDecorate %rect Binding 0
OpDecorate %flat DescriptorSet 0
OpDecorate %flat Binding 1
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%rectType = OpTypeImage %uint Rect 0 0 0 2 R32ui
%flatType = OpTypeImage %uint 2D 0 0 0 2 R32ui
%rectPtr = OpTypePointer UniformConstant %rectType
%flatPtr = OpTypePointer UniformConstant %flatType
%rect = OpVariable %rectPtr UniformConstant
%flat = OpVariable %flatPtr UniformConstant
%mainType = OpTypeFunction %void
%main = OpFunction %void None %mainType
%entry = OpLabel
%r = OpLoad %rectType %rect
%f = OpLoad %flatType %flat
OpReturn
OpFunctionEnd
)";
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
Vector<Uint32> inputBinary;
ASSERT_TRUE(tools.Assemble(spirvText, &inputBinary));
Vector<Uint32> outputBinary;
ASSERT_TRUE(ShaderCompiler::LowerRectImages(inputBinary, outputBinary));
EXPECT_TRUE(tools.Validate(outputBinary));
String outputText;
ASSERT_TRUE(tools.Disassemble(outputBinary, &outputText));
EXPECT_NE(outputText.find("Binding 0"), String::npos);
EXPECT_NE(outputText.find("Binding 1"), String::npos);
const auto firstImage = outputText.find("OpTypeImage");
ASSERT_NE(firstImage, String::npos);
EXPECT_EQ(outputText.find("OpTypeImage", firstImage + 1), String::npos);
}
TEST_F(ProgramUtilTest, UnformattedFloatStorageImagesKeepFloatAtomicImageTypesTyped) {
using namespace MG_Util::ShaderTranspiler;
@@ -1813,6 +1813,8 @@ namespace MobileGL {
using namespace spvtools;
Optimizer optimizer(SPV_ENV_VULKAN_1_1);
optimizer.RegisterPass(NormalizeRectCoordinatesPass::CreateNormalizeRectCoordinatesPass());
// Rect and 2D images of the same format become the same type after lowering.
optimizer.RegisterPass(CreateRemoveDuplicatesPass());
return RunOptimizerChecked("LowerRectImages", optimizer, inputBinary, outputBinary, true, enableSpirvValidation);
}
@@ -2103,11 +2105,14 @@ namespace MobileGL {
}
outputBinary.insert(outputBinary.begin() + static_cast<std::ptrdiff_t>(capabilityInsertOffset),
addedCapabilities.begin(), addedCapabilities.end());
// Hand-rolled word walk, so no Optimizer wrapper ever sees this rewrite;
// check the modified module explicitly in validating lanes.
ValidateOrLatch("UseUnformattedFloatStorageImagesForVulkan", outputBinary,
enableSpirvValidation);
return true;
// Erasing the format can make distinct image types identical. Use the same
// cleanup as the image-widening/lowering paths to merge their dependent types
// and update every use before validating the rewritten module.
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
Vector<Uint32> rewrittenBinary = std::move(outputBinary);
return RunOptimizerChecked("UseUnformattedFloatStorageImagesForVulkan", optimizer,
rewrittenBinary, outputBinary, true, enableSpirvValidation);
}
namespace {