enable setting output directory for recording

This commit is contained in:
Nick Fisher
2023-11-20 22:59:50 +08:00
parent 5c1be3d047
commit 660395a40e
8 changed files with 83 additions and 25 deletions

View File

@@ -46,6 +46,7 @@ typedef int32_t EntityId;
namespace polyvox namespace polyvox
{ {
enum ToneMapping enum ToneMapping
{ {
ACES, ACES,
@@ -124,6 +125,7 @@ namespace polyvox
void setPostProcessing(bool enabled); void setPostProcessing(bool enabled);
void setRecording(bool recording); void setRecording(bool recording);
void setRecordingOutputDirectory(const char* path);
AssetManager *const getAssetManager() AssetManager *const getAssetManager()
@@ -156,11 +158,11 @@ namespace polyvox
Skybox *_skybox = nullptr; Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr; Texture *_iblTexture = nullptr;
IndirectLight *_indirectLight = nullptr; IndirectLight *_indirectLight = nullptr;
bool _recomputeAabb = false; bool _recomputeAabb = false;
bool _actualSize = false; bool _actualSize = false;
float _frameInterval = 1000.0 / 60.0;
// Camera properties // Camera properties
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View. Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
float _cameraFocalLength = 28.0f; float _cameraFocalLength = 28.0f;
@@ -192,13 +194,18 @@ namespace polyvox
void loadKtxTexture(string path, ResourceBuffer data); void loadKtxTexture(string path, ResourceBuffer data);
void loadPngTexture(string path, ResourceBuffer data); void loadPngTexture(string path, ResourceBuffer data);
void loadTextureFromPath(string path); void loadTextureFromPath(string path);
void savePng(void* data, size_t size); void savePng(void* data, size_t size, int frameNumber);
int _frameNumber = 0; time_point_t _startTime = std::chrono::high_resolution_clock::now();
uint32_t _lastFrameTimeInNanos;
bool _recording = false; bool _recording = false;
std::string _recordingOutputDirectory = std::string("/tmp");
std::mutex _recordingMutex;
};
struct FrameCallbackData {
FilamentViewer* viewer;
uint32_t frameNumber;
}; };
} }

View File

@@ -170,6 +170,7 @@ extern "C"
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId); FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId);
FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name); FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name);
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording); FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording);
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory);
FLUTTER_PLUGIN_EXPORT void ios_dummy(); FLUTTER_PLUGIN_EXPORT void ios_dummy();
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr); FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -54,7 +54,7 @@ public:
private: private:
void add_worker() { void add_worker() {
std::thread t([this]() { std::thread t([this]() {
while(!stop) { while(!stop || tasks.size() > 0) {
std::function<void()> task; std::function<void()> task;
{ {
std::unique_lock<std::mutex> lock(access); std::unique_lock<std::mutex> lock(access);

View File

@@ -123,8 +123,6 @@ namespace polyvox
: _resourceLoaderWrapper(resourceLoaderWrapper) : _resourceLoaderWrapper(resourceLoaderWrapper)
{ {
_tp = new flutter_filament::ThreadPool(1);
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null"); ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
@@ -139,12 +137,9 @@ namespace polyvox
_renderer = _engine->createRenderer(); _renderer = _engine->createRenderer();
float fr = 60.0f; _frameInterval = 1000.0f / 60.0f;
_renderer->setDisplayInfo({.refreshRate = fr});
Renderer::FrameRateOptions fro; setFrameInterval(_frameInterval);
fro.interval = 1 / fr;
_renderer->setFrameRateOptions(fro);
_scene = _engine->createScene(); _scene = _engine->createScene();
@@ -307,6 +302,7 @@ namespace polyvox
void FilamentViewer::setFrameInterval(float frameInterval) void FilamentViewer::setFrameInterval(float frameInterval)
{ {
_frameInterval = frameInterval;
Renderer::FrameRateOptions fro; Renderer::FrameRateOptions fro;
fro.interval = frameInterval; fro.interval = frameInterval;
_renderer->setFrameRateOptions(fro); _renderer->setFrameRateOptions(fro);
@@ -1020,13 +1016,23 @@ namespace polyvox
size_t pixelBufferSize = vp.width * vp.height * 4; size_t pixelBufferSize = vp.width * vp.height * 4;
auto* pixelBuffer = new uint8_t[pixelBufferSize]; auto* pixelBuffer = new uint8_t[pixelBufferSize];
auto callback = [](void *buf, size_t size, void *data) { auto callback = [](void *buf, size_t size, void *data) {
((FilamentViewer*)data)->savePng(buf, size); auto frameCallbackData = (FrameCallbackData*)data;
auto viewer = (FilamentViewer*)frameCallbackData->viewer;
viewer->savePng(buf, size, frameCallbackData->frameNumber);
delete frameCallbackData;
}; };
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - _startTime).count());
auto frameNumber = uint32_t(floor(elapsed / _frameInterval));
auto userData = new FrameCallbackData { this, frameNumber };
auto pbd = Texture::PixelBufferDescriptor( auto pbd = Texture::PixelBufferDescriptor(
pixelBuffer, pixelBufferSize, pixelBuffer, pixelBufferSize,
Texture::Format::RGBA, Texture::Format::RGBA,
Texture::Type::UBYTE, nullptr, callback, this); Texture::Type::UBYTE, nullptr, callback, userData);
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
} }
@@ -1039,23 +1045,28 @@ namespace polyvox
} }
} }
void FilamentViewer::savePng(void* buf, size_t size) { void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) {
std::lock_guard lock(_recordingMutex);
std::packaged_task<void()> lambda([=]() mutable { if(!_recording) {
delete[] static_cast<uint8_t*>(buf);
return;
}
Viewport const &vp = _view->getViewport(); Viewport const &vp = _view->getViewport();
std::packaged_task<void()> lambda([=]() mutable {
int digits = 6; int digits = 6;
std::ostringstream stringStream; std::ostringstream stringStream;
stringStream << "/tmp/output_"; stringStream << _recordingOutputDirectory << "/output_";
stringStream << std::setfill('0') << std::setw(digits); stringStream << std::setfill('0') << std::setw(digits);
stringStream << std::to_string(_frameNumber); stringStream << std::to_string(frameNumber);
stringStream << ".png"; stringStream << ".png";
std::string filename = stringStream.str(); std::string filename = stringStream.str();
ofstream wf(filename, ios::out | ios::binary); ofstream wf(filename, ios::out | ios::binary);
// Log("%d %d %d", vp.width, vp.height, size);
LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4, LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
static_cast<uint8_t*>(buf))); static_cast<uint8_t*>(buf)));
@@ -1064,7 +1075,6 @@ namespace polyvox
); );
delete[] static_cast<uint8_t*>(buf); delete[] static_cast<uint8_t*>(buf);
_frameNumber++;
if(!result) { if(!result) {
Log("Failed to encode"); Log("Failed to encode");
@@ -1074,12 +1084,28 @@ namespace polyvox
if(!wf.good()) { if(!wf.good()) {
Log("Write failed!"); Log("Write failed!");
} }
}); });
_tp->add_task(lambda); _tp->add_task(lambda);
} }
void FilamentViewer::setRecordingOutputDirectory(const char* path) {
_recordingOutputDirectory = std::string(path);
auto outputDirAsPath = std::filesystem::path(path);
if(!std::filesystem::is_directory(outputDirAsPath)) {
std::filesystem::create_directories(outputDirAsPath);
}
}
void FilamentViewer::setRecording(bool recording) { void FilamentViewer::setRecording(bool recording) {
std::lock_guard lock(_recordingMutex);
this->_recording = recording; this->_recording = recording;
if(recording) {
_tp = new flutter_filament::ThreadPool(8);
_startTime = std::chrono::high_resolution_clock::now();
} else {
delete _tp;
}
} }
void FilamentViewer::updateViewportAndCameraProjection( void FilamentViewer::updateViewportAndCameraProjection(

View File

@@ -487,6 +487,10 @@ extern "C"
((FilamentViewer*)viewer)->setRecording(recording); ((FilamentViewer*)viewer)->setRecording(recording);
} }
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory) {
((FilamentViewer*)viewer)->setRecordingOutputDirectory(outputDirectory);
}
FLUTTER_PLUGIN_EXPORT void ios_dummy() FLUTTER_PLUGIN_EXPORT void ios_dummy()
{ {
Log("Dummy called"); Log("Dummy called");

View File

@@ -498,4 +498,9 @@ abstract class FilamentController {
/// This will impact performance; handle with care. /// This will impact performance; handle with care.
/// ///
Future setRecording(bool recording); Future setRecording(bool recording);
///
/// Sets the output directory where recorded PNGs will be placed.
///
Future setRecordingOutputDirectory(String outputDirectory);
} }

View File

@@ -1212,4 +1212,11 @@ class FilamentControllerFFI extends FilamentController {
Future setRecording(bool recording) async { Future setRecording(bool recording) async {
set_recording(_viewer!, recording); set_recording(_viewer!, recording);
} }
@override
Future setRecordingOutputDirectory(String outputDir) async {
var pathPtr = outputDir.toNativeUtf8(allocator: calloc);
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
calloc.free(pathPtr);
}
} }

View File

@@ -753,6 +753,14 @@ external void set_recording(
bool recording, bool recording,
); );
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>)>(
symbol: 'set_recording_output_directory',
assetId: 'flutter_filament_plugin')
external void set_recording_output_directory(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.Char> outputDirectory,
);
@ffi.Native<ffi.Void Function()>( @ffi.Native<ffi.Void Function()>(
symbol: 'ios_dummy', assetId: 'flutter_filament_plugin') symbol: 'ios_dummy', assetId: 'flutter_filament_plugin')
external void ios_dummy(); external void ios_dummy();