I started building a little renderer to try out Vulkan (coming from OpenGL). I started by a good old triangle but the fragment shader doesn't want to output anything. My output image (coming directly from the swapchain) stays filled with the clear color. I'm using Vulkan 1.3 for dynamic rendering.
Using RenderDoc, I can see that the vertex shader works, but the output stays desperately empty. Here are some parts of my capture (if that can help) :
Rasterizer tab of my graphics pipeline during the vkCmdDraw call
I try to disable rasterizer discard, disable face culling. I also try to tweak the blend state in all way possible, but nothing changed.
Here is my mainloop (if that can help) :
VkRenderingAttachmentInfo renderingAttachmentInfos {};
renderingAttachmentInfos.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
renderingAttachmentInfos.clearValue.color = {{0.1f, 0.1f, 0.1f, 1.f}};
renderingAttachmentInfos.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
renderingAttachmentInfos.imageView = context.getSwapchain()->getImageViews()[imageIndex];
renderingAttachmentInfos.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
renderingAttachmentInfos.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
renderingAttachmentInfos.resolveMode = VK_RESOLVE_MODE_NONE;
renderingAttachmentInfos.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
renderingAttachmentInfos.resolveImageView = VK_NULL_HANDLE;
VkRenderingInfo renderingInfos {};
renderingInfos.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfos.colorAttachmentCount = 1;
renderingInfos.pColorAttachments = &renderingAttachmentInfos;
renderingInfos.renderArea.offset = {0, 0};
renderingInfos.renderArea.extent = context.getSwapchain()->getExtent();
renderingInfos.layerCount = 1;
renderingInfos.viewMask = 0;
VkCommandBufferBeginInfo beginInfos {};
beginInfos.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfos.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfos.pInheritanceInfo = nullptr;
(void)vkBeginCommandBuffer(graphicsCommandBuffer, &beginInfos);
VkImageMemoryBarrier imageMemoryBarrier {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.image = context.getSwapchain()->getImages()[imageIndex];
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(
graphicsCommandBuffer,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier
);
vkCmdBeginRendering(graphicsCommandBuffer, &renderingInfos);
vkCmdBindPipeline(graphicsCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.getPipeline());
VkViewport viewport {};
viewport.x = 0.f;
viewport.y = 0.f;
viewport.width = context.getSwapchain()->getExtent().width;
viewport.height = context.getSwapchain()->getExtent().height;
viewport.minDepth = 0.f;
viewport.maxDepth = 1.f;
vkCmdSetViewport(graphicsCommandBuffer, 0, 1, &viewport);
VkRect2D scissor {};
scissor.offset = {0, 0};
scissor.extent = context.getSwapchain()->getExtent();
vkCmdSetScissor(graphicsCommandBuffer, 0, 1, &scissor);
VkBuffer buffer {vertexBuffer.getBuffer()};
VkDeviceSize offset {vertexBufferView.getInfos().offset};
vkCmdBindVertexBuffers(graphicsCommandBuffer, 0, 1, &buffer, &offset);
vkCmdDraw(graphicsCommandBuffer, vertices.size() / 5, vertices.size() / 15, 0, 0);
vkCmdEndRendering(graphicsCommandBuffer);
imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.image = context.getSwapchain()->getImages()[imageIndex];
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(
graphicsCommandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier
);
(void)vkEndCommandBuffer(graphicsCommandBuffer);
VkSubmitInfo submitInfos {};
VkPipelineStageFlags dstStageMask {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfos.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfos.commandBufferCount = 1;
submitInfos.pCommandBuffers = &graphicsCommandBuffer;
submitInfos.waitSemaphoreCount = 1;
submitInfos.pWaitSemaphores = &imageReadySemaphore;
submitInfos.pWaitDstStageMask = &dstStageMask;
submitInfos.signalSemaphoreCount = 1;
submitInfos.pSignalSemaphores = &imageDrawnSemaphore;
(void)vkQueueSubmit(context.getDevice()->getQueue(se::renderer::vulkan::QueueType::eGraphics), 1, &submitInfos, previousFrameReadyFence);
VkSwapchainKHR swapchain {context.getSwapchain()->getSwapChain()};
VkPresentInfoKHR presentInfos {};
presentInfos.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfos.swapchainCount = 1;
presentInfos.pSwapchains = &swapchain;
presentInfos.pImageIndices = &imageIndex;
presentInfos.pResults = nullptr;
presentInfos.waitSemaphoreCount = 1;
presentInfos.pWaitSemaphores = &imageDrawnSemaphore;
(void)vkQueuePresentKHR(context.getDevice()->getQueue(se::renderer::vulkan::QueueType::ePresent), &presentInfos);
After a bit more testing, the only place where my code works is with an Nvidia GPU (rtx 4060 Mobile) on Arch with the proprietary driver
The problem was that I was not passing VkPipelineRenderingCreateInfo
object in the pNext
chain of VkGraphicsPipelineCreateInfo