blob: eafb6120b37747b1ebb16ca2f34df4d688f9dd0a [file] [log] [blame]
// 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"
// These are common defines present on all OpenGL headers. However, we don't
// want to perform GL header reasolution 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 = GrDirectContext::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(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;
GrBackendRenderTarget render_target(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 SkSurface::MakeFromBackendRenderTarget(
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;
supports_partial_repaint_ = fbo_info.partial_repaint_enabled;
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, std::move(framebuffer_info),
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return true;
});
}
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,
SkCanvas* canvas) {
return weak ? weak->PresentSurface(surface_frame, canvas) : false;
};
framebuffer_info = delegate_->GLContextFramebufferInfo();
// Partial repaint is enabled by default
framebuffer_info.supports_partial_repaint = supports_partial_repaint_;
framebuffer_info.existing_damage = existing_damage_;
return std::make_unique<SurfaceFrame>(surface, std::move(framebuffer_info),
submit_callback,
std::move(context_switch));
}
bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame,
SkCanvas* canvas) {
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
return false;
}
delegate_->GLContextSetDamageRegion(frame.submit_info().buffer_damage);
{
TRACE_EVENT0("flutter", "SkCanvas::Flush");
onscreen_surface_->getCanvas()->flush();
}
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;
supports_partial_repaint_ = fbo_info.partial_repaint_enabled;
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