1 // Copyright (C)2004 Landmark Graphics Corporation
2 // Copyright (C)2005 Sun Microsystems, Inc.
3 // Copyright (C)2011, 2014, 2017-2020 D. R. Commander
4 //
5 // This library is free software and may be redistributed and/or modified under
6 // the terms of the wxWindows Library License, Version 3.1 or (at your option)
7 // any later version.  The full license is in the LICENSE.txt file included
8 // with this distribution.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // wxWindows Library License for more details.
14 
15 #include "Thread.h"
16 #include "Frame.h"
17 #include "../client/GLFrame.h"
18 #include "vglutil.h"
19 #include "Timer.h"
20 #include "bmp.h"
21 #include "vgllogo.h"
22 #ifdef USEHELGRIND
23 	#include <valgrind/helgrind.h>
24 #endif
25 
26 using namespace vglutil;
27 using namespace vglcommon;
28 
29 #define ITER  50
30 #define NFRAMES  2
31 #define MINW  1
32 #define MAXW  400
33 #define BORDER  0
34 #define NUMWIN  1
35 
36 bool useGL = false, useXV = false, doRgbBench = false, useRGB = false,
37 	addLogo = false, anaglyph = false, check = false;
38 
39 
resizeWindow(Display * dpy,Window win,int width,int height,int myID)40 void resizeWindow(Display *dpy, Window win, int width, int height, int myID)
41 {
42 	XWindowAttributes xwa;
43 	XGetWindowAttributes(dpy, win, &xwa);
44 	if(width != xwa.width || height != xwa.height)
45 	{
46 		XLockDisplay(dpy);
47 		XWindowChanges xwc;
48 		xwc.x = (width + BORDER * 2) * myID;  xwc.y = 0;
49 		xwc.width = width;  xwc.height = height;
50 		XConfigureWindow(dpy, win, CWWidth | CWHeight | CWX | CWY, &xwc);
51 		XFlush(dpy);
52 		XSync(dpy, False);
53 		XUnlockDisplay(dpy);
54 	}
55 }
56 
57 
58 class Blitter : public Runnable
59 {
60 	public:
61 
Blitter(Display * dpy_,Window win_,int myID_)62 		Blitter(Display *dpy_, Window win_, int myID_) : findex(0), deadYet(false),
63 			dpy(dpy_), win(win_), thread(NULL), myID(myID_)
64 		{
65 			for(int i = 0; i < NFRAMES; i++)
66 			{
67 				if(useGL) { frames[i] = new GLFrame(dpy, win); }
68 				#ifdef USEXV
69 				else if(useXV) { frames[i] = new XVFrame(dpy, win); }
70 				#endif
71 				else { frames[i] = new FBXFrame(dpy, win); }
72 			}
73 			thread = new Thread(this);
74 			thread->start();
75 			#ifdef USEHELGRIND
76 			ANNOTATE_BENIGN_RACE_SIZED(&deadYet, sizeof(bool), );
77 			#endif
78 		}
79 
~Blitter(void)80 		virtual ~Blitter(void)
81 		{
82 			shutdown();
83 			for(int i = 0; i < NFRAMES; i++)
84 			{
85 				if(frames[i])
86 				{
87 					if(frames[i]->isGL) delete (GLFrame *)frames[i];
88 					#ifdef USEXV
89 					else if(frames[i]->isXV) delete (XVFrame *)frames[i];
90 					#endif
91 					else delete (FBXFrame *)frames[i];
92 					frames[i] = NULL;
93 				}
94 			}
95 		}
96 
get(void)97 		Frame *get(void)
98 		{
99 			Frame *frame = frames[findex];
100 			findex = (findex + 1) % NFRAMES;
101 			if(thread) thread->checkError();
102 			if(!deadYet) frame->waitUntilComplete();
103 			if(thread) thread->checkError();
104 			return frame;
105 		}
106 
put(Frame * frame)107 		void put(Frame *frame)
108 		{
109 			if(thread) thread->checkError();
110 			frame->signalReady();
111 		}
112 
shutdown(void)113 		void shutdown(void)
114 		{
115 			deadYet = true;
116 			int i;
117 			for(i = 0; i < NFRAMES; i++)
118 				frames[i]->signalReady();  // Release my thread
119 			if(thread)
120 			{
121 				thread->stop();  delete thread;  thread = NULL;
122 			}
123 			for(i = 0; i < NFRAMES; i++)
124 				frames[i]->signalComplete();  // Release Decompressor
125 		}
126 
127 	private:
128 
run(void)129 		void run(void)
130 		{
131 			Timer timer;
132 			double mpixels = 0., totalTime = 0.;
133 			int index = 0;  Frame *frame;
134 			try
135 			{
136 				while(!deadYet)
137 				{
138 					frame = frames[index];
139 					index = (index + 1) % NFRAMES;
140 					frame->waitUntilReady();  if(deadYet) break;
141 					if(useXV)
142 						resizeWindow(dpy, win, frame->hdr.width, frame->hdr.height, myID);
143 					timer.start();
144 					if(frame->isGL) ((GLFrame *)frame)->redraw();
145 					#ifdef USEXV
146 					else if(frame->isXV) ((XVFrame *)frame)->redraw();
147 					#endif
148 					else ((FBXFrame *)frame)->redraw();
149 					mpixels += (double)(frame->hdr.width * frame->hdr.height) / 1000000.;
150 					totalTime += timer.elapsed();
151 					if(check && !checkFrame(frame))
152 						THROW("Pixel data is bogus");
153 					frame->signalComplete();
154 				}
155 				fprintf(stderr, "Average Blitter performance = %f Mpixels/sec%s\n",
156 					mpixels / totalTime, check ? " [PASSED]" : "");
157 			}
158 			catch(std::exception &e)
159 			{
160 				if(thread) thread->setError(e);
161 				for(int i = 0; i < NFRAMES; i++)
162 					if(frames[i]) frames[i]->signalComplete();
163 				throw;
164 			}
165 			fprintf(stderr, "Blitter exiting ...\n");
166 		}
167 
checkFrame(Frame * frame)168 		bool checkFrame(Frame *frame)
169 		{
170 			int i, j, _j, pitch = frame->pitch, seed = frame->hdr.winid;
171 			unsigned char *ptr = frame->bits, *pixel;
172 			PF *pf = pf_get(frame->hdr.dpynum);
173 			int maxRGB = (1 << pf->bpc);
174 
175 			for(_j = 0; _j < frame->hdr.height; _j++, ptr += pitch)
176 			{
177 				j = frame->flags & FRAME_BOTTOMUP ? frame->hdr.height - _j - 1 : _j;
178 				for(i = 0, pixel = ptr; i < frame->hdr.width;
179 					i++, pixel += frame->pf->size)
180 				{
181 					int r, g, b;
182 					frame->pf->getRGB(pixel, &r, &g, &b);
183 					if(frame->pf->bpc == 10 && pf->bpc != 10)
184 					{
185 						r >>= 2;  g >>= 2;  b >>= 2;
186 					}
187 					if(addLogo && !useXV)
188 					{
189 						int lw = min(VGLLOGO_WIDTH, frame->hdr.width - 1);
190 						int lh = min(VGLLOGO_HEIGHT, frame->hdr.height - 1);
191 						int li = i - (frame->hdr.width - lw - 1);
192 						int lj = j - (frame->hdr.height - lh - 1);
193 						if(lw > 0 && lh > 0 && li >= 0 && lj >= 0 && li < lw && lj < lh
194 							&& vgllogo[lj * VGLLOGO_WIDTH + li])
195 						{
196 							r ^= 113;  g ^= 162;  b ^= 117;
197 						}
198 					}
199 					if(r != (i + seed) % maxRGB || g != (j + seed) % maxRGB
200 						|| b != (i + j + seed) % maxRGB)
201 						return false;
202 				}
203 			}
204 
205 			return true;
206 		}
207 
208 		int findex;  bool deadYet;
209 		Frame *frames[NFRAMES];
210 		Display *dpy;  Window win;
211 		Thread *thread;
212 		int myID;
213 };
214 
215 
216 class Decompressor : public Runnable
217 {
218 	public:
219 
Decompressor(Blitter * blitter_,Display * dpy_,Window win_,int myID_)220 		Decompressor(Blitter *blitter_, Display *dpy_, Window win_, int myID_) :
221 			blitter(blitter_), findex(0), deadYet(false), dpy(dpy_), win(win_),
222 			myID(myID_), thread(NULL)
223 		{
224 			thread = new Thread(this);
225 			thread->start();
226 			#ifdef USEHELGRIND
227 			ANNOTATE_BENIGN_RACE_SIZED(&deadYet, sizeof(bool), );
228 			#endif
229 		}
230 
~Decompressor(void)231 		virtual ~Decompressor(void) { shutdown(); }
232 
get(void)233 		CompressedFrame &get(void)
234 		{
235 			CompressedFrame &cframe = cframes[findex];
236 			findex = (findex + 1) % NFRAMES;
237 			if(deadYet) return cframe;
238 			if(thread) thread->checkError();
239 			if(!deadYet) cframe.waitUntilComplete();
240 			if(thread) thread->checkError();
241 			return cframe;
242 		}
243 
put(CompressedFrame & cframe)244 		void put(CompressedFrame &cframe)
245 		{
246 			if(thread) thread->checkError();
247 			cframe.signalReady();
248 		}
249 
shutdown(void)250 		void shutdown(void)
251 		{
252 			deadYet = true;
253 			int i;
254 			for(i = 0; i < NFRAMES; i++)
255 				cframes[i].signalReady();  // Release my thread
256 			if(thread)
257 			{
258 				thread->stop();  delete thread;  thread = NULL;
259 			}
260 			for(i = 0; i < NFRAMES; i++)
261 				cframes[i].signalComplete();  // Release compressor
262 		}
263 
264 	private:
265 
run(void)266 		void run(void)
267 		{
268 			int index = 0;  Frame *frame = NULL;
269 			try
270 			{
271 				while(!deadYet)
272 				{
273 					CompressedFrame &cframe = cframes[index];
274 					index = (index + 1) % NFRAMES;
275 					cframe.waitUntilReady();  if(deadYet) break;
276 					frame = blitter->get();  if(deadYet) break;
277 					resizeWindow(dpy, win, cframe.hdr.width, cframe.hdr.height, myID);
278 					if(frame->isGL) *((GLFrame *)frame) = cframe;
279 					#ifdef USEXV
280 					else if(frame->isXV) *((XVFrame *)frame) = cframe;
281 					#endif
282 					else *((FBXFrame *)frame) = cframe;
283 					blitter->put(frame);
284 					cframe.signalComplete();
285 				}
286 			}
287 			catch(std::exception &e)
288 			{
289 				if(thread) thread->setError(e);
290 				for(int i = 0; i < NFRAMES; i++) cframes[i].signalComplete();
291 				throw;
292 			}
293 			fprintf(stderr, "Decompressor exiting ...\n");
294 		}
295 
296 		Blitter *blitter;
297 		int findex;  bool deadYet;
298 		Display *dpy;  Window win;
299 		CompressedFrame cframes[NFRAMES];
300 		int myID;  Thread *thread;
301 };
302 
303 
304 class Compressor : public Runnable
305 {
306 	public:
307 
Compressor(Decompressor * decompressor_,Blitter * blitter_)308 		Compressor(Decompressor *decompressor_, Blitter *blitter_) : findex(0),
309 			deadYet(false), thread(NULL), decompressor(decompressor_)
310 			#ifdef USEXV
311 			, blitter(blitter_)
312 			#endif
313 		{
314 			thread = new Thread(this);
315 			thread->start();
316 			#ifdef USEHELGRIND
317 			ANNOTATE_BENIGN_RACE_SIZED(&deadYet, sizeof(bool), );
318 			#endif
319 		}
320 
~Compressor(void)321 		virtual ~Compressor(void)
322 		{
323 			if(thread) thread->stop();
324 		}
325 
get(int width,int height,int pixelFormat)326 		Frame &get(int width, int height, int pixelFormat)
327 		{
328 			Frame &frame = frames[findex];
329 			findex = (findex + 1) % NFRAMES;
330 			if(thread) thread->checkError();
331 			if(!deadYet) frame.waitUntilComplete();
332 			if(thread) thread->checkError();
333 			rrframeheader hdr;
334 			memset(&hdr, 0, sizeof(rrframeheader));
335 			hdr.framew = hdr.width = width + BORDER;
336 			hdr.frameh = hdr.height = height + BORDER;
337 			hdr.x = hdr.y = BORDER;
338 			hdr.qual = 80;
339 			hdr.subsamp = 2;
340 			hdr.compress = useRGB ? RRCOMP_RGB : RRCOMP_JPEG;
341 			if(useXV) hdr.compress = RRCOMP_YUV;
342 			frame.init(hdr, pixelFormat, 0);
343 			return frame;
344 		}
345 
put(Frame & frame)346 		void put(Frame &frame)
347 		{
348 			if(thread) thread->checkError();
349 			frame.signalReady();
350 		}
351 
shutdown(void)352 		void shutdown(void)
353 		{
354 			deadYet = true;
355 			int i;
356 			for(i = 0; i < NFRAMES; i++)
357 				frames[i].signalReady();  // Release my thread
358 			if(thread)
359 			{
360 				thread->stop();  delete thread;  thread = NULL;
361 			}
362 			for(i = 0; i < NFRAMES; i++)
363 				frames[i].signalComplete();  // Release main thread
364 		}
365 
366 	private:
367 
run(void)368 		void run(void)
369 		{
370 			int index = 0;
371 			try
372 			{
373 				while(!deadYet)
374 				{
375 					Frame &frame = frames[index];
376 					index = (index + 1) % NFRAMES;
377 					frame.waitUntilReady();  if(deadYet) break;
378 					#ifdef USEXV
379 					if(useXV)
380 					{
381 						XVFrame *xvframe = (XVFrame *)blitter->get();  if(deadYet) break;
382 						*xvframe = frame;
383 						blitter->put(xvframe);
384 					}
385 					else
386 					#endif
387 					{
388 						CompressedFrame &cframe = decompressor->get();  if(deadYet) break;
389 						cframe = frame;
390 						decompressor->put(cframe);
391 					}
392 					frame.signalComplete();
393 				}
394 			}
395 			catch(std::exception &e)
396 			{
397 				if(thread) thread->setError(e);
398 				for(int i = 0; i < NFRAMES; i++) frames[i].signalComplete();
399 				throw;
400 			}
401 			fprintf(stderr, "Compressor exiting ...\n");
402 		}
403 
404 		int findex;  bool deadYet;
405 		Thread *thread;
406 		Frame frames[NFRAMES];
407 		Decompressor *decompressor;
408 		#ifdef USEXV
409 		Blitter *blitter;
410 		#endif
411 };
412 
413 
414 class FrameTest
415 {
416 	public:
417 
FrameTest(Display * dpy_,int myID_)418 		FrameTest(Display *dpy_, int myID_) : dpy(dpy_), compressor(NULL),
419 			decompressor(NULL), blitter(NULL), myID(myID_)
420 		{
421 			ERRIFNOT(win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
422 				myID * (MINW + BORDER * 2), 0, MINW + BORDER, MINW + BORDER, 0,
423 				WhitePixel(dpy, DefaultScreen(dpy)),
424 				BlackPixel(dpy, DefaultScreen(dpy))));
425 			ERRIFNOT(XMapRaised(dpy, win));
426 
427 			blitter = new Blitter(dpy, win, myID);
428 			if(!useXV)
429 				decompressor = new Decompressor(blitter, dpy, win, myID);
430 			compressor = new Compressor(decompressor, blitter);
431 		}
432 
~FrameTest(void)433 		~FrameTest(void) { shutdown();  XDestroyWindow(dpy, win); }
434 
dotest(int width,int height,int seed,PF * pf)435 		void dotest(int width, int height, int seed, PF *pf)
436 		{
437 			if(pf->bpc == 8)
438 			{
439 				Frame &frame = compressor->get(width, height, pf->id);
440 				if(anaglyph) makeAnaglyph(frame, seed);
441 				else initFrame(frame, seed);
442 				// This unit test doesn't use winid, so use it to track the seed.
443 				frame.hdr.winid = seed;
444 				// This unit test doesn't use dpynum, so use it to track the source
445 				// pixel format.
446 				frame.hdr.dpynum = pf->id;
447 				if(addLogo) frame.addLogo();
448 				compressor->put(frame);
449 			}
450 			else
451 			{
452 				FBXFrame *frame = (FBXFrame *)blitter->get();
453 				rrframeheader hdr;
454 				memset(&hdr, 0, sizeof(rrframeheader));
455 				hdr.width = hdr.framew = width;
456 				hdr.height = hdr.frameh = height;
457 				frame->init(hdr);
458 				if(anaglyph) makeAnaglyph(*frame, seed);
459 				else initFrame(*frame, seed);
460 				// This unit test doesn't use winid, so use it to track the seed.
461 				frame->hdr.winid = seed;
462 				// This unit test doesn't use dpynum, so use it to track the source
463 				// pixel format.
464 				frame->hdr.dpynum = pf->id;
465 				if(addLogo) frame->addLogo();
466 				resizeWindow(dpy, win, width, height, myID);
467 				blitter->put(frame);
468 			}
469 		}
470 
471 	private:
472 
initFrame(Frame & frame,int seed)473 		void initFrame(Frame &frame, int seed)
474 		{
475 			int i, j, pitch = frame.pitch;
476 			unsigned char *ptr = frame.bits, *pixel;
477 			int maxRGB = (1 << frame.pf->bpc);
478 
479 			for(j = 0; j < frame.hdr.height; j++, ptr += pitch)
480 			{
481 				for(i = 0, pixel = ptr; i < frame.hdr.width;
482 					i++, pixel += frame.pf->size)
483 					frame.pf->setRGB(pixel, (i + seed) % maxRGB, (j + seed) % maxRGB,
484 						(i + j + seed) % maxRGB);
485 			}
486 		}
487 
makeAnaglyph(Frame & frame,int seed)488 		void makeAnaglyph(Frame &frame, int seed)
489 		{
490 			Frame rFrame, gFrame, bFrame;
491 			int i, j;
492 			unsigned char *ptr;
493 
494 			rFrame.init(frame.hdr, PF_COMP, frame.flags, false);
495 			for(j = 0, ptr = rFrame.bits; j < frame.hdr.height;
496 				j++, ptr += rFrame.pitch)
497 			{
498 				for(i = 0; i < frame.hdr.width; i++) ptr[i] = (i + seed) % 256;
499 			}
500 			gFrame.init(frame.hdr, PF_COMP, frame.flags, false);
501 			for(j = 0, ptr = gFrame.bits; j < frame.hdr.height;
502 				j++, ptr += gFrame.pitch)
503 			{
504 				memset(ptr, (j + seed) % 256, frame.hdr.width);
505 			}
506 			bFrame.init(frame.hdr, PF_COMP, frame.flags, false);
507 			for(j = 0, ptr = bFrame.bits; j < frame.hdr.height;
508 				j++, ptr += bFrame.pitch)
509 			{
510 				for(i = 0; i < frame.hdr.width; i++) ptr[i] = (i + j + seed) % 256;
511 			}
512 
513 			frame.makeAnaglyph(rFrame, gFrame, bFrame);
514 		}
515 
shutdown(void)516 		void shutdown(void)
517 		{
518 			fprintf(stderr, "Shutting down....\n");  fflush(stderr);
519 			if(compressor) compressor->shutdown();
520 			if(decompressor) decompressor->shutdown();
521 			if(blitter) blitter->shutdown();
522 			delete compressor;  compressor = NULL;
523 			delete decompressor;  decompressor = NULL;
524 			delete blitter;  blitter = NULL;
525 		}
526 
527 		Display *dpy;  Window win;
528 		Compressor *compressor;  Decompressor *decompressor;  Blitter *blitter;
529 		int myID;
530 };
531 
532 
cmpFrame(unsigned char * buf,int width,int height,Frame & dst)533 int cmpFrame(unsigned char *buf, int width, int height, Frame &dst)
534 {
535 	int _i;  int pitch = width * 3;
536 	bool dstbu = (dst.flags & FRAME_BOTTOMUP);
537 	for(int i = 0; i < height; i++)
538 	{
539 		_i = dstbu ? i : height - i - 1;
540 		for(int j = 0; j < height; j++)
541 		{
542 			int r, g, b;
543 			dst.pf->getRGB(&dst.bits[dst.pitch * i + j * dst.pf->size], &r, &g, &b);
544 			if(dst.pf->bpc == 10)
545 			{
546 				r >>= 2;  g >>= 2;  b >>= 2;
547 			}
548 			if(r != buf[pitch * _i + j * 3] || g != buf[pitch * _i + j * 3 + 1]
549 				|| b != buf[pitch * _i + j * 3 + 2])
550 				return 1;
551 		}
552 	}
553 	return 0;
554 }
555 
556 
rgbBench(char * filename)557 void rgbBench(char *filename)
558 {
559 	unsigned char *buf;  int width, height, dstbu;
560 	CompressedFrame src;  Frame dst;  int dstformat;
561 
562 	for(dstformat = 0; dstformat < PIXELFORMATS - 1; dstformat++)
563 	{
564 		PF *dstpf = pf_get(dstformat);
565 		for(dstbu = 0; dstbu < 2; dstbu++)
566 		{
567 			if(bmp_load(filename, &buf, &width, 1, &height, PF_RGB,
568 				BMPORN_BOTTOMUP) == -1)
569 				THROW(bmp_geterr());
570 			rrframeheader hdr;
571 			memset(&hdr, 0, sizeof(hdr));
572 			hdr.width = hdr.framew = width;
573 			hdr.height = hdr.frameh = height;
574 			hdr.compress = RRCOMP_RGB;  hdr.size = width * 3 * height;
575 			src.init(hdr, hdr.flags);
576 			memcpy(src.bits, buf, width * 3 * height);
577 			dst.init(hdr, dstpf->id, dstbu ? FRAME_BOTTOMUP : 0);
578 			memset(dst.bits, 0, dst.pitch * dst.hdr.frameh);
579 			fprintf(stderr, "RGB (BOTTOM-UP) -> %s (%s)\n", dstpf->name,
580 				dstbu ? "BOTTOM-UP" : "TOP-DOWN");
581 			double tStart, tTotal = 0.;  int iter = 0;
582 			do
583 			{
584 				tStart = GetTime();
585 				dst.decompressRGB(src, width, height, false);
586 				tTotal += GetTime() - tStart;  iter++;
587 			} while(tTotal < 1.);
588 			fprintf(stderr, "%f Mpixels/sec - ", (double)width * (double)height *
589 				(double)iter / 1000000. / tTotal);
590 			if(cmpFrame(buf, width, height, dst))
591 				fprintf(stderr, "FAILED!\n");
592 			else fprintf(stderr, "Passed.\n");
593 			free(buf);
594 		}
595 		fprintf(stderr, "\n");
596 	}
597 }
598 
599 
usage(char ** argv)600 void usage(char **argv)
601 {
602 	fprintf(stderr, "\nUSAGE: %s [options]\n\n", argv[0]);
603 	fprintf(stderr, "Options:\n");
604 	fprintf(stderr, "-gl = Use OpenGL instead of X11 for blitting\n");
605 	fprintf(stderr, "-xv = Test X Video encoding/display\n");
606 	fprintf(stderr, "-rgb = Use RGB encoding instead of JPEG compression\n");
607 	fprintf(stderr, "-logo = Add VirtualGL logo\n");
608 	fprintf(stderr, "-anaglyph = Test anaglyph creation\n");
609 	fprintf(stderr, "-rgbbench <filename> = Benchmark the decoding of RGB-encoded frames.\n");
610 	fprintf(stderr, "                       <filename> should be a BMP or PPM file.\n");
611 	fprintf(stderr, "-v = Verbose output (may affect benchmark results)\n");
612 	fprintf(stderr, "-check = Check correctness of pixel paths (implies -rgb)\n\n");
613 	exit(1);
614 }
615 
616 
main(int argc,char ** argv)617 int main(int argc, char **argv)
618 {
619 	Display *dpy = NULL;
620 	FrameTest *test[NUMWIN];
621 	int i, j, w, h;
622 	char *fileName = NULL;
623 	bool verbose = false;
624 
625 	if(argc > 1) for(i = 1; i < argc; i++)
626 	{
627 		if(!stricmp(argv[i], "-h") || !strcmp(argv[i], "-?")) usage(argv);
628 		else if(!stricmp(argv[i], "-gl"))
629 		{
630 			fprintf(stderr, "Using OpenGL for blitting ...\n");
631 			useGL = true;
632 		}
633 		else if(!stricmp(argv[i], "-logo")) addLogo = true;
634 		else if(!stricmp(argv[i], "-anaglyph")) anaglyph = true;
635 		#ifdef USEXV
636 		else if(!stricmp(argv[i], "-xv"))
637 		{
638 			fprintf(stderr, "Using X Video ...\n");
639 			useXV = true;
640 		}
641 		#endif
642 		else if(!stricmp(argv[i], "-rgb"))
643 		{
644 			fprintf(stderr, "Using RGB encoding ...\n");
645 			useRGB = true;
646 		}
647 		else if(!stricmp(argv[i], "-rgbbench") && i < argc - 1)
648 		{
649 			fileName = argv[++i];  doRgbBench = true;
650 		}
651 		else if(!stricmp(argv[i], "-v")) verbose = true;
652 		else if(!stricmp(argv[i], "-check")) { check = true;  useRGB = true; }
653 		else usage(argv);
654 	}
655 
656 	try
657 	{
658 		if(doRgbBench) { rgbBench(fileName);  exit(0); }
659 
660 		ERRIFNOT(XInitThreads());
661 		if(!(dpy = XOpenDisplay(0)))
662 		{
663 			fprintf(stderr, "Could not open display %s\n", XDisplayName(0));
664 			exit(1);
665 		}
666 
667 		for(int format = 0; format < PIXELFORMATS - 1; format++)
668 		{
669 			PF *pf = pf_get(format);
670 
671 			if((useXV || anaglyph || useGL) && pf->bpc != 8) continue;
672 			if(DefaultDepth(dpy, DefaultScreen(dpy)) != 30 && pf->bpc == 10)
673 				continue;
674 
675 			fprintf(stderr, "Pixel format: %s\n", pf->name);
676 
677 			for(i = 0; i < NUMWIN; i++)
678 			{
679 				test[i] = new FrameTest(dpy, i);
680 			}
681 
682 			for(w = MINW; w <= MAXW; w += 33)
683 			{
684 				h = 1;
685 				if(verbose) fprintf(stderr, "%.4d x %.4d: ", w, h);
686 				for(i = 0; i < ITER; i++)
687 				{
688 					if(verbose) fprintf(stderr, ".");
689 					for(j = 0; j < NUMWIN; j++) test[j]->dotest(w, h, i, pf);
690 				}
691 				if(verbose) fprintf(stderr, "\n");
692 			}
693 
694 			for(h = MINW; h <= MAXW; h += 33)
695 			{
696 				w = 1;
697 				if(verbose) fprintf(stderr, "%.4d x %.4d: ", w, h);
698 				for(i = 0; i < ITER; i++)
699 				{
700 					if(verbose) fprintf(stderr, ".");
701 					for(j = 0; j < NUMWIN; j++) test[j]->dotest(w, h, i, pf);
702 				}
703 				if(verbose) fprintf(stderr, "\n");
704 			}
705 
706 			for(w = MINW; w <= MAXW; w += 33)
707 			{
708 				h = w;
709 				if(verbose) fprintf(stderr, "%.4d x %.4d: ", w, h);
710 				for(i = 0; i < ITER; i++)
711 				{
712 					if(verbose) fprintf(stderr, ".");
713 					for(j = 0; j < NUMWIN; j++) test[j]->dotest(w, h, i, pf);
714 				}
715 				if(verbose) fprintf(stderr, "\n");
716 			}
717 
718 			for(i = 0; i < NUMWIN; i++)
719 			{
720 				delete test[i];
721 			}
722 			fprintf(stderr, "\n");
723 		}
724 	}
725 	catch(std::exception &e)
726 	{
727 		fprintf(stderr, "%s\n%s\n", GET_METHOD(e), e.what());
728 		if(dpy) XCloseDisplay(dpy);
729 		exit(1);
730 	}
731 	if(dpy) XCloseDisplay(dpy);
732 	return 0;
733 }
734