Validate binding array in IR.
Update the IR validator to validate the `binding_array` requirements.
Fixed: 441538564
Change-Id: I5656f35108c97392bef6fdc3c6132d7fc7b97167
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/260314
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 105c3f3..1f0e65b 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -81,6 +81,7 @@
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/lang/core/ir/var.h"
#include "src/tint/lang/core/type/array.h"
+#include "src/tint/lang/core/type/binding_array.h"
#include "src/tint/lang/core/type/bool.h"
#include "src/tint/lang/core/type/f16.h"
#include "src/tint/lang/core/type/f32.h"
@@ -2073,6 +2074,20 @@
}
return true;
},
+ [&](const core::type::BindingArray* t) {
+ if (!t->Count()->Is<core::type::ConstantArrayCount>()) {
+ diag() << "binding_array count must be a constant expression";
+ return false;
+ }
+
+ if (!capabilities_.Contains(Capability::kAllowNonCoreTypes)) {
+ if (!t->ElemType()->Is<core::type::SampledTexture>()) {
+ diag() << "binding_array element type must be a sampled texture type";
+ return false;
+ }
+ }
+ return true;
+ },
[](Default) { return true; });
};
diff --git a/src/tint/lang/core/ir/validator_type_test.cc b/src/tint/lang/core/ir/validator_type_test.cc
index 17aa8f2..a072eaa 100644
--- a/src/tint/lang/core/ir/validator_type_test.cc
+++ b/src/tint/lang/core/ir/validator_type_test.cc
@@ -39,6 +39,7 @@
#include "src/tint/lang/core/number.h"
#include "src/tint/lang/core/type/abstract_float.h"
#include "src/tint/lang/core/type/abstract_int.h"
+#include "src/tint/lang/core/type/binding_array.h"
#include "src/tint/lang/core/type/clone_context.h"
#include "src/tint/lang/core/type/manager.h"
#include "src/tint/lang/core/type/matrix.h"
@@ -473,6 +474,51 @@
std::make_tuple(false, TypeBuilder<core::type::Bool>),
std::make_tuple(false, TypeBuilder<core::type::Void>)));
+TEST_F(IR_ValidatorTest, BindingArray) {
+ b.Append(mod.root_block, [&] {
+ auto* var = b.Var(
+ "m", AddressSpace::kHandle,
+ ty.binding_array(ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()), 4));
+ var->SetBindingPoint(0, 0);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, BindingArrayRuntimeCount) {
+ b.Append(mod.root_block, [&] {
+ auto* var = b.Var("m", AddressSpace::kHandle,
+ ty.Get<core::type::BindingArray>(
+ ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()),
+ ty.Get<core::type::RuntimeArrayCount>()));
+ var->SetBindingPoint(0, 0);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason,
+ testing::HasSubstr(R"(:2:3 error: var: binding_array count must be a constant expression
+ %m:ptr<handle, binding_array<texture_2d<f32>, >, read> = var undef @binding_point(0, 0)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^)"));
+}
+
+TEST_F(IR_ValidatorTest, BindingArrayNonSampledTexture) {
+ b.Append(mod.root_block, [&] {
+ auto* var = b.Var("m", AddressSpace::kHandle, ty.binding_array(ty.external_texture(), 5));
+ var->SetBindingPoint(0, 0);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(res.Failure().reason,
+ testing::HasSubstr(
+ R"(:2:3 error: var: binding_array element type must be a sampled texture type
+ %m:ptr<handle, binding_array<texture_external, 5>, read> = var undef @binding_point(0, 0)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^)"));
+}
+
using Type_MultisampledTextureTypeAndDimension =
IRTestParamHelper<std::tuple<std::tuple<
/* type_allowed */ bool,
diff --git a/src/tint/lang/msl/writer/raise/argument_buffers_test.cc b/src/tint/lang/msl/writer/raise/argument_buffers_test.cc
index e6e17b4..b56c6ee 100644
--- a/src/tint/lang/msl/writer/raise/argument_buffers_test.cc
+++ b/src/tint/lang/msl/writer/raise/argument_buffers_test.cc
@@ -420,7 +420,7 @@
TEST_F(MslWriter_ArgumentBuffersTest, HandleTypes_BindingArray) {
auto* texture_type = ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32());
auto* var_t = b.Var("t", ty.ptr<handle>(ty.binding_array(texture_type, 3u)));
- auto* var_s = b.Var("s", ty.ptr<handle>(ty.binding_array(ty.sampler(), 3u)));
+ auto* var_s = b.Var("s", ty.ptr<handle>(ty.sampler()));
var_t->SetBindingPoint(1, 2);
var_s->SetBindingPoint(3, 4);
mod.root_block->Append(var_t);
@@ -429,7 +429,7 @@
auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
auto* load_t = b.Load(b.Access(ty.ptr<handle>(texture_type), var_t, 0_i));
- auto* load_s = b.Load(b.Access(ty.ptr<handle>(ty.sampler()), var_s, 0_i));
+ auto* load_s = b.Load(var_s);
b.Call<vec4<f32>>(core::BuiltinFn::kTextureSample, load_t, load_s, b.Splat<vec2<f32>>(0_f));
b.Return(func);
});
@@ -437,16 +437,15 @@
auto* src = R"(
$B1: { # root
%t:ptr<handle, binding_array<texture_2d<f32>, 3>, read> = var undef @binding_point(1, 2)
- %s:ptr<handle, binding_array<sampler, 3>, read> = var undef @binding_point(3, 4)
+ %s:ptr<handle, sampler, read> = var undef @binding_point(3, 4)
}
%foo = @fragment func():void {
$B2: {
%4:ptr<handle, texture_2d<f32>, read> = access %t, 0i
%5:texture_2d<f32> = load %4
- %6:ptr<handle, sampler, read> = access %s, 0i
- %7:sampler = load %6
- %8:vec4<f32> = textureSample %5, %7, vec2<f32>(0.0f)
+ %6:sampler = load %s
+ %7:vec4<f32> = textureSample %5, %6, vec2<f32>(0.0f)
ret
}
}
@@ -459,7 +458,7 @@
}
tint_arg_buffer_struct_3 = struct @align(1), @core.explicit_layout {
- s:binding_array<sampler, 3> @offset(0), @binding_point(3, 4)
+ s:sampler @offset(0), @binding_point(3, 4)
}
%foo = @fragment func(%tint_arg_buffer_1:ptr<uniform, tint_arg_buffer_struct_1, read> [@binding_point(0, 20)], %tint_arg_buffer_3:ptr<uniform, tint_arg_buffer_struct_3, read> [@binding_point(0, 30)]):void {
@@ -468,9 +467,8 @@
%5:tint_arg_buffer_struct_1 = load %tint_arg_buffer_1
%6:binding_array<texture_2d<f32>, 3> = access %5, 0u
%7:texture_2d<f32> = access %6, 0i
- %8:binding_array<sampler, 3> = access %4, 0u
- %9:sampler = access %8, 0i
- %10:vec4<f32> = textureSample %7, %9, vec2<f32>(0.0f)
+ %8:sampler = access %4, 0u
+ %9:vec4<f32> = textureSample %7, %8, vec2<f32>(0.0f)
ret
}
}
@@ -937,7 +935,7 @@
TEST_F(MslWriter_ArgumentBuffersTest, CallFunctionThatUsesVars_HandleTypes_BindingArray) {
auto* texture_type = ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32());
auto* var_t = b.Var("t", ty.ptr<handle>(ty.binding_array(texture_type, 3u)));
- auto* var_s = b.Var("s", ty.ptr<handle>(ty.binding_array(ty.sampler(), 3u)));
+ auto* var_s = b.Var("s", ty.ptr<handle>(ty.sampler()));
var_t->SetBindingPoint(1, 2);
var_s->SetBindingPoint(3, 4);
mod.root_block->Append(var_t);
@@ -948,7 +946,7 @@
foo->SetParams({param});
b.Append(foo->Block(), [&] {
auto* load_t = b.Load(b.Access(ty.ptr<handle>(texture_type), var_t, 0_i));
- auto* load_s = b.Load(b.Access(ty.ptr<handle>(ty.sampler()), var_s, 0_i));
+ auto* load_s = b.Load(var_s);
auto* result = b.Call<vec4<f32>>(core::BuiltinFn::kTextureSample, load_t, load_s,
b.Splat<vec2<f32>>(0_f));
b.Return(foo, result);
@@ -963,22 +961,21 @@
auto* src = R"(
$B1: { # root
%t:ptr<handle, binding_array<texture_2d<f32>, 3>, read> = var undef @binding_point(1, 2)
- %s:ptr<handle, binding_array<sampler, 3>, read> = var undef @binding_point(3, 4)
+ %s:ptr<handle, sampler, read> = var undef @binding_point(3, 4)
}
%foo = func(%param:i32):vec4<f32> {
$B2: {
%5:ptr<handle, texture_2d<f32>, read> = access %t, 0i
%6:texture_2d<f32> = load %5
- %7:ptr<handle, sampler, read> = access %s, 0i
- %8:sampler = load %7
- %9:vec4<f32> = textureSample %6, %8, vec2<f32>(0.0f)
- ret %9
+ %7:sampler = load %s
+ %8:vec4<f32> = textureSample %6, %7, vec2<f32>(0.0f)
+ ret %8
}
}
%main = @fragment func():void {
$B3: {
- %11:vec4<f32> = call %foo, 42i
+ %10:vec4<f32> = call %foo, 42i
ret
}
}
@@ -991,24 +988,23 @@
}
tint_arg_buffer_struct_3 = struct @align(1), @core.explicit_layout {
- s:binding_array<sampler, 3> @offset(0), @binding_point(3, 4)
+ s:sampler @offset(0), @binding_point(3, 4)
}
-%foo = func(%param:i32, %t:binding_array<texture_2d<f32>, 3>, %s:binding_array<sampler, 3>):vec4<f32> {
+%foo = func(%param:i32, %t:binding_array<texture_2d<f32>, 3>, %s:sampler):vec4<f32> {
$B1: {
%5:texture_2d<f32> = access %t, 0i
- %6:sampler = access %s, 0i
- %7:vec4<f32> = textureSample %5, %6, vec2<f32>(0.0f)
- ret %7
+ %6:vec4<f32> = textureSample %5, %s, vec2<f32>(0.0f)
+ ret %6
}
}
%main = @fragment func(%tint_arg_buffer_1:ptr<uniform, tint_arg_buffer_struct_1, read> [@binding_point(0, 20)], %tint_arg_buffer_3:ptr<uniform, tint_arg_buffer_struct_3, read> [@binding_point(0, 30)]):void {
$B2: {
- %11:tint_arg_buffer_struct_3 = load %tint_arg_buffer_3
- %12:tint_arg_buffer_struct_1 = load %tint_arg_buffer_1
- %13:binding_array<texture_2d<f32>, 3> = access %12, 0u
- %14:binding_array<sampler, 3> = access %11, 0u
- %15:vec4<f32> = call %foo, 42i, %13, %14
+ %10:tint_arg_buffer_struct_3 = load %tint_arg_buffer_3
+ %11:tint_arg_buffer_struct_1 = load %tint_arg_buffer_1
+ %12:binding_array<texture_2d<f32>, 3> = access %11, 0u
+ %13:sampler = access %10, 0u
+ %14:vec4<f32> = call %foo, 42i, %12, %13
ret
}
}
diff --git a/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc b/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
index f0215bd1..ff2391c 100644
--- a/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
+++ b/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
@@ -377,7 +377,7 @@
TEST_F(MslWriter_ModuleScopeVarsTest, HandleTypes_BindingArray) {
auto* texture_type = ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32());
auto* var_t = b.Var("t", ty.ptr<handle>(ty.binding_array(texture_type, 3u)));
- auto* var_s = b.Var("s", ty.ptr<handle>(ty.binding_array(ty.sampler(), 3u)));
+ auto* var_s = b.Var("s", ty.ptr<handle>(ty.sampler()));
var_t->SetBindingPoint(1, 2);
var_s->SetBindingPoint(3, 4);
mod.root_block->Append(var_t);
@@ -386,7 +386,7 @@
auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
auto* load_t = b.Load(b.Access(ty.ptr<handle>(texture_type), var_t, 0_i));
- auto* load_s = b.Load(b.Access(ty.ptr<handle>(ty.sampler()), var_s, 0_i));
+ auto* load_s = b.Load(var_s);
b.Call<vec4<f32>>(core::BuiltinFn::kTextureSample, load_t, load_s, b.Splat<vec2<f32>>(0_f));
b.Return(func);
});
@@ -394,16 +394,15 @@
auto* src = R"(
$B1: { # root
%t:ptr<handle, binding_array<texture_2d<f32>, 3>, read> = var undef @binding_point(1, 2)
- %s:ptr<handle, binding_array<sampler, 3>, read> = var undef @binding_point(3, 4)
+ %s:ptr<handle, sampler, read> = var undef @binding_point(3, 4)
}
%foo = @fragment func():void {
$B2: {
%4:ptr<handle, texture_2d<f32>, read> = access %t, 0i
%5:texture_2d<f32> = load %4
- %6:ptr<handle, sampler, read> = access %s, 0i
- %7:sampler = load %6
- %8:vec4<f32> = textureSample %5, %7, vec2<f32>(0.0f)
+ %6:sampler = load %s
+ %7:vec4<f32> = textureSample %5, %6, vec2<f32>(0.0f)
ret
}
}
@@ -413,18 +412,17 @@
auto* expect = R"(
tint_module_vars_struct = struct @align(1) {
t:binding_array<texture_2d<f32>, 3> @offset(0)
- s:binding_array<sampler, 3> @offset(0)
+ s:sampler @offset(0)
}
-%foo = @fragment func(%t:binding_array<texture_2d<f32>, 3> [@binding_point(1, 2)], %s:binding_array<sampler, 3> [@binding_point(3, 4)]):void {
+%foo = @fragment func(%t:binding_array<texture_2d<f32>, 3> [@binding_point(1, 2)], %s:sampler [@binding_point(3, 4)]):void {
$B1: {
%4:tint_module_vars_struct = construct %t, %s
%tint_module_vars:tint_module_vars_struct = let %4
%6:binding_array<texture_2d<f32>, 3> = access %tint_module_vars, 0u
%7:texture_2d<f32> = access %6, 0i
- %8:binding_array<sampler, 3> = access %tint_module_vars, 1u
- %9:sampler = access %8, 0i
- %10:vec4<f32> = textureSample %7, %9, vec2<f32>(0.0f)
+ %8:sampler = access %tint_module_vars, 1u
+ %9:vec4<f32> = textureSample %7, %8, vec2<f32>(0.0f)
ret
}
}
@@ -882,7 +880,7 @@
TEST_F(MslWriter_ModuleScopeVarsTest, CallFunctionThatUsesVars_HandleTypes_BindingArray) {
auto* texture_type = ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32());
auto* var_t = b.Var("t", ty.ptr<handle>(ty.binding_array(texture_type, 3u)));
- auto* var_s = b.Var("s", ty.ptr<handle>(ty.binding_array(ty.sampler(), 3u)));
+ auto* var_s = b.Var("s", ty.ptr<handle>(ty.sampler()));
var_t->SetBindingPoint(1, 2);
var_s->SetBindingPoint(3, 4);
mod.root_block->Append(var_t);
@@ -893,7 +891,7 @@
foo->SetParams({param});
b.Append(foo->Block(), [&] {
auto* load_t = b.Load(b.Access(ty.ptr<handle>(texture_type), var_t, 0_i));
- auto* load_s = b.Load(b.Access(ty.ptr<handle>(ty.sampler()), var_s, 0_i));
+ auto* load_s = b.Load(var_s);
auto* result = b.Call<vec4<f32>>(core::BuiltinFn::kTextureSample, load_t, load_s,
b.Splat<vec2<f32>>(0_f));
b.Return(foo, result);
@@ -908,22 +906,21 @@
auto* src = R"(
$B1: { # root
%t:ptr<handle, binding_array<texture_2d<f32>, 3>, read> = var undef @binding_point(1, 2)
- %s:ptr<handle, binding_array<sampler, 3>, read> = var undef @binding_point(3, 4)
+ %s:ptr<handle, sampler, read> = var undef @binding_point(3, 4)
}
%foo = func(%param:i32):vec4<f32> {
$B2: {
%5:ptr<handle, texture_2d<f32>, read> = access %t, 0i
%6:texture_2d<f32> = load %5
- %7:ptr<handle, sampler, read> = access %s, 0i
- %8:sampler = load %7
- %9:vec4<f32> = textureSample %6, %8, vec2<f32>(0.0f)
- ret %9
+ %7:sampler = load %s
+ %8:vec4<f32> = textureSample %6, %7, vec2<f32>(0.0f)
+ ret %8
}
}
%main = @fragment func():void {
$B3: {
- %11:vec4<f32> = call %foo, 42i
+ %10:vec4<f32> = call %foo, 42i
ret
}
}
@@ -933,24 +930,23 @@
auto* expect = R"(
tint_module_vars_struct = struct @align(1) {
t:binding_array<texture_2d<f32>, 3> @offset(0)
- s:binding_array<sampler, 3> @offset(0)
+ s:sampler @offset(0)
}
%foo = func(%param:i32, %tint_module_vars:tint_module_vars_struct):vec4<f32> {
$B1: {
%4:binding_array<texture_2d<f32>, 3> = access %tint_module_vars, 0u
%5:texture_2d<f32> = access %4, 0i
- %6:binding_array<sampler, 3> = access %tint_module_vars, 1u
- %7:sampler = access %6, 0i
- %8:vec4<f32> = textureSample %5, %7, vec2<f32>(0.0f)
- ret %8
+ %6:sampler = access %tint_module_vars, 1u
+ %7:vec4<f32> = textureSample %5, %6, vec2<f32>(0.0f)
+ ret %7
}
}
-%main = @fragment func(%t:binding_array<texture_2d<f32>, 3> [@binding_point(1, 2)], %s:binding_array<sampler, 3> [@binding_point(3, 4)]):void {
+%main = @fragment func(%t:binding_array<texture_2d<f32>, 3> [@binding_point(1, 2)], %s:sampler [@binding_point(3, 4)]):void {
$B2: {
- %12:tint_module_vars_struct = construct %t, %s
- %tint_module_vars_1:tint_module_vars_struct = let %12 # %tint_module_vars_1: 'tint_module_vars'
- %14:vec4<f32> = call %foo, 42i, %tint_module_vars_1
+ %11:tint_module_vars_struct = construct %t, %s
+ %tint_module_vars_1:tint_module_vars_struct = let %11 # %tint_module_vars_1: 'tint_module_vars'
+ %13:vec4<f32> = call %foo, 42i, %tint_module_vars_1
ret
}
}