gpu_surface_gl.cc 7.5 KB
Newer Older
1 2 3 4
// 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.

5
#include "gpu_surface_gl.h"
6

7
#include "flutter/glue/trace_event.h"
8 9
#include "lib/fxl/arraysize.h"
#include "lib/fxl/logging.h"
10
#include "third_party/skia/include/core/SkColorFilter.h"
11
#include "third_party/skia/include/core/SkSurface.h"
12
#include "third_party/skia/include/gpu/GrBackendSurface.h"
13
#include "third_party/skia/include/gpu/GrContextOptions.h"
14
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
15
#include "third_party/skia/src/gpu/gl/GrGLDefines.h"
16

17 18
namespace shell {

19 20
// Default maximum number of budgeted resources in the cache.
static const int kGrCacheMaxCount = 8192;
21

22 23 24
// Default maximum number of bytes of GPU memory of budgeted resources in the
// cache.
static const size_t kGrCacheMaxByteSize = 512 * (1 << 20);
25

26
GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate)
27
    : delegate_(delegate), weak_factory_(this) {
28
  if (!delegate_->GLContextMakeCurrent()) {
29
    FXL_LOG(ERROR)
30 31
        << "Could not make the context current to setup the gr context.";
    return;
32 33
  }

34 35 36
  GrContextOptions options;
  options.fAvoidStencilBuffers = true;

37 38 39 40
  // To get video playback on the widest range of devices, we limit Skia to
  // ES2 shading language when the ES3 external image extension is missing.
  options.fPreferExternalImagesOverES3 = true;

41
  auto context = GrContext::MakeGL(GrGLMakeNativeInterface(), options);
42 43

  if (context == nullptr) {
44
    FXL_LOG(ERROR) << "Failed to setup Skia Gr context.";
45
    return;
46 47
  }

48 49
  context_ = std::move(context);

50
  context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
51

52 53
  delegate_->GLContextClearCurrent();

54 55 56 57 58 59 60 61 62
  valid_ = true;
}

GPUSurfaceGL::~GPUSurfaceGL() {
  if (!valid_) {
    return;
  }

  if (!delegate_->GLContextMakeCurrent()) {
63
    FXL_LOG(ERROR) << "Could not make the context current to destroy the "
64 65 66 67 68 69 70 71 72
                      "GrContext resources.";
    return;
  }

  onscreen_surface_ = nullptr;
  context_->releaseResourcesAndAbandonContext();
  context_ = nullptr;

  delegate_->GLContextClearCurrent();
73 74
}

75
bool GPUSurfaceGL::IsValid() {
76 77 78
  return valid_;
}

79 80
static SkColorType FirstSupportedColorType(GrContext* context,
                                           GrGLenum* format) {
81 82 83 84
#define RETURN_IF_RENDERABLE(x, y)                 \
  if (context->colorTypeSupportedAsSurface((x))) { \
    *format = (y);                                 \
    return (x);                                    \
85
  }
86 87 88
  RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GR_GL_RGBA8);
  RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GR_GL_RGBA4);
  RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GR_GL_RGB565);
89
  return kUnknown_SkColorType;
90 91
}

92 93
static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
                                            const SkISize& size,
94
                                            intptr_t fbo) {
95
  GrGLenum format;
96 97
  const SkColorType color_type = FirstSupportedColorType(context, &format);

98
  const GrGLFramebufferInfo framebuffer_info = {
99
      .fFBOID = static_cast<GrGLuint>(fbo),
100
      .fFormat = format,
101 102 103 104 105 106 107
  };

  GrBackendRenderTarget render_target(size.fWidth,      // width
                                      size.fHeight,     // height
                                      0,                // sample count
                                      0,                // stencil bits (TODO)
                                      framebuffer_info  // framebuffer info
108
  );
109

110
  sk_sp<SkColorSpace> colorspace = nullptr;
111 112 113 114 115 116 117 118

  SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeFromBackendRenderTarget(
      context,                                       // gr context
      render_target,                                 // render target
      GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,  // origin
119
      color_type,                                    // color type
120 121
      colorspace,                                    // colorspace
      &surface_props                                 // surface properties
122
  );
123 124
}

125 126 127 128 129 130 131 132 133 134 135 136 137
static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
                                               const SkISize& size) {
  const SkImageInfo image_info =
      SkImageInfo::MakeN32(size.fWidth, size.fHeight, kOpaque_SkAlphaType);

  const SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0,
                                     kBottomLeft_GrSurfaceOrigin,
                                     &surface_props);
}

138 139 140 141
bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
  if (onscreen_surface_ != nullptr &&
      size == SkISize::Make(onscreen_surface_->width(),
                            onscreen_surface_->height())) {
142
    // Surface size appears unchanged. So bail.
143 144 145 146 147 148
    return true;
  }

  // We need to do some updates.
  TRACE_EVENT0("flutter", "UpdateSurfacesSize");

149
  // Either way, we need to get rid of previous surface.
150
  onscreen_surface_ = nullptr;
151
  offscreen_surface_ = nullptr;
152 153

  if (size.isEmpty()) {
154
    FXL_LOG(ERROR) << "Cannot create surfaces of empty size.";
155 156 157
    return false;
  }

158
  sk_sp<SkSurface> onscreen_surface, offscreen_surface;
159

160 161
  onscreen_surface =
      WrapOnscreenSurface(context_.get(), size, delegate_->GLContextFBO());
162

163
  if (onscreen_surface == nullptr) {
164 165
    // If the onscreen surface could not be wrapped. There is absolutely no
    // point in moving forward.
166
    FXL_LOG(ERROR) << "Could not wrap onscreen surface.";
167 168 169
    return false;
  }

170 171 172 173 174 175 176 177
  if (delegate_->UseOffscreenSurface()) {
    offscreen_surface = CreateOffscreenSurface(context_.get(), size);
    if (offscreen_surface == nullptr) {
      FXL_LOG(ERROR) << "Could not create offscreen surface.";
      return false;
    }
  }

178
  onscreen_surface_ = std::move(onscreen_surface);
179
  offscreen_surface_ = std::move(offscreen_surface);
180 181

  return true;
182 183
}

184 185 186 187 188
std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
  if (delegate_ == nullptr) {
    return nullptr;
  }

189
  if (!delegate_->GLContextMakeCurrent()) {
190
    FXL_LOG(ERROR)
191
        << "Could not make the context current to acquire the frame.";
192 193 194
    return nullptr;
  }

195
  sk_sp<SkSurface> surface = AcquireRenderSurface(size);
196 197 198 199 200

  if (surface == nullptr) {
    return nullptr;
  }

201 202 203 204 205
  SurfaceFrame::SubmitCallback submit_callback =
      [weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
                                          SkCanvas* canvas) {
        return weak ? weak->PresentSurface(canvas) : false;
      };
206

207
  return std::make_unique<SurfaceFrame>(surface, submit_callback);
208 209 210
}

bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
211
  if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
212 213 214
    return false;
  }

215 216 217 218 219 220 221
  if (offscreen_surface_ != nullptr) {
    TRACE_EVENT0("flutter", "CopyTextureOnscreen");
    SkPaint paint;
    onscreen_surface_->getCanvas()->drawImage(
        offscreen_surface_->makeImageSnapshot(), 0, 0, &paint);
  }

222 223
  {
    TRACE_EVENT0("flutter", "SkCanvas::Flush");
224
    onscreen_surface_->getCanvas()->flush();
225
  }
226 227 228 229

  delegate_->GLContextPresent();

  return true;
230 231
}

232 233
sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(const SkISize& size) {
  if (!CreateOrUpdateSurfaces(size)) {
234 235 236
    return nullptr;
  }

237
  return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
238 239
}

240
GrContext* GPUSurfaceGL::GetContext() {
241 242
  return context_.get();
}
243 244

}  // namespace shell