blob: 2a4800dff7029aae678c7b603aa5a76366f1b41c [file] [log] [blame]
// Copyright 2016 The Chromium 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 "gpu_surface_gl.h"
#include "flutter/glue/trace_event.h"
#include "lib/ftl/arraysize.h"
#include "lib/ftl/logging.h"
#include "third_party/skia/include/core/SkColorFilter.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/gl/GrGLInterface.h"
namespace shell {
// Default maximum number of budgeted resources in the cache.
static const int kGrCacheMaxCount = 8192;
// Default maximum number of bytes of GPU memory of budgeted resources in the
// cache.
static const size_t kGrCacheMaxByteSize = 512 * (1 << 20);
GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate)
: delegate_(delegate),
onscreen_surface_supports_srgb_(delegate_->SurfaceSupportsSRGB()),
weak_factory_(this) {
if (!delegate_->GLContextMakeCurrent()) {
FTL_LOG(ERROR)
<< "Could not make the context current to setup the gr context.";
return;
}
auto backend_context =
reinterpret_cast<GrBackendContext>(GrGLCreateNativeInterface());
GrContextOptions options;
options.fRequireDecodeDisableForSRGB = false;
auto context = sk_sp<GrContext>(
GrContext::Create(kOpenGL_GrBackend, backend_context, options));
if (context == nullptr) {
FTL_LOG(ERROR) << "Failed to setup Skia Gr context.";
return;
}
context_ = std::move(context);
context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
delegate_->GLContextClearCurrent();
valid_ = true;
}
GPUSurfaceGL::~GPUSurfaceGL() {
if (!valid_) {
return;
}
if (!delegate_->GLContextMakeCurrent()) {
FTL_LOG(ERROR) << "Could not make the context current to destroy the "
"GrContext resources.";
return;
}
onscreen_surface_ = nullptr;
offscreen_surface_ = nullptr;
context_->releaseResourcesAndAbandonContext();
context_ = nullptr;
delegate_->GLContextClearCurrent();
}
bool GPUSurfaceGL::IsValid() {
return valid_;
}
static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
const SkISize& size,
intptr_t fbo,
bool supports_srgb) {
const GrGLFramebufferInfo framebuffer_info = {
.fFBOID = static_cast<GrGLuint>(fbo),
};
const GrPixelConfig pixel_config =
supports_srgb ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
GrBackendRenderTarget render_target(size.fWidth, // width
size.fHeight, // height
0, // sample count
0, // stencil bits (TODO)
pixel_config, // pixel config
framebuffer_info // framebuffer info
);
sk_sp<SkColorSpace> colorspace =
supports_srgb ? SkColorSpace::MakeSRGB() : nullptr;
SkSurfaceProps surface_props(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
return SkSurface::MakeFromBackendRenderTarget(
context, // gr context
render_target, // render target
GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin, // origin
colorspace, // colorspace
&surface_props // surface properties
);
}
static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
const SkISize& size) {
const SkImageInfo image_info =
SkImageInfo::MakeS32(size.fWidth, size.fHeight, kOpaque_SkAlphaType);
const SkSurfaceProps surface_props(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
return SkSurface::MakeRenderTarget(
context, // context
SkBudgeted::kNo, // budgeted
image_info, // image info
0, // sample count
kBottomLeft_GrSurfaceOrigin, // surface origin
&surface_props // surface props
);
}
bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
if (onscreen_surface_ != nullptr &&
size == SkISize::Make(onscreen_surface_->width(),
onscreen_surface_->height())) {
// We know that if there is an offscreen surface, it will be sized to be
// equal to the size of the onscreen surface. And the onscreen 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 surfaces.
onscreen_surface_ = nullptr;
offscreen_surface_ = nullptr;
if (size.isEmpty()) {
FTL_LOG(ERROR) << "Cannot create surfaces of empty size.";
return false;
}
sk_sp<SkSurface> onscreen_surface, offscreen_surface;
onscreen_surface =
WrapOnscreenSurface(context_.get(), size, delegate_->GLContextFBO(),
onscreen_surface_supports_srgb_);
if (onscreen_surface == nullptr) {
FTL_LOG(ERROR) << "Could not wrap onscreen surface.";
return false;
}
if (!onscreen_surface_supports_srgb_) {
offscreen_surface = CreateOffscreenSurface(context_.get(), size);
if (offscreen_surface == nullptr) {
FTL_LOG(ERROR) << "Could not create offscreen surface.";
return false;
}
}
onscreen_surface_ = std::move(onscreen_surface);
offscreen_surface_ = std::move(offscreen_surface);
return true;
}
std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
if (delegate_ == nullptr) {
return nullptr;
}
if (!delegate_->GLContextMakeCurrent()) {
FTL_LOG(ERROR)
<< "Could not make the context current to acquire the frame.";
return nullptr;
}
sk_sp<SkSurface> surface = AcquireRenderSurface(size);
if (surface == nullptr) {
return nullptr;
}
auto weak_this = weak_factory_.GetWeakPtr();
SurfaceFrame::SubmitCallback submit_callback =
[weak_this](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return weak_this ? weak_this->PresentSurface(canvas) : false;
};
return std::make_unique<SurfaceFrame>(surface, submit_callback);
}
bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
return false;
}
if (!onscreen_surface_supports_srgb_) {
// Because the surface did not support sRGB, we rendered to an offscreen
// surface. Now we must ensure that the texture is copied onscreen.
TRACE_EVENT0("flutter", "CopyTextureOnscreen");
FTL_DCHECK(offscreen_surface_ != nullptr);
SkPaint paint;
const GrCaps* caps = context_->caps();
if (caps->srgbSupport() && !caps->srgbDecodeDisableSupport()) {
paint.setColorFilter(SkColorFilter::MakeLinearToSRGBGamma());
}
onscreen_surface_->getCanvas()->drawImage(
offscreen_surface_->makeImageSnapshot(), // image
0, // left
0, // top
&paint // paint
);
}
{
TRACE_EVENT0("flutter", "SkCanvas::Flush");
onscreen_surface_->getCanvas()->flush();
}
delegate_->GLContextPresent();
return true;
}
sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(const SkISize& size) {
if (!CreateOrUpdateSurfaces(size)) {
return nullptr;
}
return onscreen_surface_supports_srgb_ ? onscreen_surface_
: offscreen_surface_;
}
GrContext* GPUSurfaceGL::GetContext() {
return context_.get();
}
} // namespace shell