From 18b6b2e5f01558b4218d8b4a92aa13b53821d2c4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 23 Jun 2025 12:27:05 +0800 Subject: [PATCH] (web) in capture, add extra call to render a frame to flush frame callbacks --- .../src/implementation/ffi_filament_app.dart | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/thermion_dart/lib/src/filament/src/implementation/ffi_filament_app.dart b/thermion_dart/lib/src/filament/src/implementation/ffi_filament_app.dart index b7322eef..7d74d7d9 100644 --- a/thermion_dart/lib/src/filament/src/implementation/ffi_filament_app.dart +++ b/thermion_dart/lib/src/filament/src/implementation/ffi_filament_app.dart @@ -830,6 +830,42 @@ class FFIFilamentApp extends FilamentApp { await flush(); + // on web/WebGL backend, the callback in readPixels isn't actually + // fired until a subsequent render call (and possibly the presentation to the + // canvas when the render thread yields). + // We need to wait at least one frame before the pixel buffer is populated; + // by this point, we've called setRendering(true), but this is actually + // synchronous, so we'll add a ~2 frame delay to wait for this to be available. + if (FILAMENT_SINGLE_THREADED) { + await withBoolCallback((cb) => Renderer_beginFrameRenderThread( + renderer, swapChain!.swapChain, 0.toBigInt, cb)); + for (final view in views) { + await withVoidCallback((requestId, cb) { + Renderer_renderRenderThread( + renderer, + view.view, + requestId, + cb, + ); + }); + } + await withVoidCallback((requestId, cb) { + Renderer_endFrameRenderThread(renderer, requestId, cb); + }); + await flush(); + + await Future.delayed(Duration(milliseconds: 33)); + + // now copy the pixel buffer into a GC'd Uint8List and destroy the manually + // allocated buffer so invokers don't have to worry about taking ownership + // of malloc memory + return pixelBuffers.map((element) { + final wrapped = (element.$1, Uint8List.fromList(element.$2)); + element.$2.free(); + return wrapped; + }).toList(); + } + return pixelBuffers; }