| // 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/skwasm/render_context.h" |
| |
| #include "flutter/impeller/display_list/dl_dispatcher.h" |
| #include "flutter/impeller/display_list/dl_image_impeller.h" |
| #include "flutter/impeller/entity/gles3/entity_shaders_gles.h" |
| #include "flutter/impeller/renderer/backend/gles/context_gles.h" |
| #include "flutter/impeller/renderer/backend/gles/surface_gles.h" |
| #include "flutter/impeller/renderer/backend/gles/texture_gles.h" |
| #include "flutter/impeller/typographer/backends/skia/text_frame_skia.h" |
| #include "flutter/impeller/typographer/backends/skia/typographer_context_skia.h" |
| #include "flutter/skwasm/export.h" |
| |
| SKWASM_EXPORT bool skwasm_isWimp() { |
| return true; |
| } |
| |
| namespace { |
| std::vector<std::shared_ptr<fml::Mapping>> |
| ShaderLibraryMappingsForApplication() { |
| return { |
| std::make_shared<fml::NonOwnedMapping>( |
| impeller_entity_shaders_gles3_data, |
| impeller_entity_shaders_gles3_length), |
| }; |
| } |
| |
| class ReactorWorker : public impeller::ReactorGLES::Worker { |
| public: |
| ReactorWorker() = default; |
| ReactorWorker(const ReactorWorker&) = delete; |
| |
| ReactorWorker& operator=(const ReactorWorker&) = delete; |
| |
| virtual bool CanReactorReactOnCurrentThreadNow( |
| const impeller::ReactorGLES& reactor) const override { |
| return true; |
| } |
| }; |
| |
| class ImpellerRenderContext; |
| thread_local std::vector<ImpellerRenderContext*> active_contexts; |
| |
| class ImpellerRenderContext : public Skwasm::RenderContext { |
| public: |
| ImpellerRenderContext(std::shared_ptr<impeller::ContextGLES> context, |
| std::shared_ptr<ReactorWorker> worker) |
| : context_(std::move(context)), |
| worker_(std::move(worker)), |
| typographer_context_(impeller::TypographerContextSkia::Make()), |
| content_context_( |
| std::make_unique<impeller::ContentContext>(context_, |
| typographer_context_, |
| nullptr)) { |
| content_context_->SetTextureCachingEnabled(true); |
| active_contexts.push_back(this); |
| } |
| |
| virtual ~ImpellerRenderContext() { |
| auto it = std::find(active_contexts.begin(), active_contexts.end(), this); |
| if (it != active_contexts.end()) { |
| active_contexts.erase(it); |
| } |
| } |
| |
| virtual void RenderPicture( |
| const sk_sp<flutter::DisplayList> display_list) override { |
| impeller::RenderToTarget( |
| *content_context_, surface_->GetRenderTarget(), display_list, |
| impeller::Rect::MakeLTRB(0, 0, width_, height_), true, true); |
| } |
| |
| virtual bool RasterizeImage(flutter::DlImage* image, |
| Skwasm::ImageByteFormat format, |
| void* out_pixels) override { |
| auto impeller_image = image ? image->asImpellerImage() : nullptr; |
| auto texture = |
| impeller_image ? impeller_image->GetImpellerTexture(context_) : nullptr; |
| if (!texture) { |
| return false; |
| } |
| |
| auto gles_texture = static_cast<impeller::TextureGLES*>(texture.get()); |
| GLuint fbo = 0; |
| glGenFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| bool attached = gles_texture->SetAsFramebufferAttachment( |
| GL_FRAMEBUFFER, impeller::TextureGLES::AttachmentType::kColor0); |
| if (!attached) { |
| glDeleteFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| return false; |
| } |
| |
| glReadPixels(0, 0, image->width(), image->height(), GL_RGBA, |
| GL_UNSIGNED_BYTE, out_pixels); |
| |
| glDeleteFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| return true; |
| } |
| |
| virtual void Resize(int width, int height) override { |
| if (width_ != width || height_ != height) { |
| width_ = width; |
| height_ = height; |
| RecreateSurface(); |
| } |
| } |
| |
| void RemoveImage(const flutter::DlImage* image) { |
| if (content_context_) { |
| content_context_->RemoveCachedTexture(image); |
| } |
| } |
| |
| virtual void SetResourceCacheLimit(int bytes) override { |
| // No-op |
| } |
| |
| private: |
| void RecreateSurface() { |
| surface_ = impeller::SurfaceGLES::WrapFBO( |
| /*context=*/context_, |
| /*swap_callback=*/[]() { return true; }, |
| /*fbo=*/0u, |
| /*color_format=*/impeller::PixelFormat::kR8G8B8A8UNormInt, |
| /*fbo_size=*/{width_, height_}); |
| } |
| |
| std::shared_ptr<impeller::ContextGLES> context_; |
| std::shared_ptr<ReactorWorker> worker_; |
| std::shared_ptr<impeller::TypographerContext> typographer_context_; |
| std::unique_ptr<impeller::ContentContext> content_context_; |
| std::unique_ptr<impeller::Surface> surface_; |
| int width_ = 0; |
| int height_ = 0; |
| }; |
| } // namespace |
| |
| std::unique_ptr<Skwasm::RenderContext> Skwasm::RenderContext::Make( |
| int sample_count, |
| int stencil) { |
| auto clear_depth_emulated = [](float depth) {}; |
| auto depth_range_emulated = [](float near_val, float far_val) {}; |
| |
| std::map<std::string, void*> gl_procs; |
| |
| gl_procs["glGetError"] = (void*)&glGetError; |
| gl_procs["glClearDepthf"] = (void*)&clear_depth_emulated; |
| gl_procs["glDepthRangef"] = (void*)&depth_range_emulated; |
| |
| #define IMPELLER_PROC(name) gl_procs["gl" #name] = (void*)&gl##name; |
| FOR_EACH_IMPELLER_PROC(IMPELLER_PROC); |
| FOR_EACH_IMPELLER_ES_ONLY_PROC(IMPELLER_PROC); |
| FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC); |
| IMPELLER_PROC(GenQueriesEXT); |
| IMPELLER_PROC(DeleteQueriesEXT); |
| IMPELLER_PROC(GetQueryObjectui64vEXT); |
| IMPELLER_PROC(BeginQueryEXT); |
| IMPELLER_PROC(EndQueryEXT); |
| IMPELLER_PROC(GetQueryObjectuivEXT); |
| #undef IMPELLER_PROC |
| |
| auto gl = std::make_unique<impeller::ProcTableGLES>( |
| [gl_procs = std::move(gl_procs)](const char* function_name) -> void* { |
| auto found = gl_procs.find(function_name); |
| if (found == gl_procs.end()) { |
| return nullptr; |
| } |
| return found->second; |
| }); |
| |
| auto context = impeller::ContextGLES::Create( |
| impeller::Flags{}, std::move(gl), ShaderLibraryMappingsForApplication(), |
| false); |
| |
| auto worker = std::make_shared<ReactorWorker>(); |
| context->AddReactorWorker(worker); |
| return std::make_unique<ImpellerRenderContext>(std::move(context), |
| std::move(worker)); |
| } |
| |
| SKWASM_EXPORT void skwasm_disposeDlImageOnWorker(void* dl_image_ptr) { |
| const flutter::DlImage* image = |
| reinterpret_cast<const flutter::DlImage*>(dl_image_ptr); |
| for (auto* context : active_contexts) { |
| context->RemoveImage(image); |
| } |
| } |