Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
surface.cc
Go to the documentation of this file.
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
6
7#include <algorithm>
8
9#include <emscripten/wasm_worker.h>
10
15#include "third_party/skia/include/core/SkColorSpace.h"
16
17// This file implements the C++ side of the Skwasm Surface API.
18//
19// The general lifecycle of a method call that needs to be performed on the
20// web worker thread is as follows:
21//
22// 1. The method is called on the [Surface] object on the main thread.
23// This method will have the same name as the dart method that is calling it.
24// It will extract the arguments, generate a callback id, and then call a
25// `skwasm_dispatch*` method to send a message to the worker thread.
26// 2. The `skwasm_dispatch*` method will be a javascript method in
27// `library_skwasm_support.js`. This method will use `postMessage` to send a
28// message to the worker thread.
29// 3. The worker thread will receive the message in its `message` event
30// listener. The listener will call a `surface_*OnWorker` C++ method.
31// 4. The `surface_*OnWorker` method will call the corresponding `*OnWorker`
32// method on the [Surface] object. This method will do the actual work of
33// the method call.
34// 5. When the work is complete, the `*OnWorker` method will call a
35// `skwasm_report*` method. This will be a javascript method in
36// `library_skwasm_support.js` which will use `postMessage` to send a
37// message back to the main thread.
38// 6. The main thread will receive the message in its `message` event listener.
39// The listener will call an `on*` method on the C++ [Surface] object.
40// 7. The `on*` method will invoke the callback handler that was registered by
41// the Dart code, which will complete the future that was returned by the
42// original Dart method call.
43
47 } else {
48 assert(emscripten_is_main_browser_thread());
49
50 thread_ = emscripten_malloc_wasm_worker(65536);
51 emscripten_wasm_worker_post_function_v(thread_, []() {
52 // Listen to the main thread from the worker
54 });
55
56 // Listen to messages from the worker
57 skwasm_connectThread(thread_);
58 }
59}
60
61// General getters are implemented in the header.
62
63// Lifecycle
64
66 assert(emscripten_is_main_browser_thread());
67 callback_handler_ = callback_handler;
68}
69
71 if (gl_context_) {
72 skwasm_destroyContext(gl_context_);
73 }
74 delete this;
75}
76
77// Main thread only
79 assert(emscripten_is_main_browser_thread());
80 uint32_t callback_id = ++current_callback_id_;
81 skwasm_dispatchTransferCanvas(thread_, this, canvas, callback_id);
82 return callback_id;
83}
84
85void Skwasm::Surface::OnInitialized(uint32_t callback_id) {
86 assert(emscripten_is_main_browser_thread());
87 callback_handler_(callback_id, (void*)context_lost_callback_id_,
88 __builtin_wasm_ref_null_extern());
89}
90
91// Worker thread only
93 uint32_t callback_id) {
94 if (render_context_) {
95 render_context_.reset();
96 }
97 canvas_width_ = 1;
98 canvas_height_ = 1;
99 gl_context_ = skwasm_getGlContextForCanvas(canvas, this);
100 if (!gl_context_) {
101 printf("Failed to create context!\n");
102 return;
103 }
104
105 Skwasm::makeCurrent(gl_context_);
106 emscripten_webgl_enable_extension(gl_context_, "WEBGL_debug_renderer_info");
107
108 // WebGL should already be clearing the color and stencil buffers, but do it
109 // again here to ensure Skia receives them in the expected state.
110 emscripten_glBindFramebuffer(GL_FRAMEBUFFER, 0);
111 emscripten_glClearColor(0, 0, 0, 0);
112 emscripten_glClearStencil(0);
113 emscripten_glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
114
115 int sample_count;
116 int stencil;
117 emscripten_glGetIntegerv(GL_SAMPLES, &sample_count);
118 emscripten_glGetIntegerv(GL_STENCIL_BITS, &stencil);
119
120 render_context_ = Skwasm::RenderContext::Make(sample_count, stencil);
121 render_context_->Resize(canvas_width_, canvas_height_);
122
123 context_lost_callback_id_ = ++current_callback_id_;
124
125 skwasm_reportInitialized(this, context_lost_callback_id_, callback_id);
126}
127
128// Resizing
129
131 assert(emscripten_is_main_browser_thread());
132 uint32_t callback_id = ++current_callback_id_;
133
134 skwasm_dispatchResizeSurface(thread_, this, width, height, callback_id);
135 return callback_id;
136}
137
138void Skwasm::Surface::OnResizeComplete(uint32_t callback_id) {
139 assert(emscripten_is_main_browser_thread());
140 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
141}
142
144 int height,
145 uint32_t callback_id) {
146 ResizeSurface(width, height);
147 skwasm_reportResizeComplete(this, callback_id);
148}
149
150void Skwasm::Surface::ResizeSurface(int width, int height) {
151 if (width != canvas_width_ || height != canvas_height_) {
152 canvas_width_ = width;
153 canvas_height_ = height;
154 RecreateSurface();
155 }
156}
157
158// Rendering
159
161 int count) {
162 assert(emscripten_is_main_browser_thread());
163 uint32_t callback_id = ++current_callback_id_;
164 std::unique_ptr<sk_sp<flutter::DisplayList>[]> picture_pointers =
165 std::make_unique<sk_sp<flutter::DisplayList>[]>(count);
166 for (int i = 0; i < count; i++) {
167 picture_pointers[i] = sk_ref_sp(pictures[i]);
168 }
169
170 // Releasing picture_pointers here and will recreate the unique_ptr on the
171 // other thread See surface_renderPicturesOnWorker
172 skwasm_dispatchRenderPictures(thread_, this, picture_pointers.release(),
173 count, callback_id);
174 return callback_id;
175}
176
177void Skwasm::Surface::OnRenderComplete(uint32_t callback_id,
178 SkwasmObject image_bitmap) {
179 assert(emscripten_is_main_browser_thread());
180 callback_handler_(callback_id, nullptr, image_bitmap);
181}
182
184 sk_sp<flutter::DisplayList>* pictures,
185 int picture_count,
186 uint32_t callback_id,
187 double raster_start) {
188 Skwasm::makeCurrent(gl_context_);
189 // This is initialized on the first call to `skwasm_captureImageBitmap` and
190 // then populated with more bitmaps on subsequent calls.
191 SkwasmObject image_bitmap_array = __builtin_wasm_ref_null_extern();
192 for (int i = 0; i < picture_count; i++) {
193 sk_sp<flutter::DisplayList> picture = pictures[i];
194 render_context_->RenderPicture(picture);
195 image_bitmap_array =
196 skwasm_captureImageBitmap(gl_context_, image_bitmap_array);
197 }
198 skwasm_resolveAndPostImages(this, image_bitmap_array, raster_start,
199 callback_id);
200}
201
202// Image Rasterization
203
206 assert(emscripten_is_main_browser_thread());
207 uint32_t callback_id = ++current_callback_id_;
208 image->ref();
209
210 skwasm_dispatchRasterizeImage(thread_, this, image, format, callback_id);
211 return callback_id;
212}
213
214void Skwasm::Surface::OnRasterizeComplete(uint32_t callback_id, SkData* data) {
215 assert(emscripten_is_main_browser_thread());
216 callback_handler_(callback_id, data, __builtin_wasm_ref_null_extern());
217}
218
221 uint32_t callback_id) {
222 // We handle PNG encoding with browser APIs so that we can omit libpng from
223 // skia to save binary size.
225 Skwasm::makeCurrent(gl_context_);
227 ? SkAlphaType::kUnpremul_SkAlphaType
228 : SkAlphaType::kPremul_SkAlphaType;
229 SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
230 SkColorType::kRGBA_8888_SkColorType,
231 alpha_type, SkColorSpace::MakeSRGB());
232 sk_sp<SkData> data;
233 size_t bytes_per_row = 4 * image->width();
234 size_t byte_size = info.computeByteSize(bytes_per_row);
235 data = SkData::MakeUninitialized(byte_size);
236 uint8_t* pixels = reinterpret_cast<uint8_t*>(data->writable_data());
237
238 // TODO(jacksongardner):
239 // Normally we'd just call `readPixels` on the image. However, this doesn't
240 // actually work in some cases due to a skia bug. Instead, we just draw the
241 // image to our scratch canvas and grab the pixels out directly with
242 // `glReadPixels`. Once the skia bug is fixed, we should switch back to using
243 // `SkImage::readPixels` instead.
244 // See https://g-issues.skia.org/issues/349201915
245 render_context_->RenderImage(image, format);
246
247 emscripten_glReadPixels(0, 0, image->width(), image->height(), GL_RGBA,
248 GL_UNSIGNED_BYTE, reinterpret_cast<void*>(pixels));
249
250 image->unref();
251 skwasm_postRasterizeResult(this, data.release(), callback_id);
252}
253
254// Context Loss
255
257 assert(emscripten_is_main_browser_thread());
258 uint32_t callback_id = ++current_callback_id_;
259 skwasm_dispatchTriggerContextLoss(thread_, this, callback_id);
260 return callback_id;
261}
262
263void Skwasm::Surface::OnContextLossTriggered(uint32_t callback_id) {
264 assert(emscripten_is_main_browser_thread());
265 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
266}
267
268void Skwasm::Surface::ReportContextLost(uint32_t callback_id) {
269 assert(emscripten_is_main_browser_thread());
270 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
271}
272
278
280 if (!context_lost_callback_id_) {
281 printf("Received context lost event but never set callback handler!\n");
282 return;
283 }
284 skwasm_reportContextLost(this, context_lost_callback_id_);
285}
286
287// Other
288
290 render_context_->SetResourceCacheLimit(bytes);
291}
292
293std::unique_ptr<Skwasm::TextureSourceWrapper>
295 return std::unique_ptr<Skwasm::TextureSourceWrapper>(
296 new Skwasm::TextureSourceWrapper(thread_, texture_source));
297}
298
299// Private methods
300
301void Skwasm::Surface::RecreateSurface() {
302 Skwasm::makeCurrent(gl_context_);
303 skwasm_resizeCanvas(gl_context_, canvas_width_, canvas_height_);
304 render_context_->Resize(canvas_width_, canvas_height_);
305}
306
307// TextureSourceWrapper implementation
308
310 SkwasmObject texture_source)
311 : raster_thread_id_(thread_id) {
312 skwasm_setAssociatedObjectOnThread(thread_id, this, texture_source);
313}
314
318
322
323// C-style API
324
329
331 SkwasmObject canvas) {
332 // Dispatch to the worker so the canvas can be transferred to the worker.
333 return surface->SetCanvas(canvas);
334}
335
337 SkwasmObject canvas,
338 uint32_t callback_id) {
339 surface->ReceiveCanvasOnWorker(canvas, callback_id);
340}
341
343 uint32_t callback_id) {
344 surface->OnInitialized(callback_id);
345}
346
348 int width,
349 int height) {
350 return surface->SetSize(width, height);
351}
352
354 int width,
355 int height,
356 uint32_t callback_id) {
357 surface->ResizeOnWorker(width, height, callback_id);
358}
359
361 uint32_t callback_id) {
362 surface->OnResizeComplete(callback_id);
363}
364
366 return surface->GetThreadId();
367}
368
369SKWASM_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
371 return surface->GetGlContext();
372}
373
375 return surface->TriggerContextLoss();
376}
377
379 uint32_t callback_id) {
380 surface->TriggerContextLossOnWorker(callback_id);
381}
382
384 uint32_t callback_id) {
385 surface->OnContextLossTriggered(callback_id);
386}
387
389 uint32_t callback_id) {
390 surface->ReportContextLost(callback_id);
391}
392
398
401 // Dispatch to the worker
403}
404
406 // This should be called directly only on the worker
407 surface->Dispose();
408}
409
411 int bytes) {
412 surface->SetResourceCacheLimit(bytes);
413}
414
416 flutter::DisplayList** pictures,
417 int count) {
418 return surface->RenderPictures(pictures, count);
419}
420
422 Skwasm::Surface* surface,
423 sk_sp<flutter::DisplayList>* pictures,
424 int picture_count,
425 uint32_t callback_id,
426 double raster_start) {
427 // This will release the pictures when they leave scope.
428 std::unique_ptr<sk_sp<flutter::DisplayList>[]> pictures_pointer =
429 std::unique_ptr<sk_sp<flutter::DisplayList>[]>(pictures);
430 surface->RenderPicturesOnWorker(pictures, picture_count, callback_id,
431 raster_start);
432}
433
437 return surface->RasterizeImage(image, format);
438}
439
441 Skwasm::Surface* surface,
444 uint32_t callback_id) {
445 surface->RasterizeImageOnWorker(image, format, callback_id);
446}
447
448// This is used by the skwasm JS support code to call back into C++ when the
449// we finish creating the image bitmap, which is an asynchronous operation.
451 uint32_t callback_id,
452 SkwasmObject image_bitmap) {
453 surface->OnRenderComplete(callback_id, image_bitmap);
454}
455
457 SkData* data,
458 uint32_t callback_id) {
459 surface->OnRasterizeComplete(callback_id, data);
460}
461
463 surface->OnContextLost();
464}
465
static std::unique_ptr< RenderContext > Make(int sample_count, int stencil)
void TriggerContextLossOnWorker(uint32_t callback_id)
Definition surface.cc:273
void ReceiveCanvasOnWorker(SkwasmObject canvas, uint32_t callback_id)
Definition surface.cc:92
void ReportContextLost(uint32_t callback_id)
Definition surface.cc:268
std::unique_ptr< TextureSourceWrapper > CreateTextureSourceWrapper(SkwasmObject textureSource)
Definition surface.cc:294
uint32_t SetSize(int width, int height)
Definition surface.cc:130
uint32_t TriggerContextLoss()
Definition surface.cc:256
void OnContextLost()
Definition surface.cc:279
void RasterizeImageOnWorker(flutter::DlImage *image, ImageByteFormat format, uint32_t callback_id)
Definition surface.cc:219
void OnInitialized(uint32_t callback_id)
Definition surface.cc:85
uint32_t SetCanvas(SkwasmObject canvas)
Definition surface.cc:78
void(uint32_t, void *, SkwasmObject) CallbackHandler
Definition surface.h:38
uint32_t RasterizeImage(flutter::DlImage *image, ImageByteFormat format)
Definition surface.cc:204
uint32_t RenderPictures(flutter::DisplayList **picture, int count)
Definition surface.cc:160
void OnRenderComplete(uint32_t callback_id, SkwasmObject image_bitmap)
Definition surface.cc:177
void OnRasterizeComplete(uint32_t callback_id, SkData *data)
Definition surface.cc:214
void OnResizeComplete(uint32_t callback_id)
Definition surface.cc:138
void Dispose()
Definition surface.cc:70
void RenderPicturesOnWorker(sk_sp< flutter::DisplayList > *picture, int picture_count, uint32_t callback_id, double raster_start)
Definition surface.cc:183
void SetCallbackHandler(CallbackHandler *callback_handler)
Definition surface.cc:65
void ResizeOnWorker(int width, int height, uint32_t callback_id)
Definition surface.cc:143
void SetResourceCacheLimit(int bytes)
Definition surface.cc:289
void OnContextLossTriggered(uint32_t callback_id)
Definition surface.cc:263
SkwasmObject GetTextureSource()
Definition surface.cc:319
TextureSourceWrapper(unsigned long thread_id, SkwasmObject texture_source)
Definition surface.cc:309
Represents an image whose allocation is (usually) resident on device memory.
Definition dl_image.h:32
FlutterVulkanImage * image
VkSurfaceKHR surface
Definition main.cc:65
uint32_t uint32_t * format
CallbackHandler callback_handler
__externref_t SkwasmObject
Definition wrappers.h:18
void makeCurrent(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE handle)
Definition wrappers.h:26
uint32_t live_surface_count
ImageByteFormat
Definition helpers.h:75
int32_t height
int32_t width
uint32_t alpha_type
#define SKWASM_EXPORT
Definition export.h:10
SKWASM_EXPORT void surface_onInitialized(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:342
SKWASM_EXPORT void surface_rasterizeImageOnWorker(Skwasm::Surface *surface, flutter::DlImage *image, Skwasm::ImageByteFormat format, uint32_t callback_id)
Definition surface.cc:440
SKWASM_EXPORT void surface_onContextLost(Skwasm::Surface *surface)
Definition surface.cc:462
SKWASM_EXPORT void surface_onRenderComplete(Skwasm::Surface *surface, uint32_t callback_id, SkwasmObject image_bitmap)
Definition surface.cc:450
SKWASM_EXPORT void surface_resizeOnWorker(Skwasm::Surface *surface, int width, int height, uint32_t callback_id)
Definition surface.cc:353
SKWASM_EXPORT Skwasm::Surface * surface_create()
Definition surface.cc:325
SKWASM_EXPORT uint32_t surface_setCanvas(Skwasm::Surface *surface, SkwasmObject canvas)
Definition surface.cc:330
SKWASM_EXPORT unsigned long surface_getThreadId(Skwasm::Surface *surface)
Definition surface.cc:365
SKWASM_EXPORT void surface_destroy(Skwasm::Surface *surface)
Definition surface.cc:399
SKWASM_EXPORT void surface_onRasterizeComplete(Skwasm::Surface *surface, SkData *data, uint32_t callback_id)
Definition surface.cc:456
SKWASM_EXPORT uint32_t surface_renderPictures(Skwasm::Surface *surface, flutter::DisplayList **pictures, int count)
Definition surface.cc:415
SKWASM_EXPORT void surface_renderPicturesOnWorker(Skwasm::Surface *surface, sk_sp< flutter::DisplayList > *pictures, int picture_count, uint32_t callback_id, double raster_start)
Definition surface.cc:421
SKWASM_EXPORT void surface_setCallbackHandler(Skwasm::Surface *surface, Skwasm::Surface::CallbackHandler *callback_handler)
Definition surface.cc:393
SKWASM_EXPORT void surface_receiveCanvasOnWorker(Skwasm::Surface *surface, SkwasmObject canvas, uint32_t callback_id)
Definition surface.cc:336
SKWASM_EXPORT bool skwasm_isMultiThreaded()
Definition surface.cc:466
SKWASM_EXPORT uint32_t surface_rasterizeImage(Skwasm::Surface *surface, flutter::DlImage *image, Skwasm::ImageByteFormat format)
Definition surface.cc:434
SKWASM_EXPORT uint32_t surface_setSize(Skwasm::Surface *surface, int width, int height)
Definition surface.cc:347
SKWASM_EXPORT void surface_reportContextLost(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:388
SKWASM_EXPORT void surface_dispose(Skwasm::Surface *surface)
Definition surface.cc:405
SKWASM_EXPORT void surface_onResizeComplete(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:360
SKWASM_EXPORT void surface_setResourceCacheLimitBytes(Skwasm::Surface *surface, int bytes)
Definition surface.cc:410
SKWASM_EXPORT void surface_triggerContextLossOnWorker(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:378
SKWASM_EXPORT uint32_t surface_triggerContextLoss(Skwasm::Surface *surface)
Definition surface.cc:374
SKWASM_EXPORT void surface_onContextLossTriggered(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:383
SKWASM_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE surface_getGlContext(Skwasm::Surface *surface)
Definition surface.cc:370
void skwasm_resolveAndPostImages(Skwasm::Surface *surface, SkwasmObject image_bitmaps, double raster_start, uint32_t callback_id)
SkwasmObject skwasm_getAssociatedObject(void *pointer)
void skwasm_reportContextLossTriggered(Skwasm::Surface *surface, uint32_t callback_id)
SkwasmObject skwasm_captureImageBitmap(uint32_t context_handle, SkwasmObject image_bitmaps)
void skwasm_connectThread(pthread_t thread_id)
void skwasm_dispatchDisposeSurface(unsigned long thread_id, Skwasm::Surface *surface)
void skwasm_postRasterizeResult(Skwasm::Surface *surface, SkData *data, uint32_t callback_id)
uint32_t skwasm_getGlContextForCanvas(SkwasmObject canvas, Skwasm::Surface *surface)
void skwasm_dispatchRenderPictures(unsigned long thread_id, Skwasm::Surface *surface, sk_sp< flutter::DisplayList > *pictures, int count, uint32_t callback_id)
void skwasm_dispatchTransferCanvas(unsigned long thread_id, Skwasm::Surface *surface, SkwasmObject canvas, uint32_t callback_id)
void skwasm_dispatchRasterizeImage(unsigned long thread_id, Skwasm::Surface *surface, flutter::DlImage *image, Skwasm::ImageByteFormat format, uint32_t callback_id)
bool skwasm_isSingleThreaded()
void skwasm_reportInitialized(Skwasm::Surface *surface, uint32_t callback_id, uint32_t context_lost_callback_id)
void skwasm_destroyContext(uint32_t context_handle)
void skwasm_resizeCanvas(uint32_t contextHandle, int width, int height)
void skwasm_triggerContextLossOnCanvas()
void skwasm_dispatchResizeSurface(unsigned long thread_id, Skwasm::Surface *surface, int width, int height, uint32_t callback_id)
void skwasm_reportResizeComplete(Skwasm::Surface *surface, uint32_t callback_id)
__externref_t SkwasmObject
void skwasm_disposeAssociatedObjectOnThread(unsigned long thread_id, void *pointer)
void skwasm_reportContextLost(Skwasm::Surface *surface, uint32_t callback_id)
void skwasm_dispatchTriggerContextLoss(unsigned long thread_id, Skwasm::Surface *surface, uint32_t callback_id)
void skwasm_setAssociatedObjectOnThread(unsigned long thread_id, void *pointer, SkwasmObject object)
std::shared_ptr< const fml::Mapping > data