aboutsummaryrefslogtreecommitdiffstats
path: root/renderer.h
blob: 864c0b8e3dc2959ef4e4085a4110c11ee9c312d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef RENDERER_H
#define RENDERER_H

#include <algorithm>
#include <atomic>
#include <memory>
#include <ranges>
#include <semaphore>
#include <thread>

class Renderer
{
public:
    static constexpr int MaxThreads = 64;

private:
    int N;
    std::counting_semaphore<MaxThreads> Workers;
    std::atomic_uint processed;
    unsigned total;
    std::atomic_bool Stop;
    std::unique_ptr<std::thread> 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