I have a minimalistic Vulkan pipeline set up and all it does is clearing the backbuffer before presenting it. However, if I set .commandBufferCount = 0
I get the following validation error:
vkQueuePresentKHR(): pSwapchains[0] images passed to present must be in layout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR or VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is in VK_IMAGE_LAYOUT_UNDEFINED. The Vulkan spec states: Each element of pImageIndices must be the index of a presentable image acquired from the swapchain specified by the corresponding element of the pSwapchains array, and the presented image subresource must be in the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout at the time the operation is executed on a VkDevice (https://github.com/KhronosGroup/Vulkan-Docs/search?q=)VUID-VkPresentInfoKHR-pImageIndices-01296)
The application still clears the image and seems to keep working, but why do I need to supply a command buffer for the backbuffer image transition into the expected layout? Doesn't the render pass take care of the transition?
Here is the code used for rendering; I'm using vulkan_raii.hpp:
device->waitForFences(*queueDoneFences[currentFrame % BACKBUFFER_COUNT], true, UINT64_MAX);
vk::Result res;
std::tie(res, currentSwapchainImageIndex) = device->acquireNextImageKHR(
*swapchain,
UINT64_MAX,
*imageAvailableSemaphores[currentFrame % BACKBUFFER_COUNT],
VK_NULL_HANDLE);
device->resetFences(*queueDoneFences[currentFrame % BACKBUFFER_COUNT]);
assert(res == vk::Result::eSuccess);
commandBuffers[currentFrame % BACKBUFFER_COUNT]->reset();
commandBuffers[currentFrame % BACKBUFFER_COUNT]->begin(
{.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
vk::ClearColorValue clearColor = {std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}};
vk::RenderPassBeginInfo info = {
.renderPass = *renderPass,
.framebuffer = *framebuffers[currentSwapchainImageIndex],
.renderArea =
{
.offset = {0, 0},
.extent = {1280, 720} // TODO
},
.clearValueCount = 1,
.pClearValues = (vk::ClearValue*)&clearColor,
};
commandBuffers[currentFrame % BACKBUFFER_COUNT]->bindPipeline(
vk::PipelineBindPoint::eGraphics,
*pipeline);
commandBuffers[currentFrame % BACKBUFFER_COUNT]->beginRenderPass(
info,
vk::SubpassContents::eInline);
commandBuffers[currentFrame % BACKBUFFER_COUNT]->endRenderPass();
commandBuffers[currentFrame % BACKBUFFER_COUNT]->end();
vk::PipelineStageFlags waitFlags = vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*imageAvailableSemaphores[currentFrame % BACKBUFFER_COUNT],
.pWaitDstStageMask = &waitFlags,
.commandBufferCount = 1,
.pCommandBuffers = &*commandBuffers[currentFrame % BACKBUFFER_COUNT],
.signalSemaphoreCount = 1,
.pSignalSemaphores = &*renderFinishedSemaphores[currentFrame % BACKBUFFER_COUNT],
};
graphicsQueue.submit({submitInfo}, *queueDoneFences[currentFrame % BACKBUFFER_COUNT]);
vk::PresentInfoKHR presentInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*renderFinishedSemaphores[currentFrame % BACKBUFFER_COUNT],
.swapchainCount = 1,
.pSwapchains = &*swapchain,
.pImageIndices = ¤tSwapchainImageIndex,
.pResults = nullptr,
};
// This will _not_ return success if the window is resized
assert(graphicsQueue.presentKHR(presentInfo) == vk::Result::eSuccess);
currentFrame++;
First of all, you start and end the render pass in the command buffer. So if you don't submit the command buffer, then the render pass will not run and won't transition the image from one layout to another.
Anyway, it doesn't really make sense to submit 0 command buffers. Also, not sure how the synchronization would work in this case, because according to the docs:
pSignalSemaphores is a pointer to an array of VkSemaphore handles which will be signaled when the command buffers for this batch have completed execution