77#include " impeller/aiks/canvas.h"
88#include " impeller/aiks/paint_pass_delegate.h"
99#include " impeller/base/validation.h"
10+ #include " impeller/core/allocator.h"
1011#include " impeller/core/formats.h"
1112#include " impeller/entity/contents/framebuffer_blend_contents.h"
1213#include " impeller/entity/contents/text_contents.h"
@@ -44,6 +45,106 @@ static void ApplyFramebufferBlend(Entity& entity) {
4445 entity.SetBlendMode (BlendMode::kSource );
4546}
4647
48+ // / End the current render pass, saving the result as a texture, and then
49+ // / restart it with the backdrop cleared to the previous contents.
50+ // /
51+ // / This method is used to set up the input for emulated advanced blends and
52+ // / backdrop filters.
53+ // /
54+ // / Returns the previous render pass stored as a texture, or nullptr if there
55+ // / was a validation failure.
56+ static std::shared_ptr<Texture> FlipBackdrop (
57+ std::vector<LazyRenderingConfig>& render_passes,
58+ Point global_pass_position,
59+ EntityPassClipStack& clip_coverage_stack,
60+ ContentContext& renderer) {
61+ auto rendering_config = std::move (render_passes.back ());
62+ render_passes.pop_back ();
63+
64+ // If the very first thing we render in this EntityPass is a subpass that
65+ // happens to have a backdrop filter or advanced blend, than that backdrop
66+ // filter/blend will sample from an uninitialized texture.
67+ //
68+ // By calling `pass_context.GetRenderPass` here, we force the texture to pass
69+ // through at least one RenderPass with the correct clear configuration before
70+ // any sampling occurs.
71+ //
72+ // In cases where there are no contents, we
73+ // could instead check the clear color and initialize a 1x2 CPU texture
74+ // instead of ending the pass.
75+ rendering_config.inline_pass_context ->GetRenderPass (0 );
76+ if (!rendering_config.inline_pass_context ->EndPass ()) {
77+ VALIDATION_LOG
78+ << " Failed to end the current render pass in order to read from "
79+ " the backdrop texture and apply an advanced blend or backdrop "
80+ " filter." ;
81+ // Note: adding this render pass ensures there are no later crashes from
82+ // unbalanced save layers. Ideally, this method would return false and the
83+ // renderer could handle that by terminating dispatch.
84+ render_passes.push_back (LazyRenderingConfig (
85+ renderer, std::move (rendering_config.entity_pass_target )));
86+ return nullptr ;
87+ }
88+
89+ std::shared_ptr<Texture> input_texture =
90+ rendering_config.entity_pass_target ->Flip (
91+ *renderer.GetContext ()->GetResourceAllocator ());
92+
93+ if (!input_texture) {
94+ VALIDATION_LOG << " Failed to fetch the color texture in order to "
95+ " apply an advanced blend or backdrop filter." ;
96+
97+ // Note: see above.
98+ render_passes.push_back (LazyRenderingConfig (
99+ renderer, std::move (rendering_config.entity_pass_target )));
100+ return nullptr ;
101+ }
102+
103+ render_passes.push_back (LazyRenderingConfig (
104+ renderer, std::move (rendering_config.entity_pass_target )));
105+ // Eagerly restore the BDF contents.
106+
107+ // If the pass context returns a backdrop texture, we need to draw it to the
108+ // current pass. We do this because it's faster and takes significantly less
109+ // memory than storing/loading large MSAA textures. Also, it's not possible
110+ // to blit the non-MSAA resolve texture of the previous pass to MSAA
111+ // textures (let alone a transient one).
112+ Rect size_rect = Rect::MakeSize (input_texture->GetSize ());
113+ auto msaa_backdrop_contents = TextureContents::MakeRect (size_rect);
114+ msaa_backdrop_contents->SetStencilEnabled (false );
115+ msaa_backdrop_contents->SetLabel (" MSAA backdrop" );
116+ msaa_backdrop_contents->SetSourceRect (size_rect);
117+ msaa_backdrop_contents->SetTexture (input_texture);
118+
119+ Entity msaa_backdrop_entity;
120+ msaa_backdrop_entity.SetContents (std::move (msaa_backdrop_contents));
121+ msaa_backdrop_entity.SetBlendMode (BlendMode::kSource );
122+ msaa_backdrop_entity.SetClipDepth (std::numeric_limits<uint32_t >::max ());
123+ if (!msaa_backdrop_entity.Render (
124+ renderer,
125+ *render_passes.back ().inline_pass_context ->GetRenderPass (0 ).pass )) {
126+ VALIDATION_LOG << " Failed to render MSAA backdrop entity." ;
127+ return nullptr ;
128+ }
129+
130+ // Restore any clips that were recorded before the backdrop filter was
131+ // applied.
132+ auto & replay_entities = clip_coverage_stack.GetReplayEntities ();
133+ for (const auto & replay : replay_entities) {
134+ SetClipScissor (
135+ clip_coverage_stack.CurrentClipCoverage (),
136+ *render_passes.back ().inline_pass_context ->GetRenderPass (0 ).pass ,
137+ global_pass_position);
138+ if (!replay.entity .Render (
139+ renderer,
140+ *render_passes.back ().inline_pass_context ->GetRenderPass (0 ).pass )) {
141+ VALIDATION_LOG << " Failed to render entity for clip restore." ;
142+ }
143+ }
144+
145+ return input_texture;
146+ }
147+
47148} // namespace
48149
49150static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
@@ -276,23 +377,12 @@ void ExperimentalCanvas::SaveLayer(
276377 return filter;
277378 };
278379
279- auto rendering_config = std::move (render_passes_.back ());
280- render_passes_.pop_back ();
281-
282- // If the very first thing we render in this EntityPass is a subpass that
283- // happens to have a backdrop filter, than that backdrop filter will end
284- // may wind up sampling from the raw, uncleared texture that came straight
285- // out of the texture cache. By calling `pass_context.GetRenderPass` here,
286- // we force the texture to pass through at least one RenderPass with the
287- // correct clear configuration before any sampling occurs.
288- rendering_config.inline_pass_context ->GetRenderPass (0 );
289-
290- ISize restore_size =
291- rendering_config.inline_pass_context ->GetTexture ()->GetSize ();
292-
293- std::shared_ptr<Texture> input_texture =
294- rendering_config.entity_pass_target ->Flip (
295- *renderer_.GetContext ()->GetResourceAllocator ());
380+ auto input_texture = FlipBackdrop (render_passes_, GetGlobalPassPosition (),
381+ clip_coverage_stack_, renderer_);
382+ if (!input_texture) {
383+ // Validation failures are logged in FlipBackdrop.
384+ return ;
385+ }
296386
297387 backdrop_filter_contents = backdrop_filter_proc (
298388 FilterInput::Make (std::move (input_texture)),
@@ -302,58 +392,6 @@ void ExperimentalCanvas::SaveLayer(
302392 transform_stack_.back ().transform .HasTranslation ()
303393 ? Entity::RenderingMode::kSubpassPrependSnapshotTransform
304394 : Entity::RenderingMode::kSubpassAppendSnapshotTransform );
305-
306- // The subpass will need to read from the current pass texture when
307- // rendering the backdrop, so if there's an active pass, end it prior to
308- // rendering the subpass.
309- rendering_config.inline_pass_context ->EndPass ();
310-
311- // Create a new render pass that the backdrop filter contents will be
312- // restored to in order to continue rendering.
313- render_passes_.push_back (LazyRenderingConfig (
314- renderer_, std::move (rendering_config.entity_pass_target )));
315- // Eagerly restore the BDF contents.
316-
317- // If the pass context returns a backdrop texture, we need to draw it to the
318- // current pass. We do this because it's faster and takes significantly less
319- // memory than storing/loading large MSAA textures. Also, it's not possible
320- // to blit the non-MSAA resolve texture of the previous pass to MSAA
321- // textures (let alone a transient one).
322- Rect size_rect = Rect::MakeSize (restore_size);
323- auto msaa_backdrop_contents = TextureContents::MakeRect (size_rect);
324- msaa_backdrop_contents->SetStencilEnabled (false );
325- msaa_backdrop_contents->SetLabel (" MSAA backdrop" );
326- msaa_backdrop_contents->SetSourceRect (size_rect);
327- msaa_backdrop_contents->SetTexture (
328- rendering_config.inline_pass_context ->GetTexture ());
329-
330- Entity msaa_backdrop_entity;
331- msaa_backdrop_entity.SetContents (std::move (msaa_backdrop_contents));
332- msaa_backdrop_entity.SetBlendMode (BlendMode::kSource );
333- msaa_backdrop_entity.SetClipDepth (std::numeric_limits<uint32_t >::max ());
334- if (!msaa_backdrop_entity.Render (renderer_,
335- *render_passes_.back ()
336- .inline_pass_context ->GetRenderPass (0 )
337- .pass )) {
338- VALIDATION_LOG << " Failed to render MSAA backdrop filter entity." ;
339- return ;
340- }
341-
342- // Restore any clips that were recorded before the backdrop filter was
343- // applied.
344- auto & replay_entities = clip_coverage_stack_.GetReplayEntities ();
345- for (const auto & replay : replay_entities) {
346- SetClipScissor (
347- clip_coverage_stack_.CurrentClipCoverage (),
348- *render_passes_.back ().inline_pass_context ->GetRenderPass (0 ).pass ,
349- GetGlobalPassPosition ());
350- if (!replay.entity .Render (renderer_,
351- *render_passes_.back ()
352- .inline_pass_context ->GetRenderPass (0 )
353- .pass )) {
354- VALIDATION_LOG << " Failed to render entity for clip restore." ;
355- }
356- }
357395 }
358396
359397 // When applying a save layer, absorb any pending distributed opacity.
@@ -484,8 +522,31 @@ bool ExperimentalCanvas::Restore() {
484522 if (renderer_.GetDeviceCapabilities ().SupportsFramebufferFetch ()) {
485523 ApplyFramebufferBlend (element_entity);
486524 } else {
487- VALIDATION_LOG << " Emulated advanced blends are currently unsupported." ;
488- element_entity.SetBlendMode (BlendMode::kSourceOver );
525+ // End the active pass and flush the buffer before rendering "advanced"
526+ // blends. Advanced blends work by binding the current render target
527+ // texture as an input ("destination"), blending with a second texture
528+ // input ("source"), writing the result to an intermediate texture, and
529+ // finally copying the data from the intermediate texture back to the
530+ // render target texture. And so all of the commands that have written
531+ // to the render target texture so far need to execute before it's bound
532+ // for blending (otherwise the blend pass will end up executing before
533+ // all the previous commands in the active pass).
534+ auto input_texture =
535+ FlipBackdrop (render_passes_, GetGlobalPassPosition (),
536+ clip_coverage_stack_, renderer_);
537+ if (!input_texture) {
538+ return false ;
539+ }
540+
541+ FilterInput::Vector inputs = {
542+ FilterInput::Make (input_texture,
543+ element_entity.GetTransform ().Invert ()),
544+ FilterInput::Make (element_entity.GetContents ())};
545+ auto contents = ColorFilterContents::MakeBlend (
546+ element_entity.GetBlendMode (), inputs);
547+ contents->SetCoverageHint (element_entity.GetCoverage ());
548+ element_entity.SetContents (std::move (contents));
549+ element_entity.SetBlendMode (BlendMode::kSource );
489550 }
490551 }
491552
0 commit comments