Skip to content

Commit d99a438

Browse files
authored
[Impeller] Wrap provided FBO instead of defaulting to FBO0 and cleanup/document the texture API. (flutter#56543)
Previously, the FBO argument was dropped on the floor. The API was also confusing as the Android subsystems were using the IsWrapped call to sidestep texture contents initialization without actually performing any wrapping. Now, there are separate and documented calls to wrap a texture, wrap a framebuffer (as a texture), and to create a placeholder texture. Callers can also mark any texture as being initialized out of band instead of depending on overloading the meaning of IsWrapped. Fixes flutter/flutter#158486
1 parent ef760d6 commit d99a438

File tree

10 files changed

+163
-64
lines changed

10 files changed

+163
-64
lines changed

impeller/renderer/backend/gles/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impeller_component("gles_unittests") {
2424
"test/proc_table_gles_unittests.cc",
2525
"test/reactor_unittests.cc",
2626
"test/specialization_constants_unittests.cc",
27+
"test/surface_gles_unittests.cc",
2728
]
2829
deps = [
2930
":gles",

impeller/renderer/backend/gles/surface_gles.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ std::unique_ptr<Surface> SurfaceGLES::WrapFBO(
3434
color0_tex.storage_mode = StorageMode::kDevicePrivate;
3535

3636
ColorAttachment color0;
37-
color0.texture = std::make_shared<TextureGLES>(
38-
gl_context.GetReactor(), color0_tex, TextureGLES::IsWrapped::kWrapped);
37+
color0.texture =
38+
TextureGLES::WrapFBO(gl_context.GetReactor(), color0_tex, fbo);
3939
color0.clear_color = Color::DarkSlateGray();
4040
color0.load_action = LoadAction::kClear;
4141
color0.store_action = StoreAction::kStore;
@@ -47,9 +47,10 @@ std::unique_ptr<Surface> SurfaceGLES::WrapFBO(
4747
depth_stencil_texture_desc.usage = TextureUsage::kRenderTarget;
4848
depth_stencil_texture_desc.sample_count = SampleCount::kCount1;
4949

50-
auto depth_stencil_tex = std::make_shared<TextureGLES>(
51-
gl_context.GetReactor(), depth_stencil_texture_desc,
52-
TextureGLES::IsWrapped::kWrapped);
50+
auto depth_stencil_tex =
51+
TextureGLES::CreatePlaceholder(gl_context.GetReactor(), //
52+
depth_stencil_texture_desc //
53+
);
5354

5455
DepthAttachment depth0;
5556
depth0.clear_depth = 0;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/impeller/playground/playground_test.h"
6+
#include "flutter/impeller/renderer/backend/gles/context_gles.h"
7+
#include "flutter/impeller/renderer/backend/gles/surface_gles.h"
8+
#include "flutter/impeller/renderer/backend/gles/texture_gles.h"
9+
#include "flutter/testing/testing.h"
10+
11+
namespace impeller::testing {
12+
13+
using SurfaceGLESTest = PlaygroundTest;
14+
INSTANTIATE_OPENGLES_PLAYGROUND_SUITE(SurfaceGLESTest);
15+
16+
TEST_P(SurfaceGLESTest, CanWrapNonZeroFBO) {
17+
const GLuint fbo = 1988;
18+
auto surface =
19+
SurfaceGLES::WrapFBO(GetContext(), []() { return true; }, fbo,
20+
PixelFormat::kR8G8B8A8UNormInt, {100, 100});
21+
ASSERT_TRUE(!!surface);
22+
ASSERT_TRUE(surface->IsValid());
23+
ASSERT_TRUE(surface->GetRenderTarget().HasColorAttachment(0));
24+
const auto& texture = TextureGLES::Cast(
25+
*(surface->GetRenderTarget().GetColorAttachments().at(0).texture));
26+
auto wrapped = texture.GetFBO();
27+
ASSERT_TRUE(wrapped.has_value());
28+
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
29+
ASSERT_EQ(wrapped.value(), fbo);
30+
}
31+
32+
} // namespace impeller::testing

impeller/renderer/backend/gles/texture_gles.cc

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -140,34 +140,52 @@ HandleType ToHandleType(TextureGLES::Type type) {
140140
FML_UNREACHABLE();
141141
}
142142

143-
TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc)
144-
: TextureGLES(std::move(reactor), desc, false, std::nullopt, std::nullopt) {
145-
}
146-
147-
TextureGLES::TextureGLES(ReactorGLES::Ref reactor,
148-
TextureDescriptor desc,
149-
enum IsWrapped wrapped)
150-
: TextureGLES(std::move(reactor), desc, true, std::nullopt, std::nullopt) {}
151-
152-
TextureGLES::TextureGLES(ReactorGLES::Ref reactor,
153-
TextureDescriptor desc,
154-
HandleGLES external_handle)
155-
: TextureGLES(std::move(reactor),
156-
desc,
157-
true,
158-
std::nullopt,
159-
external_handle) {}
160-
161143
std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(ReactorGLES::Ref reactor,
162144
TextureDescriptor desc,
163145
GLuint fbo) {
164-
return std::shared_ptr<TextureGLES>(
165-
new TextureGLES(std::move(reactor), desc, true, fbo, std::nullopt));
146+
auto texture = std::shared_ptr<TextureGLES>(
147+
new TextureGLES(std::move(reactor), desc, fbo, std::nullopt));
148+
if (!texture->IsValid()) {
149+
return nullptr;
150+
}
151+
return texture;
152+
}
153+
154+
std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
155+
ReactorGLES::Ref reactor,
156+
TextureDescriptor desc,
157+
HandleGLES external_handle) {
158+
if (external_handle.IsDead()) {
159+
VALIDATION_LOG << "Cannot wrap a dead handle.";
160+
return nullptr;
161+
}
162+
if (external_handle.type != HandleType::kTexture) {
163+
VALIDATION_LOG << "Cannot wrap a non-texture handle.";
164+
return nullptr;
165+
}
166+
auto texture = std::shared_ptr<TextureGLES>(
167+
new TextureGLES(std::move(reactor), desc, std::nullopt, external_handle));
168+
if (!texture->IsValid()) {
169+
return nullptr;
170+
}
171+
return texture;
172+
}
173+
174+
std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
175+
ReactorGLES::Ref reactor,
176+
TextureDescriptor desc) {
177+
return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
166178
}
167179

180+
TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc)
181+
: TextureGLES(std::move(reactor), //
182+
desc, //
183+
std::nullopt, //
184+
std::nullopt //
185+
) {}
186+
168187
TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
169188
TextureDescriptor desc,
170-
bool is_wrapped,
171189
std::optional<GLuint> fbo,
172190
std::optional<HandleGLES> external_handle)
173191
: Texture(desc),
@@ -176,7 +194,7 @@ TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
176194
handle_(external_handle.has_value()
177195
? external_handle.value()
178196
: reactor_->CreateHandle(ToHandleType(type_))),
179-
is_wrapped_(is_wrapped),
197+
is_wrapped_(fbo.has_value() || external_handle.has_value()),
180198
wrapped_fbo_(fbo) {
181199
// Ensure the texture descriptor itself is valid.
182200
if (!GetTextureDescriptor().IsValid()) {
@@ -479,6 +497,12 @@ bool TextureGLES::Bind() const {
479497
return true;
480498
}
481499

500+
void TextureGLES::MarkContentsInitialized() {
501+
for (size_t i = 0; i < slices_initialized_.size(); i++) {
502+
slices_initialized_[i] = true;
503+
}
504+
}
505+
482506
void TextureGLES::MarkSliceInitialized(size_t slice) const {
483507
slices_initialized_[slice] = true;
484508
}

impeller/renderer/backend/gles/texture_gles.h

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,56 @@ class TextureGLES final : public Texture,
2424
kRenderBufferMultisampled,
2525
};
2626

27-
enum class IsWrapped {
28-
kWrapped,
29-
};
30-
31-
TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc);
32-
33-
TextureGLES(ReactorGLES::Ref reactor,
34-
TextureDescriptor desc,
35-
IsWrapped wrapped);
36-
37-
TextureGLES(ReactorGLES::Ref reactor,
38-
TextureDescriptor desc,
39-
HandleGLES external_handle);
40-
27+
//----------------------------------------------------------------------------
28+
/// @brief Create a texture by wrapping an external framebuffer object
29+
/// whose lifecycle is owned by the caller.
30+
///
31+
/// This is useful for creating a render target for the default
32+
/// window managed framebuffer.
33+
///
34+
/// @param[in] reactor The reactor
35+
/// @param[in] desc The description
36+
/// @param[in] fbo The fbo
37+
///
38+
/// @return If a texture representation of the framebuffer could be
39+
/// created.
40+
///
4141
static std::shared_ptr<TextureGLES> WrapFBO(ReactorGLES::Ref reactor,
4242
TextureDescriptor desc,
4343
GLuint fbo);
4444

45+
//----------------------------------------------------------------------------
46+
/// @brief Create a texture by wrapping an external OpenGL texture
47+
/// handle. Ownership of the texture handle is assumed by the
48+
/// reactor.
49+
///
50+
/// @param[in] reactor The reactor
51+
/// @param[in] desc The description
52+
/// @param[in] external_handle The external handle
53+
///
54+
/// @return If a texture representation of the framebuffer could be
55+
/// created.
56+
///
57+
static std::shared_ptr<TextureGLES> WrapTexture(ReactorGLES::Ref reactor,
58+
TextureDescriptor desc,
59+
HandleGLES external_handle);
60+
61+
//----------------------------------------------------------------------------
62+
/// @brief Create a "texture" that is never expected to be bound/unbound
63+
/// explicitly or initialized in any way. It only exists to setup
64+
/// a render pass description.
65+
///
66+
/// @param[in] reactor The reactor
67+
/// @param[in] desc The description
68+
///
69+
/// @return If a texture placeholder could be created.
70+
///
71+
static std::shared_ptr<TextureGLES> CreatePlaceholder(
72+
ReactorGLES::Ref reactor,
73+
TextureDescriptor desc);
74+
75+
TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc);
76+
4577
// |Texture|
4678
~TextureGLES() override;
4779

@@ -69,9 +101,22 @@ class TextureGLES final : public Texture,
69101

70102
std::optional<GLuint> GetFBO() const;
71103

72-
// For non cubemap textures, 0 indicates uninitialized and 1 indicates
73-
// initialized. For cubemap textures, each face is initialized separately with
74-
// each bit tracking the initialization of the corresponding slice.
104+
//----------------------------------------------------------------------------
105+
/// @brief Indicates that all texture storage has already been allocated
106+
/// and contents initialized.
107+
///
108+
/// This is similar to calling `MarkSliceInitialized` with all
109+
/// slices.
110+
///
111+
/// @see MarkSliceInitialized.
112+
///
113+
void MarkContentsInitialized();
114+
115+
//----------------------------------------------------------------------------
116+
/// @brief Indicates that a specific texture slice has been initialized.
117+
///
118+
/// @param[in] slice The slice to mark as being initialized.
119+
///
75120
void MarkSliceInitialized(size_t slice) const;
76121

77122
bool IsSliceInitialized(size_t slice) const;
@@ -87,7 +132,6 @@ class TextureGLES final : public Texture,
87132

88133
TextureGLES(std::shared_ptr<ReactorGLES> reactor,
89134
TextureDescriptor desc,
90-
bool is_wrapped,
91135
std::optional<GLuint> fbo,
92136
std::optional<HandleGLES> external_handle);
93137

impeller/toolkit/interop/impeller.cc

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -528,13 +528,6 @@ ImpellerTexture ImpellerTextureCreateWithOpenGLTextureHandleNew(
528528
const auto& impeller_context_gl = ContextGLES::Cast(*impeller_context);
529529
const auto& reactor = impeller_context_gl.GetReactor();
530530

531-
auto wrapped_external_gl_handle =
532-
reactor->CreateHandle(HandleType::kTexture, external_gl_handle);
533-
if (wrapped_external_gl_handle.IsDead()) {
534-
VALIDATION_LOG << "Could not wrap external handle.";
535-
return nullptr;
536-
}
537-
538531
TextureDescriptor desc;
539532
desc.storage_mode = StorageMode::kDevicePrivate;
540533
desc.type = TextureType::kTexture2D;
@@ -543,9 +536,11 @@ ImpellerTexture ImpellerTextureCreateWithOpenGLTextureHandleNew(
543536
desc.mip_count = std::min(descriptor->mip_count, 1u);
544537
desc.usage = TextureUsage::kShaderRead;
545538
desc.compression_type = CompressionType::kLossless;
546-
auto texture = std::make_shared<TextureGLES>(reactor, //
547-
desc, //
548-
wrapped_external_gl_handle //
539+
540+
auto texture = TextureGLES::WrapTexture(
541+
reactor, //
542+
desc, //
543+
reactor->CreateHandle(HandleType::kTexture, external_gl_handle) //
549544
);
550545
if (!texture || !texture->IsValid()) {
551546
VALIDATION_LOG << "Could not wrap external texture.";

shell/platform/android/image_external_texture_gl_impeller.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ sk_sp<flutter::DlImage> ImageExternalTextureGLImpeller::CreateDlImage(
3838
static_cast<int>(bounds.height())};
3939
desc.mip_count = 1;
4040
auto texture = std::make_shared<impeller::TextureGLES>(
41-
impeller_context_->GetReactor(), desc,
42-
impeller::TextureGLES::IsWrapped::kWrapped);
41+
impeller_context_->GetReactor(), desc);
42+
// The contents will be initialized later in the call to
43+
// `glEGLImageTargetTexture2DOES` instead of by Impeller.
44+
texture->MarkContentsInitialized();
4345
texture->SetCoordinateSystem(
4446
impeller::TextureCoordinateSystem::kUploadFromHost);
4547
if (!texture->Bind()) {

shell/platform/android/surface_texture_external_texture_gl_impeller.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ void SurfaceTextureExternalTextureGLImpeller::ProcessFrame(
3333
static_cast<int>(bounds.height())};
3434
desc.mip_count = 1;
3535
texture_ = std::make_shared<impeller::TextureGLES>(
36-
impeller_context_->GetReactor(), desc,
37-
impeller::TextureGLES::IsWrapped::kWrapped);
36+
impeller_context_->GetReactor(), desc);
37+
// The contents will be initialized later in the call to `Attach` instead of
38+
// by Impeller.
39+
texture_->MarkContentsInitialized();
3840
texture_->SetCoordinateSystem(
3941
impeller::TextureCoordinateSystem::kUploadFromHost);
4042
auto maybe_handle = texture_->GetGLHandle();

shell/platform/embedder/embedder.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,9 +1196,8 @@ MakeRenderTargetFromBackingStoreImpeller(
11961196
depth_stencil_texture_desc.sample_count = impeller::SampleCount::kCount1;
11971197
}
11981198

1199-
auto depth_stencil_tex = std::make_shared<impeller::TextureGLES>(
1200-
gl_context.GetReactor(), depth_stencil_texture_desc,
1201-
impeller::TextureGLES::IsWrapped::kWrapped);
1199+
auto depth_stencil_tex = impeller::TextureGLES::CreatePlaceholder(
1200+
gl_context.GetReactor(), depth_stencil_texture_desc);
12021201

12031202
impeller::DepthAttachment depth0;
12041203
depth0.clear_depth = 0;

shell/platform/embedder/embedder_external_texture_gl.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpeller(
147147
impeller::HandleGLES handle = context.GetReactor()->CreateHandle(
148148
impeller::HandleType::kTexture, texture->target);
149149
std::shared_ptr<impeller::TextureGLES> image =
150-
std::make_shared<impeller::TextureGLES>(context.GetReactor(), desc,
151-
handle);
150+
impeller::TextureGLES::WrapTexture(context.GetReactor(), desc, handle);
152151

153152
if (!image) {
154153
// In case Skia rejects the image, call the release proc so that

0 commit comments

Comments
 (0)