feat!: big refactor to support multiple swapchains

This commit is contained in:
Nick Fisher
2024-09-27 18:39:20 +08:00
parent 399d447eec
commit a6d2f2ecf9
24 changed files with 752 additions and 626 deletions

View File

@@ -26,7 +26,7 @@ Future<void> withVoidCallback(
nativeCallable.close();
}
Future<int> withPointerCallback<T extends NativeType>(
Future<Pointer<T>> withPointerCallback<T extends NativeType>(
Function(Pointer<NativeFunction<Void Function(Pointer<T>)>>)
func) async {
final completer = Completer<Pointer<T>>();
@@ -39,7 +39,7 @@ Future<int> withPointerCallback<T extends NativeType>(
func.call(nativeCallable.nativeFunction);
var ptr = await completer.future;
nativeCallable.close();
return ptr.address;
return ptr;
}
Future<bool> withBoolCallback(

View File

@@ -27,6 +27,101 @@ external ffi.Pointer<TSceneManager> Viewer_getSceneManager(
ffi.Pointer<TViewer> viewer,
);
@ffi.Native<
ffi.Pointer<TRenderTarget> Function(
ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
external ffi.Pointer<TRenderTarget> Viewer_createRenderTarget(
ffi.Pointer<TViewer> viewer,
int texture,
int width,
int height,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>, ffi.Pointer<TRenderTarget>)>(isLeaf: true)
external void Viewer_destroyRenderTarget(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TRenderTarget> tRenderTarget,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>, ffi.Pointer<TRenderTarget>)>(isLeaf: true)
external void Viewer_setRenderTarget(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TRenderTarget> tRenderTarget,
);
@ffi.Native<
ffi.Pointer<TSwapChain> Function(ffi.Pointer<TViewer>,
ffi.Pointer<ffi.Void>, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
external ffi.Pointer<TSwapChain> Viewer_createSwapChain(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Void> window,
int width,
int height,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>)>(
isLeaf: true)
external void Viewer_destroySwapChain(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TViewer>,
ffi.Pointer<TSwapChain>,
ffi.Uint64,
ffi.Pointer<ffi.Void>,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
ffi.Pointer<ffi.Void> data)>>,
ffi.Pointer<ffi.Void>)>(isLeaf: true)
external bool Viewer_render(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
int frameTimeInNanos,
ffi.Pointer<ffi.Void> pixelBuffer,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
ffi.Pointer<ffi.Void> data)>>
callback,
ffi.Pointer<ffi.Void> data,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<TSwapChain>,
ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Viewer_capture(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
ffi.Pointer<ffi.Uint8> pixelBuffer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<TSwapChain>,
ffi.Pointer<TRenderTarget>,
ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Viewer_captureRenderTarget(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
ffi.Pointer<TRenderTarget> renderTarget,
ffi.Pointer<ffi.Uint8> pixelBuffer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<ffi.Pointer<TEngine> Function(ffi.Pointer<TViewer>)>(isLeaf: true)
external ffi.Pointer<TEngine> Viewer_getEngine(
ffi.Pointer<TViewer> viewer,
@@ -47,16 +142,6 @@ external void Engine_setTransform(
double4x4 transform,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
external void create_render_target(
ffi.Pointer<TViewer> viewer,
int texture,
int width,
int height,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
external void clear_background_image(
ffi.Pointer<TViewer> viewer,
@@ -300,52 +385,6 @@ external void set_view_frustum_culling(
bool enabled,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<TViewer>,
ffi.Uint64,
ffi.Pointer<ffi.Void>,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
ffi.Pointer<ffi.Void> data)>>,
ffi.Pointer<ffi.Void>)>(isLeaf: true)
external bool render(
ffi.Pointer<TViewer> viewer,
int frameTimeInNanos,
ffi.Pointer<ffi.Void> pixelBuffer,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
ffi.Pointer<ffi.Void> data)>>
callback,
ffi.Pointer<ffi.Void> data,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void capture(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Uint8> pixelBuffer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Void>, ffi.Uint32,
ffi.Uint32)>(isLeaf: true)
external void create_swap_chain(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Void> window,
int width,
int height,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
external void destroy_swap_chain(
ffi.Pointer<TViewer> viewer,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Float)>(isLeaf: true)
external void set_frame_interval(
ffi.Pointer<TViewer> viewer,
@@ -1111,19 +1150,6 @@ external ffi.Pointer<ffi.Char> get_entity_name_at(
bool renderableOnly,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Bool)>(isLeaf: true)
external void set_recording(
ffi.Pointer<TViewer> viewer,
bool recording,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Char>)>(
isLeaf: true)
external void set_recording_output_directory(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Char> outputDirectory,
);
@ffi.Native<ffi.Void Function()>(isLeaf: true)
external void ios_dummy();
@@ -1484,36 +1510,65 @@ external void create_filament_viewer_render_thread(
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<ffi.Void>,
ffi.Uint32,
ffi.Uint32,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void create_swap_chain_render_thread(
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<ffi.Void>,
ffi.Uint32,
ffi.Uint32,
ffi.Pointer<
ffi
.NativeFunction<ffi.Void Function(ffi.Pointer<TSwapChain>)>>)>(
isLeaf: true)
external void Viewer_createSwapChainRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Void> surface,
int width,
int height,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<TSwapChain>)>>
onComplete,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Viewer_destroySwapChainRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>)>(
isLeaf: true)
external void Viewer_renderRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<TSwapChain>,
ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Viewer_captureRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<TSwapChain> swapChain,
ffi.Pointer<ffi.Uint8> out,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>,
ffi.Void Function(
ffi.Pointer<TViewer>,
ffi.Pointer<TSwapChain>,
ffi.Pointer<TRenderTarget>,
ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void destroy_swap_chain_render_thread(
external void Viewer_captureRenderTargetRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void create_render_target_render_thread(
ffi.Pointer<TViewer> viewer,
int nativeTextureId,
int width,
int height,
ffi.Pointer<TSwapChain> swapChain,
ffi.Pointer<TRenderTarget> renderTarget,
ffi.Pointer<ffi.Uint8> out,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@@ -1522,20 +1577,6 @@ external void destroy_filament_viewer_render_thread(
ffi.Pointer<TViewer> viewer,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
external void render_render_thread(
ffi.Pointer<TViewer> viewer,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void capture_render_thread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Uint8> out,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<FilamentRenderCallback Function(FilamentRenderCallback)>(
isLeaf: true)
external FilamentRenderCallback make_render_callback_fn_pointer(
@@ -1927,6 +1968,10 @@ final class TViewer extends ffi.Opaque {}
final class TSceneManager extends ffi.Opaque {}
final class TRenderTarget extends ffi.Opaque {}
final class TSwapChain extends ffi.Opaque {}
final class TMaterialKey extends ffi.Struct {
@ffi.Bool()
external bool doubleSided;

View File

@@ -3,6 +3,7 @@ import 'dart:ffi';
import 'dart:math';
import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
import 'package:vector_math/vector_math_64.dart';
import 'package:vector_math/vector_math_64.dart' as v64;
import '../../../../entities/gizmo.dart';
@@ -96,10 +97,21 @@ class ThermionViewerFFI extends ThermionViewer {
_initialize();
}
Future createRenderTarget(
double width, double height, int textureHandle) async {
await withVoidCallback((callback) => create_render_target_render_thread(
_viewer!, textureHandle, width.toInt(), height.toInt(), callback));
///
///
///
Future<RenderTarget> createRenderTarget(
int width, int height, int textureHandle) async {
final renderTarget =
Viewer_createRenderTarget(_viewer!, textureHandle, width, height);
return FFIRenderTarget(renderTarget, _viewer!);
}
///
///
///
Future setRenderTarget(FFIRenderTarget renderTarget) async {
Viewer_setRenderTarget(_viewer!, renderTarget.renderTarget);
}
Future updateViewportAndCameraProjection(double width, double height) async {
@@ -129,18 +141,13 @@ class ThermionViewerFFI extends ThermionViewer {
}
}
Future createSwapChain(double width, double height,
Future<SwapChain> createSwapChain(int width, int height,
{Pointer<Void>? surface}) async {
await withVoidCallback((callback) {
create_swap_chain_render_thread(_viewer!, surface ?? nullptr,
var swapChain = await withPointerCallback<TSwapChain>((callback) {
return Viewer_createSwapChainRenderThread(_viewer!, surface ?? nullptr,
width.toInt(), height.toInt(), callback);
});
}
Future destroySwapChain() async {
await withVoidCallback((callback) {
destroy_swap_chain_render_thread(_viewer!, callback);
});
return FFISwapChain(swapChain, _viewer!);
}
Gizmo? _gizmo;
@@ -150,7 +157,7 @@ class ThermionViewerFFI extends ThermionViewer {
final uberarchivePtr =
uberArchivePath?.toNativeUtf8(allocator: allocator).cast<Char>() ??
nullptr;
var viewer = await withPointerCallback(
_viewer = await withPointerCallback(
(Pointer<NativeFunction<Void Function(Pointer<TViewer>)>> callback) {
create_filament_viewer_render_thread(
_sharedContext,
@@ -161,7 +168,7 @@ class ThermionViewerFFI extends ThermionViewer {
_renderCallbackOwner,
callback);
});
_viewer = Pointer.fromAddress(viewer);
allocator.free(uberarchivePtr);
if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details");
@@ -202,25 +209,30 @@ class ThermionViewerFFI extends ThermionViewer {
///
///
@override
Future render() async {
render_render_thread(_viewer!);
Future render(FFISwapChain swapChain) async {
Viewer_renderRenderThread(_viewer!, swapChain.swapChain);
}
///
///
///
@override
Future<Uint8List> capture() async {
Future<Uint8List> capture(FFISwapChain swapChain,
{FFIRenderTarget? renderTarget}) async {
final length = this.viewportDimensions.$1.toInt() *
this.viewportDimensions.$2.toInt() *
4;
final out = allocator<Uint8>(length);
final out = Uint8List(length);
await withVoidCallback((cb) {
capture_render_thread(_viewer!, out, cb);
if (renderTarget != null) {
Viewer_captureRenderTargetRenderThread(
_viewer!, swapChain.swapChain, renderTarget.renderTarget, out.address, cb);
} else {
Viewer_captureRenderThread(
_viewer!, swapChain.swapChain, out.address, cb);
}
});
final data = Uint8List.fromList(out.asTypedList(length));
allocator.free(out);
return data;
return out;
}
///
@@ -246,9 +258,7 @@ class ThermionViewerFFI extends ThermionViewer {
await setRendering(false);
await clearEntities();
await clearLights();
print("DESTROYING");
destroy_filament_viewer_render_thread(_viewer!);
print("DESTROYED");
_sceneManager = null;
_viewer = null;
await _pickResultController.close();
@@ -259,7 +269,6 @@ class ThermionViewerFFI extends ThermionViewer {
await callback.call();
}
_onDispose.clear();
print("DONE");
}
///
@@ -409,24 +418,24 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future<ThermionEntity> addDirectLight(DirectLight directLight) async {
var entity = add_light(
_viewer!,
directLight.type.index,
directLight.color,
directLight.intensity,
directLight.position.x,
directLight.position.y,
directLight.position.z,
directLight.direction.x,
directLight.direction.y,
directLight.direction.z,
directLight.falloffRadius,
directLight.spotLightConeInner,
directLight.spotLightConeOuter,
directLight.sunAngularRadius,
directLight.sunHaloSize,
directLight.sunHaloFallof,
directLight.castShadows,
);
_viewer!,
directLight.type.index,
directLight.color,
directLight.intensity,
directLight.position.x,
directLight.position.y,
directLight.position.z,
directLight.direction.x,
directLight.direction.y,
directLight.direction.z,
directLight.falloffRadius,
directLight.spotLightConeInner,
directLight.spotLightConeOuter,
directLight.sunAngularRadius,
directLight.sunHaloSize,
directLight.sunHaloFallof,
directLight.castShadows,
);
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to add light to scene");
}
@@ -1747,24 +1756,6 @@ class ThermionViewerFFI extends ThermionViewer {
return names;
}
///
///
///
@override
Future setRecording(bool recording) async {
set_recording(_viewer!, recording);
}
///
///
///
@override
Future setRecordingOutputDirectory(String outputDir) async {
var pathPtr = outputDir.toNativeUtf8(allocator: allocator);
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
allocator.free(pathPtr);
}
final _collisions = <ThermionEntity, NativeCallable>{};
///
@@ -2254,3 +2245,28 @@ class ThermionFFIMaterialInstance extends MaterialInstance {
_pointer, name.toNativeUtf8().cast<Char>(), x, y);
}
}
class FFIRenderTarget extends RenderTarget {
final Pointer<TRenderTarget> renderTarget;
final Pointer<TViewer> viewer;
FFIRenderTarget(this.renderTarget, this.viewer);
@override
Future destroy() async {
Viewer_destroyRenderTarget(viewer, renderTarget);
}
}
class FFISwapChain extends SwapChain {
final Pointer<TSwapChain> swapChain;
final Pointer<TViewer> viewer;
FFISwapChain(this.swapChain, this.viewer);
Future destroy() async {
await withVoidCallback((callback) {
Viewer_destroySwapChainRenderThread(viewer, swapChain, callback);
});
}
}

View File

@@ -0,0 +1,3 @@
abstract class RenderTarget {
Future destroy();
}

View File

@@ -1,5 +1,7 @@
library shared_types;
export 'swap_chain.dart';
export 'render_target.dart';
export 'camera.dart';
export 'material.dart';
export 'texture.dart';

View File

@@ -0,0 +1,3 @@
abstract class SwapChain {
Future destroy();
}

View File

@@ -10,6 +10,8 @@ import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'shared_types/swap_chain.dart';
const double kNear = 0.05;
const double kFar = 1000.0;
const double kFocalLength = 28.0;
@@ -60,7 +62,7 @@ abstract class ThermionViewer {
///
/// Render a single frame immediately.
///
Future render();
Future render(covariant SwapChain swapChain);
///
/// Requests a single frame to be rendered. This is only intended to be used internally.
@@ -70,7 +72,23 @@ abstract class ThermionViewer {
///
/// Render a single frame and copy the pixel buffer to [out].
///
Future<Uint8List> capture();
Future<Uint8List> capture(covariant SwapChain swapChain, { covariant RenderTarget? renderTarget });
///
///
///
Future<SwapChain> createSwapChain(int width, int height);
///
///
///
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle);
///
///
///
Future setRenderTarget(covariant RenderTarget renderTarget);
///
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
@@ -368,7 +386,8 @@ abstract class ThermionViewer {
/// Sets multiple transforms (relative to parent) simultaneously for [entity].
/// Uses mutex to ensure that transform updates aren't split across frames.
///
Future queueTransformUpdates(List<ThermionEntity> entities, List<Matrix4> transforms);
Future queueTransformUpdates(
List<ThermionEntity> entities, List<Matrix4> transforms);
///
/// Updates the bone matrices for [entity] (which must be the ThermionEntity
@@ -757,17 +776,6 @@ abstract class ThermionViewer {
Future<List<String>> getChildEntityNames(ThermionEntity entity,
{bool renderableOnly = true});
///
/// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png.
/// This will impact performance; handle with care.
///
Future setRecording(bool recording);
///
/// Sets the output directory where recorded PNGs will be placed.
///
Future setRecordingOutputDirectory(String outputDirectory);
///
/// An [entity] will only be animatable after an animation component is attached.
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
@@ -723,12 +724,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future<Uint8List> capture() {
// TODO: implement capture
throw UnimplementedError();
}
@override
Future<Aabb2> getBoundingBox(ThermionEntity entity) {
// TODO: implement getBoundingBox
@@ -1020,5 +1015,23 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future<SwapChain> createSwapChain(int width, int height) {
// TODO: implement createSwapChain
throw UnimplementedError();
}
@override
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle) {
// TODO: implement createRenderTarget
throw UnimplementedError();
}
@override
Future setRenderTarget(covariant RenderTarget renderTarget) {
// TODO: implement setRenderTarget
throw UnimplementedError();
}
}