[spirv-reader] Run ResolveBindingConflictsPass pass
Binding conflicts can be produced by SplitCombinedImageSamplerPass, or
be present in the original SPIR-V. If no sampler_mappings are
provided, run this SPIR-V opt pass to resolve conflicts.
Fixed: 435251397
Change-Id: Ieb437aa3ae009d0154283955a89026fbc4eee84a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/256695
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/reader/common/options.h b/src/tint/lang/spirv/reader/common/options.h
index c6a56a9..32d13d3 100644
--- a/src/tint/lang/spirv/reader/common/options.h
+++ b/src/tint/lang/spirv/reader/common/options.h
@@ -46,6 +46,8 @@
/// Mapping from a SPIR-V Sampler binding point to a WGSL sampler binding
/// point. This allows remapping samplers which are split out of the
/// combined texture/sampler pairs in SPIR-V.
+ /// If this map is empty, any binding conflicts will be automatically resolved by incrementing
+ /// binding numbers until they are unique.
std::unordered_map<BindingPoint, BindingPoint> sampler_mappings{};
};
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 5e82caf..2704f96 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -44,6 +44,7 @@
TINT_BEGIN_DISABLE_WARNING(WEAK_VTABLES);
TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
#include "source/opt/build_module.h"
+#include "source/opt/resolve_binding_conflicts_pass.h"
#include "source/opt/split_combined_image_sampler_pass.h"
TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
TINT_END_DISABLE_WARNING(WEAK_VTABLES);
@@ -108,6 +109,7 @@
return Failure("failed to build the internal representation of the module");
}
+ // Run SPIR-V opt transforms to make the input friendlier for the SPIR-V frontend.
{
spvtools::opt::SplitCombinedImageSamplerPass pass;
auto status = pass.Run(spirv_context_.get());
@@ -115,6 +117,13 @@
return Failure("failed to run SplitCombinedImageSamplerPass in SPIR-V opt");
}
}
+ if (options_.sampler_mappings.empty()) {
+ spvtools::opt::ResolveBindingConflictsPass pass;
+ auto status = pass.Run(spirv_context_.get());
+ if (status == spvtools::opt::Pass::Status::Failure) {
+ return Failure("failed to run ResolveBindingConflictsPass in SPIR-V opt");
+ }
+ }
// Check for unsupported extensions.
for (const auto& ext : spirv_context_->extensions()) {
diff --git a/src/tint/lang/spirv/reader/reader_test.cc b/src/tint/lang/spirv/reader/reader_test.cc
index 7b65f1c..9563d39 100644
--- a/src/tint/lang/spirv/reader/reader_test.cc
+++ b/src/tint/lang/spirv/reader/reader_test.cc
@@ -683,5 +683,146 @@
)");
}
+// In Vulkan it is valid to have a texture and sampler with the same set and binding, so we use the
+// `ResolveBindingConflictsPass` SPIR-V tools pass to remove the conflicts.
+// See crbug.com/435251397.
+TEST_F(SpirvReaderTest, ResolveBindingConflicts) {
+ auto got = Run(R"(
+; SPIR-V
+; Version: 1.0
+; Generator: Google spiregg; 0
+; Bound: 40
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %PS_Viewport "PS_Viewport" %in_var_TEXCOORD0 %out_var_SV_Target
+ OpExecutionMode %PS_Viewport OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_constants_ "type.constants_"
+ OpMemberName %type_constants_ 0 "constants"
+ OpName %Constants "Constants"
+ OpMemberName %Constants 0 "color"
+ OpMemberName %Constants 1 "scale"
+ OpMemberName %Constants 2 "padding"
+ OpName %constants_ "constants_"
+ OpName %type_2d_image "type.2d.image"
+ OpName %tex "tex"
+ OpName %type_sampler "type.sampler"
+ OpName %samplerTex "samplerTex"
+ OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0"
+ OpName %out_var_SV_Target "out.var.SV_Target"
+ OpName %PS_Viewport "PS_Viewport"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD0 Location 0
+ OpDecorate %out_var_SV_Target Location 0
+ OpDecorate %constants_ DescriptorSet 0
+ OpDecorate %constants_ Binding 0
+ OpDecorate %tex DescriptorSet 0
+ OpDecorate %tex Binding 0
+ OpDecorate %samplerTex DescriptorSet 0
+ OpDecorate %samplerTex Binding 0
+ OpMemberDecorate %Constants 0 Offset 0
+ OpMemberDecorate %Constants 1 Offset 16
+ OpMemberDecorate %Constants 2 Offset 24
+ OpMemberDecorate %type_constants_ 0 Offset 0
+ OpDecorate %type_constants_ Block
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+ %v4float = OpTypeVector %float 4
+ %v2float = OpTypeVector %float 2
+ %Constants = OpTypeStruct %v4float %v2float %v2float
+%type_constants_ = OpTypeStruct %Constants
+%_ptr_Uniform_type_constants_ = OpTypePointer Uniform %type_constants_
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %24 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %bool = OpTypeBool
+ %constants_ = OpVariable %_ptr_Uniform_type_constants_ Uniform
+ %tex = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %samplerTex = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+%PS_Viewport = OpFunction %void None %24
+ %27 = OpLabel
+ %28 = OpLoad %v2float %in_var_TEXCOORD0
+ %29 = OpAccessChain %_ptr_Uniform_v4float %constants_ %int_0 %int_0
+ %30 = OpLoad %v4float %29
+ %31 = OpLoad %type_2d_image %tex
+ %32 = OpLoad %type_sampler %samplerTex
+ %33 = OpSampledImage %type_sampled_image %31 %32
+ %34 = OpImageSampleImplicitLod %v4float %33 %28 None
+ %35 = OpFMul %v4float %30 %34
+ %36 = OpCompositeExtract %float %35 3
+ %37 = OpFOrdLessThan %bool %36 %float_0
+ OpSelectionMerge %38 None
+ OpBranchConditional %37 %39 %38
+ %39 = OpLabel
+ OpKill
+ %38 = OpLabel
+ OpStore %out_var_SV_Target %35
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(got, Success);
+ EXPECT_EQ(got, R"(
+Constants = struct @align(16) {
+ color:vec4<f32> @offset(0)
+ scale:vec2<f32> @offset(16)
+ padding:vec2<f32> @offset(24)
+}
+
+type.constants_ = struct @align(16) {
+ constants:Constants @offset(0)
+}
+
+$B1: { # root
+ %constants_:ptr<uniform, type.constants_, read> = var undef @binding_point(0, 0)
+ %tex:ptr<handle, texture_2d<f32>, read> = var undef @binding_point(0, 1)
+ %samplerTex:ptr<handle, sampler, read> = var undef @binding_point(0, 2)
+ %out.var.SV_Target:ptr<private, vec4<f32>, read_write> = var undef
+}
+
+%PS_Viewport_inner = func(%in.var.TEXCOORD0:vec2<f32>):void {
+ $B2: {
+ %7:ptr<uniform, vec4<f32>, read> = access %constants_, 0i, 0i
+ %8:vec4<f32> = load %7
+ %9:texture_2d<f32> = load %tex
+ %10:sampler = load %samplerTex
+ %11:vec4<f32> = textureSample %9, %10, %in.var.TEXCOORD0
+ %12:vec4<f32> = mul %8, %11
+ %13:f32 = access %12, 3u
+ %14:bool = lt %13, 0.0f
+ if %14 [t: $B3, f: $B4] { # if_1
+ $B3: { # true
+ discard
+ ret
+ }
+ $B4: { # false
+ exit_if # if_1
+ }
+ }
+ store %out.var.SV_Target, %12
+ ret
+ }
+}
+%PS_Viewport = @fragment func(%in.var.TEXCOORD0_1:vec2<f32> [@location(0)]):vec4<f32> [@location(0)] { # %in.var.TEXCOORD0_1: 'in.var.TEXCOORD0'
+ $B5: {
+ %17:void = call %PS_Viewport_inner, %in.var.TEXCOORD0_1
+ %18:vec4<f32> = load %out.var.SV_Target
+ ret %18
+ }
+}
+)");
+}
+
} // namespace
} // namespace tint::spirv::reader