From aceb49e0be525a3a58b3c0c14ea8898108bcbd98 Mon Sep 17 00:00:00 2001 From: ink-soul Date: Tue, 6 Jun 2023 15:52:39 +0800 Subject: [PATCH] reconstruct render update lib --- base/VulkanRaytracingSample.cpp | 329 ---- base/VulkanRaytracingSample.h | 90 - base/VulkanSwapChain.cpp | 616 ------- base/VulkanSwapChain.h | 84 - base/VulkanSwapChain.hpp | 662 ++++++++ base/vulkanexamplebase.cpp | 2763 +++++++++++-------------------- base/vulkanexamplebase.h | 465 ++---- src/render/glTFModel.h | 1 + src/render/render.cpp | 285 ++-- src/render/render.h | 17 +- 10 files changed, 1878 insertions(+), 3434 deletions(-) delete mode 100644 base/VulkanRaytracingSample.cpp delete mode 100644 base/VulkanRaytracingSample.h delete mode 100644 base/VulkanSwapChain.cpp delete mode 100644 base/VulkanSwapChain.h create mode 100644 base/VulkanSwapChain.hpp diff --git a/base/VulkanRaytracingSample.cpp b/base/VulkanRaytracingSample.cpp deleted file mode 100644 index 60aeb70..0000000 --- a/base/VulkanRaytracingSample.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* -* Extended sample base class for ray tracing based samples -* -* Copyright (C) 2020-2021 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#include "VulkanRaytracingSample.h" - -void VulkanRaytracingSample::updateRenderPass() -{ - // Update the default render pass with different color attachment load ops to keep attachment contents - // With this change, we can e.g. draw an UI on top of the ray traced scene - - vkDestroyRenderPass(device, renderPass, nullptr); - - std::array attachments = {}; - // Color attachment - attachments[0].format = swapChain.colorFormat; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Depth attachment - attachments[1].format = depthFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depthReference = {}; - depthReference.attachment = 1; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pDepthStencilAttachment = &depthReference; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; - subpassDescription.pResolveAttachments = nullptr; - - // Subpass dependencies for layout transitions - std::array dependencies; - - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = static_cast(attachments.size()); - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpassDescription; - renderPassInfo.dependencyCount = static_cast(dependencies.size()); - renderPassInfo.pDependencies = dependencies.data(); - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)); -} - -void VulkanRaytracingSample::enableExtensions() -{ - // Require Vulkan 1.1 - apiVersion = VK_API_VERSION_1_1; - - // Ray tracing related extensions required by this sample - enabledDeviceExtensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME); - if (!rayQueryOnly) { - enabledDeviceExtensions.push_back(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); - } - - // Required by VK_KHR_acceleration_structure - enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); - enabledDeviceExtensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); - enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); - - // Required for VK_KHR_ray_tracing_pipeline - enabledDeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); - - // Required by VK_KHR_spirv_1_4 - enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); -} - -VulkanRaytracingSample::ScratchBuffer VulkanRaytracingSample::createScratchBuffer(VkDeviceSize size) -{ - ScratchBuffer scratchBuffer{}; - // Buffer and memory - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = size; - bufferCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; - VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &scratchBuffer.handle)); - VkMemoryRequirements memoryRequirements{}; - vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, scratchBuffer.handle, &memoryRequirements); - VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{}; - memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; - memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; - VkMemoryAllocateInfo memoryAllocateInfo = {}; - memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo; - memoryAllocateInfo.allocationSize = memoryRequirements.size; - memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &scratchBuffer.memory)); - VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, scratchBuffer.handle, scratchBuffer.memory, 0)); - // Buffer device address - VkBufferDeviceAddressInfoKHR bufferDeviceAddresInfo{}; - bufferDeviceAddresInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; - bufferDeviceAddresInfo.buffer = scratchBuffer.handle; - scratchBuffer.deviceAddress = vkGetBufferDeviceAddressKHR(vulkanDevice->logicalDevice, &bufferDeviceAddresInfo); - return scratchBuffer; -} - -void VulkanRaytracingSample::deleteScratchBuffer(ScratchBuffer& scratchBuffer) -{ - if (scratchBuffer.memory != VK_NULL_HANDLE) { - vkFreeMemory(vulkanDevice->logicalDevice, scratchBuffer.memory, nullptr); - } - if (scratchBuffer.handle != VK_NULL_HANDLE) { - vkDestroyBuffer(vulkanDevice->logicalDevice, scratchBuffer.handle, nullptr); - } -} - -void VulkanRaytracingSample::createAccelerationStructure(AccelerationStructure& accelerationStructure, VkAccelerationStructureTypeKHR type, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo) -{ - // Buffer and memory - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = buildSizeInfo.accelerationStructureSize; - bufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; - VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &accelerationStructure.buffer)); - VkMemoryRequirements memoryRequirements{}; - vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, accelerationStructure.buffer, &memoryRequirements); - VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{}; - memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; - memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; - VkMemoryAllocateInfo memoryAllocateInfo{}; - memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo; - memoryAllocateInfo.allocationSize = memoryRequirements.size; - memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &accelerationStructure.memory)); - VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, accelerationStructure.buffer, accelerationStructure.memory, 0)); - // Acceleration structure - VkAccelerationStructureCreateInfoKHR accelerationStructureCreate_info{}; - accelerationStructureCreate_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; - accelerationStructureCreate_info.buffer = accelerationStructure.buffer; - accelerationStructureCreate_info.size = buildSizeInfo.accelerationStructureSize; - accelerationStructureCreate_info.type = type; - vkCreateAccelerationStructureKHR(vulkanDevice->logicalDevice, &accelerationStructureCreate_info, nullptr, &accelerationStructure.handle); - // AS device address - VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; - accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; - accelerationDeviceAddressInfo.accelerationStructure = accelerationStructure.handle; - accelerationStructure.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(vulkanDevice->logicalDevice, &accelerationDeviceAddressInfo); -} - -void VulkanRaytracingSample::deleteAccelerationStructure(AccelerationStructure& accelerationStructure) -{ - vkFreeMemory(device, accelerationStructure.memory, nullptr); - vkDestroyBuffer(device, accelerationStructure.buffer, nullptr); - vkDestroyAccelerationStructureKHR(device, accelerationStructure.handle, nullptr); -} - -uint64_t VulkanRaytracingSample::getBufferDeviceAddress(VkBuffer buffer) -{ - VkBufferDeviceAddressInfoKHR bufferDeviceAI{}; - bufferDeviceAI.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; - bufferDeviceAI.buffer = buffer; - return vkGetBufferDeviceAddressKHR(vulkanDevice->logicalDevice, &bufferDeviceAI); -} - -void VulkanRaytracingSample::createStorageImage(VkFormat format, VkExtent3D extent) -{ - // Release ressources if image is to be recreated - if (storageImage.image != VK_NULL_HANDLE) { - vkDestroyImageView(device, storageImage.view, nullptr); - vkDestroyImage(device, storageImage.image, nullptr); - vkFreeMemory(device, storageImage.memory, nullptr); - storageImage = {}; - } - - VkImageCreateInfo image = vks::initializers::imageCreateInfo(); - image.imageType = VK_IMAGE_TYPE_2D; - image.format = format; - image.extent = extent; - image.mipLevels = 1; - image.arrayLayers = 1; - image.samples = VK_SAMPLE_COUNT_1_BIT; - image.tiling = VK_IMAGE_TILING_OPTIMAL; - image.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &image, nullptr, &storageImage.image)); - - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, storageImage.image, &memReqs); - VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); - memoryAllocateInfo.allocationSize = memReqs.size; - memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &storageImage.memory)); - VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, storageImage.image, storageImage.memory, 0)); - - VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); - colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; - colorImageView.format = format; - colorImageView.subresourceRange = {}; - colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - colorImageView.subresourceRange.baseMipLevel = 0; - colorImageView.subresourceRange.levelCount = 1; - colorImageView.subresourceRange.baseArrayLayer = 0; - colorImageView.subresourceRange.layerCount = 1; - colorImageView.image = storageImage.image; - VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &colorImageView, nullptr, &storageImage.view)); - - VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - vks::tools::setImageLayout(cmdBuffer, storageImage.image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_GENERAL, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - vulkanDevice->flushCommandBuffer(cmdBuffer, queue); -} - -void VulkanRaytracingSample::deleteStorageImage() -{ - vkDestroyImageView(vulkanDevice->logicalDevice, storageImage.view, nullptr); - vkDestroyImage(vulkanDevice->logicalDevice, storageImage.image, nullptr); - vkFreeMemory(vulkanDevice->logicalDevice, storageImage.memory, nullptr); -} - -void VulkanRaytracingSample::prepare() -{ - VulkanExampleBase::prepare(); - // Get properties and features - rayTracingPipelineProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; - VkPhysicalDeviceProperties2 deviceProperties2{}; - deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - deviceProperties2.pNext = &rayTracingPipelineProperties; - vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2); - accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; - VkPhysicalDeviceFeatures2 deviceFeatures2{}; - deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - deviceFeatures2.pNext = &accelerationStructureFeatures; - vkGetPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2); - // Get the function pointers required for ray tracing - vkGetBufferDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); - vkCmdBuildAccelerationStructuresKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR")); - vkBuildAccelerationStructuresKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR")); - vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR")); - vkDestroyAccelerationStructureKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR")); - vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR")); - vkGetAccelerationStructureDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR")); - vkCmdTraceRaysKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysKHR")); - vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR")); - vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); - // Update the render pass to keep the color attachment contents, so we can draw the UI on top of the ray traced output - if (!rayQueryOnly) { - updateRenderPass(); - } -} - -VkStridedDeviceAddressRegionKHR VulkanRaytracingSample::getSbtEntryStridedDeviceAddressRegion(VkBuffer buffer, uint32_t handleCount) -{ - const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment); - VkStridedDeviceAddressRegionKHR stridedDeviceAddressRegionKHR{}; - stridedDeviceAddressRegionKHR.deviceAddress = getBufferDeviceAddress(buffer); - stridedDeviceAddressRegionKHR.stride = handleSizeAligned; - stridedDeviceAddressRegionKHR.size = handleCount * handleSizeAligned; - return stridedDeviceAddressRegionKHR; -} - -void VulkanRaytracingSample::createShaderBindingTable(ShaderBindingTable& shaderBindingTable, uint32_t handleCount) -{ - // Create buffer to hold all shader handles for the SBT - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &shaderBindingTable, - rayTracingPipelineProperties.shaderGroupHandleSize * handleCount)); - // Get the strided address to be used when dispatching the rays - shaderBindingTable.stridedDeviceAddressRegion = getSbtEntryStridedDeviceAddressRegion(shaderBindingTable.buffer, handleCount); - // Map persistent - shaderBindingTable.map(); -} - -void VulkanRaytracingSample::drawUI(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer) -{ - VkClearValue clearValues[2]; - clearValues[0].color = defaultClearColor; - clearValues[1].depthStencil = { 1.0f, 0 }; - - VkRenderPassBeginInfo renderPassBeginInfo = vks::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; - renderPassBeginInfo.framebuffer = framebuffer; - - vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - VulkanExampleBase::drawUI(commandBuffer); - vkCmdEndRenderPass(commandBuffer); -} diff --git a/base/VulkanRaytracingSample.h b/base/VulkanRaytracingSample.h deleted file mode 100644 index 31ffeb3..0000000 --- a/base/VulkanRaytracingSample.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -* Extended sample base class for ray tracing based samples -* -* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once - -#include "vulkan/vulkan.h" -#include "vulkanexamplebase.h" -#include "VulkanTools.h" -#include "VulkanDevice.hpp" - -class VulkanRaytracingSample : public VulkanExampleBase -{ -protected: - // Update the default render pass with different color attachment load ops - virtual void updateRenderPass(); -public: - // Function pointers for ray tracing related stuff - PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; - PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; - PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR; - PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; - PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; - PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR; - PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; - PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; - PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; - PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; - - // Available features and properties - VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties{}; - VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{}; - - // Enabled features and properties - VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{}; - VkPhysicalDeviceRayTracingPipelineFeaturesKHR enabledRayTracingPipelineFeatures{}; - VkPhysicalDeviceAccelerationStructureFeaturesKHR enabledAccelerationStructureFeatures{}; - - // Holds information for a ray tracing scratch buffer that is used as a temporary storage - struct ScratchBuffer - { - uint64_t deviceAddress = 0; - VkBuffer handle = VK_NULL_HANDLE; - VkDeviceMemory memory = VK_NULL_HANDLE; - }; - - // Holds information for a ray tracing acceleration structure - struct AccelerationStructure { - VkAccelerationStructureKHR handle; - uint64_t deviceAddress = 0; - VkDeviceMemory memory; - VkBuffer buffer; - }; - - // Holds information for a storage image that the ray tracing shaders output to - struct StorageImage { - VkDeviceMemory memory = VK_NULL_HANDLE; - VkImage image = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - VkFormat format; - } storageImage; - - // Extends the buffer class and holds information for a shader binding table - class ShaderBindingTable : public vks::Buffer { - public: - VkStridedDeviceAddressRegionKHR stridedDeviceAddressRegion{}; - }; - - // Set to true, to denote that the sample only uses ray queries (changes extension and render pass handling) - bool rayQueryOnly = false; - - void enableExtensions(); - ScratchBuffer createScratchBuffer(VkDeviceSize size); - void deleteScratchBuffer(ScratchBuffer& scratchBuffer); - void createAccelerationStructure(AccelerationStructure& accelerationStructure, VkAccelerationStructureTypeKHR type, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo); - void deleteAccelerationStructure(AccelerationStructure& accelerationStructure); - uint64_t getBufferDeviceAddress(VkBuffer buffer); - void createStorageImage(VkFormat format, VkExtent3D extent); - void deleteStorageImage(); - VkStridedDeviceAddressRegionKHR getSbtEntryStridedDeviceAddressRegion(VkBuffer buffer, uint32_t handleCount); - void createShaderBindingTable(ShaderBindingTable& shaderBindingTable, uint32_t handleCount); - // Draw the ImGUI UI overlay using a render pass - void drawUI(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer); - - virtual void prepare(); -}; diff --git a/base/VulkanSwapChain.cpp b/base/VulkanSwapChain.cpp deleted file mode 100644 index e12405d..0000000 --- a/base/VulkanSwapChain.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* -* Class wrapping access to the swap chain -* -* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system -* -* Copyright (C) 2016-2021 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#include "VulkanSwapChain.h" - -/** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */ -#if defined(VK_USE_PLATFORM_WIN32_KHR) -void VulkanSwapChain::initSurface(void* platformHandle, void* platformWindow) -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) -void VulkanSwapChain::initSurface(ANativeWindow* window) -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) -void VulkanSwapChain::initSurface(IDirectFB* dfb, IDirectFBSurface* window) -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) -void VulkanSwapChain::initSurface(wl_display *display, wl_surface *window) -#elif defined(VK_USE_PLATFORM_XCB_KHR) -void VulkanSwapChain::initSurface(xcb_connection_t* connection, xcb_window_t window) -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) -void VulkanSwapChain::initSurface(void* view) -#elif (defined(_DIRECT2DISPLAY) || defined(VK_USE_PLATFORM_HEADLESS_EXT)) -void VulkanSwapChain::initSurface(uint32_t width, uint32_t height) -#endif -{ - VkResult err = VK_SUCCESS; - - // Create the os-specific surface -#if defined(VK_USE_PLATFORM_WIN32_KHR) - VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle; - surfaceCreateInfo.hwnd = (HWND)platformWindow; - err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.window = window; - err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface); -#elif defined(VK_USE_PLATFORM_IOS_MVK) - VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface); -#elif defined(_DIRECT2DISPLAY) - createDirect2DisplaySurface(width, height); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - VkDirectFBSurfaceCreateInfoEXT surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT; - surfaceCreateInfo.dfb = dfb; - surfaceCreateInfo.surface = window; - err = vkCreateDirectFBSurfaceEXT(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.display = display; - surfaceCreateInfo.surface = window; - err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.connection = connection; - surfaceCreateInfo.window = window; - err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - VkHeadlessSurfaceCreateInfoEXT surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT; - PFN_vkCreateHeadlessSurfaceEXT fpCreateHeadlessSurfaceEXT = (PFN_vkCreateHeadlessSurfaceEXT)vkGetInstanceProcAddr(instance, "vkCreateHeadlessSurfaceEXT"); - if (!fpCreateHeadlessSurfaceEXT){ - vks::tools::exitFatal("Could not fetch function pointer for the headless extension!", -1); - } - err = fpCreateHeadlessSurfaceEXT(instance, &surfaceCreateInfo, nullptr, &surface); -#endif - - if (err != VK_SUCCESS) { - vks::tools::exitFatal("Could not create surface!", err); - } - - // Get available queue family properties - uint32_t queueCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); - assert(queueCount >= 1); - - std::vector queueProps(queueCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); - - // Iterate over each queue to learn whether it supports presenting: - // Find a queue with present support - // Will be used to present the swap chain images to the windowing system - std::vector supportsPresent(queueCount); - for (uint32_t i = 0; i < queueCount; i++) - { - fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]); - } - - // Search for a graphics and a present queue in the array of queue - // families, try to find one that supports both - uint32_t graphicsQueueNodeIndex = UINT32_MAX; - uint32_t presentQueueNodeIndex = UINT32_MAX; - for (uint32_t i = 0; i < queueCount; i++) - { - if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) - { - if (graphicsQueueNodeIndex == UINT32_MAX) - { - graphicsQueueNodeIndex = i; - } - - if (supportsPresent[i] == VK_TRUE) - { - graphicsQueueNodeIndex = i; - presentQueueNodeIndex = i; - break; - } - } - } - if (presentQueueNodeIndex == UINT32_MAX) - { - // If there's no queue that supports both present and graphics - // try to find a separate present queue - for (uint32_t i = 0; i < queueCount; ++i) - { - if (supportsPresent[i] == VK_TRUE) - { - presentQueueNodeIndex = i; - break; - } - } - } - - // Exit if either a graphics or a presenting queue hasn't been found - if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) - { - vks::tools::exitFatal("Could not find a graphics and/or presenting queue!", -1); - } - - // todo : Add support for separate graphics and presenting queue - if (graphicsQueueNodeIndex != presentQueueNodeIndex) - { - vks::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", -1); - } - - queueNodeIndex = graphicsQueueNodeIndex; - - // Get list of supported surface formats - uint32_t formatCount; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL)); - assert(formatCount > 0); - - std::vector surfaceFormats(formatCount); - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data())); - - // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, - // there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM - if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) - { - colorFormat = VK_FORMAT_B8G8R8A8_UNORM; - colorSpace = surfaceFormats[0].colorSpace; - } - else - { - // iterate over the list of available surface format and - // check for the presence of VK_FORMAT_B8G8R8A8_UNORM - bool found_B8G8R8A8_UNORM = false; - for (auto&& surfaceFormat : surfaceFormats) - { - if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM) - { - colorFormat = surfaceFormat.format; - colorSpace = surfaceFormat.colorSpace; - found_B8G8R8A8_UNORM = true; - break; - } - } - - // in case VK_FORMAT_B8G8R8A8_UNORM is not available - // select the first available color format - if (!found_B8G8R8A8_UNORM) - { - colorFormat = surfaceFormats[0].format; - colorSpace = surfaceFormats[0].colorSpace; - } - } - -} - -/** -* Set instance, physical and logical device to use for the swapchain and get all required function pointers -* -* @param instance Vulkan instance to use -* @param physicalDevice Physical device used to query properties and formats relevant to the swapchain -* @param device Logical representation of the device to create the swapchain for -* -*/ -void VulkanSwapChain::connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device) -{ - this->instance = instance; - this->physicalDevice = physicalDevice; - this->device = device; - fpGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR")); - fpGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); - fpGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR")); - fpGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR")); - - fpCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateSwapchainKHR")); - fpDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkDestroySwapchainKHR")); - fpGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetSwapchainImagesKHR")); - fpAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkAcquireNextImageKHR")); - fpQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkQueuePresentKHR")); -} - -/** -* Create the swapchain and get its images with given width and height -* -* @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain) -* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain) -* @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode) -*/ -void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync, bool fullscreen) -{ - // Store the current swap chain handle so we can use it later on to ease up recreation - VkSwapchainKHR oldSwapchain = swapChain; - - // Get physical device surface properties and formats - VkSurfaceCapabilitiesKHR surfCaps; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps)); - - // Get available present modes - uint32_t presentModeCount; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); - assert(presentModeCount > 0); - - std::vector presentModes(presentModeCount); - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); - - VkExtent2D swapchainExtent = {}; - // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain - if (surfCaps.currentExtent.width == (uint32_t)-1) - { - // If the surface size is undefined, the size is set to - // the size of the images requested. - swapchainExtent.width = *width; - swapchainExtent.height = *height; - } - else - { - // If the surface size is defined, the swap chain size must match - swapchainExtent = surfCaps.currentExtent; - *width = surfCaps.currentExtent.width; - *height = surfCaps.currentExtent.height; - } - - - // Select a present mode for the swapchain - - // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec - // This mode waits for the vertical blank ("v-sync") - VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - - // If v-sync is not requested, try to find a mailbox mode - // It's the lowest latency non-tearing present mode available - if (!vsync) - { - for (size_t i = 0; i < presentModeCount; i++) - { - if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) - { - swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; - break; - } - if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) - { - swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - } - } - } - - // Determine the number of images - uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; -#if (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED)) - // SRS - Work around known MoltenVK issue re 2x frame rate when vsync (VK_PRESENT_MODE_FIFO_KHR) enabled - struct utsname sysInfo; - uname(&sysInfo); - // SRS - When vsync is on, use minImageCount when not in fullscreen or when running on Apple Silcon - // This forces swapchain image acquire frame rate to match display vsync frame rate - if (vsync && (!fullscreen || strcmp(sysInfo.machine, "arm64") == 0)) - { - desiredNumberOfSwapchainImages = surfCaps.minImageCount; - } -#endif - if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) - { - desiredNumberOfSwapchainImages = surfCaps.maxImageCount; - } - - // Find the transformation of the surface - VkSurfaceTransformFlagsKHR preTransform; - if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) - { - // We prefer a non-rotated transform - preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - } - else - { - preTransform = surfCaps.currentTransform; - } - - // Find a supported composite alpha format (not all devices support alpha opaque) - VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - // Simply select the first composite alpha format available - std::vector compositeAlphaFlags = { - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - }; - for (auto& compositeAlphaFlag : compositeAlphaFlags) { - if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) { - compositeAlpha = compositeAlphaFlag; - break; - }; - } - - VkSwapchainCreateInfoKHR swapchainCI = {}; - swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainCI.surface = surface; - swapchainCI.minImageCount = desiredNumberOfSwapchainImages; - swapchainCI.imageFormat = colorFormat; - swapchainCI.imageColorSpace = colorSpace; - swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; - swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; - swapchainCI.imageArrayLayers = 1; - swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainCI.queueFamilyIndexCount = 0; - swapchainCI.presentMode = swapchainPresentMode; - // Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images - swapchainCI.oldSwapchain = oldSwapchain; - // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area - swapchainCI.clipped = VK_TRUE; - swapchainCI.compositeAlpha = compositeAlpha; - - // Enable transfer source on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - - // Enable transfer destination on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain)); - - // If an existing swap chain is re-created, destroy the old swap chain - // This also cleans up all the presentable images - if (oldSwapchain != VK_NULL_HANDLE) - { - for (uint32_t i = 0; i < imageCount; i++) - { - vkDestroyImageView(device, buffers[i].view, nullptr); - } - fpDestroySwapchainKHR(device, oldSwapchain, nullptr); - } - VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL)); - - // Get the swap chain images - images.resize(imageCount); - VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data())); - - // Get the swap chain buffers containing the image and imageview - buffers.resize(imageCount); - for (uint32_t i = 0; i < imageCount; i++) - { - VkImageViewCreateInfo colorAttachmentView = {}; - colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - colorAttachmentView.pNext = NULL; - colorAttachmentView.format = colorFormat; - colorAttachmentView.components = { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - colorAttachmentView.subresourceRange.baseMipLevel = 0; - colorAttachmentView.subresourceRange.levelCount = 1; - colorAttachmentView.subresourceRange.baseArrayLayer = 0; - colorAttachmentView.subresourceRange.layerCount = 1; - colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; - colorAttachmentView.flags = 0; - - buffers[i].image = images[i]; - - colorAttachmentView.image = buffers[i].image; - - VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view)); - } -} - -/** -* Acquires the next image in the swap chain -* -* @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use -* @param imageIndex Pointer to the image index that will be increased if the next image could be acquired -* -* @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX -* -* @return VkResult of the image acquisition -*/ -VkResult VulkanSwapChain::acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex) -{ - // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown - // With that we don't have to handle VK_NOT_READY - return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex); -} - -/** -* Queue an image for presentation -* -* @param queue Presentation queue for presenting the image -* @param imageIndex Index of the swapchain image to queue for presentation -* @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE) -* -* @return VkResult of the queue presentation -*/ -VkResult VulkanSwapChain::queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore) -{ - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = NULL; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &swapChain; - presentInfo.pImageIndices = &imageIndex; - // Check if a wait semaphore has been specified to wait for before presenting the image - if (waitSemaphore != VK_NULL_HANDLE) - { - presentInfo.pWaitSemaphores = &waitSemaphore; - presentInfo.waitSemaphoreCount = 1; - } - return fpQueuePresentKHR(queue, &presentInfo); -} - - -/** -* Destroy and free Vulkan resources used for the swapchain -*/ -void VulkanSwapChain::cleanup() -{ - if (swapChain != VK_NULL_HANDLE) - { - for (uint32_t i = 0; i < imageCount; i++) - { - vkDestroyImageView(device, buffers[i].view, nullptr); - } - } - if (surface != VK_NULL_HANDLE) - { - fpDestroySwapchainKHR(device, swapChain, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - } - surface = VK_NULL_HANDLE; - swapChain = VK_NULL_HANDLE; -} - -#if defined(_DIRECT2DISPLAY) -/** -* Create direct to display surface -*/ -void VulkanSwapChain::createDirect2DisplaySurface(uint32_t width, uint32_t height) -{ - uint32_t displayPropertyCount; - - // Get display property - vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL); - VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount]; - vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties); - - // Get plane property - uint32_t planePropertyCount; - vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL); - VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount]; - vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties); - - VkDisplayKHR display = VK_NULL_HANDLE; - VkDisplayModeKHR displayMode; - VkDisplayModePropertiesKHR* pModeProperties; - bool foundMode = false; - - for(uint32_t i = 0; i < displayPropertyCount;++i) - { - display = pDisplayProperties[i].display; - uint32_t modeCount; - vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL); - pModeProperties = new VkDisplayModePropertiesKHR[modeCount]; - vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties); - - for (uint32_t j = 0; j < modeCount; ++j) - { - const VkDisplayModePropertiesKHR* mode = &pModeProperties[j]; - - if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height) - { - displayMode = mode->displayMode; - foundMode = true; - break; - } - } - if (foundMode) - { - break; - } - delete [] pModeProperties; - } - - if(!foundMode) - { - vks::tools::exitFatal("Can't find a display and a display mode!", -1); - return; - } - - // Search for a best plane we can use - uint32_t bestPlaneIndex = UINT32_MAX; - VkDisplayKHR* pDisplays = NULL; - for(uint32_t i = 0; i < planePropertyCount; i++) - { - uint32_t planeIndex=i; - uint32_t displayCount; - vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL); - if (pDisplays) - { - delete [] pDisplays; - } - pDisplays = new VkDisplayKHR[displayCount]; - vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays); - - // Find a display that matches the current plane - bestPlaneIndex = UINT32_MAX; - for(uint32_t j = 0; j < displayCount; j++) - { - if(display == pDisplays[j]) - { - bestPlaneIndex = i; - break; - } - } - if(bestPlaneIndex != UINT32_MAX) - { - break; - } - } - - if(bestPlaneIndex == UINT32_MAX) - { - vks::tools::exitFatal("Can't find a plane for displaying!", -1); - return; - } - - VkDisplayPlaneCapabilitiesKHR planeCap; - vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap); - VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0; - - if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; - } - - VkDisplaySurfaceCreateInfoKHR surfaceInfo{}; - surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; - surfaceInfo.pNext = NULL; - surfaceInfo.flags = 0; - surfaceInfo.displayMode = displayMode; - surfaceInfo.planeIndex = bestPlaneIndex; - surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex; - surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - surfaceInfo.globalAlpha = 1.0; - surfaceInfo.alphaMode = alphaMode; - surfaceInfo.imageExtent.width = width; - surfaceInfo.imageExtent.height = height; - - VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface); - if (result !=VK_SUCCESS) { - vks::tools::exitFatal("Failed to create surface!", result); - } - - delete[] pDisplays; - delete[] pModeProperties; - delete[] pDisplayProperties; - delete[] pPlaneProperties; -} -#endif diff --git a/base/VulkanSwapChain.h b/base/VulkanSwapChain.h deleted file mode 100644 index 8d59110..0000000 --- a/base/VulkanSwapChain.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Class wrapping access to the swap chain -* -* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system -* -* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include "VulkanTools.h" - -#ifdef __ANDROID__ -#include "VulkanAndroid.h" -#endif - -#ifdef __APPLE__ -#include -#endif - -typedef struct _SwapChainBuffers { - VkImage image; - VkImageView view; -} SwapChainBuffer; - -class VulkanSwapChain -{ -private: - VkInstance instance; - VkDevice device; - VkPhysicalDevice physicalDevice; - VkSurfaceKHR surface; - // Function pointers - PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; - PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; - PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; - PFN_vkQueuePresentKHR fpQueuePresentKHR; -public: - VkFormat colorFormat; - VkColorSpaceKHR colorSpace; - VkSwapchainKHR swapChain = VK_NULL_HANDLE; - uint32_t imageCount; - std::vector images; - std::vector buffers; - uint32_t queueNodeIndex = UINT32_MAX; - -#if defined(VK_USE_PLATFORM_WIN32_KHR) - void initSurface(void* platformHandle, void* platformWindow); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - void initSurface(ANativeWindow* window); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - void initSurface(IDirectFB* dfb, IDirectFBSurface* window); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - void initSurface(wl_display* display, wl_surface* window); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - void initSurface(xcb_connection_t* connection, xcb_window_t window); -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - void initSurface(void* view); -#elif (defined(_DIRECT2DISPLAY) || defined(VK_USE_PLATFORM_HEADLESS_EXT)) - void initSurface(uint32_t width, uint32_t height); -#if defined(_DIRECT2DISPLAY) - void createDirect2DisplaySurface(uint32_t width, uint32_t height); -#endif -#endif - void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device); - void create(uint32_t* width, uint32_t* height, bool vsync = false, bool fullscreen = false); - VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t* imageIndex); - VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE); - void cleanup(); -}; diff --git a/base/VulkanSwapChain.hpp b/base/VulkanSwapChain.hpp new file mode 100644 index 0000000..2aa53c8 --- /dev/null +++ b/base/VulkanSwapChain.hpp @@ -0,0 +1,662 @@ +/* +* Class wrapping access to the swap chain +* +* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include "macros.h" + +#ifdef __ANDROID__ +#include "VulkanAndroid.h" +#endif + +typedef struct _SwapChainBuffers { + VkImage image; + VkImageView view; +} SwapChainBuffer; + +class VulkanSwapChain +{ +private: + VkInstance instance; + VkDevice device; + VkPhysicalDevice physicalDevice; + VkSurfaceKHR surface = VK_NULL_HANDLE; + // Function pointers + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; + PFN_vkQueuePresentKHR fpQueuePresentKHR; +public: + VkFormat colorFormat; + VkColorSpaceKHR colorSpace; + VkSwapchainKHR swapChain = VK_NULL_HANDLE; + uint32_t imageCount; + std::vector images; + std::vector buffers; + VkExtent2D extent = {}; + uint32_t queueNodeIndex = UINT32_MAX; + + /** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */ +#if defined(VK_USE_PLATFORM_WIN32_KHR) + void initSurface(void* platformHandle, void* platformWindow) +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + void initSurface(ANativeWindow* window) +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + void initSurface(wl_display *display, wl_surface *window) +#elif defined(VK_USE_PLATFORM_XCB_KHR) + void initSurface(xcb_connection_t* connection, xcb_window_t window) +#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) + void initSurface(void* view) +#elif defined(_DIRECT2DISPLAY) + void initSurface(uint32_t width, uint32_t height) +#endif + { + VkResult err = VK_SUCCESS; + + // Create the os-specific surface +#if defined(VK_USE_PLATFORM_WIN32_KHR) + VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle; + surfaceCreateInfo.hwnd = (HWND)platformWindow; + err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.window = window; + err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface); +#elif defined(VK_USE_PLATFORM_IOS_MVK) + VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; + surfaceCreateInfo.pNext = NULL; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.pView = view; + err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + surfaceCreateInfo.pNext = NULL; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.pView = view; + err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface); +#elif defined(_DIRECT2DISPLAY) + createDirect2DisplaySurface(width, height); +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.display = display; + surfaceCreateInfo.surface = window; + err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_XCB_KHR) + VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.connection = connection; + surfaceCreateInfo.window = window; + err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#endif + + if (err != VK_SUCCESS) { + std::cerr << "Could not create surface!" << std::endl; + exit(err); + } + + // Get available queue family properties + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); + assert(queueCount >= 1); + + std::vector queueProps(queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); + + // Iterate over each queue to learn whether it supports presenting: + // Find a queue with present support + // Will be used to present the swap chain images to the windowing system + std::vector supportsPresent(queueCount); + for (uint32_t i = 0; i < queueCount; i++) + { + fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]); + } + + // Search for a graphics and a present queue in the array of queue + // families, try to find one that supports both + uint32_t graphicsQueueNodeIndex = UINT32_MAX; + uint32_t presentQueueNodeIndex = UINT32_MAX; + for (uint32_t i = 0; i < queueCount; i++) + { + if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + if (graphicsQueueNodeIndex == UINT32_MAX) + { + graphicsQueueNodeIndex = i; + } + + if (supportsPresent[i] == VK_TRUE) + { + graphicsQueueNodeIndex = i; + presentQueueNodeIndex = i; + break; + } + } + } + if (presentQueueNodeIndex == UINT32_MAX) + { + // If there's no queue that supports both present and graphics + // try to find a separate present queue + for (uint32_t i = 0; i < queueCount; ++i) + { + if (supportsPresent[i] == VK_TRUE) + { + presentQueueNodeIndex = i; + break; + } + } + } + + // Exit if either a graphics or a presenting queue hasn't been found + if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) { + std::cerr << "Could not find a graphics and/or presenting queue!" << std::endl; + exit(-1); + } + + // todo : Add support for separate graphics and presenting queue + if (graphicsQueueNodeIndex != presentQueueNodeIndex) { + std::cerr << "Separate graphics and presenting queues are not supported yet!" << std::endl; + exit(-1); + } + + queueNodeIndex = graphicsQueueNodeIndex; + + // Get list of supported surface formats + uint32_t formatCount; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL)); + assert(formatCount > 0); + + std::vector surfaceFormats(formatCount); + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data())); + + // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, + // there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM + if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) + { + colorFormat = VK_FORMAT_B8G8R8A8_UNORM; + colorSpace = surfaceFormats[0].colorSpace; + } + else + { + // iterate over the list of available surface format and + // check for the presence of VK_FORMAT_B8G8R8A8_UNORM + bool found_B8G8R8A8_UNORM = false; + for (auto&& surfaceFormat : surfaceFormats) + { + // Prefer SRGB + if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB) + { + colorFormat = surfaceFormat.format; + colorSpace = surfaceFormat.colorSpace; + found_B8G8R8A8_UNORM = true; + break; + } + //if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM) + //{ + // colorFormat = surfaceFormat.format; + // colorSpace = surfaceFormat.colorSpace; + // found_B8G8R8A8_UNORM = true; + // break; + //} + } + + // in case VK_FORMAT_B8G8R8A8_UNORM is not available + // select the first available color format + if (!found_B8G8R8A8_UNORM) + { + colorFormat = surfaceFormats[0].format; + colorSpace = surfaceFormats[0].colorSpace; + } + } + + } + + /** + * Set instance, physical and logical device to use for the swapchain and get all required function pointers + * + * @param instance Vulkan instance to use + * @param physicalDevice Physical device used to query properties and formats relevant to the swapchain + * @param device Logical representation of the device to create the swapchain for + * + */ + void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device) + { + this->instance = instance; + this->physicalDevice = physicalDevice; + this->device = device; + GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR); + GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR); + GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR); + GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR); + GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR); + GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR); + GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR); + GET_DEVICE_PROC_ADDR(device, QueuePresentKHR); + } + + /** + * Create the swapchain and get it's images with given width and height + * + * @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain) + * @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain) + * @param vsync (Optional) Can be used to force vsync'd rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode) + */ + void create(uint32_t *width, uint32_t *height, bool vsync = false) + { + VkSwapchainKHR oldSwapchain = swapChain; + + // Get physical device surface properties and formats + VkSurfaceCapabilitiesKHR surfCaps; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps)); + + // Get available present modes + uint32_t presentModeCount; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); + assert(presentModeCount > 0); + + std::vector presentModes(presentModeCount); + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); + + // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain + if (surfCaps.currentExtent.width == (uint32_t)-1) + { + // If the surface size is undefined, the size is set to + // the size of the images requested. + extent.width = *width; + extent.height = *height; + } + else + { + // If the surface size is defined, the swap chain size must match + extent = surfCaps.currentExtent; + *width = surfCaps.currentExtent.width; + *height = surfCaps.currentExtent.height; + } + + + // Select a present mode for the swapchain + + // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec + // This mode waits for the vertical blank ("v-sync") + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + // If v-sync is not requested, try to find a mailbox mode + // It's the lowest latency non-tearing present mode available + if (!vsync) + { + for (size_t i = 0; i < presentModeCount; i++) + { + if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) + { + swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; + break; + } + if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) + { + swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } + } + } + + // Determine the number of images + uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; + if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) + { + desiredNumberOfSwapchainImages = surfCaps.maxImageCount; + } + + // Find the transformation of the surface + VkSurfaceTransformFlagsKHR preTransform; + if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + { + // We prefer a non-rotated transform + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } + else + { + preTransform = surfCaps.currentTransform; + } + + // Find a supported composite alpha format (not all devices support alpha opaque) + VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + // Simply select the first composite alpha format available + std::vector compositeAlphaFlags = { + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + }; + for (auto& compositeAlphaFlag : compositeAlphaFlags) { + if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) { + compositeAlpha = compositeAlphaFlag; + break; + }; + } + + VkSwapchainCreateInfoKHR swapchainCI = {}; + swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCI.pNext = NULL; + swapchainCI.surface = surface; + swapchainCI.minImageCount = desiredNumberOfSwapchainImages; + swapchainCI.imageFormat = colorFormat; + swapchainCI.imageColorSpace = colorSpace; + swapchainCI.imageExtent = { extent.width, extent.height }; + swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; + swapchainCI.imageArrayLayers = 1; + swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCI.queueFamilyIndexCount = 0; + swapchainCI.pQueueFamilyIndices = NULL; + swapchainCI.presentMode = swapchainPresentMode; + swapchainCI.oldSwapchain = oldSwapchain; + // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area + swapchainCI.clipped = VK_TRUE; + swapchainCI.compositeAlpha = compositeAlpha; + + // Set additional usage flag for blitting from the swapchain images if supported + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(physicalDevice, colorFormat, &formatProps); + if ((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR) || (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { + swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + + VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain)); + + // If an existing swap chain is re-created, destroy the old swap chain + // This also cleans up all the presentable images + if (oldSwapchain != VK_NULL_HANDLE) + { + for (uint32_t i = 0; i < imageCount; i++) + { + vkDestroyImageView(device, buffers[i].view, nullptr); + } + fpDestroySwapchainKHR(device, oldSwapchain, nullptr); + } + VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL)); + + // Get the swap chain images + images.resize(imageCount); + VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data())); + + // Get the swap chain buffers containing the image and imageview + buffers.resize(imageCount); + for (uint32_t i = 0; i < imageCount; i++) + { + VkImageViewCreateInfo colorAttachmentView = {}; + colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + colorAttachmentView.pNext = NULL; + colorAttachmentView.format = colorFormat; + colorAttachmentView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorAttachmentView.subresourceRange.baseMipLevel = 0; + colorAttachmentView.subresourceRange.levelCount = 1; + colorAttachmentView.subresourceRange.baseArrayLayer = 0; + colorAttachmentView.subresourceRange.layerCount = 1; + colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorAttachmentView.flags = 0; + + buffers[i].image = images[i]; + + colorAttachmentView.image = buffers[i].image; + + VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view)); + } + } + + /** + * Acquires the next image in the swap chain + * + * @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use + * @param imageIndex Pointer to the image index that will be increased if the next image could be acquired + * + * @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX + * + * @return VkResult of the image acquisition + */ + VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex) + { + if (swapChain == VK_NULL_HANDLE) { + // Probably acquireNextImage() is called just after cleanup() (e.g. window has been terminated on Android). + // todo : Use a dedicated error code. + return VK_ERROR_OUT_OF_DATE_KHR; + } + + // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown + // With that we don't have to handle VK_NOT_READY + return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex); + } + + /** + * Queue an image for presentation + * + * @param queue Presentation queue for presenting the image + * @param imageIndex Index of the swapchain image to queue for presentation + * @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE) + * + * @return VkResult of the queue presentation + */ + VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE) + { + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.pNext = NULL; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &swapChain; + presentInfo.pImageIndices = &imageIndex; + // Check if a wait semaphore has been specified to wait for before presenting the image + if (waitSemaphore != VK_NULL_HANDLE) + { + presentInfo.pWaitSemaphores = &waitSemaphore; + presentInfo.waitSemaphoreCount = 1; + } + return fpQueuePresentKHR(queue, &presentInfo); + } + + + /** + * Destroy and free Vulkan resources used for the swapchain + */ + void cleanup() + { + if (swapChain != VK_NULL_HANDLE) + { + for (uint32_t i = 0; i < imageCount; i++) + { + vkDestroyImageView(device, buffers[i].view, nullptr); + } + } + if (surface != VK_NULL_HANDLE) + { + fpDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + } + surface = VK_NULL_HANDLE; + swapChain = VK_NULL_HANDLE; + } + +#if defined(_DIRECT2DISPLAY) + + void exitFatal(const std::string& message, int32_t exitCode) + { + #if defined(_WIN32) + if (!errorModeSilent) { + MessageBox(NULL, message.c_str(), NULL, MB_OK | MB_ICONERROR); + } + #elif defined(__ANDROID__) + LOGE("Fatal error: %s", message.c_str()); + vks::android::showAlert(message.c_str()); + #endif + std::cerr << message << "\n"; + #if !defined(__ANDROID__) + exit(exitCode); + #endif + } + + /** + * Create direct to display surface + */ + void createDirect2DisplaySurface(uint32_t width, uint32_t height) + { + uint32_t displayPropertyCount; + + // Get display property + vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL); + VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount]; + vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties); + + // Get plane property + uint32_t planePropertyCount; + vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL); + VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount]; + vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties); + + VkDisplayKHR display = VK_NULL_HANDLE; + VkDisplayModeKHR displayMode; + VkDisplayModePropertiesKHR* pModeProperties; + bool foundMode = false; + + for(uint32_t i = 0; i < displayPropertyCount;++i) + { + display = pDisplayProperties[i].display; + uint32_t modeCount; + vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL); + pModeProperties = new VkDisplayModePropertiesKHR[modeCount]; + vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties); + + for (uint32_t j = 0; j < modeCount; ++j) + { + const VkDisplayModePropertiesKHR* mode = &pModeProperties[j]; + + if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height) + { + displayMode = mode->displayMode; + foundMode = true; + break; + } + } + if (foundMode) + { + break; + } + delete [] pModeProperties; + } + + if(!foundMode) + { + exitFatal("Can't find a display and a display mode!", -1); + return; + } + + // Search for a best plane we can use + uint32_t bestPlaneIndex = UINT32_MAX; + VkDisplayKHR* pDisplays = NULL; + for(uint32_t i = 0; i < planePropertyCount; i++) + { + uint32_t planeIndex=i; + uint32_t displayCount; + vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL); + if (pDisplays) + { + delete [] pDisplays; + } + pDisplays = new VkDisplayKHR[displayCount]; + vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays); + + // Find a display that matches the current plane + bestPlaneIndex = UINT32_MAX; + for(uint32_t j = 0; j < displayCount; j++) + { + if(display == pDisplays[j]) + { + bestPlaneIndex = i; + break; + } + } + if(bestPlaneIndex != UINT32_MAX) + { + break; + } + } + + if(bestPlaneIndex == UINT32_MAX) + { + exitFatal("Can't find a plane for displaying!", -1); + return; + } + + VkDisplayPlaneCapabilitiesKHR planeCap; + vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap); + VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0; + + if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + } + + VkDisplaySurfaceCreateInfoKHR surfaceInfo{}; + surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; + surfaceInfo.pNext = NULL; + surfaceInfo.flags = 0; + surfaceInfo.displayMode = displayMode; + surfaceInfo.planeIndex = bestPlaneIndex; + surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex; + surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + surfaceInfo.globalAlpha = 1.0; + surfaceInfo.alphaMode = alphaMode; + surfaceInfo.imageExtent.width = width; + surfaceInfo.imageExtent.height = height; + + VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface); + if (result !=VK_SUCCESS) { + exitFatal("Failed to create surface!", result); + } + + delete[] pDisplays; + delete[] pModeProperties; + delete[] pDisplayProperties; + delete[] pPlaneProperties; + } +#endif +}; diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index 61c4953..89ca645 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -1,21 +1,38 @@ /* -* Vulkan Example base class +* Vulkan Example base class, stripped down version * -* Copyright (C) by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016-2018 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ -#include "vulkanexamplebase.h" - -#if (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED)) -#include -#include -#include -#endif +#include "VulkanExampleBase.h" std::vector VulkanExampleBase::args; +VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char * pLayerPrefix, const char * pMsg, void * pUserData) +{ + std::string prefix(""); + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + prefix += "ERROR:"; + }; + if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + prefix += "WARNING:"; + }; + if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { + prefix += "DEBUG:"; + } + std::stringstream debugMessage; + debugMessage << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg; +#if defined(__ANDROID__) + LOGD("%s", debugMessage.str().c_str()); +#else + std::cout << debugMessage.str() << "\n"; +#endif + fflush(stdout); + return VK_FALSE; +} + VkResult VulkanExampleBase::createInstance(bool enableValidation) { this->settings.validation = enableValidation; @@ -23,13 +40,13 @@ VkResult VulkanExampleBase::createInstance(bool enableValidation) // Validation can also be forced via a define #if defined(_VALIDATION) this->settings.validation = true; -#endif +#endif VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = name.c_str(); appInfo.pEngineName = name.c_str(); - appInfo.apiVersion = apiVersion; + appInfo.apiVersion = VK_API_VERSION_1_0; std::vector instanceExtensions = { VK_KHR_SURFACE_EXTENSION_NAME }; @@ -40,279 +57,270 @@ VkResult VulkanExampleBase::createInstance(bool enableValidation) instanceExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); #elif defined(_DIRECT2DISPLAY) instanceExtensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - instanceExtensions.push_back(VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) instanceExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_XCB_KHR) instanceExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); -#elif defined(VK_USE_PLATFORM_IOS_MVK) - instanceExtensions.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_MACOS_MVK) instanceExtensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - instanceExtensions.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME); -#endif - - // Get extensions supported by the instance and store for later use - uint32_t extCount = 0; - vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr); - if (extCount > 0) - { - std::vector extensions(extCount); - if (vkEnumerateInstanceExtensionProperties(nullptr, &extCount, &extensions.front()) == VK_SUCCESS) - { - for (VkExtensionProperties extension : extensions) - { - supportedInstanceExtensions.push_back(extension.extensionName); - } - } - } - -#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - // SRS - When running on iOS/macOS with MoltenVK, enable VK_KHR_get_physical_device_properties2 if not already enabled by the example (required by VK_KHR_portability_subset) - if (std::find(enabledInstanceExtensions.begin(), enabledInstanceExtensions.end(), VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == enabledInstanceExtensions.end()) - { - enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); - } #endif - // Enabled requested instance extensions - if (enabledInstanceExtensions.size() > 0) - { - for (const char * enabledExtension : enabledInstanceExtensions) - { - // Output message if requested extension is not available - if (std::find(supportedInstanceExtensions.begin(), supportedInstanceExtensions.end(), enabledExtension) == supportedInstanceExtensions.end()) - { - std::cerr << "Enabled instance extension \"" << enabledExtension << "\" is not present at instance level\n"; - } - instanceExtensions.push_back(enabledExtension); - } - } +#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) + instanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#endif VkInstanceCreateInfo instanceCreateInfo = {}; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pNext = NULL; instanceCreateInfo.pApplicationInfo = &appInfo; -#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) && defined(VK_KHR_portability_enumeration) - // SRS - When running on iOS/macOS with MoltenVK and VK_KHR_portability_enumeration is defined and supported by the instance, enable the extension and the flag - if (std::find(supportedInstanceExtensions.begin(), supportedInstanceExtensions.end(), VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) != supportedInstanceExtensions.end()) - { - instanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; - } +#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) + instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; #endif if (instanceExtensions.size() > 0) { - if (settings.validation) - { - instanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); // SRS - Dependency when VK_EXT_DEBUG_MARKER is enabled - instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + if (settings.validation) { + instanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size(); instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data(); } - - // The VK_LAYER_KHRONOS_validation contains all current validation functionality. - // Note that on Android this layer requires at least NDK r20 - const char* validationLayerName = "VK_LAYER_KHRONOS_validation"; - if (settings.validation) - { - // Check if this layer is available at instance level - uint32_t instanceLayerCount; - vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr); - std::vector instanceLayerProperties(instanceLayerCount); - vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayerProperties.data()); - bool validationLayerPresent = false; - for (VkLayerProperties layer : instanceLayerProperties) { - if (strcmp(layer.layerName, validationLayerName) == 0) { - validationLayerPresent = true; - break; - } - } - if (validationLayerPresent) { - instanceCreateInfo.ppEnabledLayerNames = &validationLayerName; - instanceCreateInfo.enabledLayerCount = 1; - } else { - std::cerr << "Validation layer VK_LAYER_KHRONOS_validation not present, validation is disabled"; - } + std::vector validationLayerNames; + if (settings.validation) { + validationLayerNames.push_back("VK_LAYER_KHRONOS_validation"); + instanceCreateInfo.enabledLayerCount = (uint32_t)validationLayerNames.size(); + instanceCreateInfo.ppEnabledLayerNames = validationLayerNames.data(); } return vkCreateInstance(&instanceCreateInfo, nullptr, &instance); } +void VulkanExampleBase::prepare() +{ + /* + Swapchain + */ + initSwapchain(); + setupSwapChain(); + +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + width = swapChain.extent.width; + height = swapChain.extent.height; +#endif + + /* + Command pool + */ + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool)); + + /* + Render pass + */ + + if (settings.multiSampling) { + std::array attachments = {}; + + // Multisampled attachment that we render to + attachments[0].format = swapChain.colorFormat; + attachments[0].samples = settings.sampleCount; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + // This is the frame buffer attachment to where the multisampled image + // will be resolved to and which will be presented to the swapchain + attachments[1].format = swapChain.colorFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // Multisampled depth attachment we render to + attachments[2].format = depthFormat; + attachments[2].samples = settings.sampleCount; + attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Depth resolve attachment + attachments[3].format = depthFormat; + attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[3].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 2; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Resolve attachment reference for the color attachment + VkAttachmentReference resolveReference = {}; + resolveReference.attachment = 1; + resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorReference; + // Pass our resolve attachments to the sub pass + subpass.pResolveAttachments = &resolveReference; + subpass.pDepthStencilAttachment = &depthReference; + + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCI = {}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpass; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } + else { + std::array attachments = {}; + // Color attachment + attachments[0].format = swapChain.colorFormat; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + // Depth attachment + attachments[1].format = depthFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 1; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + subpassDescription.pDepthStencilAttachment = &depthReference; + subpassDescription.inputAttachmentCount = 0; + subpassDescription.pInputAttachments = nullptr; + subpassDescription.preserveAttachmentCount = 0; + subpassDescription.pPreserveAttachments = nullptr; + subpassDescription.pResolveAttachments = nullptr; + + // Subpass dependencies for layout transitions + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCI{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = static_cast(dependencies.size()); + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } + + /* + Pipeline cache + */ + VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); + + /* + Frame buffer + */ + setupFrameBuffer(); +} + +void VulkanExampleBase::fileDropped(std::string filename) { } void VulkanExampleBase::renderFrame() -{ - VulkanExampleBase::prepareFrame(); - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); - VulkanExampleBase::submitFrame(); -} - -std::string VulkanExampleBase::getWindowTitle() -{ - std::string device(deviceProperties.deviceName); - std::string windowTitle; - windowTitle = title + " - " + device; - if (!settings.overlay) { - windowTitle += " - " + std::to_string(frameCounter) + " fps"; - } - return windowTitle; -} - -void VulkanExampleBase::createCommandBuffers() -{ - // Create one command buffer for each swap chain image and reuse for rendering - drawCmdBuffers.resize(swapChain.imageCount); - - VkCommandBufferAllocateInfo cmdBufAllocateInfo = - vks::initializers::commandBufferAllocateInfo( - cmdPool, - VK_COMMAND_BUFFER_LEVEL_PRIMARY, - static_cast(drawCmdBuffers.size())); - - VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data())); -} - -void VulkanExampleBase::destroyCommandBuffers() -{ - vkFreeCommandBuffers(device, cmdPool, static_cast(drawCmdBuffers.size()), drawCmdBuffers.data()); -} - -std::string VulkanExampleBase::getShadersPath() const -{ - return getAssetPath() + "shaders/" + shaderDir + "/"; -} - -std::string VulkanExampleBase::getHomeworkShadersPath() const -{ - return getAssetPath() + "homework/shaders/" + shaderDir + "/"; -} - -void VulkanExampleBase::createPipelineCache() -{ - VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; - pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); -} - -void VulkanExampleBase::prepare() -{ - if (vulkanDevice->enableDebugMarkers) { - vks::debugmarker::setup(device); - } - initSwapchain(); - createCommandPool(); - setupSwapChain(); - createCommandBuffers(); - createSynchronizationPrimitives(); - setupDepthStencil(); - setupRenderPass(); - createPipelineCache(); - setupFrameBuffer(); - settings.overlay = settings.overlay && (!benchmark.active); - if (settings.overlay) { - UIOverlay.device = vulkanDevice; - UIOverlay.queue = queue; - UIOverlay.shaders = { - loadShader(getShadersPath() + "base/uioverlay.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), - loadShader(getShadersPath() + "base/uioverlay.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT), - }; - UIOverlay.prepareResources(); - UIOverlay.preparePipeline(pipelineCache, renderPass, swapChain.colorFormat, depthFormat); - } -} - -VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(std::string fileName, VkShaderStageFlagBits stage) -{ - VkPipelineShaderStageCreateInfo shaderStage = {}; - shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStage.stage = stage; -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - shaderStage.module = vks::tools::loadShader(androidApp->activity->assetManager, fileName.c_str(), device); -#else - shaderStage.module = vks::tools::loadShader(fileName.c_str(), device); -#endif - shaderStage.pName = "main"; - assert(shaderStage.module != VK_NULL_HANDLE); - shaderModules.push_back(shaderStage.module); - return shaderStage; -} - -void VulkanExampleBase::nextFrame() { auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } render(); frameCounter++; auto tEnd = std::chrono::high_resolution_clock::now(); -#if (defined(VK_USE_PLATFORM_IOS_MVK) || (defined(VK_USE_PLATFORM_MACOS_MVK) && !defined(VK_EXAMPLE_XCODE_GENERATED))) - // SRS - Calculate tDiff as time between frames vs. rendering time for iOS/macOS displayLink-driven examples project - auto tDiff = std::chrono::duration(tEnd - tPrevEnd).count(); -#else auto tDiff = std::chrono::duration(tEnd - tStart).count(); -#endif frameTimer = (float)tDiff / 1000.0f; camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = (float)(std::chrono::duration(tEnd - lastTimestamp).count()); - if (fpsTimer > 1000.0f) - { + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) { lastFPS = static_cast((float)frameCounter * (1000.0f / fpsTimer)); -#if defined(_WIN32) - if (!settings.overlay) { - std::string windowTitle = getWindowTitle(); - SetWindowText(window, windowTitle.c_str()); - } -#endif + fpsTimer = 0.0f; frameCounter = 0; - lastTimestamp = tEnd; } - tPrevEnd = tEnd; - - // TODO: Cap UI overlay update rates - updateOverlay(); } void VulkanExampleBase::renderLoop() { -// SRS - for non-apple plaforms, handle benchmarking here within VulkanExampleBase::renderLoop() -// - for macOS, handle benchmarking within NSApp rendering loop via displayLinkOutputCb() -#if !(defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - if (benchmark.active) { - benchmark.run([=] { render(); }, vulkanDevice->properties); - vkDeviceWaitIdle(device); - if (benchmark.filename != "") { - benchmark.saveResults(); - } - return; - } -#endif - destWidth = width; destHeight = height; - lastTimestamp = std::chrono::high_resolution_clock::now(); - tPrevEnd = lastTimestamp; #if defined(_WIN32) MSG msg; bool quitMessageReceived = false; @@ -325,8 +333,8 @@ void VulkanExampleBase::renderLoop() break; } } - if (prepared && !IsIconic(window)) { - nextFrame(); + if (!IsIconic(window)) { + renderFrame(); } } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) @@ -357,7 +365,6 @@ void VulkanExampleBase::renderLoop() // Exit loop, example will be destroyed in application main if (destroy) { - ANativeActivity_finish(androidApp->activity); break; } @@ -371,35 +378,12 @@ void VulkanExampleBase::renderLoop() auto tDiff = std::chrono::duration(tEnd - tStart).count(); frameTimer = tDiff / 1000.0f; camera.update(frameTimer); - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); + fpsTimer += (float)tDiff; if (fpsTimer > 1000.0f) { lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; frameCounter = 0; - lastTimestamp = tEnd; - } - - // TODO: Cap UI overlay update rates/only issue when update requested - updateOverlay(); - - bool updateView = false; - - // Check touch state (for movement) - if (touchDown) { - touchTimer += frameTimer; - } - if (touchTimer >= 1.0) { - camera.keys.up = true; - viewChanged(); } // Check gamepad state @@ -409,34 +393,14 @@ void VulkanExampleBase::renderLoop() if (camera.type != Camera::CameraType::firstperson) { // Rotate - if (std::abs(gamePadState.axisLeft.x) > deadZone) - { + if (std::abs(gamePadState.axisLeft.x) > deadZone) { camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f)); - updateView = true; } - if (std::abs(gamePadState.axisLeft.y) > deadZone) - { + if (std::abs(gamePadState.axisLeft.y) > deadZone) { camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f)); - updateView = true; - } - // Zoom - if (std::abs(gamePadState.axisRight.y) > deadZone) - { - camera.translate(glm::vec3(0.0f, 0.0f, gamePadState.axisRight.y * 0.01f)); - updateView = true; - } - if (updateView) - { - viewChanged(); - } - } - else - { - updateView = camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); - if (updateView) - { - viewChanged(); } + } else { + camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); } } } @@ -444,93 +408,25 @@ void VulkanExampleBase::renderLoop() while (!quit) { auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } render(); frameCounter++; auto tEnd = std::chrono::high_resolution_clock::now(); auto tDiff = std::chrono::duration(tEnd - tStart).count(); frameTimer = tDiff / 1000.0f; camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); + fpsTimer += (float)tDiff; if (fpsTimer > 1000.0f) { lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; frameCounter = 0; - lastTimestamp = tEnd; } - updateOverlay(); - } -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } - DFBWindowEvent event; - while (!event_buffer->GetEvent(event_buffer, DFB_EVENT(&event))) - { - handleEvent(&event); - } - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - frameCounter = 0; - lastTimestamp = tEnd; - } - updateOverlay(); } #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) while (!quit) { auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } - while (!configured) - wl_display_dispatch(display); while (wl_display_prepare_read(display) != 0) wl_display_dispatch_pending(display); wl_display_flush(display); @@ -543,43 +439,20 @@ void VulkanExampleBase::renderLoop() auto tDiff = std::chrono::duration(tEnd - tStart).count(); frameTimer = tDiff / 1000.0f; camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); + fpsTimer += (float)tDiff; if (fpsTimer > 1000.0f) { - if (!settings.overlay) - { - std::string windowTitle = getWindowTitle(); - xdg_toplevel_set_title(xdg_toplevel, windowTitle.c_str()); - } + wl_shell_surface_set_title(shell_surface, title.c_str()); lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; frameCounter = 0; - lastTimestamp = tEnd; } - updateOverlay(); } #elif defined(VK_USE_PLATFORM_XCB_KHR) xcb_flush(connection); while (!quit) { auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } xcb_generic_event_t *event; while ((event = xcb_poll_for_event(connection))) { @@ -592,263 +465,49 @@ void VulkanExampleBase::renderLoop() auto tDiff = std::chrono::duration(tEnd - tStart).count(); frameTimer = tDiff / 1000.0f; camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - if (!paused) - { - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); + fpsTimer += (float)tDiff; if (fpsTimer > 1000.0f) { - if (!settings.overlay) - { - std::string windowTitle = getWindowTitle(); - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - windowTitle.size(), windowTitle.c_str()); - } + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, + window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + title.size(), title.c_str()); lastFPS = (float)frameCounter * (1000.0f / fpsTimer); + fpsTimer = 0.0f; frameCounter = 0; - lastTimestamp = tEnd; } - updateOverlay(); } -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - if (viewUpdated) - { - viewUpdated = false; - viewChanged(); - } - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - if (camera.moving()) - { - viewUpdated = true; - } - // Convert to clamped timer value - timer += timerSpeed * frameTimer; - if (timer > 1.0) - { - timer -= 1.0f; - } - float fpsTimer = std::chrono::duration(tEnd - lastTimestamp).count(); - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - frameCounter = 0; - lastTimestamp = tEnd; - } - updateOverlay(); - } -#elif (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED)) +#elif defined(VK_USE_PLATFORM_MACOS_MVK) [NSApp run]; #endif - // Flush device to make sure all resources can be freed - if (device != VK_NULL_HANDLE) { - vkDeviceWaitIdle(device); - } + // Flush device to make sure all resources can be freed + vkDeviceWaitIdle(device); } -void VulkanExampleBase::updateOverlay() +VulkanExampleBase::VulkanExampleBase() { - if (!settings.overlay) - return; - - ImGuiIO& io = ImGui::GetIO(); - - io.DisplaySize = ImVec2((float)width, (float)height); - io.DeltaTime = frameTimer; - - io.MousePos = ImVec2(mousePos.x, mousePos.y); - io.MouseDown[0] = mouseButtons.left && UIOverlay.visible; - io.MouseDown[1] = mouseButtons.right && UIOverlay.visible; - io.MouseDown[2] = mouseButtons.middle && UIOverlay.visible; - - ImGui::NewFrame(); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); - ImGui::SetNextWindowPos(ImVec2(10 * UIOverlay.scale, 10 * UIOverlay.scale)); - ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiSetCond_FirstUseEver); - ImGui::Begin("Vulkan Example", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); - ImGui::TextUnformatted(title.c_str()); - ImGui::TextUnformatted(deviceProperties.deviceName); - ImGui::Text("%.2f ms/frame (%.1d fps)", (1000.0f / lastFPS), lastFPS); - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 5.0f * UIOverlay.scale)); -#endif - ImGui::PushItemWidth(110.0f * UIOverlay.scale); - OnUpdateUIOverlay(&UIOverlay); - ImGui::PopItemWidth(); -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - ImGui::PopStyleVar(); -#endif - - ImGui::End(); - ImGui::PopStyleVar(); - ImGui::Render(); - - if (UIOverlay.update() || UIOverlay.updated) { - buildCommandBuffers(); - UIOverlay.updated = false; - } - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - if (mouseButtons.left) { - mouseButtons.left = false; - } -#endif -} - -void VulkanExampleBase::drawUI(const VkCommandBuffer commandBuffer) -{ - if (settings.overlay && UIOverlay.visible) { - const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); - const VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - UIOverlay.draw(commandBuffer); - } -} - -void VulkanExampleBase::prepareFrame() -{ - // Acquire the next image from the swap chain - VkResult result = swapChain.acquireNextImage(semaphores.presentComplete, ¤tBuffer); - // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) - // SRS - If no longer optimal (VK_SUBOPTIMAL_KHR), wait until submitFrame() in case number of swapchain images will change on resize - if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) { - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - windowResize(); - } - return; - } - else { - VK_CHECK_RESULT(result); - } -} - -void VulkanExampleBase::submitFrame() -{ - VkResult result = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete); - // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) or no longer optimal for presentation (SUBOPTIMAL) - if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) { - windowResize(); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - return; - } - } - else { - VK_CHECK_RESULT(result); - } - VK_CHECK_RESULT(vkQueueWaitIdle(queue)); -} - -VulkanExampleBase::VulkanExampleBase(bool enableValidation) -{ -#if !defined(VK_USE_PLATFORM_ANDROID_KHR) - // Check for a valid asset path - struct stat info; - if (stat(getAssetPath().c_str(), &info) != 0) + char* numConvPtr; + // Parse command line arguments + for (size_t i = 0; i < args.size(); i++) { -#if defined(_WIN32) - std::string msg = "Could not locate asset path in \"" + getAssetPath() + "\" !"; - MessageBox(NULL, msg.c_str(), "Fatal error", MB_OK | MB_ICONERROR); -#else - std::cerr << "Error: Could not find asset path in " << getAssetPath() << "\n"; -#endif - exit(-1); + if (args[i] == std::string("-validation")) { + settings.validation = true; + } + if (args[i] == std::string("-vsync")) { + settings.vsync = true; + } + if ((args[i] == std::string("-f")) || (args[i] == std::string("--fullscreen"))) { + settings.fullscreen = true; + } + if ((args[i] == std::string("-w")) || (args[i] == std::string("--width"))) { + uint32_t w = strtol(args[i + 1], &numConvPtr, 10); + if (numConvPtr != args[i + 1]) { width = w; }; + } + if ((args[i] == std::string("-h")) || (args[i] == std::string("--height"))) { + uint32_t h = strtol(args[i + 1], &numConvPtr, 10); + if (numConvPtr != args[i + 1]) { height = h; }; + } } -#endif - - settings.validation = enableValidation; - // Command line arguments - commandLineParser.add("help", { "--help" }, 0, "Show help"); - commandLineParser.add("validation", { "-v", "--validation" }, 0, "Enable validation layers"); - commandLineParser.add("vsync", { "-vs", "--vsync" }, 0, "Enable V-Sync"); - commandLineParser.add("fullscreen", { "-f", "--fullscreen" }, 0, "Start in fullscreen mode"); - commandLineParser.add("width", { "-w", "--width" }, 1, "Set window width"); - commandLineParser.add("height", { "-h", "--height" }, 1, "Set window height"); - commandLineParser.add("shaders", { "-s", "--shaders" }, 1, "Select shader type to use (glsl or hlsl)"); - commandLineParser.add("gpuselection", { "-g", "--gpu" }, 1, "Select GPU to run on"); - commandLineParser.add("gpulist", { "-gl", "--listgpus" }, 0, "Display a list of available Vulkan devices"); - commandLineParser.add("benchmark", { "-b", "--benchmark" }, 0, "Run example in benchmark mode"); - commandLineParser.add("benchmarkwarmup", { "-bw", "--benchwarmup" }, 1, "Set warmup time for benchmark mode in seconds"); - commandLineParser.add("benchmarkruntime", { "-br", "--benchruntime" }, 1, "Set duration time for benchmark mode in seconds"); - commandLineParser.add("benchmarkresultfile", { "-bf", "--benchfilename" }, 1, "Set file name for benchmark results"); - commandLineParser.add("benchmarkresultframes", { "-bt", "--benchframetimes" }, 0, "Save frame times to benchmark results file"); - commandLineParser.add("benchmarkframes", { "-bfs", "--benchmarkframes" }, 1, "Only render the given number of frames"); - - commandLineParser.parse(args); - if (commandLineParser.isSet("help")) { -#if defined(_WIN32) - setupConsole("Vulkan example"); -#endif - commandLineParser.printHelp(); - std::cin.get(); - exit(0); - } - if (commandLineParser.isSet("validation")) { - settings.validation = true; - } - if (commandLineParser.isSet("vsync")) { - settings.vsync = true; - } - if (commandLineParser.isSet("height")) { - height = commandLineParser.getValueAsInt("height", width); - } - if (commandLineParser.isSet("width")) { - width = commandLineParser.getValueAsInt("width", width); - } - if (commandLineParser.isSet("fullscreen")) { - settings.fullscreen = true; - } - if (commandLineParser.isSet("shaders")) { - std::string value = commandLineParser.getValueAsString("shaders", "glsl"); - if ((value != "glsl") && (value != "hlsl")) { - std::cerr << "Shader type must be one of 'glsl' or 'hlsl'\n"; - } - else { - shaderDir = value; - } - } - if (commandLineParser.isSet("benchmark")) { - benchmark.active = true; - vks::tools::errorModeSilent = true; - } - if (commandLineParser.isSet("benchmarkwarmup")) { - benchmark.warmup = commandLineParser.getValueAsInt("benchmarkwarmup", benchmark.warmup); - } - if (commandLineParser.isSet("benchmarkruntime")) { - benchmark.duration = commandLineParser.getValueAsInt("benchmarkruntime", benchmark.duration); - } - if (commandLineParser.isSet("benchmarkresultfile")) { - benchmark.filename = commandLineParser.getValueAsString("benchmarkresultfile", benchmark.filename); - } - if (commandLineParser.isSet("benchmarkresultframes")) { - benchmark.outputFrameTimes = true; - } - if (commandLineParser.isSet("benchmarkframes")) { - benchmark.outputFrames = commandLineParser.getValueAsInt("benchmarkframes", benchmark.outputFrames); - } - #if defined(VK_USE_PLATFORM_ANDROID_KHR) // Vulkan library is loaded dynamically on Android bool libLoaded = vks::android::loadVulkanLibrary(); @@ -862,12 +521,12 @@ VulkanExampleBase::VulkanExampleBase(bool enableValidation) #endif #if defined(_WIN32) - // Enable console if validation is active, debug message callback will output to it - if (this->settings.validation) - { - setupConsole("Vulkan example"); - } - setupDPIAwareness(); + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + FILE *stream; + freopen_s(&stream, "CONOUT$", "w+", stdout); + freopen_s(&stream, "CONOUT$", "w+", stderr); + SetConsoleTitle(TEXT("Vulkan validation output")); #endif } @@ -875,75 +534,39 @@ VulkanExampleBase::~VulkanExampleBase() { // Clean up Vulkan resources swapChain.cleanup(); - if (descriptorPool != VK_NULL_HANDLE) - { - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - } - destroyCommandBuffers(); - if (renderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(device, renderPass, nullptr); - } - for (uint32_t i = 0; i < frameBuffers.size(); i++) - { + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (uint32_t i = 0; i < frameBuffers.size(); i++) { vkDestroyFramebuffer(device, frameBuffers[i], nullptr); } - - for (auto& shaderModule : shaderModules) - { - vkDestroyShaderModule(device, shaderModule, nullptr); - } vkDestroyImageView(device, depthStencil.view, nullptr); vkDestroyImage(device, depthStencil.image, nullptr); vkFreeMemory(device, depthStencil.mem, nullptr); - vkDestroyPipelineCache(device, pipelineCache, nullptr); - vkDestroyCommandPool(device, cmdPool, nullptr); - - vkDestroySemaphore(device, semaphores.presentComplete, nullptr); - vkDestroySemaphore(device, semaphores.renderComplete, nullptr); - for (auto& fence : waitFences) { - vkDestroyFence(device, fence, nullptr); + if (settings.multiSampling) { + vkDestroyImage(device, multisampleTarget.color.image, nullptr); + vkDestroyImageView(device, multisampleTarget.color.view, nullptr); + vkFreeMemory(device, multisampleTarget.color.memory, nullptr); + vkDestroyImage(device, multisampleTarget.depth.image, nullptr); + vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); + vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); } - - if (settings.overlay) { - UIOverlay.freeResources(); - } - delete vulkanDevice; - - if (settings.validation) - { - vks::debug::freeDebugCallback(instance); + if (settings.validation) { + vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); } - vkDestroyInstance(instance, nullptr); - #if defined(_DIRECT2DISPLAY) - -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - if (event_buffer) - event_buffer->Release(event_buffer); - if (surface) - surface->Release(surface); - if (window) - window->Release(window); - if (layer) - layer->Release(layer); - if (dfb) - dfb->Release(dfb); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - xdg_toplevel_destroy(xdg_toplevel); - xdg_surface_destroy(xdg_surface); + wl_shell_surface_destroy(shell_surface); wl_surface_destroy(surface); if (keyboard) wl_keyboard_destroy(keyboard); if (pointer) wl_pointer_destroy(pointer); - if (seat) - wl_seat_destroy(seat); - xdg_wm_base_destroy(shell); + wl_seat_destroy(seat); + wl_shell_destroy(shell); wl_compositor_destroy(compositor); wl_registry_destroy(registry); wl_display_disconnect(display); @@ -955,158 +578,129 @@ VulkanExampleBase::~VulkanExampleBase() #endif } -bool VulkanExampleBase::initVulkan() +void VulkanExampleBase::initVulkan() { VkResult err; - // Vulkan instance + /* + Instance creation + */ err = createInstance(settings.validation); if (err) { - vks::tools::exitFatal("Could not create Vulkan instance : \n" + vks::tools::errorString(err), err); - return false; + std::cerr << "Could not create Vulkan instance!" << std::endl; + exit(err); } #if defined(VK_USE_PLATFORM_ANDROID_KHR) vks::android::loadVulkanFunctions(instance); #endif - // If requested, we enable the default validation layers for debugging - if (settings.validation) - { - vks::debug::setupDebugging(instance); + /* + Validation layers + */ + if (settings.validation) { + vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + VkDebugReportCallbackCreateInfoEXT debugCreateInfo{}; + debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + debugCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)debugMessageCallback; + debugCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + VK_CHECK_RESULT(vkCreateDebugReportCallback(instance, &debugCreateInfo, nullptr, &debugReportCallback)); } - // Physical device + /* + GPU selection + */ uint32_t gpuCount = 0; - // Get number of available physical devices VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr)); - if (gpuCount == 0) { - vks::tools::exitFatal("No device with Vulkan support found", -1); - return false; - } - // Enumerate devices + assert(gpuCount > 0); std::vector physicalDevices(gpuCount); err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()); if (err) { - vks::tools::exitFatal("Could not enumerate physical devices : \n" + vks::tools::errorString(err), err); - return false; + std::cerr << "Could not enumerate physical devices!" << std::endl; + exit(err); } - - // GPU selection - - // Select physical device to be used for the Vulkan example - // Defaults to the first device unless specified by command line uint32_t selectedDevice = 0; - -#if !defined(VK_USE_PLATFORM_ANDROID_KHR) - // GPU selection via command line argument - if (commandLineParser.isSet("gpuselection")) { - uint32_t index = commandLineParser.getValueAsInt("gpuselection", 0); - if (index > gpuCount - 1) { - std::cerr << "Selected device index " << index << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << "\n"; - } else { - selectedDevice = index; - } - } - if (commandLineParser.isSet("gpulist")) { - std::cout << "Available Vulkan devices" << "\n"; - for (uint32_t i = 0; i < gpuCount; i++) { - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProperties); - std::cout << "Device [" << i << "] : " << deviceProperties.deviceName << std::endl; - std::cout << " Type: " << vks::tools::physicalDeviceTypeString(deviceProperties.deviceType) << "\n"; - std::cout << " API: " << (deviceProperties.apiVersion >> 22) << "." << ((deviceProperties.apiVersion >> 12) & 0x3ff) << "." << (deviceProperties.apiVersion & 0xfff) << "\n"; +#if !defined(VK_USE_PLATFORM_ANDROID_KHR) + for (size_t i = 0; i < args.size(); i++) { + if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) { + char* endptr; + uint32_t index = strtol(args[i + 1], &endptr, 10); + if (endptr != args[i + 1]) { + if (index > gpuCount - 1) { + std::cerr << "Selected device index " << index << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; + } else { + std::cout << "Selected Vulkan device " << index << std::endl; + selectedDevice = index; + } + }; + break; } } #endif physicalDevice = physicalDevices[selectedDevice]; - // Store properties (including limits), features and memory properties of the physical device (so that examples can check against them) vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); - // Derived examples can override this to set actual features (based on above readings) to enable for logical device creation - getEnabledFeatures(); - - // Vulkan device creation - // This is handled by a separate class that gets a logical device representation - // and encapsulates functions related to a device + /* + Device creation + */ vulkanDevice = new vks::VulkanDevice(physicalDevice); - - // Derived examples can enable extensions based on the list of supported extensions read from the physical device - getEnabledExtensions(); - - VkResult res = vulkanDevice->createLogicalDevice(enabledFeatures, enabledDeviceExtensions, deviceCreatepNextChain); + VkPhysicalDeviceFeatures enabledFeatures{}; + if (deviceFeatures.samplerAnisotropy) { + enabledFeatures.samplerAnisotropy = VK_TRUE; + } + std::vector enabledExtensions{}; + VkResult res = vulkanDevice->createLogicalDevice(enabledFeatures, enabledExtensions); if (res != VK_SUCCESS) { - vks::tools::exitFatal("Could not create Vulkan device: \n" + vks::tools::errorString(res), res); - return false; + std::cerr << "Could not create Vulkan device!" << std::endl; + exit(res); } device = vulkanDevice->logicalDevice; - // Get a graphics queue from the device + /* + Graphics queue + */ vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.graphics, 0, &queue); - // Find a suitable depth format - VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &depthFormat); + /* + Suitable depth format + */ + std::vector depthFormats = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM }; + VkBool32 validDepthFormat = false; + for (auto& format : depthFormats) { + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps); + if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { + depthFormat = format; + validDepthFormat = true; + break; + } + } assert(validDepthFormat); swapChain.connect(instance, physicalDevice, device); - // Create synchronization objects - VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo(); - // Create a semaphore used to synchronize image presentation - // Ensures that the image is displayed before we start submitting new commands to the queue - VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete)); - // Create a semaphore used to synchronize command submission - // Ensures that the image is not presented until all commands have been submitted and executed - VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete)); - - // Set up submit info structure - // Semaphores will stay the same during application lifetime - // Command buffer submission info is set by each example - submitInfo = vks::initializers::submitInfo(); - submitInfo.pWaitDstStageMask = &submitPipelineStages; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &semaphores.presentComplete; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &semaphores.renderComplete; - - return true; +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + // Get Android device name and manufacturer (to display along GPU name) + androidProduct = ""; + char prop[PROP_VALUE_MAX+1]; + int len = __system_property_get("ro.product.manufacturer", prop); + if (len > 0) { + androidProduct += std::string(prop) + " "; + }; + len = __system_property_get("ro.product.model", prop); + if (len > 0) { + androidProduct += std::string(prop); + }; + LOGD("androidProduct = %s", androidProduct.c_str()); +#endif } #if defined(_WIN32) -// Win32 : Sets up a console window and redirects standard output to it -void VulkanExampleBase::setupConsole(std::string title) -{ - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - FILE *stream; - freopen_s(&stream, "CONIN$", "r", stdin); - freopen_s(&stream, "CONOUT$", "w+", stdout); - freopen_s(&stream, "CONOUT$", "w+", stderr); - SetConsoleTitle(TEXT(title.c_str())); -} - -void VulkanExampleBase::setupDPIAwareness() -{ - typedef HRESULT *(__stdcall *SetProcessDpiAwarenessFunc)(PROCESS_DPI_AWARENESS); - - HMODULE shCore = LoadLibraryA("Shcore.dll"); - if (shCore) - { - SetProcessDpiAwarenessFunc setProcessDpiAwareness = - (SetProcessDpiAwarenessFunc)GetProcAddress(shCore, "SetProcessDpiAwareness"); - - if (setProcessDpiAwareness != nullptr) - { - setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - - FreeLibrary(shCore); - } -} HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) { @@ -1127,8 +721,7 @@ HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) wndClass.lpszClassName = name.c_str(); wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); - if (!RegisterClassEx(&wndClass)) - { + if (!RegisterClassEx(&wndClass)) { std::cout << "Could not register window class!\n"; fflush(stdout); exit(1); @@ -1137,44 +730,32 @@ HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); - if (settings.fullscreen) - { - if ((width != (uint32_t)screenWidth) && (height != (uint32_t)screenHeight)) - { - DEVMODE dmScreenSettings; - memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); - dmScreenSettings.dmSize = sizeof(dmScreenSettings); - dmScreenSettings.dmPelsWidth = width; - dmScreenSettings.dmPelsHeight = height; - dmScreenSettings.dmBitsPerPel = 32; - dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) - { - if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) - { + if (settings.fullscreen) { + DEVMODE dmScreenSettings; + memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); + dmScreenSettings.dmSize = sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = screenWidth; + dmScreenSettings.dmPelsHeight = screenHeight; + dmScreenSettings.dmBitsPerPel = 32; + dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + if ((width != (uint32_t)screenWidth) && (height != (uint32_t)screenHeight)) { + if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { + if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { settings.fullscreen = false; - } - else - { + } else { return nullptr; } } - screenWidth = width; - screenHeight = height; } - } DWORD dwExStyle; DWORD dwStyle; - if (settings.fullscreen) - { + if (settings.fullscreen) { dwExStyle = WS_EX_APPWINDOW; dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - } - else - { + } else { dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; } @@ -1187,10 +768,9 @@ HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); - std::string windowTitle = getWindowTitle(); - window = CreateWindowEx(0, + window = CreateWindowEx(WS_EX_ACCEPTFILES, name.c_str(), - windowTitle.c_str(), + title.c_str(), dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, @@ -1201,19 +781,17 @@ HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) hinstance, NULL); - if (!settings.fullscreen) - { - // Center on screen + if (!settings.fullscreen) { uint32_t x = (GetSystemMetrics(SM_CXSCREEN) - windowRect.right) / 2; uint32_t y = (GetSystemMetrics(SM_CYSCREEN) - windowRect.bottom) / 2; SetWindowPos(window, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } - if (!window) - { + if (!window) { printf("Could not create window!\n"); fflush(stdout); return nullptr; + exit(1); } ShowWindow(window, SW_SHOW); @@ -1241,16 +819,12 @@ void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case KEY_P: paused = !paused; break; - case KEY_F1: - UIOverlay.visible = !UIOverlay.visible; - UIOverlay.updated = true; - break; case KEY_ESCAPE: PostQuitMessage(0); break; } - if (camera.type == Camera::firstperson) + if (camera.firstperson) { switch (wParam) { @@ -1269,10 +843,9 @@ void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR } } - keyPressed((uint32_t)wParam); break; case WM_KEYUP: - if (camera.type == Camera::firstperson) + if (camera.firstperson) { switch (wParam) { @@ -1315,8 +888,7 @@ void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_MOUSEWHEEL: { short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); - camera.translate(glm::vec3(0.0f, 0.0f, (float)wheelDelta * 0.005f)); - viewUpdated = true; + camera.translate(glm::vec3(0.0f, 0.0f, -(float)wheelDelta * 0.005f * camera.movementSpeed)); break; } case WM_MOUSEMOVE: @@ -1325,36 +897,45 @@ void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; } case WM_SIZE: - if ((prepared) && (wParam != SIZE_MINIMIZED)) - { - if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))) - { + if ((prepared) && (wParam != SIZE_MINIMIZED)) { + if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))) { destWidth = LOWORD(lParam); destHeight = HIWORD(lParam); windowResize(); } } break; - case WM_GETMINMAXINFO: - { - LPMINMAXINFO minMaxInfo = (LPMINMAXINFO)lParam; - minMaxInfo->ptMinTrackSize.x = 64; - minMaxInfo->ptMinTrackSize.y = 64; - break; - } case WM_ENTERSIZEMOVE: resizing = true; break; case WM_EXITSIZEMOVE: resizing = false; break; + case WM_DROPFILES: + { + std::string fname; + HDROP hDrop = reinterpret_cast(wParam); + // extract files here + char filename[MAX_PATH]; + uint32_t count = DragQueryFileA(hDrop, -1, nullptr, 0); + for (uint32_t i = 0; i < count; ++i) { + if (DragQueryFileA(hDrop, i, filename, MAX_PATH)) { + fname = filename; + } + break; + } + DragFinish(hDrop); + fileDropped(fname); + break; + } } - - OnHandleMessage(hWnd, uMsg, wParam, lParam); } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* event) { + ImGuiIO& io = ImGui::GetIO(); + bool uiMouseCapture = io.WantCaptureMouse; + VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { @@ -1370,80 +951,92 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* break; } + // FIXME: Reusing code for TOUCHSCREEN seemingly works well for MOUSE event source, + // but it would be better to provide a dedicated event handling logic for MOUSE event source. + case AINPUT_SOURCE_MOUSE: case AINPUT_SOURCE_TOUCHSCREEN: { int32_t action = AMotionEvent_getAction(event); + int32_t pointerCount = AMotionEvent_getPointerCount(event); + int32_t flags = action & AMOTION_EVENT_ACTION_MASK; - switch (action) { - case AMOTION_EVENT_ACTION_UP: { - vulkanExample->lastTapTime = AMotionEvent_getEventTime(event); - vulkanExample->touchPos.x = AMotionEvent_getX(event, 0); - vulkanExample->touchPos.y = AMotionEvent_getY(event, 0); - vulkanExample->touchTimer = 0.0; - vulkanExample->touchDown = false; - vulkanExample->camera.keys.up = false; - - // Detect single tap - int64_t eventTime = AMotionEvent_getEventTime(event); - int64_t downTime = AMotionEvent_getDownTime(event); - if (eventTime - downTime <= vks::android::TAP_TIMEOUT) { - float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP; - float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPos.x; - float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPos.y; - if ((x * x + y * y) < deadZone) { - vulkanExample->mouseButtons.left = true; - } + switch (flags) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + for (uint32_t i = 0; i < pointerCount; i++) { + vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); + vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); }; - - return 1; + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (pointerIndex < 2) { + vulkanExample->touchPoints[pointerIndex].down = true; + } + if (pointerCount < 2) { + // Detect single tap + int64_t eventTime = AMotionEvent_getEventTime(event); + int64_t downTime = AMotionEvent_getDownTime(event); + if (eventTime - downTime <= vks::android::TAP_TIMEOUT) { + float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP; + float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPoints[0].x; + float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPoints[0].y; + if ((x * x + y * y) < deadZone) { + vulkanExample->mousePos.x = vulkanExample->touchPoints[0].x; + vulkanExample->mousePos.y = vulkanExample->touchPoints[0].y; + vulkanExample->mouseButtons.left = true; + } + }; + } break; } - case AMOTION_EVENT_ACTION_DOWN: { - // Detect double tap - int64_t eventTime = AMotionEvent_getEventTime(event); - if (eventTime - vulkanExample->lastTapTime <= vks::android::DOUBLE_TAP_TIMEOUT) { - float deadZone = (160.f / vks::android::screenDensity) * vks::android::DOUBLE_TAP_SLOP * vks::android::DOUBLE_TAP_SLOP; - float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPos.x; - float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPos.y; - if ((x * x + y * y) < deadZone) { - vulkanExample->keyPressed(TOUCH_DOUBLE_TAP); - vulkanExample->touchDown = false; - } + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_UP: { + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (pointerIndex < 2) { + vulkanExample->touchPoints[pointerIndex].down = false; } - else { - vulkanExample->touchDown = true; + if (pointerCount < 2) { + vulkanExample->touchPoints[1].down = false; } - vulkanExample->touchPos.x = AMotionEvent_getX(event, 0); - vulkanExample->touchPos.y = AMotionEvent_getY(event, 0); - vulkanExample->mousePos.x = AMotionEvent_getX(event, 0); - vulkanExample->mousePos.y = AMotionEvent_getY(event, 0); break; } case AMOTION_EVENT_ACTION_MOVE: { - bool handled = false; - if (vulkanExample->settings.overlay) { - ImGuiIO& io = ImGui::GetIO(); - handled = io.WantCaptureMouse && vulkanExample->UIOverlay.visible; - } - if (!handled) { - int32_t eventX = AMotionEvent_getX(event, 0); - int32_t eventY = AMotionEvent_getY(event, 0); + // Pinch and zoom + if (!uiMouseCapture && vulkanExample->touchPoints[0].down && vulkanExample->touchPoints[1].down) { + for (uint32_t i = 0; i < pointerCount; i++) { + if (vulkanExample->touchPoints[i].down) { + vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); + vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); + } + }; + float dx = vulkanExample->touchPoints[1].x - vulkanExample->touchPoints[0].x; + float dy = vulkanExample->touchPoints[1].y - vulkanExample->touchPoints[0].y; + float d = sqrt(dx * dx + dy * dy); + if (d < vulkanExample->pinchDist) { + vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, 0.03f)); + }; + if (d > vulkanExample->pinchDist) { + vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, -0.03f)); + }; + vulkanExample->pinchDist = d; + } else { + // Rotate + if (!uiMouseCapture && vulkanExample->touchPoints[0].down) { + int32_t eventX = AMotionEvent_getX(event, 0); + int32_t eventY = AMotionEvent_getY(event, 0); - float deltaX = (float)(vulkanExample->touchPos.y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f; - float deltaY = (float)(vulkanExample->touchPos.x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f; + float deltaX = (vulkanExample->touchPoints[0].y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f; + float deltaY = (vulkanExample->touchPoints[0].x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f; - vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); - vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); + vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); + vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); - vulkanExample->viewChanged(); - - vulkanExample->touchPos.x = eventX; - vulkanExample->touchPos.y = eventY; + vulkanExample->touchPoints[0].x = eventX; + vulkanExample->touchPoints[0].y = eventY; + } } break; } default: return 1; - break; } } @@ -1462,34 +1055,9 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* switch (keyCode) { - case AKEYCODE_BUTTON_A: - vulkanExample->keyPressed(GAMEPAD_BUTTON_A); - break; - case AKEYCODE_BUTTON_B: - vulkanExample->keyPressed(GAMEPAD_BUTTON_B); - break; - case AKEYCODE_BUTTON_X: - vulkanExample->keyPressed(GAMEPAD_BUTTON_X); - break; - case AKEYCODE_BUTTON_Y: - vulkanExample->keyPressed(GAMEPAD_BUTTON_Y); - break; - case AKEYCODE_1: // support keyboards with no function keys - case AKEYCODE_F1: - case AKEYCODE_BUTTON_L1: - vulkanExample->UIOverlay.visible = !vulkanExample->UIOverlay.visible; - vulkanExample->UIOverlay.updated = true; - break; - case AKEYCODE_BUTTON_R1: - vulkanExample->keyPressed(GAMEPAD_BUTTON_R1); - break; - case AKEYCODE_P: case AKEYCODE_BUTTON_START: vulkanExample->paused = !vulkanExample->paused; break; - default: - vulkanExample->keyPressed(keyCode); // handle example-specific key press events - break; }; LOGD("Button %d pressed", keyCode); @@ -1516,14 +1084,9 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) LOGD("APP_CMD_INIT_WINDOW"); if (androidApp->window != NULL) { - if (vulkanExample->initVulkan()) { - vulkanExample->prepare(); - assert(vulkanExample->prepared); - } - else { - LOGE("Could not initialize Vulkan, exiting!"); - androidApp->destroyRequested = 1; - } + vulkanExample->initVulkan(); + vulkanExample->prepare(); + assert(vulkanExample->prepared); } else { @@ -1541,563 +1104,11 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) case APP_CMD_TERM_WINDOW: // Window is hidden or closed, clean up resources LOGD("APP_CMD_TERM_WINDOW"); - if (vulkanExample->prepared) { - vulkanExample->swapChain.cleanup(); - } + vulkanExample->swapChain.cleanup(); break; } } -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) -#if defined(VK_EXAMPLE_XCODE_GENERATED) -@interface AppDelegate : NSObject -{ -@public - VulkanExampleBase *vulkanExample; -} - -@end - -@implementation AppDelegate -{ -} - -// SRS - Dispatch rendering loop onto a queue for max frame rate concurrent rendering vs displayLink vsync rendering -// - vsync command line option (-vs) on macOS now works like other platforms (using VK_PRESENT_MODE_FIFO_KHR) -dispatch_group_t concurrentGroup; - -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification -{ - [NSApp activateIgnoringOtherApps:YES]; // SRS - Make sure app window launches in front of Xcode window - - concurrentGroup = dispatch_group_create(); - dispatch_queue_t concurrentQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0); - dispatch_group_async(concurrentGroup, concurrentQueue, ^{ - - while (!vulkanExample->quit) { - vulkanExample->displayLinkOutputCb(); - } - }); - - // SRS - When benchmarking, set up termination notification on main thread when concurrent queue completes - if (vulkanExample->benchmark.active) { - dispatch_queue_t notifyQueue = dispatch_get_main_queue(); - dispatch_group_notify(concurrentGroup, notifyQueue, ^{ [NSApp terminate:nil]; }); - } -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender -{ - return YES; -} - -// SRS - Tell rendering loop to quit, then wait for concurrent queue to terminate before deleting vulkanExample -- (void)applicationWillTerminate:(NSNotification *)aNotification -{ - vulkanExample->quit = YES; - dispatch_group_wait(concurrentGroup, DISPATCH_TIME_FOREVER); - vkDeviceWaitIdle(vulkanExample->vulkanDevice->logicalDevice); - delete(vulkanExample); -} - -@end - -const std::string getAssetPath() { - return [NSBundle.mainBundle.resourcePath stringByAppendingString: @"/../../data/"].UTF8String; -} - -static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, - const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, - void *displayLinkContext) -{ - @autoreleasepool - { - auto vulkanExample = static_cast(displayLinkContext); - vulkanExample->displayLinkOutputCb(); - } - return kCVReturnSuccess; -} - -@interface View : NSView -{ -@public - VulkanExampleBase *vulkanExample; -} - -@end - -@implementation View -{ - CVDisplayLinkRef displayLink; -} - -- (instancetype)initWithFrame:(NSRect)frameRect -{ - self = [super initWithFrame:(frameRect)]; - if (self) - { - self.wantsLayer = YES; - self.layer = [CAMetalLayer layer]; - } - return self; -} - -- (void)viewDidMoveToWindow -{ - CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); - // SRS - Disable displayLink vsync rendering in favour of max frame rate concurrent rendering - // - vsync command line option (-vs) on macOS now works like other platforms (using VK_PRESENT_MODE_FIFO_KHR) - //CVDisplayLinkSetOutputCallback(displayLink, &displayLinkOutputCallback, vulkanExample); - CVDisplayLinkStart(displayLink); -} - -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -- (BOOL)acceptsFirstMouse:(NSEvent *)event -{ - return YES; -} - -- (void)keyDown:(NSEvent*)event -{ - switch (event.keyCode) - { - case KEY_P: - vulkanExample->paused = !vulkanExample->paused; - break; - case KEY_1: // support keyboards with no function keys - case KEY_F1: - vulkanExample->UIOverlay.visible = !vulkanExample->UIOverlay.visible; - vulkanExample->UIOverlay.updated = true; - break; - case KEY_DELETE: // support keyboards with no escape key - case KEY_ESCAPE: - [NSApp terminate:nil]; - break; - case KEY_W: - vulkanExample->camera.keys.up = true; - break; - case KEY_S: - vulkanExample->camera.keys.down = true; - break; - case KEY_A: - vulkanExample->camera.keys.left = true; - break; - case KEY_D: - vulkanExample->camera.keys.right = true; - break; - default: - vulkanExample->keyPressed(event.keyCode); // handle example-specific key press events - break; - } -} - -- (void)keyUp:(NSEvent*)event -{ - switch (event.keyCode) - { - case KEY_W: - vulkanExample->camera.keys.up = false; - break; - case KEY_S: - vulkanExample->camera.keys.down = false; - break; - case KEY_A: - vulkanExample->camera.keys.left = false; - break; - case KEY_D: - vulkanExample->camera.keys.right = false; - break; - default: - break; - } -} - -- (NSPoint)getMouseLocalPoint:(NSEvent*)event -{ - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - point.y = self.frame.size.height - point.y; - return point; -} - -- (void)mouseDown:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mousePos = glm::vec2(point.x, point.y); - vulkanExample->mouseButtons.left = true; -} - -- (void)mouseUp:(NSEvent *)event -{ - vulkanExample->mouseButtons.left = false; -} - -- (void)rightMouseDown:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mousePos = glm::vec2(point.x, point.y); - vulkanExample->mouseButtons.right = true; -} - -- (void)rightMouseUp:(NSEvent *)event -{ - vulkanExample->mouseButtons.right = false; -} - -- (void)otherMouseDown:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mousePos = glm::vec2(point.x, point.y); - vulkanExample->mouseButtons.middle = true; -} - -- (void)otherMouseUp:(NSEvent *)event -{ - vulkanExample->mouseButtons.middle = false; -} - -- (void)mouseDragged:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mouseDragged(point.x, point.y); -} - -- (void)rightMouseDragged:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mouseDragged(point.x, point.y); -} - -- (void)otherMouseDragged:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mouseDragged(point.x, point.y); -} - -- (void)mouseMoved:(NSEvent *)event -{ - auto point = [self getMouseLocalPoint:event]; - vulkanExample->mouseDragged(point.x, point.y); -} - -- (void)scrollWheel:(NSEvent *)event -{ - short wheelDelta = [event deltaY]; - vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, - -(float)wheelDelta * 0.05f * vulkanExample->camera.movementSpeed)); - vulkanExample->viewUpdated = true; -} - -// SRS - Window resizing already handled by windowResize() in VulkanExampleBase::submitFrame() -// - handling window resize events here is redundant and can cause thread interaction problems -/* -- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize -{ - CVDisplayLinkStop(displayLink); - vulkanExample->windowWillResize(frameSize.width, frameSize.height); - return frameSize; -} - -- (void)windowDidResize:(NSNotification *)notification -{ - vulkanExample->windowDidResize(); - CVDisplayLinkStart(displayLink); -} -*/ - -- (void)windowWillEnterFullScreen:(NSNotification *)notification -{ - vulkanExample->settings.fullscreen = true; -} - -- (void)windowWillExitFullScreen:(NSNotification *)notification -{ - vulkanExample->settings.fullscreen = false; -} - -- (BOOL)windowShouldClose:(NSWindow *)sender -{ - return TRUE; -} - -- (void)windowWillClose:(NSNotification *)notification -{ - CVDisplayLinkStop(displayLink); - CVDisplayLinkRelease(displayLink); -} - -@end -#endif - -void* VulkanExampleBase::setupWindow(void* view) -{ -#if defined(VK_EXAMPLE_XCODE_GENERATED) - NSApp = [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - auto nsAppDelegate = [AppDelegate new]; - nsAppDelegate->vulkanExample = this; - [NSApp setDelegate:nsAppDelegate]; - - const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height); - const auto kWindowStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable; - - auto window = [[NSWindow alloc] initWithContentRect:kContentRect - styleMask:kWindowStyle - backing:NSBackingStoreBuffered - defer:NO]; - [window setTitle:@(title.c_str())]; - [window setAcceptsMouseMovedEvents:YES]; - [window center]; - [window makeKeyAndOrderFront:nil]; - if (settings.fullscreen) { - [window toggleFullScreen:nil]; - } - - auto nsView = [[View alloc] initWithFrame:kContentRect]; - nsView->vulkanExample = this; - [window setDelegate:nsView]; - [window setContentView:nsView]; - this->view = (__bridge void*)nsView; -#else - this->view = view; -#endif - return view; -} - -void VulkanExampleBase::displayLinkOutputCb() -{ -#if defined(VK_EXAMPLE_XCODE_GENERATED) - if (benchmark.active) { - benchmark.run([=] { render(); }, vulkanDevice->properties); - if (benchmark.filename != "") { - benchmark.saveResults(); - } - quit = true; // SRS - quit NSApp rendering loop when benchmarking complete - return; - } -#endif - - if (prepared) - nextFrame(); -} - -void VulkanExampleBase::mouseDragged(float x, float y) -{ - handleMouseMove(static_cast(x), static_cast(y)); -} - -void VulkanExampleBase::windowWillResize(float x, float y) -{ - resizing = true; - if (prepared) - { - destWidth = x; - destHeight = y; - windowResize(); - } -} - -void VulkanExampleBase::windowDidResize() -{ - resizing = false; -} #elif defined(_DIRECT2DISPLAY) -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) -IDirectFBSurface *VulkanExampleBase::setupWindow() -{ - DFBResult ret; - int posx = 0, posy = 0; - - ret = DirectFBInit(NULL, NULL); - if (ret) - { - std::cout << "Could not initialize DirectFB!\n"; - fflush(stdout); - exit(1); - } - - ret = DirectFBCreate(&dfb); - if (ret) - { - std::cout << "Could not create main interface of DirectFB!\n"; - fflush(stdout); - exit(1); - } - - ret = dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer); - if (ret) - { - std::cout << "Could not get DirectFB display layer interface!\n"; - fflush(stdout); - exit(1); - } - - DFBDisplayLayerConfig layer_config; - ret = layer->GetConfiguration(layer, &layer_config); - if (ret) - { - std::cout << "Could not get DirectFB display layer configuration!\n"; - fflush(stdout); - exit(1); - } - - if (settings.fullscreen) - { - width = layer_config.width; - height = layer_config.height; - } - else - { - if (layer_config.width > width) - posx = (layer_config.width - width) / 2; - if (layer_config.height > height) - posy = (layer_config.height - height) / 2; - } - - DFBWindowDescription desc; - desc.flags = (DFBWindowDescriptionFlags)(DWDESC_WIDTH | DWDESC_HEIGHT | DWDESC_POSX | DWDESC_POSY); - desc.width = width; - desc.height = height; - desc.posx = posx; - desc.posy = posy; - ret = layer->CreateWindow(layer, &desc, &window); - if (ret) - { - std::cout << "Could not create DirectFB window interface!\n"; - fflush(stdout); - exit(1); - } - - ret = window->GetSurface(window, &surface); - if (ret) - { - std::cout << "Could not get DirectFB surface interface!\n"; - fflush(stdout); - exit(1); - } - - ret = window->CreateEventBuffer(window, &event_buffer); - if (ret) - { - std::cout << "Could not create DirectFB event buffer interface!\n"; - fflush(stdout); - exit(1); - } - - ret = window->SetOpacity(window, 0xFF); - if (ret) - { - std::cout << "Could not set DirectFB window opacity!\n"; - fflush(stdout); - exit(1); - } - - return surface; -} - -void VulkanExampleBase::handleEvent(const DFBWindowEvent *event) -{ - switch (event->type) - { - case DWET_CLOSE: - quit = true; - break; - case DWET_MOTION: - handleMouseMove(event->x, event->y); - break; - case DWET_BUTTONDOWN: - switch (event->button) - { - case DIBI_LEFT: - mouseButtons.left = true; - break; - case DIBI_MIDDLE: - mouseButtons.middle = true; - break; - case DIBI_RIGHT: - mouseButtons.right = true; - break; - default: - break; - } - break; - case DWET_BUTTONUP: - switch (event->button) - { - case DIBI_LEFT: - mouseButtons.left = false; - break; - case DIBI_MIDDLE: - mouseButtons.middle = false; - break; - case DIBI_RIGHT: - mouseButtons.right = false; - break; - default: - break; - } - break; - case DWET_KEYDOWN: - switch (event->key_symbol) - { - case KEY_W: - camera.keys.up = true; - break; - case KEY_S: - camera.keys.down = true; - break; - case KEY_A: - camera.keys.left = true; - break; - case KEY_D: - camera.keys.right = true; - break; - case KEY_P: - paused = !paused; - break; - case KEY_F1: - UIOverlay.visible = !UIOverlay.visible; - UIOverlay.updated = true; - break; - default: - break; - } - break; - case DWET_KEYUP: - switch (event->key_symbol) - { - case KEY_W: - camera.keys.up = false; - break; - case KEY_S: - camera.keys.down = false; - break; - case KEY_A: - camera.keys.left = false; - break; - case KEY_D: - camera.keys.right = false; - break; - case KEY_ESCAPE: - quit = true; - break; - default: - break; - } - keyPressed(event->key_symbol); - break; - case DWET_SIZE: - destWidth = event->w; - destHeight = event->h; - windowResize(); - break; - default: - break; - } -} #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) /*static*/void VulkanExampleBase::registryGlobalCb(void *data, wl_registry *registry, uint32_t name, const char *interface, @@ -2178,8 +1189,7 @@ void VulkanExampleBase::pointerAxis(wl_pointer *pointer, uint32_t time, switch (axis) { case REL_X: - camera.translate(glm::vec3(0.0f, 0.0f, d * 0.005f)); - viewUpdated = true; + camera.translate(glm::vec3(0.0f, 0.0f, -d * 0.005f * camera.movementSpeed)); break; default: break; @@ -2232,19 +1242,10 @@ void VulkanExampleBase::keyboardKey(struct wl_keyboard *keyboard, if (state) paused = !paused; break; - case KEY_F1: - if (state) { - UIOverlay.visible = !UIOverlay.visible; - UIOverlay.updated = true; - } - break; - case KEY_ESCAPE: + case KEY_ESC: quit = true; break; } - - if (state) - keyPressed(key); } /*static*/void VulkanExampleBase::keyboardModifiersCb(void *data, @@ -2284,15 +1285,6 @@ void VulkanExampleBase::seatCapabilities(wl_seat *seat, uint32_t caps) } } -static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) -{ - xdg_wm_base_pong(shell, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - xdg_wm_base_ping, -}; - void VulkanExampleBase::registryGlobal(wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { @@ -2301,11 +1293,10 @@ void VulkanExampleBase::registryGlobal(wl_registry *registry, uint32_t name, compositor = (wl_compositor *) wl_registry_bind(registry, name, &wl_compositor_interface, 3); } - else if (strcmp(interface, "xdg_wm_base") == 0) + else if (strcmp(interface, "wl_shell") == 0) { - shell = (xdg_wm_base *) wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(shell, &xdg_wm_base_listener, nullptr); + shell = (wl_shell *) wl_registry_bind(registry, name, + &wl_shell_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { @@ -2346,83 +1337,41 @@ void VulkanExampleBase::initWaylandConnection() wl_registry_add_listener(registry, ®istry_listener, this); wl_display_dispatch(display); wl_display_roundtrip(display); - if (!compositor || !shell) + if (!compositor || !shell || !seat) { std::cout << "Could not bind Wayland protocols!\n"; fflush(stdout); exit(1); } - if (!seat) - { - std::cout << "WARNING: Input handling not available!\n"; - fflush(stdout); - } } -void VulkanExampleBase::setSize(int width, int height) +static void PingCb(void *data, struct wl_shell_surface *shell_surface, + uint32_t serial) { - if (width <= 0 || height <= 0) - return; - - destWidth = width; - destHeight = height; - - windowResize(); + wl_shell_surface_pong(shell_surface, serial); } -static void -xdg_surface_handle_configure(void *data, struct xdg_surface *surface, - uint32_t serial) +static void ConfigureCb(void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) { - VulkanExampleBase *base = (VulkanExampleBase *) data; - - xdg_surface_ack_configure(surface, serial); - base->configured = true; } -static const struct xdg_surface_listener xdg_surface_listener = { - xdg_surface_handle_configure, -}; - - -static void -xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, - int32_t width, int32_t height, - struct wl_array *states) +static void PopupDoneCb(void *data, struct wl_shell_surface *shell_surface) { - VulkanExampleBase *base = (VulkanExampleBase *) data; - - base->setSize(width, height); } -static void -xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) -{ - VulkanExampleBase *base = (VulkanExampleBase *) data; - - base->quit = true; -} - - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - xdg_toplevel_handle_configure, - xdg_toplevel_handle_close, -}; - - -struct xdg_surface *VulkanExampleBase::setupWindow() +wl_shell_surface *VulkanExampleBase::setupWindow() { surface = wl_compositor_create_surface(compositor); - xdg_surface = xdg_wm_base_get_xdg_surface(shell, surface); + shell_surface = wl_shell_get_shell_surface(shell, surface); - xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, this); - xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); - xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, this); + static const struct wl_shell_surface_listener shell_surface_listener = + { PingCb, ConfigureCb, PopupDoneCb }; - std::string windowTitle = getWindowTitle(); - xdg_toplevel_set_title(xdg_toplevel, windowTitle.c_str()); - wl_surface_commit(surface); - return xdg_surface; + wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this); + wl_shell_surface_set_toplevel(shell_surface); + wl_shell_surface_set_title(shell_surface, title.c_str()); + return shell_surface; } #elif defined(VK_USE_PLATFORM_XCB_KHR) @@ -2473,25 +1422,12 @@ xcb_window_t VulkanExampleBase::setupWindow() window, (*reply).atom, 4, 32, 1, &(*atom_wm_delete_window).atom); - std::string windowTitle = getWindowTitle(); xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - title.size(), windowTitle.c_str()); + title.size(), title.c_str()); free(reply); - /** - * Set the WM_CLASS property to display - * title in dash tooltip and application menu - * on GNOME and other desktop environments - */ - std::string wm_class; - wm_class = wm_class.insert(0, name); - wm_class = wm_class.insert(name.size(), 1, '\0'); - wm_class = wm_class.insert(name.size() + 1, title); - wm_class = wm_class.insert(wm_class.size(), 1, '\0'); - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, wm_class.size() + 2, wm_class.c_str()); - if (settings.fullscreen) { xcb_intern_atom_reply_t *atom_wm_state = intern_atom_helper(connection, false, "_NET_WM_STATE"); @@ -2503,7 +1439,7 @@ xcb_window_t VulkanExampleBase::setupWindow() &(atom_wm_fullscreen->atom)); free(atom_wm_fullscreen); free(atom_wm_state); - } + } xcb_map_window(connection, window); @@ -2517,13 +1453,8 @@ void VulkanExampleBase::initxcbConnection() xcb_screen_iterator_t iter; int scr; - // xcb_connect always returns a non-NULL pointer to a xcb_connection_t, - // even on failure. Callers need to use xcb_connection_has_error() to - // check for failure. When finished, use xcb_disconnect() to close the - // connection and free the structure. connection = xcb_connect(NULL, &scr); - assert( connection ); - if( xcb_connection_has_error(connection) ) { + if (connection == NULL) { printf("Could not find a compatible Vulkan ICD!\n"); fflush(stdout); exit(1); @@ -2595,13 +1526,9 @@ void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event) case KEY_P: paused = !paused; break; - case KEY_F1: - UIOverlay.visible = !UIOverlay.visible; - UIOverlay.updated = true; - break; } } - break; + break; case XCB_KEY_RELEASE: { const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; @@ -2618,12 +1545,11 @@ void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event) break; case KEY_D: camera.keys.right = false; - break; + break; case KEY_ESCAPE: quit = true; break; } - keyPressed(keyEvent->detail); } break; case XCB_DESTROY_NOTIFY: @@ -2647,236 +1573,438 @@ void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event) break; } } -#else -void VulkanExampleBase::setupWindow() +#elif defined(VK_USE_PLATFORM_MACOS_MVK) +@interface AppDelegate : NSObject { } + +@end + +@implementation AppDelegate +{ +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender +{ + return YES; +} + +@end + +CVReturn OnDisplayLinkOutput(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) +{ + @autoreleasepool + { + auto vulkanExampleBase = static_cast(displayLinkContext); + vulkanExampleBase->renderFrame(); + } + return kCVReturnSuccess; +} + +@interface View : NSView +{ +@public + VulkanExampleBase* vulkanExampleBase; +} + +@end + +@implementation View +{ + CVDisplayLinkRef displayLink; +} + +- (instancetype)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:(frameRect)]; + if (self) + { + self.wantsLayer = YES; + self.layer = [CAMetalLayer layer]; + } + return self; +} + +- (void)viewDidMoveToWindow +{ + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + CVDisplayLinkSetOutputCallback(displayLink, &OnDisplayLinkOutput, vulkanExampleBase); + CVDisplayLinkStart(displayLink); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)keyDown:(NSEvent*)event +{ + switch (event.keyCode) + { + case kVK_ANSI_P: + vulkanExampleBase->paused = !vulkanExampleBase->paused; + break; + case kVK_Escape: + [NSApp terminate:nil]; + break; + default: + break; + } +} + +- (void)keyUp:(NSEvent*)event +{ + if (vulkanExampleBase->camera.firstperson) + { + switch (event.keyCode) + { + case kVK_ANSI_W: + vulkanExampleBase->camera.keys.up = false; + break; + case kVK_ANSI_S: + vulkanExampleBase->camera.keys.down = false; + break; + case kVK_ANSI_A: + vulkanExampleBase->camera.keys.left = false; + break; + case kVK_ANSI_D: + vulkanExampleBase->camera.keys.right = false; + break; + default: + break; + } + } +} + +- (void)mouseDown:(NSEvent *)event +{ + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); + vulkanExampleBase->mouseButtons.left = true; +} + +- (void)mouseUp:(NSEvent *)event +{ + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); + vulkanExampleBase->mouseButtons.left = false; +} + +- (void)otherMouseDown:(NSEvent *)event +{ + vulkanExampleBase->mouseButtons.right = true; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + vulkanExampleBase->mouseButtons.right = false; +} + +- (void)mouseDragged:(NSEvent *)event +{ + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mouseDragged(point.x, point.y); +} + +- (void)mouseMoved:(NSEvent *)event +{ + NSPoint location = [event locationInWindow]; + NSPoint point = [self convertPoint:location fromView:nil]; + vulkanExampleBase->mouseDragged(point.x, point.y); +} + +- (void)scrollWheel:(NSEvent *)event +{ + short wheelDelta = [event deltaY]; + vulkanExampleBase->camera.translate(glm::vec3(0.0f, 0.0f, + -(float)wheelDelta * 0.05f * vulkanExampleBase->camera.movementSpeed)); +} + +- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize +{ + CVDisplayLinkStop(displayLink); + vulkanExampleBase->windowWillResize(frameSize.width, frameSize.height); + return frameSize; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + vulkanExampleBase->windowDidResize(); + CVDisplayLinkStart(displayLink); +} + +- (BOOL)windowShouldClose:(NSWindow *)sender +{ + return TRUE; +} + +- (void)windowWillClose:(NSNotification *)notification +{ + CVDisplayLinkStop(displayLink); +} + +@end + +NSWindow* VulkanExampleBase::setupWindow() +{ + NSApp = [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp setDelegate:[AppDelegate new]]; + + const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height); + + window = [[NSWindow alloc] initWithContentRect:kContentRect + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@(title.c_str())]; + [window setAcceptsMouseMovedEvents:YES]; + [window center]; + [window makeKeyAndOrderFront:nil]; + + auto view = [[View alloc] initWithFrame:kContentRect]; + view->vulkanExampleBase = this; + + [window setDelegate:view]; + [window setContentView:view]; + + return window; +} + +void VulkanExampleBase::mouseDragged(float x, float y) +{ + handleMouseMove(static_cast(x), static_cast(y)); +} + +void VulkanExampleBase::windowWillResize(float x, float y) +{ + resizing = true; + if (prepared) + { + destWidth = x; + destHeight = y; + windowResize(); + } + std::cout << "resize" << std::endl; +} + +void VulkanExampleBase::windowDidResize() +{ + std::cout << "done" << std::endl; + resizing = false; +} #endif -void VulkanExampleBase::viewChanged() {} - -void VulkanExampleBase::keyPressed(uint32_t) {} - -void VulkanExampleBase::mouseMoved(double x, double y, bool & handled) {} - -void VulkanExampleBase::buildCommandBuffers() {} - -void VulkanExampleBase::createSynchronizationPrimitives() -{ - // Wait fences to sync command buffer access - VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo(VK_FENCE_CREATE_SIGNALED_BIT); - waitFences.resize(drawCmdBuffers.size()); - for (auto& fence : waitFences) { - VK_CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); - } -} - -void VulkanExampleBase::createCommandPool() -{ - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; - cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool)); -} - -void VulkanExampleBase::setupDepthStencil() -{ - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = depthFormat; - imageCI.extent = { width, height, 1 }; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.samples = VK_SAMPLE_COUNT_1_BIT; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &depthStencil.image)); - VkMemoryRequirements memReqs{}; - vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); - - VkMemoryAllocateInfo memAllloc{}; - memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllloc.allocationSize = memReqs.size; - memAllloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllloc, nullptr, &depthStencil.mem)); - VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); - - VkImageViewCreateInfo imageViewCI{}; - imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.image = depthStencil.image; - imageViewCI.format = depthFormat; - imageViewCI.subresourceRange.baseMipLevel = 0; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.baseArrayLayer = 0; - imageViewCI.subresourceRange.layerCount = 1; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - // Stencil aspect should only be set on depth + stencil formats (VK_FORMAT_D16_UNORM_S8_UINT..VK_FORMAT_D32_SFLOAT_S8_UINT - if (depthFormat >= VK_FORMAT_D16_UNORM_S8_UINT) { - imageViewCI.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &depthStencil.view)); -} +void VulkanExampleBase::windowResized() {} void VulkanExampleBase::setupFrameBuffer() { - VkImageView attachments[2]; + /* + MSAA + */ + if (settings.multiSampling) { + // Check if device supports requested sample count for color and depth frame buffer + //assert((deviceProperties.limits.framebufferColorSampleCounts >= sampleCount) && (deviceProperties.limits.framebufferDepthSampleCounts >= sampleCount)); + + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = swapChain.colorFormat; + imageCI.extent.width = width; + imageCI.extent.height = height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.color.image)); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, multisampleTarget.color.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + VkBool32 lazyMemTypePresent; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.color.memory)); + vkBindImageMemory(device, multisampleTarget.color.image, multisampleTarget.color.memory, 0); + + // Create image view for the MSAA target + VkImageViewCreateInfo imageViewCI{}; + imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCI.image = multisampleTarget.color.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = swapChain.colorFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.color.view)); + + // Depth target + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = depthFormat; + imageCI.extent.width = width; + imageCI.extent.height = height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depth.image)); + + vkGetImageMemoryRequirements(device, multisampleTarget.depth.image, &memReqs); + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depth.memory)); + vkBindImageMemory(device, multisampleTarget.depth.image, multisampleTarget.depth.memory, 0); + + // Create image view for the MSAA target + imageViewCI.image = multisampleTarget.depth.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = depthFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depth.view)); + } + // Depth/Stencil attachment is the same for all frame buffers - attachments[1] = depthStencil.view; - VkFramebufferCreateInfo frameBufferCreateInfo = {}; - frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - frameBufferCreateInfo.pNext = NULL; - frameBufferCreateInfo.renderPass = renderPass; - frameBufferCreateInfo.attachmentCount = 2; - frameBufferCreateInfo.pAttachments = attachments; - frameBufferCreateInfo.width = width; - frameBufferCreateInfo.height = height; - frameBufferCreateInfo.layers = 1; + VkImageCreateInfo image = {}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depthFormat; + image.extent = { width, height, 1 }; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image.flags = 0; + + VkMemoryAllocateInfo mem_alloc = {}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkImageViewCreateInfo depthStencilView = {}; + depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + depthStencilView.pNext = NULL; + depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; + depthStencilView.format = depthFormat; + depthStencilView.flags = 0; + depthStencilView.subresourceRange = {}; + depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + depthStencilView.subresourceRange.baseMipLevel = 0; + depthStencilView.subresourceRange.levelCount = 1; + depthStencilView.subresourceRange.baseArrayLayer = 0; + depthStencilView.subresourceRange.layerCount = 1; + + VkMemoryRequirements memReqs; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthStencil.image)); + vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); + mem_alloc.allocationSize = memReqs.size; + mem_alloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem)); + VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); + + depthStencilView.image = depthStencil.image; + VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view)); + + // + + VkImageView attachments[4]; + + if (settings.multiSampling) { + attachments[0] = multisampleTarget.color.view; + attachments[2] = multisampleTarget.depth.view; + attachments[3] = depthStencil.view; + } + else { + attachments[1] = depthStencil.view; + } + + VkFramebufferCreateInfo frameBufferCI{}; + frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCI.pNext = NULL; + frameBufferCI.renderPass = renderPass; + frameBufferCI.attachmentCount = settings.multiSampling ? 4 :2; + frameBufferCI.pAttachments = attachments; + frameBufferCI.width = width; + frameBufferCI.height = height; + frameBufferCI.layers = 1; // Create frame buffers for every swap chain image frameBuffers.resize(swapChain.imageCount); - for (uint32_t i = 0; i < frameBuffers.size(); i++) - { - attachments[0] = swapChain.buffers[i].view; - VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i])); + for (uint32_t i = 0; i < frameBuffers.size(); i++) { + if (settings.multiSampling) { + attachments[1] = swapChain.buffers[i].view; + } + else { + attachments[0] = swapChain.buffers[i].view; + } + VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &frameBuffers[i])); } } -void VulkanExampleBase::setupRenderPass() -{ - std::array attachments = {}; - // Color attachment - attachments[0].format = swapChain.colorFormat; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Depth attachment - attachments[1].format = depthFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depthReference = {}; - depthReference.attachment = 1; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pDepthStencilAttachment = &depthReference; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; - subpassDescription.pResolveAttachments = nullptr; - - // Subpass dependencies for layout transitions - std::array dependencies; - - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - dependencies[0].dependencyFlags = 0; - - dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].dstSubpass = 0; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].srcAccessMask = 0; - dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - dependencies[1].dependencyFlags = 0; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = static_cast(attachments.size()); - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpassDescription; - renderPassInfo.dependencyCount = static_cast(dependencies.size()); - renderPassInfo.pDependencies = dependencies.data(); - - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)); -} - -void VulkanExampleBase::getEnabledFeatures() {} - -void VulkanExampleBase::getEnabledExtensions() {} - void VulkanExampleBase::windowResize() { - if (!prepared) - { + if (!prepared) { return; } prepared = false; - resized = true; - // Ensure all operations on the device have been finished before destroying resources vkDeviceWaitIdle(device); - - // Recreate swap chain width = destWidth; height = destHeight; setupSwapChain(); - - // Recreate the frame buffers + if (settings.multiSampling) { + vkDestroyImageView(device, multisampleTarget.color.view, nullptr); + vkDestroyImage(device, multisampleTarget.color.image, nullptr); + vkFreeMemory(device, multisampleTarget.color.memory, nullptr); + vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); + vkDestroyImage(device, multisampleTarget.depth.image, nullptr); + vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); + } 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(); - - if ((width > 0.0f) && (height > 0.0f)) { - if (settings.overlay) { - UIOverlay.resize(width, height); - } - } - - // Command buffers need to be recreated as they may store - // references to the recreated frame buffer - destroyCommandBuffers(); - createCommandBuffers(); - buildCommandBuffers(); - - // SRS - Recreate fences in case number of swapchain images has changed on resize - for (auto& fence : waitFences) { - vkDestroyFence(device, fence, nullptr); - } - createSynchronizationPrimitives(); - vkDeviceWaitIdle(device); - if ((width > 0.0f) && (height > 0.0f)) { - camera.updateAspectRatio((float)width / (float)height); - } - - // Notify derived class + camera.updateAspectRatio((float)width / (float)height); windowResized(); - viewChanged(); prepared = true; } @@ -2886,13 +2014,13 @@ void VulkanExampleBase::handleMouseMove(int32_t x, int32_t y) int32_t dx = (int32_t)mousePos.x - x; int32_t dy = (int32_t)mousePos.y - y; - bool handled = false; + ImGuiIO& io = ImGui::GetIO(); + bool handled = io.WantCaptureMouse; - if (settings.overlay) { - ImGuiIO& io = ImGui::GetIO(); - handled = io.WantCaptureMouse && UIOverlay.visible; + if (handled) { + mousePos = glm::vec2((float)x, (float)y); + return; } - mouseMoved((float)x, (float)y, handled); if (handled) { mousePos = glm::vec2((float)x, (float)y); @@ -2901,47 +2029,34 @@ void VulkanExampleBase::handleMouseMove(int32_t x, int32_t y) if (mouseButtons.left) { camera.rotate(glm::vec3(dy * camera.rotationSpeed, -dx * camera.rotationSpeed, 0.0f)); - viewUpdated = true; } if (mouseButtons.right) { - camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f)); - viewUpdated = true; + camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f * camera.movementSpeed)); } if (mouseButtons.middle) { - camera.translate(glm::vec3(-dx * 0.005f, -dy * 0.005f, 0.0f)); - viewUpdated = true; + camera.translate(glm::vec3(-dx * 0.01f, -dy * 0.01f, 0.0f)); } mousePos = glm::vec2((float)x, (float)y); } -void VulkanExampleBase::windowResized() {} - void VulkanExampleBase::initSwapchain() { #if defined(_WIN32) swapChain.initSurface(windowInstance, window); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) swapChain.initSurface(androidApp->window); -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - swapChain.initSurface(view); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - swapChain.initSurface(dfb, surface); +#elif defined(_DIRECT2DISPLAY) + swapChain.initSurface(width, height); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) swapChain.initSurface(display, surface); #elif defined(VK_USE_PLATFORM_XCB_KHR) swapChain.initSurface(connection, window); -#elif (defined(_DIRECT2DISPLAY) || defined(VK_USE_PLATFORM_HEADLESS_EXT)) - swapChain.initSurface(width, height); +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + swapChain.initSurface((__bridge void*)[window contentView]); #endif } void VulkanExampleBase::setupSwapChain() { - swapChain.create(&width, &height, settings.vsync, settings.fullscreen); + swapChain.create(&width, &height, settings.vsync); } - -void VulkanExampleBase::OnUpdateUIOverlay(vks::UIOverlay *overlay) {} - -#if defined(_WIN32) -void VulkanExampleBase::OnHandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {}; -#endif diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 273c715..6ab7aec 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -1,7 +1,7 @@ /* * Vulkan Example base class * -* Copyright (C) by Sascha Willems - www.saschawillems.de +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ @@ -13,252 +13,156 @@ #include #include #include -#include #elif defined(VK_USE_PLATFORM_ANDROID_KHR) #include #include #include #include #include "VulkanAndroid.h" -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) -#include #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) #include -#include "xdg-shell-client-protocol.h" #elif defined(_DIRECT2DISPLAY) // #elif defined(VK_USE_PLATFORM_XCB_KHR) #include +#elif defined(VK_USE_PLATFORM_MACOS_MVK) +#include +#include +#include +#include #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_ENABLE_EXPERIMENTAL #include -#include -#include -#include #include -#include +#include #include +#include #include "vulkan/vulkan.h" -#include "CommandLineParser.hpp" -#include "keycodes.hpp" -#include "VulkanTools.h" -#include "VulkanDebug.h" -#include "VulkanUIOverlay.h" -#include "VulkanSwapChain.h" -#include "VulkanBuffer.h" -#include "VulkanDevice.hpp" -#include "VulkanTexture.h" - -#include "VulkanInitializers.hpp" +#include "macros.h" #include "camera.hpp" -#include "benchmark.hpp" +#include "keycodes.hpp" + +#include "VulkanDevice.hpp" +#include "VulkanSwapChain.hpp" + +#include "imgui/imgui.h" class VulkanExampleBase { -private: - std::string getWindowTitle(); +private: + float fpsTimer = 0.0f; + uint32_t frameCounter = 0; uint32_t destWidth; uint32_t destHeight; bool resizing = false; - void windowResize(); void handleMouseMove(int32_t x, int32_t y); - void nextFrame(); - void updateOverlay(); - void createPipelineCache(); - void createCommandPool(); - void createSynchronizationPrimitives(); - void initSwapchain(); - void setupSwapChain(); - void createCommandBuffers(); - void destroyCommandBuffers(); - std::string shaderDir = "glsl"; + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallback; + PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallback; + VkDebugReportCallbackEXT debugReportCallback; + struct MultisampleTarget { + struct { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } color; + struct { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } depth; + } multisampleTarget; protected: - // Returns the path to the root of the glsl or hlsl shader directory. - std::string getShadersPath() const; - // Returns the path to the root of the homework glsl or hlsl shader directory. - std::string getHomeworkShadersPath() const; - // Frame counter to display fps - uint32_t frameCounter = 0; - uint32_t lastFPS = 0; - std::chrono::time_point lastTimestamp, tPrevEnd; - // Vulkan instance, stores all per-application states VkInstance instance; - std::vector supportedInstanceExtensions; - // Physical device (GPU) that Vulkan will use VkPhysicalDevice physicalDevice; - // Stores physical device properties (for e.g. checking device limits) VkPhysicalDeviceProperties deviceProperties; - // Stores the features available on the selected physical device (for e.g. checking if a feature is available) VkPhysicalDeviceFeatures deviceFeatures; - // Stores all available memory (type) properties for the physical device VkPhysicalDeviceMemoryProperties deviceMemoryProperties; - /** @brief Set of physical device features to be enabled for this example (must be set in the derived constructor) */ - VkPhysicalDeviceFeatures enabledFeatures{}; - /** @brief Set of device extensions to be enabled for this example (must be set in the derived constructor) */ - std::vector enabledDeviceExtensions; - std::vector enabledInstanceExtensions; - /** @brief Optional pNext structure for passing extension structures to device creation */ - void* deviceCreatepNextChain = nullptr; - /** @brief Logical device, application's view of the physical device (GPU) */ VkDevice device; - // Handle to the device graphics queue that command buffers are submitted to - VkQueue queue; - // Depth buffer format (selected during Vulkan initialization) - VkFormat depthFormat; - // Command buffer pool - VkCommandPool cmdPool; - /** @brief Pipeline stages used to wait at for graphics queue submissions */ - VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - // Contains command buffers and semaphores to be presented to the queue - VkSubmitInfo submitInfo; - // Command buffers used for rendering - std::vector drawCmdBuffers; - // Global render pass for frame buffer writes - VkRenderPass renderPass = VK_NULL_HANDLE; - // List of available frame buffers (same as number of swap chain images) - std::vectorframeBuffers; - // Active frame buffer index - uint32_t currentBuffer = 0; - // Descriptor set pool - VkDescriptorPool descriptorPool = VK_NULL_HANDLE; - // List of shader modules created (stored for cleanup) - std::vector shaderModules; - // Pipeline cache object - VkPipelineCache pipelineCache; - // Wraps the swap chain to present images (framebuffers) to the windowing system - VulkanSwapChain swapChain; - // Synchronization semaphores - struct { - // Swap chain image presentation - VkSemaphore presentComplete; - // Command buffer submission and execution - VkSemaphore renderComplete; - } semaphores; - std::vector waitFences; -public: - bool prepared = false; - bool resized = false; - bool viewUpdated = false; - uint32_t width = 1280; - uint32_t height = 720; - - vks::UIOverlay UIOverlay; - CommandLineParser commandLineParser; - - /** @brief Last frame time measured using a high performance timer (if available) */ - float frameTimer = 1.0f; - - vks::Benchmark benchmark; - - /** @brief Encapsulated physical and logical vulkan device */ vks::VulkanDevice *vulkanDevice; - - /** @brief Example settings that can be changed e.g. by command line arguments */ - struct Settings { - /** @brief Activates validation layers (and message output) when set to true */ - bool validation = false; - /** @brief Set to true if fullscreen mode has been requested via command line */ - bool fullscreen = false; - /** @brief Set to true if v-sync will be forced for the swapchain */ - bool vsync = false; - /** @brief Enable UI overlay */ - bool overlay = true; - } settings; - - VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } }; - - static std::vector args; - - // Defines a frame rate independent timer value clamped from -1.0...1.0 - // For use in animations, rotations, etc. - float timer = 0.0f; - // Multiplier for speeding up (or slowing down) the global timer - float timerSpeed = 0.25f; - bool paused = false; - - Camera camera; - glm::vec2 mousePos; - + VkQueue queue; + VkFormat depthFormat; + VkCommandPool cmdPool; + VkRenderPass renderPass; + std::vectorframeBuffers; + uint32_t currentBuffer = 0; + VkDescriptorPool descriptorPool; + VkPipelineCache pipelineCache; + VulkanSwapChain swapChain; std::string title = "Vulkan Example"; std::string name = "vulkanExample"; - uint32_t apiVersion = VK_API_VERSION_1_0; + void windowResize(); +public: + static std::vector args; + bool prepared = false; + uint32_t width = 1280; + uint32_t height = 720; + float frameTimer = 1.0f; + Camera camera; + glm::vec2 mousePos; + bool paused = false; + uint32_t lastFPS = 0; - struct { + struct Settings { + bool validation = false; + bool fullscreen = false; + bool vsync = false; + bool multiSampling = true; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; + } settings; + + struct DepthStencil { VkImage image; VkDeviceMemory mem; VkImageView view; } depthStencil; - struct { + struct GamePadState { glm::vec2 axisLeft = glm::vec2(0.0f); glm::vec2 axisRight = glm::vec2(0.0f); } gamePadState; - struct { + struct MouseButtons { bool left = false; bool right = false; bool middle = false; } mouseButtons; - // OS specific + // OS specific #if defined(_WIN32) HWND window; HINSTANCE windowInstance; #elif defined(VK_USE_PLATFORM_ANDROID_KHR) // true if application has focused, false if moved to background bool focused = false; - struct TouchPos { - int32_t x; - int32_t y; - } touchPos; - bool touchDown = false; - double touchTimer = 0.0; - int64_t lastTapTime = 0; -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - void* view; -#if defined(VK_EXAMPLE_XCODE_GENERATED) - bool quit = false; -#endif -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - bool quit = false; - IDirectFB *dfb = nullptr; - IDirectFBDisplayLayer *layer = nullptr; - IDirectFBWindow *window = nullptr; - IDirectFBSurface *surface = nullptr; - IDirectFBEventBuffer *event_buffer = nullptr; + std::string androidProduct; + struct TouchPoint { + int32_t id; + float x; + float y; + bool down = false; + }; + float pinchDist = 0.0f; + std::array touchPoints; #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) wl_display *display = nullptr; wl_registry *registry = nullptr; wl_compositor *compositor = nullptr; - struct xdg_wm_base *shell = nullptr; + wl_shell *shell = nullptr; wl_seat *seat = nullptr; wl_pointer *pointer = nullptr; wl_keyboard *keyboard = nullptr; wl_surface *surface = nullptr; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; + wl_shell_surface *shell_surface = nullptr; bool quit = false; - bool configured = false; #elif defined(_DIRECT2DISPLAY) bool quit = false; @@ -268,36 +172,19 @@ public: xcb_screen_t *screen; xcb_window_t window; xcb_intern_atom_reply_t *atom_wm_delete_window; -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - bool quit = false; +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + NSWindow* window; #endif - VulkanExampleBase(bool enableValidation = false); - virtual ~VulkanExampleBase(); - /** @brief Setup the vulkan instance, enable required extensions and connect to the physical device (GPU) */ - bool initVulkan(); - #if defined(_WIN32) - void setupConsole(std::string title); - void setupDPIAwareness(); HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc); void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); #elif defined(VK_USE_PLATFORM_ANDROID_KHR) static int32_t handleAppInput(struct android_app* app, AInputEvent* event); static void handleAppCommand(android_app* app, int32_t cmd); -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - void* setupWindow(void* view); - void displayLinkOutputCb(); - void mouseDragged(float x, float y); - void windowWillResize(float x, float y); - void windowDidResize(); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - IDirectFBSurface *setupWindow(); - void handleEvent(const DFBWindowEvent *event); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - struct xdg_surface *setupWindow(); + wl_shell_surface *setupWindow(); void initWaylandConnection(); - void setSize(int width, int height); static void registryGlobalCb(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); void registryGlobal(struct wl_registry *registry, uint32_t name, @@ -343,192 +230,28 @@ public: xcb_window_t setupWindow(); void initxcbConnection(); void handleEvent(const xcb_generic_event_t *event); -#else - void setupWindow(); +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + NSWindow* setupWindow(); + void mouseDragged(float x, float y); + void windowWillResize(float x, float y); + void windowDidResize(); #endif - /** @brief (Virtual) Creates the application wide Vulkan instance */ + + VulkanExampleBase(); + virtual ~VulkanExampleBase(); + + void initVulkan(); + virtual VkResult createInstance(bool enableValidation); - /** @brief (Pure virtual) Render function to be implemented by the sample application */ virtual void render() = 0; - /** @brief (Virtual) Called when the camera view has changed */ - virtual void viewChanged(); - /** @brief (Virtual) Called after a key was pressed, can be used to do custom key handling */ - virtual void keyPressed(uint32_t); - /** @brief (Virtual) Called after the mouse cursor moved and before internal events (like camera rotation) is handled */ - virtual void mouseMoved(double x, double y, bool &handled); - /** @brief (Virtual) Called when the window has been resized, can be used by the sample application to recreate resources */ virtual void windowResized(); - /** @brief (Virtual) Called when resources have been recreated that require a rebuild of the command buffers (e.g. frame buffer), to be implemented by the sample application */ - virtual void buildCommandBuffers(); - /** @brief (Virtual) Setup default depth and stencil views */ - virtual void setupDepthStencil(); - /** @brief (Virtual) Setup default framebuffers for all requested swapchain images */ virtual void setupFrameBuffer(); - /** @brief (Virtual) Setup a default renderpass */ - virtual void setupRenderPass(); - /** @brief (Virtual) Called after the physical device features have been read, can be used to set features to enable on the device */ - virtual void getEnabledFeatures(); - /** @brief (Virtual) Called after the physical device extensions have been read, can be used to enable extensions based on the supported extension listing*/ - virtual void getEnabledExtensions(); - - /** @brief Prepares all Vulkan resources and functions required to run the sample */ virtual void prepare(); + virtual void fileDropped(std::string filename); - /** @brief Loads a SPIR-V shader file for the given shader stage */ - VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage); + void initSwapchain(); + void setupSwapChain(); - /** @brief Entry point for the main render loop */ void renderLoop(); - - /** @brief Adds the drawing commands for the ImGui overlay to the given command buffer */ - void drawUI(const VkCommandBuffer commandBuffer); - - /** Prepare the next frame for workload submission by acquiring the next swap chain image */ - void prepareFrame(); - /** @brief Presents the current image to the swap chain */ - void submitFrame(); - /** @brief (Virtual) Default image acquire + submission and command buffer submission function */ - virtual void renderFrame(); - - /** @brief (Virtual) Called when the UI overlay is updating, can be used to add custom elements to the overlay */ - virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay); - -#if defined(_WIN32) - virtual void OnHandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -#endif + void renderFrame(); }; - -// OS specific macros for the example main entry points -#if defined(_WIN32) -// Windows entry point -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) \ -{ \ - if (plumageRender != NULL) \ - { \ - plumageRender->handleMessages(hWnd, uMsg, wParam, lParam); \ - } \ - return (DefWindowProc(hWnd, uMsg, wParam, lParam)); \ -} \ -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) \ -{ \ - for (int32_t i = 0; i < __argc; i++) { PlumageRender::args.push_back(__argv[i]); }; \ - plumageRender = new PlumageRender(); \ - plumageRender->initVulkan(); \ - plumageRender->setupWindow(hInstance, WndProc); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - return 0; \ -} -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) -// Android entry point -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -void android_main(android_app* state) \ -{ \ - plumageRender = new PlumageRender(); \ - state->userData = plumageRender; \ - state->onAppCmd = PlumageRender::handleAppCommand; \ - state->onInputEvent = PlumageRender::handleAppInput; \ - androidApp = state; \ - vks::android::getDeviceConfig(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ -} -#elif defined(_DIRECT2DISPLAY) -// Linux entry point with direct to display wsi -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -static void handleEvent() \ -{ \ -} \ -int main(const int argc, const char *argv[]) \ -{ \ - for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \ - plumageRender = new PlumageRender(); \ - plumageRender->initVulkan(); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - return 0; \ -} -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -static void handleEvent(const DFBWindowEvent *event) \ -{ \ - if (plumageRender != NULL) \ - { \ - plumageRender->handleEvent(event); \ - } \ -} \ -int main(const int argc, const char *argv[]) \ -{ \ - for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \ - plumageRender = new PlumageRender(); \ - plumageRender->initVulkan(); \ - plumageRender->setupWindow(); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - return 0; \ -} -#elif (defined(VK_USE_PLATFORM_WAYLAND_KHR) || defined(VK_USE_PLATFORM_HEADLESS_EXT)) -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -int main(const int argc, const char *argv[]) \ -{ \ - for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \ - plumageRender = new PlumageRender(); \ - plumageRender->initVulkan(); \ - plumageRender->setupWindow(); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - return 0; \ -} -#elif defined(VK_USE_PLATFORM_XCB_KHR) -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -static void handleEvent(const xcb_generic_event_t *event) \ -{ \ - if (plumageRender != NULL) \ - { \ - plumageRender->handleEvent(event); \ - } \ -} \ -int main(const int argc, const char *argv[]) \ -{ \ - for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \ - plumageRender = new VulkanExample(); \ - plumageRender->initVulkan(); \ - plumageRender->setupWindow(); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - return 0; \ -} -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) -#if defined(VK_EXAMPLE_XCODE_GENERATED) -#define PLUMAGE_RENDER_MAIN() \ -PlumageRender *plumageRender; \ -int main(const int argc, const char *argv[]) \ -{ \ - @autoreleasepool \ - { \ - for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \ - plumageRender = new PlumageRender(); \ - plumageRender->initVulkan(); \ - plumageRender->setupWindow(nullptr); \ - plumageRender->prepare(); \ - plumageRender->renderLoop(); \ - delete(plumageRender); \ - } \ - return 0; \ -} -#else -#define PLUMAGE_RENDER_MAIN() -#endif -#endif diff --git a/src/render/glTFModel.h b/src/render/glTFModel.h index 894a350..3de9c97 100644 --- a/src/render/glTFModel.h +++ b/src/render/glTFModel.h @@ -24,6 +24,7 @@ #include "tiny_gltf.h" #include "VulkanDevice.hpp" +//#include "VulkanUtils.hpp" #include "vulkan/vulkan.h" #define ENABLE_VALIDATION false diff --git a/src/render/render.cpp b/src/render/render.cpp index 69c7e08..ba66c03 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -722,10 +722,9 @@ PlumageRender::PlumageRender(): } - //----------------------------Prepare precompute Lighting or BRDF LUT-----------------------------------------------// - //Irradiance map for diffuse lighting - - + // generate two cube maps + // irradiance cube map + // prefileter environment cube map void PlumageRender::generateCubemaps() { enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 }; @@ -1138,12 +1137,12 @@ PlumageRender::PlumageRender(): switch (target) { case IRRADIANCE: irradiancePushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; - vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockIrradiance), &pushBlockIrradiance); + vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(IrradiancePushBlock), &irradiancePushBlock); break; case PREFILTEREDENV: prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; prefilterPushBlock.roughness = (float)m / (float)(numMips - 1); - vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockPrefilterEnv), &pushBlockPrefilterEnv); + vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock); break; }; @@ -1263,8 +1262,8 @@ PlumageRender::PlumageRender(): std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; } } - - void PlumageRender::GenerateBRDFLUT() + // generate BRDF integration map for roughness/NdotV + void PlumageRender::generateBRDFLUT() { auto tStart = std::chrono::high_resolution_clock::now(); @@ -1461,8 +1460,8 @@ PlumageRender::PlumageRender(): // Look-up-table (from BRDF) pipeline shaderStages = { - loadShader(device, "genbrdflut.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device, "genbrdflut.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) + loadShader(filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), + loadShader(filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); @@ -1520,136 +1519,196 @@ PlumageRender::PlumageRender(): auto tDiff = std::chrono::duration(tEnd - tStart).count(); std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl; } - //----------------------------End Precompute brick------------------------------------------------------------------// -#pragma region pbr render pass setting - - void PlumageRender::createAttachment( - VkFormat format, - VkImageUsageFlagBits usage, - FrameBufferAttachment* attachment, - uint32_t width, - uint32_t height) - { - VkImageAspectFlags aspectMask = 0; - VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - attachment->format = format; - if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) - { - aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageUsage |= VK_IMAGE_USAGE_SAMPLED_BIT; - } - if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) - { - aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if (format >= VK_FORMAT_D16_UNORM_S8_UINT) - aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - - assert(aspectMask > 0); - - VkImageCreateInfo image = vks::initializers::imageCreateInfo(); - image.imageType = VK_IMAGE_TYPE_2D; - image.format = format; - image.extent.width = width; - image.extent.height = height; - image.extent.depth = 1; - image.mipLevels = 1; - image.arrayLayers = 1; - image.samples = VK_SAMPLE_COUNT_1_BIT; - image.tiling = VK_IMAGE_TILING_OPTIMAL; - image.usage = imageUsage | usage; - - VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &attachment->image)); - vkGetImageMemoryRequirements(device, attachment->image, &memReqs); - memAlloc.allocationSize = memReqs.size; - memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device, attachment->image, attachment->deviceMemory, 0)); - - VkImageViewCreateInfo imageView = vks::initializers::imageViewCreateInfo(); - imageView.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageView.format = format; - imageView.subresourceRange = {}; - imageView.subresourceRange.aspectMask = aspectMask; - imageView.subresourceRange.baseMipLevel = 0; - imageView.subresourceRange.levelCount = 1; - imageView.subresourceRange.baseArrayLayer = 0; - imageView.subresourceRange.layerCount = 1; - imageView.image = attachment->image; - VK_CHECK_RESULT(vkCreateImageView(device, &imageView, nullptr, &attachment->imageView)); - } - -#pragma endregion + // Prepare and initialize uniform buffer containing shader uniforms void PlumageRender::prepareUniformBuffers() { - // Vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &shaderData.buffer, - sizeof(shaderData.values))); - - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &shaderData.skinSSBO, - sizeof(glm::mat4) * glTFModel.nodeCount)); - - // Map persistent - VK_CHECK_RESULT(shaderData.buffer.map()); - VK_CHECK_RESULT(shaderData.skinSSBO.map()); - - for (auto& material : glTFModel.materials) - { - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - &material.materialData.buffer, - sizeof(VulkanglTFModel::MaterialData::Values), - &material.materialData.values)); + for (auto& uniformBuffer : uniformBuffers) { + uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene)); + uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox)); + uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData)); } - updateUniformBuffers(); } void PlumageRender::updateUniformBuffers() { - shaderData.values.projection = camera.matrices.perspective; - shaderData.values.model = camera.matrices.view; - shaderData.values.viewPos = camera.viewPos; - shaderData.values.bFlagSet.x = normalMapping; - shaderData.values.bFlagSet.y = pbrEnabled; - memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values)); + // Scene + shaderDataScene.projection = camera.matrices.perspective; + shaderDataScene.view = camera.matrices.view; + + // Center and scale model + float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f; + glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]); + translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]); + + shaderDataScene.model = glm::mat4(1.0f); + shaderDataScene.model[0][0] = scale; + shaderDataScene.model[1][1] = scale; + shaderDataScene.model[2][2] = scale; + shaderDataScene.model = glm::translate(shaderDataScene.model, translate); + + shaderDataScene.camPos = glm::vec3( + -camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)), + -camera.position.z * sin(glm::radians(camera.rotation.x)), + camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)) + ); + + // Skybox + shaderDataSkybox.projection = camera.matrices.perspective; + shaderDataSkybox.view = camera.matrices.view; + shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view)); + } + + void PlumageRender::updateShaderData() + { + shaderData.lightDir = glm::vec4( + sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), + sin(glm::radians(lightSource.rotation.y)), + cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), + 0.0f); + } + + void PlumageRender::windowResized() + { + buildCommandBuffers(); + vkDeviceWaitIdle(device); + updateUniformBuffers(); + // update UI + //updateUIOverlay(); } void PlumageRender::prepare() { VulkanExampleBase::prepare(); + + camera.type = Camera::CameraType::lookat; + + camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f); + camera.rotationSpeed = 0.25f; + camera.movementSpeed = 0.1f; + camera.setPosition({ 0.0f, 0.0f, 1.0f }); + camera.setRotation({ 0.0f, 0.0f, 0.0f }); + + waitFences.resize(renderAhead); + presentCompleteSemaphores.resize(renderAhead); + renderCompleteSemaphores.resize(renderAhead); + commandBuffers.resize(swapChain.imageCount); + uniformBuffers.resize(swapChain.imageCount); + descriptorSets.resize(swapChain.imageCount); + // Command buffer execution fences + for (auto& waitFence : waitFences) { + VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT }; + VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence)); + } + // Queue ordering semaphores + for (auto& semaphore : presentCompleteSemaphores) { + VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; + VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); + } + for (auto& semaphore : renderCompleteSemaphores) { + VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; + VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); + } + // Command buffers + { + VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; + cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdBufAllocateInfo.commandPool = cmdPool; + cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdBufAllocateInfo.commandBufferCount = static_cast(commandBuffers.size()); + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandBuffers.data())); + } + loadAssets(); - GenerateBRDFLUT(); - GenerateIrradianceCubemap(); - GeneratePrefilteredCubemap(); + generateBRDFLUT(); + generateCubemaps(); prepareUniformBuffers(); setupDescriptors(); preparePipelines(); + + //ui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount); + //updateOverlay(); + buildCommandBuffers(); + prepared = true; } void PlumageRender::render() { - renderFrame(); + if (!prepared) { + return; + } + + //updateOverlay(); + + VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); + VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); + + VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer); + if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) { + windowResize(); + } + else { + VK_CHECK_RESULT(acquire); + } + + // Update UBOs + updateUniformBuffers(); + UniformBufferSet currentUB = uniformBuffers[currentBuffer]; + memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene)); + memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData)); + memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox)); + + const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pWaitDstStageMask = &waitDstStageMask; + submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; + submitInfo.commandBufferCount = 1; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); + + VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); + if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { + if (present == VK_ERROR_OUT_OF_DATE_KHR) { + windowResize(); + return; + } + else { + VK_CHECK_RESULT(present); + } + } + + frameIndex += 1; + frameIndex %= renderAhead; + + if (!paused) { + if (rotateModel) { + modelrot.y += frameTimer * 35.0f; + if (modelrot.y > 360.0f) { + modelrot.y -= 360.0f; + } + } + if ((animate) && (models.scene.animations.size() > 0)) { + animationTimer += frameTimer; + if (animationTimer > models.scene.animations[animationIndex].end) { + animationTimer -= models.scene.animations[animationIndex].end; + } + models.scene.updateAnimation(animationIndex, animationTimer); + } + updateShaderData(); + if (rotateModel) { + updateUniformBuffers(); + } + } if (camera.updated) { updateUniformBuffers(); } - if (!paused && glTFModel.animations.size() > 0) - { - glTFModel.updateAnimation(frameTimer, shaderData.skinSSBO); - } } diff --git a/src/render/render.h b/src/render/render.h index 435d1cf..08d9a89 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -14,6 +14,7 @@ #include #include "VulkanExampleBase.h" #include "glTFModel.h" +#include "VulkanUtils.hpp" //#include "VulkanDevice.hpp" @@ -60,9 +61,9 @@ public: } shaderData; struct UniformBufferSet { - vks::Buffer scene; - vks::Buffer skybox; - vks::Buffer params; + Buffer scene; + Buffer skybox; + Buffer params; }; struct UBOMatrices { @@ -70,7 +71,7 @@ public: glm::mat4 model; glm::mat4 view; glm::vec3 camPos; - } shaderValuesScene, shaderValuesSkybox; + } shaderDataScene, shaderDataSkybox; struct PushConstBlockMaterial { glm::vec4 baseColorFactor; @@ -112,8 +113,8 @@ public: std::string filterVertShaderPath = getAssetPath() + "shaders/filtercube.vert.spv"; std::string prefilterEnvmapFragShaderPath = getAssetPath() + "shaders/prefilterenvmap.frag.spv"; //brdf cube map - std::string brdfVertShaderPath = getAssetPath() + "buster_drone/shaders/glsl/genbrdflut.vert.spv"; - std::string brdfFragShaderPath = getAssetPath() + "buster_drone/shaders/glsl/genbrdflut.frag.spv"; + std::string brdfVertShaderPath = getAssetPath() + "shaders/genbrdflut.vert.spv"; + std::string brdfFragShaderPath = getAssetPath() + "shaders/genbrdflut.frag.spv"; // environment map texture std::string envMapFilePath = getAssetPath() + "environments/papermill.ktx"; @@ -284,9 +285,11 @@ public: void preparePipelines(); void CreateToneMappingPipeline(); void generateCubemaps(); - void GenerateBRDFLUT(); + void generateBRDFLUT(); void prepareUniformBuffers(); void updateUniformBuffers(); + void updateShaderData(); + void windowResized(); void prepare(); virtual void render(); virtual void viewChanged();