mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Make filter inputs lazy and sharable (flutter/engine#103)
This commit is contained in:
parent
bc8eb90c27
commit
e1bda0b4f9
@ -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",
|
||||
|
||||
@ -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)
|
||||
@ -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_;
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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();
|
||||
@ -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:
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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:
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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();
|
||||
|
||||
@ -5,15 +5,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class BlendFilterContents : public FilterContents {
|
||||
public:
|
||||
using AdvancedBlendProc =
|
||||
std::function<bool(const std::vector<Snapshot>& input_textures,
|
||||
std::function<bool(const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
RenderPass& pass)>;
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Rect& bounds)>;
|
||||
|
||||
BlendFilterContents();
|
||||
|
||||
@ -23,10 +26,11 @@ class BlendFilterContents : public FilterContents {
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
bool RenderFilter(const std::vector<Snapshot>& input_textures,
|
||||
bool RenderFilter(const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Matrix& transform) const override;
|
||||
const Rect& bounds) const override;
|
||||
|
||||
Entity::BlendMode blend_mode_;
|
||||
AdvancedBlendProc advanced_blend_proc_;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "impeller/base/validation.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/filters/blend_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
@ -27,61 +28,62 @@ namespace impeller {
|
||||
|
||||
std::shared_ptr<FilterContents> FilterContents::MakeBlend(
|
||||
Entity::BlendMode blend_mode,
|
||||
InputTextures input_textures) {
|
||||
FilterInput::Vector inputs) {
|
||||
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 ||
|
||||
if (inputs.size() < 2 ||
|
||||
blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) {
|
||||
auto blend = std::make_shared<BlendFilterContents>();
|
||||
blend->SetInputTextures(input_textures);
|
||||
blend->SetInputs(inputs);
|
||||
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});
|
||||
auto blend_input = inputs[0];
|
||||
std::shared_ptr<BlendFilterContents> new_blend;
|
||||
for (auto in_i = inputs.begin() + 1; in_i < inputs.end(); in_i++) {
|
||||
new_blend = std::make_shared<BlendFilterContents>();
|
||||
new_blend->SetInputs({blend_input, *in_i});
|
||||
new_blend->SetBlendMode(blend_mode);
|
||||
blend = new_blend;
|
||||
blend_input = FilterInput::Make(new_blend);
|
||||
}
|
||||
auto contents = std::get<std::shared_ptr<Contents>>(blend);
|
||||
// This downcast is safe because we know blend is a BlendFilterContents.
|
||||
return std::static_pointer_cast<FilterContents>(contents);
|
||||
// new_blend will always be assigned because inputs.size() >= 2.
|
||||
return new_blend;
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::shared_ptr<FilterContents> FilterContents::MakeDirectionalGaussianBlur(
|
||||
InputVariant input_texture,
|
||||
FilterInput::Ref input,
|
||||
Vector2 blur_vector) {
|
||||
auto blur = std::make_shared<DirectionalGaussianBlurFilterContents>();
|
||||
blur->SetInputTextures({input_texture});
|
||||
blur->SetInputs({input});
|
||||
blur->SetBlurVector(blur_vector);
|
||||
return blur;
|
||||
}
|
||||
|
||||
std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
|
||||
InputVariant input_texture,
|
||||
FilterInput::Ref input,
|
||||
Scalar sigma_x,
|
||||
Scalar sigma_y) {
|
||||
auto x_blur = MakeDirectionalGaussianBlur(input_texture, Point(sigma_x, 0));
|
||||
return MakeDirectionalGaussianBlur(x_blur, Point(0, sigma_y));
|
||||
auto x_blur = MakeDirectionalGaussianBlur(input, Point(sigma_x, 0));
|
||||
auto y_blur =
|
||||
MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), Point(0, sigma_y));
|
||||
return y_blur;
|
||||
}
|
||||
|
||||
FilterContents::FilterContents() = default;
|
||||
|
||||
FilterContents::~FilterContents() = default;
|
||||
|
||||
void FilterContents::SetInputTextures(InputTextures input_textures) {
|
||||
input_textures_ = std::move(input_textures);
|
||||
void FilterContents::SetInputs(FilterInput::Vector inputs) {
|
||||
inputs_ = std::move(inputs);
|
||||
}
|
||||
|
||||
bool FilterContents::Render(const ContentContext& renderer,
|
||||
@ -108,81 +110,24 @@ bool FilterContents::Render(const ContentContext& renderer,
|
||||
return contents->Render(renderer, e, pass);
|
||||
}
|
||||
|
||||
Rect FilterContents::GetBoundsForInput(const Entity& entity,
|
||||
const InputVariant& input) {
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input)) {
|
||||
return contents->get()->GetBounds(entity);
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
|
||||
auto points = entity.GetPath().GetBoundingBox()->GetPoints();
|
||||
|
||||
const auto& transform = entity.GetTransformation();
|
||||
for (uint i = 0; i < points.size(); i++) {
|
||||
points[i] = transform * points[i];
|
||||
}
|
||||
return Rect::MakePointBounds({points.begin(), points.end()});
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
Rect FilterContents::GetBounds(const Entity& entity) const {
|
||||
// The default bounds of FilterContents is just the union of its inputs.
|
||||
// FilterContents implementations may choose to increase the bounds in any
|
||||
// direction, but it should never
|
||||
|
||||
if (input_textures_.empty()) {
|
||||
if (inputs_.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
Rect result = GetBoundsForInput(entity, input_textures_.front());
|
||||
for (auto input_i = input_textures_.begin() + 1;
|
||||
input_i < input_textures_.end(); input_i++) {
|
||||
result.Union(GetBoundsForInput(entity, *input_i));
|
||||
Rect result = inputs_.front()->GetBounds(entity);
|
||||
for (auto input_i = inputs_.begin() + 1; input_i < inputs_.end(); input_i++) {
|
||||
result.Union(input_i->get()->GetBounds(entity));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::optional<Contents::Snapshot> ResolveSnapshotForInput(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
FilterContents::InputVariant input) {
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input)) {
|
||||
return contents->get()->RenderToTexture(renderer, entity);
|
||||
}
|
||||
|
||||
if (auto input_texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
|
||||
auto input_bounds = FilterContents::GetBoundsForInput(entity, input);
|
||||
// If the input is a texture, render the version of it which is transformed.
|
||||
auto texture = Contents::MakeSubpass(
|
||||
renderer, ISize(input_bounds.size),
|
||||
[texture = *input_texture, entity, input_bounds](
|
||||
const ContentContext& renderer, RenderPass& pass) -> bool {
|
||||
TextureContents contents;
|
||||
contents.SetTexture(texture);
|
||||
contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize())));
|
||||
Entity sub_entity;
|
||||
sub_entity.SetPath(entity.GetPath());
|
||||
sub_entity.SetBlendMode(Entity::BlendMode::kSource);
|
||||
sub_entity.SetTransformation(
|
||||
Matrix::MakeTranslation(Vector3(-input_bounds.origin)) *
|
||||
entity.GetTransformation());
|
||||
return contents.Render(renderer, sub_entity, pass);
|
||||
});
|
||||
if (!texture.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Contents::Snapshot{.texture = texture.value(),
|
||||
.position = input_bounds.origin};
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::optional<Contents::Snapshot> FilterContents::RenderToTexture(
|
||||
std::optional<Snapshot> FilterContents::RenderToTexture(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
auto bounds = GetBounds(entity);
|
||||
@ -190,38 +135,18 @@ std::optional<Contents::Snapshot> FilterContents::RenderToTexture(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Resolve all inputs as textures.
|
||||
|
||||
std::vector<Snapshot> input_textures;
|
||||
|
||||
input_textures.reserve(input_textures_.size());
|
||||
for (const auto& input : input_textures_) {
|
||||
auto texture_and_offset = ResolveSnapshotForInput(renderer, entity, input);
|
||||
if (!texture_and_offset.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make the position of all input snapshots relative to this filter's
|
||||
// snapshot position.
|
||||
texture_and_offset->position -= bounds.origin;
|
||||
|
||||
input_textures.push_back(texture_and_offset.value());
|
||||
}
|
||||
|
||||
// Create a new texture and render the filter to it.
|
||||
|
||||
auto texture = MakeSubpass(
|
||||
renderer, ISize(GetBounds(entity).size),
|
||||
// Render the filter into a new texture.
|
||||
auto texture = renderer.MakeSubpass(
|
||||
ISize(GetBounds(entity).size),
|
||||
[=](const ContentContext& renderer, RenderPass& pass) -> bool {
|
||||
return RenderFilter(input_textures, renderer, pass,
|
||||
entity.GetTransformation());
|
||||
return RenderFilter(inputs_, renderer, entity, pass, bounds);
|
||||
});
|
||||
|
||||
if (!texture.has_value()) {
|
||||
if (!texture) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Snapshot{.texture = texture.value(), .position = bounds.origin};
|
||||
return Snapshot{.texture = texture, .position = bounds.origin};
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/renderer/formats.h"
|
||||
|
||||
@ -17,35 +18,26 @@ class Pipeline;
|
||||
|
||||
class FilterContents : public Contents {
|
||||
public:
|
||||
using InputVariant =
|
||||
std::variant<std::shared_ptr<Texture>, std::shared_ptr<Contents>>;
|
||||
using InputTextures = std::vector<InputVariant>;
|
||||
|
||||
static std::shared_ptr<FilterContents> MakeBlend(
|
||||
Entity::BlendMode blend_mode,
|
||||
InputTextures input_textures);
|
||||
static std::shared_ptr<FilterContents> MakeBlend(Entity::BlendMode blend_mode,
|
||||
FilterInput::Vector inputs);
|
||||
|
||||
static std::shared_ptr<FilterContents> MakeDirectionalGaussianBlur(
|
||||
InputVariant input_texture,
|
||||
FilterInput::Ref input,
|
||||
Vector2 blur_vector);
|
||||
|
||||
static std::shared_ptr<FilterContents>
|
||||
MakeGaussianBlur(InputVariant input_texture, Scalar sigma_x, Scalar sigma_y);
|
||||
|
||||
static Rect GetBoundsForInput(const Entity& entity,
|
||||
const InputVariant& input);
|
||||
MakeGaussianBlur(FilterInput::Ref input, Scalar sigma_x, Scalar sigma_y);
|
||||
|
||||
FilterContents();
|
||||
|
||||
~FilterContents() override;
|
||||
|
||||
/// @brief The input texture sources for this filter. All texture sources are
|
||||
/// expected to have or produce premultiplied alpha colors.
|
||||
/// Any input can either be a `Texture` or another `FilterContents`.
|
||||
/// @brief The input texture sources for this filter. Each input's emitted
|
||||
/// texture is expected to have premultiplied alpha colors.
|
||||
///
|
||||
/// The number of required or optional textures depends on the
|
||||
/// particular filter's implementation.
|
||||
void SetInputTextures(InputTextures input_textures);
|
||||
void SetInputs(FilterInput::Vector inputs);
|
||||
|
||||
// |Contents|
|
||||
bool Render(const ContentContext& renderer,
|
||||
@ -63,12 +55,13 @@ class FilterContents : public Contents {
|
||||
private:
|
||||
/// @brief Takes a set of zero or more input textures and writes to an output
|
||||
/// texture.
|
||||
virtual bool RenderFilter(const std::vector<Snapshot>& input_textures,
|
||||
virtual bool RenderFilter(const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Matrix& transform) const = 0;
|
||||
const Rect& bounds) const = 0;
|
||||
|
||||
InputTextures input_textures_;
|
||||
FilterInput::Vector inputs_;
|
||||
Rect destination_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(FilterContents);
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
#include "impeller/entity/contents/snapshot.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
FilterInput::Ref FilterInput::Make(Variant input) {
|
||||
return std::shared_ptr<FilterInput>(new FilterInput(input));
|
||||
}
|
||||
|
||||
FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
|
||||
FilterInput::Vector result;
|
||||
result.reserve(inputs.size());
|
||||
for (const auto& input : inputs) {
|
||||
result.push_back(Make(input));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
FilterInput::Variant FilterInput::GetInput() const {
|
||||
return input_;
|
||||
}
|
||||
|
||||
Rect FilterInput::GetBounds(const Entity& entity) const {
|
||||
if (snapshot_) {
|
||||
return Rect(snapshot_->position, Size(snapshot_->texture->GetSize()));
|
||||
}
|
||||
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
|
||||
return contents->get()->GetBounds(entity);
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
|
||||
return entity.GetTransformedPathBounds();
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::optional<Snapshot> FilterInput::GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
if (snapshot_) {
|
||||
return snapshot_;
|
||||
}
|
||||
snapshot_ = RenderToTexture(renderer, entity);
|
||||
|
||||
return snapshot_;
|
||||
}
|
||||
|
||||
FilterInput::FilterInput(Variant input) : input_(input) {}
|
||||
|
||||
FilterInput::~FilterInput() = default;
|
||||
|
||||
std::optional<Snapshot> FilterInput::RenderToTexture(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
|
||||
return contents->get()->RenderToTexture(renderer, entity);
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
|
||||
return Snapshot::FromTransformedTexture(renderer, entity, *texture);
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@ -0,0 +1,60 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
#include "impeller/renderer/formats.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ContentContext;
|
||||
class Entity;
|
||||
|
||||
/// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across
|
||||
/// filter parameters and used to evaluate input bounds.
|
||||
///
|
||||
/// A `FilterInput` can be created from either a `Texture` or any `Contents`
|
||||
/// class (including `FilterContents`), and can be re-used for any filter inputs
|
||||
/// across an entity's filter graph without repeating subpasses unnecessarily.
|
||||
///
|
||||
/// Filters may decide to not evaluate inputs in situations where they won't
|
||||
/// contribute to the filter's output texture.
|
||||
class FilterInput final {
|
||||
public:
|
||||
using Ref = std::shared_ptr<FilterInput>;
|
||||
using Vector = std::vector<FilterInput::Ref>;
|
||||
using Variant =
|
||||
std::variant<std::shared_ptr<Texture>, std::shared_ptr<Contents>>;
|
||||
|
||||
~FilterInput();
|
||||
|
||||
static FilterInput::Ref Make(Variant input);
|
||||
|
||||
static FilterInput::Vector Make(std::initializer_list<Variant> inputs);
|
||||
|
||||
Variant GetInput() const;
|
||||
|
||||
Rect GetBounds(const Entity& entity) const;
|
||||
|
||||
std::optional<Snapshot> GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const;
|
||||
|
||||
private:
|
||||
FilterInput(Variant input);
|
||||
|
||||
std::optional<Snapshot> RenderToTexture(const ContentContext& renderer,
|
||||
const Entity& entity) const;
|
||||
|
||||
Variant input_;
|
||||
mutable std::optional<Snapshot> snapshot_;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <valarray>
|
||||
|
||||
#include "impeller/base/validation.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
@ -30,11 +31,12 @@ void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) {
|
||||
}
|
||||
|
||||
bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
const std::vector<Snapshot>& input_textures,
|
||||
const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Matrix& transform) const {
|
||||
if (input_textures.empty()) {
|
||||
const Rect& bounds) const {
|
||||
if (inputs.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -43,16 +45,19 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
|
||||
// Because this filter is intended to be used with only one input parameter,
|
||||
// Because this filter is intended to be used with only one input parameter,
|
||||
// and GetBounds just increases the input size by a factor of the direction,
|
||||
// we we can just scale up the UVs by the same amount and don't need to worry
|
||||
// about mapping the UVs to destination rect (like we do in
|
||||
// about mapping the UVs to the destination rect (like we do in
|
||||
// BlendFilterContents).
|
||||
|
||||
auto size = pass.GetRenderTargetSize();
|
||||
auto transformed_blur = transform.TransformDirection(blur_vector_);
|
||||
auto uv_offset = transformed_blur.Abs() / size;
|
||||
auto input = inputs[0]->GetSnapshot(renderer, entity);
|
||||
auto input_size = input->texture->GetSize();
|
||||
|
||||
auto transformed_blur =
|
||||
entity.GetTransformation().TransformDirection(blur_vector_);
|
||||
|
||||
auto uv_offset = transformed_blur.Abs() / input_size;
|
||||
// LTRB
|
||||
Scalar uv[4] = {
|
||||
-uv_offset.x,
|
||||
@ -62,6 +67,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
};
|
||||
|
||||
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
|
||||
auto size = pass.GetRenderTargetSize();
|
||||
vtx_builder.AddVertices({
|
||||
{Point(0, 0), Point(uv[0], uv[1])},
|
||||
{Point(size.width, 0), Point(uv[2], uv[1])},
|
||||
@ -73,7 +79,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
|
||||
|
||||
VS::FrameInfo frame_info;
|
||||
frame_info.texture_size = Point(size);
|
||||
frame_info.texture_size = Point(input_size);
|
||||
frame_info.blur_radius = transformed_blur.GetLength();
|
||||
frame_info.blur_direction = transformed_blur.Normalize();
|
||||
|
||||
@ -86,16 +92,13 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
|
||||
cmd.BindVertices(vtx_buffer);
|
||||
|
||||
const auto& [texture, _] = input_textures[0];
|
||||
FS::BindTextureSampler(cmd, texture, sampler);
|
||||
FS::BindTextureSampler(cmd, input->texture, sampler);
|
||||
|
||||
frame_info.mvp = Matrix::MakeOrthographic(size);
|
||||
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
|
||||
VS::BindFrameInfo(cmd, uniform_view);
|
||||
|
||||
pass.AddCommand(cmd);
|
||||
|
||||
return true;
|
||||
return pass.AddCommand(cmd);
|
||||
}
|
||||
|
||||
Rect DirectionalGaussianBlurFilterContents::GetBounds(
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
#include "impeller/geometry/matrix.h"
|
||||
|
||||
namespace impeller {
|
||||
@ -22,10 +23,11 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents {
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
bool RenderFilter(const std::vector<Snapshot>& input_textures,
|
||||
bool RenderFilter(const FilterInput::Vector& input_textures,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Matrix& transform) const override;
|
||||
const Rect& bounds) const override;
|
||||
|
||||
Vector2 blur_vector_;
|
||||
|
||||
|
||||
40
engine/src/flutter/impeller/entity/contents/snapshot.cc
Normal file
40
engine/src/flutter/impeller/entity/contents/snapshot.cc
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/entity/contents/snapshot.h"
|
||||
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
std::optional<Snapshot> Snapshot::FromTransformedTexture(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
std::shared_ptr<Texture> texture) {
|
||||
Rect bounds = entity.GetTransformedPathBounds();
|
||||
|
||||
auto result = renderer.MakeSubpass(
|
||||
ISize(bounds.size),
|
||||
[&texture, &entity, bounds](const ContentContext& renderer,
|
||||
RenderPass& pass) -> bool {
|
||||
TextureContents contents;
|
||||
contents.SetTexture(texture);
|
||||
contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize())));
|
||||
Entity sub_entity;
|
||||
sub_entity.SetPath(entity.GetPath());
|
||||
sub_entity.SetBlendMode(Entity::BlendMode::kSource);
|
||||
sub_entity.SetTransformation(
|
||||
Matrix::MakeTranslation(Vector3(-bounds.origin)) *
|
||||
entity.GetTransformation());
|
||||
return contents.Render(renderer, sub_entity, pass);
|
||||
});
|
||||
if (!result) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Snapshot{.texture = result, .position = bounds.origin};
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
36
engine/src/flutter/impeller/entity/contents/snapshot.h
Normal file
36
engine/src/flutter/impeller/entity/contents/snapshot.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/geometry/matrix.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
#include "impeller/renderer/texture.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ContentContext;
|
||||
class Entity;
|
||||
|
||||
/// Represents a texture and its 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;
|
||||
|
||||
/// Transform a texture by the given `entity`'s transformation matrix to a new
|
||||
/// texture.
|
||||
static std::optional<Snapshot> FromTransformedTexture(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
std::shared_ptr<Texture> texture);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
@ -30,6 +30,16 @@ void Entity::SetPath(Path path) {
|
||||
path_ = std::move(path);
|
||||
}
|
||||
|
||||
Rect Entity::GetTransformedPathBounds() const {
|
||||
auto points = GetPath().GetBoundingBox()->GetPoints();
|
||||
|
||||
const auto& transform = GetTransformation();
|
||||
for (uint i = 0; i < points.size(); i++) {
|
||||
points[i] = transform * points[i];
|
||||
}
|
||||
return Rect::MakePointBounds({points.begin(), points.end()});
|
||||
}
|
||||
|
||||
void Entity::SetAddsToCoverage(bool adds) {
|
||||
adds_to_coverage_ = adds;
|
||||
}
|
||||
|
||||
@ -65,6 +65,8 @@ class Entity {
|
||||
|
||||
void SetPath(Path path);
|
||||
|
||||
Rect GetTransformedPathBounds() const;
|
||||
|
||||
void SetAddsToCoverage(bool adds);
|
||||
|
||||
bool AddsToCoverage() const;
|
||||
|
||||
@ -2,8 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/filter_input.h"
|
||||
#include "impeller/entity/contents/solid_color_contents.h"
|
||||
#include "impeller/entity/contents/solid_stroke_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
@ -657,11 +660,16 @@ TEST_F(EntityTest, Filters) {
|
||||
ASSERT_TRUE(bridge && boston && kalimba);
|
||||
|
||||
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
|
||||
auto blend0 = FilterContents::MakeBlend(Entity::BlendMode::kModulate,
|
||||
{kalimba, boston});
|
||||
auto fi_bridge = FilterInput::Make(bridge);
|
||||
auto fi_boston = FilterInput::Make(boston);
|
||||
auto fi_kalimba = FilterInput::Make(kalimba);
|
||||
|
||||
auto blend1 = FilterContents::MakeBlend(Entity::BlendMode::kScreen,
|
||||
{bridge, blend0, bridge, bridge});
|
||||
auto blend0 = FilterContents::MakeBlend(Entity::BlendMode::kModulate,
|
||||
{fi_kalimba, fi_boston});
|
||||
|
||||
auto blend1 = FilterContents::MakeBlend(
|
||||
Entity::BlendMode::kScreen,
|
||||
{fi_bridge, FilterInput::Make(blend0), fi_bridge, fi_bridge});
|
||||
|
||||
Entity entity;
|
||||
entity.SetPath(PathBuilder{}.AddRect({100, 100, 300, 300}).TakePath());
|
||||
@ -681,52 +689,71 @@ TEST_F(EntityTest, GaussianBlurFilter) {
|
||||
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
|
||||
if (first_frame) {
|
||||
first_frame = false;
|
||||
ImGui::SetNextWindowSize({500, 170});
|
||||
ImGui::SetNextWindowSize({500, 190});
|
||||
ImGui::SetNextWindowPos({300, 550});
|
||||
}
|
||||
|
||||
ImGui::Begin("Controls");
|
||||
static float blur_amount[2] = {20, 20};
|
||||
ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200);
|
||||
static Color cover_color(1, 0, 0, 0.2);
|
||||
ImGui::ColorEdit4("Cover color", reinterpret_cast<float*>(&cover_color));
|
||||
static Color bounds_color(0, 1, 0, 0.1);
|
||||
static float offset[2] = {500, 400};
|
||||
static float rotation = 0;
|
||||
static float scale[2] = {0.8, 0.8};
|
||||
static float skew[2] = {0, 0};
|
||||
|
||||
ImGui::Begin("Controls");
|
||||
ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200);
|
||||
ImGui::ColorEdit4("Cover color", reinterpret_cast<float*>(&cover_color));
|
||||
ImGui::ColorEdit4("Bounds color", reinterpret_cast<float*>(&bounds_color));
|
||||
ImGui::SliderFloat2("Translation", &offset[0], 0,
|
||||
pass.GetRenderTargetSize().width);
|
||||
static float rotation = 0;
|
||||
ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2);
|
||||
static float scale[2] = {0.8, 0.8};
|
||||
ImGui::SliderFloat2("Scale", &scale[0], 0, 3);
|
||||
static float skew[2] = {0, 0};
|
||||
ImGui::SliderFloat2("Skew", &skew[0], -3, 3);
|
||||
ImGui::End();
|
||||
|
||||
auto blend = FilterContents::MakeBlend(Entity::BlendMode::kPlus,
|
||||
{boston, bridge, bridge});
|
||||
auto blend = FilterContents::MakeBlend(
|
||||
Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge}));
|
||||
|
||||
auto blur =
|
||||
FilterContents::MakeGaussianBlur(blend, blur_amount[0], blur_amount[1]);
|
||||
auto blur = FilterContents::MakeGaussianBlur(
|
||||
FilterInput::Make(blend), blur_amount[0], blur_amount[1]);
|
||||
|
||||
auto rect = Rect(-Point(boston->GetSize()) / 2, Size(boston->GetSize()));
|
||||
ISize input_size = boston->GetSize();
|
||||
auto rect = Rect(-Point(input_size) / 2, Size(input_size));
|
||||
auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
|
||||
Matrix::MakeRotation(rotation, Vector4(0, 0, 1, 1)) *
|
||||
Matrix::MakeRotationZ(Radians(rotation)) *
|
||||
Matrix::MakeScale(Vector3(scale[0], scale[1])) *
|
||||
Matrix::MakeSkew(skew[0], skew[1]);
|
||||
|
||||
auto target_contents = blur;
|
||||
|
||||
Entity entity;
|
||||
entity.SetPath(PathBuilder{}.AddRect(rect).TakePath());
|
||||
entity.SetContents(blur);
|
||||
entity.SetContents(target_contents);
|
||||
entity.SetTransformation(ctm);
|
||||
|
||||
entity.Render(context, pass);
|
||||
|
||||
// The following entity renders the expected transformed input.
|
||||
// Renders a red "cover" rectangle that shows the original position of the
|
||||
// unfiltered input.
|
||||
Entity cover_entity;
|
||||
cover_entity.SetPath(PathBuilder{}.AddRect(rect).TakePath());
|
||||
cover_entity.SetContents(SolidColorContents::Make(cover_color));
|
||||
cover_entity.SetContents(
|
||||
SolidColorContents::Make(cover_color.Premultiply()));
|
||||
cover_entity.SetTransformation(ctm);
|
||||
|
||||
cover_entity.Render(context, pass);
|
||||
|
||||
// Renders a green bounding rect of the target filter.
|
||||
Entity bounds_entity;
|
||||
bounds_entity.SetPath(
|
||||
PathBuilder{}.AddRect(target_contents->GetBounds(entity)).TakePath());
|
||||
bounds_entity.SetContents(
|
||||
SolidColorContents::Make(bounds_color.Premultiply()));
|
||||
bounds_entity.SetTransformation(Matrix());
|
||||
|
||||
bounds_entity.Render(context, pass);
|
||||
|
||||
return true;
|
||||
};
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
|
||||
@ -35,8 +35,6 @@ vec4 SampleWithBorder(vec2 uv) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 blur_radius_uv = vec2(v_blur_radius) / v_texture_size;
|
||||
|
||||
vec4 total = vec4(0);
|
||||
float total_gaussian = 0;
|
||||
for (float i = -v_blur_radius; i <= v_blur_radius; i++) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user