9 #include <unordered_map>
12 #include "display_list/effects/color_filters/dl_blend_color_filter.h"
13 #include "display_list/effects/color_filters/dl_matrix_color_filter.h"
14 #include "display_list/effects/dl_color_filter.h"
15 #include "display_list/effects/dl_color_source.h"
16 #include "display_list/effects/dl_image_filter.h"
17 #include "display_list/geometry/dl_path.h"
18 #include "display_list/image/dl_image.h"
19 #include "flutter/fml/logging.h"
20 #include "flutter/fml/trace_event.h"
60 bool IsPipelineBlendOrMatrixFilter(
const flutter::DlColorFilter* filter) {
61 return filter->type() == flutter::DlColorFilterType::kMatrix ||
62 (filter->type() == flutter::DlColorFilterType::kBlend &&
66 static bool UseColorSourceContents(
67 const std::shared_ptr<VerticesGeometry>& vertices,
71 if (vertices->HasVertexColors()) {
74 if (vertices->HasTextureCoordinates() && !paint.color_source) {
77 return !vertices->HasTextureCoordinates();
80 static void SetClipScissor(std::optional<Rect> clip_coverage,
82 Point global_pass_position) {
86 if (clip_coverage.has_value()) {
87 clip_coverage = clip_coverage->
Shift(-global_pass_position);
90 scissor = scissor.Intersection(
IRect::MakeSize(pass.GetRenderTargetSize()))
93 pass.SetScissor(scissor);
96 static void ApplyFramebufferBlend(Entity& entity) {
97 auto src_contents = entity.GetContents();
98 auto contents = std::make_shared<FramebufferBlendContents>();
99 contents->SetChildContents(src_contents);
100 contents->SetBlendMode(entity.GetBlendMode());
101 entity.SetContents(std::move(contents));
107 static std::shared_ptr<Contents> CreateContentsForSubpassTarget(
109 const std::shared_ptr<Texture>& target,
110 const Matrix& effect_transform) {
112 contents->SetTexture(target);
113 contents->SetLabel(
"Subpass");
115 contents->SetOpacity(paint.color.alpha);
116 contents->SetDeferApplyingOpacity(
true);
118 return paint.WithFiltersForSubpassTarget(std::move(contents),
122 static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
123 RenderTarget::AttachmentConfig{
129 static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
130 ContentContext& renderer,
132 const Color& clear_color) {
133 const std::shared_ptr<Context>& context = renderer.GetContext();
141 if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
142 target = renderer.GetRenderTargetCache()->CreateOffscreenMSAA(
148 RenderTarget::AttachmentConfigMSAA{
153 .clear_color = clear_color},
154 kDefaultStencilConfig);
156 target = renderer.GetRenderTargetCache()->CreateOffscreen(
161 RenderTarget::AttachmentConfig{
165 .clear_color = clear_color,
167 kDefaultStencilConfig
171 return std::make_unique<EntityPassTarget>(
173 renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
174 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA()
183 bool requires_readback)
184 : renderer_(renderer),
185 render_target_(render_target),
186 is_onscreen_(is_onscreen),
187 requires_readback_(requires_readback),
189 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
190 Initialize(std::nullopt);
197 bool requires_readback,
199 : renderer_(renderer),
200 render_target_(render_target),
201 is_onscreen_(is_onscreen),
202 requires_readback_(requires_readback),
204 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
205 Initialize(cull_rect);
212 bool requires_readback,
214 : renderer_(renderer),
215 render_target_(render_target),
216 is_onscreen_(is_onscreen),
217 requires_readback_(requires_readback),
219 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
225 void Canvas::Initialize(std::optional<Rect> cull_rect) {
226 initial_cull_rect_ = cull_rect;
233 void Canvas::Reset() {
235 transform_stack_ = {};
247 transform_stack_.back().transform = {};
255 return transform_stack_.back().transform;
278 Point Canvas::GetGlobalPassPosition()
const {
279 if (save_layer_state_.empty()) {
282 return save_layer_state_.back().coverage.GetOrigin();
286 size_t Canvas::GetClipHeightFloor()
const {
287 if (transform_stack_.size() > 1) {
288 return transform_stack_[transform_stack_.size() - 2].clip_height;
294 return transform_stack_.size();
297 bool Canvas::IsSkipping()
const {
298 return transform_stack_.back().skipping;
316 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
320 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
330 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
339 bool Canvas::AttemptColorFilterOptimization(
340 const std::shared_ptr<Texture>& image,
355 if (paint.
color_filter->type() == flutter::DlColorFilterType::kBlend) {
356 const flutter::DlBlendColorFilter* blend_filter =
358 DrawImageRectAtlasGeometry geometry = DrawImageRectAtlasGeometry(
363 blend_filter->mode(),
366 auto atlas_contents = std::make_shared<AtlasContents>();
367 atlas_contents->SetGeometry(&geometry);
373 entity.SetContents(atlas_contents);
375 AddRenderEntityToCurrentPass(entity);
377 const flutter::DlMatrixColorFilter* matrix_filter =
380 DrawImageRectAtlasGeometry geometry = DrawImageRectAtlasGeometry(
388 auto atlas_contents = std::make_shared<ColorFilterAtlasContents>();
389 atlas_contents->SetGeometry(&geometry);
392 matrix_filter->get_matrix(color_matrix.
array);
393 atlas_contents->SetMatrix(color_matrix);
398 entity.SetContents(atlas_contents);
400 AddRenderEntityToCurrentPass(entity);
405 bool Canvas::AttemptDrawBlurredRRect(
const Rect& rect,
407 const Paint& paint) {
412 if (paint.color_source) {
416 if (!paint.mask_blur_descriptor.has_value()) {
426 if (fabsf(corner_radii.width - corner_radii.height) >
kEhCloseEnough) {
432 Color rrect_color = paint.color;
433 if (paint.invert_colors) {
436 if (paint.color_filter) {
440 Paint rrect_paint = {.mask_blur_descriptor = paint.mask_blur_descriptor};
461 if ((paint.mask_blur_descriptor->style !=
463 paint.image_filter) ||
466 Rect render_bounds = rect;
467 if (paint.mask_blur_descriptor->style !=
470 render_bounds.
Expand(paint.mask_blur_descriptor->sigma.sigma * 4.0);
476 .image_filter = paint.image_filter,
477 .blend_mode = paint.blend_mode,
480 rrect_paint.color = rrect_color.WithAlpha(1);
482 rrect_paint.color = rrect_color;
483 rrect_paint.blend_mode = paint.blend_mode;
484 rrect_paint.image_filter = paint.image_filter;
488 auto draw_blurred_rrect = [
this, &rect, &corner_radii, &rrect_paint]() {
489 auto contents = std::make_shared<SolidRRectBlurContents>();
491 contents->SetColor(rrect_paint.color);
492 contents->SetSigma(rrect_paint.mask_blur_descriptor->sigma);
493 contents->SetRRect(rect, corner_radii);
495 Entity blurred_rrect_entity;
497 blurred_rrect_entity.SetBlendMode(rrect_paint.blend_mode);
499 rrect_paint.mask_blur_descriptor = std::nullopt;
500 blurred_rrect_entity.SetContents(
501 rrect_paint.WithFilters(std::move(contents)));
502 AddRenderEntityToCurrentPass(blurred_rrect_entity);
505 switch (rrect_paint.mask_blur_descriptor->style) {
507 draw_blurred_rrect();
512 draw_blurred_rrect();
516 entity.SetBlendMode(rrect_paint.blend_mode);
518 RoundRectGeometry geom(rect, corner_radii);
519 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, rrect_paint,
524 RoundRectGeometry geom(rect, corner_radii);
526 draw_blurred_rrect();
530 RoundRectGeometry geom(rect, corner_radii);
532 draw_blurred_rrect();
550 auto geometry = std::make_unique<LineGeometry>(p0, p1, paint.
stroke_width,
553 if (renderer_.
GetContext()->GetFlags().antialiased_lines &&
558 AddRenderEntityToCurrentPass(entity, reuse_depth);
560 AddRenderEntityWithFiltersToCurrentPass(entity, geometry.get(), paint,
571 if (AttemptDrawBlurredRRect(rect, {}, paint)) {
580 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
601 if (AttemptDrawBlurredRRect(rect, rect.
GetSize() * 0.5f, paint)) {
610 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
615 auto& radii = round_rect.
GetRadii();
616 if (radii.AreAllCornersSame()) {
617 if (AttemptDrawBlurredRRect(rect, radii.top_left, paint)) {
627 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
641 const Paint& paint) {
651 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
665 const Paint& paint) {
666 Size half_size(radius, radius);
667 if (AttemptDrawBlurredRRect(
669 {radius, radius}, paint)) {
679 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
682 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
703 FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
704 << current_depth_ <<
" <=? " << transform_stack_.back().clip_depth;
705 uint32_t clip_depth = transform_stack_.back().clip_depth;
707 const Matrix clip_transform =
711 std::optional<Rect> clip_coverage = geometry.
GetCoverage(clip_transform);
712 if (!clip_coverage.has_value()) {
717 clip_coverage.value(),
726 GetGlobalPassPosition(),
728 GetClipHeightFloor(),
734 *render_passes_.back().inline_pass_context->GetRenderPass(),
735 GetGlobalPassPosition());
738 ++transform_stack_.back().clip_height;
739 ++transform_stack_.back().num_clips;
756 *render_passes_.back().inline_pass_context->GetRenderPass()
763 renderer_, *render_passes_.back().inline_pass_context->GetRenderPass(),
782 AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
809 ISize size = image->GetSize();
814 std::optional<Rect> clipped_source =
816 if (!clipped_source) {
820 if (AttemptColorFilterOptimization(image, source, dest, paint, sampler,
821 src_rect_constraint)) {
825 if (*clipped_source != source) {
835 texture_contents->SetTexture(image);
836 texture_contents->SetSourceRect(*clipped_source);
837 texture_contents->SetStrictSourceRect(src_rect_constraint ==
839 texture_contents->SetSamplerDescriptor(sampler);
840 texture_contents->SetOpacity(paint.
color.
alpha);
841 texture_contents->SetDeferApplyingOpacity(paint.
HasColorFilter());
849 AddRenderEntityToCurrentPass(entity);
857 AddRenderEntityToCurrentPass(entity);
860 size_t Canvas::GetClipHeight()
const {
861 return transform_stack_.back().clip_height;
866 const Paint& paint) {
879 if (UseColorSourceContents(vertices, paint)) {
880 AddRenderEntityWithFiltersToCurrentPass(entity, vertices.get(), paint);
886 auto contents = std::make_shared<VerticesSimpleBlendContents>();
887 contents->SetBlendMode(blend_mode);
889 contents->SetGeometry(vertices);
891 AddRenderEntityToCurrentPass(entity);
898 paint.
color_source->type() == flutter::DlColorSourceType::kImage) {
899 const flutter::DlImageColorSource* image_color_source =
901 FML_DCHECK(image_color_source &&
902 image_color_source->image()->impeller_texture());
903 auto texture = image_color_source->image()->impeller_texture();
905 image_color_source->horizontal_tile_mode());
908 auto sampler_descriptor =
910 auto effect_transform = image_color_source->matrix();
912 auto contents = std::make_shared<VerticesSimpleBlendContents>();
913 contents->SetBlendMode(blend_mode);
915 contents->SetGeometry(vertices);
916 contents->SetEffectTransform(effect_transform);
917 contents->SetTexture(texture);
918 contents->SetTileMode(x_tile_mode, y_tile_mode);
919 contents->SetSamplerDescriptor(sampler_descriptor);
922 AddRenderEntityToCurrentPass(entity);
926 auto src_paint = paint;
929 std::shared_ptr<ColorSourceContents> src_contents =
930 src_paint.CreateContents();
931 src_contents->SetGeometry(vertices.get());
939 auto size = src_contents->GetColorSourceSize();
940 if (size.has_value()) {
943 auto cvg = vertices->GetCoverage(
Matrix{});
944 FML_CHECK(cvg.has_value());
945 auto texture_coverage = vertices->GetTextureCoordinateCoverage();
946 if (texture_coverage.has_value()) {
949 texture_coverage->GetSize().Max({1, 1}));
951 src_coverage = cvg.value();
954 src_contents = src_paint.CreateContents();
957 src_contents->SetGeometry(clip_geometry_.back().get());
959 auto contents = std::make_shared<VerticesSimpleBlendContents>();
960 contents->SetBlendMode(blend_mode);
962 contents->SetGeometry(vertices);
963 contents->SetLazyTextureCoverage(src_coverage);
964 contents->SetLazyTexture([src_contents,
970 src_contents->RenderToSnapshot(renderer, {},
Rect::Round(src_coverage));
971 return snapshot.has_value() ? snapshot->texture :
nullptr;
974 AddRenderEntityToCurrentPass(entity);
978 const Paint& paint) {
986 AddRenderEntityToCurrentPass(entity);
992 void Canvas::SetupRenderPass() {
998 if (!stencil_attachment.has_value() || !depth_attachment.has_value()) {
1003 *renderer_.
GetContext()->GetResourceAllocator(),
1005 renderer_.
GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
1006 "ImpellerOnscreen", kDefaultStencilConfig);
1018 if (requires_readback_) {
1019 auto entity_pass_target =
1020 CreateRenderTarget(renderer_,
1023 render_passes_.push_back(
1024 LazyRenderingConfig(renderer_, std::move(entity_pass_target)));
1026 auto entity_pass_target = std::make_unique<EntityPassTarget>(
1031 render_passes_.push_back(
1032 LazyRenderingConfig(renderer_, std::move(entity_pass_target)));
1036 void Canvas::SkipUntilMatchingRestore(
size_t total_content_depth) {
1037 auto entry = CanvasStackEntry{};
1038 entry.skipping =
true;
1039 entry.clip_depth = current_depth_ + total_content_depth;
1040 transform_stack_.push_back(entry);
1045 return SkipUntilMatchingRestore(total_content_depth);
1049 entry.
transform = transform_stack_.back().transform;
1050 entry.clip_depth = current_depth_ + total_content_depth;
1051 entry.distributed_opacity = transform_stack_.back().distributed_opacity;
1052 FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1053 << entry.clip_depth <<
" <=? " << transform_stack_.back().clip_depth
1054 <<
" after allocating " << total_content_depth;
1055 entry.clip_height = transform_stack_.back().clip_height;
1057 transform_stack_.push_back(entry);
1064 return std::nullopt;
1068 if (!maybe_current_clip_coverage.has_value()) {
1069 return std::nullopt;
1072 auto current_clip_coverage = maybe_current_clip_coverage.value();
1076 std::optional<Rect> maybe_coverage_limit =
1078 Size(render_passes_.back()
1079 .inline_pass_context->GetTexture()
1083 if (!maybe_coverage_limit.has_value() || maybe_coverage_limit->IsEmpty()) {
1084 return std::nullopt;
1087 return maybe_coverage_limit->Intersection(
1092 std::optional<Rect> bounds,
1093 const flutter::DlImageFilter* backdrop_filter,
1095 uint32_t total_content_depth,
1096 bool can_distribute_opacity,
1097 std::optional<int64_t> backdrop_id) {
1098 TRACE_EVENT0(
"flutter",
"Canvas::saveLayer");
1100 return SkipUntilMatchingRestore(total_content_depth);
1104 if (!maybe_coverage_limit.has_value()) {
1105 return SkipUntilMatchingRestore(total_content_depth);
1107 auto coverage_limit = maybe_coverage_limit.value();
1109 if (can_distribute_opacity && !backdrop_filter &&
1112 Save(total_content_depth);
1113 transform_stack_.back().distributed_opacity *= paint.
color.
alpha;
1117 std::shared_ptr<FilterContents> filter_contents = paint.
WithImageFilter(
1118 Rect(), transform_stack_.back().transform,
1123 transform_stack_.back().transform,
1128 !!backdrop_filter ||
1133 if (!maybe_subpass_coverage.has_value()) {
1134 return SkipUntilMatchingRestore(total_content_depth);
1137 auto subpass_coverage = maybe_subpass_coverage.value();
1148 bool did_round_out =
false;
1149 Point coverage_origin_adjustment =
Point{0, 0};
1153 did_round_out =
true;
1157 coverage_origin_adjustment =
1158 Point(subpass_coverage.GetLeftTop().x -
1159 std::floor(subpass_coverage.GetLeftTop().x),
1160 subpass_coverage.GetLeftTop().y -
1161 std::floor(subpass_coverage.GetLeftTop().y));
1164 return SkipUntilMatchingRestore(total_content_depth);
1172 ->GetMaximumRenderPassAttachmentSize());
1175 std::shared_ptr<FilterContents> backdrop_filter_contents;
1177 if (backdrop_filter) {
1178 local_position = subpass_coverage.GetOrigin() - GetGlobalPassPosition();
1180 [backdrop_filter = backdrop_filter](
1183 auto filter =
WrapInput(backdrop_filter, input);
1184 filter->SetEffectTransform(effect_transform);
1185 filter->SetRenderingMode(rendering_mode);
1189 std::shared_ptr<Texture> input_texture;
1194 bool will_cache_backdrop_texture =
false;
1199 size_t backdrop_count = 1;
1200 if (backdrop_id.has_value()) {
1201 std::unordered_map<int64_t, BackdropData>::iterator backdrop_data_it =
1202 backdrop_data_.find(backdrop_id.value());
1203 if (backdrop_data_it != backdrop_data_.end()) {
1204 backdrop_data = &backdrop_data_it->second;
1205 will_cache_backdrop_texture =
1207 backdrop_count = backdrop_data_it->second.backdrop_count;
1211 if (!will_cache_backdrop_texture || !backdrop_data->
texture_slot) {
1212 backdrop_count_ -= backdrop_count;
1218 const bool should_use_onscreen =
1220 backdrop_count_ == 0 && render_passes_.size() == 1u;
1221 input_texture = FlipBackdrop(
1222 GetGlobalPassPosition(),
1223 will_cache_backdrop_texture,
1226 if (!input_texture) {
1231 if (will_cache_backdrop_texture) {
1238 backdrop_filter_contents = backdrop_filter_proc(
1240 transform_stack_.back().transform.Basis(),
1243 transform_stack_.back().transform.HasTranslation()
1247 if (will_cache_backdrop_texture) {
1248 FML_DCHECK(backdrop_data);
1255 backdrop_filter_contents->RenderToSnapshot(renderer_, {});
1258 std::optional<Snapshot> maybe_snapshot =
1260 if (maybe_snapshot.has_value()) {
1261 Snapshot snapshot = maybe_snapshot.value();
1263 subpass_coverage.Shift(-GetGlobalPassPosition()));
1266 contents->SetTexture(snapshot.
texture);
1267 contents->SetSourceRect(scaled);
1277 backdrop_entity.
Render(renderer_, GetCurrentRenderPass());
1285 Paint paint_copy = paint;
1286 paint_copy.
color.
alpha *= transform_stack_.back().distributed_opacity;
1287 transform_stack_.back().distributed_opacity = 1.0;
1289 render_passes_.push_back(
1291 CreateRenderTarget(renderer_,
1296 paint_copy, subpass_coverage.Shift(-coverage_origin_adjustment)});
1299 entry.
transform = transform_stack_.back().transform;
1300 entry.
clip_depth = current_depth_ + total_content_depth;
1301 FML_DCHECK(entry.
clip_depth <= transform_stack_.back().clip_depth)
1302 << entry.
clip_depth <<
" <=? " << transform_stack_.back().clip_depth
1303 <<
" after allocating " << total_content_depth;
1304 entry.
clip_height = transform_stack_.back().clip_height;
1307 transform_stack_.emplace_back(entry);
1314 clip_coverage_stack_.
PushSubpass(subpass_coverage, GetClipHeight());
1316 if (!backdrop_filter_contents) {
1322 backdrop_entity.
SetContents(std::move(backdrop_filter_contents));
1325 backdrop_entity.
SetClipDepth(std::numeric_limits<uint32_t>::max());
1326 backdrop_entity.
Render(renderer_, GetCurrentRenderPass());
1330 FML_DCHECK(transform_stack_.size() > 0);
1331 if (transform_stack_.size() == 1) {
1348 FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1349 << current_depth_ <<
" <=? " << transform_stack_.back().clip_depth;
1350 current_depth_ = transform_stack_.back().clip_depth;
1353 transform_stack_.pop_back();
1357 if (transform_stack_.back().rendering_mode ==
1359 transform_stack_.back().rendering_mode ==
1361 auto lazy_render_pass = std::move(render_passes_.back());
1362 render_passes_.pop_back();
1364 lazy_render_pass.inline_pass_context->GetRenderPass();
1367 save_layer_state_.pop_back();
1368 auto global_pass_position = GetGlobalPassPosition();
1370 std::shared_ptr<Contents> contents = CreateContentsForSubpassTarget(
1371 save_layer_state.
paint,
1372 lazy_render_pass.inline_pass_context->GetTexture(),
1374 transform_stack_.back().transform
1377 lazy_render_pass.inline_pass_context->EndPass();
1384 Point subpass_texture_position;
1385 if (transform_stack_.back().did_round_out) {
1388 subpass_texture_position =
1393 subpass_texture_position =
1407 ApplyFramebufferBlend(element_entity);
1418 auto input_texture = FlipBackdrop(GetGlobalPassPosition());
1419 if (!input_texture) {
1429 contents->SetCoverageHint(element_entity.
GetCoverage());
1437 *render_passes_.back().inline_pass_context->GetRenderPass()
1440 transform_stack_.pop_back();
1448 size_t num_clips = transform_stack_.back().num_clips;
1449 transform_stack_.pop_back();
1451 if (num_clips > 0) {
1462 *render_passes_.back().inline_pass_context->GetRenderPass(),
1463 GetGlobalPassPosition()
1471 bool Canvas::AttemptBlurredTextOptimization(
1472 const std::shared_ptr<TextFrame>& text_frame,
1473 const std::shared_ptr<TextContents>& text_contents,
1475 const Paint& paint) {
1489 std::shared_ptr<FilterContents> filter =
1494 std::optional<Glyph> maybe_glyph = text_frame->AsSingleGlyph();
1495 int64_t identifier = maybe_glyph.has_value()
1496 ? maybe_glyph.value().index
1497 :
reinterpret_cast<int64_t
>(text_frame.get());
1498 TextShadowCache::TextShadowCacheKey cache_key(
1501 maybe_glyph.has_value(),
1502 text_frame->GetFont(),
1506 renderer_, entity, filter, cache_key);
1507 if (result.has_value()) {
1508 AddRenderEntityToCurrentPass(result.value(),
false);
1522 const Paint& paint) {
1524 if (max_scale * text_frame->GetFont().GetMetrics().point_size >
1526 fml::StatusOr<flutter::DlPath> path = text_frame->GetPath();
1530 DrawPath(path.value().GetPath(), paint);
1540 auto text_contents = std::make_shared<TextContents>();
1541 text_contents->SetTextFrame(text_frame);
1543 text_contents->SetScale(max_scale);
1544 text_contents->SetColor(paint.
color);
1545 text_contents->SetOffset(position);
1546 text_contents->SetTextProperties(paint.
color,
1557 if (AttemptBlurredTextOptimization(text_frame, text_contents, entity,
1563 AddRenderEntityToCurrentPass(entity,
false);
1566 void Canvas::AddRenderEntityWithFiltersToCurrentPass(
Entity& entity,
1570 std::shared_ptr<ColorSourceContents> contents = paint.
CreateContents();
1573 contents->SetGeometry(geometry);
1575 AddRenderEntityToCurrentPass(entity, reuse_depth);
1583 if (needs_color_filter &&
1584 contents->ApplyColorFilter([&](Color color) -> Color {
1585 if (paint.color_filter) {
1586 color = GetCPUColorFilterProc(paint.color_filter)(color);
1589 color = color.ApplyColorMatrix(kColorInversion);
1593 needs_color_filter =
false;
1597 contents->SetGeometry(geometry);
1603 RectGeometry out_rect(
Rect{});
1605 contents, needs_color_filter ? paint.
color_filter :
nullptr,
1606 needs_color_filter ? paint.
invert_colors :
false, &out_rect);
1608 AddRenderEntityToCurrentPass(entity, reuse_depth);
1612 std::shared_ptr<Contents> contents_copy = std::move(contents);
1616 if (needs_color_filter &&
1618 paint.
color_source->type() != flutter::DlColorSourceType::kImage)) {
1632 std::shared_ptr<FilterContents> filter =
WrapInput(
1636 AddRenderEntityToCurrentPass(entity, reuse_depth);
1641 AddRenderEntityToCurrentPass(entity, reuse_depth);
1644 void Canvas::AddRenderEntityToCurrentPass(Entity& entity,
bool reuse_depth) {
1649 entity.SetTransform(
1650 Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) *
1651 entity.GetTransform());
1652 entity.SetInheritedOpacity(transform_stack_.back().distributed_opacity);
1653 if (entity.GetBlendMode() == BlendMode::kSrcOver &&
1654 entity.GetContents()->IsOpaque(entity.GetTransform())) {
1655 entity.SetBlendMode(BlendMode::kSrc);
1661 if (render_passes_.back().IsApplyingClearColor()) {
1662 std::optional<Color> maybe_color = entity.AsBackgroundColor(
1663 render_passes_.back().inline_pass_context->GetTexture()->GetSize());
1664 if (maybe_color.has_value()) {
1665 Color color = maybe_color.value();
1666 RenderTarget& render_target = render_passes_.back()
1667 .inline_pass_context->GetPassTarget()
1669 ColorAttachment attachment = render_target.GetColorAttachment(0);
1672 attachment.clear_color = attachment.clear_color.Unpremultiply()
1673 .Blend(color, entity.GetBlendMode())
1675 render_target.SetColorAttachment(attachment, 0u);
1686 FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1687 << current_depth_ <<
" <=? " << transform_stack_.back().clip_depth;
1688 entity.SetClipDepth(current_depth_);
1690 if (entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
1691 if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
1692 ApplyFramebufferBlend(entity);
1703 auto input_texture = FlipBackdrop(GetGlobalPassPosition());
1704 if (!input_texture) {
1712 auto element_coverage_hint = entity.GetContents()->GetCoverageHint();
1713 entity.GetContents()->SetCoverageHint(Rect::Intersection(
1714 element_coverage_hint, clip_coverage_stack_.CurrentClipCoverage()));
1716 FilterInput::Vector inputs = {
1717 FilterInput::Make(input_texture, entity.GetTransform().Invert()),
1718 FilterInput::Make(entity.GetContents())};
1720 ColorFilterContents::MakeBlend(entity.GetBlendMode(), inputs);
1721 entity.SetContents(std::move(contents));
1722 entity.SetBlendMode(BlendMode::kSrc);
1726 const std::shared_ptr<RenderPass>& result =
1727 render_passes_.back().inline_pass_context->GetRenderPass();
1735 entity.Render(renderer_, *result);
1738 RenderPass& Canvas::GetCurrentRenderPass()
const {
1739 return *render_passes_.back().inline_pass_context->GetRenderPass();
1742 void Canvas::SetBackdropData(
1743 std::unordered_map<int64_t, BackdropData> backdrop_data,
1744 size_t backdrop_count) {
1745 backdrop_data_ = std::move(backdrop_data);
1746 backdrop_count_ = backdrop_count;
1749 std::shared_ptr<Texture> Canvas::FlipBackdrop(
Point global_pass_position,
1750 bool should_remove_texture,
1751 bool should_use_onscreen) {
1753 render_passes_.pop_back();
1769 <<
"Failed to end the current render pass in order to read from "
1770 "the backdrop texture and apply an advanced blend or backdrop "
1781 const std::shared_ptr<Texture>& input_texture =
1784 if (!input_texture) {
1785 VALIDATION_LOG <<
"Failed to fetch the color texture in order to "
1786 "apply an advanced blend or backdrop filter.";
1789 render_passes_.push_back(LazyRenderingConfig(
1795 if (should_use_onscreen) {
1796 ColorAttachment color0 = render_target_.GetColorAttachment(0);
1800 color0.
load_action = color0.resolve_texture !=
nullptr
1801 ? LoadAction::kDontCare
1802 : LoadAction::kLoad;
1803 render_target_.SetColorAttachment(color0, 0);
1805 auto entity_pass_target = std::make_unique<EntityPassTarget>(
1807 renderer_.GetDeviceCapabilities().SupportsReadFromResolve(),
1808 renderer_.GetDeviceCapabilities().SupportsImplicitResolvingMSAA()
1810 render_passes_.push_back(
1811 LazyRenderingConfig(renderer_, std::move(entity_pass_target)));
1812 requires_readback_ =
false;
1814 render_passes_.push_back(LazyRenderingConfig(
1819 if (should_remove_texture) {
1820 render_passes_.back().entity_pass_target->RemoveSecondary();
1823 RenderPass& current_render_pass =
1824 *render_passes_.back().inline_pass_context->GetRenderPass();
1833 Rect size_rect = Rect::MakeSize(input_texture->GetSize());
1834 auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
1835 msaa_backdrop_contents->SetStencilEnabled(
false);
1836 msaa_backdrop_contents->SetLabel(
"MSAA backdrop");
1837 msaa_backdrop_contents->SetSourceRect(size_rect);
1838 msaa_backdrop_contents->SetTexture(input_texture);
1840 Entity msaa_backdrop_entity;
1841 msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
1842 msaa_backdrop_entity.SetBlendMode(BlendMode::kSrc);
1843 msaa_backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
1844 if (!msaa_backdrop_entity.Render(renderer_, current_render_pass)) {
1851 auto& replay_entities = clip_coverage_stack_.GetReplayEntities();
1852 for (
const auto& replay : replay_entities) {
1853 if (replay.clip_depth <= current_depth_) {
1857 SetClipScissor(replay.clip_coverage, current_render_pass,
1858 global_pass_position);
1859 if (!replay.clip_contents.Render(renderer_, current_render_pass,
1860 replay.clip_depth)) {
1865 return input_texture;
1868 bool Canvas::SupportsBlitToOnscreen()
const {
1869 return renderer_.GetContext()
1871 ->SupportsTextureToTextureBlits() &&
1872 renderer_.GetContext()->GetBackendType() ==
1873 Context::BackendType::kMetal;
1876 bool Canvas::BlitToOnscreen(
bool is_onscreen) {
1877 auto command_buffer = renderer_.GetContext()->CreateCommandBuffer();
1878 command_buffer->SetLabel(
"EntityPass Root Command Buffer");
1879 auto offscreen_target = render_passes_.back()
1880 .inline_pass_context->GetPassTarget()
1882 if (SupportsBlitToOnscreen()) {
1883 auto blit_pass = command_buffer->CreateBlitPass();
1884 blit_pass->AddCopy(offscreen_target.GetRenderTargetTexture(),
1885 render_target_.GetRenderTargetTexture());
1886 if (!blit_pass->EncodeCommands()) {
1891 auto render_pass = command_buffer->CreateRenderPass(render_target_);
1892 render_pass->SetLabel(
"EntityPass Root Render Pass");
1895 auto size_rect = Rect::MakeSize(offscreen_target.GetRenderTargetSize());
1896 auto contents = TextureContents::MakeRect(size_rect);
1897 contents->SetTexture(offscreen_target.GetRenderTargetTexture());
1898 contents->SetSourceRect(size_rect);
1899 contents->SetLabel(
"Root pass blit");
1902 entity.SetContents(contents);
1903 entity.SetBlendMode(BlendMode::kSrc);
1905 if (!entity.Render(renderer_, *render_pass)) {
1911 if (!render_pass->EncodeCommands()) {
1918 return renderer_.GetContext()->SubmitOnscreen(std::move(command_buffer));
1920 return renderer_.GetContext()->EnqueueCommandBuffer(
1921 std::move(command_buffer));
1925 bool Canvas::EnsureFinalMipmapGeneration()
const {
1926 if (!render_target_.GetRenderTargetTexture()->NeedsMipmapGeneration()) {
1929 std::shared_ptr<CommandBuffer> cmd_buffer =
1930 renderer_.GetContext()->CreateCommandBuffer();
1934 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
1938 blit_pass->GenerateMipmap(render_target_.GetRenderTargetTexture());
1939 blit_pass->EncodeCommands();
1940 return renderer_.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer));
1943 void Canvas::EndReplay() {
1944 FML_DCHECK(render_passes_.size() == 1u);
1945 render_passes_.back().inline_pass_context->GetRenderPass();
1946 render_passes_.back().inline_pass_context->EndPass(
1947 !requires_readback_ && is_onscreen_);
1948 backdrop_data_.clear();
1953 if (requires_readback_) {
1954 BlitToOnscreen(is_onscreen_);
1956 if (!EnsureFinalMipmapGeneration()) {
1959 if (!renderer_.GetContext()->FlushCommandBuffers()) {
1963 render_passes_.clear();
1964 renderer_.GetRenderTargetCache()->End();
1965 clip_geometry_.clear();
1968 Initialize(initial_cull_rect_);
void ClipGeometry(const Geometry &geometry, Entity::ClipOperation clip_op, bool is_aa=true)
static constexpr uint32_t kMaxDepth
Canvas(ContentContext &renderer, const RenderTarget &render_target, bool is_onscreen, bool requires_readback)
void DrawRoundSuperellipse(const RoundSuperellipse &rse, const Paint &paint)
std::optional< Rect > GetLocalCoverageLimit() const
Return the culling bounds of the current render target, or nullopt if there is no coverage.
void SaveLayer(const Paint &paint, std::optional< Rect > bounds=std::nullopt, const flutter::DlImageFilter *backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown, uint32_t total_content_depth=kMaxDepth, bool can_distribute_opacity=false, std::optional< int64_t > backdrop_id=std::nullopt)
const Matrix & GetCurrentTransform() const
void DrawVertices(const std::shared_ptr< VerticesGeometry > &vertices, BlendMode blend_mode, const Paint &paint)
void DrawOval(const Rect &rect, const Paint &paint)
void DrawImageRect(const std::shared_ptr< Texture > &image, Rect source, Rect dest, const Paint &paint, const SamplerDescriptor &sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
void RestoreToCount(size_t count)
size_t GetSaveCount() const
void Concat(const Matrix &transform)
void Transform(const Matrix &transform)
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
void PreConcat(const Matrix &transform)
void Rotate(Radians radians)
void DrawPoints(const Point points[], uint32_t count, Scalar radius, const Paint &paint, PointStyle point_style)
void DrawTextFrame(const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint)
void DrawImage(const std::shared_ptr< Texture > &image, Point offset, const Paint &paint, const SamplerDescriptor &sampler={})
void DrawPaint(const Paint &paint)
void DrawRoundRect(const RoundRect &rect, const Paint &paint)
void Skew(Scalar sx, Scalar sy)
void Scale(const Vector2 &scale)
void DrawPath(const Path &path, const Paint &paint)
void Save(uint32_t total_content_depth=kMaxDepth)
void DrawRect(const Rect &rect, const Paint &paint)
void DrawAtlas(const std::shared_ptr< AtlasContents > &atlas_contents, const Paint &paint)
void DrawLine(const Point &p0, const Point &p1, const Paint &paint, bool reuse_depth=false)
void Translate(const Vector3 &offset)
void DrawCircle(const Point ¢er, Scalar radius, const Paint &paint)
virtual bool SupportsImplicitResolvingMSAA() const =0
Whether the context backend supports multisampled rendering to the on-screen surface without requirin...
virtual bool SupportsFramebufferFetch() const =0
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
virtual bool SupportsReadFromResolve() const =0
Whether the context backend supports binding the current RenderPass attachments. This is supported if...
void SetGeometry(GeometryResult geometry)
Set the pre-tessellated clip geometry.
void SetClipOperation(Entity::ClipOperation clip_op)
bool Render(const ContentContext &renderer, RenderPass &pass, uint32_t clip_depth) const
static std::shared_ptr< ColorFilterContents > MakeBlend(BlendMode blend_mode, FilterInput::Vector inputs, std::optional< Color > foreground_color=std::nullopt)
the [inputs] are expected to be in the order of dst, src.
const Capabilities & GetDeviceCapabilities() const
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
TextShadowCache & GetTextShadowCache() const
std::shared_ptr< Context > GetContext() const
A geometry that implements "drawPaint" like behavior by covering the entire render pass area.
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
std::optional< Rect > GetCoverage() const
@ kSubpassPrependSnapshotTransform
@ kSubpassAppendSnapshotTransform
const std::shared_ptr< Contents > & GetContents() const
void SetClipDepth(uint32_t clip_depth)
BlendMode GetBlendMode() const
void SetContents(std::shared_ptr< Contents > contents)
void SetBlendMode(BlendMode blend_mode)
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
static constexpr BlendMode kLastPipelineBlendMode
static bool IsBlendModeDestructive(BlendMode blend_mode)
Returns true if the blend mode is "destructive", meaning that even fully transparent source colors wo...
A class that tracks all clips that have been recorded in the current entity pass stencil.
std::optional< Rect > CurrentClipCoverage() const
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
ReplayResult & GetLastReplayResult()
ClipStateResult RecordClip(const ClipContents &clip_contents, Matrix transform, Point global_pass_position, uint32_t clip_depth, size_t clip_height_floor, bool is_aa)
ClipStateResult RecordRestore(Point global_pass_position, size_t restore_height)
A geometry that is created from a filled path object.
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
static std::unique_ptr< Geometry > MakeRect(const Rect &rect)
virtual GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const =0
virtual bool CanApplyMaskFilter() const
virtual std::optional< Rect > GetCoverage(const Matrix &transform) const =0
virtual bool IsAxisAlignedRect() const
static std::unique_ptr< LineContents > Make(std::unique_ptr< LineGeometry > geometry, Color color)
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & AddRoundRect(RoundRect rect)
PathBuilder & AddRect(const Rect &rect)
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
PathBuilder & AddOval(const Rect &rect)
PathBuilder & AddRoundSuperellipse(RoundSuperellipse rse)
PathBuilder & SetConvexity(Convexity value)
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
A geometry class specialized for Canvas::DrawPoints.
ColorAttachment GetColorAttachment(size_t index) const
Get the color attachment at [index].
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
ISize GetRenderTargetSize() const
const std::optional< DepthAttachment > & GetDepthAttachment() const
const std::optional< StencilAttachment > & GetStencilAttachment() const
void SetupDepthStencilAttachments(const Context &context, Allocator &allocator, ISize size, bool msaa, std::string_view label="Offscreen", RenderTarget::AttachmentConfig stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &depth_stencil_texture=nullptr)
A geometry that is created from a stroked path object.
std::optional< Entity > Lookup(const ContentContext &renderer, const Entity &entity, const std::shared_ptr< FilterContents > &contents, const TextShadowCacheKey &)
Lookup the entity in the cache with the given filter/text contents, returning the new entity to rende...
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
ISize subpass_size
The output size of the down-sampling pass.
impeller::SamplerDescriptor ToSamplerDescriptor(const flutter::DlImageSampling options)
Color ToColor(const flutter::DlColor &color)
static constexpr Scalar kMaxTextScale
std::shared_ptr< ColorFilterContents > WrapWithGPUColorFilter(const flutter::DlColorFilter *filter, const std::shared_ptr< FilterInput > &input, ColorFilterContents::AbsorbOpacity absorb_opacity)
SourceRectConstraint
Controls the behavior of the source rectangle given to DrawImageRect.
@ kStrict
Sample only within the source rectangle. May be slower.
std::shared_ptr< FilterContents > WrapInput(const flutter::DlImageFilter *filter, const FilterInput::Ref &input)
Generate a new FilterContents using this filter's configuration.
constexpr float kEhCloseEnough
std::shared_ptr< ColorFilterContents > WrapWithInvertColors(const std::shared_ptr< FilterInput > &input, ColorFilterContents::AbsorbOpacity absorb_opacity)
@ kRound
Points are drawn as squares.
ColorFilterProc GetCPUColorFilterProc(const flutter::DlColorFilter *filter)
@ kMayClipContents
The caller claims the bounds are a subset of an estimate of the reasonably tight bounds but likely cl...
@ kContainsContents
The caller claims the bounds are a reasonably tight estimate of the coverage of the contents and shou...
static constexpr const ColorMatrix kColorInversion
A color matrix which inverts colors.
std::optional< Rect > ComputeSaveLayerCoverage(const Rect &content_coverage, const Matrix &effect_transform, const Rect &coverage_limit, const std::shared_ptr< FilterContents > &image_filter, bool flood_output_coverage, bool flood_input_coverage)
Compute the coverage of a subpass in the global coordinate space.
std::shared_ptr< Texture > texture
std::shared_ptr< Texture > texture_slot
std::optional< Snapshot > shared_filter_snapshot
Entity::RenderingMode rendering_mode
static constexpr Color BlackTransparent()
static constexpr Color Khaki()
static constexpr Color White()
constexpr Color WithAlpha(Scalar new_alpha) const
ClipContents clip_contents
std::unique_ptr< InlinePassContext > inline_pass_context
std::unique_ptr< EntityPassTarget > entity_pass_target
A 4x4 matrix using column-major storage.
static constexpr Matrix MakeTranslation(const Vector3 &t)
constexpr Scalar GetMaxBasisLengthXY() const
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
static Matrix MakeRotationZ(Radians r)
static constexpr Matrix MakeScale(const Vector3 &s)
std::shared_ptr< Contents > WithFilters(std::shared_ptr< Contents > input) const
Wrap this paint's configured filters to the given contents.
const flutter::DlColorFilter * color_filter
const flutter::DlColorSource * color_source
const flutter::DlImageFilter * image_filter
static bool CanApplyOpacityPeephole(const Paint &paint)
Whether or not a save layer with the provided paint can perform the opacity peephole optimization.
std::optional< MaskBlurDescriptor > mask_blur_descriptor
std::shared_ptr< FilterContents > WithImageFilter(const FilterInput::Variant &input, const Matrix &effect_transform, Entity::RenderingMode rendering_mode) const
std::shared_ptr< ColorSourceContents > CreateContents() const
bool HasColorFilter() const
Whether this paint has a color filter that can apply opacity.
constexpr const RoundingRadii & GetRadii() const
constexpr const Rect & GetBounds() const
constexpr const RoundingRadii & GetRadii() const
constexpr const Rect & GetBounds() const
Represents a texture and its intended draw transform/sampler configuration.
Matrix transform
The transform that should be applied to this texture for rendering.
std::shared_ptr< Texture > texture
SamplerDescriptor sampler_descriptor
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
constexpr auto GetBottom() const
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
constexpr auto GetTop() const
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
constexpr std::optional< TRect > Intersection(const TRect &o) const
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
constexpr auto GetLeft() const
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Round(const TRect< U > &r)
RoundOut(const TRect< U > &r)
constexpr auto GetRight() const
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
constexpr static TRect MakeSize(const TSize< U > &size)
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
constexpr Point GetCenter() const
Get the center point as a |Point|.
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
constexpr static TRect MakeMaximum()
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.