[spirv-reader] Add support for read-write textures
Generate a read-write storage texture if a texture is both read from
and written to. This enables the
`chromium_experimental_read_write_storage_texture` extension.
Add an option to the SPIR-V reader that enables the use of
Chromium-specific extensions, defaulting to `false`.
Bug: tint:2007
Change-Id: I55b768aa8cbb9ee22a3722177f813cb04a049fc0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/147120
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 2e78452..50ec18e 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -2009,6 +2009,7 @@
"lang/spirv/reader/ast_parser/module_var_test.cc",
"lang/spirv/reader/ast_parser/named_types_test.cc",
"lang/spirv/reader/ast_parser/namer_test.cc",
+ "lang/spirv/reader/ast_parser/parser_test.cc",
"lang/spirv/reader/ast_parser/spirv_tools_helpers_test.cc",
"lang/spirv/reader/ast_parser/spirv_tools_helpers_test.h",
"lang/spirv/reader/ast_parser/type_test.cc",
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 2520816..57f2662 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -313,6 +313,14 @@
opts->spirv_reader_options.allow_non_uniform_derivatives = true;
}
});
+ auto& allow_chromium_extensions = options.Add<BoolOption>(
+ "allow-chromium-extensions",
+ "When using SPIR-V input, allow the use of Chromium-specific extensions", Default{false});
+ TINT_DEFER({
+ if (allow_chromium_extensions.value.value_or(false)) {
+ opts->spirv_reader_options.allow_chromium_extensions = true;
+ }
+ });
#endif
auto& disable_wg_init = options.Add<BoolOption>(
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
index 70fa074..69995f6 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser.cc
@@ -2542,9 +2542,8 @@
return nullptr;
}
- // WGSL textures are always formatted. Unformatted textures are always
- // sampled.
- if (usage.IsSampledTexture() || usage.IsStorageReadTexture() ||
+ // WGSL storage textures are always formatted. Unformatted textures are always sampled.
+ if (usage.IsSampledTexture() || usage.IsStorageReadOnlyTexture() ||
(uint32_t(image_type->format()) == uint32_t(spv::ImageFormat::Unknown))) {
// Make a sampled texture type.
auto* ast_sampled_component_type =
@@ -2572,7 +2571,11 @@
ast_handle_type = ty_.SampledTexture(dim, ast_sampled_component_type);
}
} else {
- const auto access = core::Access::kWrite;
+ const auto access =
+ usage.IsStorageReadWriteTexture() ? core::Access::kReadWrite : core::Access::kWrite;
+ if (access == core::Access::kReadWrite) {
+ Enable(core::Extension::kChromiumExperimentalReadWriteStorageTexture);
+ }
const auto format = enum_converter_.ToTexelFormat(image_type->format());
if (format == core::TexelFormat::kUndefined) {
return nullptr;
diff --git a/src/tint/lang/spirv/reader/ast_parser/ast_parser.h b/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
index f8304a7..bf5df6b 100644
--- a/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
+++ b/src/tint/lang/spirv/reader/ast_parser/ast_parser.h
@@ -738,6 +738,14 @@
/// @returns the SPIR-V binary.
const std::vector<uint32_t>& spv_binary() { return spv_binary_; }
+ /// Enable a WGSL extension, if not already enabled.
+ /// @param extension the extension to enable
+ void Enable(core::Extension extension) {
+ if (enabled_extensions_.Add(extension)) {
+ builder_.Enable(extension);
+ }
+ }
+
private:
/// Converts a specific SPIR-V type to a Tint type. Integer case
const Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
@@ -925,6 +933,9 @@
/// field will be 0. Sadly, in SPIR-V right now, there's only one workgroup
/// size object in the module.
WorkgroupSizeInfo workgroup_size_builtin_;
+
+ /// Set of WGSL extensions that have been enabled.
+ Hashset<core::Extension, 4> enabled_extensions_;
};
} // namespace tint::spirv::reader::ast_parser
diff --git a/src/tint/lang/spirv/reader/ast_parser/function.cc b/src/tint/lang/spirv/reader/ast_parser/function.cc
index 658652d..3cb3a66f 100644
--- a/src/tint/lang/spirv/reader/ast_parser/function.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/function.cc
@@ -5633,9 +5633,10 @@
image_operands_mask ^= uint32_t(spv::ImageOperandsMask::Lod);
arg_index++;
} else if ((op == spv::Op::OpImageFetch || op == spv::Op::OpImageRead) &&
- !texture_type->IsAnyOf<DepthMultisampledTexture, MultisampledTexture>()) {
- // textureLoad requires an explicit level-of-detail parameter for
- // non-multisampled texture types.
+ !texture_type
+ ->IsAnyOf<DepthMultisampledTexture, MultisampledTexture, StorageTexture>()) {
+ // textureLoad requires an explicit level-of-detail parameter for non-multisampled and
+ // non-storage texture types.
args.Push(parser_impl_.MakeNullValue(ty_.I32()));
}
if (arg_index + 1 < num_args &&
diff --git a/src/tint/lang/spirv/reader/ast_parser/handle_test.cc b/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
index 8fb26ed..fcc1ca8 100644
--- a/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/handle_test.cc
@@ -4181,6 +4181,65 @@
ASSERT_EQ(expect, got);
}
+TEST_F(SpvParserHandleTest, ReadWriteStorageTexture) {
+ const auto assembly = Preamble() + R"(
+ OpCapability Shader
+ OpCapability StorageImageExtendedFormats
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %100 "main"
+ OpExecutionMode %100 LocalSize 8 8 1
+ OpSource HLSL 600
+ OpName %type_2d_image "type.2d.image"
+ OpName %RWTexture2D "RWTexture2D"
+ OpName %100 "main"
+ OpDecorate %RWTexture2D DescriptorSet 0
+ OpDecorate %RWTexture2D Binding 0
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %v2uint = OpTypeVector %uint 2
+ %coord = OpConstantComposite %v2uint %uint_1 %uint_1
+%type_2d_image = OpTypeImage %float 2D 2 0 0 2 Rgba32f
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %void = OpTypeVoid
+ %20 = OpTypeFunction %void
+%RWTexture2D = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %100 = OpFunction %void None %20
+ %22 = OpLabel
+ %30 = OpLoad %type_2d_image %RWTexture2D
+ %31 = OpImageRead %v4float %30 %coord None
+ %32 = OpFAdd %v4float %31 %31
+ OpImageWrite %30 %coord %32 None
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
+
+ EXPECT_TRUE(p->error().empty()) << p->error();
+ const auto got = test::ToString(p->program());
+ auto* expect = R"(enable chromium_experimental_read_write_storage_texture;
+
+@group(0) @binding(0) var RWTexture2D : texture_storage_2d<rgba32float, read_write>;
+
+const x_9 = vec2u(1u);
+
+fn main_1() {
+ let x_31 = textureLoad(RWTexture2D, vec2i(x_9));
+ textureStore(RWTexture2D, vec2i(x_9), (x_31 + x_31));
+ return;
+}
+
+@compute @workgroup_size(8i, 8i, 1i)
+fn main() {
+ main_1();
+}
+)";
+ ASSERT_EQ(expect, got);
+}
+
TEST_F(SpvParserHandleTest, SimpleSelectCanSelectFromHoistedConstant) {
// Demonstrates fix for crbug.com/tint/1642
// The problem is an operand to a simple select can be a value
diff --git a/src/tint/lang/spirv/reader/ast_parser/parse.cc b/src/tint/lang/spirv/reader/ast_parser/parse.cc
index 27dc272..7b60434 100644
--- a/src/tint/lang/spirv/reader/ast_parser/parse.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/parse.cc
@@ -47,6 +47,33 @@
builder.DiagnosticDirective(core::DiagnosticSeverity::kOff, "derivative_uniformity");
}
+ if (!options.allow_chromium_extensions) {
+ // Check if any Chromium extensions were used.
+ for (auto* enable : builder.AST().Enables()) {
+ for (auto* extension : enable->extensions) {
+ switch (extension->name) {
+ case core::Extension::kUndefined:
+ case core::Extension::kChromiumDisableUniformityAnalysis:
+ case core::Extension::kChromiumExperimentalDp4A:
+ case core::Extension::kChromiumExperimentalFullPtrParameters:
+ case core::Extension::kChromiumExperimentalPushConstant:
+ case core::Extension::kChromiumExperimentalReadWriteStorageTexture:
+ case core::Extension::kChromiumExperimentalSubgroups:
+ case core::Extension::kChromiumInternalDualSourceBlending:
+ case core::Extension::kChromiumInternalRelaxedUniformLayout: {
+ StringStream ss;
+ ss << "module requires " << ToString(extension->name)
+ << ", but 'allow-chromium-extensions' was not passed";
+ builder.Diagnostics().add_error(diag::System::Reader, ss.str());
+ return Program(std::move(builder));
+ }
+ case core::Extension::kF16:
+ break;
+ }
+ }
+ }
+ }
+
// The SPIR-V parser can construct disjoint AST nodes, which is invalid for
// the Resolver. Clone the Program to clean these up.
Program program_with_disjoint_ast(std::move(builder));
diff --git a/src/tint/lang/spirv/reader/ast_parser/parser_test.cc b/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
index 29c222a..d5f8c90 100644
--- a/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/parser_test.cc
@@ -78,6 +78,63 @@
EXPECT_EQ(program.Diagnostics().count(), 0u) << errs;
}
+constexpr auto kShaderWithReadWriteStorageTexture = R"(
+ OpCapability Shader
+ OpCapability StorageImageExtendedFormats
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %100 "main"
+ OpExecutionMode %100 LocalSize 8 8 1
+ OpSource HLSL 600
+ OpName %type_2d_image "type.2d.image"
+ OpName %RWTexture2D "RWTexture2D"
+ OpName %100 "main"
+ OpDecorate %RWTexture2D DescriptorSet 0
+ OpDecorate %RWTexture2D Binding 0
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %v2uint = OpTypeVector %uint 2
+ %coord = OpConstantComposite %v2uint %uint_1 %uint_1
+%type_2d_image = OpTypeImage %float 2D 2 0 0 2 Rgba32f
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %void = OpTypeVoid
+ %20 = OpTypeFunction %void
+%RWTexture2D = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %100 = OpFunction %void None %20
+ %22 = OpLabel
+ %30 = OpLoad %type_2d_image %RWTexture2D
+ %31 = OpImageRead %v4float %30 %coord None
+ %32 = OpFAdd %v4float %31 %31
+ OpImageWrite %30 %coord %32 None
+ OpReturn
+ OpFunctionEnd
+ )";
+
+TEST_F(ParserTest, AllowChromiumExtensions_False) {
+ auto spv = test::Assemble(kShaderWithReadWriteStorageTexture);
+ Options options;
+ options.allow_chromium_extensions = false;
+ auto program = Parse(spv, options);
+ auto errs = program.Diagnostics().str();
+ EXPECT_FALSE(program.IsValid()) << errs;
+ EXPECT_THAT(errs,
+ ::testing::HasSubstr(
+ "error: module requires chromium_experimental_read_write_storage_texture, but "
+ "'allow-chromium-extensions' was not passed"));
+}
+
+TEST_F(ParserTest, AllowChromiumExtensions_True) {
+ auto spv = test::Assemble(kShaderWithReadWriteStorageTexture);
+ Options options;
+ options.allow_chromium_extensions = true;
+ auto program = Parse(spv, options);
+ auto errs = program.Diagnostics().str();
+ EXPECT_TRUE(program.IsValid()) << errs;
+ EXPECT_EQ(program.Diagnostics().count(), 0u) << errs;
+}
+
// TODO(dneto): uint32 vec, valid SPIR-V
// TODO(dneto): uint32 vec, invalid SPIR-V
diff --git a/src/tint/lang/spirv/reader/ast_parser/usage.cc b/src/tint/lang/spirv/reader/ast_parser/usage.cc
index 0a67cac..910024a 100644
--- a/src/tint/lang/spirv/reader/ast_parser/usage.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/usage.cc
@@ -98,11 +98,6 @@
return false;
}
}
-
- // Can't be both read and write. This is a restriction in WebGPU.
- if (is_storage_read_ && is_storage_write_) {
- return false;
- }
}
return true;
}
diff --git a/src/tint/lang/spirv/reader/ast_parser/usage.h b/src/tint/lang/spirv/reader/ast_parser/usage.h
index cf55287..732ad9f 100644
--- a/src/tint/lang/spirv/reader/ast_parser/usage.h
+++ b/src/tint/lang/spirv/reader/ast_parser/usage.h
@@ -66,9 +66,11 @@
/// @returns true if this usage is a dpeth texture usage.
bool IsDepthTexture() const { return is_depth_; }
/// @returns true if this usage is a read-only storage texture
- bool IsStorageReadTexture() const { return is_storage_read_; }
+ bool IsStorageReadOnlyTexture() const { return is_storage_read_ && !is_storage_write_; }
+ /// @returns true if this usage is a read-write storage texture
+ bool IsStorageReadWriteTexture() const { return is_storage_read_ && is_storage_write_; }
/// @returns true if this usage is a write-only storage texture
- bool IsStorageWriteTexture() const { return is_storage_write_; }
+ bool IsStorageWriteOnlyTexture() const { return is_storage_write_ && !is_storage_read_; }
/// @returns true if this is a storage texture.
bool IsStorageTexture() const { return is_storage_read_ || is_storage_write_; }
diff --git a/src/tint/lang/spirv/reader/ast_parser/usage_test.cc b/src/tint/lang/spirv/reader/ast_parser/usage_test.cc
index fb6dee1..08c06dd 100644
--- a/src/tint/lang/spirv/reader/ast_parser/usage_test.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/usage_test.cc
@@ -34,8 +34,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
}
TEST_F(SpirvASTParserTest, Usage_Trivial_Output) {
@@ -87,8 +88,9 @@
EXPECT_FALSE(a.IsSampledTexture());
EXPECT_FALSE(a.IsMultisampledTexture());
EXPECT_FALSE(a.IsDepthTexture());
- EXPECT_TRUE(a.IsStorageReadTexture());
- EXPECT_FALSE(a.IsStorageWriteTexture());
+ EXPECT_TRUE(a.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(a.IsStorageReadWriteTexture());
+ EXPECT_FALSE(a.IsStorageWriteOnlyTexture());
StringStream ss;
ss << a;
@@ -108,8 +110,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Sampler( ))"));
@@ -133,8 +136,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Sampler( comparison ))"));
@@ -157,8 +161,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( ))"));
@@ -181,8 +186,9 @@
EXPECT_TRUE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled ))"));
@@ -205,8 +211,9 @@
EXPECT_TRUE(u.IsSampledTexture());
EXPECT_TRUE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled ms ))"));
@@ -229,8 +236,9 @@
EXPECT_TRUE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_TRUE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled depth ))"));
@@ -253,8 +261,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_TRUE(u.IsStorageReadTexture());
- EXPECT_FALSE(u.IsStorageWriteTexture());
+ EXPECT_TRUE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( read ))"));
@@ -277,8 +286,9 @@
EXPECT_FALSE(u.IsSampledTexture());
EXPECT_FALSE(u.IsMultisampledTexture());
EXPECT_FALSE(u.IsDepthTexture());
- EXPECT_FALSE(u.IsStorageReadTexture());
- EXPECT_TRUE(u.IsStorageWriteTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_FALSE(u.IsStorageReadWriteTexture());
+ EXPECT_TRUE(u.IsStorageWriteOnlyTexture());
ss << u;
EXPECT_THAT(ss.str(), Eq("Usage(Texture( write ))"));
@@ -288,5 +298,32 @@
EXPECT_TRUE(u == copy);
}
+TEST_F(SpirvASTParserTest, Usage_AddStorageReadWriteTexture) {
+ StringStream ss;
+ Usage u;
+ u.AddStorageReadTexture();
+ u.AddStorageWriteTexture();
+
+ EXPECT_TRUE(u.IsValid());
+ EXPECT_TRUE(u.IsComplete());
+ EXPECT_FALSE(u.IsSampler());
+ EXPECT_FALSE(u.IsComparisonSampler());
+ EXPECT_TRUE(u.IsTexture());
+ EXPECT_FALSE(u.IsSampledTexture());
+ EXPECT_FALSE(u.IsMultisampledTexture());
+ EXPECT_FALSE(u.IsDepthTexture());
+ EXPECT_FALSE(u.IsStorageReadOnlyTexture());
+ EXPECT_TRUE(u.IsStorageReadWriteTexture());
+ EXPECT_FALSE(u.IsStorageWriteOnlyTexture());
+
+ ss << u;
+ EXPECT_THAT(ss.str(), Eq("Usage(Texture( read write ))"));
+
+ auto copy(u);
+ u.AddStorageReadTexture();
+ u.AddStorageWriteTexture();
+ EXPECT_TRUE(u == copy);
+}
+
} // namespace
} // namespace tint::spirv::reader::ast_parser
diff --git a/src/tint/lang/spirv/reader/common/options.h b/src/tint/lang/spirv/reader/common/options.h
index 6dddb44..38b9518 100644
--- a/src/tint/lang/spirv/reader/common/options.h
+++ b/src/tint/lang/spirv/reader/common/options.h
@@ -21,6 +21,8 @@
struct Options {
/// Set to `true` to allow calls to derivative builtins in non-uniform control flow.
bool allow_non_uniform_derivatives = false;
+ /// Set to `true` to allow use of Chromium-specific extensions.
+ bool allow_chromium_extensions = false;
};
} // namespace tint::spirv::reader