diff --git a/base/vulkandevice.hpp b/base/vulkandevice.hpp new file mode 100644 index 00000000..d8c5a055 --- /dev/null +++ b/base/vulkandevice.hpp @@ -0,0 +1,162 @@ +/* +* Vulkan device class +* +* Encapsulates a physical Vulkan device and it's logical representation +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include "vulkan/vulkan.h" +#include "vulkantools.h" + +namespace vk +{ + struct VulkanDevice + { + /** @brief Physical device representation */ + VkPhysicalDevice physicalDevice; + /** @brief Logical device representation (application's view of the device) */ + VkDevice device; + /** @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 Memory types and heaps of the physical device */ + VkPhysicalDeviceMemoryProperties memoryProperties; + + /** @brief Set to true when the debug marker extension is detected */ + bool enableDebugMarkers = false; + + /** + * Return 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 + * + * @return Index of the requested memory type + * + * @throw Throws an exception if no memory type could be found that supports the requested properties + */ + uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties) + { + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount - 1; i++) + { + if ((typeBits & 1) == 1) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + return i; + } + } + typeBits >>= 1; + } + +#if defined(__ANDROID__) + //todo : Exceptions are disabled by default on Android (need to add LOCAL_CPP_FEATURES += exceptions to Android.mk), so for now just return zero + return 0; +#else + throw "Could not find a memory type for the passed properties"; +#endif + } + + /** + * Create the logical device based on the passed physical device + * + * @param physicalDevice The physical device for which the logical reprenstation is to be created + * @param queueCreateInfos A vector containing queue create infos for all queues to be requested on the device + * @param enabledFeatures Can be used to enable certain features upon device creation + * @param useSwapChain Set to false for headless rendering to omit the swapchain device extensions + * + * @return VkResult of the device creation call + */ + VkResult create(VkPhysicalDevice physicalDevice, std::vector &queueCreateInfos, VkPhysicalDeviceFeatures enabledFeatures, bool useSwapChain = true) + { + 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 buffer + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + + // Create the logical device representation + std::vector deviceExtensions; + 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; + + // Cnable the debug marker extension if it is present (likely meaning a debugging tool is present) + if (vkTools::checkDeviceExtensionPresent(physicalDevice, 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(); + } + + return vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); + } + + /** + * 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 = vkTools::initializers::bufferCreateInfo(usageFlags, size); + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); + vkGetBufferMemoryRequirements(device, *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(device, &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(device, *memory, 0, size, 0, &mapped)); + memcpy(mapped, data, size); + vkUnmapMemory(device, *memory); + } + + // Attach the memory to the buffer object + VK_CHECK_RESULT(vkBindBufferMemory(device, *buffer, *memory, 0)); + + return VK_SUCCESS; + } + }; +} diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index ae41406a..304ef0ab 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -50,33 +50,6 @@ VkResult VulkanExampleBase::createInstance(bool enableValidation) return vkCreateInstance(&instanceCreateInfo, nullptr, &instance); } -VkResult VulkanExampleBase::createDevice(VkDeviceQueueCreateInfo requestedQueues, bool enableValidation) -{ - std::vector enabledExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.pNext = NULL; - deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = &requestedQueues; - deviceCreateInfo.pEnabledFeatures = &enabledFeatures; - - // enable the debug marker extension if it is present (likely meaning a debugging tool is present) - if (vkTools::checkDeviceExtensionPresent(physicalDevice, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) - { - enabledExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - enableDebugMarkers = true; - } - - if (enabledExtensions.size() > 0) - { - deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); - deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); - } - - return vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); -} - std::string VulkanExampleBase::getWindowTitle() { std::string device(deviceProperties.deviceName); @@ -858,21 +831,27 @@ void VulkanExampleBase::initVulkan(bool enableValidation) } assert(graphicsQueueIndex < queueCount); - // Vulkan device - std::array queuePriorities = { 0.0f }; - VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = graphicsQueueIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = queuePriorities.data(); + // Vulkan device creation - VK_CHECK_RESULT(createDevice(queueCreateInfo, enableValidation)); + // We will be requesting queues from one family only + // todo: Multiple queue families for transfer and async compute + std::vector queuePriorities = { 0.0f }; + std::vector queueCreateInfos = {}; + queueCreateInfos.resize(1); + queueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfos[0].queueFamilyIndex = graphicsQueueIndex; + queueCreateInfos[0].queueCount = 1; + queueCreateInfos[0].pQueuePriorities = queuePriorities.data(); + + VK_CHECK_RESULT(vulkanDevice.create(physicalDevice, queueCreateInfos, enabledFeatures)); + + // Assign device to base class context + device = vulkanDevice.device; // Store properties (including limits) and features of the phyiscal device // So examples can check against them and see if a feature is actually supported vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); - // Gather physical device memory properties vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 3938cb8d..541d8784 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -36,6 +36,7 @@ #include "vulkantools.h" #include "vulkandebug.h" +#include "vulkandevice.hpp" #include "vulkanswapchain.hpp" #include "vulkanTextureLoader.hpp" #include "vulkanMeshLoader.hpp" @@ -60,7 +61,7 @@ private: bool enableValidation = false; // Set to true when the debug marker extension is detected bool enableDebugMarkers = false; - // Set tot true if v-sync will be forced for the swapchain + // Set to true if v-sync will be forced for the swapchain bool enableVSync = false; // Device features enabled by the example // If not set, no additional features are enabled (may result in validation layer errors) @@ -69,8 +70,6 @@ private: float fpsTimer = 0.0f; // Create application wide Vulkan instance VkResult createInstance(bool enableValidation); - // Create logical Vulkan device based on physical device - VkResult createDevice(VkDeviceQueueCreateInfo requestedQueues, bool enableValidation); // Get window title with example name, device, et. std::string getWindowTitle(); // Destination dimensions for resizing the window @@ -94,8 +93,11 @@ protected: VkPhysicalDeviceFeatures deviceFeatures; // Stores all available memory (type) properties for the physical device VkPhysicalDeviceMemoryProperties deviceMemoryProperties; - // Logical device, application's view of the physical device (GPU) + /** @brief Logical device, application's view of the physical device (GPU) */ + // todo: getter? should always point to VulkanDevice->device VkDevice device; + /** @brief Encapsulated physical and logical vulkan device */ + vk::VulkanDevice vulkanDevice; // Handle to the device graphics queue that command buffers are submitted to VkQueue queue; // Color buffer format