Import Tint changes from Dawn
Changes:
- 328ffe7fbbc04ae41312347d1a9ab6d7a8bfe2dc Revert "Add deprecated proxy methods." by dan sinclair <dsinclair@chromium.org>
- e25ac2f0fc3a36b1210dfc5788ef61948fb3d749 [tint][utils] Add AlignedStorage by Ben Clayton <bclayton@google.com>
- 95cada18965836843ef1ee92cffeed6623adb2d5 [tint][exe] Add --use-storage-input-output-16 flag by James Price <jrprice@google.com>
- 2f85bc743dda22b69f280b2dd5c034f3cde4919d [spirv-writer][ast] Polyfill f16 shader IO by James Price <jrprice@google.com>
- 7c14dbaaf61a7cf95f852c07570dcd275385b6ff [spirv-writer][ast] Only use StorageInputOutput16 if needed by James Price <jrprice@google.com>
- e90702ebf9fe7a9edf2c1e5e824ede95ae729e03 [spirv-writer] Polyfill f16 shader IO by James Price <jrprice@google.com>
- 2c1743ab159399f0f0154088805a3db841836926 [spirv-writer] Only use StorageInputOutput16 if needed by James Price <jrprice@google.com>
- 9ae3ff6eee865192b7ce95da7a46474a638c0855 GLSL: run OFI if any stage uses firstInstance. by Stephen White <senorblanco@chromium.org>
- ca9f38634fcf3f782eb59db1894046aa117e857a GLSL: set integers to precision highp. by Stephen White <senorblanco@chromium.org>
GitOrigin-RevId: 328ffe7fbbc04ae41312347d1a9ab6d7a8bfe2dc
Change-Id: Ie94905aba99133a90ef23b32579b7007c5e8fae5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/174060
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Commit-Queue: Copybara Prod <copybara-worker@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 81a9e5b..4d18bb9 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -178,6 +178,10 @@
tint::Vector<std::string, 4> transforms;
+#if TINT_BUILD_SPV_WRITER
+ bool use_storage_input_output_16 = true;
+#endif // TINT_BULD_SPV_WRITER
+
#if TINT_BUILD_HLSL_WRITER
std::string fxc_path;
std::string dxc_path;
@@ -345,6 +349,13 @@
});
#endif
+#if TINT_BUILD_SPV_WRITER
+ auto& use_storage_input_output_16 =
+ options.Add<BoolOption>("use-storage-input-output-16",
+ "Use the StorageInputOutput16 SPIR-V capability", Default{true});
+ TINT_DEFER(opts->use_storage_input_output_16 = *use_storage_input_output_16.value);
+#endif
+
auto& disable_wg_init = options.Add<BoolOption>(
"disable-workgroup-init", "Disable workgroup memory zero initialization", Default{false});
TINT_DEFER(opts->disable_workgroup_init = *disable_wg_init.value);
@@ -687,6 +698,7 @@
tint::spirv::writer::Options gen_options;
gen_options.disable_robustness = !options.enable_robustness;
gen_options.disable_workgroup_init = options.disable_workgroup_init;
+ gen_options.use_storage_input_output_16 = options.use_storage_input_output_16;
gen_options.bindings = tint::spirv::writer::GenerateBindings(program);
tint::Result<tint::spirv::writer::Output> result;
@@ -1067,9 +1079,13 @@
gen_options.texture_builtins_from_uniform = std::move(textureBuiltinsFromUniform);
- // Place the first_instance push constant member after user-defined push constants (if any).
- gen_options.first_instance_offset =
- inspector.GetEntryPoint(entry_point_name).push_constant_size;
+ auto entry_point = inspector.GetEntryPoint(entry_point_name);
+
+ if (entry_point.instance_index_used) {
+ // Place the first_instance push constant member after user-defined push constants (if
+ // any).
+ gen_options.first_instance_offset = entry_point.push_constant_size;
+ }
auto result = tint::glsl::writer::Generate(prg, gen_options, entry_point_name);
if (result != tint::Success) {
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index a9c8c31..326c1a2 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -347,6 +347,7 @@
if (version_.IsES() && requires_default_precision_qualifier_) {
current_buffer_->Insert("precision highp float;", helpers_insertion_point++, indent);
+ current_buffer_->Insert("precision highp int;", helpers_insertion_point++, indent);
}
if (!helpers_.lines.empty()) {
diff --git a/src/tint/lang/glsl/writer/ast_printer/function_test.cc b/src/tint/lang/glsl/writer/ast_printer/function_test.cc
index 32a8b0f..a04b329 100644
--- a/src/tint/lang/glsl/writer/ast_printer/function_test.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/function_test.cc
@@ -113,6 +113,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
void func() {
return;
@@ -160,6 +161,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
layout(location = 0) in float foo_1;
layout(location = 1) out float value;
@@ -200,6 +202,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
float frag_main(vec4 coord) {
return coord.x;
@@ -252,6 +255,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
layout(location = 1) out float col1_1;
layout(location = 2) out float col2_1;
@@ -391,6 +395,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct UBO {
vec4 coord;
@@ -432,6 +437,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct Uniforms {
vec4 coord;
@@ -473,6 +479,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
int a;
@@ -521,6 +528,7 @@
EXPECT_EQ(gen.Result(),
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
int a;
@@ -566,6 +574,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
int a;
@@ -611,6 +620,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
int a;
@@ -658,6 +668,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
struct S {
float x;
@@ -705,6 +716,7 @@
EXPECT_EQ(gen.Result(),
R"(#version 310 es
precision highp float;
+precision highp int;
struct S {
float x;
@@ -741,6 +753,7 @@
EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
EXPECT_EQ(gen.Result(), R"(#version 310 es
precision highp float;
+precision highp int;
void tint_symbol() {
}
diff --git a/src/tint/lang/glsl/writer/ast_printer/member_accessor_test.cc b/src/tint/lang/glsl/writer/ast_printer/member_accessor_test.cc
index ef7e638..302d899 100644
--- a/src/tint/lang/glsl/writer/ast_printer/member_accessor_test.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/member_accessor_test.cc
@@ -298,6 +298,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
int a;
@@ -351,6 +352,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
float z;
@@ -399,6 +401,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
float z;
@@ -447,6 +450,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
float z;
@@ -494,6 +498,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Data {
float z;
@@ -547,6 +552,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
vec3 a;
@@ -608,6 +614,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
vec3 a;
@@ -670,6 +677,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
vec3 a;
@@ -731,6 +739,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
vec3 a;
@@ -791,6 +800,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
vec3 a;
@@ -852,6 +862,7 @@
auto* expected =
R"(#version 310 es
precision highp float;
+precision highp int;
struct Inner {
ivec3 a;
diff --git a/src/tint/lang/glsl/writer/ast_printer/sanitizer_test.cc b/src/tint/lang/glsl/writer/ast_printer/sanitizer_test.cc
index 83a653c..0cd03ab 100644
--- a/src/tint/lang/glsl/writer/ast_printer/sanitizer_test.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/sanitizer_test.cc
@@ -60,6 +60,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
layout(binding = 1, std430) buffer my_struct_ssbo {
float a[];
@@ -100,6 +101,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
layout(binding = 1, std430) buffer my_struct_ssbo {
float z;
@@ -144,6 +146,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
layout(binding = 1, std430) buffer my_struct_ssbo {
float a[];
@@ -181,6 +184,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
void tint_symbol() {
int idx = 3;
@@ -223,6 +227,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
struct S {
int a;
@@ -269,6 +274,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
void tint_symbol() {
int v = 0;
@@ -314,6 +320,7 @@
auto got = gen.Result();
auto* expect = R"(#version 310 es
precision highp float;
+precision highp int;
void tint_symbol() {
mat4 a[4] = mat4[4](mat4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), mat4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), mat4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f), mat4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
diff --git a/src/tint/lang/spirv/writer/ast_printer/ast_builtin_test.cc b/src/tint/lang/spirv/writer/ast_printer/ast_builtin_test.cc
index d9eee77..9c58491 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ast_builtin_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ast_builtin_test.cc
@@ -1722,7 +1722,6 @@
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16
%15 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %3 "a_func"
@@ -1822,7 +1821,6 @@
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %3 "a_func"
OpExecutionMode %3 OriginUpperLeft
@@ -1928,7 +1926,6 @@
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16
%17 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %3 "a_func"
@@ -2031,7 +2028,6 @@
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %3 "a_func"
OpExecutionMode %3 OriginUpperLeft
diff --git a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
index d76a591..4272172 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
@@ -188,7 +188,7 @@
data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(
ast::transform::CanonicalizeEntryPointIO::Config(
ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF,
- options.emit_vertex_point_size));
+ options.emit_vertex_point_size, !options.use_storage_input_output_16));
SanitizedResult result;
ast::transform::DataMap outputs;
diff --git a/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc b/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
index 15e4fb2..570448e 100644
--- a/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/binary_expression_test.cc
@@ -1344,8 +1344,7 @@
return R"(OpCapability Shader
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
-OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16)";
+OpCapability StorageBuffer16BitAccess)";
}
return {};
}
@@ -1639,8 +1638,7 @@
return R"(OpCapability Shader
OpCapability Float16
OpCapability UniformAndStorageBuffer16BitAccess
-OpCapability StorageBuffer16BitAccess
-OpCapability StorageInputOutput16)";
+OpCapability StorageBuffer16BitAccess)";
}
return {};
}
diff --git a/src/tint/lang/spirv/writer/ast_printer/builder.cc b/src/tint/lang/spirv/writer/ast_printer/builder.cc
index eb393ca..67d829d 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builder.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/builder.cc
@@ -350,7 +350,6 @@
module_.PushCapability(SpvCapabilityFloat16);
module_.PushCapability(SpvCapabilityUniformAndStorageBuffer16BitAccess);
module_.PushCapability(SpvCapabilityStorageBuffer16BitAccess);
- module_.PushCapability(SpvCapabilityStorageInputOutput16);
break;
default:
return false;
@@ -739,6 +738,13 @@
return false;
}
+ // Emit the StorageInputOutput16 capability if needed.
+ if (sc == core::AddressSpace::kIn || sc == core::AddressSpace::kOut) {
+ if (type->DeepestElement()->Is<core::type::F16>()) {
+ module_.PushCapability(SpvCapabilityStorageInputOutput16);
+ }
+ }
+
module_.PushDebug(spv::Op::OpName, {Operand(var_id), Operand(v->name->symbol.Name())});
OperandList ops = {Operand(type_id), result, U32Operand(ConvertAddressSpace(sc))};
diff --git a/src/tint/lang/spirv/writer/common/options.h b/src/tint/lang/spirv/writer/common/options.h
index e16b267..f7fdee7 100644
--- a/src/tint/lang/spirv/writer/common/options.h
+++ b/src/tint/lang/spirv/writer/common/options.h
@@ -141,6 +141,9 @@
/// VK_KHR_zero_initialize_workgroup_memory is enabled.
bool use_zero_initialize_workgroup_memory_extension = false;
+ /// Set to `true` to use the StorageInputOutput16 capability for shader IO that uses f16 types.
+ bool use_storage_input_output_16 = true;
+
/// Set to `true` to generate a PointSize builtin and have it set to 1.0
/// from all vertex shaders in the module.
bool emit_vertex_point_size = true;
@@ -172,6 +175,7 @@
disable_runtime_sized_array_index_clamping,
disable_workgroup_init,
use_zero_initialize_workgroup_memory_extension,
+ use_storage_input_output_16,
emit_vertex_point_size,
clamp_frag_depth,
pass_matrix_by_pointer,
diff --git a/src/tint/lang/spirv/writer/function_test.cc b/src/tint/lang/spirv/writer/function_test.cc
index 2658f3f..bb68130 100644
--- a/src/tint/lang/spirv/writer/function_test.cc
+++ b/src/tint/lang/spirv/writer/function_test.cc
@@ -346,6 +346,118 @@
)");
}
+TEST_F(SpirvWriterTest, Function_ShaderIO_F16_Input_WithCapability) {
+ auto* input = b.FunctionParam("input", ty.vec4<f16>());
+ input->SetLocation(1, std::nullopt);
+ auto* func = b.Function("main", ty.vec4<f32>(), core::ir::Function::PipelineStage::kFragment);
+ func->SetReturnLocation(2, std::nullopt);
+ func->SetParams({input});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Convert(ty.vec4<f32>(), input));
+ });
+
+ Options options;
+ options.use_storage_input_output_16 = true;
+ ASSERT_TRUE(Generate(options)) << Error() << output_;
+ EXPECT_INST("OpCapability StorageInputOutput16");
+ EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc1_Input %main_loc2_Output)");
+ EXPECT_INST("%main_loc1_Input = OpVariable %_ptr_Input_v4half Input");
+ EXPECT_INST("%main_loc2_Output = OpVariable %_ptr_Output_v4float Output");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %16
+ %17 = OpLabel
+ %18 = OpLoad %v4half %main_loc1_Input
+ %19 = OpFunctionCall %v4float %main_inner %18
+ OpStore %main_loc2_Output %19
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
+TEST_F(SpirvWriterTest, Function_ShaderIO_F16_Input_WithoutCapability) {
+ auto* input = b.FunctionParam("input", ty.vec4<f16>());
+ input->SetLocation(1, std::nullopt);
+ auto* func = b.Function("main", ty.vec4<f32>(), core::ir::Function::PipelineStage::kFragment);
+ func->SetReturnLocation(2, std::nullopt);
+ func->SetParams({input});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Convert(ty.vec4<f32>(), input));
+ });
+
+ Options options;
+ options.use_storage_input_output_16 = false;
+ ASSERT_TRUE(Generate(options)) << Error() << output_;
+ EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc1_Input %main_loc2_Output)");
+ EXPECT_INST("%main_loc1_Input = OpVariable %_ptr_Input_v4float Input");
+ EXPECT_INST("%main_loc2_Output = OpVariable %_ptr_Output_v4float Output");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %16
+ %17 = OpLabel
+ %18 = OpLoad %v4float %main_loc1_Input
+ %19 = OpFConvert %v4half %18
+ %20 = OpFunctionCall %v4float %main_inner %19
+ OpStore %main_loc2_Output %20
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
+TEST_F(SpirvWriterTest, Function_ShaderIO_F16_Output_WithCapability) {
+ auto* input = b.FunctionParam("input", ty.vec4<f32>());
+ input->SetLocation(1, std::nullopt);
+ auto* func = b.Function("main", ty.vec4<f16>(), core::ir::Function::PipelineStage::kFragment);
+ func->SetReturnLocation(2, std::nullopt);
+ func->SetParams({input});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Convert(ty.vec4<f16>(), input));
+ });
+
+ Options options;
+ options.use_storage_input_output_16 = true;
+ ASSERT_TRUE(Generate(options)) << Error() << output_;
+ EXPECT_INST("OpCapability StorageInputOutput16");
+ EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc1_Input %main_loc2_Output)");
+ EXPECT_INST("%main_loc1_Input = OpVariable %_ptr_Input_v4float Input");
+ EXPECT_INST("%main_loc2_Output = OpVariable %_ptr_Output_v4half Output");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %16
+ %17 = OpLabel
+ %18 = OpLoad %v4float %main_loc1_Input
+ %19 = OpFunctionCall %v4half %main_inner %18
+ OpStore %main_loc2_Output %19
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
+TEST_F(SpirvWriterTest, Function_ShaderIO_F16_Output_WithoutCapability) {
+ auto* input = b.FunctionParam("input", ty.vec4<f32>());
+ input->SetLocation(1, std::nullopt);
+ auto* func = b.Function("main", ty.vec4<f16>(), core::ir::Function::PipelineStage::kFragment);
+ func->SetReturnLocation(2, std::nullopt);
+ func->SetParams({input});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Convert(ty.vec4<f16>(), input));
+ });
+
+ Options options;
+ options.use_storage_input_output_16 = false;
+ ASSERT_TRUE(Generate(options)) << Error() << output_;
+ EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc1_Input %main_loc2_Output)");
+ EXPECT_INST("%main_loc1_Input = OpVariable %_ptr_Input_v4float Input");
+ EXPECT_INST("%main_loc2_Output = OpVariable %_ptr_Output_v4float Output");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %16
+ %17 = OpLabel
+ %18 = OpLoad %v4float %main_loc1_Input
+ %19 = OpFunctionCall %v4half %main_inner %18
+ %20 = OpFConvert %v4float %19
+ OpStore %main_loc2_Output %20
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
TEST_F(SpirvWriterTest, Function_ShaderIO_DualSourceBlend) {
auto* outputs =
ty.Struct(mod.symbols.New("Outputs"), {
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index ef41faf..9fc98fd 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -493,7 +493,6 @@
module_.PushCapability(SpvCapabilityFloat16);
module_.PushCapability(SpvCapabilityUniformAndStorageBuffer16BitAccess);
module_.PushCapability(SpvCapabilityStorageBuffer16BitAccess);
- module_.PushCapability(SpvCapabilityStorageInputOutput16);
module_.PushType(spv::Op::OpTypeFloat, {id, 16u});
},
[&](const core::type::Vector* vec) {
@@ -2065,6 +2064,9 @@
}
case core::AddressSpace::kIn: {
TINT_ASSERT(!current_function_);
+ if (store_ty->DeepestElement()->Is<core::type::F16>()) {
+ module_.PushCapability(SpvCapabilityStorageInputOutput16);
+ }
module_.PushType(spv::Op::OpVariable, {ty, id, U32Operand(SpvStorageClassInput)});
EmitIOAttributes(id, var->Attributes(), core::AddressSpace::kIn);
break;
@@ -2089,6 +2091,9 @@
}
case core::AddressSpace::kOut: {
TINT_ASSERT(!current_function_);
+ if (store_ty->DeepestElement()->Is<core::type::F16>()) {
+ module_.PushCapability(SpvCapabilityStorageInputOutput16);
+ }
module_.PushType(spv::Op::OpVariable, {ty, id, U32Operand(SpvStorageClassOutput)});
EmitIOAttributes(id, var->Attributes(), core::AddressSpace::kOut);
break;
diff --git a/src/tint/lang/spirv/writer/raise/raise.cc b/src/tint/lang/spirv/writer/raise/raise.cc
index 70d4669..bbaeb1f 100644
--- a/src/tint/lang/spirv/writer/raise/raise.cc
+++ b/src/tint/lang/spirv/writer/raise/raise.cc
@@ -144,7 +144,8 @@
RUN_TRANSFORM(raise::HandleMatrixArithmetic, module);
RUN_TRANSFORM(raise::MergeReturn, module);
RUN_TRANSFORM(raise::ShaderIO, module,
- raise::ShaderIOConfig{options.clamp_frag_depth, options.emit_vertex_point_size});
+ raise::ShaderIOConfig{options.clamp_frag_depth, options.emit_vertex_point_size,
+ !options.use_storage_input_output_16});
RUN_TRANSFORM(core::ir::transform::Std140, module);
RUN_TRANSFORM(raise::VarForDynamicIndex, module);
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.cc b/src/tint/lang/spirv/writer/raise/shader_io.cc
index 8266f23..ddb311b 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io.cc
@@ -112,8 +112,19 @@
}
name << name_suffix;
+ // Replace f16 types with f32 types if necessary.
+ auto* store_type = io.type;
+ if (config.polyfill_f16_io) {
+ if (store_type->DeepestElement()->Is<core::type::F16>()) {
+ store_type = ty.f32();
+ if (auto* vec = io.type->As<core::type::Vector>()) {
+ store_type = ty.vec(store_type, vec->Width());
+ }
+ }
+ }
+
// Create an IO variable and add it to the root block.
- auto* ptr = ty.ptr(addrspace, io.type, access);
+ auto* ptr = ty.ptr(addrspace, store_type, access);
auto* var = b.Var(name.str(), ptr);
var->SetAttributes(core::ir::IOAttributes{
io.attributes.location,
@@ -150,7 +161,15 @@
from = builder.Access(ptr, input_vars[idx], 0_u)->Result(0);
}
}
- return builder.Load(from)->Result(0);
+
+ auto* value = builder.Load(from)->Result(0);
+
+ // Convert f32 values to f16 values if needed.
+ if (config.polyfill_f16_io && inputs[idx].type->DeepestElement()->Is<core::type::F16>()) {
+ value = builder.Convert(inputs[idx].type, value)->Result(0);
+ }
+
+ return value;
}
/// @copydoc ShaderIO::BackendState::SetOutput
@@ -169,6 +188,12 @@
value = ClampFragDepth(builder, value);
}
}
+
+ // Convert f16 values to f32 values if needed.
+ if (config.polyfill_f16_io && value->Type()->DeepestElement()->Is<core::type::F16>()) {
+ value = builder.Convert(to->Type()->UnwrapPtr(), value)->Result(0);
+ }
+
builder.Store(to, value);
}
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.h b/src/tint/lang/spirv/writer/raise/shader_io.h
index 3ec521c..38cd9ad 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.h
+++ b/src/tint/lang/spirv/writer/raise/shader_io.h
@@ -46,6 +46,8 @@
bool clamp_frag_depth = false;
/// true if a vertex point size builtin output should be added
bool emit_vertex_point_size = false;
+ /// true if f16 IO types should be replaced with f32 types and converted
+ bool polyfill_f16_io = false;
};
/// ShaderIO is a transform that moves each entry point function's parameters and return value to
diff --git a/src/tint/lang/spirv/writer/raise/shader_io_test.cc b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
index 5738a20..61d3ef7 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
@@ -1491,5 +1491,197 @@
EXPECT_EQ(expect, str());
}
+TEST_F(SpirvWriter_ShaderIOTest, F16_IO_WithoutPolyfill) {
+ auto* outputs =
+ ty.Struct(mod.symbols.New("Outputs"), {
+ {
+ mod.symbols.New("out1"),
+ ty.f16(),
+ core::type::StructMemberAttributes{
+ /* location */ 1u,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */ std::nullopt,
+ /* invariant */ false,
+ },
+ },
+ {
+ mod.symbols.New("out2"),
+ ty.vec4<f16>(),
+ core::type::StructMemberAttributes{
+ /* location */ 2u,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */ std::nullopt,
+ /* invariant */ false,
+ },
+ },
+ });
+
+ auto* in1 = b.FunctionParam("in1", ty.f16());
+ auto* in2 = b.FunctionParam("in2", ty.vec4<f16>());
+ in1->SetLocation(1, std::nullopt);
+ in1->SetLocation(2, std::nullopt);
+ auto* func = b.Function("main", outputs, core::ir::Function::PipelineStage::kFragment);
+ func->SetParams({in1, in2});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Construct(outputs, in1, in2));
+ });
+
+ auto* src = R"(
+Outputs = struct @align(8) {
+ out1:f16 @offset(0), @location(1)
+ out2:vec4<f16> @offset(8), @location(2)
+}
+
+%main = @fragment func(%in1:f16 [@location(2)], %in2:vec4<f16>):Outputs -> %b1 {
+ %b1 = block {
+ %4:Outputs = construct %in1, %in2
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Outputs = struct @align(8) {
+ out1:f16 @offset(0)
+ out2:vec4<f16> @offset(8)
+}
+
+%b1 = block { # root
+ %main_loc2_Input:ptr<__in, f16, read> = var @location(2)
+ %main_Input:ptr<__in, vec4<f16>, read> = var
+ %main_loc1_Output:ptr<__out, f16, write> = var @location(1)
+ %main_loc2_Output:ptr<__out, vec4<f16>, write> = var @location(2)
+}
+
+%main_inner = func(%in1:f16, %in2:vec4<f16>):Outputs -> %b2 {
+ %b2 = block {
+ %8:Outputs = construct %in1, %in2
+ ret %8
+ }
+}
+%main = @fragment func():void -> %b3 {
+ %b3 = block {
+ %10:f16 = load %main_loc2_Input
+ %11:vec4<f16> = load %main_Input
+ %12:Outputs = call %main_inner, %10, %11
+ %13:f16 = access %12, 0u
+ store %main_loc1_Output, %13
+ %14:vec4<f16> = access %12, 1u
+ store %main_loc2_Output, %14
+ ret
+ }
+}
+)";
+
+ ShaderIOConfig config;
+ config.polyfill_f16_io = false;
+ Run(ShaderIO, config);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ShaderIOTest, F16_IO_WithPolyfill) {
+ auto* outputs =
+ ty.Struct(mod.symbols.New("Outputs"), {
+ {
+ mod.symbols.New("out1"),
+ ty.f16(),
+ core::type::StructMemberAttributes{
+ /* location */ 1u,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */ std::nullopt,
+ /* invariant */ false,
+ },
+ },
+ {
+ mod.symbols.New("out2"),
+ ty.vec4<f16>(),
+ core::type::StructMemberAttributes{
+ /* location */ 2u,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */ std::nullopt,
+ /* invariant */ false,
+ },
+ },
+ });
+
+ auto* in1 = b.FunctionParam("in1", ty.f16());
+ auto* in2 = b.FunctionParam("in2", ty.vec4<f16>());
+ in1->SetLocation(1, std::nullopt);
+ in1->SetLocation(2, std::nullopt);
+ auto* func = b.Function("main", outputs, core::ir::Function::PipelineStage::kFragment);
+ func->SetParams({in1, in2});
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Construct(outputs, in1, in2));
+ });
+
+ auto* src = R"(
+Outputs = struct @align(8) {
+ out1:f16 @offset(0), @location(1)
+ out2:vec4<f16> @offset(8), @location(2)
+}
+
+%main = @fragment func(%in1:f16 [@location(2)], %in2:vec4<f16>):Outputs -> %b1 {
+ %b1 = block {
+ %4:Outputs = construct %in1, %in2
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Outputs = struct @align(8) {
+ out1:f16 @offset(0)
+ out2:vec4<f16> @offset(8)
+}
+
+%b1 = block { # root
+ %main_loc2_Input:ptr<__in, f32, read> = var @location(2)
+ %main_Input:ptr<__in, vec4<f32>, read> = var
+ %main_loc1_Output:ptr<__out, f32, write> = var @location(1)
+ %main_loc2_Output:ptr<__out, vec4<f32>, write> = var @location(2)
+}
+
+%main_inner = func(%in1:f16, %in2:vec4<f16>):Outputs -> %b2 {
+ %b2 = block {
+ %8:Outputs = construct %in1, %in2
+ ret %8
+ }
+}
+%main = @fragment func():void -> %b3 {
+ %b3 = block {
+ %10:f32 = load %main_loc2_Input
+ %11:f16 = convert %10
+ %12:vec4<f32> = load %main_Input
+ %13:vec4<f16> = convert %12
+ %14:Outputs = call %main_inner, %11, %13
+ %15:f16 = access %14, 0u
+ %16:f32 = convert %15
+ store %main_loc1_Output, %16
+ %17:vec4<f16> = access %14, 1u
+ %18:vec4<f32> = convert %17
+ store %main_loc2_Output, %18
+ ret
+ }
+}
+)";
+
+ ShaderIOConfig config;
+ config.polyfill_f16_io = true;
+ Run(ShaderIO, config);
+
+ EXPECT_EQ(expect, str());
+}
+
} // namespace
} // namespace tint::spirv::writer::raise
diff --git a/src/tint/lang/spirv/writer/type_test.cc b/src/tint/lang/spirv/writer/type_test.cc
index 08fd086..27fdc1b 100644
--- a/src/tint/lang/spirv/writer/type_test.cc
+++ b/src/tint/lang/spirv/writer/type_test.cc
@@ -98,7 +98,6 @@
EXPECT_INST("OpCapability Float16");
EXPECT_INST("OpCapability UniformAndStorageBuffer16BitAccess");
EXPECT_INST("OpCapability StorageBuffer16BitAccess");
- EXPECT_INST("OpCapability StorageInputOutput16");
EXPECT_INST("%half = OpTypeFloat 16");
}
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
index 842e41b..db9a682 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
@@ -339,6 +339,17 @@
value = b.IndexAccessor(value, 0_i);
}
}
+
+ // Replace f16 types with f32 types if necessary.
+ if (cfg.polyfill_f16_io && type->DeepestElement()->Is<core::type::F16>()) {
+ value = b.Call(ast_type, value);
+
+ ast_type = b.ty.f32();
+ if (auto* vec = type->As<core::type::Vector>()) {
+ ast_type = b.ty.vec(ast_type, vec->Width());
+ }
+ }
+
b.GlobalVar(symbol, ast_type, core::AddressSpace::kIn, std::move(attrs));
return value;
} else if (cfg.shader_style == ShaderStyle::kMsl &&
@@ -406,9 +417,27 @@
}
}
+ ast::Type ast_type;
+
+ // Replace f16 types with f32 types if necessary.
+ if (cfg.shader_style == ShaderStyle::kSpirv && cfg.polyfill_f16_io &&
+ type->DeepestElement()->Is<core::type::F16>()) {
+ auto make_ast_type = [&] {
+ auto ty = b.ty.f32();
+ if (auto* vec = type->As<core::type::Vector>()) {
+ ty = b.ty.vec(ty, vec->Width());
+ }
+ return ty;
+ };
+ ast_type = make_ast_type();
+ value = b.Call(make_ast_type(), value);
+ } else {
+ ast_type = CreateASTTypeFor(ctx, type);
+ }
+
OutputValue output;
output.name = name;
- output.type = CreateASTTypeFor(ctx, type);
+ output.type = ast_type;
output.attributes = std::move(attrs);
output.value = value;
output.location = location;
@@ -984,10 +1013,12 @@
CanonicalizeEntryPointIO::Config::Config(ShaderStyle style,
uint32_t sample_mask,
- bool emit_point_size)
+ bool emit_point_size,
+ bool polyfill_f16)
: shader_style(style),
fixed_sample_mask(sample_mask),
- emit_vertex_point_size(emit_point_size) {}
+ emit_vertex_point_size(emit_point_size),
+ polyfill_f16_io(polyfill_f16) {}
CanonicalizeEntryPointIO::Config::Config(const Config&) = default;
CanonicalizeEntryPointIO::Config::~Config() = default;
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
index d63d479..ddf761d 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
@@ -118,9 +118,11 @@
/// @param style the approach to use for emitting shader IO.
/// @param sample_mask an optional sample mask to combine with shader masks
/// @param emit_vertex_point_size `true` to generate a pointsize builtin
+ /// @param polyfill_f16_io `true` to replace f16 types with f32 types
explicit Config(ShaderStyle style,
uint32_t sample_mask = 0xFFFFFFFF,
- bool emit_vertex_point_size = false);
+ bool emit_vertex_point_size = false,
+ bool polyfill_f16_io = false);
/// Copy constructor
Config(const Config&);
@@ -137,6 +139,9 @@
/// Set to `true` to generate a pointsize builtin and have it set to 1.0
/// from all vertex shaders in the module.
const bool emit_vertex_point_size;
+
+ /// Set to `true` to replace f16 IO types with f32 types and convert them.
+ const bool polyfill_f16_io = false;
};
/// HLSLWaveIntrinsic is an InternalAttribute that is used to decorate a stub function so that
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
index 7c33b29..ec83e9e 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
@@ -4420,5 +4420,60 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, F16_Polyfill_Spirv) {
+ auto* src = R"(
+enable f16;
+
+struct Outputs {
+ @location(1) a : f16,
+ @location(2) b : vec4<f16>,
+}
+
+@fragment
+fn frag_main(@location(1) loc1 : f16,
+ @location(2) loc2 : vec4<f16>) -> Outputs {
+ return Outputs(loc1 * 2, loc2 * 3);
+}
+)";
+
+ auto* expect = R"(
+enable f16;
+
+@location(1) @internal(disable_validation__ignore_address_space) var<__in> loc1_1 : f32;
+
+@location(2) @internal(disable_validation__ignore_address_space) var<__in> loc2_1 : vec4<f32>;
+
+@location(1) @internal(disable_validation__ignore_address_space) var<__out> a_1 : f32;
+
+@location(2) @internal(disable_validation__ignore_address_space) var<__out> b_1 : vec4<f32>;
+
+struct Outputs {
+ a : f16,
+ b : vec4<f16>,
+}
+
+fn frag_main_inner(loc1 : f16, loc2 : vec4<f16>) -> Outputs {
+ return Outputs((loc1 * 2), (loc2 * 3));
+}
+
+@fragment
+fn frag_main() {
+ let inner_result = frag_main_inner(f16(loc1_1), vec4<f16>(loc2_1));
+ a_1 = f32(inner_result.a);
+ b_1 = vec4<f32>(inner_result.b);
+}
+)";
+
+ DataMap data;
+
+ data.Add<CanonicalizeEntryPointIO::Config>(CanonicalizeEntryPointIO::ShaderStyle::kSpirv,
+ /* fixed_sample_mask */ 0xFFFFFFFF,
+ /* emit_vertex_point_size */ false,
+ /* polyfill_f16_io */ true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index.cc b/src/tint/lang/wgsl/ast/transform/offset_first_index.cc
index 04bdd9a..143b43f 100644
--- a/src/tint/lang/wgsl/ast/transform/offset_first_index.cc
+++ b/src/tint/lang/wgsl/ast/transform/offset_first_index.cc
@@ -53,15 +53,6 @@
constexpr char kFirstVertexName[] = "first_vertex";
constexpr char kFirstInstanceName[] = "first_instance";
-bool ShouldRun(const Program& program) {
- for (auto* fn : program.AST().Functions()) {
- if (fn->PipelineStage() == PipelineStage::kVertex) {
- return true;
- }
- }
- return false;
-}
-
} // namespace
OffsetFirstIndex::OffsetFirstIndex() = default;
@@ -70,14 +61,13 @@
Transform::ApplyResult OffsetFirstIndex::Apply(const Program& src,
const DataMap& inputs,
DataMap&) const {
- if (!ShouldRun(src)) {
- return SkipTransform;
- }
-
const Config* cfg = inputs.Get<Config>();
if (!cfg) {
return SkipTransform;
}
+ if (!cfg->first_vertex_offset.has_value() && !cfg->first_instance_offset.has_value()) {
+ return SkipTransform;
+ }
ProgramBuilder b;
program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
@@ -86,9 +76,6 @@
std::unordered_map<const sem::Variable*, const char*> builtin_vars;
std::unordered_map<const core::type::StructMember*, const char*> builtin_members;
- bool has_vertex_index = false;
- bool has_instance_index = false;
-
// Traverse the AST scanning for builtin accesses via variables (includes
// parameters) or structure member accesses.
for (auto* node : ctx.src->ASTNodes().Objects()) {
@@ -100,13 +87,11 @@
cfg->first_vertex_offset.has_value()) {
auto* sem_var = ctx.src->Sem().Get(var);
builtin_vars.emplace(sem_var, kFirstVertexName);
- has_vertex_index = true;
}
if (builtin == core::BuiltinValue::kInstanceIndex && cfg &&
cfg->first_instance_offset.has_value()) {
auto* sem_var = ctx.src->Sem().Get(var);
builtin_vars.emplace(sem_var, kFirstInstanceName);
- has_instance_index = true;
}
}
}
@@ -119,23 +104,17 @@
cfg->first_vertex_offset.has_value()) {
auto* sem_mem = ctx.src->Sem().Get(member);
builtin_members.emplace(sem_mem, kFirstVertexName);
- has_vertex_index = true;
}
if (builtin == core::BuiltinValue::kInstanceIndex && cfg &&
cfg->first_instance_offset.has_value()) {
auto* sem_mem = ctx.src->Sem().Get(member);
builtin_members.emplace(sem_mem, kFirstInstanceName);
- has_instance_index = true;
}
}
}
}
}
- if (!has_vertex_index && !has_instance_index) {
- return SkipTransform;
- }
-
Vector<const ast::StructMember*, 8> members;
const ast::Variable* push_constants_var = nullptr;
@@ -159,11 +138,11 @@
}
// Add push constant members and calculate byte offsets
- if (has_vertex_index) {
+ if (cfg->first_vertex_offset.has_value()) {
members.Push(b.Member(kFirstVertexName, b.ty.u32(),
Vector{b.MemberOffset(AInt(*cfg->first_vertex_offset))}));
}
- if (has_instance_index) {
+ if (cfg->first_instance_offset.has_value()) {
members.Push(b.Member(kFirstInstanceName, b.ty.u32(),
Vector{b.MemberOffset(AInt(*cfg->first_instance_offset))}));
}
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc b/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc
index 7d026ed..09dd505 100644
--- a/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc
@@ -41,12 +41,49 @@
TEST_F(OffsetFirstIndexTest, ShouldRunEmptyModule) {
auto* src = R"()";
- DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
- EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config));
+ EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src));
}
-TEST_F(OffsetFirstIndexTest, ShouldRunFragmentStage) {
+TEST_F(OffsetFirstIndexTest, ShouldRunNoVertexIndexNoInstanceIndex) {
+ auto* src = R"(
+@fragment
+fn entry() {
+ return;
+}
+)";
+
+ DataMap config;
+ config.Add<OffsetFirstIndex::Config>(std::nullopt, std::nullopt);
+ EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
+}
+
+TEST_F(OffsetFirstIndexTest, ShouldRunNoVertexIndex) {
+ auto* src = R"(
+@fragment
+fn entry() {
+ return;
+}
+)";
+
+ DataMap config;
+ config.Add<OffsetFirstIndex::Config>(std::nullopt, 0);
+ EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
+}
+
+TEST_F(OffsetFirstIndexTest, ShouldRunNoInstanceIndex) {
+ auto* src = R"(
+@fragment
+fn entry() {
+ return;
+}
+)";
+
+ DataMap config;
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
+ EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
+}
+
+TEST_F(OffsetFirstIndexTest, ShouldRun) {
auto* src = R"(
@fragment
fn entry() {
@@ -56,20 +93,7 @@
DataMap config;
config.Add<OffsetFirstIndex::Config>(0, 4);
- EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config));
-}
-
-TEST_F(OffsetFirstIndexTest, ShouldRunVertexStage) {
- auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
- return vec4<f32>();
-}
-)";
-
- DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
- EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config));
+ EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
}
TEST_F(OffsetFirstIndexTest, ShouldRunVertexStageWithVertexIndex) {
@@ -82,7 +106,7 @@
DataMap config;
config.Add<OffsetFirstIndex::Config>(0, 4);
- EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, config));
+ EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
}
TEST_F(OffsetFirstIndexTest, ShouldRunVertexStageWithInstanceIndex) {
@@ -95,16 +119,14 @@
DataMap config;
config.Add<OffsetFirstIndex::Config>(0, 4);
- EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, config));
+ EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, std::move(config)));
}
TEST_F(OffsetFirstIndexTest, EmptyModule) {
auto* src = "";
auto* expect = "";
- DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
- auto got = Run<OffsetFirstIndex>(src, std::move(config));
+ auto got = Run<OffsetFirstIndex>(src);
EXPECT_EQ(expect, str(got));
}
@@ -118,9 +140,7 @@
)";
auto* expect = src;
- DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
- auto got = Run<OffsetFirstIndex>(src, std::move(config));
+ auto got = Run<OffsetFirstIndex>(src);
EXPECT_EQ(expect, str(got));
}
@@ -160,7 +180,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -201,7 +221,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -244,7 +264,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(std::nullopt, 4);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -287,7 +307,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(std::nullopt, 4);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -577,7 +597,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -614,7 +634,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -663,7 +683,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
@@ -712,7 +732,7 @@
)";
DataMap config;
- config.Add<OffsetFirstIndex::Config>(0, 4);
+ config.Add<OffsetFirstIndex::Config>(0, std::nullopt);
auto got = Run<OffsetFirstIndex>(src, std::move(config));
EXPECT_EQ(expect, str(got));
diff --git a/src/tint/utils/containers/hashmap_base.h b/src/tint/utils/containers/hashmap_base.h
index 2a86e3a..ba78fd0 100644
--- a/src/tint/utils/containers/hashmap_base.h
+++ b/src/tint/utils/containers/hashmap_base.h
@@ -38,6 +38,7 @@
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/math/hash.h"
#include "src/tint/utils/math/math.h"
+#include "src/tint/utils/memory/aligned_storage.h"
#include "src/tint/utils/traits/traits.h"
namespace tint {
@@ -418,21 +419,14 @@
protected:
/// Node holds an Entry in a linked list.
struct Node {
- /// A structure that has the same size and alignment as Entry.
- /// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
- struct alignas(alignof(ENTRY)) Storage {
- /// Byte array of length sizeof(ENTRY)
- uint8_t data[sizeof(ENTRY)];
- };
-
/// Destructs the entry.
void Destroy() { Entry().~ENTRY(); }
/// @returns the storage reinterpreted as an `Entry&`
- ENTRY& Entry() { return *Bitcast<ENTRY*>(&storage.data[0]); }
+ ENTRY& Entry() { return storage.Get(); }
/// @returns the storage reinterpreted as a `const Entry&`
- const ENTRY& Entry() const { return *Bitcast<const ENTRY*>(&storage.data[0]); }
+ const ENTRY& Entry() const { return storage.Get(); }
/// @returns a reference to the Entry's HashmapKey
const HashmapBase::Key& Key() const { return HashmapBase::Entry::KeyOf(Entry()); }
@@ -450,7 +444,7 @@
/// storage is a buffer that has the same size and alignment as Entry.
/// The storage holds a constructed Entry when linked in the slots, and is destructed when
/// removed from slots.
- Storage storage;
+ AlignedStorage<ENTRY> storage;
/// next is the next Node in the slot, or in the free list.
Node* next;
diff --git a/src/tint/utils/containers/vector.h b/src/tint/utils/containers/vector.h
index c7edd0e..cd806c1 100644
--- a/src/tint/utils/containers/vector.h
+++ b/src/tint/utils/containers/vector.h
@@ -41,6 +41,7 @@
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/macros/compiler.h"
#include "src/tint/utils/math/hash.h"
+#include "src/tint/utils/memory/aligned_storage.h"
#include "src/tint/utils/memory/bitcast.h"
#ifndef TINT_VECTOR_MUTATION_CHECKS_ENABLED
@@ -912,26 +913,18 @@
constexpr static bool HasSmallArray = N > 0;
/// A structure that has the same size and alignment as T.
- /// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
- struct alignas(alignof(T)) TStorage {
- /// @returns the storage reinterpreted as a T*
- T* Get() { return Bitcast<T*>(&data[0]); }
- /// @returns the storage reinterpreted as a T*
- const T* Get() const { return Bitcast<const T*>(&data[0]); }
- /// Byte array of length sizeof(T)
- uint8_t data[sizeof(T)];
- };
+ using TStorage = AlignedStorage<T>;
/// The internal structure for the vector with a small array.
struct ImplWithSmallArray {
TStorage small_arr[N];
- tint::Slice<T> slice = {small_arr[0].Get(), 0, N};
+ tint::Slice<T> slice = {&small_arr[0].Get(), 0, N};
/// Allocates a new vector of `T` either from #small_arr, or from the heap, then assigns the
/// pointer it to #slice.data, and updates #slice.cap.
void Allocate(size_t new_cap) {
if (new_cap < N) {
- slice.data = small_arr[0].Get();
+ slice.data = &small_arr[0].Get();
slice.cap = N;
} else {
slice.data = Bitcast<T*>(new TStorage[new_cap]);
@@ -941,14 +934,14 @@
/// Frees `data`, if isn't a pointer to #small_arr
void Free(T* data) const {
- if (data != small_arr[0].Get()) {
+ if (data != &small_arr[0].Get()) {
delete[] Bitcast<TStorage*>(data);
}
}
/// Indicates whether the slice structure can be std::move()d.
/// @returns true if #slice.data does not point to #small_arr
- bool CanMove() const { return slice.data != small_arr[0].Get(); }
+ bool CanMove() const { return slice.data != &small_arr[0].Get(); }
};
/// The internal structure for the vector without a small array.
diff --git a/src/tint/utils/diagnostic/diagnostic.h b/src/tint/utils/diagnostic/diagnostic.h
index fc4438d..ac5c42a 100644
--- a/src/tint/utils/diagnostic/diagnostic.h
+++ b/src/tint/utils/diagnostic/diagnostic.h
@@ -252,8 +252,6 @@
/// @returns true iff the diagnostic list contains errors diagnostics (or of
/// higher severity).
bool ContainsErrors() const { return error_count_ > 0; }
- /// deprecated, use `ContainsErrors`
- bool contains_errors() const { return ContainsErrors(); }
/// @returns the number of error diagnostics (or of higher severity).
size_t NumErrors() const { return error_count_; }
/// @returns the number of entries in the list.
diff --git a/src/tint/utils/diagnostic/formatter.h b/src/tint/utils/diagnostic/formatter.h
index 1841fe0..63da5e2 100644
--- a/src/tint/utils/diagnostic/formatter.h
+++ b/src/tint/utils/diagnostic/formatter.h
@@ -69,8 +69,6 @@
/// @return the list of diagnostics `list` formatted to a string.
/// @param list the list of diagnostic messages to format
std::string Format(const List& list) const;
- /// deprecated, use `Format`
- std::string format(const List& list) const { return Format(list); }
private:
struct State;
diff --git a/src/tint/utils/memory/BUILD.bazel b/src/tint/utils/memory/BUILD.bazel
index 0bd13c9..6cb1b67 100644
--- a/src/tint/utils/memory/BUILD.bazel
+++ b/src/tint/utils/memory/BUILD.bazel
@@ -42,6 +42,7 @@
"memory.cc",
],
hdrs = [
+ "aligned_storage.h",
"bitcast.h",
"block_allocator.h",
"bump_allocator.h",
diff --git a/src/tint/utils/memory/BUILD.cmake b/src/tint/utils/memory/BUILD.cmake
index 3151d3f..1d69077 100644
--- a/src/tint/utils/memory/BUILD.cmake
+++ b/src/tint/utils/memory/BUILD.cmake
@@ -39,6 +39,7 @@
# Kind: lib
################################################################################
tint_add_target(tint_utils_memory lib
+ utils/memory/aligned_storage.h
utils/memory/bitcast.h
utils/memory/block_allocator.h
utils/memory/bump_allocator.h
diff --git a/src/tint/utils/memory/BUILD.gn b/src/tint/utils/memory/BUILD.gn
index e5b44f2..0ab12e1 100644
--- a/src/tint/utils/memory/BUILD.gn
+++ b/src/tint/utils/memory/BUILD.gn
@@ -44,6 +44,7 @@
libtint_source_set("memory") {
sources = [
+ "aligned_storage.h",
"bitcast.h",
"block_allocator.h",
"bump_allocator.h",
diff --git a/src/tint/utils/memory/aligned_storage.h b/src/tint/utils/memory/aligned_storage.h
new file mode 100644
index 0000000..c532c4f
--- /dev/null
+++ b/src/tint/utils/memory/aligned_storage.h
@@ -0,0 +1,53 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
+#define SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
+
+#include <cstddef>
+
+#include "src/tint/utils/memory/bitcast.h"
+
+namespace tint {
+
+/// A structure that has the same size and alignment as Entry.
+/// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
+template <typename T>
+struct alignas(alignof(T)) AlignedStorage {
+ /// Byte array of length sizeof(T)
+ std::byte data[sizeof(T)];
+
+ /// @returns a pointer to aligned storage, reinterpreted as T&
+ T& Get() { return *Bitcast<T*>(&data[0]); }
+
+ /// @returns a pointer to aligned storage, reinterpreted as T&
+ const T& Get() const { return *Bitcast<const T*>(&data[0]); }
+};
+
+} // namespace tint
+
+#endif // SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
diff --git a/src/tint/utils/rtti/switch.h b/src/tint/utils/rtti/switch.h
index 24173d0..2436f5f 100644
--- a/src/tint/utils/rtti/switch.h
+++ b/src/tint/utils/rtti/switch.h
@@ -33,7 +33,7 @@
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/macros/defer.h"
-#include "src/tint/utils/memory/bitcast.h"
+#include "src/tint/utils/memory/aligned_storage.h"
#include "src/tint/utils/rtti/castable.h"
#include "src/tint/utils/rtti/ignore.h"
@@ -300,13 +300,8 @@
}
}
- // Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
- using ReturnTypeOrU8 = std::conditional_t<kHasReturnType, ReturnType, uint8_t>;
- struct alignas(alignof(ReturnTypeOrU8)) ReturnStorage {
- uint8_t data[sizeof(ReturnTypeOrU8)];
- };
- ReturnStorage return_storage;
- auto* result = tint::Bitcast<ReturnTypeOrU8*>(&return_storage);
+ AlignedStorage<std::conditional_t<kHasReturnType, ReturnType, uint8_t>> return_storage;
+ auto* result = &return_storage.Get();
const tint::TypeInfo& type_info = object->TypeInfo();