tests: Add a unit test for a simple memcpy compute shader

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>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6850639..45e0f33 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2353,6 +2353,7 @@
 
 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 @@
         ${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 @@
         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
index 0000000..1fcc8b8
--- /dev/null
+++ b/tests/VulkanUnitTests/Device.cpp
@@ -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
index 0000000..a235f61
--- /dev/null
+++ b/tests/VulkanUnitTests/Device.hpp
@@ -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;
+};
diff --git a/tests/VulkanUnitTests/VulkanUnitTests.vcxproj b/tests/VulkanUnitTests/VulkanUnitTests.vcxproj
index ebef195..460c99a 100644
--- a/tests/VulkanUnitTests/VulkanUnitTests.vcxproj
+++ b/tests/VulkanUnitTests/VulkanUnitTests.vcxproj
@@ -98,6 +98,7 @@
   </ItemDefinitionGroup>

   <ItemGroup>

     <ClCompile Include="..\..\third_party\googletest\googletest\src\gtest-all.cc" />

+    <ClCompile Include="Device.cpp" />

     <ClCompile Include="Driver.cpp" />

     <ClCompile Include="main.cpp" />

     <ClCompile Include="unittests.cpp" />

@@ -108,6 +109,7 @@
     </ProjectReference>

   </ItemGroup>

   <ItemGroup>

+    <ClInclude Include="Device.hpp" />

     <ClInclude Include="Driver.hpp" />

     <ClInclude Include="VkGlobalFuncs.hpp" />

     <ClInclude Include="VkInstanceFuncs.hpp" />

diff --git a/tests/VulkanUnitTests/unittests.cpp b/tests/VulkanUnitTests/unittests.cpp
index be2d924..78eb489 100644
--- a/tests/VulkanUnitTests/unittests.cpp
+++ b/tests/VulkanUnitTests/unittests.cpp
@@ -16,10 +16,14 @@
 // the dEQP test suite. Also used as a smoke test.

 

 #include "Driver.hpp"

+#include "Device.hpp"

 

 #include "gmock/gmock.h"

 #include "gtest/gtest.h"

 

+#include "spirv-tools/libspirv.hpp"

+

+#include <sstream>

 #include <cstring>

 

 class SwiftShaderVulkanTest : public testing::Test

@@ -92,3 +96,280 @@
 

     EXPECT_EQ(strncmp(physicalDeviceProperties.deviceName, "SwiftShader Device", VK_MAX_PHYSICAL_DEVICE_NAME_SIZE), 0);

 }

+

+std::vector<uint32_t> compileSpirv(const char* assembly)

+{

+    spvtools::SpirvTools core(SPV_ENV_VULKAN_1_0);

+

+    core.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t& p, const char* m) {

+        FAIL() << p.line << ":" << p.column << ": " << m;

+    });

+

+    std::vector<uint32_t> spirv;

+    EXPECT_TRUE(core.Assemble(assembly, &spirv));

+    EXPECT_TRUE(core.Validate(spirv));

+

+    // Warn if the disassembly does not match the source assembly.

+    // We do this as debugging tests in the debugger is often made much harder

+    // if the SSA names (%X) in the debugger do not match the source.

+    std::string disassembled;

+    core.Disassemble(spirv, &disassembled, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);

+    if (disassembled != assembly)

+    {

+        printf("-- WARNING: Disassembly does not match assembly: ---\n\n");

+

+        auto splitLines = [](const std::string& str) -> std::vector<std::string>

+        {

+            std::stringstream ss(str);

+            std::vector<std::string> out;

+            std::string line;

+            while (std::getline(ss, line, '\n')) { out.push_back(line); }

+            return out;

+        };

+

+        auto srcLines = splitLines(std::string(assembly));

+        auto disLines = splitLines(disassembled);

+

+        for (size_t line = 0; line < srcLines.size() && line < disLines.size(); line++)

+        {

+            auto srcLine = (line < srcLines.size()) ? srcLines[line] : "<missing>";

+            auto disLine = (line < disLines.size()) ? disLines[line] : "<missing>";

+            if (srcLine != disLine)

+            {

+                printf("%zu: '%s' != '%s'\n", line, srcLine.c_str(), disLine.c_str());

+            }

+        }

+        printf("\n\n---\n");

+    }

+

+    return spirv;

+}

+

+#define VK_ASSERT(x) ASSERT_EQ(x, VK_SUCCESS)

+

+struct ComputeParams

+{

+    int localSizeX;

+    int localSizeY;

+    int localSizeZ;

+};

+

+class SwiftShaderVulkanComputeTest : public testing::TestWithParam<ComputeParams> {};

+

+INSTANTIATE_TEST_CASE_P(ComputeParams, SwiftShaderVulkanComputeTest, testing::Values(

+    ComputeParams{1, 1, 1},

+    ComputeParams{2, 1, 1},

+    ComputeParams{4, 1, 1},

+    ComputeParams{8, 1, 1},

+    ComputeParams{16, 1, 1},

+    ComputeParams{32, 1, 1}

+));

+

+TEST_P(SwiftShaderVulkanComputeTest, Memcpy)

+{

+    Driver driver;

+    ASSERT_TRUE(driver.loadSwiftShader());

+

+    auto params = GetParam();

+

+    std::stringstream src;

+    src <<

+              "OpCapability Shader\n"

+              "OpMemoryModel Logical GLSL450\n"

+              "OpEntryPoint GLCompute %1 \"main\" %2\n"

+              "OpExecutionMode %1 LocalSize " <<

+                params.localSizeX << " " <<

+                params.localSizeY << " " <<

+                params.localSizeZ << "\n" <<

+              "OpDecorate %3 ArrayStride 4\n"

+              "OpMemberDecorate %4 0 Offset 0\n"

+              "OpDecorate %4 BufferBlock\n"

+              "OpDecorate %5 DescriptorSet 0\n"

+              "OpDecorate %5 Binding 1\n"

+              "OpDecorate %2 BuiltIn GlobalInvocationId\n"

+              "OpDecorate %6 ArrayStride 4\n"

+              "OpMemberDecorate %7 0 Offset 0\n"

+              "OpDecorate %7 BufferBlock\n"

+              "OpDecorate %8 DescriptorSet 0\n"

+              "OpDecorate %8 Binding 0\n"

+         "%9 = OpTypeVoid\n"

+        "%10 = OpTypeFunction %9\n"

+        "%11 = OpTypeInt 32 1\n"

+         "%3 = OpTypeRuntimeArray %11\n"

+         "%4 = OpTypeStruct %3\n"

+        "%12 = OpTypePointer Uniform %4\n"

+         "%5 = OpVariable %12 Uniform\n"

+        "%13 = OpConstant %11 0\n"

+        "%14 = OpTypeInt 32 0\n"

+        "%15 = OpTypeVector %14 3\n"

+        "%16 = OpTypePointer Input %15\n"

+         "%2 = OpVariable %16 Input\n"

+        "%17 = OpConstant %14 0\n"

+        "%18 = OpTypePointer Input %14\n"

+         "%6 = OpTypeRuntimeArray %11\n"

+         "%7 = OpTypeStruct %6\n"

+        "%19 = OpTypePointer Uniform %7\n"

+         "%8 = OpVariable %19 Uniform\n"

+        "%20 = OpTypePointer Uniform %11\n"

+        "%21 = OpConstant %11 1\n"

+         "%1 = OpFunction %9 None %10\n"

+        "%22 = OpLabel\n"

+        "%23 = OpAccessChain %18 %2 %17\n"

+        "%24 = OpLoad %14 %23\n"

+        "%25 = OpAccessChain %20 %8 %13 %24\n"

+        "%26 = OpLoad %11 %25\n"

+        "%27 = OpAccessChain %20 %5 %13 %24\n"

+              "OpStore %27 %26\n"

+              "OpReturn\n"

+              "OpFunctionEnd\n";

+

+    auto code = compileSpirv(src.str().c_str());

+

+    const VkInstanceCreateInfo createInfo = {

+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  // sType

+        nullptr,                                 // pNext

+        0,                                       // flags

+        nullptr,                                 // pApplicationInfo

+        0,                                       // enabledLayerCount

+        nullptr,                                 // ppEnabledLayerNames

+        0,                                       // enabledExtensionCount

+        nullptr,                                 // ppEnabledExtensionNames

+    };

+

+    VkInstance instance = VK_NULL_HANDLE;

+    VK_ASSERT(driver.vkCreateInstance(&createInfo, nullptr, &instance));

+

+    ASSERT_TRUE(driver.resolve(instance));

+

+    Device device;

+    VK_ASSERT(Device::CreateComputeDevice(&driver, instance, &device));

+    ASSERT_TRUE(device.IsValid());

+

+    constexpr int NUM_ELEMENTS = 256;

+

+    struct Buffers

+    {

+        uint32_t magic0;

+        uint32_t in[NUM_ELEMENTS];

+        uint32_t magic1;

+        uint32_t out[NUM_ELEMENTS];

+        uint32_t magic2;

+    };

+

+    constexpr uint32_t magic0 = 0x01234567;

+    constexpr uint32_t magic1 = 0x89abcdef;

+    constexpr uint32_t magic2 = 0xfedcba99;

+

+    VkDeviceMemory memory;

+    VK_ASSERT(device.AllocateMemory(sizeof(Buffers),

+            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,

+            &memory));

+

+    Buffers* buffers;

+    VK_ASSERT(device.MapMemory(memory, 0, sizeof(Buffers), 0, (void**)&buffers));

+

+    memset(buffers, 0, sizeof(Buffers));

+

+    buffers->magic0 = magic0;

+    buffers->magic1 = magic1;

+    buffers->magic2 = magic2;

+

+    for(int i = 0; i < NUM_ELEMENTS; i++)

+    {

+        buffers->in[i] = (uint32_t)i;

+    }

+

+    device.UnmapMemory(memory);

+    buffers = nullptr;

+

+    VkBuffer bufferIn;

+    VK_ASSERT(device.CreateStorageBuffer(memory, sizeof(Buffers::in), offsetof(Buffers, in), &bufferIn));

+

+    VkBuffer bufferOut;

+    VK_ASSERT(device.CreateStorageBuffer(memory, sizeof(Buffers::out), offsetof(Buffers, out), &bufferOut));

+

+    VkShaderModule shaderModule;

+    VK_ASSERT(device.CreateShaderModule(code, &shaderModule));

+

+    std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings =

+    {

+        {

+            0,                                  // binding

+            VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType

+            1,                                  // descriptorCount

+            VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags

+            0,                                  // pImmutableSamplers

+        },

+        {

+            1,                                  // binding

+            VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType

+            1,                                  // descriptorCount

+            VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags

+            0,                                  // pImmutableSamplers

+        }

+    };

+

+    VkDescriptorSetLayout descriptorSetLayout;

+    VK_ASSERT(device.CreateDescriptorSetLayout(descriptorSetLayoutBindings, &descriptorSetLayout));

+

+    VkPipelineLayout pipelineLayout;

+    VK_ASSERT(device.CreatePipelineLayout(descriptorSetLayout, &pipelineLayout));

+

+    VkPipeline pipeline;

+    VK_ASSERT(device.CreateComputePipeline(shaderModule, pipelineLayout, &pipeline));

+

+    VkDescriptorPool descriptorPool;

+    VK_ASSERT(device.CreateStorageBufferDescriptorPool(2, &descriptorPool));

+

+    VkDescriptorSet descriptorSet;

+    VK_ASSERT(device.AllocateDescriptorSet(descriptorPool, descriptorSetLayout, &descriptorSet));

+

+    std::vector<VkDescriptorBufferInfo> descriptorBufferInfos =

+    {

+        {

+            bufferIn,       // buffer

+            0,              // offset

+            VK_WHOLE_SIZE,  // range

+        },

+        {

+            bufferOut,      // buffer

+            0,              // offset

+            VK_WHOLE_SIZE,  // range

+        }

+    };

+    device.UpdateStorageBufferDescriptorSets(descriptorSet, descriptorBufferInfos);

+

+    VkCommandPool commandPool;

+    VK_ASSERT(device.CreateCommandPool(&commandPool));

+

+    VkCommandBuffer commandBuffer;

+    VK_ASSERT(device.AllocateCommandBuffer(commandPool, &commandBuffer));

+

+    VK_ASSERT(device.BeginCommandBuffer(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, commandBuffer));

+

+    driver.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);

+

+    driver.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet,

+                                   0, nullptr);

+

+    driver.vkCmdDispatch(commandBuffer, NUM_ELEMENTS / params.localSizeX, 1, 1);

+

+    VK_ASSERT(driver.vkEndCommandBuffer(commandBuffer));

+

+    VK_ASSERT(device.QueueSubmitAndWait(commandBuffer));

+

+    VK_ASSERT(device.MapMemory(memory, 0, sizeof(Buffers), 0, (void**)&buffers));

+

+    for (int i = 0; i < NUM_ELEMENTS; ++i)

+    {

+        EXPECT_EQ(buffers->in[i], buffers->out[i]) << "Unexpected output at " << i;

+    }

+

+    // Check for writes outside of bounds.

+    EXPECT_EQ(buffers->magic0, magic0);

+    EXPECT_EQ(buffers->magic1, magic1);

+    EXPECT_EQ(buffers->magic2, magic2);

+

+    device.UnmapMemory(memory);

+    buffers = nullptr;

+}