Will do some more looking into RenderDoc and the API.
I think these are the parts you are asking about right?
Pipeline (line 533 in project):
void createComputePipeline(
VkDevice& device,
char* shaderFile,
VkShaderModule* computeShaderModule,
VkDescriptorSetLayout* descriptorSetLayout,
VkPipelineLayout* pipelineLayout,
VkPipeline* pipeline,
float const* pushConstants,
uint32_t numPushConstants
) {
// Creates shader module (just a wrapper around our shader)
VkShaderModuleCreateInfo createInfo = {};
{
uint32_t filelength;
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.pCode = readFile(filelength, shaderFile);
createInfo.codeSize = filelength;
}
VK_CHECK_RESULT(vkCreateShaderModule(device, &createInfo, NULL, computeShaderModule));
VkPushConstantRange push_constant;
{
push_constant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
push_constant.offset = 0;
push_constant.size = numPushConstants * sizeof(float);
}
// The pipeline layout allows the pipeline to access descriptor sets.
// So we just specify the descriptor set layout we created earlier.
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
{
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = 1; // 1 shader
pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayout; // Descriptor set
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &push_constant;
}
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, NULL, pipelineLayout));
// Set our pipeline options
VkComputePipelineCreateInfo pipelineCreateInfo = {};
{
// We specify the compute shader stage, and it's entry point(main).
VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {};
{
shaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; // Shader type
shaderStageCreateInfo.module = *computeShaderModule; // Shader module
shaderStageCreateInfo.pName = "main"; // Shader entry point
}
// We set our pipeline options
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipelineCreateInfo.stage = shaderStageCreateInfo; // Shader stage info
pipelineCreateInfo.layout = *pipelineLayout;
}
// Create compute pipeline
VK_CHECK_RESULT(vkCreateComputePipelines(
device, VK_NULL_HANDLE,
1, &pipelineCreateInfo,
NULL, pipeline));
}
Dispatch (line 605 in project):
void createCommandBuffer(
uint32_t queueFamilyIndex,
VkDevice& device,
VkCommandPool* commandPool,
VkCommandBuffer* commandBuffer,
VkPipeline& pipeline,
VkPipelineLayout& pipelineLayout,
float const* pushConstants,
uint32_t numPushConstants,
int const* dims, // [x,y,z],
int const* dimLengths // [local_size_x, local_size_y, local_size_z]
) {
// Creates command pool
VkCommandPoolCreateInfo commandPoolCreateInfo = {};
{
commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
commandPoolCreateInfo.flags = 0;
// Sets queue family
commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
}
VK_CHECK_RESULT(vkCreateCommandPool(device, &commandPoolCreateInfo, NULL, commandPool));
// Allocates command buffer
VkCommandBufferAllocateInfo commandBufferAllocateInfo = {};
{
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAllocateInfo.commandPool = *commandPool; // Pool to allocate from
commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
commandBufferAllocateInfo.commandBufferCount = 1; // Allocates 1 command buffer.
}
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, commandBuffer)); // allocate command buffer.
// Allocated command buffer options
VkCommandBufferBeginInfo beginInfo = {};
{
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
// Buffer only submitted once
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
}
// Start recording commands
VK_CHECK_RESULT(vkBeginCommandBuffer(*commandBuffer, &beginInfo));
// Binds pipeline (our functions)
vkCmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
// Binds descriptor set (our data)
vkCmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
// Sets push constants
vkCmdPushConstants(*commandBuffer, pipelineLayout,VK_SHADER_STAGE_COMPUTE_BIT,0, numPushConstants*sizeof(float), pushConstants);
std::cout << "workgroups: " << '(' <<
(uint32_t)ceil(dims[0] / (float)dimLengths[0]) << ',' <<
(uint32_t)ceil(dims[1] / (float)dimLengths[1]) << ',' <<
(uint32_t)ceil(dims[2] / (float)dimLengths[2]) << ')' <<
std::endl << std::endl;
// Sets invocations
vkCmdDispatch(
*commandBuffer,
(uint32_t) ceil(dims[0] / (float) dimLengths[0]),
(uint32_t) ceil(dims[1] / (float) dimLengths[1]),
(uint32_t) ceil(dims[2] / (float) dimLengths[2])
);
// End recording commands
VK_CHECK_RESULT(vkEndCommandBuffer(*commandBuffer));
}
Running (line 674 in project):
void runCommandBuffer(
VkCommandBuffer* commandBuffer,
VkDevice& device,
VkQueue& queue
) {
VkSubmitInfo submitInfo = {};
{
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
// submit 1 command buffer
submitInfo.commandBufferCount = 1;
// pointer to array of command buffers to submit
submitInfo.pCommandBuffers = commandBuffer;
}
// Creates fence (so we can await for command buffer to finish)
VkFence fence;
VkFenceCreateInfo fenceCreateInfo = {};
{
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;
}
VK_CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, NULL, &fence));
// Submit command buffer with fence
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
// Wait for fence to signal (which it does when command buffer has finished)
VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, 100000000000));
// Destructs fence
vkDestroyFence(device, fence, NULL);
}
Reading (line 708 in project):
void printOutput(
VkDevice& device,
VkDeviceMemory& bufferMemory,
uint32_t size
) {
void* data = nullptr;
vkMapMemory(device, bufferMemory, 0, VK_WHOLE_SIZE, 0, &data);
float* actualData = (float*)data;
std::cout << "out:" << std::endl;
std::cout << '\t';
for (int i = 0; i < size; ++i) {
std::cout << actualData[i] << ' ';
}
std::cout << std::endl;
vkUnmapMemory(device, bufferMemory);
}