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();