Toast Engine
Loading...
Searching...
No Matches
async_compute_task.hpp
Go to the documentation of this file.
1
4
5#pragma once
6
7#include "async_compute.hpp"
8#include "gpu_sync.hpp"
9#include "shader_layout.hpp"
10#include "vulkan_pipeline.hpp"
11#include "vulkan_renderer.hpp"
12
13#include <array>
14#include <cstddef>
15#include <functional>
16#include <memory>
17#include <mutex>
18#include <optional>
19#include <span>
20#include <string>
21#include <toast/export.hpp>
22#include <vector>
23#include <vulkan-memory-allocator-hpp/vk_mem_alloc_raii.hpp>
24
25namespace renderer {
26
33class TOAST_API AsyncComputeTask {
34public:
35 using BufferId = uint32_t;
36
38 enum class Copy : uint8_t {
40 current,
42 previous,
43 };
44
45 struct Config {
46 std::string shader_uri;
47 std::vector<std::string> entries {"computeMain"};
48 std::string debug_name = "AsyncComputeTask";
49 };
50
52 class TOAST_API StepContext {
53 public:
54 template<typename Push>
55 void dispatch(uint32_t groups_x, const Push& push, uint32_t entry = 0, uint32_t groups_y = 1, uint32_t groups_z = 1) {
56 dispatchRaw(entry, {groups_x, groups_y, groups_z}, &push, sizeof(Push));
57 }
58
59 void dispatch(uint32_t groups_x, uint32_t entry = 0, uint32_t groups_y = 1, uint32_t groups_z = 1) {
60 dispatchRaw(entry, {groups_x, groups_y, groups_z}, nullptr, 0);
61 }
62
64 [[nodiscard]]
65 auto commandBuffer() const -> vk::CommandBuffer {
66 return m_cmd;
67 }
68
69 [[nodiscard]]
70 auto hostData(BufferId id) const -> std::span<std::byte>;
71
72 template<typename T>
73 [[nodiscard]]
74 auto hostData(BufferId id) const -> std::span<T> {
75 const std::span<std::byte> bytes = hostData(id);
76 return {reinterpret_cast<T*>(bytes.data()), bytes.size() / sizeof(T)};
77 }
78
79 private:
80 friend class AsyncComputeTask;
81
82 StepContext(AsyncComputeTask& task, vk::CommandBuffer cmd, uint32_t slot) : m_task(task), m_cmd(cmd), m_slot(slot) { }
83
84 void dispatchRaw(uint32_t entry, std::array<uint32_t, 3> groups, const void* push, uint32_t push_size);
85
86 AsyncComputeTask& m_task;
87 vk::CommandBuffer m_cmd;
88 uint32_t m_slot;
89 bool m_dispatched = false;
90 };
91
93 class FrameView {
94 public:
95 [[nodiscard]]
96 explicit operator bool() const {
97 return m_task != nullptr;
98 }
99
101 [[nodiscard]]
102 auto buffer(BufferId id) const -> vk::Buffer;
103
105 [[nodiscard]]
106 auto step() const -> uint64_t {
107 return m_step;
108 }
109
110 private:
111 friend class AsyncComputeTask;
112 const AsyncComputeTask* m_task = nullptr;
113 uint32_t m_slot = 0;
114 uint64_t m_step = 0;
115 };
116
117 virtual ~AsyncComputeTask();
118
119 AsyncComputeTask(const AsyncComputeTask&) = delete;
120 auto operator=(const AsyncComputeTask&) -> AsyncComputeTask& = delete;
122 auto operator=(AsyncComputeTask&&) -> AsyncComputeTask& = delete;
123
126 auto acquireForFrame(vk::CommandBuffer cmd, uint32_t frame_index, sync::Usage next) -> FrameView;
127
129 auto read(BufferId id, std::span<std::byte> out) -> bool;
130
131 template<typename T>
132 auto read(BufferId id, std::span<T> out) -> bool {
133 return read(id, std::as_writable_bytes(out));
134 }
135
137 [[nodiscard]]
138 auto finishedStep() const -> uint64_t;
139
140 [[nodiscard]]
141 auto isReady() const -> bool {
142 return m_ready;
143 }
144
145protected:
146 explicit AsyncComputeTask(Config config);
147
149 auto addRingBuffer(vk::DeviceSize size, bool cpu_readback = false) -> BufferId;
150 auto addPersistentBuffer(vk::DeviceSize size) -> BufferId;
151 auto addHostBuffer(vk::DeviceSize size) -> BufferId;
152 void bind(uint32_t binding, BufferId id, Copy copy = Copy::current);
153 void bindExternal(uint32_t set, uint32_t binding, std::function<vk::Buffer()> source);
154 void setAcquireUsage(BufferId id, sync::Usage usage);
155
157 void upload(BufferId id, std::span<const std::byte> data);
158
159 template<typename T>
160 void upload(BufferId id, std::span<const T> data) {
161 upload(id, std::as_bytes(data));
162 }
163
165 auto step(
166 const std::function<void(StepContext&)>& record, std::string_view debug_name = {},
167 std::shared_ptr<const void> keep_alive = {}
168 ) -> uint64_t;
169
170private:
172 static constexpr uint32_t k_slot_count = VulkanRenderer::k_frames_in_flight + 3;
173
174 struct Buffer {
175 bool ring = false;
176 bool host = false;
177 bool cpu_readback = false;
178 vk::DeviceSize size = 0;
180 std::vector<vma::raii::Buffer> copies;
181 std::vector<vma::raii::Buffer> readbacks;
182 std::optional<sync::Usage> acquire_usage;
183 };
184
185 struct Binding {
186 uint32_t binding = 0;
187 BufferId id = 0;
188 Copy copy = Copy::current;
189 };
190
191 struct ExternalBinding {
192 uint32_t set = 0;
193 uint32_t binding = 0;
194 std::function<vk::Buffer()> source;
195 };
196
197 struct Slot {
198 std::vector<vk::raii::DescriptorSet> sets;
199 AsyncCompute::Ticket ticket = AsyncCompute::k_no_ticket;
201 uint32_t pins = 0;
203 uint64_t step = 0;
204 std::shared_ptr<const void> keep_alive;
205 };
206
207 void createDescriptors();
208 void writeDescriptors(uint32_t slot);
209 auto claimFreeSlot() -> uint32_t;
210 void publish(uint32_t slot, AsyncCompute::Ticket ticket);
212 auto newestFinishedLocked() const -> int32_t;
213
214 Config m_config;
215 const VulkanCore* m_core = nullptr;
216 bool m_ready = false;
217
218 ShaderLayout m_layout;
219 std::vector<std::unique_ptr<VulkanPipeline>> m_pipelines;
220 vk::raii::DescriptorPool m_pool = nullptr;
221
222 std::vector<Buffer> m_buffers;
223 std::vector<Binding> m_bindings;
224 std::vector<ExternalBinding> m_externals;
225 vma::raii::Buffer m_placeholder = nullptr;
226
227 mutable std::mutex m_mutex;
228 std::array<Slot, k_slot_count> m_slots;
229 uint64_t m_step_counter = 0;
230 int32_t m_last_written = -1;
231
233 std::array<int32_t, VulkanRenderer::k_frames_in_flight> m_frame_slot {};
234 std::array<uint64_t, k_slot_count> m_acquired_step {};
235};
236
237}
Buffers the renderer may read this frame.
Definition async_compute_task.hpp:93
auto buffer(BufferId id) const -> vk::Buffer
Null for a persistent buffer since only the task touches those.
Definition async_compute_task.cpp:71
auto step() const -> uint64_t
Step counter of the data.
Definition async_compute_task.hpp:106
auto commandBuffer() const -> vk::CommandBuffer
For anything the helpers do not cover.
Definition async_compute_task.hpp:65
Definition async_compute_task.hpp:33
Copy
Which copy of a ring buffer a binding sees during a step.
Definition async_compute_task.hpp:38
Usage
Definition gpu_sync.hpp:15
Definition async_compute_task.hpp:45