1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 Google LLC.
6 *
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Test no-op image layout transitions in VK_KHR_synchronization2
23 *//*--------------------------------------------------------------------*/
24
25 #include "deUniquePtr.hpp"
26
27 #include "tcuTextureUtil.hpp"
28 #include "tcuImageCompare.hpp"
29
30 #include "vkBarrierUtil.hpp"
31 #include "vkImageUtil.hpp"
32 #include "vkCmdUtil.hpp"
33 #include "vkObjUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkImageWithMemory.hpp"
36 #include "vktTestCaseUtil.hpp"
37 #include "vktSynchronizationUtil.hpp"
38 #include "tcuTestLog.hpp"
39
40 #include <string>
41
42 using namespace vk;
43
44 namespace vkt
45 {
46 namespace synchronization
47 {
48 namespace
49 {
50
51 using de::MovePtr;
52 using std::vector;
53 using tcu::TextureLevel;
54 using tcu::Vec4;
55
56 const int WIDTH = 64;
57 const int HEIGHT = 64;
58 const VkFormat FORMAT = VK_FORMAT_R8G8B8A8_UNORM;
59
makeImageCreateInfo()60 inline VkImageCreateInfo makeImageCreateInfo()
61 {
62 const VkImageUsageFlags usage =
63 VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
64 const VkImageCreateInfo imageParams = {
65 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
66 DE_NULL, // const void* pNext;
67 0, // VkImageCreateFlags flags;
68 VK_IMAGE_TYPE_2D, // VkImageType imageType;
69 FORMAT, // VkFormat format;
70 makeExtent3D(WIDTH, HEIGHT, 1u), // VkExtent3D extent;
71 1u, // uint32_t mipLevels;
72 1u, // uint32_t arrayLayers;
73 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
74 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
75 usage, // VkImageUsageFlags usage;
76 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
77 0u, // uint32_t queueFamilyIndexCount;
78 DE_NULL, // const uint32_t* pQueueFamilyIndices;
79 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
80 };
81
82 return imageParams;
83 }
84
makeVertexBuffer(const DeviceInterface & vk,const VkDevice device,const uint32_t queueFamilyIndex)85 Move<VkBuffer> makeVertexBuffer(const DeviceInterface &vk, const VkDevice device, const uint32_t queueFamilyIndex)
86 {
87 const VkBufferCreateInfo vertexBufferParams = {
88 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
89 DE_NULL, // const void* pNext;
90 0u, // VkBufferCreateFlags flags;
91 1024u, // VkDeviceSize size;
92 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
93 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
94 1u, // uint32_t queueFamilyIndexCount;
95 &queueFamilyIndex // const uint32_t* pQueueFamilyIndices;
96 };
97
98 Move<VkBuffer> vertexBuffer = createBuffer(vk, device, &vertexBufferParams);
99 ;
100 return vertexBuffer;
101 }
102
103 class SynchronizationImageLayoutTransitionTestInstance : public TestInstance
104 {
105 public:
106 SynchronizationImageLayoutTransitionTestInstance(Context &context);
107 tcu::TestStatus iterate(void);
108 };
109
SynchronizationImageLayoutTransitionTestInstance(Context & context)110 SynchronizationImageLayoutTransitionTestInstance::SynchronizationImageLayoutTransitionTestInstance(Context &context)
111 : TestInstance(context)
112 {
113 }
114
115 template <typename T>
sizeInBytes(const vector<T> & vec)116 inline size_t sizeInBytes(const vector<T> &vec)
117 {
118 return vec.size() * sizeof(vec[0]);
119 }
120
121 // Draw a quad covering the whole framebuffer
genFullQuadVertices(void)122 vector<Vec4> genFullQuadVertices(void)
123 {
124 vector<Vec4> vertices;
125 vertices.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
126 vertices.push_back(Vec4(1.0f, -1.0f, 0.0f, 1.0f));
127 vertices.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
128 vertices.push_back(Vec4(1.0f, -1.0f, 0.0f, 1.0f));
129 vertices.push_back(Vec4(1.0f, 1.0f, 0.0f, 1.0f));
130 vertices.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
131
132 return vertices;
133 }
134
135 struct Vertex
136 {
Vertexvkt::synchronization::__anon273e365a0111::Vertex137 Vertex(Vec4 vertices_) : vertices(vertices_)
138 {
139 }
140 Vec4 vertices;
141
142 static VkVertexInputBindingDescription getBindingDescription(void);
143 static vector<VkVertexInputAttributeDescription> getAttributeDescriptions(void);
144 };
145
getBindingDescription(void)146 VkVertexInputBindingDescription Vertex::getBindingDescription(void)
147 {
148 static const VkVertexInputBindingDescription desc = {
149 0u, // uint32_t binding;
150 static_cast<uint32_t>(sizeof(Vertex)), // uint32_t stride;
151 VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
152 };
153
154 return desc;
155 }
156
getAttributeDescriptions(void)157 vector<VkVertexInputAttributeDescription> Vertex::getAttributeDescriptions(void)
158 {
159 static const vector<VkVertexInputAttributeDescription> desc = {
160 {
161 0u, // uint32_t location;
162 0u, // uint32_t binding;
163 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
164 static_cast<uint32_t>(offsetof(Vertex, vertices)), // uint32_t offset;
165 },
166 };
167
168 return desc;
169 }
170
iterate(void)171 tcu::TestStatus SynchronizationImageLayoutTransitionTestInstance::iterate(void)
172 {
173 const DeviceInterface &vk = m_context.getDeviceInterface();
174 const VkDevice device = m_context.getDevice();
175 Allocator &allocator = m_context.getDefaultAllocator();
176 const VkQueue queue = m_context.getUniversalQueue();
177 const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
178 const VkDeviceSize bufferSize = 16 * 1024;
179
180 const VkExtent2D renderSize = {uint32_t(WIDTH), uint32_t(HEIGHT)};
181 const VkRect2D renderArea = makeRect2D(makeExtent3D(WIDTH, HEIGHT, 1u));
182 const vector<VkRect2D> scissors(1u, renderArea);
183 const vector<VkViewport> viewports(1u, makeViewport(makeExtent3D(WIDTH, HEIGHT, 1u)));
184
185 const vector<Vec4> vertices = genFullQuadVertices();
186 Move<VkBuffer> vertexBuffer = makeVertexBuffer(vk, device, queueFamilyIndex);
187 MovePtr<Allocation> vertexBufferAlloc =
188 bindBuffer(vk, device, allocator, *vertexBuffer, MemoryRequirement::HostVisible);
189 const VkDeviceSize vertexBufferOffset = 0ull;
190
191 deMemcpy(vertexBufferAlloc->getHostPtr(), &vertices[0], sizeInBytes(vertices));
192 flushAlloc(vk, device, *vertexBufferAlloc);
193
194 const VkImageCreateInfo targetCreateInfo = makeImageCreateInfo();
195 const VkImageSubresourceRange targetSubresourceRange =
196 makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1, 0, 1);
197 const ImageWithMemory targetImage(vk, device, m_context.getDefaultAllocator(), targetCreateInfo,
198 MemoryRequirement::Any);
199 Move<VkImageView> targetImageView =
200 makeImageView(vk, device, *targetImage, VK_IMAGE_VIEW_TYPE_2D, FORMAT, targetSubresourceRange);
201
202 const Move<VkCommandPool> cmdPool =
203 createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
204 const Move<VkCommandBuffer> cmdBuffer =
205 allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
206
207 Move<VkRenderPass> renderPass = makeRenderPass(vk, device, FORMAT, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
208 Move<VkFramebuffer> framebuffer =
209 makeFramebuffer(vk, device, *renderPass, targetImageView.get(), renderSize.width, renderSize.height);
210
211 const Move<VkShaderModule> vertexModule =
212 createShaderModule(vk, device, m_context.getBinaryCollection().get("vert1"), 0u);
213 const Move<VkShaderModule> fragmentModule =
214 createShaderModule(vk, device, m_context.getBinaryCollection().get("frag1"), 0u);
215
216 const Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vk, device, DE_NULL);
217
218 const VkPipelineColorBlendAttachmentState clrBlendAttachmentState = {
219 VK_TRUE, // VkBool32 blendEnable;
220 VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcColorBlendFactor;
221 VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, // VkBlendFactor dstColorBlendFactor;
222 VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
223 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
224 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
225 VK_BLEND_OP_MAX, // VkBlendOp alphaBlendOp;
226 (VkColorComponentFlags)0xF // VkColorComponentFlags colorWriteMask;
227 };
228
229 const VkPipelineColorBlendStateCreateInfo clrBlendStateCreateInfo = {
230 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
231 DE_NULL, // const void* pNext;
232 (VkPipelineColorBlendStateCreateFlags)0u, // VkPipelineColorBlendStateCreateFlags flags;
233 VK_FALSE, // VkBool32 logicOpEnable;
234 VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp;
235 1u, // uint32_t attachmentCount;
236 &clrBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
237 {1.0f, 1.0f, 1.0f, 1.0f} // float blendConstants[4];
238 };
239
240 const VkVertexInputBindingDescription vtxBindingDescription = Vertex::getBindingDescription();
241 const auto vtxAttrDescriptions = Vertex::getAttributeDescriptions();
242
243 const VkPipelineVertexInputStateCreateInfo vtxInputStateCreateInfo = {
244 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
245 DE_NULL, // const void* pNext;
246 (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
247 1u, // uint32_t vertexBindingDescriptionCount;
248 &vtxBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions
249 static_cast<uint32_t>(
250 vtxAttrDescriptions.size()), // uint32_t vertexAttributeDescriptionCount
251 vtxAttrDescriptions.data(), // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions
252 };
253
254 const Move<VkPipeline> graphicsPipeline = makeGraphicsPipeline(
255 vk, device, pipelineLayout.get(), vertexModule.get(), DE_NULL, DE_NULL, DE_NULL, fragmentModule.get(),
256 renderPass.get(), viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0u, 0u, &vtxInputStateCreateInfo,
257 DE_NULL, DE_NULL, DE_NULL, &clrBlendStateCreateInfo);
258
259 const VkBufferCreateInfo resultBufferCreateInfo =
260 makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
261 Move<VkBuffer> resultBuffer = createBuffer(vk, device, &resultBufferCreateInfo);
262 MovePtr<Allocation> resultBufferMemory =
263 allocator.allocate(getBufferMemoryRequirements(vk, device, *resultBuffer), MemoryRequirement::HostVisible);
264 MovePtr<TextureLevel> resultImage(new TextureLevel(mapVkFormat(FORMAT), renderSize.width, renderSize.height, 1));
265
266 VK_CHECK(
267 vk.bindBufferMemory(device, *resultBuffer, resultBufferMemory->getMemory(), resultBufferMemory->getOffset()));
268
269 const Vec4 clearColor(0.0f, 0.0f, 0.0f, 0.0f);
270
271 clearColorImage(vk, device, m_context.getUniversalQueue(), m_context.getUniversalQueueFamilyIndex(),
272 targetImage.get(), clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
273 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1);
274
275 beginCommandBuffer(vk, *cmdBuffer);
276
277 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
278 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
279
280 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(0, 0, WIDTH, HEIGHT), 0, DE_NULL);
281 vk.cmdDraw(*cmdBuffer, static_cast<uint32_t>(vertices.size()), 1u, 0u, 0u);
282 endRenderPass(vk, *cmdBuffer);
283
284 // Define an execution dependency and skip the layout transition. This is allowed when oldLayout
285 // and newLayout are both UNDEFINED. The test will fail if the driver discards the contents of
286 // the image.
287 const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2(
288 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags2KHR srcStageMask
289 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags2KHR srcAccessMask
290 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags2KHR dstStageMask
291 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, // VkAccessFlags2KHR dstAccessMask
292 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout
293 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout newLayout
294 targetImage.get(), // VkImage image
295 targetSubresourceRange // VkImageSubresourceRange subresourceRange
296 );
297 VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
298 #ifndef CTS_USES_VULKANSC
299 vk.cmdPipelineBarrier2(cmdBuffer.get(), &dependencyInfo);
300 #else
301 vk.cmdPipelineBarrier2KHR(cmdBuffer.get(), &dependencyInfo);
302 #endif // CTS_USES_VULKANSC
303
304 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(0, 0, WIDTH, HEIGHT), 0, DE_NULL);
305 vk.cmdDraw(*cmdBuffer, static_cast<uint32_t>(vertices.size()), 1u, 0u, 0u);
306 endRenderPass(vk, *cmdBuffer);
307
308 // Read the result buffer data
309 copyImageToBuffer(vk, *cmdBuffer, *targetImage, *resultBuffer, tcu::IVec2(WIDTH, HEIGHT),
310 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
311
312 endCommandBuffer(vk, *cmdBuffer);
313 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
314
315 invalidateAlloc(vk, device, *resultBufferMemory);
316
317 tcu::clear(resultImage->getAccess(), tcu::IVec4(0));
318 tcu::copy(resultImage->getAccess(),
319 tcu::ConstPixelBufferAccess(resultImage.get()->getFormat(), resultImage.get()->getSize(),
320 resultBufferMemory->getHostPtr()));
321
322 TextureLevel textureLevel(mapVkFormat(FORMAT), WIDTH, HEIGHT, 1);
323 const tcu::PixelBufferAccess expectedImage = textureLevel.getAccess();
324
325 const float alpha = 0.4f;
326 const float red = (2.0f - alpha) * alpha;
327 const float green = red;
328 const float blue = 0;
329 const Vec4 color = Vec4(red, green, blue, alpha);
330
331 for (int y = 0; y < HEIGHT; y++)
332 for (int x = 0; x < WIDTH; x++)
333 expectedImage.setPixel(color, x, y, 0);
334
335 bool ok = tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Image comparison", "", expectedImage,
336 resultImage->getAccess(), tcu::Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
337 return ok ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail");
338 }
339
340 class SynchronizationImageLayoutTransitionTest : public TestCase
341 {
342 public:
343 SynchronizationImageLayoutTransitionTest(tcu::TestContext &testCtx, const std::string &name);
344
345 virtual void checkSupport(Context &context) const;
346 void initPrograms(SourceCollections &programCollection) const;
347 TestInstance *createInstance(Context &context) const;
348 };
349
SynchronizationImageLayoutTransitionTest(tcu::TestContext & testCtx,const std::string & name)350 SynchronizationImageLayoutTransitionTest::SynchronizationImageLayoutTransitionTest(tcu::TestContext &testCtx,
351 const std::string &name)
352 : TestCase(testCtx, name)
353 {
354 }
355
initPrograms(SourceCollections & programCollection) const356 void SynchronizationImageLayoutTransitionTest::initPrograms(SourceCollections &programCollection) const
357 {
358 std::ostringstream vertexSrc;
359 vertexSrc << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
360 << "layout(location = 0) in vec4 a_position;\n"
361 << "void main (void) {\n"
362 << " gl_Position = a_position;\n"
363 << "}\n";
364
365 std::ostringstream fragmentSrc;
366 fragmentSrc << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
367 << "layout(location = 0) out vec4 outColor;\n"
368 << "void main() {\n"
369 << " outColor = vec4(1., 1., 0., .4);\n"
370 << "}\n";
371
372 programCollection.glslSources.add("vert1") << glu::VertexSource(vertexSrc.str());
373 programCollection.glslSources.add("frag1") << glu::FragmentSource(fragmentSrc.str());
374 }
375
checkSupport(Context & context) const376 void SynchronizationImageLayoutTransitionTest::checkSupport(Context &context) const
377 {
378 context.requireDeviceFunctionality("VK_KHR_synchronization2");
379 }
380
createInstance(Context & context) const381 TestInstance *SynchronizationImageLayoutTransitionTest::createInstance(Context &context) const
382 {
383 return new SynchronizationImageLayoutTransitionTestInstance(context);
384 }
385
386 } // namespace
387
createImageLayoutTransitionTests(tcu::TestContext & testCtx)388 tcu::TestCaseGroup *createImageLayoutTransitionTests(tcu::TestContext &testCtx)
389 {
390 // No-op image layout transition tests
391 de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "layout_transition"));
392 testGroup->addChild(new SynchronizationImageLayoutTransitionTest(testCtx, "no_op"));
393
394 return testGroup.release();
395 }
396
397 } // namespace synchronization
398 } // namespace vkt
399