[CP][Impeller] Fix crash when using BackdropFilters in certain GLES drivers. (#163581)

This is a cherry picked of https://github.com/flutter/flutter/pull/163345 and https://github.com/flutter/flutter/pull/163327 combined, as they both are parts of the same bug.

The issues are:
  https://github.com/flutter/flutter/issues/163304
  https://github.com/flutter/flutter/issues/163421
  
And potentially others, though we don't have clear repros or stack traces for all of them.
This commit is contained in:
Jonah Williams 2025-02-19 15:21:55 -08:00 committed by GitHub
parent 652d388b0a
commit 383d56ed91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 65 additions and 4 deletions

View File

@ -30,6 +30,7 @@ INTERNAL NOTE
### [3.29.0](https://github.com/flutter/flutter/releases/tag/3.29.0)
Initial stable release.
- [flutter/163304](https://github.com/flutter/flutter/issues/163304) Fixes crash when using backdrop filter on GLES backend.
- [flutter/161334](https://github.com/flutter/flutter/issues/161334) Disable Vulkan on certain Xclipse GPU models.
## Flutter 3.27 Changes

View File

@ -1677,6 +1677,14 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
return input_texture;
}
bool Canvas::SupportsBlitToOnscreen() const {
return renderer_.GetContext()
->GetCapabilities()
->SupportsTextureToTextureBlits() &&
renderer_.GetContext()->GetBackendType() !=
Context::BackendType::kOpenGLES;
}
bool Canvas::BlitToOnscreen(bool is_onscreen) {
auto command_buffer = renderer_.GetContext()->CreateCommandBuffer();
command_buffer->SetLabel("EntityPass Root Command Buffer");
@ -1684,9 +1692,7 @@ bool Canvas::BlitToOnscreen(bool is_onscreen) {
.inline_pass_context->GetPassTarget()
.GetRenderTarget();
if (renderer_.GetContext()
->GetCapabilities()
->SupportsTextureToTextureBlits()) {
if (SupportsBlitToOnscreen()) {
auto blit_pass = command_buffer->CreateBlitPass();
blit_pass->AddCopy(offscreen_target.GetRenderTargetTexture(),
render_target_.GetRenderTargetTexture());

View File

@ -251,6 +251,16 @@ class Canvas {
// Visible for testing.
bool RequiresReadback() const { return requires_readback_; }
// Whether the current device has the capabilities to blit an offscreen
// texture into the onscreen.
//
// This requires the availibility of the blit framebuffer command, but is
// disabled for GLES. A simple glBlitFramebuffer does not support resolving
// different sample counts which may be present in GLES when using MSAA.
//
// Visible for testing.
bool SupportsBlitToOnscreen() const;
private:
ContentContext& renderer_;
RenderTarget render_target_;

View File

@ -14,6 +14,7 @@
#include "impeller/display_list/aiks_unittests.h"
#include "impeller/display_list/canvas.h"
#include "impeller/geometry/geometry_asserts.h"
#include "impeller/playground/playground.h"
#include "impeller/renderer/render_target.h"
namespace impeller {
@ -278,5 +279,17 @@ TEST_P(AiksTest, BackdropCountDownWithNestedSaveLayers) {
EXPECT_TRUE(canvas->RequiresReadback());
}
TEST_P(AiksTest, SupportsBlitToOnscreen) {
ContentContext context(GetContext(), nullptr);
auto canvas = CreateTestCanvas(context, Rect::MakeLTRB(0, 0, 100, 100),
/*requires_readback=*/true);
if (GetBackend() == PlaygroundBackend::kOpenGLES) {
EXPECT_FALSE(canvas->SupportsBlitToOnscreen());
} else {
EXPECT_TRUE(canvas->SupportsBlitToOnscreen());
}
}
} // namespace testing
} // namespace impeller

View File

@ -65,4 +65,23 @@ TEST_P(TextureGLESTest, CanSetSyncFence) {
ASSERT_FALSE(sync_fence.has_value());
}
TEST_P(TextureGLESTest, Binds2DTexture) {
TextureDescriptor desc;
desc.storage_mode = StorageMode::kDevicePrivate;
desc.size = {100, 100};
desc.format = PixelFormat::kR8G8B8A8UNormInt;
desc.type = TextureType::kTexture2DMultisample;
desc.sample_count = SampleCount::kCount4;
auto texture = GetContext()->GetResourceAllocator()->CreateTexture(desc);
ASSERT_TRUE(texture);
EXPECT_EQ(
TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_READ_FRAMEBUFFER),
TextureGLES::Type::kTexture);
EXPECT_EQ(TextureGLES::Cast(*texture).ComputeTypeForBinding(GL_FRAMEBUFFER),
TextureGLES::Type::kTextureMultisampled);
}
} // namespace impeller::testing

View File

@ -392,6 +392,15 @@ static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
FML_UNREACHABLE();
}
TextureGLES::Type TextureGLES::ComputeTypeForBinding(GLenum target) const {
// When binding to a GL_READ_FRAMEBUFFER, any multisampled
// textures must be bound as single sampled.
if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
return Type::kTexture;
}
return type_;
}
void TextureGLES::InitializeContentsIfNecessary() const {
if (!IsValid() || slices_initialized_[0]) {
return;
@ -588,7 +597,7 @@ bool TextureGLES::SetAsFramebufferAttachment(
}
const auto& gl = reactor_->GetProcTable();
switch (type_) {
switch (ComputeTypeForBinding(target)) {
case Type::kTexture:
gl.FramebufferTexture2D(target, // target
ToAttachmentType(attachment_type), // attachment

View File

@ -144,6 +144,9 @@ class TextureGLES final : public Texture,
// Visible for testing.
std::optional<HandleGLES> GetSyncFence() const;
// visible for testing
Type ComputeTypeForBinding(GLenum target) const;
private:
std::shared_ptr<ReactorGLES> reactor_;
const Type type_;