diff --git a/engine/inc/uf/utils/thread/thread.h b/engine/inc/uf/utils/thread/thread.h index deee2192..923b33ab 100644 --- a/engine/inc/uf/utils/thread/thread.h +++ b/engine/inc/uf/utils/thread/thread.h @@ -16,6 +16,8 @@ #include #include +#define UF_THREAD_METRICS 1 + namespace uf { namespace thread { extern UF_API uf::stl::string mainThreadName; @@ -29,6 +31,19 @@ namespace pod { typedef uint16_t id_t; typedef std::function function_t; typedef uf::stl::vector container_t; + + struct UF_API Tasks { + uf::stl::string name = uf::thread::workerThreadName; + bool waits = true; + + pod::Thread::container_t container; + + inline void add( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } + inline void emplace( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } + inline void queue( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } + inline bool empty() { return container.empty(); } + inline void clear() { container = {}; } + }; pod::Thread::id_t uid; uf::stl::string name; @@ -51,19 +66,20 @@ namespace pod { pod::Thread::container_t container; uint32_t affinity = 0; + #if UF_THREAD_METRICS + struct Performance { + typedef std::tuple tuple_t; - struct UF_API Tasks { - uf::stl::string name = uf::thread::workerThreadName; - bool waits = true; + std::atomic activeTimeMs{0.0f}; + std::atomic idleTimeMs{0.0f}; + std::atomic totalFrameTimeMs{0.0f}; + std::atomic tasksProcessed{0}; - pod::Thread::container_t container; - - inline void add( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } - inline void emplace( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } - inline void queue( const pod::Thread::function_t& fun ) { container.emplace_back(fun); } - inline bool empty() { return container.empty(); } - inline void clear() { container = {}; } - }; + inline tuple_t collect() { + return std::make_tuple( activeTimeMs.load(), idleTimeMs.load(), totalFrameTimeMs.load(), tasksProcessed.load() ); + } + } metrics; + #endif }; } @@ -133,5 +149,9 @@ namespace uf { std::thread::id UF_API id( const pod::Thread& ); pod::Thread::id_t UF_API uid( const pod::Thread& ); bool UF_API running( const pod::Thread& ); + + #if UF_THREAD_METRICS + uf::stl::unordered_map collectStats(); + #endif } } \ No newline at end of file diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index 3d1d26e4..9faaf3de 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -873,6 +873,11 @@ void UF_API uf::tick() { #if UF_ENV_DREAMCAST DC_STATS(); #endif + #if UF_THREAD_METRICS + auto metrics = uf::thread::collectStats(); + for ( auto& [ name, stats ] : metrics ) UF_MSG_DEBUG("Thread {}: active={}, idle={}, total={}, tasks={}", name, std::get<0>(stats), std::get<1>(stats), std::get<2>(stats), std::get<3>(stats) ); + #endif + /*global*/::times.frames = 0; } } diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 8e86ea47..d1e6e706 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -1475,23 +1475,24 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { UF_MSG_DEBUG("Graph buffers requesting renderer update"); uf::renderer::states::rebuild = true; + #if UF_USE_VULKAN if ( uf::renderer::hasRenderMode("", true) ) { auto& renderMode = uf::renderer::getRenderMode("", true); - - #if UF_USE_VULKAN auto& blitter = renderMode.getBlitter(); - auto& shader = blitter.material.getShader(blitter.material.hasShader("compute", "deferred") ? "compute" : "fragment", "deferred"); + if ( blitter.material.hasShader("compute", "deferred") || blitter.material.hasShader("fragment", "deferred") ) { + auto& shader = blitter.material.getShader(blitter.material.hasShader("compute", "deferred") ? "compute" : "fragment", "deferred"); - shader.metadata.aliases.buffers.clear(); + shader.metadata.aliases.buffers.clear(); - shader.aliasBuffer( "drawCommands", storage.buffers.drawCommands ); - shader.aliasBuffer( "instance", storage.buffers.instance ); - shader.aliasBuffer( "instanceAddresses", storage.buffers.instanceAddresses ); - shader.aliasBuffer( "material", storage.buffers.material ); - shader.aliasBuffer( "texture", storage.buffers.texture ); - shader.aliasBuffer( "light", storage.buffers.light ); - #endif + shader.aliasBuffer( "drawCommands", storage.buffers.drawCommands ); + shader.aliasBuffer( "instance", storage.buffers.instance ); + shader.aliasBuffer( "instanceAddresses", storage.buffers.instanceAddresses ); + shader.aliasBuffer( "material", storage.buffers.material ); + shader.aliasBuffer( "texture", storage.buffers.texture ); + shader.aliasBuffer( "light", storage.buffers.light ); + } } + #endif } return rebuild; diff --git a/engine/src/utils/thread/thread.cpp b/engine/src/utils/thread/thread.cpp index 6f8a97cd..6de5e4bc 100644 --- a/engine/src/utils/thread/thread.cpp +++ b/engine/src/utils/thread/thread.cpp @@ -119,6 +119,13 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(thread.n local_queue.clear(); local_container.clear(); +#if UF_THREAD_METRICS + uint32_t tasksThisFrame = 0; + auto frameStart = std::chrono::high_resolution_clock::now(); + auto idleStart = std::chrono::high_resolution_clock::now(); +#endif + + // wait for work { std::unique_lock lock(thread.mutex); if ( thread.limiter > 0 ) { @@ -139,6 +146,16 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(thread.n std::swap( local_queue, thread.queue ); } + // update stats +#if UF_THREAD_METRICS + { + std::chrono::duration idleTime = std::chrono::high_resolution_clock::now() - idleStart; + thread.metrics.idleTimeMs.store(idleTime.count(), std::memory_order_relaxed); + } + auto activeStart = std::chrono::high_resolution_clock::now(); +#endif + + // iterate through queued work for ( auto& function : local_queue ) { #if UF_EXCEPTIONS try { @@ -149,17 +166,21 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(thread.n UF_MSG_ERROR("Thread {} (UID: {}) caught exception: {}", thread.name, thread.uid, e.what()); } #endif - if ( thread.pending.fetch_sub(1) == 1 ) { thread.conditions.finished.notify_all(); } + #if UF_THREAD_METRICS + ++tasksThisFrame; + #endif } + // buffer persistent work { std::lock_guard lock(thread.mutex); local_container = thread.container; } + // iterate through persistent work for ( auto& function : local_container ) { #if UF_EXCEPTIONS try { @@ -170,12 +191,27 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(thread.n UF_MSG_ERROR("Thread {} (UID: {}) caught exception: {}", thread.name, thread.uid, e.what()); } #endif + #if UF_THREAD_METRICS + ++tasksThisFrame; + #endif } { std::lock_guard lock(thread.mutex); thread.conditions.finished.notify_all(); } + + // update metrics +#if UF_THREAD_METRICS + { + std::chrono::duration activeTime = std::chrono::high_resolution_clock::now() - activeStart; + std::chrono::duration frameTime = std::chrono::high_resolution_clock::now() - frameStart; + + thread.metrics.activeTimeMs.store(activeTime.count(), std::memory_order_relaxed); + thread.metrics.totalFrameTimeMs.store(frameTime.count(), std::memory_order_relaxed); + thread.metrics.tasksProcessed.store(tasksThisFrame, std::memory_order_relaxed); + } +#endif } void uf::thread::wait( pod::Thread& thread ) { std::unique_lock lock(thread.mutex); @@ -204,7 +240,7 @@ void uf::thread::terminate() { std::swap( local_threads, uf::thread::threads ); } - for ( auto& [ key, thread ] : local_threads ) { + for ( auto& [ key, thread ] : local_threads ) { uf::thread::quit( *thread ); delete thread; } @@ -252,4 +288,13 @@ bool uf::thread::has( const uf::stl::string& name ) { pod::Thread& uf::thread::get( const uf::stl::string& name ) { if ( !uf::thread::has(name) ) return uf::thread::create(name); return *uf::thread::threads[name]; -} \ No newline at end of file +} + +#if UF_THREAD_METRICS +uf::stl::unordered_map uf::thread::collectStats() { + uf::stl::unordered_map stats; + // possible mutex issue + for ( auto& [ key, thread ] : uf::thread::threads ) stats[thread->name] = thread->metrics.collect(); + return stats; +} +#endif \ No newline at end of file