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