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