MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ComputeProcessor.cpp
Go to the documentation of this file.
2
3namespace MayaFlux::Buffers {
4
5//==============================================================================
6// Construction
7//==============================================================================
8
9ComputeProcessor::ComputeProcessor(const std::string& shader_path, uint32_t workgroup_x)
10 : ShaderProcessor(shader_path)
11
12{
13 m_dispatch_config.workgroup_x = workgroup_x;
14}
15
23
24void ComputeProcessor::initialize_pipeline(const std::shared_ptr<VKBuffer>& buffer)
25{
28 "Cannot create pipeline without shader");
29 return;
30 }
31
32 auto& compute_press = Portal::Graphics::get_compute_press();
33
34 std::map<std::pair<uint32_t, uint32_t>, Portal::Graphics::DescriptorBindingInfo> unified_bindings;
35
36 const auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
37 for (const auto& binding : descriptor_bindings) {
38 unified_bindings[{ binding.set, binding.binding }] = binding;
39 }
40
41 for (const auto& [name, binding] : m_config.bindings) {
42 auto key = std::make_pair(binding.set, binding.binding);
43 if (unified_bindings.find(key) == unified_bindings.end()) {
44 unified_bindings[key] = Portal::Graphics::DescriptorBindingInfo {
45 .set = binding.set,
46 .binding = binding.binding,
47 .type = binding.type,
48 .buffer_info = {},
49 .name = name
50 };
51 }
52 }
53
54 std::map<uint32_t, std::vector<Portal::Graphics::DescriptorBindingInfo>> bindings_by_set;
55 for (const auto& [key, binding] : unified_bindings) {
56 bindings_by_set[binding.set].push_back(binding);
57 }
58
59 std::vector<std::vector<Portal::Graphics::DescriptorBindingInfo>> descriptor_sets;
60
61 descriptor_sets.reserve(bindings_by_set.size());
62 for (const auto& [set_index, set_bindings] : bindings_by_set) {
63 descriptor_sets.push_back(set_bindings);
64 }
65
66 m_pipeline_id = compute_press.create_pipeline(
68 descriptor_sets,
70
73 "Failed to create compute pipeline");
74 return;
75 }
76
78
80 "Compute pipeline created (ID: {}, {} descriptor sets, {} bytes push constants)",
81 m_pipeline_id, descriptor_sets.size(), m_config.push_constant_size);
82}
83
84void ComputeProcessor::initialize_descriptors(const std::shared_ptr<VKBuffer>& buffer)
85{
88 "Cannot allocate descriptor sets without pipeline");
89 return;
90 }
91
93
94 auto& compute_press = Portal::Graphics::get_compute_press();
95
96 m_descriptor_set_ids = compute_press.allocate_pipeline_descriptors(m_pipeline_id);
97
98 if (m_descriptor_set_ids.empty()) {
100 "Failed to allocate descriptor sets");
101 return;
102 }
103
104 update_descriptors(buffer);
106
108 "Descriptor sets initialized: {} sets", m_descriptor_set_ids.size());
109}
110
111//==============================================================================
112// Dispatch Configuration
113//==============================================================================
114
115void ComputeProcessor::set_workgroup_size(uint32_t x, uint32_t y, uint32_t z)
116{
120}
121
126
134
136 std::function<std::array<uint32_t, 3>(const std::shared_ptr<VKBuffer>&)> calculator)
137{
139 m_dispatch_config.custom_calculator = std::move(calculator);
140}
141
142std::array<uint32_t, 3> ComputeProcessor::calculate_dispatch_size(const std::shared_ptr<VKBuffer>& buffer)
143{
144 using DispatchMode = ShaderDispatchConfig::DispatchMode;
145
146 switch (m_dispatch_config.mode) {
147 case DispatchMode::MANUAL:
149
150 case DispatchMode::ELEMENT_COUNT: {
151 uint64_t element_count = 0;
152 const auto& dimensions = buffer->get_dimensions();
153
154 if (!dimensions.empty()) {
155 element_count = dimensions[0].size;
156 } else {
157 element_count = buffer->get_size_bytes() / sizeof(float);
158 }
159
160 auto groups_x = static_cast<uint32_t>(
162 return { groups_x, 1, 1 };
163 }
164
165 case DispatchMode::BUFFER_SIZE: {
166 uint64_t size_bytes = buffer->get_size_bytes();
167 auto groups_x = static_cast<uint32_t>(
169 return { groups_x, 1, 1 };
170 }
171
172 case DispatchMode::CUSTOM:
175 }
176 return { 1, 1, 1 };
177
178 default:
179 return { 1, 1, 1 };
180 }
181}
182
187
190 const std::shared_ptr<VKBuffer>&,
191 uint32_t)
192{
193 return true;
194}
195
198 const std::shared_ptr<VKBuffer>& buffer,
199 uint32_t)
200{
202 cmd_id,
203 buffer->get_buffer(),
204 vk::AccessFlagBits::eShaderWrite,
205 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
206 vk::PipelineStageFlagBits::eComputeShader,
207 vk::PipelineStageFlagBits::eComputeShader);
208}
209
210void ComputeProcessor::execute_shader(const std::shared_ptr<VKBuffer>& buffer)
211{
214 "Cannot dispatch without pipeline and descriptors");
215 return;
216 }
217
218 if (m_descriptor_set_ids.empty()) {
220 "Descriptor sets not initialized");
221 return;
222 }
223
224 auto& foundry = Portal::Graphics::get_shader_foundry();
225 auto& compute_press = Portal::Graphics::get_compute_press();
226
227 auto cmd_id = foundry.begin_commands(Portal::Graphics::ShaderFoundry::CommandBufferType::COMPUTE);
228
229 m_last_command_buffer = cmd_id;
231
232 compute_press.bind_pipeline(cmd_id, m_pipeline_id);
233
234 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
235 if (!descriptor_bindings.empty()) {
236 for (const auto& binding : descriptor_bindings) {
237 if (binding.set >= m_descriptor_set_ids.size()) {
239 "Descriptor set index {} out of range", binding.set);
240 continue;
241 }
242
243 foundry.update_descriptor_buffer(
244 m_descriptor_set_ids[binding.set],
245 binding.binding,
246 binding.type,
247 binding.buffer_info.buffer,
248 binding.buffer_info.offset,
249 binding.buffer_info.range);
250 }
251 }
252
253 if (!m_descriptor_set_ids.empty()) {
254 compute_press.bind_descriptor_sets(cmd_id, m_pipeline_id, m_descriptor_set_ids);
255 }
256
257 on_before_execute(cmd_id, buffer);
258
259 const uint32_t iterations = std::max(1U, m_dispatch_config.iteration_count);
260 const auto dispatch_size = calculate_dispatch_size(buffer);
261
262 for (uint32_t i = 0; i < iterations; ++i) {
263 if (!on_iteration(cmd_id, buffer, i)) {
264 continue;
265 }
266
267 const auto& pc_bindings = buffer->get_pipeline_context().push_constant_bindings;
268
269 if (!pc_bindings.empty()) {
270 size_t required = 0;
271 for (const auto& pc : pc_bindings) {
272 required = std::max(required, static_cast<size_t>(pc.offset) + pc.data.size());
273 }
274
275 m_push_constant_scratch.assign(required, 0);
276
277 for (const auto& pc : pc_bindings) {
278 std::memcpy(m_push_constant_scratch.data() + pc.offset, pc.data.data(), pc.data.size());
279 }
280
281 compute_press.push_constants(
282 cmd_id, m_pipeline_id,
285 } else if (!m_push_constant_data.empty()) {
286 compute_press.push_constants(
287 cmd_id, m_pipeline_id,
289 m_push_constant_data.size());
290 }
291
292 compute_press.dispatch(cmd_id, dispatch_size[0], dispatch_size[1], dispatch_size[2]);
293
294 if (i + 1 < iterations) {
295 on_iteration_barrier(cmd_id, buffer, i);
296 }
297 }
298
299 on_after_execute(cmd_id, buffer);
300
301 foundry.buffer_barrier(
302 cmd_id,
303 buffer->get_buffer(),
304 vk::AccessFlagBits::eShaderWrite,
305 vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead,
306 vk::PipelineStageFlagBits::eComputeShader,
307 vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eTransfer);
308
309 submit_recorded(cmd_id, buffer);
310}
311
313{
314 auto& foundry = Portal::Graphics::get_shader_foundry();
315 auto& compute_press = Portal::Graphics::get_compute_press();
316
318 compute_press.destroy_pipeline(m_pipeline_id);
320 }
321
323 foundry.destroy_shader(m_shader_id);
325 }
326
327 m_descriptor_set_ids.clear();
328 m_bound_buffers.clear();
329 m_initialized = false;
330}
331
332} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::string name
Definition VKDevice.cpp:143
size_t count
virtual void on_iteration_barrier(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer, uint32_t index)
Called after each iteration except the last.
std::vector< uint8_t > m_push_constant_scratch
Coalesced push constant bindings, reused across iterations.
void set_workgroup_size(uint32_t x, uint32_t y=1, uint32_t z=1)
Set workgroup size (should match shader local_size)
void initialize_descriptors(const std::shared_ptr< VKBuffer > &buffer) override
void execute_shader(const std::shared_ptr< VKBuffer > &buffer) override
ComputeProcessor(const std::string &shader_path, uint32_t workgroup_x=256)
Construct processor with shader path.
void initialize_pipeline(const std::shared_ptr< VKBuffer > &buffer) override
Portal::Graphics::ComputePipelineID m_pipeline_id
void set_iteration_count(uint32_t count)
Set how many dispatches are recorded per execute cycle.
void set_dispatch_mode(ShaderDispatchConfig::DispatchMode mode)
Set dispatch mode.
virtual bool on_iteration(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer, uint32_t index)
Called before each iteration's push constants and dispatch.
void set_manual_dispatch(uint32_t x, uint32_t y=1, uint32_t z=1)
Set manual dispatch group counts.
virtual std::array< uint32_t, 3 > calculate_dispatch_size(const std::shared_ptr< VKBuffer > &buffer)
Calculate dispatch size from buffer.
void set_custom_dispatch(std::function< std::array< uint32_t, 3 >(const std::shared_ptr< VKBuffer > &)> calculator)
Set custom dispatch calculator.
Portal::Graphics::CommandBufferID m_last_command_buffer
std::unordered_map< std::string, std::shared_ptr< VKBuffer > > m_bound_buffers
Portal::Graphics::ShaderID m_shader_id
std::vector< uint8_t > m_push_constant_data
virtual void on_after_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Called after each process callback.
virtual void on_descriptors_created()
Called after descriptor sets are created.
size_t resolve_push_constant_size(const std::shared_ptr< VKBuffer > &buffer) const
Byte width of this processor's push constant block, extended to cover any fragment staged on the buff...
virtual void update_descriptors(const std::shared_ptr< VKBuffer > &buffer)
virtual bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Called before each process callback.
virtual void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id)
Called after pipeline is created.
std::shared_ptr< VKBuffer > m_last_processed_buffer
void submit_recorded(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Submit a recorded command buffer honoring the submission mode.
virtual void on_before_descriptors_create()
Called before descriptor sets are created.
std::vector< Portal::Graphics::DescriptorSetID > m_descriptor_set_ids
Abstract base class for shader-based buffer processing.
void buffer_barrier(CommandBufferID cmd_id, vk::Buffer buffer, vk::AccessFlags src_access, vk::AccessFlags dst_access, vk::PipelineStageFlags src_stage, vk::PipelineStageFlags dst_stage)
Insert buffer memory barrier.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
constexpr ShaderID INVALID_SHADER
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
constexpr ComputePipelineID INVALID_COMPUTE_PIPELINE
MAYAFLUX_API ComputePress & get_compute_press()
std::unordered_map< std::string, ShaderBinding > bindings
enum MayaFlux::Buffers::ShaderDispatchConfig::DispatchMode mode
std::function< std::array< uint32_t, 3 >(const std::shared_ptr< VKBuffer > &)> custom_calculator
@ CUSTOM
User-provided calculation function.
uint32_t workgroup_x
Workgroup size X (should match shader)
uint32_t iteration_count
Dispatches recorded per execute cycle.
std::array< uint32_t, 3 > workgroup_size
Complete declarative description of a generated compute shader.