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