[Impeller] Add a TextureGLES API for wrapping a framebuffer and use it to implement OpenGL FBO targets in the embedder library (flutter/engine#51269)

The Linux embedder is rendering to a FlutterOpenGLFramebuffer that specifies an OpenGL FBO.  This API allows the embedder to create a color attachment that wraps the FBO so that the render pass will bind to that FBO during encoding.
This commit is contained in:
Jason Simmons 2024-04-01 13:19:14 -07:00 committed by GitHub
parent a16ee1a7ac
commit 1bfca0c056
6 changed files with 57 additions and 19 deletions

View File

@ -170,19 +170,22 @@ struct RenderPassData {
}
});
const auto is_default_fbo =
TextureGLES::Cast(*pass_data.color_attachment).IsWrapped();
TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
const bool is_default_fbo = color_gles.IsWrapped();
if (!is_default_fbo) {
if (is_default_fbo) {
if (color_gles.GetFBO().has_value()) {
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
gl.BindFramebuffer(GL_FRAMEBUFFER, *color_gles.GetFBO());
}
} else {
// Create and bind an offscreen FBO.
gl.GenFramebuffers(1u, &fbo);
gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
if (auto color = TextureGLES::Cast(pass_data.color_attachment.get())) {
if (!color->SetAsFramebufferAttachment(
GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) {
return false;
}
if (!color_gles.SetAsFramebufferAttachment(
GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) {
return false;
}
if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) {

View File

@ -68,21 +68,30 @@ HandleType ToHandleType(TextureGLES::Type type) {
}
TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc)
: TextureGLES(std::move(reactor), desc, false) {}
: TextureGLES(std::move(reactor), desc, false, std::nullopt) {}
TextureGLES::TextureGLES(ReactorGLES::Ref reactor,
TextureDescriptor desc,
enum IsWrapped wrapped)
: TextureGLES(std::move(reactor), desc, true) {}
: TextureGLES(std::move(reactor), desc, true, std::nullopt) {}
std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(ReactorGLES::Ref reactor,
TextureDescriptor desc,
GLuint fbo) {
return std::shared_ptr<TextureGLES>(
new TextureGLES(std::move(reactor), desc, true, fbo));
}
TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool is_wrapped)
bool is_wrapped,
std::optional<GLuint> fbo)
: Texture(desc),
reactor_(std::move(reactor)),
type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
handle_(reactor_->CreateHandle(ToHandleType(type_))),
is_wrapped_(is_wrapped) {
is_wrapped_(is_wrapped),
wrapped_fbo_(fbo) {
// Ensure the texture descriptor itself is valid.
if (!GetTextureDescriptor().IsValid()) {
VALIDATION_LOG << "Invalid texture descriptor.";

View File

@ -32,6 +32,10 @@ class TextureGLES final : public Texture,
TextureDescriptor desc,
IsWrapped wrapped);
static std::shared_ptr<TextureGLES> WrapFBO(ReactorGLES::Ref reactor,
TextureDescriptor desc,
GLuint fbo);
// |Texture|
~TextureGLES() override;
@ -54,6 +58,8 @@ class TextureGLES final : public Texture,
bool IsWrapped() const { return is_wrapped_; }
std::optional<GLuint> GetFBO() const { return wrapped_fbo_; }
private:
friend class AllocatorMTL;
@ -62,11 +68,13 @@ class TextureGLES final : public Texture,
HandleGLES handle_;
mutable bool contents_initialized_ = false;
const bool is_wrapped_;
const std::optional<GLuint> wrapped_fbo_;
bool is_valid_ = false;
TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool is_wrapped);
bool is_wrapped,
std::optional<GLuint> fbo);
// |Texture|
void SetLabel(std::string_view label) override;

View File

@ -979,9 +979,8 @@ MakeRenderTargetFromBackingStoreImpeller(
color0_tex.storage_mode = impeller::StorageMode::kDevicePrivate;
impeller::ColorAttachment color0;
color0.texture = std::make_shared<impeller::TextureGLES>(
gl_context.GetReactor(), color0_tex,
impeller::TextureGLES::IsWrapped::kWrapped);
color0.texture = impeller::TextureGLES::WrapFBO(
gl_context.GetReactor(), color0_tex, framebuffer->name);
color0.clear_color = impeller::Color::DarkSlateGray();
color0.load_action = impeller::LoadAction::kClear;
color0.store_action = impeller::StoreAction::kStore;

View File

@ -4747,7 +4747,8 @@ TEST_F(EmbedderTest,
}
TEST_F(EmbedderTest, CanRenderWithImpellerOpenGL) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
EmbedderTestContextGL& context = static_cast<EmbedderTestContextGL&>(
GetEmbedderContext(EmbedderTestContextType::kOpenGLContext));
EmbedderConfigBuilder builder(context);
bool present_called = false;
@ -4768,6 +4769,24 @@ TEST_F(EmbedderTest, CanRenderWithImpellerOpenGL) {
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Bind to an arbitrary FBO in order to verify that Impeller binds to the
// provided FBO during rendering.
typedef void (*glGenFramebuffersProc)(GLsizei n, GLuint* ids);
typedef void (*glBindFramebufferProc)(GLenum target, GLuint framebuffer);
auto glGenFramebuffers = reinterpret_cast<glGenFramebuffersProc>(
context.GLGetProcAddress("glGenFramebuffers"));
auto glBindFramebuffer = reinterpret_cast<glBindFramebufferProc>(
context.GLGetProcAddress("glBindFramebuffer"));
const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell();
fml::AutoResetWaitableEvent raster_event;
shell.GetTaskRunners().GetRasterTaskRunner()->PostTask([&] {
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
raster_event.Signal();
});
raster_event.Wait();
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);

View File

@ -62,6 +62,8 @@ class EmbedderTestContextGL : public EmbedderTestContext {
void GLPopulateExistingDamage(const intptr_t id,
FlutterDamage* existing_damage);
void* GLGetProcAddress(const char* name);
protected:
virtual void SetupCompositor() override;
@ -88,8 +90,6 @@ class EmbedderTestContextGL : public EmbedderTestContext {
bool GLMakeResourceCurrent();
void* GLGetProcAddress(const char* name);
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestContextGL);
};