| // 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); |
| } |