MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKComputePipeline.cpp
Go to the documentation of this file.
2
4
5namespace MayaFlux::Core {
6
7// ============================================================================
8// Lifecycle
9// ============================================================================
10
12{
13 if (m_pipeline || m_layout) {
15 "VKComputePipeline destroyed without cleanup() - potential leak");
16 }
17}
18
20 : m_pipeline(other.m_pipeline)
21 , m_layout(other.m_layout)
22 , m_shader(std::move(other.m_shader))
23 , m_workgroup_size(other.m_workgroup_size)
24{
25 other.m_pipeline = nullptr;
26 other.m_layout = nullptr;
27}
28
30{
31 if (this != &other) {
32 if (m_pipeline || m_layout) {
34 "VKComputePipeline move-assigned without cleanup() - potential leak");
35 }
36
37 m_pipeline = other.m_pipeline;
38 m_layout = other.m_layout;
39 m_workgroup_size = other.m_workgroup_size;
40 m_shader = std::move(other.m_shader);
41
42 other.m_pipeline = nullptr;
43 other.m_layout = nullptr;
44 }
45 return *this;
46}
47
49{
50 if (m_pipeline) {
51 device.destroyPipeline(m_pipeline);
52 m_pipeline = nullptr;
54 "Compute pipeline destroyed");
55 }
56
57 if (m_layout) {
58 device.destroyPipelineLayout(m_layout);
59 m_layout = nullptr;
61 "Pipeline layout destroyed");
62 }
63
64 m_workgroup_size.reset();
65 m_shader.reset();
66}
67
68// ============================================================================
69// Pipeline Creation
70// ============================================================================
71
73{
74 if (!config.shader) {
76 "Cannot create compute pipeline without shader");
77 return false;
78 }
79
80 if (!config.shader->is_valid()) {
82 "Cannot create compute pipeline with invalid shader module");
83 return false;
84 }
85
86 if (config.shader->get_stage() != vk::ShaderStageFlagBits::eCompute) {
88 "Shader is not a compute shader (stage: {})",
89 vk::to_string(config.shader->get_stage()));
90 return false;
91 }
92
94 if (!m_layout) {
96 "Failed to create pipeline layout");
97 return false;
98 }
99
100 auto shader_stage = config.shader->get_stage_create_info();
101
102 vk::ComputePipelineCreateInfo pipeline_info;
103 pipeline_info.stage = shader_stage;
104 pipeline_info.layout = m_layout;
105 pipeline_info.basePipelineHandle = nullptr;
106 pipeline_info.basePipelineIndex = -1;
107
108 try {
109 auto result = device.createComputePipeline(config.cache, pipeline_info);
110 if (result.result != vk::Result::eSuccess) {
112 "Failed to create compute pipeline: {}",
113 vk::to_string(result.result));
114 device.destroyPipelineLayout(m_layout);
115 m_layout = nullptr;
116 return false;
117 }
118 m_pipeline = result.value;
119 } catch (const vk::SystemError& e) {
121 "Failed to create compute pipeline: {}", e.what());
122 device.destroyPipelineLayout(m_layout);
123 m_layout = nullptr;
124 return false;
125 }
126
127 m_shader = config.shader;
128 const auto& reflection = config.shader->get_reflection();
129 m_workgroup_size = reflection.workgroup_size;
130
131 if (m_workgroup_size) {
133 "Compute pipeline created (workgroup: {}x{}x{}, {} descriptor sets, {} push constants)",
135 config.set_layouts.size(), config.push_constants.size());
136 } else {
138 "Compute pipeline created ({} descriptor sets, {} push constants)",
139 config.set_layouts.size(), config.push_constants.size());
140 }
141
142 return true;
143}
144
146 vk::Device device,
147 const ComputePipelineConfig& config)
148{
149 std::vector<vk::PushConstantRange> vk_push_constants;
150 vk_push_constants.reserve(config.push_constants.size());
151
152 for (const auto& pc : config.push_constants) {
153 vk::PushConstantRange range;
154 range.stageFlags = pc.stage_flags;
155 range.offset = pc.offset;
156 range.size = pc.size;
157 vk_push_constants.push_back(range);
158 }
159
160 vk::PipelineLayoutCreateInfo layout_info;
161 layout_info.setLayoutCount = static_cast<uint32_t>(config.set_layouts.size());
162 layout_info.pSetLayouts = config.set_layouts.empty() ? nullptr : config.set_layouts.data();
163 layout_info.pushConstantRangeCount = static_cast<uint32_t>(vk_push_constants.size());
164 layout_info.pPushConstantRanges = vk_push_constants.empty() ? nullptr : vk_push_constants.data();
165
166 vk::PipelineLayout layout;
167 try {
168 layout = device.createPipelineLayout(layout_info);
169 } catch (const vk::SystemError& e) {
171 "Failed to create pipeline layout: {}", e.what());
172 return nullptr;
173 }
174
176 "Pipeline layout created ({} sets, {} push constant ranges)",
177 config.set_layouts.size(), config.push_constants.size());
178
179 return layout;
180}
181
182// ============================================================================
183// Pipeline Binding
184// ============================================================================
185
186void VKComputePipeline::bind(vk::CommandBuffer cmd) const
187{
188 if (!m_pipeline) {
190 "Cannot bind invalid compute pipeline");
191 return;
192 }
193
194 cmd.bindPipeline(vk::PipelineBindPoint::eCompute, m_pipeline);
195}
196
198 vk::CommandBuffer cmd,
199 const std::vector<vk::DescriptorSet>& descriptor_sets,
200 uint32_t first_set,
201 const std::vector<uint32_t>& dynamic_offsets) const
202{
203 if (!m_layout) {
205 "Cannot bind descriptor sets without pipeline layout");
206 return;
207 }
208
209 if (descriptor_sets.empty()) {
211 "Binding empty descriptor sets");
212 return;
213 }
214
215 for (size_t i = 0; i < descriptor_sets.size(); ++i) {
216 if (!descriptor_sets[i]) {
218 "Descriptor set at index {} is null", first_set + i);
219 return;
220 }
221 }
222
223 cmd.bindDescriptorSets(
224 vk::PipelineBindPoint::eCompute,
225 m_layout,
226 first_set,
227 static_cast<uint32_t>(descriptor_sets.size()),
228 descriptor_sets.data(),
229 static_cast<uint32_t>(dynamic_offsets.size()),
230 dynamic_offsets.empty() ? nullptr : dynamic_offsets.data());
231}
232
234 vk::CommandBuffer cmd,
235 vk::ShaderStageFlags stage_flags,
236 uint32_t offset,
237 uint32_t size,
238 const void* data) const
239{
240 if (!m_layout) {
242 "Cannot push constants without pipeline layout");
243 return;
244 }
245
246 if (!data) {
248 "Cannot push null data");
249 return;
250 }
251
252 if (size == 0) {
254 "Pushing zero-sized constant data");
255 return;
256 }
257
258 cmd.pushConstants(m_layout, stage_flags, offset, size, data);
259}
260
261// ============================================================================
262// Dispatch
263// ============================================================================
264
266 vk::CommandBuffer cmd,
267 uint32_t group_count_x,
268 uint32_t group_count_y,
269 uint32_t group_count_z) const
270{
271 if (!m_pipeline) {
273 "Cannot dispatch invalid compute pipeline");
274 return;
275 }
276
277 if (group_count_x == 0 || group_count_y == 0 || group_count_z == 0) {
279 "Dispatching with zero workgroups ({}x{}x{})",
280 group_count_x, group_count_y, group_count_z);
281 return;
282 }
283
284 cmd.dispatch(group_count_x, group_count_y, group_count_z);
285}
286
288 vk::CommandBuffer cmd,
289 uint32_t element_count,
290 uint32_t local_size_x) const
291{
292 if (local_size_x == 0) {
294 "Invalid workgroup size: {}", local_size_x);
295 return;
296 }
297
298 uint32_t workgroups = calculate_workgroups(element_count, local_size_x);
299 dispatch(cmd, workgroups, 1, 1);
300}
301
303 vk::CommandBuffer cmd,
304 uint32_t width_elements,
305 uint32_t height_elements,
306 uint32_t local_size_x,
307 uint32_t local_size_y) const
308{
309 if (local_size_x == 0 || local_size_y == 0) {
311 "Invalid workgroup size: {}x{}", local_size_x, local_size_y);
312 return;
313 }
314
315 uint32_t workgroups_x = calculate_workgroups(width_elements, local_size_x);
316 uint32_t workgroups_y = calculate_workgroups(height_elements, local_size_y);
317 dispatch(cmd, workgroups_x, workgroups_y, 1);
318}
319
321 vk::CommandBuffer cmd,
322 uint32_t width_elements,
323 uint32_t height_elements,
324 uint32_t depth_elements,
325 uint32_t local_size_x,
326 uint32_t local_size_y,
327 uint32_t local_size_z) const
328{
329 if (local_size_x == 0 || local_size_y == 0 || local_size_z == 0) {
331 "Invalid workgroup size: {}x{}x{}", local_size_x, local_size_y, local_size_z);
332 return;
333 }
334
335 uint32_t workgroups_x = calculate_workgroups(width_elements, local_size_x);
336 uint32_t workgroups_y = calculate_workgroups(height_elements, local_size_y);
337 uint32_t workgroups_z = calculate_workgroups(depth_elements, local_size_z);
338 dispatch(cmd, workgroups_x, workgroups_y, workgroups_z);
339}
340
341// ============================================================================
342// Utility
343// ============================================================================
344
345std::optional<std::array<uint32_t, 3>> VKComputePipeline::get_workgroup_size() const
346{
347 return m_workgroup_size;
348}
349
350uint32_t VKComputePipeline::calculate_workgroups(uint32_t element_count, uint32_t workgroup_size)
351{
352 if (workgroup_size == 0) {
353 return 0;
354 }
355
356 return (element_count + workgroup_size - 1) / workgroup_size;
357}
358
360 vk::Device device,
361 const ComputePipelineConfig& config,
362 const std::unordered_map<uint32_t, uint32_t>& specialization_data)
363{
364 if (!config.shader) {
366 "Cannot create compute pipeline without shader");
367 return false;
368 }
369
370 config.shader->set_specialization_constants(specialization_data);
371 return create(device, config);
372}
373
375 uint32_t element_count,
376 uint32_t workgroup_size)
377{
378 return { calculate_workgroups(element_count, workgroup_size), 1, 1 };
379}
380
382 uint32_t width, uint32_t height,
383 uint32_t workgroup_x, uint32_t workgroup_y)
384{
385 return {
386 calculate_workgroups(width, workgroup_x),
387 calculate_workgroups(height, workgroup_y),
388 1
389 };
390}
391
393 uint32_t width, uint32_t height, uint32_t depth,
394 uint32_t workgroup_x, uint32_t workgroup_y, uint32_t workgroup_z)
395{
396 return {
397 calculate_workgroups(width, workgroup_x),
398 calculate_workgroups(height, workgroup_y),
399 calculate_workgroups(depth, workgroup_z)
400 };
401}
402
404 vk::CommandBuffer cmd,
405 vk::Buffer buffer,
406 vk::DeviceSize offset)
407{
408 if (!m_pipeline) {
410 "Cannot dispatch invalid compute pipeline");
411 return;
412 }
413
414 if (!buffer) {
416 "Cannot dispatch with null indirect buffer");
417 return;
418 }
419
420 cmd.dispatchIndirect(buffer, offset);
421}
422
424{
425 if (!m_shader) {
427 "Cannot get shader reflection - no shader attached");
428 static ShaderReflection empty;
429 return empty;
430 }
431 return m_shader->get_reflection();
432}
433
434} // namespace MayaFlux::Core
#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
std::shared_ptr< NetworkGeometryBuffer > buffer
vk::PhysicalDevice device
Definition VKDevice.cpp:121
uint32_t width
uint32_t height
uint32_t depth
bool create_specialized(vk::Device device, const ComputePipelineConfig &config, const std::unordered_map< uint32_t, uint32_t > &specialization_data)
Create pipeline with specialization constants.
void dispatch_1d(vk::CommandBuffer cmd, uint32_t element_count, uint32_t local_size_x) const
Dispatch compute workgroups with automatic calculation.
void bind(vk::CommandBuffer cmd) const
Bind pipeline to command buffer.
std::optional< std::array< uint32_t, 3 > > m_workgroup_size
void cleanup(vk::Device device)
Cleanup pipeline resources.
const ShaderReflection & get_shader_reflection() const
Get shader module reflection data.
void dispatch_indirect(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset=0)
Dispatch compute shader indirectly from GPU buffer.
static std::array< uint32_t, 3 > calculate_dispatch_2d(uint32_t width, uint32_t height, uint32_t workgroup_x, uint32_t workgroup_y)
Calculate 2D dispatch size.
static uint32_t calculate_workgroups(uint32_t element_count, uint32_t workgroup_size)
Calculate number of workgroups needed.
void dispatch(vk::CommandBuffer cmd, uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) const
Dispatch compute workgroups.
std::shared_ptr< VKShaderModule > m_shader
void dispatch_2d(vk::CommandBuffer cmd, uint32_t width_elements, uint32_t height_elements, uint32_t local_size_x, uint32_t local_size_y) const
Dispatch compute workgroups in 2D with automatic calculation.
static std::array< uint32_t, 3 > calculate_dispatch_1d(uint32_t element_count, uint32_t workgroup_size)
Calculate dispatch size from element count.
std::optional< std::array< uint32_t, 3 > > get_workgroup_size() const
Get shader workgroup size from reflection.
void bind_descriptor_sets(vk::CommandBuffer cmd, const std::vector< vk::DescriptorSet > &descriptor_sets, uint32_t first_set=0, const std::vector< uint32_t > &dynamic_offsets={}) const
Bind descriptor sets to pipeline.
bool create(vk::Device device, const ComputePipelineConfig &config)
Create compute pipeline from configuration.
VKComputePipeline & operator=(const VKComputePipeline &)=delete
void push_constants(vk::CommandBuffer cmd, vk::ShaderStageFlags stage_flags, uint32_t offset, uint32_t size, const void *data) const
Update push constants.
void dispatch_3d(vk::CommandBuffer cmd, uint32_t width_elements, uint32_t height_elements, uint32_t depth_elements, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z) const
Dispatch compute workgroups in 3D with automatic calculation.
static std::array< uint32_t, 3 > calculate_dispatch_3d(uint32_t width, uint32_t height, uint32_t depth, uint32_t workgroup_x, uint32_t workgroup_y, uint32_t workgroup_z)
Calculate 3D dispatch size.
vk::PipelineLayout create_pipeline_layout(vk::Device device, const ComputePipelineConfig &config)
Create pipeline layout from configuration.
Wrapper for Vulkan compute pipeline with simplified interface.
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
std::vector< vk::DescriptorSetLayout > set_layouts
Descriptor layouts.
std::shared_ptr< VKShaderModule > shader
Compute shader.
std::vector< PushConstantRange > push_constants
Push constant ranges.
Configuration for creating a compute pipeline.
Metadata extracted from shader module.