| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/shell/platform/embedder/embedder_surface_gl_impeller.h" | |
| #include <utility> | |
| #include "impeller/entity/gles/entity_shaders_gles.h" | |
| #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" | |
| #include "impeller/entity/gles/modern_shaders_gles.h" | |
| #include "impeller/renderer/backend/gles/context_gles.h" | |
| #include "impeller/renderer/backend/gles/proc_table_gles.h" | |
| #if IMPELLER_ENABLE_3D | |
| #include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogncheck | |
| #endif // IMPELLER_ENABLE_3D | |
| namespace flutter { | |
| class ReactorWorker final : public impeller::ReactorGLES::Worker { | |
| public: | |
| ReactorWorker() = default; | |
| // |ReactorGLES::Worker| | |
| bool CanReactorReactOnCurrentThreadNow( | |
| const impeller::ReactorGLES& reactor) const override { | |
| impeller::ReaderLock lock(mutex_); | |
| auto found = reactions_allowed_.find(std::this_thread::get_id()); | |
| if (found == reactions_allowed_.end()) { | |
| return false; | |
| } | |
| return found->second; | |
| } | |
| void SetReactionsAllowedOnCurrentThread(bool allowed) { | |
| impeller::WriterLock lock(mutex_); | |
| reactions_allowed_[std::this_thread::get_id()] = allowed; | |
| } | |
| private: | |
| mutable impeller::RWMutex mutex_; | |
| std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_); | |
| FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker); | |
| }; | |
| EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( | |
| EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table, | |
| bool fbo_reset_after_present, | |
| std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder) | |
| : gl_dispatch_table_(std::move(gl_dispatch_table)), | |
| fbo_reset_after_present_(fbo_reset_after_present), | |
| external_view_embedder_(std::move(external_view_embedder)), | |
| worker_(std::make_shared<ReactorWorker>()) { | |
| // Make sure all required members of the dispatch table are checked. | |
| if (!gl_dispatch_table_.gl_make_current_callback || | |
| !gl_dispatch_table_.gl_clear_current_callback || | |
| !gl_dispatch_table_.gl_present_callback || | |
| !gl_dispatch_table_.gl_fbo_callback || | |
| !gl_dispatch_table_.gl_populate_existing_damage || | |
| !gl_dispatch_table_.gl_proc_resolver) { | |
| return; | |
| } | |
| // Certain GL backends need to made current before any GL | |
| // state can be accessed. | |
| gl_dispatch_table_.gl_make_current_callback(); | |
| std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = { | |
| std::make_shared<fml::NonOwnedMapping>( | |
| impeller_entity_shaders_gles_data, | |
| impeller_entity_shaders_gles_length), | |
| std::make_shared<fml::NonOwnedMapping>( | |
| impeller_modern_shaders_gles_data, | |
| impeller_modern_shaders_gles_length), | |
| std::make_shared<fml::NonOwnedMapping>( | |
| impeller_framebuffer_blend_shaders_gles_data, | |
| impeller_framebuffer_blend_shaders_gles_length), | |
| #if IMPELLER_ENABLE_3D | |
| std::make_shared<fml::NonOwnedMapping>( | |
| impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), | |
| #endif // IMPELLER_ENABLE_3D | |
| }; | |
| auto gl = std::make_unique<impeller::ProcTableGLES>( | |
| gl_dispatch_table_.gl_proc_resolver); | |
| if (!gl->IsValid()) { | |
| return; | |
| } | |
| impeller_context_ = impeller::ContextGLES::Create( | |
| std::move(gl), shader_mappings, /*enable_gpu_tracing=*/false); | |
| if (!impeller_context_) { | |
| FML_LOG(ERROR) << "Could not create Impeller context."; | |
| return; | |
| } | |
| auto worker_id = impeller_context_->AddReactorWorker(worker_); | |
| if (!worker_id.has_value()) { | |
| FML_LOG(ERROR) << "Could not add reactor worker."; | |
| return; | |
| } | |
| gl_dispatch_table_.gl_clear_current_callback(); | |
| FML_LOG(IMPORTANT) << "Using the Impeller rendering backend (OpenGL)."; | |
| valid_ = true; | |
| } | |
| EmbedderSurfaceGLImpeller::~EmbedderSurfaceGLImpeller() = default; | |
| // |EmbedderSurface| | |
| bool EmbedderSurfaceGLImpeller::IsValid() const { | |
| return valid_; | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| std::unique_ptr<GLContextResult> | |
| EmbedderSurfaceGLImpeller::GLContextMakeCurrent() { | |
| worker_->SetReactionsAllowedOnCurrentThread(true); | |
| return std::make_unique<GLContextDefaultResult>( | |
| gl_dispatch_table_.gl_make_current_callback()); | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| bool EmbedderSurfaceGLImpeller::GLContextClearCurrent() { | |
| worker_->SetReactionsAllowedOnCurrentThread(false); | |
| return gl_dispatch_table_.gl_clear_current_callback(); | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| bool EmbedderSurfaceGLImpeller::GLContextPresent( | |
| const GLPresentInfo& present_info) { | |
| // Pass the present information to the embedder present callback. | |
| return gl_dispatch_table_.gl_present_callback(present_info); | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| GLFBOInfo EmbedderSurfaceGLImpeller::GLContextFBO( | |
| GLFrameInfo frame_info) const { | |
| // Get the FBO ID using the gl_fbo_callback and then get exiting damage by | |
| // passing that ID to the gl_populate_existing_damage. | |
| return gl_dispatch_table_.gl_populate_existing_damage( | |
| gl_dispatch_table_.gl_fbo_callback(frame_info)); | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| bool EmbedderSurfaceGLImpeller::GLContextFBOResetAfterPresent() const { | |
| return fbo_reset_after_present_; | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| SkMatrix EmbedderSurfaceGLImpeller::GLContextSurfaceTransformation() const { | |
| auto callback = gl_dispatch_table_.gl_surface_transformation_callback; | |
| if (!callback) { | |
| SkMatrix matrix; | |
| matrix.setIdentity(); | |
| return matrix; | |
| } | |
| return callback(); | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGLImpeller::GetGLProcResolver() | |
| const { | |
| return gl_dispatch_table_.gl_proc_resolver; | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| SurfaceFrame::FramebufferInfo | |
| EmbedderSurfaceGLImpeller::GLContextFramebufferInfo() const { | |
| // Enable partial repaint by default on the embedders. | |
| auto info = SurfaceFrame::FramebufferInfo{}; | |
| info.supports_readback = true; | |
| info.supports_partial_repaint = | |
| gl_dispatch_table_.gl_populate_existing_damage != nullptr; | |
| return info; | |
| } | |
| // |EmbedderSurface| | |
| std::unique_ptr<Surface> EmbedderSurfaceGLImpeller::CreateGPUSurface() { | |
| // Ensure that the GL context is current before creating the GPU surface. | |
| // GPUSurfaceGLImpeller initialization will set up shader pipelines, and the | |
| // current thread needs to be able to execute reactor operations. | |
| GLContextMakeCurrent(); | |
| return std::make_unique<GPUSurfaceGLImpeller>( | |
| this, // GPU surface GL delegate | |
| impeller_context_, // Impeller context | |
| !external_view_embedder_ // render to surface | |
| ); | |
| } | |
| // |EmbedderSurface| | |
| std::shared_ptr<impeller::Context> | |
| EmbedderSurfaceGLImpeller::CreateImpellerContext() const { | |
| return impeller_context_; | |
| } | |
| // |EmbedderSurface| | |
| sk_sp<GrDirectContext> EmbedderSurfaceGLImpeller::CreateResourceContext() | |
| const { | |
| if (gl_dispatch_table_.gl_make_resource_current_callback()) { | |
| worker_->SetReactionsAllowedOnCurrentThread(true); | |
| } else { | |
| FML_DLOG(ERROR) << "Could not make the resource context current."; | |
| worker_->SetReactionsAllowedOnCurrentThread(false); | |
| } | |
| return nullptr; | |
| } | |
| } // namespace flutter |