I've managed to write a "small" Vulkan program that outputs a solid color using compute shader directly to swapchain.
However, it doesn't work properly. ImageMemoryBarriers I've setup don't transition the layouts the way they should and only every third swapchain image (it has three in my implementation) is actually rendered to.
Two most interesting functions are the one where I record the command buffers and the one where is submit them to the compute/present queue:
Recording:
void createCommandBuffers() {
commandBuffers.resize(swapchainImages.size());
vk::CommandBufferAllocateInfo allocInfo = {};
allocInfo.commandPool = commandPool.get();
allocInfo.level = vk::CommandBufferLevel::ePrimary;
allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();
commandBuffers = device->allocateCommandBuffersUnique(allocInfo);
for (size_t i = 0; i < commandBuffers.size(); ++i) {
vk::CommandBufferBeginInfo beginInfo = {};
beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse;
commandBuffers[i]->begin(beginInfo);
commandBuffers[i]->bindPipeline(vk::PipelineBindPoint::eCompute, *pipeline);
commandBuffers[i]->bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipelineLayout, 0, descriptorSets[i], nullptr);
vk::ImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.oldLayout = vk::ImageLayout::eUndefined;
imageMemoryBarrier.newLayout = vk::ImageLayout::eGeneral;
imageMemoryBarrier.image = swapchainImages[i];
imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
commandBuffers[i]->pipelineBarrier(
vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eComputeShader,
vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr,
imageMemoryBarrier);
commandBuffers[i]->dispatch(WIDTH, HEIGHT, 1);
imageMemoryBarrier = {};
imageMemoryBarrier.oldLayout = vk::ImageLayout::eGeneral;
imageMemoryBarrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
imageMemoryBarrier.image = swapchainImages[i];
imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
commandBuffers[i]->pipelineBarrier(
vk::PipelineStageFlagBits::eComputeShader,
vk::PipelineStageFlagBits::eBottomOfPipe,
vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr,
imageMemoryBarrier);
commandBuffers[i]->end();
}
}
Submitting:
void drawFrame() {
device->waitForFences(*inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());
device->resetFences(*inFlightFences[currentFrame]);
uint32_t imageIndex;
try {
device->acquireNextImageKHR(*swapchain, std::numeric_limits<uint64_t>::max(), *imageAvailableSemaphores[currentFrame], {}, &imageIndex);
}
catch (vk::OutOfDateKHRError) {
recreateSwapchain();
return;
}
vk::SubmitInfo submitInfo = {};
vk::Semaphore waitSemaphores[] = { *imageAvailableSemaphores[currentFrame] };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &waitSemaphores[0];
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &*commandBuffers[imageIndex];
vk::Semaphore signalSemaphores[] = { *renderFinishedSemaphores[currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &signalSemaphores[0];
vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eTopOfPipe };
submitInfo.pWaitDstStageMask = waitStages;
computeQueue.submit(submitInfo, *inFlightFences[currentFrame]);
vk::PresentInfoKHR presentInfo = {};
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &signalSemaphores[0];
vk::SwapchainKHR swapchains[] = { *swapchain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapchains;
presentInfo.pImageIndices = &imageIndex;
try {
vk::Result result = computeQueue.presentKHR(presentInfo);
if (result == vk::Result::eSuboptimalKHR || framebufferResized) {
framebufferResized = false;
recreateSwapchain();
}
}
catch (vk::OutOfDateKHRError) {
framebufferResized = false;
recreateSwapchain();
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
The last function is basically the main loop at this point.
I've been staring at it for a while now and I don't get it. I don't know what is wrong. This is also the first Vulkan program I've written semi independently, so there's that as well.
Here's the rest of the code, if something is unclear: pastebin
Also, for completeness sake, here's the shader, as simple as can be:
#version 450
layout (binding = 0, rgba8) uniform image2D resultImage;
void main() {
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), green);
}