Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/web_ui/dev/test_dart2wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ window.onload = async function () {
const skwasmInstance = await skwasm();
window._flutter_skwasmInstance = skwasmInstance;
resolve({
"skwasm": skwasmInstance.asm ?? skwasmInstance.wasmExports,
"skwasm": skwasmInstance.wasmExports,
"skwasmWrapper": skwasmInstance,
"ffi": {
"memory": skwasmInstance.wasmMemory,
}
Expand Down
49 changes: 16 additions & 33 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@DefaultAsset('skwasm')
library skwasm_impl;

import 'dart:_wasm';
import 'dart:ffi';
import 'dart:js_interop';

Expand Down Expand Up @@ -39,45 +40,27 @@ external ImageHandle imageCreateFromPixels(
int rowByteCount,
);

// We actually want this function to look something like this:
//
// @Native<ImageHandle Function(
// WasmExternRef,
// Int,
// Int,
// SurfaceHandle,
// )>(symbol: 'image_createFromTextureSource', isLeaf: true)
// external ImageHandle imageCreateFromTextureSource(
// JSAny textureSource,
// int width,
// int height,
// SurfaceHandle handle,
// );
//
// However, this doesn't work because we cannot use extern refs as part of @Native
// annotations currently. For now, we can use JS interop to expose this function
// instead.
extension SkwasmImageExtension on SkwasmInstance {
@JS('wasmExports.image_createFromTextureSource')
external JSNumber imageCreateFromTextureSource(
JSAny textureSource,
JSNumber width,
JSNumber height,
JSNumber surfaceHandle,
);
}
// We use a wasm import directly here instead of @Native since this uses an externref
// in the function signature.
ImageHandle imageCreateFromTextureSource(
JSAny frame,
int width,
int height,
SurfaceHandle handle
) => ImageHandle.fromAddress(
skwasmInstance.imageCreateFromTextureSource(
frame,
width.toJS,
height.toJS,
handle.address.toJS,
).toDartInt
imageCreateFromTextureSourceImpl(
externRefForJSAny(frame),
width.toWasmI32(),
height.toWasmI32(),
handle.address.toWasmI32(),
).toIntUnsigned()
);
@pragma('wasm:import', 'skwasm.image_createFromTextureSource')
external WasmI32 imageCreateFromTextureSourceImpl(
WasmExternRef? frame,
WasmI32 width,
WasmI32 height,
WasmI32 surfaceHandle,
);

@Native<Void Function(ImageHandle)>(symbol:'image_ref', isLeaf: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:_wasm';
import 'dart:js_interop';

@JS()
Expand All @@ -17,37 +18,11 @@ extension WebAssemblyMemoryExtension on WebAssemblyMemory {
class SkwasmInstance {}

extension SkwasmInstanceExtension on SkwasmInstance {
external JSNumber getEmptyTableSlot();

// The function here *must* be a directly exported wasm function, not a
// JavaScript function. If you actually need to add a JavaScript function,
// use `addFunction` instead.
external void setWasmTableEntry(JSNumber index, JSAny function);

external JSNumber addFunction(WebAssemblyFunction function);
external void removeFunction(JSNumber functionPointer);

external WebAssemblyMemory get wasmMemory;
}

@JS('window._flutter_skwasmInstance')
external SkwasmInstance get skwasmInstance;

@JS()
@staticInterop
@anonymous
class WebAssemblyFunctionType {
external factory WebAssemblyFunctionType({
required JSArray parameters,
required JSArray results,
});
}

@JS('WebAssembly.Function')
@staticInterop
class WebAssemblyFunction {
external factory WebAssemblyFunction(
WebAssemblyFunctionType functionType,
JSFunction function
);
}
@pragma('wasm:import', 'skwasmWrapper.addFunction')
external WasmI32 addFunction(WasmFuncRef function);
84 changes: 49 additions & 35 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New code here (and this file in general) could use some dartdocs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments.

// found in the LICENSE file.

import 'dart:_wasm';
import 'dart:async';
import 'dart:ffi';
import 'dart:js_interop';
Expand All @@ -11,7 +12,52 @@ import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;

@pragma('wasm:export')
WasmVoid callbackHandler(WasmI32 callbackId, WasmI32 context, WasmExternRef? jsContext) {
// Actually hide this call behind whether skwasm is enabled. Otherwise, the SkwasmCallbackHandler
// won't actually be tree-shaken, and we end up with skwasm imports in non-skwasm builds.
if (FlutterConfiguration.flutterWebUseSkwasm) {
SkwasmCallbackHandler.instance.handleCallback(callbackId, context, jsContext);
}
return WasmVoid();
}

// This class handles callbacks coming from Skwasm by keeping a map of callback IDs to Completers
class SkwasmCallbackHandler {
SkwasmCallbackHandler._withCallbackPointer(this.callbackPointer);

factory SkwasmCallbackHandler._() {
final WasmFuncRef wasmFunction = WasmFunction<WasmVoid Function(WasmI32, WasmI32, WasmExternRef?)>.fromFunction(callbackHandler);
final int functionIndex = addFunction(wasmFunction).toIntUnsigned();
return SkwasmCallbackHandler._withCallbackPointer(
OnRenderCallbackHandle.fromAddress(functionIndex)
);
}
static SkwasmCallbackHandler instance = SkwasmCallbackHandler._();

final OnRenderCallbackHandle callbackPointer;
final Map<CallbackId, Completer<JSAny>> _pendingCallbacks = <int, Completer<JSAny>>{};

// Returns a future that will resolve when Skwasm calls back with the given callbackID
Future<JSAny> registerCallback(int callbackId) {
final Completer<JSAny> completer = Completer<JSAny>();
_pendingCallbacks[callbackId] = completer;
return completer.future;
}

void handleCallback(WasmI32 callbackId, WasmI32 context, WasmExternRef? jsContext) {
// Skwasm can either callback with a JS object (an externref) or it can call back
// with a simple integer, which usually refers to a pointer on its heap. In order
// to coerce these into a single type, we just make the completers take a JSAny
// that either contains the JS object or a JSNumber that contains the integer value.
final Completer<JSAny> completer = _pendingCallbacks.remove(callbackId.toIntUnsigned())!;
if (!jsContext.isNull) {
completer.complete(jsContext!.toJS);
} else {
completer.complete(context.toIntUnsigned().toJS);
}
}
}

class SkwasmSurface {
factory SkwasmSurface() {
Expand All @@ -25,32 +71,16 @@ class SkwasmSurface {

SkwasmSurface._fromHandle(this.handle) : threadId = surfaceGetThreadId(handle);
final SurfaceHandle handle;
OnRenderCallbackHandle _callbackHandle = nullptr;
final Map<CallbackId, Completer<JSAny>> _pendingCallbacks = <int, Completer<JSAny>>{};

final int threadId;

void _initialize() {
final WebAssemblyFunction wasmFunction = WebAssemblyFunction(
WebAssemblyFunctionType(
parameters: <JSString>[
'i32'.toJS,
'i32'.toJS,
'externref'.toJS
].toJS,
results: <JSString>[].toJS
),
_callbackHandler.toJS,
);
_callbackHandle = OnRenderCallbackHandle.fromAddress(
skwasmInstance.addFunction(wasmFunction).toDartInt,
);
surfaceSetCallbackHandler(handle, _callbackHandle);
surfaceSetCallbackHandler(handle, SkwasmCallbackHandler.instance.callbackPointer);
}

Future<DomImageBitmap> renderPicture(SkwasmPicture picture) async {
final int callbackId = surfaceRenderPicture(handle, picture.handle);
final DomImageBitmap bitmap = (await _registerCallback(callbackId)) as DomImageBitmap;
final DomImageBitmap bitmap = (await SkwasmCallbackHandler.instance.registerCallback(callbackId)) as DomImageBitmap;
return bitmap;
}

Expand All @@ -60,7 +90,7 @@ class SkwasmSurface {
image.handle,
format.index,
);
final int context = (await _registerCallback(callbackId) as JSNumber).toDartInt;
final int context = (await SkwasmCallbackHandler.instance.registerCallback(callbackId) as JSNumber).toDartInt;
final SkDataHandle dataHandle = SkDataHandle.fromAddress(context);
final int byteCount = skDataGetSize(dataHandle);
final Pointer<Uint8> dataPointer = skDataGetConstPointer(dataHandle).cast<Uint8>();
Expand All @@ -72,23 +102,7 @@ class SkwasmSurface {
return ByteData.sublistView(output);
}

Future<JSAny> _registerCallback(int callbackId) {
final Completer<JSAny> completer = Completer<JSAny>();
_pendingCallbacks[callbackId] = completer;
return completer.future;
}

void _callbackHandler(JSNumber callbackId, JSNumber context, JSAny? jsContext) {
final Completer<JSAny> completer = _pendingCallbacks.remove(callbackId.toDartInt)!;
if (jsContext.isUndefinedOrNull) {
completer.complete(context);
} else {
completer.complete(jsContext);
}
}

void dispose() {
surfaceDestroy(handle);
skwasmInstance.removeFunction(_callbackHandle.address.toJS);
}
}
3 changes: 3 additions & 0 deletions web_sdk/sdk_rewriter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ List<String> getExtraImportsForLibrary(String libraryName) {
extraImports.add(entry.value);
}
}
if (libraryName == 'skwasm_impl') {
extraImports.add("import 'dart:_wasm';");
}
return extraImports;
}

Expand Down
1 change: 1 addition & 0 deletions web_sdk/test/sdk_rewriter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ void printSomething() {
"import 'dart:_web_unicode';",
"import 'dart:_web_test_fonts';",
"import 'dart:_web_locale_keymap' as locale_keymap;",
"import 'dart:_wasm';",
]);

// Other libraries (should not have extra imports).
Expand Down