diff --git a/base/VulkanDevice.cpp b/base/VulkanDevice.cpp new file mode 100644 index 00000000..a0275c48 --- /dev/null +++ b/base/VulkanDevice.cpp @@ -0,0 +1,562 @@ +/* +* Vulkan device class +* +* Encapsulates a physical Vulkan device and its logical representation +* +* Copyright (C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include + +namespace vks +{ + /** + * Default constructor + * + * @param physicalDevice Physical device that is to be used + */ + VulkanDevice::VulkanDevice(VkPhysicalDevice physicalDevice) + { + assert(physicalDevice); + this->physicalDevice = physicalDevice; + + // Store Properties features, limits and properties of the physical device for later use + // Device properties also contain limits and sparse properties + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + // Features should be checked by the examples before using them + vkGetPhysicalDeviceFeatures(physicalDevice, &features); + // Memory properties are used regularly for creating all kinds of buffers + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + // Queue family properties, used for setting up requested queues upon device creation + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + assert(queueFamilyCount > 0); + queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); + + // Get list of supported extensions + uint32_t extCount = 0; + vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, nullptr); + if (extCount > 0) + { + std::vector extensions(extCount); + if (vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, &extensions.front()) == VK_SUCCESS) + { + for (auto ext : extensions) + { + supportedExtensions.push_back(ext.extensionName); + } + } + } + } + + /** + * Default destructor + * + * @note Frees the logical device + */ + VulkanDevice::~VulkanDevice() + { + if (commandPool) + { + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); + } + if (logicalDevice) + { + vkDestroyDevice(logicalDevice, nullptr); + } + } + + /** + * Get the index of a memory type that has all the requested property bits set + * + * @param typeBits Bit mask with bits set for each memory type supported by the resource to request for (from VkMemoryRequirements) + * @param properties Bit mask of properties for the memory type to request + * @param (Optional) memTypeFound Pointer to a bool that is set to true if a matching memory type has been found + * + * @return Index of the requested memory type + * + * @throw Throws an exception if memTypeFound is null and no memory type could be found that supports the requested properties + */ + uint32_t VulkanDevice::getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound) const + { + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) == 1) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + if (memTypeFound) + { + *memTypeFound = true; + } + return i; + } + } + typeBits >>= 1; + } + + if (memTypeFound) + { + *memTypeFound = false; + return 0; + } + else + { + throw std::runtime_error("Could not find a matching memory type"); + } + } + + /** + * Get the index of a queue family that supports the requested queue flags + * + * @param queueFlags Queue flags to find a queue family index for + * + * @return Index of the queue family index that matches the flags + * + * @throw Throws an exception if no queue family index could be found that supports the requested flags + */ + uint32_t VulkanDevice::getQueueFamilyIndex(VkQueueFlagBits queueFlags) const + { + // Dedicated queue for compute + // Try to find a queue family index that supports compute but not graphics + if (queueFlags & VK_QUEUE_COMPUTE_BIT) + { + for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) + { + if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) + { + return i; + } + } + } + + // Dedicated queue for transfer + // Try to find a queue family index that supports transfer but not graphics and compute + if (queueFlags & VK_QUEUE_TRANSFER_BIT) + { + for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) + { + if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0)) + { + return i; + } + } + } + + // For other queue types or if no separate compute queue is present, return the first one to support the requested flags + for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) + { + if (queueFamilyProperties[i].queueFlags & queueFlags) + { + return i; + } + } + + throw std::runtime_error("Could not find a matching queue family index"); + } + + /** + * Create the logical device based on the assigned physical device, also gets default queue family indices + * + * @param enabledFeatures Can be used to enable certain features upon device creation + * @param pNextChain Optional chain of pointer to extension structures + * @param useSwapChain Set to false for headless rendering to omit the swapchain device extensions + * @param requestedQueueTypes Bit flags specifying the queue types to be requested from the device + * + * @return VkResult of the device creation call + */ + VkResult VulkanDevice::createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, void* pNextChain, bool useSwapChain, VkQueueFlags requestedQueueTypes) + { + // Desired queues need to be requested upon logical device creation + // Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application + // requests different queue types + + std::vector queueCreateInfos{}; + + // Get queue family indices for the requested queue family types + // Note that the indices may overlap depending on the implementation + + const float defaultQueuePriority(0.0f); + + // Graphics queue + if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT) + { + queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT); + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = queueFamilyIndices.graphics; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &defaultQueuePriority; + queueCreateInfos.push_back(queueInfo); + } + else + { + queueFamilyIndices.graphics = VK_NULL_HANDLE; + } + + // Dedicated compute queue + if (requestedQueueTypes & VK_QUEUE_COMPUTE_BIT) + { + queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT); + if (queueFamilyIndices.compute != queueFamilyIndices.graphics) + { + // If compute family index differs, we need an additional queue create info for the compute queue + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = queueFamilyIndices.compute; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &defaultQueuePriority; + queueCreateInfos.push_back(queueInfo); + } + } + else + { + // Else we use the same queue + queueFamilyIndices.compute = queueFamilyIndices.graphics; + } + + // Dedicated transfer queue + if (requestedQueueTypes & VK_QUEUE_TRANSFER_BIT) + { + queueFamilyIndices.transfer = getQueueFamilyIndex(VK_QUEUE_TRANSFER_BIT); + if ((queueFamilyIndices.transfer != queueFamilyIndices.graphics) && (queueFamilyIndices.transfer != queueFamilyIndices.compute)) + { + // If compute family index differs, we need an additional queue create info for the compute queue + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = queueFamilyIndices.transfer; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &defaultQueuePriority; + queueCreateInfos.push_back(queueInfo); + } + } + else + { + // Else we use the same queue + queueFamilyIndices.transfer = queueFamilyIndices.graphics; + } + + // Create the logical device representation + std::vector deviceExtensions(enabledExtensions); + if (useSwapChain) + { + // If the device will be used for presenting to a display via a swapchain we need to request the swapchain extension + deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size());; + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.pEnabledFeatures = &enabledFeatures; + + // If a pNext(Chain) has been passed, we need to add it to the device creation info + VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{}; + if (pNextChain) { + physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + physicalDeviceFeatures2.features = enabledFeatures; + physicalDeviceFeatures2.pNext = pNextChain; + deviceCreateInfo.pEnabledFeatures = nullptr; + deviceCreateInfo.pNext = &physicalDeviceFeatures2; + } + + // Enable the debug marker extension if it is present (likely meaning a debugging tool is present) + if (extensionSupported(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) + { + deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); + enableDebugMarkers = true; + } + + if (deviceExtensions.size() > 0) + { + deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size(); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); + } + + VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice); + + if (result == VK_SUCCESS) + { + // Create a default command pool for graphics command buffers + commandPool = createCommandPool(queueFamilyIndices.graphics); + } + + this->enabledFeatures = enabledFeatures; + + return result; + } + + /** + * Create a buffer on the device + * + * @param usageFlags Usage flag bit mask for the buffer (i.e. index, vertex, uniform buffer) + * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) + * @param size Size of the buffer in byes + * @param buffer Pointer to the buffer handle acquired by the function + * @param memory Pointer to the memory handle acquired by the function + * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) + * + * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied + */ + VkResult VulkanDevice::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data) + { + // Create the buffer handle + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size); + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + // Find a memory type index that fits the properties of the buffer + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory)); + + // If a pointer to the buffer data has been passed, map the buffer and copy over the data + if (data != nullptr) + { + void *mapped; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, *memory, 0, size, 0, &mapped)); + memcpy(mapped, data, size); + // If host coherency hasn't been requested, do a manual flush to make writes visible + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + { + VkMappedMemoryRange mappedRange = vks::initializers::mappedMemoryRange(); + mappedRange.memory = *memory; + mappedRange.offset = 0; + mappedRange.size = size; + vkFlushMappedMemoryRanges(logicalDevice, 1, &mappedRange); + } + vkUnmapMemory(logicalDevice, *memory); + } + + // Attach the memory to the buffer object + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, *buffer, *memory, 0)); + + return VK_SUCCESS; + } + + /** + * Create a buffer on the device + * + * @param usageFlags Usage flag bit mask for the buffer (i.e. index, vertex, uniform buffer) + * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) + * @param buffer Pointer to a vk::Vulkan buffer object + * @param size Size of the buffer in byes + * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) + * + * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied + */ + VkResult VulkanDevice::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vks::Buffer *buffer, VkDeviceSize size, void *data) + { + buffer->device = logicalDevice; + + // Create the buffer handle + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size); + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &buffer->buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + vkGetBufferMemoryRequirements(logicalDevice, buffer->buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + // Find a memory type index that fits the properties of the buffer + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, &buffer->memory)); + + buffer->alignment = memReqs.alignment; + buffer->size = size; + buffer->usageFlags = usageFlags; + buffer->memoryPropertyFlags = memoryPropertyFlags; + + // If a pointer to the buffer data has been passed, map the buffer and copy over the data + if (data != nullptr) + { + VK_CHECK_RESULT(buffer->map()); + memcpy(buffer->mapped, data, size); + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + buffer->flush(); + + buffer->unmap(); + } + + // Initialize a default descriptor that covers the whole buffer size + buffer->setupDescriptor(); + + // Attach the memory to the buffer object + return buffer->bind(); + } + + /** + * Copy buffer data from src to dst using VkCmdCopyBuffer + * + * @param src Pointer to the source buffer to copy from + * @param dst Pointer to the destination buffer to copy to + * @param queue Pointer + * @param copyRegion (Optional) Pointer to a copy region, if NULL, the whole buffer is copied + * + * @note Source and destination pointers must have the appropriate transfer usage flags set (TRANSFER_SRC / TRANSFER_DST) + */ + void VulkanDevice::copyBuffer(vks::Buffer *src, vks::Buffer *dst, VkQueue queue, VkBufferCopy *copyRegion) + { + assert(dst->size <= src->size); + assert(src->buffer); + VkCommandBuffer copyCmd = createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkBufferCopy bufferCopy{}; + if (copyRegion == nullptr) + { + bufferCopy.size = src->size; + } + else + { + bufferCopy = *copyRegion; + } + + vkCmdCopyBuffer(copyCmd, src->buffer, dst->buffer, 1, &bufferCopy); + + flushCommandBuffer(copyCmd, queue); + } + + /** + * Create a command pool for allocation command buffers from + * + * @param queueFamilyIndex Family index of the queue to create the command pool for + * @param createFlags (Optional) Command pool creation flags (Defaults to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + * + * @note Command buffers allocated from the created pool can only be submitted to a queue with the same family index + * + * @return A handle to the created command buffer + */ + VkCommandPool VulkanDevice::createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags) + { + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; + cmdPoolInfo.flags = createFlags; + VkCommandPool cmdPool; + VK_CHECK_RESULT(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); + return cmdPool; + } + + /** + * Allocate a command buffer from the command pool + * + * @param level Level of the new command buffer (primary or secondary) + * @param pool Command pool from which the command buffer will be allocated + * @param (Optional) begin If true, recording on the new command buffer will be started (vkBeginCommandBuffer) (Defaults to false) + * + * @return A handle to the allocated command buffer + */ + VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, VkCommandPool pool, bool begin) + { + VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(pool, level, 1); + VkCommandBuffer cmdBuffer; + VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); + // If requested, also start recording for the new command buffer + if (begin) + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo)); + } + return cmdBuffer; + } + + VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, bool begin) + { + return createCommandBuffer(level, commandPool, begin); + } + + /** + * Finish command buffer recording and submit it to a queue + * + * @param commandBuffer Command buffer to flush + * @param queue Queue to submit the command buffer to + * @param pool Command pool on which the command buffer has been created + * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true) + * + * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from + * @note Uses a fence to ensure command buffer has finished executing + */ + void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free) + { + if (commandBuffer == VK_NULL_HANDLE) + { + return; + } + + VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo = vks::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + // Create fence to ensure that the command buffer has finished executing + VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); + // Submit to the queue + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + // Wait for the fence to signal that command buffer has finished executing + VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT)); + vkDestroyFence(logicalDevice, fence, nullptr); + if (free) + { + vkFreeCommandBuffers(logicalDevice, pool, 1, &commandBuffer); + } + } + + void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free) + { + return flushCommandBuffer(commandBuffer, queue, commandPool, free); + } + + /** + * Check if an extension is supported by the (physical device) + * + * @param extension Name of the extension to check + * + * @return True if the extension is supported (present in the list read at device creation time) + */ + bool VulkanDevice::extensionSupported(std::string extension) + { + return (std::find(supportedExtensions.begin(), supportedExtensions.end(), extension) != supportedExtensions.end()); + } + + /** + * Select the best-fit depth format for this device from a list of possible depth (and stencil) formats + * + * @param checkSamplingSupport Check if the format can be sampled from (e.g. for shader reads) + * + * @return The depth format that best fits for the current device + * + * @throw Throws an exception if no depth format fits the requirements + */ + VkFormat VulkanDevice::getSupportedDepthFormat(bool checkSamplingSupport) + { + // All depth formats may be optional, so we need to find a suitable depth format to use + 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 }; + for (auto& format : depthFormats) + { + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); + // Format must support depth stencil attachment for optimal tiling + if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + if (checkSamplingSupport) { + if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { + continue; + } + } + return format; + } + } + throw std::runtime_error("Could not find a matching depth format"); + } + +}; diff --git a/base/VulkanDevice.h b/base/VulkanDevice.h new file mode 100644 index 00000000..0bb49879 --- /dev/null +++ b/base/VulkanDevice.h @@ -0,0 +1,71 @@ +/* +* Vulkan device class +* +* Encapsulates a physical Vulkan device and its logical representation +* +* Copyright (C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include "VulkanBuffer.h" +#include "VulkanTools.h" +#include "vulkan/vulkan.h" +#include +#include +#include + +namespace vks +{ +struct VulkanDevice +{ + /** @brief Physical device representation */ + VkPhysicalDevice physicalDevice; + /** @brief Logical device representation (application's view of the device) */ + VkDevice logicalDevice; + /** @brief Properties of the physical device including limits that the application can check against */ + VkPhysicalDeviceProperties properties; + /** @brief Features of the physical device that an application can use to check if a feature is supported */ + VkPhysicalDeviceFeatures features; + /** @brief Features that have been enabled for use on the physical device */ + VkPhysicalDeviceFeatures enabledFeatures; + /** @brief Memory types and heaps of the physical device */ + VkPhysicalDeviceMemoryProperties memoryProperties; + /** @brief Queue family properties of the physical device */ + std::vector queueFamilyProperties; + /** @brief List of extensions supported by the device */ + std::vector supportedExtensions; + /** @brief Default command pool for the graphics queue family index */ + VkCommandPool commandPool = VK_NULL_HANDLE; + /** @brief Set to true when the debug marker extension is detected */ + bool enableDebugMarkers = false; + /** @brief Contains queue family indices */ + struct + { + uint32_t graphics; + uint32_t compute; + uint32_t transfer; + } queueFamilyIndices; + operator VkDevice() const + { + return logicalDevice; + }; + explicit VulkanDevice(VkPhysicalDevice physicalDevice); + ~VulkanDevice(); + uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr) const; + uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags) const; + VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, void *pNextChain, bool useSwapChain = true, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr); + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vks::Buffer *buffer, VkDeviceSize size, void *data = nullptr); + void copyBuffer(vks::Buffer *src, vks::Buffer *dst, VkQueue queue, VkBufferCopy *copyRegion = nullptr); + VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, VkCommandPool pool, bool begin = false); + VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false); + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free = true); + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true); + bool extensionSupported(std::string extension); + VkFormat getSupportedDepthFormat(bool checkSamplingSupport); +}; +} // namespace vks diff --git a/base/VulkanDevice.hpp b/base/VulkanDevice.hpp deleted file mode 100644 index 4317339f..00000000 --- a/base/VulkanDevice.hpp +++ /dev/null @@ -1,604 +0,0 @@ -/* -* Vulkan device class -* -* Encapsulates a physical Vulkan device and its logical representation -* -* Copyright (C) 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 "vulkan/vulkan.h" -#include "VulkanTools.h" -#include "VulkanBuffer.h" - -namespace vks -{ - struct VulkanDevice - { - /** @brief Physical device representation */ - VkPhysicalDevice physicalDevice; - /** @brief Logical device representation (application's view of the device) */ - VkDevice logicalDevice; - /** @brief Properties of the physical device including limits that the application can check against */ - VkPhysicalDeviceProperties properties; - /** @brief Features of the physical device that an application can use to check if a feature is supported */ - VkPhysicalDeviceFeatures features; - /** @brief Features that have been enabled for use on the physical device */ - VkPhysicalDeviceFeatures enabledFeatures; - /** @brief Memory types and heaps of the physical device */ - VkPhysicalDeviceMemoryProperties memoryProperties; - /** @brief Queue family properties of the physical device */ - std::vector queueFamilyProperties; - /** @brief List of extensions supported by the device */ - std::vector supportedExtensions; - - /** @brief Default command pool for the graphics queue family index */ - VkCommandPool commandPool = VK_NULL_HANDLE; - - /** @brief Set to true when the debug marker extension is detected */ - bool enableDebugMarkers = false; - - /** @brief Contains queue family indices */ - struct - { - uint32_t graphics; - uint32_t compute; - uint32_t transfer; - } queueFamilyIndices; - - /** @brief Typecast to VkDevice */ - operator VkDevice() const { return logicalDevice; }; - - /** - * Default constructor - * - * @param physicalDevice Physical device that is to be used - */ - explicit VulkanDevice(VkPhysicalDevice physicalDevice) - { - assert(physicalDevice); - this->physicalDevice = physicalDevice; - - // Store Properties features, limits and properties of the physical device for later use - // Device properties also contain limits and sparse properties - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - // Features should be checked by the examples before using them - vkGetPhysicalDeviceFeatures(physicalDevice, &features); - // Memory properties are used regularly for creating all kinds of buffers - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); - // Queue family properties, used for setting up requested queues upon device creation - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - assert(queueFamilyCount > 0); - queueFamilyProperties.resize(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); - - // Get list of supported extensions - uint32_t extCount = 0; - vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, nullptr); - if (extCount > 0) - { - std::vector extensions(extCount); - if (vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, &extensions.front()) == VK_SUCCESS) - { - for (auto ext : extensions) - { - supportedExtensions.push_back(ext.extensionName); - } - } - } - } - - /** - * Default destructor - * - * @note Frees the logical device - */ - ~VulkanDevice() - { - if (commandPool) - { - vkDestroyCommandPool(logicalDevice, commandPool, nullptr); - } - if (logicalDevice) - { - vkDestroyDevice(logicalDevice, nullptr); - } - } - - /** - * Get the index of a memory type that has all the requested property bits set - * - * @param typeBits Bitmask with bits set for each memory type supported by the resource to request for (from VkMemoryRequirements) - * @param properties Bitmask of properties for the memory type to request - * @param (Optional) memTypeFound Pointer to a bool that is set to true if a matching memory type has been found - * - * @return Index of the requested memory type - * - * @throw Throws an exception if memTypeFound is null and no memory type could be found that supports the requested properties - */ - uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr) const - { - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) - { - if ((typeBits & 1) == 1) - { - if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - if (memTypeFound) - { - *memTypeFound = true; - } - return i; - } - } - typeBits >>= 1; - } - - if (memTypeFound) - { - *memTypeFound = false; - return 0; - } - else - { - throw std::runtime_error("Could not find a matching memory type"); - } - } - - /** - * Get the index of a queue family that supports the requested queue flags - * - * @param queueFlags Queue flags to find a queue family index for - * - * @return Index of the queue family index that matches the flags - * - * @throw Throws an exception if no queue family index could be found that supports the requested flags - */ - uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags) const - { - // Dedicated queue for compute - // Try to find a queue family index that supports compute but not graphics - if (queueFlags & VK_QUEUE_COMPUTE_BIT) - { - for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) - { - if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) - { - return i; - } - } - } - - // Dedicated queue for transfer - // Try to find a queue family index that supports transfer but not graphics and compute - if (queueFlags & VK_QUEUE_TRANSFER_BIT) - { - for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) - { - if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0)) - { - return i; - } - } - } - - // For other queue types or if no separate compute queue is present, return the first one to support the requested flags - for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) - { - if (queueFamilyProperties[i].queueFlags & queueFlags) - { - return i; - } - } - - throw std::runtime_error("Could not find a matching queue family index"); - } - - /** - * Create the logical device based on the assigned physical device, also gets default queue family indices - * - * @param enabledFeatures Can be used to enable certain features upon device creation - * @param pNextChain Optional chain of pointer to extension structures - * @param useSwapChain Set to false for headless rendering to omit the swapchain device extensions - * @param requestedQueueTypes Bit flags specifying the queue types to be requested from the device - * - * @return VkResult of the device creation call - */ - VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, void* pNextChain, bool useSwapChain = true, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT) - { - // Desired queues need to be requested upon logical device creation - // Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application - // requests different queue types - - std::vector queueCreateInfos{}; - - // Get queue family indices for the requested queue family types - // Note that the indices may overlap depending on the implementation - - const float defaultQueuePriority(0.0f); - - // Graphics queue - if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT) - { - queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT); - VkDeviceQueueCreateInfo queueInfo{}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = queueFamilyIndices.graphics; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &defaultQueuePriority; - queueCreateInfos.push_back(queueInfo); - } - else - { - queueFamilyIndices.graphics = VK_NULL_HANDLE; - } - - // Dedicated compute queue - if (requestedQueueTypes & VK_QUEUE_COMPUTE_BIT) - { - queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT); - if (queueFamilyIndices.compute != queueFamilyIndices.graphics) - { - // If compute family index differs, we need an additional queue create info for the compute queue - VkDeviceQueueCreateInfo queueInfo{}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = queueFamilyIndices.compute; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &defaultQueuePriority; - queueCreateInfos.push_back(queueInfo); - } - } - else - { - // Else we use the same queue - queueFamilyIndices.compute = queueFamilyIndices.graphics; - } - - // Dedicated transfer queue - if (requestedQueueTypes & VK_QUEUE_TRANSFER_BIT) - { - queueFamilyIndices.transfer = getQueueFamilyIndex(VK_QUEUE_TRANSFER_BIT); - if ((queueFamilyIndices.transfer != queueFamilyIndices.graphics) && (queueFamilyIndices.transfer != queueFamilyIndices.compute)) - { - // If compute family index differs, we need an additional queue create info for the compute queue - VkDeviceQueueCreateInfo queueInfo{}; - queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo.queueFamilyIndex = queueFamilyIndices.transfer; - queueInfo.queueCount = 1; - queueInfo.pQueuePriorities = &defaultQueuePriority; - queueCreateInfos.push_back(queueInfo); - } - } - else - { - // Else we use the same queue - queueFamilyIndices.transfer = queueFamilyIndices.graphics; - } - - // Create the logical device representation - std::vector deviceExtensions(enabledExtensions); - if (useSwapChain) - { - // If the device will be used for presenting to a display via a swapchain we need to request the swapchain extension - deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - } - - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size());; - deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); - deviceCreateInfo.pEnabledFeatures = &enabledFeatures; - - // If a pNext(Chain) has been passed, we need to add it to the device creation info - VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{}; - if (pNextChain) { - physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - physicalDeviceFeatures2.features = enabledFeatures; - physicalDeviceFeatures2.pNext = pNextChain; - deviceCreateInfo.pEnabledFeatures = nullptr; - deviceCreateInfo.pNext = &physicalDeviceFeatures2; - } - - // Enable the debug marker extension if it is present (likely meaning a debugging tool is present) - if (extensionSupported(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) - { - deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - enableDebugMarkers = true; - } - - if (deviceExtensions.size() > 0) - { - deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size(); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); - } - - VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice); - - if (result == VK_SUCCESS) - { - // Create a default command pool for graphics command buffers - commandPool = createCommandPool(queueFamilyIndices.graphics); - } - - this->enabledFeatures = enabledFeatures; - - return result; - } - - /** - * Create a buffer on the device - * - * @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer) - * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) - * @param size Size of the buffer in byes - * @param buffer Pointer to the buffer handle acquired by the function - * @param memory Pointer to the memory handle acquired by the function - * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) - * - * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied - */ - VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr) - { - // Create the buffer handle - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size); - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer)); - - // Create the memory backing up the buffer handle - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); - vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs); - memAlloc.allocationSize = memReqs.size; - // Find a memory type index that fits the properties of the buffer - memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); - VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory)); - - // If a pointer to the buffer data has been passed, map the buffer and copy over the data - if (data != nullptr) - { - void *mapped; - VK_CHECK_RESULT(vkMapMemory(logicalDevice, *memory, 0, size, 0, &mapped)); - memcpy(mapped, data, size); - // If host coherency hasn't been requested, do a manual flush to make writes visible - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) - { - VkMappedMemoryRange mappedRange = vks::initializers::mappedMemoryRange(); - mappedRange.memory = *memory; - mappedRange.offset = 0; - mappedRange.size = size; - vkFlushMappedMemoryRanges(logicalDevice, 1, &mappedRange); - } - vkUnmapMemory(logicalDevice, *memory); - } - - // Attach the memory to the buffer object - VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, *buffer, *memory, 0)); - - return VK_SUCCESS; - } - - /** - * Create a buffer on the device - * - * @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer) - * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) - * @param buffer Pointer to a vk::Vulkan buffer object - * @param size Size of the buffer in byes - * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) - * - * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied - */ - VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vks::Buffer *buffer, VkDeviceSize size, void *data = nullptr) - { - buffer->device = logicalDevice; - - // Create the buffer handle - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size); - VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &buffer->buffer)); - - // Create the memory backing up the buffer handle - VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); - vkGetBufferMemoryRequirements(logicalDevice, buffer->buffer, &memReqs); - memAlloc.allocationSize = memReqs.size; - // Find a memory type index that fits the properties of the buffer - memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); - VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, &buffer->memory)); - - buffer->alignment = memReqs.alignment; - buffer->size = size; - buffer->usageFlags = usageFlags; - buffer->memoryPropertyFlags = memoryPropertyFlags; - - // If a pointer to the buffer data has been passed, map the buffer and copy over the data - if (data != nullptr) - { - VK_CHECK_RESULT(buffer->map()); - memcpy(buffer->mapped, data, size); - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) - buffer->flush(); - - buffer->unmap(); - } - - // Initialize a default descriptor that covers the whole buffer size - buffer->setupDescriptor(); - - // Attach the memory to the buffer object - return buffer->bind(); - } - - /** - * Copy buffer data from src to dst using VkCmdCopyBuffer - * - * @param src Pointer to the source buffer to copy from - * @param dst Pointer to the destination buffer to copy tp - * @param queue Pointer - * @param copyRegion (Optional) Pointer to a copy region, if NULL, the whole buffer is copied - * - * @note Source and destination pointers must have the appropriate transfer usage flags set (TRANSFER_SRC / TRANSFER_DST) - */ - void copyBuffer(vks::Buffer *src, vks::Buffer *dst, VkQueue queue, VkBufferCopy *copyRegion = nullptr) - { - assert(dst->size <= src->size); - assert(src->buffer); - VkCommandBuffer copyCmd = createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - VkBufferCopy bufferCopy{}; - if (copyRegion == nullptr) - { - bufferCopy.size = src->size; - } - else - { - bufferCopy = *copyRegion; - } - - vkCmdCopyBuffer(copyCmd, src->buffer, dst->buffer, 1, &bufferCopy); - - flushCommandBuffer(copyCmd, queue); - } - - /** - * Create a command pool for allocation command buffers from - * - * @param queueFamilyIndex Family index of the queue to create the command pool for - * @param createFlags (Optional) Command pool creation flags (Defaults to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) - * - * @note Command buffers allocated from the created pool can only be submitted to a queue with the same family index - * - * @return A handle to the created command buffer - */ - VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) - { - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; - cmdPoolInfo.flags = createFlags; - VkCommandPool cmdPool; - VK_CHECK_RESULT(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); - return cmdPool; - } - - /** - * Allocate a command buffer from the command pool - * - * @param level Level of the new command buffer (primary or secondary) - * @param pool Command pool from which the command buffer will be allocated - * @param (Optional) begin If true, recording on the new command buffer will be started (vkBeginCommandBuffer) (Defaults to false) - * - * @return A handle to the allocated command buffer - */ - VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, VkCommandPool pool, bool begin = false) - { - VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(pool, level, 1); - VkCommandBuffer cmdBuffer; - VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); - // If requested, also start recording for the new command buffer - if (begin) - { - VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); - VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo)); - } - return cmdBuffer; - } - - VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false) - { - return createCommandBuffer(level, commandPool, begin); - } - - /** - * Finish command buffer recording and submit it to a queue - * - * @param commandBuffer Command buffer to flush - * @param queue Queue to submit the command buffer to - * @param pool Command pool on which the command buffer has been created - * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true) - * - * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from - * @note Uses a fence to ensure command buffer has finished executing - */ - void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free = true) - { - if (commandBuffer == VK_NULL_HANDLE) - { - return; - } - - VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); - - VkSubmitInfo submitInfo = vks::initializers::submitInfo(); - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - // Create fence to ensure that the command buffer has finished executing - VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE); - VkFence fence; - VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); - // Submit to the queue - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); - // Wait for the fence to signal that command buffer has finished executing - VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT)); - vkDestroyFence(logicalDevice, fence, nullptr); - if (free) - { - vkFreeCommandBuffers(logicalDevice, pool, 1, &commandBuffer); - } - } - - void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) - { - return flushCommandBuffer(commandBuffer, queue, commandPool, free); - } - - /** - * Check if an extension is supported by the (physical device) - * - * @param extension Name of the extension to check - * - * @return True if the extension is supported (present in the list read at device creation time) - */ - bool extensionSupported(std::string extension) - { - return (std::find(supportedExtensions.begin(), supportedExtensions.end(), extension) != supportedExtensions.end()); - } - - /** - * Select the best-fit depth format for this device from a list of possible depth (and stencil) formats - * - * @param checkSamplingSupport Check if the format can be sampled from (e.g. for shader reads) - * - * @return The depth format that best fits for the current device - * - * @throw Throws an exception if no depth format fits the requirements - */ - VkFormat getSupportedDepthFormat(bool checkSamplingSupport) - { - // All depth formats may be optional, so we need to find a suitable depth format to use - 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 }; - for (auto& format : depthFormats) - { - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); - // Format must support depth stencil attachment for optimal tiling - if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) - { - if (checkSamplingSupport) { - if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { - continue; - } - } - return format; - } - } - throw std::runtime_error("Could not find a matching depth format"); - } - - }; -} diff --git a/base/VulkanFrameBuffer.hpp b/base/VulkanFrameBuffer.hpp index 5e5b9a5c..617a2aab 100644 --- a/base/VulkanFrameBuffer.hpp +++ b/base/VulkanFrameBuffer.hpp @@ -12,7 +12,7 @@ #include #include #include "vulkan/vulkan.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include "VulkanTools.h" namespace vks diff --git a/base/VulkanHeightmap.hpp b/base/VulkanHeightmap.hpp index 37d11c64..08f66e3b 100644 --- a/base/VulkanHeightmap.hpp +++ b/base/VulkanHeightmap.hpp @@ -10,7 +10,7 @@ #include #include "vulkan/vulkan.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include "VulkanBuffer.h" #include #include diff --git a/base/VulkanTexture.hpp b/base/VulkanTexture.hpp index 2c37a881..54e68e37 100644 --- a/base/VulkanTexture.hpp +++ b/base/VulkanTexture.hpp @@ -19,7 +19,7 @@ #include #include "VulkanTools.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include "VulkanBuffer.h" #if defined(__ANDROID__) diff --git a/base/VulkanUIOverlay.h b/base/VulkanUIOverlay.h index 754d4c89..afb46ed6 100644 --- a/base/VulkanUIOverlay.h +++ b/base/VulkanUIOverlay.h @@ -20,7 +20,7 @@ #include "VulkanTools.h" #include "VulkanDebug.h" #include "VulkanBuffer.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include "../external/imgui/imgui.h" diff --git a/base/VulkanglTFModel.h b/base/VulkanglTFModel.h index 77b7e65b..0f3f3713 100644 --- a/base/VulkanglTFModel.h +++ b/base/VulkanglTFModel.h @@ -14,7 +14,7 @@ #include #include "vulkan/vulkan.h" -#include "VulkanDevice.hpp" +#include "VulkanDevice.h" #include #include diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 0e2a5d67..a391b9d6 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -49,9 +49,9 @@ #include "VulkanUIOverlay.h" #include "VulkanSwapChain.h" #include "VulkanBuffer.h" +#include "VulkanDevice.h" #include "VulkanInitializers.hpp" -#include "VulkanDevice.hpp" #include "camera.hpp" #include "benchmark.hpp" diff --git a/examples/dynamicuniformbuffer/dynamicuniformbuffer.cpp b/examples/dynamicuniformbuffer/dynamicuniformbuffer.cpp index f2e12161..1a2a80c4 100644 --- a/examples/dynamicuniformbuffer/dynamicuniformbuffer.cpp +++ b/examples/dynamicuniformbuffer/dynamicuniformbuffer.cpp @@ -31,7 +31,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/gears/vulkangear.h b/examples/gears/vulkangear.h index b34f78b9..cedad895 100644 --- a/examples/gears/vulkangear.h +++ b/examples/gears/vulkangear.h @@ -21,7 +21,8 @@ #include "vulkan/vulkan.h" #include "VulkanTools.h" -#include "VulkanDevice.hpp" +#include "VulkanBuffer.h" +#include "VulkanDevice.h" struct Vertex { diff --git a/examples/imgui/main.cpp b/examples/imgui/main.cpp index ab53e1b5..1d290419 100644 --- a/examples/imgui/main.cpp +++ b/examples/imgui/main.cpp @@ -23,7 +23,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp b/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp index 4b3ea16c..6a4e89f0 100644 --- a/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp +++ b/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" // Ray tracing acceleration structure struct AccelerationStructure { diff --git a/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp b/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp index 414a74f0..2d8acd57 100644 --- a/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp +++ b/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" // Ray tracing acceleration structure diff --git a/examples/nv_ray_tracing_shadows/nv_ray_tracing_shadows.cpp b/examples/nv_ray_tracing_shadows/nv_ray_tracing_shadows.cpp index 26c811a8..1a189de2 100644 --- a/examples/nv_ray_tracing_shadows/nv_ray_tracing_shadows.cpp +++ b/examples/nv_ray_tracing_shadows/nv_ray_tracing_shadows.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" // Ray tracing acceleration structure diff --git a/examples/textoverlay/textoverlay.cpp b/examples/textoverlay/textoverlay.cpp index ae471798..86a41b69 100644 --- a/examples/textoverlay/textoverlay.cpp +++ b/examples/textoverlay/textoverlay.cpp @@ -23,7 +23,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" #include "../external/stb/stb_font_consolas_24_latin1.inl" diff --git a/examples/texture/texture.cpp b/examples/texture/texture.cpp index 613f4203..5b461e57 100644 --- a/examples/texture/texture.cpp +++ b/examples/texture/texture.cpp @@ -18,7 +18,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include #include diff --git a/examples/texture3d/texture3d.cpp b/examples/texture3d/texture3d.cpp index 5f0a480b..eb0e908b 100644 --- a/examples/texture3d/texture3d.cpp +++ b/examples/texture3d/texture3d.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/texturemipmapgen/texturemipmapgen.cpp b/examples/texturemipmapgen/texturemipmapgen.cpp index 781e6fab..b2d0d527 100644 --- a/examples/texturemipmapgen/texturemipmapgen.cpp +++ b/examples/texturemipmapgen/texturemipmapgen.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" #include #include diff --git a/examples/texturesparseresidency/texturesparseresidency.h b/examples/texturesparseresidency/texturesparseresidency.h index a333fef4..78cb46c3 100644 --- a/examples/texturesparseresidency/texturesparseresidency.h +++ b/examples/texturesparseresidency/texturesparseresidency.h @@ -26,7 +26,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanDevice.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false