12f083884Ss.makeev_local // The MIT License (MIT)
22f083884Ss.makeev_local //
32f083884Ss.makeev_local // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
42f083884Ss.makeev_local //
52f083884Ss.makeev_local // 	Permission is hereby granted, free of charge, to any person obtaining a copy
62f083884Ss.makeev_local // 	of this software and associated documentation files (the "Software"), to deal
72f083884Ss.makeev_local // 	in the Software without restriction, including without limitation the rights
82f083884Ss.makeev_local // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92f083884Ss.makeev_local // 	copies of the Software, and to permit persons to whom the Software is
102f083884Ss.makeev_local // 	furnished to do so, subject to the following conditions:
112f083884Ss.makeev_local //
122f083884Ss.makeev_local //  The above copyright notice and this permission notice shall be included in
132f083884Ss.makeev_local // 	all copies or substantial portions of the Software.
142f083884Ss.makeev_local //
152f083884Ss.makeev_local // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162f083884Ss.makeev_local // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172f083884Ss.makeev_local // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
182f083884Ss.makeev_local // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
192f083884Ss.makeev_local // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
202f083884Ss.makeev_local // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
212f083884Ss.makeev_local // 	THE SOFTWARE.
222f083884Ss.makeev_local 
23*3d930776Ss.makeev_local 
24*3d930776Ss.makeev_local #include <MTConfig.h>
25*3d930776Ss.makeev_local 
26*3d930776Ss.makeev_local 
272f083884Ss.makeev_local #include "Tests.h"
282f083884Ss.makeev_local #include <UnitTest++.h>
292f083884Ss.makeev_local #include <MTScheduler.h>
30b23bdf5aSs.makeev_local #include <MTStaticVector.h>
312f083884Ss.makeev_local 
322f083884Ss.makeev_local #include <squish.h>
332f083884Ss.makeev_local #include <string.h>
34faa6c6e5Ss.makeev_local #include <math.h>
352f083884Ss.makeev_local 
3660ac17fbSs.makeev_local 
372f083884Ss.makeev_local /*
382f083884Ss.makeev_local #ifdef _WIN32
392f083884Ss.makeev_local 
402f083884Ss.makeev_local #include <conio.h>
412f083884Ss.makeev_local 
422f083884Ss.makeev_local #else
432f083884Ss.makeev_local 
442f083884Ss.makeev_local #include <stdio.h>
452f083884Ss.makeev_local #include <termios.h>
462f083884Ss.makeev_local #include <unistd.h>
472f083884Ss.makeev_local #include <fcntl.h>
482f083884Ss.makeev_local 
492f083884Ss.makeev_local int _kbhit(void)
502f083884Ss.makeev_local {
512f083884Ss.makeev_local 	struct termios oldt, newt;
522f083884Ss.makeev_local 	int ch;
532f083884Ss.makeev_local 	int oldf;
542f083884Ss.makeev_local 
552f083884Ss.makeev_local 	tcgetattr(STDIN_FILENO, &oldt);
562f083884Ss.makeev_local 	newt = oldt;
572f083884Ss.makeev_local 	newt.c_lflag &= ~(ICANON | ECHO);
582f083884Ss.makeev_local 	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
592f083884Ss.makeev_local 	oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
602f083884Ss.makeev_local 	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
612f083884Ss.makeev_local 
622f083884Ss.makeev_local 	ch = getchar();
632f083884Ss.makeev_local 
642f083884Ss.makeev_local 	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
652f083884Ss.makeev_local 	fcntl(STDIN_FILENO, F_SETFL, oldf);
662f083884Ss.makeev_local 
672f083884Ss.makeev_local 	if(ch != EOF)
682f083884Ss.makeev_local 	{
692f083884Ss.makeev_local 		ungetc(ch, stdin);
702f083884Ss.makeev_local 		return 1;
712f083884Ss.makeev_local 	}
722f083884Ss.makeev_local 
732f083884Ss.makeev_local 	return 0;
742f083884Ss.makeev_local }
752f083884Ss.makeev_local #endif
762f083884Ss.makeev_local */
772f083884Ss.makeev_local 
782f083884Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
792f083884Ss.makeev_local 
806e90b535Ss.makeev_local 
81*3d930776Ss.makeev_local void PushPerfMarker(const char* name, MT::Color::Type color)
82*3d930776Ss.makeev_local {
83*3d930776Ss.makeev_local 	MT_UNUSED(name);
84*3d930776Ss.makeev_local 	MT_UNUSED(color);
85*3d930776Ss.makeev_local }
86*3d930776Ss.makeev_local 
87*3d930776Ss.makeev_local void PopPerfMarker(const char* name)
88*3d930776Ss.makeev_local {
89*3d930776Ss.makeev_local 	MT_UNUSED(name);
90*3d930776Ss.makeev_local }
91*3d930776Ss.makeev_local 
926e90b535Ss.makeev_local 
932f083884Ss.makeev_local class Microprofile : public MT::IProfilerEventListener
942f083884Ss.makeev_local {
95*3d930776Ss.makeev_local public:
96*3d930776Ss.makeev_local 
97*3d930776Ss.makeev_local 	Microprofile()
98*3d930776Ss.makeev_local 	{
99*3d930776Ss.makeev_local 	}
100*3d930776Ss.makeev_local 
101*3d930776Ss.makeev_local 	~Microprofile()
102*3d930776Ss.makeev_local 	{
103*3d930776Ss.makeev_local 	}
104*3d930776Ss.makeev_local 
1056e90b535Ss.makeev_local 	virtual void OnThreadCreated(uint32 workerIndex) override
1062f083884Ss.makeev_local 	{
1072f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
1082f083884Ss.makeev_local 	}
1092f083884Ss.makeev_local 
1106e90b535Ss.makeev_local 	virtual void OnThreadStarted(uint32 workerIndex) override
1112f083884Ss.makeev_local 	{
1122f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
1132f083884Ss.makeev_local 	}
1142f083884Ss.makeev_local 
1156e90b535Ss.makeev_local 	virtual void OnThreadStoped(uint32 workerIndex) override
1162f083884Ss.makeev_local 	{
1172f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
1182f083884Ss.makeev_local 	}
1192f083884Ss.makeev_local 
120*3d930776Ss.makeev_local 	virtual void OnThreadIdleStarted(uint32 workerIndex) override
1212f083884Ss.makeev_local 	{
1222f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
123*3d930776Ss.makeev_local 		PushPerfMarker("ThreadIdle", MT::Color::Red);
1242f083884Ss.makeev_local 	}
1252f083884Ss.makeev_local 
126*3d930776Ss.makeev_local 	virtual void OnThreadIdleFinished(uint32 workerIndex) override
1272f083884Ss.makeev_local 	{
1282f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
129*3d930776Ss.makeev_local 		PopPerfMarker("ThreadIdle");
130*3d930776Ss.makeev_local 	}
131*3d930776Ss.makeev_local 
132*3d930776Ss.makeev_local 	virtual void OnThreadWaitStarted() override
133*3d930776Ss.makeev_local 	{
134*3d930776Ss.makeev_local 		PushPerfMarker("ThreadWait", MT::Color::Red);
135*3d930776Ss.makeev_local 	}
136*3d930776Ss.makeev_local 
137*3d930776Ss.makeev_local 	virtual void OnThreadWaitFinished() override
138*3d930776Ss.makeev_local 	{
139*3d930776Ss.makeev_local 		PopPerfMarker("ThreadWait");
1402f083884Ss.makeev_local 	}
1412f083884Ss.makeev_local 
1426e90b535Ss.makeev_local 	virtual void NotifyTaskExecuteStateChanged(MT::Color::Type debugColor, const mt_char* debugID, MT::TaskExecuteState::Type type) override
1432f083884Ss.makeev_local 	{
144*3d930776Ss.makeev_local 		switch(type)
145*3d930776Ss.makeev_local 		{
146*3d930776Ss.makeev_local 		case MT::TaskExecuteState::START:
147*3d930776Ss.makeev_local 		case MT::TaskExecuteState::RESUME:
148*3d930776Ss.makeev_local 			PushPerfMarker(debugID, debugColor);
149*3d930776Ss.makeev_local 			break;
150*3d930776Ss.makeev_local 		case MT::TaskExecuteState::STOP:
151*3d930776Ss.makeev_local 		case MT::TaskExecuteState::SUSPEND:
152*3d930776Ss.makeev_local 			PopPerfMarker(debugID);
153*3d930776Ss.makeev_local 			break;
1542f083884Ss.makeev_local 		}
155*3d930776Ss.makeev_local 	}
1562f083884Ss.makeev_local };
1572f083884Ss.makeev_local 
1582f083884Ss.makeev_local 
1592f083884Ss.makeev_local #endif
1602f083884Ss.makeev_local 
1612f083884Ss.makeev_local 
1622f083884Ss.makeev_local 
1632f083884Ss.makeev_local 
1642f083884Ss.makeev_local 
1652f083884Ss.makeev_local namespace EmbeddedImage
1662f083884Ss.makeev_local {
1672f083884Ss.makeev_local 	#include "LenaDxt/LenaColor.h"
1682f083884Ss.makeev_local 	#include "LenaDxt/HeaderDDS.h"
1692f083884Ss.makeev_local }
1702f083884Ss.makeev_local 
1712f083884Ss.makeev_local 
1722f083884Ss.makeev_local bool CompareImagesPSNR(uint8 * img1, uint8 * img2, uint32 bytesCount, double psnrThreshold)
1732f083884Ss.makeev_local {
1742f083884Ss.makeev_local 	double mse = 0.0;
1752f083884Ss.makeev_local 
1762f083884Ss.makeev_local 	for (uint32 i = 0; i < bytesCount; i++)
1772f083884Ss.makeev_local 	{
1782f083884Ss.makeev_local 		double error = (double)img1[0] - (double)img2[1];
1792f083884Ss.makeev_local 		mse += (error * error);
1802f083884Ss.makeev_local 	}
1812f083884Ss.makeev_local 
1822f083884Ss.makeev_local 	mse = mse / (double)bytesCount;
1832f083884Ss.makeev_local 
1842f083884Ss.makeev_local 	if (mse > 0.0)
1852f083884Ss.makeev_local 	{
1862f083884Ss.makeev_local 		double psnr = 10.0 * log10(255.0*255.0/mse);
1872f083884Ss.makeev_local 		if (psnr < psnrThreshold)
1882f083884Ss.makeev_local 		{
1892f083884Ss.makeev_local 			return false;
1902f083884Ss.makeev_local 		}
1912f083884Ss.makeev_local 	}
1922f083884Ss.makeev_local 
1932f083884Ss.makeev_local 	return true;
1942f083884Ss.makeev_local }
1952f083884Ss.makeev_local 
1962f083884Ss.makeev_local 
1972f083884Ss.makeev_local 
1982f083884Ss.makeev_local SUITE(DxtTests)
1992f083884Ss.makeev_local {
2002f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2012f083884Ss.makeev_local 	struct CompressDxtBlock
2022f083884Ss.makeev_local 	{
203b23bdf5aSs.makeev_local 		MT_DECLARE_TASK(CompressDxtBlock, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
2042f083884Ss.makeev_local 
2052f083884Ss.makeev_local 		MT::ArrayView<uint8> srcPixels;
2062f083884Ss.makeev_local 		MT::ArrayView<uint8> dstBlocks;
2072f083884Ss.makeev_local 
2082f083884Ss.makeev_local 		int srcX;
2092f083884Ss.makeev_local 		int srcY;
2102f083884Ss.makeev_local 
2112f083884Ss.makeev_local 		int stride;
2122f083884Ss.makeev_local 		int dstBlockOffset;
2132f083884Ss.makeev_local 
2142f083884Ss.makeev_local 		CompressDxtBlock(int _srcX, int _srcY, int _stride, const MT::ArrayView<uint8> & _srcPixels, const MT::ArrayView<uint8> & _dstBlocks, int _dstBlockOffset)
2152f083884Ss.makeev_local 			: srcPixels(_srcPixels)
2162f083884Ss.makeev_local 			, dstBlocks(_dstBlocks)
2172f083884Ss.makeev_local 		{
2182f083884Ss.makeev_local 				srcX = _srcX;
2192f083884Ss.makeev_local 				srcY = _srcY;
2202f083884Ss.makeev_local 				stride = _stride;
2212f083884Ss.makeev_local 				dstBlockOffset = _dstBlockOffset;
2222f083884Ss.makeev_local 		}
2232f083884Ss.makeev_local 
2242f083884Ss.makeev_local 		CompressDxtBlock(CompressDxtBlock&& other)
2252f083884Ss.makeev_local 			: srcPixels(other.srcPixels)
2262f083884Ss.makeev_local 			, dstBlocks(other.dstBlocks)
2272f083884Ss.makeev_local 			, srcX(other.srcX)
2282f083884Ss.makeev_local 			, srcY(other.srcY)
2292f083884Ss.makeev_local 			, stride(other.stride)
2302f083884Ss.makeev_local 			, dstBlockOffset(other.dstBlockOffset)
2312f083884Ss.makeev_local 		{
2322f083884Ss.makeev_local 			other.srcX = -1;
2332f083884Ss.makeev_local 			other.srcY = -1;
2342f083884Ss.makeev_local 			other.stride = -1;
2352f083884Ss.makeev_local 			other.dstBlockOffset = -1;
2362f083884Ss.makeev_local 		}
2372f083884Ss.makeev_local 
2382f083884Ss.makeev_local 		~CompressDxtBlock()
2392f083884Ss.makeev_local 		{
2402f083884Ss.makeev_local 			srcX = -1;
2412f083884Ss.makeev_local 			srcY = -1;
2422f083884Ss.makeev_local 			stride = -1;
2432f083884Ss.makeev_local 			dstBlockOffset = -1;
2442f083884Ss.makeev_local 		}
2452f083884Ss.makeev_local 
2462f083884Ss.makeev_local 		void Do(MT::FiberContext&)
2472f083884Ss.makeev_local 		{
2482f083884Ss.makeev_local 			// 16 pixels of input
2492f083884Ss.makeev_local 			uint32 pixels[4*4];
2502f083884Ss.makeev_local 
2512f083884Ss.makeev_local 			// copy dxt1 block from image
2522f083884Ss.makeev_local 			for (int y = 0; y < 4; y++)
2532f083884Ss.makeev_local 			{
2542f083884Ss.makeev_local 				for (int x = 0; x < 4; x++)
2552f083884Ss.makeev_local 				{
2562f083884Ss.makeev_local 					int posX = srcX + x;
2572f083884Ss.makeev_local 					int posY = srcY + y;
2582f083884Ss.makeev_local 
2592f083884Ss.makeev_local 					int index = posY * stride + (posX * 3);
2602f083884Ss.makeev_local 
2612f083884Ss.makeev_local 					MT_ASSERT(index >= 0 && ((size_t)(index + 2) < MT_ARRAY_SIZE(EmbeddedImage::lenaColor)), "Invalid index");
2622f083884Ss.makeev_local 
2632f083884Ss.makeev_local 					uint8 r = srcPixels[index + 0];
2642f083884Ss.makeev_local 					uint8 g = srcPixels[index + 1];
2652f083884Ss.makeev_local 					uint8 b = srcPixels[index + 2];
2662f083884Ss.makeev_local 
2672f083884Ss.makeev_local 					uint32 color = 0xFF000000 | ((b << 16) | (g << 8) | (r));
2682f083884Ss.makeev_local 
2692f083884Ss.makeev_local 					pixels[y * 4 + x] = color;
2702f083884Ss.makeev_local 				}
2712f083884Ss.makeev_local 			}
2722f083884Ss.makeev_local 
2732f083884Ss.makeev_local 			// compress the 4x4 block using DXT1 compression
2742f083884Ss.makeev_local 			squish::Compress( (squish::u8 *)&pixels[0], &dstBlocks[dstBlockOffset], squish::kDxt1 );
2752f083884Ss.makeev_local 		}
2762f083884Ss.makeev_local 	};
2772f083884Ss.makeev_local 
2782f083884Ss.makeev_local 
2792f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2802f083884Ss.makeev_local 	struct CompressDxt
2812f083884Ss.makeev_local 	{
282b23bdf5aSs.makeev_local 		MT_DECLARE_TASK(CompressDxt, MT::StackRequirements::EXTENDED, MT::TaskPriority::NORMAL, MT::Color::Aqua);
2832f083884Ss.makeev_local 
2842f083884Ss.makeev_local 		uint32 width;
2852f083884Ss.makeev_local 		uint32 height;
2862f083884Ss.makeev_local 		uint32 stride;
2872f083884Ss.makeev_local 
2882f083884Ss.makeev_local 		uint32 blkWidth;
2892f083884Ss.makeev_local 		uint32 blkHeight;
2902f083884Ss.makeev_local 
291*3d930776Ss.makeev_local 		uint32 passCount;
292*3d930776Ss.makeev_local 
2932f083884Ss.makeev_local 		MT::ArrayView<uint8> srcPixels;
2942f083884Ss.makeev_local 		MT::ArrayView<uint8> dxtBlocks;
295d7cf17b1Ss.makeev_local 		MT::Atomic32<uint32>* pIsFinished;
2962f083884Ss.makeev_local 
297d7cf17b1Ss.makeev_local 
298*3d930776Ss.makeev_local 		CompressDxt(uint32 _width, uint32 _height, uint32 _stride, const MT::ArrayView<uint8> & _srcPixels, MT::Atomic32<uint32>* _pIsFinished = nullptr, uint32 _passCount = 1)
2992f083884Ss.makeev_local 			: srcPixels(_srcPixels)
300d7cf17b1Ss.makeev_local 			, pIsFinished(_pIsFinished)
3012f083884Ss.makeev_local 		{
302*3d930776Ss.makeev_local 			passCount = _passCount;
303*3d930776Ss.makeev_local 
3042f083884Ss.makeev_local 			width = _width;
3052f083884Ss.makeev_local 			height = _height;
3062f083884Ss.makeev_local 			stride = _stride;
3072f083884Ss.makeev_local 
3082f083884Ss.makeev_local 			blkWidth = width >> 2;
3092f083884Ss.makeev_local 			blkHeight = height >> 2;
3102f083884Ss.makeev_local 
3112f083884Ss.makeev_local 			int dxtBlocksTotalSizeInBytes = blkWidth * blkHeight * 8; // 8 bytes = 64 bits per block (dxt1)
312d7cf17b1Ss.makeev_local 			dxtBlocks = MT::ArrayView<uint8>( MT::Memory::Alloc( dxtBlocksTotalSizeInBytes ), dxtBlocksTotalSizeInBytes);
3132f083884Ss.makeev_local 		}
3142f083884Ss.makeev_local 
3152f083884Ss.makeev_local 		~CompressDxt()
3162f083884Ss.makeev_local 		{
3172f083884Ss.makeev_local 			void* pDxtBlocks = dxtBlocks.GetRawData();
3182f083884Ss.makeev_local 			if (pDxtBlocks)
3192f083884Ss.makeev_local 			{
320d7cf17b1Ss.makeev_local 				MT::Memory::Free(pDxtBlocks);
3212f083884Ss.makeev_local 			}
3222f083884Ss.makeev_local 		}
3232f083884Ss.makeev_local 
3242f083884Ss.makeev_local 
3252f083884Ss.makeev_local 		void Do(MT::FiberContext& context)
3262f083884Ss.makeev_local 		{
327*3d930776Ss.makeev_local 			for(uint32 i = 0; i < passCount; i++)
328*3d930776Ss.makeev_local 			{
329b23bdf5aSs.makeev_local 				// use StaticVector as subtask container. beware stack overflow!
330b23bdf5aSs.makeev_local 				MT::StaticVector<CompressDxtBlock, 1024> subTasks;
3312f083884Ss.makeev_local 
3322f083884Ss.makeev_local 				for (uint32 blkY = 0; blkY < blkHeight; blkY++)
3332f083884Ss.makeev_local 				{
3342f083884Ss.makeev_local 					for (uint32 blkX = 0; blkX < blkWidth; blkX++)
3352f083884Ss.makeev_local 					{
3362f083884Ss.makeev_local 						uint32 blockIndex = blkY * blkWidth + blkX;
3372f083884Ss.makeev_local 						subTasks.PushBack( CompressDxtBlock(blkX * 4, blkY * 4, stride, srcPixels, dxtBlocks, blockIndex * 8) );
3382f083884Ss.makeev_local 					}
3392f083884Ss.makeev_local 				}
3402f083884Ss.makeev_local 
3412f083884Ss.makeev_local 				context.RunSubtasksAndYield(MT::TaskGroup::Default(), &subTasks[0], subTasks.Size());
342*3d930776Ss.makeev_local 			}
343*3d930776Ss.makeev_local 
344d7cf17b1Ss.makeev_local 
345d7cf17b1Ss.makeev_local 			if (pIsFinished != nullptr)
346d7cf17b1Ss.makeev_local 			{
347d7cf17b1Ss.makeev_local 				pIsFinished->Store(1);
348d7cf17b1Ss.makeev_local 			}
3492f083884Ss.makeev_local 		}
3502f083884Ss.makeev_local 	};
3512f083884Ss.makeev_local 
3522f083884Ss.makeev_local 
3532f083884Ss.makeev_local 
3542f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3552f083884Ss.makeev_local 	struct DecompressDxtBlock
3562f083884Ss.makeev_local 	{
357b23bdf5aSs.makeev_local 		MT_DECLARE_TASK(DecompressDxtBlock, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Red);
3582f083884Ss.makeev_local 
3592f083884Ss.makeev_local 		MT::ArrayView<uint8> srcBlocks;
3602f083884Ss.makeev_local 		MT::ArrayView<uint8> dstPixels;
3612f083884Ss.makeev_local 
3622f083884Ss.makeev_local 		int dstX;
3632f083884Ss.makeev_local 		int dstY;
3642f083884Ss.makeev_local 
3652f083884Ss.makeev_local 		int stride;
3662f083884Ss.makeev_local 		int srcBlockOffset;
3672f083884Ss.makeev_local 
3682f083884Ss.makeev_local 		DecompressDxtBlock(int _dstX, int _dstY, int _stride, const MT::ArrayView<uint8> & _dstPixels, const MT::ArrayView<uint8> & _srcBlocks, int _srcBlockOffset)
3692f083884Ss.makeev_local 			: srcBlocks(_srcBlocks)
3702f083884Ss.makeev_local 			, dstPixels(_dstPixels)
3712f083884Ss.makeev_local 		{
3722f083884Ss.makeev_local 			dstX = _dstX;
3732f083884Ss.makeev_local 			dstY = _dstY;
3742f083884Ss.makeev_local 			stride = _stride;
3752f083884Ss.makeev_local 			srcBlockOffset = _srcBlockOffset;
3762f083884Ss.makeev_local 		}
3772f083884Ss.makeev_local 
3782f083884Ss.makeev_local 		DecompressDxtBlock(DecompressDxtBlock&& other)
3792f083884Ss.makeev_local 			: srcBlocks(other.srcBlocks)
3802f083884Ss.makeev_local 			, dstPixels(other.dstPixels)
3812f083884Ss.makeev_local 			, dstX(other.dstX)
3822f083884Ss.makeev_local 			, dstY(other.dstY)
3832f083884Ss.makeev_local 			, stride(other.stride)
3842f083884Ss.makeev_local 			, srcBlockOffset(other.srcBlockOffset)
3852f083884Ss.makeev_local 		{
3862f083884Ss.makeev_local 			other.dstX = -1;
3872f083884Ss.makeev_local 			other.dstY = -1;
3882f083884Ss.makeev_local 			other.stride = -1;
3892f083884Ss.makeev_local 			other.srcBlockOffset = -1;
3902f083884Ss.makeev_local 		}
3912f083884Ss.makeev_local 
3922f083884Ss.makeev_local 		~DecompressDxtBlock()
3932f083884Ss.makeev_local 		{
3942f083884Ss.makeev_local 			dstX = -1;
3952f083884Ss.makeev_local 			dstY = -1;
3962f083884Ss.makeev_local 			stride = -1;
3972f083884Ss.makeev_local 			srcBlockOffset = -1;
3982f083884Ss.makeev_local 		}
3992f083884Ss.makeev_local 
4002f083884Ss.makeev_local 
4012f083884Ss.makeev_local 		void Do(MT::FiberContext&)
4022f083884Ss.makeev_local 		{
4032f083884Ss.makeev_local 			// 16 pixels of output
4042f083884Ss.makeev_local 			uint32 pixels[4*4];
4052f083884Ss.makeev_local 
4062f083884Ss.makeev_local 			// copy dxt1 block from image
4072f083884Ss.makeev_local 			for (int y = 0; y < 4; y++)
4082f083884Ss.makeev_local 			{
4092f083884Ss.makeev_local 				for (int x = 0; x < 4; x++)
4102f083884Ss.makeev_local 				{
4112f083884Ss.makeev_local 					squish::Decompress((squish::u8 *)&pixels[0], &srcBlocks[srcBlockOffset], squish::kDxt1);
4122f083884Ss.makeev_local 
4132f083884Ss.makeev_local 					int posX = dstX + x;
4142f083884Ss.makeev_local 					int posY = dstY + y;
4152f083884Ss.makeev_local 
4162f083884Ss.makeev_local 					int index = posY * stride + (posX * 3);
4172f083884Ss.makeev_local 
4182f083884Ss.makeev_local 					uint32 pixel = pixels[y * 4 + x];
4192f083884Ss.makeev_local 
4202f083884Ss.makeev_local 					MT_ASSERT(index >= 0 && ((size_t)(index + 2) < MT_ARRAY_SIZE(EmbeddedImage::lenaColor)), "Invalid index");
4212f083884Ss.makeev_local 
4222f083884Ss.makeev_local 					dstPixels[index + 0] = (pixel & 0xFF);
4232f083884Ss.makeev_local 					dstPixels[index + 1] = (pixel >> 8 & 0xFF);
4242f083884Ss.makeev_local 					dstPixels[index + 2] = (pixel >> 16 & 0xFF);
4252f083884Ss.makeev_local 				}
4262f083884Ss.makeev_local 			}
4272f083884Ss.makeev_local 
4282f083884Ss.makeev_local 		}
4292f083884Ss.makeev_local 	};
4302f083884Ss.makeev_local 
4312f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4322f083884Ss.makeev_local 	struct DecompressDxt
4332f083884Ss.makeev_local 	{
434b23bdf5aSs.makeev_local 		MT_DECLARE_TASK(DecompressDxt, MT::StackRequirements::EXTENDED, MT::TaskPriority::NORMAL, MT::Color::Yellow);
4352f083884Ss.makeev_local 
4362f083884Ss.makeev_local 		MT::ArrayView<uint8> dxtBlocks;
4372f083884Ss.makeev_local 		MT::ArrayView<uint8> decompressedImage;
4382f083884Ss.makeev_local 
4392f083884Ss.makeev_local 		uint32 blkWidth;
4402f083884Ss.makeev_local 		uint32 blkHeight;
4412f083884Ss.makeev_local 
4422f083884Ss.makeev_local 
4432f083884Ss.makeev_local 		DecompressDxt(const MT::ArrayView<uint8> & _dxtBlocks, uint32 dxtBlocksCountWidth, uint32 dxtBlocksCountHeight)
4442f083884Ss.makeev_local 			: dxtBlocks(_dxtBlocks)
4452f083884Ss.makeev_local 		{
4462f083884Ss.makeev_local 			blkWidth = dxtBlocksCountWidth;
4472f083884Ss.makeev_local 			blkHeight = dxtBlocksCountHeight;
4482f083884Ss.makeev_local 
4492f083884Ss.makeev_local 			// dxt1 block = 16 rgb pixels = 48 bytes
4502f083884Ss.makeev_local 			uint32 bytesCount = blkWidth * blkHeight * 48;
451d7cf17b1Ss.makeev_local 			decompressedImage = MT::ArrayView<uint8>( MT::Memory::Alloc(bytesCount), bytesCount);
4522f083884Ss.makeev_local 		}
4532f083884Ss.makeev_local 
4542f083884Ss.makeev_local 		~DecompressDxt()
4552f083884Ss.makeev_local 		{
4562f083884Ss.makeev_local 			void* pDxtBlocks = dxtBlocks.GetRawData();
4572f083884Ss.makeev_local 			if (pDxtBlocks)
4582f083884Ss.makeev_local 			{
459d7cf17b1Ss.makeev_local 				MT::Memory::Free(pDxtBlocks);
4602f083884Ss.makeev_local 			}
4612f083884Ss.makeev_local 
4622f083884Ss.makeev_local 			void* pDecompressedImage = decompressedImage.GetRawData();
4632f083884Ss.makeev_local 			if (pDecompressedImage)
4642f083884Ss.makeev_local 			{
465d7cf17b1Ss.makeev_local 				MT::Memory::Free(pDecompressedImage);
4662f083884Ss.makeev_local 			}
4672f083884Ss.makeev_local 
4682f083884Ss.makeev_local 		}
4692f083884Ss.makeev_local 
4702f083884Ss.makeev_local 		void Do(MT::FiberContext& context)
4712f083884Ss.makeev_local 		{
472b23bdf5aSs.makeev_local 			// use StaticVector as subtask container. beware stack overflow!
473b23bdf5aSs.makeev_local 			MT::StaticVector<DecompressDxtBlock, 1024> subTasks;
4742f083884Ss.makeev_local 
4752f083884Ss.makeev_local 			int stride = blkWidth * 4 * 3;
4762f083884Ss.makeev_local 
4772f083884Ss.makeev_local 			for (uint32 blkY = 0; blkY < blkHeight; blkY++)
4782f083884Ss.makeev_local 			{
4792f083884Ss.makeev_local 				for (uint32 blkX = 0; blkX < blkWidth; blkX++)
4802f083884Ss.makeev_local 				{
4812f083884Ss.makeev_local 					uint32 blockIndex = blkY * blkWidth + blkX;
4822f083884Ss.makeev_local 					subTasks.PushBack( DecompressDxtBlock(blkX * 4, blkY * 4, stride, decompressedImage, dxtBlocks, blockIndex * 8) );
4832f083884Ss.makeev_local 				}
4842f083884Ss.makeev_local 			}
4852f083884Ss.makeev_local 
4862f083884Ss.makeev_local 			context.RunSubtasksAndYield(MT::TaskGroup::Default(), &subTasks[0], subTasks.Size());
4872f083884Ss.makeev_local 		}
4882f083884Ss.makeev_local 
4892f083884Ss.makeev_local 	};
4902f083884Ss.makeev_local 
4912f083884Ss.makeev_local 
4922f083884Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4932f083884Ss.makeev_local 	void Wait(MT::TaskScheduler & scheduler)
4942f083884Ss.makeev_local 	{
4952f083884Ss.makeev_local 		//emulate game loop
4962f083884Ss.makeev_local 		for(;;)
4972f083884Ss.makeev_local 		{
4982f083884Ss.makeev_local 			bool waitDone = scheduler.WaitAll(33);
4992f083884Ss.makeev_local 			if (waitDone)
5002f083884Ss.makeev_local 			{
5012f083884Ss.makeev_local 				break;
5022f083884Ss.makeev_local 			}
5032f083884Ss.makeev_local 		}
5042f083884Ss.makeev_local 	}
5052f083884Ss.makeev_local 
5064965e494Ss.makeev_local 
5076e90b535Ss.makeev_local /*
508d7cf17b1Ss.makeev_local 	// dxt compressor Hiload test (for profiling purposes)
509d7cf17b1Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
510d7cf17b1Ss.makeev_local 	TEST(HiloadDxtTest)
511d7cf17b1Ss.makeev_local 	{
512d7cf17b1Ss.makeev_local 		MT::Atomic32<uint32> isFinished1;
513d7cf17b1Ss.makeev_local 		MT::Atomic32<uint32> isFinished2;
514d7cf17b1Ss.makeev_local 
515d7cf17b1Ss.makeev_local 		static_assert(MT_ARRAY_SIZE(EmbeddedImage::lenaColor) == 49152, "Image size is invalid");
516d7cf17b1Ss.makeev_local 
517d7cf17b1Ss.makeev_local 		int stride = 384;
518d7cf17b1Ss.makeev_local 
519d7cf17b1Ss.makeev_local 		MT::ArrayView<uint8> srcImage((void*)&EmbeddedImage::lenaColor[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor));
520d7cf17b1Ss.makeev_local 
521d7cf17b1Ss.makeev_local 		CompressDxt compressTask1(128, 128, stride, srcImage, &isFinished1);
522d7cf17b1Ss.makeev_local 		MT_ASSERT ((compressTask1.width & 3) == 0 && (compressTask1.height & 3) == 0, "Image size must be a multiple of 4");
523d7cf17b1Ss.makeev_local 
524d7cf17b1Ss.makeev_local 		CompressDxt compressTask2(128, 128, stride, srcImage, &isFinished2);
525d7cf17b1Ss.makeev_local 		MT_ASSERT ((compressTask2.width & 3) == 0 && (compressTask2.height & 3) == 0, "Image size must be a multiple of 4");
526d7cf17b1Ss.makeev_local 
5276e90b535Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
5286e90b535Ss.makeev_local 		Microprofile profiler;
5296e90b535Ss.makeev_local 		MT::TaskScheduler scheduler(0, nullptr, &profiler);
5306e90b535Ss.makeev_local #else
531d7cf17b1Ss.makeev_local 		MT::TaskScheduler scheduler;
5326e90b535Ss.makeev_local #endif
533d7cf17b1Ss.makeev_local 
534d7cf17b1Ss.makeev_local 		int workersCount = (int)scheduler.GetWorkersCount();
535d7cf17b1Ss.makeev_local 		printf("Scheduler started, %d workers\n", workersCount);
536d7cf17b1Ss.makeev_local 
537d7cf17b1Ss.makeev_local 		isFinished1.Store(0);
538d7cf17b1Ss.makeev_local 		isFinished2.Store(0);
539d7cf17b1Ss.makeev_local 
540d7cf17b1Ss.makeev_local 		printf("HiloadDxtTest\n");
541d7cf17b1Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask1, 1);
542d7cf17b1Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask2, 1);
543d7cf17b1Ss.makeev_local 
544d7cf17b1Ss.makeev_local 		for(;;)
545d7cf17b1Ss.makeev_local 		{
546d7cf17b1Ss.makeev_local 			if (isFinished1.Load() != 0)
547d7cf17b1Ss.makeev_local 			{
548d7cf17b1Ss.makeev_local 				isFinished1.Store(0);
549d7cf17b1Ss.makeev_local 				scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask1, 1);
550d7cf17b1Ss.makeev_local 			}
551d7cf17b1Ss.makeev_local 
552d7cf17b1Ss.makeev_local 			if (isFinished2.Load() != 0)
553d7cf17b1Ss.makeev_local 			{
554d7cf17b1Ss.makeev_local 				isFinished2.Store(0);
555d7cf17b1Ss.makeev_local 				scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask2, 1);
556d7cf17b1Ss.makeev_local 			}
557d7cf17b1Ss.makeev_local 
558d7cf17b1Ss.makeev_local 			MT::Thread::Sleep(1);
559d7cf17b1Ss.makeev_local 		}
560d7cf17b1Ss.makeev_local 	}
561c121d748Ss.makeev_local */
56260ac17fbSs.makeev_local 
563*3d930776Ss.makeev_local /*
564*3d930776Ss.makeev_local 	// dxt compressor stress test (for profiling purposes)
565*3d930776Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
566*3d930776Ss.makeev_local 	TEST(DxtStressTest)
567*3d930776Ss.makeev_local 	{
568*3d930776Ss.makeev_local 		static_assert(MT_ARRAY_SIZE(EmbeddedImage::lenaColor) == 49152, "Image size is invalid");
569*3d930776Ss.makeev_local 
570*3d930776Ss.makeev_local 		int stride = 384;
571*3d930776Ss.makeev_local 
572*3d930776Ss.makeev_local 		MT::ArrayView<uint8> srcImage((void*)&EmbeddedImage::lenaColor[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor));
573*3d930776Ss.makeev_local 
574*3d930776Ss.makeev_local 		uint32 passCount = 10;
575*3d930776Ss.makeev_local 
576*3d930776Ss.makeev_local 		CompressDxt compressTask(128, 128, stride, srcImage, nullptr, passCount);
577*3d930776Ss.makeev_local 		MT_ASSERT ((compressTask.width & 3) == 0 && (compressTask.height & 3) == 0, "Image size must be a multiple of 4");
578*3d930776Ss.makeev_local 
579*3d930776Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
580*3d930776Ss.makeev_local 		Microprofile profiler;
581*3d930776Ss.makeev_local 		MT::TaskScheduler scheduler(0, nullptr, &profiler);
582*3d930776Ss.makeev_local #else
583*3d930776Ss.makeev_local 		MT::TaskScheduler scheduler;
584*3d930776Ss.makeev_local #endif
585*3d930776Ss.makeev_local 
586*3d930776Ss.makeev_local 		int workersCount = (int)scheduler.GetWorkersCount();
587*3d930776Ss.makeev_local 		printf("Scheduler started, %d workers\n", workersCount);
588*3d930776Ss.makeev_local 
589*3d930776Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
590*3d930776Ss.makeev_local 		PushPerfMarker("DxtStressTest", MT::Color::Red);
591*3d930776Ss.makeev_local #endif
592*3d930776Ss.makeev_local 
593*3d930776Ss.makeev_local 		printf("DxtStressTest\n");
594*3d930776Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask, 1);
595*3d930776Ss.makeev_local 
596*3d930776Ss.makeev_local 		CHECK(scheduler.WaitAll(10000000));
597*3d930776Ss.makeev_local 
598*3d930776Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
599*3d930776Ss.makeev_local 		PopPerfMarker("DxtStressTest");
600*3d930776Ss.makeev_local #endif
601*3d930776Ss.makeev_local 	}
602*3d930776Ss.makeev_local */
603*3d930776Ss.makeev_local 
6042f083884Ss.makeev_local 	// dxt compressor complex test
6052f083884Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
6062f083884Ss.makeev_local 	TEST(RunComplexDxtTest)
6072f083884Ss.makeev_local 	{
6082f083884Ss.makeev_local 		static_assert(MT_ARRAY_SIZE(EmbeddedImage::lenaColor) == 49152, "Image size is invalid");
6092f083884Ss.makeev_local 
6102f083884Ss.makeev_local 		int stride = 384;
6112f083884Ss.makeev_local 
6122f083884Ss.makeev_local 		MT::ArrayView<uint8> srcImage((void*)&EmbeddedImage::lenaColor[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor));
6132f083884Ss.makeev_local 
6142f083884Ss.makeev_local 		CompressDxt compressTask(128, 128, stride, srcImage);
6152f083884Ss.makeev_local 		MT_ASSERT ((compressTask.width & 3) == 0 && (compressTask.height & 3) == 0, "Image size must be a multiple of 4");
6162f083884Ss.makeev_local 
6172f083884Ss.makeev_local 		MT::TaskScheduler scheduler;
6182f083884Ss.makeev_local 
6192f083884Ss.makeev_local 		int workersCount = (int)scheduler.GetWorkersCount();
6202f083884Ss.makeev_local 		printf("Scheduler started, %d workers\n", workersCount);
6212f083884Ss.makeev_local 
6222f083884Ss.makeev_local 		printf("Compress image\n");
6232f083884Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask, 1);
6242f083884Ss.makeev_local 
6252f083884Ss.makeev_local 		Wait(scheduler);
6262f083884Ss.makeev_local 
6272f083884Ss.makeev_local 		DecompressDxt decompressTask(compressTask.dxtBlocks, compressTask.blkWidth, compressTask.blkHeight);
6282f083884Ss.makeev_local 		compressTask.dxtBlocks = MT::ArrayView<uint8>(); //transfer memory ownership to Decompress task
6292f083884Ss.makeev_local 
6302f083884Ss.makeev_local 		printf("Decompress image\n");
6312f083884Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &decompressTask, 1);
6322f083884Ss.makeev_local 
6332f083884Ss.makeev_local 		Wait(scheduler);
6342f083884Ss.makeev_local 
6352f083884Ss.makeev_local /*
6362f083884Ss.makeev_local 		//save compressed image
6372f083884Ss.makeev_local 		{
6382f083884Ss.makeev_local 			FILE * file = fopen("lena_dxt1.dds", "w+b");
6392f083884Ss.makeev_local 			fwrite(&EmbeddedImage::ddsHeader[0], MT_ARRAY_SIZE(EmbeddedImage::ddsHeader), 1, file);
6402f083884Ss.makeev_local 			fwrite(decompressTask.dxtBlocks, decompressTask.blkWidth * decompressTask.blkHeight * 8, 1, file);
6412f083884Ss.makeev_local 			fclose(file);
6422f083884Ss.makeev_local 		}
6432f083884Ss.makeev_local 
6442f083884Ss.makeev_local 		//save uncompressed image
6452f083884Ss.makeev_local 		{
6462f083884Ss.makeev_local 			FILE * file = fopen("lena_rgb.raw", "w+b");
6472f083884Ss.makeev_local 			fwrite(decompressTask.decompressedImage, decompressTask.blkWidth * decompressTask.blkHeight * 48, 1, file);
6482f083884Ss.makeev_local 			fclose(file);
6492f083884Ss.makeev_local 		}
6502f083884Ss.makeev_local */
6512f083884Ss.makeev_local 
6522f083884Ss.makeev_local 		printf("Compare images\n");
6532f083884Ss.makeev_local 		bool imagesEqual = CompareImagesPSNR(&srcImage[0], &decompressTask.decompressedImage[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor), 8.0);
6542f083884Ss.makeev_local 		CHECK_EQUAL(true, imagesEqual);
6552f083884Ss.makeev_local 
6562f083884Ss.makeev_local /*
6572f083884Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
6582f083884Ss.makeev_local 		// waiting for profiler attach
6592f083884Ss.makeev_local 		printf("Press any key to continue\n");
6602f083884Ss.makeev_local 		while(true)
6612f083884Ss.makeev_local 		{
6622f083884Ss.makeev_local 			if (_kbhit() != 0)
6632f083884Ss.makeev_local 			{
6642f083884Ss.makeev_local 				break;
6652f083884Ss.makeev_local 			}
6662f083884Ss.makeev_local 		}
6672f083884Ss.makeev_local #endif
6682f083884Ss.makeev_local */
6692f083884Ss.makeev_local 	}
6702f083884Ss.makeev_local 
6712f083884Ss.makeev_local 
6722f083884Ss.makeev_local }
673