GLSL: implement desktop GLSL support.

Introduce a glsl::Version, to allow the client to specify ES or
Desktop, as well as the desired GLSL major and minor version.

Bug: tint:1422

Change-Id: I4116bc2da40ae6a553dc2522d042dda1464a0c05
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79700
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Stephen White 2022-02-09 17:44:17 +00:00 committed by Tint LUCI CQ
parent d21ebe74bd
commit 35cc663130
11 changed files with 343 additions and 23 deletions

View File

@ -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

View File

@ -53,7 +53,7 @@ Result Generate(const Program* program,
}
// Generate the GLSL code.
auto impl = std::make_unique<GeneratorImpl>(&output.program);
auto impl = std::make_unique<GeneratorImpl>(&output.program, options.version);
result.success = impl->Generate();
result.error = impl->error();
result.glsl = impl->result();

View File

@ -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.

View File

@ -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;

View File

@ -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<const sem::Vector*, std::string> int_dot_funcs_;
bool requires_oes_sample_variables_ = false;
bool requires_default_precision_qualifier_ = false;
Version version_;
};
} // namespace glsl

View File

@ -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<storage, read_write> 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<ast::BindingAttribute>(0),
ctx->create<ast::GroupAttribute>(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

View File

@ -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

View File

@ -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

View File

@ -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<GeneratorImpl>(program.get());
gen_ = std::make_unique<GeneratorImpl>(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<GeneratorImpl>(program.get());
gen_ = std::make_unique<GeneratorImpl>(program.get(), version);
return *gen_;
}

56
src/writer/glsl/version.h Normal file
View File

@ -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_

View File

@ -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",