diff --git a/BUILD.gn b/BUILD.gn index cd54e34d8e..2f831c82d7 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -412,6 +412,8 @@ source_set("libtint_core_src") { "src/scope_stack.h", "src/source.cc", "src/source.h", + "src/transform/emit_vertex_point_size_transform.cc", + "src/transform/emit_vertex_point_size_transform.h", "src/transform/bound_array_accessors_transform.cc", "src/transform/bound_array_accessors_transform.h", "src/transform/manager.cc", @@ -816,6 +818,7 @@ source_set("tint_unittests_core_src") { "src/inspector/inspector_test.cc", "src/namer_test.cc", "src/scope_stack_test.cc", + "src/transform/emit_vertex_point_size_transform_test.cc", "src/transform/bound_array_accessors_transform_test.cc", "src/transform/vertex_pulling_transform_test.cc", "src/type_determiner_test.cc", diff --git a/include/tint/tint.h b/include/tint/tint.h index 5d9a84a793..53cf343de0 100644 --- a/include/tint/tint.h +++ b/include/tint/tint.h @@ -26,6 +26,7 @@ #include "src/namer.h" #include "src/reader/reader.h" #include "src/transform/bound_array_accessors_transform.h" +#include "src/transform/emit_vertex_point_size_transform.h" #include "src/transform/manager.h" #include "src/transform/vertex_pulling_transform.h" #include "src/type_determiner.h" diff --git a/samples/main.cc b/samples/main.cc index dcdea27398..e4cf9d00e2 100644 --- a/samples/main.cc +++ b/samples/main.cc @@ -74,6 +74,7 @@ const char kUsage[] = R"(Usage: tint [options] --transform -- Runs transformers, name list is comma separated Available transforms: bound_array_accessors + emit_vertex_point_size --parse-only -- Stop after parsing the input --dump-ast -- Dump the generated AST to stdout --dawn-validation -- SPIRV outputs are validated with the same flags @@ -516,6 +517,10 @@ int main(int argc, const char** argv) { transform_manager.append( std::make_unique( &mod)); + } else if (name == "emit_vertex_point_size") { + transform_manager.append( + std::make_unique( + &mod)); } else { std::cerr << "Unknown transform name: " << name << std::endl; return 1; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da3f67d2ce..b3eb97a138 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -233,6 +233,8 @@ set(TINT_LIB_SRCS scope_stack.h source.cc source.h + transform/emit_vertex_point_size_transform.cc + transform/emit_vertex_point_size_transform.h transform/bound_array_accessors_transform.cc transform/bound_array_accessors_transform.h transform/manager.cc @@ -426,6 +428,7 @@ set(TINT_TEST_SRCS inspector/inspector_test.cc namer_test.cc scope_stack_test.cc + transform/emit_vertex_point_size_transform_test.cc transform/bound_array_accessors_transform_test.cc transform/vertex_pulling_transform_test.cc type_determiner_test.cc diff --git a/src/ast/builtin.cc b/src/ast/builtin.cc index d5818a14e8..5a7eb63b59 100644 --- a/src/ast/builtin.cc +++ b/src/ast/builtin.cc @@ -59,6 +59,9 @@ std::ostream& operator<<(std::ostream& out, Builtin builtin) { out << "global_invocation_id"; break; } + case Builtin::kPointSize: { + out << "pointsize"; + } } return out; } diff --git a/src/ast/builtin.h b/src/ast/builtin.h index 5d0d763e66..9b529180ea 100644 --- a/src/ast/builtin.h +++ b/src/ast/builtin.h @@ -31,7 +31,11 @@ enum class Builtin { kFragDepth, kLocalInvocationId, kLocalInvocationIdx, - kGlobalInvocationId + kGlobalInvocationId, + + // Below are not currently WGSL builtins, but are included in this enum as + // they are used by certain backends. + kPointSize, }; std::ostream& operator<<(std::ostream& out, Builtin builtin); diff --git a/src/ast/module.cc b/src/ast/module.cc index 635d89b4d3..a5fe39ae28 100644 --- a/src/ast/module.cc +++ b/src/ast/module.cc @@ -65,6 +65,15 @@ Function* Module::FindFunctionByNameAndStage(const std::string& name, return nullptr; } +bool Module::HasStage(ast::PipelineStage stage) const { + for (auto* func : functions_) { + if (func->pipeline_stage() == stage) { + return true; + } + } + return false; +} + bool Module::IsValid() const { for (auto* var : global_variables_) { if (var == nullptr || !var->IsValid()) { diff --git a/src/ast/module.h b/src/ast/module.h index 35c5a76e50..e4313c38e4 100644 --- a/src/ast/module.h +++ b/src/ast/module.h @@ -83,6 +83,10 @@ class Module { /// @returns the associated function or nullptr if none exists Function* FindFunctionByNameAndStage(const std::string& name, PipelineStage stage) const; + /// @param stage the pipeline stage + /// @returns true if the module contains an entrypoint function with the given + /// stage + bool HasStage(PipelineStage stage) const; /// @returns true if all required fields in the AST are present. bool IsValid() const; diff --git a/src/transform/emit_vertex_point_size_transform.cc b/src/transform/emit_vertex_point_size_transform.cc new file mode 100644 index 0000000000..d20c8eacc7 --- /dev/null +++ b/src/transform/emit_vertex_point_size_transform.cc @@ -0,0 +1,78 @@ +// Copyright 2020 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 "src/transform/emit_vertex_point_size_transform.h" + +#include +#include + +#include "src/ast/assignment_statement.h" +#include "src/ast/block_statement.h" +#include "src/ast/decorated_variable.h" +#include "src/ast/float_literal.h" +#include "src/ast/identifier_expression.h" +#include "src/ast/scalar_constructor_expression.h" +#include "src/ast/type/f32_type.h" +#include "src/ast/type_manager.h" + +namespace tint { +namespace transform { +namespace { + +const char kPointSizeVar[] = "tint_pointsize"; + +} // namespace + +EmitVertexPointSizeTransform::EmitVertexPointSizeTransform(ast::Module* mod) + : Transformer(mod) {} + +EmitVertexPointSizeTransform::~EmitVertexPointSizeTransform() = default; + +bool EmitVertexPointSizeTransform::Run() { + if (!mod_->HasStage(ast::PipelineStage::kVertex)) { + // If the module doesn't have any vertex stages, then there's nothing to do. + return true; + } + + auto* f32 = mod_->create(); + + // Declare the pointsize builtin output variable. + auto* pointsize_var = + mod_->create(mod_->create( + kPointSizeVar, ast::StorageClass::kOutput, f32)); + pointsize_var->set_decorations({ + mod_->create(ast::Builtin::kPointSize, Source{}), + }); + mod_->AddGlobalVariable(pointsize_var); + + // Build the AST expression & statement for assigning pointsize one. + auto* one = mod_->create( + mod_->create(f32, 1.0f)); + auto* pointsize_ident = + mod_->create(Source{}, kPointSizeVar); + auto* pointsize_assign = + mod_->create(pointsize_ident, one); + + // Add the pointsize assignment statement to the front of all vertex stages. + for (auto* func : mod_->functions()) { + if (func->pipeline_stage() == ast::PipelineStage::kVertex) { + func->body()->insert(0, pointsize_assign); + } + } + + return true; +} + +} // namespace transform +} // namespace tint diff --git a/src/transform/emit_vertex_point_size_transform.h b/src/transform/emit_vertex_point_size_transform.h new file mode 100644 index 0000000000..6ffc913b75 --- /dev/null +++ b/src/transform/emit_vertex_point_size_transform.h @@ -0,0 +1,45 @@ +// Copyright 2020 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_TRANSFORM_EMIT_VERTEX_POINT_SIZE_TRANSFORM_H_ +#define SRC_TRANSFORM_EMIT_VERTEX_POINT_SIZE_TRANSFORM_H_ + +#include "src/transform/transformer.h" + +namespace tint { +namespace transform { + +/// EmitVertexPointSizeTransform is a Transformer that adds a PointSize builtin +/// global output variable to the module which is assigned 1.0 as the new first +/// statement for all vertex stage entry points. +/// If the module does not contain a vertex pipeline stage entry point then then +/// this transformer is a no-op. +class EmitVertexPointSizeTransform : public Transformer { + public: + /// Constructor + /// @param mod the module transform + explicit EmitVertexPointSizeTransform(ast::Module* mod); + ~EmitVertexPointSizeTransform() override; + + /// Users of Tint should register the transform with transform manager and + /// invoke its Run(), instead of directly calling the transform's Run(). + /// Calling Run() directly does not perform module state cleanup operations. + /// @returns true if the transformation was successful + bool Run() override; +}; + +} // namespace transform +} // namespace tint + +#endif // SRC_TRANSFORM_EMIT_VERTEX_POINT_SIZE_TRANSFORM_H_ diff --git a/src/transform/emit_vertex_point_size_transform_test.cc b/src/transform/emit_vertex_point_size_transform_test.cc new file mode 100644 index 0000000000..0167bf61bf --- /dev/null +++ b/src/transform/emit_vertex_point_size_transform_test.cc @@ -0,0 +1,186 @@ +// Copyright 2020 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 "src/transform/emit_vertex_point_size_transform.h" + +#include +#include + +#include "gtest/gtest.h" +#include "src/ast/builder.h" +#include "src/ast/call_statement.h" +#include "src/ast/stage_decoration.h" +#include "src/transform/manager.h" + +namespace tint { +namespace transform { +namespace { + +class EmitVertexPointSizeTransformTest : public testing::Test, + public ast::BuilderWithModule { + public: + EmitVertexPointSizeTransformTest() { + auto transform = std::make_unique(mod); + manager = std::make_unique(); + manager->append(std::move(transform)); + } + + std::unique_ptr manager; +}; + +TEST_F(EmitVertexPointSizeTransformTest, VertexStageBasic) { + auto* block = create(Source{}); + block->append(create(create( + Source{}, + create( + Source{}, "builtin_assignments_should_happen_before_this"), + ast::ExpressionList{}))); + + mod->AddFunction(create( + "non_entry_a", ast::VariableList{}, create(), + create(Source{}))); + + auto* entry = create("entry", ast::VariableList{}, + create(), block); + entry->set_decorations( + {create(ast::PipelineStage::kVertex, Source{})}); + mod->AddFunction(entry); + + mod->AddFunction(create( + "non_entry_b", ast::VariableList{}, create(), + create(Source{}))); + + manager->Run(mod); + + auto* expected = R"(Module{ + DecoratedVariable{ + Decorations{ + BuiltinDecoration{pointsize} + } + tint_pointsize + out + __f32 + } + Function non_entry_a -> __void + () + { + } + Function entry -> __void + StageDecoration{vertex} + () + { + Assignment{ + Identifier[__ptr_out__f32]{tint_pointsize} + ScalarConstructor[__f32]{1.000000} + } + Call[not set]{ + Identifier[not set]{builtin_assignments_should_happen_before_this} + ( + ) + } + } + Function non_entry_b -> __void + () + { + } +} +)"; + EXPECT_EQ(expected, mod->to_str()); +} + +TEST_F(EmitVertexPointSizeTransformTest, VertexStageEmpty) { + mod->AddFunction(create( + "non_entry_a", ast::VariableList{}, create(), + create(Source{}))); + + auto* entry = create("entry", ast::VariableList{}, + create(), + create(Source{})); + entry->set_decorations( + {create(ast::PipelineStage::kVertex, Source{})}); + mod->AddFunction(entry); + + mod->AddFunction(create( + "non_entry_b", ast::VariableList{}, create(), + create(Source{}))); + + manager->Run(mod); + + auto* expected = R"(Module{ + DecoratedVariable{ + Decorations{ + BuiltinDecoration{pointsize} + } + tint_pointsize + out + __f32 + } + Function non_entry_a -> __void + () + { + } + Function entry -> __void + StageDecoration{vertex} + () + { + Assignment{ + Identifier[__ptr_out__f32]{tint_pointsize} + ScalarConstructor[__f32]{1.000000} + } + } + Function non_entry_b -> __void + () + { + } +} +)"; + EXPECT_EQ(expected, mod->to_str()); +} + +TEST_F(EmitVertexPointSizeTransformTest, NonVertexStage) { + auto* fragment_entry = create( + "fragment_entry", ast::VariableList{}, create(), + create(Source{})); + fragment_entry->set_decorations( + {create(ast::PipelineStage::kFragment, Source{})}); + mod->AddFunction(fragment_entry); + + auto* compute_entry = create( + "compute_entry", ast::VariableList{}, create(), + create(Source{})); + compute_entry->set_decorations( + {create(ast::PipelineStage::kCompute, Source{})}); + mod->AddFunction(compute_entry); + + manager->Run(mod); + + auto* expected = R"(Module{ + Function fragment_entry -> __void + StageDecoration{fragment} + () + { + } + Function compute_entry -> __void + StageDecoration{compute} + () + { + } +} +)"; + EXPECT_EQ(expected, mod->to_str()); +} + +} // namespace +} // namespace transform +} // namespace tint diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index dd7e1c178e..0fc7f49950 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc @@ -2766,6 +2766,8 @@ SpvBuiltIn Builder::ConvertBuiltin(ast::Builtin builtin) const { return SpvBuiltInLocalInvocationIndex; case ast::Builtin::kGlobalInvocationId: return SpvBuiltInGlobalInvocationId; + case ast::Builtin::kPointSize: + return SpvBuiltInPointSize; case ast::Builtin::kNone: break; }