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

Commit 176e277

Browse files
author
Jonah Williams
authored
[Impeller] Improve atlas blending performance by reducing size of subpass. (#39669)
[Impeller] Improve atlas blending performance by reducing size of subpass.
1 parent ca173b4 commit 176e277

File tree

6 files changed

+287
-17
lines changed

6 files changed

+287
-17
lines changed

impeller/display_list/display_list_unittests.cc

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,8 @@ namespace impeller {
3535
namespace testing {
3636

3737
flutter::DlColor toColor(const float* components) {
38-
auto value = (((std::lround(components[3] * 255) & 0xff) << 24) |
39-
((std::lround(components[0] * 255) & 0xff) << 16) |
40-
((std::lround(components[1] * 255) & 0xff) << 8) |
41-
((std::lround(components[2] * 255) & 0xff) << 0)) &
42-
0xFFFFFFFF;
43-
return flutter::DlColor(value);
38+
return flutter::DlColor(Color::ToIColor(
39+
Color(components[0], components[1], components[2], components[3])));
4440
}
4541

4642
using DisplayListTest = DisplayListPlayground;

impeller/entity/contents/atlas_contents.cc

Lines changed: 176 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
// found in the LICENSE file.
44

55
#include <optional>
6+
#include <unordered_map>
67
#include <utility>
78

9+
#include "flutter/fml/macros.h"
10+
811
#include "impeller/entity/contents/atlas_contents.h"
912
#include "impeller/entity/contents/content_context.h"
1013
#include "impeller/entity/contents/filters/color_filter_contents.h"
@@ -19,6 +22,10 @@
1922
#include "impeller/renderer/sampler_library.h"
2023
#include "impeller/renderer/vertex_buffer_builder.h"
2124

25+
#ifdef FML_OS_PHYSICAL_IOS
26+
#include "impeller/entity/contents/framebuffer_blend_contents.h"
27+
#endif
28+
2229
namespace impeller {
2330

2431
AtlasContents::AtlasContents() = default;
@@ -59,6 +66,87 @@ void AtlasContents::SetCullRect(std::optional<Rect> cull_rect) {
5966
cull_rect_ = cull_rect;
6067
}
6168

69+
struct AtlasBlenderKey {
70+
Color color;
71+
Rect rect;
72+
uint32_t color_key;
73+
74+
struct Hash {
75+
std::size_t operator()(const AtlasBlenderKey& key) const {
76+
return fml::HashCombine(key.color_key, key.rect.size.width,
77+
key.rect.size.height, key.rect.origin.x,
78+
key.rect.origin.y);
79+
}
80+
};
81+
82+
struct Equal {
83+
bool operator()(const AtlasBlenderKey& lhs,
84+
const AtlasBlenderKey& rhs) const {
85+
return lhs.rect == rhs.rect && lhs.color_key == rhs.color_key;
86+
}
87+
};
88+
};
89+
90+
std::shared_ptr<SubAtlasResult> AtlasContents::GenerateSubAtlas() const {
91+
FML_DCHECK(colors_.size() > 0 && blend_mode_ != BlendMode::kSource &&
92+
blend_mode_ != BlendMode::kDestination);
93+
94+
std::unordered_map<AtlasBlenderKey, std::vector<Matrix>,
95+
AtlasBlenderKey::Hash, AtlasBlenderKey::Equal>
96+
sub_atlas = {};
97+
98+
for (auto i = 0u; i < texture_coords_.size(); i++) {
99+
AtlasBlenderKey key = {.color = colors_[i],
100+
.rect = texture_coords_[i],
101+
.color_key = Color::ToIColor(colors_[i])};
102+
if (sub_atlas.find(key) == sub_atlas.end()) {
103+
sub_atlas[key] = {transforms_[i]};
104+
} else {
105+
sub_atlas[key].push_back(transforms_[i]);
106+
}
107+
}
108+
109+
auto result = std::make_shared<SubAtlasResult>();
110+
Scalar x_offset = 0.0;
111+
Scalar y_offset = 0.0;
112+
Scalar x_extent = 0.0;
113+
Scalar y_extent = 0.0;
114+
115+
for (auto it = sub_atlas.begin(); it != sub_atlas.end(); it++) {
116+
// This size was arbitrarily chosen to keep the textures from getting too
117+
// wide. We could instead use a more generic rect packer but in the majority
118+
// of cases the sample rects will be fairly close in size making this a good
119+
// enough approximation.
120+
if (x_offset >= 1000) {
121+
y_offset = y_extent + 1;
122+
x_offset = 0.0;
123+
}
124+
125+
auto key = it->first;
126+
auto transforms = it->second;
127+
128+
auto new_rect = Rect::MakeXYWH(x_offset, y_offset, key.rect.size.width,
129+
key.rect.size.height);
130+
auto sub_transform = Matrix::MakeTranslation(Vector2(x_offset, y_offset));
131+
132+
x_offset += std::ceil(key.rect.size.width) + 1.0;
133+
134+
result->sub_texture_coords.push_back(key.rect);
135+
result->sub_colors.push_back(key.color);
136+
result->sub_transforms.push_back(sub_transform);
137+
138+
x_extent = std::max(x_extent, x_offset);
139+
y_extent = std::max(y_extent, std::ceil(y_offset + key.rect.size.height));
140+
141+
for (auto transform : transforms) {
142+
result->result_texture_coords.push_back(new_rect);
143+
result->result_transforms.push_back(transform);
144+
}
145+
}
146+
result->size = ISize(std::ceil(x_extent), std::ceil(y_extent));
147+
return result;
148+
}
149+
62150
std::optional<Rect> AtlasContents::GetCoverage(const Entity& entity) const {
63151
if (cull_rect_.has_value()) {
64152
return cull_rect_.value().TransformBounds(entity.GetTransformation());
@@ -125,17 +213,56 @@ bool AtlasContents::Render(const ContentContext& renderer,
125213
return child_contents.Render(renderer, entity, pass);
126214
}
127215

216+
auto sub_atlas = GenerateSubAtlas();
217+
auto sub_coverage = Rect::MakeSize(sub_atlas->size);
218+
128219
auto src_contents = std::make_shared<AtlasTextureContents>(*this);
129-
src_contents->SetCoverage(coverage);
220+
src_contents->SetSubAtlas(sub_atlas);
221+
src_contents->SetCoverage(sub_coverage);
130222

131223
auto dst_contents = std::make_shared<AtlasColorContents>(*this);
132-
dst_contents->SetCoverage(coverage);
133-
224+
dst_contents->SetSubAtlas(sub_atlas);
225+
dst_contents->SetCoverage(sub_coverage);
226+
227+
#ifdef FML_OS_PHYSICAL_IOS
228+
auto new_texture = renderer.MakeSubpass(
229+
sub_atlas->size, [&](const ContentContext& context, RenderPass& pass) {
230+
Entity entity;
231+
entity.SetContents(dst_contents);
232+
entity.SetBlendMode(BlendMode::kSource);
233+
if (!entity.Render(context, pass)) {
234+
return false;
235+
}
236+
if (blend_mode_ >= Entity::kLastPipelineBlendMode) {
237+
auto contents = std::make_shared<FramebufferBlendContents>();
238+
contents->SetBlendMode(blend_mode_);
239+
contents->SetChildContents(src_contents);
240+
entity.SetContents(std::move(contents));
241+
entity.SetBlendMode(BlendMode::kSource);
242+
return entity.Render(context, pass);
243+
}
244+
entity.SetContents(src_contents);
245+
entity.SetBlendMode(blend_mode_);
246+
return entity.Render(context, pass);
247+
});
248+
#else
134249
auto contents = ColorFilterContents::MakeBlend(
135250
blend_mode_,
136251
{FilterInput::Make(dst_contents), FilterInput::Make(src_contents)});
137-
contents->SetAlpha(alpha_);
138-
return contents->Render(renderer, entity, pass);
252+
auto snapshot = contents->RenderToSnapshot(renderer, entity);
253+
if (!snapshot.has_value()) {
254+
return false;
255+
}
256+
auto new_texture = snapshot.value().texture;
257+
#endif
258+
259+
auto child_contents = AtlasTextureContents(*this);
260+
child_contents.SetAlpha(alpha_);
261+
child_contents.SetCoverage(coverage);
262+
child_contents.SetTexture(new_texture);
263+
child_contents.SetUseDestination(true);
264+
child_contents.SetSubAtlas(sub_atlas);
265+
return child_contents.Render(renderer, entity, pass);
139266
}
140267

141268
// AtlasTextureContents
@@ -159,15 +286,38 @@ void AtlasTextureContents::SetCoverage(Rect coverage) {
159286
coverage_ = coverage;
160287
}
161288

289+
void AtlasTextureContents::SetUseDestination(bool value) {
290+
use_destination_ = value;
291+
}
292+
293+
void AtlasTextureContents::SetSubAtlas(
294+
const std::shared_ptr<SubAtlasResult>& subatlas) {
295+
subatlas_ = subatlas;
296+
}
297+
298+
void AtlasTextureContents::SetTexture(std::shared_ptr<Texture> texture) {
299+
texture_ = std::move(texture);
300+
}
301+
162302
bool AtlasTextureContents::Render(const ContentContext& renderer,
163303
const Entity& entity,
164304
RenderPass& pass) const {
165305
using VS = TextureFillVertexShader;
166306
using FS = TextureFillFragmentShader;
167307

168-
auto texture = parent_.GetTexture();
169-
auto texture_coords = parent_.GetTextureCoordinates();
170-
auto transforms = parent_.GetTransforms();
308+
auto texture = texture_.value_or(parent_.GetTexture());
309+
std::vector<Rect> texture_coords;
310+
std::vector<Matrix> transforms;
311+
if (subatlas_.has_value()) {
312+
auto subatlas = subatlas_.value();
313+
texture_coords = use_destination_ ? subatlas->result_texture_coords
314+
: subatlas->sub_texture_coords;
315+
transforms = use_destination_ ? subatlas->result_transforms
316+
: subatlas->sub_transforms;
317+
} else {
318+
texture_coords = parent_.GetTextureCoordinates();
319+
transforms = parent_.GetTransforms();
320+
}
171321

172322
const Size texture_size(texture->GetSize());
173323
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
@@ -242,15 +392,30 @@ void AtlasColorContents::SetCoverage(Rect coverage) {
242392
coverage_ = coverage;
243393
}
244394

395+
void AtlasColorContents::SetSubAtlas(
396+
const std::shared_ptr<SubAtlasResult>& subatlas) {
397+
subatlas_ = subatlas;
398+
}
399+
245400
bool AtlasColorContents::Render(const ContentContext& renderer,
246401
const Entity& entity,
247402
RenderPass& pass) const {
248403
using VS = GeometryColorPipeline::VertexShader;
249404
using FS = GeometryColorPipeline::FragmentShader;
250405

251-
auto texture_coords = parent_.GetTextureCoordinates();
252-
auto transforms = parent_.GetTransforms();
253-
auto colors = parent_.GetColors();
406+
std::vector<Rect> texture_coords;
407+
std::vector<Matrix> transforms;
408+
std::vector<Color> colors;
409+
if (subatlas_.has_value()) {
410+
auto subatlas = subatlas_.value();
411+
texture_coords = subatlas->sub_texture_coords;
412+
colors = subatlas->sub_colors;
413+
transforms = subatlas->sub_transforms;
414+
} else {
415+
texture_coords = parent_.GetTextureCoordinates();
416+
transforms = parent_.GetTransforms();
417+
colors = parent_.GetColors();
418+
}
254419

255420
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
256421
vertex_builder.Reserve(texture_coords.size() * 6);

impeller/entity/contents/atlas_contents.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@
1515

1616
namespace impeller {
1717

18+
struct SubAtlasResult {
19+
// Sub atlas values.
20+
std::vector<Rect> sub_texture_coords;
21+
std::vector<Color> sub_colors;
22+
std::vector<Matrix> sub_transforms;
23+
24+
// Result atlas values.
25+
std::vector<Rect> result_texture_coords;
26+
std::vector<Matrix> result_transforms;
27+
28+
// Size of the sub-atlass.
29+
ISize size;
30+
};
31+
1832
class AtlasContents final : public Contents {
1933
public:
2034
explicit AtlasContents();
@@ -47,6 +61,11 @@ class AtlasContents final : public Contents {
4761

4862
const std::vector<Color>& GetColors() const;
4963

64+
/// @brief Compress a drawAtlas call with blending into a smaller sized atlas.
65+
/// This atlas has no overlapping to ensure
66+
/// blending behaves as if it were done in the fragment shader.
67+
std::shared_ptr<SubAtlasResult> GenerateSubAtlas() const;
68+
5069
// |Contents|
5170
std::optional<Rect> GetCoverage(const Entity& entity) const override;
5271

@@ -89,10 +108,19 @@ class AtlasTextureContents final : public Contents {
89108

90109
void SetCoverage(Rect coverage);
91110

111+
void SetTexture(std::shared_ptr<Texture> texture);
112+
113+
void SetUseDestination(bool value);
114+
115+
void SetSubAtlas(const std::shared_ptr<SubAtlasResult>& subatlas);
116+
92117
private:
93118
const AtlasContents& parent_;
94119
Scalar alpha_ = 1.0;
95120
Rect coverage_;
121+
std::optional<std::shared_ptr<Texture>> texture_;
122+
bool use_destination_ = false;
123+
std::optional<std::shared_ptr<SubAtlasResult>> subatlas_ = std::nullopt;
96124

97125
FML_DISALLOW_COPY_AND_ASSIGN(AtlasTextureContents);
98126
};
@@ -115,10 +143,13 @@ class AtlasColorContents final : public Contents {
115143

116144
void SetCoverage(Rect coverage);
117145

146+
void SetSubAtlas(const std::shared_ptr<SubAtlasResult>& subatlas);
147+
118148
private:
119149
const AtlasContents& parent_;
120150
Scalar alpha_ = 1.0;
121151
Rect coverage_;
152+
std::optional<std::shared_ptr<SubAtlasResult>> subatlas_ = std::nullopt;
122153

123154
FML_DISALLOW_COPY_AND_ASSIGN(AtlasColorContents);
124155
};

0 commit comments

Comments
 (0)