| // 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/android/android_context_gl_impeller.h" | |
| #include "flutter/impeller/renderer/backend/gles/context_gles.h" | |
| #include "flutter/impeller/renderer/backend/gles/proc_table_gles.h" | |
| #include "flutter/impeller/renderer/backend/gles/reactor_gles.h" | |
| #include "flutter/impeller/toolkit/egl/context.h" | |
| #include "flutter/impeller/toolkit/egl/surface.h" | |
| #include "impeller/entity/gles/entity_shaders_gles.h" | |
| #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" | |
| #if IMPELLER_ENABLE_3D | |
| // This include was turned to an error since it breaks GN. | |
| #include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogncheck | |
| #endif // IMPELLER_ENABLE_3D | |
| namespace flutter { | |
| class AndroidContextGLImpeller::ReactorWorker final | |
| : public impeller::ReactorGLES::Worker { | |
| public: | |
| ReactorWorker() = default; | |
| // |impeller::ReactorGLES::Worker| | |
| ~ReactorWorker() override = default; | |
| // |impeller::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); | |
| }; | |
| static std::shared_ptr<impeller::Context> CreateImpellerContext( | |
| const std::shared_ptr<impeller::ReactorGLES::Worker>& worker, | |
| bool enable_gpu_tracing) { | |
| auto proc_table = std::make_unique<impeller::ProcTableGLES>( | |
| impeller::egl::CreateProcAddressResolver()); | |
| if (!proc_table->IsValid()) { | |
| FML_LOG(ERROR) << "Could not create OpenGL proc table."; | |
| return nullptr; | |
| } | |
| 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_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 context = impeller::ContextGLES::Create( | |
| std::move(proc_table), shader_mappings, enable_gpu_tracing); | |
| if (!context) { | |
| FML_LOG(ERROR) << "Could not create OpenGLES Impeller Context."; | |
| return nullptr; | |
| } | |
| if (!context->AddReactorWorker(worker).has_value()) { | |
| FML_LOG(ERROR) << "Could not add reactor worker."; | |
| return nullptr; | |
| } | |
| FML_LOG(IMPORTANT) << "Using the Impeller rendering backend (OpenGLES)."; | |
| return context; | |
| } | |
| AndroidContextGLImpeller::AndroidContextGLImpeller( | |
| std::unique_ptr<impeller::egl::Display> display, | |
| bool enable_gpu_tracing) | |
| : AndroidContext(AndroidRenderingAPI::kImpellerOpenGLES), | |
| reactor_worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())), | |
| display_(std::move(display)) { | |
| if (!display_ || !display_->IsValid()) { | |
| FML_DLOG(ERROR) << "Could not create context with invalid EGL display."; | |
| return; | |
| } | |
| impeller::egl::ConfigDescriptor desc; | |
| desc.api = impeller::egl::API::kOpenGLES2; | |
| desc.color_format = impeller::egl::ColorFormat::kRGBA8888; | |
| desc.depth_bits = impeller::egl::DepthBits::kZero; | |
| desc.stencil_bits = impeller::egl::StencilBits::kEight; | |
| desc.samples = impeller::egl::Samples::kFour; | |
| desc.surface_type = impeller::egl::SurfaceType::kWindow; | |
| std::unique_ptr<impeller::egl::Config> onscreen_config = | |
| display_->ChooseConfig(desc); | |
| if (!onscreen_config) { | |
| // Fallback for Android emulator. | |
| desc.samples = impeller::egl::Samples::kOne; | |
| onscreen_config = display_->ChooseConfig(desc); | |
| if (onscreen_config) { | |
| FML_LOG(INFO) << "Warning: This device doesn't support MSAA for onscreen " | |
| "framebuffers. Falling back to a single sample."; | |
| } else { | |
| FML_DLOG(ERROR) << "Could not choose onscreen config."; | |
| return; | |
| } | |
| } | |
| desc.surface_type = impeller::egl::SurfaceType::kPBuffer; | |
| auto offscreen_config = display_->ChooseConfig(desc); | |
| if (!offscreen_config) { | |
| FML_DLOG(ERROR) << "Could not choose offscreen config."; | |
| return; | |
| } | |
| auto onscreen_context = display_->CreateContext(*onscreen_config, nullptr); | |
| if (!onscreen_context) { | |
| FML_DLOG(ERROR) << "Could not create onscreen context."; | |
| return; | |
| } | |
| auto offscreen_context = | |
| display_->CreateContext(*offscreen_config, onscreen_context.get()); | |
| if (!offscreen_context) { | |
| FML_DLOG(ERROR) << "Could not create offscreen context."; | |
| return; | |
| } | |
| // Creating the impeller::Context requires a current context, which requires | |
| // some surface. | |
| auto offscreen_surface = | |
| display_->CreatePixelBufferSurface(*offscreen_config, 1u, 1u); | |
| if (!offscreen_context->MakeCurrent(*offscreen_surface)) { | |
| FML_DLOG(ERROR) << "Could not make offscreen context current."; | |
| return; | |
| } | |
| auto impeller_context = | |
| CreateImpellerContext(reactor_worker_, enable_gpu_tracing); | |
| if (!impeller_context) { | |
| FML_DLOG(ERROR) << "Could not create Impeller context."; | |
| return; | |
| } | |
| if (!offscreen_context->ClearCurrent()) { | |
| FML_DLOG(ERROR) << "Could not clear offscreen context."; | |
| return; | |
| } | |
| // Setup context listeners. | |
| impeller::egl::Context::LifecycleListener listener = | |
| [worker = | |
| reactor_worker_](impeller::egl ::Context::LifecycleEvent event) { | |
| switch (event) { | |
| case impeller::egl::Context::LifecycleEvent::kDidMakeCurrent: | |
| worker->SetReactionsAllowedOnCurrentThread(true); | |
| break; | |
| case impeller::egl::Context::LifecycleEvent::kWillClearCurrent: | |
| worker->SetReactionsAllowedOnCurrentThread(false); | |
| break; | |
| } | |
| }; | |
| if (!onscreen_context->AddLifecycleListener(listener).has_value() || | |
| !offscreen_context->AddLifecycleListener(listener).has_value()) { | |
| FML_DLOG(ERROR) << "Could not add lifecycle listeners"; | |
| } | |
| onscreen_config_ = std::move(onscreen_config); | |
| offscreen_config_ = std::move(offscreen_config); | |
| onscreen_context_ = std::move(onscreen_context); | |
| offscreen_context_ = std::move(offscreen_context); | |
| SetImpellerContext(impeller_context); | |
| is_valid_ = true; | |
| } | |
| AndroidContextGLImpeller::~AndroidContextGLImpeller() = default; | |
| bool AndroidContextGLImpeller::IsValid() const { | |
| return is_valid_; | |
| } | |
| bool AndroidContextGLImpeller::ResourceContextClearCurrent() { | |
| if (!offscreen_context_) { | |
| return false; | |
| } | |
| return offscreen_context_->ClearCurrent(); | |
| } | |
| bool AndroidContextGLImpeller::ResourceContextMakeCurrent( | |
| impeller::egl::Surface* offscreen_surface) { | |
| if (!offscreen_context_ || !offscreen_surface) { | |
| return false; | |
| } | |
| return offscreen_context_->MakeCurrent(*offscreen_surface); | |
| } | |
| std::unique_ptr<impeller::egl::Surface> | |
| AndroidContextGLImpeller::CreateOffscreenSurface() { | |
| return display_->CreatePixelBufferSurface(*offscreen_config_, 1u, 1u); | |
| } | |
| bool AndroidContextGLImpeller::OnscreenContextMakeCurrent( | |
| impeller::egl::Surface* onscreen_surface) { | |
| if (!onscreen_surface || !onscreen_context_) { | |
| return false; | |
| } | |
| return onscreen_context_->MakeCurrent(*onscreen_surface); | |
| } | |
| bool AndroidContextGLImpeller::OnscreenContextClearCurrent() { | |
| if (!onscreen_context_) { | |
| return false; | |
| } | |
| return onscreen_context_->ClearCurrent(); | |
| } | |
| std::unique_ptr<impeller::egl::Surface> | |
| AndroidContextGLImpeller::CreateOnscreenSurface(EGLNativeWindowType window) { | |
| return display_->CreateWindowSurface(*onscreen_config_, window); | |
| } | |
| } // namespace flutter |