1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gpu_surface_gl.h"
6
7 #include "flutter/fml/logging.h"
8 #include "flutter/fml/size.h"
9 #include "flutter/fml/trace_event.h"
10 #include "flutter/shell/common/persistent_cache.h"
11 #include "third_party/skia/include/core/SkColorFilter.h"
12 #include "third_party/skia/include/core/SkSurface.h"
13 #include "third_party/skia/include/gpu/GrBackendSurface.h"
14 #include "third_party/skia/include/gpu/GrContextOptions.h"
15
16 // These are common defines present on all OpenGL headers. However, we don't
17 // want to perform GL header reasolution on each platform we support. So just
18 // define these upfront. It is unlikely we will need more. But, if we do, we can
19 // add the same here.
20 #define GPU_GL_RGBA8 0x8058
21 #define GPU_GL_RGBA4 0x8056
22 #define GPU_GL_RGB565 0x8D62
23
24 namespace flutter {
25
26 // Default maximum number of budgeted resources in the cache.
27 static const int kGrCacheMaxCount = 8192;
28
29 // Default maximum number of bytes of GPU memory of budgeted resources in the
30 // cache.
31 // The shell will dynamically increase or decrease this cache based on the
32 // viewport size, unless a user has specifically requested a size on the Skia
33 // system channel.
34 static const size_t kGrCacheMaxByteSize = 24 * (1 << 20);
35
GPUSurfaceGL(GPUSurfaceGLDelegate * delegate,bool render_to_surface)36 GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate,
37 bool render_to_surface)
38 : delegate_(delegate),
39 render_to_surface_(render_to_surface),
40 weak_factory_(this) {
41 if (!delegate_->GLContextMakeCurrent()) {
42 FML_LOG(ERROR)
43 << "Could not make the context current to setup the gr context.";
44 return;
45 }
46
47 GrContextOptions options;
48
49 options.fPersistentCache = PersistentCache::GetCacheForProcess();
50
51 options.fAvoidStencilBuffers = true;
52
53 // To get video playback on the widest range of devices, we limit Skia to
54 // ES2 shading language when the ES3 external image extension is missing.
55 options.fPreferExternalImagesOverES3 = true;
56
57 // TODO(goderbauer): remove option when skbug.com/7523 is fixed.
58 // A similar work-around is also used in shell/common/io_manager.cc.
59 options.fDisableGpuYUVConversion = true;
60
61 auto context = GrContext::MakeGL(delegate_->GetGLInterface(), options);
62
63 if (context == nullptr) {
64 FML_LOG(ERROR) << "Failed to setup Skia Gr context.";
65 return;
66 }
67
68 context_ = std::move(context);
69
70 context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
71
72 delegate_->GLContextClearCurrent();
73
74 context_owner_ = true;
75
76 valid_ = true;
77 }
78
GPUSurfaceGL(sk_sp<GrContext> gr_context,GPUSurfaceGLDelegate * delegate,bool render_to_surface)79 GPUSurfaceGL::GPUSurfaceGL(sk_sp<GrContext> gr_context,
80 GPUSurfaceGLDelegate* delegate,
81 bool render_to_surface)
82 : delegate_(delegate),
83 context_(gr_context),
84 render_to_surface_(render_to_surface),
85 weak_factory_(this) {
86 if (!delegate_->GLContextMakeCurrent()) {
87 FML_LOG(ERROR)
88 << "Could not make the context current to setup the gr context.";
89 return;
90 }
91
92 delegate_->GLContextClearCurrent();
93
94 valid_ = true;
95 context_owner_ = false;
96 }
97
~GPUSurfaceGL()98 GPUSurfaceGL::~GPUSurfaceGL() {
99 if (!valid_) {
100 return;
101 }
102
103 if (!delegate_->GLContextMakeCurrent()) {
104 FML_LOG(ERROR) << "Could not make the context current to destroy the "
105 "GrContext resources.";
106 return;
107 }
108
109 onscreen_surface_ = nullptr;
110 if (context_owner_) {
111 context_->releaseResourcesAndAbandonContext();
112 }
113 context_ = nullptr;
114
115 delegate_->GLContextClearCurrent();
116 }
117
118 // |Surface|
IsValid()119 bool GPUSurfaceGL::IsValid() {
120 return valid_;
121 }
122
FirstSupportedColorType(GrContext * context,GrGLenum * format)123 static SkColorType FirstSupportedColorType(GrContext* context,
124 GrGLenum* format) {
125 #define RETURN_IF_RENDERABLE(x, y) \
126 if (context->colorTypeSupportedAsSurface((x))) { \
127 *format = (y); \
128 return (x); \
129 }
130 RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8);
131 RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4);
132 RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565);
133 return kUnknown_SkColorType;
134 }
135
WrapOnscreenSurface(GrContext * context,const SkISize & size,intptr_t fbo)136 static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
137 const SkISize& size,
138 intptr_t fbo) {
139 GrGLenum format;
140 const SkColorType color_type = FirstSupportedColorType(context, &format);
141
142 GrGLFramebufferInfo framebuffer_info = {};
143 framebuffer_info.fFBOID = static_cast<GrGLuint>(fbo);
144 framebuffer_info.fFormat = format;
145
146 GrBackendRenderTarget render_target(size.width(), // width
147 size.height(), // height
148 0, // sample count
149 0, // stencil bits (TODO)
150 framebuffer_info // framebuffer info
151 );
152
153 sk_sp<SkColorSpace> colorspace = SkColorSpace::MakeSRGB();
154
155 SkSurfaceProps surface_props(
156 SkSurfaceProps::InitType::kLegacyFontHost_InitType);
157
158 return SkSurface::MakeFromBackendRenderTarget(
159 context, // gr context
160 render_target, // render target
161 GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin, // origin
162 color_type, // color type
163 colorspace, // colorspace
164 &surface_props // surface properties
165 );
166 }
167
CreateOffscreenSurface(GrContext * context,const SkISize & size)168 static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
169 const SkISize& size) {
170 const SkImageInfo image_info = SkImageInfo::MakeN32(
171 size.fWidth, size.fHeight, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
172
173 const SkSurfaceProps surface_props(
174 SkSurfaceProps::InitType::kLegacyFontHost_InitType);
175
176 return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0,
177 kBottomLeft_GrSurfaceOrigin,
178 &surface_props);
179 }
180
CreateOrUpdateSurfaces(const SkISize & size)181 bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
182 if (onscreen_surface_ != nullptr &&
183 size == SkISize::Make(onscreen_surface_->width(),
184 onscreen_surface_->height())) {
185 // Surface size appears unchanged. So bail.
186 return true;
187 }
188
189 // We need to do some updates.
190 TRACE_EVENT0("flutter", "UpdateSurfacesSize");
191
192 // Either way, we need to get rid of previous surface.
193 onscreen_surface_ = nullptr;
194 offscreen_surface_ = nullptr;
195
196 if (size.isEmpty()) {
197 FML_LOG(ERROR) << "Cannot create surfaces of empty size.";
198 return false;
199 }
200
201 sk_sp<SkSurface> onscreen_surface, offscreen_surface;
202
203 onscreen_surface =
204 WrapOnscreenSurface(context_.get(), // GL context
205 size, // root surface size
206 delegate_->GLContextFBO() // window FBO ID
207 );
208
209 if (onscreen_surface == nullptr) {
210 // If the onscreen surface could not be wrapped. There is absolutely no
211 // point in moving forward.
212 FML_LOG(ERROR) << "Could not wrap onscreen surface.";
213 return false;
214 }
215
216 if (delegate_->UseOffscreenSurface()) {
217 offscreen_surface = CreateOffscreenSurface(context_.get(), size);
218 if (offscreen_surface == nullptr) {
219 FML_LOG(ERROR) << "Could not create offscreen surface.";
220 return false;
221 }
222 }
223
224 onscreen_surface_ = std::move(onscreen_surface);
225 offscreen_surface_ = std::move(offscreen_surface);
226
227 return true;
228 }
229
230 // |Surface|
GetRootTransformation() const231 SkMatrix GPUSurfaceGL::GetRootTransformation() const {
232 return delegate_->GLContextSurfaceTransformation();
233 }
234
235 // |Surface|
AcquireFrame(const SkISize & size)236 std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
237 if (delegate_ == nullptr) {
238 return nullptr;
239 }
240
241 if (!delegate_->GLContextMakeCurrent()) {
242 FML_LOG(ERROR)
243 << "Could not make the context current to acquire the frame.";
244 return nullptr;
245 }
246
247 // TODO(38466): Refactor GPU surface APIs take into account the fact that an
248 // external view embedder may want to render to the root surface.
249 if (!render_to_surface_) {
250 return std::make_unique<SurfaceFrame>(
251 nullptr, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
252 return true;
253 });
254 }
255
256 const auto root_surface_transformation = GetRootTransformation();
257
258 sk_sp<SkSurface> surface =
259 AcquireRenderSurface(size, root_surface_transformation);
260
261 if (surface == nullptr) {
262 return nullptr;
263 }
264
265 surface->getCanvas()->setMatrix(root_surface_transformation);
266
267 SurfaceFrame::SubmitCallback submit_callback =
268 [weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
269 SkCanvas* canvas) {
270 return weak ? weak->PresentSurface(canvas) : false;
271 };
272
273 return std::make_unique<SurfaceFrame>(surface, submit_callback);
274 }
275
PresentSurface(SkCanvas * canvas)276 bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
277 if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
278 return false;
279 }
280
281 if (offscreen_surface_ != nullptr) {
282 TRACE_EVENT0("flutter", "CopyTextureOnscreen");
283 SkPaint paint;
284 SkCanvas* onscreen_canvas = onscreen_surface_->getCanvas();
285 onscreen_canvas->clear(SK_ColorTRANSPARENT);
286 onscreen_canvas->drawImage(offscreen_surface_->makeImageSnapshot(), 0, 0,
287 &paint);
288 }
289
290 {
291 TRACE_EVENT0("flutter", "SkCanvas::Flush");
292 onscreen_surface_->getCanvas()->flush();
293
294 // ACE PC preview
295 #if defined(PREVIEW)
296 int32_t width = onscreen_surface_->width();
297 int32_t height = onscreen_surface_->height();
298 SkImageInfo info = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
299 int32_t pixelSize = info.bytesPerPixel();
300 auto pixelBuffer = std::make_unique<char[]>(width * height * pixelSize);
301 SkPixmap data(info, pixelBuffer.get(), width * pixelSize);
302
303 if (onscreen_surface_->readPixels(data, 0, 0)) {
304 delegate_->GLContextSendSurface(data.addr(), data.rowBytes() * data.height(), width, height);
305 } else {
306 FML_LOG(ERROR) << "GLContextSendSurface failed";
307 }
308 #endif
309
310 }
311
312 if (!delegate_->GLContextPresent()) {
313 return false;
314 }
315
316 if (delegate_->GLContextFBOResetAfterPresent()) {
317 auto current_size =
318 SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height());
319
320 // The FBO has changed, ask the delegate for the new FBO and do a surface
321 // re-wrap.
322 auto new_onscreen_surface =
323 WrapOnscreenSurface(context_.get(), // GL context
324 current_size, // root surface size
325 delegate_->GLContextFBO() // window FBO ID
326 );
327
328 if (!new_onscreen_surface) {
329 return false;
330 }
331
332 onscreen_surface_ = std::move(new_onscreen_surface);
333 }
334
335 return true;
336 }
337
AcquireRenderSurface(const SkISize & untransformed_size,const SkMatrix & root_surface_transformation)338 sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(
339 const SkISize& untransformed_size,
340 const SkMatrix& root_surface_transformation) {
341 const auto transformed_rect = root_surface_transformation.mapRect(
342 SkRect::MakeWH(untransformed_size.width(), untransformed_size.height()));
343
344 const auto transformed_size =
345 SkISize::Make(transformed_rect.width(), transformed_rect.height());
346
347 if (!CreateOrUpdateSurfaces(transformed_size)) {
348 return nullptr;
349 }
350
351 return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
352 }
353
354 // |Surface|
GetContext()355 GrContext* GPUSurfaceGL::GetContext() {
356 return context_.get();
357 }
358
359 // |Surface|
GetExternalViewEmbedder()360 flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() {
361 return delegate_->GetExternalViewEmbedder();
362 }
363
364 // |Surface|
MakeRenderContextCurrent()365 bool GPUSurfaceGL::MakeRenderContextCurrent() {
366 return delegate_->GLContextMakeCurrent();
367 }
368
369 } // namespace flutter
370