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 tcu::Vec4;
52 using std::vector;
53 using de::MovePtr;
54 using tcu::TextureLevel;
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 = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT
63 | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
64 const VkImageCreateInfo imageParams =
65 {
66 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
67 DE_NULL, // const void* pNext;
68 0, // VkImageCreateFlags flags;
69 VK_IMAGE_TYPE_2D, // VkImageType imageType;
70 FORMAT, // VkFormat format;
71 makeExtent3D(WIDTH, HEIGHT, 1u), // VkExtent3D extent;
72 1u, // deUint32 mipLevels;
73 1u, // deUint32 arrayLayers;
74 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
75 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
76 usage, // VkImageUsageFlags usage;
77 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
78 0u, // deUint32 queueFamilyIndexCount;
79 DE_NULL, // const deUint32* pQueueFamilyIndices;
80 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
81 };
82
83 return imageParams;
84 }
85
makeVertexBuffer(const DeviceInterface & vk,const VkDevice device,const deUint32 queueFamilyIndex)86 Move<VkBuffer> makeVertexBuffer (const DeviceInterface& vk, const VkDevice device, const deUint32 queueFamilyIndex)
87 {
88 const VkBufferCreateInfo vertexBufferParams =
89 {
90 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
91 DE_NULL, // const void* pNext;
92 0u, // VkBufferCreateFlags flags;
93 1024u, // VkDeviceSize size;
94 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
95 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
96 1u, // deUint32 queueFamilyIndexCount;
97 &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
98 };
99
100 Move<VkBuffer> vertexBuffer = createBuffer(vk, device, &vertexBufferParams);;
101 return vertexBuffer;
102 }
103
104 class SynchronizationImageLayoutTransitionTestInstance : public TestInstance
105 {
106 public:
107 SynchronizationImageLayoutTransitionTestInstance (Context& context);
108 tcu::TestStatus iterate (void);
109 };
110
SynchronizationImageLayoutTransitionTestInstance(Context & context)111 SynchronizationImageLayoutTransitionTestInstance::SynchronizationImageLayoutTransitionTestInstance (Context& context)
112 : TestInstance (context)
113 {
114 }
115
116 template<typename T>
sizeInBytes(const vector<T> & vec)117 inline size_t sizeInBytes (const vector<T>& vec)
118 {
119 return vec.size() * sizeof(vec[0]);
120 }
121
122 // Draw a quad covering the whole framebuffer
genFullQuadVertices(void)123 vector<Vec4> genFullQuadVertices (void)
124 {
125 vector<Vec4> vertices;
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 vertices.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
132
133 return vertices;
134 }
135
136 struct Vertex
137 {
Vertexvkt::synchronization::__anon343906920111::Vertex138 Vertex(Vec4 vertices_) : vertices(vertices_) {}
139 Vec4 vertices;
140
141 static VkVertexInputBindingDescription getBindingDescription (void);
142 static vector<VkVertexInputAttributeDescription> getAttributeDescriptions (void);
143 };
144
getBindingDescription(void)145 VkVertexInputBindingDescription Vertex::getBindingDescription (void)
146 {
147 static const VkVertexInputBindingDescription desc =
148 {
149 0u, // deUint32 binding;
150 static_cast<deUint32>(sizeof(Vertex)), // deUint32 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 {
162 0u, // deUint32 location;
163 0u, // deUint32 binding;
164 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
165 static_cast<deUint32>(offsetof(Vertex, vertices)), // deUint32 offset;
166 },
167 };
168
169 return desc;
170 }
171
iterate(void)172 tcu::TestStatus SynchronizationImageLayoutTransitionTestInstance::iterate (void)
173 {
174 const DeviceInterface& vk = m_context.getDeviceInterface();
175 const VkDevice device = m_context.getDevice();
176 Allocator& allocator = m_context.getDefaultAllocator();
177 const VkQueue queue = m_context.getUniversalQueue();
178 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
179 const VkDeviceSize bufferSize = 16 * 1024;
180
181 const VkExtent2D renderSize = {deUint32(WIDTH), deUint32(HEIGHT)};
182 const VkRect2D renderArea = makeRect2D(makeExtent3D(WIDTH, HEIGHT, 1u));
183 const vector<VkRect2D> scissors (1u, renderArea);
184 const vector<VkViewport> viewports (1u, makeViewport(makeExtent3D(WIDTH, HEIGHT, 1u)));
185
186 const vector<Vec4> vertices = genFullQuadVertices();
187 Move<VkBuffer> vertexBuffer = makeVertexBuffer(vk, device, queueFamilyIndex);
188 MovePtr<Allocation> vertexBufferAlloc = 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 = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1, 0, 1);
196 const ImageWithMemory targetImage (vk, device, m_context.getDefaultAllocator(), targetCreateInfo, MemoryRequirement::Any);
197 Move<VkImageView> targetImageView = makeImageView(vk, device, *targetImage, VK_IMAGE_VIEW_TYPE_2D, FORMAT, targetSubresourceRange);
198
199 const Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
200 const Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
201
202 Move<VkRenderPass> renderPass = makeRenderPass(vk, device, FORMAT, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
203 Move<VkFramebuffer> framebuffer = makeFramebuffer(vk, device, *renderPass, targetImageView.get(), renderSize.width, renderSize.height);
204
205 const Move<VkShaderModule> vertexModule = createShaderModule (vk, device, m_context.getBinaryCollection().get("vert1"), 0u);
206 const Move<VkShaderModule> fragmentModule = createShaderModule (vk, device, m_context.getBinaryCollection().get("frag1"), 0u);
207
208 const Move<VkPipelineLayout> pipelineLayout = makePipelineLayout (vk, device, DE_NULL);
209
210 const VkPipelineColorBlendAttachmentState clrBlendAttachmentState =
211 {
212 VK_TRUE, // VkBool32 blendEnable;
213 VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcColorBlendFactor;
214 VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, // VkBlendFactor dstColorBlendFactor;
215 VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
216 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
217 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
218 VK_BLEND_OP_MAX, // VkBlendOp alphaBlendOp;
219 (VkColorComponentFlags)0xF // VkColorComponentFlags colorWriteMask;
220 };
221
222 const VkPipelineColorBlendStateCreateInfo clrBlendStateCreateInfo =
223 {
224 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
225 DE_NULL, // const void* pNext;
226 (VkPipelineColorBlendStateCreateFlags)0u, // VkPipelineColorBlendStateCreateFlags flags;
227 VK_FALSE, // VkBool32 logicOpEnable;
228 VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp;
229 1u, // deUint32 attachmentCount;
230 &clrBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
231 { 1.0f, 1.0f, 1.0f, 1.0f } // float blendConstants[4];
232 };
233
234 const VkVertexInputBindingDescription vtxBindingDescription = Vertex::getBindingDescription();
235 const auto vtxAttrDescriptions = Vertex::getAttributeDescriptions();
236
237 const VkPipelineVertexInputStateCreateInfo vtxInputStateCreateInfo =
238 {
239 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
240 DE_NULL, // const void* pNext;
241 (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
242 1u, // deUint32 vertexBindingDescriptionCount;
243 &vtxBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions
244 static_cast<deUint32>(vtxAttrDescriptions.size()), // deUint32 vertexAttributeDescriptionCount
245 vtxAttrDescriptions.data(), // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions
246 };
247
248 const Move<VkPipeline> graphicsPipeline = makeGraphicsPipeline(vk, device, pipelineLayout.get(), vertexModule.get(), DE_NULL, DE_NULL,
249 DE_NULL, fragmentModule.get(), renderPass.get(),
250 viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
251 0u, 0u, &vtxInputStateCreateInfo, DE_NULL,
252 DE_NULL, DE_NULL, &clrBlendStateCreateInfo);
253
254 const VkBufferCreateInfo resultBufferCreateInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
255 Move<VkBuffer> resultBuffer = createBuffer(vk, device, &resultBufferCreateInfo);
256 MovePtr<Allocation> resultBufferMemory = allocator.allocate(getBufferMemoryRequirements(vk, device, *resultBuffer), MemoryRequirement::HostVisible);
257 MovePtr<TextureLevel> resultImage (new TextureLevel(mapVkFormat(FORMAT), renderSize.width, renderSize.height, 1));
258
259 VK_CHECK(vk.bindBufferMemory(device, *resultBuffer, resultBufferMemory->getMemory(), resultBufferMemory->getOffset()));
260
261 const Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f);
262
263 clearColorImage(vk, device, m_context.getUniversalQueue(), m_context.getUniversalQueueFamilyIndex(),
264 targetImage.get(), clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
265 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 1);
266
267 beginCommandBuffer(vk, *cmdBuffer);
268
269 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
270 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
271
272 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(0, 0, WIDTH, HEIGHT), 0, DE_NULL);
273 vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(vertices.size()), 1u, 0u, 0u);
274 endRenderPass(vk, *cmdBuffer);
275
276 // Define an execution dependency and skip the layout transition. This is allowed when oldLayout
277 // and newLayout are both UNDEFINED. The test will fail if the driver discards the contents of
278 // the image.
279 const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2(
280 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags2KHR srcStageMask
281 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags2KHR srcAccessMask
282 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags2KHR dstStageMask
283 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, // VkAccessFlags2KHR dstAccessMask
284 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout
285 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout newLayout
286 targetImage.get(), // VkImage image
287 targetSubresourceRange // VkImageSubresourceRange subresourceRange
288 );
289 VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
290 #ifndef CTS_USES_VULKANSC
291 vk.cmdPipelineBarrier2(cmdBuffer.get(), &dependencyInfo);
292 #else
293 vk.cmdPipelineBarrier2KHR(cmdBuffer.get(), &dependencyInfo);
294 #endif // CTS_USES_VULKANSC
295
296 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(0, 0, WIDTH, HEIGHT), 0, DE_NULL);
297 vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(vertices.size()), 1u, 0u, 0u);
298 endRenderPass(vk, *cmdBuffer);
299
300 // Read the result buffer data
301 copyImageToBuffer(vk, *cmdBuffer, *targetImage, *resultBuffer, tcu::IVec2(WIDTH, HEIGHT), VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
302
303 endCommandBuffer(vk, *cmdBuffer);
304 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
305
306 invalidateAlloc(vk, device, *resultBufferMemory);
307
308 tcu::clear(resultImage->getAccess(), tcu::IVec4(0));
309 tcu::copy(resultImage->getAccess(), tcu::ConstPixelBufferAccess(resultImage.get()->getFormat(),
310 resultImage.get()->getSize(), resultBufferMemory->getHostPtr()));
311
312 TextureLevel textureLevel (mapVkFormat(FORMAT), WIDTH, HEIGHT, 1);
313 const tcu::PixelBufferAccess expectedImage = textureLevel.getAccess();
314
315 const float alpha = 0.4f;
316 const float red = (2.0f - alpha) * alpha;
317 const float green = red;
318 const float blue = 0;
319 const Vec4 color = Vec4(red, green, blue, alpha);
320
321 for (int y = 0; y < HEIGHT; y++)
322 for (int x = 0; x < WIDTH; x++)
323 expectedImage.setPixel(color, x, y, 0);
324
325 bool ok = tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Image comparison", "", expectedImage, resultImage->getAccess(), tcu::Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
326 return ok ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail");
327 }
328
329 class SynchronizationImageLayoutTransitionTest : public TestCase
330 {
331 public:
332 SynchronizationImageLayoutTransitionTest (tcu::TestContext& testCtx,
333 const std::string& name,
334 const std::string& description);
335
336 virtual void checkSupport (Context& context) const;
337 void initPrograms (SourceCollections& programCollection) const;
338 TestInstance* createInstance (Context& context) const;
339 };
340
SynchronizationImageLayoutTransitionTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description)341 SynchronizationImageLayoutTransitionTest::SynchronizationImageLayoutTransitionTest (tcu::TestContext& testCtx,
342 const std::string& name,
343 const std::string& description)
344 : TestCase (testCtx, name, description)
345 {
346 }
347
initPrograms(SourceCollections & programCollection) const348 void SynchronizationImageLayoutTransitionTest::initPrograms (SourceCollections& programCollection) const
349 {
350 std::ostringstream vertexSrc;
351 vertexSrc
352 << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
353 << "layout(location = 0) in vec4 a_position;\n"
354 << "void main (void) {\n"
355 << " gl_Position = a_position;\n"
356 << "}\n";
357
358 std::ostringstream fragmentSrc;
359 fragmentSrc
360 << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
361 << "layout(location = 0) out vec4 outColor;\n"
362 << "void main() {\n"
363 << " outColor = vec4(1., 1., 0., .4);\n"
364 << "}\n";
365
366 programCollection.glslSources.add("vert1") << glu::VertexSource(vertexSrc.str());
367 programCollection.glslSources.add("frag1") << glu::FragmentSource(fragmentSrc.str());
368 }
369
checkSupport(Context & context) const370 void SynchronizationImageLayoutTransitionTest::checkSupport (Context& context) const
371 {
372 context.requireDeviceFunctionality("VK_KHR_synchronization2");
373 }
374
createInstance(Context & context) const375 TestInstance* SynchronizationImageLayoutTransitionTest::createInstance (Context& context) const
376 {
377 return new SynchronizationImageLayoutTransitionTestInstance(context);
378 }
379
380 } // anonymous ns
381
createImageLayoutTransitionTests(tcu::TestContext & testCtx)382 tcu::TestCaseGroup* createImageLayoutTransitionTests (tcu::TestContext& testCtx)
383 {
384 de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "layout_transition", "No-op image layout transition tests"));
385 testGroup->addChild(new SynchronizationImageLayoutTransitionTest(testCtx, "no_op", ""));
386
387 return testGroup.release();
388 }
389
390 } // synchronization
391 } // vkt
392