1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2020 The Khronos Group Inc.
6 * Copyright (c) 2020 Valve Corporation.
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 vktImageSubresourceLayoutTests.cpp
22 * \brief Tests for vkGetImageSubresourceLayout
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTestCase.hpp"
26
27 #include "vkDefs.hpp"
28 #include "vkImageUtil.hpp"
29 #include "vkQueryUtil.hpp"
30 #include "vkObjUtil.hpp"
31 #include "vkBarrierUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkCmdUtil.hpp"
34 #include "vkStrUtil.hpp"
35 #include "vkBufferWithMemory.hpp"
36 #include "vkImageWithMemory.hpp"
37
38 #include "tcuTestLog.hpp"
39 #include "tcuTextureUtil.hpp"
40 #include "tcuFloat.hpp"
41
42 #include "deRandom.hpp"
43
44 #include <vector>
45 #include <sstream>
46 #include <limits>
47 #include <string>
48
49 using namespace vk;
50
51 namespace vkt
52 {
53 namespace image
54 {
55 namespace
56 {
57
58 // Helper class to calculate buffer sizes and offsets for image mipmap levels.
59 class BufferLevels
60 {
61 public:
62 struct Level
63 {
64 VkDeviceSize offset; // In bytes.
65 VkDeviceSize size; // In bytes.
66 VkExtent3D dimensions; // .depth will be the number of layers for 2D images and the depth for 3D images.
67 };
68
69 BufferLevels (VkImageType type, VkFormat format, VkExtent3D levelZero, deUint32 maxLevels, VkImageAspectFlags aspects = 0u);
70 VkDeviceSize totalSize () const;
71 VkDeviceSize pixelSize () const;
72 deUint32 numLevels () const;
73 const Level& getLevel (deUint32 level) const;
74
75 private:
76 VkDeviceSize m_pixelSize; // In bytes.
77 std::vector<Level> m_levels;
78 };
79
BufferLevels(VkImageType type,VkFormat format,VkExtent3D levelZero,deUint32 maxLevels,VkImageAspectFlags aspects)80 BufferLevels::BufferLevels (VkImageType type, VkFormat format, VkExtent3D levelZero, deUint32 maxLevels, VkImageAspectFlags aspects)
81 {
82 DE_ASSERT(type == VK_IMAGE_TYPE_2D || type == VK_IMAGE_TYPE_3D);
83 DE_ASSERT(maxLevels >= 1u);
84
85 const auto tcuFormat = vk::mapVkFormat(format);
86 const auto maxLevelsSz = static_cast<size_t>(maxLevels);
87
88 VkDeviceSize currentOffset = 0ull;
89 VkExtent3D nextExtent = levelZero;
90 deUint32 levelCount = 0;
91
92 if (!aspects || (aspects & VK_IMAGE_ASPECT_COLOR_BIT))
93 {
94 m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(tcuFormat));
95 }
96 else if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
97 {
98 const auto copyFormat = getDepthCopyFormat(format);
99 m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(copyFormat));
100 }
101 else if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
102 {
103 const auto copyFormat = getStencilCopyFormat(format);
104 m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(copyFormat));
105 }
106 else
107 DE_ASSERT(false);
108
109 while (m_levels.size() < maxLevelsSz)
110 {
111 Level level;
112
113 level.offset = currentOffset;
114 level.size = m_pixelSize * nextExtent.width * nextExtent.height * nextExtent.depth;
115 level.dimensions = nextExtent;
116
117 m_levels.push_back(level);
118
119 // This was the last available level.
120 if (nextExtent.width == 1u && nextExtent.height == 1u && (type == VK_IMAGE_TYPE_2D || nextExtent.depth == 1u))
121 break;
122
123 nextExtent.width = std::max(1u, (nextExtent.width / 2u));
124 nextExtent.height = std::max(1u, (nextExtent.height / 2u));
125
126 // 2D arrays all have the same array size.
127 if (type == VK_IMAGE_TYPE_3D)
128 nextExtent.depth = std::max(1u, (nextExtent.depth / 2u));
129
130 currentOffset += level.size;
131 ++levelCount;
132 }
133 }
134
totalSize() const135 VkDeviceSize BufferLevels::totalSize () const
136 {
137 VkDeviceSize total = 0ull;
138 std::for_each(begin(m_levels), end(m_levels), [&total] (const Level& l) { total += l.size; });
139 return total;
140 }
141
pixelSize() const142 VkDeviceSize BufferLevels::pixelSize () const
143 {
144 return m_pixelSize;
145 }
146
numLevels() const147 deUint32 BufferLevels::numLevels () const
148 {
149 return static_cast<deUint32>(m_levels.size());
150 }
151
getLevel(deUint32 level) const152 const BufferLevels::Level& BufferLevels::getLevel (deUint32 level) const
153 {
154 return m_levels.at(level);
155 }
156
157 // Default image dimensions. For 2D images, .depth indicates the number of layers.
getDefaultDimensions(VkImageType type,bool array)158 VkExtent3D getDefaultDimensions (VkImageType type, bool array)
159 {
160 DE_ASSERT(type == VK_IMAGE_TYPE_2D || type == VK_IMAGE_TYPE_3D);
161 DE_ASSERT(!array || type == VK_IMAGE_TYPE_2D);
162
163 constexpr VkExtent3D kDefault3D = { 32u, 48u, 56u };
164 constexpr VkExtent3D kDefault2DArray = kDefault3D;
165 constexpr VkExtent3D kDefault2D = { 240u, 320u, 1u };
166
167 if (type == VK_IMAGE_TYPE_3D)
168 return kDefault3D;
169 if (array)
170 return kDefault2DArray;
171 return kDefault2D;
172 }
173
174 class ImageSubresourceLayoutCase : public vkt::TestCase
175 {
176 public:
177 struct TestParams
178 {
179 VkImageType imageType;
180 VkFormat imageFormat;
181 VkExtent3D dimensions; // .depth will be the number of layers for 2D images and the depth for 3D images.
182 deUint32 mipLevels;
183 };
184
185 ImageSubresourceLayoutCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
~ImageSubresourceLayoutCase(void)186 virtual ~ImageSubresourceLayoutCase (void) {}
187
initPrograms(vk::SourceCollections &) const188 virtual void initPrograms (vk::SourceCollections&) const {}
189 virtual TestInstance* createInstance (Context& context) const;
190 virtual void checkSupport (Context& context) const;
191
192 static constexpr VkFormatFeatureFlags kRequiredFeatures = (VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT);
193 static constexpr VkImageUsageFlags kImageUsageFlags = (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
194 static constexpr VkImageTiling kImageTiling = VK_IMAGE_TILING_LINEAR;
195 private:
196 TestParams m_params;
197 };
198
199 class ImageSubresourceLayoutInstance : public vkt::TestInstance
200 {
201 public:
202 ImageSubresourceLayoutInstance (Context& context, const ImageSubresourceLayoutCase::TestParams& params);
~ImageSubresourceLayoutInstance(void)203 virtual ~ImageSubresourceLayoutInstance (void) {}
204
205 virtual tcu::TestStatus iterate (void);
206 tcu::TestStatus iterateAspect (VkImageAspectFlagBits aspect);
207 private:
208 ImageSubresourceLayoutCase::TestParams m_params;
209 };
210
ImageSubresourceLayoutCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)211 ImageSubresourceLayoutCase::ImageSubresourceLayoutCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
212 : vkt::TestCase (testCtx, name, description)
213 , m_params (params)
214 {
215 }
216
createInstance(Context & context) const217 TestInstance* ImageSubresourceLayoutCase::createInstance (Context& context) const
218 {
219 return new ImageSubresourceLayoutInstance (context, m_params);
220 }
221
checkSupport(Context & context) const222 void ImageSubresourceLayoutCase::checkSupport (Context& context) const
223 {
224 const auto& vki = context.getInstanceInterface();
225 const auto physicalDevice = context.getPhysicalDevice();
226
227 const auto formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, m_params.imageFormat);
228 if ((formatProperties.linearTilingFeatures & kRequiredFeatures) != kRequiredFeatures)
229 TCU_THROW(NotSupportedError, "Required format features not supported");
230
231 VkImageFormatProperties imgFormatProperties;
232 const auto result = vki.getPhysicalDeviceImageFormatProperties(physicalDevice, m_params.imageFormat, m_params.imageType, kImageTiling, kImageUsageFlags, 0u, &imgFormatProperties);
233 if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
234 TCU_THROW(NotSupportedError, "Linear tiling not supported for format");
235 VK_CHECK(result);
236
237 {
238 BufferLevels levels (m_params.imageType, m_params.imageFormat, m_params.dimensions, m_params.mipLevels);
239 if (imgFormatProperties.maxMipLevels < levels.numLevels())
240 TCU_THROW(NotSupportedError, "Required number of mip levels not supported for format");
241 }
242
243 if (m_params.imageType == VK_IMAGE_TYPE_2D && imgFormatProperties.maxArrayLayers < m_params.dimensions.depth)
244 TCU_THROW(NotSupportedError, "Required number of layers not supported for format");
245 }
246
ImageSubresourceLayoutInstance(Context & context,const ImageSubresourceLayoutCase::TestParams & params)247 ImageSubresourceLayoutInstance::ImageSubresourceLayoutInstance (Context& context, const ImageSubresourceLayoutCase::TestParams& params)
248 : vkt::TestInstance (context)
249 , m_params (params)
250 {
251 }
252
253 // Fills length bytes starting at location with pseudorandom data.
fillWithRandomData(de::Random & rnd,void * location,VkDeviceSize length)254 void fillWithRandomData (de::Random& rnd, void* location, VkDeviceSize length)
255 {
256 auto bytePtr = reinterpret_cast<unsigned char*>(location);
257 const auto endPtr = bytePtr + length;
258
259 while (bytePtr != endPtr)
260 {
261 const auto remaining = (endPtr - bytePtr);
262
263 if (remaining >= 8) { const auto data = rnd.getUint64(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); }
264 else if (remaining >= 4) { const auto data = rnd.getUint32(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); }
265 else if (remaining >= 2) { const auto data = rnd.getUint16(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); }
266 else { const auto data = rnd.getUint8(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); }
267 }
268 }
269
270 // Fills data in blocks of 32 bits, discarding the higher 8 bits of each block.
fillWithRandomData24In32(de::Random & rnd,void * location,VkDeviceSize length)271 void fillWithRandomData24In32 (de::Random& rnd, void* location, VkDeviceSize length)
272 {
273 static const auto blockSize = sizeof(deUint32);
274 DE_ASSERT(length % blockSize == 0);
275
276 auto dataPtr = reinterpret_cast<unsigned char*>(location);
277 const auto numBlocks = length / blockSize;
278
279 for (VkDeviceSize i = 0; i < numBlocks; ++i)
280 {
281 auto data = rnd.getUint32();
282 data &= 0xFFFFFFu; // Remove the higher 8 bits.
283 deMemcpy(dataPtr, &data, blockSize);
284 dataPtr += blockSize;
285 }
286 }
287
288 // Helpers to make fillWithRandomFloatingPoint a template. Returns normal numbers in the range [0, 1).
289 template <class T>
290 T getNormalFPValue (de::Random& rnd);
291
292 template<>
getNormalFPValue(de::Random & rnd)293 float getNormalFPValue<float> (de::Random& rnd)
294 {
295 float value;
296 do {
297 value = rnd.getFloat();
298 } while (tcu::Float32(value).isDenorm());
299 return value;
300 }
301
302 template<>
getNormalFPValue(de::Random & rnd)303 double getNormalFPValue<double> (de::Random& rnd)
304 {
305 double value;
306 do {
307 value = rnd.getDouble();
308 } while (tcu::Float64(value).isDenorm());
309 return value;
310 }
311
312 template<>
getNormalFPValue(de::Random & rnd)313 tcu::Float16 getNormalFPValue<tcu::Float16> (de::Random& rnd)
314 {
315 tcu::Float16 value;
316 do {
317 value = tcu::Float16(rnd.getFloat());
318 } while (value.isDenorm());
319 return value;
320 }
321
322 template <class T>
fillWithRandomFloatingPoint(de::Random & rnd,void * location,VkDeviceSize length)323 void fillWithRandomFloatingPoint (de::Random& rnd, void* location, VkDeviceSize length)
324 {
325 static const auto typeSize = sizeof(T);
326
327 DE_ASSERT(length % typeSize == 0);
328
329 const auto numElements = length / typeSize;
330 auto elemPtr = reinterpret_cast<unsigned char*>(location);
331 T elem;
332
333 for (VkDeviceSize i = 0; i < numElements; ++i)
334 {
335 elem = getNormalFPValue<T>(rnd);
336 deMemcpy(elemPtr, &elem, typeSize);
337 elemPtr += typeSize;
338 }
339 }
340
iterate(void)341 tcu::TestStatus ImageSubresourceLayoutInstance::iterate (void)
342 {
343 // Test every aspect supported by the image format.
344 const auto tcuFormat = mapVkFormat(m_params.imageFormat);
345 const auto aspectFlags = getImageAspectFlags(tcuFormat);
346
347 static const VkImageAspectFlagBits aspectBits[] =
348 {
349 VK_IMAGE_ASPECT_COLOR_BIT,
350 VK_IMAGE_ASPECT_DEPTH_BIT,
351 VK_IMAGE_ASPECT_STENCIL_BIT,
352 };
353
354 for (int i = 0; i < DE_LENGTH_OF_ARRAY(aspectBits); ++i)
355 {
356 const auto bit = aspectBits[i];
357 if (aspectFlags & bit)
358 {
359 const auto aspectResult = iterateAspect(bit);
360 if (aspectResult.getCode() != QP_TEST_RESULT_PASS)
361 return aspectResult; // Early return for failures.
362 }
363 }
364
365 return tcu::TestStatus::pass("Pass");
366 }
367
iterateAspect(VkImageAspectFlagBits imageAspect)368 tcu::TestStatus ImageSubresourceLayoutInstance::iterateAspect (VkImageAspectFlagBits imageAspect)
369 {
370 // * Create linear image with several mipmaps
371 // * Fill its levels with unique appropriate data (avoiding invalid sfloat values, for example).
372 // * Ask for the subresource layout parameters.
373 // * Verify they make sense.
374 // * Check accessing data with the given parameters gives back the original data.
375
376 const auto& vkd = m_context.getDeviceInterface();
377 const auto device = m_context.getDevice();
378 auto& alloc = m_context.getDefaultAllocator();
379 const auto queue = m_context.getUniversalQueue();
380 const auto queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
381 auto& log = m_context.getTestContext().getLog();
382
383 log << tcu::TestLog::Message << "Testing aspect " << imageAspect << tcu::TestLog::EndMessage;
384
385 // Get an idea of the buffer size and parameters to prepare image data.
386 const BufferLevels bufferLevels (m_params.imageType, m_params.imageFormat, m_params.dimensions, m_params.mipLevels, imageAspect);
387 const auto pixelSize = bufferLevels.pixelSize();
388 const auto pixelSizeSz = static_cast<size_t>(pixelSize);
389 const auto numLevels = bufferLevels.numLevels();
390
391 // Create source buffer.
392 const auto bufferSize = bufferLevels.totalSize();
393 const auto bufferInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
394 BufferWithMemory buffer (vkd, device, alloc, bufferInfo, MemoryRequirement::HostVisible);
395 auto& bufferAlloc = buffer.getAllocation();
396 auto* bufferPtr = reinterpret_cast<unsigned char*>(bufferAlloc.getHostPtr());
397
398 // Fill buffer with random appropriate data.
399 const deUint32 randomSeed = 1594055758u + static_cast<deUint32>(m_params.imageFormat) + static_cast<deUint32>(imageAspect);
400 de::Random rnd (randomSeed);
401 const auto tcuFormat = mapVkFormat(m_params.imageFormat);
402 // For some formats, the copy block is 32 bits wide but the 8 MSB need to be ignored, so we zero them out.
403 const bool use24LSB = ((m_params.imageFormat == VK_FORMAT_X8_D24_UNORM_PACK32 || m_params.imageFormat == VK_FORMAT_D24_UNORM_S8_UINT) && imageAspect == VK_IMAGE_ASPECT_DEPTH_BIT);
404
405 if (tcuFormat.type == tcu::TextureFormat::FLOAT || (m_params.imageFormat == VK_FORMAT_D32_SFLOAT_S8_UINT && imageAspect == VK_IMAGE_ASPECT_DEPTH_BIT))
406 fillWithRandomFloatingPoint<float>(rnd, bufferPtr, bufferSize);
407 else if (tcuFormat.type == tcu::TextureFormat::FLOAT64)
408 fillWithRandomFloatingPoint<double>(rnd, bufferPtr, bufferSize);
409 else if (tcuFormat.type == tcu::TextureFormat::HALF_FLOAT)
410 fillWithRandomFloatingPoint<tcu::Float16>(rnd, bufferPtr, bufferSize);
411 else if (use24LSB)
412 fillWithRandomData24In32(rnd, bufferPtr, bufferSize);
413 else
414 fillWithRandomData(rnd, bufferPtr, bufferSize);
415
416 flushAlloc(vkd, device, bufferAlloc);
417
418 // Reinterpret the depth dimension parameter as the number of layers if needed.
419 const auto numLayers = ((m_params.imageType == VK_IMAGE_TYPE_3D) ? 1u : m_params.dimensions.depth);
420 VkExtent3D imageExtent = m_params.dimensions;
421 if (m_params.imageType == VK_IMAGE_TYPE_2D)
422 imageExtent.depth = 1u;
423
424 // Create image.
425 const VkImageCreateInfo imageInfo =
426 {
427 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
428 nullptr, // const void* pNext;
429 0u, // VkImageCreateFlags flags;
430 m_params.imageType, // VkImageType imageType;
431 m_params.imageFormat, // VkFormat format;
432 imageExtent, // VkExtent3D extent;
433 numLevels, // deUint32 mipLevels;
434 numLayers, // deUint32 arrayLayers;
435 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
436 ImageSubresourceLayoutCase::kImageTiling, // VkImageTiling tiling;
437 ImageSubresourceLayoutCase::kImageUsageFlags, // VkImageUsageFlags usage;
438 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
439 0u, // deUint32 queueFamilyIndexCount;
440 nullptr, // const deUint32* pQueueFamilyIndices;
441 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
442 };
443 ImageWithMemory image (vkd, device, alloc, imageInfo, MemoryRequirement::HostVisible);
444 auto& imageAlloc = image.getAllocation();
445 auto* imagePtr = reinterpret_cast<unsigned char*>(imageAlloc.getHostPtr());
446
447 // Copy regions.
448 std::vector<VkBufferImageCopy> copyRegions;
449 copyRegions.reserve(numLevels);
450
451 for (deUint32 levelNdx = 0u; levelNdx < numLevels; ++levelNdx)
452 {
453 const auto& level = bufferLevels.getLevel(levelNdx);
454 auto levelExtent = level.dimensions;
455
456 if (m_params.imageType == VK_IMAGE_TYPE_2D)
457 levelExtent.depth = 1u; // For 2D images, .depth indicates the number of layers.
458
459 VkBufferImageCopy region;
460 region.bufferOffset = level.offset;
461 region.bufferRowLength = 0u; // Tightly packed data.
462 region.bufferImageHeight = 0u; // Ditto.
463 region.imageSubresource.aspectMask = imageAspect;
464 region.imageSubresource.baseArrayLayer = 0u;
465 region.imageSubresource.layerCount = numLayers;
466 region.imageSubresource.mipLevel = levelNdx;
467 region.imageOffset = { 0, 0, 0 };
468 region.imageExtent = levelExtent;
469
470 copyRegions.push_back(region);
471 }
472
473 // Image layout transitions.
474 const auto imageSubresourceRange = makeImageSubresourceRange(imageAspect, 0u, numLevels, 0u, numLayers);
475 const auto initialLayoutBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image.get(), imageSubresourceRange);
476 const auto finalLayoutBarrier = makeImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, image.get(), imageSubresourceRange);
477
478 // Command buffer.
479 const auto cmdPool = makeCommandPool(vkd, device, queueFamilyIndex);
480 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
481 const auto cmdBuffer = cmdBufferPtr.get();
482
483 // Transition layout, copy, transition layout.
484 beginCommandBuffer(vkd, cmdBuffer);
485 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &initialLayoutBarrier);
486 vkd.cmdCopyBufferToImage(cmdBuffer, buffer.get(), image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<deUint32>(copyRegions.size()), copyRegions.data());
487 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &finalLayoutBarrier);
488 endCommandBuffer(vkd, cmdBuffer);
489 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
490
491 // Sync image memory for host access.
492 invalidateAlloc(vkd, device, imageAlloc);
493
494 VkSubresourceLayout levelSubresourceLayout;
495 VkSubresourceLayout subresourceLayout;
496 for (deUint32 levelNdx = 0u; levelNdx < numLevels; ++levelNdx)
497 {
498 // Get base level subresource.
499 const auto levelSubresource = makeImageSubresource(imageAspect, levelNdx, 0u);
500 vkd.getImageSubresourceLayout(device, image.get(), &levelSubresource, &levelSubresourceLayout);
501
502 const auto& level = bufferLevels.getLevel(levelNdx);
503 for (deUint32 layerNdx = 0; layerNdx < numLayers; ++layerNdx)
504 {
505 const auto imageSubresource = makeImageSubresource(imageAspect, levelNdx, layerNdx);
506 vkd.getImageSubresourceLayout(device, image.get(), &imageSubresource, &subresourceLayout);
507
508 // Verify returned values.
509 const auto subresourceWidth = level.dimensions.width;
510 const auto subresourceHeight = level.dimensions.height;
511 const auto subresourceDepth = ((m_params.imageType == VK_IMAGE_TYPE_2D) ? 1u : level.dimensions.depth);
512 const auto numPixels = subresourceWidth * subresourceHeight * subresourceDepth;
513
514 if (numLayers > 1u && levelSubresourceLayout.arrayPitch != subresourceLayout.arrayPitch)
515 {
516 // Inconsistent arrayPitch.
517 std::ostringstream msg;
518 msg << "Image level " << levelNdx
519 << " layer " << layerNdx
520 << " reports array pitch of " << subresourceLayout.arrayPitch << " bytes in size"
521 << " with base layer reporting array pitch of " << levelSubresourceLayout.arrayPitch << " bytes in size";
522 return tcu::TestStatus::fail(msg.str());
523 }
524
525 if ((subresourceLayout.offset - levelSubresourceLayout.offset) != (layerNdx * subresourceLayout.arrayPitch))
526 {
527 // Inconsistent offset.
528 std::ostringstream msg;
529 msg << "Image level " << levelNdx
530 << " layer " << layerNdx
531 << " has offset inconsistent with array pitch: base offset " << levelSubresourceLayout.offset
532 << ", layer offset " << subresourceLayout.offset
533 << ", array pitch " << subresourceLayout.arrayPitch;
534 return tcu::TestStatus::fail(msg.str());
535 }
536
537 if (subresourceLayout.size < pixelSize * numPixels)
538 {
539 // Subresource size too small.
540 std::ostringstream msg;
541 msg << "Image level " << levelNdx
542 << " layer " << layerNdx
543 << " reports " << subresourceLayout.size << " bytes in size"
544 << " with pixel size " << pixelSize
545 << " and dimensions " << subresourceWidth << "x" << subresourceHeight << "x" << subresourceDepth;
546 return tcu::TestStatus::fail(msg.str());
547 }
548
549 // Note: if subresourceHeight is <= 1u, rowPitch can be zero.
550 if (subresourceHeight > 1u && subresourceLayout.rowPitch < pixelSize * subresourceWidth)
551 {
552 // Row pitch too small.
553 std::ostringstream msg;
554 msg << "Image level " << levelNdx
555 << " layer " << layerNdx
556 << " reports row pitch of " << subresourceLayout.rowPitch
557 << " bytes with " << pixelSize
558 << " bytes in pixel size and width " << subresourceWidth;
559 return tcu::TestStatus::fail(msg.str());
560 }
561
562 if (numLayers > 1u && subresourceLayout.arrayPitch < pixelSize * numPixels)
563 {
564 // Array pitch too small.
565 std::ostringstream msg;
566 msg << "Image level " << levelNdx
567 << " layer " << layerNdx
568 << " reports array pitch of " << subresourceLayout.arrayPitch
569 << " bytes with " << pixelSize
570 << " bytes in pixel size and layer dimensions " << subresourceWidth << "x" << subresourceHeight;
571 return tcu::TestStatus::fail(msg.str());
572 }
573
574 // If subresourceDepth is <= 1u, depthPitch can be zero.
575 if (subresourceDepth > 1u && m_params.imageType == VK_IMAGE_TYPE_3D && subresourceLayout.depthPitch < pixelSize * subresourceWidth * subresourceHeight)
576 {
577 // Depth pitch too small.
578 std::ostringstream msg;
579 msg << "Image level " << levelNdx
580 << " layer " << layerNdx
581 << " reports depth pitch of " << subresourceLayout.depthPitch << " bytes"
582 << " with pixel size " << pixelSize
583 << " and dimensions " << subresourceWidth << "x" << subresourceHeight << "x" << subresourceDepth;
584 return tcu::TestStatus::fail(msg.str());
585 }
586
587 // Verify image data.
588 const auto layerBufferOffset = level.offset + layerNdx * numPixels * pixelSize;
589 const auto layerImageOffset = subresourceLayout.offset;
590 const auto layerBufferPtr = bufferPtr + layerBufferOffset;
591 const auto layerImagePtr = imagePtr + layerImageOffset;
592 bool pixelMatch;
593
594 // We could do this row by row to be faster, but in the use24LSB case we need to manipulate pixels independently.
595 for (deUint32 x = 0u; x < subresourceWidth; ++x)
596 for (deUint32 y = 0u; y < subresourceHeight; ++y)
597 for (deUint32 z = 0u; z < subresourceDepth; ++z)
598 {
599 const auto bufferPixelOffset = (z * subresourceWidth * subresourceHeight + y * subresourceWidth + x) * pixelSize;
600 const auto imagePixelOffset = z * subresourceLayout.depthPitch + y * subresourceLayout.rowPitch + x * pixelSize;
601 const auto bufferPixelPtr = layerBufferPtr + bufferPixelOffset;
602 const auto imagePixelPtr = layerImagePtr + imagePixelOffset;
603
604 if (use24LSB)
605 {
606 DE_ASSERT(pixelSize == sizeof(deUint32));
607 deUint32 pixelValue;
608 deMemcpy(&pixelValue, imagePixelPtr, pixelSizeSz);
609 pixelValue &= 0xFFFFFFu; // Discard the 8 MSB.
610 pixelMatch = (deMemCmp(bufferPixelPtr, &pixelValue, pixelSizeSz) == 0);
611 }
612 else
613 pixelMatch = (deMemCmp(bufferPixelPtr, imagePixelPtr, pixelSizeSz) == 0);
614
615 if (!pixelMatch)
616 {
617 std::ostringstream msg;
618 msg << "Found difference from image pixel to buffer pixel at coordinates"
619 << " level=" << levelNdx
620 << " layer=" << layerNdx
621 << " x=" << x
622 << " y=" << y
623 << " z=" << z
624 ;
625 return tcu::TestStatus::fail(msg.str());
626 }
627 }
628 }
629 }
630
631 return tcu::TestStatus::pass("Pass");
632 }
633
634 } // anonymous namespace
635
createImageSubresourceLayoutTests(tcu::TestContext & testCtx)636 tcu::TestCaseGroup* createImageSubresourceLayoutTests (tcu::TestContext& testCtx)
637 {
638 de::MovePtr<tcu::TestCaseGroup> layoutTestGroup (new tcu::TestCaseGroup(testCtx, "subresource_layout", "Tests for vkGetImageSubresourceLayout"));
639
640 struct
641 {
642 VkImageType type;
643 bool array;
644 const char* name;
645 const char* desc;
646 } imageClass[] =
647 {
648 { VK_IMAGE_TYPE_2D, false, "2d", "2D images" },
649 { VK_IMAGE_TYPE_2D, true, "2d_array", "2D images with multiple layers" },
650 { VK_IMAGE_TYPE_3D, false, "3d", "3D images" },
651 };
652
653 struct
654 {
655 deUint32 maxLevels;
656 const char* name;
657 const char* desc;
658 } mipLevels[] =
659 {
660 { 1u, "1_level", "Single mip level" },
661 { 2u, "2_levels", "Two mip levels" },
662 { 4u, "4_levels", "Four mip levels" },
663 { std::numeric_limits<deUint32>::max(), "all_levels", "All possible levels" },
664 };
665
666 VkFormat testFormats[] =
667 {
668 VK_FORMAT_R4G4_UNORM_PACK8,
669 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
670 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
671 VK_FORMAT_R5G6B5_UNORM_PACK16,
672 VK_FORMAT_B5G6R5_UNORM_PACK16,
673 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
674 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
675 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
676 VK_FORMAT_R8_UNORM,
677 VK_FORMAT_R8_SNORM,
678 VK_FORMAT_R8_USCALED,
679 VK_FORMAT_R8_SSCALED,
680 VK_FORMAT_R8_UINT,
681 VK_FORMAT_R8_SINT,
682 VK_FORMAT_R8_SRGB,
683 VK_FORMAT_R8G8_UNORM,
684 VK_FORMAT_R8G8_SNORM,
685 VK_FORMAT_R8G8_USCALED,
686 VK_FORMAT_R8G8_SSCALED,
687 VK_FORMAT_R8G8_UINT,
688 VK_FORMAT_R8G8_SINT,
689 VK_FORMAT_R8G8_SRGB,
690 VK_FORMAT_R8G8B8_UNORM,
691 VK_FORMAT_R8G8B8_SNORM,
692 VK_FORMAT_R8G8B8_USCALED,
693 VK_FORMAT_R8G8B8_SSCALED,
694 VK_FORMAT_R8G8B8_UINT,
695 VK_FORMAT_R8G8B8_SINT,
696 VK_FORMAT_R8G8B8_SRGB,
697 VK_FORMAT_B8G8R8_UNORM,
698 VK_FORMAT_B8G8R8_SNORM,
699 VK_FORMAT_B8G8R8_USCALED,
700 VK_FORMAT_B8G8R8_SSCALED,
701 VK_FORMAT_B8G8R8_UINT,
702 VK_FORMAT_B8G8R8_SINT,
703 VK_FORMAT_B8G8R8_SRGB,
704 VK_FORMAT_R8G8B8A8_UNORM,
705 VK_FORMAT_R8G8B8A8_SNORM,
706 VK_FORMAT_R8G8B8A8_USCALED,
707 VK_FORMAT_R8G8B8A8_SSCALED,
708 VK_FORMAT_R8G8B8A8_UINT,
709 VK_FORMAT_R8G8B8A8_SINT,
710 VK_FORMAT_R8G8B8A8_SRGB,
711 VK_FORMAT_B8G8R8A8_UNORM,
712 VK_FORMAT_B8G8R8A8_SNORM,
713 VK_FORMAT_B8G8R8A8_USCALED,
714 VK_FORMAT_B8G8R8A8_SSCALED,
715 VK_FORMAT_B8G8R8A8_UINT,
716 VK_FORMAT_B8G8R8A8_SINT,
717 VK_FORMAT_B8G8R8A8_SRGB,
718 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
719 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
720 VK_FORMAT_A8B8G8R8_USCALED_PACK32,
721 VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
722 VK_FORMAT_A8B8G8R8_UINT_PACK32,
723 VK_FORMAT_A8B8G8R8_SINT_PACK32,
724 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
725 VK_FORMAT_A2R10G10B10_UNORM_PACK32,
726 VK_FORMAT_A2R10G10B10_SNORM_PACK32,
727 VK_FORMAT_A2R10G10B10_USCALED_PACK32,
728 VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
729 VK_FORMAT_A2R10G10B10_UINT_PACK32,
730 VK_FORMAT_A2R10G10B10_SINT_PACK32,
731 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
732 VK_FORMAT_A2B10G10R10_SNORM_PACK32,
733 VK_FORMAT_A2B10G10R10_USCALED_PACK32,
734 VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
735 VK_FORMAT_A2B10G10R10_UINT_PACK32,
736 VK_FORMAT_A2B10G10R10_SINT_PACK32,
737 VK_FORMAT_R16_UNORM,
738 VK_FORMAT_R16_SNORM,
739 VK_FORMAT_R16_USCALED,
740 VK_FORMAT_R16_SSCALED,
741 VK_FORMAT_R16_UINT,
742 VK_FORMAT_R16_SINT,
743 VK_FORMAT_R16_SFLOAT,
744 VK_FORMAT_R16G16_UNORM,
745 VK_FORMAT_R16G16_SNORM,
746 VK_FORMAT_R16G16_USCALED,
747 VK_FORMAT_R16G16_SSCALED,
748 VK_FORMAT_R16G16_UINT,
749 VK_FORMAT_R16G16_SINT,
750 VK_FORMAT_R16G16_SFLOAT,
751 VK_FORMAT_R16G16B16_UNORM,
752 VK_FORMAT_R16G16B16_SNORM,
753 VK_FORMAT_R16G16B16_USCALED,
754 VK_FORMAT_R16G16B16_SSCALED,
755 VK_FORMAT_R16G16B16_UINT,
756 VK_FORMAT_R16G16B16_SINT,
757 VK_FORMAT_R16G16B16_SFLOAT,
758 VK_FORMAT_R16G16B16A16_UNORM,
759 VK_FORMAT_R16G16B16A16_SNORM,
760 VK_FORMAT_R16G16B16A16_USCALED,
761 VK_FORMAT_R16G16B16A16_SSCALED,
762 VK_FORMAT_R16G16B16A16_UINT,
763 VK_FORMAT_R16G16B16A16_SINT,
764 VK_FORMAT_R16G16B16A16_SFLOAT,
765 VK_FORMAT_R32_UINT,
766 VK_FORMAT_R32_SINT,
767 VK_FORMAT_R32_SFLOAT,
768 VK_FORMAT_R32G32_UINT,
769 VK_FORMAT_R32G32_SINT,
770 VK_FORMAT_R32G32_SFLOAT,
771 VK_FORMAT_R32G32B32_UINT,
772 VK_FORMAT_R32G32B32_SINT,
773 VK_FORMAT_R32G32B32_SFLOAT,
774 VK_FORMAT_R32G32B32A32_UINT,
775 VK_FORMAT_R32G32B32A32_SINT,
776 VK_FORMAT_R32G32B32A32_SFLOAT,
777 VK_FORMAT_R64_UINT,
778 VK_FORMAT_R64_SINT,
779 VK_FORMAT_R64_SFLOAT,
780 VK_FORMAT_R64G64_UINT,
781 VK_FORMAT_R64G64_SINT,
782 VK_FORMAT_R64G64_SFLOAT,
783 VK_FORMAT_R64G64B64_UINT,
784 VK_FORMAT_R64G64B64_SINT,
785 VK_FORMAT_R64G64B64_SFLOAT,
786 VK_FORMAT_R64G64B64A64_UINT,
787 VK_FORMAT_R64G64B64A64_SINT,
788 VK_FORMAT_R64G64B64A64_SFLOAT,
789 // Leaving out depth/stencil formats due to this part of the spec:
790 //
791 // "Depth/stencil formats are considered opaque and need not be stored in the exact number of bits per texel or component
792 // ordering indicated by the format enum. However, implementations must not substitute a different depth or stencil
793 // precision than that described in the format (e.g. D16 must not be implemented as D24 or D32)."
794 //
795 // Which means the size of the texel is not known for depth/stencil formats and we cannot iterate over them to check their
796 // values.
797 #if 0
798 VK_FORMAT_D16_UNORM,
799 VK_FORMAT_X8_D24_UNORM_PACK32,
800 VK_FORMAT_D32_SFLOAT,
801 VK_FORMAT_S8_UINT,
802 VK_FORMAT_D16_UNORM_S8_UINT,
803 VK_FORMAT_D24_UNORM_S8_UINT,
804 VK_FORMAT_D32_SFLOAT_S8_UINT,
805 #endif
806 };
807
808 for (int classIdx = 0; classIdx < DE_LENGTH_OF_ARRAY(imageClass); ++classIdx)
809 {
810 const auto& imgClass = imageClass[classIdx];
811 de::MovePtr<tcu::TestCaseGroup> classGroup (new tcu::TestCaseGroup(testCtx, imgClass.name, imgClass.desc));
812
813 for (int mipIdx = 0; mipIdx < DE_LENGTH_OF_ARRAY(mipLevels); ++mipIdx)
814 {
815 const auto &mipLevel = mipLevels[mipIdx];
816 de::MovePtr<tcu::TestCaseGroup> mipGroup (new tcu::TestCaseGroup(testCtx, mipLevel.name, mipLevel.desc));
817
818 for (int formatIdx = 0; formatIdx < DE_LENGTH_OF_ARRAY(testFormats); ++formatIdx)
819 {
820 static const auto prefixLen = std::string("VK_FORMAT_").size();
821 const auto format = testFormats[formatIdx];
822 const auto fmtName = std::string(getFormatName(format));
823 const auto name = de::toLower(fmtName.substr(prefixLen)); // Remove VK_FORMAT_ prefix.
824 const auto desc = "Using format " + fmtName;
825
826 ImageSubresourceLayoutCase::TestParams params;
827 params.imageFormat = format;
828 params.imageType = imgClass.type;
829 params.mipLevels = mipLevel.maxLevels;
830 params.dimensions = getDefaultDimensions(imgClass.type, imgClass.array);
831
832 mipGroup->addChild(new ImageSubresourceLayoutCase(testCtx, name, desc, params));
833 }
834
835 classGroup->addChild(mipGroup.release());
836 }
837
838 layoutTestGroup->addChild(classGroup.release());
839 }
840
841 return layoutTestGroup.release();
842 }
843
844 } // namespace image
845 } // namespace vkt
846