Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
experimental_canvas.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6#include "fml/logging.h"
14
15namespace impeller {
16
17static void ApplyFramebufferBlend(Entity& entity) {
18 auto src_contents = entity.GetContents();
19 auto contents = std::make_shared<FramebufferBlendContents>();
20 contents->SetChildContents(src_contents);
21 contents->SetBlendMode(entity.GetBlendMode());
22 entity.SetContents(std::move(contents));
24}
25
32
33static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
34 ContentContext& renderer,
35 ISize size,
36 int mip_count,
37 const Color& clear_color) {
38 const std::shared_ptr<Context>& context = renderer.GetContext();
39
40 /// All of the load/store actions are managed by `InlinePassContext` when
41 /// `RenderPasses` are created, so we just set them to `kDontCare` here.
42 /// What's important is the `StorageMode` of the textures, which cannot be
43 /// changed for the lifetime of the textures.
44
45 if (context->GetBackendType() == Context::BackendType::kOpenGLES) {
46 // TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
47 // generation on opengles.
48 mip_count = 1;
49 }
50
52 if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
53 target = renderer.GetRenderTargetCache()->CreateOffscreenMSAA(
54 /*context=*/*context,
55 /*size=*/size,
56 /*mip_count=*/mip_count,
57 /*label=*/"EntityPass",
58 /*color_attachment_config=*/
61 .resolve_storage_mode = StorageMode::kDevicePrivate,
62 .load_action = LoadAction::kDontCare,
64 .clear_color = clear_color},
65 /*stencil_attachment_config=*/
67 } else {
68 target = renderer.GetRenderTargetCache()->CreateOffscreen(
69 *context, // context
70 size, // size
71 /*mip_count=*/mip_count,
72 "EntityPass", // label
75 .load_action = LoadAction::kDontCare,
76 .store_action = StoreAction::kDontCare,
77 .clear_color = clear_color,
78 }, // color_attachment_config
79 kDefaultStencilConfig // stencil_attachment_config
80 );
81 }
82
83 return std::make_unique<EntityPassTarget>(
84 target, renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
85 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA());
86}
87
89 RenderTarget& render_target)
90 : Canvas(), renderer_(renderer), render_target_(render_target) {
91 SetupRenderPass();
92}
93
95 RenderTarget& render_target,
96 Rect cull_rect)
97 : Canvas(cull_rect), renderer_(renderer), render_target_(render_target) {
98 SetupRenderPass();
99}
100
102 RenderTarget& render_target,
103 IRect cull_rect)
104 : Canvas(cull_rect), renderer_(renderer), render_target_(render_target) {
105 SetupRenderPass();
106}
107
108void ExperimentalCanvas::SetupRenderPass() {
109 auto color0 = render_target_.GetColorAttachments().find(0u)->second;
110
111 auto& stencil_attachment = render_target_.GetStencilAttachment();
112 auto& depth_attachment = render_target_.GetDepthAttachment();
113 if (!stencil_attachment.has_value() || !depth_attachment.has_value()) {
114 // Setup a new root stencil with an optimal configuration if one wasn't
115 // provided by the caller.
116 render_target_.SetupDepthStencilAttachments(
117 *renderer_.GetContext(),
118 *renderer_.GetContext()->GetResourceAllocator(),
119 color0.texture->GetSize(),
120 renderer_.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
121 "ImpellerOnscreen", kDefaultStencilConfig);
122 }
123
124 // Set up the clear color of the root pass.
125 color0.clear_color = Color::BlackTransparent();
126 render_target_.SetColorAttachment(color0, 0);
127
128 entity_pass_targets_.push_back(std::make_unique<EntityPassTarget>(
129 render_target_,
132
133 auto inline_pass = std::make_unique<InlinePassContext>(
134 renderer_, *entity_pass_targets_.back(), 0);
135 inline_pass_contexts_.emplace_back(std::move(inline_pass));
136 auto result = inline_pass_contexts_.back()->GetRenderPass(0u);
137 render_passes_.push_back(result.pass);
138
139 renderer_.GetRenderTargetCache()->Start();
140}
141
142void ExperimentalCanvas::Save(uint32_t total_content_depth) {
143 auto entry = CanvasStackEntry{};
144 entry.transform = transform_stack_.back().transform;
145 entry.cull_rect = transform_stack_.back().cull_rect;
146 entry.clip_depth = current_depth_ + total_content_depth;
147 FML_CHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
148 << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
149 << " after allocating " << total_content_depth;
150 entry.clip_height = transform_stack_.back().clip_height;
151 entry.rendering_mode = Entity::RenderingMode::kDirect;
152 transform_stack_.emplace_back(entry);
153}
154
156 const Paint& paint,
157 std::optional<Rect> bounds,
158 const std::shared_ptr<ImageFilter>& backdrop_filter,
159 ContentBoundsPromise bounds_promise,
160 uint32_t total_content_depth) {
161 // Can we always guarantee that we get a bounds? Does a lack of bounds
162 // indicate something?
163 if (!bounds.has_value()) {
164 bounds = Rect::MakeSize(render_target_.GetRenderTargetSize());
165 }
166 Rect subpass_coverage = bounds->TransformBounds(GetCurrentTransform());
167 auto target =
168 CreateRenderTarget(renderer_,
169 ISize::MakeWH(subpass_coverage.GetSize().width,
170 subpass_coverage.GetSize().height),
172 entity_pass_targets_.push_back(std::move(target));
173 save_layer_state_.push_back(SaveLayerState{paint, subpass_coverage});
174
175 CanvasStackEntry entry;
176 entry.transform = transform_stack_.back().transform;
177 entry.cull_rect = transform_stack_.back().cull_rect;
178 entry.clip_depth = current_depth_ + total_content_depth;
179 FML_CHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
180 << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
181 << " after allocating " << total_content_depth;
182 entry.clip_height = transform_stack_.back().clip_height;
184 transform_stack_.emplace_back(entry);
185
186 auto inline_pass = std::make_unique<InlinePassContext>(
187 renderer_, *entity_pass_targets_.back(), 0);
188 inline_pass_contexts_.emplace_back(std::move(inline_pass));
189
190 auto result = inline_pass_contexts_.back()->GetRenderPass(0u);
191 render_passes_.push_back(result.pass);
192}
193
195 FML_DCHECK(transform_stack_.size() > 0);
196 if (transform_stack_.size() == 1) {
197 return false;
198 }
199
200 // This check is important to make sure we didn't exceed the depth
201 // that the clips were rendered at while rendering any of the
202 // rendering ops. It is OK for the current depth to equal the
203 // outgoing clip depth because that means the clipping would have
204 // been successful up through the last rendering op, but it cannot
205 // be greater.
206 // Also, we bump the current rendering depth to the outgoing clip
207 // depth so that future rendering operations are not clipped by
208 // any of the pixels set by the expiring clips. It is OK for the
209 // estimates used to determine the clip depth in save/saveLayer
210 // to be overly conservative, but we need to jump the depth to
211 // the clip depth so that the next rendering op will get a
212 // larger depth (it will pre-increment the current_depth_ value).
213 FML_CHECK(current_depth_ <= transform_stack_.back().clip_depth)
214 << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
215 current_depth_ = transform_stack_.back().clip_depth;
216
217 if (transform_stack_.back().rendering_mode ==
219 auto inline_pass = std::move(inline_pass_contexts_.back());
220
221 SaveLayerState save_layer_state = save_layer_state_.back();
222 save_layer_state_.pop_back();
223
224 std::shared_ptr<Contents> contents =
225 PaintPassDelegate(save_layer_state.paint)
226 .CreateContentsForSubpassTarget(inline_pass->GetTexture(),
227 transform_stack_.back().transform);
228
229 inline_pass->EndPass();
230 render_passes_.pop_back();
231 inline_pass_contexts_.pop_back();
232
233 Entity element_entity;
234 element_entity.SetClipDepth(++current_depth_);
235 element_entity.SetContents(std::move(contents));
236 element_entity.SetBlendMode(save_layer_state.paint.blend_mode);
238 Vector3(save_layer_state.coverage.GetOrigin())));
239
240 if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
242 ApplyFramebufferBlend(element_entity);
243 } else {
244 VALIDATION_LOG << "Emulated advanced blends are currently unsupported.";
245 element_entity.SetBlendMode(BlendMode::kSourceOver);
246 }
247 }
248
249 element_entity.Render(renderer_, *render_passes_.back());
250 }
251
252 transform_stack_.pop_back();
253
254 return true;
255}
256
258 const std::shared_ptr<TextFrame>& text_frame,
259 Point position,
260 const Paint& paint) {
261 Entity entity;
262 entity.SetClipDepth(GetClipHeight());
263 entity.SetBlendMode(paint.blend_mode);
264
265 auto text_contents = std::make_shared<TextContents>();
266 text_contents->SetTextFrame(text_frame);
267 text_contents->SetColor(paint.color);
268 text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
269 text_contents->SetScale(GetCurrentTransform().GetMaxBasisLengthXY());
270
272 Matrix::MakeTranslation(position));
273
274 // TODO(bdero): This mask blur application is a hack. It will always wind up
275 // doing a gaussian blur that affects the color source itself
276 // instead of just the mask. The color filter text support
277 // needs to be reworked in order to interact correctly with
278 // mask filters.
279 // https://github.com/flutter/flutter/issues/133297
280 entity.SetContents(
281 paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true)));
282
283 AddRenderEntityToCurrentPass(std::move(entity), false);
284}
285
287 bool reuse_depth) {
288 auto transform = entity.GetTransform();
289 entity.SetTransform(
290 Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) * transform);
291 if (!reuse_depth) {
293 }
294 // We can render at a depth up to and including the depth of the currently
295 // active clips and we will still be clipped out, but we cannot render at
296 // a depth that is greater than the current clips or we will not be clipped.
297 FML_CHECK(current_depth_ <= transform_stack_.back().clip_depth)
298 << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
300
303 ApplyFramebufferBlend(entity);
304 } else {
305 VALIDATION_LOG << "Emulated advanced blends are currently unsupported.";
306 return;
307 }
308 }
309
310 entity.Render(renderer_, *render_passes_.back());
311}
312
314 auto transform = entity.GetTransform();
315 entity.SetTransform(
316 Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) * transform);
317 // Ideally the clip depth would be greater than the current rendering
318 // depth because any rendering calls that follow this clip operation will
319 // pre-increment the depth and then be rendering above our clip depth,
320 // but that case will be caught by the CHECK in AddRenderEntity above.
321 // In practice we sometimes have a clip set with no rendering after it
322 // and in such cases the current depth will equal the clip depth.
323 // Eventually the DisplayList should optimize these out, but it is hard
324 // to know if a clip will actually be used in advance of storing it in
325 // the DisplayList buffer.
326 // See https://github.com/flutter/flutter/issues/147021
327 FML_CHECK(current_depth_ <= transform_stack_.back().clip_depth)
328 << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
329 entity.SetClipDepth(transform_stack_.back().clip_depth);
330 entity.Render(renderer_, *render_passes_.back());
331}
332
333} // namespace impeller
const Matrix & GetCurrentTransform() const
Definition canvas.cc:296
size_t GetClipHeight() const
Definition canvas.cc:823
std::deque< CanvasStackEntry > transform_stack_
Definition canvas.h:187
uint64_t current_depth_
Definition canvas.h:189
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...
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
const Capabilities & GetDeviceCapabilities() const
std::shared_ptr< Context > GetContext() const
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition entity.cc:62
const std::shared_ptr< Contents > & GetContents() const
Definition entity.cc:94
void SetClipDepth(uint32_t clip_depth)
Definition entity.cc:98
BlendMode GetBlendMode() const
Definition entity.cc:119
void SetContents(std::shared_ptr< Contents > contents)
Definition entity.cc:90
void SetBlendMode(BlendMode blend_mode)
Definition entity.cc:115
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition entity.cc:173
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition entity.cc:46
static constexpr BlendMode kLastPipelineBlendMode
Definition entity.h:23
void DrawTextFrame(const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint) override
void SaveLayer(const Paint &paint, std::optional< Rect > bounds, const std::shared_ptr< ImageFilter > &backdrop_filter, ContentBoundsPromise bounds_promise, uint32_t total_content_depth) override
void Save(uint32_t total_content_depth) override
ExperimentalCanvas(ContentContext &renderer, RenderTarget &render_target)
void AddClipEntityToCurrentPass(Entity entity) override
void AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth) override
std::shared_ptr< Contents > CreateContentsForSubpassTarget(std::shared_ptr< Texture > target, const Matrix &effect_transform) override
const std::map< size_t, ColorAttachment > & GetColorAttachments() const
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
void SetupDepthStencilAttachments(const Context &context, Allocator &allocator, ISize size, bool msaa, const std::string &label="Offscreen", RenderTarget::AttachmentConfig stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &depth_stencil_texture=nullptr)
ISize GetRenderTargetSize() const
const std::optional< DepthAttachment > & GetDepthAttachment() const
const std::optional< StencilAttachment > & GetStencilAttachment() const
const Paint & paint
GAsyncResult * result
uint32_t * target
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
static std::unique_ptr< EntityPassTarget > CreateRenderTarget(ContentContext &renderer, ISize size, int mip_count, const Color &clear_color)
static void ApplyFramebufferBlend(Entity &entity)
ContentBoundsPromise
Definition entity_pass.h:28
static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
Entity::RenderingMode rendering_mode
Definition canvas.h:39
std::optional< Rect > cull_rect
Definition canvas.h:34
static constexpr Color BlackTransparent()
Definition color.h:262
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
BlendMode blend_mode
Definition paint.h:64
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...
Definition rect.h:294
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:146
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...
Definition rect.h:287
static constexpr TSize MakeWH(Type width, Type height)
Definition size.h:34
#define VALIDATION_LOG
Definition validation.h:73