1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2017 Google 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 Testing compute shader writing to separate planes of a multiplanar format
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktYCbCrStorageImageWriteTests.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vktTestGroupUtil.hpp"
27 #include "vktYCbCrUtil.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkCmdUtil.hpp"
31 #include "vkBarrierUtil.hpp"
32 #include "vkImageUtil.hpp"
33 #include "tcuTexVerifierUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkRefUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "tcuTestLog.hpp"
38
39 namespace vkt
40 {
41 namespace ycbcr
42 {
43 namespace
44 {
45
46 using namespace vk;
47
48 struct TestParameters
49 {
50 VkFormat format;
51 tcu::UVec3 size;
52 VkImageCreateFlags flags;
53
TestParametersvkt::ycbcr::__anon1aedeedf0111::TestParameters54 TestParameters (VkFormat format_,
55 const tcu::UVec3& size_,
56 VkImageCreateFlags flags_)
57 : format (format_)
58 , size (size_)
59 , flags (flags_)
60 {
61 }
62
TestParametersvkt::ycbcr::__anon1aedeedf0111::TestParameters63 TestParameters (void)
64 : format (VK_FORMAT_UNDEFINED)
65 , flags (0u)
66 {
67 }
68 };
69
checkSupport(Context & context,const TestParameters params)70 void checkSupport (Context& context, const TestParameters params)
71 {
72 const bool disjoint = (params.flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0;
73 std::vector<std::string> reqExts;
74
75 if (disjoint)
76 {
77 if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_bind_memory2"))
78 reqExts.push_back("VK_KHR_bind_memory2");
79 if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_get_memory_requirements2"))
80 reqExts.push_back("VK_KHR_get_memory_requirements2");
81 }
82
83 for ( const auto& extIter : reqExts )
84 {
85 if (!context.isDeviceFunctionalitySupported(extIter))
86 TCU_THROW(NotSupportedError, (extIter + " is not supported").c_str());
87 }
88
89 {
90 const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(),
91 context.getPhysicalDevice(),
92 params.format);
93
94 if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
95 TCU_THROW(NotSupportedError, "Storage images are not supported for this format");
96
97 if (disjoint && ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT) == 0))
98 TCU_THROW(NotSupportedError, "Disjoint planes are not supported for this format");
99 }
100 }
101
102 template<typename T>
makeVkSharedPtr(vk::Move<T> vkMove)103 inline de::SharedPtr<vk::Unique<T> > makeVkSharedPtr(vk::Move<T> vkMove)
104 {
105 return de::SharedPtr<vk::Unique<T> >(new vk::Unique<T>(vkMove));
106 }
107
computeWorkGroupSize(const VkExtent3D & planeExtent)108 tcu::UVec3 computeWorkGroupSize(const VkExtent3D& planeExtent)
109 {
110 const deUint32 maxComputeWorkGroupInvocations = 128u;
111 const tcu::UVec3 maxComputeWorkGroupSize = tcu::UVec3(128u, 128u, 64u);
112
113 const deUint32 xWorkGroupSize = std::min(std::min(planeExtent.width, maxComputeWorkGroupSize.x()), maxComputeWorkGroupInvocations);
114 const deUint32 yWorkGroupSize = std::min(std::min(planeExtent.height, maxComputeWorkGroupSize.y()), maxComputeWorkGroupInvocations / xWorkGroupSize);
115 const deUint32 zWorkGroupSize = std::min(std::min(planeExtent.depth, maxComputeWorkGroupSize.z()), maxComputeWorkGroupInvocations / (xWorkGroupSize*yWorkGroupSize));
116
117 return tcu::UVec3(xWorkGroupSize, yWorkGroupSize, zWorkGroupSize);
118 }
119
makeComputePipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkShaderModule shaderModule,const VkSpecializationInfo * specializationInfo)120 Move<VkPipeline> makeComputePipeline (const DeviceInterface& vk,
121 const VkDevice device,
122 const VkPipelineLayout pipelineLayout,
123 const VkShaderModule shaderModule,
124 const VkSpecializationInfo* specializationInfo)
125 {
126 const VkPipelineShaderStageCreateInfo pipelineShaderStageParams =
127 {
128 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
129 DE_NULL, // const void* pNext;
130 0u, // VkPipelineShaderStageCreateFlags flags;
131 VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
132 shaderModule, // VkShaderModule module;
133 "main", // const char* pName;
134 specializationInfo, // const VkSpecializationInfo* pSpecializationInfo;
135 };
136 const VkComputePipelineCreateInfo pipelineCreateInfo =
137 {
138 VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
139 DE_NULL, // const void* pNext;
140 0u, // VkPipelineCreateFlags flags;
141 pipelineShaderStageParams, // VkPipelineShaderStageCreateInfo stage;
142 pipelineLayout, // VkPipelineLayout layout;
143 DE_NULL, // VkPipeline basePipelineHandle;
144 0, // deInt32 basePipelineIndex;
145 };
146 return createComputePipeline(vk, device, DE_NULL , &pipelineCreateInfo);
147 }
148
getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription & formatInfo,deUint32 planeNdx)149 vk::VkFormat getPlaneCompatibleFormatForWriting(const vk::PlanarFormatDescription& formatInfo, deUint32 planeNdx)
150 {
151 DE_ASSERT(planeNdx < formatInfo.numPlanes);
152 vk::VkFormat result = formatInfo.planes[planeNdx].planeCompatibleFormat;
153
154 // redirect result for some of the YCbCr image formats
155 static const std::pair<vk::VkFormat, vk::VkFormat> ycbcrFormats[] =
156 {
157 { VK_FORMAT_G8B8G8R8_422_UNORM_KHR, VK_FORMAT_R8G8B8A8_UNORM },
158 { VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR, VK_FORMAT_R16G16B16A16_UNORM },
159 { VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR, VK_FORMAT_R16G16B16A16_UNORM },
160 { VK_FORMAT_G16B16G16R16_422_UNORM_KHR, VK_FORMAT_R16G16B16A16_UNORM },
161 { VK_FORMAT_B8G8R8G8_422_UNORM_KHR, VK_FORMAT_R8G8B8A8_UNORM },
162 { VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR, VK_FORMAT_R16G16B16A16_UNORM },
163 { VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR, VK_FORMAT_R16G16B16A16_UNORM },
164 { VK_FORMAT_B16G16R16G16_422_UNORM_KHR, VK_FORMAT_R16G16B16A16_UNORM }
165 };
166 auto it = std::find_if(std::begin(ycbcrFormats), std::end(ycbcrFormats), [result](const std::pair<vk::VkFormat, vk::VkFormat>& p) { return p.first == result; });
167 if (it != std::end(ycbcrFormats))
168 result = it->second;
169 return result;
170 }
171
testStorageImageWrite(Context & context,TestParameters params)172 tcu::TestStatus testStorageImageWrite (Context& context, TestParameters params)
173 {
174 const DeviceInterface& vkd = context.getDeviceInterface();
175 const VkDevice device = context.getDevice();
176 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
177 const VkQueue queue = context.getUniversalQueue();
178 const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
179
180 VkImageCreateInfo imageCreateInfo =
181 {
182 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
183 DE_NULL,
184 params.flags,
185 VK_IMAGE_TYPE_2D,
186 params.format,
187 makeExtent3D(params.size.x(), params.size.y(), params.size.z()),
188 1u, // mipLevels
189 1u, // arrayLayers
190 VK_SAMPLE_COUNT_1_BIT,
191 VK_IMAGE_TILING_OPTIMAL,
192 VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
193 VK_SHARING_MODE_EXCLUSIVE,
194 0u,
195 (const deUint32*)DE_NULL,
196 VK_IMAGE_LAYOUT_UNDEFINED,
197 };
198
199 // check if we need to create VkImageView with different VkFormat than VkImage format
200 VkFormat planeCompatibleFormat0 = getPlaneCompatibleFormatForWriting(formatDescription, 0);
201 if (planeCompatibleFormat0 != getPlaneCompatibleFormat(formatDescription, 0))
202 {
203 imageCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
204 }
205
206 const Unique<VkImage> image (createImage(vkd, device, &imageCreateInfo));
207 // allocate memory for the whole image, or for each separate plane ( if the params.flags include VK_IMAGE_CREATE_DISJOINT_BIT )
208 const std::vector<AllocationSp> allocations (allocateAndBindImageMemory(vkd, device, context.getDefaultAllocator(), *image, params.format, params.flags, MemoryRequirement::Any));
209
210 // Create descriptor set layout
211 const Unique<VkDescriptorSetLayout> descriptorSetLayout (DescriptorSetLayoutBuilder()
212 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
213 .build(vkd, device));
214 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vkd, device, *descriptorSetLayout));
215
216 // Create descriptor sets
217 const Unique<VkDescriptorPool> descriptorPool (DescriptorPoolBuilder()
218 .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u)
219 .build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, vk::PlanarFormatDescription::MAX_PLANES));
220
221 // Create command buffer for compute and transfer operations
222 const Unique<VkCommandPool> commandPool (makeCommandPool(vkd, device, queueFamilyIndex));
223 const Unique<VkCommandBuffer> commandBuffer (allocateCommandBuffer(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
224
225 std::vector<de::SharedPtr<vk::Unique<vk::VkShaderModule>>> shaderModules;
226 std::vector<de::SharedPtr<vk::Unique<vk::VkPipeline>>> computePipelines;
227 std::vector<de::SharedPtr<vk::Unique<vk::VkDescriptorSet>>> descriptorSets;
228 std::vector<de::SharedPtr<vk::Unique<vk::VkImageView>>> imageViews;
229
230 deUint32 imageSizeInBytes = 0;
231 deUint32 planeOffsets[PlanarFormatDescription::MAX_PLANES];
232 deUint32 planeRowPitches[PlanarFormatDescription::MAX_PLANES];
233 void* planePointers[PlanarFormatDescription::MAX_PLANES];
234
235 {
236 // Start recording commands
237 beginCommandBuffer(vkd, *commandBuffer);
238
239 for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
240 {
241 const VkImageAspectFlags aspect = (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
242 const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(aspect, 0u, 1u, 0u, 1u);
243 VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
244 vk::PlanarFormatDescription compatibleFormatDescription = (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
245 const tcu::UVec3 compatibleShaderGridSize ( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u);
246 VkExtent3D shaderExtent = getPlaneExtent(compatibleFormatDescription, VkExtent3D{ compatibleShaderGridSize.x(), compatibleShaderGridSize.y(), compatibleShaderGridSize.z() }, planeNdx, 0u);
247
248 // Create and bind compute pipeline
249 std::ostringstream shaderName;
250 shaderName << "comp" << planeNdx;
251 auto shaderModule = makeVkSharedPtr(createShaderModule(vkd, device, context.getBinaryCollection().get(shaderName.str()), DE_NULL));
252 shaderModules.push_back(shaderModule);
253 auto computePipeline = makeVkSharedPtr(makeComputePipeline(vkd, device, *pipelineLayout, shaderModule->get(), DE_NULL));
254 computePipelines.push_back(computePipeline);
255 vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->get());
256
257 auto descriptorSet = makeVkSharedPtr(makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout));
258 descriptorSets.push_back(descriptorSet);
259
260 auto imageView = makeVkSharedPtr(makeImageView(vkd, device, *image, VK_IMAGE_VIEW_TYPE_2D, planeCompatibleFormat, subresourceRange));
261 imageViews.push_back(imageView);
262 const VkDescriptorImageInfo imageInfo = makeDescriptorImageInfo(DE_NULL, imageView->get(), VK_IMAGE_LAYOUT_GENERAL);
263
264 DescriptorSetUpdateBuilder()
265 .writeSingle(descriptorSet->get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo)
266 .update(vkd, device);
267
268 vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet->get(), 0u, DE_NULL);
269
270 {
271 const VkImageMemoryBarrier imageLayoutChangeBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, *image, subresourceRange, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
272 vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageLayoutChangeBarrier);
273 }
274
275 {
276 const tcu::UVec3 workGroupSize = computeWorkGroupSize(shaderExtent);
277
278 const deUint32 xWorkGroupCount = shaderExtent.width / workGroupSize.x() + (shaderExtent.width % workGroupSize.x() ? 1u : 0u);
279 const deUint32 yWorkGroupCount = shaderExtent.height / workGroupSize.y() + (shaderExtent.height % workGroupSize.y() ? 1u : 0u);
280 const deUint32 zWorkGroupCount = shaderExtent.depth / workGroupSize.z() + (shaderExtent.depth % workGroupSize.z() ? 1u : 0u);
281
282 const tcu::UVec3 maxComputeWorkGroupCount = tcu::UVec3(65535u, 65535u, 65535u);
283
284 if (maxComputeWorkGroupCount.x() < xWorkGroupCount ||
285 maxComputeWorkGroupCount.y() < yWorkGroupCount ||
286 maxComputeWorkGroupCount.z() < zWorkGroupCount)
287 {
288 TCU_THROW(NotSupportedError, "Image size is not supported");
289 }
290
291 vkd.cmdDispatch(*commandBuffer, xWorkGroupCount, yWorkGroupCount, zWorkGroupCount);
292 }
293
294 {
295 const VkImageMemoryBarrier imageTransferBarrier = makeImageMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *image, subresourceRange);
296 vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u, &imageTransferBarrier);
297 }
298 }
299
300 for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
301 {
302 planeOffsets[planeNdx] = imageSizeInBytes;
303 const deUint32 planeW = imageCreateInfo.extent.width / (formatDescription.blockWidth * formatDescription.planes[planeNdx].widthDivisor);
304 planeRowPitches[planeNdx] = formatDescription.planes[planeNdx].elementSizeBytes * planeW;
305 imageSizeInBytes += getPlaneSizeInBytes(formatDescription, makeExtent3D( params.size.x(), params.size.y(), params.size.z()) , planeNdx, 0u, BUFFER_IMAGE_COPY_OFFSET_GRANULARITY);
306 }
307
308 const VkBufferCreateInfo outputBufferCreateInfo = makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
309 const Unique<VkBuffer> outputBuffer ( createBuffer(vkd, device, &outputBufferCreateInfo) );
310 const de::UniquePtr<Allocation> outputBufferAlloc ( bindBuffer(vkd, device, context.getDefaultAllocator(), *outputBuffer, MemoryRequirement::HostVisible) );
311 std::vector<VkBufferImageCopy> bufferImageCopy ( formatDescription.numPlanes );
312
313 for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
314 {
315 const VkImageAspectFlags aspect = (formatDescription.numPlanes > 1) ? getPlaneAspect(planeNdx) : VK_IMAGE_ASPECT_COLOR_BIT;
316
317 bufferImageCopy[planeNdx] =
318 {
319 planeOffsets[planeNdx], // VkDeviceSize bufferOffset;
320 0u, // deUint32 bufferRowLength;
321 0u, // deUint32 bufferImageHeight;
322 makeImageSubresourceLayers(aspect, 0u, 0u, 1u), // VkImageSubresourceLayers imageSubresource;
323 makeOffset3D(0, 0, 0), // VkOffset3D imageOffset;
324 getPlaneExtent(formatDescription, makeExtent3D(params.size.x(), params.size.y(), params.size.z()), planeNdx, 0u) // VkExtent3D imageExtent;
325 };
326 }
327 vkd.cmdCopyImageToBuffer(*commandBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *outputBuffer, static_cast<deUint32>(bufferImageCopy.size()), bufferImageCopy.data());
328
329 {
330 const VkBufferMemoryBarrier outputBufferHostReadBarrier = makeBufferMemoryBarrier
331 (
332 VK_ACCESS_TRANSFER_WRITE_BIT,
333 VK_ACCESS_HOST_READ_BIT,
334 *outputBuffer,
335 0u,
336 imageSizeInBytes
337 );
338
339 vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferHostReadBarrier, 0u, DE_NULL);
340 }
341
342 // End recording commands
343 endCommandBuffer(vkd, *commandBuffer);
344
345 // Submit commands for execution and wait for completion
346 submitCommandsAndWait(vkd, device, queue, *commandBuffer);
347
348 // Retrieve data from buffer to host memory
349 invalidateAlloc(vkd, device, *outputBufferAlloc);
350 deUint8* outputData = static_cast<deUint8*>(outputBufferAlloc->getHostPtr());
351
352 for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
353 planePointers[planeNdx] = outputData + static_cast<size_t>(planeOffsets[planeNdx]);
354 }
355
356 // write result images to log file
357 for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
358 {
359 if (!formatDescription.hasChannelNdx(channelNdx))
360 continue;
361 deUint32 planeNdx = formatDescription.channels[channelNdx].planeNdx;
362 vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
363 vk::PlanarFormatDescription compatibleFormatDescription = (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
364 const tcu::UVec3 compatibleShaderGridSize ( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u );
365 tcu::ConstPixelBufferAccess pixelBuffer = vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches, (const void* const*)planePointers, channelNdx);
366 std::ostringstream str;
367 str << "image" << channelNdx;
368 context.getTestContext().getLog() << tcu::LogImage(str.str(), str.str(), pixelBuffer);
369 }
370
371 // verify data
372 const float epsilon = 1e-5f;
373 for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
374 {
375 if (!formatDescription.hasChannelNdx(channelNdx))
376 continue;
377
378 deUint32 planeNdx = formatDescription.channels[channelNdx].planeNdx;
379 vk::VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
380 vk::PlanarFormatDescription compatibleFormatDescription = (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
381 const tcu::UVec3 compatibleShaderGridSize ( params.size.x() / formatDescription.blockWidth, params.size.y() / formatDescription.blockHeight, params.size.z() / 1u );
382 VkExtent3D compatibleImageSize { imageCreateInfo.extent.width / formatDescription.blockWidth, imageCreateInfo.extent.height / formatDescription.blockHeight, imageCreateInfo.extent.depth / 1u };
383 tcu::ConstPixelBufferAccess pixelBuffer = vk::getChannelAccess(compatibleFormatDescription, compatibleShaderGridSize, planeRowPitches, (const void* const*)planePointers, channelNdx);
384 VkExtent3D planeExtent = getPlaneExtent(compatibleFormatDescription, compatibleImageSize, planeNdx, 0u);
385 tcu::IVec3 pixelDivider = pixelBuffer.getDivider();
386
387 for (deUint32 offsetZ = 0u; offsetZ < planeExtent.depth; ++offsetZ)
388 for (deUint32 offsetY = 0u; offsetY < planeExtent.height; ++offsetY)
389 for (deUint32 offsetX = 0u; offsetX < planeExtent.width; ++offsetX)
390 {
391 deUint32 iReferenceValue;
392 float fReferenceValue;
393 switch (channelNdx)
394 {
395 case 0:
396 iReferenceValue = offsetX % 127u;
397 fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
398 break;
399 case 1:
400 iReferenceValue = offsetY % 127u;
401 fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
402 break;
403 case 2:
404 iReferenceValue = offsetZ % 127u;
405 fReferenceValue = static_cast<float>(iReferenceValue) / 127.f;
406 break;
407 case 3:
408 iReferenceValue = 0u;
409 fReferenceValue = 0.f;
410 break;
411 default: DE_FATAL("Unexpected channel index"); break;
412 }
413 float acceptableError = epsilon;
414
415 switch (formatDescription.channels[channelNdx].type)
416 {
417 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
418 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
419 {
420 tcu::UVec4 outputValue = pixelBuffer.getPixelUint(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
421
422 if (outputValue.x() != iReferenceValue)
423 return tcu::TestStatus::fail("Failed");
424
425 break;
426 }
427 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
428 case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
429 {
430 float fixedPointError = tcu::TexVerifierUtil::computeFixedPointError(formatDescription.channels[channelNdx].sizeBits);
431 acceptableError += fixedPointError;
432 tcu::Vec4 outputValue = pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
433
434 if (deAbs(outputValue.x() - fReferenceValue) > acceptableError)
435 return tcu::TestStatus::fail("Failed");
436
437 break;
438 }
439 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
440 {
441 const tcu::Vec4 outputValue = pixelBuffer.getPixel(offsetX * pixelDivider.x(), offsetY * pixelDivider.y(), 0);
442
443 if (deAbs( outputValue.x() - fReferenceValue) > acceptableError)
444 return tcu::TestStatus::fail("Failed");
445
446 break;
447 }
448 default: DE_FATAL("Unexpected channel type"); break;
449 }
450 }
451 }
452 return tcu::TestStatus::pass("Passed");
453 }
454
getShaderImageType(const vk::PlanarFormatDescription & description)455 std::string getShaderImageType (const vk::PlanarFormatDescription& description)
456 {
457 std::string formatPart;
458
459 // all PlanarFormatDescription types have at least one channel ( 0 ) and all channel types are the same :
460 switch (description.channels[0].type)
461 {
462 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
463 formatPart = "i";
464 break;
465 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
466 formatPart = "u";
467 break;
468 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
469 case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
470 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
471 break;
472
473 default:
474 DE_FATAL("Unexpected channel type");
475 }
476
477 return formatPart + "image2D";
478 }
479
getShaderImageDataType(const vk::PlanarFormatDescription & description)480 std::string getShaderImageDataType (const vk::PlanarFormatDescription& description)
481 {
482 switch (description.channels[0].type)
483 {
484 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
485 return "uvec4";
486 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
487 return "ivec4";
488 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
489 case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
490 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
491 return "vec4";
492 default:
493 DE_FATAL("Unexpected channel type");
494 return "";
495 }
496 }
497
getFormatValueString(const std::vector<std::pair<deUint32,deUint32>> & channelsOnPlane,const std::vector<std::string> & formatValueStrings)498 std::string getFormatValueString (const std::vector<std::pair<deUint32, deUint32>>& channelsOnPlane,
499 const std::vector<std::string>& formatValueStrings)
500 {
501 std::string result = "( ";
502 deUint32 i;
503 for (i=0; i<channelsOnPlane.size(); ++i)
504 {
505 result += formatValueStrings[channelsOnPlane[i].first];
506 if (i < 3)
507 result += ", ";
508 }
509 for (; i < 4; ++i)
510 {
511 result += "0";
512 if (i < 3)
513 result += ", ";
514 }
515 result += " )";
516 return result;
517 }
518
getShaderImageFormatQualifier(VkFormat format)519 std::string getShaderImageFormatQualifier (VkFormat format)
520 {
521 switch (format)
522 {
523 case VK_FORMAT_R8_SINT: return "r8i";
524 case VK_FORMAT_R16_SINT: return "r16i";
525 case VK_FORMAT_R32_SINT: return "r32i";
526 case VK_FORMAT_R8_UINT: return "r8ui";
527 case VK_FORMAT_R16_UINT: return "r16ui";
528 case VK_FORMAT_R32_UINT: return "r32ui";
529 case VK_FORMAT_R8_SNORM: return "r8_snorm";
530 case VK_FORMAT_R16_SNORM: return "r16_snorm";
531 case VK_FORMAT_R8_UNORM: return "r8";
532 case VK_FORMAT_R16_UNORM: return "r16";
533
534 case VK_FORMAT_R8G8_SINT: return "rg8i";
535 case VK_FORMAT_R16G16_SINT: return "rg16i";
536 case VK_FORMAT_R32G32_SINT: return "rg32i";
537 case VK_FORMAT_R8G8_UINT: return "rg8ui";
538 case VK_FORMAT_R16G16_UINT: return "rg16ui";
539 case VK_FORMAT_R32G32_UINT: return "rg32ui";
540 case VK_FORMAT_R8G8_SNORM: return "rg8_snorm";
541 case VK_FORMAT_R16G16_SNORM: return "rg16_snorm";
542 case VK_FORMAT_R8G8_UNORM: return "rg8";
543 case VK_FORMAT_R16G16_UNORM: return "rg16";
544
545 case VK_FORMAT_R8G8B8A8_SINT: return "rgba8i";
546 case VK_FORMAT_R16G16B16A16_SINT: return "rgba16i";
547 case VK_FORMAT_R32G32B32A32_SINT: return "rgba32i";
548 case VK_FORMAT_R8G8B8A8_UINT: return "rgba8ui";
549 case VK_FORMAT_R16G16B16A16_UINT: return "rgba16ui";
550 case VK_FORMAT_R32G32B32A32_UINT: return "rgba32ui";
551 case VK_FORMAT_R8G8B8A8_SNORM: return "rgba8_snorm";
552 case VK_FORMAT_R16G16B16A16_SNORM: return "rgba16_snorm";
553 case VK_FORMAT_R8G8B8A8_UNORM: return "rgba8";
554 case VK_FORMAT_R16G16B16A16_UNORM: return "rgba16";
555
556 case VK_FORMAT_G8B8G8R8_422_UNORM: return "rgba8";
557 case VK_FORMAT_B8G8R8G8_422_UNORM: return "rgba8";
558 case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: return "rgba8";
559 case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: return "rgba8";
560 case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM: return "rgba8";
561 case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM: return "rgba8";
562 case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return "rgba8";
563 case VK_FORMAT_R10X6_UNORM_PACK16: return "r16";
564 case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: return "rg16";
565 case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: return "rgba16";
566 case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: return "rgba16";
567 case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: return "rgba16";
568 case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: return "rgba16";
569 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: return "rgba16";
570 case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: return "rgba16";
571 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: return "rgba16";
572 case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: return "rgba16";
573 case VK_FORMAT_R12X4_UNORM_PACK16: return "r16";
574 case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: return "rg16";
575 case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: return "rgba16";
576 case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: return "rgba16";
577 case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: return "rgba16";
578 case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: return "rgba16";
579 case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: return "rgba16";
580 case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: return "rgba16";
581 case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: return "rgba16";
582 case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: return "rgba16";
583 case VK_FORMAT_G16B16G16R16_422_UNORM: return "rgba16";
584 case VK_FORMAT_B16G16R16G16_422_UNORM: return "rgba16";
585 case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM: return "rgba16";
586 case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM: return "rgba16";
587 case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM: return "rgba16";
588 case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM: return "rgba16";
589 case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return "rgba16";
590 case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT: return "rgba8";
591 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT:return "rgba16";
592 case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT:return "rgba16";
593 case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT: return "rgba16";
594
595 default:
596 DE_FATAL("Unexpected texture format");
597 return "error";
598 }
599 }
600
initPrograms(SourceCollections & sourceCollections,TestParameters params)601 void initPrograms (SourceCollections& sourceCollections, TestParameters params)
602 {
603 // Create compute program
604 const char* const versionDecl = glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440);
605 const PlanarFormatDescription formatDescription = getPlanarFormatDescription(params.format);
606 const std::string imageTypeStr = getShaderImageType(formatDescription);
607 const std::string formatDataStr = getShaderImageDataType(formatDescription);
608 const tcu::UVec3 shaderGridSize ( params.size.x(), params.size.y(), params.size.z() );
609
610 std::vector<std::string> formatValueStrings;
611 switch (formatDescription.channels[0].type)
612 {
613 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
614 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
615 formatValueStrings = {
616 "int(gl_GlobalInvocationID.x) % 127",
617 "int(gl_GlobalInvocationID.y) % 127",
618 "int(gl_GlobalInvocationID.z) % 127",
619 "1"
620 };
621 break;
622 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
623 case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
624 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
625 formatValueStrings = {
626 "float(int(gl_GlobalInvocationID.x) % 127) / 127.0" ,
627 "float(int(gl_GlobalInvocationID.y) % 127) / 127.0",
628 "float(int(gl_GlobalInvocationID.z) % 127) / 127.0",
629 "1.0"
630 };
631 break;
632 default: DE_ASSERT(false); break;
633 }
634
635 for (deUint32 planeNdx = 0; planeNdx < formatDescription.numPlanes; ++planeNdx)
636 {
637 VkFormat planeCompatibleFormat = getPlaneCompatibleFormatForWriting(formatDescription, planeNdx);
638 vk::PlanarFormatDescription compatibleFormatDescription = (planeCompatibleFormat != getPlaneCompatibleFormat(formatDescription, planeNdx)) ? getPlanarFormatDescription(planeCompatibleFormat) : formatDescription;
639 VkExtent3D compatibleShaderGridSize { shaderGridSize.x() / formatDescription.blockWidth, shaderGridSize.y() / formatDescription.blockHeight, shaderGridSize.z() / 1u };
640
641 std::vector<std::pair<deUint32, deUint32>> channelsOnPlane;
642 for (deUint32 channelNdx = 0; channelNdx < 4; ++channelNdx)
643 {
644 if (!formatDescription.hasChannelNdx(channelNdx))
645 continue;
646 if (formatDescription.channels[channelNdx].planeNdx != planeNdx)
647 continue;
648 channelsOnPlane.push_back({ channelNdx,formatDescription.channels[channelNdx].offsetBits });
649 }
650 // reorder channels for multi-planar images
651 if (formatDescription.numPlanes > 1)
652 std::sort(begin(channelsOnPlane), end(channelsOnPlane), [](const std::pair<deUint32, deUint32>& lhs, const std::pair<deUint32, deUint32>& rhs) { return lhs.second < rhs.second; });
653 std::string formatValueStr = getFormatValueString(channelsOnPlane, formatValueStrings);
654 VkExtent3D shaderExtent = getPlaneExtent(compatibleFormatDescription, compatibleShaderGridSize, planeNdx, 0);
655 const std::string formatQualifierStr = getShaderImageFormatQualifier(formatDescription.planes[planeNdx].planeCompatibleFormat);
656 const tcu::UVec3 workGroupSize = computeWorkGroupSize(shaderExtent);
657
658 std::ostringstream src;
659 src << versionDecl << "\n"
660 << "layout (local_size_x = " << workGroupSize.x() << ", local_size_y = " << workGroupSize.y() << ", local_size_z = " << workGroupSize.z() << ") in; \n"
661 << "layout (binding = 0, " << formatQualifierStr << ") writeonly uniform highp " << imageTypeStr << " u_image;\n"
662 << "void main (void)\n"
663 << "{\n"
664 << " if( gl_GlobalInvocationID.x < " << shaderExtent.width << " ) \n"
665 << " if( gl_GlobalInvocationID.y < " << shaderExtent.height << " ) \n"
666 << " if( gl_GlobalInvocationID.z < " << shaderExtent.depth << " ) \n"
667 << " {\n"
668 << " imageStore(u_image, ivec2( gl_GlobalInvocationID.x, gl_GlobalInvocationID.y ) ,"
669 << formatDataStr << formatValueStr << ");\n"
670 << " }\n"
671 << "}\n";
672 std::ostringstream shaderName;
673 shaderName << "comp" << planeNdx;
674 sourceCollections.glslSources.add(shaderName.str()) << glu::ComputeSource(src.str());
675 }
676 }
677
populateStorageImageWriteFormatGroup(tcu::TestContext & testCtx,de::MovePtr<tcu::TestCaseGroup> testGroup)678 tcu::TestCaseGroup* populateStorageImageWriteFormatGroup (tcu::TestContext& testCtx, de::MovePtr<tcu::TestCaseGroup> testGroup)
679 {
680 const std::vector<tcu::UVec3> availableSizes{ tcu::UVec3(512u, 512u, 1u), tcu::UVec3(1024u, 128u, 1u), tcu::UVec3(66u, 32u, 1u) };
681
682 auto addTests = [&](int formatNdx)
683 {
684 const VkFormat format = (VkFormat)formatNdx;
685 tcu::UVec3 imageSizeAlignment = getImageSizeAlignment(format);
686 std::string formatName = de::toLower(de::toString(format).substr(10));
687 de::MovePtr<tcu::TestCaseGroup> formatGroup ( new tcu::TestCaseGroup(testCtx, formatName.c_str(), "") );
688
689 for (size_t sizeNdx = 0; sizeNdx < availableSizes.size(); sizeNdx++)
690 {
691 const tcu::UVec3 imageSize = availableSizes[sizeNdx];
692
693 // skip test for images with odd sizes for some YCbCr formats
694 if ((imageSize.x() % imageSizeAlignment.x()) != 0)
695 continue;
696 if ((imageSize.y() % imageSizeAlignment.y()) != 0)
697 continue;
698
699 std::ostringstream stream;
700 stream << imageSize.x() << "_" << imageSize.y() << "_" << imageSize.z();
701 de::MovePtr<tcu::TestCaseGroup> sizeGroup(new tcu::TestCaseGroup(testCtx, stream.str().c_str(), ""));
702
703 addFunctionCaseWithPrograms(sizeGroup.get(), "joint", "", checkSupport, initPrograms, testStorageImageWrite, TestParameters(format, imageSize, 0u));
704 addFunctionCaseWithPrograms(sizeGroup.get(), "disjoint", "", checkSupport, initPrograms, testStorageImageWrite, TestParameters(format, imageSize, (VkImageCreateFlags)VK_IMAGE_CREATE_DISJOINT_BIT));
705
706 formatGroup->addChild(sizeGroup.release());
707 }
708 testGroup->addChild(formatGroup.release());
709 };
710
711 for (int formatNdx = VK_YCBCR_FORMAT_FIRST; formatNdx < VK_YCBCR_FORMAT_LAST; formatNdx++)
712 {
713 addTests(formatNdx);
714 }
715
716 for (int formatNdx = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT; formatNdx <= VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT; formatNdx++)
717 {
718 addTests(formatNdx);
719 }
720
721 return testGroup.release();
722 }
723
724 } // namespace
725
createStorageImageWriteTests(tcu::TestContext & testCtx)726 tcu::TestCaseGroup* createStorageImageWriteTests (tcu::TestContext& testCtx)
727 {
728 de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "storage_image_write", "Writing to YCbCr storage images"));
729 return populateStorageImageWriteFormatGroup(testCtx, testGroup);
730 }
731
732 } // ycbcr
733 } // vkt
734