diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ff38d7316..533186bb95 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -525,6 +525,7 @@ if(${TINT_BUILD_GLSL_WRITER}) writer/glsl/generator.h writer/glsl/generator_impl.cc writer/glsl/generator_impl.h + writer/glsl/version.h ) endif() @@ -1082,10 +1083,12 @@ if(TINT_BUILD_TESTS) writer/glsl/generator_impl_module_constant_test.cc writer/glsl/generator_impl_return_test.cc writer/glsl/generator_impl_sanitizer_test.cc + writer/glsl/generator_impl_storage_buffer_test.cc writer/glsl/generator_impl_switch_test.cc writer/glsl/generator_impl_test.cc writer/glsl/generator_impl_type_test.cc writer/glsl/generator_impl_unary_op_test.cc + writer/glsl/generator_impl_uniform_buffer_test.cc writer/glsl/generator_impl_variable_decl_statement_test.cc writer/glsl/generator_impl_workgroup_var_test.cc writer/glsl/test_helper.h diff --git a/src/writer/glsl/generator.cc b/src/writer/glsl/generator.cc index 7e7f832131..3dc8f950df 100644 --- a/src/writer/glsl/generator.cc +++ b/src/writer/glsl/generator.cc @@ -53,7 +53,7 @@ Result Generate(const Program* program, } // Generate the GLSL code. - auto impl = std::make_unique(&output.program); + auto impl = std::make_unique(&output.program, options.version); result.success = impl->Generate(); result.error = impl->error(); result.glsl = impl->result(); diff --git a/src/writer/glsl/generator.h b/src/writer/glsl/generator.h index ea33060b83..16487e63c3 100644 --- a/src/writer/glsl/generator.h +++ b/src/writer/glsl/generator.h @@ -25,6 +25,7 @@ #include "src/ast/pipeline_stage.h" #include "src/sem/binding_point.h" #include "src/sem/sampler_texture_pair.h" +#include "src/writer/glsl/version.h" #include "src/writer/text.h" namespace tint { @@ -69,6 +70,9 @@ struct Options { /// If true, then validation will be disabled for binding point collisions /// generated by the BindingRemapper transform bool allow_collisions = false; + + /// The GLSL version to emit + Version version; }; /// The result produced when generating GLSL. diff --git a/src/writer/glsl/generator_impl.cc b/src/writer/glsl/generator_impl.cc index 1effa1b261..a6bab5e455 100644 --- a/src/writer/glsl/generator_impl.cc +++ b/src/writer/glsl/generator_impl.cc @@ -128,12 +128,20 @@ const char* convert_texel_format_to_glsl(const ast::TexelFormat format) { } // namespace -GeneratorImpl::GeneratorImpl(const Program* program) : TextGenerator(program) {} +GeneratorImpl::GeneratorImpl(const Program* program, const Version& version) + : TextGenerator(program), version_(version) {} GeneratorImpl::~GeneratorImpl() = default; bool GeneratorImpl::Generate() { - line() << "#version 310 es"; + { + auto out = line(); + out << "#version " << version_.major_version << version_.minor_version + << "0"; + if (version_.IsES()) { + out << " es"; + } + } auto helpers_insertion_point = current_buffer_->lines.size(); @@ -181,7 +189,7 @@ bool GeneratorImpl::Generate() { TextBuffer extensions; - if (requires_oes_sample_variables_) { + if (version_.IsES() && requires_oes_sample_variables_) { extensions.Append("#extension GL_OES_sample_variables : require"); } @@ -192,7 +200,7 @@ bool GeneratorImpl::Generate() { helpers_insertion_point += extensions.lines.size(); } - if (requires_default_precision_qualifier_) { + if (version_.IsES() && requires_default_precision_qualifier_) { current_buffer_->Insert("precision mediump float;", helpers_insertion_point++, indent); } @@ -1729,9 +1737,15 @@ bool GeneratorImpl::EmitUniformVariable(const sem::Variable* var) { return false; } ast::VariableBindingPoint bp = decl->BindingPoint(); - line() << "layout(binding = " << bp.binding->value << ") uniform " - << UniqueIdentifier(StructName(str)) << " {"; - EmitStructMembers(current_buffer_, str); + { + auto out = line(); + out << "layout(binding = " << bp.binding->value; + if (version_.IsDesktop()) { + out << ", std140"; + } + out << ") uniform " << UniqueIdentifier(StructName(str)) << " {"; + } + EmitStructMembers(current_buffer_, str, /* emit_offsets */ true); auto name = builder_.Symbols().NameFor(decl->symbol); line() << "} " << name << ";"; line(); @@ -1751,7 +1765,7 @@ bool GeneratorImpl::EmitStorageVariable(const sem::Variable* var) { ast::VariableBindingPoint bp = decl->BindingPoint(); line() << "layout(binding = " << bp.binding->value << ", std430) buffer " << UniqueIdentifier(StructName(str)) << " {"; - EmitStructMembers(current_buffer_, str); + EmitStructMembers(current_buffer_, str, /* emit_offsets */ true); auto name = builder_.Symbols().NameFor(decl->symbol); line() << "} " << name << ";"; return true; @@ -2509,14 +2523,16 @@ bool GeneratorImpl::EmitTypeAndName(std::ostream& out, bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) { auto storage_class_uses = str->StorageClassUsage(); line(b) << "struct " << StructName(str) << " {"; - EmitStructMembers(b, str); + EmitStructMembers(b, str, false); line(b) << "};"; line(b); return true; } -bool GeneratorImpl::EmitStructMembers(TextBuffer* b, const sem::Struct* str) { +bool GeneratorImpl::EmitStructMembers(TextBuffer* b, + const sem::Struct* str, + bool emit_offsets) { ScopedIndent si(b); for (auto* mem : str->Members()) { auto name = builder_.Symbols().NameFor(mem->Name()); @@ -2525,6 +2541,10 @@ bool GeneratorImpl::EmitStructMembers(TextBuffer* b, const sem::Struct* str) { auto out = line(b); + // Note: offsets are unsupported on GLSL ES. + if (emit_offsets && version_.IsDesktop() && mem->Offset() != 0) { + out << "layout(offset=" << mem->Offset() << ") "; + } if (!EmitTypeAndName(out, ty, ast::StorageClass::kNone, ast::Access::kReadWrite, name)) { return false; diff --git a/src/writer/glsl/generator_impl.h b/src/writer/glsl/generator_impl.h index ce51a03c77..886cf9e7c3 100644 --- a/src/writer/glsl/generator_impl.h +++ b/src/writer/glsl/generator_impl.h @@ -35,6 +35,7 @@ #include "src/scope_stack.h" #include "src/transform/decompose_memory_access.h" #include "src/utils/hash.h" +#include "src/writer/glsl/version.h" #include "src/writer/text_generator.h" namespace tint { @@ -55,7 +56,8 @@ class GeneratorImpl : public TextGenerator { public: /// Constructor /// @param program the program to generate - explicit GeneratorImpl(const Program* program); + /// @param version the GLSL version to use + GeneratorImpl(const Program* program, const Version& version); ~GeneratorImpl(); /// @returns true on successful generation; false otherwise @@ -388,8 +390,11 @@ class GeneratorImpl : public TextGenerator { /// Handles generating the members of a structure /// @param buffer the text buffer that the struct members will be written to /// @param ty the struct to generate + /// @param emit_offsets whether offsets should be emitted as offset= /// @returns true if the struct members are emitted - bool EmitStructMembers(TextBuffer* buffer, const sem::Struct* ty); + bool EmitStructMembers(TextBuffer* buffer, + const sem::Struct* ty, + bool emit_offsets); /// Handles a unary op expression /// @param out the output of the expression stream /// @param expr the expression to emit @@ -475,6 +480,7 @@ class GeneratorImpl : public TextGenerator { std::unordered_map int_dot_funcs_; bool requires_oes_sample_variables_ = false; bool requires_default_precision_qualifier_ = false; + Version version_; }; } // namespace glsl diff --git a/src/writer/glsl/generator_impl_storage_buffer_test.cc b/src/writer/glsl/generator_impl_storage_buffer_test.cc new file mode 100644 index 0000000000..d6896f40e9 --- /dev/null +++ b/src/writer/glsl/generator_impl_storage_buffer_test.cc @@ -0,0 +1,97 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "src/writer/glsl/test_helper.h" + +using ::testing::HasSubstr; + +namespace tint { +namespace writer { +namespace glsl { +namespace { + +using GlslGeneratorImplTest_StorageBuffer = TestHelper; + +void TestAlign(ProgramBuilder* ctx) { + // struct Nephews { + // @align(256) huey : f32; + // @align(256) dewey : f32; + // @align(256) louie : f32; + // }; + // @group(0) @binding(0) var nephews : Nephews; + auto* nephews = ctx->Structure( + "Nephews", + { + ctx->Member("huey", ctx->ty.f32(), {ctx->MemberAlign(256)}), + ctx->Member("dewey", ctx->ty.f32(), {ctx->MemberAlign(256)}), + ctx->Member("louie", ctx->ty.f32(), {ctx->MemberAlign(256)}), + }); + ctx->Global("nephews", ctx->ty.Of(nephews), ast::StorageClass::kStorage, + ast::AttributeList{ + ctx->create(0), + ctx->create(0), + }); +} + +TEST_F(GlslGeneratorImplTest_StorageBuffer, Align) { + TestAlign(this); + + GeneratorImpl& gen = Build(); + + // TODO(crbug.com/tint/1421) offsets do not currently work on GLSL ES. + // They will likely require manual padding. + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 310 es + +struct Nephews { + float huey; + float dewey; + float louie; +}; + +layout(binding = 0, std430) buffer Nephews_1 { + float huey; + float dewey; + float louie; +} nephews; +)"); +} + +TEST_F(GlslGeneratorImplTest_StorageBuffer, Align_Desktop) { + TestAlign(this); + + GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4)); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 440 + +struct Nephews { + float huey; + float dewey; + float louie; +}; + +layout(binding = 0, std430) buffer Nephews_1 { + float huey; + layout(offset=256) float dewey; + layout(offset=512) float louie; +} nephews; +)"); +} + +} // namespace +} // namespace glsl +} // namespace writer +} // namespace tint diff --git a/src/writer/glsl/generator_impl_test.cc b/src/writer/glsl/generator_impl_test.cc index cb74fe5137..471407e90f 100644 --- a/src/writer/glsl/generator_impl_test.cc +++ b/src/writer/glsl/generator_impl_test.cc @@ -36,6 +36,64 @@ void my_func() { )"); } +TEST_F(GlslGeneratorImplTest, GenerateDesktop) { + Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, + ast::AttributeList{}); + + GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4)); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 440 + +void my_func() { +} + +)"); +} + +TEST_F(GlslGeneratorImplTest, GenerateSampleIndexES) { + Global( + "gl_SampleID", ty.i32(), + ast::AttributeList{Builtin(ast::Builtin::kSampleIndex), + Disable(ast::DisabledValidation::kIgnoreStorageClass)}, + ast::StorageClass::kInput); + Func("my_func", {}, ty.i32(), + ast::StatementList{Return(Expr("gl_SampleID"))}); + + GeneratorImpl& gen = Build(Version(Version::Standard::kES, 3, 1)); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 310 es +#extension GL_OES_sample_variables : require + +int my_func() { + return gl_SampleID; +} + +)"); +} + +TEST_F(GlslGeneratorImplTest, GenerateSampleIndexDesktop) { + Global( + "gl_SampleID", ty.i32(), + ast::AttributeList{Builtin(ast::Builtin::kSampleIndex), + Disable(ast::DisabledValidation::kIgnoreStorageClass)}, + ast::StorageClass::kInput); + Func("my_func", {}, ty.i32(), + ast::StatementList{Return(Expr("gl_SampleID"))}); + + GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4)); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 440 + +int my_func() { + return gl_SampleID; +} + +)"); +} + } // namespace } // namespace glsl } // namespace writer diff --git a/src/writer/glsl/generator_impl_uniform_buffer_test.cc b/src/writer/glsl/generator_impl_uniform_buffer_test.cc new file mode 100644 index 0000000000..8dd7fd54a9 --- /dev/null +++ b/src/writer/glsl/generator_impl_uniform_buffer_test.cc @@ -0,0 +1,72 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "src/writer/glsl/test_helper.h" + +using ::testing::HasSubstr; + +namespace tint { +namespace writer { +namespace glsl { +namespace { + +using GlslGeneratorImplTest_UniformBuffer = TestHelper; + +TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple) { + auto* simple = Structure("Simple", {Member("member", ty.f32())}); + Global("simple", ty.Of(simple), ast::StorageClass::kUniform, + GroupAndBinding(0, 0)); + + GeneratorImpl& gen = Build(); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 310 es + +struct Simple { + float member; +}; + +layout(binding = 0) uniform Simple_1 { + float member; +} simple; + +)"); +} + +TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple_Desktop) { + auto* simple = Structure("Simple", {Member("member", ty.f32())}); + Global("simple", ty.Of(simple), ast::StorageClass::kUniform, + GroupAndBinding(0, 0)); + + GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4)); + + ASSERT_TRUE(gen.Generate()) << gen.error(); + EXPECT_EQ(gen.result(), R"(#version 440 + +struct Simple { + float member; +}; + +layout(binding = 0, std140) uniform Simple_1 { + float member; +} simple; + +)"); +} + +} // namespace +} // namespace glsl +} // namespace writer +} // namespace tint diff --git a/src/writer/glsl/test_helper.h b/src/writer/glsl/test_helper.h index 0364f6ca81..3df482d1e9 100644 --- a/src/writer/glsl/test_helper.h +++ b/src/writer/glsl/test_helper.h @@ -39,8 +39,9 @@ class TestHelperBase : public BODY, public ProgramBuilder { /// Builds the program and returns a GeneratorImpl from the program. /// @note The generator is only built once. Multiple calls to Build() will /// return the same GeneratorImpl without rebuilding. + /// @param version the GLSL version /// @return the built generator - GeneratorImpl& Build() { + GeneratorImpl& Build(Version version = Version()) { if (gen_) { return *gen_; } @@ -53,7 +54,7 @@ class TestHelperBase : public BODY, public ProgramBuilder { ASSERT_TRUE(program->IsValid()) << diag::Formatter().format(program->Diagnostics()); }(); - gen_ = std::make_unique(program.get()); + gen_ = std::make_unique(program.get(), version); return *gen_; } @@ -61,8 +62,9 @@ class TestHelperBase : public BODY, public ProgramBuilder { /// and returns a GeneratorImpl from the sanitized program. /// @note The generator is only built once. Multiple calls to Build() will /// return the same GeneratorImpl without rebuilding. + /// @param version the GLSL version /// @return the built generator - GeneratorImpl& SanitizeAndBuild() { + GeneratorImpl& SanitizeAndBuild(Version version = Version()) { if (gen_) { return *gen_; } @@ -89,7 +91,7 @@ class TestHelperBase : public BODY, public ProgramBuilder { << formatter.format(result.program.Diagnostics()); }(); *program = std::move(result.program); - gen_ = std::make_unique(program.get()); + gen_ = std::make_unique(program.get(), version); return *gen_; } diff --git a/src/writer/glsl/version.h b/src/writer/glsl/version.h new file mode 100644 index 0000000000..14126e7a51 --- /dev/null +++ b/src/writer/glsl/version.h @@ -0,0 +1,56 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_WRITER_GLSL_VERSION_H_ +#define SRC_WRITER_GLSL_VERSION_H_ + +namespace tint::writer::glsl { + +/// A structure representing the version of GLSL to be generated. +struct Version { + /// Is this version desktop GLSL, or GLSL ES? + enum class Standard { + kDesktop, + kES, + }; + + /// Constructor + /// @param standard_ Desktop or ES + /// @param major_ the major version + /// @param minor_ the minor version + Version(Standard standard_, uint32_t major_, uint32_t minor_) + : standard(standard_), major_version(major_), minor_version(minor_) {} + + /// Default constructor (see default values below) + Version() = default; + + /// @returns true if this version is GLSL ES + bool IsES() const { return standard == Standard::kES; } + + /// @returns true if this version is Desktop GLSL + bool IsDesktop() const { return standard == Standard::kDesktop; } + + /// Desktop or ES + Standard standard = Standard::kES; + + /// Major GLSL version + uint32_t major_version = 3; + + /// Minor GLSL version + uint32_t minor_version = 1; +}; + +} // namespace tint::writer::glsl + +#endif // SRC_WRITER_GLSL_VERSION_H_ diff --git a/test/BUILD.gn b/test/BUILD.gn index 2eb530a26d..a0952bd7f7 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -237,15 +237,15 @@ tint_unittests_source_set("tint_unittests_resolver_src") { "../src/resolver/assignment_validation_test.cc", "../src/resolver/atomics_test.cc", "../src/resolver/atomics_validation_test.cc", + "../src/resolver/attribute_validation_test.cc", "../src/resolver/bitcast_validation_test.cc", - "../src/resolver/builtins_validation_test.cc", "../src/resolver/builtin_test.cc", "../src/resolver/builtin_validation_test.cc", + "../src/resolver/builtins_validation_test.cc", "../src/resolver/call_test.cc", "../src/resolver/call_validation_test.cc", "../src/resolver/compound_statement_test.cc", "../src/resolver/control_block_validation_test.cc", - "../src/resolver/attribute_validation_test.cc", "../src/resolver/dependency_graph_test.cc", "../src/resolver/entry_point_validation_test.cc", "../src/resolver/function_validation_test.cc", @@ -473,9 +473,9 @@ tint_unittests_source_set("tint_unittests_wgsl_reader_src") { "../src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc", "../src/reader/wgsl/parser_impl_external_texture_type_test.cc", "../src/reader/wgsl/parser_impl_for_stmt_test.cc", - "../src/reader/wgsl/parser_impl_function_decl_test.cc", "../src/reader/wgsl/parser_impl_function_attribute_list_test.cc", "../src/reader/wgsl/parser_impl_function_attribute_test.cc", + "../src/reader/wgsl/parser_impl_function_decl_test.cc", "../src/reader/wgsl/parser_impl_function_header_test.cc", "../src/reader/wgsl/parser_impl_global_constant_decl_test.cc", "../src/reader/wgsl/parser_impl_global_decl_test.cc", @@ -500,10 +500,10 @@ tint_unittests_source_set("tint_unittests_wgsl_reader_src") { "../src/reader/wgsl/parser_impl_statements_test.cc", "../src/reader/wgsl/parser_impl_storage_class_test.cc", "../src/reader/wgsl/parser_impl_storage_texture_type_test.cc", - "../src/reader/wgsl/parser_impl_struct_body_decl_test.cc", - "../src/reader/wgsl/parser_impl_struct_decl_test.cc", "../src/reader/wgsl/parser_impl_struct_attribute_decl_test.cc", "../src/reader/wgsl/parser_impl_struct_attribute_test.cc", + "../src/reader/wgsl/parser_impl_struct_body_decl_test.cc", + "../src/reader/wgsl/parser_impl_struct_decl_test.cc", "../src/reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc", "../src/reader/wgsl/parser_impl_struct_member_attribute_test.cc", "../src/reader/wgsl/parser_impl_struct_member_test.cc", @@ -517,9 +517,9 @@ tint_unittests_source_set("tint_unittests_wgsl_reader_src") { "../src/reader/wgsl/parser_impl_type_alias_test.cc", "../src/reader/wgsl/parser_impl_type_decl_test.cc", "../src/reader/wgsl/parser_impl_unary_expression_test.cc", - "../src/reader/wgsl/parser_impl_variable_decl_test.cc", "../src/reader/wgsl/parser_impl_variable_attribute_list_test.cc", "../src/reader/wgsl/parser_impl_variable_attribute_test.cc", + "../src/reader/wgsl/parser_impl_variable_decl_test.cc", "../src/reader/wgsl/parser_impl_variable_ident_decl_test.cc", "../src/reader/wgsl/parser_impl_variable_qualifier_test.cc", "../src/reader/wgsl/parser_impl_variable_stmt_test.cc", @@ -674,10 +674,12 @@ tint_unittests_source_set("tint_unittests_glsl_writer_src") { "../src/writer/glsl/generator_impl_module_constant_test.cc", "../src/writer/glsl/generator_impl_return_test.cc", "../src/writer/glsl/generator_impl_sanitizer_test.cc", + "../src/writer/glsl/generator_impl_storage_buffer_test.cc", "../src/writer/glsl/generator_impl_switch_test.cc", "../src/writer/glsl/generator_impl_test.cc", "../src/writer/glsl/generator_impl_type_test.cc", "../src/writer/glsl/generator_impl_unary_op_test.cc", + "../src/writer/glsl/generator_impl_uniform_buffer_test.cc", "../src/writer/glsl/generator_impl_variable_decl_statement_test.cc", "../src/writer/glsl/generator_impl_workgroup_var_test.cc", "../src/writer/glsl/test_helper.h",