OSDN Git Service

tests: Add a unit test for a simple memcpy compute shader
authorBen Clayton <bclayton@google.com>
Fri, 1 Mar 2019 14:33:35 +0000 (14:33 +0000)
committerBen Clayton <bclayton@google.com>
Tue, 12 Mar 2019 10:04:08 +0000 (10:04 +0000)
Bug: b/126871859
Change-Id: I0b3db6c033419a2ad54453d470960330d4f337cc
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/25909
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
CMakeLists.txt
tests/VulkanUnitTests/Device.cpp [new file with mode: 0644]
tests/VulkanUnitTests/Device.hpp [new file with mode: 0644]
tests/VulkanUnitTests/VulkanUnitTests.vcxproj
tests/VulkanUnitTests/unittests.cpp

index 6850639..45e0f33 100644 (file)
@@ -2353,6 +2353,7 @@ endif()
 
 if(BUILD_TESTS AND BUILD_VULKAN)
     set(UNITTESTS_LIST
+        ${CMAKE_CURRENT_SOURCE_DIR}/tests/VulkanUnitTests/Device.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/tests/VulkanUnitTests/Driver.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/tests/VulkanUnitTests/main.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/tests/VulkanUnitTests/unittests.cpp
@@ -2363,6 +2364,7 @@ if(BUILD_TESTS AND BUILD_VULKAN)
         ${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/googletest/include/
         ${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/googlemock/include/
         ${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/googletest/
+        ${CMAKE_CURRENT_SOURCE_DIR}/third_party/SPIRV-Tools/include
         ${CMAKE_CURRENT_SOURCE_DIR}/include/
     )
 
@@ -2373,5 +2375,5 @@ if(BUILD_TESTS AND BUILD_VULKAN)
         COMPILE_DEFINITIONS "STANDALONE"
     )
 
-    target_link_libraries(vk-unittests ${OS_LIBS})
+    target_link_libraries(vk-unittests ${OS_LIBS} SPIRV-Tools)
 endif()
diff --git a/tests/VulkanUnitTests/Device.cpp b/tests/VulkanUnitTests/Device.cpp
new file mode 100644 (file)
index 0000000..1fcc8b8
--- /dev/null
@@ -0,0 +1,397 @@
+// 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) {}
+
+bool Device::IsValid() const { return device != nullptr; }
+
+VkResult Device::CreateComputeDevice(
+               Driver const *driver, VkInstance instance, 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, 0, &device);
+        if (result != VK_SUCCESS)
+        {
+            return result;
+        }
+
+               *out = 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;
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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?
+}
+
+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);
+}
+
+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);
+}
+
+VkResult Device::BeginCommandBuffer(
+               VkCommandBufferUsageFlagBits 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);
+}
diff --git a/tests/VulkanUnitTests/Device.hpp b/tests/VulkanUnitTests/Device.hpp
new file mode 100644 (file)
index 0000000..a235f61
--- /dev/null
@@ -0,0 +1,128 @@
+// 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 <vulkan/vulkan_core.h>
+
+#include <vector>
+
+class Driver;
+
+// Device provides a wrapper around a VkDevice with a number of helper functions
+// for common test operations.
+class Device
+{
+public:
+       Device();
+
+       // CreateComputeDevice enumerates the physical devices, looking for a device
+       // that supports compute.
+       // If a compatible physical device is found, then a device is created and
+       // assigned to out.
+       // If a compatible physical device is not found, VK_SUCCESS will still be
+       // returned (as there was no Vulkan error), but calling Device::IsValid()
+       // on this device will return false.
+       static VkResult CreateComputeDevice(
+                       Driver const *driver, VkInstance instance, Device *out);
+
+       // IsValid returns true if the Device is initialized and can be used.
+       bool IsValid() const;
+
+       // CreateBuffer creates a new buffer with the
+       // VK_BUFFER_USAGE_STORAGE_BUFFER_BIT usage, and
+       // VK_SHARING_MODE_EXCLUSIVE sharing mode.
+       VkResult CreateStorageBuffer(VkDeviceMemory memory, VkDeviceSize size,
+                       VkDeviceSize offset, VkBuffer *out) const;
+
+       // CreateShaderModule creates a new shader module with the given SPIR-V
+       // code.
+       VkResult CreateShaderModule(const std::vector<uint32_t> &spirv,
+                       VkShaderModule *out) const;
+
+       // CreateDescriptorSetLayout creates a new descriptor set layout with the
+       // given bindings.
+       VkResult CreateDescriptorSetLayout(
+                       const std::vector<VkDescriptorSetLayoutBinding> &bindings,
+                       VkDescriptorSetLayout *out) const;
+
+       // CreatePipelineLayout creates a new single set descriptor set layout.
+       VkResult CreatePipelineLayout(VkDescriptorSetLayout layout,
+                       VkPipelineLayout *out) const;
+
+       // CreateComputePipeline creates a new compute pipeline with the entry point
+       // "main".
+       VkResult CreateComputePipeline(VkShaderModule module,
+                       VkPipelineLayout pipelineLayout,
+                       VkPipeline *out) const;
+
+       // CreateStorageBufferDescriptorPool creates a new descriptor pool that can
+       // hold descriptorCount storage buffers.
+       VkResult CreateStorageBufferDescriptorPool(uint32_t descriptorCount,
+                       VkDescriptorPool *out) const;
+
+       // AllocateDescriptorSet allocates a single descriptor set with the given
+       // layout from pool.
+       VkResult AllocateDescriptorSet(VkDescriptorPool pool,
+                       VkDescriptorSetLayout layout,
+                       VkDescriptorSet *out) const;
+
+       // UpdateStorageBufferDescriptorSets updates the storage buffers in
+       // descriptorSet with the given list of VkDescriptorBufferInfos.
+       void UpdateStorageBufferDescriptorSets(VkDescriptorSet descriptorSet,
+               const std::vector<VkDescriptorBufferInfo> &bufferInfos) const;
+
+       // AllocateMemory allocates size bytes from a memory heap that has all the
+       // given flag bits set.
+       // If memory could not be allocated from any heap then
+       // VK_ERROR_OUT_OF_DEVICE_MEMORY is returned.
+       VkResult AllocateMemory(size_t size, VkMemoryPropertyFlags flags, VkDeviceMemory* out) const;
+
+       // MapMemory wraps vkMapMemory, supplying the first VkDevice parameter.
+       VkResult MapMemory(VkDeviceMemory memory, VkDeviceSize offset,
+                       VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) const;
+
+       // UnmapMemory wraps vkUnmapMemory, supplying the first VkDevice parameter.
+       void UnmapMemory(VkDeviceMemory memory) const;
+
+       // CreateCommandPool creates a new command pool.
+       VkResult CreateCommandPool(VkCommandPool* out) const;
+
+       // AllocateCommandBuffer creates a new command buffer with a primary level.
+       VkResult AllocateCommandBuffer(VkCommandPool pool, VkCommandBuffer* out) const;
+
+       // BeginCommandBuffer begins writing to commandBuffer.
+       VkResult BeginCommandBuffer(VkCommandBufferUsageFlagBits usage, VkCommandBuffer commandBuffer) const;
+
+       // QueueSubmitAndWait submits the given command buffer and waits for it to
+       // complete.
+       VkResult QueueSubmitAndWait(VkCommandBuffer commandBuffer) const;
+
+private:
+       Device(Driver const *driver, VkDevice device, VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
+
+       static VkResult GetPhysicalDevices(
+                       Driver const *driver, VkInstance instance,
+                       std::vector<VkPhysicalDevice> &out);
+
+       static int GetComputeQueueFamilyIndex(
+                       Driver const *driver, VkPhysicalDevice device);
+
+       static std::vector<VkQueueFamilyProperties>
+               GetPhysicalDeviceQueueFamilyProperties(
+                       Driver const *driver, VkPhysicalDevice device);
+
+       Driver const *driver;
+       VkDevice device;
+       VkPhysicalDevice physicalDevice;
+       uint32_t queueFamilyIndex;
+};
index ebef195..460c99a 100644 (file)
@@ -98,6 +98,7 @@
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
     <ClCompile Include="..\..\third_party\googletest\googletest\src\gtest-all.cc" />\r
+    <ClCompile Include="Device.cpp" />\r
     <ClCompile Include="Driver.cpp" />\r
     <ClCompile Include="main.cpp" />\r
     <ClCompile Include="unittests.cpp" />\r
     </ProjectReference>\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClInclude Include="Device.hpp" />\r
     <ClInclude Include="Driver.hpp" />\r
     <ClInclude Include="VkGlobalFuncs.hpp" />\r
     <ClInclude Include="VkInstanceFuncs.hpp" />\r
index be2d924..78eb489 100644 (file)
 // the dEQP test suite. Also used as a smoke test.\r
 \r
 #include "Driver.hpp"\r
+#include "Device.hpp"\r
 \r
 #include "gmock/gmock.h"\r
 #include "gtest/gtest.h"\r
 \r
+#include "spirv-tools/libspirv.hpp"\r
+\r
+#include <sstream>\r
 #include <cstring>\r
 \r
 class SwiftShaderVulkanTest : public testing::Test\r
@@ -92,3 +96,280 @@ TEST_F(SwiftShaderVulkanTest, Version)
 \r
     EXPECT_EQ(strncmp(physicalDeviceProperties.deviceName, "SwiftShader Device", VK_MAX_PHYSICAL_DEVICE_NAME_SIZE), 0);\r
 }\r
+\r
+std::vector<uint32_t> compileSpirv(const char* assembly)\r
+{\r
+    spvtools::SpirvTools core(SPV_ENV_VULKAN_1_0);\r
+\r
+    core.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t& p, const char* m) {\r
+        FAIL() << p.line << ":" << p.column << ": " << m;\r
+    });\r
+\r
+    std::vector<uint32_t> spirv;\r
+    EXPECT_TRUE(core.Assemble(assembly, &spirv));\r
+    EXPECT_TRUE(core.Validate(spirv));\r
+\r
+    // Warn if the disassembly does not match the source assembly.\r
+    // We do this as debugging tests in the debugger is often made much harder\r
+    // if the SSA names (%X) in the debugger do not match the source.\r
+    std::string disassembled;\r
+    core.Disassemble(spirv, &disassembled, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);\r
+    if (disassembled != assembly)\r
+    {\r
+        printf("-- WARNING: Disassembly does not match assembly: ---\n\n");\r
+\r
+        auto splitLines = [](const std::string& str) -> std::vector<std::string>\r
+        {\r
+            std::stringstream ss(str);\r
+            std::vector<std::string> out;\r
+            std::string line;\r
+            while (std::getline(ss, line, '\n')) { out.push_back(line); }\r
+            return out;\r
+        };\r
+\r
+        auto srcLines = splitLines(std::string(assembly));\r
+        auto disLines = splitLines(disassembled);\r
+\r
+        for (size_t line = 0; line < srcLines.size() && line < disLines.size(); line++)\r
+        {\r
+            auto srcLine = (line < srcLines.size()) ? srcLines[line] : "<missing>";\r
+            auto disLine = (line < disLines.size()) ? disLines[line] : "<missing>";\r
+            if (srcLine != disLine)\r
+            {\r
+                printf("%zu: '%s' != '%s'\n", line, srcLine.c_str(), disLine.c_str());\r
+            }\r
+        }\r
+        printf("\n\n---\n");\r
+    }\r
+\r
+    return spirv;\r
+}\r
+\r
+#define VK_ASSERT(x) ASSERT_EQ(x, VK_SUCCESS)\r
+\r
+struct ComputeParams\r
+{\r
+    int localSizeX;\r
+    int localSizeY;\r
+    int localSizeZ;\r
+};\r
+\r
+class SwiftShaderVulkanComputeTest : public testing::TestWithParam<ComputeParams> {};\r
+\r
+INSTANTIATE_TEST_CASE_P(ComputeParams, SwiftShaderVulkanComputeTest, testing::Values(\r
+    ComputeParams{1, 1, 1},\r
+    ComputeParams{2, 1, 1},\r
+    ComputeParams{4, 1, 1},\r
+    ComputeParams{8, 1, 1},\r
+    ComputeParams{16, 1, 1},\r
+    ComputeParams{32, 1, 1}\r
+));\r
+\r
+TEST_P(SwiftShaderVulkanComputeTest, Memcpy)\r
+{\r
+    Driver driver;\r
+    ASSERT_TRUE(driver.loadSwiftShader());\r
+\r
+    auto params = GetParam();\r
+\r
+    std::stringstream src;\r
+    src <<\r
+              "OpCapability Shader\n"\r
+              "OpMemoryModel Logical GLSL450\n"\r
+              "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+              "OpExecutionMode %1 LocalSize " <<\r
+                params.localSizeX << " " <<\r
+                params.localSizeY << " " <<\r
+                params.localSizeZ << "\n" <<\r
+              "OpDecorate %3 ArrayStride 4\n"\r
+              "OpMemberDecorate %4 0 Offset 0\n"\r
+              "OpDecorate %4 BufferBlock\n"\r
+              "OpDecorate %5 DescriptorSet 0\n"\r
+              "OpDecorate %5 Binding 1\n"\r
+              "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+              "OpDecorate %6 ArrayStride 4\n"\r
+              "OpMemberDecorate %7 0 Offset 0\n"\r
+              "OpDecorate %7 BufferBlock\n"\r
+              "OpDecorate %8 DescriptorSet 0\n"\r
+              "OpDecorate %8 Binding 0\n"\r
+         "%9 = OpTypeVoid\n"\r
+        "%10 = OpTypeFunction %9\n"\r
+        "%11 = OpTypeInt 32 1\n"\r
+         "%3 = OpTypeRuntimeArray %11\n"\r
+         "%4 = OpTypeStruct %3\n"\r
+        "%12 = OpTypePointer Uniform %4\n"\r
+         "%5 = OpVariable %12 Uniform\n"\r
+        "%13 = OpConstant %11 0\n"\r
+        "%14 = OpTypeInt 32 0\n"\r
+        "%15 = OpTypeVector %14 3\n"\r
+        "%16 = OpTypePointer Input %15\n"\r
+         "%2 = OpVariable %16 Input\n"\r
+        "%17 = OpConstant %14 0\n"\r
+        "%18 = OpTypePointer Input %14\n"\r
+         "%6 = OpTypeRuntimeArray %11\n"\r
+         "%7 = OpTypeStruct %6\n"\r
+        "%19 = OpTypePointer Uniform %7\n"\r
+         "%8 = OpVariable %19 Uniform\n"\r
+        "%20 = OpTypePointer Uniform %11\n"\r
+        "%21 = OpConstant %11 1\n"\r
+         "%1 = OpFunction %9 None %10\n"\r
+        "%22 = OpLabel\n"\r
+        "%23 = OpAccessChain %18 %2 %17\n"\r
+        "%24 = OpLoad %14 %23\n"\r
+        "%25 = OpAccessChain %20 %8 %13 %24\n"\r
+        "%26 = OpLoad %11 %25\n"\r
+        "%27 = OpAccessChain %20 %5 %13 %24\n"\r
+              "OpStore %27 %26\n"\r
+              "OpReturn\n"\r
+              "OpFunctionEnd\n";\r
+\r
+    auto code = compileSpirv(src.str().c_str());\r
+\r
+    const VkInstanceCreateInfo createInfo = {\r
+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  // sType\r
+        nullptr,                                 // pNext\r
+        0,                                       // flags\r
+        nullptr,                                 // pApplicationInfo\r
+        0,                                       // enabledLayerCount\r
+        nullptr,                                 // ppEnabledLayerNames\r
+        0,                                       // enabledExtensionCount\r
+        nullptr,                                 // ppEnabledExtensionNames\r
+    };\r
+\r
+    VkInstance instance = VK_NULL_HANDLE;\r
+    VK_ASSERT(driver.vkCreateInstance(&createInfo, nullptr, &instance));\r
+\r
+    ASSERT_TRUE(driver.resolve(instance));\r
+\r
+    Device device;\r
+    VK_ASSERT(Device::CreateComputeDevice(&driver, instance, &device));\r
+    ASSERT_TRUE(device.IsValid());\r
+\r
+    constexpr int NUM_ELEMENTS = 256;\r
+\r
+    struct Buffers\r
+    {\r
+        uint32_t magic0;\r
+        uint32_t in[NUM_ELEMENTS];\r
+        uint32_t magic1;\r
+        uint32_t out[NUM_ELEMENTS];\r
+        uint32_t magic2;\r
+    };\r
+\r
+    constexpr uint32_t magic0 = 0x01234567;\r
+    constexpr uint32_t magic1 = 0x89abcdef;\r
+    constexpr uint32_t magic2 = 0xfedcba99;\r
+\r
+    VkDeviceMemory memory;\r
+    VK_ASSERT(device.AllocateMemory(sizeof(Buffers),\r
+            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\r
+            &memory));\r
+\r
+    Buffers* buffers;\r
+    VK_ASSERT(device.MapMemory(memory, 0, sizeof(Buffers), 0, (void**)&buffers));\r
+\r
+    memset(buffers, 0, sizeof(Buffers));\r
+\r
+    buffers->magic0 = magic0;\r
+    buffers->magic1 = magic1;\r
+    buffers->magic2 = magic2;\r
+\r
+    for(int i = 0; i < NUM_ELEMENTS; i++)\r
+    {\r
+        buffers->in[i] = (uint32_t)i;\r
+    }\r
+\r
+    device.UnmapMemory(memory);\r
+    buffers = nullptr;\r
+\r
+    VkBuffer bufferIn;\r
+    VK_ASSERT(device.CreateStorageBuffer(memory, sizeof(Buffers::in), offsetof(Buffers, in), &bufferIn));\r
+\r
+    VkBuffer bufferOut;\r
+    VK_ASSERT(device.CreateStorageBuffer(memory, sizeof(Buffers::out), offsetof(Buffers, out), &bufferOut));\r
+\r
+    VkShaderModule shaderModule;\r
+    VK_ASSERT(device.CreateShaderModule(code, &shaderModule));\r
+\r
+    std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings =\r
+    {\r
+        {\r
+            0,                                  // binding\r
+            VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType\r
+            1,                                  // descriptorCount\r
+            VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags\r
+            0,                                  // pImmutableSamplers\r
+        },\r
+        {\r
+            1,                                  // binding\r
+            VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType\r
+            1,                                  // descriptorCount\r
+            VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags\r
+            0,                                  // pImmutableSamplers\r
+        }\r
+    };\r
+\r
+    VkDescriptorSetLayout descriptorSetLayout;\r
+    VK_ASSERT(device.CreateDescriptorSetLayout(descriptorSetLayoutBindings, &descriptorSetLayout));\r
+\r
+    VkPipelineLayout pipelineLayout;\r
+    VK_ASSERT(device.CreatePipelineLayout(descriptorSetLayout, &pipelineLayout));\r
+\r
+    VkPipeline pipeline;\r
+    VK_ASSERT(device.CreateComputePipeline(shaderModule, pipelineLayout, &pipeline));\r
+\r
+    VkDescriptorPool descriptorPool;\r
+    VK_ASSERT(device.CreateStorageBufferDescriptorPool(2, &descriptorPool));\r
+\r
+    VkDescriptorSet descriptorSet;\r
+    VK_ASSERT(device.AllocateDescriptorSet(descriptorPool, descriptorSetLayout, &descriptorSet));\r
+\r
+    std::vector<VkDescriptorBufferInfo> descriptorBufferInfos =\r
+    {\r
+        {\r
+            bufferIn,       // buffer\r
+            0,              // offset\r
+            VK_WHOLE_SIZE,  // range\r
+        },\r
+        {\r
+            bufferOut,      // buffer\r
+            0,              // offset\r
+            VK_WHOLE_SIZE,  // range\r
+        }\r
+    };\r
+    device.UpdateStorageBufferDescriptorSets(descriptorSet, descriptorBufferInfos);\r
+\r
+    VkCommandPool commandPool;\r
+    VK_ASSERT(device.CreateCommandPool(&commandPool));\r
+\r
+    VkCommandBuffer commandBuffer;\r
+    VK_ASSERT(device.AllocateCommandBuffer(commandPool, &commandBuffer));\r
+\r
+    VK_ASSERT(device.BeginCommandBuffer(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, commandBuffer));\r
+\r
+    driver.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);\r
+\r
+    driver.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet,\r
+                                   0, nullptr);\r
+\r
+    driver.vkCmdDispatch(commandBuffer, NUM_ELEMENTS / params.localSizeX, 1, 1);\r
+\r
+    VK_ASSERT(driver.vkEndCommandBuffer(commandBuffer));\r
+\r
+    VK_ASSERT(device.QueueSubmitAndWait(commandBuffer));\r
+\r
+    VK_ASSERT(device.MapMemory(memory, 0, sizeof(Buffers), 0, (void**)&buffers));\r
+\r
+    for (int i = 0; i < NUM_ELEMENTS; ++i)\r
+    {\r
+        EXPECT_EQ(buffers->in[i], buffers->out[i]) << "Unexpected output at " << i;\r
+    }\r
+\r
+    // Check for writes outside of bounds.\r
+    EXPECT_EQ(buffers->magic0, magic0);\r
+    EXPECT_EQ(buffers->magic1, magic1);\r
+    EXPECT_EQ(buffers->magic2, magic2);\r
+\r
+    device.UnmapMemory(memory);\r
+    buffers = nullptr;\r
+}\r