Babysitter vulkan-compute
Vulkan compute shader development and pipeline configuration. Generate GLSL/HLSL compute shaders, compile to SPIR-V, configure compute pipelines, manage descriptor sets and resource bindings, implement memory barriers and synchronization.
git clone https://github.com/a5c-ai/babysitter
T=$(mktemp -d) && git clone --depth=1 https://github.com/a5c-ai/babysitter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/library/specializations/gpu-programming/skills/vulkan-compute" ~/.claude/skills/a5c-ai-babysitter-vulkan-compute && rm -rf "$T"
library/specializations/gpu-programming/skills/vulkan-compute/SKILL.mdvulkan-compute
You are vulkan-compute - a specialized skill for Vulkan compute shader development and pipeline configuration. This skill provides expert capabilities for GPU compute using the Vulkan API.
Overview
This skill enables AI-powered Vulkan compute operations including:
- Generate GLSL/HLSL compute shaders
- Compile shaders to SPIR-V bytecode
- Configure Vulkan compute pipelines
- Manage descriptor sets and resource bindings
- Handle push constants and specialization constants
- Configure workgroup dimensions and dispatch
- Implement memory barriers and synchronization
- Support Vulkan validation layers for debugging
Prerequisites
- Vulkan SDK 1.3+
- glslangValidator or glslc (SPIR-V compiler)
- SPIRV-Tools (optional)
- Vulkan-capable GPU
Capabilities
1. GLSL Compute Shader Generation
Generate GLSL compute shaders:
#version 450 // Workgroup size specification layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; // Buffer bindings layout(set = 0, binding = 0) readonly buffer InputBuffer { float inputData[]; }; layout(set = 0, binding = 1) writeonly buffer OutputBuffer { float outputData[]; }; // Push constants for runtime parameters layout(push_constant) uniform PushConstants { uint dataSize; float multiplier; } pc; void main() { uint gid = gl_GlobalInvocationID.x; if (gid < pc.dataSize) { outputData[gid] = inputData[gid] * pc.multiplier; } }
2. SPIR-V Compilation
Compile shaders to SPIR-V:
# Using glslangValidator glslangValidator -V compute.glsl -o compute.spv # Using glslc (Google's compiler) glslc -fshader-stage=compute compute.glsl -o compute.spv # With optimization glslc -O compute.glsl -o compute.spv # Generate human-readable SPIR-V spirv-dis compute.spv -o compute.spvasm # Validate SPIR-V spirv-val compute.spv # Optimize SPIR-V spirv-opt -O compute.spv -o compute_opt.spv
3. Compute Pipeline Creation
Create Vulkan compute pipelines:
// Load SPIR-V shader VkShaderModuleCreateInfo shaderInfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = spirvSize, .pCode = spirvCode }; VkShaderModule shaderModule; vkCreateShaderModule(device, &shaderInfo, NULL, &shaderModule); // Pipeline layout with descriptor set and push constants VkPushConstantRange pushConstantRange = { .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, .offset = 0, .size = sizeof(PushConstants) }; VkPipelineLayoutCreateInfo layoutInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = &descriptorSetLayout, .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstantRange }; VkPipelineLayout pipelineLayout; vkCreatePipelineLayout(device, &layoutInfo, NULL, &pipelineLayout); // Create compute pipeline VkComputePipelineCreateInfo pipelineInfo = { .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, .stage = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_COMPUTE_BIT, .module = shaderModule, .pName = "main" }, .layout = pipelineLayout }; VkPipeline computePipeline; vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &computePipeline);
4. Descriptor Set Management
Configure resource bindings:
// Descriptor set layout VkDescriptorSetLayoutBinding bindings[] = { { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT }, { .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT } }; VkDescriptorSetLayoutCreateInfo layoutInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 2, .pBindings = bindings }; VkDescriptorSetLayout descriptorSetLayout; vkCreateDescriptorSetLayout(device, &layoutInfo, NULL, &descriptorSetLayout); // Allocate and update descriptor set VkDescriptorBufferInfo inputBufferInfo = { .buffer = inputBuffer, .offset = 0, .range = VK_WHOLE_SIZE }; VkDescriptorBufferInfo outputBufferInfo = { .buffer = outputBuffer, .offset = 0, .range = VK_WHOLE_SIZE }; VkWriteDescriptorSet writes[] = { { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptorSet, .dstBinding = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &inputBufferInfo }, { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptorSet, .dstBinding = 1, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &outputBufferInfo } }; vkUpdateDescriptorSets(device, 2, writes, 0, NULL);
5. Specialization Constants
Runtime shader customization:
// In shader layout(constant_id = 0) const uint WORKGROUP_SIZE = 256; layout(constant_id = 1) const bool USE_FAST_MATH = false; layout(local_size_x_id = 0) in;
// In C code VkSpecializationMapEntry entries[] = { {0, 0, sizeof(uint32_t)}, // WORKGROUP_SIZE {1, sizeof(uint32_t), sizeof(VkBool32)} // USE_FAST_MATH }; struct { uint32_t workgroupSize; VkBool32 useFastMath; } specData = {512, VK_TRUE}; VkSpecializationInfo specInfo = { .mapEntryCount = 2, .pMapEntries = entries, .dataSize = sizeof(specData), .pData = &specData }; // Use in pipeline creation pipelineInfo.stage.pSpecializationInfo = &specInfo;
6. Compute Dispatch
Execute compute work:
// Record command buffer vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PushConstants), &pushConstants); // Dispatch uint32_t groupCountX = (dataSize + 255) / 256; vkCmdDispatch(commandBuffer, groupCountX, 1, 1); // Indirect dispatch vkCmdDispatchIndirect(commandBuffer, indirectBuffer, 0);
7. Memory Barriers and Synchronization
Proper synchronization:
// Buffer memory barrier VkBufferMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = buffer, .offset = 0, .size = VK_WHOLE_SIZE }; vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 1, &barrier, 0, NULL); // Memory barrier for compute-to-transfer VkMemoryBarrier memoryBarrier = { .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT }; vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &memoryBarrier, 0, NULL, 0, NULL);
8. Validation Layers
Debug with validation:
// Enable validation layers const char* validationLayers[] = { "VK_LAYER_KHRONOS_validation" }; VkInstanceCreateInfo createInfo = { .enabledLayerCount = 1, .ppEnabledLayerNames = validationLayers }; // Debug messenger callback VkDebugUtilsMessengerCreateInfoEXT debugInfo = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, .pfnUserCallback = debugCallback };
Process Integration
This skill integrates with the following processes:
- Compute shader workflowscompute-shader-development.js
Output Format
{ "operation": "compile-shader", "status": "success", "input": "compute.glsl", "output": "compute.spv", "spirv_size": 1024, "workgroup_size": [256, 1, 1], "bindings": [ {"binding": 0, "type": "storage_buffer", "access": "readonly"}, {"binding": 1, "type": "storage_buffer", "access": "writeonly"} ], "push_constants_size": 8, "artifacts": ["compute.spv", "compute.spvasm"] }
Dependencies
- Vulkan SDK 1.3+
- glslangValidator or glslc
- SPIRV-Tools (optional)
Constraints
- Workgroup size limited by device (usually 1024 threads)
- Descriptor set count limited (usually 4)
- Push constant size limited (128+ bytes)
- SPIR-V version must match Vulkan version