diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 0bad4d89e830d..395ac7230fb22 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -540,6 +540,8 @@ FILE: ../../../flutter/impeller/docs/assets/xcode_frame_capture/image6.png FILE: ../../../flutter/impeller/docs/assets/xcode_frame_capture/image7.png FILE: ../../../flutter/impeller/docs/assets/xcode_frame_capture/image8.png FILE: ../../../flutter/impeller/docs/assets/xcode_frame_capture/image9.png +FILE: ../../../flutter/impeller/entity/contents/atlas_contents.cc +FILE: ../../../flutter/impeller/entity/contents/atlas_contents.h FILE: ../../../flutter/impeller/entity/contents/clip_contents.cc FILE: ../../../flutter/impeller/entity/contents/clip_contents.h FILE: ../../../flutter/impeller/entity/contents/content_context.cc @@ -590,6 +592,8 @@ FILE: ../../../flutter/impeller/entity/entity_playground.h FILE: ../../../flutter/impeller/entity/entity_unittests.cc FILE: ../../../flutter/impeller/entity/inline_pass_context.cc FILE: ../../../flutter/impeller/entity/inline_pass_context.h +FILE: ../../../flutter/impeller/entity/shaders/atlas_fill.frag +FILE: ../../../flutter/impeller/entity/shaders/atlas_fill.vert FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index dfa6371392585..e41c4647aeae5 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "impeller/aiks/paint_pass_delegate.h" +#include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -321,4 +322,39 @@ void Canvas::DrawVertices(Vertices vertices, GetCurrentPass().AddEntity(std::move(entity)); } +void Canvas::DrawAtlas(std::shared_ptr atlas, + std::vector transforms, + std::vector texture_coordinates, + std::vector colors, + Entity::BlendMode blend_mode, + SamplerDescriptor sampler, + std::optional cull_rect, + Paint paint) { + if (!atlas) { + return; + } + auto size = atlas->GetSize(); + + if (size.IsEmpty()) { + return; + } + + std::shared_ptr contents = std::make_shared(); + contents->SetColors(std::move(colors)); + contents->SetTransforms(std::move(transforms)); + contents->SetTextureCoordinates(std::move(texture_coordinates)); + contents->SetTexture(atlas->GetTexture()); + contents->SetSamplerDescriptor(std::move(sampler)); + contents->SetBlendMode(blend_mode); + // TODO(jonahwilliams): set cull rect. + + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetBlendMode(paint.blend_mode); + entity.SetContents(paint.WithFilters(contents, false)); + + GetCurrentPass().AddEntity(std::move(entity)); +} + } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 2e7abc61468f3..e2257a92ab468 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -97,6 +97,15 @@ class Canvas { Entity::BlendMode blend_mode, Paint paint); + void DrawAtlas(std::shared_ptr atlas, + std::vector transforms, + std::vector texture_coordinates, + std::vector colors, + Entity::BlendMode blend_mode, + SamplerDescriptor sampler, + std::optional cull_rect, + Paint paint); + Picture EndRecordingAsPicture(); private: diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index b06e0ca57a776..31c583765bfa2 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -189,6 +189,34 @@ static Color ToColor(const SkColor& color) { }; } +static std::vector ToColors(const flutter::DlColor colors[], int count) { + auto result = std::vector(); + if (colors == nullptr) { + return result; + } + for (int i = 0; i < count; i++) { + result.push_back(ToColor(colors[i])); + } + return result; +} + +static std::vector ToRSXForms(const SkRSXform xform[], int count) { + auto result = std::vector(); + for (int i = 0; i < count; i++) { + auto form = xform[i]; + // clang-format off + auto matrix = Matrix{ + form.fSCos, form.fSSin, 0, 0, + -form.fSSin, form.fSCos, 0, 0, + 0, 0, 1, 0, + form.fTx, form.fTy, 0, 1 + }; + // clang-format on + result.push_back(matrix); + } + return result; +} + // |flutter::Dispatcher| void DisplayListDispatcher::setColorSource( const flutter::DlColorSource* source) { @@ -392,6 +420,14 @@ static std::optional ToRect(const SkRect* rect) { return Rect::MakeLTRB(rect->fLeft, rect->fTop, rect->fRight, rect->fBottom); } +static std::vector ToRects(const SkRect tex[], int count) { + auto result = std::vector(); + for (int i = 0; i < count; i++) { + result.push_back(ToRect(&tex[i]).value()); + } + return result; +} + // |flutter::Dispatcher| void DisplayListDispatcher::saveLayer(const SkRect* bounds, const flutter::SaveLayerOptions options, @@ -838,8 +874,10 @@ void DisplayListDispatcher::drawAtlas(const sk_sp atlas, flutter::DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) { - // Needs https://github.com/flutter/flutter/issues/95434 - UNIMPLEMENTED; + canvas_.DrawAtlas(std::make_shared(atlas->impeller_texture()), + ToRSXForms(xform, count), ToRects(tex, count), + ToColors(colors, count), ToBlendMode(mode), + ToSamplerDescriptor(sampling), ToRect(cull_rect), paint_); } // |flutter::Dispatcher| diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 0c1dfefc6ba6e..5929be6ce8692 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -8,6 +8,8 @@ impeller_shaders("entity_shaders") { name = "entity" shaders = [ + "shaders/atlas_fill.frag", + "shaders/atlas_fill.vert", "shaders/blending/advanced_blend.vert", "shaders/blending/advanced_blend_color.frag", "shaders/blending/advanced_blend_colorburn.frag", @@ -49,6 +51,8 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ + "contents/atlas_contents.cc", + "contents/atlas_contents.h", "contents/clip_contents.cc", "contents/clip_contents.h", "contents/content_context.cc", diff --git a/impeller/entity/contents/atlas_contents.cc b/impeller/entity/contents/atlas_contents.cc new file mode 100644 index 0000000000000..963c8b6b9aaa0 --- /dev/null +++ b/impeller/entity/contents/atlas_contents.cc @@ -0,0 +1,137 @@ +// 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. + +#include + +#include "impeller/renderer/formats.h" +#include "impeller/renderer/sampler_library.h" +#include "impeller/renderer/vertex_buffer_builder.h" + +#include "impeller/entity/atlas_fill.frag.h" +#include "impeller/entity/atlas_fill.vert.h" +#include "impeller/entity/contents/atlas_contents.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +AtlasContents::AtlasContents() = default; + +AtlasContents::~AtlasContents() = default; + +void AtlasContents::SetTexture(std::shared_ptr texture) { + texture_ = std::move(texture); +} + +std::shared_ptr AtlasContents::GetTexture() const { + return texture_; +} + +void AtlasContents::SetTransforms(std::vector transforms) { + transforms_ = transforms; +} + +void AtlasContents::SetTextureCoordinates(std::vector texture_coords) { + texture_coords_ = texture_coords; +} + +void AtlasContents::SetColors(std::vector colors) { + colors_ = colors; +} + +void AtlasContents::SetBlendMode(Entity::BlendMode blend_mode) { + // TODO(jonahwilliams): blending of colors with texture. + blend_mode_ = blend_mode; +} + +std::optional AtlasContents::GetCoverage(const Entity& entity) const { + Rect bounding_box = {}; + for (size_t i = 0; i < texture_coords_.size(); i++) { + auto matrix = transforms_[i]; + auto sample_rect = texture_coords_[i]; + auto bounds = Rect::MakeSize(sample_rect.size).TransformBounds(matrix); + bounding_box = bounds.Union(bounding_box); + } + return bounding_box.TransformBounds(entity.GetTransformation()); +} + +void AtlasContents::SetSamplerDescriptor(SamplerDescriptor desc) { + sampler_descriptor_ = std::move(desc); +} + +const SamplerDescriptor& AtlasContents::GetSamplerDescriptor() const { + return sampler_descriptor_; +} + +bool AtlasContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (texture_ == nullptr) { + return true; + } + + using VS = AtlasFillVertexShader; + using FS = AtlasFillFragmentShader; + + const auto texture_size = texture_->GetSize(); + if (texture_size.IsEmpty()) { + return true; + } + + VertexBufferBuilder vertex_builder; + vertex_builder.Reserve(texture_coords_.size() * 6); + constexpr size_t indices[6] = {0, 1, 2, 1, 2, 3}; + constexpr Scalar width[6] = {0, 1, 0, 1, 0, 1}; + constexpr Scalar height[6] = {0, 0, 1, 0, 1, 1}; + for (size_t i = 0; i < texture_coords_.size(); i++) { + auto sample_rect = texture_coords_[i]; + auto matrix = transforms_[i]; + auto color = (colors_.size() > 0 ? colors_[i] : Color::Black()); + auto transformed_points = + Rect::MakeSize(sample_rect.size).GetTransformedPoints(matrix); + + for (size_t j = 0; j < 6; j++) { + VS::PerVertexData data; + data.position = transformed_points[indices[j]]; + data.texture_coords = + (sample_rect.origin + Point(sample_rect.size.width * width[j], + sample_rect.size.height * height[j])) / + texture_size; + data.color = color.Premultiply(); + vertex_builder.AppendVertex(data); + } + } + + if (!vertex_builder.HasVertices()) { + return true; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VS::VertInfo vert_info; + vert_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + + FS::FragInfo frag_info; + frag_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale(); + frag_info.has_color = colors_.size() > 0 ? 1.0 : 0.0; + + Command cmd; + cmd.label = "DrawAtlas"; + cmd.pipeline = + renderer.GetAtlasPipeline(OptionsFromPassAndEntity(pass, entity)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); + VS::BindVertInfo(cmd, host_buffer.EmplaceUniform(vert_info)); + FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); + FS::BindTextureSampler(cmd, texture_, + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + sampler_descriptor_)); + pass.AddCommand(std::move(cmd)); + + return true; +} + +} // namespace impeller diff --git a/impeller/entity/contents/atlas_contents.h b/impeller/entity/contents/atlas_contents.h new file mode 100644 index 0000000000000..d775f310915eb --- /dev/null +++ b/impeller/entity/contents/atlas_contents.h @@ -0,0 +1,59 @@ +// 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. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/sampler_descriptor.h" + +namespace impeller { + +class AtlasContents final : public Contents { + public: + explicit AtlasContents(); + + ~AtlasContents() override; + + void SetTexture(std::shared_ptr texture); + + std::shared_ptr GetTexture() const; + + void SetTransforms(std::vector transforms); + + void SetBlendMode(Entity::BlendMode blend_mode); + + void SetTextureCoordinates(std::vector texture_coords); + + void SetColors(std::vector colors); + + void SetSamplerDescriptor(SamplerDescriptor desc); + + const SamplerDescriptor& GetSamplerDescriptor() const; + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + std::shared_ptr texture_; + std::vector texture_coords_; + std::vector colors_; + std::vector transforms_; + Entity::BlendMode blend_mode_; + SamplerDescriptor sampler_descriptor_ = {}; + + FML_DISALLOW_COPY_AND_ASSIGN(AtlasContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 60c6a062db168..c4e7fdbd9265f 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -196,6 +196,7 @@ ContentContext::ContentContext(std::shared_ptr context) glyph_atlas_pipelines_[{}] = CreateDefaultPipeline(*context_); vertices_pipelines_[{}] = CreateDefaultPipeline(*context_); + atlas_pipelines_[{}] = CreateDefaultPipeline(*context_); // Pipelines that are variants of the base pipelines with custom descriptors. // TODO(98684): Rework this API to allow fetching the descriptor without diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index fb627bad171d9..8c8b1938c5ad5 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -27,6 +27,8 @@ #include "impeller/entity/advanced_blend_saturation.frag.h" #include "impeller/entity/advanced_blend_screen.frag.h" #include "impeller/entity/advanced_blend_softlight.frag.h" +#include "impeller/entity/atlas_fill.frag.h" +#include "impeller/entity/atlas_fill.vert.h" #include "impeller/entity/blend.frag.h" #include "impeller/entity/blend.vert.h" #include "impeller/entity/border_mask_blur.frag.h" @@ -101,6 +103,7 @@ using GlyphAtlasPipeline = PipelineT; using VerticesPipeline = PipelineT; +using AtlasPipeline = PipelineT; // Instead of requiring new shaders for clips, the solid fill stages are used // to redirect writing to the stencil instead of color attachments. using ClipPipeline = PipelineT; @@ -191,6 +194,10 @@ class ContentContext { return GetPipeline(vertices_pipelines_, opts); } + std::shared_ptr GetAtlasPipeline(ContentContextOptions opts) const { + return GetPipeline(atlas_pipelines_, opts); + } + // Advanced blends. std::shared_ptr GetBlendColorPipeline( @@ -301,6 +308,7 @@ class ContentContext { mutable Variants clip_pipelines_; mutable Variants glyph_atlas_pipelines_; mutable Variants vertices_pipelines_; + mutable Variants atlas_pipelines_; // Advanced blends. mutable Variants blend_color_pipelines_; mutable Variants blend_colorburn_pipelines_; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index dca12868062f0..f25f62bd1375f 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -6,6 +6,7 @@ #include #include "flutter/testing/testing.h" +#include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" @@ -1091,6 +1092,94 @@ TEST_P(EntityTest, DrawVerticesSolidColorTrianglesWithIndices) { ASSERT_TRUE(OpenPlaygroundHere(e)); } +TEST_P(EntityTest, DrawAtlasNoColor) { + // Draws the image as four squares stiched together. + auto atlas = CreateTextureForFixture("bay_bridge.jpg"); + auto size = atlas->GetSize(); + // Divide image into four quadrants. + Scalar half_width = size.width / 2; + Scalar half_height = size.height / 2; + std::vector texture_coordinates = { + Rect::MakeLTRB(0, 0, half_width, half_height), + Rect::MakeLTRB(half_width, 0, size.width, half_height), + Rect::MakeLTRB(0, half_height, half_width, size.height), + Rect::MakeLTRB(half_width, half_height, size.width, size.height)}; + // Position quadrants adjacent to eachother. + std::vector transforms = { + Matrix::MakeTranslation({0, 0, 0}), + Matrix::MakeTranslation({half_width, 0, 0}), + Matrix::MakeTranslation({0, half_height, 0}), + Matrix::MakeTranslation({half_width, half_height, 0})}; + std::shared_ptr contents = std::make_shared(); + + contents->SetTransforms(std::move(transforms)); + contents->SetTextureCoordinates(std::move(texture_coordinates)); + contents->SetTexture(atlas); + contents->SetBlendMode(Entity::BlendMode::kSource); + + Entity e; + e.SetTransformation(Matrix::MakeScale(GetContentScale())); + e.SetContents(contents); + + ASSERT_TRUE(OpenPlaygroundHere(e)); +} + +TEST_P(EntityTest, DrawAtlasWithColor) { + // Draws the image as four squares stiched together. Because blend modes + // aren't implented this ends up as four solid color blocks. + auto atlas = CreateTextureForFixture("bay_bridge.jpg"); + auto size = atlas->GetSize(); + // Divide image into four quadrants. + Scalar half_width = size.width / 2; + Scalar half_height = size.height / 2; + std::vector texture_coordinates = { + Rect::MakeLTRB(0, 0, half_width, half_height), + Rect::MakeLTRB(half_width, 0, size.width, half_height), + Rect::MakeLTRB(0, half_height, half_width, size.height), + Rect::MakeLTRB(half_width, half_height, size.width, size.height)}; + // Position quadrants adjacent to eachother. + std::vector transforms = { + Matrix::MakeTranslation({0, 0, 0}), + Matrix::MakeTranslation({half_width, 0, 0}), + Matrix::MakeTranslation({0, half_height, 0}), + Matrix::MakeTranslation({half_width, half_height, 0})}; + std::vector colors = {Color::Red(), Color::Green(), Color::Blue(), + Color::Yellow()}; + std::shared_ptr contents = std::make_shared(); + + contents->SetTransforms(std::move(transforms)); + contents->SetTextureCoordinates(std::move(texture_coordinates)); + contents->SetTexture(atlas); + contents->SetColors(colors); + contents->SetBlendMode(Entity::BlendMode::kSource); + + Entity e; + e.SetTransformation(Matrix::MakeScale(GetContentScale())); + e.SetContents(contents); + + ASSERT_TRUE(OpenPlaygroundHere(e)); +} + +TEST_P(EntityTest, DrawAtlasNoColorFullSize) { + auto atlas = CreateTextureForFixture("bay_bridge.jpg"); + auto size = atlas->GetSize(); + std::vector texture_coordinates = { + Rect::MakeLTRB(0, 0, size.width, size.height)}; + std::vector transforms = {Matrix::MakeTranslation({0, 0, 0})}; + std::shared_ptr contents = std::make_shared(); + + contents->SetTransforms(std::move(transforms)); + contents->SetTextureCoordinates(std::move(texture_coordinates)); + contents->SetTexture(atlas); + contents->SetBlendMode(Entity::BlendMode::kSource); + + Entity e; + e.SetTransformation(Matrix::MakeScale(GetContentScale())); + e.SetContents(contents); + + ASSERT_TRUE(OpenPlaygroundHere(e)); +} + TEST_P(EntityTest, SolidFillCoverageIsCorrect) { // No transform { diff --git a/impeller/entity/shaders/atlas_fill.frag b/impeller/entity/shaders/atlas_fill.frag new file mode 100644 index 0000000000000..f19588e567059 --- /dev/null +++ b/impeller/entity/shaders/atlas_fill.frag @@ -0,0 +1,28 @@ +// 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. + +#include + +uniform sampler2D texture_sampler; + +uniform FragInfo { + float texture_sampler_y_coord_scale; + float has_color; +} +frag_info; + +in vec2 v_texture_coords; +in vec4 v_color; + +out vec4 frag_color; + +void main() { + vec4 sampled = IPSample(texture_sampler, v_texture_coords, + frag_info.texture_sampler_y_coord_scale); + if (frag_info.has_color == 1.0) { + frag_color = sampled.aaaa * v_color; + } else { + frag_color = sampled; + } +} diff --git a/impeller/entity/shaders/atlas_fill.vert b/impeller/entity/shaders/atlas_fill.vert new file mode 100644 index 0000000000000..29d3c0a5be6e7 --- /dev/null +++ b/impeller/entity/shaders/atlas_fill.vert @@ -0,0 +1,21 @@ +// 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 VertInfo { + mat4 mvp; +} +vert_info; + +in vec2 position; +in vec2 texture_coords; +in vec4 color; + +out vec2 v_texture_coords; +out vec4 v_color; + +void main() { + gl_Position = vert_info.mvp * vec4(position, 0.0, 1.0); + v_texture_coords = texture_coords; + v_color = color; +}