| // 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/gpu/gpu_surface_gl_skia.h" | |
| #include "flutter/common/graphics/persistent_cache.h" | |
| #include "flutter/fml/base32.h" | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/size.h" | |
| #include "flutter/fml/trace_event.h" | |
| #include "flutter/shell/common/context_options.h" | |
| #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" | |
| #include "third_party/skia/include/core/SkAlphaType.h" | |
| #include "third_party/skia/include/core/SkColorFilter.h" | |
| #include "third_party/skia/include/core/SkColorSpace.h" | |
| #include "third_party/skia/include/core/SkColorType.h" | |
| #include "third_party/skia/include/core/SkSurface.h" | |
| #include "third_party/skia/include/gpu/GrBackendSurface.h" | |
| #include "third_party/skia/include/gpu/GrContextOptions.h" | |
| #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h" | |
| #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h" | |
| #include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h" | |
| #include "third_party/skia/include/gpu/gl/GrGLTypes.h" | |
| // These are common defines present on all OpenGL headers. However, we don't | |
| // want to perform GL header resolution on each platform we support. So just | |
| // define these upfront. It is unlikely we will need more. But, if we do, we can | |
| // add the same here. | |
| #define GPU_GL_RGBA8 0x8058 | |
| #define GPU_GL_RGBA4 0x8056 | |
| #define GPU_GL_RGB565 0x8D62 | |
| namespace flutter { | |
| // Default maximum number of bytes of GPU memory of budgeted resources in the | |
| // cache. | |
| // The shell will dynamically increase or decrease this cache based on the | |
| // viewport size, unless a user has specifically requested a size on the Skia | |
| // system channel. | |
| static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); | |
| sk_sp<GrDirectContext> GPUSurfaceGLSkia::MakeGLContext( | |
| GPUSurfaceGLDelegate* delegate) { | |
| auto context_switch = delegate->GLContextMakeCurrent(); | |
| if (!context_switch->GetResult()) { | |
| FML_LOG(ERROR) | |
| << "Could not make the context current to set up the Gr context."; | |
| return nullptr; | |
| } | |
| const auto options = | |
| MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); | |
| auto context = GrDirectContexts::MakeGL(delegate->GetGLInterface(), options); | |
| if (!context) { | |
| FML_LOG(ERROR) << "Failed to set up Skia Gr context."; | |
| return nullptr; | |
| } | |
| context->setResourceCacheLimit(kGrCacheMaxByteSize); | |
| PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); | |
| return context; | |
| } | |
| GPUSurfaceGLSkia::GPUSurfaceGLSkia(GPUSurfaceGLDelegate* delegate, | |
| bool render_to_surface) | |
| : GPUSurfaceGLSkia(MakeGLContext(delegate), delegate, render_to_surface) { | |
| context_owner_ = true; | |
| } | |
| GPUSurfaceGLSkia::GPUSurfaceGLSkia(const sk_sp<GrDirectContext>& gr_context, | |
| GPUSurfaceGLDelegate* delegate, | |
| bool render_to_surface) | |
| : delegate_(delegate), | |
| context_(gr_context), | |
| render_to_surface_(render_to_surface), | |
| weak_factory_(this) { | |
| auto context_switch = delegate_->GLContextMakeCurrent(); | |
| if (!context_switch->GetResult()) { | |
| FML_LOG(ERROR) | |
| << "Could not make the context current to set up the Gr context."; | |
| return; | |
| } | |
| delegate_->GLContextClearCurrent(); | |
| valid_ = gr_context != nullptr; | |
| } | |
| GPUSurfaceGLSkia::~GPUSurfaceGLSkia() { | |
| if (!valid_) { | |
| return; | |
| } | |
| auto context_switch = delegate_->GLContextMakeCurrent(); | |
| if (!context_switch->GetResult()) { | |
| FML_LOG(ERROR) << "Could not make the context current to destroy the " | |
| "GrDirectContext resources."; | |
| return; | |
| } | |
| onscreen_surface_ = nullptr; | |
| fbo_id_ = 0; | |
| if (context_owner_) { | |
| context_->releaseResourcesAndAbandonContext(); | |
| } | |
| context_ = nullptr; | |
| delegate_->GLContextClearCurrent(); | |
| } | |
| // |Surface| | |
| bool GPUSurfaceGLSkia::IsValid() { | |
| return valid_; | |
| } | |
| static SkColorType FirstSupportedColorType(GrDirectContext* context, | |
| GrGLenum* format) { | |
| #define RETURN_IF_RENDERABLE(x, y) \ | |
| if (context->colorTypeSupportedAsSurface((x))) { \ | |
| *format = (y); \ | |
| return (x); \ | |
| } | |
| RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8); | |
| RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4); | |
| RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565); | |
| return kUnknown_SkColorType; | |
| } | |
| static sk_sp<SkSurface> WrapOnscreenSurface(GrDirectContext* context, | |
| const SkISize& size, | |
| intptr_t fbo) { | |
| GrGLenum format = kUnknown_SkColorType; | |
| const SkColorType color_type = FirstSupportedColorType(context, &format); | |
| GrGLFramebufferInfo framebuffer_info = {}; | |
| framebuffer_info.fFBOID = static_cast<GrGLuint>(fbo); | |
| framebuffer_info.fFormat = format; | |
| auto render_target = | |
| GrBackendRenderTargets::MakeGL(size.width(), // width | |
| size.height(), // height | |
| 0, // sample count | |
| 0, // stencil bits | |
| framebuffer_info // framebuffer info | |
| ); | |
| sk_sp<SkColorSpace> colorspace = SkColorSpace::MakeSRGB(); | |
| SkSurfaceProps surface_props(0, kUnknown_SkPixelGeometry); | |
| return SkSurfaces::WrapBackendRenderTarget( | |
| context, // Gr context | |
| render_target, // render target | |
| GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin, // origin | |
| color_type, // color type | |
| colorspace, // colorspace | |
| &surface_props // surface properties | |
| ); | |
| } | |
| bool GPUSurfaceGLSkia::CreateOrUpdateSurfaces(const SkISize& size) { | |
| if (onscreen_surface_ != nullptr && | |
| size == SkISize::Make(onscreen_surface_->width(), | |
| onscreen_surface_->height())) { | |
| // Surface size appears unchanged. So bail. | |
| return true; | |
| } | |
| // We need to do some updates. | |
| TRACE_EVENT0("flutter", "UpdateSurfacesSize"); | |
| // Either way, we need to get rid of previous surface. | |
| onscreen_surface_ = nullptr; | |
| fbo_id_ = 0; | |
| if (size.isEmpty()) { | |
| FML_LOG(ERROR) << "Cannot create surfaces of empty size."; | |
| return false; | |
| } | |
| sk_sp<SkSurface> onscreen_surface; | |
| GLFrameInfo frame_info = {static_cast<uint32_t>(size.width()), | |
| static_cast<uint32_t>(size.height())}; | |
| const GLFBOInfo fbo_info = delegate_->GLContextFBO(frame_info); | |
| onscreen_surface = WrapOnscreenSurface(context_.get(), // GL context | |
| size, // root surface size | |
| fbo_info.fbo_id // window FBO ID | |
| ); | |
| if (onscreen_surface == nullptr) { | |
| // If the onscreen surface could not be wrapped. There is absolutely no | |
| // point in moving forward. | |
| FML_LOG(ERROR) << "Could not wrap onscreen surface."; | |
| return false; | |
| } | |
| onscreen_surface_ = std::move(onscreen_surface); | |
| fbo_id_ = fbo_info.fbo_id; | |
| existing_damage_ = fbo_info.existing_damage; | |
| return true; | |
| } | |
| // |Surface| | |
| SkMatrix GPUSurfaceGLSkia::GetRootTransformation() const { | |
| return delegate_->GLContextSurfaceTransformation(); | |
| } | |
| // |Surface| | |
| std::unique_ptr<SurfaceFrame> GPUSurfaceGLSkia::AcquireFrame( | |
| const SkISize& size) { | |
| if (delegate_ == nullptr) { | |
| return nullptr; | |
| } | |
| auto context_switch = delegate_->GLContextMakeCurrent(); | |
| if (!context_switch->GetResult()) { | |
| FML_LOG(ERROR) | |
| << "Could not make the context current to acquire the frame."; | |
| return nullptr; | |
| } | |
| SurfaceFrame::FramebufferInfo framebuffer_info; | |
| // TODO(38466): Refactor GPU surface APIs take into account the fact that an | |
| // external view embedder may want to render to the root surface. | |
| if (!render_to_surface_) { | |
| framebuffer_info.supports_readback = true; | |
| return std::make_unique<SurfaceFrame>( | |
| nullptr, framebuffer_info, | |
| [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { | |
| return true; | |
| }, | |
| size); | |
| } | |
| const auto root_surface_transformation = GetRootTransformation(); | |
| sk_sp<SkSurface> surface = | |
| AcquireRenderSurface(size, root_surface_transformation); | |
| if (surface == nullptr) { | |
| return nullptr; | |
| } | |
| surface->getCanvas()->setMatrix(root_surface_transformation); | |
| SurfaceFrame::SubmitCallback submit_callback = | |
| [weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, | |
| DlCanvas* canvas) { | |
| return weak ? weak->PresentSurface(surface_frame, canvas) : false; | |
| }; | |
| framebuffer_info = delegate_->GLContextFramebufferInfo(); | |
| if (!framebuffer_info.existing_damage.has_value()) { | |
| framebuffer_info.existing_damage = existing_damage_; | |
| } | |
| return std::make_unique<SurfaceFrame>(surface, framebuffer_info, | |
| submit_callback, size, | |
| std::move(context_switch)); | |
| } | |
| bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame, | |
| DlCanvas* canvas) { | |
| if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) { | |
| return false; | |
| } | |
| delegate_->GLContextSetDamageRegion(frame.submit_info().buffer_damage); | |
| { | |
| TRACE_EVENT0("flutter", "GrDirectContext::flushAndSubmit"); | |
| context_->flushAndSubmit(); | |
| } | |
| GLPresentInfo present_info = { | |
| .fbo_id = fbo_id_, | |
| .frame_damage = frame.submit_info().frame_damage, | |
| .presentation_time = frame.submit_info().presentation_time, | |
| .buffer_damage = frame.submit_info().buffer_damage, | |
| }; | |
| if (!delegate_->GLContextPresent(present_info)) { | |
| return false; | |
| } | |
| if (delegate_->GLContextFBOResetAfterPresent()) { | |
| auto current_size = | |
| SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height()); | |
| GLFrameInfo frame_info = {static_cast<uint32_t>(current_size.width()), | |
| static_cast<uint32_t>(current_size.height())}; | |
| // The FBO has changed, ask the delegate for the new FBO and do a surface | |
| // re-wrap. | |
| const GLFBOInfo fbo_info = delegate_->GLContextFBO(frame_info); | |
| auto new_onscreen_surface = | |
| WrapOnscreenSurface(context_.get(), // GL context | |
| current_size, // root surface size | |
| fbo_info.fbo_id // window FBO ID | |
| ); | |
| if (!new_onscreen_surface) { | |
| return false; | |
| } | |
| onscreen_surface_ = std::move(new_onscreen_surface); | |
| fbo_id_ = fbo_info.fbo_id; | |
| existing_damage_ = fbo_info.existing_damage; | |
| } | |
| return true; | |
| } | |
| sk_sp<SkSurface> GPUSurfaceGLSkia::AcquireRenderSurface( | |
| const SkISize& untransformed_size, | |
| const SkMatrix& root_surface_transformation) { | |
| const auto transformed_rect = root_surface_transformation.mapRect( | |
| SkRect::MakeWH(untransformed_size.width(), untransformed_size.height())); | |
| const auto transformed_size = | |
| SkISize::Make(transformed_rect.width(), transformed_rect.height()); | |
| if (!CreateOrUpdateSurfaces(transformed_size)) { | |
| return nullptr; | |
| } | |
| return onscreen_surface_; | |
| } | |
| // |Surface| | |
| GrDirectContext* GPUSurfaceGLSkia::GetContext() { | |
| return context_.get(); | |
| } | |
| // |Surface| | |
| std::unique_ptr<GLContextResult> GPUSurfaceGLSkia::MakeRenderContextCurrent() { | |
| return delegate_->GLContextMakeCurrent(); | |
| } | |
| // |Surface| | |
| bool GPUSurfaceGLSkia::ClearRenderContext() { | |
| return delegate_->GLContextClearCurrent(); | |
| } | |
| // |Surface| | |
| bool GPUSurfaceGLSkia::AllowsDrawingWhenGpuDisabled() const { | |
| return delegate_->AllowsDrawingWhenGpuDisabled(); | |
| } | |
| } // namespace flutter |