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
44unsigned long Skwasm::GetRasterThread() {
45 static unsigned long thread = []() {
48 return 0UL;
49 }
50 assert(emscripten_is_main_browser_thread());
51 unsigned long t = emscripten_malloc_wasm_worker(65536);
52 emscripten_wasm_worker_post_function_v(t, []() {
53 // Listen to the main thread from the worker
55 });
56
57 // Listen to messages from the worker
59 return t;
60 }();
61 return thread;
62}
63
65 // Ensure the raster thread is initialized
67}
68
69// General getters are implemented in the header.
70
71// Lifecycle
72
74 assert(emscripten_is_main_browser_thread());
75 callback_handler_ = callback_handler;
76}
77
79 if (gl_context_) {
80 skwasm_destroyContext(gl_context_);
81 }
82 delete this;
83}
84
85// Main thread only
87 assert(emscripten_is_main_browser_thread());
88 uint32_t callback_id = ++current_callback_id_;
89 skwasm_dispatchTransferCanvas(GetRasterThread(), this, canvas, callback_id);
90 return callback_id;
91}
92
93void Skwasm::Surface::OnInitialized(uint32_t callback_id) {
94 assert(emscripten_is_main_browser_thread());
95 callback_handler_(callback_id, (void*)context_lost_callback_id_,
96 __builtin_wasm_ref_null_extern());
97}
98
99// Worker thread only
101 uint32_t callback_id) {
102 if (render_context_) {
103 render_context_.reset();
104 }
105 canvas_width_ = 1;
106 canvas_height_ = 1;
107 bool antialias = skwasm_isWimp();
108 gl_context_ = skwasm_getGlContextForCanvas(canvas, antialias, this);
109 if (!gl_context_) {
110 printf("Failed to create context!\n");
111 return;
112 }
113
114 Skwasm::makeCurrent(gl_context_);
115 emscripten_webgl_enable_extension(gl_context_, "WEBGL_debug_renderer_info");
116
117 // WebGL should already be clearing the color and stencil buffers, but do it
118 // again here to ensure Skia receives them in the expected state.
119 emscripten_glBindFramebuffer(GL_FRAMEBUFFER, 0);
120 emscripten_glClearColor(0, 0, 0, 0);
121 emscripten_glClearStencil(0);
122 emscripten_glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
123
124 int sample_count;
125 int stencil;
126 emscripten_glGetIntegerv(GL_SAMPLES, &sample_count);
127 emscripten_glGetIntegerv(GL_STENCIL_BITS, &stencil);
128
129 render_context_ = Skwasm::RenderContext::Make(sample_count, stencil);
130 render_context_->Resize(canvas_width_, canvas_height_);
131
132 context_lost_callback_id_ = ++current_callback_id_;
133
134 skwasm_reportInitialized(this, context_lost_callback_id_, callback_id);
135}
136
137// Resizing
138
140 assert(emscripten_is_main_browser_thread());
141 uint32_t callback_id = ++current_callback_id_;
142
144 callback_id);
145 return callback_id;
146}
147
148void Skwasm::Surface::OnResizeComplete(uint32_t callback_id) {
149 assert(emscripten_is_main_browser_thread());
150 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
151}
152
154 int height,
155 uint32_t callback_id) {
156 ResizeSurface(width, height);
157 skwasm_reportResizeComplete(this, callback_id);
158}
159
160void Skwasm::Surface::ResizeSurface(int width, int height) {
161 if (width != canvas_width_ || height != canvas_height_) {
162 canvas_width_ = width;
163 canvas_height_ = height;
164 RecreateSurface();
165 }
166}
167
168// Rendering
169
171 int count) {
172 assert(emscripten_is_main_browser_thread());
173 uint32_t callback_id = ++current_callback_id_;
174 std::unique_ptr<sk_sp<flutter::DisplayList>[]> picture_pointers =
175 std::make_unique<sk_sp<flutter::DisplayList>[]>(count);
176 for (int i = 0; i < count; i++) {
177 picture_pointers[i] = sk_ref_sp(pictures[i]);
178 }
179
180 // Releasing picture_pointers here and will recreate the unique_ptr on the
181 // other thread See surface_renderPicturesOnWorker
183 picture_pointers.release(), count, callback_id);
184 return callback_id;
185}
186
187void Skwasm::Surface::OnRenderComplete(uint32_t callback_id,
188 SkwasmObject image_bitmap) {
189 assert(emscripten_is_main_browser_thread());
190 callback_handler_(callback_id, nullptr, image_bitmap);
191}
192
194 sk_sp<flutter::DisplayList>* pictures,
195 int picture_count,
196 uint32_t callback_id,
197 double raster_start) {
198 Skwasm::makeCurrent(gl_context_);
199 // This is initialized on the first call to `skwasm_captureImageBitmap` and
200 // then populated with more bitmaps on subsequent calls.
201 SkwasmObject image_bitmap_array = __builtin_wasm_ref_null_extern();
202 for (int i = 0; i < picture_count; i++) {
203 sk_sp<flutter::DisplayList> picture = pictures[i];
204 render_context_->RenderPicture(picture);
205 image_bitmap_array =
206 skwasm_captureImageBitmap(gl_context_, image_bitmap_array);
207 }
208 skwasm_resolveAndPostImages(this, image_bitmap_array, raster_start,
209 callback_id);
210}
211
212// Image Rasterization
213
216 assert(emscripten_is_main_browser_thread());
217 uint32_t callback_id = ++current_callback_id_;
218 image->ref();
219
221 callback_id);
222 return callback_id;
223}
224
225void Skwasm::Surface::OnRasterizeComplete(uint32_t callback_id, SkData* data) {
226 assert(emscripten_is_main_browser_thread());
227 callback_handler_(callback_id, data, __builtin_wasm_ref_null_extern());
228}
229
232 uint32_t callback_id) {
233 // We handle PNG encoding with browser APIs so that we can omit libpng from
234 // skia to save binary size.
236 Skwasm::makeCurrent(gl_context_);
238 ? SkAlphaType::kUnpremul_SkAlphaType
239 : SkAlphaType::kPremul_SkAlphaType;
240 SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
241 SkColorType::kRGBA_8888_SkColorType,
242 alpha_type, SkColorSpace::MakeSRGB());
243 sk_sp<SkData> data;
244 size_t bytes_per_row = 4 * image->width();
245 size_t byte_size = info.computeByteSize(bytes_per_row);
246 data = SkData::MakeUninitialized(byte_size);
247 uint8_t* pixels = reinterpret_cast<uint8_t*>(data->writable_data());
248
249 render_context_->RasterizeImage(image, format,
250 reinterpret_cast<void*>(pixels));
251
252 image->unref();
253 skwasm_postRasterizeResult(this, data.release(), callback_id);
254}
255
256// Context Loss
257
259 assert(emscripten_is_main_browser_thread());
260 uint32_t callback_id = ++current_callback_id_;
262 return callback_id;
263}
264
265void Skwasm::Surface::OnContextLossTriggered(uint32_t callback_id) {
266 assert(emscripten_is_main_browser_thread());
267 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
268}
269
270void Skwasm::Surface::ReportContextLost(uint32_t callback_id) {
271 assert(emscripten_is_main_browser_thread());
272 callback_handler_(callback_id, nullptr, __builtin_wasm_ref_null_extern());
273}
274
280
282 if (!context_lost_callback_id_) {
283 printf("Received context lost event but never set callback handler!\n");
284 return;
285 }
286 skwasm_reportContextLost(this, context_lost_callback_id_);
287}
288
289// Other
290
292 render_context_->SetResourceCacheLimit(bytes);
293}
294
295std::unique_ptr<Skwasm::TextureSourceWrapper>
297 return std::unique_ptr<Skwasm::TextureSourceWrapper>(
298 new Skwasm::TextureSourceWrapper(texture_source));
299}
300
301// Private methods
302
303void Skwasm::Surface::RecreateSurface() {
304 Skwasm::makeCurrent(gl_context_);
305 skwasm_resizeCanvas(gl_context_, canvas_width_, canvas_height_);
306 render_context_->Resize(canvas_width_, canvas_height_);
307}
308
309// TextureSourceWrapper implementation
310
315
319
323
324// C-style API
325
330
332 SkwasmObject canvas) {
333 // Dispatch to the worker so the canvas can be transferred to the worker.
334 return surface->SetCanvas(canvas);
335}
336
338 SkwasmObject canvas,
339 uint32_t callback_id) {
340 surface->ReceiveCanvasOnWorker(canvas, callback_id);
341}
342
344 uint32_t callback_id) {
345 surface->OnInitialized(callback_id);
346}
347
349 int width,
350 int height) {
351 return surface->SetSize(width, height);
352}
353
355 int width,
356 int height,
357 uint32_t callback_id) {
358 surface->ResizeOnWorker(width, height, callback_id);
359}
360
362 uint32_t callback_id) {
363 surface->OnResizeComplete(callback_id);
364}
365
369
370SKWASM_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
372 return surface->GetGlContext();
373}
374
376 return surface->TriggerContextLoss();
377}
378
380 uint32_t callback_id) {
381 surface->TriggerContextLossOnWorker(callback_id);
382}
383
385 uint32_t callback_id) {
386 surface->OnContextLossTriggered(callback_id);
387}
388
390 uint32_t callback_id) {
391 surface->ReportContextLost(callback_id);
392}
393
399
405
407 // This should be called directly only on the worker
408 surface->Dispose();
409}
410
412 int bytes) {
413 surface->SetResourceCacheLimit(bytes);
414}
415
417 flutter::DisplayList** pictures,
418 int count) {
419 return surface->RenderPictures(pictures, count);
420}
421
423 Skwasm::Surface* surface,
424 sk_sp<flutter::DisplayList>* pictures,
425 int picture_count,
426 uint32_t callback_id,
427 double raster_start) {
428 // This will release the pictures when they leave scope.
429 std::unique_ptr<sk_sp<flutter::DisplayList>[]> pictures_pointer =
430 std::unique_ptr<sk_sp<flutter::DisplayList>[]>(pictures);
431 surface->RenderPicturesOnWorker(pictures, picture_count, callback_id,
432 raster_start);
433}
434
438 return surface->RasterizeImage(image, format);
439}
440
442 Skwasm::Surface* surface,
445 uint32_t callback_id) {
446 surface->RasterizeImageOnWorker(image, format, callback_id);
447}
448
449// This is used by the skwasm JS support code to call back into C++ when the
450// we finish creating the image bitmap, which is an asynchronous operation.
452 uint32_t callback_id,
453 SkwasmObject image_bitmap) {
454 surface->OnRenderComplete(callback_id, image_bitmap);
455}
456
458 SkData* data,
459 uint32_t callback_id) {
460 surface->OnRasterizeComplete(callback_id, data);
461}
462
464 surface->OnContextLost();
465}
466
static std::unique_ptr< RenderContext > Make(int sample_count, int stencil)
void TriggerContextLossOnWorker(uint32_t callback_id)
Definition surface.cc:275
void ReceiveCanvasOnWorker(SkwasmObject canvas, uint32_t callback_id)
Definition surface.cc:100
void ReportContextLost(uint32_t callback_id)
Definition surface.cc:270
std::unique_ptr< TextureSourceWrapper > CreateTextureSourceWrapper(SkwasmObject textureSource)
Definition surface.cc:296
uint32_t SetSize(int width, int height)
Definition surface.cc:139
uint32_t TriggerContextLoss()
Definition surface.cc:258
void OnContextLost()
Definition surface.cc:281
void RasterizeImageOnWorker(flutter::DlImage *image, ImageByteFormat format, uint32_t callback_id)
Definition surface.cc:230
void OnInitialized(uint32_t callback_id)
Definition surface.cc:93
uint32_t SetCanvas(SkwasmObject canvas)
Definition surface.cc:86
void(uint32_t, void *, SkwasmObject) CallbackHandler
Definition surface.h:37
uint32_t RasterizeImage(flutter::DlImage *image, ImageByteFormat format)
Definition surface.cc:214
uint32_t RenderPictures(flutter::DisplayList **picture, int count)
Definition surface.cc:170
void OnRenderComplete(uint32_t callback_id, SkwasmObject image_bitmap)
Definition surface.cc:187
void OnRasterizeComplete(uint32_t callback_id, SkData *data)
Definition surface.cc:225
void OnResizeComplete(uint32_t callback_id)
Definition surface.cc:148
void Dispose()
Definition surface.cc:78
void RenderPicturesOnWorker(sk_sp< flutter::DisplayList > *picture, int picture_count, uint32_t callback_id, double raster_start)
Definition surface.cc:193
void SetCallbackHandler(CallbackHandler *callback_handler)
Definition surface.cc:73
void ResizeOnWorker(int width, int height, uint32_t callback_id)
Definition surface.cc:153
void SetResourceCacheLimit(int bytes)
Definition surface.cc:291
void OnContextLossTriggered(uint32_t callback_id)
Definition surface.cc:265
SkwasmObject GetTextureSource()
Definition surface.cc:320
TextureSourceWrapper(SkwasmObject texture_source)
Definition surface.cc:311
Represents an image whose allocation is (usually) resident on device memory.
Definition dl_image.h:34
FlutterVulkanImage * image
VkSurfaceKHR surface
Definition main.cc:65
uint32_t uint32_t * format
CallbackHandler callback_handler
__externref_t SkwasmObject
Definition wrappers.h:18
unsigned long GetRasterThread()
Definition surface.cc:44
void makeCurrent(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE handle)
Definition wrappers.h:26
uint32_t live_surface_count
ImageByteFormat
Definition helpers.h:75
SKWASM_EXPORT bool skwasm_isWimp()
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:343
SKWASM_EXPORT void surface_rasterizeImageOnWorker(Skwasm::Surface *surface, flutter::DlImage *image, Skwasm::ImageByteFormat format, uint32_t callback_id)
Definition surface.cc:441
SKWASM_EXPORT void surface_onContextLost(Skwasm::Surface *surface)
Definition surface.cc:463
SKWASM_EXPORT void surface_onRenderComplete(Skwasm::Surface *surface, uint32_t callback_id, SkwasmObject image_bitmap)
Definition surface.cc:451
SKWASM_EXPORT void surface_resizeOnWorker(Skwasm::Surface *surface, int width, int height, uint32_t callback_id)
Definition surface.cc:354
SKWASM_EXPORT Skwasm::Surface * surface_create()
Definition surface.cc:326
SKWASM_EXPORT uint32_t surface_setCanvas(Skwasm::Surface *surface, SkwasmObject canvas)
Definition surface.cc:331
SKWASM_EXPORT unsigned long surface_getThreadId(Skwasm::Surface *surface)
Definition surface.cc:366
SKWASM_EXPORT void surface_destroy(Skwasm::Surface *surface)
Definition surface.cc:400
SKWASM_EXPORT void surface_onRasterizeComplete(Skwasm::Surface *surface, SkData *data, uint32_t callback_id)
Definition surface.cc:457
SKWASM_EXPORT uint32_t surface_renderPictures(Skwasm::Surface *surface, flutter::DisplayList **pictures, int count)
Definition surface.cc:416
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:422
SKWASM_EXPORT void surface_setCallbackHandler(Skwasm::Surface *surface, Skwasm::Surface::CallbackHandler *callback_handler)
Definition surface.cc:394
SKWASM_EXPORT void surface_receiveCanvasOnWorker(Skwasm::Surface *surface, SkwasmObject canvas, uint32_t callback_id)
Definition surface.cc:337
SKWASM_EXPORT bool skwasm_isMultiThreaded()
Definition surface.cc:467
SKWASM_EXPORT uint32_t surface_rasterizeImage(Skwasm::Surface *surface, flutter::DlImage *image, Skwasm::ImageByteFormat format)
Definition surface.cc:435
SKWASM_EXPORT uint32_t surface_setSize(Skwasm::Surface *surface, int width, int height)
Definition surface.cc:348
SKWASM_EXPORT void surface_reportContextLost(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:389
SKWASM_EXPORT void surface_dispose(Skwasm::Surface *surface)
Definition surface.cc:406
SKWASM_EXPORT void surface_onResizeComplete(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:361
SKWASM_EXPORT void surface_setResourceCacheLimitBytes(Skwasm::Surface *surface, int bytes)
Definition surface.cc:411
SKWASM_EXPORT void surface_triggerContextLossOnWorker(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:379
SKWASM_EXPORT uint32_t surface_triggerContextLoss(Skwasm::Surface *surface)
Definition surface.cc:375
SKWASM_EXPORT void surface_onContextLossTriggered(Skwasm::Surface *surface, uint32_t callback_id)
Definition surface.cc:384
SKWASM_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE surface_getGlContext(Skwasm::Surface *surface)
Definition surface.cc:371
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)
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)
uint32_t skwasm_getGlContextForCanvas(SkwasmObject canvas, bool antialias, Skwasm::Surface *surface)
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)