| // Copyright 2019 The SwiftShader Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "Device.hpp" | |
| #include "Driver.hpp" | |
| Device::Device() | |
| : driver(nullptr), | |
| device(nullptr), | |
| physicalDevice(nullptr), | |
| queueFamilyIndex(0) {} | |
| Device::Device( | |
| Driver const *driver, VkDevice device, VkPhysicalDevice physicalDevice, | |
| uint32_t queueFamilyIndex) | |
| : driver(driver), | |
| device(device), | |
| physicalDevice(physicalDevice), | |
| queueFamilyIndex(queueFamilyIndex) {} | |
| Device::~Device() | |
| { | |
| if (device != nullptr) | |
| { | |
| driver->vkDeviceWaitIdle(device); | |
| driver->vkDestroyDevice(device, nullptr); | |
| } | |
| } | |
| bool Device::IsValid() const { return device != nullptr; } | |
| VkResult Device::CreateComputeDevice( | |
| Driver const *driver, VkInstance instance, std::unique_ptr<Device> &out) | |
| { | |
| VkResult result; | |
| // Gather all physical devices | |
| std::vector<VkPhysicalDevice> physicalDevices; | |
| result = GetPhysicalDevices(driver, instance, physicalDevices); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| // Inspect each physical device's queue families for compute support. | |
| for (auto physicalDevice : physicalDevices) | |
| { | |
| int queueFamilyIndex = GetComputeQueueFamilyIndex(driver, physicalDevice); | |
| if (queueFamilyIndex < 0) | |
| { | |
| continue; | |
| } | |
| const float queuePrioritory = 1.0f; | |
| const VkDeviceQueueCreateInfo deviceQueueCreateInfo = { | |
| VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| (uint32_t)queueFamilyIndex, // queueFamilyIndex | |
| 1, // queueCount | |
| &queuePrioritory, // pQueuePriorities | |
| }; | |
| const VkDeviceCreateInfo deviceCreateInfo = { | |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| 1, // queueCreateInfoCount | |
| &deviceQueueCreateInfo, // pQueueCreateInfos | |
| 0, // enabledLayerCount | |
| nullptr, // ppEnabledLayerNames | |
| 0, // enabledExtensionCount | |
| nullptr, // ppEnabledExtensionNames | |
| nullptr, // pEnabledFeatures | |
| }; | |
| VkDevice device; | |
| result = driver->vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| out.reset(new Device(driver, device, physicalDevice, static_cast<uint32_t>(queueFamilyIndex))); | |
| return VK_SUCCESS; | |
| } | |
| return VK_SUCCESS; | |
| } | |
| int Device::GetComputeQueueFamilyIndex( | |
| Driver const *driver, VkPhysicalDevice device) | |
| { | |
| auto properties = GetPhysicalDeviceQueueFamilyProperties(driver, device); | |
| for (uint32_t i = 0; i < properties.size(); i++) | |
| { | |
| if ((properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0) | |
| { | |
| return static_cast<int>(i); | |
| } | |
| } | |
| return -1; | |
| } | |
| std::vector<VkQueueFamilyProperties> | |
| Device::GetPhysicalDeviceQueueFamilyProperties( | |
| Driver const *driver, VkPhysicalDevice device) | |
| { | |
| std::vector<VkQueueFamilyProperties> out; | |
| uint32_t count = 0; | |
| driver->vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr); | |
| out.resize(count); | |
| driver->vkGetPhysicalDeviceQueueFamilyProperties(device, &count, out.data()); | |
| return out; | |
| } | |
| VkResult Device::GetPhysicalDevices( | |
| const Driver* driver, VkInstance instance, | |
| std::vector<VkPhysicalDevice>& out) | |
| { | |
| uint32_t count = 0; | |
| VkResult result = driver->vkEnumeratePhysicalDevices(instance, &count, 0); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| out.resize(count); | |
| return driver->vkEnumeratePhysicalDevices(instance, &count, out.data()); | |
| } | |
| VkResult Device::CreateStorageBuffer( | |
| VkDeviceMemory memory, VkDeviceSize size, | |
| VkDeviceSize offset, VkBuffer* out) const | |
| { | |
| const VkBufferCreateInfo info = { | |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| size, // size | |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // usage | |
| VK_SHARING_MODE_EXCLUSIVE, // sharingMode | |
| 0, // queueFamilyIndexCount | |
| nullptr, // pQueueFamilyIndices | |
| }; | |
| VkBuffer buffer; | |
| VkResult result = driver->vkCreateBuffer(device, &info, 0, &buffer); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| result = driver->vkBindBufferMemory(device, buffer, memory, offset); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| *out = buffer; | |
| return VK_SUCCESS; | |
| } | |
| void Device::DestroyBuffer(VkBuffer buffer) const | |
| { | |
| driver->vkDestroyBuffer(device, buffer, nullptr); | |
| } | |
| VkResult Device::CreateShaderModule( | |
| const std::vector<uint32_t>& spirv, VkShaderModule* out) const | |
| { | |
| VkShaderModuleCreateInfo info = { | |
| VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| spirv.size() * 4, // codeSize | |
| spirv.data(), // pCode | |
| }; | |
| return driver->vkCreateShaderModule(device, &info, 0, out); | |
| } | |
| void Device::DestroyShaderModule(VkShaderModule shaderModule) const | |
| { | |
| driver->vkDestroyShaderModule(device, shaderModule, nullptr); | |
| } | |
| VkResult Device::CreateDescriptorSetLayout( | |
| const std::vector<VkDescriptorSetLayoutBinding>& bindings, | |
| VkDescriptorSetLayout* out) const | |
| { | |
| VkDescriptorSetLayoutCreateInfo info = { | |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| (uint32_t)bindings.size(), // bindingCount | |
| bindings.data(), // pBindings | |
| }; | |
| return driver->vkCreateDescriptorSetLayout(device, &info, 0, out); | |
| } | |
| void Device::DestroyDescriptorSetLayout(VkDescriptorSetLayout descriptorSetLayout) const | |
| { | |
| driver->vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); | |
| } | |
| VkResult Device::CreatePipelineLayout( | |
| VkDescriptorSetLayout layout, VkPipelineLayout* out) const | |
| { | |
| VkPipelineLayoutCreateInfo info = { | |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| 1, // setLayoutCount | |
| &layout, // pSetLayouts | |
| 0, // pushConstantRangeCount | |
| nullptr, // pPushConstantRanges | |
| }; | |
| return driver->vkCreatePipelineLayout(device, &info, 0, out); | |
| } | |
| void Device::DestroyPipelineLayout(VkPipelineLayout pipelineLayout) const | |
| { | |
| driver->vkDestroyPipelineLayout(device, pipelineLayout, nullptr); | |
| } | |
| VkResult Device::CreateComputePipeline( | |
| VkShaderModule module, VkPipelineLayout pipelineLayout, | |
| VkPipeline* out) const | |
| { | |
| VkComputePipelineCreateInfo info = { | |
| VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| { | |
| // stage | |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| VK_SHADER_STAGE_COMPUTE_BIT, // stage | |
| module, // module | |
| "main", // pName | |
| nullptr, // pSpecializationInfo | |
| }, | |
| pipelineLayout, // layout | |
| 0, // basePipelineHandle | |
| 0, // basePipelineIndex | |
| }; | |
| return driver->vkCreateComputePipelines(device, 0, 1, &info, 0, out); | |
| } | |
| void Device::DestroyPipeline(VkPipeline pipeline) const | |
| { | |
| driver->vkDestroyPipeline(device, pipeline, nullptr); | |
| } | |
| VkResult Device::CreateStorageBufferDescriptorPool(uint32_t descriptorCount, | |
| VkDescriptorPool* out) const | |
| { | |
| VkDescriptorPoolSize size = { | |
| VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // type | |
| descriptorCount, // descriptorCount | |
| }; | |
| VkDescriptorPoolCreateInfo info = { | |
| VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| 1, // maxSets | |
| 1, // poolSizeCount | |
| &size, // pPoolSizes | |
| }; | |
| return driver->vkCreateDescriptorPool(device, &info, 0, out); | |
| } | |
| void Device::DestroyDescriptorPool(VkDescriptorPool descriptorPool) const | |
| { | |
| driver->vkDestroyDescriptorPool(device, descriptorPool, nullptr); | |
| } | |
| VkResult Device::AllocateDescriptorSet( | |
| VkDescriptorPool pool, VkDescriptorSetLayout layout, | |
| VkDescriptorSet* out) const | |
| { | |
| VkDescriptorSetAllocateInfo info = { | |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // sType | |
| nullptr, // pNext | |
| pool, // descriptorPool | |
| 1, // descriptorSetCount | |
| &layout, // pSetLayouts | |
| }; | |
| return driver->vkAllocateDescriptorSets(device, &info, out); | |
| } | |
| void Device::UpdateStorageBufferDescriptorSets( | |
| VkDescriptorSet descriptorSet, | |
| const std::vector<VkDescriptorBufferInfo>& bufferInfos) const | |
| { | |
| std::vector<VkWriteDescriptorSet> writes; | |
| writes.reserve(bufferInfos.size()); | |
| for (uint32_t i = 0; i < bufferInfos.size(); i++) | |
| { | |
| writes.push_back(VkWriteDescriptorSet{ | |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType | |
| nullptr, // pNext | |
| descriptorSet, // dstSet | |
| i, // dstBinding | |
| 0, // dstArrayElement | |
| 1, // descriptorCount | |
| VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // descriptorType | |
| nullptr, // pImageInfo | |
| &bufferInfos[i], // pBufferInfo | |
| nullptr, // pTexelBufferView | |
| }); | |
| } | |
| driver->vkUpdateDescriptorSets(device, writes.size(), writes.data(), 0, nullptr); | |
| } | |
| VkResult Device::AllocateMemory(size_t size, VkMemoryPropertyFlags flags, VkDeviceMemory* out) const | |
| { | |
| VkPhysicalDeviceMemoryProperties properties; | |
| driver->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &properties); | |
| for(uint32_t type = 0; type < properties.memoryTypeCount; type++) | |
| { | |
| if ((flags & properties.memoryTypes[type].propertyFlags) == 0) | |
| { | |
| continue; // Type mismatch | |
| } | |
| if (size > properties.memoryHeaps[properties.memoryTypes[type].heapIndex].size) | |
| { | |
| continue; // Too small. | |
| } | |
| const VkMemoryAllocateInfo info = { | |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType | |
| nullptr, // pNext | |
| size, // allocationSize | |
| type, // memoryTypeIndex | |
| }; | |
| return driver->vkAllocateMemory(device, &info, 0, out); | |
| } | |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; // TODO: Change to something not made up? | |
| } | |
| void Device::FreeMemory(VkDeviceMemory memory) const | |
| { | |
| driver->vkFreeMemory(device, memory, nullptr); | |
| } | |
| VkResult Device::MapMemory(VkDeviceMemory memory, VkDeviceSize offset, | |
| VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) const | |
| { | |
| return driver->vkMapMemory(device, memory, offset, size, flags, ppData); | |
| } | |
| void Device::UnmapMemory(VkDeviceMemory memory) const | |
| { | |
| driver->vkUnmapMemory(device, memory); | |
| } | |
| VkResult Device::CreateCommandPool(VkCommandPool* out) const | |
| { | |
| VkCommandPoolCreateInfo info = { | |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType | |
| nullptr, // pNext | |
| 0, // flags | |
| queueFamilyIndex, // queueFamilyIndex | |
| }; | |
| return driver->vkCreateCommandPool(device, &info, 0, out); | |
| } | |
| void Device::DestroyCommandPool(VkCommandPool commandPool) const | |
| { | |
| return driver->vkDestroyCommandPool(device, commandPool, nullptr); | |
| } | |
| VkResult Device::AllocateCommandBuffer( | |
| VkCommandPool pool, VkCommandBuffer* out) const | |
| { | |
| VkCommandBufferAllocateInfo info = { | |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType | |
| nullptr, // pNext | |
| pool, // commandPool | |
| VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level | |
| 1, // commandBufferCount | |
| }; | |
| return driver->vkAllocateCommandBuffers(device, &info, out); | |
| } | |
| void Device::FreeCommandBuffer(VkCommandPool pool, VkCommandBuffer buffer) | |
| { | |
| driver->vkFreeCommandBuffers(device, pool, 1, &buffer); | |
| } | |
| VkResult Device::BeginCommandBuffer( | |
| VkCommandBufferUsageFlags usage, VkCommandBuffer commandBuffer) const | |
| { | |
| VkCommandBufferBeginInfo info = { | |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType | |
| nullptr, // pNext | |
| usage, // flags | |
| nullptr, // pInheritanceInfo | |
| }; | |
| return driver->vkBeginCommandBuffer(commandBuffer, &info); | |
| } | |
| VkResult Device::QueueSubmitAndWait(VkCommandBuffer commandBuffer) const | |
| { | |
| VkQueue queue; | |
| driver->vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); | |
| VkSubmitInfo info = { | |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType | |
| nullptr, // pNext | |
| 0, // waitSemaphoreCount | |
| nullptr, // pWaitSemaphores | |
| nullptr, // pWaitDstStageMask | |
| 1, // commandBufferCount | |
| &commandBuffer, // pCommandBuffers | |
| 0, // signalSemaphoreCount | |
| nullptr, // pSignalSemaphores | |
| }; | |
| VkResult result = driver->vkQueueSubmit(queue, 1, &info, 0); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| return driver->vkQueueWaitIdle(queue); | |
| } |