xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/texture/vktSampleVerifier.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 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 GPU image sample verification
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktSampleVerifier.hpp"
25 #include "vktSampleVerifierUtil.hpp"
26 
27 #include "deMath.h"
28 #include "tcuFloat.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "vkImageUtil.hpp"
31 
32 #include <fstream>
33 #include <sstream>
34 
35 namespace vkt
36 {
37 namespace texture
38 {
39 
40 using namespace vk;
41 using namespace tcu;
42 using namespace util;
43 
44 namespace
45 {
46 
calcUnnormalizedDim(const ImgDim dim)47 int calcUnnormalizedDim(const ImgDim dim)
48 {
49     if (dim == IMG_DIM_1D)
50     {
51         return 1;
52     }
53     else if (dim == IMG_DIM_2D || dim == IMG_DIM_CUBE)
54     {
55         return 2;
56     }
57     else
58     {
59         return 3;
60     }
61 }
62 
63 } // namespace
64 
SampleVerifier(const ImageViewParameters & imParams,const SamplerParameters & samplerParams,const SampleLookupSettings & sampleLookupSettings,int coordBits,int mipmapBits,const std::vector<de::SharedPtr<tcu::FloatFormat>> & conversionPrecision,const std::vector<de::SharedPtr<tcu::FloatFormat>> & filteringPrecision,const std::vector<tcu::ConstPixelBufferAccess> & levels)65 SampleVerifier::SampleVerifier(const ImageViewParameters &imParams, const SamplerParameters &samplerParams,
66                                const SampleLookupSettings &sampleLookupSettings, int coordBits, int mipmapBits,
67                                const std::vector<de::SharedPtr<tcu::FloatFormat>> &conversionPrecision,
68                                const std::vector<de::SharedPtr<tcu::FloatFormat>> &filteringPrecision,
69                                const std::vector<tcu::ConstPixelBufferAccess> &levels)
70     : m_imParams(imParams)
71     , m_samplerParams(samplerParams)
72     , m_sampleLookupSettings(sampleLookupSettings)
73     , m_coordBits(coordBits)
74     , m_mipmapBits(mipmapBits)
75     , m_conversionPrecision(conversionPrecision)
76     , m_filteringPrecision(filteringPrecision)
77     , m_unnormalizedDim(calcUnnormalizedDim(imParams.dim))
78     , m_levels(levels)
79 {
80 }
81 
coordOutOfRange(const IVec3 & coord,int compNdx,int level) const82 bool SampleVerifier::coordOutOfRange(const IVec3 &coord, int compNdx, int level) const
83 {
84     DE_ASSERT(compNdx >= 0 && compNdx < 3);
85 
86     return coord[compNdx] < 0 || coord[compNdx] >= m_levels[level].getSize()[compNdx];
87 }
88 
fetchTexelWrapped(const IVec3 & coord,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const89 void SampleVerifier::fetchTexelWrapped(const IVec3 &coord, int layer, int level, Vec4 &resultMin, Vec4 &resultMax) const
90 {
91     const void *pixelPtr = DE_NULL;
92 
93     if (m_imParams.dim == IMG_DIM_1D)
94     {
95         pixelPtr = m_levels[level].getPixelPtr(coord[0], layer, 0);
96     }
97     else if (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE)
98     {
99         pixelPtr = m_levels[level].getPixelPtr(coord[0], coord[1], layer);
100     }
101     else
102     {
103         pixelPtr = m_levels[level].getPixelPtr(coord[0], coord[1], coord[2]);
104     }
105 
106     convertFormat(pixelPtr, mapVkFormat(m_imParams.format), m_conversionPrecision, resultMin, resultMax);
107 
108 #if defined(DE_DEBUG)
109     // Make sure tcuTexture agrees
110     const tcu::ConstPixelBufferAccess &levelAccess = m_levels[level];
111     const tcu::Vec4 refPix = (m_imParams.dim == IMG_DIM_1D) ? levelAccess.getPixel(coord[0], layer, 0) :
112                              (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE) ?
113                                                               levelAccess.getPixel(coord[0], coord[1], layer) :
114                                                               levelAccess.getPixel(coord[0], coord[1], coord[2]);
115 
116     for (int c = 0; c < 4; c++)
117         DE_ASSERT(de::inRange(refPix[c], resultMin[c], resultMax[c]));
118 #endif
119 }
120 
fetchTexel(const IVec3 & coordIn,int layer,int level,VkFilter filter,Vec4 & resultMin,Vec4 & resultMax) const121 void SampleVerifier::fetchTexel(const IVec3 &coordIn, int layer, int level, VkFilter filter, Vec4 &resultMin,
122                                 Vec4 &resultMax) const
123 {
124     IVec3 coord = coordIn;
125 
126     VkSamplerAddressMode wrappingModes[] = {m_samplerParams.wrappingModeU, m_samplerParams.wrappingModeV,
127                                             m_samplerParams.wrappingModeW};
128 
129     const bool isSrgb = isSrgbFormat(m_imParams.format);
130 
131     // Wrapping operations
132 
133     if (m_imParams.dim == IMG_DIM_CUBE && filter == VK_FILTER_LINEAR)
134     {
135         // If the image is a cubemap and we are using linear filtering, we do edge or corner wrapping
136 
137         const int arrayLayer = layer / 6;
138         int arrayFace        = layer % 6;
139 
140         if (coordOutOfRange(coord, 0, level) != coordOutOfRange(coord, 1, level))
141         {
142             // Wrap around edge
143 
144             IVec2 newCoord(0);
145             int newFace = 0;
146 
147             wrapCubemapEdge(coord.swizzle(0, 1), m_levels[level].getSize().swizzle(0, 1), arrayFace, newCoord, newFace);
148 
149             coord.xy() = newCoord;
150             layer      = arrayLayer * 6 + newFace;
151         }
152         else if (coordOutOfRange(coord, 0, level) && coordOutOfRange(coord, 1, level))
153         {
154             // Wrap corner
155 
156             int faces[3] = {arrayFace, 0, 0};
157             IVec2 cornerCoords[3];
158 
159             wrapCubemapCorner(coord.swizzle(0, 1), m_levels[level].getSize().swizzle(0, 1), arrayFace, faces[1],
160                               faces[2], cornerCoords[0], cornerCoords[1], cornerCoords[2]);
161 
162             // \todo [2016-08-01 collinbaker] Call into fetchTexelWrapped instead
163 
164             Vec4 cornerTexels[3];
165 
166             for (int ndx = 0; ndx < 3; ++ndx)
167             {
168                 int cornerLayer = faces[ndx] + arrayLayer * 6;
169 
170                 if (isSrgb)
171                 {
172                     cornerTexels[ndx] +=
173                         sRGBToLinear(m_levels[level].getPixel(cornerCoords[ndx][0], cornerCoords[ndx][1], cornerLayer));
174                 }
175                 else
176                 {
177                     cornerTexels[ndx] +=
178                         m_levels[level].getPixel(cornerCoords[ndx][0], cornerCoords[ndx][1], cornerLayer);
179                 }
180             }
181 
182             for (int compNdx = 0; compNdx < 4; ++compNdx)
183             {
184                 float compMin = cornerTexels[0][compNdx];
185                 float compMax = cornerTexels[0][compNdx];
186 
187                 for (int ndx = 1; ndx < 3; ++ndx)
188                 {
189                     const float comp = cornerTexels[ndx][compNdx];
190 
191                     compMin = de::min(comp, compMin);
192                     compMax = de::max(comp, compMax);
193                 }
194 
195                 resultMin[compNdx] = compMin;
196                 resultMax[compNdx] = compMax;
197             }
198 
199             return;
200         }
201         else
202         {
203             // If no wrapping is necessary, just do nothing
204         }
205     }
206     else
207     {
208         // Otherwise, we do normal wrapping
209 
210         if (m_imParams.dim == IMG_DIM_CUBE)
211         {
212             wrappingModes[0] = wrappingModes[1] = wrappingModes[2] = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
213         }
214 
215         for (int compNdx = 0; compNdx < 3; ++compNdx)
216         {
217             const int size = m_levels[level].getSize()[compNdx];
218 
219             coord[compNdx] = wrapTexelCoord(coord[compNdx], size, wrappingModes[compNdx]);
220         }
221     }
222 
223     if (coordOutOfRange(coord, 0, level) || coordOutOfRange(coord, 1, level) || coordOutOfRange(coord, 2, level))
224     {
225         // If after wrapping coordinates are still out of range, perform texel replacement
226 
227         switch (m_samplerParams.borderColor)
228         {
229         case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
230         {
231             resultMin = Vec4(0.0f, 0.0f, 0.0f, 0.0f);
232             resultMax = Vec4(0.0f, 0.0f, 0.0f, 0.0f);
233             return;
234         }
235         case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
236         {
237             resultMin = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
238             resultMax = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
239             return;
240         }
241         case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
242         {
243             resultMin = Vec4(1.0f, 1.0f, 1.0f, 1.0f);
244             resultMax = Vec4(1.0f, 1.0f, 1.0f, 1.0f);
245             return;
246         }
247         default:
248         {
249             // \\ [2016-07-07 collinbaker] Handle
250             // VK_BORDER_COLOR_INT_* borders
251             DE_FATAL("Not implemented");
252             break;
253         }
254         }
255     }
256     else
257     {
258         // Otherwise, actually fetch a texel
259 
260         fetchTexelWrapped(coord, layer, level, resultMin, resultMax);
261     }
262 }
263 
getFilteredSample1D(const IVec3 & texelBase,float weight,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const264 void SampleVerifier::getFilteredSample1D(const IVec3 &texelBase, float weight, int layer, int level, Vec4 &resultMin,
265                                          Vec4 &resultMax) const
266 {
267     Vec4 texelsMin[2];
268     Vec4 texelsMax[2];
269 
270     for (int i = 0; i < 2; ++i)
271     {
272         fetchTexel(texelBase + IVec3(i, 0, 0), layer, level, VK_FILTER_LINEAR, texelsMin[i], texelsMax[i]);
273     }
274 
275     for (int compNdx = 0; compNdx < 4; ++compNdx)
276     {
277         Interval resultInterval(0.0);
278 
279         for (int i = 0; i < 2; ++i)
280         {
281             const Interval weightInterval =
282                 m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weight : weight), false);
283             const Interval texelInterval(false, texelsMin[i][compNdx], texelsMax[i][compNdx]);
284 
285             resultInterval =
286                 m_filteringPrecision[compNdx]->roundOut(resultInterval + weightInterval * texelInterval, false);
287         }
288 
289         resultMin[compNdx] = (float)resultInterval.lo();
290         resultMax[compNdx] = (float)resultInterval.hi();
291     }
292 }
293 
getFilteredSample2D(const IVec3 & texelBase,const Vec2 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const294 void SampleVerifier::getFilteredSample2D(const IVec3 &texelBase, const Vec2 &weights, int layer, int level,
295                                          Vec4 &resultMin, Vec4 &resultMax) const
296 {
297     Vec4 texelsMin[4];
298     Vec4 texelsMax[4];
299 
300     for (int i = 0; i < 2; ++i)
301     {
302         for (int j = 0; j < 2; ++j)
303         {
304             fetchTexel(texelBase + IVec3(i, j, 0), layer, level, VK_FILTER_LINEAR, texelsMin[2 * j + i],
305                        texelsMax[2 * j + i]);
306         }
307     }
308 
309     for (int compNdx = 0; compNdx < 4; ++compNdx)
310     {
311         Interval resultInterval(0.0);
312 
313         for (int i = 0; i < 2; ++i)
314         {
315             const Interval iWeightInterval =
316                 m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weights[1] : weights[1]), false);
317 
318             for (int j = 0; j < 2; ++j)
319             {
320                 const Interval jWeightInterval = m_filteringPrecision[compNdx]->roundOut(
321                     iWeightInterval * Interval(j == 0 ? 1.0f - weights[0] : weights[0]), false);
322                 const Interval texelInterval(false, texelsMin[2 * i + j][compNdx], texelsMax[2 * i + j][compNdx]);
323 
324                 resultInterval =
325                     m_filteringPrecision[compNdx]->roundOut(resultInterval + jWeightInterval * texelInterval, false);
326             }
327         }
328 
329         resultMin[compNdx] = (float)resultInterval.lo();
330         resultMax[compNdx] = (float)resultInterval.hi();
331     }
332 }
333 
getFilteredSample3D(const IVec3 & texelBase,const Vec3 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const334 void SampleVerifier::getFilteredSample3D(const IVec3 &texelBase, const Vec3 &weights, int layer, int level,
335                                          Vec4 &resultMin, Vec4 &resultMax) const
336 {
337     Vec4 texelsMin[8];
338     Vec4 texelsMax[8];
339 
340     for (int i = 0; i < 2; ++i)
341     {
342         for (int j = 0; j < 2; ++j)
343         {
344             for (int k = 0; k < 2; ++k)
345             {
346                 fetchTexel(texelBase + IVec3(i, j, k), layer, level, VK_FILTER_LINEAR, texelsMin[4 * k + 2 * j + i],
347                            texelsMax[4 * k + 2 * j + i]);
348             }
349         }
350     }
351 
352     for (int compNdx = 0; compNdx < 4; ++compNdx)
353     {
354         Interval resultInterval(0.0);
355 
356         for (int i = 0; i < 2; ++i)
357         {
358             const Interval iWeightInterval =
359                 m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weights[2] : weights[2]), false);
360 
361             for (int j = 0; j < 2; ++j)
362             {
363                 const Interval jWeightInterval = m_filteringPrecision[compNdx]->roundOut(
364                     iWeightInterval * Interval(j == 0 ? 1.0f - weights[1] : weights[1]), false);
365 
366                 for (int k = 0; k < 2; ++k)
367                 {
368                     const Interval kWeightInterval = m_filteringPrecision[compNdx]->roundOut(
369                         jWeightInterval * Interval(k == 0 ? 1.0f - weights[0] : weights[0]), false);
370 
371                     const Interval texelInterval(false, texelsMin[4 * i + 2 * j + k][compNdx],
372                                                  texelsMax[4 * i + 2 * j + k][compNdx]);
373 
374                     resultInterval = m_filteringPrecision[compNdx]->roundOut(
375                         resultInterval + kWeightInterval * texelInterval, false);
376                 }
377             }
378         }
379 
380         resultMin[compNdx] = (float)resultInterval.lo();
381         resultMax[compNdx] = (float)resultInterval.hi();
382     }
383 }
384 
getFilteredSample(const IVec3 & texelBase,const Vec3 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const385 void SampleVerifier::getFilteredSample(const IVec3 &texelBase, const Vec3 &weights, int layer, int level,
386                                        Vec4 &resultMin, Vec4 &resultMax) const
387 {
388     DE_ASSERT(layer < m_imParams.arrayLayers);
389     DE_ASSERT(level < m_imParams.levels);
390 
391     if (m_imParams.dim == IMG_DIM_1D)
392     {
393         getFilteredSample1D(texelBase, weights.x(), layer, level, resultMin, resultMax);
394     }
395     else if (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE)
396     {
397         getFilteredSample2D(texelBase, weights.swizzle(0, 1), layer, level, resultMin, resultMax);
398     }
399     else
400     {
401         getFilteredSample3D(texelBase, weights, layer, level, resultMin, resultMax);
402     }
403 }
404 
getMipmapStepBounds(const Vec2 & lodFracBounds,int32_t & stepMin,int32_t & stepMax) const405 void SampleVerifier::getMipmapStepBounds(const Vec2 &lodFracBounds, int32_t &stepMin, int32_t &stepMax) const
406 {
407     DE_ASSERT(m_mipmapBits < 32);
408     const int mipmapSteps = ((int)1) << m_mipmapBits;
409 
410     stepMin = deFloorFloatToInt32(lodFracBounds[0] * (float)mipmapSteps);
411     stepMax = deCeilFloatToInt32(lodFracBounds[1] * (float)mipmapSteps);
412 
413     stepMin = de::max(stepMin, (int32_t)0);
414     stepMax = de::min(stepMax, (int32_t)mipmapSteps);
415 }
416 
verifySampleFiltered(const Vec4 & result,const IVec3 & baseTexelHiIn,const IVec3 & baseTexelLoIn,const IVec3 & texelGridOffsetHiIn,const IVec3 & texelGridOffsetLoIn,int layer,int levelHi,const Vec2 & lodFracBounds,VkFilter filter,VkSamplerMipmapMode mipmapFilter,std::ostream & report) const417 bool SampleVerifier::verifySampleFiltered(const Vec4 &result, const IVec3 &baseTexelHiIn, const IVec3 &baseTexelLoIn,
418                                           const IVec3 &texelGridOffsetHiIn, const IVec3 &texelGridOffsetLoIn, int layer,
419                                           int levelHi, const Vec2 &lodFracBounds, VkFilter filter,
420                                           VkSamplerMipmapMode mipmapFilter, std::ostream &report) const
421 {
422     DE_ASSERT(layer < m_imParams.arrayLayers);
423     DE_ASSERT(levelHi < m_imParams.levels);
424 
425     const int coordSteps = 1 << m_coordBits;
426     const int lodSteps   = 1 << m_mipmapBits;
427     const int levelLo    = (levelHi < m_imParams.levels - 1) ? levelHi + 1 : levelHi;
428 
429     IVec3 baseTexelHi       = baseTexelHiIn;
430     IVec3 baseTexelLo       = baseTexelLoIn;
431     IVec3 texelGridOffsetHi = texelGridOffsetHiIn;
432     IVec3 texelGridOffsetLo = texelGridOffsetLoIn;
433     int32_t lodStepsMin     = 0;
434     int32_t lodStepsMax     = 0;
435 
436     getMipmapStepBounds(lodFracBounds, lodStepsMin, lodStepsMax);
437 
438     report << "Testing at base texel " << baseTexelHi << ", " << baseTexelLo << " offset " << texelGridOffsetHi << ", "
439            << texelGridOffsetLo << "\n";
440 
441     Vec4 idealSampleHiMin;
442     Vec4 idealSampleHiMax;
443     Vec4 idealSampleLoMin;
444     Vec4 idealSampleLoMax;
445 
446     // Get ideal samples at steps at each mipmap level
447 
448     if (filter == VK_FILTER_LINEAR)
449     {
450         // Adjust texel grid coordinates for linear filtering
451         wrapTexelGridCoordLinear(baseTexelHi, texelGridOffsetHi, m_coordBits, m_imParams.dim);
452 
453         if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
454         {
455             wrapTexelGridCoordLinear(baseTexelLo, texelGridOffsetLo, m_coordBits, m_imParams.dim);
456         }
457 
458         const Vec3 roundedWeightsHi = texelGridOffsetHi.asFloat() / (float)coordSteps;
459         const Vec3 roundedWeightsLo = texelGridOffsetLo.asFloat() / (float)coordSteps;
460 
461         report << "Computed weights: " << roundedWeightsHi << ", " << roundedWeightsLo << "\n";
462 
463         getFilteredSample(baseTexelHi, roundedWeightsHi, layer, levelHi, idealSampleHiMin, idealSampleHiMax);
464 
465         report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n";
466 
467         if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
468         {
469             getFilteredSample(baseTexelLo, roundedWeightsLo, layer, levelLo, idealSampleLoMin, idealSampleLoMax);
470 
471             report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n";
472         }
473     }
474     else
475     {
476         fetchTexel(baseTexelHi, layer, levelHi, VK_FILTER_NEAREST, idealSampleHiMin, idealSampleHiMax);
477 
478         report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n";
479 
480         if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
481         {
482             fetchTexel(baseTexelLo, layer, levelLo, VK_FILTER_NEAREST, idealSampleLoMin, idealSampleLoMax);
483 
484             report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n";
485         }
486     }
487 
488     // Test ideal samples based on mipmap filtering mode
489 
490     if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
491     {
492         for (int32_t lodStep = lodStepsMin; lodStep <= lodStepsMax; ++lodStep)
493         {
494             float weight = (float)lodStep / (float)lodSteps;
495 
496             report << "Testing at mipmap weight " << weight << "\n";
497 
498             Vec4 idealSampleMin;
499             Vec4 idealSampleMax;
500 
501             for (int compNdx = 0; compNdx < 4; ++compNdx)
502             {
503                 const Interval idealSampleLo(false, idealSampleLoMin[compNdx], idealSampleLoMax[compNdx]);
504                 const Interval idealSampleHi(false, idealSampleHiMin[compNdx], idealSampleHiMax[compNdx]);
505 
506                 const Interval idealSample = m_filteringPrecision[compNdx]->roundOut(
507                     Interval(weight) * idealSampleLo + Interval(1.0f - weight) * idealSampleHi, false);
508 
509                 idealSampleMin[compNdx] = (float)idealSample.lo();
510                 idealSampleMax[compNdx] = (float)idealSample.hi();
511             }
512 
513             report << "Ideal sample: " << idealSampleMin << " through " << idealSampleMax << "\n";
514 
515             if (isInRange(result, idealSampleMin, idealSampleMax))
516             {
517                 return true;
518             }
519             else
520             {
521                 report << "Failed comparison\n";
522             }
523         }
524     }
525     else
526     {
527         if (isInRange(result, idealSampleHiMin, idealSampleHiMax))
528         {
529             return true;
530         }
531         else
532         {
533             report << "Failed comparison\n";
534         }
535     }
536 
537     return false;
538 }
539 
verifySampleTexelGridCoords(const SampleArguments & args,const Vec4 & result,const IVec3 & gridCoordHi,const IVec3 & gridCoordLo,const Vec2 & lodBounds,int level,VkSamplerMipmapMode mipmapFilter,std::ostream & report) const540 bool SampleVerifier::verifySampleTexelGridCoords(const SampleArguments &args, const Vec4 &result,
541                                                  const IVec3 &gridCoordHi, const IVec3 &gridCoordLo,
542                                                  const Vec2 &lodBounds, int level, VkSamplerMipmapMode mipmapFilter,
543                                                  std::ostream &report) const
544 {
545     const int layer          = m_imParams.isArrayed ? (int)deRoundEven(args.layer) : 0U;
546     const IVec3 gridCoord[2] = {gridCoordHi, gridCoordLo};
547 
548     IVec3 baseTexel[2];
549     IVec3 texelGridOffset[2];
550 
551     for (int levelNdx = 0; levelNdx < 2; ++levelNdx)
552     {
553         calcTexelBaseOffset(gridCoord[levelNdx], m_coordBits, baseTexel[levelNdx], texelGridOffset[levelNdx]);
554     }
555 
556     const bool canBeMinified  = lodBounds[1] > 0.0f;
557     const bool canBeMagnified = lodBounds[0] <= 0.0f;
558 
559     if (canBeMagnified)
560     {
561         report << "Trying magnification...\n";
562 
563         if (m_samplerParams.magFilter == VK_FILTER_NEAREST)
564         {
565             report << "Testing against nearest texel at " << baseTexel[0] << "\n";
566 
567             Vec4 idealMin;
568             Vec4 idealMax;
569 
570             fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax);
571 
572             if (isInRange(result, idealMin, idealMax))
573             {
574                 return true;
575             }
576             else
577             {
578                 report << "Failed against " << idealMin << " through " << idealMax << "\n";
579             }
580         }
581         else
582         {
583             if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer,
584                                      level, Vec2(0.0f, 0.0f), VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, report))
585                 return true;
586         }
587     }
588 
589     if (canBeMinified)
590     {
591         report << "Trying minification...\n";
592 
593         if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
594         {
595             const Vec2 lodFracBounds = lodBounds - Vec2((float)level);
596 
597             if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer,
598                                      level, lodFracBounds, m_samplerParams.minFilter, VK_SAMPLER_MIPMAP_MODE_LINEAR,
599                                      report))
600                 return true;
601         }
602         else if (m_samplerParams.minFilter == VK_FILTER_LINEAR)
603         {
604             if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer,
605                                      level, Vec2(0.0f, 0.0f), VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, report))
606                 return true;
607         }
608         else
609         {
610             report << "Testing against nearest texel at " << baseTexel[0] << "\n";
611 
612             Vec4 idealMin;
613             Vec4 idealMax;
614 
615             fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax);
616 
617             if (isInRange(result, idealMin, idealMax))
618             {
619                 return true;
620             }
621             else
622             {
623                 report << "Failed against " << idealMin << " through " << idealMax << "\n";
624             }
625         }
626     }
627 
628     return false;
629 }
630 
verifySampleMipmapLevel(const SampleArguments & args,const Vec4 & result,const Vec4 & coord,const Vec2 & lodBounds,int level,std::ostream & report) const631 bool SampleVerifier::verifySampleMipmapLevel(const SampleArguments &args, const Vec4 &result, const Vec4 &coord,
632                                              const Vec2 &lodBounds, int level, std::ostream &report) const
633 {
634     DE_ASSERT(level < m_imParams.levels);
635 
636     VkSamplerMipmapMode mipmapFilter = m_samplerParams.mipmapFilter;
637 
638     if (level == m_imParams.levels - 1)
639     {
640         mipmapFilter = VK_SAMPLER_MIPMAP_MODE_NEAREST;
641     }
642 
643     Vec3 unnormalizedCoordMin[2];
644     Vec3 unnormalizedCoordMax[2];
645     IVec3 gridCoordMin[2];
646     IVec3 gridCoordMax[2];
647 
648     const FloatFormat coordFormat(-32, 32, 16, true);
649 
650     calcUnnormalizedCoordRange(coord, m_levels[level].getSize(), coordFormat, unnormalizedCoordMin[0],
651                                unnormalizedCoordMax[0]);
652 
653     calcTexelGridCoordRange(unnormalizedCoordMin[0], unnormalizedCoordMax[0], m_coordBits, gridCoordMin[0],
654                             gridCoordMax[0]);
655 
656     report << "Level " << level << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[0] << ", "
657            << unnormalizedCoordMax[0] << "]\n";
658     report << "Level " << level << " computed texel grid coordinate range: [" << gridCoordMin[0] << ", "
659            << gridCoordMax[0] << "]\n";
660 
661     if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
662     {
663         calcUnnormalizedCoordRange(coord, m_levels[level + 1].getSize(), coordFormat, unnormalizedCoordMin[1],
664                                    unnormalizedCoordMax[1]);
665 
666         calcTexelGridCoordRange(unnormalizedCoordMin[1], unnormalizedCoordMax[1], m_coordBits, gridCoordMin[1],
667                                 gridCoordMax[1]);
668 
669         report << "Level " << level + 1 << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[1]
670                << " - " << unnormalizedCoordMax[1] << "]\n";
671         report << "Level " << level + 1 << " computed texel grid coordinate range: [" << gridCoordMin[1] << " - "
672                << gridCoordMax[1] << "]\n";
673     }
674     else
675     {
676         unnormalizedCoordMin[1] = unnormalizedCoordMax[1] = Vec3(0.0f);
677         gridCoordMin[1] = gridCoordMax[1] = IVec3(0);
678     }
679 
680     bool done = false;
681 
682     IVec3 gridCoord[2] = {gridCoordMin[0], gridCoordMin[1]};
683 
684     while (!done)
685     {
686         if (verifySampleTexelGridCoords(args, result, gridCoord[0], gridCoord[1], lodBounds, level, mipmapFilter,
687                                         report))
688             return true;
689 
690         // Get next grid coordinate to test at
691 
692         // Represents whether the increment at a position wraps and should "carry" to the next place
693         bool carry = true;
694 
695         for (int levelNdx = 0; levelNdx < 2; ++levelNdx)
696         {
697             for (int compNdx = 0; compNdx < 3; ++compNdx)
698             {
699                 if (carry)
700                 {
701                     int32_t &comp = gridCoord[levelNdx][compNdx];
702                     ++comp;
703 
704                     if (comp > gridCoordMax[levelNdx][compNdx])
705                     {
706                         comp = gridCoordMin[levelNdx][compNdx];
707                     }
708                     else
709                     {
710                         carry = false;
711                     }
712                 }
713             }
714         }
715 
716         done = carry;
717     }
718 
719     return false;
720 }
721 
verifySampleCubemapFace(const SampleArguments & args,const Vec4 & result,const Vec4 & coord,const Vec4 & dPdx,const Vec4 & dPdy,int face,std::ostream & report) const722 bool SampleVerifier::verifySampleCubemapFace(const SampleArguments &args, const Vec4 &result, const Vec4 &coord,
723                                              const Vec4 &dPdx, const Vec4 &dPdy, int face, std::ostream &report) const
724 {
725     // Will use this parameter once cubemapping is implemented completely
726     DE_UNREF(face);
727 
728     Vec2 lodBounds;
729 
730     if (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES)
731     {
732         float lodBias = m_samplerParams.lodBias;
733 
734         if (m_sampleLookupSettings.hasLodBias)
735             lodBias += args.lodBias;
736 
737         lodBounds = calcLodBounds(dPdx.swizzle(0, 1, 2), dPdy.swizzle(0, 1, 2), m_imParams.size, lodBias,
738                                   m_samplerParams.minLod, m_samplerParams.maxLod);
739     }
740     else
741     {
742         lodBounds[0] = lodBounds[1] = args.lod;
743     }
744 
745     DE_ASSERT(lodBounds[0] <= lodBounds[1]);
746 
747     const UVec2 levelBounds = calcLevelBounds(lodBounds, m_imParams.levels, m_samplerParams.mipmapFilter);
748 
749     for (uint32_t level = levelBounds[0]; level <= levelBounds[1]; ++level)
750     {
751         report << "Testing at mipmap level " << level << "...\n";
752 
753         const Vec2 levelLodBounds = calcLevelLodBounds(lodBounds, level);
754 
755         if (verifySampleMipmapLevel(args, result, coord, levelLodBounds, level, report))
756         {
757             return true;
758         }
759 
760         report << "Done testing mipmap level " << level << ".\n\n";
761     }
762 
763     return false;
764 }
765 
verifySampleImpl(const SampleArguments & args,const Vec4 & result,std::ostream & report) const766 bool SampleVerifier::verifySampleImpl(const SampleArguments &args, const Vec4 &result, std::ostream &report) const
767 {
768     // \todo [2016-07-11 collinbaker] Handle depth and stencil formats
769     // \todo [2016-07-06 collinbaker] Handle dRef
770     DE_ASSERT(m_samplerParams.isCompare == false);
771 
772     Vec4 coord    = args.coord;
773     int coordSize = 0;
774 
775     if (m_imParams.dim == IMG_DIM_1D)
776     {
777         coordSize = 1;
778     }
779     else if (m_imParams.dim == IMG_DIM_2D)
780     {
781         coordSize = 2;
782     }
783     else if (m_imParams.dim == IMG_DIM_3D || m_imParams.dim == IMG_DIM_CUBE)
784     {
785         coordSize = 3;
786     }
787 
788     // 15.6.1 Project operation
789 
790     if (m_sampleLookupSettings.isProjective)
791     {
792         DE_ASSERT(args.coord[coordSize] != 0.0f);
793         const float proj = coord[coordSize];
794 
795         coord = coord / proj;
796     }
797 
798     const Vec4 dPdx = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdx : Vec4(0);
799     const Vec4 dPdy = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdy : Vec4(0);
800 
801     // 15.6.3 Cube Map Face Selection and Transformations
802 
803     if (m_imParams.dim == IMG_DIM_CUBE)
804     {
805         const Vec3 r    = coord.swizzle(0, 1, 2);
806         const Vec3 drdx = dPdx.swizzle(0, 1, 2);
807         const Vec3 drdy = dPdy.swizzle(0, 1, 2);
808 
809         int faceBitmap = calcCandidateCubemapFaces(r);
810 
811         // We must test every possible disambiguation order
812 
813         for (int faceNdx = 0; faceNdx < 6; ++faceNdx)
814         {
815             const bool isPossible = ((faceBitmap & (1U << faceNdx)) != 0);
816 
817             if (!isPossible)
818             {
819                 continue;
820             }
821 
822             Vec2 coordFace;
823             Vec2 dPdxFace;
824             Vec2 dPdyFace;
825 
826             calcCubemapFaceCoords(r, drdx, drdy, faceNdx, coordFace, dPdxFace, dPdyFace);
827 
828             if (verifySampleCubemapFace(args, result, Vec4(coordFace[0], coordFace[1], 0.0f, 0.0f),
829                                         Vec4(dPdxFace[0], dPdxFace[1], 0.0f, 0.0f),
830                                         Vec4(dPdyFace[0], dPdyFace[1], 0.0f, 0.0f), faceNdx, report))
831             {
832                 return true;
833             }
834         }
835 
836         return false;
837     }
838     else
839     {
840         return verifySampleCubemapFace(args, result, coord, dPdx, dPdy, 0, report);
841     }
842 }
843 
verifySampleReport(const SampleArguments & args,const Vec4 & result,std::string & report) const844 bool SampleVerifier::verifySampleReport(const SampleArguments &args, const Vec4 &result, std::string &report) const
845 {
846     std::ostringstream reportStream;
847 
848     const bool isValid = verifySampleImpl(args, result, reportStream);
849 
850     report = reportStream.str();
851 
852     return isValid;
853 }
854 
verifySample(const SampleArguments & args,const Vec4 & result) const855 bool SampleVerifier::verifySample(const SampleArguments &args, const Vec4 &result) const
856 {
857     // Create unopened ofstream to simulate "null" ostream
858     std::ofstream nullStream;
859 
860     return verifySampleImpl(args, result, nullStream);
861 }
862 
863 } // namespace texture
864 } // namespace vkt
865