MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ComputePress.cpp
Go to the documentation of this file.
1#include "ComputePress.hpp"
2
6
8
10
12{
13 if (s_initialized) {
15 "ComputePress already initialized (static flag)");
16 return true;
17 }
18
19 auto* foundry = &get_shader_foundry();
20 if (!foundry->is_initialized()) {
22 "Cannot initialize ComputePress: ShaderFoundry not initialized");
23 return false;
24 }
25
26 m_shader_foundry = foundry;
27 m_descriptor_manager = std::make_shared<Core::VKDescriptorManager>();
29
30 s_initialized = true;
31
33 "ComputePress initialized");
34 return true;
35}
36
38{
39 if (!s_initialized) {
40 return;
41 }
42
44 "ComputePress stopped");
45}
46
48{
49 if (!s_initialized) {
50 return;
51 }
52
54 "Shutting down ComputePress...");
55
58 "Cannot shutdown ComputePress: ShaderFoundry not initialized");
59 return;
60 }
61
63
64 if (!device) {
66 "Cannot shutdown ComputePress: Vulkan device is null");
67 return;
68 }
69
71
75 }
76
77 m_pipelines.clear();
78 m_shader_foundry = nullptr;
79
80 s_initialized = false;
81
83 "ComputePress shutdown complete");
84}
85
86//==============================================================================
87// Pipeline Creation
88//==============================================================================
89
91 ShaderID shader_id,
92 const std::vector<std::vector<DescriptorBindingInfo>>& descriptor_sets,
93 size_t push_constant_size)
94{
95 if (!is_initialized()) {
97 "ComputePress not initialized");
99 }
100
101 auto shader_module = m_shader_foundry->get_vk_shader_module(shader_id);
102 if (!shader_module) {
104 "Invalid shader ID: {}", shader_id);
106 }
107
108 auto stage = m_shader_foundry->get_shader_stage(shader_id);
109 if (stage != ShaderStage::COMPUTE) {
111 "Shader is not a compute shader (stage: {})", static_cast<int>(stage));
113 }
114
116 PipelineState& state = m_pipelines[id];
117 state.shader_id = shader_id;
118
120
122 m_descriptor_manager = std::make_shared<Core::VKDescriptorManager>();
123 m_descriptor_manager->initialize(device, 1024);
124 }
125
126 for (const auto& set_bindings : descriptor_sets) {
128
129 for (const auto& binding : set_bindings) {
130 layout_config.add_binding(
131 binding.binding,
132 binding.type,
133 vk::ShaderStageFlagBits::eCompute,
134 1);
135 }
136
137 state.layouts.push_back(m_descriptor_manager->create_layout(device, layout_config));
138 }
139
140 Core::ComputePipelineConfig pipeline_config;
141 pipeline_config.shader = shader_module;
142 pipeline_config.set_layouts = state.layouts;
143
144 if (push_constant_size > 0) {
145 pipeline_config.add_push_constant(
146 vk::ShaderStageFlagBits::eCompute,
147 static_cast<uint32_t>(push_constant_size));
148 }
149
150 state.pipeline = std::make_shared<Core::VKComputePipeline>();
151 if (!state.pipeline->create(device, pipeline_config)) {
153 "Failed to create compute pipeline");
154 m_pipelines.erase(id);
156 }
157
158 state.layout = state.pipeline->get_layout();
159 state.push_constant_size = push_constant_size;
160
162 "Created compute pipeline (ID: {}, {} descriptor sets, {} bytes push constants)",
163 id, state.layouts.size(), push_constant_size);
164
165 return id;
166}
167
169 ShaderID shader_id,
170 size_t push_constant_size)
171{
172 if (!is_initialized()) {
174 "ComputePress not initialized");
176 }
177
178 auto reflection = m_shader_foundry->get_shader_reflection(shader_id);
179
180 std::map<uint32_t, std::vector<DescriptorBindingInfo>> bindings_by_set;
181 for (const auto& binding : reflection.descriptor_bindings) {
183 config.set = binding.set;
184 config.binding = binding.binding;
185 config.type = binding.type;
186 bindings_by_set[binding.set].push_back(config);
187 }
188
189 std::vector<std::vector<DescriptorBindingInfo>> descriptor_sets;
190 descriptor_sets.reserve(bindings_by_set.size());
191 for (const auto& [set_index, bindings] : bindings_by_set) {
192 descriptor_sets.push_back(bindings);
193 }
194
195 size_t pc_size = push_constant_size;
196 if (pc_size == 0 && !reflection.push_constant_ranges.empty()) {
197 pc_size = reflection.push_constant_ranges[0].size;
198 }
199
201 "Auto-creating pipeline: {} descriptor sets, {} bindings total",
202 descriptor_sets.size(), reflection.descriptor_bindings.size());
203
204 return create_pipeline(shader_id, descriptor_sets, pc_size);
205}
206
208{
209 auto it = m_pipelines.find(pipeline_id);
210 if (it == m_pipelines.end()) {
211 return;
212 }
213
215
216 if (it->second.pipeline) {
217 it->second.pipeline->cleanup(device);
218 }
219
220 m_pipelines.erase(it);
221
223 "Destroyed compute pipeline (ID: {})", pipeline_id);
224}
225
227{
229
230 for (auto& [id, state] : m_pipelines) {
231 if (state.pipeline) {
232 state.pipeline->cleanup(device);
233 }
234 }
235
237 "Cleaned up all compute pipelines");
238}
239
240//==============================================================================
241// Pipeline Binding
242//==============================================================================
243
245{
246 auto pipeline_it = m_pipelines.find(pipeline_id);
247 if (pipeline_it == m_pipelines.end()) {
249 "Invalid pipeline ID: {}", pipeline_id);
250 return;
251 }
252
254 if (!cmd) {
256 "Invalid command buffer ID: {}", cmd_id);
257 return;
258 }
259
260 pipeline_it->second.pipeline->bind(cmd);
261}
262
264 CommandBufferID cmd_id,
265 ComputePipelineID pipeline_id,
266 const std::vector<DescriptorSetID>& descriptor_set_ids)
267{
268 auto pipeline_it = m_pipelines.find(pipeline_id);
269 if (pipeline_it == m_pipelines.end()) {
271 "Invalid pipeline ID: {}", pipeline_id);
272 return;
273 }
274
276 if (!cmd) {
278 "Invalid command buffer ID: {}", cmd_id);
279 return;
280 }
281
282 std::vector<vk::DescriptorSet> vk_sets;
283 for (auto ds_id : descriptor_set_ids) {
284 vk_sets.push_back(m_shader_foundry->get_descriptor_set(ds_id));
285 }
286
287 pipeline_it->second.pipeline->bind_descriptor_sets(cmd, vk_sets);
288}
289
291 CommandBufferID cmd_id,
292 ComputePipelineID pipeline_id,
293 const void* data,
294 size_t size)
295{
296 auto pipeline_it = m_pipelines.find(pipeline_id);
297 if (pipeline_it == m_pipelines.end()) {
299 "Invalid pipeline ID: {}", pipeline_id);
300 return;
301 }
302
304 if (!cmd) {
306 "Invalid command buffer ID: {}", cmd_id);
307 return;
308 }
309
310 pipeline_it->second.pipeline->push_constants(
311 cmd,
312 vk::ShaderStageFlagBits::eCompute,
313 0,
314 static_cast<uint32_t>(size),
315 data);
316}
317
319 CommandBufferID cmd_id,
320 ComputePipelineID pipeline_id,
321 const void* data,
322 size_t size,
323 uint32_t offset)
324{
325 auto pipeline_it = m_pipelines.find(pipeline_id);
326 if (pipeline_it == m_pipelines.end()) {
328 "Invalid pipeline ID: {}", pipeline_id);
329 return;
330 }
331
332 if (static_cast<size_t>(offset) + size > pipeline_it->second.push_constant_size) {
334 "Push constant range [{}, {}) exceeds the {} bytes declared for pipeline {}",
335 offset, static_cast<size_t>(offset) + size,
336 pipeline_it->second.push_constant_size, pipeline_id);
337 return;
338 }
339
341 if (!cmd) {
343 "Invalid command buffer ID: {}", cmd_id);
344 return;
345 }
346
347 pipeline_it->second.pipeline->push_constants(
348 cmd,
349 vk::ShaderStageFlagBits::eCompute,
350 offset,
351 static_cast<uint32_t>(size),
352 data);
353}
354
355//==============================================================================
356// Dispatch
357//==============================================================================
358
359void ComputePress::dispatch(CommandBufferID cmd_id, uint32_t x, uint32_t y, uint32_t z)
360{
361 if (!is_initialized()) {
362 return;
363 }
364
366 if (!cmd) {
368 "Invalid command buffer ID: {}", cmd_id);
369 return;
370 }
371
372 cmd.dispatch(x, y, z);
373
375 "Dispatched compute: {}x{}x{} workgroups", x, y, z);
376}
377
379 CommandBufferID cmd_id,
380 vk::Buffer indirect_buffer,
381 vk::DeviceSize offset)
382{
383 if (!is_initialized()) {
384 return;
385 }
386
388 if (!cmd) {
390 "Invalid command buffer ID: {}", cmd_id);
391 return;
392 }
393
394 cmd.dispatchIndirect(indirect_buffer, offset);
395
397 "Dispatched compute indirect from buffer");
398}
399
400//==============================================================================
401// Convenience Wrappers
402//==============================================================================
403
404std::vector<DescriptorSetID> ComputePress::allocate_pipeline_descriptors(ComputePipelineID pipeline_id)
405{
406 auto pipeline_it = m_pipelines.find(pipeline_id);
407 if (pipeline_it == m_pipelines.end()) {
409 "Invalid pipeline ID: {}", pipeline_id);
410 return {};
411 }
412
413 std::vector<DescriptorSetID> descriptor_set_ids;
414 for (const auto& layout : pipeline_it->second.layouts) {
415 auto ds_id = m_shader_foundry->allocate_descriptor_set(layout);
416 if (ds_id == INVALID_DESCRIPTOR_SET) {
418 "Failed to allocate descriptor set for pipeline {}", pipeline_id);
419 return {};
420 }
421 descriptor_set_ids.push_back(ds_id);
422 }
423
425 "Allocated {} descriptor sets for pipeline {}",
426 descriptor_set_ids.size(), pipeline_id);
427
428 return descriptor_set_ids;
429}
430
432 CommandBufferID cmd_id,
433 ComputePipelineID pipeline_id,
434 const std::vector<DescriptorSetID>& descriptor_set_ids,
435 const void* push_constants_data,
436 size_t push_constant_size)
437{
438 bind_pipeline(cmd_id, pipeline_id);
439 bind_descriptor_sets(cmd_id, pipeline_id, descriptor_set_ids);
440
441 if (push_constants_data && push_constant_size > 0) {
442 this->push_constants(cmd_id, pipeline_id, push_constants_data, push_constant_size);
443 }
444}
445
447 CommandBufferID cmd_id,
448 const std::vector<ComputeStage>& stages)
449{
450 if (!is_initialized()) {
451 return;
452 }
453
454 auto& foundry = *m_shader_foundry;
455
456 for (size_t i = 0; i < stages.size(); ++i) {
457 const auto& stage = stages[i];
458
459 bind_all(
460 cmd_id,
461 stage.pipeline_id,
462 stage.descriptor_set_ids,
463 stage.push_constant_data.empty() ? nullptr : stage.push_constant_data.data(),
464 stage.push_constant_data.size());
465
466 if (stage.indirect.buffer) {
467 foundry.buffer_barrier(
468 cmd_id,
469 stage.indirect.buffer,
470 vk::AccessFlagBits::eShaderWrite,
471 vk::AccessFlagBits::eIndirectCommandRead,
472 vk::PipelineStageFlagBits::eComputeShader,
473 vk::PipelineStageFlagBits::eDrawIndirect);
474 dispatch_indirect(cmd_id, stage.indirect.buffer, stage.indirect.offset);
475 } else {
476 dispatch(cmd_id, stage.groups[0], stage.groups[1], stage.groups[2]);
477 }
478
479 for (const auto& hazard : stage.hazard_resources) {
480 if (hazard.binding.direction != GpuBufferBinding::Direction::INPUT_OUTPUT)
481 continue;
482
483 const bool is_image = hazard.binding.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE
484 || hazard.binding.element_type == GpuBufferBinding::ElementType::IMAGE_SAMPLED;
485
486 if (is_image) {
487 foundry.image_barrier(
488 cmd_id,
489 hazard.image,
490 vk::ImageLayout::eGeneral,
491 vk::ImageLayout::eGeneral,
492 vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead,
493 vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead,
494 vk::PipelineStageFlagBits::eComputeShader,
495 vk::PipelineStageFlagBits::eComputeShader);
496 } else {
497 foundry.buffer_barrier(
498 cmd_id,
499 hazard.buffer,
500 vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead,
501 vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead,
502 vk::PipelineStageFlagBits::eComputeShader,
503 vk::PipelineStageFlagBits::eComputeShader);
504 }
505 }
506
508 "record_sequence: stage {} of {} recorded ({} hazard barriers)",
509 i + 1, stages.size(), stage.hazard_resources.size());
510 }
511}
512
513} // namespace MayaFlux::Portal::Graphics
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::CommandBuffer cmd
uint64_t offset
Definition Composite.cpp:10
bool size
vk::PhysicalDevice device
Definition VKDevice.cpp:121
void destroy_pipeline(ComputePipelineID pipeline_id)
void push_constants(CommandBufferID cmd_id, ComputePipelineID pipeline_id, const void *data, size_t size)
Push constants to active command buffer.
void bind_pipeline(CommandBufferID cmd_id, ComputePipelineID pipeline_id)
Bind pipeline to active command buffer.
std::vector< DescriptorSetID > allocate_pipeline_descriptors(ComputePipelineID pipeline_id)
All-in-one: allocate descriptors for pipeline.
void bind_all(CommandBufferID cmd_id, ComputePipelineID pipeline_id, const std::vector< DescriptorSetID > &descriptor_set_ids, const void *push_constants_data=nullptr, size_t push_constant_size=0)
All-in-one: bind pipeline + descriptors + push constants.
void dispatch(CommandBufferID cmd_id, uint32_t x, uint32_t y, uint32_t z)
Dispatch compute workgroups.
void record_sequence(CommandBufferID cmd_id, const std::vector< ComputeStage > &stages)
Record multiple pipeline dispatches into one already-open command buffer, inserting a barrier between...
void bind_descriptor_sets(CommandBufferID cmd_id, ComputePipelineID pipeline_id, const std::vector< DescriptorSetID > &descriptor_set_ids)
Bind descriptor sets to active command buffer.
ComputePipelineID create_pipeline(ShaderID shader_id, const std::vector< std::vector< DescriptorBindingInfo > > &descriptor_sets={}, size_t push_constant_size=0)
Create compute pipeline.
std::unordered_map< ComputePipelineID, PipelineState > m_pipelines
bool is_initialized() const
Check if ComputePress is initialized.
void dispatch_indirect(CommandBufferID cmd_id, vk::Buffer indirect_buffer, vk::DeviceSize offset=0)
Dispatch compute workgroups indirectly.
ComputePipelineID create_pipeline_auto(ShaderID shader_id, size_t push_constant_size=0)
Create pipeline with auto-reflection.
std::shared_ptr< Core::VKDescriptorManager > m_descriptor_manager
vk::DescriptorSet get_descriptor_set(DescriptorSetID descriptor_set_id)
Get Vulkan descriptor set handle from DescriptorSetID.
vk::Device get_device() const
Get logical device handle.
std::shared_ptr< Core::VKShaderModule > get_vk_shader_module(ShaderID shader_id)
bool is_initialized() const
Check if compiler is initialized.
vk::CommandBuffer get_command_buffer(CommandBufferID cmd_id)
Get Vulkan command buffer handle from CommandBufferID.
ShaderReflectionInfo get_shader_reflection(ShaderID shader_id)
Get reflection info for compiled shader.
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.
ShaderStage get_shader_stage(ShaderID shader_id)
Get shader stage for compiled shader.
DescriptorSetID allocate_descriptor_set(vk::DescriptorSetLayout layout)
Allocate descriptor set for a pipeline.
@ GPUCompute
GPU compute operations (shaders, GPGPU tasks)
@ Portal
High-level user-facing API layer.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
constexpr ComputePipelineID INVALID_COMPUTE_PIPELINE
constexpr DescriptorSetID INVALID_DESCRIPTOR_SET
bool is_image(const fs::path &filepath)
Definition Depot.cpp:136
std::vector< vk::DescriptorSetLayout > set_layouts
Descriptor layouts.
std::shared_ptr< VKShaderModule > shader
Compute shader.
void add_push_constant(vk::ShaderStageFlags stages, uint32_t size, uint32_t offset=0)
Configuration for creating a compute pipeline.
void add_binding(uint32_t binding, vk::DescriptorType type, vk::ShaderStageFlags stages, uint32_t count=1)
Configuration for creating a descriptor set layout.
std::vector< vk::DescriptorSetLayout > layouts
std::shared_ptr< Core::VKComputePipeline > pipeline