236 lines
8.5 KiB
C++
236 lines
8.5 KiB
C++
//
|
|
// KRDevice.cpp
|
|
// Kraken Engine
|
|
//
|
|
// Copyright 2021 Kearwood Gilbert. All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other materials
|
|
// provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// The views and conclusions contained in the software and documentation are those of the
|
|
// authors and should not be interpreted as representing official policies, either expressed
|
|
// or implied, of Kearwood Gilbert.
|
|
//
|
|
|
|
#include "KRDevice.h"
|
|
#include "KRDeviceManager.h"
|
|
|
|
KRDevice::KRDevice(KRContext& context, const VkPhysicalDevice& device)
|
|
: KRContextObject(context)
|
|
, m_device(device)
|
|
, m_logicalDevice(VK_NULL_HANDLE)
|
|
, m_deviceProperties {}
|
|
, m_deviceFeatures{}
|
|
, m_graphicsFamilyQueueIndex(0)
|
|
, m_graphicsQueue(VK_NULL_HANDLE)
|
|
, m_computeFamilyQueueIndex(0)
|
|
, m_computeQueue(VK_NULL_HANDLE)
|
|
, m_graphicsCommandPool(VK_NULL_HANDLE)
|
|
, m_computeCommandPool(VK_NULL_HANDLE)
|
|
, m_allocator(VK_NULL_HANDLE)
|
|
{
|
|
|
|
}
|
|
|
|
KRDevice::~KRDevice()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
void KRDevice::destroy()
|
|
{
|
|
if (m_logicalDevice != VK_NULL_HANDLE) {
|
|
vkDestroyCommandPool(m_logicalDevice, m_graphicsCommandPool, nullptr);
|
|
m_graphicsCommandPool = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (m_computeCommandPool != VK_NULL_HANDLE) {
|
|
vkDestroyCommandPool(m_logicalDevice, m_computeCommandPool, nullptr);
|
|
m_computeCommandPool = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (m_logicalDevice != VK_NULL_HANDLE) {
|
|
vkDestroyDevice(m_logicalDevice, nullptr);
|
|
m_logicalDevice = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (m_allocator != VK_NULL_HANDLE) {
|
|
vmaDestroyAllocator(m_allocator);
|
|
m_allocator = VK_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
bool KRDevice::initialize(const std::vector<const char*>& deviceExtensions)
|
|
{
|
|
vkGetPhysicalDeviceProperties(m_device, &m_deviceProperties);
|
|
vkGetPhysicalDeviceFeatures(m_device, &m_deviceFeatures);
|
|
|
|
uint32_t queueFamilyCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(m_device, &queueFamilyCount, nullptr);
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(m_device, &queueFamilyCount, queueFamilies.data());
|
|
|
|
uint32_t graphicsFamilyQueue = -1;
|
|
uint32_t computeFamilyQueue = -1;
|
|
uint32_t i = 0;
|
|
for (const auto& queueFamily : queueFamilies) {
|
|
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
graphicsFamilyQueue = i;
|
|
}
|
|
if (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT) {
|
|
computeFamilyQueue = i;
|
|
}
|
|
i++;
|
|
}
|
|
if (graphicsFamilyQueue == -1) {
|
|
// No graphics queue family, not suitable
|
|
return false;
|
|
}
|
|
|
|
if (computeFamilyQueue == -1) {
|
|
// No compute queue family, not suitable
|
|
return false;
|
|
}
|
|
|
|
m_graphicsFamilyQueueIndex = graphicsFamilyQueue;
|
|
m_computeFamilyQueueIndex = computeFamilyQueue;
|
|
|
|
uint32_t extensionCount;
|
|
vkEnumerateDeviceExtensionProperties(m_device, nullptr, &extensionCount, nullptr);
|
|
|
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
|
vkEnumerateDeviceExtensionProperties(m_device, nullptr, &extensionCount, availableExtensions.data());
|
|
|
|
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
|
|
|
|
for (const auto& extension : availableExtensions) {
|
|
requiredExtensions.erase(extension.extensionName);
|
|
}
|
|
if (!requiredExtensions.empty()) {
|
|
// Missing a required extension
|
|
return false;
|
|
}
|
|
|
|
// ----
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfo[2]{};
|
|
float queuePriority = 1.0f;
|
|
queueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo[0].queueFamilyIndex = m_graphicsFamilyQueueIndex;
|
|
queueCreateInfo[0].queueCount = 1;
|
|
queueCreateInfo[0].pQueuePriorities = &queuePriority;
|
|
if (m_graphicsFamilyQueueIndex != m_computeFamilyQueueIndex) {
|
|
queueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo[1].queueFamilyIndex = m_computeFamilyQueueIndex;
|
|
queueCreateInfo[1].queueCount = 1;
|
|
queueCreateInfo[1].pQueuePriorities = &queuePriority;
|
|
}
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo{};
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
deviceCreateInfo.pQueueCreateInfos = queueCreateInfo;
|
|
deviceCreateInfo.queueCreateInfoCount = m_graphicsFamilyQueueIndex == m_computeFamilyQueueIndex ? 1 : 2;
|
|
VkPhysicalDeviceFeatures deviceFeatures{};
|
|
deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
|
|
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
|
|
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
|
if (vkCreateDevice(m_device, &deviceCreateInfo, nullptr, &m_logicalDevice) != VK_SUCCESS) {
|
|
// TODO - Log a warning...
|
|
return false;
|
|
}
|
|
vkGetDeviceQueue(m_logicalDevice, m_graphicsFamilyQueueIndex, 0, &m_graphicsQueue);
|
|
vkGetDeviceQueue(m_logicalDevice, m_computeFamilyQueueIndex, 0, &m_computeQueue);
|
|
|
|
VkCommandPoolCreateInfo poolInfo{};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
poolInfo.queueFamilyIndex = m_graphicsFamilyQueueIndex;
|
|
poolInfo.flags = 0;
|
|
|
|
if (vkCreateCommandPool(m_logicalDevice, &poolInfo, nullptr, &m_graphicsCommandPool) != VK_SUCCESS) {
|
|
destroy();
|
|
// TODO - Log a warning...
|
|
return false;
|
|
}
|
|
|
|
poolInfo.queueFamilyIndex = m_computeFamilyQueueIndex;
|
|
if (vkCreateCommandPool(m_logicalDevice, &poolInfo, nullptr, &m_computeCommandPool) != VK_SUCCESS) {
|
|
destroy();
|
|
// TODO - Log a warning...
|
|
return false;
|
|
}
|
|
|
|
const int kMaxGraphicsCommandBuffers = 10; // TODO - This needs to be dynamic?
|
|
m_graphicsCommandBuffers.resize(kMaxGraphicsCommandBuffers);
|
|
|
|
const int kMaxComputeCommandBuffers = 4; // TODO - This needs to be dynamic?
|
|
m_computeCommandBuffers.resize(kMaxComputeCommandBuffers);
|
|
|
|
VkCommandBufferAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocInfo.commandPool = m_graphicsCommandPool;
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
allocInfo.commandBufferCount = (uint32_t)m_graphicsCommandBuffers.size();
|
|
|
|
if (vkAllocateCommandBuffers(m_logicalDevice, &allocInfo, m_graphicsCommandBuffers.data()) != VK_SUCCESS) {
|
|
destroy();
|
|
// TODO - Log a warning
|
|
return false;
|
|
}
|
|
|
|
allocInfo.commandPool = m_computeCommandPool;
|
|
allocInfo.commandBufferCount = (uint32_t)m_computeCommandBuffers.size();
|
|
if (vkAllocateCommandBuffers(m_logicalDevice, &allocInfo, m_computeCommandBuffers.data()) != VK_SUCCESS) {
|
|
destroy();
|
|
// TODO - Log a warning
|
|
return false;
|
|
}
|
|
|
|
// Create Vulkan Memory Allocator instance for this device
|
|
|
|
// We are dynamically linking Vulkan, so we need to give VMA some hints
|
|
// on finding the function pointers
|
|
VmaVulkanFunctions vmaVulkanFunctions{};
|
|
vmaVulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
|
|
vmaVulkanFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
|
|
|
|
VmaAllocatorCreateInfo vmaCreateInfo{};
|
|
vmaCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
|
|
// TODO - Hook vmaCreateInfo.pAllocationCallbacks;
|
|
|
|
vmaCreateInfo.physicalDevice = m_device;
|
|
vmaCreateInfo.device = m_logicalDevice;
|
|
vmaCreateInfo.instance = m_pContext->getDeviceManager()->getVulkanInstance();
|
|
vmaCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
|
|
vmaCreateInfo.pVulkanFunctions = &vmaVulkanFunctions;
|
|
if (vmaCreateAllocator(&vmaCreateInfo, &m_allocator) != VK_SUCCESS) {
|
|
destroy();
|
|
// TODO - Log a warning
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
VmaAllocator KRDevice::getAllocator()
|
|
{
|
|
assert(m_allocator != VK_NULL_HANDLE);
|
|
return m_allocator;
|
|
} |