Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,12 @@ bool ContentContext::IsValid() const {
return is_valid_;
}

std::shared_ptr<Texture> ContentContext::MakeSubpass(
fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, this is to make just reusing the same RenderTarget easy in the blur. I think this is fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I had to do RenderTarget instead of Texture because of MSAA.

const std::string& label,
ISize texture_size,
const SubpassCallback& subpass_callback,
bool msaa_enabled) const {
auto context = GetContext();

std::shared_ptr<Context> context = GetContext();
RenderTarget subpass_target;
if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) {
subpass_target = RenderTarget::CreateOffscreenMSAA(
Expand All @@ -428,32 +427,41 @@ std::shared_ptr<Texture> ContentContext::MakeSubpass(
std::nullopt // stencil_attachment_config
);
}
return MakeSubpass(label, subpass_target, subpass_callback);
}

fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
const std::string& label,
const RenderTarget& subpass_target,
const SubpassCallback& subpass_callback) const {
std::shared_ptr<Context> context = GetContext();

auto subpass_texture = subpass_target.GetRenderTargetTexture();
if (!subpass_texture) {
return nullptr;
return fml::Status(fml::StatusCode::kUnknown, "");
}

auto sub_command_buffer = context->CreateCommandBuffer();
sub_command_buffer->SetLabel(SPrintF("%s CommandBuffer", label.c_str()));
if (!sub_command_buffer) {
return nullptr;
return fml::Status(fml::StatusCode::kUnknown, "");
}

auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target);
if (!sub_renderpass) {
return nullptr;
return fml::Status(fml::StatusCode::kUnknown, "");
}
sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.c_str()));

if (!subpass_callback(*this, *sub_renderpass)) {
return nullptr;
return fml::Status(fml::StatusCode::kUnknown, "");
}

if (!sub_command_buffer->EncodeAndSubmit(sub_renderpass)) {
return nullptr;
return fml::Status(fml::StatusCode::kUnknown, "");
}

return subpass_texture;
return subpass_target;
}

#if IMPELLER_ENABLE_3D
Expand Down
16 changes: 12 additions & 4 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/status_or.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/entity/entity.h"
Expand Down Expand Up @@ -692,10 +693,17 @@ class ContentContext {

/// @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(const std::string& label,
ISize texture_size,
const SubpassCallback& subpass_callback,
bool msaa_enabled = true) const;
fml::StatusOr<RenderTarget> MakeSubpass(
const std::string& label,
ISize texture_size,
const SubpassCallback& subpass_callback,
bool msaa_enabled = true) const;

/// Makes a subpass that will render to `subpass_target`.
fml::StatusOr<RenderTarget> MakeSubpass(
const std::string& label,
const RenderTarget& subpass_target,
const SubpassCallback& subpass_callback) const;

std::shared_ptr<LazyGlyphAtlas> GetLazyGlyphAtlas() const {
return lazy_glyph_atlas_;
Expand Down
6 changes: 3 additions & 3 deletions impeller/entity/contents/contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ std::optional<Snapshot> Contents::RenderToSnapshot(
}
}

auto texture = renderer.MakeSubpass(
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
label, ISize::Ceil(coverage->GetSize()),
[&contents = *this, &entity, &coverage](const ContentContext& renderer,
RenderPass& pass) -> bool {
Expand All @@ -92,12 +92,12 @@ std::optional<Snapshot> Contents::RenderToSnapshot(
},
msaa_enabled);

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

auto snapshot = Snapshot{
.texture = texture,
.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(coverage->GetOrigin()),
};
if (sampler_descriptor.has_value()) {
Expand Down
12 changes: 6 additions & 6 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@ static std::optional<Entity> AdvancedBlend(
return true;
};

auto out_texture = renderer.MakeSubpass(
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Advanced Blend Filter", ISize(subpass_coverage.GetSize()), callback);
if (!out_texture) {
if (!render_target.ok()) {
return std::nullopt;
}

return Entity::FromSnapshot(
Snapshot{
.texture = out_texture,
.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
// Since we absorbed the transform of the inputs and used the
// respective snapshot sampling modes when blending, pass on
Expand Down Expand Up @@ -646,16 +646,16 @@ static std::optional<Entity> PipelineBlend(
return true;
};

auto out_texture = renderer.MakeSubpass(
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Pipeline Blend Filter", ISize(subpass_coverage.GetSize()), callback);

if (!out_texture) {
if (!render_target.ok()) {
return std::nullopt;
}

return Entity::FromSnapshot(
Snapshot{
.texture = out_texture,
.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
// Since we absorbed the transform of the inputs and used the
// respective snapshot sampling modes when blending, pass on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ Sigma ScaleSigma(Sigma sigma) {
DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() =
default;

DirectionalGaussianBlurFilterContents::~
DirectionalGaussianBlurFilterContents() = default;
DirectionalGaussianBlurFilterContents::
~DirectionalGaussianBlurFilterContents() = default;

void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) {
blur_sigma_ = sigma;
Expand Down Expand Up @@ -256,10 +256,10 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
Vector2 scaled_size = pass_texture_rect.GetSize() * scale;
ISize floored_size = ISize(scaled_size.x, scaled_size.y);

auto out_texture = renderer.MakeSubpass("Directional Gaussian Blur Filter",
floored_size, subpass_callback);
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Directional Gaussian Blur Filter", floored_size, subpass_callback);

if (!out_texture) {
if (!render_target.ok()) {
return std::nullopt;
}

Expand All @@ -271,7 +271,7 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(

return Entity::FromSnapshot(
Snapshot{
.texture = out_texture,
.texture = render_target.value().GetRenderTargetTexture(),
.transform =
texture_rotate.Invert() *
Matrix::MakeTranslation(pass_texture_rect.GetOrigin()) *
Expand Down
74 changes: 54 additions & 20 deletions impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ void SetTileMode(SamplerDescriptor* descriptor,

/// Makes a subpass that will render the scaled down input and add the
/// transparent gutter required for the blur halo.
std::shared_ptr<Texture> MakeDownsampleSubpass(
fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
const ContentContext& renderer,
std::shared_ptr<Texture> input_texture,
const SamplerDescriptor& sampler_descriptor,
Expand Down Expand Up @@ -110,21 +110,24 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(

return true;
};
std::shared_ptr<Texture> out_texture = renderer.MakeSubpass(
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Gaussian Blur Filter", subpass_size, subpass_callback);
return out_texture;
return render_target;
}

std::shared_ptr<Texture> MakeBlurSubpass(
fml::StatusOr<RenderTarget> MakeBlurSubpass(
const ContentContext& renderer,
std::shared_ptr<Texture> input_texture,
const RenderTarget& input_pass,
const SamplerDescriptor& sampler_descriptor,
Entity::TileMode tile_mode,
const GaussianBlurFragmentShader::BlurInfo& blur_info) {
const GaussianBlurFragmentShader::BlurInfo& blur_info,
std::optional<RenderTarget> destination_target) {
if (blur_info.blur_sigma < kEhCloseEnough) {
return input_texture;
return input_pass;
}

std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture();

// TODO(gaaclarke): This blurs the whole image, but because we know the clip
// region we could focus on just blurring that.
ISize subpass_size = input_texture->GetSize();
Expand Down Expand Up @@ -171,9 +174,13 @@ std::shared_ptr<Texture> MakeBlurSubpass(

return true;
};
std::shared_ptr<Texture> out_texture = renderer.MakeSubpass(
"Gaussian Blur Filter", subpass_size, subpass_callback);
return out_texture;
if (destination_target.has_value()) {
return renderer.MakeSubpass("Gaussian Blur Filter",
destination_target.value(), subpass_callback);
} else {
return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
subpass_callback);
}
}

} // namespace
Expand Down Expand Up @@ -293,38 +300,65 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
Quad uvs = CalculateUVs(inputs[0], entity, source_rect_padded,
input_snapshot->texture->GetSize());

std::shared_ptr<Texture> pass1_out_texture = MakeDownsampleSubpass(
fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
renderer, input_snapshot->texture, input_snapshot->sampler_descriptor,
uvs, subpass_size, tile_mode_);

Vector2 pass1_pixel_size = 1.0 / Vector2(pass1_out_texture->GetSize());
if (!pass1_out.ok()) {
return std::nullopt;
}

std::shared_ptr<Texture> pass2_out_texture =
MakeBlurSubpass(renderer, pass1_out_texture,
Vector2 pass1_pixel_size =
1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());

fml::StatusOr<RenderTarget> pass2_out =
MakeBlurSubpass(renderer, /*input_pass=*/pass1_out.value(),
input_snapshot->sampler_descriptor, tile_mode_,
GaussianBlurFragmentShader::BlurInfo{
.blur_uv_offset = Point(0.0, pass1_pixel_size.y),
.blur_sigma = scaled_sigma.y * effective_scalar.y,
.blur_radius = blur_radius.y * effective_scalar.y,
.step_size = 1.0,
});
},
/*destination_target=*/std::nullopt);

// TODO(gaaclarke): Make this pass reuse the texture from pass1.
std::shared_ptr<Texture> pass3_out_texture =
MakeBlurSubpass(renderer, pass2_out_texture,
if (!pass2_out.ok()) {
return std::nullopt;
}

// Only ping pong if the first pass actually created a render target.
auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
pass1_out.value().GetRenderTargetTexture()
? std::optional<RenderTarget>(pass1_out.value())
: std::optional<RenderTarget>(std::nullopt);

fml::StatusOr<RenderTarget> pass3_out =
MakeBlurSubpass(renderer, /*input_pass=*/pass2_out.value(),
input_snapshot->sampler_descriptor, tile_mode_,
GaussianBlurFragmentShader::BlurInfo{
.blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
.blur_sigma = scaled_sigma.x * effective_scalar.x,
.blur_radius = blur_radius.x * effective_scalar.x,
.step_size = 1.0,
});
},
pass3_destination);

if (!pass3_out.ok()) {
return std::nullopt;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I believe all of this works out such that all three passes should always wind up attaching color textures of size subpass_size.

Since this optimization is contingent on the sizes being the same in order to render correctly, could you add a DCHECK that verifies the color attachment of the three passes is subpass_size?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// The ping-pong approach requires that each render pass output has the same
// size.
FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
pass2_out.value().GetRenderTargetSize()) &&
(pass2_out.value().GetRenderTargetSize() ==
pass3_out.value().GetRenderTargetSize()));

SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
MinMagFilter::kLinear, SamplerAddressMode::kClampToEdge);

return Entity::FromSnapshot(
Snapshot{.texture = pass3_out_texture,
Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
.transform = input_snapshot->transform *
padding_snapshot_adjustment *
Matrix::MakeScale(1 / effective_scalar),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ std::optional<Entity> DirectionalMorphologyFilterContents::RenderFilter(
return pass.AddCommand(std::move(cmd));
};

auto out_texture = renderer.MakeSubpass("Directional Morphology Filter",
ISize(coverage.GetSize()), callback);
if (!out_texture) {
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Directional Morphology Filter", ISize(coverage.GetSize()), callback);
if (!render_target.ok()) {
return std::nullopt;
}

Expand All @@ -148,7 +148,7 @@ std::optional<Entity> DirectionalMorphologyFilterContents::RenderFilter(
sampler_desc.mag_filter = MinMagFilter::kLinear;

return Entity::FromSnapshot(
Snapshot{.texture = out_texture,
Snapshot{.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(coverage.GetOrigin()),
.sampler_descriptor = sampler_desc,
.opacity = input_snapshot->opacity},
Expand Down