• Home
  • History
  • Annotate
  • 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 "vktSampleVerifierUtil.hpp"
25 
26 #include "deMath.h"
27 #include "tcuDefs.hpp"
28 #include "tcuFloat.hpp"
29 #include "tcuFloatFormat.hpp"
30 #include "tcuInterval.hpp"
31 #include "tcuTexture.hpp"
32 #include "tcuTextureUtil.hpp"
33 
34 namespace vkt
35 {
36 namespace texture
37 {
38 namespace util
39 {
40 
41 using namespace tcu;
42 using namespace vk;
43 
mod(const deInt32 a,const deInt32 n)44 deInt32 mod (const deInt32 a, const deInt32 n)
45 {
46 	const deInt32 result = a % n;
47 
48 	return (result < 0) ? result + n : result;
49 }
50 
mirror(const deInt32 n)51 deInt32 mirror (const deInt32 n)
52 {
53 	if (n >= 0)
54 	{
55 		return n;
56 	}
57 	else
58 	{
59 		return -(1 + n);
60 	}
61 }
62 
calcLevelBounds(const Vec2 & lodBounds,const int levelCount,VkSamplerMipmapMode mipmapFilter)63 UVec2 calcLevelBounds (const Vec2&			lodBounds,
64 					   const int			levelCount,
65 					   VkSamplerMipmapMode	mipmapFilter)
66 {
67 	DE_ASSERT(lodBounds[0] <= lodBounds[1]);
68 	DE_ASSERT(levelCount > 0);
69 
70 	const float q = (float) (levelCount - 1);
71 
72 	UVec2 levelBounds;
73 
74 	if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST)
75 	{
76 		if (lodBounds[0] <= 0.5f)
77 		{
78 			levelBounds[0] = 0;
79 		}
80 		else if (lodBounds[0] < q + 0.5f)
81 		{
82 			levelBounds[0] = deCeilFloatToInt32(lodBounds[0] + 0.5f) - 1;
83 		}
84 		else
85 		{
86 			levelBounds[0] = deRoundFloatToInt32(q);
87 		}
88 
89 		if (lodBounds[1] < 0.5f)
90 		{
91 			levelBounds[1] = 0;
92 		}
93 		else if (lodBounds[1] < q + 0.5f)
94 		{
95 			levelBounds[1] = deFloorFloatToInt32(lodBounds[1] + 0.5f);
96 		}
97 		else
98 		{
99 			levelBounds[1] = deRoundFloatToInt32(q);
100 		}
101 	}
102 	else
103 	{
104 		for (int ndx = 0; ndx < 2; ++ndx)
105 		{
106 			if (lodBounds[ndx] >= q)
107 			{
108 				levelBounds[ndx] = deRoundFloatToInt32(q);
109 			}
110 			else
111 			{
112 				levelBounds[ndx] = lodBounds[ndx] < 0.0f ? 0 : deFloorFloatToInt32(lodBounds[ndx]);
113 			}
114 		}
115 	}
116 
117 	return levelBounds;
118 }
119 
calcLevelLodBounds(const Vec2 & lodBounds,int level)120 Vec2 calcLevelLodBounds (const Vec2& lodBounds, int level)
121 {
122 	Vec2 levelLodBounds;
123 
124 	if (lodBounds[0] <= 0.0f)
125 	{
126 		levelLodBounds[0] = lodBounds[0];
127 	}
128 	else
129 	{
130 		levelLodBounds[0] = de::max(lodBounds[0], (float) level);
131 	}
132 
133 	levelLodBounds[1] = de::min(lodBounds[1], (float) level + 1.0f);
134 
135 	return levelLodBounds;
136 }
137 
addUlp(float num,deInt32 ulp)138 float addUlp (float num, deInt32 ulp)
139 {
140 	// Note: adding positive ulp always moves float away from zero
141 
142 	const tcu::Float32 f(num);
143 
144 	DE_ASSERT(!f.isNaN() && !f.isInf());
145 	DE_ASSERT(num > FLT_MIN * (float) ulp || num < FLT_MIN * (float) ulp);
146 
147 	return tcu::Float32(f.bits() + ulp).asFloat();
148 }
149 
wrapTexelGridCoordLinear(IVec3 & baseTexel,IVec3 & texelGridOffset,const int coordBits,const ImgDim dim)150 void wrapTexelGridCoordLinear (IVec3&		baseTexel,
151 							   IVec3&		texelGridOffset,
152 							   const int	coordBits,
153 							   const ImgDim dim)
154 {
155 	const int subdivisions = 1 << coordBits;
156 
157 	int numComp;
158 
159 	switch (dim)
160 	{
161 		case IMG_DIM_1D:
162 			numComp = 1;
163 			break;
164 
165 		case IMG_DIM_2D:
166 			numComp = 2;
167 			break;
168 
169 		case IMG_DIM_CUBE:
170 			numComp = 2;
171 			break;
172 
173 		case IMG_DIM_3D:
174 			numComp = 3;
175 			break;
176 
177 		default:
178 			numComp = 0;
179 			break;
180 	}
181 
182 	for (int compNdx = 0; compNdx < numComp; ++compNdx)
183 	{
184 		texelGridOffset[compNdx] -= subdivisions / (int) 2;
185 
186 		if (texelGridOffset[compNdx] < 0)
187 		{
188 			baseTexel      [compNdx] -= 1;
189 			texelGridOffset[compNdx] += (deInt32) subdivisions;
190 		}
191 	}
192 }
193 
calcTexelBaseOffset(const IVec3 & gridCoord,const int coordBits,IVec3 & baseTexel,IVec3 & texelGridOffset)194 void calcTexelBaseOffset (const IVec3&	gridCoord,
195 						  const int		coordBits,
196 						  IVec3&		baseTexel,
197 						  IVec3&		texelGridOffset)
198 {
199 	const int subdivisions = (int) 1 << coordBits;
200 
201 	for (int compNdx = 0; compNdx < 3; ++compNdx)
202 	{
203 		// \todo [2016-07-22 collinbaker] Do floor division to properly handle negative coords
204 		baseTexel[compNdx]		 = gridCoord[compNdx] / (deInt32) subdivisions;
205 		texelGridOffset[compNdx] = gridCoord[compNdx] % (deInt32) subdivisions;
206 	}
207 }
208 
calcTexelGridCoordRange(const Vec3 & unnormalizedCoordMin,const Vec3 & unnormalizedCoordMax,const int coordBits,IVec3 & gridCoordMin,IVec3 & gridCoordMax)209 void calcTexelGridCoordRange (const Vec3&	unnormalizedCoordMin,
210 							  const Vec3&	unnormalizedCoordMax,
211 							  const int		coordBits,
212 							  IVec3&		gridCoordMin,
213 							  IVec3&		gridCoordMax)
214 {
215 	const int subdivisions = 1 << coordBits;
216 
217 	for (int compNdx = 0; compNdx < 3; ++compNdx)
218 	{
219 		const float comp[2] = {unnormalizedCoordMin[compNdx],
220 							   unnormalizedCoordMax[compNdx]};
221 
222 		float	fracPart[2];
223 		double	intPart[2];
224 
225 		for (int ndx = 0; ndx < 2; ++ndx)
226 		{
227 			fracPart[ndx] = (float) deModf(comp[ndx], &intPart[ndx]);
228 
229 			if (comp[ndx] < 0.0f)
230 			{
231 				intPart [ndx] -= 1.0;
232 				fracPart[ndx] += 1.0f;
233 			}
234 		}
235 
236 		const deInt32	nearestTexelGridOffsetMin = (deInt32) deFloor(intPart[0]);
237 		const deInt32	nearestTexelGridOffsetMax = (deInt32) deFloor(intPart[1]);
238 
239 		const deInt32	subTexelGridCoordMin	  = de::max((deInt32) deFloor(fracPart[0] * (float) subdivisions), (deInt32) 0);
240 		const deInt32	subTexelGridCoordMax	  = de::min((deInt32) deCeil (fracPart[1] * (float) subdivisions), (deInt32) (subdivisions - 1));
241 
242 	    gridCoordMin[compNdx] = nearestTexelGridOffsetMin * (deInt32) subdivisions + subTexelGridCoordMin;
243 	    gridCoordMax[compNdx] = nearestTexelGridOffsetMax * (deInt32) subdivisions + subTexelGridCoordMax;
244 	}
245 }
246 
calcUnnormalizedCoordRange(const Vec4 & coord,const IVec3 & levelSize,const FloatFormat & internalFormat,Vec3 & unnormalizedCoordMin,Vec3 & unnormalizedCoordMax)247 void calcUnnormalizedCoordRange (const Vec4&		coord,
248 								 const IVec3&		levelSize,
249 								 const FloatFormat& internalFormat,
250 								 Vec3&				unnormalizedCoordMin,
251 								 Vec3&				unnormalizedCoordMax)
252 {
253     for (int compNdx = 0; compNdx < 3; ++compNdx)
254 	{
255 		const int size = levelSize[compNdx];
256 
257 		Interval coordInterval = Interval(coord[compNdx]);
258 		coordInterval = internalFormat.roundOut(coordInterval, false);
259 
260 		Interval unnormalizedCoordInterval = coordInterval * Interval((double) size);
261 		unnormalizedCoordInterval = internalFormat.roundOut(unnormalizedCoordInterval, false);
262 
263 		unnormalizedCoordMin[compNdx] = (float)unnormalizedCoordInterval.lo();
264 		unnormalizedCoordMax[compNdx] = (float)unnormalizedCoordInterval.hi();
265 	}
266 }
267 
calcLodBounds(const Vec3 & dPdx,const Vec3 & dPdy,const IVec3 size,const float lodBias,const float lodMin,const float lodMax)268 Vec2 calcLodBounds (const Vec3& dPdx,
269 					const Vec3& dPdy,
270 					const IVec3 size,
271 					const float lodBias,
272 					const float lodMin,
273 					const float lodMax)
274 {
275 	Vec2 lodBounds;
276 
277 	const Vec3 mx = abs(dPdx) * size.asFloat();
278 	const Vec3 my = abs(dPdy) * size.asFloat();
279 
280 	Vec2 scaleXBounds;
281 	Vec2 scaleYBounds;
282 
283 	scaleXBounds[0] = de::max(de::abs(mx[0]), de::max(de::abs(mx[1]), de::abs(mx[2])));
284 	scaleYBounds[0] = de::max(de::abs(my[0]), de::max(de::abs(my[1]), de::abs(my[2])));
285 
286 	scaleXBounds[1] = de::abs(mx[0]) + de::abs(mx[1]) + de::abs(mx[2]);
287 	scaleYBounds[1] = de::abs(my[0]) + de::abs(my[1]) + de::abs(my[2]);
288 
289 	Vec2 scaleMaxBounds;
290 
291 	for (int compNdx = 0; compNdx < 2; ++compNdx)
292 	{
293 		scaleMaxBounds[compNdx] = de::max(scaleXBounds[compNdx], scaleYBounds[compNdx]);
294 	}
295 
296 	for (int ndx = 0; ndx < 2; ++ndx)
297 	{
298 		lodBounds[ndx] = deFloatLog2(scaleMaxBounds[ndx]);
299 		lodBounds[ndx] += lodBias;
300 		lodBounds[ndx] = de::clamp(lodBounds[ndx], lodMin, lodMax);
301 	}
302 
303 	return lodBounds;
304 }
305 
calcCubemapFaceCoords(const Vec3 & r,const Vec3 & drdx,const Vec3 & drdy,const int faceNdx,Vec2 & coordFace,Vec2 & dPdxFace,Vec2 & dPdyFace)306 void calcCubemapFaceCoords (const Vec3& r,
307 							const Vec3& drdx,
308 							const Vec3& drdy,
309 							const int	faceNdx,
310 							Vec2&		coordFace,
311 							Vec2&		dPdxFace,
312 							Vec2&		dPdyFace)
313 {
314 	DE_ASSERT(faceNdx >= 0 && faceNdx < 6);
315 
316 	static const int compMap[6][3] =
317 	{
318 		{2, 1, 0},
319 		{2, 1, 0},
320 		{0, 2, 1},
321 		{0, 2, 1},
322 		{0, 1, 2},
323 		{0, 1, 2}
324 	};
325 
326 	static const int signMap[6][3] =
327 	{
328 		{-1, -1, +1},
329 		{+1, -1, -1},
330 		{+1, +1, +1},
331 		{+1, -1, -1},
332 		{+1, -1, +1},
333 		{-1, -1, -1}
334 	};
335 
336 	Vec3 coordC;
337 	Vec3 dPcdx;
338 	Vec3 dPcdy;
339 
340 	for (int compNdx = 0; compNdx < 3; ++compNdx)
341 	{
342 		const int	mappedComp = compMap[faceNdx][compNdx];
343 		const int	mappedSign = signMap[faceNdx][compNdx];
344 
345 		coordC[compNdx] = r   [mappedComp]	* (float)mappedSign;
346 		dPcdx [compNdx]	= drdx[mappedComp]	* (float)mappedSign;
347 		dPcdy [compNdx]	= drdy[mappedComp]	* (float)mappedSign;
348 	}
349 
350 	DE_ASSERT(coordC[2] != 0.0f);
351 	coordC[2] = de::abs(coordC[2]);
352 
353 	for (int compNdx = 0; compNdx < 2; ++compNdx)
354 	{
355 		coordFace[compNdx] = 0.5f * coordC[compNdx] / de::abs(coordC[2]) + 0.5f;
356 
357 		dPdxFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdx[compNdx] - coordC[compNdx] * dPcdx[2]) / (coordC[2] * coordC[2]);
358 		dPdyFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdy[compNdx] - coordC[compNdx] * dPcdy[2]) / (coordC[2] * coordC[2]);
359 	}
360 }
361 
calcCandidateCubemapFaces(const Vec3 & r)362 int calcCandidateCubemapFaces (const Vec3& r)
363 {
364 	deUint8 faceBitmap = 0;
365 	float	rMax	   = de::abs(r[0]);
366 
367 	for (int compNdx = 1; compNdx < 3; ++compNdx)
368 	{
369 		rMax = de::max(rMax, de::abs(r[compNdx]));
370 	}
371 
372 	for (int compNdx = 0; compNdx < 3; ++compNdx)
373 	{
374 		if (de::abs(r[compNdx]) == rMax)
375 		{
376 			const int faceNdx = 2 * compNdx + (r[compNdx] < 0.0f ? 1 : 0);
377 
378 			DE_ASSERT(faceNdx < 6);
379 
380 			faceBitmap = (deUint8)(faceBitmap | (deUint8) (1U << faceNdx));
381 		}
382 	}
383 
384 	DE_ASSERT(faceBitmap != 0U);
385 
386 	return faceBitmap;
387 }
388 
wrapTexelCoord(const deInt32 coord,const int size,const VkSamplerAddressMode wrap)389 deInt32 wrapTexelCoord (const deInt32 coord,
390 						const int size,
391 						const VkSamplerAddressMode wrap)
392 {
393 	deInt32 wrappedCoord = 0;
394 
395 	switch (wrap)
396 	{
397 		case VK_SAMPLER_ADDRESS_MODE_REPEAT:
398 			wrappedCoord = mod(coord, size);
399 			break;
400 
401 		case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
402 			wrappedCoord = (size - 1) - mirror(mod(coord, 2 * size) - size);
403 			break;
404 
405 		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
406 			wrappedCoord = de::clamp(coord, 0, (deInt32) size - 1);
407 			break;
408 
409 		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
410 			wrappedCoord = de::clamp(coord, -1, (deInt32) size);
411 			break;
412 
413 		case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
414 			wrappedCoord = de::clamp(mirror(coord), 0, (deInt32) size - 1);
415 			break;
416 
417 		default:
418 			DE_FATAL("Invalid VkSamplerAddressMode");
419 			break;
420 	}
421 
422 	return wrappedCoord;
423 }
424 
425 namespace
426 {
427 
428 // Cube map adjacent faces ordered clockwise from top
429 // \todo [2016-07-07 collinbaker] Verify these are correct
430 static const int adjacentFaces[6][4] =
431 {
432 	{3, 5, 2, 4},
433 	{3, 4, 2, 5},
434 	{4, 0, 5, 1},
435 	{5, 0, 4, 1},
436 	{3, 0, 2, 1},
437 	{3, 1, 2, 0}
438 };
439 
440 static const int adjacentEdges[6][4] =
441 {
442 	{1, 3, 1, 1},
443 	{3, 3, 3, 1},
444 	{2, 2, 2, 2},
445 	{0, 0, 0, 0},
446 	{2, 3, 0, 1},
447 	{0, 3, 2, 1}
448 };
449 
450 static const int adjacentEdgeDirs[6][4] =
451 {
452 	{-1, +1, +1, +1},
453 	{+1, +1, -1, +1},
454 	{+1, +1, -1, -1},
455 	{-1, -1, +1, +1},
456 	{+1, +1, +1, +1},
457 	{-1, +1, -1, +1}
458 };
459 
460 static const int edgeComponent[4] = {0, 1, 0, 1};
461 
462 static const int edgeFactors[4][2] =
463 {
464 	{0, 0},
465 	{1, 0},
466 	{0, 1},
467 	{0, 0}
468 };
469 
470 } // anonymous
471 
wrapCubemapEdge(const IVec2 & coord,const IVec2 & size,const int faceNdx,IVec2 & newCoord,int & newFaceNdx)472 void wrapCubemapEdge (const IVec2&	coord,
473 					  const IVec2&	size,
474 					  const int		faceNdx,
475 					  IVec2&		newCoord,
476 					  int&			newFaceNdx)
477 {
478 	int edgeNdx = -1;
479 
480 	if (coord[1] < 0)
481 	{
482 		edgeNdx = 0;
483 	}
484 	else if (coord[0] > 0)
485 	{
486 		edgeNdx = 1;
487 	}
488 	else if (coord[1] > 0)
489 	{
490 		edgeNdx = 2;
491 	}
492 	else
493 	{
494 		edgeNdx = 3;
495 	}
496 
497 	const int		adjacentEdgeNdx = adjacentEdges[faceNdx][edgeNdx];
498 	const IVec2		edgeFactor		= IVec2(edgeFactors[adjacentEdgeNdx][0],
499 											edgeFactors[adjacentEdgeNdx][1]);
500 	const IVec2		edgeOffset		= edgeFactor * (size - IVec2(1));
501 
502 	if (adjacentEdgeDirs[faceNdx][edgeNdx] > 0)
503 	{
504 		newCoord[edgeComponent[adjacentEdgeNdx]] = coord[edgeComponent[edgeNdx]];
505 	}
506 	else
507 	{
508 		newCoord[edgeComponent[adjacentEdgeNdx]] =
509 		    size[edgeComponent[edgeNdx]] - coord[edgeComponent[edgeNdx]] - 1;
510 	}
511 
512 	newCoord[1 - edgeComponent[adjacentEdgeNdx]] = 0;
513 	newCoord += edgeOffset;
514 
515 	newFaceNdx = adjacentFaces[faceNdx][edgeNdx];
516 }
517 
wrapCubemapCorner(const IVec2 & coord,const IVec2 & size,const int faceNdx,int & adjacentFace1,int & adjacentFace2,IVec2 & cornerCoord0,IVec2 & cornerCoord1,IVec2 & cornerCoord2)518 void wrapCubemapCorner (const IVec2&	coord,
519 						const IVec2&	size,
520 						const int		faceNdx,
521 						int&			adjacentFace1,
522 						int&			adjacentFace2,
523 						IVec2&			cornerCoord0,
524 						IVec2&			cornerCoord1,
525 						IVec2&			cornerCoord2)
526 {
527 	int cornerNdx = -1;
528 
529 	if (coord[0] < 0 && coord[1] < 0)
530 	{
531 		cornerNdx = 0;
532 	}
533 	else if (coord[0] > 0 && coord[1] < 0)
534 	{
535 		cornerNdx = 1;
536 	}
537 	else if (coord[0] > 0 && coord[1] > 0)
538 	{
539 		cornerNdx = 2;
540 	}
541 	else
542 	{
543 		cornerNdx = 3;
544 	}
545 
546 	const int cornerEdges[2] = {cornerNdx, (int) ((cornerNdx + 3) % 4)};
547 
548 	int		  faceCorners[3] = {cornerNdx, 0, 0};
549 
550 	for (int edgeNdx = 0; edgeNdx < 2; ++edgeNdx)
551 	{
552 		const int faceEdge = adjacentEdges[faceNdx][cornerEdges[edgeNdx]];
553 
554 		bool isFlipped = (adjacentEdgeDirs[faceNdx][cornerEdges[edgeNdx]] == -1);
555 
556 		if ((cornerEdges[edgeNdx] > 1) != (faceEdge > 1))
557 		{
558 			isFlipped = !isFlipped;
559 		}
560 
561 		if (isFlipped)
562 		{
563 			faceCorners[edgeNdx + 1] = (faceEdge + 1) % 4;
564 		}
565 		else
566 		{
567 			faceCorners[edgeNdx + 1] = faceEdge;
568 		}
569 	}
570 
571 	adjacentFace1 = adjacentFaces[faceNdx][cornerEdges[0]];
572 	adjacentFace2 = adjacentFaces[faceNdx][cornerEdges[1]];
573 
574 	IVec2* cornerCoords[3] = {&cornerCoord0, &cornerCoord1, &cornerCoord2};
575 
576 	for (int ndx = 0; ndx < 3; ++ndx)
577 	{
578 		IVec2 cornerFactor;
579 
580 		switch (faceCorners[faceNdx])
581 		{
582 			case 0:
583 				cornerFactor = IVec2(0, 0);
584 				break;
585 
586 			case 1:
587 				cornerFactor = IVec2(1, 0);
588 				break;
589 
590 			case 2:
591 				cornerFactor = IVec2(1, 1);
592 				break;
593 
594 			case 3:
595 				cornerFactor = IVec2(0, 1);
596 				break;
597 
598 			default:
599 				break;
600 		}
601 
602 	    *cornerCoords[ndx] = cornerFactor * (size - IVec2(1));
603 	}
604 }
605 
606 namespace
607 {
608 
signExtend(deUint64 src,int bits)609 deInt64 signExtend (deUint64 src, int bits)
610 {
611 	const deUint64 signBit = 1ull << (bits-1);
612 
613 	src |= ~((src & signBit) - 1);
614 
615 	return (deInt64) src;
616 }
617 
convertFP16(const void * fp16Ptr,const de::SharedPtr<FloatFormat> & internalFormat,float & resultMin,float & resultMax)618 void convertFP16 (const void*						fp16Ptr,
619 				  const de::SharedPtr<FloatFormat>&	internalFormat,
620 				  float&							resultMin,
621 				  float&							resultMax)
622 {
623 	const Float16  fp16(*(const deUint16*) fp16Ptr);
624 	const Interval fpInterval = internalFormat->roundOut(Interval(fp16.asDouble()), false);
625 
626 	resultMin = (float) fpInterval.lo();
627 	resultMax = (float) fpInterval.hi();
628 }
629 
convertNormalizedInt(deInt64 num,int numBits,bool isSigned,const de::SharedPtr<tcu::FloatFormat> & internalFormat,float & resultMin,float & resultMax)630 void convertNormalizedInt (deInt64									num,
631 						   int										numBits,
632 						   bool										isSigned,
633 						   const de::SharedPtr<tcu::FloatFormat>&	internalFormat,
634 						   float&									resultMin,
635 						   float&									resultMax)
636 {
637 	DE_ASSERT(numBits > 0);
638 
639 	const double	c	 = (double) num;
640 	deUint64		exp	 = numBits;
641 
642 	if (isSigned)
643 		--exp;
644 
645 	const double div = (double) (((deUint64) 1 << exp) - 1);
646 	const double value = de::max(c / div, -1.0);
647 
648 	Interval resultInterval(value - internalFormat->ulp(value), value + internalFormat->ulp(value));
649 	resultInterval = internalFormat->roundOut(resultInterval, false);
650 
651 	resultMin = (float) resultInterval.lo();
652 	resultMax = (float) resultInterval.hi();
653 }
654 
isPackedType(const TextureFormat::ChannelType type)655 bool isPackedType (const TextureFormat::ChannelType type)
656 {
657 	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48);
658 
659 	switch (type)
660 	{
661 		case TextureFormat::UNORM_BYTE_44:
662 		case TextureFormat::UNORM_SHORT_565:
663 		case TextureFormat::UNORM_SHORT_555:
664 		case TextureFormat::UNORM_SHORT_4444:
665 		case TextureFormat::UNORM_SHORT_5551:
666 		case TextureFormat::UNORM_SHORT_1555:
667 		case TextureFormat::UNORM_INT_101010:
668 		case TextureFormat::SNORM_INT_1010102_REV:
669 		case TextureFormat::UNORM_INT_1010102_REV:
670 		case TextureFormat::SSCALED_INT_1010102_REV:
671 		case TextureFormat::USCALED_INT_1010102_REV:
672 			return true;
673 
674 		default:
675 			return false;
676 	}
677 }
678 
getPackInfo(const TextureFormat texFormat,IVec4 & bitSizes,IVec4 & bitOffsets,int & baseTypeBytes)679 void getPackInfo (const TextureFormat texFormat,
680 				  IVec4& bitSizes,
681 				  IVec4& bitOffsets,
682 				  int& baseTypeBytes)
683 {
684 	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48);
685 
686 	switch (texFormat.type)
687 	{
688 		case TextureFormat::UNORM_BYTE_44:
689 			bitSizes = IVec4(4, 4, 0, 0);
690 			bitOffsets = IVec4(0, 4, 0, 0);
691 			baseTypeBytes = 1;
692 			break;
693 
694 		case TextureFormat::UNORM_SHORT_565:
695 			bitSizes = IVec4(5, 6, 5, 0);
696 			bitOffsets = IVec4(0, 5, 11, 0);
697 			baseTypeBytes = 2;
698 			break;
699 
700 		case TextureFormat::UNORM_SHORT_555:
701 			bitSizes = IVec4(5, 5, 5, 0);
702 			bitOffsets = IVec4(0, 5, 10, 0);
703 			baseTypeBytes = 2;
704 			break;
705 
706 		case TextureFormat::UNORM_SHORT_4444:
707 			bitSizes = IVec4(4, 4, 4, 4);
708 			bitOffsets = IVec4(0, 4, 8, 12);
709 			baseTypeBytes = 2;
710 			break;
711 
712 		case TextureFormat::UNORM_SHORT_5551:
713 			bitSizes = IVec4(5, 5, 5, 1);
714 			bitOffsets = IVec4(0, 5, 10, 15);
715 			baseTypeBytes = 2;
716 			break;
717 
718 		case TextureFormat::UNORM_SHORT_1555:
719 			bitSizes = IVec4(1, 5, 5, 5);
720 			bitOffsets = IVec4(0, 1, 6, 11);
721 			baseTypeBytes = 2;
722 			break;
723 
724 		case TextureFormat::UNORM_INT_101010:
725 			bitSizes = IVec4(10, 10, 10, 0);
726 			bitOffsets = IVec4(0, 10, 20, 0);
727 			baseTypeBytes = 4;
728 			break;
729 
730 		case TextureFormat::SNORM_INT_1010102_REV:
731 		case TextureFormat::SSCALED_INT_1010102_REV:
732 			bitSizes = IVec4(2, 10, 10, 10);
733 			bitOffsets = IVec4(0, 2, 12, 22);
734 			baseTypeBytes = 4;
735 			break;
736 
737 		case TextureFormat::UNORM_INT_1010102_REV:
738 		case TextureFormat::USCALED_INT_1010102_REV:
739 			bitSizes = IVec4(2, 10, 10, 10);
740 			bitOffsets = IVec4(0, 2, 12, 22);
741 			baseTypeBytes = 4;
742 			break;
743 
744 		default:
745 			DE_FATAL("Invalid texture channel type");
746 			return;
747 	}
748 }
749 
750 template <typename BaseType>
unpackBits(const BaseType pack,const int bitOffset,const int numBits)751 deUint64 unpackBits (const BaseType pack,
752 					 const int		bitOffset,
753 					 const int		numBits)
754 {
755 	DE_ASSERT(bitOffset + numBits <= 8 * (int) sizeof(BaseType));
756 
757 	const BaseType mask = (BaseType) (((BaseType) 1 << (BaseType) numBits) - (BaseType) 1);
758 
759 	return mask & (pack >> (BaseType) (8 * (int) sizeof(BaseType) - bitOffset - numBits));
760 }
761 
readChannel(const void * ptr,const int byteOffset,const int numBytes)762 deUint64 readChannel (const void* ptr,
763 					  const int byteOffset,
764 					  const int numBytes)
765 {
766 	const deUint8*	cPtr   = (const deUint8*) ptr + byteOffset;
767 	deUint64		result = 0;
768 
769 	for (int byteNdx = 0; byteNdx < numBytes; ++byteNdx)
770 	{
771 		result = (result << 8U) | (deUint64) (cPtr[numBytes - byteNdx - 1]);
772 	}
773 
774 	return result;
775 }
776 
convertNormalizedFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)777 void convertNormalizedFormat (const void*										pixelPtr,
778 							  TextureFormat										texFormat,
779 							  const std::vector<de::SharedPtr<FloatFormat>>&	internalFormat,
780 							  Vec4&												resultMin,
781 							  Vec4&												resultMax)
782 {
783     TextureSwizzle				readSwizzle	= getChannelReadSwizzle(texFormat.order);
784 	const TextureChannelClass	chanClass	= getTextureChannelClass(texFormat.type);
785 
786 	DE_ASSERT(getTextureChannelClass(texFormat.type) < 2);
787 
788 	// Information for non-packed types
789 	int chanSize = -1;
790 
791 	// Information for packed types
792 	IVec4 bitOffsets;
793 	IVec4 bitSizes;
794 	int baseTypeBytes = -1;
795 
796 	const bool isPacked = isPackedType(texFormat.type);
797 
798 	if (isPacked)
799 	{
800 		getPackInfo(texFormat, bitSizes, bitOffsets, baseTypeBytes);
801 
802 		// Kludge to work around deficiency in framework
803 
804 		if (texFormat.type == TextureFormat::UNORM_INT_1010102_REV ||
805 			texFormat.type == TextureFormat::SNORM_INT_1010102_REV)
806 		{
807 			for (int ndx = 0; ndx < 2; ++ndx)
808 			{
809 				std::swap(readSwizzle.components[ndx], readSwizzle.components[3 - ndx]);
810 			}
811 		}
812 
813 		DE_ASSERT(baseTypeBytes == 1 || baseTypeBytes == 2 || baseTypeBytes == 4);
814 	}
815 	else
816 	{
817 		chanSize = getChannelSize(texFormat.type);
818 	}
819 
820 	const bool	isSigned = (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT);
821 	const bool	isSrgb	 = isSRGB(texFormat);
822 
823 	// \todo [2016-08-01 collinbaker] Handle sRGB with correct rounding
824 	DE_ASSERT(!isSrgb);
825 	DE_UNREF(isSrgb);
826 
827 	for (int compNdx = 0; compNdx < 4; ++compNdx)
828 	{
829 		const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
830 
831 		if (chan == TextureSwizzle::CHANNEL_ZERO)
832 		{
833 			resultMin[compNdx] = 0.0f;
834 			resultMax[compNdx] = 0.0f;
835 		}
836 		else if (chan == TextureSwizzle::CHANNEL_ONE)
837 		{
838 			resultMin[compNdx] = 1.0f;
839 			resultMax[compNdx] = 1.0f;
840 		}
841 		else
842 		{
843 			deUint64 chanUVal = 0;
844 			int chanBits = 0;
845 
846 			if (isPacked)
847 			{
848 				deUint64 pack = readChannel(pixelPtr, 0, baseTypeBytes);
849 				chanBits = bitSizes[chan];
850 
851 				switch (baseTypeBytes)
852 				{
853 					case 1:
854 						chanUVal = unpackBits<deUint8>((deUint8)pack, bitOffsets[chan], bitSizes[chan]);
855 						break;
856 
857 					case 2:
858 						chanUVal = unpackBits<deUint16>((deUint16)pack, bitOffsets[chan], bitSizes[chan]);
859 						break;
860 
861 					case 4:
862 						chanUVal = unpackBits<deUint32>((deUint32)pack, bitOffsets[chan], bitSizes[chan]);
863 						break;
864 
865 					default:
866 						break;
867 				}
868 			}
869 			else
870 			{
871 			    chanUVal = readChannel(pixelPtr, chan * chanSize, chanSize);
872 				chanBits = 8 * chanSize;
873 			}
874 
875 			deInt64 chanVal = 0;
876 
877 			if (isSigned)
878 			{
879 				chanVal = signExtend(chanUVal, chanBits);
880 			}
881 			else
882 			{
883 				chanVal = (deInt64) chanUVal;
884 			}
885 
886 			convertNormalizedInt(chanVal, chanBits, isSigned, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
887 
888 			// Special handling for components represented as 1 bit. In this case the only possible
889 			// converted values are 0.0 and 1.0, even after using roundOut() to account for the min
890 			// and max range of the converted value. For 1 bit values the min will always equal max.
891 			// To better reflect actual implementations sampling and filtering of converted 1 bit
892 			// values we need to modify the min/max range to include at least one ULP of the
893 			// internalFormat we're using. So if we're using 8 bit fractional precision for the
894 			// conversion instead a 1 bit value of "0" resulting in [0.0 .. 0.0] it will instead
895 			// be [0.0 .. 0.00390625], and a value of "1" resulting in [1.0 .. 1.0] will instead
896 			// be [0.99609375 .. 1.0]. Later when these values are used for calculating the
897 			// reference sampled and filtered values there will be a range that implementations
898 			// can fall between. Without this change, even after the reference sampling and filtering
899 			// calculations, there will be zero tolerance in the acceptable range since min==max
900 			// leaving zero room for rounding errors and arithmetic precision in the implementation.
901 			if (chanBits == 1)
902 			{
903 				if (resultMin[compNdx] == 1.0f)
904 					resultMin[compNdx] -= float(internalFormat[compNdx]->ulp(1.0));
905 				if (resultMax[compNdx] == 0.0f)
906 					resultMax[compNdx] += float(internalFormat[compNdx]->ulp(0.0));
907 			}
908 		}
909 	}
910 }
911 
convertFloatFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)912 void convertFloatFormat (const void*									pixelPtr,
913 						 TextureFormat									texFormat,
914 						 const std::vector<de::SharedPtr<FloatFormat>>&	internalFormat,
915 						 Vec4&											resultMin,
916 						 Vec4&											resultMax)
917 {
918 	DE_ASSERT(getTextureChannelClass(texFormat.type) == TEXTURECHANNELCLASS_FLOATING_POINT);
919 
920 	const TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
921 
922 	for (int compNdx = 0; compNdx < 4; ++compNdx)
923 	{
924 		const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
925 
926 		if (chan == TextureSwizzle::CHANNEL_ZERO)
927 		{
928 			resultMin[compNdx] = 0.0f;
929 			resultMax[compNdx] = 0.0f;
930 		}
931 		else if (chan == TextureSwizzle::CHANNEL_ONE)
932 		{
933 			resultMin[compNdx] = 1.0f;
934 			resultMax[compNdx] = 1.0f;
935 		}
936 		else if (texFormat.type == TextureFormat::FLOAT)
937 		{
938 			resultMin[compNdx] = resultMax[compNdx] = *((const float*)pixelPtr + chan);
939 		}
940 		else if (texFormat.type == TextureFormat::HALF_FLOAT)
941 		{
942 			convertFP16((const deUint16*) pixelPtr + chan, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
943 		}
944 		else
945 		{
946 			DE_FATAL("Unsupported floating point format");
947 		}
948 	}
949 }
950 
951 } // anonymous
952 
convertFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)953 void convertFormat (const void*										pixelPtr,
954 					TextureFormat									texFormat,
955 					const std::vector<de::SharedPtr<FloatFormat>>&	internalFormat,
956 					Vec4&											resultMin,
957 					Vec4&											resultMax)
958 {
959 	const TextureChannelClass	chanClass	 = getTextureChannelClass(texFormat.type);
960 
961 	// \todo [2016-08-01 collinbaker] Handle float and shared exponent formats
962 	if (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chanClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
963 	{
964 		convertNormalizedFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
965 	}
966 	else if (chanClass == TEXTURECHANNELCLASS_FLOATING_POINT)
967 	{
968 		convertFloatFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
969 	}
970 	else
971 	{
972 		DE_FATAL("Unimplemented");
973 	}
974 }
975 
976 } // util
977 } // texture
978 } // vkt
979