// ============================================= // Aster: device.cpp // Copyright (c) 2020-2024 Anish Bhobe // ============================================= #include "device.h" #include "context.h" #include "physical_device.h" #include "queue_allocation.h" #include #include // TODO: This will need to be flexible for devices that don't support some of the extensions. constexpr eastl::array DEVICE_EXTENSIONS = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, const eastl::vector &queueAllocations, NameString &&name) : Device(context, physicalDevice, enabledFeatures, queueAllocations, {}, std::move(name)) { } Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, const eastl::vector &queueAllocations, eastl::span &&pipelineCacheData, NameString &&name) : m_Name(std::move(name)) , m_PhysicalDevice(physicalDevice->m_PhysicalDevice) , m_ValidationEnabled(context->m_DebugMessenger != nullptr) { // Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap eastl::fixed_vector deviceQueueCreateInfos; deviceQueueCreateInfos.reserve(queueAllocations.size()); u32 numPriorities = 0; for (auto [_, count] : queueAllocations) { numPriorities = std::max(count, numPriorities); } // Shouldn't have more than 4 queues either. eastl::fixed_vector priorities(numPriorities, 1.0f); for (auto [family, count] : queueAllocations) { deviceQueueCreateInfos.push_back({ .queueFamilyIndex = family, .queueCount = count, .pQueuePriorities = priorities.data(), }); } vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures->m_Vulkan10Features; vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features; vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features; vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features; vulkan11Features->pNext = vulkan12Features; vulkan12Features->pNext = vulkan13Features; vk::DeviceCreateInfo deviceCreateInfo = { .pNext = vulkan11Features, .queueCreateInfoCount = Cast(deviceQueueCreateInfos.size()), .pQueueCreateInfos = deviceQueueCreateInfos.data(), .enabledExtensionCount = Cast(DEVICE_EXTENSIONS.size()), .ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(), .pEnabledFeatures = deviceFeatures, }; vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device); ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result) THEN_ABORT(result) ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data()); SetName(m_Device, m_Name.data()); VmaVulkanFunctions vmaVulkanFunctions = { .vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr, .vkGetDeviceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr, }; const VmaAllocatorCreateInfo allocatorCreateInfo = { .physicalDevice = m_PhysicalDevice, .device = m_Device, .pVulkanFunctions = &vmaVulkanFunctions, .instance = context->m_Instance, .vulkanApiVersion = ASTER_API_VERSION, }; result = Cast(vmaCreateAllocator(&allocatorCreateInfo, &m_Allocator)); ERROR_IF(Failed(result), "Memory allocator creation failed. Cause: {}", result) DO(m_Device.destroy(nullptr)) THEN_ABORT(result) ELSE_VERBOSE("Memory Allocator Created"); vk::PipelineCacheCreateInfo pipelineCacheCreateInfo = { .initialDataSize = pipelineCacheData.size_bytes(), .pInitialData = pipelineCacheData.data(), }; result = m_Device.createPipelineCache(&pipelineCacheCreateInfo, nullptr, &m_PipelineCache); ERROR_IF(Failed(result), "Pipeline Cache creation failed. Cause: {}", result) DO(m_Device.destroy(nullptr)) THEN_ABORT(result) ELSE_VERBOSE("Pipeline Cache created."); DEBUG("Created Device '{}' Successfully", m_Name); } Device::~Device() { m_Device.destroy(m_PipelineCache, nullptr); if (m_Allocator) { vmaDestroyAllocator(m_Allocator); m_Allocator = nullptr; DEBUG("Memory Allocator Destroyed"); } m_Device.destroy(nullptr); DEBUG("Device '{}' Destroyed", m_Name); m_PhysicalDevice = nullptr; } vk::Queue Device::GetQueue(const u32 familyIndex, const u32 queueIndex) const { vk::Queue queue; m_Device.getQueue(familyIndex, queueIndex, &queue); return queue; } eastl::vector Device::DumpPipelineCache() const { usize pipelineCacheSize = 0; vk::Result result = m_Device.getPipelineCacheData(m_PipelineCache, &pipelineCacheSize, nullptr); ERROR_IF(Failed(result), "Pipeline Cache data fetch failed. Cause: {}", result); eastl::vector pipelineCacheData(pipelineCacheSize); result = m_Device.getPipelineCacheData(m_PipelineCache, &pipelineCacheSize, pipelineCacheData.data()); ERROR_IF(Failed(result), "Pipeline Cache data fetch failed. Cause: {}", result); return pipelineCacheData; } void Device::WaitIdle() const { auto deviceIdleResult = m_Device.waitIdle(); ERROR_IF(Failed(deviceIdleResult), "Device Idling Failed. Cause: {}", deviceIdleResult) THEN_ABORT(deviceIdleResult); } Device::Device(Device &&other) noexcept : m_Name(std::move(other.m_Name)) , m_PhysicalDevice(Take(other.m_PhysicalDevice)) , m_Device(Take(other.m_Device)) , m_Allocator(Take(other.m_Allocator)) { } Device & Device::operator=(Device &&other) noexcept { if (this == &other) return *this; m_Name = std::move(other.m_Name); m_PhysicalDevice = Take(other.m_PhysicalDevice); m_Device = Take(other.m_Device); m_Allocator = Take(other.m_Allocator); return *this; }