• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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