nandi/vflutter_ffipublic Fork 0
main
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 8h ago1/// Web backend: the same Nim code compiled to WebAssembly by emscripten and
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago2/// driven over `dart:js_interop`.
3///
Replace V with Nim 472eb49 nandithebull 8h ago4/// Selected by the conditional import in `../nimflutter_ffi.dart` when
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago5/// `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 8h ago11/// `createNimFlutterModule`.
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago12library;
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 7h 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 8h ago42
Replace V with Nim 472eb49 nandithebull 8h ago43@JS('createNimFlutterModule')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago44external 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 8h ago55 @JS('_nf_init')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago56 external void vfInit();
57
Replace V with Nim 472eb49 nandithebull 8h ago58 @JS('_nf_add')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago59 external int vfAdd(int a, int b);
60
Replace V with Nim 472eb49 nandithebull 8h ago61 @JS('_nf_greet')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago62 external int vfGreet(int namePtr);
63
Replace V with Nim 472eb49 nandithebull 8h ago64 @JS('_nf_free')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago65 external void vfFree(int ptr);
66
Replace V with Nim 472eb49 nandithebull 8h ago67 @JS('_nf_mandelbrot')
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago68 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 8h 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 8h ago102 '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 8h 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 8h ago118 );
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 8h ago139 throw StateError('nf_greet returned null');
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago140 }
141 return m.UTF8ToString(res);
142 } finally {
143 m.free(arg);
Replace V with Nim 472eb49 nandithebull 8h ago144 // Nim allocated the result, so Nim releases it — same rule as on native.
Add a WebAssembly backend for the example 728acaa nandithebull 8h ago145 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 7h 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 8h ago178Future<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 7h 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 8h ago195 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}