void initVulkan() { VulkanAndroidExampleBase::initVulkan(); loadTexture( "textures/android_robot.ktx", VK_FORMAT_R8G8B8A8_UNORM, &texture, false); createCommandBuffers(); // Compute stuff getComputeQueue(); createComputeCommandBuffer(); prepareStorageBuffers(); prepareUniformBuffers(); setupDescriptorSetLayout(); preparePipelines(); setupDescriptorPool(); setupDescriptorSet(); prepareCompute(); buildCommandBuffers(); buildComputeCommandBuffer(); state.zoom = -5.0f; state.rotation = glm::vec3(); prepared = true; }
void op3d::Engine::initVulkan() { instance.create(); callback.setup(instance); surface.create(instance, window); physicalDevice.create(instance, surface); device.create(physicalDevice, surface, graphicsQueue, presentQueue); swapChain.create(device, surface, physicalDevice, window); swapChain.createImageViews(device, swapChainImageViews); createRenderPass(); createDescriptorSetLayout(); createGraphicsPipeline(); commandBufferManager.createCommandPool(physicalDevice, surface); createDepthResources(); createFramebuffers(); createTextureImage(); createTextureImageView(); createTextureSampler(); createVertexBuffer(); createIndexBuffer(); createUniformBuffer(); descriptorPool.createPool(); descriptorSet.createSet(uniformBuffer, textureImageView, textureSampler, descriptorSetLayout, descriptorPool, device); createCommandBuffers(); createSemaphores(); }
void reBuildCommandBuffers() { if (!checkCommandBuffers()) { destroyCommandBuffers(); createCommandBuffers(); } buildCommandBuffers(); }
void op3d::Engine::recreateSwapChain() { vkDeviceWaitIdle(device); swapChain.create(device, surface, physicalDevice, window); swapChain.createImageViews(device, swapChainImageViews); createRenderPass(); createGraphicsPipeline(); createDepthResources(); createFramebuffers(); createCommandBuffers(); }
void VulkanBase::prepare() { if (enableValidation) vkDebug::setupDebugging(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, NULL); createCommandPool(); createSetupCommandBuffer(); setupSwapChain(); createCommandBuffers(); setupDepthStencil(); setupRenderPass(); createPipelineCache(); setupFrameBuffer(); flushSetupCommandBuffer(); // Recreate setup command buffer for derived class createSetupCommandBuffer(); // Create a simple texture loader class textureLoader = new vkTools::VulkanTextureLoader(physicalDevice, device, queue, cmdPool); }
void VulkanExampleBase::windowResize() { if (!prepared) { return; } prepared = false; // Recreate swap chain width = destWidth; height = destHeight; createSetupCommandBuffer(); setupSwapChain(); // Recreate the frame buffers vkDestroyImageView(device, depthStencil.view, nullptr); vkDestroyImage(device, depthStencil.image, nullptr); vkFreeMemory(device, depthStencil.mem, nullptr); setupDepthStencil(); for (uint32_t i = 0; i < frameBuffers.size(); i++) { vkDestroyFramebuffer(device, frameBuffers[i], nullptr); } setupFrameBuffer(); flushSetupCommandBuffer(); // Command buffers need to be recreated as they may store // references to the recreated frame buffer destroyCommandBuffers(); createCommandBuffers(); buildCommandBuffers(); vkQueueWaitIdle(queue); vkDeviceWaitIdle(device); // Notify derived class windowResized(); viewChanged(); prepared = true; }
void buildCommandBuffers() { // Destroy command buffers if already present if (!checkCommandBuffers()) { destroyCommandBuffers(); createCommandBuffers(); } VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; clearValues[0].color = defaultClearColor; clearValues[1].depthStencil = { 1.0f, 0 }; VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; renderPassBeginInfo.clearValueCount = 2; renderPassBeginInfo.pClearValues = clearValues; for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { // Set target frame buffer renderPassBeginInfo.framebuffer = frameBuffers[i]; VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); // Compute particle movement // Add memory barrier to ensure that the (rendering) vertex shader operations have finished // Required as the compute shader will overwrite the vertex buffer data VkBufferMemoryBarrier bufferBarrier = vkTools::initializers::bufferMemoryBarrier(); // Vertex shader invocations have finished reading from the buffer bufferBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; // Compute shader buffer read and write bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; bufferBarrier.buffer = computeStorageBuffer.buffer; bufferBarrier.size = computeStorageBuffer.descriptor.range; bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkCmdPipelineBarrier( drawCmdBuffers[i], VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 0, nullptr, 1, &bufferBarrier, 0, nullptr); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, pipelines.compute); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 0, 1, &computeDescriptorSet, 0, 0); // Dispatch the compute job vkCmdDispatch(drawCmdBuffers[i], PARTICLE_COUNT / 16, 1, 1); // Add memory barrier to ensure that compute shader has finished writing to the buffer // Without this the (rendering) vertex shader may display incomplete results (partial data from last frame) // Compute shader has finished writes to the buffer bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // Vertex shader access (attribute binding) bufferBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; bufferBarrier.buffer = computeStorageBuffer.buffer; bufferBarrier.size = computeStorageBuffer.descriptor.range; bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkCmdPipelineBarrier( drawCmdBuffers[i], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_FLAGS_NONE, 0, nullptr, 1, &bufferBarrier, 0, nullptr); // Draw the particle system using the update vertex buffer vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.postCompute); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSetPostCompute, 0, NULL); VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &computeStorageBuffer.buffer, offsets); vkCmdDraw(drawCmdBuffers[i], PARTICLE_COUNT, 1, 0, 0); vkCmdEndRenderPass(drawCmdBuffers[i]); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); } }
void buildCommandBuffers() { // Destroy command buffers if already present if (!checkCommandBuffers()) { destroyCommandBuffers(); createCommandBuffers(); } VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; clearValues[0].color = defaultClearColor; clearValues[1].depthStencil = { 1.0f, 0 }; VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; renderPassBeginInfo.clearValueCount = 2; renderPassBeginInfo.pClearValues = clearValues; VkResult err; for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { // Set target frame buffer renderPassBeginInfo.framebuffer = frameBuffers[i]; err = vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo); assert(!err); // Buffer memory barrier to make sure that compute shader // writes are finished before using the storage buffer // in the vertex shader VkBufferMemoryBarrier bufferBarrier = vkTools::initializers::bufferMemoryBarrier(); // Source access : Compute shader buffer write bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // Dest access : Vertex shader access (attribute binding) bufferBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; bufferBarrier.buffer = computeStorageBuffer.buffer; bufferBarrier.offset = 0; bufferBarrier.size = computeStorageBuffer.descriptor.range; bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkCmdPipelineBarrier( drawCmdBuffers[i], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_FLAGS_NONE, 0, nullptr, 1, &bufferBarrier, 0, nullptr); vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport = vkTools::initializers::viewport( (float)width, (float)height, 0.0f, 1.0f ); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); VkRect2D scissor = vkTools::initializers::rect2D( width, height, 0, 0 ); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSetPostCompute, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.postCompute); VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &computeStorageBuffer.buffer, offsets); vkCmdDraw(drawCmdBuffers[i], PARTICLE_COUNT, 1, 0, 0); vkCmdEndRenderPass(drawCmdBuffers[i]); err = vkEndCommandBuffer(drawCmdBuffers[i]); assert(!err); } }
void VulkanBase::createInstance() { // Application info init const VkApplicationInfo applicationInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = NULL, .pApplicationName = name.c_str(), .applicationVersion = 1, .pEngineName = engineName.c_str(), .engineVersion = 1, .apiVersion = VK_API_VERSION, //FIXME Nvidia driver not updated to latest Vulkan Version }; VkInstanceCreateInfo instanceCreateInfo = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE, .pApplicationInfo = &applicationInfo, .enabledLayerCount = 0, .ppEnabledLayerNames = NULL, .enabledExtensionCount = 0, .ppEnabledExtensionNames = NULL, }; std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_EXTENSION_NAME}; //Check if extensions are present vkUtils::checkGlobalExtensionPresent(VK_KHR_SURFACE_EXTENSION_NAME); vkUtils::checkGlobalExtensionPresent(VK_KHR_XCB_SURFACE_EXTENSION_NAME); #ifdef _DEBUG if (enableValidation) { //Extensions management enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); vkUtils::checkGlobalExtensionPresent(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); // Layer management instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; // Check standard debug layers are present for(uint32_t i = 0; i < instanceCreateInfo.enabledLayerCount; i++) { vkUtils::checkGlobalLayerPresent(vkDebug::validationLayerNames[i]); } } #endif // DEBUG instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); instanceCreateInfo.enabledExtensionCount = (uint32_t) enabledExtensions.size(); CHECK_RESULT(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); } void VulkanBase::selectVkPhysicalDevice() { uint32_t physicalDeviceCount = 0; CHECK_RESULT(vkEnumeratePhysicalDevices(instance,&physicalDeviceCount,nullptr)); if (physicalDeviceCount<=0) { ERROR("No physical device found"); } std::vector<VkPhysicalDevice> physicalDevicesVector(physicalDeviceCount); CHECK_RESULT(vkEnumeratePhysicalDevices(instance,&physicalDeviceCount,physicalDevicesVector.data())); #ifdef _DEBUG int deviceIndex = 0; for(const auto & phyDev : physicalDevicesVector) { VkPhysicalDeviceProperties phyDevProperties; vkGetPhysicalDeviceProperties(phyDev, &phyDevProperties); std::cout << "--- Physical device: " << phyDevProperties.deviceName << " (index: " << (deviceIndex++) << ")" << std::endl; std::cout << " apiVersion: " << phyDevProperties.apiVersion << std::endl; std::cout << " driverVersion: " << phyDevProperties.driverVersion << std::endl; std::cout << " vendorID: " << phyDevProperties.vendorID << std::endl; std::cout << " deviceID: " << phyDevProperties.deviceID << std::endl; std::cout << " deviceType: "; switch(phyDevProperties.deviceType) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: std::cout << "OTHER"; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: std::cout << "INTEGRATED_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: std::cout << "DISCRETE_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: std::cout << "VIRTUAL_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: std::cout << "CPU"; break; default: std::cout << "UNKNOWN!!!"; break; } std::cout << std::endl; } #endif // _DEBUG physicalDevice = physicalDevicesVector.at(0); // Gather Physical Device Memory Properties vkGetPhysicalDeviceMemoryProperties(physicalDevice,&physicalDeviceMemoryProperties); } void VulkanBase::selectQueue() { uint32_t queueFamilyPropertyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,&queueFamilyPropertyCount,nullptr); if (queueFamilyPropertyCount<=0) ERROR("Physical device has no queue families"); std::vector<VkQueueFamilyProperties> queueFamilyPropertiesVector(queueFamilyPropertyCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,&queueFamilyPropertyCount,queueFamilyPropertiesVector.data()); uint32_t queueFamilyIndex = 0; int32_t selectedQueueFamilyIndex = -1; VkBool32 presentSupport = VK_FALSE; #ifdef _DEBUG std::cout << std::endl << "--- Number of queue families " << queueFamilyPropertyCount << std::endl; #endif // _DEBUG for(const auto & queueFamProp : queueFamilyPropertiesVector) { CHECK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, swapchain.surface, &presentSupport)); #ifdef _DEBUG std::cout << "--- Properties for queue family " << queueFamilyIndex << std::endl; std::cout << " queueFlags:"; if(queueFamProp.queueFlags & VK_QUEUE_GRAPHICS_BIT) std::cout << " G"; if(queueFamProp.queueFlags & VK_QUEUE_COMPUTE_BIT) std::cout << " C"; if(queueFamProp.queueFlags & VK_QUEUE_TRANSFER_BIT) std::cout << " T"; if(queueFamProp.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) std::cout << " S"; std::cout << '\n'; std::cout << " queueCount: " << queueFamProp.queueCount << std::endl; std::cout << " timestampValidBits: " << queueFamProp.timestampValidBits << std::endl; std::cout << " minImageTransferGranularity: " << queueFamProp.minImageTransferGranularity.width << ", " << queueFamProp.minImageTransferGranularity.height << ", " << queueFamProp.minImageTransferGranularity.depth << std::endl; std::cout << " Supports present?: " << std::boolalpha << bool(presentSupport) << std::endl << std::endl; #endif // _DEBUG if (bool(queueFamProp.queueFlags & VK_QUEUE_GRAPHICS_BIT) && presentSupport == VK_TRUE) { if (selectedQueueFamilyIndex < 0) selectedQueueFamilyIndex = queueFamilyIndex; } queueFamilyIndex++; } if (selectedQueueFamilyIndex<0) ERROR("No queue with both graphics and present capabilities found"); // Create device after selecting the queue std::array<float,1> queuePriorities = {0.0f}; VkDeviceQueueCreateInfo queueCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .pNext = nullptr, .flags = VK_FLAGS_NONE, .queueFamilyIndex = (uint32_t) selectedQueueFamilyIndex, .queueCount = 1, //Number of queues to create .pQueuePriorities = queuePriorities.data() }; // Call to createDevice createDevice(queueCreateInfo,1); //Get a handle to the selected queue vkGetDeviceQueue(device, (uint32_t) selectedQueueFamilyIndex, 0, &queue); //TODO get handle if using multiple queues queueFamilyIndex = (uint32_t) selectedQueueFamilyIndex; } void VulkanBase::createDevice(VkDeviceQueueCreateInfo requestedQueues, uint32_t requestedQueuesCount) { //Check extensions available on the selected physical device before creating it // Check swap chain extension vkUtils::checkDeviceExtensionPresent(physicalDevice,VK_KHR_SWAPCHAIN_EXTENSION_NAME); std::vector<const char*> enabledExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; VkDeviceCreateInfo deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = nullptr, .flags = VK_FLAGS_NONE, .queueCreateInfoCount = requestedQueuesCount, .pQueueCreateInfos = &requestedQueues, .enabledLayerCount = 0, .ppEnabledLayerNames = nullptr, .enabledExtensionCount = (uint32_t) enabledExtensions.size(), .ppEnabledExtensionNames = enabledExtensions.data(), .pEnabledFeatures = NULL }; #ifdef _DEBUG if (enableValidation) { deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; // Check standard debug layers are present on the device for(uint32_t i = 0; i < deviceCreateInfo.enabledLayerCount; i++) { vkUtils::checkGlobalLayerPresent(vkDebug::validationLayerNames[i]); } } #endif // _DEBUG CHECK_RESULT(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device)); } void VulkanBase::createCommandPool(const uint32_t queueFamilyIndex, const VkCommandPoolCreateFlagBits createFlagBits) { const VkCommandPoolCreateInfo commandPoolCreateInfo= { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = NULL, .flags = createFlagBits, .queueFamilyIndex = queueFamilyIndex }; CHECK_RESULT(vkCreateCommandPool(device, &commandPoolCreateInfo,nullptr,&commandPool)); #ifdef _DEBUG std::cout << "\n+++ Created command pool" << std::endl; #endif // _DEBUG } void VulkanBase::createSynchroItems() { // Semaphores VkSemaphoreCreateInfo semaphoreCreateInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE }; // Semaphore signaled on swapchain image ready to use and wait on the queue before rendering/present CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAcquiredSemaphore)); // Semaphore signaled on queue rendering termination and waited on present operation CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderingCompletedSemaphore)); // Fences VkFenceCreateInfo fenceCreateInfo = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE }; CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, nullptr, &presentFence)); #ifdef _DEBUG std::cout << "\n+++ Created semaphores and fences\n"; #endif // _DEBUG } void VulkanBase::createCommandBuffers(VkCommandBuffer* cmdBuffer, uint32_t commandBufferCount, VkCommandBufferLevel cmdBufferLevel) { const VkCommandBufferAllocateInfo commandBufferAllocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = NULL, .commandPool = commandPool, .level = cmdBufferLevel, .commandBufferCount = commandBufferCount }; CHECK_RESULT(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, cmdBuffer)); #ifdef _DEBUG std::cout << "\n+++ Allocated " << commandBufferCount << " command buffers" << std::endl; #endif // _DEBUG } void VulkanBase::setupInitCommandBuffer() { VkCommandBufferBeginInfo commandBufferBeginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE, .pInheritanceInfo = NULL }; CHECK_RESULT(vkBeginCommandBuffer(initCommandBuffer, &commandBufferBeginInfo)); // Creates an image memory barrier to change the layout for every image on the swapchain VkImageMemoryBarrier imageMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, .srcAccessMask = VK_FLAGS_NONE, .dstAccessMask = VK_FLAGS_NONE, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = 0, .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} }; // Pipeline Barrier for each swapchain image for (const auto& image: swapchain.swapchainImagesVector){ imageMemoryBarrier.image = image; vkCmdPipelineBarrier(initCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, //Put barrier on top of the pipeline VK_FLAGS_NONE, 0, nullptr, // memoryBarrier 0, nullptr, // bufferMemoryBarrier 1, &imageMemoryBarrier); // imageMemoryBarrier } CHECK_RESULT(vkEndCommandBuffer(initCommandBuffer)); #ifdef _DEBUG std::cout << "\n+++ Finished recording initCommandBuffer\n"; #endif // _DEBUG } void VulkanBase::setupPresentCommandBuffer(const VkImage currentSwapchainImage, const float* clearColors) { VkCommandBufferBeginInfo commandBufferBeginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, .pInheritanceInfo = NULL }; CHECK_RESULT(vkBeginCommandBuffer(presentCommandBuffer, &commandBufferBeginInfo)); VkImageMemoryBarrier imageMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = currentSwapchainImage, .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} }; //Set barrier on top to change layout and access vkCmdPipelineBarrier(presentCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_FLAGS_NONE, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); VkClearColorValue clearColorValue; VkImageSubresourceRange imageSubresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; clearColorValue.float32[0] = clearColors[0]; clearColorValue.float32[1] = clearColors[1]; clearColorValue.float32[2] = clearColors[2]; clearColorValue.float32[3] = 1.0f; // Command to clear the swapchain image vkCmdClearColorImage(presentCommandBuffer,currentSwapchainImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColorValue, 1, &imageSubresourceRange); /* * Transition the swapchain image from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL * to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR */ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; // Set barrier end of pipeline vkCmdPipelineBarrier(presentCommandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier ); CHECK_RESULT(vkEndCommandBuffer(presentCommandBuffer)); #ifdef _DEBUG //std::cout << "\n+++ Finished recording presentCommandBuffer\n"; #endif // _DEBUG } void VulkanBase::renderFrame(const float* clearColors) { // Wait on previous frame fence (render too fast) //CHECK_RESULT(vkWaitForFences(device, 1, &presentFence, VK_TRUE, UINT64_MAX)); //CHECK_RESULT(vkResetFences(device, 1, &presentFence)); // Acquire next image on the swapchain uint32_t imageIndex = UINT64_MAX; CHECK_RESULT(vkAcquireNextImageKHR(device, swapchain.swapchain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &imageIndex)); // Setup the present command buffer setupPresentCommandBuffer(swapchain.swapchainImagesVector.at(imageIndex),clearColors); // Submit present command buffer to the queue // Waits on imageAcquiredSemaphore so it doesnt start rendering until the image from the swapchain is ready and // it also signals the renderingCompletedSemaphore used by the later present VkPipelineStageFlags pipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, .waitSemaphoreCount = 1, .pWaitSemaphores = &imageAcquiredSemaphore, .pWaitDstStageMask = &pipelineStageFlags, .commandBufferCount = 1, .pCommandBuffers = &presentCommandBuffer, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderingCompletedSemaphore }; CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); // Present the rendered image VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = NULL, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderingCompletedSemaphore, .swapchainCount = 1, .pSwapchains = &swapchain.swapchain, .pImageIndices = &imageIndex, .pResults = nullptr }; CHECK_RESULT(vkQueuePresentKHR(queue,&presentInfo)); CHECK_RESULT(vkQueueWaitIdle(queue)); //TODO Not sure this is the correct way... } void VulkanBase::prepare() { //Allocate command Buffers createCommandBuffers(&initCommandBuffer, 1, VK_COMMAND_BUFFER_LEVEL_PRIMARY); createCommandBuffers(&presentCommandBuffer, 1, VK_COMMAND_BUFFER_LEVEL_PRIMARY); commandBuffersVector.push_back(initCommandBuffer); commandBuffersVector.push_back(presentCommandBuffer); //Initialize command Buffers setupInitCommandBuffer(); // Submit initialization command buffer to the queue VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 0, .pWaitSemaphores = nullptr, .pWaitDstStageMask = VK_FLAGS_NONE, .commandBufferCount = 1, .pCommandBuffers = &initCommandBuffer, .signalSemaphoreCount = 0, .pSignalSemaphores = nullptr }; CHECK_RESULT(vkQueueSubmit(queue,1,&submitInfo, VK_NULL_HANDLE)); CHECK_RESULT(vkQueueWaitIdle(queue)); vkFreeCommandBuffers(device, commandPool, 1, &initCommandBuffer); #ifdef _DEBUG std::cout << "\n+++ initCommandBuffer work complete!\n"; std::cout << "\n******* Rendering Start ******\n"; #endif // _DEBUG }