1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2019 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief SPIR-V Assembly Tests for indexing with access chain operations.
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktSpvAsmFromHlslTests.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vkPrograms.hpp"
27 #include "vkObjUtil.hpp"
28 #include "vkRefUtil.hpp"
29 #include "vkTypeUtil.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vkCmdUtil.hpp"
34
35 namespace vkt
36 {
37 namespace SpirVAssembly
38 {
39
40 namespace
41 {
42
43 using namespace vk;
44
45 enum TestType
46 {
47 TT_CBUFFER_PACKING = 0,
48 };
49
50 struct TestConfig
51 {
52 TestType type;
53 };
54
55 struct Programs
56 {
initvkt::SpirVAssembly::__anon4dbd7b600111::Programs57 void init(vk::SourceCollections &dst, TestConfig config) const
58 {
59 if (config.type == TT_CBUFFER_PACKING)
60 {
61 // HLSL shaders has a packing corner case that GLSL shaders cannot exhibit.
62 // Below shader, foo has an ArrayStride of 16, which leaves bar effectively
63 // 'within' the end of the foo array. This is entirely valid for HLSL and
64 // with the VK_EXT_scalar_block_layout extension.
65 std::string source("cbuffer cbIn\n"
66 "{\n"
67 " int foo[2] : packoffset(c0);\n"
68 " int bar : packoffset(c1.y);\n"
69 "};\n"
70 "RWStructuredBuffer<int> result : register(u1);\n"
71 "[numthreads(1, 1, 1)]\n"
72 "void main(uint3 dispatchThreadID : SV_DispatchThreadID)\n"
73 "{\n"
74 " result[0] = bar;\n"
75 "}\n");
76
77 dst.hlslSources.add("comp") << glu::ComputeSource(source)
78 << vk::ShaderBuildOptions(dst.usedVulkanVersion, vk::SPIRV_VERSION_1_0,
79 vk::ShaderBuildOptions::FLAG_ALLOW_SCALAR_OFFSETS);
80 }
81 }
82 };
83
84 class HlslTest : public TestInstance
85 {
86 public:
87 HlslTest(Context &context, TestConfig config);
88 virtual ~HlslTest(void) = default;
89
90 tcu::TestStatus iterate(void);
91 };
92
HlslTest(Context & context,TestConfig config)93 HlslTest::HlslTest(Context &context, TestConfig config) : TestInstance(context)
94 {
95 DE_UNREF(config);
96 }
97
iterate(void)98 tcu::TestStatus HlslTest::iterate(void)
99 {
100 const DeviceInterface &vk = m_context.getDeviceInterface();
101 const VkDevice device = m_context.getDevice();
102 const VkQueue queue = m_context.getUniversalQueue();
103 const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
104 Allocator &allocator = m_context.getDefaultAllocator();
105 const int testValue = 5;
106
107 // Create an input buffer
108 const VkBufferUsageFlags inBufferUsageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
109 const VkDeviceSize inBufferSizeBytes = 32; // 2 element array with 16B stride
110 VkBufferCreateInfo inBufferCreateInfo = makeBufferCreateInfo(inBufferSizeBytes, inBufferUsageFlags);
111 vk::Move<vk::VkBuffer> inBuffer = createBuffer(vk, device, &inBufferCreateInfo);
112 de::MovePtr<vk::Allocation> inAllocation =
113 allocator.allocate(getBufferMemoryRequirements(vk, device, *inBuffer), MemoryRequirement::HostVisible);
114 VK_CHECK(vk.bindBufferMemory(device, *inBuffer, inAllocation->getMemory(), inAllocation->getOffset()));
115
116 // Fill the input structure with data - first attribute is array that has 16B stride,
117 // this means that second attribute has to start at offset 20B (4B + 16B)
118 {
119 int *bufferPtr = static_cast<int *>(inAllocation->getHostPtr());
120 memset(bufferPtr, 0, inBufferSizeBytes);
121 bufferPtr[5] = testValue;
122 flushAlloc(vk, device, *inAllocation);
123 }
124
125 // Create an output buffer
126 const VkBufferUsageFlags outBufferUsageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
127 const VkDeviceSize outBufferSizeBytes = sizeof(int);
128 VkBufferCreateInfo outBufferCreateInfo = makeBufferCreateInfo(outBufferSizeBytes, outBufferUsageFlags);
129 vk::Move<vk::VkBuffer> outBuffer = createBuffer(vk, device, &outBufferCreateInfo);
130 de::MovePtr<vk::Allocation> outAllocation =
131 allocator.allocate(getBufferMemoryRequirements(vk, device, *outBuffer), MemoryRequirement::HostVisible);
132 VK_CHECK(vk.bindBufferMemory(device, *outBuffer, outAllocation->getMemory(), outAllocation->getOffset()));
133
134 // Create descriptor set
135 const VkDescriptorType uniBufDesc = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
136 const VkDescriptorType storBufDesc = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
137 const Unique<VkDescriptorSetLayout> descriptorSetLayout(
138 DescriptorSetLayoutBuilder()
139 .addSingleBinding(uniBufDesc, VK_SHADER_STAGE_COMPUTE_BIT)
140 .addSingleBinding(storBufDesc, VK_SHADER_STAGE_COMPUTE_BIT)
141 .build(vk, device));
142
143 const Unique<VkDescriptorPool> descriptorPool(
144 DescriptorPoolBuilder()
145 .addType(uniBufDesc)
146 .addType(storBufDesc)
147 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
148
149 const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
150
151 const VkDescriptorBufferInfo inputBufferDescriptorInfo =
152 makeDescriptorBufferInfo(*inBuffer, 0ull, inBufferSizeBytes);
153 const VkDescriptorBufferInfo outputBufferDescriptorInfo =
154 makeDescriptorBufferInfo(*outBuffer, 0ull, outBufferSizeBytes);
155 DescriptorSetUpdateBuilder()
156 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), uniBufDesc,
157 &inputBufferDescriptorInfo)
158 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), storBufDesc,
159 &outputBufferDescriptorInfo)
160 .update(vk, device);
161
162 // Perform the computation
163 const Unique<VkShaderModule> shaderModule(
164 createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
165 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
166
167 const VkPipelineShaderStageCreateInfo pipelineShaderStageParams = {
168 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
169 DE_NULL,
170 static_cast<VkPipelineShaderStageCreateFlags>(0u),
171 VK_SHADER_STAGE_COMPUTE_BIT,
172 *shaderModule,
173 "main",
174 DE_NULL,
175 };
176 const VkComputePipelineCreateInfo pipelineCreateInfo = {
177 VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
178 DE_NULL,
179 static_cast<VkPipelineCreateFlags>(0u),
180 pipelineShaderStageParams,
181 *pipelineLayout,
182 DE_NULL,
183 0,
184 };
185 Unique<VkPipeline> pipeline(createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo));
186 const VkBufferMemoryBarrier hostWriteBarrier = makeBufferMemoryBarrier(
187 VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, *inBuffer, 0ull, inBufferSizeBytes);
188 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
189 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *outBuffer, 0ull, outBufferSizeBytes);
190
191 const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
192 const Unique<VkCommandBuffer> cmdBuffer(
193 allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
194
195 // Start recording commands
196 beginCommandBuffer(vk, *cmdBuffer);
197
198 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
199 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(),
200 0u, DE_NULL);
201
202 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
203 (VkDependencyFlags)0, 0, (const VkMemoryBarrier *)DE_NULL, 1, &hostWriteBarrier, 0,
204 (const VkImageMemoryBarrier *)DE_NULL);
205 vk.cmdDispatch(*cmdBuffer, 1, 1, 1);
206 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
207 (VkDependencyFlags)0, 0, (const VkMemoryBarrier *)DE_NULL, 1, &shaderWriteBarrier, 0,
208 (const VkImageMemoryBarrier *)DE_NULL);
209
210 endCommandBuffer(vk, *cmdBuffer);
211
212 // Wait for completion
213 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
214
215 // Validate the results
216 invalidateAlloc(vk, device, *outAllocation);
217 const int *bufferPtr = static_cast<int *>(outAllocation->getHostPtr());
218 if (*bufferPtr != testValue)
219 return tcu::TestStatus::fail("Fail");
220 return tcu::TestStatus::pass("Pass");
221 }
222
checkSupport(Context & context)223 void checkSupport(Context &context)
224 {
225 context.requireDeviceFunctionality("VK_EXT_scalar_block_layout");
226 }
227
228 } // namespace
229
createHlslComputeGroup(tcu::TestContext & testCtx)230 tcu::TestCaseGroup *createHlslComputeGroup(tcu::TestContext &testCtx)
231 {
232 typedef InstanceFactory1WithSupport<HlslTest, TestConfig, FunctionSupport0, Programs> HlslTestInstance;
233 de::MovePtr<tcu::TestCaseGroup> hlslCasesGroup(new tcu::TestCaseGroup(testCtx, "hlsl_cases"));
234
235 TestConfig testConfig = {TT_CBUFFER_PACKING};
236 hlslCasesGroup->addChild(new HlslTestInstance(testCtx, "cbuffer_packing", testConfig, checkSupport));
237
238 return hlslCasesGroup.release();
239 }
240
241 } // namespace SpirVAssembly
242 } // namespace vkt
243