nandi/vflutter_ffipublic Fork 0
728acaab2594667f4998fc18812e7c1e2e4cc549
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_native.dart · 141 lines · 4.2 KBDart Blame HistoryRaw
Add a WebAssembly backend for the example 728acaa nandithebull yesterday1/// Native backend: the V library as a shared object / static archive, reached
2/// through `dart:ffi`.
3///
4/// Selected by the conditional import in `../vflutter_ffi.dart` on every
5/// target except web. The web twin is `backend_web.dart`; the two files must
6/// keep the same top-level signatures.
7library;
8
9import 'dart:ffi';
10import 'dart:io';
11import 'dart:isolate';
12import 'dart:typed_data';
13
14import 'package:ffi/ffi.dart';
15
16import 'fractal_view.dart';
17
18const String backendName = 'dart:ffi';
19
20/// Isolates are real threads here, and `-gc none` means there is no
21/// stop-the-world phase to serialise them.
22const bool supportsIsolateParallelism = true;
23
24const String _libName = 'vflutter';
25
26DynamicLibrary _open() {
27 if (Platform.isMacOS || Platform.isIOS) {
28 // Static archive linked into the app binary.
29 return DynamicLibrary.process();
30 }
31 if (Platform.isAndroid || Platform.isLinux) {
32 return DynamicLibrary.open('lib$_libName.so');
33 }
34 if (Platform.isWindows) {
35 return DynamicLibrary.open('$_libName.dll');
36 }
37 throw UnsupportedError('vflutter_ffi: unsupported platform');
38}
39
40final DynamicLibrary _lib = _open();
41
42final void Function() _vfInit =
43 _lib.lookupFunction<Void Function(), void Function()>('vf_init');
44
45final int Function(int, int) _vfAdd =
46 _lib.lookupFunction<Int32 Function(Int32, Int32), int Function(int, int)>(
47 'vf_add');
48
49final Pointer<Utf8> Function(Pointer<Utf8>) _vfGreet = _lib.lookupFunction<
50 Pointer<Utf8> Function(Pointer<Utf8>),
51 Pointer<Utf8> Function(Pointer<Utf8>)>('vf_greet');
52
53final void Function(Pointer<Void>) _vfFree = _lib.lookupFunction<
54 Void Function(Pointer<Void>), void Function(Pointer<Void>)>('vf_free');
55
56final void Function(Pointer<Uint8>, int, int, int, double, double, double, int,
57 int, int) _vfMandelbrot = _lib.lookupFunction<
58 Void Function(Pointer<Uint8>, Size, Int32, Int32, Double, Double,
59 Double, Int32, Int32, Int32),
60 void Function(Pointer<Uint8>, int, int, int, double, double, double,
61 int, int, int)>('vf_mandelbrot');
62
63bool _ready = false;
64
65bool get isInitialized => _ready;
66
67/// Loading a shared object is synchronous, so this is only here to match the
68/// web backend's contract. Nothing awaits anything.
69Future<void> initialize() async => ensureInitialized();
70
71void ensureInitialized() {
72 if (_ready) return;
73 _vfInit();
74 _ready = true;
75}
76
77int add(int a, int b) {
78 ensureInitialized();
79 return _vfAdd(a, b);
80}
81
82String greet(String name) {
83 ensureInitialized();
84 final arg = name.toNativeUtf8();
85 Pointer<Utf8> res = nullptr;
86 try {
87 res = _vfGreet(arg);
88 if (res == nullptr) {
89 throw StateError('vf_greet returned null');
90 }
91 return res.toDartString();
92 } finally {
93 calloc.free(arg);
94 if (res != nullptr) _vfFree(res.cast());
95 }
96}
97
98Future<String> greetAsync(String name) => Isolate.run(() => greet(name));
99
100Uint8List renderBand(FractalView view, int y0, int y1) {
101 ensureInitialized();
102 final rows = y1 - y0;
103 if (rows <= 0) return Uint8List(0);
104
105 final bytes = rows * view.width * 4;
106 final buf = calloc<Uint8>(bytes);
107 try {
108 // `bytes` is the real allocation size, not a restatement of the band: V
109 // checks it before writing, so an arithmetic slip here is caught there
110 // rather than becoming a heap overflow.
111 _vfMandelbrot(buf, bytes, view.width, view.height, view.centerX,
112 view.centerY, view.scale, view.maxIter, y0, y1);
113 // Copy out of native memory before it is freed.
114 return Uint8List.fromList(buf.asTypedList(bytes));
115 } finally {
116 calloc.free(buf);
117 }
118}
119
120Future<Uint8List> renderParallel(FractalView view, int tiles) async {
121 ensureInitialized();
122 final count = tiles.clamp(1, view.height);
123 final rowsPer = (view.height / count).ceil();
124
125 final futures = <Future<Uint8List>>[];
126 for (var t = 0; t < count; t++) {
127 final y0 = t * rowsPer;
128 final y1 = ((t + 1) * rowsPer).clamp(0, view.height);
129 if (y0 >= y1) break;
130 futures.add(Isolate.run(() => renderBand(view, y0, y1)));
131 }
132
133 final bands = await Future.wait(futures);
134 final out = Uint8List(view.width * view.height * 4);
135 var offset = 0;
136 for (final band in bands) {
137 out.setRange(offset, offset + band.length, band);
138 offset += band.length;
139 }
140 return out;
141}