forked from oomer/bellatui
first working EFSW file monitoring, defaults to ., tested on macos
This commit is contained in:
parent
c2087251e2
commit
7f478c7d6b
29
README.md
29
README.md
@ -1,6 +1,6 @@
|
||||
# bellatui
|
||||
|
||||
Command line bella renderer with encrypted networking and text user interface.
|
||||
Command line bella renderer with encrypted networking ,text user interface and file monitoring.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -54,6 +54,7 @@ workdir/
|
||||
├── bella_engine_sdk/
|
||||
├── libzmq/
|
||||
├── cppzmq/
|
||||
├── efsw/
|
||||
├── belatui/
|
||||
|
||||
( additional Windows package manager dependency )
|
||||
@ -95,9 +96,14 @@ cd build
|
||||
make -j4
|
||||
cd ../..
|
||||
git clone https://github.com/zeromq/cppzmq
|
||||
git clone https://github.com/SpartanJ/efsw.git
|
||||
mkdir -p efsw/build
|
||||
cd efsw/build
|
||||
/Applications/CMake.app/Contents/bin/cmake ..
|
||||
cd ../..
|
||||
git clone https://github.com/oomer/bellatui.git
|
||||
cd bellatui
|
||||
make -j4
|
||||
make all -j4
|
||||
```
|
||||
|
||||
## Linux
|
||||
@ -152,6 +158,12 @@ cd cppzmq
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cd ../..
|
||||
git clone https://github.com/SpartanJ/efsw.git
|
||||
mkdir -p efsw/build
|
||||
cd efsw/build
|
||||
cmake ..
|
||||
make -j4
|
||||
```
|
||||
|
||||
### compiling bellatui
|
||||
@ -159,7 +171,7 @@ cmake ..
|
||||
cd ../..
|
||||
git clone https://github.com/oomer/bellatui.git
|
||||
cd bellatui
|
||||
make
|
||||
make all -j4
|
||||
```
|
||||
|
||||
# Windows
|
||||
@ -173,18 +185,17 @@ Get bella_engine_sdk
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
vcpkg install zeromq[sodium]:x64-windows
|
||||
cd ..
|
||||
git clone https://github.com/SpartanJ/efsw.git
|
||||
mkdir -p efsw/build
|
||||
cd efsw/build
|
||||
cmake ..
|
||||
|
||||
git clone https://github.com/oomer/bellatui.git
|
||||
|
||||
msbuild bellatui.vcxproj /p:Configuration=release /p:Platform=x64 /p:PlatformToolset=v143
|
||||
```
|
||||
|
||||
Build directories expected to be relative
|
||||
```
|
||||
--folder
|
||||
--bella_engine_sdk
|
||||
--bellatui
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
473
bellatui.cpp
473
bellatui.cpp
@ -37,21 +37,263 @@
|
||||
#include <sys/wait.h> // For waitpid
|
||||
#endif
|
||||
|
||||
#include "bella_sdk/bella_engine.h"
|
||||
#include "dl_core/dl_fs.h"
|
||||
#include <efsw/FileSystem.hpp> // For file watching
|
||||
#include <efsw/System.hpp> // For file watching
|
||||
#include <efsw/efsw.hpp> // For file watching
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
#include "../bella_engine_sdk/src/bella_sdk/bella_engine.h" // For rendering
|
||||
#include "../bella_engine_sdk/src/dl_core/dl_fs.h" // For rendering
|
||||
using namespace dl;
|
||||
using namespace dl::bella_sdk;
|
||||
|
||||
/// A class that manages a queue of files to render with both FIFO order and fast lookups
|
||||
class RenderQueue {
|
||||
public:
|
||||
// Default constructor
|
||||
RenderQueue() = default;
|
||||
|
||||
// Move constructor
|
||||
RenderQueue(RenderQueue&& other) noexcept {
|
||||
std::lock_guard<std::mutex> lock(other.mutex);
|
||||
pathVector = std::move(other.pathVector);
|
||||
pathMap = std::move(other.pathMap);
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
RenderQueue& operator=(RenderQueue&& other) noexcept {
|
||||
if (this != &other) {
|
||||
std::lock_guard<std::mutex> lock1(mutex);
|
||||
std::lock_guard<std::mutex> lock2(other.mutex);
|
||||
pathVector = std::move(other.pathVector);
|
||||
pathMap = std::move(other.pathMap);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Delete copy operations since mutexes can't be copied
|
||||
RenderQueue(const RenderQueue&) = delete;
|
||||
RenderQueue& operator=(const RenderQueue&) = delete;
|
||||
|
||||
// Add a file to the queue if it's not already there
|
||||
bool push(const dl::String& path) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (pathMap.find(path) == pathMap.end()) {
|
||||
pathVector.push_back(path);
|
||||
pathMap[path] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the next file to render (FIFO order)
|
||||
bool pop(dl::String& outPath) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (!pathVector.empty()) {
|
||||
outPath = pathVector.front();
|
||||
pathVector.erase(pathVector.begin());
|
||||
pathMap.erase(outPath);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove a specific file by name
|
||||
bool remove(const dl::String& path) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (pathMap.find(path) != pathMap.end()) {
|
||||
// Remove from vector using erase-remove idiom
|
||||
pathVector.erase(
|
||||
std::remove(pathVector.begin(), pathVector.end(), path),
|
||||
pathVector.end()
|
||||
);
|
||||
// Remove from map
|
||||
pathMap.erase(path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if a file exists in the queue
|
||||
bool contains(const dl::String& path) const {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return pathMap.find(path) != pathMap.end();
|
||||
}
|
||||
|
||||
// Get the number of files in the queue
|
||||
size_t size() const {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return pathVector.size();
|
||||
}
|
||||
|
||||
// Check if the queue is empty
|
||||
bool empty() const {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return pathVector.empty();
|
||||
}
|
||||
|
||||
// Clear all files from the queue
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
pathVector.clear();
|
||||
pathMap.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<dl::String> pathVector; // Maintains FIFO order
|
||||
std::map<dl::String, bool> pathMap; // Enables fast lookups
|
||||
mutable std::mutex mutex; // Thread safety
|
||||
};
|
||||
|
||||
std::atomic<bool> active_render(false);
|
||||
RenderQueue renderQueue; // Replace the old vector and map with our new class
|
||||
std::mutex renderQueueMutex; // Add mutex for thread safety
|
||||
std::vector<dl::String> renderDelete; // This is the efsw queue for when we delete a file
|
||||
std::mutex renderDeleteMutex; // Add mutex for thread safety
|
||||
|
||||
dl::String currentRender;
|
||||
std::mutex currentRenderMutex; // Add mutex for thread safety
|
||||
|
||||
// Queues for incoming files from the efsw watcher
|
||||
RenderQueue incomingDeleteQueue;
|
||||
RenderQueue incomingRenderQueue;
|
||||
std::mutex incomingDeleteQueueMutex; // Add mutex for thread safety
|
||||
std::mutex incomingRenderQueueMutex; // Add mutex for thread safety
|
||||
|
||||
|
||||
/// Processes a file action
|
||||
class UpdateListener : public efsw::FileWatchListener {
|
||||
public:
|
||||
UpdateListener() : should_stop_(false) {}
|
||||
|
||||
void stop() {
|
||||
should_stop_ = true;
|
||||
}
|
||||
|
||||
std::string getActionName( efsw::Action action ) {
|
||||
switch ( action ) {
|
||||
case efsw::Actions::Add:
|
||||
return "Add";
|
||||
case efsw::Actions::Modified:
|
||||
return "Modified";
|
||||
case efsw::Actions::Delete:
|
||||
return "Delete";
|
||||
case efsw::Actions::Moved:
|
||||
return "Moved";
|
||||
default:
|
||||
return "Bad Action";
|
||||
}
|
||||
}
|
||||
|
||||
void handleFileAction( efsw::WatchID watchid, const std::string& dir,
|
||||
const std::string& filename, efsw::Action action,
|
||||
std::string oldFilename = "" ) override {
|
||||
if (should_stop_) return; // Early exit if we're stopping
|
||||
|
||||
std::string actionName = getActionName( action );
|
||||
/*std::cout << "Watch ID " << watchid << " DIR ("
|
||||
<< dir + ") FILE (" +
|
||||
( oldFilename.empty() ? "" : "from file " + oldFilename + " to " ) +
|
||||
filename + ") has event "
|
||||
<< actionName << std::endl;*/
|
||||
if (actionName == "Delete") {
|
||||
if (active_render || !incomingRenderQueue.empty()) {
|
||||
dl::String belPath = (dir + filename).c_str();
|
||||
if (belPath.endsWith(".bsz")) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(incomingDeleteQueueMutex);
|
||||
if (!incomingDeleteQueue.contains(belPath)) {
|
||||
incomingDeleteQueue.push(belPath);
|
||||
std::cout << "\n==" << "STOP RENDER: " << belPath.buf() << "\n==" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionName == "Add" || actionName == "Modified") {
|
||||
dl::String belPath = (dir + filename).c_str();
|
||||
if (should_stop_) return; // Check again before starting render
|
||||
if (belPath.endsWith(".bsz")) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(incomingRenderQueueMutex);
|
||||
if (!incomingRenderQueue.contains(belPath)) {
|
||||
incomingRenderQueue.push(belPath);
|
||||
std::cout << "\n==" << "RENDER QUEUED: " << belPath.buf() << "\n==" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::atomic<bool> should_stop_; // ctrl-c was not working, so we use this to stop the thread
|
||||
};
|
||||
|
||||
// Global state variables
|
||||
std::string initializeGlobalLicense(); // Function to return license text
|
||||
std::string initializeGlobalThirdPartyLicences(); // Function to return third-party licenses
|
||||
std::atomic<bool> connection_state (false); // Tracks if client/server are connected
|
||||
std::atomic<bool> abort_state (false); // Used to signal program termination
|
||||
std::atomic<bool> server (false); // Indicates if running in server mode
|
||||
UpdateListener* global_ul = nullptr; // Global pointer to UpdateListener
|
||||
|
||||
// Function declarations
|
||||
std::string get_pubkey_from_srv(std::string server_address, uint16_t publickey_port); // Gets server's public key for encryption
|
||||
|
||||
|
||||
bool STOP = false;
|
||||
|
||||
void sigend( int ) {
|
||||
std::cout << std::endl << "Bye bye" << std::endl;
|
||||
STOP = true;
|
||||
if (global_ul) { // Use the global pointer
|
||||
global_ul->stop();
|
||||
}
|
||||
// Give a short time for cleanup
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
exit(0); // Force exit after cleanup
|
||||
}
|
||||
|
||||
efsw::WatchID handleWatchID( efsw::WatchID watchid ) {
|
||||
switch ( watchid ) {
|
||||
case efsw::Errors::FileNotFound:
|
||||
case efsw::Errors::FileRepeated:
|
||||
case efsw::Errors::FileOutOfScope:
|
||||
case efsw::Errors::FileRemote:
|
||||
case efsw::Errors::WatcherFailed:
|
||||
case efsw::Errors::Unspecified: {
|
||||
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
std::cout << "Added WatchID: " << watchid << std::endl;
|
||||
}
|
||||
}
|
||||
return watchid;
|
||||
}
|
||||
|
||||
|
||||
static int s_logCtx = 0;
|
||||
static void log(void* /*ctx*/, LogType type, const char* msg)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LogType_Info:
|
||||
DL_PRINT("[INFO] %s\n", msg);
|
||||
break;
|
||||
case LogType_Warning:
|
||||
DL_PRINT("[WARN] %s\n", msg);
|
||||
break;
|
||||
case LogType_Error:
|
||||
DL_PRINT("[ERROR] %s\n", msg);
|
||||
break;
|
||||
case LogType_Custom:
|
||||
DL_PRINT("%s\n", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Main client communication thread
|
||||
void client_thread( std::string server_pkey,
|
||||
std::string client_pkey,
|
||||
@ -59,12 +301,6 @@ void client_thread( std::string server_pkey,
|
||||
std::string server_address,
|
||||
uint16_t command_port);
|
||||
|
||||
// Main server thread that handles rendering and client requests
|
||||
void server_thread( std::string server_skey,
|
||||
uint16_t command_port,
|
||||
bool test_render,
|
||||
Engine engine);
|
||||
|
||||
// Utility function to open files with system default program
|
||||
void openFileWithDefaultProgram(const std::string& filePath);
|
||||
|
||||
@ -89,14 +325,15 @@ public:
|
||||
// Called when a rendering pass starts
|
||||
void onStarted(String pass) override
|
||||
{
|
||||
std::cout << "Started pass " << pass.buf() << std::endl;
|
||||
logInfo("Started pass %s", pass.buf());
|
||||
}
|
||||
|
||||
// Called to update the current status of rendering
|
||||
void onStatus(String pass, String status) override
|
||||
{
|
||||
logInfo("%s [%s]", status.buf(), pass.buf());
|
||||
}
|
||||
//void onStatus(String pass, String status) override
|
||||
//{
|
||||
// logInfo("%s [%s]", status.buf(), pass.buf());
|
||||
//}
|
||||
|
||||
// Called to update rendering progress (percentage, time remaining, etc)
|
||||
void onProgress(String pass, Progress progress) override
|
||||
@ -106,6 +343,11 @@ public:
|
||||
logInfo("%s [%s]", progress.toString().buf(), pass.buf());
|
||||
}
|
||||
|
||||
void onImage(String pass, Image image) override
|
||||
{
|
||||
logInfo("We got an image %d x %d.", (int)image.width(), (int)image.height());
|
||||
}
|
||||
|
||||
// Called when an error occurs during rendering
|
||||
void onError(String pass, String msg) override
|
||||
{
|
||||
@ -116,6 +358,7 @@ public:
|
||||
void onStopped(String pass) override
|
||||
{
|
||||
logInfo("Stopped %s", pass.buf());
|
||||
active_render = false;
|
||||
}
|
||||
|
||||
// Returns the current progress as a string
|
||||
@ -143,6 +386,16 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
// Main server thread that handles client requests
|
||||
void server_thread( std::string server_skey,
|
||||
uint16_t command_port,
|
||||
bool test_render,
|
||||
Engine& engine,
|
||||
MyEngineObserver& engineObserver);
|
||||
|
||||
void render_thread( Engine& engine,
|
||||
MyEngineObserver& engineObserver);
|
||||
|
||||
/*
|
||||
* Heartbeat Monitoring System
|
||||
*
|
||||
@ -245,6 +498,41 @@ void heartbeat_thread( std::string server_pkey,
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
void file_watcher_thread(const std::string& watch_path = "") {
|
||||
bool commonTest = true;
|
||||
bool useGeneric = false;
|
||||
|
||||
global_ul = new UpdateListener();
|
||||
efsw::FileWatcher fileWatcher(useGeneric);
|
||||
|
||||
fileWatcher.followSymlinks(false);
|
||||
fileWatcher.allowOutOfScopeLinks(false);
|
||||
|
||||
if (!watch_path.empty() && dl::fs::exists(watch_path.data())) {
|
||||
commonTest = false;
|
||||
if (fileWatcher.addWatch(watch_path, global_ul, true) > 0) {
|
||||
fileWatcher.watch();
|
||||
std::cout << "Watching directory: " << watch_path << std::endl;
|
||||
} else {
|
||||
std::cout << "Error trying to watch directory: " << watch_path << std::endl;
|
||||
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
|
||||
return;
|
||||
}
|
||||
} else if (commonTest) {
|
||||
std::string CurPath(efsw::System::getProcessPath());
|
||||
std::cout << "CurPath: " << CurPath.c_str() << std::endl;
|
||||
fileWatcher.watch();
|
||||
handleWatchID(fileWatcher.addWatch(CurPath + "test", global_ul, true));
|
||||
}
|
||||
|
||||
while(STOP == false) {
|
||||
efsw::System::sleep(500);
|
||||
}
|
||||
|
||||
delete global_ul;
|
||||
global_ul = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main Program Entry Point
|
||||
*
|
||||
@ -274,6 +562,18 @@ int DL_main(Args& args)
|
||||
uint16_t publickey_port = 5799;
|
||||
bool test_render = false;
|
||||
|
||||
Engine engine;
|
||||
engine.scene().loadDefs();
|
||||
MyEngineObserver engineObserver;
|
||||
engine.subscribe(&engineObserver);
|
||||
|
||||
// Very early on, we will subscribe to the global bella logging callback, and ask to flush
|
||||
// any messages that may have accumulated prior to this point.
|
||||
//
|
||||
subscribeLog(&s_logCtx, log);
|
||||
flushStartupMessages();
|
||||
|
||||
|
||||
// Register command-line arguments
|
||||
args.add("sa", "serverAddress", "", "Bella render server ip address");
|
||||
args.add("cp", "commandPort", "", "tcp port for zmq server socket for commands");
|
||||
@ -283,6 +583,8 @@ int DL_main(Args& args)
|
||||
args.add("tr", "testRender", "", "force res to 100x100");
|
||||
args.add("tp", "thirdparty", "", "prints third party licenses");
|
||||
args.add("li", "licenseinfo", "", "prints license info");
|
||||
args.add("ef", "efsw", "", "mode efsw");
|
||||
args.add("wd", "watchdir", "", "mode file warch");
|
||||
|
||||
// Handle special command-line options
|
||||
if (args.versionReqested())
|
||||
@ -296,6 +598,39 @@ int DL_main(Args& args)
|
||||
printf("%s", args.help("SDK Test", fs::exePath(), bellaSdkVersion().toString()).buf());
|
||||
return 0;
|
||||
}
|
||||
std::string path=".";
|
||||
|
||||
if (args.have("--watchdir")) {
|
||||
path = args.value("--watchdir").buf();
|
||||
}
|
||||
|
||||
|
||||
//EFSW mode alwys on
|
||||
// Create the file watcher thread
|
||||
std::thread watcher_thread(file_watcher_thread, path);
|
||||
// Don't wait for the thread to finish here, let it run in background
|
||||
watcher_thread.detach();
|
||||
|
||||
/*if (args.have("--efswxxxxx"))
|
||||
{
|
||||
std::cout << "EFSW mode" << std::endl;
|
||||
signal( SIGABRT, sigend );
|
||||
signal( SIGINT, sigend );
|
||||
signal( SIGTERM, sigend );
|
||||
|
||||
//std::cout << "Press ^C to exit demo" << std::endl;
|
||||
|
||||
std::string path;
|
||||
if (args.have("--watchdir")) {
|
||||
path = args.value("--watchdir").buf();
|
||||
}
|
||||
|
||||
// Create the file watcher thread
|
||||
std::thread watcher_thread(file_watcher_thread, path);
|
||||
|
||||
// Don't wait for the thread to finish here, let it run in background
|
||||
watcher_thread.detach();
|
||||
}*/
|
||||
|
||||
// Show license information if requested
|
||||
if (args.have("--licenseinfo"))
|
||||
@ -364,9 +699,6 @@ int DL_main(Args& args)
|
||||
}
|
||||
|
||||
|
||||
Engine engine;
|
||||
engine.scene().loadDefs();
|
||||
|
||||
// Generate brand new keypair on launch
|
||||
// [TODO] Add client side public key fingerprinting for added security
|
||||
if(server.load()) {
|
||||
@ -378,7 +710,8 @@ int DL_main(Args& args)
|
||||
std::cout << "\ncurve keypair gen failed.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::thread server_t(server_thread, server_skey, command_port, test_render, engine);
|
||||
std::thread server_t(server_thread, server_skey, command_port, test_render, std::ref(engine), std::ref(engineObserver));
|
||||
std::thread render_t(render_thread, std::ref(engine), std::ref(engineObserver));
|
||||
///std::thread heartbeat_t(heartbeat_thread, server_skey, server.load(), 5555);
|
||||
std::thread heartbeat_t(heartbeat_thread, //function
|
||||
"", //NA Public server key
|
||||
@ -397,7 +730,7 @@ int DL_main(Args& args)
|
||||
|
||||
while(true) { // inner loop
|
||||
if (connection_state.load()==false) {
|
||||
std::cout << "Client connectiono dead" << std::endl;
|
||||
std::cout << "Client connection dead" << std::endl;
|
||||
break; // Go back to awaiting client
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
@ -595,6 +928,16 @@ void client_thread( std::string server_pkey,
|
||||
exit(0);
|
||||
// RENDER
|
||||
} else if(command == "render") {
|
||||
std::string compoundArg;
|
||||
if(num_args > 1) {
|
||||
for (size_t i = 1; i < args.size(); ++i) {
|
||||
compoundArg += args[i];
|
||||
if (i < args.size() - 1) {
|
||||
compoundArg += " "; // Add spaces between arguments
|
||||
}
|
||||
}
|
||||
std::cout << compoundArg << std::endl;
|
||||
}
|
||||
//>>>ZOUT
|
||||
command_sock.send(zmq::message_t("render"), zmq::send_flags::none);
|
||||
//ZIN<<<
|
||||
@ -694,9 +1037,10 @@ void client_thread( std::string server_pkey,
|
||||
void server_thread( std::string server_skey,
|
||||
uint16_t command_port,
|
||||
bool test_render,
|
||||
Engine engine) {
|
||||
MyEngineObserver engineObserver;
|
||||
engine.subscribe(&engineObserver);
|
||||
Engine& engine,
|
||||
MyEngineObserver& engineObserver) {
|
||||
//MyEngineObserver engineObserver;
|
||||
//engine.subscribe(&engineObserver);
|
||||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t command_sock(ctx, zmq::socket_type::rep);
|
||||
@ -733,12 +1077,9 @@ void server_thread( std::string server_skey,
|
||||
command_sock.send(zmq::message_t("RDY"), zmq::send_flags::none);
|
||||
connection_state = false; //<<
|
||||
// RENDER
|
||||
} else if (client_command == "render") {
|
||||
} else if (client_command == "xxxxrender") {
|
||||
std::cout << "start render" << std::endl;
|
||||
if(test_render) {
|
||||
engine.scene().camera()["resolution"]= Vec2 {100, 100};
|
||||
}
|
||||
engine.start();
|
||||
|
||||
//>>>ZOUT
|
||||
command_sock.send(zmq::message_t("render started...type stat to get progress"), zmq::send_flags::none);
|
||||
} else if (client_command == "stop") {
|
||||
@ -834,6 +1175,9 @@ void server_thread( std::string server_skey,
|
||||
//>>ZOUT
|
||||
command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
@ -854,6 +1198,85 @@ void server_thread( std::string server_skey,
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
void render_thread( Engine& engine,
|
||||
MyEngineObserver& engineObserver) {
|
||||
// Create persistent instances outside the loop
|
||||
RenderQueue renderThreadQueue;
|
||||
RenderQueue renderThreadDeleteQueue;
|
||||
|
||||
while (true) {
|
||||
// Append items from incoming queues to our persistent queues
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(incomingRenderQueueMutex);
|
||||
// Process each item in the incoming queue and add it to our persistent queue
|
||||
dl::String path;
|
||||
while (incomingRenderQueue.pop(path)) {
|
||||
renderThreadQueue.push(path);
|
||||
}
|
||||
incomingRenderQueue.clear();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(incomingDeleteQueueMutex);
|
||||
// Process each item in the incoming queue and add it to our persistent queue
|
||||
dl::String path;
|
||||
while (incomingDeleteQueue.pop(path)) {
|
||||
renderThreadDeleteQueue.push(path);
|
||||
}
|
||||
incomingDeleteQueue.clear();
|
||||
}
|
||||
|
||||
// Process the files without holding the mutex
|
||||
bool expected = false;
|
||||
|
||||
// This is an atomic operation that does two things at once:
|
||||
// 1. Checks if active_render equals expected (false)
|
||||
// 2. If they are equal, sets active_render to true
|
||||
//
|
||||
// The operation is atomic, meaning no other thread can interfere
|
||||
// between the check and the set. This prevents two threads from
|
||||
// both thinking they can start rendering at the same time.
|
||||
//
|
||||
// Returns true if the exchange was successful (we got the render slot)
|
||||
// Returns false if active_render was already true (someone else is rendering)
|
||||
dl::String belPath;
|
||||
if (active_render.compare_exchange_strong(expected, true)) {
|
||||
// We successfully got the render slot - no one else is rendering
|
||||
if (renderThreadQueue.pop(belPath)) {
|
||||
std::cout << "\n==" << "RENDERING: " << belPath.buf() << "\n==" << std::endl;
|
||||
engine.loadScene(belPath);
|
||||
engine.scene().camera()["resolution"]= Vec2 {100, 100};
|
||||
engine.start();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(currentRenderMutex);
|
||||
currentRender = belPath;
|
||||
}
|
||||
} else {
|
||||
active_render = false; // Release the render slot
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
} else { // someone else is rendering
|
||||
std::cout << "Waiting for render slot" << std::endl;
|
||||
|
||||
//std::cout << "Render Queue size: " << renderThreadQueue.size() << std::endl;
|
||||
//std::cout << "Delete Queue size: " << renderThreadDeleteQueue.size() << std::endl;
|
||||
while (renderThreadDeleteQueue.pop(belPath)) { // pop all the deletes
|
||||
std::cout << "renderThreadDeleteQueue contains " << belPath.buf() << " " << renderThreadDeleteQueue.contains(belPath) << std::endl;
|
||||
if (belPath == currentRender) {
|
||||
std::cout << "/n==/nStopping render" << belPath.buf() << std::endl;
|
||||
engine.stop();
|
||||
active_render = false;
|
||||
} else if (renderThreadQueue.contains(belPath)) { // dequeue deletes
|
||||
renderThreadQueue.remove(belPath);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void openFileWithDefaultProgram(const std::string& filePath) {
|
||||
#ifdef _WIN32
|
||||
|
||||
232
makefile
232
makefile
@ -1,143 +1,137 @@
|
||||
|
||||
SDKNAME =bella_engine_sdk
|
||||
OUTNAME =bellatui
|
||||
UNAME =$(shell uname)
|
||||
BELLA_SDK_NAME = bella_engine_sdk
|
||||
EXECUTABLE_NAME = bellatui
|
||||
PLATFORM = $(shell uname)
|
||||
BUILD_TYPE ?= release# Default to release build if not specified
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
# Common paths
|
||||
BELLA_SDK_PATH = ../bella_engine_sdk
|
||||
LIBEFSW_PATH = ../efsw
|
||||
LIBZMQ_PATH = ../libzmq
|
||||
CPPZMQ_PATH = ../cppzmq
|
||||
|
||||
SDKBASE = ../bella_engine_sdk
|
||||
OBJ_DIR = obj/$(PLATFORM)/$(BUILD_TYPE)
|
||||
BIN_DIR = bin/$(PLATFORM)/$(BUILD_TYPE)
|
||||
OUTPUT_FILE = $(BIN_DIR)/$(EXECUTABLE_NAME)
|
||||
|
||||
SDKFNAME = lib$(SDKNAME).dylib
|
||||
ZMQNAME = libzmq.5.dylib
|
||||
INCLUDEDIRS = -I$(SDKBASE)/src
|
||||
INCLUDEDIRS2 = -I../cppzmq
|
||||
INCLUDEDIRS3 = -I../libzmq/include
|
||||
LIBDIR = $(SDKBASE)/lib
|
||||
ZMQDIR = ../libzmq/build/lib
|
||||
LIBDIRS2 = -L../libzmq/build/lib
|
||||
LIBDIRS = -L$(LIBDIR)
|
||||
OBJDIR = obj/$(UNAME)
|
||||
BINDIR = bin/$(UNAME)
|
||||
OUTPUT = $(BINDIR)/$(OUTNAME)
|
||||
|
||||
ISYSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
|
||||
# Platform-specific configuration
|
||||
ifeq ($(PLATFORM), Darwin)
|
||||
# macOS configuration
|
||||
SDK_LIB_EXT = dylib
|
||||
# LZFSE_LIB_NAME = liblzfse.$(SDK_LIB_EXT)
|
||||
# PLIST_LIB_NAME = libplist-2.0.4.$(SDK_LIB_EXT)
|
||||
ZMQ_LIB_NAME = libzmq.5.$(SDK_LIB_EXT)
|
||||
EFSW_LIB_NAME = libefsw.$(SDK_LIB_EXT)
|
||||
MACOS_SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
|
||||
|
||||
# Compiler settings
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
|
||||
CCFLAGS = -arch x86_64\
|
||||
-arch arm64\
|
||||
-mmacosx-version-min=11.0\
|
||||
-isysroot $(ISYSROOT)\
|
||||
-fvisibility=hidden\
|
||||
-O3\
|
||||
$(INCLUDEDIRS)\
|
||||
$(INCLUDEDIRS2)\
|
||||
$(INCLUDEDIRS3)\
|
||||
$(LIBDIRS2)
|
||||
# Architecture flags
|
||||
ARCH_FLAGS = -arch arm64 -mmacosx-version-min=11.0 -isysroot $(MACOS_SDK_PATH)
|
||||
|
||||
CFLAGS = $(CCFLAGS)\
|
||||
-std=c11
|
||||
|
||||
CXXFLAGS = $(CCFLAGS)\
|
||||
-std=c++11
|
||||
|
||||
CPPDEFINES = -DNDEBUG=1\
|
||||
-DDL_USE_SHARED
|
||||
|
||||
LIBS = -l$(SDKNAME)\
|
||||
-lm\
|
||||
-lzmq\
|
||||
-ldl
|
||||
|
||||
LINKFLAGS = -mmacosx-version-min=11.0\
|
||||
-isysroot $(ISYSROOT)\
|
||||
-framework Cocoa\
|
||||
-framework IOKit\
|
||||
-framework CoreVideo\
|
||||
-framework CoreFoundation\
|
||||
-framework Accelerate\
|
||||
-fvisibility=hidden\
|
||||
-O5\
|
||||
# Linking flags - Use multiple rpath entries to look in executable directory
|
||||
LINKER_FLAGS = $(ARCH_FLAGS) -framework Cocoa -framework IOKit -fvisibility=hidden -O5 \
|
||||
-rpath @executable_path \
|
||||
-weak_library $(LIBDIR)/libvulkan.dylib
|
||||
-rpath .
|
||||
|
||||
#-rpath @loader_path \
|
||||
#-Xlinker -rpath -Xlinker @executable_path
|
||||
|
||||
else
|
||||
# Linux configuration
|
||||
SDK_LIB_EXT = so
|
||||
# LZFSE_LIB_NAME = liblzfse.$(SDK_LIB_EXT)
|
||||
# PLIST_LIB_NAME = libplist.$(SDK_LIB_EXT)
|
||||
ZMQ_LIB_NAME = libzmq.$(SDK_LIB_EXT)
|
||||
EFSW_LIB_NAME = libefsw.$(SDK_LIB_EXT)
|
||||
|
||||
SDKBASE = ../bella_engine_sdk
|
||||
|
||||
SDKFNAME = lib$(SDKNAME).so
|
||||
ZMQNAME = libzmq.so.5
|
||||
SODNAME = libsodium.so.23
|
||||
INCLUDEDIRS = -I$(SDKBASE)/src
|
||||
INCLUDEDIRS2 = -I../cppzmq
|
||||
INCLUDEDIRS3 = -I../libzmq/include
|
||||
LIBDIR = $(SDKBASE)/lib
|
||||
ZMQDIR = ../libzmq/build/lib
|
||||
SODDIR = /usr/lib/x86_64-linux-gnu/
|
||||
LIBDIRS = -L$(LIBDIR)
|
||||
LIBDIRS2 = -L../libzmq/build/lib
|
||||
OBJDIR = obj/$(UNAME)
|
||||
BINDIR = bin/$(UNAME)
|
||||
OUTPUT = $(BINDIR)/$(OUTNAME)
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
|
||||
CCFLAGS = -m64\
|
||||
-Wall\
|
||||
-fvisibility=hidden\
|
||||
-D_FILE_OFFSET_BITS=64\
|
||||
-O3\
|
||||
$(INCLUDEDIRS)\
|
||||
$(INCLUDEDIRS2)\
|
||||
$(INCLUDEDIRS3)\
|
||||
$(LIBDIRS2)
|
||||
# Architecture flags
|
||||
ARCH_FLAGS = -m64 -D_FILE_OFFSET_BITS=64
|
||||
|
||||
CFLAGS = $(CCFLAGS)\
|
||||
-std=c11
|
||||
# Linking flags
|
||||
LINKER_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 -Wl,-rpath,'$$ORIGIN' -Wl,-rpath,'$$ORIGIN/lib' -weak_library $(LIBDIR)/libvulkan.dylib
|
||||
|
||||
CXXFLAGS = $(CCFLAGS)\
|
||||
-std=c++11
|
||||
|
||||
CPPDEFINES = -DNDEBUG=1\
|
||||
-DDL_USE_SHARED
|
||||
|
||||
LIBS = -l$(SDKNAME)\
|
||||
-lm\
|
||||
-ldl\
|
||||
-lrt\
|
||||
-lpthread\
|
||||
-lX11\
|
||||
-lGL\
|
||||
-lzmq\
|
||||
-lvulkan
|
||||
|
||||
LINKFLAGS = -m64\
|
||||
-fvisibility=hidden\
|
||||
-O3\
|
||||
-Wl,-rpath,'$$ORIGIN'\
|
||||
-Wl,-rpath,'$$ORIGIN/lib'
|
||||
# Platform-specific libraries
|
||||
#PLIST_LIB = -lplist
|
||||
endif
|
||||
|
||||
OBJS = bellatui.o
|
||||
OBJ = $(patsubst %,$(OBJDIR)/%,$(OBJS))
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -c -o $@ $< $(CXXFLAGS) $(CPPDEFINES)
|
||||
# Common include and library paths
|
||||
INCLUDE_PATHS = -I$(BELLA_SDK_PATH)/src -I$(LIBEFSW_PATH)/include -I$(LIBEFSW_PATH)/src -I$(LIBZMQ_PATH)/include -I$(CPPZMQ_PATH)
|
||||
SDK_LIB_PATH = $(BELLA_SDK_PATH)/lib
|
||||
SDK_LIB_FILE = lib$(BELLA_SDK_NAME).$(SDK_LIB_EXT)
|
||||
EFSW_LIB_PATH = $(LIBEFSW_PATH)/build
|
||||
#EFSW_LIB_FILE = lib$(EFSW_LIB_NAME)
|
||||
ZMQ_LIB_PATH = $(LIBZMQ_PATH)/build/lib
|
||||
# Library flags
|
||||
LIB_PATHS = -L$(SDK_LIB_PATH) -L$(EFSW_LIB_PATH) -L$(ZMQ_LIB_PATH)
|
||||
LIBRARIES = -l$(BELLA_SDK_NAME) -lm -ldl -lefsw -lzmq
|
||||
|
||||
$(OUTPUT): $(OBJ)
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -o $@ $^ $(LINKFLAGS) $(LIBDIRS) $(LIBDIRS2) $(LIBS)
|
||||
@cp $(LIBDIR)/$(SDKFNAME) $(BINDIR)/$(SDKFNAME)
|
||||
@cp $(ZMQDIR)/$(ZMQNAME) $(BINDIR)/$(ZMQNAME)
|
||||
chmod 755 $(BINDIR)/$(ZMQNAME)
|
||||
ifeq ($(UNAME), Linux)
|
||||
@cp $(SODDIR)/$(SODNAME) $(BINDIR)/$(SODNAME)
|
||||
# Build type specific flags
|
||||
ifeq ($(BUILD_TYPE), debug)
|
||||
CPP_DEFINES = -D_DEBUG -DDL_USE_SHARED
|
||||
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -g -O0 $(INCLUDE_PATHS)
|
||||
else
|
||||
CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED
|
||||
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 $(INCLUDE_PATHS)
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
# Language-specific flags
|
||||
C_FLAGS = $(COMMON_FLAGS) -std=c17
|
||||
CXX_FLAGS = $(COMMON_FLAGS) -std=c++17 -Wno-deprecated-declarations
|
||||
|
||||
# Objects
|
||||
OBJECTS = $(EXECUTABLE_NAME).o
|
||||
OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS))
|
||||
|
||||
# Build rules
|
||||
$(OBJ_DIR)/$(EXECUTABLE_NAME).o: $(EXECUTABLE_NAME).cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -c -o $@ $< $(CXX_FLAGS) $(CPP_DEFINES)
|
||||
|
||||
$(OUTPUT_FILE): $(OBJECT_FILES)
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -o $@ $(OBJECT_FILES) $(LINKER_FLAGS) $(LIB_PATHS) $(LIBRARIES)
|
||||
@echo "Copying libraries to $(BIN_DIR)..."
|
||||
@cp $(SDK_LIB_PATH)/$(SDK_LIB_FILE) $(BIN_DIR)/$(SDK_LIB_FILE)
|
||||
# @cp $(LZFSE_BUILD_DIR)/$(LZFSE_LIB_NAME) $(BIN_DIR)/$(LZFSE_LIB_NAME)
|
||||
# @cp $(PLIST_LIB_DIR)/$(PLIST_LIB_NAME) $(BIN_DIR)/$(PLIST_LIB_NAME)
|
||||
@cp $(EFSW_LIB_PATH)/$(EFSW_LIB_NAME) $(BIN_DIR)/$(EFSW_LIB_NAME)
|
||||
@cp $(ZMQ_LIB_PATH)/$(ZMQ_LIB_NAME) $(BIN_DIR)/$(ZMQ_LIB_NAME)
|
||||
@echo "Build complete: $(OUTPUT_FILE)"
|
||||
|
||||
# Add default target
|
||||
all: $(OUTPUT_FILE)
|
||||
|
||||
.PHONY: clean cleanall all
|
||||
clean:
|
||||
rm -f $(OBJDIR)/*.o
|
||||
rm -f $(OUTPUT)
|
||||
rm -f $(BINDIR)/$(SDKFNAME)
|
||||
rm -f $(OBJ_DIR)/$(EXECUTABLE_NAME).o
|
||||
rm -f $(OUTPUT_FILE)
|
||||
rm -f $(BIN_DIR)/$(SDK_LIB_FILE)
|
||||
rm -f $(BIN_DIR)/*.dylib
|
||||
rmdir $(OBJ_DIR) 2>/dev/null || true
|
||||
rmdir $(BIN_DIR) 2>/dev/null || true
|
||||
|
||||
cleanall:
|
||||
rm -f obj/*/release/*.o
|
||||
rm -f obj/*/debug/*.o
|
||||
rm -f bin/*/release/$(EXECUTABLE_NAME)
|
||||
rm -f bin/*/debug/$(EXECUTABLE_NAME)
|
||||
rm -f bin/*/release/$(SDK_LIB_FILE)
|
||||
rm -f bin/*/debug/$(SDK_LIB_FILE)
|
||||
rm -f bin/*/release/*.dylib
|
||||
rm -f bin/*/debug/*.dylib
|
||||
rmdir obj/*/release 2>/dev/null || true
|
||||
rmdir obj/*/debug 2>/dev/null || true
|
||||
rmdir bin/*/release 2>/dev/null || true
|
||||
rmdir bin/*/debug 2>/dev/null || true
|
||||
rmdir obj/* 2>/dev/null || true
|
||||
rmdir bin/* 2>/dev/null || true
|
||||
rmdir obj 2>/dev/null || true
|
||||
rmdir bin 2>/dev/null || true
|
||||
Loading…
x
Reference in New Issue
Block a user