validation: validate store type of builtin variables

Bug: tint:506
Change-Id: I780e9bfaa1963b351312916630ef017c3e89db02
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55122
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
Sarah 2021-06-23 17:35:42 +00:00 committed by Sarah Mashayekhi
parent ae0bf04ad2
commit 57a737ba3b
8 changed files with 326 additions and 51 deletions

View File

@ -20,6 +20,44 @@ namespace {
class ResolverBuiltinsValidationTest : public resolver::TestHelper,
public testing::Test {};
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
// struct MyInputs {
// [[builtin(kPosition)]] p: vec4<u32>;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* m = Member(
"position", ty.vec4<u32>(),
ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
auto* s = Structure("MyInputs", {m});
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
// struct MyInputs {
// [[builtin(kFragDepth)]] p: i32;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* m = Member(
"frag_depth", ty.i32(),
ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
auto* s = Structure("MyInputs", {m});
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
// struct MyInputs {
// [[builtin(sample_mask)]] m: f32;
@ -90,43 +128,244 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
"12:34 error: store type of builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(kPosition)]] p: vec3<f32>,
// ) -> [[location(0)]] f32 { return 1.0; }
auto* p = Param(
"p", ty.vec3<f32>(),
ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
Func("fs_main", ast::VariableList{p}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(kFragDepth)]] fd: f32,
// ) -> [[location(0)]] f32 { return 1.0; }
auto* fd = Param(
"fd", ty.i32(),
ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
Func("fs_main", ast::VariableList{fd}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
// [[stage(vertex)]]
// fn main(
// [[builtin(kVertexIndex)]] vi : f32,
// [[builtin(kPosition)]] p :vec4<f32>
// ) -> [[builtin(kPosition)]] vec4<f32> { return vec4<f32>(); }
auto* p = Param("p", ty.vec4<f32>(),
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
auto* vi = Param("vi", ty.f32(),
ast::DecorationList{
Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
Func("main", ast::VariableList{vi, p}, ty.vec4<f32>(), {Return(Expr("p"))},
ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(vertex_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
// [[stage(vertex)]]
// fn main(
// [[builtin(kInstanceIndex)]] ii : f32,
// [[builtin(kPosition)]] p :vec4<f32>
// ) -> [[builtin(kPosition)]] vec4<f32> { return vec4<f32>(); }
auto* p = Param("p", ty.vec4<f32>(),
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
auto* ii = Param("ii", ty.f32(),
ast::DecorationList{Builtin(Source{{12, 34}},
ast::Builtin::kInstanceIndex)});
Func("main", ast::VariableList{ii, p}, ty.vec4<f32>(), {Return(Expr("p"))},
ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(instance_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
// [[stage(fragment)]]
// fn fs_main(
// [[builtin(kPosition)]] p: vec4<f32>,
// [[builtin(front_facing)]] ff: bool,
// [[builtin(frag_depth)]] fd: f32,
// [[builtin(sample_index)]] si: u32,
// [[builtin(sample_mask)]] sm : u32
// ) -> [[location(0)]] f32 { return 1.0; }
auto* p = Param("p", ty.vec4<f32>(),
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
auto* ff = Param("ff", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
auto* fd = Param("fd", ty.f32(),
ast::DecorationList{Builtin(ast::Builtin::kFragDepth)});
auto* si = Param("si", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)});
auto* sm = Param("sm", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)});
Func("fs_main", ast::VariableList{ff, si, sm}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0)});
Func(
"fs_main", ast::VariableList{p, ff, fd, si, sm}, ty.f32(), {Return(1.0f)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, VertexBuiltin_Pass) {
// [[stage(vertex)]]
// fn main(
// [[builtin(kVertexIndex)]] vi : u32,
// [[builtin(kInstanceIndex)]] ii : u32,
// [[builtin(kPosition)]] p :vec4<f32>
// ) {}
auto* vi = Param("vi", ty.u32(),
ast::DecorationList{
Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
auto* p = Param("p", ty.vec4<f32>(),
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
auto* ii = Param("ii", ty.u32(),
ast::DecorationList{Builtin(Source{{12, 34}},
ast::Builtin::kInstanceIndex)});
Func("main", ast::VariableList{vi, ii, p}, ty.vec4<f32>(),
{
Return(Expr(p)),
},
ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_Pass) {
// [[stage(compute)]]
// fn main(
// [[builtin(local_invocationId)]] li_id: vec3<u32>,
// [[builtin(local_invocationIndex)]] li_index: u32,
// [[builtin(global_invocationId)]] gi: vec3<u32>,
// [[builtin(workgroup_id)]] wi: vec3<u32>,
// ) {}
auto* li_id =
Param("li_id", ty.vec3<u32>(),
ast::DecorationList{Builtin(ast::Builtin::kLocalInvocationId)});
auto* li_index =
Param("li_index", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kLocalInvocationIndex)});
auto* gi =
Param("gi", ty.vec3<u32>(),
ast::DecorationList{Builtin(ast::Builtin::kGlobalInvocationId)});
auto* wi = Param("wi", ty.vec3<u32>(),
ast::DecorationList{Builtin(ast::Builtin::kWorkgroupId)});
Func("main", ast::VariableList{li_id, li_index, gi, wi}, ty.void_(), {},
ast::DecorationList{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
auto* wi = Param("wi", ty.f32(),
ast::DecorationList{
Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)});
Func("main", ast::VariableList{wi}, ty.void_(), {},
ast::DecorationList{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(workgroup_id) must be "
"'vec3<u32>'");
}
TEST_F(ResolverBuiltinsValidationTest,
ComputeBuiltin_GlobalInvocationNotVec3U32) {
auto* gi = Param("gi", ty.vec3<i32>(),
ast::DecorationList{Builtin(
Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)});
Func("main", ast::VariableList{gi}, ty.void_(), {},
ast::DecorationList{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(global_invocation_id) must be "
"'vec3<u32>'");
}
TEST_F(ResolverBuiltinsValidationTest,
ComputeBuiltin_LocalInvocationIndexNotU32) {
auto* li_index =
Param("li_index", ty.vec3<u32>(),
ast::DecorationList{Builtin(Source{{12, 34}},
ast::Builtin::kLocalInvocationIndex)});
Func("main", ast::VariableList{li_index}, ty.void_(), {},
ast::DecorationList{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
"12:34 error: store type of builtin(local_invocation_index) must be "
"'u32'");
}
TEST_F(ResolverBuiltinsValidationTest,
ComputeBuiltin_LocalInvocationNotVec3U32) {
auto* li_id = Param("li_id", ty.vec2<u32>(),
ast::DecorationList{Builtin(
Source{{12, 34}}, ast::Builtin::kLocalInvocationId)});
Func("main", ast::VariableList{li_id}, ty.void_(), {},
ast::DecorationList{
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(local_invocation_id) must be "
"'vec3<u32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) {
// Struct MyInputs {
// [[builtin(kPosition)]] p: vec4<f32>;
// [[builtin(front_facing)]] ff: bool;
// [[builtin(frag_depth)]] fd: f32;
// [[builtin(sample_index)]] si: u32;
// [[builtin(sample_mask)]] sm : u32;
// [[builtin(sample_mask)]] sm : u32;;
// };
// [[stage(fragment)]]
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
// fn fragShader(arg: MyInputs) -> [[location(0)]] f32 { return 1.0; }
auto* s = Structure(
"MyInputs",
{Member("ff", ty.bool_(),
{Member("position", ty.vec4<f32>(),
ast::DecorationList{Builtin(ast::Builtin::kPosition)}),
Member("front_facing", ty.bool_(),
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)}),
Member("si", ty.u32(),
Member("frag_depth", ty.f32(),
ast::DecorationList{Builtin(ast::Builtin::kFragDepth)}),
Member("sample_index", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)}),
Member("sm", ty.u32(),
Member("sample_mask", ty.u32(),
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)})});
Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}

View File

@ -268,8 +268,16 @@ using StructMemberDecorationTest = TestWithParams;
TEST_P(StructMemberDecorationTest, IsValid) {
auto& params = GetParam();
ast::StructMemberList members{Member(
"a", ty.i32(), createDecorations(Source{{12, 34}}, *this, params.kind))};
ast::StructMemberList members;
if (params.kind == DecorationKind::kBuiltin) {
members.push_back(
{Member("a", ty.vec4<f32>(),
createDecorations(Source{{12, 34}}, *this, params.kind))});
} else {
members.push_back(
{Member("a", ty.i32(),
createDecorations(Source{{12, 34}}, *this, params.kind))});
}
Structure("mystruct", members);

View File

@ -293,8 +293,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Location) {
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Builtin) {
// [[stage(fragment)]]
// fn main([[builtin(frag_depth)]] param : f32) {}
auto* param =
Param("param", ty.vec4<f32>(), {Builtin(ast::Builtin::kFragDepth)});
auto* param = Param("param", ty.f32(), {Builtin(ast::Builtin::kFragDepth)});
Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
{Stage(ast::PipelineStage::kFragment)});

View File

@ -950,6 +950,33 @@ bool Resolver::ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
const sem::Type* storage_type) {
auto* type = storage_type->UnwrapRef();
switch (deco->value()) {
case ast::Builtin::kPosition:
if (!(type->is_float_vector() && type->As<sem::Vector>()->size() == 4)) {
diagnostics_.add_error(
"store type of " + deco_to_str(deco) + " must be 'vec4<f32>'",
deco->source());
return false;
}
break;
case ast::Builtin::kGlobalInvocationId:
case ast::Builtin::kLocalInvocationId:
case ast::Builtin::kWorkgroupId:
if (!(type->is_unsigned_integer_vector() &&
type->As<sem::Vector>()->size() == 3)) {
diagnostics_.add_error(
"store type of " + deco_to_str(deco) + " must be 'vec3<u32>'",
deco->source());
return false;
}
break;
case ast::Builtin::kFragDepth:
if (!type->Is<sem::F32>()) {
diagnostics_.add_error(
"store type of " + deco_to_str(deco) + " must be 'f32'",
deco->source());
return false;
}
break;
case ast::Builtin::kFrontFacing:
if (!type->Is<sem::Bool>()) {
diagnostics_.add_error(
@ -958,6 +985,9 @@ bool Resolver::ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
return false;
}
break;
case ast::Builtin::kLocalInvocationIndex:
case ast::Builtin::kVertexIndex:
case ast::Builtin::kInstanceIndex:
case ast::Builtin::kSampleMask:
case ast::Builtin::kSampleIndex:
if (!type->Is<sem::U32>()) {

View File

@ -79,7 +79,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
auto* s = Structure(
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
"S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0.f)))},
{Stage(ast::PipelineStage::kVertex)});
@ -138,7 +138,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
auto* s = Structure(
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
"S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
Func("vert_main", {Param("param", ty.Of(s))}, ty.Of(s),
{Return(Construct(ty.Of(s), Expr(0.f)))},

View File

@ -13,11 +13,11 @@ struct vertexUniformBuffer2 {
[[stage(vertex)]]
fn main([[builtin(vertex_idx)]] gl_VertexIndex : i32) -> [[builtin(position)]] vec4<f32> {
fn main([[builtin(vertex_idx)]] gl_VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
var indexable : array<vec2<f32>, 3>;
let x_23 : mat2x2<f32> = x_20.transform1;
let x_28 : mat2x2<f32> = x_26.transform2;
let x_46 : i32 = gl_VertexIndex;
let x_46 : u32 = gl_VertexIndex;
indexable = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0));
let x_51 : vec2<f32> = indexable[x_46];
let x_52 : vec2<f32> = (mat2x2<f32>((x_23[0u] + x_28[0u]), (x_23[1u] + x_28[1u])) * x_51);

View File

@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 64
; Bound: 63
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
@ -49,9 +49,9 @@
%vertexUniformBuffer2 = OpTypeStruct %mat2v2float
%_ptr_Uniform_vertexUniformBuffer2 = OpTypePointer Uniform %vertexUniformBuffer2
%x_26 = OpVariable %_ptr_Uniform_vertexUniformBuffer2 Uniform
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%tint_symbol = OpVariable %_ptr_Input_int Input
%uint = OpTypeInt 32 0
%_ptr_Input_uint = OpTypePointer Input %uint
%tint_symbol = OpVariable %_ptr_Input_uint Input
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%19 = OpConstantNull %v4float
@ -60,18 +60,17 @@
%20 = OpTypeFunction %void %v4float
%25 = OpTypeFunction %void
%float_1 = OpConstant %float 1
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3
%_ptr_Function__arr_v2float_uint_3 = OpTypePointer Function %_arr_v2float_uint_3
%34 = OpConstantNull %_arr_v2float_uint_3
%33 = OpConstantNull %_arr_v2float_uint_3
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_mat2v2float = OpTypePointer Uniform %mat2v2float
%float_n1 = OpConstant %float -1
%43 = OpConstantComposite %v2float %float_n1 %float_1
%44 = OpConstantComposite %v2float %float_1 %float_1
%45 = OpConstantComposite %v2float %float_n1 %float_n1
%46 = OpConstantComposite %_arr_v2float_uint_3 %43 %44 %45
%42 = OpConstantComposite %v2float %float_n1 %float_1
%43 = OpConstantComposite %v2float %float_1 %float_1
%44 = OpConstantComposite %v2float %float_n1 %float_n1
%45 = OpConstantComposite %_arr_v2float_uint_3 %42 %43 %44
%_ptr_Function_v2float = OpTypePointer Function %v2float
%uint_1 = OpConstant %uint 1
%float_0 = OpConstant %float 0
@ -83,27 +82,27 @@
OpFunctionEnd
%main = OpFunction %void None %25
%27 = OpLabel
%indexable = OpVariable %_ptr_Function__arr_v2float_uint_3 Function %34
%indexable = OpVariable %_ptr_Function__arr_v2float_uint_3 Function %33
OpStore %tint_pointsize %float_1
%37 = OpAccessChain %_ptr_Uniform_mat2v2float %x_20 %uint_0
%38 = OpLoad %mat2v2float %37
%39 = OpAccessChain %_ptr_Uniform_mat2v2float %x_26 %uint_0
%40 = OpLoad %mat2v2float %39
%41 = OpLoad %int %tint_symbol
OpStore %indexable %46
%48 = OpAccessChain %_ptr_Function_v2float %indexable %41
%49 = OpLoad %v2float %48
%50 = OpCompositeExtract %v2float %38 0
%51 = OpCompositeExtract %v2float %40 0
%52 = OpFAdd %v2float %50 %51
%54 = OpCompositeExtract %v2float %38 1
%55 = OpCompositeExtract %v2float %40 1
%56 = OpFAdd %v2float %54 %55
%57 = OpCompositeConstruct %mat2v2float %52 %56
%58 = OpMatrixTimesVector %v2float %57 %49
%60 = OpCompositeExtract %float %58 0
%61 = OpCompositeExtract %float %58 1
%63 = OpCompositeConstruct %v4float %60 %61 %float_0 %float_1
%59 = OpFunctionCall %void %tint_symbol_3 %63
%36 = OpAccessChain %_ptr_Uniform_mat2v2float %x_20 %uint_0
%37 = OpLoad %mat2v2float %36
%38 = OpAccessChain %_ptr_Uniform_mat2v2float %x_26 %uint_0
%39 = OpLoad %mat2v2float %38
%40 = OpLoad %uint %tint_symbol
OpStore %indexable %45
%47 = OpAccessChain %_ptr_Function_v2float %indexable %40
%48 = OpLoad %v2float %47
%49 = OpCompositeExtract %v2float %37 0
%50 = OpCompositeExtract %v2float %39 0
%51 = OpFAdd %v2float %49 %50
%53 = OpCompositeExtract %v2float %37 1
%54 = OpCompositeExtract %v2float %39 1
%55 = OpFAdd %v2float %53 %54
%56 = OpCompositeConstruct %mat2v2float %51 %55
%57 = OpMatrixTimesVector %v2float %56 %48
%59 = OpCompositeExtract %float %57 0
%60 = OpCompositeExtract %float %57 1
%62 = OpCompositeConstruct %v4float %59 %60 %float_0 %float_1
%58 = OpFunctionCall %void %tint_symbol_3 %62
OpReturn
OpFunctionEnd

View File

@ -13,11 +13,11 @@ struct vertexUniformBuffer2 {
[[group(1), binding(0)]] var<uniform> x_26 : vertexUniformBuffer2;
[[stage(vertex)]]
fn main([[builtin(vertex_index)]] gl_VertexIndex : i32) -> [[builtin(position)]] vec4<f32> {
fn main([[builtin(vertex_index)]] gl_VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
var indexable : array<vec2<f32>, 3>;
let x_23 : mat2x2<f32> = x_20.transform1;
let x_28 : mat2x2<f32> = x_26.transform2;
let x_46 : i32 = gl_VertexIndex;
let x_46 : u32 = gl_VertexIndex;
indexable = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0));
let x_51 : vec2<f32> = indexable[x_46];
let x_52 : vec2<f32> = (mat2x2<f32>((x_23[0u] + x_28[0u]), (x_23[1u] + x_28[1u])) * x_51);