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 tcu::FloatFormat & conversionPrecision,const 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 tcu::FloatFormat& conversionPrecision,
71 const 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 Interval resultIntervals[4];
306
307 for (int ndx = 0; ndx < 4; ++ndx)
308 {
309 resultIntervals[ndx] = Interval(0.0);
310 }
311
312 for (int i = 0; i < 2; ++i)
313 {
314 const Interval weightInterval = m_filteringPrecision.roundOut(Interval(i == 0 ? 1.0f - weight : weight), false);
315
316 for (int compNdx = 0; compNdx < 4; ++compNdx)
317 {
318 const Interval texelInterval(false, texelsMin[i][compNdx], texelsMax[i][compNdx]);
319
320 resultIntervals[compNdx] = m_filteringPrecision.roundOut(resultIntervals[compNdx] + weightInterval * texelInterval, false);
321 }
322 }
323
324 for (int compNdx = 0; compNdx < 4; ++compNdx)
325 {
326 resultMin[compNdx] = (float)resultIntervals[compNdx].lo();
327 resultMax[compNdx] = (float)resultIntervals[compNdx].hi();
328 }
329 }
330
331
getFilteredSample2D(const IVec3 & texelBase,const Vec2 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const332 void SampleVerifier::getFilteredSample2D (const IVec3& texelBase,
333 const Vec2& weights,
334 int layer,
335 int level,
336 Vec4& resultMin,
337 Vec4& resultMax) const
338 {
339 Vec4 texelsMin[4];
340 Vec4 texelsMax[4];
341
342 for (int i = 0; i < 2; ++i)
343 {
344 for (int j = 0; j < 2; ++j)
345 {
346 fetchTexel(texelBase + IVec3(i, j, 0), layer, level, VK_FILTER_LINEAR, texelsMin[2 * j + i], texelsMax[2 * j + i]);
347 }
348 }
349
350 Interval resultIntervals[4];
351
352 for (int ndx = 0; ndx < 4; ++ndx)
353 {
354 resultIntervals[ndx] = Interval(0.0);
355 }
356
357 for (int i = 0; i < 2; ++i)
358 {
359 const Interval iWeightInterval = m_filteringPrecision.roundOut(Interval(i == 0 ? 1.0f - weights[1] : weights[1]), false);
360
361 for (int j = 0; j < 2; ++j)
362 {
363 const Interval jWeightInterval = m_filteringPrecision.roundOut(iWeightInterval * Interval(j == 0 ? 1.0f - weights[0] : weights[0]), false);
364
365 for (int compNdx = 0; compNdx < 4; ++compNdx)
366 {
367 const Interval texelInterval(false, texelsMin[2 * i + j][compNdx], texelsMax[2 * i + j][compNdx]);
368
369 resultIntervals[compNdx] = m_filteringPrecision.roundOut(resultIntervals[compNdx] + jWeightInterval * texelInterval, false);
370 }
371 }
372 }
373
374 for (int compNdx = 0; compNdx < 4; ++compNdx)
375 {
376 resultMin[compNdx] = (float)resultIntervals[compNdx].lo();
377 resultMax[compNdx] = (float)resultIntervals[compNdx].hi();
378 }
379 }
380
getFilteredSample3D(const IVec3 & texelBase,const Vec3 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const381 void SampleVerifier::getFilteredSample3D (const IVec3& texelBase,
382 const Vec3& weights,
383 int layer,
384 int level,
385 Vec4& resultMin,
386 Vec4& resultMax) const
387 {
388 Vec4 texelsMin[8];
389 Vec4 texelsMax[8];
390
391 for (int i = 0; i < 2; ++i)
392 {
393 for (int j = 0; j < 2; ++j)
394 {
395 for (int k = 0; k < 2; ++k)
396 {
397 fetchTexel(texelBase + IVec3(i, j, k), layer, level, VK_FILTER_LINEAR, texelsMin[4 * k + 2 * j + i], texelsMax[4 * k + 2 * j + i]);
398 }
399 }
400 }
401
402 Interval resultIntervals[4];
403
404 for (int ndx = 0; ndx < 4; ++ndx)
405 {
406 resultIntervals[ndx] = Interval(0.0);
407 }
408
409 for (int i = 0; i < 2; ++i)
410 {
411 const Interval iWeightInterval = m_filteringPrecision.roundOut(Interval(i == 0 ? 1.0f - weights[2] : weights[2]), false);
412
413 for (int j = 0; j < 2; ++j)
414 {
415 const Interval jWeightInterval = m_filteringPrecision.roundOut(iWeightInterval * Interval(j == 0 ? 1.0f - weights[1] : weights[1]), false);
416
417 for (int k = 0; k < 2; ++k)
418 {
419 const Interval kWeightInterval = m_filteringPrecision.roundOut(jWeightInterval * Interval(k == 0 ? 1.0f - weights[0] : weights[0]), false);
420
421 for (int compNdx = 0; compNdx < 4; ++compNdx)
422 {
423 const Interval texelInterval(false, texelsMin[4 * i + 2 * j + k][compNdx], texelsMax[4 * i + 2 * j + k][compNdx]);
424
425 resultIntervals[compNdx] = m_filteringPrecision.roundOut(resultIntervals[compNdx] + kWeightInterval * texelInterval, false);
426 }
427 }
428 }
429 }
430
431 for (int compNdx = 0; compNdx < 4; ++compNdx)
432 {
433 resultMin[compNdx] = (float)resultIntervals[compNdx].lo();
434 resultMax[compNdx] = (float)resultIntervals[compNdx].hi();
435 }
436 }
437
getFilteredSample(const IVec3 & texelBase,const Vec3 & weights,int layer,int level,Vec4 & resultMin,Vec4 & resultMax) const438 void SampleVerifier::getFilteredSample (const IVec3& texelBase,
439 const Vec3& weights,
440 int layer,
441 int level,
442 Vec4& resultMin,
443 Vec4& resultMax) const
444 {
445 DE_ASSERT(layer < m_imParams.arrayLayers);
446 DE_ASSERT(level < m_imParams.levels);
447
448 if (m_imParams.dim == IMG_DIM_1D)
449 {
450 getFilteredSample1D(texelBase, weights.x(), layer, level, resultMin, resultMax);
451 }
452 else if (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE)
453 {
454 getFilteredSample2D(texelBase, weights.swizzle(0, 1), layer, level, resultMin, resultMax);
455 }
456 else
457 {
458 getFilteredSample3D(texelBase, weights, layer, level, resultMin, resultMax);
459 }
460 }
461
getMipmapStepBounds(const Vec2 & lodFracBounds,deInt32 & stepMin,deInt32 & stepMax) const462 void SampleVerifier::getMipmapStepBounds (const Vec2& lodFracBounds,
463 deInt32& stepMin,
464 deInt32& stepMax) const
465 {
466 DE_ASSERT(m_mipmapBits < 32);
467 const int mipmapSteps = ((int)1) << m_mipmapBits;
468
469 stepMin = deFloorFloatToInt32(lodFracBounds[0] * (float)mipmapSteps);
470 stepMax = deCeilFloatToInt32 (lodFracBounds[1] * (float)mipmapSteps);
471
472 stepMin = de::max(stepMin, (deInt32)0);
473 stepMax = de::min(stepMax, (deInt32)mipmapSteps);
474 }
475
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) const476 bool SampleVerifier::verifySampleFiltered (const Vec4& result,
477 const IVec3& baseTexelHiIn,
478 const IVec3& baseTexelLoIn,
479 const IVec3& texelGridOffsetHiIn,
480 const IVec3& texelGridOffsetLoIn,
481 int layer,
482 int levelHi,
483 const Vec2& lodFracBounds,
484 VkFilter filter,
485 VkSamplerMipmapMode mipmapFilter,
486 std::ostream& report) const
487 {
488 DE_ASSERT(layer < m_imParams.arrayLayers);
489 DE_ASSERT(levelHi < m_imParams.levels);
490
491 const int coordSteps = 1 << m_coordBits;
492 const int lodSteps = 1 << m_mipmapBits;
493 const int levelLo = (levelHi < m_imParams.levels - 1) ? levelHi + 1 : levelHi;
494
495 IVec3 baseTexelHi = baseTexelHiIn;
496 IVec3 baseTexelLo = baseTexelLoIn;
497 IVec3 texelGridOffsetHi = texelGridOffsetHiIn;
498 IVec3 texelGridOffsetLo = texelGridOffsetLoIn;
499 deInt32 lodStepsMin = 0;
500 deInt32 lodStepsMax = 0;
501
502 getMipmapStepBounds(lodFracBounds, lodStepsMin, lodStepsMax);
503
504 report << "Testing at base texel " << baseTexelHi << ", " << baseTexelLo << " offset " << texelGridOffsetHi << ", " << texelGridOffsetLo << "\n";
505
506 Vec4 idealSampleHiMin;
507 Vec4 idealSampleHiMax;
508 Vec4 idealSampleLoMin;
509 Vec4 idealSampleLoMax;
510
511 // Get ideal samples at steps at each mipmap level
512
513 if (filter == VK_FILTER_LINEAR)
514 {
515 // Adjust texel grid coordinates for linear filtering
516 wrapTexelGridCoordLinear(baseTexelHi, texelGridOffsetHi, m_coordBits, m_imParams.dim);
517
518 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
519 {
520 wrapTexelGridCoordLinear(baseTexelLo, texelGridOffsetLo, m_coordBits, m_imParams.dim);
521 }
522
523 const Vec3 roundedWeightsHi = texelGridOffsetHi.asFloat() / (float)coordSteps;
524 const Vec3 roundedWeightsLo = texelGridOffsetLo.asFloat() / (float)coordSteps;
525
526 report << "Computed weights: " << roundedWeightsHi << ", " << roundedWeightsLo << "\n";
527
528 getFilteredSample(baseTexelHi, roundedWeightsHi, layer, levelHi, idealSampleHiMin, idealSampleHiMax);
529
530 report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n";
531
532 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
533 {
534 getFilteredSample(baseTexelLo, roundedWeightsLo, layer, levelLo, idealSampleLoMin, idealSampleLoMax);
535
536 report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n";
537 }
538 }
539 else
540 {
541 fetchTexel(baseTexelHi, layer, levelHi, VK_FILTER_NEAREST, idealSampleHiMin, idealSampleHiMax);
542
543 report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n";
544
545 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
546 {
547 fetchTexel(baseTexelLo, layer, levelLo, VK_FILTER_NEAREST, idealSampleLoMin, idealSampleLoMax);
548
549 report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n";
550 }
551 }
552
553 // Test ideal samples based on mipmap filtering mode
554
555 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
556 {
557 for (deInt32 lodStep = lodStepsMin; lodStep <= lodStepsMax; ++lodStep)
558 {
559 float weight = (float)lodStep / (float)lodSteps;
560
561 report << "Testing at mipmap weight " << weight << "\n";
562
563 Vec4 idealSampleMin;
564 Vec4 idealSampleMax;
565
566 for (int compNdx = 0; compNdx < 4; ++compNdx)
567 {
568 const Interval idealSampleLo(false, idealSampleLoMin[compNdx], idealSampleLoMax[compNdx]);
569 const Interval idealSampleHi(false, idealSampleHiMin[compNdx], idealSampleHiMax[compNdx]);
570
571 const Interval idealSample
572 = m_filteringPrecision.roundOut(Interval(weight) * idealSampleLo + Interval(1.0f - weight) * idealSampleHi, false);
573
574 idealSampleMin[compNdx] = (float)idealSample.lo();
575 idealSampleMax[compNdx] = (float)idealSample.hi();
576 }
577
578 report << "Ideal sample: " << idealSampleMin << " through " << idealSampleMax << "\n";
579
580 if (isInRange(result, idealSampleMin, idealSampleMax))
581 {
582 return true;
583 }
584 else
585 {
586 report << "Failed comparison\n";
587 }
588 }
589 }
590 else
591 {
592 if (isInRange(result, idealSampleHiMin, idealSampleHiMax))
593 {
594 return true;
595 }
596 else
597 {
598 report << "Failed comparison\n";
599 }
600 }
601
602 return false;
603 }
604
verifySampleTexelGridCoords(const SampleArguments & args,const Vec4 & result,const IVec3 & gridCoordHi,const IVec3 & gridCoordLo,const Vec2 & lodBounds,int level,VkSamplerMipmapMode mipmapFilter,std::ostream & report) const605 bool SampleVerifier::verifySampleTexelGridCoords (const SampleArguments& args,
606 const Vec4& result,
607 const IVec3& gridCoordHi,
608 const IVec3& gridCoordLo,
609 const Vec2& lodBounds,
610 int level,
611 VkSamplerMipmapMode mipmapFilter,
612 std::ostream& report) const
613 {
614 const int layer = m_imParams.isArrayed ? (int)deRoundEven(args.layer) : 0U;
615 const IVec3 gridCoord[2] = {gridCoordHi, gridCoordLo};
616
617 IVec3 baseTexel[2];
618 IVec3 texelGridOffset[2];
619
620 for (int levelNdx = 0; levelNdx < 2; ++levelNdx)
621 {
622 calcTexelBaseOffset(gridCoord[levelNdx], m_coordBits, baseTexel[levelNdx], texelGridOffset[levelNdx]);
623 }
624
625 const bool canBeMinified = lodBounds[1] > 0.0f;
626 const bool canBeMagnified = lodBounds[0] <= 0.0f;
627
628 if (canBeMagnified)
629 {
630 report << "Trying magnification...\n";
631
632 if (m_samplerParams.magFilter == VK_FILTER_NEAREST)
633 {
634 report << "Testing against nearest texel at " << baseTexel[0] << "\n";
635
636 Vec4 idealMin;
637 Vec4 idealMax;
638
639 fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax);
640
641 if (isInRange(result, idealMin, idealMax))
642 {
643 return true;
644 }
645 else
646 {
647 report << "Failed against " << idealMin << " through " << idealMax << "\n";
648 }
649 }
650 else
651 {
652 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))
653 return true;
654 }
655 }
656
657 if (canBeMinified)
658 {
659 report << "Trying minification...\n";
660
661 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
662 {
663 const Vec2 lodFracBounds = lodBounds - Vec2((float)level);
664
665 if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer, level, lodFracBounds, m_samplerParams.minFilter, VK_SAMPLER_MIPMAP_MODE_LINEAR, report))
666 return true;
667 }
668 else if (m_samplerParams.minFilter == VK_FILTER_LINEAR)
669 {
670 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))
671 return true;
672 }
673 else
674 {
675 report << "Testing against nearest texel at " << baseTexel[0] << "\n";
676
677 Vec4 idealMin;
678 Vec4 idealMax;
679
680 fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax);
681
682 if (isInRange(result, idealMin, idealMax))
683 {
684 return true;
685 }
686 else
687 {
688 report << "Failed against " << idealMin << " through " << idealMax << "\n";
689 }
690 }
691 }
692
693 return false;
694 }
695
verifySampleMipmapLevel(const SampleArguments & args,const Vec4 & result,const Vec4 & coord,const Vec2 & lodBounds,int level,std::ostream & report) const696 bool SampleVerifier::verifySampleMipmapLevel (const SampleArguments& args,
697 const Vec4& result,
698 const Vec4& coord,
699 const Vec2& lodBounds,
700 int level,
701 std::ostream& report) const
702 {
703 DE_ASSERT(level < m_imParams.levels);
704
705 VkSamplerMipmapMode mipmapFilter = m_samplerParams.mipmapFilter;
706
707 if (level == m_imParams.levels - 1)
708 {
709 mipmapFilter = VK_SAMPLER_MIPMAP_MODE_NEAREST;
710 }
711
712 Vec3 unnormalizedCoordMin[2];
713 Vec3 unnormalizedCoordMax[2];
714 IVec3 gridCoordMin[2];
715 IVec3 gridCoordMax[2];
716
717 const FloatFormat coordFormat(-32, 32, 16, true);
718
719 calcUnnormalizedCoordRange(coord,
720 m_levels[level].getSize(),
721 coordFormat,
722 unnormalizedCoordMin[0],
723 unnormalizedCoordMax[0]);
724
725 calcTexelGridCoordRange(unnormalizedCoordMin[0],
726 unnormalizedCoordMax[0],
727 m_coordBits,
728 gridCoordMin[0],
729 gridCoordMax[0]);
730
731 report << "Level " << level << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[0] << ", " << unnormalizedCoordMax[0] << "]\n";
732 report << "Level " << level << " computed texel grid coordinate range: [" << gridCoordMin[0] << ", " << gridCoordMax[0] << "]\n";
733
734 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
735 {
736 calcUnnormalizedCoordRange(coord,
737 m_levels[level+1].getSize(),
738 coordFormat,
739 unnormalizedCoordMin[1],
740 unnormalizedCoordMax[1]);
741
742 calcTexelGridCoordRange(unnormalizedCoordMin[1],
743 unnormalizedCoordMax[1],
744 m_coordBits,
745 gridCoordMin[1],
746 gridCoordMax[1]);
747
748
749 report << "Level " << level+1 << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[1] << " - " << unnormalizedCoordMax[1] << "]\n";
750 report << "Level " << level+1 << " computed texel grid coordinate range: [" << gridCoordMin[1] << " - " << gridCoordMax[1] << "]\n";
751 }
752 else
753 {
754 unnormalizedCoordMin[1] = unnormalizedCoordMax[1] = Vec3(0.0f);
755 gridCoordMin[1] = gridCoordMax[1] = IVec3(0);
756 }
757
758 bool done = false;
759
760 IVec3 gridCoord[2] = {gridCoordMin[0], gridCoordMin[1]};
761
762 while (!done)
763 {
764 if (verifySampleTexelGridCoords(args, result, gridCoord[0], gridCoord[1], lodBounds, level, mipmapFilter, report))
765 return true;
766
767 // Get next grid coordinate to test at
768
769 // Represents whether the increment at a position wraps and should "carry" to the next place
770 bool carry = true;
771
772 for (int levelNdx = 0; levelNdx < 2; ++levelNdx)
773 {
774 for (int compNdx = 0; compNdx < 3; ++compNdx)
775 {
776 if (carry)
777 {
778 deInt32& comp = gridCoord[levelNdx][compNdx];
779 ++comp;
780
781 if (comp > gridCoordMax[levelNdx][compNdx])
782 {
783 comp = gridCoordMin[levelNdx][compNdx];
784 }
785 else
786 {
787 carry = false;
788 }
789 }
790 }
791 }
792
793 done = carry;
794 }
795
796 return false;
797 }
798
verifySampleCubemapFace(const SampleArguments & args,const Vec4 & result,const Vec4 & coord,const Vec4 & dPdx,const Vec4 & dPdy,int face,std::ostream & report) const799 bool SampleVerifier::verifySampleCubemapFace (const SampleArguments& args,
800 const Vec4& result,
801 const Vec4& coord,
802 const Vec4& dPdx,
803 const Vec4& dPdy,
804 int face,
805 std::ostream& report) const
806 {
807 // Will use this parameter once cubemapping is implemented completely
808 DE_UNREF(face);
809
810 Vec2 lodBounds;
811
812 if (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES)
813 {
814 float lodBias = m_samplerParams.lodBias;
815
816 if (m_sampleLookupSettings.hasLodBias)
817 lodBias += args.lodBias;
818
819 lodBounds = calcLodBounds(dPdx.swizzle(0, 1, 2),
820 dPdy.swizzle(0, 1, 2),
821 m_imParams.size,
822 lodBias,
823 m_samplerParams.minLod,
824 m_samplerParams.maxLod);
825 }
826 else
827 {
828 lodBounds[0] = lodBounds[1] = args.lod;
829 }
830
831 DE_ASSERT(lodBounds[0] <= lodBounds[1]);
832
833 const UVec2 levelBounds = calcLevelBounds(lodBounds, m_imParams.levels, m_samplerParams.mipmapFilter);
834
835 for (deUint32 level = levelBounds[0]; level <= levelBounds[1]; ++level)
836 {
837 report << "Testing at mipmap level " << level << "...\n";
838
839 const Vec2 levelLodBounds = calcLevelLodBounds(lodBounds, level);
840
841 if (verifySampleMipmapLevel(args, result, coord, levelLodBounds, level, report))
842 {
843 return true;
844 }
845
846 report << "Done testing mipmap level " << level << ".\n\n";
847 }
848
849 return false;
850 }
851
verifySampleImpl(const SampleArguments & args,const Vec4 & result,std::ostream & report) const852 bool SampleVerifier::verifySampleImpl (const SampleArguments& args,
853 const Vec4& result,
854 std::ostream& report) const
855 {
856 // \todo [2016-07-11 collinbaker] Handle depth and stencil formats
857 // \todo [2016-07-06 collinbaker] Handle dRef
858 DE_ASSERT(m_samplerParams.isCompare == false);
859
860 Vec4 coord = args.coord;
861 int coordSize = 0;
862
863 if (m_imParams.dim == IMG_DIM_1D)
864 {
865 coordSize = 1;
866 }
867 else if (m_imParams.dim == IMG_DIM_2D)
868 {
869 coordSize = 2;
870 }
871 else if (m_imParams.dim == IMG_DIM_3D || m_imParams.dim == IMG_DIM_CUBE)
872 {
873 coordSize = 3;
874 }
875
876 // 15.6.1 Project operation
877
878 if (m_sampleLookupSettings.isProjective)
879 {
880 DE_ASSERT(args.coord[coordSize] != 0.0f);
881 const float proj = coord[coordSize];
882
883 coord = coord / proj;
884 }
885
886 const Vec4 dPdx = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdx : Vec4(0);
887 const Vec4 dPdy = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdy : Vec4(0);
888
889 // 15.6.3 Cube Map Face Selection and Transformations
890
891 if (m_imParams.dim == IMG_DIM_CUBE)
892 {
893 const Vec3 r = coord.swizzle(0, 1, 2);
894 const Vec3 drdx = dPdx.swizzle(0, 1, 2);
895 const Vec3 drdy = dPdy.swizzle(0, 1, 2);
896
897 int faceBitmap = calcCandidateCubemapFaces(r);
898
899 // We must test every possible disambiguation order
900
901 for (int faceNdx = 0; faceNdx < 6; ++faceNdx)
902 {
903 const bool isPossible = ((faceBitmap & (1U << faceNdx)) != 0);
904
905 if (!isPossible)
906 {
907 continue;
908 }
909
910 Vec2 coordFace;
911 Vec2 dPdxFace;
912 Vec2 dPdyFace;
913
914 calcCubemapFaceCoords(r, drdx, drdy, faceNdx, coordFace, dPdxFace, dPdyFace);
915
916 if (verifySampleCubemapFace(args,
917 result,
918 Vec4(coordFace[0], coordFace[1], 0.0f, 0.0f),
919 Vec4(dPdxFace[0], dPdxFace[1], 0.0f, 0.0f),
920 Vec4(dPdyFace[0], dPdyFace[1], 0.0f, 0.0f),
921 faceNdx,
922 report))
923 {
924 return true;
925 }
926 }
927
928 return false;
929 }
930 else
931 {
932 return verifySampleCubemapFace(args, result, coord, dPdx, dPdy, 0, report);
933 }
934 }
935
verifySampleReport(const SampleArguments & args,const Vec4 & result,std::string & report) const936 bool SampleVerifier::verifySampleReport (const SampleArguments& args,
937 const Vec4& result,
938 std::string& report) const
939 {
940 std::ostringstream reportStream;
941
942 const bool isValid = verifySampleImpl(args, result, reportStream);
943
944 report = reportStream.str();
945
946 return isValid;
947 }
948
verifySample(const SampleArguments & args,const Vec4 & result) const949 bool SampleVerifier::verifySample (const SampleArguments& args,
950 const Vec4& result) const
951 {
952 // Create unopened ofstream to simulate "null" ostream
953 std::ofstream nullStream;
954
955 return verifySampleImpl(args, result, nullStream);
956 }
957
958 } // texture
959 } // vkt
960