Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] Migrate all ColorSourceContents to use a shared rendering routine. #50261

Merged
merged 16 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
97 changes: 97 additions & 0 deletions impeller/entity/contents/color_source_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_

#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/geometry/geometry.h"
#include "impeller/geometry/matrix.h"
Expand Down Expand Up @@ -100,6 +101,102 @@ class ColorSourceContents : public Contents {
// |Contents|
void SetInheritedOpacity(Scalar opacity) override;

protected:
using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
using PipelineBuilderMethod = std::shared_ptr<Pipeline<PipelineDescriptor>> (
impeller::ContentContext::*)(ContentContextOptions) const;
using PipelineBuilderCallback =
std::function<std::shared_ptr<Pipeline<PipelineDescriptor>>(
ContentContextOptions)>;

template <typename VertexShaderT>
bool DrawGeometry(GeometryResult geometry_result,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback) const {
auto options = OptionsFromPassAndEntity(pass, entity);

// If overdraw prevention is enabled (like when drawing stroke paths), we
// increment the stencil buffer as we draw, preventing overlapping fragments
// from drawing. Afterwards, we need to append another draw call to clean up
// the stencil buffer (happens below in this method).
if (geometry_result.prevent_overdraw) {
options.stencil_mode =
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
options.primitive_type = geometry_result.type;
pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
pass.SetStencilReference(entity.GetClipDepth());

// Take the pre-populated vertex shader uniform struct and set managed
// values.
frame_info.depth = entity.GetShaderClipDepth();
frame_info.mvp = geometry_result.transform;

VertexShaderT::BindFrameInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));

pass.SetPipeline(pipeline_callback(options));

// The reason we need to have a callback mechanism here is that this routine
// may insert draw calls before the main draw call below. For example, for
// sufficiently complex paths we may opt to use stencil-then-cover to avoid
// tessellation.
if (!bind_fragment_callback(pass)) {
return false;
}

if (!pass.Draw().ok()) {
return false;
}

// If we performed overdraw prevention, a subsection of the clip heightmap
// was incremented by 1 in order to self-clip. So simply append a clip
// restore to clean it up.
if (geometry_result.prevent_overdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
return restore.Render(renderer, entity, pass);
}
return true;
}

template <typename VertexShaderT>
bool DrawPositions(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_pipeline_callback) const {
GeometryResult geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);

return DrawGeometry<VertexShaderT>(std::move(geometry_result), renderer,
entity, pass, pipeline_callback,
frame_info, bind_pipeline_callback);
}

template <typename VertexShaderT>
bool DrawPositionsAndUVs(
Rect texture_coverage,
const Matrix& effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_pipeline_callback) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

Alternative suggestion, make color source contents have a virtual BindFragmentCallback method and make this call it. If we have to pass a closure for non trivial cases then its going to end up closing over the entire contents anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems like a good approach. Done.

Copy link
Member Author

Choose a reason for hiding this comment

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

Uhh actually applying this to my follow-up patch is a painful because we have multiple rendering branches for all of the gradients that need different binding behavior.

In all of the use cases we can capture the things we need by reference, and my understanding is that assigning a lambda to a stack std::function shouldn't heap allocate as long as the set of captures is reasonably small.

One sec, gonna check this...

Copy link
Contributor

Choose a reason for hiding this comment

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

The different gradients are basically different content classes though
if the contents is less verbose with this change it would make more sense to split them in two IMO.

Copy link
Member Author

@bdero bdero Feb 8, 2024

Choose a reason for hiding this comment

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

Briefly discussed offline, we can just switch this out later if necessary / ends up being too annoying. Some legit concerns over expanding our use of std::function, like annoying stack traces, compiler differences, etc.

auto geometry_result = GetGeometry()->GetPositionUVBuffer(
texture_coverage, effect_transform, renderer, entity, pass);

return DrawGeometry<VertexShaderT>(std::move(geometry_result), renderer,
entity, pass, pipeline_callback,
frame_info, bind_pipeline_callback);
}

private:
std::shared_ptr<Geometry> geometry_;
Matrix inverse_matrix_;
Expand Down
181 changes: 76 additions & 105 deletions impeller/entity/contents/conical_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,62 +64,45 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer,
using VS = ConicalGradientSSBOFillPipeline::VertexShader;
using FS = ConicalGradientSSBOFillPipeline::FragmentShader;

FS::FragInfo frag_info;
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha = GetOpacityFactor();
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
} else {
frag_info.focus = center_;
frag_info.focus_radius = 0.0;
}

auto& host_buffer = renderer.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_);

frag_info.colors_length = colors.size();
auto color_buffer =
host_buffer.Emplace(colors.data(), colors.size() * sizeof(StopData),
DefaultUniformAlignment());

VS::FrameInfo frame_info;
frame_info.depth = entity.GetShaderClipDepth();
frame_info.mvp = pass.GetOrthographicTransform() * entity.GetTransform();
frame_info.matrix = GetInverseEffectTransform();

auto geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
auto options = OptionsFromPassAndEntity(pass, entity);
if (geometry_result.prevent_overdraw) {
options.stencil_mode =
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
options.primitive_type = geometry_result.type;

pass.SetCommandLabel("ConicalGradientSSBOFill");
pass.SetStencilReference(entity.GetClipDepth());
pass.SetPipeline(renderer.GetConicalGradientSSBOFillPipeline(options));
pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
FS::BindFragInfo(pass,
renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
FS::BindColorData(pass, color_buffer);
VS::BindFrameInfo(pass,
renderer.GetTransientsBuffer().EmplaceUniform(frame_info));

if (!pass.Draw().ok()) {
return false;
}

if (geometry_result.prevent_overdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
return restore.Render(renderer, entity, pass);
}
return true;
PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
return renderer.GetConicalGradientSSBOFillPipeline(options);
};
return ColorSourceContents::DrawPositions<VS>(
renderer, entity, pass, pipeline_callback, frame_info,
[this, &renderer](RenderPass& pass) {
FS::FragInfo frag_info;
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha = GetOpacityFactor();
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
} else {
frag_info.focus = center_;
frag_info.focus_radius = 0.0;
}

auto& host_buffer = renderer.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_);

frag_info.colors_length = colors.size();
auto color_buffer =
host_buffer.Emplace(colors.data(), colors.size() * sizeof(StopData),
DefaultUniformAlignment());

FS::BindFragInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
FS::BindColorData(pass, color_buffer);

pass.SetCommandLabel("ConicalGradientSSBOFill");
return true;
});
}

bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
Expand All @@ -135,64 +118,52 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
return false;
}

FS::FragInfo frag_info;
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale();
frag_info.alpha = GetOpacityFactor();
frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width,
0.5 / gradient_texture->GetSize().height);
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
} else {
frag_info.focus = center_;
frag_info.focus_radius = 0.0;
}

auto geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);

VS::FrameInfo frame_info;
frame_info.depth = entity.GetShaderClipDepth();
frame_info.mvp = geometry_result.transform;
frame_info.matrix = GetInverseEffectTransform();

pass.SetCommandLabel("ConicalGradientFill");
pass.SetStencilReference(entity.GetClipDepth());

auto options = OptionsFromPassAndEntity(pass, entity);
if (geometry_result.prevent_overdraw) {
options.stencil_mode =
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
options.primitive_type = geometry_result.type;
pass.SetPipeline(renderer.GetConicalGradientFillPipeline(options));

pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
FS::BindFragInfo(pass,
renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
SamplerDescriptor sampler_desc;
sampler_desc.min_filter = MinMagFilter::kLinear;
sampler_desc.mag_filter = MinMagFilter::kLinear;
FS::BindTextureSampler(
pass, gradient_texture,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc));
VS::BindFrameInfo(pass,
renderer.GetTransientsBuffer().EmplaceUniform(frame_info));

if (!pass.Draw().ok()) {
return false;
}

if (geometry_result.prevent_overdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
return restore.Render(renderer, entity, pass);
}
return true;
PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
return renderer.GetConicalGradientFillPipeline(options);
};
return ColorSourceContents::DrawPositions<VS>(
renderer, entity, pass, pipeline_callback, frame_info,
[this, &renderer, &gradient_texture](RenderPass& pass) {
FS::FragInfo frag_info;
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.texture_sampler_y_coord_scale =
gradient_texture->GetYCoordScale();
frag_info.alpha = GetOpacityFactor();
frag_info.half_texel =
Vector2(0.5 / gradient_texture->GetSize().width,
0.5 / gradient_texture->GetSize().height);
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
} else {
frag_info.focus = center_;
frag_info.focus_radius = 0.0;
}

pass.SetCommandLabel("ConicalGradientFill");

FS::BindFragInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frag_info));
SamplerDescriptor sampler_desc;
sampler_desc.min_filter = MinMagFilter::kLinear;
sampler_desc.mag_filter = MinMagFilter::kLinear;
FS::BindTextureSampler(
pass, gradient_texture,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
sampler_desc));

return true;
});
}

bool ConicalGradientContents::ApplyColorFilter(
Expand Down
Loading