#ifndef RENDERER_H #define RENDERER_H #include #include #include #include #include #include class Renderer { public: static constexpr int MaxThreads = 64; private: int N; std::counting_semaphore Workers; std::atomic_uint processed; unsigned total; std::atomic_bool Stop; std::unique_ptr primary; public: Renderer(int n, auto func, unsigned width, unsigned height, auto pbuf): N(n), Workers(N), processed(0), total(N * 8) { Stop.store(false); auto threads = std::views::transform( std::views::chunk( std::views::cartesian_product( std::views::iota(0u, width), std::views::iota(0u, height), std::views::single(pbuf)), width * height / total), [=, this](auto chunk) { return std::thread([=, this] { worker(func, chunk); }); }); primary.reset(new std::thread([=, this] { for (auto th : threads) { Workers.acquire(); th.detach(); } for (int i : std::views::iota(0, N)) Workers.acquire(); Stop.store(true); })); } ~Renderer() { stop(); } operator bool() const { return !Stop.load(); } unsigned progress() const { return processed.load() * 100 / total; } void stop() { Stop.store(true); if (primary->joinable()) primary->join(); } private: void worker(auto func, auto chunk) { for (auto xyb : chunk) { if (Stop.load()) break; std::apply(func, xyb); } processed++; Workers.release(); } }; #endif // RENDERER_H