Split device class into header and implementation
Moved include to base class
This commit is contained in:
parent
122288fe25
commit
231e97b8c0
20 changed files with 641 additions and 621 deletions
562
base/VulkanDevice.cpp
Normal file
562
base/VulkanDevice.cpp
Normal file
|
|
@ -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 <VulkanDevice.h>
|
||||||
|
|
||||||
|
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<VkExtensionProperties> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<const char*> 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<VkDeviceQueueCreateInfo> 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<const char*> 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<uint32_t>(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<VkFormat> 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
71
base/VulkanDevice.h
Normal file
71
base/VulkanDevice.h
Normal file
|
|
@ -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 <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
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<VkQueueFamilyProperties> queueFamilyProperties;
|
||||||
|
/** @brief List of extensions supported by the device */
|
||||||
|
std::vector<std::string> 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<const char *> 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
|
||||||
|
|
@ -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 <exception>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#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<VkQueueFamilyProperties> queueFamilyProperties;
|
|
||||||
/** @brief List of extensions supported by the device */
|
|
||||||
std::vector<std::string> 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<VkExtensionProperties> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<const char*> 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<VkDeviceQueueCreateInfo> 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<const char*> 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<uint32_t>(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<VkFormat> 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.h"
|
||||||
#include "VulkanTools.h"
|
#include "VulkanTools.h"
|
||||||
|
|
||||||
namespace vks
|
namespace vks
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.h"
|
||||||
#include "VulkanBuffer.h"
|
#include "VulkanBuffer.h"
|
||||||
#include <ktx.h>
|
#include <ktx.h>
|
||||||
#include <ktxvulkan.h>
|
#include <ktxvulkan.h>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
#include <ktxvulkan.h>
|
#include <ktxvulkan.h>
|
||||||
|
|
||||||
#include "VulkanTools.h"
|
#include "VulkanTools.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.h"
|
||||||
#include "VulkanBuffer.h"
|
#include "VulkanBuffer.h"
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include "VulkanTools.h"
|
#include "VulkanTools.h"
|
||||||
#include "VulkanDebug.h"
|
#include "VulkanDebug.h"
|
||||||
#include "VulkanBuffer.h"
|
#include "VulkanBuffer.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.h"
|
||||||
|
|
||||||
#include "../external/imgui/imgui.h"
|
#include "../external/imgui/imgui.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.h"
|
||||||
|
|
||||||
#include <ktx.h>
|
#include <ktx.h>
|
||||||
#include <ktxvulkan.h>
|
#include <ktxvulkan.h>
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,9 @@
|
||||||
#include "VulkanUIOverlay.h"
|
#include "VulkanUIOverlay.h"
|
||||||
#include "VulkanSwapChain.h"
|
#include "VulkanSwapChain.h"
|
||||||
#include "VulkanBuffer.h"
|
#include "VulkanBuffer.h"
|
||||||
|
#include "VulkanDevice.h"
|
||||||
|
|
||||||
#include "VulkanInitializers.hpp"
|
#include "VulkanInitializers.hpp"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
#include "benchmark.hpp"
|
#include "benchmark.hpp"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
|
|
||||||
#define VERTEX_BUFFER_BIND_ID 0
|
#define VERTEX_BUFFER_BIND_ID 0
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@
|
||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
#include "VulkanTools.h"
|
#include "VulkanTools.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanBuffer.h"
|
||||||
|
#include "VulkanDevice.h"
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
|
|
||||||
// Ray tracing acceleration structure
|
// Ray tracing acceleration structure
|
||||||
struct AccelerationStructure {
|
struct AccelerationStructure {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
// Ray tracing acceleration structure
|
// Ray tracing acceleration structure
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
// Ray tracing acceleration structure
|
// Ray tracing acceleration structure
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
#include "../external/stb/stb_font_consolas_24_latin1.inl"
|
#include "../external/stb/stb_font_consolas_24_latin1.inl"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include <ktx.h>
|
#include <ktx.h>
|
||||||
#include <ktxvulkan.h>
|
#include <ktxvulkan.h>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
|
|
||||||
#define VERTEX_BUFFER_BIND_ID 0
|
#define VERTEX_BUFFER_BIND_ID 0
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
#include <ktx.h>
|
#include <ktx.h>
|
||||||
#include <ktxvulkan.h>
|
#include <ktxvulkan.h>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "VulkanDevice.hpp"
|
|
||||||
#include "VulkanglTFModel.h"
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue