initial work to re-implement FFI with background thread render loop
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "AssetManager.hpp"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/Texture.h>
|
||||
@@ -17,8 +19,8 @@
|
||||
#include "StreamBufferAdapter.hpp"
|
||||
#include "SceneAsset.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "AssetManager.hpp"
|
||||
|
||||
#include "material/StandardMaterialProvider.hpp"
|
||||
#include "material/UnlitMaterialProvider.hpp"
|
||||
#include "material/FileMaterialProvider.hpp"
|
||||
#include "gltfio/materials/uberarchive.h"
|
||||
@@ -52,16 +54,13 @@ _scene(scene) {
|
||||
_gltfResourceLoader = new ResourceLoader({.engine = _engine,
|
||||
.normalizeSkinningWeights = true });
|
||||
|
||||
// auto uberdata = resourceLoaderWrapper->load("packages/polyvox_filament/assets/materials.uberz");
|
||||
// auto uberdata = resourceLoaderWrapper->load("packages/polyvox_filament/assets/materials_ios_arm64.uberz");
|
||||
|
||||
// _ubershaderProvider = gltfio::createUbershaderProvider(
|
||||
// _engine, uberdata.data, uberdata.size);
|
||||
// _engine, uberdata.data, uberdata.size);
|
||||
_ubershaderProvider = gltfio::createUbershaderProvider(
|
||||
_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
|
||||
|
||||
|
||||
|
||||
// _ubershaderProvider = gltfio::createJitShaderProvider(_engine, true);
|
||||
// _ubershaderProvider = new StandardMaterialProvider(_engine);
|
||||
EntityManager &em = EntityManager::get();
|
||||
|
||||
//_unlitProvider = new UnlitMaterialProvider(_engine);
|
||||
@@ -78,6 +77,7 @@ _scene(scene) {
|
||||
AssetManager::~AssetManager() {
|
||||
_gltfResourceLoader->asyncCancelLoad();
|
||||
_ubershaderProvider->destroyMaterials();
|
||||
//_unlitProvider->destroyMaterials();
|
||||
destroyAll();
|
||||
AssetLoader::destroy(&_assetLoader);
|
||||
|
||||
@@ -252,7 +252,8 @@ void AssetManager::updateAnimations() {
|
||||
|
||||
for (auto& asset : _assets) {
|
||||
|
||||
vector<AnimationStatus> completed;
|
||||
vector<int> completed;
|
||||
int index = 0;
|
||||
for(auto& anim : asset.mAnimations) {
|
||||
|
||||
auto elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - anim.mStart).count()) / 1000.0f;
|
||||
@@ -316,10 +317,14 @@ void AssetManager::updateAnimations() {
|
||||
}
|
||||
// animation has completed
|
||||
} else {
|
||||
completed.push_back(anim);
|
||||
completed.push_back(index);
|
||||
asset.fadeGltfAnimationIndex = -1;
|
||||
}
|
||||
asset.mAnimator->updateBoneMatrices();
|
||||
index++;
|
||||
}
|
||||
for(auto& it : completed) {
|
||||
asset.mAnimations.erase(asset.mAnimations.begin() + it);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -657,6 +662,8 @@ void AssetManager::playAnimation(EntityId e, int index, bool loop, bool reverse,
|
||||
animation.mDuration = asset.mAnimator->getAnimationDuration(index);
|
||||
|
||||
asset.mAnimations.push_back(animation);
|
||||
|
||||
Log("Current animation count %d ", asset.mAnimations.size());
|
||||
}
|
||||
|
||||
void AssetManager::stopAnimation(EntityId entityId, int index) {
|
||||
|
||||
@@ -112,10 +112,8 @@ FilamentViewer::FilamentViewer(const void* context, const ResourceLoaderWrapper*
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
_engine = Engine::create(Engine::Backend::METAL);
|
||||
#elif TARGET_OS_MAC
|
||||
_engine = Engine::create(Engine::Backend::METAL);
|
||||
#else
|
||||
_engine = Engine::create(Engine::Backend::OPENGL, nullptr, (void*)context, nullptr);
|
||||
_engine = Engine::create(Engine::Backend::OPENGL); //L, nullptr, (void*)context, nullptr);
|
||||
#endif
|
||||
|
||||
_renderer = _engine->createRenderer();
|
||||
@@ -153,7 +151,6 @@ FilamentViewer::FilamentViewer(const void* context, const ResourceLoaderWrapper*
|
||||
const float aperture = _mainCamera->getAperture();
|
||||
const float shutterSpeed = _mainCamera->getShutterSpeed();
|
||||
const float sens = _mainCamera->getSensitivity();
|
||||
// _mainCamera->setExposure(2.0f, 1.0f, 1.0f);
|
||||
|
||||
Log("Camera aperture %f shutter %f sensitivity %f", aperture, shutterSpeed, sens);
|
||||
|
||||
@@ -173,10 +170,6 @@ FilamentViewer::FilamentViewer(const void* context, const ResourceLoaderWrapper*
|
||||
|
||||
_view->setAntiAliasing(AntiAliasing::NONE);
|
||||
|
||||
// auto materialRb = _resourceLoader->load("file:///mnt/hdd_2tb/home/hydroxide/projects/filament/unlit.filamat");
|
||||
// Log("Loaded resource of size %d", materialRb.size);
|
||||
// _materialProvider = new FileMaterialProvider(_engine, (void*) materialRb.data, (size_t)materialRb.size);
|
||||
|
||||
EntityManager &em = EntityManager::get();
|
||||
|
||||
_ncm = new NameComponentManager(em);
|
||||
@@ -377,7 +370,6 @@ void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) {
|
||||
|
||||
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
|
||||
void *data) {
|
||||
Log("Deleting LinearImage");
|
||||
delete reinterpret_cast<LinearImage*>(data);
|
||||
};
|
||||
|
||||
@@ -423,14 +415,13 @@ void FilamentViewer::setBackgroundColor(const float r, const float g, const floa
|
||||
void FilamentViewer::clearBackgroundImage() {
|
||||
_imageMaterial->setDefaultParameter("showImage", 0);
|
||||
if (_imageTexture) {
|
||||
Log("Destroying existing texture");
|
||||
_engine->destroy(_imageTexture);
|
||||
Log("Destroyed.");
|
||||
_imageTexture = nullptr;
|
||||
Log("Destroyed background image texture");
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentViewer::setBackgroundImage(const char *resourcePath) {
|
||||
void FilamentViewer::setBackgroundImage(const char *resourcePath, bool fillHeight) {
|
||||
|
||||
string resourcePathString(resourcePath);
|
||||
|
||||
@@ -444,7 +435,18 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath) {
|
||||
// TODO - implement stretch/etc
|
||||
const Viewport& vp = _view->getViewport();
|
||||
Log("Image width %d height %d vp width %d height %d", _imageWidth, _imageHeight, vp.width, vp.height);
|
||||
_imageScale = mat4f { float(vp.width) / float(_imageWidth) , 0.0f, 0.0f, 0.0f, 0.0f, float(vp.height) / float(_imageHeight), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
|
||||
float xScale = float(vp.width) / float(_imageWidth);
|
||||
|
||||
float yScale;
|
||||
if(fillHeight) {
|
||||
yScale = 1.0f;
|
||||
} else {
|
||||
yScale = float(vp.height) / float(_imageHeight);
|
||||
}
|
||||
|
||||
_imageScale = mat4f { xScale , 0.0f, 0.0f, 0.0f, 0.0f, yScale , 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
_imageMaterial->setDefaultParameter("transform", _imageScale);
|
||||
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
|
||||
@@ -550,14 +552,14 @@ FilamentViewer::~FilamentViewer() {
|
||||
|
||||
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
||||
|
||||
void FilamentViewer::createSwapChain(const void *surface, uint32_t width, uint32_t height) {
|
||||
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height) {
|
||||
#if TARGET_OS_IPHONE
|
||||
_swapChain = _engine->createSwapChain((void*)surface, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||
_swapChain = _engine->createSwapChain((void*)window, filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||
#else
|
||||
if(surface) {
|
||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
if(window) {
|
||||
_swapChain = _engine->createSwapChain((void*)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
} else {
|
||||
_swapChain = _engine->createSwapChain((void*)surface, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
}
|
||||
#endif
|
||||
Log("Swapchain created.");
|
||||
@@ -588,7 +590,7 @@ void FilamentViewer::createRenderTarget(intptr_t textureId, uint32_t width, uint
|
||||
// Make a specific viewport just for our render target
|
||||
_view->setRenderTarget(_rt);
|
||||
|
||||
Log("Set render target for textureId %u %u x %u", textureId, width, height);
|
||||
Log("Set render target for glTextureId %u %u x %u", textureId, width, height);
|
||||
|
||||
}
|
||||
|
||||
@@ -861,8 +863,18 @@ void FilamentViewer::updateViewportAndCameraProjection(
|
||||
contentScaleFactor);
|
||||
}
|
||||
|
||||
void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
||||
void FilamentViewer::setCameraPosition(float x, float y, float z) {
|
||||
Camera& cam =_view->getCamera();
|
||||
|
||||
_cameraPosition = math::mat4f::translation(math::float3(x,y,z));
|
||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||
}
|
||||
|
||||
void FilamentViewer::setViewFrustumCulling(bool enabled) {
|
||||
_view->setFrustumCullingEnabled(enabled);
|
||||
}
|
||||
|
||||
void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
||||
auto asset = _assetManager->getAssetByEntityId(entityId);
|
||||
if(!asset) {
|
||||
Log("Failed to find asset attached to specified entity id.");
|
||||
@@ -878,20 +890,8 @@ void FilamentViewer::moveCameraToAsset(EntityId entityId) {
|
||||
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
|
||||
}
|
||||
|
||||
void FilamentViewer::setViewFrustumCulling(bool enabled) {
|
||||
_view->setFrustumCullingEnabled(enabled);
|
||||
}
|
||||
|
||||
void FilamentViewer::setCameraPosition(float x, float y, float z) {
|
||||
Camera& cam =_view->getCamera();
|
||||
|
||||
_cameraPosition = math::mat4f::translation(math::float3(x,y,z));
|
||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||
}
|
||||
|
||||
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z) {
|
||||
Camera& cam =_view->getCamera();
|
||||
|
||||
_cameraRotation = math::mat4f::rotation(rads, math::float3(x,y,z));
|
||||
cam.setModelMatrix(_cameraPosition * _cameraRotation);
|
||||
}
|
||||
@@ -936,7 +936,7 @@ void FilamentViewer::grabUpdate(float x, float y) {
|
||||
return;
|
||||
}
|
||||
Camera& cam =_view->getCamera();
|
||||
auto eye = cam.getPosition();
|
||||
auto eye = cam.getPosition();// math::float3 {0.0f, 0.5f, 50.0f } ;// ; //
|
||||
auto target = eye + cam.getForwardVector();
|
||||
auto upward = cam.getUpVector();
|
||||
Viewport const& vp = _view->getViewport();
|
||||
@@ -945,7 +945,9 @@ void FilamentViewer::grabUpdate(float x, float y) {
|
||||
cam.setModelMatrix(trans);
|
||||
} else {
|
||||
auto trans = cam.getModelMatrix() * mat4::rotation(
|
||||
0.02,
|
||||
|
||||
0.01,
|
||||
// math::float3 { 0.0f, 1.0f, 0.0f });
|
||||
math::float3 { (y - _startY) / vp.height, (x - _startX) / vp.width, 0.0f });
|
||||
cam.setModelMatrix(trans);
|
||||
}
|
||||
|
||||
@@ -17,31 +17,31 @@ extern "C" {
|
||||
#include "PolyvoxFilamentApi.h"
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* context, const ResourceLoaderWrapper* const loader) {
|
||||
return (void*) new FilamentViewer(context, loader);
|
||||
return (const void*) new FilamentViewer(context, loader);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT ResourceLoaderWrapper* make_resource_loader(LoadResourceFromOwner loadFn, FreeResourceFromOwner freeFn, void* const owner) {
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT ResourceLoaderWrapper* make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void* const owner) {
|
||||
return new ResourceLoaderWrapper(loadFn, freeFn, owner);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void create_render_target(const void* const viewer, intptr_t textureId, uint32_t width, uint32_t height) {
|
||||
FLUTTER_PLUGIN_EXPORT void create_render_target(const void* const viewer, uint32_t textureId, uint32_t width, uint32_t height) {
|
||||
((FilamentViewer*)viewer)->createRenderTarget(textureId, width, height);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void delete_filament_viewer(const void* const viewer) {
|
||||
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer(const void* const viewer) {
|
||||
delete((FilamentViewer*)viewer);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_color(const void* const viewer, const float r, const float g, const float b, const float a) {
|
||||
((FilamentViewer*)viewer)->setBackgroundColor(r, g, b, a);
|
||||
((FilamentViewer*)viewer)->setBackgroundColor(r, g, b, a);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void clear_background_image(const void* const viewer) {
|
||||
((FilamentViewer*)viewer)->clearBackgroundImage();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image(const void* const viewer, const char* path) {
|
||||
((FilamentViewer*)viewer)->setBackgroundImage(path);
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image(const void* const viewer, const char* path, bool fillHeight) {
|
||||
((FilamentViewer*)viewer)->setBackgroundImage(path, fillHeight);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_image_position(const void* const viewer, float x, float y, bool clamp) {
|
||||
@@ -97,14 +97,14 @@ extern "C" {
|
||||
return ((FilamentViewer*)viewer)->setCamera(asset, nodeName);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void move_camera_to_asset(const void* const viewer, EntityId asset) {
|
||||
((FilamentViewer*)viewer)->moveCameraToAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_view_frustum_culling(const void* const viewer, bool enabled) {
|
||||
((FilamentViewer*)viewer)->setViewFrustumCulling(enabled);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void move_camera_to_asset(const void* const viewer, EntityId asset) {
|
||||
((FilamentViewer*)viewer)->moveCameraToAsset(asset);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_camera_focus_distance(const void* const viewer, float distance) {
|
||||
((FilamentViewer*)viewer)->setCameraFocusDistance(distance);
|
||||
}
|
||||
@@ -147,8 +147,8 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->destroySwapChain();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const surface=nullptr, uint32_t width=0, uint32_t height=0) {
|
||||
((FilamentViewer*)viewer)->createSwapChain(surface, width, height);
|
||||
FLUTTER_PLUGIN_EXPORT void create_swap_chain(const void* const viewer, const void* const window, uint32_t width, uint32_t height) {
|
||||
((FilamentViewer*)viewer)->createSwapChain(window, width, height);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(const void* const viewer, uint32_t width, uint32_t height, float scaleFactor) {
|
||||
@@ -348,14 +348,6 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->clearAssets();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void load_texture(void* assetManager, EntityId asset, const char* assetPath, int renderableIndex) {
|
||||
// ((AssetManager*)assetManager)->loadTexture(assetPath, renderableIndex);
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_texture(void* assetManager, EntityId asset) {
|
||||
// ((AssetManager*)assetManager)->setTexture();
|
||||
}
|
||||
|
||||
bool set_material_color(void* assetManager, EntityId asset, const char* meshName, int materialIndex, const float r, const float g, const float b, const float a) {
|
||||
return ((AssetManager*)assetManager)->setMaterialColor(asset, meshName, materialIndex, r, g, b, a);
|
||||
}
|
||||
@@ -370,7 +362,7 @@ extern "C" {
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) {
|
||||
((AssetManager*)assetManager)->setRotation(asset, rads, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) {
|
||||
((AssetManager*)assetManager)->setScale(asset, scale);
|
||||
|
||||
111
macos/src/PolyvoxFilamentFFIApi.cpp
Normal file
111
macos/src/PolyvoxFilamentFFIApi.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "ResourceBuffer.hpp"
|
||||
|
||||
#include "FilamentViewer.hpp"
|
||||
#include "filament/LightManager.h"
|
||||
#include "Log.hpp"
|
||||
#include "ThreadPool.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
using namespace polyvox;
|
||||
|
||||
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
|
||||
|
||||
class RenderLoop {
|
||||
public:
|
||||
explicit RenderLoop() {
|
||||
_t = new std::thread([this]() {
|
||||
while(!_stop) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_access);
|
||||
if(_tasks.empty()) {
|
||||
_cond.wait_for(lock, std::chrono::duration<int, std::milli>(5));
|
||||
continue;
|
||||
}
|
||||
task = std::move(_tasks.front());
|
||||
_tasks.pop_front();
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(_frameIntervalInMilliseconds));
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
});
|
||||
}
|
||||
~RenderLoop() {
|
||||
_stop = true;
|
||||
_t->join();
|
||||
}
|
||||
|
||||
void setRendering(bool rendering) {
|
||||
_rendering = rendering;
|
||||
}
|
||||
|
||||
template<class Rt>
|
||||
auto add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> {
|
||||
std::unique_lock<std::mutex> lock(_access);
|
||||
auto ret = pt.get_future();
|
||||
_tasks.push_back([pt=std::make_shared<std::packaged_task<Rt()>>(std::move(pt))]{ (*pt)();});
|
||||
_cond.notify_one();
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
bool _stop = false;
|
||||
bool _rendering = false;
|
||||
int _frameIntervalInMilliseconds = 1000 / 60;
|
||||
std::mutex _access;
|
||||
FilamentViewer* _viewer = nullptr;
|
||||
std::thread* _t = nullptr;
|
||||
std::condition_variable _cond;
|
||||
std::deque<std::function<void()>> _tasks;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "PolyvoxFilamentApi.h"
|
||||
|
||||
static RenderLoop* _rl;
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer_ffi(const void* context, const ResourceLoaderWrapper* const loader) {
|
||||
if(!_rl) {
|
||||
_rl = new RenderLoop();
|
||||
}
|
||||
std::packaged_task<const void*()> lambda([&]() mutable {
|
||||
return (const void*) new FilamentViewer(context, loader);
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
fut.wait();
|
||||
return fut.get();
|
||||
}
|
||||
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT bool set_rendering(bool rendering) {
|
||||
if(!_rl) {
|
||||
return false;
|
||||
}
|
||||
_rl->setRendering(rendering);
|
||||
return true;
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void render_ffi(void* const viewer) {
|
||||
std::packaged_task<void()> lambda([&]() mutable {
|
||||
render(viewer, 0);
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
fut.wait();
|
||||
}
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(const void* const viewer, const float r, const float g, const float b, const float a) {
|
||||
std::packaged_task<void()> lambda([&]() mutable {
|
||||
set_background_color(viewer, r, g,b, a);
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
fut.wait();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user