first commit

This commit is contained in:
Jason Ly 2026-03-02 21:17:56 -05:00
parent 40b986dd0b
commit ef4ea5b5df
3 changed files with 525 additions and 8 deletions

View File

@ -16,7 +16,7 @@ joomer-efsw-file-monitoring
# Build
```
learndir/
└── joomer-efsw-file-monitoring/
└── joomer-efsw-bsz-file-monitoring/
└── efsw
```
@ -30,14 +30,14 @@ cd efsw/build
cmake ..
make -j4
cd ../..
git clone https://git.indoodle.com/jason/joomer-efsw-file-monitoring.git
cd joomer-efsw-file-monitoring
git clone https://git.indoodle.com/jason/joomer-bsz-efsw-file-monitoring.git
cd joomer-bsz-efsw-file-monitoring
git switch ProposedCleanup
wget https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
wget https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
make all -j4
mkdir bin/Linux/release/test
bin/Linux/release/joomer-efsw-file-monitoring
bin/Linux/release/joomer-efsw-bsz-file-monitoring
```
## Windows
@ -57,12 +57,12 @@ cd build
cmake ..
msbuild efsw.sln /p:Configuration=Release
cd ..
git clone https://git.indoodle.com/jason/joomer-efsw-file-monitoring.git
cd joomer-efsw-file-monitoring
git clone https://git.indoodle.com/jason/joomer-efsw-bsz-monitoring.git
cd joomer-bsz-efsw-monitoring
curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
msbuild joomer-efsw-file-monitoring.vcxproj /p:Configuration=release
msbuild joomer-efsw-bsz-monitoring.vcxproj /p:Configuration=release
mkdir -p x64\release\test
x64\release\joomer-efsw-file-monitoring.exe
x64\release\joomer-efsw-bsz-monitoring.exe
```

View File

@ -0,0 +1,397 @@
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/efsw.hpp>
#include <iostream>
#include <signal.h>
#include <cstring>
#include "stb_image.h"
#include "stb_image_write.h"
#include "bella_sdk/bella_engine.h" // Core rendering engine
#include "bella_sdk/bella_scene.h" // Scene management
#include "dl_core/dl_logging.h" // Logging utilities
#include <thread> // For std::this_thread (C++ standard library)
#include <chrono> // For timing operations (C++ standard library)
#include <cstdio> // For freopen
#include <regex>
std::string directory_path = ".";
bool STOP = false;
dl::bella_sdk::Engine engine;
std::string fileToRender = "";
bool pendingRender = false;
//void flip_image_horizontally(unsigned char* data, int width, int height, int channels);
void flip_image_vertically(unsigned char* data, int width, int height, int channels);
void sigend( int ) {
std::cout << std::endl << "close file monitoring" << std::endl;
STOP = true;
}
/*
* MyEngineObserver Class
* This class receives callbacks from the Bella rendering engine to track rendering progress.
* It implements the EngineObserver interface and provides methods to:
* - Handle render start/stop events
* - Track rendering progress
* - Handle error conditions
* - Store and retrieve the current progress state
*/
struct MyEngineObserver : public dl::bella_sdk::EngineObserver
{
public:
// Called when a rendering pass starts
void onStarted(dl::String pass) override
{
//std::cout << "Started pass " << pass.buf() << std::endl;
// dl::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());
//}
// Called to update rendering progress (percentage, time remaining, etc)
void onProgress(dl::String pass, dl::bella_sdk::Progress progress) override
{
//std::cout << progress.toString().buf() << std::endl;
setString(new std::string(progress.toString().buf()));
//dl::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(dl::String pass, dl::String msg) override
{
//dl::logError("%s [%s]", msg.buf(), pass.buf());
}
// Called when a rendering pass completes
void onStopped(dl::String pass) override
{
// dl::logInfo("Stopped %s", pass.buf());
std::atomic_bool active_render = false;
}
// Returns the current progress as a string
std::string getProgress() const {
std::string* currentProgress = progressPtr.load();
if (currentProgress) {
return *currentProgress;
}
else {
return "";
}
}
// Cleanup resources in destructor
~MyEngineObserver() {
setString(nullptr);
}
private:
// Thread-safe pointer to current progress string
std::atomic<std::string*> progressPtr{ nullptr };
// Helper function to safely update the progress string
void setString(std::string* newStatus) {
std::string* oldStatus = progressPtr.exchange(newStatus);
delete oldStatus; // Clean up old string if it exists
}
};
MyEngineObserver engineObserver;
//void render_thread(dl::bella_sdk::Engine& engine, MyEngineObserver& engineObserver, std::string selected_name) {
// // Construct the full path once
// dl::String fullPath = dl::String(directory_path.c_str()) + "/" + dl::String(selected_name.c_str());
//
// // Load and then Read using the SAME full path
// engine.loadScene(fullPath);
// if (!engine.scene().read(fullPath)) {
// std::cout << "Failed to read scene: " << selected_name << std::endl;
// return;
// }
//
// //dl::String belPath = dl::String(directory_path.c_str()) + "\\";
// //engine.loadScene(belPath + dl::String(selected_name.c_str()));
//
// //if (!engine.scene().read(dl::String(selected_name.c_str()))) return;
//
// dl::bella_sdk::Node bPass = engine.scene().beautyPass();
//
// // Setup the outputImagePath node as we did in the synchronous version
// dl::bella_sdk::Node pathNode = engine.scene().createNode("outputImagePath");
// pathNode["dir"] = dl::String(directory_path.c_str());
// pathNode["name"] = "render_result";
// pathNode["ext"] = ".png";
// // In render_thread
// pathNode["dir"] = dl::String("../renders/");
// bPass["overridePath"] = pathNode;
// bPass["timeLimit"] = 30.0f;
//
// engine.start();
//
// // While rendering, sleep and then tell the UI to refresh
// while (engine.rendering()) {
// std::this_thread::sleep_for(std::chrono::milliseconds(500));
// // This is the key to seeing progress while it's "asynchronous"
// //screen.PostEvent(ftxui::Event::Custom);
// //std::cout << "rendering...";
// }
// std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Finalize save
//}
void do_sync_render(std::string fileToRender)
{
// This removes any trailing slashes from directory_path to prevent the \/ issue
if (!directory_path.empty() && (directory_path.back() == '/' || directory_path.back() == '\\')) {
directory_path.pop_back();
}
// 1. Force the path to look in the 'test' folder specifically
std::string fullPathStr = directory_path + "/" + fileToRender;
dl::String fullPath(fullPathStr.c_str());
std::cout << ">>> ATTEMPTING TO READ: " << fullPathStr << std::endl;
engine.loadScene(fullPath);
if (!engine.scene().read(fullPath)) {
std::cout << ">>> ERROR: Bella could not read scene file!" << std::endl;
return;
}
// 2. Setup the output path using a relative path that goes OUTSIDE 'test'
// This assumes your 'renders' folder is in x64/release/renders
dl::bella_sdk::Node pathNode = engine.scene().createNode("outputImagePath");
pathNode["dir"] = dl::String("./renders/");
pathNode["name"] = dl::String("render_result");
pathNode["ext"] = dl::String(".png");
dl::bella_sdk::Node bPass = engine.scene().beautyPass();
bPass["overridePath"] = pathNode;
std::cout << ">>> STARTING BELLA ENGINE..." << std::endl;
engine.start();
// 3. Monitor progress
while (engine.rendering()) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "rendering..." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Give Windows time to finish the file write
std::cout << ">>> RENDER FINISHED. Check x64/release/renders/" << std::endl;
}
void render_thread(dl::bella_sdk::Engine& engine, MyEngineObserver& engineObserver, std::string selected_name) {
// This removes any trailing slashes from directory_path to prevent the \/ issue
if (!directory_path.empty() && (directory_path.back() == '/' || directory_path.back() == '\\')) {
directory_path.pop_back();
}
// 1. Force the path to look in the 'test' folder specifically
std::string fullPathStr = directory_path + "/" + selected_name;
dl::String fullPath(fullPathStr.c_str());
std::cout << ">>> ATTEMPTING TO READ: " << fullPathStr << std::endl;
engine.loadScene(fullPath);
if (!engine.scene().read(fullPath)) {
std::cout << ">>> ERROR: Bella could not read scene file!" << std::endl;
return;
}
// 2. Setup the output path using a relative path that goes OUTSIDE 'test'
// This assumes your 'renders' folder is in x64/release/renders
dl::bella_sdk::Node pathNode = engine.scene().createNode("outputImagePath");
pathNode["dir"] = dl::String("../renders/");
pathNode["name"] = dl::String("render_result");
pathNode["ext"] = dl::String(".png");
dl::bella_sdk::Node bPass = engine.scene().beautyPass();
bPass["overridePath"] = pathNode;
std::cout << ">>> STARTING BELLA ENGINE..." << std::endl;
engine.start();
// 3. Monitor progress
while (engine.rendering()) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "rendering..." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Give Windows time to finish the file write
std::cout << ">>> RENDER FINISHED. Check x64/release/renders/" << std::endl;
}
/// Processes a file action
class UpdateListener : public efsw::FileWatchListener {
public:
UpdateListener() {}
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 {
std::cout << "Watch ID " << watchid << " DIR ("
<< dir + ") FILE (" +
( oldFilename.empty() ? "" : "from file " + oldFilename + " to " ) +
filename + ") has event "
<< getActionName( action ) << std::endl;
if (action == efsw::Actions::Add || action == efsw::Actions::Modified) {
directory_path = dir;
std::string fullPath = dir + filename;
int width, height, channels;
if (filename.substr(filename.length() - 3) == "bsz" ) {
std::cout << "Converting " << filename << " to a PNG" << std::endl;
// 2. Start the thread and DETACH it
// Note: We pass 'screen' as a reference so render_thread can call PostEvent
fileToRender = filename;
pendingRender = true;
//std::thread(render_thread, std::ref(engine), std::ref(engineObserver), filename).detach();
}
}
}
};
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;
}
void flip_image_vertically(unsigned char* data, int width, int height, int channels) {
// ... (your vertical flip implementation from before)
int row_stride = width * channels;
unsigned char* temp_row = new unsigned char [row_stride];
for (int y = 0; y < height / 2; ++y) {
unsigned char* top_row = data + y * row_stride;
unsigned char* bottom_row = data + (height - 1 - y) * row_stride;
std::memcpy(temp_row, top_row, row_stride);
std::memcpy(top_row, bottom_row, row_stride);
std::memcpy(bottom_row, temp_row, row_stride);
}
delete[] temp_row;
}
int main( int argc, char** argv ) {
signal( SIGABRT, sigend );
signal( SIGINT, sigend );
signal( SIGTERM, sigend );
// 1. Initialize engine and observer locally within the logic block
engine.subscribe(&engineObserver);
std::cout << "Press ^C to exit demo" << std::endl;
bool commonTest = true;
bool useGeneric = false;
std::string path;
if ( argc >= 2 ) {
path = std::string( argv[1] );
if ( efsw::FileSystem::isDirectory( path ) ) {
commonTest = false;
}
if ( argc >= 3 ) {
if ( std::string( argv[2] ) == "true" ) {
useGeneric = true;
}
}
}
UpdateListener* ul = new UpdateListener();
/// create the file watcher object
efsw::FileWatcher fileWatcher( useGeneric );
fileWatcher.followSymlinks( false );
fileWatcher.allowOutOfScopeLinks( false );
if ( commonTest ) {
std::string CurPath( efsw::System::getProcessPath() );
std::cout << "CurPath: " << CurPath.c_str() << std::endl;
/// starts watching
fileWatcher.watch();
/// add a watch to the system
handleWatchID( fileWatcher.addWatch( CurPath + "test", ul, true ) );
} else {
if ( fileWatcher.addWatch( path, ul, true ) > 0 ) {
fileWatcher.watch();
std::cout << "Watching directory: " << path.c_str() << std::endl;
if ( useGeneric ) {
std::cout << "Using generic backend watcher" << std::endl;
}
} else {
std::cout << "Error trying to watch directory: " << path.c_str() << std::endl;
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
}
}
while ( !STOP ) {
if (pendingRender) {
// We are now in the main thread.
// 1. Give the OS a moment to release file locks
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// 2. Call render logic (not in a thread)
// Note: You'll need to move render_thread logic into a regular function
do_sync_render(fileToRender);
pendingRender = false;
}
efsw::System::sleep( 100 );
}
return 0;
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<RootNamespace>joomer-efsw-bsz-monitoring</RootNamespace>
<DefaultItemExcludes>$(DefaultItemExcludes);$(ProjectDir)obj\**</DefaultItemExcludes>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectGuid>{7AEA0690-36B7-4596-9DEE-C3AB3C11D282}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>DEBUG;_CONSOLE;DL_USE_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\efsw\include;..\efsw\src;..\bella_engine_sdk\src;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>lib;..\efsw\build\$(Configuration)\$(Platform)</AdditionalLibraryDirectories>
<AdditionalDependencies>efsw-static.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;DL_USE_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\efsw\include;..\efsw\src;..\bella_engine_sdk\src;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>lib;..\efsw\build\Release;..\bella_engine_sdk\lib;</AdditionalLibraryDirectories>
<AdditionalDependencies>efsw-static.lib;bella_engine_sdk.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
echo Copying Bella SDK DLLs...
copy "$(ProjectDir)..\bella_engine_sdk\lib\bella_engine_sdk.dll" "$(TargetDir)"
copy "$(ProjectDir)..\bella_engine_sdk\lib\dl_core.dll" "$(TargetDir)"
copy "$(ProjectDir)..\bella_engine_sdk\lib\dl_oidn_core.dll" "$(TargetDir)"
copy "$(ProjectDir)..\bella_engine_sdk\lib\dl_usd_ms.dll" "$(TargetDir)"
echo DLL copy finished.
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="$(PlatformToolset.Contains('Intel'))">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PreprocessorDefinitions>_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<InterproceduralOptimization>NoIPO</InterproceduralOptimization>
</ClCompile>
<Link>
<InterproceduralOptimization>false</InterproceduralOptimization>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="joomer-efsw-bsz-monitoring.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>