Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] eliminate sub-render pass for blended color + texture vertices. #51778

Merged
merged 19 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint16_t> indices = {};
std::vector<Point> texture_coordinates = {};
std::vector<Color> 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<VerticesGeometry>(
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());
Expand Down
65 changes: 65 additions & 0 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

#include "impeller/aiks/canvas.h"

#include <memory>
#include <optional>
#include <utility>

#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"
Expand All @@ -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"

Expand Down Expand Up @@ -76,6 +79,47 @@ static std::shared_ptr<Contents> CreateContentsForGeometryWithFilters(
return contents_copy;
}

struct GetTextureColorSourceDataVisitor {
GetTextureColorSourceDataVisitor() {}

std::optional<ImageData> operator()(const LinearGradientData& data) {
return std::nullopt;
}

std::optional<ImageData> operator()(const RadialGradientData& data) {
return std::nullopt;
}

std::optional<ImageData> operator()(const ConicalGradientData& data) {
return std::nullopt;
}

std::optional<ImageData> operator()(const SweepGradientData& data) {
return std::nullopt;
}

std::optional<ImageData> operator()(const ImageData& data) { return data; }

std::optional<ImageData> operator()(const RuntimeEffectData& data) {
return std::nullopt;
}

std::optional<ImageData> operator()(const std::monostate& data) {
return std::nullopt;
}

#if IMPELLER_ENABLE_3D
std::optional<ImageData> operator()(const SceneData& data) {
return std::nullopt;
}
#endif // IMPELLER_ENABLE_3D
};

static std::optional<ImageData> GetImageColorSourceData(
const ColorSource& color_source) {
return std::visit(GetTextureColorSourceDataVisitor{}, color_source.GetData());
}

static std::shared_ptr<Contents> CreatePathContentsWithFilters(
const Paint& paint,
const Path& path) {
Expand Down Expand Up @@ -885,6 +929,27 @@ void Canvas::DrawVertices(const std::shared_ptr<VerticesGeometry>& 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<ImageData> maybe_image_data =
GetImageColorSourceData(paint.color_source)) {
const ImageData& image_data = maybe_image_data.value();
auto contents = std::make_shared<VerticesSimpleBlendContents>();
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);

Expand Down
4 changes: 4 additions & 0 deletions impeller/aiks/color_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,8 @@ std::shared_ptr<ColorSourceContents> ColorSource::GetContents(
return std::visit(CreateContentsVisitor{paint}, color_source_data_);
}

const ColorSourceData& ColorSource::GetData() const {
return color_source_data_;
}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/aiks/color_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ class ColorSource {

std::shared_ptr<ColorSourceContents> GetContents(const Paint& paint) const;

const ColorSourceData& GetData() const;

private:
Type type_ = Type::kColor;
ColorSourceData color_source_data_;
Expand Down
1 change: 0 additions & 1 deletion impeller/entity/contents/tiled_texture_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <memory>
#include <vector>

#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"
Expand Down
143 changes: 143 additions & 0 deletions impeller/entity/contents/vertices_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -15,6 +19,29 @@

namespace impeller {

namespace {
static std::optional<SamplerAddressMode> 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;
Expand Down Expand Up @@ -53,6 +80,7 @@ bool VerticesContents::Render(const ContentContext& renderer,
if (blend_mode_ == BlendMode::kClear) {
return true;
}

std::shared_ptr<Contents> src_contents = src_contents_;
src_contents->SetCoverageHint(GetCoverageHint());
if (geometry_->HasTextureCoordinates()) {
Expand Down Expand Up @@ -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<VerticesGeometry> 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) {
texture_ = std::move(texture);
}

std::optional<Rect> 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<const Sampler>& 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<int>(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
46 changes: 46 additions & 0 deletions impeller/entity/contents/vertices_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<VerticesGeometry> geometry);

void SetAlpha(Scalar alpha);

void SetBlendMode(BlendMode blend_mode);

void SetTexture(std::shared_ptr<Texture> texture);

void SetSamplerDescriptor(SamplerDescriptor descriptor);

void SetTileMode(Entity::TileMode tile_mode_x, Entity::TileMode tile_mode_y);

void SetEffectTransform(Matrix transform);

// |Contents|
std::optional<Rect> 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<VerticesGeometry> geometry_;
std::shared_ptr<Texture> 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_
Loading