#pragma once #include #include #include #include namespace renderive::detail { struct Render_Partition_Range { std::size_t first{}; std::size_t last{}; }; inline Render_Partition_Range render_partition_range(std::size_t size, int index, int count) noexcept { if (size == 0 || count <= 0 || index < 0 || index >= count) return {}; return {size * static_cast(index) / static_cast(count), size * static_cast(index + 1) / static_cast(count)}; } class Adaptive_Render_Partitioner { public: [[nodiscard]] int graph_partition_count(int configured_count, int worker_count) noexcept { if (configured_count > 0) return configured_count; if (automatic_count_ == 0) automatic_count_ = std::max(1, worker_count); automatic_count_ = std::clamp(automatic_count_, 1, std::max(1, worker_count)); return automatic_count_; } int begin(int graph_partition_count, std::size_t work_size) noexcept { const int useful = static_cast(std::min( static_cast(std::numeric_limits::max()), std::max(1, work_size))); started_at_ = Clock::now(); return std::clamp(graph_partition_count, 1, useful); } [[nodiscard]] bool finish(int configured_count, int active_count, int worker_count, std::size_t work_size) noexcept { if (configured_count != 0) return false; const auto elapsed = std::chrono::duration_cast(Clock::now() - started_at_); constexpr auto target = std::chrono::microseconds(1500); const int previous = automatic_count_; const int maximum = std::max(1, worker_count); if (elapsed > target * 2 && active_count < maximum && work_size / static_cast(active_count) >= 512) automatic_count_ = std::min(maximum, active_count + 1); else if (elapsed < target / 2 && active_count > 1) automatic_count_ = active_count - 1; else automatic_count_ = active_count; return automatic_count_ != previous; } private: using Clock = std::chrono::steady_clock; Clock::time_point started_at_{}; int automatic_count_{}; }; } // namespace renderive::detail