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
34namespace vkt
35{
36namespace texture
37{
38namespace util
39{
40
41using namespace tcu;
42using namespace vk;
43
44deInt32 mod (const deInt32 a, const deInt32 n)
45{
46	const deInt32 result = a % n;
47
48	return (result < 0) ? result + n : result;
49}
50
51deInt32 mirror (const deInt32 n)
52{
53	if (n >= 0)
54	{
55		return n;
56	}
57	else
58	{
59		return -(1 + n);
60	}
61}
62
63UVec2 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
120Vec2 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
138float 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
150void 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
194void 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
209void 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
247void 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
268Vec2 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
306void 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
362int 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
389deInt32 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
425namespace
426{
427
428// Cube map adjacent faces ordered clockwise from top
429// \todo [2016-07-07 collinbaker] Verify these are correct
430static 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
440static 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
450static 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
460static const int edgeComponent[4] = {0, 1, 0, 1};
461
462static const int edgeFactors[4][2] =
463{
464	{0, 0},
465	{1, 0},
466	{0, 1},
467	{0, 0}
468};
469
470} // anonymous
471
472void 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
518void 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
606namespace
607{
608
609deInt64 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
618void convertFP16 (const void*	fp16Ptr,
619				  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
630void convertNormalizedInt (deInt64		num,
631						   int			numBits,
632						   bool			isSigned,
633						   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
647	Interval resultInterval(de::max(c / div, -1.0));
648	resultInterval = internalFormat.roundOut(resultInterval, false);
649
650	resultMin = (float) resultInterval.lo();
651	resultMax = (float) resultInterval.hi();
652}
653
654bool isPackedType (const TextureFormat::ChannelType type)
655{
656	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
657
658	switch (type)
659	{
660		case TextureFormat::UNORM_BYTE_44:
661		case TextureFormat::UNORM_SHORT_565:
662		case TextureFormat::UNORM_SHORT_555:
663		case TextureFormat::UNORM_SHORT_4444:
664		case TextureFormat::UNORM_SHORT_5551:
665		case TextureFormat::UNORM_SHORT_1555:
666		case TextureFormat::UNORM_INT_101010:
667		case TextureFormat::SNORM_INT_1010102_REV:
668		case TextureFormat::UNORM_INT_1010102_REV:
669			return true;
670
671		default:
672			return false;
673	}
674}
675
676void getPackInfo (const TextureFormat texFormat,
677				  IVec4& bitSizes,
678				  IVec4& bitOffsets,
679				  int& baseTypeBytes)
680{
681	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
682
683	switch (texFormat.type)
684	{
685		case TextureFormat::UNORM_BYTE_44:
686			bitSizes = IVec4(4, 4, 0, 0);
687			bitOffsets = IVec4(0, 4, 0, 0);
688			baseTypeBytes = 1;
689			break;
690
691		case TextureFormat::UNORM_SHORT_565:
692			bitSizes = IVec4(5, 6, 5, 0);
693			bitOffsets = IVec4(0, 5, 11, 0);
694			baseTypeBytes = 2;
695			break;
696
697		case TextureFormat::UNORM_SHORT_555:
698			bitSizes = IVec4(5, 5, 5, 0);
699			bitOffsets = IVec4(0, 5, 10, 0);
700			baseTypeBytes = 2;
701			break;
702
703		case TextureFormat::UNORM_SHORT_4444:
704			bitSizes = IVec4(4, 4, 4, 4);
705			bitOffsets = IVec4(0, 4, 8, 12);
706			baseTypeBytes = 2;
707			break;
708
709		case TextureFormat::UNORM_SHORT_5551:
710			bitSizes = IVec4(5, 5, 5, 1);
711			bitOffsets = IVec4(0, 5, 10, 15);
712			baseTypeBytes = 2;
713			break;
714
715		case TextureFormat::UNORM_SHORT_1555:
716			bitSizes = IVec4(1, 5, 5, 5);
717			bitOffsets = IVec4(0, 1, 6, 11);
718			baseTypeBytes = 2;
719			break;
720
721		case TextureFormat::UNORM_INT_101010:
722			bitSizes = IVec4(10, 10, 10, 0);
723			bitOffsets = IVec4(0, 10, 20, 0);
724			baseTypeBytes = 4;
725			break;
726
727		case TextureFormat::SNORM_INT_1010102_REV:
728			bitSizes = IVec4(2, 10, 10, 10);
729			bitOffsets = IVec4(0, 2, 12, 22);
730			baseTypeBytes = 4;
731			break;
732
733		case TextureFormat::UNORM_INT_1010102_REV:
734			bitSizes = IVec4(2, 10, 10, 10);
735			bitOffsets = IVec4(0, 2, 12, 22);
736			baseTypeBytes = 4;
737			break;
738
739		default:
740			DE_FATAL("Invalid texture channel type");
741			return;
742	}
743}
744
745template <typename BaseType>
746deUint64 unpackBits (const BaseType pack,
747					 const int		bitOffset,
748					 const int		numBits)
749{
750	DE_ASSERT(bitOffset + numBits <= 8 * (int) sizeof(BaseType));
751
752	const BaseType mask = (BaseType) (((BaseType) 1 << (BaseType) numBits) - (BaseType) 1);
753
754	return mask & (pack >> (BaseType) (8 * (int) sizeof(BaseType) - bitOffset - numBits));
755}
756
757deUint64 readChannel (const void* ptr,
758					  const int byteOffset,
759					  const int numBytes)
760{
761	const deUint8*	cPtr   = (const deUint8*) ptr + byteOffset;
762	deUint64		result = 0;
763
764	for (int byteNdx = 0; byteNdx < numBytes; ++byteNdx)
765	{
766		result = (result << 8U) | (deUint64) (cPtr[numBytes - byteNdx - 1]);
767	}
768
769	return result;
770}
771
772void convertNormalizedFormat (const void*	pixelPtr,
773							  TextureFormat	texFormat,
774							  FloatFormat	internalFormat,
775							  Vec4&			resultMin,
776							  Vec4&			resultMax)
777{
778    TextureSwizzle				readSwizzle	= getChannelReadSwizzle(texFormat.order);
779	const TextureChannelClass	chanClass	= getTextureChannelClass(texFormat.type);
780
781	DE_ASSERT(getTextureChannelClass(texFormat.type) < 2);
782
783	// Information for non-packed types
784	int chanSize = -1;
785
786	// Information for packed types
787	IVec4 bitOffsets;
788	IVec4 bitSizes;
789	int baseTypeBytes = -1;
790
791	const bool isPacked = isPackedType(texFormat.type);
792
793	if (isPacked)
794	{
795		getPackInfo(texFormat, bitSizes, bitOffsets, baseTypeBytes);
796
797		// Kludge to work around deficiency in framework
798
799		if (texFormat.type == TextureFormat::UNORM_INT_1010102_REV ||
800			texFormat.type == TextureFormat::SNORM_INT_1010102_REV)
801		{
802			for (int ndx = 0; ndx < 2; ++ndx)
803			{
804				std::swap(readSwizzle.components[ndx], readSwizzle.components[3 - ndx]);
805			}
806		}
807
808		DE_ASSERT(baseTypeBytes == 1 || baseTypeBytes == 2 || baseTypeBytes == 4);
809	}
810	else
811	{
812		chanSize = getChannelSize(texFormat.type);
813	}
814
815	const bool	isSigned = (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT);
816	const bool	isSrgb	 = isSRGB(texFormat);
817
818	// \todo [2016-08-01 collinbaker] Handle sRGB with correct rounding
819	DE_ASSERT(!isSrgb);
820	DE_UNREF(isSrgb);
821
822	for (int compNdx = 0; compNdx < 4; ++compNdx)
823	{
824		const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
825
826		if (chan == TextureSwizzle::CHANNEL_ZERO)
827		{
828			resultMin[compNdx] = 0.0f;
829			resultMax[compNdx] = 0.0f;
830		}
831		else if (chan == TextureSwizzle::CHANNEL_ONE)
832		{
833			resultMin[compNdx] = 1.0f;
834			resultMax[compNdx] = 1.0f;
835		}
836		else
837		{
838			deUint64 chanUVal = 0;
839			int chanBits = 0;
840
841			if (isPacked)
842			{
843				deUint64 pack = readChannel(pixelPtr, 0, baseTypeBytes);
844				chanBits = bitSizes[chan];
845
846				switch (baseTypeBytes)
847				{
848					case 1:
849						chanUVal = unpackBits<deUint8>((deUint8)pack, bitOffsets[chan], bitSizes[chan]);
850						break;
851
852					case 2:
853						chanUVal = unpackBits<deUint16>((deUint16)pack, bitOffsets[chan], bitSizes[chan]);
854						break;
855
856					case 4:
857						chanUVal = unpackBits<deUint32>((deUint32)pack, bitOffsets[chan], bitSizes[chan]);
858						break;
859
860					default:
861						break;
862				}
863			}
864			else
865			{
866			    chanUVal = readChannel(pixelPtr, chan * chanSize, chanSize);
867				chanBits = 8 * chanSize;
868			}
869
870			deInt64 chanVal = 0;
871
872			if (isSigned)
873			{
874				chanVal = signExtend(chanUVal, chanBits);
875			}
876			else
877			{
878				chanVal = (deInt64) chanUVal;
879			}
880
881			convertNormalizedInt(chanVal, chanBits, isSigned, internalFormat, resultMin[compNdx], resultMax[compNdx]);
882		}
883	}
884}
885
886void convertFloatFormat (const void*	pixelPtr,
887						 TextureFormat	texFormat,
888						 FloatFormat	internalFormat,
889						 Vec4&			resultMin,
890						 Vec4&			resultMax)
891{
892	DE_ASSERT(getTextureChannelClass(texFormat.type) == TEXTURECHANNELCLASS_FLOATING_POINT);
893
894	const TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
895
896	for (int compNdx = 0; compNdx < 4; ++compNdx)
897	{
898		const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
899
900		if (chan == TextureSwizzle::CHANNEL_ZERO)
901		{
902			resultMin[compNdx] = 0.0f;
903			resultMax[compNdx] = 0.0f;
904		}
905		else if (chan == TextureSwizzle::CHANNEL_ONE)
906		{
907			resultMin[compNdx] = 1.0f;
908			resultMax[compNdx] = 1.0f;
909		}
910		else if (texFormat.type == TextureFormat::FLOAT)
911		{
912			resultMin[compNdx] = resultMax[compNdx] = *((const float*)pixelPtr + chan);
913		}
914		else if (texFormat.type == TextureFormat::HALF_FLOAT)
915		{
916			convertFP16((const deUint16*) pixelPtr + chan, internalFormat, resultMin[compNdx], resultMax[compNdx]);
917		}
918		else
919		{
920			DE_FATAL("Unsupported floating point format");
921		}
922	}
923}
924
925} // anonymous
926
927void convertFormat (const void*		pixelPtr,
928					TextureFormat	texFormat,
929					FloatFormat		internalFormat,
930					Vec4&			resultMin,
931					Vec4&			resultMax)
932{
933	const TextureChannelClass	chanClass	 = getTextureChannelClass(texFormat.type);
934
935	// \todo [2016-08-01 collinbaker] Handle float and shared exponent formats
936	if (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chanClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
937	{
938		convertNormalizedFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
939	}
940	else if (chanClass == TEXTURECHANNELCLASS_FLOATING_POINT)
941	{
942		convertFloatFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
943	}
944	else
945	{
946		DE_FATAL("Unimplemented");
947	}
948}
949
950} // util
951} // texture
952} // vkt
953