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