/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2021 The Khronos Group Inc. * Copyright (c) 2016 The Android Open Source Project * * 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. * *//*! * \file * \brief SPIR-V Assembly Tests for PhysicalStorageBuffer. *//*--------------------------------------------------------------------*/ #include "vktSpvAsmPhysicalStorageBufferPointerTests.hpp" #include "vktTestCase.hpp" #include "vktTestCaseUtil.hpp" #include "vkBarrierUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkCmdUtil.hpp" #include "vkImageUtil.hpp" #include "vkMemUtil.hpp" #include "vkObjUtil.hpp" #include "vkQueryUtil.hpp" #include "vkTypeUtil.hpp" #include "deSharedPtr.hpp" #include "deUniquePtr.hpp" #include "tcuTextureUtil.hpp" #include "tcuVectorUtil.hpp" #include using namespace vk; using de::MovePtr; using de::SharedPtr; namespace vkt { namespace SpirVAssembly { namespace { enum class PassMethod { PUSH_CONSTANTS, PUSH_CONSTANTS_FUNCTION, VERTEX_IN_OUT_IN, ADDRESSES_IN_SSBO }; struct TestParams { PassMethod method; deUint32 elements; }; typedef SharedPtr TestParamsPtr; namespace ut { class Buffer { public: Buffer (Context& ctx, VkBufferUsageFlags usage, VkDeviceSize size, bool address = false); Buffer (const Buffer& src); VkBuffer getBuffer (void) const { return **m_buffer; } VkDeviceSize getSize (void) const { return m_size; } void* getData (void) const { return (*m_bufferMemory)->getHostPtr(); } deUint64 getDeviceAddress(void) const; void zero (bool flushAfter = false); void flush (void); void invalidate (void); protected: Context& m_context; const VkDeviceSize m_size; const bool m_address; SharedPtr> m_buffer; SharedPtr> m_bufferMemory; }; template class TypedBuffer : public Buffer { public: TypedBuffer (Context& ctx, VkBufferUsageFlags usage, deUint32 nelements, bool address = false); TypedBuffer (Context& ctx, VkBufferUsageFlags usage, std::initializer_list items, bool address = false); TypedBuffer (const TypedBuffer& src); TypedBuffer (const Buffer& src); deUint32 getElements (void) const { return m_elements; } X* getData (void) const { return reinterpret_cast(Buffer::getData()); } void iota (X start, bool flushAfter = false); X& operator[] (deUint32 at); struct iterator; iterator begin () { return iterator(getData()); } iterator end () { return iterator(&getData()[m_elements]); } private: const deUint32 m_elements; }; class Image { public: Image (Context& ctx, deUint32 width, deUint32 height, VkFormat format); Image (const Image&) = delete; Image (Image&&) = delete; Move createRenderPass (void) const; Move createFramebuffer (VkRenderPass rp) const; template TypedBuffer getBuffer (void); void downloadAfterDraw (VkCommandBuffer cmdBuffer); private: Context& m_context; const deUint32 m_width; const deUint32 m_height; const VkFormat m_format; VkImageLayout m_layout; Buffer m_buffer; Move m_image; Move m_view; de::MovePtr m_imageMemory; }; template SharedPtr> makeShared(Move move) { return SharedPtr>(new Move(move)); } template SharedPtr> makeShared(MovePtr move) { return SharedPtr>(new MovePtr(move)); } Buffer::Buffer (Context& ctx, VkBufferUsageFlags usage, VkDeviceSize size, bool address) : m_context (ctx) , m_size (size) , m_address (address) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Allocator& allocator = m_context.getDefaultAllocator(); const VkBufferUsageFlags bufferUsageFlags = address ? (usage | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) : usage; const MemoryRequirement requirements = MemoryRequirement::Coherent | MemoryRequirement::HostVisible | (address ? MemoryRequirement::DeviceAddress : MemoryRequirement::Any); const VkBufferCreateInfo bufferCreateInfo { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; size, // VkDeviceSize size; bufferUsageFlags, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_buffer = makeShared(createBuffer(vki, dev, &bufferCreateInfo)); m_bufferMemory = makeShared(allocator.allocate(getBufferMemoryRequirements(vki, dev, **m_buffer), requirements)); VK_CHECK(vki.bindBufferMemory(dev, **m_buffer, (*m_bufferMemory)->getMemory(), (*m_bufferMemory)->getOffset())); } Buffer::Buffer (const Buffer& src) : m_context (src.m_context) , m_size (src.m_size) , m_address (src.m_address) , m_buffer (src.m_buffer) , m_bufferMemory(src.m_bufferMemory) { } deUint64 Buffer::getDeviceAddress (void) const { DE_ASSERT(m_address); const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkBufferDeviceAddressInfo info { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; **m_buffer // VkBuffer buffer; }; return vki.getBufferDeviceAddress(dev, &info); } void Buffer::zero (bool flushAfter) { deMemset(getData(), 0, static_cast(m_size)); if (flushAfter) flush(); } void Buffer::flush (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); flushAlloc(vki, dev, **m_bufferMemory); } void Buffer::invalidate (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); invalidateAlloc(vki, dev, **m_bufferMemory); } template struct TypedBuffer::iterator { typedef std::forward_iterator_tag iterator_category; typedef std::ptrdiff_t difference_type; typedef X value_type; typedef X& reference; typedef X* pointer; iterator (pointer p) : m_p(p) { DE_ASSERT(p); } reference operator* () { return *m_p; } iterator& operator++ () { ++m_p; return *this; } iterator operator++ (int) { return iterator(m_p++); } bool operator== (const iterator& other) const { return (m_p == other.m_p); } bool operator!= (const iterator& other) const { return (m_p != other.m_p); } private: pointer m_p; }; template TypedBuffer::TypedBuffer (Context& ctx, VkBufferUsageFlags usage, deUint32 nelements, bool address) : Buffer (ctx, usage, (nelements * sizeof(X)), address) , m_elements (nelements) { } template TypedBuffer::TypedBuffer (Context& ctx, VkBufferUsageFlags usage, std::initializer_list items, bool address) : Buffer (ctx, usage, (items.size() * sizeof(X)), address) , m_elements (static_cast(items.size())) { std::copy(items.begin(), items.end(), begin()); } template TypedBuffer::TypedBuffer (const TypedBuffer& src) : Buffer (src) , m_elements(src.m_elements) { } template TypedBuffer::TypedBuffer (const Buffer& src) : Buffer (src) , m_elements(static_cast(m_size/sizeof(X))) { } template void TypedBuffer::iota (X start, bool flushAfter) { X* data = getData(); for (deUint32 i = 0; i < m_elements; ++i) data[i] = start++; if (flushAfter) flush(); } template X& TypedBuffer::operator[] (deUint32 at) { DE_ASSERT(at < m_elements); return getData()[at]; } Image::Image (Context& ctx, deUint32 width, deUint32 height, VkFormat format) : m_context (ctx) , m_width (width) , m_height (height) , m_format (format) , m_layout (VK_IMAGE_LAYOUT_UNDEFINED) , m_buffer (ctx, (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT), (m_width * m_height * vk::mapVkFormat(m_format).getPixelSize()), false) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; const VkImageSubresourceRange viewResourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); Allocator& allocator = m_context.getDefaultAllocator(); const VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkImageCreateFlags flags; VK_IMAGE_TYPE_2D, // VkImageType imageType; m_format, // VkFormat format; { m_width, m_height, 1u }, // VkExtent3D extent; 1u, // deUint32 mipLevels; 1u, // deUint32 arrayLayers; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; imageUsageFlags, // VkImageUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; m_layout // VkImageLayout initialLayout; }; m_image = createImage(vki, dev, &imageCreateInfo); m_imageMemory = allocator.allocate(getImageMemoryRequirements(vki, dev, *m_image), MemoryRequirement::Any); VK_CHECK(vki.bindImageMemory(dev, *m_image, m_imageMemory->getMemory(), m_imageMemory->getOffset())); m_view = makeImageView(vki, dev, *m_image, VK_IMAGE_VIEW_TYPE_2D, m_format, viewResourceRange); } template TypedBuffer Image::getBuffer (void) { m_buffer.invalidate(); return TypedBuffer(m_buffer); } Move Image::createRenderPass (void) const { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkAttachmentDescription attachmentDescription = { (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags m_format, // VkFormat format VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp m_layout, // VkImageLayout initialLayout VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout }; const VkAttachmentReference attachmentReference = { 0u, // deUint32 attachment VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout }; const VkSubpassDescription subpassDescription = { (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint 0u, // deUint32 inputAttachmentCount DE_NULL, // const VkAttachmentReference* pInputAttachments 1u, // deUint32 colorAttachmentCount &attachmentReference, // const VkAttachmentReference* pColorAttachments DE_NULL, // const VkAttachmentReference* pResolveAttachments DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment 0u, // deUint32 preserveAttachmentCount DE_NULL // const deUint32* pPreserveAttachments }; const VkRenderPassCreateInfo renderPassInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType DE_NULL, // const void* pNext (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags 1u, // deUint32 attachmentCount &attachmentDescription, // const VkAttachmentDescription* pAttachments 1u, // deUint32 subpassCount &subpassDescription, // const VkSubpassDescription* pSubpasses 0u, // deUint32 dependencyCount DE_NULL // const VkSubpassDependency* pDependencies }; return vk::createRenderPass(vki, dev, &renderPassInfo); } Move Image::createFramebuffer (VkRenderPass rp) const { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); return makeFramebuffer(vki, dev, rp, 1u, &m_view.get(), m_width, m_height, 1u); } void Image::downloadAfterDraw (VkCommandBuffer cmdBuffer) { const DeviceInterface& vki = m_context.getDeviceInterface(); vk::copyImageToBuffer(vki, cmdBuffer, *m_image, m_buffer.getBuffer(), { deInt32(m_width), deInt32(m_height) }); m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; } } // ut class SpvAsmPhysicalStorageBufferTestInstance : public TestInstance { public: SpvAsmPhysicalStorageBufferTestInstance (Context& ctx) : TestInstance (ctx) { } }; class SpvAsmPhysicalStorageBufferVertexInOutInTestInstance : public SpvAsmPhysicalStorageBufferTestInstance { public: SpvAsmPhysicalStorageBufferVertexInOutInTestInstance (Context& ctx, const TestParamsPtr params) : SpvAsmPhysicalStorageBufferTestInstance (ctx) , m_params (params) { } tcu::TestStatus iterate (void); static void initPrograms (vk::SourceCollections& programCollection, const TestParamsPtr params); struct alignas(16) Attribute { tcu::Vec4 position; deUint64 address; }; ut::TypedBuffer prepareColorBuffer (bool flushAfter = true) const; ut::TypedBuffer prepareVertexAttributes (deUint64 address) const; Move createGraphicsPipeline (VkPipelineLayout pipelineLayout, VkRenderPass renderPass, VkShaderModule vertexModule, VkShaderModule fragmentModule) const; private: const TestParamsPtr m_params; }; class SpvAsmPhysicalStorageBufferPushConstantsTestInstance : public SpvAsmPhysicalStorageBufferTestInstance { public: SpvAsmPhysicalStorageBufferPushConstantsTestInstance (Context& ctx, const TestParamsPtr params) : SpvAsmPhysicalStorageBufferTestInstance (ctx) , m_params (params) { } tcu::TestStatus iterate (void); static void initPrograms (vk::SourceCollections& programCollection, const TestParamsPtr params); private: const TestParamsPtr m_params; }; class SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance : public SpvAsmPhysicalStorageBufferTestInstance { public: SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance (Context& ctx, const TestParamsPtr params) : SpvAsmPhysicalStorageBufferTestInstance (ctx) , m_params (params) { } tcu::TestStatus iterate (void); static void initPrograms (vk::SourceCollections& programCollection, const TestParamsPtr params); private: const TestParamsPtr m_params; }; class SpvAsmPhysicalStorageBufferTestCase : public TestCase { public: SpvAsmPhysicalStorageBufferTestCase (tcu::TestContext& testCtx, const std::string& name, const TestParamsPtr params) : TestCase (testCtx, name) , m_params (params) { } void checkSupport (Context& context) const; void initPrograms (vk::SourceCollections& programCollection) const; TestInstance* createInstance (Context& ctx) const; private: const TestParamsPtr m_params; }; void SpvAsmPhysicalStorageBufferTestCase::checkSupport (Context& context) const { context.requireInstanceFunctionality("VK_KHR_get_physical_device_properties2"); if (!context.isBufferDeviceAddressSupported()) TCU_THROW(NotSupportedError, "Request physical storage buffer feature not supported"); if (m_params->method == PassMethod::ADDRESSES_IN_SSBO) { if (!context.getDeviceFeatures().shaderInt64) TCU_THROW(NotSupportedError, "Int64 not supported"); } if (m_params->method == PassMethod::VERTEX_IN_OUT_IN) { if (!context.getDeviceFeatures().shaderInt64) TCU_THROW(NotSupportedError, "Int64 not supported"); VkFormatProperties2 properties { VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, DE_NULL, {} }; context.getInstanceInterface().getPhysicalDeviceFormatProperties2(context.getPhysicalDevice(), VK_FORMAT_R64_UINT, &properties); if ((properties.formatProperties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) != VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) TCU_THROW(NotSupportedError, "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT not supported"); } } TestInstance* SpvAsmPhysicalStorageBufferTestCase::createInstance (Context& ctx) const { switch (m_params->method) { case PassMethod::PUSH_CONSTANTS: case PassMethod::PUSH_CONSTANTS_FUNCTION: return new SpvAsmPhysicalStorageBufferPushConstantsTestInstance(ctx, m_params); case PassMethod::VERTEX_IN_OUT_IN: return new SpvAsmPhysicalStorageBufferVertexInOutInTestInstance(ctx, m_params); case PassMethod::ADDRESSES_IN_SSBO: return new SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance(ctx, m_params); } DE_ASSERT(DE_FALSE); return DE_NULL; } void SpvAsmPhysicalStorageBufferTestCase::initPrograms (vk::SourceCollections& programCollection) const { switch (m_params->method) { case PassMethod::PUSH_CONSTANTS: case PassMethod::PUSH_CONSTANTS_FUNCTION: SpvAsmPhysicalStorageBufferPushConstantsTestInstance::initPrograms(programCollection, m_params); break; case PassMethod::VERTEX_IN_OUT_IN: SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::initPrograms(programCollection, m_params); break; case PassMethod::ADDRESSES_IN_SSBO: SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance::initPrograms(programCollection, m_params); break; } } void SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::initPrograms (SourceCollections &programCollection, const TestParamsPtr params) { DE_UNREF(params); const std::string vert(R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Vertex %vert "main" %gl_PerVertex %in_pos %out_idx %gl_VertexIndex %in_addr %out_addr OpDecorate %PerVertex Block OpDecorate %gl_VertexIndex BuiltIn VertexIndex OpDecorate %in_pos Location 0 OpDecorate %in_addr Location 1 OpDecorate %in_addr RestrictPointerEXT OpDecorate %out_addr RestrictPointerEXT OpDecorate %out_idx Location 0 OpDecorate %out_addr Location 1 OpMemberDecorate %PerVertex 0 BuiltIn Position OpMemberDecorate %PerVertex 1 BuiltIn PointSize OpMemberDecorate %PerVertex 2 BuiltIn ClipDistance OpMemberDecorate %PerVertex 3 BuiltIn CullDistance OpDecorate %srta Block OpMemberDecorate %srta 0 Offset 0 OpDecorate %rta ArrayStride 16 %void = OpTypeVoid %voidf = OpTypeFunction %void %int = OpTypeInt 32 1 %flt = OpTypeFloat 32 %vec4 = OpTypeVector %flt 4 %rta = OpTypeRuntimeArray %vec4 %zero = OpConstant %int 0 %one = OpConstant %int 1 %srta = OpTypeStruct %rta %srta_psb = OpTypePointer PhysicalStorageBuffer %srta %srta_psb_in = OpTypePointer Input %srta_psb %srta_psb_out = OpTypePointer Output %srta_psb %in_addr = OpVariable %srta_psb_in Input %out_addr = OpVariable %srta_psb_out Output %vec4_in = OpTypePointer Input %vec4 %vec4_out = OpTypePointer Output %vec4 %vec4_psb = OpTypePointer PhysicalStorageBuffer %vec4 %in_pos = OpVariable %vec4_in Input %int_in = OpTypePointer Input %int %int_out = OpTypePointer Output %int %gl_VertexIndex = OpVariable %int_in Input %out_idx = OpVariable %int_out Output %flt_arr_1 = OpTypeArray %flt %one %PerVertex = OpTypeStruct %vec4 %flt %flt_arr_1 %flt_arr_1 %pv_out = OpTypePointer Output %PerVertex %gl_PerVertex = OpVariable %pv_out Output %vert = OpFunction %void None %voidf %vert_begin = OpLabel %vpos = OpLoad %vec4 %in_pos %gl_Position = OpAccessChain %vec4_out %gl_PerVertex %zero OpStore %gl_Position %vpos %vidx = OpLoad %int %gl_VertexIndex OpStore %out_idx %vidx %vaddr = OpLoad %srta_psb %in_addr Aligned 8 OpStore %out_addr %vaddr OpReturn OpFunctionEnd )"); const std::string frag(R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %frag "main" %in_idx %in_addr %dEQP_FragColor OpExecutionMode %frag OriginUpperLeft OpDecorate %in_idx Location 0 OpDecorate %in_idx Flat OpDecorate %in_addr Location 1 OpDecorate %in_addr AliasedPointerEXT OpDecorate %in_addr Flat OpDecorate %dEQP_FragColor Location 0 OpDecorate %rta ArrayStride 16 OpDecorate %vec4_psb ArrayStride 16 OpDecorate %srta Block OpMemberDecorate %srta 0 Offset 0 %void = OpTypeVoid %voidf = OpTypeFunction %void %int = OpTypeInt 32 1 %flt = OpTypeFloat 32 %vec4 = OpTypeVector %flt 4 %rta = OpTypeRuntimeArray %vec4 %zero = OpConstant %int 0 %int_in = OpTypePointer Input %int %in_idx = OpVariable %int_in Input %vec4_out = OpTypePointer Output %vec4 %dEQP_FragColor = OpVariable %vec4_out Output %srta = OpTypeStruct %rta %srta_psb = OpTypePointer PhysicalStorageBuffer %srta %srta_psb_in = OpTypePointer Input %srta_psb %in_addr = OpVariable %srta_psb_in Input %rta_psb = OpTypePointer PhysicalStorageBuffer %rta %rta_in = OpTypePointer Input %rta %vec4_psb = OpTypePointer PhysicalStorageBuffer %vec4 %frag = OpFunction %void None %voidf %frag_begin = OpLabel %vidx = OpLoad %int %in_idx %vaddr = OpLoad %srta_psb %in_addr %pcolor = OpAccessChain %vec4_psb %vaddr %zero %vidx %color = OpLoad %vec4 %pcolor Aligned 16 OpStore %dEQP_FragColor %color OpReturn OpFunctionEnd )"); programCollection.spirvAsmSources.add("vert") << vert << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); programCollection.spirvAsmSources.add("frag") << frag << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); } ut::TypedBuffer SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::prepareColorBuffer (bool flushAfter) const { const deUint32 colorCount = 21; tcu::Vec4 colors [colorCount]; tcu::Vec4 color (-1.0f, +1.0f, +1.0f, -1.0f); for (deUint32 c = 0; c < colorCount; ++c) { colors[c] = color; color[0] += 0.1f; color[1] -= 0.1f; color[2] -= 0.1f; color[3] += 0.1f; } ut::TypedBuffer buffer(m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, (m_params->elements * m_params->elements), true); for (auto j = buffer.begin(), begin = j; j != buffer.end(); ++j) { *j = colors[std::distance(begin, j) % colorCount]; } if (flushAfter) buffer.flush(); return buffer; } ut::TypedBuffer SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::prepareVertexAttributes (deUint64 address) const { const float xStep = 2.0f / static_cast(m_params->elements); const float yStep = 2.0f / static_cast(m_params->elements); const float xStart = -1.0f + xStep / 2.0f; const float yStart = -1.0f + yStep / 2.0f; float x = xStart; float y = yStart; ut::TypedBuffer attrs (m_context, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, (m_params->elements * m_params->elements)); for (deUint32 row = 0; row < m_params->elements; ++row) { for (deUint32 col = 0; col < m_params->elements; ++col) { Attribute& attr = attrs[(row*m_params->elements)+col]; attr.position = tcu::Vec4(x, y, 0.0f, 1.0f); attr.address = address; x += xStep; } y += yStep; x = xStart; } attrs.flush(); return attrs; } Move SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::createGraphicsPipeline (VkPipelineLayout pipelineLayout, VkRenderPass renderPass, VkShaderModule vertexModule, VkShaderModule fragmentModule) const { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const std::vector scissors (1, makeRect2D(m_params->elements, m_params->elements)); const std::vector viewports (1, makeViewport(m_params->elements, m_params->elements)); const VkVertexInputBindingDescription bindingDescriptions[] = { { 0u, // binding sizeof(Attribute), // stride VK_VERTEX_INPUT_RATE_VERTEX, // inputRate }, }; const VkVertexInputAttributeDescription attributeDescriptions[] = { { 0u, // location 0u, // binding VK_FORMAT_R32G32B32A32_SFLOAT, // format 0u // offset }, { 1u, // location 0u, // binding VK_FORMAT_R64_UINT, // format static_cast(sizeof(Attribute::position)) // offset }, }; const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, DE_NULL, (VkPipelineVertexInputStateCreateFlags)0, // flags DE_LENGTH_OF_ARRAY(bindingDescriptions), // vertexBindingDescriptionCount bindingDescriptions, // pVertexBindingDescriptions DE_LENGTH_OF_ARRAY(attributeDescriptions), // vertexAttributeDescriptionCount attributeDescriptions // pVertexAttributeDescriptions }; return vk::makeGraphicsPipeline( vk, // vk device, // device pipelineLayout, // pipelineLayout vertexModule, // vertexShaderModule DE_NULL, // tessellationControlModule DE_NULL, // tessellationEvalModule DE_NULL, // geometryShaderModule fragmentModule, // fragmentShaderModule renderPass, // renderPass viewports, // viewports scissors, // scissors VK_PRIMITIVE_TOPOLOGY_POINT_LIST, // topology 0U, // subpass 0U, // patchControlPoints &vertexInputStateCreateInfo, // vertexInputStateCreateInfo DE_NULL, // rasterizationStateCreateInfo DE_NULL, // multisampleStateCreateInfo DE_NULL, // depthStencilStateCreateInfo DE_NULL, // colorBlendStateCreateInfo DE_NULL); // dynamicStateCreateInfo } tcu::TestStatus SpvAsmPhysicalStorageBufferVertexInOutInTestInstance::iterate (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; const VkRect2D renderArea = makeRect2D(m_params->elements, m_params->elements); Move cmdPool = createCommandPool(vki, dev, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex); Move cmdBuffer = allocateCommandBuffer(vki, dev, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); ut::Image image (m_context, m_params->elements, m_params->elements, format); Move renderPass = image.createRenderPass(); Move framebuffer = image.createFramebuffer(*renderPass); Move vertexModule = createShaderModule(vki, dev, m_context.getBinaryCollection().get("vert"), 0); Move fragmentModule = createShaderModule(vki, dev, m_context.getBinaryCollection().get("frag"), 0); Move pipelineLayout = makePipelineLayout(vki, dev, 0u, DE_NULL); Move pipeline = createGraphicsPipeline(*pipelineLayout, *renderPass, *vertexModule, *fragmentModule); ut::TypedBuffer colorBuffer = prepareColorBuffer(); ut::TypedBuffer attributes = prepareVertexAttributes(colorBuffer.getDeviceAddress()); const VkBuffer vertexBuffers[] = { attributes.getBuffer() }; const VkDeviceSize vertexOffsets[] = { 0u }; const tcu::Vec4 clearColor (-1.0f); beginCommandBuffer(vki, *cmdBuffer); vki.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); vki.cmdBindVertexBuffers(*cmdBuffer, 0, 1, vertexBuffers, vertexOffsets); beginRenderPass(vki, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor); vki.cmdDraw(*cmdBuffer, (m_params->elements * m_params->elements), 1u, 0u, 0u); endRenderPass(vki, *cmdBuffer); image.downloadAfterDraw(*cmdBuffer); endCommandBuffer(vki, *cmdBuffer); submitCommandsAndWait(vki, dev, queue, *cmdBuffer); ut::TypedBuffer resultBuffer = image.getBuffer(); return std::equal(resultBuffer.begin(), resultBuffer.end(), colorBuffer.begin()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail(""); } void SpvAsmPhysicalStorageBufferPushConstantsTestInstance::initPrograms (vk::SourceCollections& programCollection, const TestParamsPtr params) { DE_UNREF(params); const std::string program(R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" %id %str OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpName %main "main" OpName %id "gl_GlobalInvocationID" OpName %src "source" OpName %dst "destination" OpName %src_buf "source" OpName %dst_buf "destination" OpDecorate %id BuiltIn GlobalInvocationId OpDecorate %str_t Block OpMemberDecorate %str_t 0 Offset 0 OpMemberDecorate %str_t 1 Offset 8 OpMemberDecorate %str_t 2 Offset 16 OpMemberDecorate %str_t 3 Offset 20 OpDecorate %src_buf Restrict OpDecorate %dst_buf Restrict OpDecorate %int_arr ArrayStride 4 %int = OpTypeInt 32 1 %int_ptr = OpTypePointer PhysicalStorageBuffer %int %int_fptr = OpTypePointer Function %int %zero = OpConstant %int 0 %one = OpConstant %int 1 %two = OpConstant %int 2 %three = OpConstant %int 3 %uint = OpTypeInt 32 0 %uint_ptr = OpTypePointer Input %uint %uint_fptr = OpTypePointer Function %uint %uvec3 = OpTypeVector %uint 3 %uvec3ptr = OpTypePointer Input %uvec3 %uzero = OpConstant %uint 0 %id = OpVariable %uvec3ptr Input %int_arr = OpTypeRuntimeArray %int %buf_ptr = OpTypePointer PhysicalStorageBuffer %int_arr %str_t = OpTypeStruct %buf_ptr %buf_ptr %int %int %str_ptr = OpTypePointer PushConstant %str_t %str = OpVariable %str_ptr PushConstant %buf_ptr_fld = OpTypePointer PushConstant %buf_ptr %int_fld = OpTypePointer PushConstant %int %bool = OpTypeBool %void = OpTypeVoid %voidf = OpTypeFunction %void %cpbuffsf = OpTypeFunction %void %buf_ptr %buf_ptr %int %cpbuffs = OpFunction %void None %cpbuffsf %src_buf = OpFunctionParameter %buf_ptr %dst_buf = OpFunctionParameter %buf_ptr %elements = OpFunctionParameter %int %cp_begin = OpLabel %j = OpVariable %int_fptr Function OpStore %j %zero OpBranch %for %for = OpLabel %vj = OpLoad %int %j %cj = OpULessThan %bool %vj %elements OpLoopMerge %for_end %incj None OpBranchConditional %cj %for_body %for_end %for_body = OpLabel %src_el_lnk = OpAccessChain %int_ptr %src_buf %vj %dst_el_lnk = OpAccessChain %int_ptr %dst_buf %vj %src_el = OpLoad %int %src_el_lnk Aligned 4 OpStore %dst_el_lnk %src_el Aligned 4 OpBranch %incj %incj = OpLabel %nj = OpIAdd %int %vj %one OpStore %j %nj OpBranch %for %for_end = OpLabel OpReturn OpFunctionEnd %main = OpFunction %void None %voidf %begin = OpLabel %i = OpVariable %int_fptr Function OpStore %i %zero %src_lnk = OpAccessChain %buf_ptr_fld %str %zero %dst_lnk = OpAccessChain %buf_ptr_fld %str %one %cnt_lnk = OpAccessChain %int_fld %str %two %use_fun_lnk = OpAccessChain %int_fld %str %three %src = OpLoad %buf_ptr %src_lnk %dst = OpLoad %buf_ptr %dst_lnk %cnt = OpLoad %int %cnt_lnk %use_fun = OpLoad %int %use_fun_lnk %cuf = OpINotEqual %bool %use_fun %zero OpSelectionMerge %use_fun_end None OpBranchConditional %cuf %copy %loop %copy = OpLabel %unused = OpFunctionCall %void %cpbuffs %src %dst %cnt OpBranch %use_fun_end %loop = OpLabel %vi = OpLoad %int %i %ci = OpSLessThan %bool %vi %cnt OpLoopMerge %loop_end %inci None OpBranchConditional %ci %loop_body %loop_end %loop_body = OpLabel %src_px_lnk = OpAccessChain %int_ptr %src %vi %dst_px_lnk = OpAccessChain %int_ptr %dst %vi %src_px = OpLoad %int %src_px_lnk Aligned 4 OpStore %dst_px_lnk %src_px Aligned 4 OpBranch %inci %inci = OpLabel %ni = OpIAdd %int %vi %one OpStore %i %ni OpBranch %loop %loop_end = OpLabel OpBranch %use_fun_end %use_fun_end = OpLabel OpReturn OpFunctionEnd )"); programCollection.spirvAsmSources.add("comp") << program << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); } tcu::TestStatus SpvAsmPhysicalStorageBufferPushConstantsTestInstance::iterate (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Move cmdPool = createCommandPool(vki, dev, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex); Move cmdBuffer = allocateCommandBuffer(vki, dev, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); Move shaderModule = createShaderModule(vki, dev, m_context.getBinaryCollection().get("comp"), 0); struct PushConstant { deUint64 src; deUint64 dst; deInt32 cnt; deBool use_fun; }; VkPushConstantRange pushConstantRange = { VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags stageFlags; 0, // deUint32 offset; sizeof(PushConstant) // deUint32 size; }; Move pipelineLayout = makePipelineLayout(vki, dev, 0, DE_NULL, 1, &pushConstantRange); Move pipeline = makeComputePipeline(vki, dev, *pipelineLayout, *shaderModule); ut::TypedBuffer src (m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, m_params->elements, true); ut::TypedBuffer dst (m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, m_params->elements, true); src.iota(m_params->elements, true); dst.zero(true); const PushConstant pc = { src.getDeviceAddress(), dst.getDeviceAddress(), deInt32(m_params->elements), m_params->method == PassMethod::PUSH_CONSTANTS_FUNCTION }; beginCommandBuffer(vki, *cmdBuffer); vki.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vki.cmdPushConstants(*cmdBuffer, *pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); vki.cmdDispatch(*cmdBuffer, 1, 1, 1); endCommandBuffer(vki, *cmdBuffer); submitCommandsAndWait(vki, dev, queue, *cmdBuffer); dst.invalidate(); return std::equal(src.begin(), src.end(), dst.begin()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail(""); } void SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance::initPrograms (vk::SourceCollections& programCollection, const TestParamsPtr params) { DE_UNREF(params); const std::string comp(R"( OpCapability Shader OpCapability Int64 OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %comp "main" %id %ssbo OpExecutionMode %comp LocalSize 1 1 1 OpDecorate %id BuiltIn GlobalInvocationId OpDecorate %sssbo Block OpMemberDecorate %sssbo 0 Offset 0 OpMemberDecorate %sssbo 1 Offset 8 OpMemberDecorate %sssbo 2 Offset 16 OpMemberDecorate %sssbo 3 Offset 24 OpDecorate %ssbo DescriptorSet 0 OpDecorate %ssbo Binding 0 OpDecorate %rta ArrayStride 4 %bool = OpTypeBool %int = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %ulong = OpTypeInt 64 0 %zero = OpConstant %int 0 %one = OpConstant %int 1 %two = OpConstant %int 2 %three = OpConstant %int 3 %uvec3 = OpTypeVector %uint 3 %rta = OpTypeRuntimeArray %int %rta_psb = OpTypePointer PhysicalStorageBuffer %rta %sssbo = OpTypeStruct %rta_psb %ulong %rta_psb %ulong %sssbo_buf = OpTypePointer StorageBuffer %sssbo %ssbo = OpVariable %sssbo_buf StorageBuffer %rta_psb_sb = OpTypePointer StorageBuffer %rta_psb %int_psb = OpTypePointer PhysicalStorageBuffer %int %ulong_sb = OpTypePointer StorageBuffer %ulong %uvec3_in = OpTypePointer Input %uvec3 %id = OpVariable %uvec3_in Input %uint_in = OpTypePointer Input %uint %void = OpTypeVoid %voidf = OpTypeFunction %void %comp = OpFunction %void None %voidf %comp_begin = OpLabel %pgid_x = OpAccessChain %uint_in %id %zero %gid_x = OpLoad %uint %pgid_x %mod2 = OpSMod %int %gid_x %two %even = OpIEqual %bool %mod2 %zero %psrc_buff_p = OpAccessChain %rta_psb_sb %ssbo %zero %pdst_buff_p = OpAccessChain %rta_psb_sb %ssbo %two %src_buff_p = OpLoad %rta_psb %psrc_buff_p %dst_buff_p = OpLoad %rta_psb %pdst_buff_p %psrc_buff_u = OpAccessChain %ulong_sb %ssbo %one %psrc_buff_v = OpLoad %ulong %psrc_buff_u %src_buff_v = OpConvertUToPtr %rta_psb %psrc_buff_v %pdst_buff_u = OpAccessChain %ulong_sb %ssbo %three %pdst_buff_v = OpLoad %ulong %pdst_buff_u %dst_buff_v = OpConvertUToPtr %rta_psb %pdst_buff_v %src = OpSelect %rta_psb %even %src_buff_p %src_buff_v %dst = OpSelect %rta_psb %even %dst_buff_v %dst_buff_p %psrc_color = OpAccessChain %int_psb %src %gid_x %src_color = OpLoad %int %psrc_color Aligned 4 %pdst_color = OpAccessChain %int_psb %dst %gid_x OpStore %pdst_color %src_color Aligned 4 OpReturn OpFunctionEnd )"); programCollection.spirvAsmSources.add("comp") << comp << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); } /* Below test does not add anything new. The main purpose of this test is to show that both PhysicalStorageBuffer and 64-bit integer value can coexist in one array one next to the other. In the both cases, when the one address has its own dedicated storage class and the other is regular integer, the shader is responsible for how to interpret and use input addresses. Regardless of the shader, the application always passes them as 64-bit integers. */ tcu::TestStatus SpvAsmPhysicalStorageBufferAddrsInSSBOTestInstance::iterate (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Move cmdPool = createCommandPool(vki, dev, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex); Move cmdBuffer = allocateCommandBuffer(vki, dev, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); Move shaderModule = createShaderModule(vki, dev, m_context.getBinaryCollection().get("comp"), 0); Move descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .build(vki, dev); Move descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) .build(vki, dev, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); Move descriptorSet = makeDescriptorSet(vki, dev, *descriptorPool, *descriptorSetLayout); Move pipelineLayout = makePipelineLayout(vki, dev, 1u, &descriptorSetLayout.get()); Move pipeline = makeComputePipeline(vki, dev, *pipelineLayout, *shaderModule); ut::TypedBuffer src (m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, m_params->elements, true); ut::TypedBuffer dst (m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, m_params->elements, true); struct SSBO { deUint64 srcAsBuff; deUint64 srcAsUint; deUint64 dstAsBuff; deUint64 dstAsUint; }; ut::TypedBuffer ssbo (m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, { { src.getDeviceAddress(), src.getDeviceAddress(), dst.getDeviceAddress(), dst.getDeviceAddress() } }); VkDescriptorBufferInfo ssboBufferInfo = makeDescriptorBufferInfo(ssbo.getBuffer(), 0, ssbo.getSize()); DescriptorSetUpdateBuilder () .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &ssboBufferInfo) .update(vki, dev); src.iota(m_params->elements, true); dst.zero(true); beginCommandBuffer(vki, *cmdBuffer); vki.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vki.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); vki.cmdDispatch(*cmdBuffer, m_params->elements, 1, 1); endCommandBuffer(vki, *cmdBuffer); submitCommandsAndWait(vki, dev, queue, *cmdBuffer); dst.invalidate(); return std::equal(src.begin(), src.end(), dst.begin()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail(""); } } // unnamed tcu::TestCaseGroup* createPhysicalStorageBufferTestGroup (tcu::TestContext& testCtx) { struct { PassMethod method; std::string testName; } const methods[] = { { PassMethod::PUSH_CONSTANTS, "push_constants" }, { PassMethod::PUSH_CONSTANTS_FUNCTION, "push_constants_function" }, { PassMethod::VERTEX_IN_OUT_IN, "vertex_in_out_in" }, { PassMethod::ADDRESSES_IN_SSBO, "addrs_in_ssbo" }, }; tcu::TestCaseGroup* group = new tcu::TestCaseGroup(testCtx, "physical_storage_buffer"); for (const auto& method : methods) { group->addChild(new SpvAsmPhysicalStorageBufferTestCase(testCtx, method.testName, TestParamsPtr(new TestParams({method.method, 64})))); } return group; } } // SpirVAssembly } // vkt