Skip to content

Commit b3cbf05

Browse files
bderodnfield
authored andcommitted
Chainable texture filters (#67)
1 parent 1828c4c commit b3cbf05

8 files changed

+339
-5
lines changed

impeller/entity/BUILD.gn

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ impeller_component("entity") {
2929
"contents/content_context.h",
3030
"contents/contents.cc",
3131
"contents/contents.h",
32+
"contents/filter_contents.cc",
33+
"contents/filter_contents.h",
3234
"contents/linear_gradient_contents.cc",
3335
"contents/linear_gradient_contents.h",
3436
"contents/solid_color_contents.cc",
@@ -55,7 +57,6 @@ impeller_component("entity") {
5557
"../typographer",
5658
]
5759

58-
5960
deps = [
6061
"//flutter/fml",
6162

impeller/entity/contents/content_context.h

+6
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ class ContentContext {
159159
color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
160160
color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
161161
break;
162+
case Entity::BlendMode::kPlus:
163+
color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha;
164+
color0.dst_color_blend_factor = BlendFactor::kOne;
165+
color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha;
166+
color0.src_color_blend_factor = BlendFactor::kOne;
167+
break;
162168
}
163169
desc.SetColorAttachmentDescriptor(0u, std::move(color0));
164170
}
+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
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+
5+
#include "filter_contents.h"
6+
7+
#include <memory>
8+
#include <optional>
9+
#include <variant>
10+
11+
#include "flutter/fml/logging.h"
12+
#include "impeller/base/validation.h"
13+
#include "impeller/entity/contents/content_context.h"
14+
#include "impeller/entity/contents/solid_color_contents.h"
15+
#include "impeller/entity/contents/texture_contents.h"
16+
#include "impeller/entity/entity.h"
17+
#include "impeller/geometry/path_builder.h"
18+
#include "impeller/renderer/command_buffer.h"
19+
#include "impeller/renderer/render_pass.h"
20+
#include "impeller/renderer/sampler_library.h"
21+
22+
namespace impeller {
23+
24+
/*******************************************************************************
25+
******* FilterContents
26+
******************************************************************************/
27+
28+
std::shared_ptr<FilterContents> FilterContents::MakeBlend(
29+
Entity::BlendMode blend_mode,
30+
InputTextures input_textures) {
31+
auto blend = std::make_shared<BlendFilterContents>();
32+
blend->SetInputTextures(input_textures);
33+
blend->SetBlendMode(blend_mode);
34+
return blend;
35+
}
36+
37+
FilterContents::FilterContents() = default;
38+
39+
FilterContents::~FilterContents() = default;
40+
41+
void FilterContents::SetInputTextures(InputTextures input_textures) {
42+
input_textures_ = std::move(input_textures);
43+
}
44+
45+
bool FilterContents::Render(const ContentContext& renderer,
46+
const Entity& entity,
47+
RenderPass& pass) const {
48+
// Run the filter.
49+
50+
auto maybe_texture = RenderFilterToTexture(renderer, entity, pass);
51+
if (!maybe_texture.has_value()) {
52+
return false;
53+
}
54+
auto& texture = maybe_texture.value();
55+
56+
// Draw the resulting texture to the given destination rect, respecting the
57+
// transform and clip stack.
58+
59+
auto contents = std::make_shared<TextureContents>();
60+
contents->SetTexture(texture);
61+
contents->SetSourceRect(IRect::MakeSize(texture->GetSize()));
62+
63+
return contents->Render(renderer, entity, pass);
64+
}
65+
66+
std::optional<std::shared_ptr<Texture>> FilterContents::RenderFilterToTexture(
67+
const ContentContext& renderer,
68+
const Entity& entity,
69+
RenderPass& pass) const {
70+
auto output_size = GetOutputSize();
71+
if (output_size.IsZero()) {
72+
return std::nullopt;
73+
}
74+
75+
// Resolve all inputs as textures.
76+
77+
std::vector<std::shared_ptr<Texture>> input_textures;
78+
input_textures.reserve(input_textures_.size());
79+
for (const auto& input : input_textures_) {
80+
if (auto filter = std::get_if<std::shared_ptr<FilterContents>>(&input)) {
81+
auto texture =
82+
filter->get()->RenderFilterToTexture(renderer, entity, pass);
83+
if (!texture.has_value()) {
84+
return std::nullopt;
85+
}
86+
input_textures.push_back(std::move(texture.value()));
87+
} else if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
88+
input_textures.push_back(*texture);
89+
} else {
90+
FML_UNREACHABLE();
91+
}
92+
}
93+
94+
// Create a new texture and render the filter to it.
95+
96+
auto context = renderer.GetContext();
97+
98+
auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size);
99+
auto subpass_texture = subpass_target.GetRenderTargetTexture();
100+
if (!subpass_texture) {
101+
return std::nullopt;
102+
}
103+
104+
auto sub_command_buffer = context->CreateRenderCommandBuffer();
105+
sub_command_buffer->SetLabel("Offscreen Filter Command Buffer");
106+
if (!sub_command_buffer) {
107+
return std::nullopt;
108+
}
109+
110+
auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target);
111+
if (!sub_renderpass) {
112+
return std::nullopt;
113+
}
114+
sub_renderpass->SetLabel("OffscreenFilterPass");
115+
116+
if (!RenderFilter(input_textures, renderer, *sub_renderpass)) {
117+
return std::nullopt;
118+
}
119+
120+
if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) {
121+
return std::nullopt;
122+
}
123+
124+
if (!sub_command_buffer->SubmitCommands()) {
125+
return std::nullopt;
126+
}
127+
128+
return subpass_texture;
129+
}
130+
131+
ISize FilterContents::GetOutputSize() const {
132+
if (input_textures_.empty()) {
133+
return {};
134+
}
135+
136+
if (auto filter =
137+
std::get_if<std::shared_ptr<FilterContents>>(&input_textures_[0])) {
138+
return filter->get()->GetOutputSize();
139+
}
140+
141+
if (auto texture =
142+
std::get_if<std::shared_ptr<Texture>>(&input_textures_[0])) {
143+
return texture->get()->GetSize();
144+
}
145+
146+
FML_UNREACHABLE();
147+
}
148+
149+
/*******************************************************************************
150+
******* BlendFilterContents
151+
******************************************************************************/
152+
153+
BlendFilterContents::BlendFilterContents() = default;
154+
155+
BlendFilterContents::~BlendFilterContents() = default;
156+
157+
void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) {
158+
blend_mode_ = blend_mode;
159+
}
160+
161+
bool BlendFilterContents::RenderFilter(
162+
const std::vector<std::shared_ptr<Texture>>& input_textures,
163+
const ContentContext& renderer,
164+
RenderPass& pass) const {
165+
using VS = TexturePipeline::VertexShader;
166+
using FS = TexturePipeline::FragmentShader;
167+
168+
auto& host_buffer = pass.GetTransientsBuffer();
169+
170+
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
171+
vtx_builder.AddVertices({
172+
{Point(0, 0), Point(0, 0)},
173+
{Point(1, 0), Point(1, 0)},
174+
{Point(1, 1), Point(1, 1)},
175+
{Point(0, 0), Point(0, 0)},
176+
{Point(1, 1), Point(1, 1)},
177+
{Point(0, 1), Point(0, 1)},
178+
});
179+
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
180+
181+
VS::FrameInfo frame_info;
182+
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
183+
frame_info.alpha = 1;
184+
185+
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
186+
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
187+
188+
Command cmd;
189+
cmd.label = "Blend Filter";
190+
auto options = OptionsFromPass(pass);
191+
options.blend_mode = blend_mode_;
192+
cmd.pipeline = renderer.GetTexturePipeline(options);
193+
cmd.BindVertices(vtx_buffer);
194+
VS::BindFrameInfo(cmd, uniform_view);
195+
for (const auto& texture : input_textures) {
196+
FS::BindTextureSampler(cmd, texture, sampler);
197+
pass.AddCommand(cmd);
198+
}
199+
200+
return true;
201+
}
202+
203+
} // namespace impeller
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
5+
#pragma once
6+
7+
#include <memory>
8+
#include <variant>
9+
#include <vector>
10+
11+
#include "impeller/entity/contents/contents.h"
12+
#include "impeller/entity/entity.h"
13+
#include "impeller/renderer/formats.h"
14+
15+
namespace impeller {
16+
17+
/*******************************************************************************
18+
******* FilterContents
19+
******************************************************************************/
20+
21+
class FilterContents : public Contents {
22+
public:
23+
using InputVariant =
24+
std::variant<std::shared_ptr<Texture>, std::shared_ptr<FilterContents>>;
25+
using InputTextures = std::vector<InputVariant>;
26+
27+
static std::shared_ptr<FilterContents> MakeBlend(
28+
Entity::BlendMode blend_mode,
29+
InputTextures input_textures);
30+
31+
FilterContents();
32+
33+
~FilterContents() override;
34+
35+
/// @brief The input texture sources for this filter. All texture sources are
36+
/// expected to have or produce premultiplied alpha colors.
37+
/// Any input can either be a `Texture` or another `FilterContents`.
38+
///
39+
/// The number of required or optional textures depends on the
40+
/// particular filter's implementation.
41+
void SetInputTextures(InputTextures input_textures);
42+
43+
// |Contents|
44+
bool Render(const ContentContext& renderer,
45+
const Entity& entity,
46+
RenderPass& pass) const override;
47+
48+
/// @brief Renders dependency filters, creates a subpass, and calls the
49+
/// `RenderFilter` defined by the subclasses.
50+
std::optional<std::shared_ptr<Texture>> RenderFilterToTexture(
51+
const ContentContext& renderer,
52+
const Entity& entity,
53+
RenderPass& pass) const;
54+
55+
private:
56+
/// @brief Takes a set of zero or more input textures and writes to an output
57+
/// texture.
58+
virtual bool RenderFilter(
59+
const std::vector<std::shared_ptr<Texture>>& input_textures,
60+
const ContentContext& renderer,
61+
RenderPass& pass) const = 0;
62+
63+
/// @brief Determines the size of the output texture.
64+
virtual ISize GetOutputSize() const;
65+
66+
InputTextures input_textures_;
67+
Rect destination_;
68+
69+
FML_DISALLOW_COPY_AND_ASSIGN(FilterContents);
70+
};
71+
72+
/*******************************************************************************
73+
******* BlendFilterContents
74+
******************************************************************************/
75+
76+
class BlendFilterContents : public FilterContents {
77+
public:
78+
BlendFilterContents();
79+
80+
~BlendFilterContents() override;
81+
82+
void SetBlendMode(Entity::BlendMode blend_mode);
83+
84+
private:
85+
bool RenderFilter(const std::vector<std::shared_ptr<Texture>>& input_textures,
86+
const ContentContext& renderer,
87+
RenderPass& pass) const override;
88+
89+
Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver;
90+
91+
FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents);
92+
};
93+
94+
} // namespace impeller

impeller/entity/entity.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ void Entity::IncrementStencilDepth(uint32_t increment) {
6565
stencil_depth_ += increment;
6666
}
6767

68-
bool Entity::Render(ContentContext& renderer, RenderPass& parent_pass) const {
68+
bool Entity::Render(const ContentContext& renderer,
69+
RenderPass& parent_pass) const {
6970
if (!contents_) {
7071
return true;
7172
}

impeller/entity/entity.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Entity {
2727
kDestination,
2828
kSourceOver,
2929
kDestinationOver,
30+
kPlus,
3031
};
3132

3233
Entity();
@@ -57,7 +58,7 @@ class Entity {
5758

5859
uint32_t GetStencilDepth() const;
5960

60-
bool Render(ContentContext& renderer, RenderPass& parent_pass) const;
61+
bool Render(const ContentContext& renderer, RenderPass& parent_pass) const;
6162

6263
private:
6364
Matrix transformation_;

impeller/entity/entity_pass.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ std::optional<Rect> EntityPass::GetSubpassCoverage(
7575
return entities_coverage;
7676
}
7777

78-
// If the delete tells us the coverage is smaller than it needs to be, then
78+
// If the delegate tells us the coverage is smaller than it needs to be, then
7979
// great. OTOH, if the delegate is being wasteful, limit coverage to what is
8080
// actually needed.
8181
return entities_coverage->Intersection(delegate_coverage.value());

0 commit comments

Comments
 (0)