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