summaryrefslogtreecommitdiffstats
path: root/src/video_core/gpu_thread.h
blob: ad9f9462b96e0ccf1a62b70cf6cd9642364e6fcb (plain) (blame)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <variant>

namespace Tegra {
struct FramebufferConfig;
class DmaPusher;
} // namespace Tegra

namespace VideoCore {
class RendererBase;
} // namespace VideoCore

namespace VideoCommon::GPUThread {

/// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {
    explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}

    Tegra::CommandList entries;
};

/// Command to signal to the GPU thread that a swap buffers is pending
struct SwapBuffersCommand final {
    explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
        : framebuffer{std::move(framebuffer)} {}

    std::optional<const Tegra::FramebufferConfig> framebuffer;
};

/// Command to signal to the GPU thread to flush a region
struct FlushRegionCommand final {
    explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}

    const VAddr addr;
    const u64 size;
};

/// Command to signal to the GPU thread to invalidate a region
struct InvalidateRegionCommand final {
    explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}

    const VAddr addr;
    const u64 size;
};

/// Command to signal to the GPU thread to flush and invalidate a region
struct FlushAndInvalidateRegionCommand final {
    explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size)
        : addr{addr}, size{size} {}

    const VAddr addr;
    const u64 size;
};

using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
                                 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;

/// Struct used to synchronize the GPU thread
struct SynchState final {
    std::atomic<bool> is_running{true};
    std::condition_variable signal_condition;
    std::mutex signal_mutex;
    std::condition_variable idle_condition;
    std::mutex idle_mutex;

    // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and
    // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes
    // empty. This allows for efficient thread-safe access, as it does not require any copies.

    using CommandQueue = std::queue<CommandData>;
    std::array<CommandQueue, 2> command_queues;
    CommandQueue* push_queue{&command_queues[0]};
    CommandQueue* pop_queue{&command_queues[1]};

    /// Returns true if the GPU thread should be idle, meaning there are no commands to process
    bool IsIdle() const {
        return command_queues[0].empty() && command_queues[1].empty();
    }
};

/// Class used to manage the GPU thread
class ThreadManager final {
public:
    explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
    ~ThreadManager();

    /// Push GPU command entries to be processed
    void SubmitList(Tegra::CommandList&& entries);

    /// Swap buffers (render frame)
    void SwapBuffers(
        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);

    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
    void FlushRegion(VAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be invalidated
    void InvalidateRegion(VAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
    void FlushAndInvalidateRegion(VAddr addr, u64 size);

    /// Waits the caller until the GPU thread is idle, used for synchronization
    void WaitForIdle();

private:
    /// Pushes a command to be executed by the GPU thread
    void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu);

    /// Returns true if this is called by the GPU thread
    bool IsGpuThread() const {
        return std::this_thread::get_id() == thread_id;
    }

private:
    SynchState state;
    std::thread thread;
    std::thread::id thread_id;
    VideoCore::RendererBase& renderer;
    Tegra::DmaPusher& dma_pusher;
};

} // namespace VideoCommon::GPUThread