From 75daec6e7b12e20ee0883158389645ecf970aa8e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 18 Nov 2021 11:42:00 -0800 Subject: [PATCH] Rendering of continuous strokes. --- .../flutter/impeller/aiks/aiks_unittests.cc | 12 ++++ engine/src/flutter/impeller/entity/BUILD.gn | 2 + .../impeller/entity/content_renderer.cc | 9 +++ .../impeller/entity/content_renderer.h | 7 ++ .../src/flutter/impeller/entity/contents.cc | 68 +++++++++++++++++-- .../impeller/entity/shaders/solid_stroke.frag | 11 +++ .../impeller/entity/shaders/solid_stroke.vert | 23 +++++++ .../flutter/impeller/geometry/path_builder.cc | 5 ++ .../flutter/impeller/geometry/path_builder.h | 2 + engine/src/flutter/impeller/geometry/point.h | 12 ++++ .../backend/metal/vertex_descriptor_mtl.h | 11 ++- .../backend/metal/vertex_descriptor_mtl.mm | 5 +- .../flutter/impeller/renderer/shader_types.h | 2 + .../impeller/renderer/vertex_buffer_builder.h | 2 + 14 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 engine/src/flutter/impeller/entity/shaders/solid_stroke.frag create mode 100644 engine/src/flutter/impeller/entity/shaders/solid_stroke.vert diff --git a/engine/src/flutter/impeller/aiks/aiks_unittests.cc b/engine/src/flutter/impeller/aiks/aiks_unittests.cc index d0135aa8683..f713c007949 100644 --- a/engine/src/flutter/impeller/aiks/aiks_unittests.cc +++ b/engine/src/flutter/impeller/aiks/aiks_unittests.cc @@ -81,6 +81,18 @@ TEST_F(AiksTest, CanRenderStrokes) { Paint paint; paint.color = Color::Red(); paint.stroke_width = 20.0; + paint.style = Paint::Style::kStroke; + canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).CreatePath(), + paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderCurvedStrokes) { + Canvas canvas; + Paint paint; + paint.color = Color::Blue(); + paint.stroke_width = 25.0; + paint.style = Paint::Style::kStroke; canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/engine/src/flutter/impeller/entity/BUILD.gn b/engine/src/flutter/impeller/entity/BUILD.gn index b465c0cdd5d..e3c1d596ef5 100644 --- a/engine/src/flutter/impeller/entity/BUILD.gn +++ b/engine/src/flutter/impeller/entity/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("entity_shaders") { "shaders/gradient_fill.vert", "shaders/solid_fill.frag", "shaders/solid_fill.vert", + "shaders/solid_stroke.frag", + "shaders/solid_stroke.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", ] diff --git a/engine/src/flutter/impeller/entity/content_renderer.cc b/engine/src/flutter/impeller/entity/content_renderer.cc index 6ea7fb76e42..4209027a842 100644 --- a/engine/src/flutter/impeller/entity/content_renderer.cc +++ b/engine/src/flutter/impeller/entity/content_renderer.cc @@ -15,6 +15,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) gradient_fill_pipeline_ = std::make_unique(*context_); solid_fill_pipeline_ = std::make_unique(*context_); texture_pipeline_ = std::make_unique(*context_); + solid_stroke_pipeline_ = std::make_unique(*context_); is_valid_ = true; } @@ -52,4 +53,12 @@ std::shared_ptr ContentRenderer::GetTexturePipeline() const { return texture_pipeline_->WaitAndGet(); } +std::shared_ptr ContentRenderer::GetSolidStrokePipeline() const { + if (!IsValid()) { + return nullptr; + } + + return solid_stroke_pipeline_->WaitAndGet(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/content_renderer.h b/engine/src/flutter/impeller/entity/content_renderer.h index 997655329b5..781c6aeb36e 100644 --- a/engine/src/flutter/impeller/entity/content_renderer.h +++ b/engine/src/flutter/impeller/entity/content_renderer.h @@ -11,6 +11,8 @@ #include "flutter/impeller/entity/gradient_fill.vert.h" #include "flutter/impeller/entity/solid_fill.frag.h" #include "flutter/impeller/entity/solid_fill.vert.h" +#include "flutter/impeller/entity/solid_stroke.frag.h" +#include "flutter/impeller/entity/solid_stroke.vert.h" #include "flutter/impeller/entity/texture_fill.frag.h" #include "flutter/impeller/entity/texture_fill.vert.h" #include "impeller/renderer/pipeline.h" @@ -23,6 +25,8 @@ using SolidFillPipeline = PipelineT; using TexturePipeline = PipelineT; +using SolidStrokePipeline = + PipelineT; class ContentRenderer { public: @@ -38,6 +42,8 @@ class ContentRenderer { std::shared_ptr GetTexturePipeline() const; + std::shared_ptr GetSolidStrokePipeline() const; + std::shared_ptr GetContext() const; private: @@ -45,6 +51,7 @@ class ContentRenderer { std::unique_ptr gradient_fill_pipeline_; std::unique_ptr solid_fill_pipeline_; std::unique_ptr texture_pipeline_; + std::unique_ptr solid_stroke_pipeline_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/engine/src/flutter/impeller/entity/contents.cc b/engine/src/flutter/impeller/entity/contents.cc index e99e442c783..a9dd5e306a1 100644 --- a/engine/src/flutter/impeller/entity/contents.cc +++ b/engine/src/flutter/impeller/entity/contents.cc @@ -10,6 +10,7 @@ #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/vector.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" @@ -236,13 +237,11 @@ bool TextureContents::Render(const ContentRenderer& renderer, frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); - auto frame_info_view = host_buffer.EmplaceUniform(frame_info); - Command cmd; cmd.label = "TextureFill"; cmd.pipeline = renderer.GetTexturePipeline(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); - VS::BindFrameInfo(cmd, frame_info_view); + VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindTextureSampler( cmd, texture_, renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); @@ -275,6 +274,45 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +static VertexBuffer CreateSolidStrokeVertices(const Path& path, + HostBuffer& buffer) { + using VS = SolidStrokeVertexShader; + + VertexBufferBuilder vtx_builder; + auto polyline = path.CreatePolyline(); + + for (size_t i = 0, polyline_size = polyline.size(); i < polyline_size; i++) { + const auto is_last_point = i == polyline_size - 1; + + const auto& p1 = polyline[i]; + const auto& p2 = is_last_point ? polyline[i - 1] : polyline[i + 1]; + + const auto diff = p2 - p1; + + const Scalar direction = is_last_point ? -1.0 : 1.0; + + const auto normal = + Point{-diff.y * direction, diff.x * direction}.Normalize(); + + VS::PerVertexData vtx; + vtx.vertex_position = p1; + + if (i == 0) { + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + } + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + } + + return vtx_builder.CreateVertexBuffer(buffer); +} + bool SolidStrokeContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, @@ -283,7 +321,29 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer, return true; } - return false; + using VS = SolidStrokeVertexShader; + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + + VS::StrokeInfo stroke_info; + stroke_info.color = color_; + stroke_info.size = stroke_size_; + + Command cmd; + cmd.primitive_type = PrimitiveType::kTriangleStrip; + cmd.label = "SolidStroke"; + cmd.pipeline = renderer.GetSolidStrokePipeline(); + cmd.BindVertices( + CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer())); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + VS::BindStrokeInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); + + pass.AddCommand(std::move(cmd)); + + return true; } void SolidStrokeContents::SetStrokeSize(Scalar size) { diff --git a/engine/src/flutter/impeller/entity/shaders/solid_stroke.frag b/engine/src/flutter/impeller/entity/shaders/solid_stroke.frag new file mode 100644 index 00000000000..88b1f7635d9 --- /dev/null +++ b/engine/src/flutter/impeller/entity/shaders/solid_stroke.frag @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +in vec4 stroke_color; + +out vec4 frag_color; + +void main() { + frag_color = stroke_color; +} diff --git a/engine/src/flutter/impeller/entity/shaders/solid_stroke.vert b/engine/src/flutter/impeller/entity/shaders/solid_stroke.vert new file mode 100644 index 00000000000..696c0398013 --- /dev/null +++ b/engine/src/flutter/impeller/entity/shaders/solid_stroke.vert @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +uniform StrokeInfo { + vec4 color; + float size; +} stroke_info; + +in vec2 vertex_position; +in vec2 vertex_normal; + +out vec4 stroke_color; + +void main() { + vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5); + gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0); + stroke_color = stroke_info.color; +} diff --git a/engine/src/flutter/impeller/geometry/path_builder.cc b/engine/src/flutter/impeller/geometry/path_builder.cc index 835e15ad349..94062420dd3 100644 --- a/engine/src/flutter/impeller/geometry/path_builder.cc +++ b/engine/src/flutter/impeller/geometry/path_builder.cc @@ -298,6 +298,11 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { return *this; } +PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) { + prototype_.AddLinearComponent(p1, p2); + return *this; +} + const Path& PathBuilder::GetCurrentPath() const { return prototype_; } diff --git a/engine/src/flutter/impeller/geometry/path_builder.h b/engine/src/flutter/impeller/geometry/path_builder.h index 36e391fa81f..84057fa79d3 100644 --- a/engine/src/flutter/impeller/geometry/path_builder.h +++ b/engine/src/flutter/impeller/geometry/path_builder.h @@ -54,6 +54,8 @@ class PathBuilder { PathBuilder& AddOval(const Rect& rect); + PathBuilder& AddLine(const Point& p1, const Point& p2); + struct RoundingRadii { Scalar topLeft = 0.0; Scalar bottomLeft = 0.0; diff --git a/engine/src/flutter/impeller/geometry/point.h b/engine/src/flutter/impeller/geometry/point.h index 6270f1714eb..b015a2ee44b 100644 --- a/engine/src/flutter/impeller/geometry/point.h +++ b/engine/src/flutter/impeller/geometry/point.h @@ -86,6 +86,18 @@ struct TPoint { constexpr Type GetDistance(const TPoint& p) const { return sqrt(GetDistanceSquared(p)); } + + constexpr Type GetLengthSquared() const { return GetDistanceSquared({}); } + + constexpr Type GetLength() const { return GetDistance({}); } + + constexpr TPoint Normalize() const { + const auto length = GetLength(); + if (length == 0) { + return {}; + } + return {x / length, y / length}; + } }; using Point = TPoint; diff --git a/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h b/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h index 7f8851f4b47..82dcceb4dab 100644 --- a/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h +++ b/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.h @@ -4,6 +4,8 @@ #include +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/vertex_descriptor.h" @@ -28,8 +30,15 @@ class VertexDescriptorMTL { StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length) : location(p_location), format(p_format), length(p_length) {} + + struct Compare { + constexpr bool operator()(const StageInput& lhs, + const StageInput& rhs) const { + return lhs.location < rhs.location; + } + }; }; - std::vector stage_inputs_; + std::set stage_inputs_; FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptorMTL); }; diff --git a/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index 02b45f9c3d6..0ae058f6d87 100644 --- a/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/engine/src/flutter/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -179,8 +179,9 @@ bool VertexDescriptorMTL::SetStageInputs( FML_LOG(ERROR) << "Format for input " << input.name << " not supported."; return false; } - stage_inputs_.emplace_back(StageInput{ - input.location, vertex_format, (input.bit_width * input.vec_size) / 8}); + + stage_inputs_.insert(StageInput{input.location, vertex_format, + (input.bit_width * input.vec_size) / 8}); } return true; diff --git a/engine/src/flutter/impeller/renderer/shader_types.h b/engine/src/flutter/impeller/renderer/shader_types.h index efcc28a2a34..fb6e51b65e6 100644 --- a/engine/src/flutter/impeller/renderer/shader_types.h +++ b/engine/src/flutter/impeller/renderer/shader_types.h @@ -50,6 +50,8 @@ struct ShaderUniformSlot { struct ShaderStageIOSlot { // Statically allocated const string containing advisory debug description. + // This may be absent in release modes and the runtime may not use this string + // for normal operation. const char* name; size_t location; size_t set; diff --git a/engine/src/flutter/impeller/renderer/vertex_buffer_builder.h b/engine/src/flutter/impeller/renderer/vertex_buffer_builder.h index 0d92e2debe7..c217241e486 100644 --- a/engine/src/flutter/impeller/renderer/vertex_buffer_builder.h +++ b/engine/src/flutter/impeller/renderer/vertex_buffer_builder.h @@ -35,6 +35,8 @@ class VertexBufferBuilder { bool HasVertices() const { return !vertices_.empty(); } + size_t GetVertexCount() const { return vertices_.size(); } + VertexBufferBuilder& AppendVertex(VertexType_ vertex) { vertices_.emplace_back(std::move(vertex)); return *this;