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 | |
26 | namespace flutter { |
27 | |
28 | // Default maximum number of budgeted resources in the cache. |
29 | static 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. |
36 | static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); |
37 | |
38 | GPUSurfaceGL::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 | |
96 | GPUSurfaceGL::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 | |
116 | GPUSurfaceGL::~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| |
137 | bool GPUSurfaceGL::IsValid() { |
138 | return valid_; |
139 | } |
140 | |
141 | static 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 | |
154 | static 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 | |
186 | bool 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| |
226 | SkMatrix GPUSurfaceGL::GetRootTransformation() const { |
227 | return delegate_->GLContextSurfaceTransformation(); |
228 | } |
229 | |
230 | // |Surface| |
231 | std::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 | |
272 | bool 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 | |
308 | sk_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| |
325 | GrDirectContext* GPUSurfaceGL::GetContext() { |
326 | return context_.get(); |
327 | } |
328 | |
329 | // |Surface| |
330 | flutter::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { |
331 | return delegate_->GetExternalViewEmbedder(); |
332 | } |
333 | |
334 | // |Surface| |
335 | std::unique_ptr<GLContextResult> GPUSurfaceGL::MakeRenderContextCurrent() { |
336 | return delegate_->GLContextMakeCurrent(); |
337 | } |
338 | |
339 | } // namespace flutter |
340 | |