15 "VKComputePipeline destroyed without cleanup() - potential leak");
20 : m_pipeline(other.m_pipeline)
21 , m_layout(other.m_layout)
22 , m_workgroup_size(other.m_workgroup_size)
24 other.m_pipeline =
nullptr;
25 other.m_layout =
nullptr;
31 if (m_pipeline || m_layout) {
33 "VKComputePipeline move-assigned without cleanup() - potential leak");
36 m_pipeline = other.m_pipeline;
37 m_layout = other.m_layout;
38 m_workgroup_size = other.m_workgroup_size;
40 other.m_pipeline =
nullptr;
41 other.m_layout =
nullptr;
52 "Compute pipeline destroyed");
56 device.destroyPipelineLayout(
m_layout);
59 "Pipeline layout destroyed");
73 "Cannot create compute pipeline without shader");
77 if (!config.
shader->is_valid()) {
79 "Cannot create compute pipeline with invalid shader module");
83 if (config.
shader->get_stage() != vk::ShaderStageFlagBits::eCompute) {
85 "Shader is not a compute shader (stage: {})",
86 vk::to_string(config.
shader->get_stage()));
93 "Failed to create pipeline layout");
97 auto shader_stage = config.
shader->get_stage_create_info();
99 vk::ComputePipelineCreateInfo pipeline_info;
100 pipeline_info.stage = shader_stage;
102 pipeline_info.basePipelineHandle =
nullptr;
103 pipeline_info.basePipelineIndex = -1;
106 auto result = device.createComputePipeline(config.
cache, pipeline_info);
107 if (result.result != vk::Result::eSuccess) {
109 "Failed to create compute pipeline: {}",
110 vk::to_string(result.result));
111 device.destroyPipelineLayout(
m_layout);
116 }
catch (
const vk::SystemError& e) {
118 "Failed to create compute pipeline: {}", e.what());
119 device.destroyPipelineLayout(
m_layout);
124 const auto& reflection = config.
shader->get_reflection();
129 "Compute pipeline created (workgroup: {}x{}x{}, {} descriptor sets, {} push constants)",
134 "Compute pipeline created ({} descriptor sets, {} push constants)",
145 std::vector<vk::PushConstantRange> vk_push_constants;
149 vk::PushConstantRange range;
150 range.stageFlags = pc.stage_flags;
151 range.offset = pc.offset;
152 range.size = pc.size;
153 vk_push_constants.push_back(range);
156 vk::PipelineLayoutCreateInfo layout_info;
157 layout_info.setLayoutCount =
static_cast<uint32_t
>(config.
set_layouts.size());
159 layout_info.pushConstantRangeCount =
static_cast<uint32_t
>(vk_push_constants.size());
160 layout_info.pPushConstantRanges = vk_push_constants.empty() ? nullptr : vk_push_constants.data();
162 vk::PipelineLayout layout;
164 layout = device.createPipelineLayout(layout_info);
165 }
catch (
const vk::SystemError& e) {
167 "Failed to create pipeline layout: {}", e.what());
172 "Pipeline layout created ({} sets, {} push constant ranges)",
186 "Cannot bind invalid compute pipeline");
190 cmd.bindPipeline(vk::PipelineBindPoint::eCompute,
m_pipeline);
194 vk::CommandBuffer cmd,
195 const std::vector<vk::DescriptorSet>& descriptor_sets,
197 const std::vector<uint32_t>& dynamic_offsets)
const
201 "Cannot bind descriptor sets without pipeline layout");
205 if (descriptor_sets.empty()) {
207 "Binding empty descriptor sets");
211 for (
size_t i = 0; i < descriptor_sets.size(); ++i) {
212 if (!descriptor_sets[i]) {
214 "Descriptor set at index {} is null", first_set + i);
219 cmd.bindDescriptorSets(
220 vk::PipelineBindPoint::eCompute,
223 static_cast<uint32_t
>(descriptor_sets.size()),
224 descriptor_sets.data(),
225 static_cast<uint32_t
>(dynamic_offsets.size()),
226 dynamic_offsets.empty() ?
nullptr : dynamic_offsets.data());
230 vk::CommandBuffer cmd,
231 vk::ShaderStageFlags stage_flags,
234 const void* data)
const
238 "Cannot push constants without pipeline layout");
244 "Cannot push null data");
250 "Pushing zero-sized constant data");
254 cmd.pushConstants(
m_layout, stage_flags, offset, size, data);
262 vk::CommandBuffer cmd,
263 uint32_t group_count_x,
264 uint32_t group_count_y,
265 uint32_t group_count_z)
const
269 "Cannot dispatch invalid compute pipeline");
273 if (group_count_x == 0 || group_count_y == 0 || group_count_z == 0) {
275 "Dispatching with zero workgroups ({}x{}x{})",
276 group_count_x, group_count_y, group_count_z);
280 cmd.dispatch(group_count_x, group_count_y, group_count_z);
284 vk::CommandBuffer cmd,
285 uint32_t element_count,
286 uint32_t local_size_x)
const
288 if (local_size_x == 0) {
290 "Invalid workgroup size: {}", local_size_x);
299 vk::CommandBuffer cmd,
300 uint32_t width_elements,
301 uint32_t height_elements,
302 uint32_t local_size_x,
303 uint32_t local_size_y)
const
305 if (local_size_x == 0 || local_size_y == 0) {
307 "Invalid workgroup size: {}x{}", local_size_x, local_size_y);
313 dispatch(cmd, workgroups_x, workgroups_y, 1);
317 vk::CommandBuffer cmd,
318 uint32_t width_elements,
319 uint32_t height_elements,
320 uint32_t depth_elements,
321 uint32_t local_size_x,
322 uint32_t local_size_y,
323 uint32_t local_size_z)
const
325 if (local_size_x == 0 || local_size_y == 0 || local_size_z == 0) {
327 "Invalid workgroup size: {}x{}x{}", local_size_x, local_size_y, local_size_z);
334 dispatch(cmd, workgroups_x, workgroups_y, workgroups_z);
348 if (workgroup_size == 0) {
352 return (element_count + workgroup_size - 1) / workgroup_size;
358 const std::unordered_map<uint32_t, uint32_t>& specialization_data)
362 "Cannot create compute pipeline without shader");
366 config.
shader->set_specialization_constants(specialization_data);
367 return create(device, config);
371 uint32_t element_count,
372 uint32_t workgroup_size)
378 uint32_t width, uint32_t height,
379 uint32_t workgroup_x, uint32_t workgroup_y)
389 uint32_t width, uint32_t height, uint32_t depth,
390 uint32_t workgroup_x, uint32_t workgroup_y, uint32_t workgroup_z)
400 vk::CommandBuffer cmd,
402 vk::DeviceSize offset)
406 "Cannot dispatch invalid compute pipeline");
412 "Cannot dispatch with null indirect buffer");
416 cmd.dispatchIndirect(buffer, offset);
423 "Cannot get shader reflection - no shader attached");
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
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.
VKComputePipeline()=default
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 m_layout
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.