Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
runtime_effect_filter_contents.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
7#include <cstring>
8#include <optional>
9
16
17namespace impeller {
18
20 std::shared_ptr<RuntimeStage> runtime_stage) {
21 runtime_stage_ = std::move(runtime_stage);
22}
23
25 std::shared_ptr<std::vector<uint8_t>> uniforms) {
26 uniforms_ = std::move(uniforms);
27}
28
30 std::vector<RuntimeEffectContents::TextureInput> texture_inputs) {
31 texture_inputs_ = std::move(texture_inputs);
32}
33
34// |FilterContents|
35std::optional<Entity> RuntimeEffectFilterContents::RenderFilter(
36 const FilterInput::Vector& inputs,
37 const ContentContext& renderer,
38 const Entity& entity,
39 const Matrix& effect_transform,
40 const Rect& coverage,
41 const std::optional<Rect>& coverage_hint) const {
42 if (inputs.empty()) {
43 return std::nullopt;
44 }
45
46 std::optional<Snapshot> input_snapshot =
47 inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity);
48 if (!input_snapshot.has_value()) {
49 return std::nullopt;
50 }
51
52 std::optional<Rect> maybe_input_coverage = input_snapshot->GetCoverage();
53 if (!maybe_input_coverage.has_value()) {
54 return std::nullopt;
55 }
56
57 // If the input snapshot does not have an identity transform the
58 // ImageFilter.shader will not correctly render as it does not know what the
59 // transform is in order to incorporate this into sampling. We need to
60 // re-rasterize the input snapshot so that the transform is absorbed into the
61 // texture.
62 // We can technically render this only when the snapshot is just a translated
63 // version of the original. Unfortunately there isn't a way to test for that
64 // though. Blur with low sigmas will return a transform that doesn't scale but
65 // has a tiny offset to account for the blur radius. That's indistinguishable
66 // from `ImageFilter.compose` which slightly increase the size to account for
67 // rounding errors and add an offset. Said another way; ideally we would skip
68 // this branch for the unit test `ComposePaintRuntimeOuter`, but do it for
69 // `ComposeBackdropRuntimeOuterBlurInner`.
70 if (input_snapshot->ShouldRasterizeForRuntimeEffects()) {
71 Vector2 entity_offset =
72 Vector2(entity.GetTransform().m[12], entity.GetTransform().m[13]);
73 Matrix inverse = input_snapshot->transform.Invert();
74 Quad quad = inverse.Transform(Quad{
75 coverage.GetLeftTop(), //
76 coverage.GetRightTop(), //
77 coverage.GetLeftBottom(), //
78 coverage.GetRightBottom() //
79 });
80 TextureContents texture_contents;
81 texture_contents.SetTexture(input_snapshot->texture);
82 std::optional<Rect> bounds =
83 Rect::MakePointBounds(quad.begin(), quad.end());
84 if (bounds.has_value()) {
85 texture_contents.SetSourceRect(bounds.value());
86 texture_contents.SetDestinationRect(coverage);
87 texture_contents.SetStencilEnabled(false);
88 texture_contents.SetSamplerDescriptor(input_snapshot->sampler_descriptor);
89
90 // Use an AnonymousContents to restore the padding around the input that
91 // may have been cut out with a clip rect to maintain the correct
92 // coordinates for the fragment shader to perform.
93 auto anonymous_contents = AnonymousContents::Make(
94 [&texture_contents](const ContentContext& renderer,
95 const Entity& entity, RenderPass& pass) -> bool {
96 return texture_contents.Render(renderer, entity, pass);
97 },
98 [maybe_input_coverage,
99 entity_offset](const Entity& entity) -> std::optional<Rect> {
100 Rect coverage = maybe_input_coverage.value();
101 // The LT values come from the offset of the clip rect, that creates
102 // the clipping effect on the content that will be rendered from
103 // the fragment shader. The RB values define the region we'll be
104 // synthesizing and ultimately defines the width and the height of
105 // the rasterized image. The LT values can be thought of shifting
106 // the window that will be rasterized. Since we are shifting from
107 // the top-left corner, that is effectively pushing the the bottom
108 // right corner lower, outside of the rendering space. So, we can
109 // clamp those values to the coverage's RB values. This doesn't
110 // cause the fragment shader's rendering to deform because the
111 // magic width/height values sent to the fragment shader don't take
112 // the rasterized image's size into account.
113 return Rect::MakeLTRB(entity_offset.x, entity_offset.y,
114 coverage.GetRight(), coverage.GetBottom());
115 });
116
117 Entity entity;
118 // In order to maintain precise coordinates in the fragment shader we need
119 // to eliminate the padding typically given to RenderToSnapshot results.
120 input_snapshot = anonymous_contents->RenderToSnapshot(
121 renderer, entity, {.coverage_expansion = 0});
122 if (!input_snapshot.has_value()) {
123 return std::nullopt;
124 }
125 }
126 }
127
128 // The shader is required to have at least one sampler, the first of
129 // which is treated as the input and a vec2 size uniform to compute the
130 // offsets. These are validated at the dart:ui layer, but to avoid crashes we
131 // check here too.
132 if (texture_inputs_.size() < 1 || uniforms_->size() < 8) {
134 << "Invalid fragment shader in RuntimeEffectFilterContents. "
135 << "Shader must have at least one sampler and a vec2 size uniform.";
136 return std::nullopt;
137 }
138
139 // Update uniform values.
140 std::vector<RuntimeEffectContents::TextureInput> texture_input_copy =
141 texture_inputs_;
142 texture_input_copy[0].texture = input_snapshot->texture;
143
144 Size size = Size(input_snapshot->texture->GetSize());
145 memcpy(uniforms_->data(), &size, sizeof(Size));
146
147 Matrix snapshot_transform = input_snapshot->transform;
148 //----------------------------------------------------------------------------
149 /// Create AnonymousContents for rendering.
150 ///
151 RenderProc render_proc =
152 [snapshot_transform, input_snapshot, runtime_stage = runtime_stage_,
153 uniforms = uniforms_, texture_inputs = texture_input_copy](
154 const ContentContext& renderer, const Entity& entity,
155 RenderPass& pass) -> bool {
156 RuntimeEffectContents contents;
157 FillRectGeometry geom(Rect::MakeSize(input_snapshot->texture->GetSize()));
158 contents.SetRuntimeStage(runtime_stage);
159 contents.SetUniformData(uniforms);
160 contents.SetTextureInputs(texture_inputs);
161 contents.SetGeometry(&geom);
162 Entity offset_entity = entity.Clone();
163 offset_entity.SetTransform(entity.GetTransform() * snapshot_transform);
164 return contents.Render(renderer, offset_entity, pass);
165 };
166
167 CoverageProc coverage_proc =
168 [coverage](const Entity& entity) -> std::optional<Rect> {
169 return coverage;
170 };
171
172 auto contents = AnonymousContents::Make(render_proc, coverage_proc);
173
174 Entity sub_entity;
175 sub_entity.SetContents(std::move(contents));
176 sub_entity.SetBlendMode(entity.GetBlendMode());
177 sub_entity.SetTransform(input_snapshot->transform *
178 snapshot_transform.Invert());
179
180 return sub_entity;
181}
182
183// |FilterContents|
184std::optional<Rect> RuntimeEffectFilterContents::GetFilterSourceCoverage(
185 const Matrix& effect_transform,
186 const Rect& output_limit) const {
187 return output_limit;
188}
189
190} // namespace impeller
static std::shared_ptr< Contents > Make(RenderProc render_proc, CoverageProc coverage_proc)
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition contents.h:40
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition contents.h:39
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition entity.cc:46
std::vector< FilterInput::Ref > Vector
void SetUniforms(std::shared_ptr< std::vector< uint8_t > > uniforms)
void SetTextureInputs(std::vector< RuntimeEffectContents::TextureInput > texture_inputs)
void SetRuntimeStage(std::shared_ptr< RuntimeStage > runtime_stage)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Point Vector2
Definition point.h:429
TRect< Scalar > Rect
Definition rect.h:788
TSize< Scalar > Size
Definition size.h:159
std::array< Point, 4 > Quad
Definition point.h:430
A 4x4 matrix using column-major storage.
Definition matrix.h:37
Scalar m[16]
Definition matrix.h:39
constexpr TPoint< T > GetLeftTop() const
Definition rect.h:359
constexpr TPoint< T > GetRightBottom() const
Definition rect.h:371
constexpr TPoint< T > GetLeftBottom() const
Definition rect.h:367
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
constexpr TPoint< T > GetRightTop() const
Definition rect.h:363
static constexpr std::optional< TRect > MakePointBounds(const U &value)
Definition rect.h:165
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
#define VALIDATION_LOG
Definition validation.h:91