1*2f083884Ss.makeev_local // The MIT License (MIT)
2*2f083884Ss.makeev_local //
3*2f083884Ss.makeev_local // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4*2f083884Ss.makeev_local //
5*2f083884Ss.makeev_local // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6*2f083884Ss.makeev_local // 	of this software and associated documentation files (the "Software"), to deal
7*2f083884Ss.makeev_local // 	in the Software without restriction, including without limitation the rights
8*2f083884Ss.makeev_local // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9*2f083884Ss.makeev_local // 	copies of the Software, and to permit persons to whom the Software is
10*2f083884Ss.makeev_local // 	furnished to do so, subject to the following conditions:
11*2f083884Ss.makeev_local //
12*2f083884Ss.makeev_local //  The above copyright notice and this permission notice shall be included in
13*2f083884Ss.makeev_local // 	all copies or substantial portions of the Software.
14*2f083884Ss.makeev_local //
15*2f083884Ss.makeev_local // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*2f083884Ss.makeev_local // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*2f083884Ss.makeev_local // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18*2f083884Ss.makeev_local // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*2f083884Ss.makeev_local // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20*2f083884Ss.makeev_local // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21*2f083884Ss.makeev_local // 	THE SOFTWARE.
22*2f083884Ss.makeev_local 
23*2f083884Ss.makeev_local #include "Tests.h"
24*2f083884Ss.makeev_local #include <UnitTest++.h>
25*2f083884Ss.makeev_local #include <MTScheduler.h>
26*2f083884Ss.makeev_local 
27*2f083884Ss.makeev_local #include <squish.h>
28*2f083884Ss.makeev_local #include <string.h>
29*2f083884Ss.makeev_local 
30*2f083884Ss.makeev_local /*
31*2f083884Ss.makeev_local #ifdef _WIN32
32*2f083884Ss.makeev_local 
33*2f083884Ss.makeev_local #include <conio.h>
34*2f083884Ss.makeev_local 
35*2f083884Ss.makeev_local #else
36*2f083884Ss.makeev_local 
37*2f083884Ss.makeev_local #include <math.h>
38*2f083884Ss.makeev_local #include <stdio.h>
39*2f083884Ss.makeev_local #include <termios.h>
40*2f083884Ss.makeev_local #include <unistd.h>
41*2f083884Ss.makeev_local #include <fcntl.h>
42*2f083884Ss.makeev_local 
43*2f083884Ss.makeev_local int _kbhit(void)
44*2f083884Ss.makeev_local {
45*2f083884Ss.makeev_local 	struct termios oldt, newt;
46*2f083884Ss.makeev_local 	int ch;
47*2f083884Ss.makeev_local 	int oldf;
48*2f083884Ss.makeev_local 
49*2f083884Ss.makeev_local 	tcgetattr(STDIN_FILENO, &oldt);
50*2f083884Ss.makeev_local 	newt = oldt;
51*2f083884Ss.makeev_local 	newt.c_lflag &= ~(ICANON | ECHO);
52*2f083884Ss.makeev_local 	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
53*2f083884Ss.makeev_local 	oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
54*2f083884Ss.makeev_local 	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
55*2f083884Ss.makeev_local 
56*2f083884Ss.makeev_local 	ch = getchar();
57*2f083884Ss.makeev_local 
58*2f083884Ss.makeev_local 	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
59*2f083884Ss.makeev_local 	fcntl(STDIN_FILENO, F_SETFL, oldf);
60*2f083884Ss.makeev_local 
61*2f083884Ss.makeev_local 	if(ch != EOF)
62*2f083884Ss.makeev_local 	{
63*2f083884Ss.makeev_local 		ungetc(ch, stdin);
64*2f083884Ss.makeev_local 		return 1;
65*2f083884Ss.makeev_local 	}
66*2f083884Ss.makeev_local 
67*2f083884Ss.makeev_local 	return 0;
68*2f083884Ss.makeev_local }
69*2f083884Ss.makeev_local #endif
70*2f083884Ss.makeev_local */
71*2f083884Ss.makeev_local 
72*2f083884Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
73*2f083884Ss.makeev_local 
74*2f083884Ss.makeev_local class Microprofile : public MT::IProfilerEventListener
75*2f083884Ss.makeev_local {
76*2f083884Ss.makeev_local 	void OnThreadCreated(uint32 workerIndex)
77*2f083884Ss.makeev_local 	{
78*2f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
79*2f083884Ss.makeev_local 	}
80*2f083884Ss.makeev_local 
81*2f083884Ss.makeev_local 	void OnThreadStarted(uint32 workerIndex)
82*2f083884Ss.makeev_local 	{
83*2f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
84*2f083884Ss.makeev_local 	}
85*2f083884Ss.makeev_local 
86*2f083884Ss.makeev_local 	void OnThreadStoped(uint32 workerIndex)
87*2f083884Ss.makeev_local 	{
88*2f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
89*2f083884Ss.makeev_local 	}
90*2f083884Ss.makeev_local 
91*2f083884Ss.makeev_local 	void OnThreadIdleBegin(uint32 workerIndex)
92*2f083884Ss.makeev_local 	{
93*2f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
94*2f083884Ss.makeev_local 	}
95*2f083884Ss.makeev_local 
96*2f083884Ss.makeev_local 	void OnThreadIdleEnd(uint32 workerIndex)
97*2f083884Ss.makeev_local 	{
98*2f083884Ss.makeev_local 		MT_UNUSED(workerIndex);
99*2f083884Ss.makeev_local 	}
100*2f083884Ss.makeev_local 
101*2f083884Ss.makeev_local 	void OnTaskFinished(MT::Color::Type debugColor, const mt_char* debugID)
102*2f083884Ss.makeev_local 	{
103*2f083884Ss.makeev_local 		MT_UNUSED(debugColor);
104*2f083884Ss.makeev_local 		MT_UNUSED(debugID);
105*2f083884Ss.makeev_local 	}
106*2f083884Ss.makeev_local 
107*2f083884Ss.makeev_local 	void OnTaskResumed(MT::Color::Type debugColor, const mt_char* debugID)
108*2f083884Ss.makeev_local 	{
109*2f083884Ss.makeev_local 		MT_UNUSED(debugColor);
110*2f083884Ss.makeev_local 		MT_UNUSED(debugID);
111*2f083884Ss.makeev_local 	}
112*2f083884Ss.makeev_local 
113*2f083884Ss.makeev_local 	void OnTaskYielded(MT::Color::Type debugColor, const mt_char* debugID)
114*2f083884Ss.makeev_local 	{
115*2f083884Ss.makeev_local 		MT_UNUSED(debugColor);
116*2f083884Ss.makeev_local 		MT_UNUSED(debugID);
117*2f083884Ss.makeev_local 	}
118*2f083884Ss.makeev_local 
119*2f083884Ss.makeev_local };
120*2f083884Ss.makeev_local 
121*2f083884Ss.makeev_local 
122*2f083884Ss.makeev_local #endif
123*2f083884Ss.makeev_local 
124*2f083884Ss.makeev_local 
125*2f083884Ss.makeev_local 
126*2f083884Ss.makeev_local 
127*2f083884Ss.makeev_local 
128*2f083884Ss.makeev_local namespace EmbeddedImage
129*2f083884Ss.makeev_local {
130*2f083884Ss.makeev_local 	#include "LenaDxt/LenaColor.h"
131*2f083884Ss.makeev_local 	#include "LenaDxt/HeaderDDS.h"
132*2f083884Ss.makeev_local }
133*2f083884Ss.makeev_local 
134*2f083884Ss.makeev_local 
135*2f083884Ss.makeev_local bool CompareImagesPSNR(uint8 * img1, uint8 * img2, uint32 bytesCount, double psnrThreshold)
136*2f083884Ss.makeev_local {
137*2f083884Ss.makeev_local 	double mse = 0.0;
138*2f083884Ss.makeev_local 
139*2f083884Ss.makeev_local 	for (uint32 i = 0; i < bytesCount; i++)
140*2f083884Ss.makeev_local 	{
141*2f083884Ss.makeev_local 		double error = (double)img1[0] - (double)img2[1];
142*2f083884Ss.makeev_local 		mse += (error * error);
143*2f083884Ss.makeev_local 	}
144*2f083884Ss.makeev_local 
145*2f083884Ss.makeev_local 	mse = mse / (double)bytesCount;
146*2f083884Ss.makeev_local 
147*2f083884Ss.makeev_local 	if (mse > 0.0)
148*2f083884Ss.makeev_local 	{
149*2f083884Ss.makeev_local 		double psnr = 10.0 * log10(255.0*255.0/mse);
150*2f083884Ss.makeev_local 		if (psnr < psnrThreshold)
151*2f083884Ss.makeev_local 		{
152*2f083884Ss.makeev_local 			return false;
153*2f083884Ss.makeev_local 		}
154*2f083884Ss.makeev_local 	}
155*2f083884Ss.makeev_local 
156*2f083884Ss.makeev_local 	return true;
157*2f083884Ss.makeev_local }
158*2f083884Ss.makeev_local 
159*2f083884Ss.makeev_local 
160*2f083884Ss.makeev_local 
161*2f083884Ss.makeev_local SUITE(DxtTests)
162*2f083884Ss.makeev_local {
163*2f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
164*2f083884Ss.makeev_local 	struct CompressDxtBlock
165*2f083884Ss.makeev_local 	{
166*2f083884Ss.makeev_local 		MT_DECLARE_TASK(CompressDxtBlock, MT::StackRequirements::STANDARD, MT::Color::Blue);
167*2f083884Ss.makeev_local 
168*2f083884Ss.makeev_local 		MT::ArrayView<uint8> srcPixels;
169*2f083884Ss.makeev_local 		MT::ArrayView<uint8> dstBlocks;
170*2f083884Ss.makeev_local 
171*2f083884Ss.makeev_local 		int srcX;
172*2f083884Ss.makeev_local 		int srcY;
173*2f083884Ss.makeev_local 
174*2f083884Ss.makeev_local 		int stride;
175*2f083884Ss.makeev_local 		int dstBlockOffset;
176*2f083884Ss.makeev_local 
177*2f083884Ss.makeev_local 		CompressDxtBlock(int _srcX, int _srcY, int _stride, const MT::ArrayView<uint8> & _srcPixels, const MT::ArrayView<uint8> & _dstBlocks, int _dstBlockOffset)
178*2f083884Ss.makeev_local 			: srcPixels(_srcPixels)
179*2f083884Ss.makeev_local 			, dstBlocks(_dstBlocks)
180*2f083884Ss.makeev_local 		{
181*2f083884Ss.makeev_local 				srcX = _srcX;
182*2f083884Ss.makeev_local 				srcY = _srcY;
183*2f083884Ss.makeev_local 				stride = _stride;
184*2f083884Ss.makeev_local 				dstBlockOffset = _dstBlockOffset;
185*2f083884Ss.makeev_local 		}
186*2f083884Ss.makeev_local 
187*2f083884Ss.makeev_local 		CompressDxtBlock(CompressDxtBlock&& other)
188*2f083884Ss.makeev_local 			: srcPixels(other.srcPixels)
189*2f083884Ss.makeev_local 			, dstBlocks(other.dstBlocks)
190*2f083884Ss.makeev_local 			, srcX(other.srcX)
191*2f083884Ss.makeev_local 			, srcY(other.srcY)
192*2f083884Ss.makeev_local 			, stride(other.stride)
193*2f083884Ss.makeev_local 			, dstBlockOffset(other.dstBlockOffset)
194*2f083884Ss.makeev_local 		{
195*2f083884Ss.makeev_local 			other.srcX = -1;
196*2f083884Ss.makeev_local 			other.srcY = -1;
197*2f083884Ss.makeev_local 			other.stride = -1;
198*2f083884Ss.makeev_local 			other.dstBlockOffset = -1;
199*2f083884Ss.makeev_local 		}
200*2f083884Ss.makeev_local 
201*2f083884Ss.makeev_local 		~CompressDxtBlock()
202*2f083884Ss.makeev_local 		{
203*2f083884Ss.makeev_local 			srcX = -1;
204*2f083884Ss.makeev_local 			srcY = -1;
205*2f083884Ss.makeev_local 			stride = -1;
206*2f083884Ss.makeev_local 			dstBlockOffset = -1;
207*2f083884Ss.makeev_local 		}
208*2f083884Ss.makeev_local 
209*2f083884Ss.makeev_local 		void Do(MT::FiberContext&)
210*2f083884Ss.makeev_local 		{
211*2f083884Ss.makeev_local 			// 16 pixels of input
212*2f083884Ss.makeev_local 			uint32 pixels[4*4];
213*2f083884Ss.makeev_local 
214*2f083884Ss.makeev_local 			// copy dxt1 block from image
215*2f083884Ss.makeev_local 			for (int y = 0; y < 4; y++)
216*2f083884Ss.makeev_local 			{
217*2f083884Ss.makeev_local 				for (int x = 0; x < 4; x++)
218*2f083884Ss.makeev_local 				{
219*2f083884Ss.makeev_local 					int posX = srcX + x;
220*2f083884Ss.makeev_local 					int posY = srcY + y;
221*2f083884Ss.makeev_local 
222*2f083884Ss.makeev_local 					int index = posY * stride + (posX * 3);
223*2f083884Ss.makeev_local 
224*2f083884Ss.makeev_local 					MT_ASSERT(index >= 0 && ((size_t)(index + 2) < MT_ARRAY_SIZE(EmbeddedImage::lenaColor)), "Invalid index");
225*2f083884Ss.makeev_local 
226*2f083884Ss.makeev_local 					uint8 r = srcPixels[index + 0];
227*2f083884Ss.makeev_local 					uint8 g = srcPixels[index + 1];
228*2f083884Ss.makeev_local 					uint8 b = srcPixels[index + 2];
229*2f083884Ss.makeev_local 
230*2f083884Ss.makeev_local 					uint32 color = 0xFF000000 | ((b << 16) | (g << 8) | (r));
231*2f083884Ss.makeev_local 
232*2f083884Ss.makeev_local 					pixels[y * 4 + x] = color;
233*2f083884Ss.makeev_local 				}
234*2f083884Ss.makeev_local 			}
235*2f083884Ss.makeev_local 
236*2f083884Ss.makeev_local 			// compress the 4x4 block using DXT1 compression
237*2f083884Ss.makeev_local 			squish::Compress( (squish::u8 *)&pixels[0], &dstBlocks[dstBlockOffset], squish::kDxt1 );
238*2f083884Ss.makeev_local 		}
239*2f083884Ss.makeev_local 	};
240*2f083884Ss.makeev_local 
241*2f083884Ss.makeev_local 
242*2f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
243*2f083884Ss.makeev_local 	struct CompressDxt
244*2f083884Ss.makeev_local 	{
245*2f083884Ss.makeev_local 		MT_DECLARE_TASK(CompressDxt, MT::StackRequirements::EXTENDED, MT::Color::Aqua);
246*2f083884Ss.makeev_local 
247*2f083884Ss.makeev_local 		uint32 width;
248*2f083884Ss.makeev_local 		uint32 height;
249*2f083884Ss.makeev_local 		uint32 stride;
250*2f083884Ss.makeev_local 
251*2f083884Ss.makeev_local 		uint32 blkWidth;
252*2f083884Ss.makeev_local 		uint32 blkHeight;
253*2f083884Ss.makeev_local 
254*2f083884Ss.makeev_local 		MT::ArrayView<uint8> srcPixels;
255*2f083884Ss.makeev_local 		MT::ArrayView<uint8> dxtBlocks;
256*2f083884Ss.makeev_local 
257*2f083884Ss.makeev_local 		CompressDxt(uint32 _width, uint32 _height, uint32 _stride, const MT::ArrayView<uint8> & _srcPixels )
258*2f083884Ss.makeev_local 			: srcPixels(_srcPixels)
259*2f083884Ss.makeev_local 		{
260*2f083884Ss.makeev_local 			width = _width;
261*2f083884Ss.makeev_local 			height = _height;
262*2f083884Ss.makeev_local 			stride = _stride;
263*2f083884Ss.makeev_local 
264*2f083884Ss.makeev_local 			blkWidth = width >> 2;
265*2f083884Ss.makeev_local 			blkHeight = height >> 2;
266*2f083884Ss.makeev_local 
267*2f083884Ss.makeev_local 			int dxtBlocksTotalSizeInBytes = blkWidth * blkHeight * 8; // 8 bytes = 64 bits per block (dxt1)
268*2f083884Ss.makeev_local 			dxtBlocks = MT::ArrayView<uint8>( malloc( dxtBlocksTotalSizeInBytes ), dxtBlocksTotalSizeInBytes);
269*2f083884Ss.makeev_local 		}
270*2f083884Ss.makeev_local 
271*2f083884Ss.makeev_local 		~CompressDxt()
272*2f083884Ss.makeev_local 		{
273*2f083884Ss.makeev_local 			void* pDxtBlocks = dxtBlocks.GetRawData();
274*2f083884Ss.makeev_local 			if (pDxtBlocks)
275*2f083884Ss.makeev_local 			{
276*2f083884Ss.makeev_local 				free(pDxtBlocks);
277*2f083884Ss.makeev_local 			}
278*2f083884Ss.makeev_local 		}
279*2f083884Ss.makeev_local 
280*2f083884Ss.makeev_local 
281*2f083884Ss.makeev_local 		void Do(MT::FiberContext& context)
282*2f083884Ss.makeev_local 		{
283*2f083884Ss.makeev_local 			// use stack_array as subtask container. beware stack overflow!
284*2f083884Ss.makeev_local 			MT::StackArray<CompressDxtBlock, 1024> subTasks;
285*2f083884Ss.makeev_local 
286*2f083884Ss.makeev_local 			for (uint32 blkY = 0; blkY < blkHeight; blkY++)
287*2f083884Ss.makeev_local 			{
288*2f083884Ss.makeev_local 				for (uint32 blkX = 0; blkX < blkWidth; blkX++)
289*2f083884Ss.makeev_local 				{
290*2f083884Ss.makeev_local 					uint32 blockIndex = blkY * blkWidth + blkX;
291*2f083884Ss.makeev_local 					subTasks.PushBack( CompressDxtBlock(blkX * 4, blkY * 4, stride, srcPixels, dxtBlocks, blockIndex * 8) );
292*2f083884Ss.makeev_local 				}
293*2f083884Ss.makeev_local 			}
294*2f083884Ss.makeev_local 
295*2f083884Ss.makeev_local 			context.RunSubtasksAndYield(MT::TaskGroup::Default(), &subTasks[0], subTasks.Size());
296*2f083884Ss.makeev_local 		}
297*2f083884Ss.makeev_local 	};
298*2f083884Ss.makeev_local 
299*2f083884Ss.makeev_local 
300*2f083884Ss.makeev_local 
301*2f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
302*2f083884Ss.makeev_local 	struct DecompressDxtBlock
303*2f083884Ss.makeev_local 	{
304*2f083884Ss.makeev_local 		MT_DECLARE_TASK(DecompressDxtBlock, MT::StackRequirements::STANDARD, MT::Color::Red);
305*2f083884Ss.makeev_local 
306*2f083884Ss.makeev_local 		MT::ArrayView<uint8> srcBlocks;
307*2f083884Ss.makeev_local 		MT::ArrayView<uint8> dstPixels;
308*2f083884Ss.makeev_local 
309*2f083884Ss.makeev_local 		int dstX;
310*2f083884Ss.makeev_local 		int dstY;
311*2f083884Ss.makeev_local 
312*2f083884Ss.makeev_local 		int stride;
313*2f083884Ss.makeev_local 		int srcBlockOffset;
314*2f083884Ss.makeev_local 
315*2f083884Ss.makeev_local 		DecompressDxtBlock(int _dstX, int _dstY, int _stride, const MT::ArrayView<uint8> & _dstPixels, const MT::ArrayView<uint8> & _srcBlocks, int _srcBlockOffset)
316*2f083884Ss.makeev_local 			: srcBlocks(_srcBlocks)
317*2f083884Ss.makeev_local 			, dstPixels(_dstPixels)
318*2f083884Ss.makeev_local 		{
319*2f083884Ss.makeev_local 			dstX = _dstX;
320*2f083884Ss.makeev_local 			dstY = _dstY;
321*2f083884Ss.makeev_local 			stride = _stride;
322*2f083884Ss.makeev_local 			srcBlockOffset = _srcBlockOffset;
323*2f083884Ss.makeev_local 		}
324*2f083884Ss.makeev_local 
325*2f083884Ss.makeev_local 		DecompressDxtBlock(DecompressDxtBlock&& other)
326*2f083884Ss.makeev_local 			: srcBlocks(other.srcBlocks)
327*2f083884Ss.makeev_local 			, dstPixels(other.dstPixels)
328*2f083884Ss.makeev_local 			, dstX(other.dstX)
329*2f083884Ss.makeev_local 			, dstY(other.dstY)
330*2f083884Ss.makeev_local 			, stride(other.stride)
331*2f083884Ss.makeev_local 			, srcBlockOffset(other.srcBlockOffset)
332*2f083884Ss.makeev_local 		{
333*2f083884Ss.makeev_local 			other.dstX = -1;
334*2f083884Ss.makeev_local 			other.dstY = -1;
335*2f083884Ss.makeev_local 			other.stride = -1;
336*2f083884Ss.makeev_local 			other.srcBlockOffset = -1;
337*2f083884Ss.makeev_local 		}
338*2f083884Ss.makeev_local 
339*2f083884Ss.makeev_local 		~DecompressDxtBlock()
340*2f083884Ss.makeev_local 		{
341*2f083884Ss.makeev_local 			dstX = -1;
342*2f083884Ss.makeev_local 			dstY = -1;
343*2f083884Ss.makeev_local 			stride = -1;
344*2f083884Ss.makeev_local 			srcBlockOffset = -1;
345*2f083884Ss.makeev_local 		}
346*2f083884Ss.makeev_local 
347*2f083884Ss.makeev_local 
348*2f083884Ss.makeev_local 		void Do(MT::FiberContext&)
349*2f083884Ss.makeev_local 		{
350*2f083884Ss.makeev_local 			// 16 pixels of output
351*2f083884Ss.makeev_local 			uint32 pixels[4*4];
352*2f083884Ss.makeev_local 
353*2f083884Ss.makeev_local 			// copy dxt1 block from image
354*2f083884Ss.makeev_local 			for (int y = 0; y < 4; y++)
355*2f083884Ss.makeev_local 			{
356*2f083884Ss.makeev_local 				for (int x = 0; x < 4; x++)
357*2f083884Ss.makeev_local 				{
358*2f083884Ss.makeev_local 					squish::Decompress((squish::u8 *)&pixels[0], &srcBlocks[srcBlockOffset], squish::kDxt1);
359*2f083884Ss.makeev_local 
360*2f083884Ss.makeev_local 					int posX = dstX + x;
361*2f083884Ss.makeev_local 					int posY = dstY + y;
362*2f083884Ss.makeev_local 
363*2f083884Ss.makeev_local 					int index = posY * stride + (posX * 3);
364*2f083884Ss.makeev_local 
365*2f083884Ss.makeev_local 					uint32 pixel = pixels[y * 4 + x];
366*2f083884Ss.makeev_local 
367*2f083884Ss.makeev_local 					MT_ASSERT(index >= 0 && ((size_t)(index + 2) < MT_ARRAY_SIZE(EmbeddedImage::lenaColor)), "Invalid index");
368*2f083884Ss.makeev_local 
369*2f083884Ss.makeev_local 					dstPixels[index + 0] = (pixel & 0xFF);
370*2f083884Ss.makeev_local 					dstPixels[index + 1] = (pixel >> 8 & 0xFF);
371*2f083884Ss.makeev_local 					dstPixels[index + 2] = (pixel >> 16 & 0xFF);
372*2f083884Ss.makeev_local 				}
373*2f083884Ss.makeev_local 			}
374*2f083884Ss.makeev_local 
375*2f083884Ss.makeev_local 		}
376*2f083884Ss.makeev_local 	};
377*2f083884Ss.makeev_local 
378*2f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
379*2f083884Ss.makeev_local 	struct DecompressDxt
380*2f083884Ss.makeev_local 	{
381*2f083884Ss.makeev_local 		MT_DECLARE_TASK(DecompressDxt, MT::StackRequirements::EXTENDED, MT::Color::Yellow);
382*2f083884Ss.makeev_local 
383*2f083884Ss.makeev_local 		MT::ArrayView<uint8> dxtBlocks;
384*2f083884Ss.makeev_local 		MT::ArrayView<uint8> decompressedImage;
385*2f083884Ss.makeev_local 
386*2f083884Ss.makeev_local 		uint32 blkWidth;
387*2f083884Ss.makeev_local 		uint32 blkHeight;
388*2f083884Ss.makeev_local 
389*2f083884Ss.makeev_local 
390*2f083884Ss.makeev_local 		DecompressDxt(const MT::ArrayView<uint8> & _dxtBlocks, uint32 dxtBlocksCountWidth, uint32 dxtBlocksCountHeight)
391*2f083884Ss.makeev_local 			: dxtBlocks(_dxtBlocks)
392*2f083884Ss.makeev_local 		{
393*2f083884Ss.makeev_local 			blkWidth = dxtBlocksCountWidth;
394*2f083884Ss.makeev_local 			blkHeight = dxtBlocksCountHeight;
395*2f083884Ss.makeev_local 
396*2f083884Ss.makeev_local 			// dxt1 block = 16 rgb pixels = 48 bytes
397*2f083884Ss.makeev_local 			uint32 bytesCount = blkWidth * blkHeight * 48;
398*2f083884Ss.makeev_local 			decompressedImage = MT::ArrayView<uint8>( malloc(bytesCount), bytesCount);
399*2f083884Ss.makeev_local 		}
400*2f083884Ss.makeev_local 
401*2f083884Ss.makeev_local 		~DecompressDxt()
402*2f083884Ss.makeev_local 		{
403*2f083884Ss.makeev_local 			void* pDxtBlocks = dxtBlocks.GetRawData();
404*2f083884Ss.makeev_local 			if (pDxtBlocks)
405*2f083884Ss.makeev_local 			{
406*2f083884Ss.makeev_local 				free(pDxtBlocks);
407*2f083884Ss.makeev_local 			}
408*2f083884Ss.makeev_local 
409*2f083884Ss.makeev_local 			void* pDecompressedImage = decompressedImage.GetRawData();
410*2f083884Ss.makeev_local 			if (pDecompressedImage)
411*2f083884Ss.makeev_local 			{
412*2f083884Ss.makeev_local 				free(pDecompressedImage);
413*2f083884Ss.makeev_local 			}
414*2f083884Ss.makeev_local 
415*2f083884Ss.makeev_local 		}
416*2f083884Ss.makeev_local 
417*2f083884Ss.makeev_local 		void Do(MT::FiberContext& context)
418*2f083884Ss.makeev_local 		{
419*2f083884Ss.makeev_local 			// use stack_array as subtask container. beware stack overflow!
420*2f083884Ss.makeev_local 			MT::StackArray<DecompressDxtBlock, 1024> subTasks;
421*2f083884Ss.makeev_local 
422*2f083884Ss.makeev_local 			int stride = blkWidth * 4 * 3;
423*2f083884Ss.makeev_local 
424*2f083884Ss.makeev_local 			for (uint32 blkY = 0; blkY < blkHeight; blkY++)
425*2f083884Ss.makeev_local 			{
426*2f083884Ss.makeev_local 				for (uint32 blkX = 0; blkX < blkWidth; blkX++)
427*2f083884Ss.makeev_local 				{
428*2f083884Ss.makeev_local 					uint32 blockIndex = blkY * blkWidth + blkX;
429*2f083884Ss.makeev_local 					subTasks.PushBack( DecompressDxtBlock(blkX * 4, blkY * 4, stride, decompressedImage, dxtBlocks, blockIndex * 8) );
430*2f083884Ss.makeev_local 				}
431*2f083884Ss.makeev_local 			}
432*2f083884Ss.makeev_local 
433*2f083884Ss.makeev_local 			context.RunSubtasksAndYield(MT::TaskGroup::Default(), &subTasks[0], subTasks.Size());
434*2f083884Ss.makeev_local 		}
435*2f083884Ss.makeev_local 
436*2f083884Ss.makeev_local 	};
437*2f083884Ss.makeev_local 
438*2f083884Ss.makeev_local 
439*2f083884Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
440*2f083884Ss.makeev_local 	void Wait(MT::TaskScheduler & scheduler)
441*2f083884Ss.makeev_local 	{
442*2f083884Ss.makeev_local 		//emulate game loop
443*2f083884Ss.makeev_local 		for(;;)
444*2f083884Ss.makeev_local 		{
445*2f083884Ss.makeev_local 			bool waitDone = scheduler.WaitAll(33);
446*2f083884Ss.makeev_local 			if (waitDone)
447*2f083884Ss.makeev_local 			{
448*2f083884Ss.makeev_local 				break;
449*2f083884Ss.makeev_local 			}
450*2f083884Ss.makeev_local 		}
451*2f083884Ss.makeev_local 	}
452*2f083884Ss.makeev_local 
453*2f083884Ss.makeev_local 
454*2f083884Ss.makeev_local 	// dxt compressor complex test
455*2f083884Ss.makeev_local 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
456*2f083884Ss.makeev_local 	TEST(RunComplexDxtTest)
457*2f083884Ss.makeev_local 	{
458*2f083884Ss.makeev_local 		static_assert(MT_ARRAY_SIZE(EmbeddedImage::lenaColor) == 49152, "Image size is invalid");
459*2f083884Ss.makeev_local 
460*2f083884Ss.makeev_local 		int stride = 384;
461*2f083884Ss.makeev_local 
462*2f083884Ss.makeev_local 		MT::ArrayView<uint8> srcImage((void*)&EmbeddedImage::lenaColor[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor));
463*2f083884Ss.makeev_local 
464*2f083884Ss.makeev_local 		CompressDxt compressTask(128, 128, stride, srcImage);
465*2f083884Ss.makeev_local 		MT_ASSERT ((compressTask.width & 3) == 0 && (compressTask.height & 3) == 0, "Image size must be a multiple of 4");
466*2f083884Ss.makeev_local 
467*2f083884Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
468*2f083884Ss.makeev_local 		Microprofile profiler;
469*2f083884Ss.makeev_local 		MT::TaskScheduler scheduler(0, &profiler);
470*2f083884Ss.makeev_local #else
471*2f083884Ss.makeev_local 		MT::TaskScheduler scheduler;
472*2f083884Ss.makeev_local #endif
473*2f083884Ss.makeev_local 
474*2f083884Ss.makeev_local 		int workersCount = (int)scheduler.GetWorkersCount();
475*2f083884Ss.makeev_local 		printf("Scheduler started, %d workers\n", workersCount);
476*2f083884Ss.makeev_local 
477*2f083884Ss.makeev_local 		printf("Compress image\n");
478*2f083884Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &compressTask, 1);
479*2f083884Ss.makeev_local 
480*2f083884Ss.makeev_local 		Wait(scheduler);
481*2f083884Ss.makeev_local 
482*2f083884Ss.makeev_local 		DecompressDxt decompressTask(compressTask.dxtBlocks, compressTask.blkWidth, compressTask.blkHeight);
483*2f083884Ss.makeev_local 		compressTask.dxtBlocks = MT::ArrayView<uint8>(); //transfer memory ownership to Decompress task
484*2f083884Ss.makeev_local 
485*2f083884Ss.makeev_local 		printf("Decompress image\n");
486*2f083884Ss.makeev_local 		scheduler.RunAsync(MT::TaskGroup::Default(), &decompressTask, 1);
487*2f083884Ss.makeev_local 
488*2f083884Ss.makeev_local 		Wait(scheduler);
489*2f083884Ss.makeev_local 
490*2f083884Ss.makeev_local /*
491*2f083884Ss.makeev_local 		//save compressed image
492*2f083884Ss.makeev_local 		{
493*2f083884Ss.makeev_local 			FILE * file = fopen("lena_dxt1.dds", "w+b");
494*2f083884Ss.makeev_local 			fwrite(&EmbeddedImage::ddsHeader[0], MT_ARRAY_SIZE(EmbeddedImage::ddsHeader), 1, file);
495*2f083884Ss.makeev_local 			fwrite(decompressTask.dxtBlocks, decompressTask.blkWidth * decompressTask.blkHeight * 8, 1, file);
496*2f083884Ss.makeev_local 			fclose(file);
497*2f083884Ss.makeev_local 		}
498*2f083884Ss.makeev_local 
499*2f083884Ss.makeev_local 		//save uncompressed image
500*2f083884Ss.makeev_local 		{
501*2f083884Ss.makeev_local 			FILE * file = fopen("lena_rgb.raw", "w+b");
502*2f083884Ss.makeev_local 			fwrite(decompressTask.decompressedImage, decompressTask.blkWidth * decompressTask.blkHeight * 48, 1, file);
503*2f083884Ss.makeev_local 			fclose(file);
504*2f083884Ss.makeev_local 		}
505*2f083884Ss.makeev_local */
506*2f083884Ss.makeev_local 
507*2f083884Ss.makeev_local 		printf("Compare images\n");
508*2f083884Ss.makeev_local 		bool imagesEqual = CompareImagesPSNR(&srcImage[0], &decompressTask.decompressedImage[0], MT_ARRAY_SIZE(EmbeddedImage::lenaColor), 8.0);
509*2f083884Ss.makeev_local 		CHECK_EQUAL(true, imagesEqual);
510*2f083884Ss.makeev_local 
511*2f083884Ss.makeev_local /*
512*2f083884Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
513*2f083884Ss.makeev_local 		// waiting for profiler attach
514*2f083884Ss.makeev_local 		printf("Press any key to continue\n");
515*2f083884Ss.makeev_local 		while(true)
516*2f083884Ss.makeev_local 		{
517*2f083884Ss.makeev_local 			if (_kbhit() != 0)
518*2f083884Ss.makeev_local 			{
519*2f083884Ss.makeev_local 				break;
520*2f083884Ss.makeev_local 			}
521*2f083884Ss.makeev_local 		}
522*2f083884Ss.makeev_local #endif
523*2f083884Ss.makeev_local */
524*2f083884Ss.makeev_local 	}
525*2f083884Ss.makeev_local 
526*2f083884Ss.makeev_local 
527*2f083884Ss.makeev_local }
528