Skip to content

Commit

Permalink
Add blend filter support for advanced blends (flutter#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored and dnfield committed Apr 27, 2022
1 parent 9e33c6f commit d916e2c
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 27 deletions.
4 changes: 4 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ impeller_shaders("entity_shaders") {
"shaders/solid_fill.vert",
"shaders/solid_stroke.frag",
"shaders/solid_stroke.vert",
"shaders/texture_blend.frag",
"shaders/texture_blend.vert",
"shaders/texture_blend_screen.frag",
"shaders/texture_blend_screen.vert",
"shaders/texture_fill.frag",
"shaders/texture_fill.vert",
"shaders/glyph_atlas.frag",
Expand Down
4 changes: 4 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
gradient_fill_pipelines_[{}] =
std::make_unique<GradientFillPipeline>(*context_);
solid_fill_pipelines_[{}] = std::make_unique<SolidFillPipeline>(*context_);
texture_blend_pipelines_[{}] =
std::make_unique<TextureBlendPipeline>(*context_);
texture_blend_screen_pipelines_[{}] =
std::make_unique<TextureBlendScreenPipeline>(*context_);
texture_pipelines_[{}] = std::make_unique<TexturePipeline>(*context_);
solid_stroke_pipelines_[{}] =
std::make_unique<SolidStrokePipeline>(*context_);
Expand Down
38 changes: 37 additions & 1 deletion impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"
#include "fml/logging.h"
#include "impeller/base/validation.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/glyph_atlas.frag.h"
#include "impeller/entity/glyph_atlas.vert.h"
Expand All @@ -21,13 +23,21 @@
#include "impeller/entity/texture_fill.frag.h"
#include "impeller/entity/texture_fill.vert.h"
#include "impeller/renderer/formats.h"
#include "texture_blend.frag.h"
#include "texture_blend.vert.h"
#include "texture_blend_screen.frag.h"
#include "texture_blend_screen.vert.h"

namespace impeller {

using GradientFillPipeline =
PipelineT<GradientFillVertexShader, GradientFillFragmentShader>;
using SolidFillPipeline =
PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
using TextureBlendPipeline =
PipelineT<TextureBlendVertexShader, TextureBlendFragmentShader>;
using TextureBlendScreenPipeline =
PipelineT<TextureBlendScreenVertexShader, TextureBlendScreenFragmentShader>;
using TexturePipeline =
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
using SolidStrokePipeline =
Expand Down Expand Up @@ -75,6 +85,16 @@ class ContentContext {
return GetPipeline(solid_fill_pipelines_, opts);
}

std::shared_ptr<Pipeline> GetTextureBlendPipeline(
ContentContextOptions opts) const {
return GetPipeline(texture_blend_pipelines_, opts);
}

std::shared_ptr<Pipeline> GetTextureBlendScreenPipeline(
ContentContextOptions opts) const {
return GetPipeline(texture_blend_screen_pipelines_, opts);
}

std::shared_ptr<Pipeline> GetTexturePipeline(
ContentContextOptions opts) const {
return GetPipeline(texture_pipelines_, opts);
Expand Down Expand Up @@ -115,6 +135,8 @@ class ContentContext {
// map.
mutable Variants<GradientFillPipeline> gradient_fill_pipelines_;
mutable Variants<SolidFillPipeline> solid_fill_pipelines_;
mutable Variants<TextureBlendPipeline> texture_blend_pipelines_;
mutable Variants<TextureBlendScreenPipeline> texture_blend_screen_pipelines_;
mutable Variants<TexturePipeline> texture_pipelines_;
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
mutable Variants<ClipPipeline> clip_pipelines_;
Expand All @@ -123,12 +145,24 @@ class ContentContext {

static void ApplyOptionsToDescriptor(PipelineDescriptor& desc,
const ContentContextOptions& options) {
auto blend_mode = options.blend_mode;
if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) {
VALIDATION_LOG << "Cannot use blend mode "
<< static_cast<int>(options.blend_mode)
<< " as a pipeline blend.";
blend_mode = Entity::BlendMode::kSourceOver;
}

desc.SetSampleCount(options.sample_count);

ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u);
color0.alpha_blend_op = BlendOperation::kAdd;
color0.color_blend_op = BlendOperation::kAdd;
switch (options.blend_mode) {

static_assert(Entity::BlendMode::kLastPipelineBlendMode ==
Entity::BlendMode::kModulate);

switch (blend_mode) {
case Entity::BlendMode::kClear:
color0.dst_alpha_blend_factor = BlendFactor::kZero;
color0.dst_color_blend_factor = BlendFactor::kZero;
Expand Down Expand Up @@ -214,6 +248,8 @@ class ContentContext {
color0.src_alpha_blend_factor = BlendFactor::kZero;
color0.src_color_blend_factor = BlendFactor::kZero;
break;
default:
FML_UNREACHABLE();
}
desc.SetColorAttachmentDescriptor(0u, std::move(color0));
}
Expand Down
180 changes: 164 additions & 16 deletions impeller/entity/contents/filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,33 @@ namespace impeller {
std::shared_ptr<FilterContents> FilterContents::MakeBlend(
Entity::BlendMode blend_mode,
InputTextures input_textures) {
auto blend = std::make_shared<BlendFilterContents>();
blend->SetInputTextures(input_textures);
blend->SetBlendMode(blend_mode);
return blend;
if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) {
VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
<< " passed to FilterContents::MakeBlend.";
return nullptr;
}

if (input_textures.size() < 2 ||
blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) {
auto blend = std::make_shared<BlendFilterContents>();
blend->SetInputTextures(input_textures);
blend->SetBlendMode(blend_mode);
return blend;
}

if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) {
InputVariant blend = input_textures[0];
for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end();
in_i++) {
auto new_blend = std::make_shared<BlendFilterContents>();
new_blend->SetInputTextures({blend, *in_i});
new_blend->SetBlendMode(blend_mode);
blend = new_blend;
}
return std::get<std::shared_ptr<FilterContents>>(blend);
}

FML_UNREACHABLE();
}

FilterContents::FilterContents() = default;
Expand Down Expand Up @@ -150,20 +173,106 @@ ISize FilterContents::GetOutputSize() const {
******* BlendFilterContents
******************************************************************************/

BlendFilterContents::BlendFilterContents() = default;
BlendFilterContents::BlendFilterContents() {
SetBlendMode(Entity::BlendMode::kSourceOver);
}

BlendFilterContents::~BlendFilterContents() = default;

using PipelineProc =
std::shared_ptr<Pipeline> (ContentContext::*)(ContentContextOptions) const;

template <typename VS, typename FS>
static void AdvancedBlendPass(std::shared_ptr<Texture> input_d,
std::shared_ptr<Texture> input_s,
std::shared_ptr<const Sampler> sampler,
const ContentContext& renderer,
RenderPass& pass,
Command& cmd) {}

template <typename VS, typename FS>
static bool AdvancedBlend(
const std::vector<std::shared_ptr<Texture>>& input_textures,
const ContentContext& renderer,
RenderPass& pass,
PipelineProc pipeline_proc) {
if (input_textures.size() < 2) {
return false;
}

auto& host_buffer = pass.GetTransientsBuffer();

VertexBufferBuilder<typename VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{Point(0, 0), Point(0, 0)},
{Point(1, 0), Point(1, 0)},
{Point(1, 1), Point(1, 1)},
{Point(0, 0), Point(0, 0)},
{Point(1, 1), Point(1, 1)},
{Point(0, 1), Point(0, 1)},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);

typename VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));

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

auto options = OptionsFromPass(pass);
options.blend_mode = Entity::BlendMode::kSource;
std::shared_ptr<Pipeline> pipeline =
std::invoke(pipeline_proc, renderer, options);

Command cmd;
cmd.label = "Advanced Blend Filter";
cmd.BindVertices(vtx_buffer);
cmd.pipeline = std::move(pipeline);
VS::BindFrameInfo(cmd, uniform_view);

FS::BindTextureSamplerDst(cmd, input_textures[0], sampler);
FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler);
pass.AddCommand(cmd);

return true;
}

void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) {
if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) {
VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
<< " assigned to BlendFilterContents.";
}

blend_mode_ = blend_mode;

if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) {
static_assert(Entity::BlendMode::kLastAdvancedBlendMode ==
Entity::BlendMode::kScreen);

switch (blend_mode) {
case Entity::BlendMode::kScreen:
advanced_blend_proc_ =
[](const std::vector<std::shared_ptr<Texture>>& input_textures,
const ContentContext& renderer, RenderPass& pass) {
PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline;
return AdvancedBlend<TextureBlendScreenPipeline::VertexShader,
TextureBlendScreenPipeline::FragmentShader>(
input_textures, renderer, pass, p);
};
break;
default:
FML_UNREACHABLE();
}
}
}

bool BlendFilterContents::RenderFilter(
static bool BasicBlend(
const std::vector<std::shared_ptr<Texture>>& input_textures,
const ContentContext& renderer,
RenderPass& pass) const {
using VS = TexturePipeline::VertexShader;
using FS = TexturePipeline::FragmentShader;
RenderPass& pass,
Entity::BlendMode basic_blend) {
using VS = TextureBlendPipeline::VertexShader;
using FS = TextureBlendPipeline::FragmentShader;

auto& host_buffer = pass.GetTransientsBuffer();

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

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
frame_info.alpha = 1;

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

// Draw the first texture using kSource.

Command cmd;
cmd.label = "Blend Filter";
auto options = OptionsFromPass(pass);
options.blend_mode = blend_mode_;
cmd.pipeline = renderer.GetTexturePipeline(options);
cmd.label = "Basic Blend Filter";
cmd.BindVertices(vtx_buffer);
auto options = OptionsFromPass(pass);
options.blend_mode = Entity::BlendMode::kSource;
cmd.pipeline = renderer.GetTextureBlendPipeline(options);
FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler);
VS::BindFrameInfo(cmd, uniform_view);
for (const auto& texture : input_textures) {
FS::BindTextureSampler(cmd, texture, sampler);
pass.AddCommand(cmd);

if (input_textures.size() < 2) {
return true;
}

// Write subsequent textures using the selected blend mode.

options.blend_mode = basic_blend;
cmd.pipeline = renderer.GetTextureBlendPipeline(options);

for (auto texture_i = input_textures.begin() + 1;
texture_i < input_textures.end(); texture_i++) {
FS::BindTextureSamplerSrc(cmd, *texture_i, sampler);
pass.AddCommand(cmd);
}

return true;
}

bool BlendFilterContents::RenderFilter(
const std::vector<std::shared_ptr<Texture>>& input_textures,
const ContentContext& renderer,
RenderPass& pass) const {
if (input_textures.empty()) {
return true;
}

if (input_textures.size() == 1) {
// Nothing to blend.
return BasicBlend(input_textures, renderer, pass,
Entity::BlendMode::kSource);
}

if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) {
return BasicBlend(input_textures, renderer, pass, blend_mode_);
}

if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) {
return advanced_blend_proc_(input_textures, renderer, pass);
}

FML_UNREACHABLE();
}

} // namespace impeller
10 changes: 9 additions & 1 deletion impeller/entity/contents/filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

namespace impeller {

class Pipeline;

/*******************************************************************************
******* FilterContents
******************************************************************************/
Expand Down Expand Up @@ -75,6 +77,11 @@ class FilterContents : public Contents {

class BlendFilterContents : public FilterContents {
public:
using AdvancedBlendProc = std::function<bool(
const std::vector<std::shared_ptr<Texture>>& input_textures,
const ContentContext& renderer,
RenderPass& pass)>;

BlendFilterContents();

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

Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver;
Entity::BlendMode blend_mode_;
AdvancedBlendProc advanced_blend_proc_;

FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents);
};
Expand Down
Loading

0 comments on commit d916e2c

Please sign in to comment.