1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2020 The Khronos Group Inc.
6 * Copyright (c) 2020 Google Inc.
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 Texture conversion tests.
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTextureConversionTests.hpp"
26 #include "vktAmberTestCase.hpp"
27 #include "vktTestGroupUtil.hpp"
28 #include "vktTextureTestUtil.hpp"
29 #include "vkImageUtil.hpp"
30 #include "tcuTexture.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "deSharedPtr.hpp"
34
35 #include <cmath>
36 #include <memory>
37
38 namespace vkt
39 {
40 namespace texture
41 {
42
43 using namespace vk;
44
45 namespace
46 {
47
48 using namespace texture::util;
49 using namespace glu::TextureTestUtil;
50
51 class SnormLinearClampInstance : public TestInstance
52 {
53 public:
54 struct Params
55 {
56 VkFormat format;
57 int width;
58 int height;
59 };
60 SnormLinearClampInstance (vkt::Context& context,
61 de::SharedPtr<Params> params);
62
63 virtual tcu::TestStatus iterate (void) override;
64
65 protected:
66 tcu::IVec4 computeColorDistance () const;
67 bool verifyPixels (const tcu::PixelBufferAccess& rendered,
68 const tcu::PixelBufferAccess& reference,
69 const ReferenceParams& samplerParams,
70 const std::vector<float>& texCoords) const;
71
72 static int lim (const tcu::TextureFormat& format,
73 int channelIdx);
74
75 private:
76 const de::SharedPtr<Params> m_params;
77 const tcu::TextureFormat m_inFormat;
78 const VkFormat m_outFormat;
79 TestTexture2DSp m_hwTexture;
80 tcu::Texture2D m_swTexture;
81 TextureRenderer m_renderer;
82
83 const tcu::IVec4 m_cd;
84 const tcu::IVec4 m_a;
85 const tcu::IVec4 m_b;
86 const tcu::IVec4 m_c;
87 const tcu::IVec4 m_d;
88
89 public:
90 static const int textureWidth = 7;
91 static const int textureHeight = 7;
92 };
93
SnormLinearClampInstance(vkt::Context & context,de::SharedPtr<Params> params)94 SnormLinearClampInstance::SnormLinearClampInstance (vkt::Context& context, de::SharedPtr<Params> params)
95 : TestInstance (context)
96 , m_params (params)
97 , m_inFormat (mapVkFormat(m_params->format))
98 , m_outFormat (VK_FORMAT_R32G32B32A32_SFLOAT)
99 , m_hwTexture (TestTexture2DSp(new pipeline::TestTexture2D(m_inFormat, textureWidth, textureHeight)))
100 , m_swTexture (m_inFormat, textureWidth, textureHeight, 1)
101 , m_renderer (context, VK_SAMPLE_COUNT_1_BIT, m_params->width, m_params->height, 1u, makeComponentMappingRGBA(), VK_IMAGE_TYPE_2D, VK_IMAGE_VIEW_TYPE_2D, m_outFormat)
102 , m_cd (computeColorDistance())
103 , m_a (lim(m_inFormat, 0), lim(m_inFormat, 1)+m_cd[1]*2, lim(m_inFormat, 2), lim(m_inFormat, 3)+m_cd[3]*2)
104 , m_b (lim(m_inFormat, 0)+m_cd[0]*2, lim(m_inFormat, 1), lim(m_inFormat, 2)+m_cd[2]*2, lim(m_inFormat, 3) )
105 , m_c (lim(m_inFormat, 0)+m_cd[0]*1, lim(m_inFormat, 1)+m_cd[1]*1, lim(m_inFormat, 2)+m_cd[2]*1, lim(m_inFormat, 3)+m_cd[3]*1)
106 , m_d (lim(m_inFormat, 0), lim(m_inFormat, 1), lim(m_inFormat, 2), lim(m_inFormat, 3) )
107 {
108 tcu::IVec4 data[textureWidth * textureHeight] =
109 {
110 m_a, m_b, m_c, m_d, m_c, m_b, m_a,
111 m_b, m_a, m_c, m_d, m_c, m_a, m_b,
112 m_c, m_c, m_c, m_d, m_c, m_c, m_c,
113 m_d, m_d, m_d, m_c, m_d, m_d, m_d,
114 m_c, m_c, m_c, m_d, m_c, m_c, m_c,
115 m_b, m_a, m_c, m_d, m_c, m_a, m_b,
116 m_a, m_b, m_c, m_d, m_c, m_b, m_a,
117 };
118
119 m_swTexture.allocLevel(0);
120
121 const tcu::PixelBufferAccess& swAccess = m_swTexture.getLevel(0);
122 const tcu::PixelBufferAccess& hwAccess = m_hwTexture->getLevel(0, 0);
123
124 for (int y = 0; y < textureHeight; ++y)
125 {
126 for (int x = 0; x < textureWidth; ++x)
127 {
128 swAccess.setPixel(data[y*textureWidth+x], x, y);
129 hwAccess.setPixel(data[y*textureWidth+x], x, y);
130 }
131 }
132
133 m_renderer.add2DTexture(m_hwTexture, VK_IMAGE_ASPECT_COLOR_BIT, TextureBinding::ImageBackingMode::IMAGE_BACKING_MODE_REGULAR);
134 }
135
lim(const tcu::TextureFormat & format,int channelIdx)136 int SnormLinearClampInstance::lim (const tcu::TextureFormat& format, int channelIdx)
137 {
138 auto channelBits(getTextureFormatBitDepth(format));
139 return channelBits[channelIdx] ? (-deIntMaxValue32(channelBits[channelIdx])) : (-1);
140 }
141
computeColorDistance() const142 tcu::IVec4 SnormLinearClampInstance::computeColorDistance () const
143 {
144 return tcu::IVec4
145 (
146 static_cast<int>(std::floor(static_cast<float>(-lim(m_inFormat, 0)) / 127.0f)),
147 static_cast<int>(std::floor(static_cast<float>(-lim(m_inFormat, 0)) / 127.0f)),
148 static_cast<int>(std::floor(static_cast<float>(-lim(m_inFormat, 0)) / 127.0f)),
149 static_cast<int>(std::floor(static_cast<float>(-lim(m_inFormat, 0)) / 127.0f))
150 );
151 }
152
verifyPixels(const tcu::PixelBufferAccess & rendered,const tcu::PixelBufferAccess & reference,const ReferenceParams & samplerParams,const std::vector<float> & texCoords) const153 bool SnormLinearClampInstance::verifyPixels (const tcu::PixelBufferAccess& rendered, const tcu::PixelBufferAccess& reference, const ReferenceParams& samplerParams, const std::vector<float>& texCoords) const
154 {
155 tcu::LodPrecision lodPrec;
156 tcu::LookupPrecision lookupPrec;
157
158 const int nuc (getNumUsedChannels(m_inFormat.order));
159 const int width (m_renderer.getRenderWidth());
160 const int height (m_renderer.getRenderHeight());
161
162 const tcu::IVec4 colorDistance (computeColorDistance());
163 std::unique_ptr<deUint8[]> errorMaskData (new deUint8[width * height * 4 * 4]);
164 tcu::PixelBufferAccess errorMask (mapVkFormat(m_outFormat), width, height, 1, errorMaskData.get());
165
166
167 lodPrec.derivateBits = 18;
168 lodPrec.lodBits = 5;
169
170 lookupPrec.uvwBits = tcu::IVec3(5,5,0);
171 lookupPrec.coordBits = tcu::IVec3(20,20,0);
172 lookupPrec.colorMask = tcu::BVec4(nuc >= 1, nuc >= 2, nuc >=3, nuc >= 4);
173 lookupPrec.colorThreshold = tcu::Vec4(0.9f/float(colorDistance[0]),
174 0.9f/float(colorDistance[1]),
175 0.9f/float(colorDistance[2]),
176 0.9f/float(colorDistance[3]));
177
178 const int numFailedPixels = glu::TextureTestUtil::computeTextureLookupDiff(rendered, reference, errorMask,
179 m_swTexture, texCoords.data(), samplerParams,
180 lookupPrec, lodPrec, /*watchDog*/nullptr);
181 if (numFailedPixels)
182 {
183 const int numTotalPixels = width * height;
184 auto& log = m_context.getTestContext().getLog();
185 const auto formatName = de::toLower(std::string(getFormatName(m_params->format)).substr(10));
186
187 log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage;
188 log << tcu::TestLog::Message << " " << float(numFailedPixels * 100)/float(numTotalPixels) << "% failed from " << numTotalPixels << " compared pixel count." << tcu::TestLog::EndMessage;
189 log << tcu::TestLog::Message << " ColorThreshold: " << lookupPrec.colorThreshold << ", ColorMask: " << lookupPrec.colorMask << tcu::TestLog::EndMessage;
190
191 log << tcu::TestLog::ImageSet("VerifyResult", "Verification result");
192 {
193 log << tcu::TestLog::Image("Res_"+formatName, "Rendered image", rendered);
194 log << tcu::TestLog::Image("Ref_"+formatName, "Reference image", reference);
195 log << tcu::TestLog::Image("Err_"+formatName, "Error mask image", errorMask);
196 }
197 log << tcu::TestLog::EndImageSet;
198 }
199
200 int numOutOfRangePixels = 0;
201 for (int y = 0; y < height; ++y)
202 {
203 for (int x = 0; x < width; ++x)
204 {
205 const auto px = rendered.getPixel(x, y);
206 if (tcu::boolAny(tcu::lessThan(px, tcu::Vec4(-1.0f))) || tcu::boolAny(tcu::greaterThan(px, tcu::Vec4(+1.0f))))
207 ++numOutOfRangePixels;
208 }
209 }
210
211 if (numOutOfRangePixels)
212 {
213 auto& log = m_context.getTestContext().getLog();
214 log << tcu::TestLog::Message << "ERROR: Found " << numOutOfRangePixels << " out of range [-1.0f, +1.0f]." << tcu::TestLog::EndMessage;
215 }
216
217 return (numFailedPixels == 0 && numOutOfRangePixels == 0);
218 }
219
iterate(void)220 tcu::TestStatus SnormLinearClampInstance::iterate (void)
221 {
222 std::vector<float> texCoords (8);
223 ReferenceParams samplerParams (TEXTURETYPE_2D);
224 tcu::TextureFormat resultFormat (mapVkFormat(m_outFormat));
225
226 // Setup renderers.
227 const int width (m_renderer.getRenderWidth());
228 const int height (m_renderer.getRenderHeight());
229 std::unique_ptr<deUint8[]> renderedData (new deUint8[width * height * 4 * 4]);
230 std::unique_ptr<deUint8[]> referenceData (new deUint8[width * height * 4 * 4]);
231 tcu::PixelBufferAccess rendered (resultFormat, width, height, 1, renderedData.get());
232 tcu::PixelBufferAccess reference (resultFormat, width, height, 1, referenceData.get());
233
234 // Setup sampler params.
235 samplerParams.sampler = util::createSampler(tcu::Sampler::WrapMode::REPEAT_GL, tcu::Sampler::WrapMode::REPEAT_GL,
236 tcu::Sampler::FilterMode::LINEAR, tcu::Sampler::FilterMode::LINEAR, true);
237 samplerParams.samplerType = SAMPLERTYPE_FLOAT;
238 samplerParams.lodMode = LODMODE_EXACT;
239 samplerParams.colorScale = tcu::Vec4(1.0f);
240 samplerParams.colorBias = tcu::Vec4(0.0f);
241
242 // Compute texture coordinates.
243 computeQuadTexCoord2D(texCoords, tcu::Vec2(0.0f), tcu::Vec2(1.0f));
244
245 // Peform online rendering with Vulkan.
246 m_renderer.renderQuad(rendered, 0, texCoords.data(), samplerParams);
247
248 // Perform offline rendering with software renderer.
249 sampleTexture(reference, m_swTexture, texCoords.data(), samplerParams);
250
251 return verifyPixels(rendered, reference, samplerParams, texCoords)
252 ? tcu::TestStatus::pass("")
253 : tcu::TestStatus::fail("Pixels verification failed");
254 }
255
256 class SnormLinearClampTestCase : public TestCase
257 {
258 public:
259 using ParamsSp = de::SharedPtr<SnormLinearClampInstance::Params>;
260
SnormLinearClampTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,ParamsSp params)261 SnormLinearClampTestCase (tcu::TestContext& testCtx,
262 const std::string& name,
263 const std::string& description,
264 ParamsSp params)
265 : TestCase (testCtx, name, description)
266 , m_params (params)
267 {
268 }
269
createInstance(vkt::Context & context) const270 vkt::TestInstance* createInstance (vkt::Context& context) const override
271 {
272 return new SnormLinearClampInstance(context, m_params);
273 }
274
checkSupport(vkt::Context & context) const275 virtual void checkSupport (vkt::Context& context) const override
276 {
277 VkFormatProperties formatProperties;
278
279 context.getInstanceInterface().getPhysicalDeviceFormatProperties(
280 context.getPhysicalDevice(),
281 m_params->format,
282 &formatProperties);
283
284 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
285 TCU_THROW(NotSupportedError, "Linear filtering for this image format is not supported");
286 }
287
initPrograms(SourceCollections & programCollection) const288 virtual void initPrograms (SourceCollections& programCollection) const override
289 {
290 initializePrograms(programCollection, glu::Precision::PRECISION_HIGHP, std::vector<Program>({PROGRAM_2D_FLOAT}), DE_NULL, glu::Precision::PRECISION_HIGHP);
291 }
292
293 private:
294 ParamsSp m_params;
295 };
296
populateUfloatNegativeValuesTests(tcu::TestCaseGroup * group)297 void populateUfloatNegativeValuesTests (tcu::TestCaseGroup* group)
298 {
299 #ifndef CTS_USES_VULKANSC
300 tcu::TestContext& testCtx = group->getTestContext();
301 VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
302
303 VkImageCreateInfo info =
304 {
305 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType
306 DE_NULL, // const void* pNext
307 0, // VkImageCreateFlags flags
308 VK_IMAGE_TYPE_2D, // VkImageType imageType
309 VK_FORMAT_B10G11R11_UFLOAT_PACK32, // VkFormat format
310 {50u, 50u, 1u}, // VkExtent3D extent
311 1u, // uint32_t mipLevels
312 1u, // uint32_t arrayLayers
313 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
314 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling
315 usage, // VkImageUsageFlags usage
316 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
317 0u, // uint32_t queueFamilyIndexCount
318 DE_NULL, // const uint32_t* pQueueFamilyIndices
319 VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout
320 };
321
322 group->addChild(cts_amber::createAmberTestCase(testCtx, "b10g11r11", "", "texture/conversion/ufloat_negative_values", "b10g11r11-ufloat-pack32.amber",
323 std::vector<std::string>(), std::vector<VkImageCreateInfo>(1, info)));
324 #else
325 DE_UNREF(group);
326 #endif
327 }
328
populateSnormClampTests(tcu::TestCaseGroup * group)329 void populateSnormClampTests (tcu::TestCaseGroup* group)
330 {
331 #ifndef CTS_USES_VULKANSC
332 tcu::TestContext& testCtx = group->getTestContext();
333 VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
334
335 VkImageCreateInfo info =
336 {
337 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType
338 DE_NULL, // const void* pNext
339 0, // VkImageCreateFlags flags
340 VK_IMAGE_TYPE_1D, // VkImageType imageType
341 VK_FORMAT_UNDEFINED, // VkFormat format
342 {1u, 1u, 1u}, // VkExtent3D extent
343 1u, // uint32_t mipLevels
344 1u, // uint32_t arrayLayers
345 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
346 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling
347 usage, // VkImageUsageFlags usage
348 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
349 0u, // uint32_t queueFamilyIndexCount
350 DE_NULL, // const uint32_t* pQueueFamilyIndices
351 VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout
352 };
353
354 struct TestParams
355 {
356 std::string testName;
357 std::string amberFile;
358 VkFormat format;
359 } params[] =
360 {
361 { "a2b10g10r10_snorm_pack32", "a2b10g10r10-snorm-pack32.amber", VK_FORMAT_A2B10G10R10_SNORM_PACK32 },
362 { "a2r10g10b10_snorm_pack32", "a2r10g10b10-snorm-pack32.amber", VK_FORMAT_A2R10G10B10_SNORM_PACK32 },
363 { "a8b8g8r8_snorm_pack32", "a8b8g8r8-snorm-pack32.amber", VK_FORMAT_A8B8G8R8_SNORM_PACK32 },
364 { "b8g8r8a8_snorm", "b8g8r8a8-snorm.amber", VK_FORMAT_B8G8R8A8_SNORM },
365 { "b8g8r8_snorm", "b8g8r8-snorm.amber", VK_FORMAT_B8G8R8_SNORM },
366 { "r16g16b16a16_snorm", "r16g16b16a16-snorm.amber", VK_FORMAT_R16G16B16A16_SNORM },
367 { "r16g16b16_snorm", "r16g16b16-snorm.amber", VK_FORMAT_R16G16B16_SNORM },
368 { "r16g16_snorm", "r16g16-snorm.amber", VK_FORMAT_R16G16_SNORM },
369 { "r16_snorm", "r16-snorm.amber", VK_FORMAT_R16_SNORM },
370 { "r8g8b8a8_snorm", "r8g8b8a8-snorm.amber", VK_FORMAT_R8G8B8A8_SNORM },
371 { "r8g8b8_snorm", "r8g8b8-snorm.amber", VK_FORMAT_R8G8B8_SNORM },
372 { "r8g8_snorm", "r8g8-snorm.amber", VK_FORMAT_R8G8_SNORM },
373 { "r8_snorm", "r8-snorm.amber", VK_FORMAT_R8_SNORM },
374 };
375
376 for (const auto& param : params)
377 {
378 info.format = param.format;
379 group->addChild(cts_amber::createAmberTestCase(testCtx, param.testName.c_str(), "", "texture/conversion/snorm_clamp", param.amberFile.c_str(),
380 std::vector<std::string>(), std::vector<VkImageCreateInfo>(1, info)));
381 }
382 #else
383 DE_UNREF(group);
384 #endif
385 }
386
populateSnormLinearClampTests(tcu::TestCaseGroup * group)387 void populateSnormLinearClampTests (tcu::TestCaseGroup* group)
388 {
389 struct TestParams
390 {
391 std::string testName;
392 VkFormat format;
393 }
394 testParams[] =
395 {
396 { "a2b10g10r10_snorm_pack32", VK_FORMAT_A2B10G10R10_SNORM_PACK32 },
397 { "a2r10g10b10_snorm_pack32", VK_FORMAT_A2R10G10B10_SNORM_PACK32 },
398 { "a8b8g8r8_snorm_pack32", VK_FORMAT_A8B8G8R8_SNORM_PACK32 },
399 { "b8g8r8a8_snorm", VK_FORMAT_B8G8R8A8_SNORM },
400 { "b8g8r8_snorm", VK_FORMAT_B8G8R8_SNORM },
401 { "r16g16b16a16_snorm", VK_FORMAT_R16G16B16A16_SNORM },
402 { "r16g16b16_snorm", VK_FORMAT_R16G16B16_SNORM },
403 { "r16g16_snorm", VK_FORMAT_R16G16_SNORM },
404 { "r16_snorm", VK_FORMAT_R16_SNORM },
405 { "r8g8b8a8_snorm", VK_FORMAT_R8G8B8A8_SNORM },
406 { "r8g8b8_snorm", VK_FORMAT_R8G8B8_SNORM },
407 { "r8g8_snorm", VK_FORMAT_R8G8_SNORM },
408 { "r8_snorm", VK_FORMAT_R8_SNORM },
409 };
410
411 tcu::TestContext& testCtx = group->getTestContext();
412 int sizeMultipler = 20;
413
414 for (const auto& testParam : testParams)
415 {
416 const int tw = SnormLinearClampInstance::textureWidth * sizeMultipler;
417 const int th = SnormLinearClampInstance::textureHeight * sizeMultipler;
418
419 de::SharedPtr<SnormLinearClampInstance::Params> params(new SnormLinearClampInstance::Params{testParam.format, tw, th});
420 group->addChild(new SnormLinearClampTestCase(testCtx, testParam.testName, {}, params));
421
422 sizeMultipler += 2;
423 }
424 }
425
populateTextureConversionTests(tcu::TestCaseGroup * group)426 void populateTextureConversionTests (tcu::TestCaseGroup* group)
427 {
428 tcu::TestContext& testCtx = group->getTestContext();
429
430 group->addChild(createTestGroup(testCtx, "ufloat_negative_values", "Tests for converting negative floats to unsigned floats", populateUfloatNegativeValuesTests));
431 group->addChild(createTestGroup(testCtx, "snorm_clamp", "Tests for SNORM corner cases when smallest negative number gets clamped to -1", populateSnormClampTests));
432 group->addChild(createTestGroup(testCtx, "snorm_clamp_linear", "Tests for SNORM corner cases when negative number gets clamped to -1 after applying linear filtering", populateSnormLinearClampTests));
433 }
434
435 } // anonymous namespace
436
createTextureConversionTests(tcu::TestContext & testCtx)437 tcu::TestCaseGroup* createTextureConversionTests (tcu::TestContext& testCtx)
438 {
439 return createTestGroup(testCtx, "conversion", "Texture conversion tests.", populateTextureConversionTests);
440 }
441
442 } // texture
443 } // vkt
444