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