1 // Copyright (C)2004 Landmark Graphics Corporation
2 // Copyright (C)2005, 2006 Sun Microsystems, Inc.
3 // Copyright (C)2009-2015, 2017-2021 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 "VirtualWin.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include "fakerconfig.h"
20 #include "glxvisual.h"
21 #include "vglutil.h"
22 
23 using namespace vglutil;
24 using namespace vglcommon;
25 using namespace vglfaker;
26 using namespace vglserver;
27 
28 
29 static const int trans2pf[RRTRANS_FORMATOPT] =
30 {
31 	PF_RGB, PF_RGBX, PF_BGR, PF_BGRX, PF_XBGR, PF_XRGB
32 };
33 
34 
35 #define LEYE(buf) \
36 	(buf == GL_BACK ? GL_BACK_LEFT : (buf == GL_FRONT ? GL_FRONT_LEFT : buf))
37 #define REYE(buf) \
38 	(buf == GL_BACK ? GL_BACK_RIGHT : (buf == GL_FRONT ? GL_FRONT_RIGHT : buf))
39 #define IS_ANAGLYPHIC(mode) \
40 	(mode >= RRSTEREO_REDCYAN && mode <= RRSTEREO_BLUEYELLOW)
41 #define IS_PASSIVE(mode) \
42 	(mode >= RRSTEREO_INTERLEAVED && mode <= RRSTEREO_SIDEBYSIDE)
43 
44 
45 // This class encapsulates the 3D off-screen drawable, its most recent
46 // ancestor, and information specific to its corresponding X window
47 
VirtualWin(Display * dpy_,Window win)48 VirtualWin::VirtualWin(Display *dpy_, Window win) :
49 	VirtualDrawable(dpy_, win)
50 {
51 	eventdpy = NULL;
52 	oldDraw = NULL;  newWidth = newHeight = -1;
53 	x11trans = NULL;
54 	#ifdef USEXV
55 	xvtrans = NULL;
56 	#endif
57 	vglconn = NULL;
58 	profGamma.setName("Gamma     ");
59 	profAnaglyph.setName("Anaglyph  ");
60 	profPassive.setName("Stereo Gen");
61 	syncdpy = false;
62 	dirty = false;
63 	rdirty = false;
64 	fconfig_setdefaultsfromdpy(dpy);
65 	plugin = NULL;
66 	doWMDelete = false;
67 	doVGLWMDelete = false;
68 	newConfig = false;
69 	swapInterval = 0;
70 	alreadyWarnedPluginRenderMode = false;
71 	XWindowAttributes xwa;
72 	if(!XGetWindowAttributes(dpy, win, &xwa) || !xwa.visual)
73 		throw(Error(__FUNCTION__, "Invalid window", -1));
74 	if(!fconfig.wm && !(xwa.your_event_mask & StructureNotifyMask))
75 	{
76 		if(!(eventdpy = _XOpenDisplay(DisplayString(dpy))))
77 			THROW("Could not clone X display connection");
78 		XSelectInput(eventdpy, win, StructureNotifyMask);
79 		if(fconfig.verbose)
80 			vglout.println("[VGL] Selecting structure notify events in window 0x%.8x",
81 				win);
82 	}
83 	stereoVisual = glxvisual::visAttrib(dpy, DefaultScreen(dpy),
84 		xwa.visual->visualid, GLX_STEREO);
85 }
86 
87 
~VirtualWin(void)88 VirtualWin::~VirtualWin(void)
89 {
90 	mutex.lock(false);
91 	delete oldDraw;  oldDraw = NULL;
92 	delete x11trans;  x11trans = NULL;
93 	delete vglconn;  vglconn = NULL;
94 	#ifdef USEXV
95 	delete xvtrans;  xvtrans = NULL;
96 	#endif
97 	if(plugin)
98 	{
99 		try
100 		{
101 			delete plugin;  plugin = NULL;
102 		}
103 		catch(std::exception &e)
104 		{
105 			if(fconfig.verbose)
106 				vglout.println("[VGL] WARNING: %s", e.what());
107 		}
108 	}
109 	if(eventdpy) { _XCloseDisplay(eventdpy);  eventdpy = NULL; }
110 	mutex.unlock(false);
111 }
112 
113 
init(int w,int h,VGLFBConfig config_)114 int VirtualWin::init(int w, int h, VGLFBConfig config_)
115 {
116 	CriticalSection::SafeLock l(mutex);
117 	if(doWMDelete) THROW("Window has been deleted by window manager");
118 	return VirtualDrawable::init(w, h, config_);
119 }
120 
121 
122 // The resize doesn't actually occur until the next time updatedrawable() is
123 // called
124 
resize(int width,int height)125 void VirtualWin::resize(int width, int height)
126 {
127 	CriticalSection::SafeLock l(mutex);
128 	if(doWMDelete) THROW("Window has been deleted by window manager");
129 	if(width == 0 && oglDraw) width = oglDraw->getWidth();
130 	if(height == 0 && oglDraw) height = oglDraw->getHeight();
131 	if(oglDraw && oglDraw->getWidth() == width && oglDraw->getHeight() == height)
132 	{
133 		newWidth = newHeight = -1;
134 		return;
135 	}
136 	newWidth = width;  newHeight = height;
137 }
138 
139 
140 // The FB config change doesn't actually occur until the next time
141 // updatedrawable() is called
142 
checkConfig(VGLFBConfig config_)143 void VirtualWin::checkConfig(VGLFBConfig config_)
144 {
145 	CriticalSection::SafeLock l(mutex);
146 	if(doWMDelete) THROW("Window has been deleted by window manager");
147 	if(FBCID(config_) != FBCID(config))
148 	{
149 		config = config_;  newConfig = true;
150 	}
151 }
152 
153 
clear(void)154 void VirtualWin::clear(void)
155 {
156 	CriticalSection::SafeLock l(mutex);
157 	if(doWMDelete) THROW("Window has been deleted by window manager");
158 	VirtualDrawable::clear();
159 }
160 
161 
cleanup(void)162 void VirtualWin::cleanup(void)
163 {
164 	CriticalSection::SafeLock l(mutex);
165 	if(doWMDelete) THROW("Window has been deleted by window manager");
166 	delete oldDraw;  oldDraw = NULL;
167 }
168 
169 
initFromWindow(VGLFBConfig config_)170 void VirtualWin::initFromWindow(VGLFBConfig config_)
171 {
172 	XSync(dpy, False);
173 	XWindowAttributes xwa;
174 	XGetWindowAttributes(dpy, x11Draw, &xwa);
175 	init(xwa.width, xwa.height, config_);
176 }
177 
178 
179 // Get the current 3D off-screen drawable
180 
getGLXDrawable(void)181 GLXDrawable VirtualWin::getGLXDrawable(void)
182 {
183 	CriticalSection::SafeLock l(mutex);
184 	if(doWMDelete) THROW("Window has been deleted by window manager");
185 	return VirtualDrawable::getGLXDrawable();
186 }
187 
188 
checkResize(void)189 void VirtualWin::checkResize(void)
190 {
191 	if(eventdpy)
192 	{
193 		XSync(dpy, False);
194 		while(XPending(eventdpy) > 0)
195 		{
196 			XEvent event;
197 			_XNextEvent(eventdpy, &event);
198 			if(event.type == ConfigureNotify && event.xconfigure.window == x11Draw
199 				&& event.xconfigure.width > 0 && event.xconfigure.height > 0)
200 				resize(event.xconfigure.width, event.xconfigure.height);
201 		}
202 	}
203 }
204 
205 
206 // Get the current 3D off-screen drawable, but resize the drawable (or change
207 // its FB config) first if necessary
208 
updateGLXDrawable(void)209 GLXDrawable VirtualWin::updateGLXDrawable(void)
210 {
211 	GLXDrawable retval = 0;
212 	CriticalSection::SafeLock l(mutex);
213 	if(doWMDelete) THROW("Window has been deleted by window manager");
214 	if(newConfig)
215 	{
216 		if(newWidth <= 0 && oglDraw) newWidth = oglDraw->getWidth();
217 		if(newHeight <= 0 && oglDraw) newHeight = oglDraw->getHeight();
218 		newConfig = false;
219 	}
220 	if(newWidth > 0 && newHeight > 0)
221 	{
222 		OGLDrawable *draw = oglDraw;
223 		if(init(newWidth, newHeight, config)) oldDraw = draw;
224 		newWidth = newHeight = -1;
225 	}
226 	retval = oglDraw->getGLXDrawable();
227 	return retval;
228 }
229 
230 
swapBuffers(void)231 void VirtualWin::swapBuffers(void)
232 {
233 	CriticalSection::SafeLock l(mutex);
234 	if(doWMDelete) THROW("Window has been deleted by window manager");
235 	if(oglDraw)
236 	{
237 		if(fconfig.amdgpuHack)
238 			copyPixels(0, 0, oglDraw->getWidth(), oglDraw->getHeight(), 0, 0,
239 				getGLXDrawable(), GL_BACK, GL_FRONT);
240 		else
241 			oglDraw->swap();
242 	}
243 }
244 
245 
wmDelete(void)246 void VirtualWin::wmDelete(void)
247 {
248 	CriticalSection::SafeLock l(mutex);
249 	doWMDelete = doVGLWMDelete;
250 }
251 
252 
vglWMDelete(void)253 void VirtualWin::vglWMDelete(void)
254 {
255 	CriticalSection::SafeLock l(mutex);
256 	doVGLWMDelete = true;
257 }
258 
259 
readback(GLint drawBuf,bool spoilLast,bool sync)260 void VirtualWin::readback(GLint drawBuf, bool spoilLast, bool sync)
261 {
262 	fconfig_reloadenv();
263 	bool doStereo = false;  int stereoMode = fconfig.stereo;
264 
265 	if(fconfig.readback == RRREAD_NONE || !checkRenderMode())
266 		return;
267 
268 	CriticalSection::SafeLock l(mutex);
269 	if(doWMDelete) THROW("Window has been deleted by window manager");
270 
271 	dirty = false;
272 
273 	int compress = fconfig.compress;
274 	if(sync && strlen(fconfig.transport) == 0) compress = RRCOMP_PROXY;
275 
276 	if(isStereo() && stereoMode != RRSTEREO_LEYE && stereoMode != RRSTEREO_REYE)
277 	{
278 		if(DrawingToRight() || rdirty) doStereo = true;
279 		rdirty = false;
280 		if(doStereo && compress == RRCOMP_YUV && strlen(fconfig.transport) == 0)
281 		{
282 			static bool message3 = false;
283 			if(!message3)
284 			{
285 				vglout.println("[VGL] NOTICE: Quad-buffered stereo cannot be used with YUV encoding.");
286 				vglout.println("[VGL]    Using anaglyphic stereo instead.");
287 				message3 = true;
288 			}
289 			stereoMode = RRSTEREO_REDCYAN;
290 		}
291 		else if(doStereo && _Trans[compress] != RRTRANS_VGL
292 			&& stereoMode == RRSTEREO_QUADBUF && strlen(fconfig.transport) == 0)
293 		{
294 			static bool message = false;
295 			if(!message)
296 			{
297 				vglout.println("[VGL] NOTICE: Quad-buffered stereo requires the VGL Transport.");
298 				vglout.println("[VGL]    Using anaglyphic stereo instead.");
299 				message = true;
300 			}
301 			stereoMode = RRSTEREO_REDCYAN;
302 		}
303 		else if(doStereo && !stereoVisual && stereoMode == RRSTEREO_QUADBUF
304 			&& strlen(fconfig.transport) == 0)
305 		{
306 			static bool message2 = false;
307 			if(!message2)
308 			{
309 				vglout.println("[VGL] NOTICE: Cannot use quad-buffered stereo because no stereo visuals are");
310 				vglout.println("[VGL]    available on the 2D X server.  Using anaglyphic stereo instead.");
311 				message2 = true;
312 			}
313 			stereoMode = RRSTEREO_REDCYAN;
314 		}
315 	}
316 
317 	if(strlen(fconfig.transport) > 0)
318 	{
319 		sendPlugin(drawBuf, spoilLast, sync, doStereo, stereoMode);
320 		return;
321 	}
322 
323 	switch(compress)
324 	{
325 		case RRCOMP_PROXY:
326 			sendX11(drawBuf, spoilLast, sync, doStereo, stereoMode);
327 			break;
328 
329 		case RRCOMP_JPEG:
330 		case RRCOMP_RGB:
331 		case RRCOMP_YUV:
332 			if(!vglconn)
333 			{
334 				vglconn = new VGLTrans();
335 				vglconn->connect(
336 					strlen(fconfig.client) > 0 ? fconfig.client : DisplayString(dpy),
337 					fconfig.port);
338 			}
339 			sendVGL(drawBuf, spoilLast, doStereo, stereoMode, compress, fconfig.qual,
340 				fconfig.subsamp);
341 			break;
342 		#ifdef USEXV
343 		case RRCOMP_XV:
344 			sendXV(drawBuf, spoilLast, sync, doStereo, stereoMode);
345 		#endif
346 	}
347 }
348 
349 
setupPluginTempContext(GLint drawBuf)350 TempContext *VirtualWin::setupPluginTempContext(GLint drawBuf)
351 {
352 	// This code is largely copied from VirtualDrawable::readPixels().  It
353 	// establishes a temporary OpenGL context suitable for creating GPU-based
354 	// buffer objects in RRTransGetFrame() and reading back the rendered frame in
355 	// RRTransSendFrame(), should a plugin choose to do so.
356 	TempContext *tc = NULL;
357 
358 	int renderMode = 0;
359 	_glGetIntegerv(GL_RENDER_MODE, &renderMode);
360 	if(renderMode != GL_RENDER && renderMode != 0)
361 	{
362 		if(!alreadyWarnedPluginRenderMode && fconfig.verbose)
363 		{
364 			vglout.print("[VGL] WARNING: Failed to establish temporary OpenGL context for image\n");
365 			vglout.print("[VGL]    transport plugin one or more times because render mode != GL_RENDER.\n");
366 			alreadyWarnedPluginRenderMode = true;
367 		}
368 	}
369 	else
370 	{
371 		initReadbackContext();
372 		tc = new TempContext(dpy, getGLXDrawable(), getGLXDrawable(), ctx);
373 		VGLReadBuffer(drawBuf);
374 	}
375 
376 	return tc;
377 }
378 
379 
sendPlugin(GLint drawBuf,bool spoilLast,bool sync,bool doStereo,int stereoMode)380 void VirtualWin::sendPlugin(GLint drawBuf, bool spoilLast, bool sync,
381 	bool doStereo, int stereoMode)
382 {
383 	Frame f;
384 	int w = oglDraw->getWidth(), h = oglDraw->getHeight();
385 	RRFrame *rrframe = NULL;
386 	TempContext *tc = NULL;
387 
388 	try
389 	{
390 		if(!plugin)
391 		{
392 			tc = setupPluginTempContext(drawBuf);
393 			plugin = new TransPlugin(dpy, x11Draw, fconfig.transport);
394 			plugin->connect(
395 				strlen(fconfig.client) > 0 ? fconfig.client : DisplayString(dpy),
396 				fconfig.port);
397 		}
398 
399 		if(spoilLast && fconfig.spoil && !plugin->ready())
400 		{
401 			delete tc;  return;
402 		}
403 		if(!tc) tc = setupPluginTempContext(drawBuf);
404 		if(!fconfig.spoil) plugin->synchronize();
405 
406 		if(oglDraw->getRGBSize() != 24)
407 			THROW("Transport plugins require 8 bits per component");
408 		int desiredFormat = RRTRANS_RGB;
409 		if(oglDraw->getFormat() == GL_BGR) desiredFormat = RRTRANS_BGR;
410 		else if(oglDraw->getFormat() == GL_BGRA) desiredFormat = RRTRANS_BGRA;
411 		else if(oglDraw->getFormat() == GL_RGBA) desiredFormat = RRTRANS_RGBA;
412 
413 		rrframe = plugin->getFrame(w, h, desiredFormat,
414 			doStereo && stereoMode == RRSTEREO_QUADBUF);
415 		if(rrframe->bits)
416 		{
417 			f.init(rrframe->bits, rrframe->w, rrframe->pitch, rrframe->h,
418 				trans2pf[rrframe->format], FRAME_BOTTOMUP);
419 
420 			if(doStereo && stereoMode == RRSTEREO_QUADBUF && rrframe->rbits == NULL)
421 			{
422 				static bool message = false;
423 				if(!message)
424 				{
425 					vglout.println("[VGL] NOTICE: Quad-buffered stereo is not supported by the plugin.");
426 					vglout.println("[VGL]    Using anaglyphic stereo instead.");
427 					message = true;
428 				}
429 				stereoMode = RRSTEREO_REDCYAN;
430 			}
431 			if(doStereo && IS_ANAGLYPHIC(stereoMode))
432 			{
433 				stereoFrame.deInit();
434 				makeAnaglyph(&f, drawBuf, stereoMode);
435 			}
436 			else if(doStereo && IS_PASSIVE(stereoMode))
437 			{
438 				rFrame.deInit();  gFrame.deInit();  bFrame.deInit();
439 				makePassive(&f, drawBuf, GL_NONE, stereoMode);
440 			}
441 			else
442 			{
443 				rFrame.deInit();  gFrame.deInit();  bFrame.deInit();
444 				stereoFrame.deInit();
445 				GLint readBuf = drawBuf;
446 				if(doStereo || stereoMode == RRSTEREO_LEYE) readBuf = LEYE(drawBuf);
447 				if(stereoMode == RRSTEREO_REYE) readBuf = REYE(drawBuf);
448 				readPixels(0, 0, rrframe->w, rrframe->pitch, rrframe->h, GL_NONE, f.pf,
449 					rrframe->bits, readBuf, doStereo);
450 				if(doStereo && rrframe->rbits)
451 					readPixels(0, 0, rrframe->w, rrframe->pitch, rrframe->h, GL_NONE,
452 						f.pf, rrframe->rbits, REYE(drawBuf), doStereo);
453 			}
454 			if(!syncdpy) { XSync(dpy, False);  syncdpy = true; }
455 			if(fconfig.logo) f.addLogo();
456 		}
457 		plugin->sendFrame(rrframe, sync);
458 	}
459 	catch(...)
460 	{
461 		delete tc;
462 		throw;
463 	}
464 	delete tc;
465 }
466 
467 
sendVGL(GLint drawBuf,bool spoilLast,bool doStereo,int stereoMode,int compress,int qual,int subsamp)468 void VirtualWin::sendVGL(GLint drawBuf, bool spoilLast, bool doStereo,
469 	int stereoMode, int compress, int qual, int subsamp)
470 {
471 	int w = oglDraw->getWidth(), h = oglDraw->getHeight();
472 
473 	if(spoilLast && fconfig.spoil && !vglconn->isReady())
474 		return;
475 	Frame *f;
476 
477 	if(oglDraw->getRGBSize() != 24)
478 		THROW("The VGL Transport requires 8 bits per component");
479 	int glFormat = GL_RGB, pixelFormat = PF_RGB;
480 	if(compress != RRCOMP_RGB)
481 	{
482 		glFormat = oglDraw->getFormat();
483 		if(glFormat == GL_RGBA) pixelFormat = PF_RGBX;
484 		else if(glFormat == GL_BGR) pixelFormat = PF_BGR;
485 		else if(glFormat == GL_BGRA) pixelFormat = PF_BGRX;
486 	}
487 
488 	if(!fconfig.spoil) vglconn->synchronize();
489 	ERRIFNOT(f = vglconn->getFrame(w, h, pixelFormat, FRAME_BOTTOMUP,
490 		doStereo && stereoMode == RRSTEREO_QUADBUF));
491 	if(doStereo && IS_ANAGLYPHIC(stereoMode))
492 	{
493 		stereoFrame.deInit();
494 		makeAnaglyph(f, drawBuf, stereoMode);
495 	}
496 	else if(doStereo && IS_PASSIVE(stereoMode))
497 	{
498 		rFrame.deInit();  gFrame.deInit();  bFrame.deInit();
499 		makePassive(f, drawBuf, glFormat, stereoMode);
500 	}
501 	else
502 	{
503 		rFrame.deInit();  gFrame.deInit();  bFrame.deInit();  stereoFrame.deInit();
504 		GLint readBuf = drawBuf;
505 		if(doStereo || stereoMode == RRSTEREO_LEYE) readBuf = LEYE(drawBuf);
506 		if(stereoMode == RRSTEREO_REYE) readBuf = REYE(drawBuf);
507 		readPixels(0, 0, f->hdr.framew, f->pitch, f->hdr.frameh, glFormat, f->pf,
508 			f->bits, readBuf, doStereo);
509 		if(doStereo && f->rbits)
510 			readPixels(0, 0, f->hdr.framew, f->pitch, f->hdr.frameh, glFormat, f->pf,
511 				f->rbits, REYE(drawBuf), doStereo);
512 	}
513 	f->hdr.winid = x11Draw;
514 	f->hdr.framew = f->hdr.width;
515 	f->hdr.frameh = f->hdr.height;
516 	f->hdr.x = 0;
517 	f->hdr.y = 0;
518 	f->hdr.qual = qual;
519 	f->hdr.subsamp = subsamp;
520 	f->hdr.compress = (unsigned char)compress;
521 	if(!syncdpy) { XSync(dpy, False);  syncdpy = true; }
522 	if(fconfig.logo) f->addLogo();
523 	vglconn->sendFrame(f);
524 }
525 
526 
sendX11(GLint drawBuf,bool spoilLast,bool sync,bool doStereo,int stereoMode)527 void VirtualWin::sendX11(GLint drawBuf, bool spoilLast, bool sync,
528 	bool doStereo, int stereoMode)
529 {
530 	int width = oglDraw->getWidth(), height = oglDraw->getHeight();
531 
532 	FBXFrame *f;
533 	if(!x11trans) x11trans = new X11Trans();
534 	if(spoilLast && fconfig.spoil && !x11trans->isReady()) return;
535 	if(!fconfig.spoil) x11trans->synchronize();
536 	ERRIFNOT(f = x11trans->getFrame(dpy, x11Draw, width, height));
537 	f->flags |= FRAME_BOTTOMUP;
538 	if(doStereo && IS_ANAGLYPHIC(stereoMode))
539 	{
540 		stereoFrame.deInit();
541 		makeAnaglyph(f, drawBuf, stereoMode);
542 	}
543 	else
544 	{
545 		rFrame.deInit();  gFrame.deInit();  bFrame.deInit();
546 		if(doStereo && IS_PASSIVE(stereoMode))
547 			makePassive(f, drawBuf, GL_NONE, stereoMode);
548 		else
549 		{
550 			stereoFrame.deInit();
551 			GLint readBuf = drawBuf;
552 			if(stereoMode == RRSTEREO_REYE) readBuf = REYE(drawBuf);
553 			else if(stereoMode == RRSTEREO_LEYE) readBuf = LEYE(drawBuf);
554 			readPixels(0, 0, min(width, f->hdr.framew), f->pitch,
555 				min(height, f->hdr.frameh), GL_NONE, f->pf, f->bits, readBuf, false);
556 		}
557 	}
558 	if(fconfig.logo) f->addLogo();
559 	x11trans->sendFrame(f, sync);
560 }
561 
562 
563 #ifdef USEXV
564 
sendXV(GLint drawBuf,bool spoilLast,bool sync,bool doStereo,int stereoMode)565 void VirtualWin::sendXV(GLint drawBuf, bool spoilLast, bool sync,
566 	bool doStereo, int stereoMode)
567 {
568 	int width = oglDraw->getWidth(), height = oglDraw->getHeight();
569 
570 	XVFrame *f;
571 	if(!xvtrans) xvtrans = new XVTrans();
572 	if(spoilLast && fconfig.spoil && !xvtrans->isReady()) return;
573 	if(!fconfig.spoil) xvtrans->synchronize();
574 	ERRIFNOT(f = xvtrans->getFrame(dpy, x11Draw, width, height));
575 	rrframeheader hdr;
576 	hdr.x = hdr.y = 0;
577 	hdr.width = hdr.framew = width;
578 	hdr.height = hdr.frameh = height;
579 
580 	if(oglDraw->getRGBSize() != 24)
581 		THROW("The XV Transport requires 8 bits per component");
582 	int glFormat = oglDraw->getFormat(), pixelFormat = PF_RGB;
583 	if(glFormat == GL_RGBA) pixelFormat = PF_RGBX;
584 	else if(glFormat == GL_BGR) pixelFormat = PF_BGR;
585 	else if(glFormat == GL_BGRA) pixelFormat = PF_BGRX;
586 
587 	frame.init(hdr, pixelFormat, FRAME_BOTTOMUP, false);
588 
589 	if(doStereo && IS_ANAGLYPHIC(stereoMode))
590 	{
591 		stereoFrame.deInit();
592 		makeAnaglyph(&frame, drawBuf, stereoMode);
593 	}
594 	else if(doStereo && IS_PASSIVE(stereoMode))
595 	{
596 		rFrame.deInit();  gFrame.deInit();  bFrame.deInit();
597 		makePassive(&frame, drawBuf, glFormat, stereoMode);
598 	}
599 	else
600 	{
601 		rFrame.deInit();  gFrame.deInit();  bFrame.deInit();  stereoFrame.deInit();
602 		GLint readBuf = drawBuf;
603 		if(stereoMode == RRSTEREO_REYE) readBuf = REYE(drawBuf);
604 		else if(stereoMode == RRSTEREO_LEYE) readBuf = LEYE(drawBuf);
605 		readPixels(0, 0, min(width, frame.hdr.framew), frame.pitch,
606 			min(height, frame.hdr.frameh), glFormat, frame.pf, frame.bits, readBuf,
607 			false);
608 	}
609 
610 	if(fconfig.logo) frame.addLogo();
611 
612 	*f = frame;
613 	xvtrans->sendFrame(f, sync);
614 }
615 
616 #endif
617 
618 
makeAnaglyph(Frame * f,int drawBuf,int stereoMode)619 void VirtualWin::makeAnaglyph(Frame *f, int drawBuf, int stereoMode)
620 {
621 	int rbuf = LEYE(drawBuf), gbuf = REYE(drawBuf),  bbuf = REYE(drawBuf);
622 	if(stereoMode == RRSTEREO_GREENMAGENTA)
623 	{
624 		rbuf = REYE(drawBuf);  gbuf = LEYE(drawBuf);  bbuf = REYE(drawBuf);
625 	}
626 	else if(stereoMode == RRSTEREO_BLUEYELLOW)
627 	{
628 		rbuf = REYE(drawBuf);  gbuf = REYE(drawBuf);  bbuf = LEYE(drawBuf);
629 	}
630 	rFrame.init(f->hdr, PF_COMP, f->flags, false);
631 	readPixels(0, 0, rFrame.hdr.framew, rFrame.pitch, rFrame.hdr.frameh, GL_RED,
632 		rFrame.pf, rFrame.bits, rbuf, false);
633 	gFrame.init(f->hdr, PF_COMP, f->flags, false);
634 	readPixels(0, 0, gFrame.hdr.framew, gFrame.pitch, gFrame.hdr.frameh,
635 		GL_GREEN, gFrame.pf, gFrame.bits, gbuf, false);
636 	bFrame.init(f->hdr, PF_COMP, f->flags, false);
637 	readPixels(0, 0, bFrame.hdr.framew, bFrame.pitch, bFrame.hdr.frameh, GL_BLUE,
638 		bFrame.pf, bFrame.bits, bbuf, false);
639 	profAnaglyph.startFrame();
640 	f->makeAnaglyph(rFrame, gFrame, bFrame);
641 	profAnaglyph.endFrame(f->hdr.framew * f->hdr.frameh, 0, 1);
642 }
643 
644 
makePassive(Frame * f,int drawBuf,GLenum glFormat,int stereoMode)645 void VirtualWin::makePassive(Frame *f, int drawBuf, GLenum glFormat,
646 	int stereoMode)
647 {
648 	stereoFrame.init(f->hdr, f->pf->id, f->flags, true);
649 	readPixels(0, 0, stereoFrame.hdr.framew, stereoFrame.pitch,
650 		stereoFrame.hdr.frameh, glFormat, stereoFrame.pf, stereoFrame.bits,
651 		LEYE(drawBuf), true);
652 	readPixels(0, 0, stereoFrame.hdr.framew, stereoFrame.pitch,
653 		stereoFrame.hdr.frameh, glFormat, stereoFrame.pf, stereoFrame.rbits,
654 		REYE(drawBuf), true);
655 	profPassive.startFrame();
656 	f->makePassive(stereoFrame, stereoMode);
657 	profPassive.endFrame(f->hdr.framew * f->hdr.frameh, 0, 1);
658 }
659 
660 
readPixels(GLint x,GLint y,GLint width,GLint pitch,GLint height,GLenum glFormat,PF * pf,GLubyte * bits,GLint buf,bool stereo)661 void VirtualWin::readPixels(GLint x, GLint y, GLint width, GLint pitch,
662 	GLint height, GLenum glFormat, PF *pf, GLubyte *bits, GLint buf, bool stereo)
663 {
664 	VirtualDrawable::readPixels(x, y, width, pitch, height, glFormat, pf, bits,
665 		buf, stereo);
666 
667 	// Gamma correction
668 	if(fconfig.gamma != 0.0 && fconfig.gamma != 1.0 && fconfig.gamma != -1.0)
669 	{
670 		profGamma.startFrame();
671 		static bool first = true;
672 		if(first)
673 		{
674 			first = false;
675 			if(fconfig.verbose)
676 				vglout.println("[VGL] Using software gamma correction (correction factor=%f)\n",
677 					fconfig.gamma);
678 		}
679 		if(pf->bpc == 10)
680 		{
681 			int h = height;
682 			while(h--)
683 			{
684 				int w = width;
685 				unsigned int *srcPixel = (unsigned int *)bits;
686 				while(w--)
687 				{
688 					unsigned int r =
689 						fconfig.gamma_lut10[(*srcPixel >> pf->rshift) & 1023];
690 					unsigned int g =
691 						fconfig.gamma_lut10[(*srcPixel >> pf->gshift) & 1023];
692 					unsigned int b =
693 						fconfig.gamma_lut10[(*srcPixel >> pf->bshift) & 1023];
694 					*srcPixel++ =
695 						(r << pf->rshift) | (g << pf->gshift) | (b << pf->bshift);
696 				}
697 				bits += pitch;
698 			}
699 		}
700 		else
701 		{
702 			unsigned short *ptr1, *ptr2 = (unsigned short *)(&bits[pitch * height]);
703 			for(ptr1 = (unsigned short *)bits; ptr1 < ptr2; ptr1++)
704 				*ptr1 = fconfig.gamma_lut16[*ptr1];
705 			if((pitch * height) % 2 != 0)
706 				bits[pitch * height - 1] = fconfig.gamma_lut[bits[pitch * height - 1]];
707 		}
708 		profGamma.endFrame(width * height, 0, stereo ? 0.5 : 1);
709 	}
710 }
711 
712 
isStereo(void)713 bool VirtualWin::isStereo(void)
714 {
715 	return oglDraw && oglDraw->isStereo();
716 }
717