diff --git a/docs/amber_script.md b/docs/amber_script.md index e395911b4..51eac9be8 100644 --- a/docs/amber_script.md +++ b/docs/amber_script.md @@ -315,6 +315,12 @@ The following commands are all specified within the `PIPELINE` command. END ``` +```groovy + # Set the polygon mode used for all drawing with the pipeline. + # |mode| is fill, line, or point and it defaults to fill. + POLYGON_MODE {mode} +``` + ```groovy # Set the size of the render buffers. |width| and |height| are integers and # default to 250x250. diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index 7b6613ada..8d124836d 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -446,6 +446,8 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name, r = ParsePipelineSet(pipeline.get()); } else if (tok == "COMPILE_OPTIONS") { r = ParsePipelineShaderCompileOptions(pipeline.get()); + } else if (tok == "POLYGON_MODE") { + r = ParsePipelinePolygonMode(pipeline.get()); } else { r = Result("unknown token in pipeline block: " + tok); } @@ -1045,6 +1047,25 @@ Result Parser::ParsePipelineSet(Pipeline* pipeline) { return ValidateEndOfStatement("SET command"); } +Result Parser::ParsePipelinePolygonMode(Pipeline* pipeline) { + auto token = tokenizer_->NextToken(); + if (!token->IsIdentifier()) + return Result("missing mode in POLYGON_MODE command"); + + auto mode = token->AsString(); + + if (mode == "fill") + pipeline->SetPolygonMode(PolygonMode::kFill); + else if (mode == "line") + pipeline->SetPolygonMode(PolygonMode::kLine); + else if (mode == "point") + pipeline->SetPolygonMode(PolygonMode::kPoint); + else + return Result("invalid polygon mode: " + mode); + + return ValidateEndOfStatement("POLYGON_MODE command"); +} + Result Parser::ParseStruct() { auto token = tokenizer_->NextToken(); if (!token->IsIdentifier()) @@ -1646,6 +1667,7 @@ Result Parser::ParseRun() { command_list_.push_back(std::move(cmd)); return ValidateEndOfStatement("RUN command"); } + if (!token->IsIdentifier()) return Result("invalid token in RUN command: " + token->ToOriginalString()); @@ -1675,6 +1697,7 @@ Result Parser::ParseRun() { auto cmd = MakeUnique(pipeline, PipelineData{}); cmd->SetLine(line); cmd->EnableOrtho(); + cmd->SetPolygonMode(pipeline->GetPolygonMode()); Result r = token->ConvertToDouble(); if (!r.IsSuccess()) @@ -1743,6 +1766,7 @@ Result Parser::ParseRun() { auto cmd = MakeUnique(pipeline); cmd->SetLine(line); + cmd->SetPolygonMode(pipeline->GetPolygonMode()); Result r = token->ConvertToDouble(); if (!r.IsSuccess()) @@ -1884,6 +1908,7 @@ Result Parser::ParseRun() { cmd->SetTopology(topo); cmd->SetFirstVertexIndex(start_idx); cmd->SetVertexCount(count); + cmd->SetPolygonMode(pipeline->GetPolygonMode()); if (indexed) cmd->EnableIndexed(); diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h index eb40d1559..8f62e9014 100644 --- a/src/amberscript/parser.h +++ b/src/amberscript/parser.h @@ -66,6 +66,7 @@ class Parser : public amber::Parser { Result ParsePipelineVertexData(Pipeline*); Result ParsePipelineIndexData(Pipeline*); Result ParsePipelineSet(Pipeline*); + Result ParsePipelinePolygonMode(Pipeline*); Result ParseRun(); Result ParseDebug(); Result ParseDebugThread(debug::Events*); diff --git a/src/amberscript/parser_pipeline_test.cc b/src/amberscript/parser_pipeline_test.cc index 98c85ced0..3e2922b51 100644 --- a/src/amberscript/parser_pipeline_test.cc +++ b/src/amberscript/parser_pipeline_test.cc @@ -240,6 +240,97 @@ END)"; r.Error()); } +TEST_F(AmberScriptParserTest, PipelinePolygonMode) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END + +PIPELINE graphics my_pipeline_default + ATTACH my_shader + ATTACH my_fragment + FRAMEBUFFER_SIZE 256 256 +END +PIPELINE graphics my_pipeline_fill + ATTACH my_shader + ATTACH my_fragment + POLYGON_MODE fill + FRAMEBUFFER_SIZE 256 256 +END +PIPELINE graphics my_pipeline_line + ATTACH my_shader + ATTACH my_fragment + POLYGON_MODE line + FRAMEBUFFER_SIZE 256 256 +END +PIPELINE graphics my_pipeline_point + ATTACH my_shader + ATTACH my_fragment + POLYGON_MODE point + FRAMEBUFFER_SIZE 256 256 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(4U, pipelines.size()); + + auto mode0 = pipelines[0]->GetPolygonMode(); + ASSERT_EQ(mode0, PolygonMode::kFill); + auto mode1 = pipelines[1]->GetPolygonMode(); + ASSERT_EQ(mode1, PolygonMode::kFill); + auto mode2 = pipelines[2]->GetPolygonMode(); + ASSERT_EQ(mode2, PolygonMode::kLine); + auto mode3 = pipelines[3]->GetPolygonMode(); + ASSERT_EQ(mode3, PolygonMode::kPoint); +} + +TEST_F(AmberScriptParserTest, PipelineMissingPolygonMode) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + POLYGON_MODE + FRAMEBUFFER_SIZE 256 256 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + + EXPECT_EQ("11: missing mode in POLYGON_MODE command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineInvalidPolygonMode) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + POLYGON_MODE foo + FRAMEBUFFER_SIZE 256 256 +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + + EXPECT_EQ("10: invalid polygon mode: foo", r.Error()); +} + TEST_F(AmberScriptParserTest, DerivePipeline) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH diff --git a/src/command.h b/src/command.h index 939ab9ef7..270864d6e 100644 --- a/src/command.h +++ b/src/command.h @@ -175,6 +175,8 @@ class DrawRectCommand : public PipelineCommand { void SetHeight(float h) { height_ = h; } float GetHeight() const { return height_; } + void SetPolygonMode(PolygonMode mode) { data_.SetPolygonMode(mode); } + std::string ToString() const override { return "DrawRectCommand"; } private: @@ -211,6 +213,9 @@ class DrawGridCommand : public PipelineCommand { void SetRows(uint32_t r) { rows_ = r; } uint32_t GetRows() const { return rows_; } + void SetPolygonMode(PolygonMode mode) { polygon_mode_ = mode; } + PolygonMode GetPolygonMode() const { return polygon_mode_; } + std::string ToString() const override { return "DrawGridCommand"; } private: @@ -220,6 +225,7 @@ class DrawGridCommand : public PipelineCommand { float height_ = 0.0; uint32_t columns_ = 0; uint32_t rows_ = 0; + PolygonMode polygon_mode_ = PolygonMode::kFill; }; /// Command to draw from a vertex and index buffer. @@ -239,6 +245,8 @@ class DrawArraysCommand : public PipelineCommand { void SetTopology(Topology topo) { topology_ = topo; } Topology GetTopology() const { return topology_; } + void SetPolygonMode(PolygonMode mode) { data_.SetPolygonMode(mode); } + void SetFirstVertexIndex(uint32_t idx) { first_vertex_index_ = idx; } uint32_t GetFirstVertexIndex() const { return first_vertex_index_; } diff --git a/src/pipeline.cc b/src/pipeline.cc index 747a6c344..eca6326b7 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -182,6 +182,16 @@ Result Pipeline::SetShaderType(const Shader* shader, ShaderType type) { shader->GetName()); } +Result Pipeline::SetPolygonMode(PolygonMode mode) { + if (mode != PolygonMode::kFill && mode != PolygonMode::kLine && + mode != PolygonMode::kPoint) + return Result("invalid polygon mode specified for pipeline"); + + polygon_mode_ = mode; + + return {}; +} + Result Pipeline::Validate() const { for (const auto& attachment : color_attachments_) { if (attachment.buffer->ElementCount() != diff --git a/src/pipeline.h b/src/pipeline.h index c545a5965..a25b18435 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -24,6 +24,7 @@ #include "amber/result.h" #include "src/buffer.h" +#include "src/command_data.h" #include "src/sampler.h" #include "src/shader.h" @@ -284,6 +285,9 @@ class Pipeline { return push_constant_buffer_; } + Result SetPolygonMode(PolygonMode mode); + PolygonMode GetPolygonMode() const { return polygon_mode_; } + /// Validates that the pipeline has been created correctly. Result Validate() const; @@ -332,6 +336,7 @@ class Pipeline { BufferInfo depth_buffer_; BufferInfo push_constant_buffer_; Buffer* index_buffer_ = nullptr; + PolygonMode polygon_mode_ = PolygonMode::kFill; uint32_t fb_width_ = 250; uint32_t fb_height_ = 250; diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index afb09923e..d4a402802 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -531,6 +531,7 @@ Result EngineVulkan::DoDrawGrid(const DrawGridCommand* command) { draw.SetFirstVertexIndex(0); draw.SetVertexCount(vertices); draw.SetInstanceCount(1); + draw.SetPolygonMode(command->GetPolygonMode()); Result r = graphics->Draw(&draw, vertex_buffer.get()); if (!r.IsSuccess()) diff --git a/tests/cases/draw_polygon_mode.amber b/tests/cases/draw_polygon_mode.amber new file mode 100644 index 000000000..70ae15bd4 --- /dev/null +++ b/tests/cases/draw_polygon_mode.amber @@ -0,0 +1,90 @@ +#!amber +# Copyright 2020 The Amber 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 +# +# https://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. + +DEVICE_FEATURE fillModeNonSolid + +SHADER vertex vert_shader PASSTHROUGH + +SHADER fragment frag_shader GLSL +#version 430 + +layout(location = 0) out vec4 color; + +void main() +{ + color = vec4(1); +} +END + +BUFFER position_large DATA_TYPE R8G8_SNORM DATA +-120 -120 + 0 120 + 120 -120 +END + +BUFFER position_small DATA_TYPE R8G8_SNORM DATA +-60 -60 + 0 60 + 60 -60 +END + +BUFFER framebuffer FORMAT B8G8R8A8_UNORM + +PIPELINE graphics pipeline_line + ATTACH vert_shader + ATTACH frag_shader + + VERTEX_DATA position_large LOCATION 0 + POLYGON_MODE line + + BIND BUFFER framebuffer AS color LOCATION 0 + FRAMEBUFFER_SIZE 256 256 +END + +PIPELINE graphics pipeline_point + ATTACH vert_shader + ATTACH frag_shader + + VERTEX_DATA position_small LOCATION 0 + POLYGON_MODE point + + BIND BUFFER framebuffer AS color LOCATION 0 + FRAMEBUFFER_SIZE 256 256 +END + +PIPELINE graphics pipeline_line_grid + ATTACH vert_shader + ATTACH frag_shader + POLYGON_MODE line + + BIND BUFFER framebuffer AS color LOCATION 0 + FRAMEBUFFER_SIZE 256 256 +END + +CLEAR_COLOR pipeline_line 0 0 0 255 +CLEAR pipeline_line +# Draw a large triangle with lines +RUN pipeline_line DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 3 +# Draw a rect with points +RUN pipeline_point DRAW_RECT POS 20 20 SIZE 216 216 +# Draw a small triangle with points +RUN pipeline_point DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 3 +# Draw a line grid overlapping the small triangle +RUN pipeline_line_grid DRAW_GRID POS 80 40 SIZE 96 96 CELLS 3 3 + +# Make sure no solid triangle was drawn by checking the center +# of the middle grid triangle. That location is covered by all +# drawn primitives. +EXPECT framebuffer IDX 134 82 SIZE 5 5 EQ_RGBA 0 0 0 255