mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Refactored Canvas to disallow null inline contexts. (#174530)
One of the possible explanations for the crash in https://github.com/flutter/flutter/issues/168210#issuecomment-3130088036 is a null inline pass context. This makes that statically impossible (barring an allocation problem). tests: exempt, just a refactor ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
066982b537
commit
b897fb76a0
@ -871,9 +871,10 @@ void Canvas::ClipGeometry(const Geometry& geometry,
|
||||
|
||||
if (clip_state_result.clip_did_change) {
|
||||
// We only need to update the pass scissor if the clip state has changed.
|
||||
SetClipScissor(clip_coverage_stack_.CurrentClipCoverage(),
|
||||
*render_passes_.back().inline_pass_context->GetRenderPass(),
|
||||
GetGlobalPassPosition());
|
||||
SetClipScissor(
|
||||
clip_coverage_stack_.CurrentClipCoverage(),
|
||||
*render_passes_.back().GetInlinePassContext()->GetRenderPass(),
|
||||
GetGlobalPassPosition());
|
||||
}
|
||||
|
||||
++transform_stack_.back().clip_height;
|
||||
@ -892,16 +893,16 @@ void Canvas::ClipGeometry(const Geometry& geometry,
|
||||
entity.SetClipDepth(clip_depth);
|
||||
|
||||
GeometryResult geometry_result = geometry.GetPositionBuffer(
|
||||
renderer_, //
|
||||
entity, //
|
||||
*render_passes_.back().inline_pass_context->GetRenderPass() //
|
||||
renderer_, //
|
||||
entity, //
|
||||
*render_passes_.back().GetInlinePassContext()->GetRenderPass() //
|
||||
);
|
||||
clip_contents.SetGeometry(geometry_result);
|
||||
clip_coverage_stack_.GetLastReplayResult().clip_contents.SetGeometry(
|
||||
geometry_result);
|
||||
|
||||
clip_contents.Render(
|
||||
renderer_, *render_passes_.back().inline_pass_context->GetRenderPass(),
|
||||
renderer_, *render_passes_.back().GetInlinePassContext()->GetRenderPass(),
|
||||
clip_depth);
|
||||
}
|
||||
|
||||
@ -1217,9 +1218,9 @@ std::optional<Rect> Canvas::GetLocalCoverageLimit() const {
|
||||
FML_CHECK(!render_passes_.empty());
|
||||
const LazyRenderingConfig& back_render_pass = render_passes_.back();
|
||||
std::shared_ptr<Texture> back_texture =
|
||||
back_render_pass.inline_pass_context->GetTexture();
|
||||
back_render_pass.GetInlinePassContext()->GetTexture();
|
||||
FML_CHECK(back_texture) << "Context is valid:"
|
||||
<< back_render_pass.inline_pass_context->IsValid();
|
||||
<< back_render_pass.GetInlinePassContext()->IsValid();
|
||||
|
||||
// The maximum coverage of the subpass. Subpasses textures should never
|
||||
// extend outside the parent pass texture or the current clip coverage.
|
||||
@ -1509,7 +1510,7 @@ bool Canvas::Restore() {
|
||||
auto lazy_render_pass = std::move(render_passes_.back());
|
||||
render_passes_.pop_back();
|
||||
// Force the render pass to be constructed if it never was.
|
||||
lazy_render_pass.inline_pass_context->GetRenderPass();
|
||||
lazy_render_pass.GetInlinePassContext()->GetRenderPass();
|
||||
|
||||
SaveLayerState save_layer_state = save_layer_state_.back();
|
||||
save_layer_state_.pop_back();
|
||||
@ -1517,12 +1518,12 @@ bool Canvas::Restore() {
|
||||
|
||||
std::shared_ptr<Contents> contents = CreateContentsForSubpassTarget(
|
||||
save_layer_state.paint, //
|
||||
lazy_render_pass.inline_pass_context->GetTexture(), //
|
||||
lazy_render_pass.GetInlinePassContext()->GetTexture(), //
|
||||
Matrix::MakeTranslation(Vector3{-global_pass_position}) * //
|
||||
transform_stack_.back().transform //
|
||||
);
|
||||
|
||||
lazy_render_pass.inline_pass_context->EndPass();
|
||||
lazy_render_pass.GetInlinePassContext()->EndPass();
|
||||
|
||||
// Round the subpass texture position for pixel alignment with the parent
|
||||
// pass render target. By default, we draw subpass textures with nearest
|
||||
@ -1581,8 +1582,8 @@ bool Canvas::Restore() {
|
||||
}
|
||||
|
||||
element_entity.Render(
|
||||
renderer_, //
|
||||
*render_passes_.back().inline_pass_context->GetRenderPass() //
|
||||
renderer_, //
|
||||
*render_passes_.back().GetInlinePassContext()->GetRenderPass() //
|
||||
);
|
||||
clip_coverage_stack_.PopSubpass();
|
||||
transform_stack_.pop_back();
|
||||
@ -1606,9 +1607,9 @@ bool Canvas::Restore() {
|
||||
if (clip_state_result.clip_did_change) {
|
||||
// We only need to update the pass scissor if the clip state has changed.
|
||||
SetClipScissor(
|
||||
clip_coverage_stack_.CurrentClipCoverage(), //
|
||||
*render_passes_.back().inline_pass_context->GetRenderPass(), //
|
||||
GetGlobalPassPosition() //
|
||||
clip_coverage_stack_.CurrentClipCoverage(), //
|
||||
*render_passes_.back().GetInlinePassContext()->GetRenderPass(), //
|
||||
GetGlobalPassPosition() //
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1805,11 +1806,12 @@ void Canvas::AddRenderEntityToCurrentPass(Entity& entity, bool reuse_depth) {
|
||||
// with the current backdrop.
|
||||
if (render_passes_.back().IsApplyingClearColor()) {
|
||||
std::optional<Color> maybe_color = entity.AsBackgroundColor(
|
||||
render_passes_.back().inline_pass_context->GetTexture()->GetSize());
|
||||
render_passes_.back().GetInlinePassContext()->GetTexture()->GetSize());
|
||||
if (maybe_color.has_value()) {
|
||||
Color color = maybe_color.value();
|
||||
RenderTarget& render_target = render_passes_.back()
|
||||
.inline_pass_context->GetPassTarget()
|
||||
.GetInlinePassContext()
|
||||
->GetPassTarget()
|
||||
.GetRenderTarget();
|
||||
ColorAttachment attachment = render_target.GetColorAttachment(0);
|
||||
// Attachment.clear color needs to be premultiplied at all times, but the
|
||||
@ -1872,7 +1874,7 @@ void Canvas::AddRenderEntityToCurrentPass(Entity& entity, bool reuse_depth) {
|
||||
}
|
||||
|
||||
const std::shared_ptr<RenderPass>& result =
|
||||
render_passes_.back().inline_pass_context->GetRenderPass();
|
||||
render_passes_.back().GetInlinePassContext()->GetRenderPass();
|
||||
if (!result) {
|
||||
// Failure to produce a render pass should be explained by specific errors
|
||||
// in `InlinePassContext::GetRenderPass()`, so avoid log spam and don't
|
||||
@ -1884,7 +1886,7 @@ void Canvas::AddRenderEntityToCurrentPass(Entity& entity, bool reuse_depth) {
|
||||
}
|
||||
|
||||
RenderPass& Canvas::GetCurrentRenderPass() const {
|
||||
return *render_passes_.back().inline_pass_context->GetRenderPass();
|
||||
return *render_passes_.back().GetInlinePassContext()->GetRenderPass();
|
||||
}
|
||||
|
||||
void Canvas::SetBackdropData(
|
||||
@ -1912,8 +1914,8 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
|
||||
// In cases where there are no contents, we
|
||||
// could instead check the clear color and initialize a 1x2 CPU texture
|
||||
// instead of ending the pass.
|
||||
rendering_config.inline_pass_context->GetRenderPass();
|
||||
if (!rendering_config.inline_pass_context->EndPass()) {
|
||||
rendering_config.GetInlinePassContext()->GetRenderPass();
|
||||
if (!rendering_config.GetInlinePassContext()->EndPass()) {
|
||||
VALIDATION_LOG
|
||||
<< "Failed to end the current render pass in order to read from "
|
||||
"the backdrop texture and apply an advanced blend or backdrop "
|
||||
@ -1921,23 +1923,19 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
|
||||
// Note: adding this render pass ensures there are no later crashes from
|
||||
// unbalanced save layers. Ideally, this method would return false and the
|
||||
// renderer could handle that by terminating dispatch.
|
||||
render_passes_.push_back(LazyRenderingConfig(
|
||||
renderer_, std::move(rendering_config.entity_pass_target),
|
||||
std::move(rendering_config.inline_pass_context)));
|
||||
render_passes_.emplace_back(std::move(rendering_config));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Texture>& input_texture =
|
||||
rendering_config.inline_pass_context->GetTexture();
|
||||
rendering_config.GetInlinePassContext()->GetTexture();
|
||||
|
||||
if (!input_texture) {
|
||||
VALIDATION_LOG << "Failed to fetch the color texture in order to "
|
||||
"apply an advanced blend or backdrop filter.";
|
||||
|
||||
// Note: see above.
|
||||
render_passes_.push_back(LazyRenderingConfig(
|
||||
renderer_, std::move(rendering_config.entity_pass_target),
|
||||
std::move(rendering_config.inline_pass_context)));
|
||||
render_passes_.emplace_back(std::move(rendering_config));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1960,17 +1958,15 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
|
||||
LazyRenderingConfig(renderer_, std::move(entity_pass_target)));
|
||||
requires_readback_ = false;
|
||||
} else {
|
||||
render_passes_.push_back(LazyRenderingConfig(
|
||||
renderer_, std::move(rendering_config.entity_pass_target),
|
||||
std::move(rendering_config.inline_pass_context)));
|
||||
render_passes_.emplace_back(std::move(rendering_config));
|
||||
// If the current texture is being cached for a BDF we need to ensure we
|
||||
// don't recycle it during recording; remove it from the entity pass target.
|
||||
if (should_remove_texture) {
|
||||
render_passes_.back().entity_pass_target->RemoveSecondary();
|
||||
render_passes_.back().GetEntityPassTarget()->RemoveSecondary();
|
||||
}
|
||||
}
|
||||
RenderPass& current_render_pass =
|
||||
*render_passes_.back().inline_pass_context->GetRenderPass();
|
||||
*render_passes_.back().GetInlinePassContext()->GetRenderPass();
|
||||
|
||||
// Eagerly restore the BDF contents.
|
||||
|
||||
@ -2028,7 +2024,8 @@ bool Canvas::BlitToOnscreen(bool is_onscreen) {
|
||||
auto command_buffer = renderer_.GetContext()->CreateCommandBuffer();
|
||||
command_buffer->SetLabel("EntityPass Root Command Buffer");
|
||||
auto offscreen_target = render_passes_.back()
|
||||
.inline_pass_context->GetPassTarget()
|
||||
.GetInlinePassContext()
|
||||
->GetPassTarget()
|
||||
.GetRenderTarget();
|
||||
if (SupportsBlitToOnscreen()) {
|
||||
auto blit_pass = command_buffer->CreateBlitPass();
|
||||
@ -2093,8 +2090,8 @@ bool Canvas::EnsureFinalMipmapGeneration() const {
|
||||
|
||||
void Canvas::EndReplay() {
|
||||
FML_DCHECK(render_passes_.size() == 1u);
|
||||
render_passes_.back().inline_pass_context->GetRenderPass();
|
||||
render_passes_.back().inline_pass_context->EndPass(
|
||||
render_passes_.back().GetInlinePassContext()->GetRenderPass();
|
||||
render_passes_.back().GetInlinePassContext()->EndPass(
|
||||
/*is_onscreen=*/!requires_readback_ && is_onscreen_);
|
||||
backdrop_data_.clear();
|
||||
|
||||
@ -2119,4 +2116,24 @@ void Canvas::EndReplay() {
|
||||
Initialize(initial_cull_rect_);
|
||||
}
|
||||
|
||||
LazyRenderingConfig::LazyRenderingConfig(
|
||||
ContentContext& renderer,
|
||||
std::unique_ptr<EntityPassTarget> p_entity_pass_target)
|
||||
: entity_pass_target_(std::move(p_entity_pass_target)) {
|
||||
inline_pass_context_ =
|
||||
std::make_unique<InlinePassContext>(renderer, *entity_pass_target_);
|
||||
}
|
||||
|
||||
bool LazyRenderingConfig::IsApplyingClearColor() const {
|
||||
return !inline_pass_context_->IsActive();
|
||||
}
|
||||
|
||||
EntityPassTarget* LazyRenderingConfig::GetEntityPassTarget() const {
|
||||
return entity_pass_target_.get();
|
||||
}
|
||||
|
||||
InlinePassContext* LazyRenderingConfig::GetInlinePassContext() const {
|
||||
return inline_pass_context_.get();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -96,25 +96,23 @@ enum class ContentBoundsPromise {
|
||||
kMayClipContents,
|
||||
};
|
||||
|
||||
struct LazyRenderingConfig {
|
||||
std::unique_ptr<EntityPassTarget> entity_pass_target;
|
||||
std::unique_ptr<InlinePassContext> inline_pass_context;
|
||||
class LazyRenderingConfig {
|
||||
public:
|
||||
LazyRenderingConfig(ContentContext& renderer,
|
||||
std::unique_ptr<EntityPassTarget> p_entity_pass_target);
|
||||
|
||||
LazyRenderingConfig(LazyRenderingConfig&&) = default;
|
||||
|
||||
/// Whether or not the clear color texture can still be updated.
|
||||
bool IsApplyingClearColor() const { return !inline_pass_context->IsActive(); }
|
||||
bool IsApplyingClearColor() const;
|
||||
|
||||
LazyRenderingConfig(ContentContext& renderer,
|
||||
std::unique_ptr<EntityPassTarget> p_entity_pass_target)
|
||||
: entity_pass_target(std::move(p_entity_pass_target)) {
|
||||
inline_pass_context =
|
||||
std::make_unique<InlinePassContext>(renderer, *entity_pass_target);
|
||||
}
|
||||
EntityPassTarget* GetEntityPassTarget() const;
|
||||
|
||||
LazyRenderingConfig(ContentContext& renderer,
|
||||
std::unique_ptr<EntityPassTarget> entity_pass_target,
|
||||
std::unique_ptr<InlinePassContext> inline_pass_context)
|
||||
: entity_pass_target(std::move(entity_pass_target)),
|
||||
inline_pass_context(std::move(inline_pass_context)) {}
|
||||
InlinePassContext* GetInlinePassContext() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<EntityPassTarget> entity_pass_target_;
|
||||
std::unique_ptr<InlinePassContext> inline_pass_context_;
|
||||
};
|
||||
|
||||
class Canvas {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user