mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
277 lines
8.9 KiB
C++
277 lines
8.9 KiB
C++
// 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/fxl/arraysize.h"
|
|
#include "lib/fxl/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), weak_factory_(this) {
|
|
if (!delegate_->GLContextMakeCurrent()) {
|
|
FXL_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) {
|
|
FXL_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()) {
|
|
FXL_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 GrPixelConfig FirstSupportedNonSRGBConfig(GrContext* context) {
|
|
#define RETURN_IF_RENDERABLE(x) \
|
|
if (context->caps()->isConfigRenderable((x), false)) { \
|
|
return (x); \
|
|
}
|
|
|
|
RETURN_IF_RENDERABLE(kRGBA_8888_GrPixelConfig);
|
|
RETURN_IF_RENDERABLE(kRGBA_4444_GrPixelConfig);
|
|
RETURN_IF_RENDERABLE(kRGB_565_GrPixelConfig);
|
|
return kUnknown_GrPixelConfig;
|
|
}
|
|
|
|
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
|
|
: FirstSupportedNonSRGBConfig(context);
|
|
|
|
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()) {
|
|
FXL_LOG(ERROR) << "Cannot create surfaces of empty size.";
|
|
return false;
|
|
}
|
|
|
|
sk_sp<SkSurface> onscreen_surface, offscreen_surface;
|
|
|
|
const bool surface_supports_srgb = delegate_->SurfaceSupportsSRGB();
|
|
|
|
onscreen_surface = WrapOnscreenSurface(
|
|
context_.get(), size, delegate_->GLContextFBO(), surface_supports_srgb);
|
|
|
|
if (onscreen_surface == nullptr) {
|
|
// If the onscreen surface could not be wrapped. There is absolutely no
|
|
// point in moving forward.
|
|
FXL_LOG(ERROR) << "Could not wrap onscreen surface.";
|
|
return false;
|
|
}
|
|
|
|
if (!surface_supports_srgb) {
|
|
offscreen_surface = CreateOffscreenSurface(context_.get(), size);
|
|
if (offscreen_surface == nullptr) {
|
|
// If the offscreen surface was needed but could not be wrapped. Render to
|
|
// the onscreen surface directly but warn the user that color correctness
|
|
// is not available.
|
|
static bool warned_once = false;
|
|
if (!warned_once) {
|
|
warned_once = true;
|
|
FXL_LOG(ERROR) << "WARNING: Could not create offscreen surface. This "
|
|
"device or emulator does not support "
|
|
"color correct rendering. Fallbacks are in effect. "
|
|
"Colors on this device will differ from those "
|
|
"displayed on most other devices. This warning will "
|
|
"only be logged once.";
|
|
}
|
|
}
|
|
}
|
|
|
|
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()) {
|
|
FXL_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 (offscreen_surface_ != nullptr) {
|
|
// 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");
|
|
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 offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
|
|
}
|
|
|
|
GrContext* GPUSurfaceGL::GetContext() {
|
|
return context_.get();
|
|
}
|
|
|
|
} // namespace shell
|