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