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
${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/
)
COMPILE_DEFINITIONS "STANDALONE"
)
- target_link_libraries(vk-unittests ${OS_LIBS})
+ target_link_libraries(vk-unittests ${OS_LIBS} SPIRV-Tools)
endif()
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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;
+};
</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
// 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
\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