diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index aef97c8855d18..fce5d0dd8ad27 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2733,6 +2733,39 @@ TEST_P(AiksTest, VerticesGeometryUVPositionDataWithTranslate) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +// Regression test for https://github.com/flutter/flutter/issues/145707 +TEST_P(AiksTest, VerticesGeometryColorUVPositionData) { + Canvas canvas; + Paint paint; + auto texture = CreateTextureForFixture("table_mountain_nx.png"); + + paint.color_source = + ColorSource::MakeImage(texture, Entity::TileMode::kClamp, + Entity::TileMode::kClamp, {}, Matrix()); + + auto vertices = { + Point(0, 0), + Point(texture->GetSize().width, 0), + Point(0, texture->GetSize().height), + Point(texture->GetSize().width, 0), + Point(0, 0), + Point(texture->GetSize().width, texture->GetSize().height), + }; + std::vector indices = {}; + std::vector texture_coordinates = {}; + std::vector vertex_colors = { + Color::Red().WithAlpha(0.5), Color::Blue().WithAlpha(0.5), + Color::Green().WithAlpha(0.5), Color::Red().WithAlpha(0.5), + Color::Blue().WithAlpha(0.5), Color::Green().WithAlpha(0.5), + }; + auto geometry = std::make_shared( + vertices, indices, texture_coordinates, vertex_colors, + Rect::MakeLTRB(0, 0, 1, 1), VerticesGeometry::VertexMode::kTriangles); + + canvas.DrawVertices(geometry, BlendMode::kDestinationOver, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_P(AiksTest, MatrixImageFilterMagnify) { Canvas canvas; canvas.Scale(GetContentScale()); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 22a698474d2ac..9b121e80d4edb 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -4,11 +4,13 @@ #include "impeller/aiks/canvas.h" +#include #include #include #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "impeller/aiks/color_source.h" #include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/entity/contents/atlas_contents.h" @@ -20,6 +22,7 @@ #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/contents/vertices_contents.h" #include "impeller/entity/geometry/geometry.h" +#include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/path_builder.h" @@ -76,6 +79,47 @@ static std::shared_ptr CreateContentsForGeometryWithFilters( return contents_copy; } +struct GetTextureColorSourceDataVisitor { + GetTextureColorSourceDataVisitor() {} + + std::optional operator()(const LinearGradientData& data) { + return std::nullopt; + } + + std::optional operator()(const RadialGradientData& data) { + return std::nullopt; + } + + std::optional operator()(const ConicalGradientData& data) { + return std::nullopt; + } + + std::optional operator()(const SweepGradientData& data) { + return std::nullopt; + } + + std::optional operator()(const ImageData& data) { return data; } + + std::optional operator()(const RuntimeEffectData& data) { + return std::nullopt; + } + + std::optional operator()(const std::monostate& data) { + return std::nullopt; + } + +#if IMPELLER_ENABLE_3D + std::optional operator()(const SceneData& data) { + return std::nullopt; + } +#endif // IMPELLER_ENABLE_3D +}; + +static std::optional GetImageColorSourceData( + const ColorSource& color_source) { + return std::visit(GetTextureColorSourceDataVisitor{}, color_source.GetData()); +} + static std::shared_ptr CreatePathContentsWithFilters( const Paint& paint, const Path& path) { @@ -885,6 +929,27 @@ void Canvas::DrawVertices(const std::shared_ptr& vertices, return; } + // If there is are per-vertex colors, an image, and the blend mode + // is simple we can draw without a sub-renderpass. + if (blend_mode <= BlendMode::kModulate && vertices->HasVertexColors()) { + if (std::optional maybe_image_data = + GetImageColorSourceData(paint.color_source)) { + const ImageData& image_data = maybe_image_data.value(); + auto contents = std::make_shared(); + contents->SetBlendMode(blend_mode); + contents->SetAlpha(paint.color.alpha); + contents->SetGeometry(vertices); + + contents->SetEffectTransform(image_data.effect_transform); + contents->SetTexture(image_data.texture); + contents->SetTileMode(image_data.x_tile_mode, image_data.y_tile_mode); + + entity.SetContents(paint.WithFilters(std::move(contents))); + AddEntityToCurrentPass(std::move(entity)); + return; + } + } + auto src_paint = paint; src_paint.color = paint.color.WithAlpha(1.0); diff --git a/impeller/aiks/color_source.cc b/impeller/aiks/color_source.cc index da85ce6cb8bbb..bcb2721fc06e0 100644 --- a/impeller/aiks/color_source.cc +++ b/impeller/aiks/color_source.cc @@ -266,4 +266,8 @@ std::shared_ptr ColorSource::GetContents( return std::visit(CreateContentsVisitor{paint}, color_source_data_); } +const ColorSourceData& ColorSource::GetData() const { + return color_source_data_; +} + } // namespace impeller diff --git a/impeller/aiks/color_source.h b/impeller/aiks/color_source.h index 3178467b1faa0..195f204b9c542 100644 --- a/impeller/aiks/color_source.h +++ b/impeller/aiks/color_source.h @@ -166,6 +166,8 @@ class ColorSource { std::shared_ptr GetContents(const Paint& paint) const; + const ColorSourceData& GetData() const; + private: Type type_ = Type::kColor; ColorSourceData color_source_data_; diff --git a/impeller/entity/contents/tiled_texture_contents.h b/impeller/entity/contents/tiled_texture_contents.h index f798c13622a86..f26ebd36a45f2 100644 --- a/impeller/entity/contents/tiled_texture_contents.h +++ b/impeller/entity/contents/tiled_texture_contents.h @@ -9,7 +9,6 @@ #include #include -#include "flutter/fml/macros.h" #include "impeller/core/sampler_descriptor.h" #include "impeller/entity/contents/color_source_contents.h" #include "impeller/entity/contents/filters/color_filter_contents.h" diff --git a/impeller/entity/contents/vertices_contents.cc b/impeller/entity/contents/vertices_contents.cc index f5d433d70f009..c94e306533e3b 100644 --- a/impeller/entity/contents/vertices_contents.cc +++ b/impeller/entity/contents/vertices_contents.cc @@ -4,7 +4,11 @@ #include "vertices_contents.h" +#include "fml/logging.h" +#include "impeller/core/formats.h" #include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/geometry/geometry.h" #include "impeller/entity/geometry/vertices_geometry.h" @@ -15,6 +19,29 @@ namespace impeller { +namespace { +static std::optional TileModeToAddressMode( + Entity::TileMode tile_mode, + const Capabilities& capabilities) { + switch (tile_mode) { + case Entity::TileMode::kClamp: + return SamplerAddressMode::kClampToEdge; + break; + case Entity::TileMode::kMirror: + return SamplerAddressMode::kMirror; + break; + case Entity::TileMode::kRepeat: + return SamplerAddressMode::kRepeat; + break; + case Entity::TileMode::kDecal: + if (capabilities.SupportsDecalSamplerAddressMode()) { + return SamplerAddressMode::kDecal; + } + return std::nullopt; + } +} +} // namespace + VerticesContents::VerticesContents() = default; VerticesContents::~VerticesContents() = default; @@ -53,6 +80,7 @@ bool VerticesContents::Render(const ContentContext& renderer, if (blend_mode_ == BlendMode::kClear) { return true; } + std::shared_ptr src_contents = src_contents_; src_contents->SetCoverageHint(GetCoverageHint()); if (geometry_->HasTextureCoordinates()) { @@ -198,4 +226,119 @@ bool VerticesColorContents::Render(const ContentContext& renderer, return pass.Draw().ok(); } +//------------------------------------------------------ +// VerticesSimpleBlendContents + +VerticesSimpleBlendContents::VerticesSimpleBlendContents() {} + +VerticesSimpleBlendContents::~VerticesSimpleBlendContents() {} + +void VerticesSimpleBlendContents::SetGeometry( + std::shared_ptr geometry) { + geometry_ = std::move(geometry); +} + +void VerticesSimpleBlendContents::SetAlpha(Scalar alpha) { + alpha_ = alpha; +} + +void VerticesSimpleBlendContents::SetBlendMode(BlendMode blend_mode) { + FML_DCHECK(blend_mode <= BlendMode::kModulate); + blend_mode_ = blend_mode; +} + +void VerticesSimpleBlendContents::SetTexture(std::shared_ptr texture) { + texture_ = std::move(texture); +} + +std::optional VerticesSimpleBlendContents::GetCoverage( + const Entity& entity) const { + return geometry_->GetCoverage(entity.GetTransform()); +} + +void VerticesSimpleBlendContents::SetSamplerDescriptor( + SamplerDescriptor descriptor) { + descriptor_ = std::move(descriptor); +} + +void VerticesSimpleBlendContents::SetTileMode(Entity::TileMode tile_mode_x, + Entity::TileMode tile_mode_y) { + tile_mode_x_ = tile_mode_x; + tile_mode_y_ = tile_mode_y; +} + +void VerticesSimpleBlendContents::SetEffectTransform(Matrix transform) { + inverse_matrix_ = transform.Invert(); +} + +bool VerticesSimpleBlendContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + FML_DCHECK(texture_); + FML_DCHECK(geometry_->HasVertexColors()); + + // Simple Porter-Duff blends can be accomplished without a sub renderpass. + using VS = PorterDuffBlendPipeline::VertexShader; + using FS = PorterDuffBlendPipeline::FragmentShader; + + GeometryResult geometry_result = geometry_->GetPositionUVColorBuffer( + Rect::MakeSize(texture_->GetSize()), inverse_matrix_, renderer, entity, + pass); + if (geometry_result.vertex_buffer.vertex_count == 0) { + return true; + } + FML_DCHECK(geometry_result.mode == GeometryResult::Mode::kNormal); + +#ifdef IMPELLER_DEBUG + pass.SetCommandLabel(SPrintF("DrawVertices Porterduff Blend (%s)", + BlendModeToString(blend_mode_))); +#endif // IMPELLER_DEBUG + pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer)); + pass.SetStencilReference(entity.GetClipDepth()); + + auto options = OptionsFromPassAndEntity(pass, entity); + options.primitive_type = geometry_result.type; + pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options)); + + auto dst_sampler_descriptor = descriptor_; + dst_sampler_descriptor.width_address_mode = + TileModeToAddressMode(tile_mode_x_, renderer.GetDeviceCapabilities()) + .value_or(SamplerAddressMode::kClampToEdge); + dst_sampler_descriptor.height_address_mode = + TileModeToAddressMode(tile_mode_y_, renderer.GetDeviceCapabilities()) + .value_or(SamplerAddressMode::kClampToEdge); + + const std::unique_ptr& dst_sampler = + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + dst_sampler_descriptor); + FS::BindTextureSamplerDst(pass, texture_, dst_sampler); + + FS::FragInfo frag_info; + VS::FrameInfo frame_info; + + frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale(); + frag_info.output_alpha = alpha_; + frag_info.input_alpha = 1.0; + + auto inverted_blend_mode = + InvertPorterDuffBlend(blend_mode_).value_or(BlendMode::kSource); + auto blend_coefficients = + kPorterDuffCoefficients[static_cast(inverted_blend_mode)]; + frag_info.src_coeff = blend_coefficients[0]; + frag_info.src_coeff_dst_alpha = blend_coefficients[1]; + frag_info.dst_coeff = blend_coefficients[2]; + frag_info.dst_coeff_src_alpha = blend_coefficients[3]; + frag_info.dst_coeff_src_color = blend_coefficients[4]; + + auto& host_buffer = renderer.GetTransientsBuffer(); + FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); + + frame_info.mvp = geometry_result.transform; + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(pass, uniform_view); + + return pass.Draw().ok(); +} + } // namespace impeller diff --git a/impeller/entity/contents/vertices_contents.h b/impeller/entity/contents/vertices_contents.h index b5d3590f02421..8a28a8d6751c2 100644 --- a/impeller/entity/contents/vertices_contents.h +++ b/impeller/entity/contents/vertices_contents.h @@ -108,6 +108,52 @@ class VerticesUVContents final : public Contents { VerticesUVContents& operator=(const VerticesUVContents&) = delete; }; +/// A vertices contents for per-color vertices + texture and porter duff +/// blended. +class VerticesSimpleBlendContents final : public Contents { + public: + VerticesSimpleBlendContents(); + + ~VerticesSimpleBlendContents() override; + + void SetGeometry(std::shared_ptr geometry); + + void SetAlpha(Scalar alpha); + + void SetBlendMode(BlendMode blend_mode); + + void SetTexture(std::shared_ptr texture); + + void SetSamplerDescriptor(SamplerDescriptor descriptor); + + void SetTileMode(Entity::TileMode tile_mode_x, Entity::TileMode tile_mode_y); + + void SetEffectTransform(Matrix transform); + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + Scalar alpha_ = 1.0; + std::shared_ptr geometry_; + std::shared_ptr texture_; + BlendMode blend_mode_ = BlendMode::kSource; + SamplerDescriptor descriptor_ = {}; + Entity::TileMode tile_mode_x_ = Entity::TileMode::kClamp; + Entity::TileMode tile_mode_y_ = Entity::TileMode::kClamp; + Matrix inverse_matrix_ = {}; + + VerticesSimpleBlendContents(const VerticesSimpleBlendContents&) = delete; + + VerticesSimpleBlendContents& operator=(const VerticesSimpleBlendContents&) = + delete; +}; + } // namespace impeller #endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_VERTICES_CONTENTS_H_ diff --git a/impeller/entity/geometry/vertices_geometry.cc b/impeller/entity/geometry/vertices_geometry.cc index eedb7553b6ec9..a352a212c59d5 100644 --- a/impeller/entity/geometry/vertices_geometry.cc +++ b/impeller/entity/geometry/vertices_geometry.cc @@ -8,6 +8,7 @@ #include #include "impeller/core/buffer_view.h" +#include "impeller/entity/contents/content_context.h" namespace impeller { @@ -145,7 +146,7 @@ GeometryResult VerticesGeometry::GetPositionBuffer( GeometryResult VerticesGeometry::GetPositionColorBuffer( const ContentContext& renderer, const Entity& entity, - RenderPass& pass) { + RenderPass& pass) const { using VS = GeometryColorPipeline::VertexShader; auto index_count = indices_.size(); @@ -243,6 +244,64 @@ GeometryResult VerticesGeometry::GetPositionUVBuffer( }; } +GeometryResult VerticesGeometry::GetPositionUVColorBuffer( + Rect texture_coverage, + Matrix effect_transform, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = PorterDuffBlendPipeline::VertexShader; + + auto vertex_count = vertices_.size(); + auto uv_transform = + texture_coverage.GetNormalizingTransform() * effect_transform; + auto has_texture_coordinates = HasTextureCoordinates(); + + size_t total_vtx_bytes = vertices_.size() * sizeof(VS::PerVertexData); + auto vertex_buffer = renderer.GetTransientsBuffer().Emplace( + total_vtx_bytes, alignof(VS::PerVertexData), [&](uint8_t* data) { + VS::PerVertexData* vtx_contents = + reinterpret_cast(data); + for (auto i = 0u; i < vertices_.size(); i++) { + auto vertex = vertices_[i]; + auto texture_coord = + has_texture_coordinates ? texture_coordinates_[i] : vertices_[i]; + auto uv = uv_transform * texture_coord; + // From experimentation we need to clamp these values to < 1.0 or else + // there can be flickering. + VS::PerVertexData vertex_data = { + .vertices = vertex, + .texture_coords = + Point(std::clamp(uv.x, 0.0f, 1.0f - kEhCloseEnough), + std::clamp(uv.y, 0.0f, 1.0f - kEhCloseEnough)), + .color = colors_[i], + }; + std::memcpy(vtx_contents++, &vertex_data, sizeof(VS::PerVertexData)); + } + }); + + BufferView index_buffer = {}; + auto index_count = indices_.size(); + size_t total_idx_bytes = index_count * sizeof(uint16_t); + if (index_count > 0) { + index_buffer = renderer.GetTransientsBuffer().Emplace( + indices_.data(), total_idx_bytes, alignof(uint16_t)); + } + + return GeometryResult{ + .type = GetPrimitiveType(), + .vertex_buffer = + { + .vertex_buffer = vertex_buffer, + .index_buffer = index_buffer, + .vertex_count = index_count > 0 ? index_count : vertex_count, + .index_type = + index_count > 0 ? IndexType::k16bit : IndexType::kNone, + }, + .transform = entity.GetShaderTransform(pass), + }; +} + GeometryVertexType VerticesGeometry::GetVertexType() const { if (HasVertexColors()) { return GeometryVertexType::kColor; diff --git a/impeller/entity/geometry/vertices_geometry.h b/impeller/entity/geometry/vertices_geometry.h index 8f4d2be485e62..c28b4288a1b8b 100644 --- a/impeller/entity/geometry/vertices_geometry.h +++ b/impeller/entity/geometry/vertices_geometry.h @@ -29,7 +29,13 @@ class VerticesGeometry final : public Geometry { GeometryResult GetPositionColorBuffer(const ContentContext& renderer, const Entity& entity, - RenderPass& pass); + RenderPass& pass) const; + + GeometryResult GetPositionUVColorBuffer(Rect texture_coverage, + Matrix effect_transform, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const; // |Geometry| GeometryResult GetPositionUVBuffer(Rect texture_coverage, diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index ad1ff4b8d6062..871ad5a194190 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -756,6 +756,9 @@ impeller_Play_AiksTest_TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrec impeller_Play_AiksTest_TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly_Metal.png impeller_Play_AiksTest_TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly_OpenGLES.png impeller_Play_AiksTest_TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly_Vulkan.png +impeller_Play_AiksTest_VerticesGeometryColorUVPositionData_Metal.png +impeller_Play_AiksTest_VerticesGeometryColorUVPositionData_OpenGLES.png +impeller_Play_AiksTest_VerticesGeometryColorUVPositionData_Vulkan.png impeller_Play_AiksTest_VerticesGeometryUVPositionDataWithTranslate_Metal.png impeller_Play_AiksTest_VerticesGeometryUVPositionDataWithTranslate_OpenGLES.png impeller_Play_AiksTest_VerticesGeometryUVPositionDataWithTranslate_Vulkan.png