Skip to content

Commit d916e2c

Browse files
bderodnfield
authored andcommitted
Add blend filter support for advanced blends (flutter#81)
1 parent 9e33c6f commit d916e2c

11 files changed

+298
-27
lines changed

impeller/entity/BUILD.gn

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ impeller_shaders("entity_shaders") {
1414
"shaders/solid_fill.vert",
1515
"shaders/solid_stroke.frag",
1616
"shaders/solid_stroke.vert",
17+
"shaders/texture_blend.frag",
18+
"shaders/texture_blend.vert",
19+
"shaders/texture_blend_screen.frag",
20+
"shaders/texture_blend_screen.vert",
1721
"shaders/texture_fill.frag",
1822
"shaders/texture_fill.vert",
1923
"shaders/glyph_atlas.frag",

impeller/entity/contents/content_context.cc

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
1818
gradient_fill_pipelines_[{}] =
1919
std::make_unique<GradientFillPipeline>(*context_);
2020
solid_fill_pipelines_[{}] = std::make_unique<SolidFillPipeline>(*context_);
21+
texture_blend_pipelines_[{}] =
22+
std::make_unique<TextureBlendPipeline>(*context_);
23+
texture_blend_screen_pipelines_[{}] =
24+
std::make_unique<TextureBlendScreenPipeline>(*context_);
2125
texture_pipelines_[{}] = std::make_unique<TexturePipeline>(*context_);
2226
solid_stroke_pipelines_[{}] =
2327
std::make_unique<SolidStrokePipeline>(*context_);

impeller/entity/contents/content_context.h

+37-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#include "flutter/fml/hash_combine.h"
1111
#include "flutter/fml/macros.h"
12+
#include "fml/logging.h"
13+
#include "impeller/base/validation.h"
1214
#include "impeller/entity/entity.h"
1315
#include "impeller/entity/glyph_atlas.frag.h"
1416
#include "impeller/entity/glyph_atlas.vert.h"
@@ -21,13 +23,21 @@
2123
#include "impeller/entity/texture_fill.frag.h"
2224
#include "impeller/entity/texture_fill.vert.h"
2325
#include "impeller/renderer/formats.h"
26+
#include "texture_blend.frag.h"
27+
#include "texture_blend.vert.h"
28+
#include "texture_blend_screen.frag.h"
29+
#include "texture_blend_screen.vert.h"
2430

2531
namespace impeller {
2632

2733
using GradientFillPipeline =
2834
PipelineT<GradientFillVertexShader, GradientFillFragmentShader>;
2935
using SolidFillPipeline =
3036
PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
37+
using TextureBlendPipeline =
38+
PipelineT<TextureBlendVertexShader, TextureBlendFragmentShader>;
39+
using TextureBlendScreenPipeline =
40+
PipelineT<TextureBlendScreenVertexShader, TextureBlendScreenFragmentShader>;
3141
using TexturePipeline =
3242
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
3343
using SolidStrokePipeline =
@@ -75,6 +85,16 @@ class ContentContext {
7585
return GetPipeline(solid_fill_pipelines_, opts);
7686
}
7787

88+
std::shared_ptr<Pipeline> GetTextureBlendPipeline(
89+
ContentContextOptions opts) const {
90+
return GetPipeline(texture_blend_pipelines_, opts);
91+
}
92+
93+
std::shared_ptr<Pipeline> GetTextureBlendScreenPipeline(
94+
ContentContextOptions opts) const {
95+
return GetPipeline(texture_blend_screen_pipelines_, opts);
96+
}
97+
7898
std::shared_ptr<Pipeline> GetTexturePipeline(
7999
ContentContextOptions opts) const {
80100
return GetPipeline(texture_pipelines_, opts);
@@ -115,6 +135,8 @@ class ContentContext {
115135
// map.
116136
mutable Variants<GradientFillPipeline> gradient_fill_pipelines_;
117137
mutable Variants<SolidFillPipeline> solid_fill_pipelines_;
138+
mutable Variants<TextureBlendPipeline> texture_blend_pipelines_;
139+
mutable Variants<TextureBlendScreenPipeline> texture_blend_screen_pipelines_;
118140
mutable Variants<TexturePipeline> texture_pipelines_;
119141
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
120142
mutable Variants<ClipPipeline> clip_pipelines_;
@@ -123,12 +145,24 @@ class ContentContext {
123145

124146
static void ApplyOptionsToDescriptor(PipelineDescriptor& desc,
125147
const ContentContextOptions& options) {
148+
auto blend_mode = options.blend_mode;
149+
if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) {
150+
VALIDATION_LOG << "Cannot use blend mode "
151+
<< static_cast<int>(options.blend_mode)
152+
<< " as a pipeline blend.";
153+
blend_mode = Entity::BlendMode::kSourceOver;
154+
}
155+
126156
desc.SetSampleCount(options.sample_count);
127157

128158
ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u);
129159
color0.alpha_blend_op = BlendOperation::kAdd;
130160
color0.color_blend_op = BlendOperation::kAdd;
131-
switch (options.blend_mode) {
161+
162+
static_assert(Entity::BlendMode::kLastPipelineBlendMode ==
163+
Entity::BlendMode::kModulate);
164+
165+
switch (blend_mode) {
132166
case Entity::BlendMode::kClear:
133167
color0.dst_alpha_blend_factor = BlendFactor::kZero;
134168
color0.dst_color_blend_factor = BlendFactor::kZero;
@@ -214,6 +248,8 @@ class ContentContext {
214248
color0.src_alpha_blend_factor = BlendFactor::kZero;
215249
color0.src_color_blend_factor = BlendFactor::kZero;
216250
break;
251+
default:
252+
FML_UNREACHABLE();
217253
}
218254
desc.SetColorAttachmentDescriptor(0u, std::move(color0));
219255
}

impeller/entity/contents/filter_contents.cc

+164-16
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,33 @@ namespace impeller {
2828
std::shared_ptr<FilterContents> FilterContents::MakeBlend(
2929
Entity::BlendMode blend_mode,
3030
InputTextures input_textures) {
31-
auto blend = std::make_shared<BlendFilterContents>();
32-
blend->SetInputTextures(input_textures);
33-
blend->SetBlendMode(blend_mode);
34-
return blend;
31+
if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) {
32+
VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
33+
<< " passed to FilterContents::MakeBlend.";
34+
return nullptr;
35+
}
36+
37+
if (input_textures.size() < 2 ||
38+
blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) {
39+
auto blend = std::make_shared<BlendFilterContents>();
40+
blend->SetInputTextures(input_textures);
41+
blend->SetBlendMode(blend_mode);
42+
return blend;
43+
}
44+
45+
if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) {
46+
InputVariant blend = input_textures[0];
47+
for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end();
48+
in_i++) {
49+
auto new_blend = std::make_shared<BlendFilterContents>();
50+
new_blend->SetInputTextures({blend, *in_i});
51+
new_blend->SetBlendMode(blend_mode);
52+
blend = new_blend;
53+
}
54+
return std::get<std::shared_ptr<FilterContents>>(blend);
55+
}
56+
57+
FML_UNREACHABLE();
3558
}
3659

3760
FilterContents::FilterContents() = default;
@@ -150,20 +173,106 @@ ISize FilterContents::GetOutputSize() const {
150173
******* BlendFilterContents
151174
******************************************************************************/
152175

153-
BlendFilterContents::BlendFilterContents() = default;
176+
BlendFilterContents::BlendFilterContents() {
177+
SetBlendMode(Entity::BlendMode::kSourceOver);
178+
}
154179

155180
BlendFilterContents::~BlendFilterContents() = default;
156181

182+
using PipelineProc =
183+
std::shared_ptr<Pipeline> (ContentContext::*)(ContentContextOptions) const;
184+
185+
template <typename VS, typename FS>
186+
static void AdvancedBlendPass(std::shared_ptr<Texture> input_d,
187+
std::shared_ptr<Texture> input_s,
188+
std::shared_ptr<const Sampler> sampler,
189+
const ContentContext& renderer,
190+
RenderPass& pass,
191+
Command& cmd) {}
192+
193+
template <typename VS, typename FS>
194+
static bool AdvancedBlend(
195+
const std::vector<std::shared_ptr<Texture>>& input_textures,
196+
const ContentContext& renderer,
197+
RenderPass& pass,
198+
PipelineProc pipeline_proc) {
199+
if (input_textures.size() < 2) {
200+
return false;
201+
}
202+
203+
auto& host_buffer = pass.GetTransientsBuffer();
204+
205+
VertexBufferBuilder<typename VS::PerVertexData> vtx_builder;
206+
vtx_builder.AddVertices({
207+
{Point(0, 0), Point(0, 0)},
208+
{Point(1, 0), Point(1, 0)},
209+
{Point(1, 1), Point(1, 1)},
210+
{Point(0, 0), Point(0, 0)},
211+
{Point(1, 1), Point(1, 1)},
212+
{Point(0, 1), Point(0, 1)},
213+
});
214+
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
215+
216+
typename VS::FrameInfo frame_info;
217+
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
218+
219+
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
220+
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
221+
222+
auto options = OptionsFromPass(pass);
223+
options.blend_mode = Entity::BlendMode::kSource;
224+
std::shared_ptr<Pipeline> pipeline =
225+
std::invoke(pipeline_proc, renderer, options);
226+
227+
Command cmd;
228+
cmd.label = "Advanced Blend Filter";
229+
cmd.BindVertices(vtx_buffer);
230+
cmd.pipeline = std::move(pipeline);
231+
VS::BindFrameInfo(cmd, uniform_view);
232+
233+
FS::BindTextureSamplerDst(cmd, input_textures[0], sampler);
234+
FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler);
235+
pass.AddCommand(cmd);
236+
237+
return true;
238+
}
239+
157240
void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) {
241+
if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) {
242+
VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
243+
<< " assigned to BlendFilterContents.";
244+
}
245+
158246
blend_mode_ = blend_mode;
247+
248+
if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) {
249+
static_assert(Entity::BlendMode::kLastAdvancedBlendMode ==
250+
Entity::BlendMode::kScreen);
251+
252+
switch (blend_mode) {
253+
case Entity::BlendMode::kScreen:
254+
advanced_blend_proc_ =
255+
[](const std::vector<std::shared_ptr<Texture>>& input_textures,
256+
const ContentContext& renderer, RenderPass& pass) {
257+
PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline;
258+
return AdvancedBlend<TextureBlendScreenPipeline::VertexShader,
259+
TextureBlendScreenPipeline::FragmentShader>(
260+
input_textures, renderer, pass, p);
261+
};
262+
break;
263+
default:
264+
FML_UNREACHABLE();
265+
}
266+
}
159267
}
160268

161-
bool BlendFilterContents::RenderFilter(
269+
static bool BasicBlend(
162270
const std::vector<std::shared_ptr<Texture>>& input_textures,
163271
const ContentContext& renderer,
164-
RenderPass& pass) const {
165-
using VS = TexturePipeline::VertexShader;
166-
using FS = TexturePipeline::FragmentShader;
272+
RenderPass& pass,
273+
Entity::BlendMode basic_blend) {
274+
using VS = TextureBlendPipeline::VertexShader;
275+
using FS = TextureBlendPipeline::FragmentShader;
167276

168277
auto& host_buffer = pass.GetTransientsBuffer();
169278

@@ -180,24 +289,63 @@ bool BlendFilterContents::RenderFilter(
180289

181290
VS::FrameInfo frame_info;
182291
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
183-
frame_info.alpha = 1;
184292

185293
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
186294
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
187295

296+
// Draw the first texture using kSource.
297+
188298
Command cmd;
189-
cmd.label = "Blend Filter";
190-
auto options = OptionsFromPass(pass);
191-
options.blend_mode = blend_mode_;
192-
cmd.pipeline = renderer.GetTexturePipeline(options);
299+
cmd.label = "Basic Blend Filter";
193300
cmd.BindVertices(vtx_buffer);
301+
auto options = OptionsFromPass(pass);
302+
options.blend_mode = Entity::BlendMode::kSource;
303+
cmd.pipeline = renderer.GetTextureBlendPipeline(options);
304+
FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler);
194305
VS::BindFrameInfo(cmd, uniform_view);
195-
for (const auto& texture : input_textures) {
196-
FS::BindTextureSampler(cmd, texture, sampler);
306+
pass.AddCommand(cmd);
307+
308+
if (input_textures.size() < 2) {
309+
return true;
310+
}
311+
312+
// Write subsequent textures using the selected blend mode.
313+
314+
options.blend_mode = basic_blend;
315+
cmd.pipeline = renderer.GetTextureBlendPipeline(options);
316+
317+
for (auto texture_i = input_textures.begin() + 1;
318+
texture_i < input_textures.end(); texture_i++) {
319+
FS::BindTextureSamplerSrc(cmd, *texture_i, sampler);
197320
pass.AddCommand(cmd);
198321
}
199322

200323
return true;
201324
}
202325

326+
bool BlendFilterContents::RenderFilter(
327+
const std::vector<std::shared_ptr<Texture>>& input_textures,
328+
const ContentContext& renderer,
329+
RenderPass& pass) const {
330+
if (input_textures.empty()) {
331+
return true;
332+
}
333+
334+
if (input_textures.size() == 1) {
335+
// Nothing to blend.
336+
return BasicBlend(input_textures, renderer, pass,
337+
Entity::BlendMode::kSource);
338+
}
339+
340+
if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) {
341+
return BasicBlend(input_textures, renderer, pass, blend_mode_);
342+
}
343+
344+
if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) {
345+
return advanced_blend_proc_(input_textures, renderer, pass);
346+
}
347+
348+
FML_UNREACHABLE();
349+
}
350+
203351
} // namespace impeller

impeller/entity/contents/filter_contents.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
namespace impeller {
1616

17+
class Pipeline;
18+
1719
/*******************************************************************************
1820
******* FilterContents
1921
******************************************************************************/
@@ -75,6 +77,11 @@ class FilterContents : public Contents {
7577

7678
class BlendFilterContents : public FilterContents {
7779
public:
80+
using AdvancedBlendProc = std::function<bool(
81+
const std::vector<std::shared_ptr<Texture>>& input_textures,
82+
const ContentContext& renderer,
83+
RenderPass& pass)>;
84+
7885
BlendFilterContents();
7986

8087
~BlendFilterContents() override;
@@ -86,7 +93,8 @@ class BlendFilterContents : public FilterContents {
8693
const ContentContext& renderer,
8794
RenderPass& pass) const override;
8895

89-
Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver;
96+
Entity::BlendMode blend_mode_;
97+
AdvancedBlendProc advanced_blend_proc_;
9098

9199
FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents);
92100
};

0 commit comments

Comments
 (0)