Skip to content

Commit

Permalink
Add pipeline blend modes & demo (flutter#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored Mar 8, 2022
1 parent 46f95c3 commit d0b9f21
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 8 deletions.
2 changes: 1 addition & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame,
auto text_contents = std::make_shared<TextContents>();
text_contents->SetTextFrame(std::move(text_frame));
text_contents->SetGlyphAtlas(std::move(atlas));
text_contents->SetColor(paint.color);
text_contents->SetColor(paint.color.Premultiply());

Entity entity;
entity.SetTransformation(GetCurrentTransformation() *
Expand Down
4 changes: 2 additions & 2 deletions impeller/aiks/paint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ std::shared_ptr<Contents> Paint::CreateContentsForEntity() const {
switch (style) {
case Style::kFill: {
auto solid_color = std::make_shared<SolidColorContents>();
solid_color->SetColor(color);
solid_color->SetColor(color.Premultiply());
return solid_color;
}
case Style::kStroke: {
auto solid_stroke = std::make_shared<SolidStrokeContents>();
solid_stroke->SetColor(color);
solid_stroke->SetColor(color.Premultiply());
solid_stroke->SetStrokeSize(stroke_width);
return solid_stroke;
}
Expand Down
45 changes: 43 additions & 2 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "flutter/impeller/entity/solid_stroke.vert.h"
#include "flutter/impeller/entity/texture_fill.frag.h"
#include "flutter/impeller/entity/texture_fill.vert.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/formats.h"

namespace impeller {

Expand All @@ -38,17 +40,19 @@ using ClipPipeline = PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;

struct ContentContextOptions {
SampleCount sample_count = SampleCount::kCount1;
Entity::BlendMode blend_mode = Entity::BlendMode::kSource;

struct Hash {
constexpr std::size_t operator()(const ContentContextOptions& o) const {
return fml::HashCombine(o.sample_count);
return fml::HashCombine(o.sample_count, o.blend_mode);
}
};

struct Equal {
constexpr bool operator()(const ContentContextOptions& lhs,
const ContentContextOptions& rhs) const {
return lhs.sample_count == rhs.sample_count;
return lhs.sample_count == rhs.sample_count &&
lhs.blend_mode == rhs.blend_mode;
}
};
};
Expand Down Expand Up @@ -120,6 +124,43 @@ class ContentContext {
static void ApplyOptionsToDescriptor(PipelineDescriptor& desc,
const ContentContextOptions& options) {
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) {
case Entity::BlendMode::kClear:
color0.dst_alpha_blend_factor = BlendFactor::kZero;
color0.dst_color_blend_factor = BlendFactor::kZero;
color0.src_alpha_blend_factor = BlendFactor::kZero;
color0.src_color_blend_factor = BlendFactor::kZero;
break;
case Entity::BlendMode::kSource:
color0.dst_alpha_blend_factor = BlendFactor::kZero;
color0.dst_color_blend_factor = BlendFactor::kZero;
color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha;
color0.src_color_blend_factor = BlendFactor::kOne;
break;
case Entity::BlendMode::kDestination:
color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha;
color0.dst_color_blend_factor = BlendFactor::kOne;
color0.src_alpha_blend_factor = BlendFactor::kZero;
color0.src_color_blend_factor = BlendFactor::kZero;
break;
case Entity::BlendMode::kSourceOver:
color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha;
color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha;
color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha;
color0.src_color_blend_factor = BlendFactor::kOne;
break;
case Entity::BlendMode::kDestinationOver:
color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha;
color0.dst_color_blend_factor = BlendFactor::kOne;
color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
break;
}
desc.SetColorAttachmentDescriptor(0u, std::move(color0));
}

template <class TypedPipeline>
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ class RenderPass;

class Entity {
public:
/// All pipeline blend mode presets assume that both the source (fragment
/// output) and destination (first color attachment) have colors with
/// premultiplied alpha.
enum class BlendMode {
kClear,
kSource,
kDestination,
kSourceOver,
kDestinationOver,
};

Entity();

~Entity();
Expand Down
116 changes: 113 additions & 3 deletions impeller/entity/entity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "impeller/geometry/path_builder.h"
#include "impeller/playground/playground.h"
#include "impeller/playground/widgets.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/vertex_buffer_builder.h"
#include "third_party/imgui/imgui.h"

namespace impeller {
Expand Down Expand Up @@ -42,7 +44,7 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) {
Entity entity;
entity.SetPath(path);
auto contents = std::make_unique<SolidStrokeContents>();
contents->SetColor(Color::Red());
contents->SetColor(Color::Red().Premultiply());
contents->SetStrokeSize(5.0);
entity.SetContents(std::move(contents));
ASSERT_TRUE(OpenPlaygroundHere(entity));
Expand Down Expand Up @@ -72,7 +74,7 @@ TEST_F(EntityTest, TriangleInsideASquare) {
Entity entity;
entity.SetPath(path);
auto contents = std::make_unique<SolidStrokeContents>();
contents->SetColor(Color::Red());
contents->SetColor(Color::Red().Premultiply());
contents->SetStrokeSize(20.0);
entity.SetContents(std::move(contents));

Expand Down Expand Up @@ -110,7 +112,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
auto create_contents = [width = width](SolidStrokeContents::Cap cap,
SolidStrokeContents::Join join) {
auto contents = std::make_unique<SolidStrokeContents>();
contents->SetColor(Color::Red());
contents->SetColor(Color::Red().Premultiply());
contents->SetStrokeSize(width);
contents->SetStrokeCap(cap);
contents->SetStrokeJoin(join);
Expand Down Expand Up @@ -491,5 +493,113 @@ TEST_F(EntityTest, SolidStrokeContentsSetMiter) {
ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8);
}

TEST_F(EntityTest, BlendingModeOptions) {
std::vector<const char*> blend_mode_names;
std::vector<Entity::BlendMode> blend_mode_values;
{
// Force an exhausiveness check with a switch. When adding blend modes,
// update this switch with a new name/value to to make it selectable in the
// test GUI.

const Entity::BlendMode b{};
static_assert(
b == Entity::BlendMode::kClear); // Ensure the first item in
// the switch is the first
// item in the enum.
switch (b) {
case Entity::BlendMode::kClear:
blend_mode_names.push_back("Clear");
blend_mode_values.push_back(Entity::BlendMode::kClear);
case Entity::BlendMode::kSource:
blend_mode_names.push_back("Source");
blend_mode_values.push_back(Entity::BlendMode::kSource);
case Entity::BlendMode::kDestination:
blend_mode_names.push_back("Destination");
blend_mode_values.push_back(Entity::BlendMode::kDestination);
case Entity::BlendMode::kSourceOver:
blend_mode_names.push_back("SourceOver");
blend_mode_values.push_back(Entity::BlendMode::kSourceOver);
case Entity::BlendMode::kDestinationOver:
blend_mode_names.push_back("DestinationOver");
blend_mode_values.push_back(
Entity::BlendMode::kDestinationOver);
};
}

bool first_frame = true;
auto callback = [&](ContentContext& context, RenderPass& pass) {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowSize({350, 200});
ImGui::SetNextWindowPos({200, 450});
}

auto draw_rect = [&context, &pass](
Rect rect, Color color,
Entity::BlendMode blend_mode) -> bool {
using VS = SolidFillPipeline::VertexShader;
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
{
auto r = rect.GetLTRB();
vtx_builder.AddVertices({
{Point(r[0], r[1])},
{Point(r[2], r[1])},
{Point(r[2], r[3])},
{Point(r[0], r[1])},
{Point(r[2], r[3])},
{Point(r[0], r[3])},
});
}

Command cmd;
cmd.label = "Blended Rectangle";
auto options = OptionsFromPass(pass);
options.blend_mode = blend_mode;
cmd.pipeline = context.GetSolidFillPipeline(options);
cmd.BindVertices(
vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
frame_info.color = color.Premultiply();
VS::BindFrameInfo(cmd,
pass.GetTransientsBuffer().EmplaceUniform(frame_info));

cmd.primitive_type = PrimitiveType::kTriangle;

return pass.AddCommand(std::move(cmd));
};

ImGui::Begin("Controls");
static Color color1(1, 0, 0, 0.5), color2(0, 1, 0, 0.5);
ImGui::ColorEdit4("Color 1", reinterpret_cast<float*>(&color1));
ImGui::ColorEdit4("Color 2", reinterpret_cast<float*>(&color2));
static int current_blend_index = 3;
ImGui::ListBox("Blending mode", &current_blend_index,
blend_mode_names.data(), blend_mode_names.size());
ImGui::End();

Entity::BlendMode selected_mode =
blend_mode_values[current_blend_index];

Point a, b, c, d;
std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(
Point(400, 100), Point(200, 300), 20, Color::White(), Color::White());
std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(
Point(470, 190), Point(270, 390), 20, Color::White(), Color::White());

bool result = true;
result = result && draw_rect(Rect(0, 0, pass.GetRenderTargetSize().width,
pass.GetRenderTargetSize().height),
Color(), Entity::BlendMode::kClear);
result = result && draw_rect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), color1,
Entity::BlendMode::kSourceOver);
result = result && draw_rect(Rect::MakeLTRB(c.x, c.y, d.x, d.y), color2,
selected_mode);
return result;
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}

} // namespace testing
} // namespace impeller

0 comments on commit d0b9f21

Please sign in to comment.