Skip to content

Commit

Permalink
Make filter inputs lazy and sharable (flutter#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored and dnfield committed Apr 27, 2022
1 parent 9886da8 commit 82a3403
Show file tree
Hide file tree
Showing 19 changed files with 447 additions and 263 deletions.
4 changes: 4 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ impeller_component("entity") {
"contents/filters/blend_filter_contents.h",
"contents/filters/filter_contents.cc",
"contents/filters/filter_contents.h",
"contents/filters/filter_input.cc",
"contents/filters/filter_input.h",
"contents/filters/gaussian_blur_filter_contents.cc",
"contents/filters/gaussian_blur_filter_contents.h",
"contents/linear_gradient_contents.cc",
"contents/linear_gradient_contents.h",
"contents/snapshot.cc",
"contents/snapshot.h",
"contents/solid_color_contents.cc",
"contents/solid_color_contents.h",
"contents/solid_stroke_contents.cc",
Expand Down
42 changes: 42 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#include <sstream>

#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"

namespace impeller {

ContentContext::ContentContext(std::shared_ptr<Context> context)
Expand Down Expand Up @@ -60,6 +64,44 @@ bool ContentContext::IsValid() const {
return is_valid_;
}

std::shared_ptr<Texture> ContentContext::MakeSubpass(
ISize texture_size,
SubpassCallback subpass_callback) const {
auto context = GetContext();

auto subpass_target = RenderTarget::CreateOffscreen(*context, texture_size);
auto subpass_texture = subpass_target.GetRenderTargetTexture();
if (!subpass_texture) {
return nullptr;
}

auto sub_command_buffer = context->CreateRenderCommandBuffer();
sub_command_buffer->SetLabel("Offscreen Contents Command Buffer");
if (!sub_command_buffer) {
return nullptr;
}

auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target);
if (!sub_renderpass) {
return nullptr;
}
sub_renderpass->SetLabel("OffscreenContentsPass");

if (!subpass_callback(*this, *sub_renderpass)) {
return nullptr;
}

if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) {
return nullptr;
}

if (!sub_command_buffer->SubmitCommands()) {
return nullptr;
}

return subpass_texture;
}

std::shared_ptr<Context> ContentContext::GetContext() const {
return context_;
}
Expand Down
8 changes: 8 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ class ContentContext {

std::shared_ptr<Context> GetContext() const;

using SubpassCallback =
std::function<bool(const ContentContext&, RenderPass&)>;

/// @brief Creates a new texture of size `texture_size` and calls
/// `subpass_callback` with a `RenderPass` for drawing to the texture.
std::shared_ptr<Texture> MakeSubpass(ISize texture_size,
SubpassCallback subpass_callback) const;

private:
std::shared_ptr<Context> context_;

Expand Down
51 changes: 5 additions & 46 deletions impeller/entity/contents/contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ Rect Contents::GetBounds(const Entity& entity) const {
return Rect::MakePointBounds({points.begin(), points.end()});
}

std::optional<Contents::Snapshot> Contents::RenderToTexture(
std::optional<Snapshot> Contents::RenderToTexture(
const ContentContext& renderer,
const Entity& entity) const {
auto bounds = GetBounds(entity);

auto texture = MakeSubpass(
renderer, ISize(bounds.size),
auto texture = renderer.MakeSubpass(
ISize::Ceil(bounds.size),
[&contents = *this, &entity, &bounds](const ContentContext& renderer,
RenderPass& pass) -> bool {
Entity sub_entity;
Expand All @@ -56,52 +56,11 @@ std::optional<Contents::Snapshot> Contents::RenderToTexture(
return contents.Render(renderer, sub_entity, pass);
});

if (!texture.has_value()) {
if (!texture) {
return std::nullopt;
}

return Snapshot{.texture = texture.value(), .position = bounds.origin};
}

using SubpassCallback = std::function<bool(const ContentContext&, RenderPass&)>;

std::optional<std::shared_ptr<Texture>> Contents::MakeSubpass(
const ContentContext& renderer,
ISize texture_size,
SubpassCallback subpass_callback) {
auto context = renderer.GetContext();

auto subpass_target = RenderTarget::CreateOffscreen(*context, texture_size);
auto subpass_texture = subpass_target.GetRenderTargetTexture();
if (!subpass_texture) {
return std::nullopt;
}

auto sub_command_buffer = context->CreateRenderCommandBuffer();
sub_command_buffer->SetLabel("Offscreen Contents Command Buffer");
if (!sub_command_buffer) {
return std::nullopt;
}

auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target);
if (!sub_renderpass) {
return std::nullopt;
}
sub_renderpass->SetLabel("OffscreenContentsPass");

if (!subpass_callback(renderer, *sub_renderpass)) {
return std::nullopt;
}

if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) {
return std::nullopt;
}

if (!sub_command_buffer->SubmitCommands()) {
return std::nullopt;
}

return subpass_texture;
return Snapshot{.texture = texture, .position = bounds.origin};
}

} // namespace impeller
16 changes: 1 addition & 15 deletions impeller/entity/contents/contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <vector>

#include "flutter/fml/macros.h"
#include "impeller/entity/contents/snapshot.h"
#include "impeller/geometry/rect.h"
#include "impeller/renderer/texture.h"

Expand All @@ -27,14 +28,6 @@ ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass,

class Contents {
public:
/// Represents a screen space texture and it's intended draw position.
struct Snapshot {
std::shared_ptr<Texture> texture;
/// The offset from the origin where this texture is intended to be
/// rendered.
Vector2 position;
};

Contents();

virtual ~Contents();
Expand All @@ -54,13 +47,6 @@ class Contents {
const ContentContext& renderer,
const Entity& entity) const;

using SubpassCallback =
std::function<bool(const ContentContext&, RenderPass&)>;
static std::optional<std::shared_ptr<Texture>> MakeSubpass(
const ContentContext& renderer,
ISize texture_size,
SubpassCallback subpass_callback);

protected:

private:
Expand Down
78 changes: 43 additions & 35 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "impeller/entity/contents/filters/blend_filter_contents.h"

#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_input.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"

Expand All @@ -20,11 +21,13 @@ using PipelineProc =
std::shared_ptr<Pipeline> (ContentContext::*)(ContentContextOptions) const;

template <typename VS, typename FS>
static bool AdvancedBlend(const std::vector<Contents::Snapshot>& input_textures,
static bool AdvancedBlend(const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Rect& bounds,
PipelineProc pipeline_proc) {
if (input_textures.size() < 2) {
if (inputs.size() < 2) {
return false;
}

Expand Down Expand Up @@ -56,19 +59,21 @@ static bool AdvancedBlend(const std::vector<Contents::Snapshot>& input_textures,
typename VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(size);

auto dst_snapshot = input_textures[1];
FS::BindTextureSamplerSrc(cmd, dst_snapshot.texture, sampler);
auto dst_snapshot = inputs[1]->GetSnapshot(renderer, entity);
FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, sampler);
frame_info.dst_uv_transform =
Matrix::MakeTranslation(-dst_snapshot.position / size) *
Matrix::MakeTranslation(-(dst_snapshot->position - bounds.origin) /
size) *
Matrix::MakeScale(
Vector3(Size(size) / Size(dst_snapshot.texture->GetSize())));
Vector3(Size(size) / Size(dst_snapshot->texture->GetSize())));

auto src_snapshot = input_textures[0];
FS::BindTextureSamplerDst(cmd, src_snapshot.texture, sampler);
auto src_snapshot = inputs[0]->GetSnapshot(renderer, entity);
FS::BindTextureSamplerDst(cmd, src_snapshot->texture, sampler);
frame_info.src_uv_transform =
Matrix::MakeTranslation(-src_snapshot.position / size) *
Matrix::MakeTranslation(-(src_snapshot->position - bounds.origin) /
size) *
Matrix::MakeScale(
Vector3(Size(size) / Size(src_snapshot.texture->GetSize())));
Vector3(Size(size) / Size(src_snapshot->texture->GetSize())));

auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(cmd, uniform_view);
Expand All @@ -91,13 +96,14 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) {

switch (blend_mode) {
case Entity::BlendMode::kScreen:
advanced_blend_proc_ = [](const std::vector<Snapshot>& input_textures,
advanced_blend_proc_ = [](const FilterInput::Vector& inputs,
const ContentContext& renderer,
RenderPass& pass) {
const Entity& entity, RenderPass& pass,
const Rect& bounds) {
PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline;
return AdvancedBlend<TextureBlendScreenPipeline::VertexShader,
TextureBlendScreenPipeline::FragmentShader>(
input_textures, renderer, pass, p);
inputs, renderer, entity, pass, bounds, p);
};
break;
default:
Expand All @@ -106,9 +112,11 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) {
}
}

static bool BasicBlend(const std::vector<Contents::Snapshot>& input_textures,
static bool BasicBlend(const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Rect& bounds,
Entity::BlendMode basic_blend) {
using VS = TextureBlendPipeline::VertexShader;
using FS = TextureBlendPipeline::FragmentShader;
Expand Down Expand Up @@ -138,21 +146,21 @@ static bool BasicBlend(const std::vector<Contents::Snapshot>& input_textures,
options.blend_mode = Entity::BlendMode::kSource;
cmd.pipeline = renderer.GetTextureBlendPipeline(options);
{
auto input = input_textures[0];
FS::BindTextureSamplerSrc(cmd, input.texture, sampler);
auto input = inputs[0]->GetSnapshot(renderer, entity);
FS::BindTextureSamplerSrc(cmd, input->texture, sampler);

VS::FrameInfo frame_info;
frame_info.mvp =
Matrix::MakeOrthographic(size) *
Matrix::MakeTranslation(input.position) *
Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size));
Matrix::MakeTranslation(input->position - bounds.origin) *
Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size));

auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(cmd, uniform_view);
}
pass.AddCommand(cmd);

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

Expand All @@ -161,16 +169,16 @@ static bool BasicBlend(const std::vector<Contents::Snapshot>& input_textures,
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++) {
auto input = *texture_i;
FS::BindTextureSamplerSrc(cmd, input.texture, sampler);
for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
texture_i++) {
auto input = texture_i->get()->GetSnapshot(renderer, entity);
FS::BindTextureSamplerSrc(cmd, input->texture, sampler);

VS::FrameInfo frame_info;
frame_info.mvp = frame_info.mvp =
Matrix::MakeOrthographic(size) *
Matrix::MakeTranslation(input.position) *
Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size));
Matrix::MakeTranslation(input->position - bounds.origin) *
Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size));

auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(cmd, uniform_view);
Expand All @@ -180,27 +188,27 @@ static bool BasicBlend(const std::vector<Contents::Snapshot>& input_textures,
return true;
}

bool BlendFilterContents::RenderFilter(
const std::vector<Snapshot>& input_textures,
const ContentContext& renderer,
RenderPass& pass,
const Matrix& transform) const {
if (input_textures.empty()) {
bool BlendFilterContents::RenderFilter(const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Rect& bounds) const {
if (inputs.empty()) {
return true;
}

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

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

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

FML_UNREACHABLE();
Expand Down
Loading

0 comments on commit 82a3403

Please sign in to comment.