diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart index 72e4a208..b6a45f53 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart @@ -2,6 +2,7 @@ import 'dart:ffi'; import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart'; import 'package:thermion_dart/src/viewer/src/shared_types/camera.dart'; +import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart'; import '../../shared_types/view.dart'; import '../thermion_viewer_ffi.dart'; @@ -50,7 +51,7 @@ class FFIView extends View { View_setPostProcessing(view, enabled); } - Future setRenderable(bool renderable) async { - Viewer_markViewRenderable(viewer, view, renderable); + Future setRenderable(bool renderable, FFISwapChain swapChain) async { + Viewer_setViewRenderable(viewer, swapChain.swapChain, view, renderable); } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index f8539a76..5fadb9ce 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -46,11 +46,18 @@ external void Viewer_destroyRenderTarget( ); @ffi.Native< - ffi.Pointer Function(ffi.Pointer, - ffi.Pointer, ffi.Uint32, ffi.Uint32)>(isLeaf: true) + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(isLeaf: true) external ffi.Pointer Viewer_createSwapChain( ffi.Pointer viewer, ffi.Pointer window, +); + +@ffi.Native< + ffi.Pointer Function( + ffi.Pointer, ffi.Uint32, ffi.Uint32)>(isLeaf: true) +external ffi.Pointer Viewer_createHeadlessSwapChain( + ffi.Pointer viewer, int width, int height, ); @@ -62,28 +69,9 @@ external void Viewer_destroySwapChain( ffi.Pointer swapChain, ); -@ffi.Native< - ffi.Bool Function( - ffi.Pointer, - ffi.Pointer, - ffi.Uint64, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer buf, ffi.Size size, - ffi.Pointer data)>>, - ffi.Pointer)>(isLeaf: true) -external bool Viewer_render( +@ffi.Native)>(isLeaf: true) +external void Viewer_render( ffi.Pointer viewer, - ffi.Pointer swapChain, - int frameTimeInNanos, - ffi.Pointer pixelBuffer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer buf, ffi.Size size, - ffi.Pointer data)>> - callback, - ffi.Pointer data, ); @ffi.Native< @@ -145,10 +133,11 @@ external ffi.Pointer Viewer_getSwapChainAt( ); @ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Pointer, ffi.Bool)>(isLeaf: true) -external void Viewer_markViewRenderable( + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Bool)>(isLeaf: true) +external void Viewer_setViewRenderable( ffi.Pointer viewer, + ffi.Pointer swapChain, ffi.Pointer view, bool renderable, ); @@ -1374,7 +1363,7 @@ external void MaterialInstance_setParameterFloat2( ffi.Pointer< ffi.NativeFunction< ffi.Void Function(ffi.Pointer viewer)>>)>(isLeaf: true) -external void create_filament_viewer_render_thread( +external void Viewer_createOnRenderThread( ffi.Pointer context, ffi.Pointer platform, ffi.Pointer uberArchivePath, @@ -1393,8 +1382,6 @@ external void create_filament_viewer_render_thread( ffi.Void Function( ffi.Pointer, ffi.Pointer, - ffi.Uint32, - ffi.Uint32, ffi.Pointer< ffi .NativeFunction)>>)>( @@ -1402,6 +1389,21 @@ external void create_filament_viewer_render_thread( external void Viewer_createSwapChainRenderThread( ffi.Pointer viewer, ffi.Pointer surface, + ffi.Pointer)>> + onComplete, +); + +@ffi.Native< + ffi.Void Function( + ffi.Pointer, + ffi.Uint32, + ffi.Uint32, + ffi.Pointer< + ffi + .NativeFunction)>>)>( + isLeaf: true) +external void Viewer_createHeadlessSwapChainRenderThread( + ffi.Pointer viewer, int width, int height, ffi.Pointer)>> @@ -1459,11 +1461,10 @@ external void Viewer_captureRenderTargetRenderThread( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Void Function(ffi.Pointer, ffi.Pointer>)>(isLeaf: true) external void Viewer_requestFrameRenderThread( ffi.Pointer viewer, - ffi.Pointer tSwapChain, ffi.Pointer> onComplete, ); diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index b614cd04..69a46afe 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -149,11 +149,24 @@ class ThermionViewerFFI extends ThermionViewer { } } - Future createSwapChain(int width, int height, - {Pointer? surface}) async { + /// + /// + /// + Future createHeadlessSwapChain(int width, int height) async { + var swapChain = await withPointerCallback((callback) { + return Viewer_createHeadlessSwapChainRenderThread( + _viewer!, width, height, callback); + }); + return FFISwapChain(swapChain, _viewer!); + } + + /// + /// + /// + Future createSwapChain(int surface) async { var swapChain = await withPointerCallback((callback) { return Viewer_createSwapChainRenderThread( - _viewer!, surface ?? nullptr, width, height, callback); + _viewer!, Pointer.fromAddress(surface), callback); }); return FFISwapChain(swapChain, _viewer!); } @@ -167,7 +180,7 @@ class ThermionViewerFFI extends ThermionViewer { nullptr; _viewer = await withPointerCallback( (Pointer)>> callback) { - create_filament_viewer_render_thread( + Viewer_createOnRenderThread( _sharedContext, _driver, uberarchivePtr, @@ -2039,10 +2052,8 @@ class ThermionViewerFFI extends ThermionViewer { completer.complete(true); }); - final swapChain = Viewer_getSwapChainAt(_viewer!, 0); - Viewer_requestFrameRenderThread( - _viewer!, swapChain, callback.nativeFunction); + _viewer!, callback.nativeFunction); await completer.future.timeout(Duration(seconds: 1)); } diff --git a/thermion_dart/lib/src/viewer/src/shared_types/view.dart b/thermion_dart/lib/src/viewer/src/shared_types/view.dart index 07455acd..1d88b1d9 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/view.dart +++ b/thermion_dart/lib/src/viewer/src/shared_types/view.dart @@ -1,4 +1,5 @@ import 'package:thermion_dart/thermion_dart.dart'; +import 'swap_chain.dart'; class Viewport { final int left; @@ -17,5 +18,5 @@ abstract class View { Camera getCamera(); Future setPostProcessing(bool enabled); Future setAntiAliasing(bool msaa, bool fxaa, bool taa); - Future setRenderable(bool renderable); + Future setRenderable(bool renderable, covariant SwapChain swapChain); } diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart index ff252209..bb9d5ac1 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart @@ -62,7 +62,12 @@ abstract class ThermionViewer { /// /// /// - Future createSwapChain(int width, int height); + Future createHeadlessSwapChain(int width, int height); + + /// + /// + /// + Future createSwapChain(int handle); /// /// diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart index 73795553..6212c19e 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart @@ -1006,12 +1006,6 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future createSwapChain(int width, int height) { - // TODO: implement createSwapChain - throw UnimplementedError(); - } - @override Future createRenderTarget(int width, int height, int textureHandle) { // TODO: implement createRenderTarget @@ -1036,23 +1030,36 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future render(covariant SwapChain swapChain) { - // TODO: implement render - throw UnimplementedError(); - } - - @override - Future capture(covariant SwapChain swapChain, {covariant View? view, covariant RenderTarget? renderTarget}) { - // TODO: implement capture - throw UnimplementedError(); - } @override Future createGizmo(covariant View view) { // TODO: implement createGizmo throw UnimplementedError(); } + + @override + Future createHeadlessSwapChain(int width, int height) { + // TODO: implement createHeadlessSwapChain + throw UnimplementedError(); + } + + @override + Future capture({covariant SwapChain? swapChain, covariant View? view, covariant RenderTarget? renderTarget}) { + // TODO: implement capture + throw UnimplementedError(); + } + + @override + Future createSwapChain(handle) { + // TODO: implement createSwapChain + throw UnimplementedError(); + } + + @override + Future render({covariant SwapChain? swapChain}) { + // TODO: implement render + throw UnimplementedError(); + } } diff --git a/thermion_dart/native/include/FilamentViewer.hpp b/thermion_dart/native/include/FilamentViewer.hpp index fb8a8d57..5bc0deea 100644 --- a/thermion_dart/native/include/FilamentViewer.hpp +++ b/thermion_dart/native/include/FilamentViewer.hpp @@ -68,12 +68,9 @@ namespace thermion void removeEntity(EntityId asset); void clearEntities(); - bool render( - uint64_t frameTimeInNanos, - SwapChain* swapChain, - void *pixelBuffer, - void (*callback)(void *buf, size_t size, void *data), - void *data); + void render( + uint64_t frameTimeInNanos + ); void setFrameInterval(float interval); void setMainCamera(View *view); @@ -83,7 +80,8 @@ namespace thermion float getCameraFov(bool horizontal); void setCameraFov(double fovDegrees, bool horizontal); - SwapChain* createSwapChain(const void *surface, uint32_t width, uint32_t height); + SwapChain* createSwapChain(const void *surface); + SwapChain* createSwapChain(uint32_t width, uint32_t height); void destroySwapChain(SwapChain* swapChain); RenderTarget* createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height); @@ -91,21 +89,9 @@ namespace thermion Renderer *getRenderer(); - std::vector _renderable; - void setRenderable(View* view, bool renderable) { - auto it = std::find(_renderable.begin(), _renderable.end(), view); - - if(renderable) { - if(it == _renderable.end()) { - _renderable.push_back(view); - } - } else { - if(it != _renderable.end()) { - _renderable.erase(it); - } - } - - } + std::map> _renderable; + + void setRenderable(View* view, SwapChain* swapChain, bool renderable); void setBackgroundColor(const float r, const float g, const float b, const float a); void setBackgroundImage(const char *resourcePath, bool fillHeight, uint32_t width, uint32_t height); diff --git a/thermion_dart/native/include/ThermionDartApi.h b/thermion_dart/native/include/ThermionDartApi.h index edc23b76..fdd4e557 100644 --- a/thermion_dart/native/include/ThermionDartApi.h +++ b/thermion_dart/native/include/ThermionDartApi.h @@ -54,20 +54,16 @@ extern "C" { #endif - EMSCRIPTEN_KEEPALIVE TViewer *create_filament_viewer(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath); + EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath); EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer(TViewer *viewer); EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer); EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *viewer, intptr_t texture, uint32_t width, uint32_t height); EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *viewer, TRenderTarget* tRenderTarget); - EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *viewer, const void *const window, uint32_t width, uint32_t height); + EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *viewer, const void *const window); + EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *viewer, uint32_t width, uint32_t height); EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *viewer, TSwapChain* swapChain); - EMSCRIPTEN_KEEPALIVE bool Viewer_render( - TViewer *viewer, - TSwapChain *swapChain, - uint64_t frameTimeInNanos, - void *pixelBuffer, - void (*callback)(void *buf, size_t size, void *data), - void *data); + EMSCRIPTEN_KEEPALIVE void Viewer_render( + TViewer *viewer); EMSCRIPTEN_KEEPALIVE void Viewer_capture( TViewer *viewer, TView *view, @@ -85,7 +81,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE TView* Viewer_getViewAt(TViewer *viewer, int index); EMSCRIPTEN_KEEPALIVE void Viewer_setMainCamera(TViewer *tViewer, TView *tView); EMSCRIPTEN_KEEPALIVE TSwapChain* Viewer_getSwapChainAt(TViewer *tViewer, int index); - EMSCRIPTEN_KEEPALIVE void Viewer_markViewRenderable(TViewer *viewer, TView* view, bool renderable); + EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *viewer, TSwapChain *swapChain, TView* view, bool renderable); // Engine EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer* viewer); diff --git a/thermion_dart/native/include/ThermionDartRenderThreadApi.h b/thermion_dart/native/include/ThermionDartRenderThreadApi.h index 077c1119..9c076a02 100644 --- a/thermion_dart/native/include/ThermionDartRenderThreadApi.h +++ b/thermion_dart/native/include/ThermionDartRenderThreadApi.h @@ -17,7 +17,7 @@ extern "C" typedef int32_t EntityId; typedef void (*FilamentRenderCallback)(void *const owner); - EMSCRIPTEN_KEEPALIVE void create_filament_viewer_render_thread( + EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread( void *const context, void *const platform, const char *uberArchivePath, @@ -25,12 +25,13 @@ extern "C" void (*renderCallback)(void *const renderCallbackOwner), void *const renderCallbackOwner, void (*callback)(TViewer *viewer)); - EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain*)); + EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, void (*onComplete)(TSwapChain*)); + EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain*)); EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain* swapChain, void (*onComplete)()); EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain); EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, uint8_t* out, void (*onComplete)()); EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, TRenderTarget* renderTarget, uint8_t* out, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, TSwapChain* tSwapChain, void(*onComplete)()); + EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)()); EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(TViewer *viewer); diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index 5e649024..b569e885 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -127,7 +127,16 @@ namespace thermion FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapperImpl *const resourceLoader, void *const platform, const char *uberArchivePath) : _resourceLoaderWrapper(resourceLoader) { + _context = (void *)sharedContext; + + if(!_context) { + Log("Creating, no shared context"); + } else { + Log("Creating with shared context"); + } + + ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null"); #if TARGET_OS_IPHONE @@ -166,8 +175,6 @@ namespace thermion createView(); - setRenderable(_views[0], true); - const float aperture = _mainCamera->getAperture(); const float shutterSpeed = _mainCamera->getShutterSpeed(); const float sens = _mainCamera->getSensitivity(); @@ -665,20 +672,19 @@ namespace thermion Renderer *FilamentViewer::getRenderer() { return _renderer; } - SwapChain *FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height) + SwapChain *FilamentViewer::createSwapChain(const void *window) + { + std::lock_guard lock(_renderMutex); + SwapChain *swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER); + _swapChains.push_back(swapChain); + return swapChain; + } + + SwapChain *FilamentViewer::createSwapChain(uint32_t width, uint32_t height) { std::lock_guard lock(_renderMutex); SwapChain *swapChain; - if (window) - { - swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE); - Log("Created window swapchain."); - } - else - { - Log("Created headless swapchain %dx%d.", width, height); - swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER); - } + swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER); _swapChains.push_back(swapChain); return swapChain; } @@ -734,13 +740,13 @@ namespace thermion void FilamentViewer::destroySwapChain(SwapChain *swapChain) { std::lock_guard lock(_renderMutex); + _renderable[swapChain].clear(); auto it = std::find(_swapChains.begin(), _swapChains.end(), swapChain); if (it != _swapChains.end()) { _swapChains.erase(it); } _engine->destroy(swapChain); - Log("Swapchain destroyed."); #ifdef __EMSCRIPTEN__ _engine->execute(); #else @@ -1016,53 +1022,49 @@ namespace thermion } } - bool FilamentViewer::render( - uint64_t frameTimeInNanos, - SwapChain *swapChain, - void *pixelBuffer, - void (*callback)(void *buf, size_t size, void *data), - void *data) + void FilamentViewer::setRenderable(View* view, SwapChain* swapChain, bool renderable) { + + std::lock_guard lock(_renderMutex); + + auto views = _renderable[swapChain]; + + auto it = std::find(views.begin(), views.end(), view); + + if(renderable) { + if(it == views.end()) { + views.push_back(view); + } + } else { + if(it != views.end()) { + views.erase(it); + } + } + _renderable[swapChain] = views; + } + + + void FilamentViewer::render( + uint64_t frameTimeInNanos) { - if (!swapChain) - { - return false; - } - - auto now = std::chrono::high_resolution_clock::now(); - auto secsSinceLastFpsCheck = float(std::chrono::duration_cast(now - _fpsCounterStartTime).count()); - - if (secsSinceLastFpsCheck >= 1) - { - auto fps = _frameCount / secsSinceLastFpsCheck; - _frameCount = 0; - _skippedFrames = 0; - _fpsCounterStartTime = now; - } - - Timer tmr; - _sceneManager->updateTransforms(); _sceneManager->updateAnimations(); - _cumulativeAnimationUpdateTime += tmr.elapsed(); - - // Render the scene, unless the renderer wants to skip the frame. - bool beginFrame = _renderer->beginFrame(swapChain, frameTimeInNanos); - if (!beginFrame) - { - _skippedFrames++; - } else { - for(auto *view : _renderable) { - _renderer->render(view); + for(auto swapChain : _swapChains) { + auto views = _renderable[swapChain]; + if(views.size() > 0) { + bool beginFrame = _renderer->beginFrame(swapChain, frameTimeInNanos); + if (beginFrame) { + for(auto view : views) { + _renderer->render(view); + } + } } - _frameCount++; _renderer->endFrame(); } #ifdef __EMSCRIPTEN__ _engine->execute(); #endif - return beginFrame; } class CaptureCallbackHandler : public filament::backend::CallbackHandler diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index f7b31461..39a5f824 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -44,8 +44,9 @@ extern "C" filament::math::float4{float(d_mat.col4[0]), float(d_mat.col4[1]), float(d_mat.col4[2]), float(d_mat.col4[3])}}; } - EMSCRIPTEN_KEEPALIVE TViewer *create_filament_viewer(const void *context, const void *const loader, void *const platform, const char *uberArchivePath) + EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *context, const void *const loader, void *const platform, const char *uberArchivePath) { + Log("CREATE"); const auto *loaderImpl = new ResourceLoaderWrapperImpl((ResourceLoaderWrapper *)loader); auto viewer = new FilamentViewer(context, loaderImpl, platform, uberArchivePath); return reinterpret_cast(viewer); @@ -339,29 +340,18 @@ extern "C" cam->setModelMatrix(mat); } - EMSCRIPTEN_KEEPALIVE bool Viewer_render( - TViewer *tViewer, - TSwapChain *tSwapChain, - uint64_t frameTimeInNanos, - void *pixelBuffer, - void (*callback)(void *buf, size_t size, void *data), - void *data) + EMSCRIPTEN_KEEPALIVE void Viewer_render( + TViewer *tViewer) { auto viewer = reinterpret_cast(tViewer); - auto swapChain = reinterpret_cast(tSwapChain); - - if(!swapChain) { - swapChain = viewer->getSwapChainAt(0); - } - - return viewer->render(frameTimeInNanos, swapChain, pixelBuffer, callback, data); + viewer->render(0); } - EMSCRIPTEN_KEEPALIVE void Viewer_markViewRenderable(TViewer *tViewer, TView* tView, bool renderable) { + EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *tViewer, TSwapChain *tSwapChain, TView *tView, bool renderable) { auto viewer = reinterpret_cast(tViewer); - + auto swapChain = reinterpret_cast(tSwapChain); auto *view = reinterpret_cast(tView); - viewer->setRenderable(view, renderable); + viewer->setRenderable(view, swapChain, renderable); } EMSCRIPTEN_KEEPALIVE void Viewer_capture( @@ -416,10 +406,17 @@ extern "C" viewer->destroySwapChain(swapChain); } - EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *tViewer, const void *const window, uint32_t width, uint32_t height) + EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *tViewer, uint32_t width, uint32_t height) { auto viewer = reinterpret_cast(tViewer); - auto swapChain = viewer->createSwapChain(window, width, height); + auto swapChain = viewer->createSwapChain(width, height); + return reinterpret_cast(swapChain); + } + + EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *tViewer, const void *const window) + { + auto viewer = reinterpret_cast(tViewer); + auto swapChain = viewer->createSwapChain(window); return reinterpret_cast(swapChain); } @@ -721,7 +718,8 @@ extern "C" EMSCRIPTEN_KEEPALIVE void SceneManager_queueTransformUpdates(TSceneManager *tSceneManager, EntityId *entities, const double *const transforms, int numEntities) { auto *sceneManager = reinterpret_cast(tSceneManager); - math::mat4 matrices[numEntities]; + math::mat4 matrices[ + numEntities]; for (int i = 0; i < numEntities; i++) { matrices[i] = math::mat4( diff --git a/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp index b0f4fb38..46777dc6 100644 --- a/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp +++ b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp @@ -82,10 +82,9 @@ public: } } - void requestFrame(TSwapChain* tSwapChain, void (*callback)()) + void requestFrame(void (*callback)()) { std::unique_lock lock(_mutex); - this->swapChain = tSwapChain; this->_requestFrameRenderCallback = callback; } @@ -93,12 +92,12 @@ public: { { std::unique_lock lock(_mutex); - if (swapChain) + if (_requestFrameRenderCallback) { - doRender(swapChain); + doRender(); lock.unlock(); this->_requestFrameRenderCallback(); - swapChain = nullptr; + this->_requestFrameRenderCallback = nullptr; // Calculate and print FPS auto currentTime = std::chrono::high_resolution_clock::now(); @@ -158,10 +157,13 @@ public: glClear(GL_COLOR_BUFFER_BIT); // emscripten_webgl_commit_frame(); - _viewer = (FilamentViewer *)create_filament_viewer((void *const)_context, loader, platform, uberArchivePath); + _viewer = (FilamentViewer *)Viewer_create((void *const)_context, loader, platform, uberArchivePath); MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, _viewer); #else - auto viewer = (FilamentViewer *)create_filament_viewer(context, loader, platform, uberArchivePath); + Log("CREATING1"); + + auto viewer = (FilamentViewer *)Viewer_create(context, loader, platform, uberArchivePath); + Log("CREATING2"); _viewer = reinterpret_cast(viewer); callback(_viewer); #endif @@ -180,26 +182,13 @@ public: fut.wait(); } - bool doRender(TSwapChain *tSwapChain) + void doRender() { -#ifdef __EMSCRIPTEN__ - if (emscripten_is_webgl_context_lost(_context) == EM_TRUE) - { - Log("Context lost"); - auto sleepFor = std::chrono::seconds(1); - std::this_thread::sleep_for(sleepFor); - return; - } -#endif - bool rendered = Viewer_render(_viewer, tSwapChain, 0, nullptr, nullptr, nullptr); + Viewer_render(_viewer); if (_renderCallback) { _renderCallback(_renderCallbackOwner); } - return rendered; -#ifdef __EMSCRIPTEN__ - // emscripten_webgl_commit_frame(); -#endif } void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) @@ -251,7 +240,7 @@ extern "C" static RenderLoop *_rl; - EMSCRIPTEN_KEEPALIVE void create_filament_viewer_render_thread( + EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread( void *const context, void *const platform, const char *uberArchivePath, const void *const loader, void (*renderCallback)(void *const renderCallbackOwner), @@ -274,8 +263,7 @@ extern "C" _rl = nullptr; } - EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, - void *const surface, + EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain*)) @@ -283,12 +271,21 @@ extern "C" std::packaged_task lambda( [=]() mutable { - auto *swapChain = Viewer_createSwapChain(viewer, surface, width, height); -#ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete); -#else + auto *swapChain = Viewer_createHeadlessSwapChain(viewer, width, height); + onComplete(swapChain); + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, + void *const surface, + void (*onComplete)(TSwapChain*)) + { + std::packaged_task lambda( + [=]() mutable + { + auto *swapChain = Viewer_createSwapChain(viewer, surface); onComplete(swapChain); -#endif }); auto fut = _rl->add_task(lambda); } @@ -309,7 +306,7 @@ extern "C" } - EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, TSwapChain* tSwapChain, void(*onComplete)()) + EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)()) { if (!_rl) { @@ -317,7 +314,7 @@ extern "C" } else { - _rl->requestFrame(tSwapChain, onComplete); + _rl->requestFrame(onComplete); } } @@ -334,7 +331,7 @@ extern "C" { std::packaged_task lambda([=]() mutable { - _rl->doRender(tSwapChain); + _rl->doRender(); }); auto fut = _rl->add_task(lambda); } diff --git a/thermion_flutter/thermion_flutter/android/src/main/cpp/ThermionFlutterAndroid.cpp b/thermion_flutter/thermion_flutter/android/src/main/cpp/ThermionFlutterAndroid.cpp index 91e84f4d..763859e2 100644 --- a/thermion_flutter/thermion_flutter/android/src/main/cpp/ThermionFlutterAndroid.cpp +++ b/thermion_flutter/thermion_flutter/android/src/main/cpp/ThermionFlutterAndroid.cpp @@ -15,6 +15,13 @@ extern "C" { return window; } +// int ThermionAndroid_createGLTexture(jobject ) + +// int ASurfaceTexture_attachToGLContext( +// ASurfaceTexture *st, +// uint32_t texName +// ) + ResourceLoaderWrapper* make_resource_loader_wrapper_android(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void* owner) { ResourceLoaderWrapper *rlw = (ResourceLoaderWrapper *)malloc(sizeof(ResourceLoaderWrapper)); rlw->loadToOut = nullptr; diff --git a/thermion_flutter/thermion_flutter/android/src/main/kotlin/dev/thermion/android/ThermionFlutterPlugin.kt b/thermion_flutter/thermion_flutter/android/src/main/kotlin/dev/thermion/android/ThermionFlutterPlugin.kt index e039d05d..c91e1951 100644 --- a/thermion_flutter/thermion_flutter/android/src/main/kotlin/dev/thermion/android/ThermionFlutterPlugin.kt +++ b/thermion_flutter/thermion_flutter/android/src/main/kotlin/dev/thermion/android/ThermionFlutterPlugin.kt @@ -25,6 +25,8 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.view.TextureRegistry.SurfaceTextureEntry import java.io.File import java.util.* +import android.opengl.GLES20; + class LoadFilamentResourceFromOwnerImpl(plugin:ThermionFlutterPlugin) : LoadFilamentResourceFromOwner { var plugin = plugin @@ -66,10 +68,18 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo private var lifecycle: Lifecycle? = null private lateinit var _lib : FilamentInterop + + private data class TextureEntry( + val surfaceTextureEntry: SurfaceTextureEntry, + val surfaceTexture: SurfaceTexture, + val surface: Surface + ) var _surfaceTexture: SurfaceTexture? = null private var _surfaceTextureEntry: SurfaceTextureEntry? = null var _surface: Surface? = null + private val textures: MutableMap = mutableMapOf() + private lateinit var activity:Activity @@ -153,35 +163,55 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo @RequiresApi(Build.VERSION_CODES.M) override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - Log.e("thermion_flutter", call.method, null) when (call.method) { "createTexture" -> { - if(_surfaceTextureEntry != null) { - result.error("TEXTURE_EXISTS", "Texture already exist. Make sure you call destroyTexture first", null) - return - } - val args = call.arguments as List<*> - val width = args[0] as Int - val height = args[1] as Int - if(width <1 || height < 1) { - result.error("DIMENSION_MISMATCH","Both dimensions must be greater than zero (you provided $width x $height)", null); - return; - } - Log.i("thermion_flutter", "Creating SurfaceTexture ${width}x${height}"); - - _surfaceTextureEntry = flutterPluginBinding.textureRegistry.createSurfaceTexture() - _surfaceTexture = _surfaceTextureEntry!!.surfaceTexture(); - _surfaceTexture!!.setDefaultBufferSize(width, height) + val args = call.arguments as List<*> + val width = args[0] as Int + val height = args[1] as Int + if (width < 1 || height < 1) { + result.error("DIMENSION_MISMATCH", "Both dimensions must be greater than zero (you provided $width x $height)", null) + return + } + Log.i("thermion_flutter", "Creating SurfaceTexture ${width}x${height}") + + val surfaceTextureEntry = flutterPluginBinding.textureRegistry.createSurfaceTexture() + val surfaceTexture = surfaceTextureEntry.surfaceTexture() + surfaceTexture.setDefaultBufferSize(width, height) - _surface = Surface(_surfaceTexture) + val surface = Surface(surfaceTexture) - if(!_surface!!.isValid) { - result.error("SURFACE_INVALID", "Failed to create valid surface", null) - } else { - val nativeWindow = _lib.get_native_window_from_surface(_surface!! as Object, JNIEnv.CURRENT) - result.success(listOf(_surfaceTextureEntry!!.id(), null, Pointer.nativeValue(nativeWindow))) - } - } + if (!surface.isValid) { + result.error("SURFACE_INVALID", "Failed to create valid surface", null) + } else { + val flutterTextureId = surfaceTextureEntry.id() + textures[flutterTextureId] = TextureEntry(surfaceTextureEntry, surfaceTexture, surface) + val nativeWindow = _lib.get_native_window_from_surface(surface as Object, JNIEnv.CURRENT) + result.success(listOf(flutterTextureId, flutterTextureId, Pointer.nativeValue(nativeWindow))) + } + } + "destroyTexture" -> { + val args = call.arguments as List<*> + val textureId = (args[0] as Int).toLong() + val textureEntry = textures[textureId] + if (textureEntry != null) { + textureEntry.surface.release() + textureEntry.surfaceTextureEntry.release() + textures.remove(textureId) + result.success(true) + } else { + result.error("TEXTURE_NOT_FOUND", "Texture with id $textureId not found", null) + } + } + "markTextureFrameAvailable" -> { + val textureId = (call.arguments as Int).toLong() + val textureEntry = textures[textureId] + if (textureEntry != null) { + //textureEntry.surfaceTexture.updateTexImage() + result.success(null) + } else { + result.error("TEXTURE_NOT_FOUND", "Texture with id $textureId not found", null) + } + } "getResourceLoaderWrapper" -> { val resourceLoader = _lib.make_resource_loader_wrapper_android(loadResourceWrapper, freeResourceWrapper, Pointer(0)) result.success(Pointer.nativeValue(resourceLoader)) @@ -196,13 +226,6 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo val renderCallbackFnPointer = _lib.make_render_callback_fn_pointer(RenderCallbackImpl(this)) result.success(listOf(Pointer.nativeValue(renderCallbackFnPointer), 0)) } - "destroyTexture" -> { - _surface!!.release(); - _surfaceTextureEntry!!.release(); - _surface = null - _surfaceTextureEntry = null - result.success(true) - } else -> { result.notImplemented() } @@ -210,7 +233,13 @@ class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, Lo } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) + channel.setMethodCallHandler(null) + // Release all textures + for ((_, textureEntry) in textures) { + textureEntry.surface.release() + textureEntry.surfaceTextureEntry.release() + } + textures.clear() } diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart index 9bb3e761..c8cfc949 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart @@ -50,13 +50,8 @@ class _ThermionTextureWidgetState extends State { var width = (size.width * dpr).ceil(); var height = (size.height * dpr).ceil(); - _texture = - await ThermionFlutterPlatform.instance.createTexture(width, height); - - _renderTarget = await widget.viewer.createRenderTarget( - _texture!.width, _texture!.height, _texture!.hardwareId); - - await widget.view.setRenderTarget(_renderTarget!); + _texture = await ThermionFlutterPlatform.instance + .createTexture(widget.view, width, height); await widget.view.updateViewport(_texture!.width, _texture!.height); var camera = await widget.view.getCamera(); @@ -74,13 +69,7 @@ class _ThermionTextureWidgetState extends State { if (mounted) { setState(() {}); } - if (texture != null) { - _renderTarget = await widget.viewer.createRenderTarget( - texture.width, texture.height, texture.flutterId); - await widget.view.setRenderTarget(null); - await _renderTarget!.destroy(); - texture.destroy(); - } + await texture?.destroy(); _views.clear(); }); }); @@ -100,7 +89,7 @@ class _ThermionTextureWidgetState extends State { WidgetsBinding.instance.scheduleFrameCallback((d) async { if (widget.viewer.rendering && !_rendering) { _rendering = true; - if (_callbackId == _primaryCallback) { + if (_callbackId == _primaryCallback && _texture != null) { await widget.viewer.requestFrame(); lastRender = d.inMilliseconds; } @@ -140,8 +129,6 @@ class _ThermionTextureWidgetState extends State { var newWidth = newSize.width.ceil(); var newHeight = newSize.height.ceil(); - var lastTextureId = _texture?.hardwareId; - await _texture?.resize( newWidth, newHeight, @@ -149,12 +136,6 @@ class _ThermionTextureWidgetState extends State { 0, ); - if (_texture?.hardwareId != lastTextureId) { - await _renderTarget?.destroy(); - _renderTarget = await widget.viewer.createRenderTarget( - _texture!.width, _texture!.height, _texture!.hardwareId); - await widget.view.setRenderTarget(_renderTarget!); - } await widget.view.updateViewport(_texture!.width, _texture!.height); var camera = await widget.view.getCamera(); diff --git a/thermion_flutter/thermion_flutter_ffi/lib/platform_texture.dart b/thermion_flutter/thermion_flutter_ffi/lib/platform_texture.dart new file mode 100644 index 00000000..041a2b8a --- /dev/null +++ b/thermion_flutter/thermion_flutter_ffi/lib/platform_texture.dart @@ -0,0 +1,97 @@ +import 'package:logging/logging.dart'; +import 'package:thermion_dart/thermion_dart.dart'; +import 'thermion_flutter_method_channel_interface.dart'; + +class FlutterPlatformTexture extends MethodChannelFlutterTexture { + final _logger = Logger("ThermionFlutterTexture"); + + final ThermionViewer viewer; + final View view; + + int flutterId = -1; + int _lastFlutterId = -1; + int _lastHardwareId = -1; + int hardwareId = -1; + int width = -1; + int height = -1; + + SwapChain? swapChain; + + RenderTarget? _renderTarget; + + late bool destroySwapChainOnResize; + + FlutterPlatformTexture( + super.channel, this.viewer, this.view, this.swapChain) { + if (swapChain == null) { + destroySwapChainOnResize = true; + } else { + destroySwapChainOnResize = false; + } + } + + @override + Future resize( + int newWidth, int newHeight, int newLeft, int newTop) async { + if (newWidth == this.width && + newHeight == this.height && + newLeft == 0 && + newTop == 0) { + return; + } + + this.width = newWidth; + this.height = newHeight; + + var result = + await channel.invokeMethod("createTexture", [width, height, 0, 0]); + if (result == null || (result[0] == -1)) { + throw Exception("Failed to create texture"); + } + _lastFlutterId = flutterId; + _lastHardwareId = hardwareId; + flutterId = result[0] as int; + + hardwareId = result[1] as int; + + if (destroySwapChainOnResize) { + await swapChain?.destroy(); + swapChain = await viewer.createSwapChain(result[2]); + } + + _logger.info( + "Created new texture: flutter id $flutterId, hardware id $hardwareId"); + + if (destroySwapChainOnResize) { + await view.setRenderable(true, swapChain!); + } else if (hardwareId != _lastHardwareId) { + await _renderTarget?.destroy(); + _renderTarget = + await viewer.createRenderTarget(width, height, hardwareId); + await view.setRenderTarget(_renderTarget!); + await view.setRenderable(true, swapChain!); + await _destroyTexture(_lastFlutterId, _lastHardwareId); + } + } + + Future _destroyTexture(int flutterId, int? hardwareId) async { + try { + await channel.invokeMethod("destroyTexture", [flutterId, hardwareId]); + _logger.info( + "Destroyed old texture: flutter id $flutterId, hardware id $hardwareId"); + } catch (e) { + _logger.severe("Failed to destroy texture: $e"); + } + } + + Future destroy() async { + await view.setRenderTarget(null); + await _renderTarget?.destroy(); + await swapChain?.destroy(); + await channel.invokeMethod("destroyTexture", hardwareId); + } + + Future markFrameAvailable() async { + await channel.invokeMethod("markTextureFrameAvailable", this.flutterId); + } +} diff --git a/thermion_flutter/thermion_flutter_ffi/lib/texture_cache_entry.dart b/thermion_flutter/thermion_flutter_ffi/lib/texture_cache_entry.dart new file mode 100644 index 00000000..64c88d8f --- /dev/null +++ b/thermion_flutter/thermion_flutter_ffi/lib/texture_cache_entry.dart @@ -0,0 +1,11 @@ +class TextureCacheEntry { + final int flutterId; + final int hardwareId; + final DateTime creationTime; + DateTime? removalTime; + bool inUse; + + TextureCacheEntry(this.flutterId, this.hardwareId, + {this.removalTime, this.inUse = true}) + : creationTime = DateTime.now(); +} \ No newline at end of file diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_android.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_android.dart deleted file mode 100644 index 0f6f43b4..00000000 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_android.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'dart:async'; -import 'package:flutter/services.dart'; -import 'package:thermion_dart/thermion_dart.dart'; -import 'package:thermion_dart/src/viewer/src/ffi/thermion_viewer_ffi.dart'; -import 'package:thermion_flutter_ffi/thermion_flutter_method_channel_interface.dart'; -import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; -import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; -import 'package:logging/logging.dart'; - -/// -/// An implementation of [ThermionFlutterPlatform] that uses -/// Flutter platform channels to create a rendering context, -/// resource loaders, and surface/render target(s). -/// -class ThermionFlutterAndroid - extends ThermionFlutterMethodChannelInterface { - final _channel = const MethodChannel("dev.thermion.flutter/event"); - final _logger = Logger("ThermionFlutterFFI"); - - ThermionViewerFFI? _viewer; - - ThermionFlutterAndroid._() {} - - RenderTarget? _renderTarget; - SwapChain? _swapChain; - - static void registerWith() { - ThermionFlutterPlatform.instance = ThermionFlutterAndroid._(); - } - - final _textures = {}; - - bool _creatingTexture = false; - bool _destroyingTexture = false; - - bool _resizing = false; - - /// - /// Create a rendering surface. - /// - /// This is internal; unless you are [thermion_*] package developer, don't - /// call this yourself. - /// - /// The name here is slightly misleading because we only create - /// a texture render target on macOS and iOS; on Android, we render into - /// a native window derived from a Surface, and on Windows we render into - /// a HWND. - /// - /// Currently, this only supports a single "texture" (aka rendering surface) - /// at any given time. If a [ThermionWidget] is disposed, it will call - /// [destroyTexture]; if it is resized, it will call [resizeTexture]. - /// - /// In future, we probably want to be able to create multiple distinct - /// textures/render targets. This would make it possible to have multiple - /// Flutter Texture widgets, each with its own Filament View attached. - /// The current design doesn't accommodate this (for example, it seems we can - /// only create a single native window from a Surface at any one time). - /// - Future createTexture(int width, int height) async { - throw Exception("TODO"); - // note that when [ThermionWidget] is disposed, we don't destroy the - // texture; instead, we keep it around in case a subsequent call requests - // a texture of the same size. - - // if (_textures.length > 1) { - // throw Exception("Multiple textures not yet supported"); - // } else if (_textures.length == 1) { - // if (_textures.first.height == physicalHeight && - // _textures.first.width == physicalWidth) { - // return _textures.first; - // } else { - // await _viewer!.setRendering(false); - // await _swapChain?.destroy(); - // await destroyTexture(_textures.first); - // _textures.clear(); - // } - // } - - // _creatingTexture = true; - - // var result = await _channel.invokeMethod("createTexture", - // [physicalWidth, physicalHeight, offsetLeft, offsetLeft]); - - // if (result == null || (result[0] == -1)) { - // throw Exception("Failed to create texture"); - // } - // final flutterTextureId = result[0] as int?; - // final hardwareTextureId = result[1] as int?; - // final surfaceAddress = result[2] as int?; - - // _logger.info( - // "Created texture with flutter texture id ${flutterTextureId}, hardwareTextureId $hardwareTextureId and surfaceAddress $surfaceAddress"); - - // final texture = ThermionFlutterTexture(flutterTextureId, hardwareTextureId, - // physicalWidth, physicalHeight, surfaceAddress); - - // await _viewer?.createSwapChain(physicalWidth, physicalHeight, - // surface: texture.surfaceAddress == null - // ? nullptr - // : Pointer.fromAddress(texture.surfaceAddress!)); - - // if (texture.hardwareTextureId != null) { - // if (_renderTarget != null) { - // await _renderTarget!.destroy(); - // } - // // ignore: unused_local_variable - // _renderTarget = await _viewer?.createRenderTarget( - // physicalWidth, physicalHeight, texture.hardwareTextureId!); - // } - - // await _viewer?.updateViewportAndCameraProjection( - // physicalWidth.toDouble(), physicalHeight.toDouble()); - // _creatingTexture = false; - // _textures.add(texture); - // return texture; - } - - /// - /// Called by [ThermionWidget] to resize a texture. Don't call this yourself. - /// - @override - Future resizeWindow( - int width, - int height, - int offsetLeft, - int offsetTop, - ) async { - throw Exception("Not supported on iOS"); - } -} diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 7e93a13b..d4816ed0 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -1,3 +1,2 @@ -export 'thermion_flutter_android.dart'; export 'thermion_flutter_windows.dart'; export 'thermion_flutter_texture_backed_platform.dart'; diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_method_channel_interface.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_method_channel_interface.dart index c88990bc..0a39c37c 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_method_channel_interface.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_method_channel_interface.dart @@ -14,11 +14,11 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar /// abstract class ThermionFlutterMethodChannelInterface extends ThermionFlutterPlatform { - final _channel = const MethodChannel("dev.thermion.flutter/event"); + + final channel = const MethodChannel("dev.thermion.flutter/event"); final _logger = Logger("ThermionFlutterMethodChannelInterface"); ThermionViewerFFI? viewer; - SwapChain? _swapChain; Future createViewer({ThermionFlutterOptions? options}) async { if (viewer != null) { @@ -27,25 +27,18 @@ abstract class ThermionFlutterMethodChannelInterface } var resourceLoader = Pointer.fromAddress( - await _channel.invokeMethod("getResourceLoaderWrapper")); + await channel.invokeMethod("getResourceLoaderWrapper")); if (resourceLoader == nullptr) { throw Exception("Failed to get resource loader"); } - // var renderCallbackResult = await _channel.invokeMethod("getRenderCallback"); - var renderCallback = nullptr; - // Pointer)>>.fromAddress( - // renderCallbackResult[0]); - var renderCallbackOwner = nullptr; - // Pointer.fromAddress(renderCallbackResult[1]); - - var driverPlatform = await _channel.invokeMethod("getDriverPlatform"); + var driverPlatform = await channel.invokeMethod("getDriverPlatform"); var driverPtr = driverPlatform == null ? nullptr : Pointer.fromAddress(driverPlatform); - var sharedContext = await _channel.invokeMethod("getSharedContext"); + var sharedContext = await channel.invokeMethod("getSharedContext"); var sharedContextPtr = sharedContext == null ? nullptr @@ -53,8 +46,6 @@ abstract class ThermionFlutterMethodChannelInterface viewer = ThermionViewerFFI( resourceLoader: resourceLoader, - renderCallback: renderCallback, - renderCallbackOwner: renderCallbackOwner, driver: driverPtr, sharedContext: sharedContextPtr, uberArchivePath: options?.uberarchivePath); @@ -69,9 +60,7 @@ abstract class MethodChannelFlutterTexture extends ThermionFlutterTexture { MethodChannelFlutterTexture(this.channel); - Future destroy() async { - await channel.invokeMethod("destroyTexture", hardwareId); - } + @override int get flutterId; @@ -85,7 +74,5 @@ abstract class MethodChannelFlutterTexture extends ThermionFlutterTexture { @override int get width; - Future markFrameAvailable() async { - await channel.invokeMethod("markTextureFrameAvailable", this.flutterId); - } + } diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_texture_backed_platform.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_texture_backed_platform.dart index a8fa9776..a1b4a898 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_texture_backed_platform.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_texture_backed_platform.dart @@ -1,18 +1,22 @@ import 'dart:async'; -import 'package:flutter/services.dart'; +import 'dart:io'; + import 'package:thermion_dart/thermion_dart.dart'; +import 'package:thermion_dart/thermion_dart.dart' as t; import 'package:thermion_flutter_ffi/thermion_flutter_method_channel_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; -import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; import 'package:logging/logging.dart'; +import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; + +import 'platform_texture.dart'; /// /// An implementation of [ThermionFlutterPlatform] that uses /// Flutter platform channels to create a rendering context, /// resource loaders, and surface/render target(s). /// -class ThermionFlutterTextureBackedPlatform extends ThermionFlutterMethodChannelInterface { - final _channel = const MethodChannel("dev.thermion.flutter/event"); +class ThermionFlutterTextureBackedPlatform + extends ThermionFlutterMethodChannelInterface { final _logger = Logger("ThermionFlutterTextureBackedPlatform"); static SwapChain? _swapChain; @@ -32,16 +36,19 @@ class ThermionFlutterTextureBackedPlatform extends ThermionFlutterMethodChannelI if (_swapChain != null) { throw Exception("Only a single swapchain can be created"); } + // this implementation renders directly into a texture/render target - // we still need to create a (headless) swapchain, but the actual dimensions + // we still need to create a (headless) swapchain, but the actual dimensions // don't matter - _swapChain = await viewer.createSwapChain(1, 1); + if (Platform.isMacOS || Platform.isIOS) { + _swapChain = await viewer.createHeadlessSwapChain(1, 1); + } return viewer; } // On desktop platforms, textures are always created - Future createTexture(int width, int height) async { - var texture = ThermionFlutterTexture(_channel); + Future createTexture(t.View view, int width, int height) async { + var texture = FlutterPlatformTexture(channel, viewer!, view, (Platform.isMacOS || Platform.isIOS)? _swapChain : null); await texture.resize(width, height, 0, 0); return texture; } @@ -54,119 +61,3 @@ class ThermionFlutterTextureBackedPlatform extends ThermionFlutterMethodChannelI } } -class TextureCacheEntry { - final int flutterId; - final int hardwareId; - final DateTime creationTime; - DateTime? removalTime; - bool inUse; - - TextureCacheEntry(this.flutterId, this.hardwareId, {this.removalTime, this.inUse = true}) - : creationTime = DateTime.now(); -} - - -class ThermionFlutterTexture extends MethodChannelFlutterTexture { - final _logger = Logger("ThermionFlutterTexture"); - - int flutterId = -1; - int hardwareId = -1; - int width = -1; - int height = -1; - - static final Map> _textureCache = {}; - - ThermionFlutterTexture(super.channel); - - @override - Future resize( - int newWidth, int newHeight, int newLeft, int newTop) async { - if (newWidth == this.width && - newHeight == this.height && - newLeft == 0 && - newTop == 0) { - return; - } - - this.width = newWidth; - this.height = newHeight; - - // Clean up old textures - await _cleanupOldTextures(); - - final cacheKey = '${width}x$height'; - final availableTextures = - _textureCache[cacheKey]?.where((entry) => !entry.inUse) ?? []; - if (availableTextures.isNotEmpty) { - final cachedTexture = availableTextures.first; - flutterId = cachedTexture.flutterId; - hardwareId = cachedTexture.hardwareId; - cachedTexture.inUse = true; - _logger.info( - "Using cached texture: flutter id $flutterId, hardware id $hardwareId"); - } else { - var result = - await channel.invokeMethod("createTexture", [width, height, 0, 0]); - if (result == null || (result[0] == -1)) { - throw Exception("Failed to create texture"); - } - flutterId = result[0] as int; - hardwareId = result[1] as int; - - final newEntry = TextureCacheEntry(flutterId, hardwareId, inUse: true); - _textureCache.putIfAbsent(cacheKey, () => []).add(newEntry); - _logger.info( - "Created new texture: flutter id $flutterId, hardware id $hardwareId"); - } - - // Mark old texture as not in use - if (this.width != -1 && this.height != -1) { - final oldCacheKey = '${this.width}x${this.height}'; - final oldEntry = _textureCache[oldCacheKey]?.firstWhere( - (entry) => entry.flutterId == this.flutterId, - orElse: () => TextureCacheEntry(-1, -1), - ); - if (oldEntry != null && oldEntry.flutterId != -1) { - oldEntry.inUse = false; - oldEntry.removalTime = DateTime.now(); - } - } - } - - Future _cleanupOldTextures() async { - final now = DateTime.now(); - final entriesToRemove = >{}; - - for (var entry in _textureCache.entries) { - final expiredTextures = entry.value.where((texture) { - return !texture.inUse && - texture.removalTime != null && - now.difference(texture.removalTime!).inSeconds > 5; - }).toList(); - - if (expiredTextures.isNotEmpty) { - entriesToRemove[entry.key] = expiredTextures; - } - } - - for (var entry in entriesToRemove.entries) { - for (var texture in entry.value) { - await _destroyTexture(texture.flutterId, texture.hardwareId); - _logger.info("Destroying texture: ${texture.flutterId}"); - _textureCache[entry.key]?.remove(texture); - } - if (_textureCache[entry.key]?.isEmpty ?? false) { - _textureCache.remove(entry.key); - } - } - } - - Future _destroyTexture(int flutterId, int? hardwareId) async { - try { - await channel.invokeMethod("destroyTexture", [flutterId, hardwareId]); - _logger.info("Destroyed old texture: flutter id $flutterId, hardware id $hardwareId"); - } catch (e) { - _logger.severe("Failed to destroy texture: $e"); - } - } -} diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_windows.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_windows.dart index dd41948c..e05be422 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_windows.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_windows.dart @@ -32,8 +32,9 @@ class ThermionFlutterWindows /// /// Not supported on Windows. Throws an exception. /// - Future createTexture(int width, int height) async { - throw Exception("Texture not supported on Windows"); + @override + Future createTexture(View view, int width, int height) { + throw UnimplementedError(); } bool _resizing = false; @@ -94,4 +95,6 @@ class ThermionFlutterWindows // _resizing = false; // return newTexture; } + + } diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index d794d3fc..8d095e32 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -13,7 +13,7 @@ flutter: ios: dartPluginClass: ThermionFlutterTextureBackedPlatform android: - dartPluginClass: ThermionFlutterAndroid + dartPluginClass: ThermionFlutterTextureBackedPlatform macos: dartPluginClass: ThermionFlutterTextureBackedPlatform windows: diff --git a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart index 43c06db5..d3e5e39a 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart +++ b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:thermion_dart/thermion_dart.dart' as t; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:thermion_dart/thermion_dart.dart'; import 'thermion_flutter_texture.dart'; @@ -36,11 +37,11 @@ abstract class ThermionFlutterPlatform extends PlatformInterface { /// This is internal; unless you are [thermion_*] package developer, don't /// call this yourself. May not be supported on all platforms. /// - Future createTexture(int width, int height); + Future createTexture( + t.View view, int width, int height); /// /// /// -Future resizeWindow( - int width, int height, int offsetTop, int offsetRight); + Future resizeWindow(int width, int height, int offsetTop, int offsetRight); } diff --git a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_texture.dart b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_texture.dart index 25b62394..e1635990 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_texture.dart +++ b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_texture.dart @@ -1,18 +1,5 @@ -// class ThermionFlutterTextureImpl { -// final int width; -// final int height; -// final int? flutterTextureId; -// final int? hardwareTextureId; -// final int? surfaceAddress; -// bool get usesBackingWindow => flutterTextureId == null; - -// ThermionFlutterTexture(this.flutterTextureId, this.hardwareTextureId, -// this.width, this.height, this.surfaceAddress) { - -// } -// } - abstract class ThermionFlutterTexture { + int get width; int get height;