nandi/vflutter_ffipublic Fork 0
4506a586d7400b00d732dcb97167be75ab41cab1
Commits
Clone
git clone https://git.rickub.com/nandi/vflutter_ffi.git
git clone ssh://git@rickub.com/nandi/vflutter_ffi.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

backend_web.dart · 206 lines · 6.2 KBDart Blame HistoryRaw
Replace V with Nim 472eb49 nandithebull 23h ago1/// Web backend: the same Nim code compiled to WebAssembly by emscripten and
Add a WebAssembly backend for the example 728acaa nandithebull yesterday2/// driven over `dart:js_interop`.
3///
Replace V with Nim 472eb49 nandithebull 23h ago4/// Selected by the conditional import in `../nimflutter_ffi.dart` when
Add a WebAssembly backend for the example 728acaa nandithebull yesterday5/// `dart.library.js_interop` is available. The native twin is
6/// `backend_native.dart`; the two files must keep the same top-level
7/// signatures.
8///
9/// The glue is emitted by tool/build_wasm.sh into example/web/ and loaded by
10/// a <script> tag in example/web/index.html, which defines the global
Replace V with Nim 472eb49 nandithebull 23h ago11/// `createNimFlutterModule`.
Add a WebAssembly backend for the example 728acaa nandithebull yesterday12library;
13
14import 'dart:async';
15import 'dart:js_interop';
16import 'dart:typed_data';
17
18import 'fractal_view.dart';
19
20const String backendName = 'wasm (emscripten)';
21
Render in parallel on web, via a pool of Web Workers 4506a58 nandithebull 23h ago22/// True: bands render concurrently in Web Workers.
23///
24/// The wasm module itself is single-threaded, so the parallelism comes from
25/// running several *instances* of it — one per worker, each with its own
26/// linear memory. Nothing is shared, which is why this needs no
27/// SharedArrayBuffer and no COOP/COEP headers; the cost is one ~34 KB module
28/// per worker.
29///
30/// Falls back to sequential rendering if nimflutter_pool.js is not loaded.
31bool get supportsIsolateParallelism => _pool != null;
32
33/// What one unit of parallelism is called here. Purely for display.
34const String parallelUnitName = 'worker';
35
36@JS('nimflutterRenderParallel')
37external JSFunction? get _pool;
38
39@JS('nimflutterRenderParallel')
40external JSPromise<JSUint8Array> _renderParallelInWorkers(int w, int h,
41 double cx, double cy, double scale, int iter, int tiles);
Add a WebAssembly backend for the example 728acaa nandithebull yesterday42
Replace V with Nim 472eb49 nandithebull 23h ago43@JS('createNimFlutterModule')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday44external JSFunction? get _factory;
45
46/// The emscripten module object. Only the pieces this package uses are
47/// declared; `_`-prefixed members are the C exports.
48extension type _Module._(JSObject _) implements JSObject {
49 @JS('_malloc')
50 external int malloc(int bytes);
51
52 @JS('_free')
53 external void free(int ptr);
54
Replace V with Nim 472eb49 nandithebull 23h ago55 @JS('_nf_init')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday56 external void vfInit();
57
Replace V with Nim 472eb49 nandithebull 23h ago58 @JS('_nf_add')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday59 external int vfAdd(int a, int b);
60
Replace V with Nim 472eb49 nandithebull 23h ago61 @JS('_nf_greet')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday62 external int vfGreet(int namePtr);
63
Replace V with Nim 472eb49 nandithebull 23h ago64 @JS('_nf_free')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday65 external void vfFree(int ptr);
66
Replace V with Nim 472eb49 nandithebull 23h ago67 @JS('_nf_mandelbrot')
Add a WebAssembly backend for the example 728acaa nandithebull yesterday68 external void vfMandelbrot(int buf, int bufLen, int w, int h, double cx,
69 double cy, double scale, int maxIter, int y0, int y1);
70
71 /// Re-read on every use: emscripten replaces the view when the heap grows.
72 external JSUint8Array get HEAPU8;
73
74 external String UTF8ToString(int ptr);
75 external void stringToUTF8(String s, int ptr, int maxBytesToWrite);
76 external int lengthBytesUTF8(String s);
77}
78
79extension type _U8Array._(JSObject _) implements JSObject {
80 external JSUint8Array subarray(int begin, int end);
81}
82
83_Module? _module;
84
85bool get isInitialized => _module != null;
86
87Future<void>? _pending;
88
89/// Instantiates the wasm module. Safe to call repeatedly and from several
90/// places at once; the work happens once.
91Future<void> initialize() {
92 if (_module != null) return Future<void>.value();
93 return _pending ??= _load();
94}
95
96Future<void> _load() async {
97 final factory = _factory;
98 if (factory == null) {
99 throw StateError(
Replace V with Nim 472eb49 nandithebull 23h ago100 'createNimFlutterModule is not defined. The wasm glue is missing: run '
101 '`just wasm` and make sure web/index.html loads nimflutter.js before '
Add a WebAssembly backend for the example 728acaa nandithebull yesterday102 'flutter_bootstrap.js.',
103 );
104 }
105 final module = await (factory.callAsFunction() as JSPromise<JSObject>).toDart;
106 final m = _Module._(module);
107 m.vfInit();
108 _module = m;
109 _pending = null;
110}
111
112_Module get _m {
113 final m = _module;
114 if (m == null) {
115 throw StateError(
Replace V with Nim 472eb49 nandithebull 23h ago116 'nimflutter_ffi is not initialised on web. Await initialize() before '
117 'calling into Nim — loading a wasm module cannot be synchronous.',
Add a WebAssembly backend for the example 728acaa nandithebull yesterday118 );
119 }
120 return m;
121}
122
123/// Present so callers written against the native backend keep compiling. On
124/// web it only asserts; it cannot do the loading, because that is async.
125void ensureInitialized() => _m;
126
127int add(int a, int b) => _m.vfAdd(a, b);
128
129String greet(String name) {
130 final m = _m;
131 // stringToUTF8 needs room for the trailing NUL.
132 final size = m.lengthBytesUTF8(name) + 1;
133 final arg = m.malloc(size);
134 var res = 0;
135 try {
136 m.stringToUTF8(name, arg, size);
137 res = m.vfGreet(arg);
138 if (res == 0) {
Replace V with Nim 472eb49 nandithebull 23h ago139 throw StateError('nf_greet returned null');
Add a WebAssembly backend for the example 728acaa nandithebull yesterday140 }
141 return m.UTF8ToString(res);
142 } finally {
143 m.free(arg);
Replace V with Nim 472eb49 nandithebull 23h ago144 // Nim allocated the result, so Nim releases it — same rule as on native.
Add a WebAssembly backend for the example 728acaa nandithebull yesterday145 if (res != 0) m.vfFree(res);
146 }
147}
148
149/// No isolates on web, so this is [greet] behind a `Future`. It does not move
150/// the work off the UI thread.
151Future<String> greetAsync(String name) async => greet(name);
152
153Uint8List renderBand(FractalView view, int y0, int y1) {
154 final m = _m;
155 final rows = y1 - y0;
156 if (rows <= 0) return Uint8List(0);
157
158 final bytes = rows * view.width * 4;
159 final buf = m.malloc(bytes);
160 try {
161 m.vfMandelbrot(buf, bytes, view.width, view.height, view.centerX,
162 view.centerY, view.scale, view.maxIter, y0, y1);
163 // Take a view of just this band and copy it into Dart before the wasm
164 // allocation is released. Reading m.HEAPU8 here rather than earlier
165 // matters: malloc may have grown the heap and replaced the view.
166 final band = _U8Array._(m.HEAPU8).subarray(buf, buf + bytes).toDart;
167 return Uint8List.fromList(band);
168 } finally {
169 m.free(buf);
170 }
171}
172
Render in parallel on web, via a pool of Web Workers 4506a58 nandithebull 23h ago173/// Renders the frame across the worker pool, falling back to a sequential
174/// split if the pool script is absent.
175///
176/// The split is identical to the native backend's, and so is the output: the
177/// bands are the same bands, just computed in other threads.
Add a WebAssembly backend for the example 728acaa nandithebull yesterday178Future<Uint8List> renderParallel(FractalView view, int tiles) async {
179 final count = tiles.clamp(1, view.height);
180
Render in parallel on web, via a pool of Web Workers 4506a58 nandithebull 23h ago181 if (_pool != null && count > 1) {
182 final result = await _renderParallelInWorkers(
183 view.width,
184 view.height,
185 view.centerX,
186 view.centerY,
187 view.scale,
188 view.maxIter,
189 count,
190 ).toDart;
191 return result.toDart;
192 }
193
194 final rowsPer = (view.height / count).ceil();
Add a WebAssembly backend for the example 728acaa nandithebull yesterday195 final out = Uint8List(view.width * view.height * 4);
196 var offset = 0;
197 for (var t = 0; t < count; t++) {
198 final y0 = t * rowsPer;
199 final y1 = ((t + 1) * rowsPer).clamp(0, view.height);
200 if (y0 >= y1) break;
201 final band = renderBand(view, y0, y1);
202 out.setRange(offset, offset + band.length, band);
203 offset += band.length;
204 }
205 return out;
206}