From 1bfca0c0564a6c7002277aa37c71053232cd76a2 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 1 Apr 2024 13:19:14 -0700 Subject: [PATCH] [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. --- .../renderer/backend/gles/render_pass_gles.cc | 19 ++++++++++------- .../renderer/backend/gles/texture_gles.cc | 17 +++++++++++---- .../renderer/backend/gles/texture_gles.h | 10 ++++++++- .../shell/platform/embedder/embedder.cc | 5 ++--- .../embedder/tests/embedder_gl_unittests.cc | 21 ++++++++++++++++++- .../embedder/tests/embedder_test_context_gl.h | 4 ++-- 6 files changed, 57 insertions(+), 19 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc index 6cc570ff055..fb707139a7b 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc @@ -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())) { diff --git a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc index 23e867bc069..c656b0d7d0e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.cc @@ -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::WrapFBO(ReactorGLES::Ref reactor, + TextureDescriptor desc, + GLuint fbo) { + return std::shared_ptr( + new TextureGLES(std::move(reactor), desc, true, fbo)); +} TextureGLES::TextureGLES(std::shared_ptr reactor, TextureDescriptor desc, - bool is_wrapped) + bool is_wrapped, + std::optional 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."; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h index adfc2e49d4b..5a8c3a79b33 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h @@ -32,6 +32,10 @@ class TextureGLES final : public Texture, TextureDescriptor desc, IsWrapped wrapped); + static std::shared_ptr 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 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 wrapped_fbo_; bool is_valid_ = false; TextureGLES(std::shared_ptr reactor, TextureDescriptor desc, - bool is_wrapped); + bool is_wrapped, + std::optional fbo); // |Texture| void SetLabel(std::string_view label) override; diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 52e49646c00..5b89bcfbc61 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -979,9 +979,8 @@ MakeRenderTargetFromBackingStoreImpeller( color0_tex.storage_mode = impeller::StorageMode::kDevicePrivate; impeller::ColorAttachment color0; - color0.texture = std::make_shared( - 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; diff --git a/engine/src/flutter/shell/platform/embedder/tests/embedder_gl_unittests.cc b/engine/src/flutter/shell/platform/embedder/tests/embedder_gl_unittests.cc index 0720e54a458..0d01bc19187 100644 --- a/engine/src/flutter/shell/platform/embedder/tests/embedder_gl_unittests.cc +++ b/engine/src/flutter/shell/platform/embedder/tests/embedder_gl_unittests.cc @@ -4747,7 +4747,8 @@ TEST_F(EmbedderTest, } TEST_F(EmbedderTest, CanRenderWithImpellerOpenGL) { - auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext); + EmbedderTestContextGL& context = static_cast( + 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( + context.GLGetProcAddress("glGenFramebuffers")); + auto glBindFramebuffer = reinterpret_cast( + 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); diff --git a/engine/src/flutter/shell/platform/embedder/tests/embedder_test_context_gl.h b/engine/src/flutter/shell/platform/embedder/tests/embedder_test_context_gl.h index 3241b318d8d..f23f98faeff 100644 --- a/engine/src/flutter/shell/platform/embedder/tests/embedder_test_context_gl.h +++ b/engine/src/flutter/shell/platform/embedder/tests/embedder_test_context_gl.h @@ -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); };