refactoring

This commit is contained in:
Nick Fisher
2025-03-18 16:26:47 +08:00
parent 07b80071a4
commit 77fe40848b
41 changed files with 1900 additions and 2342 deletions

View File

@@ -1,176 +0,0 @@
#pragma once
#include <filament/Camera.h>
#include <filament/Frustum.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <filament/LightManager.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <camutils/Manipulator.h>
#include <utils/NameComponentManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "ResourceBuffer.hpp"
#include "scene/SceneManager.hpp"
#include "ThreadPool.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
using namespace gltfio;
using namespace camutils;
class FilamentViewer
{
typedef int32_t EntityId;
public:
FilamentViewer(const void *context, const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
View* createView();
View* getViewAt(int index);
void loadSkybox(const char *const skyboxUri);
void removeSkybox();
void loadIbl(const char *const iblUri, float intensity);
void removeIbl();
void rotateIbl(const math::mat3f &matrix);
void createIbl(float r, float g, float b, float intensity);
void render(
uint64_t frameTimeInNanos
);
void setFrameInterval(float interval);
void setMainCamera(View *view);
EntityId getMainCamera();
float getCameraFov(bool horizontal);
void setCameraFov(double fovDegrees, bool horizontal);
SwapChain* createSwapChain(const void *surface);
SwapChain* createSwapChain(uint32_t width, uint32_t height);
void destroySwapChain(SwapChain* swapChain);
RenderTarget* createRenderTarget(intptr_t colorTextureId, intptr_t depthTextureId, uint32_t width, uint32_t height);
void destroyRenderTarget(RenderTarget* renderTarget);
Renderer *getRenderer();
std::map<SwapChain*, std::vector<View*>> _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);
void clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp, uint32_t width, uint32_t height);
Engine* getEngine() {
return _engine;
}
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)());
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)());
SceneManager *const getSceneManager()
{
return (SceneManager *const)_sceneManager;
}
SwapChain* getSwapChainAt(int index) {
if(index < _swapChains.size()) {
return _swapChains[index];
}
Log("Error: index %d is greater than available swapchains", index);
return nullptr;
}
private:
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
Scene *_scene = nullptr;
Engine *_engine = nullptr;
thermion::ThreadPool *_tp = nullptr;
Renderer *_renderer = nullptr;
SceneManager *_sceneManager = nullptr;
std::vector<RenderTarget*> _renderTargets;
std::vector<SwapChain*> _swapChains;
std::vector<View*> _views;
std::mutex _renderMutex; // mutex to ensure thread safety when removing assets
Texture *_skyboxTexture = nullptr;
Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr;
IndirectLight *_indirectLight = nullptr;
float _frameInterval = 1000.0 / 60.0;
// Camera properties
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
// background image properties
uint32_t _imageHeight = 0;
uint32_t _imageWidth = 0;
filament::math::mat4f _imageScale;
Texture *_imageTexture = nullptr;
Texture *_dummyImageTexture = nullptr;
utils::Entity _imageEntity;
VertexBuffer *_imageVb = nullptr;
IndexBuffer *_imageIb = nullptr;
Material *_imageMaterial = nullptr;
TextureSampler _imageSampler;
void loadKtx2Texture(std::string path, ResourceBuffer data);
void loadKtxTexture(std::string path, ResourceBuffer data);
void loadPngTexture(std::string path, ResourceBuffer data);
void loadTextureFromPath(std::string path);
void savePng(void *data, size_t size, int frameNumber);
void createBackgroundImage();
time_point_t _fpsCounterStartTime = std::chrono::high_resolution_clock::now();
std::mutex _imageMutex;
double _cumulativeAnimationUpdateTime = 0;
int _frameCount = 0;
int _skippedFrames = 0;
};
struct FrameCallbackData
{
FilamentViewer *viewer;
uint32_t frameNumber;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <filament/Engine.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "scene/SceneManager.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
class RenderTicker
{
public:
RenderTicker(filament::Renderer renderer, thermion::SceneManager sceneManager) : mRenderer(renderer), mSceneManager(sceneManager) { }
~RenderTicker();
void render(
uint64_t frameTimeInNanos
);
void setRenderable(SwapChain *swapChain, View **view, uint8_t numViews);
private:
std::mutex mMutex;
Renderer *mRenderer = nullptr;
SceneManager *mSceneManager = nullptr;
std::vector<SwapChain*> mSwapChains;
std::map<SwapChain*, std::vector<View*>> mRenderable;
};
}

View File

@@ -8,7 +8,7 @@ extern "C"
#include <stdint.h>
#include "APIExport.h"
typedef TRenderTicker TRenderTicker;
typedef int32_t EntityId;
typedef struct TCamera TCamera;
typedef struct TEngine TEngine;
@@ -110,6 +110,11 @@ extern "C"
double z;
} double3;
typedef struct {
double3 col1;
double3 col2;
double3 col3;
} double3x3;
typedef struct {
double x;

View File

@@ -21,10 +21,11 @@ enum TBackend {
BACKEND_NOOP = 4, //!< Selects the no-op driver for testing purposes.
};
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend);
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend, void* platform, void* sharedContext, uint8_t stereoscopicEyeCount, bool disableHandleUseAfterFreeCheck);
EMSCRIPTEN_KEEPALIVE TRenderer *Engine_createRenderer(TEngine *tEngine);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags);
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain);
EMSCRIPTEN_KEEPALIVE TCamera *Engine_createCamera(TEngine* tEngine);
EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine);

View File

@@ -23,6 +23,7 @@ EMSCRIPTEN_KEEPALIVE TFilamentAsset *GltfAssetLoader_load(
uint8_t numInstances
);
EMSCRIPTEN_KEEPALIVE TMaterialInstance *GltfAssetLoader_getMaterialInstance(TRenderableManager *tRenderableManager, TFilamentAsset *tAsset);
EMSCRIPTEN_KEEPALIVE TMaterialProvider *GltfAssetLoader_getMaterialProvider(TGltfAssetLoader *tAssetLoader);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "TTexture.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE void IndirectLight_setRotation(TIndirectLight TIndirectLight, double3x3 rotation);
#ifdef __cplusplus
}
#endif

View File

@@ -21,6 +21,11 @@ EMSCRIPTEN_KEEPALIVE TRenderTarget *RenderTarget_create(
TTexture *depth
);
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroy(
TEngine *tEngine,
TRenderTarget *tRenderTarget
);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,17 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TRenderTicker *RenderTicker_create(TRenderer *tRenderer, TSceneManager *tSceneManager);
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos);
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tFilamentRender, TSwapChain *swapChain, TView **views, uint8_t numViews);
#ifdef __cplusplus
}
#endif

View File

@@ -23,6 +23,13 @@ EMSCRIPTEN_KEEPALIVE void Renderer_readPixels(
TPixelDataType tPixelDataType,
uint8_t *out
);
EMSCRIPTEN_KEEPALIVE void Renderer_setFrameInterval(
TRenderer *tRenderer,
float headRoomRatio,
float scaleRate,
uint8_t history,
uint8_t interval
);

View File

@@ -1,85 +0,0 @@
#ifndef _FLUTTER_FILAMENT_API_H
#define _FLUTTER_FILAMENT_API_H
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath);
EMSCRIPTEN_KEEPALIVE TRenderer *Viewer_getRenderer(TViewer *tViewer);
EMSCRIPTEN_KEEPALIVE void Viewer_destroy(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *viewer, intptr_t colorTextureId, intptr_t depthTextureId, 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);
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *viewer, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *viewer, TSwapChain* swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_render(
TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
TRenderTarget *renderTarget,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE TView* Viewer_createView(TViewer *viewer);
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_setViewRenderable(TViewer *viewer, TSwapChain *swapChain, TView* view, bool renderable);
// Engine
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer* viewer);
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight);
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE EntityId Viewer_getMainCamera(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkybox(TViewer *viewer, const char *skyboxPath);
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkybox(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity);
EMSCRIPTEN_KEEPALIVE void Viewer_removeIbl(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity);
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix);
EMSCRIPTEN_KEEPALIVE void set_frame_interval(TViewer *viewer, float interval);
EMSCRIPTEN_KEEPALIVE void queue_relative_position_update_world_axis(TSceneManager *sceneManager, EntityId entity, float viewportX, float viewportY, float x, float y, float z);
EMSCRIPTEN_KEEPALIVE void queue_position_update_from_viewport_coords(TSceneManager *sceneManager, TView *view, EntityId entity, float viewportX, float viewportY);
EMSCRIPTEN_KEEPALIVE void ios_dummy();
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr);
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
EMSCRIPTEN_KEEPALIVE void remove_collision_component(TSceneManager *sceneManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE void test_collisions(TSceneManager *sceneManager, EntityId entity);
EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(TSceneManager *sceneManager, TView *view, EntityId entity);
EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(TSceneManager *sceneManager, TView *view, EntityId entity, float *minX, float *minY, float *maxX, float *maxY);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,7 +1,5 @@
#ifndef _DART_FILAMENT_FFI_API_H
#define _DART_FILAMENT_FFI_API_H
#pragma once
#include "ThermionDartApi.h"
#include "TView.h"
#include "TTexture.h"
#include "TMaterialProvider.h"
@@ -13,49 +11,30 @@ namespace thermion
{
#endif
///
/// This header replicates most of the methods in ThermionDartApi.h.
/// It represents the interface for:
/// - invoking those methods that must be called on the main Filament engine thread
/// - setting up a render loop
///
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner);
EMSCRIPTEN_KEEPALIVE void RenderLoop_create();
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy();
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context,
void *const platform,
const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *viewer));
EMSCRIPTEN_KEEPALIVE void Viewer_createViewRenderThread(TViewer *viewer, void (*onComplete)(TView *tView));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyOnRenderThread(TViewer *viewer);
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, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain, TRenderTarget *renderTarget, uint8_t *out, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeIblRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_createRenderTargetRenderThread(TViewer *viewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height, void (*onComplete)(TRenderTarget *));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTargetRenderThread(TViewer *viewer, TRenderTarget *tRenderTarget, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkyboxRenderThread(TViewer *viewer, const char *skyboxPath, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkyboxRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos,);
// EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(TRenderLoop* tRenderLoop, void (*task)());
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(TBackend backend, void (*onComplete)(TEngine *));
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(
TBackend backend,
void* platform,
void* sharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck,
void (*onComplete)(TEngine *)
);
EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *));
EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *));
EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *));
EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, void (*onComplete)());
@@ -98,13 +77,6 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, void (*callback)());
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(TViewer *viewer, bool rendering, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_frame_interval_render_thread(TViewer *viewer, float frameInterval);
EMSCRIPTEN_KEEPALIVE void set_background_color_render_thread(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *));
@@ -323,7 +295,7 @@ namespace thermion
int boneIndex,
const float *const transform,
void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(TViewer *viewer, bool enabled);
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)());
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *));
@@ -345,4 +317,3 @@ namespace thermion
}
#endif
#endif // _DART_FILAMENT_FFI_API_H

View File

@@ -0,0 +1,87 @@
#pragma once
#include <chrono>
#include <condition_variable>
#include <deque>
#include <future>
#include <mutex>
#include <thread>
#include "RenderTicker.hpp"
namespace thermion {
/**
* @brief A render loop implementation that manages rendering on a separate thread.
*
* This class handles frame rendering requests, viewer creation, and maintains
* a task queue for rendering operations.
*/
class RenderLoop {
public:
/**
* @brief Constructs a new RenderLoop and starts the render thread.
*/
explicit RenderLoop();
/**
* @brief Destroys the RenderLoop and stops the render thread.
*/
~RenderLoop();
/**
* @brief Requests a frame to be rendered.
*
* @param callback Callback function to be called after rendering completes
*/
void requestFrame(void (*callback)());
/**
* @brief Sets the render ticker used.
*/
void setRenderTicker(RenderTicker *renderTicker) {
mRenderTicker = renderTicker;
}
/**
* @brief Adds a task to the render thread's task queue.
*
* @param pt The packaged task to be executed
* @return std::future<Rt> Future for the task result
*/
template <class Rt>
auto add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt>;
private:
/**
* @brief Main iteration of the render loop.
*/
void iter();
void (*_requestFrameRenderCallback)() = nullptr;
bool _stop = false;
std::mutex _mutex;
std::mutex _taskMutex;
std::condition_variable _cv;
std::deque<std::function<void()>> _tasks;
std::chrono::high_resolution_clock::time_point _lastFrameTime;
int _frameCount = 0;
float _accumulatedTime = 0.0f;
float _fps = 0.0f;
std::thread* t = nullptr;
RenderTicker* mRenderTicker = nullptr;
};
// Template implementation
template <class Rt>
auto RenderLoop::add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> {
std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cv.notify_one();
return ret;
}
} // namespace thermion