VirtualBox

source: vbox/trunk/src/VBox/Additions/common/crOpenGL/load.c@ 28800

Last change on this file since 28800 was 26909, checked in by vboxsync, 15 years ago

crOpenGL: fix flickering when opengl context is used with multiply windows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.9 KB
Line 
1/* Copyright (c) 2001, Stanford University
2 * All rights reserved
3 *
4 * See the file LICENSE.txt for information on redistributing this software.
5 */
6
7#include "cr_spu.h"
8#include "cr_net.h"
9#include "cr_error.h"
10#include "cr_mem.h"
11#include "cr_string.h"
12#include "cr_net.h"
13#include "cr_environment.h"
14#include "cr_process.h"
15#include "cr_rand.h"
16#include "cr_netserver.h"
17#include "stub.h"
18#include <stdlib.h>
19#include <string.h>
20#include <signal.h>
21#ifndef WINDOWS
22#include <sys/types.h>
23#include <unistd.h>
24#endif
25#ifdef CHROMIUM_THREADSAFE
26#include "cr_threads.h"
27#endif
28
29/**
30 * If you change this, see the comments in tilesortspu_context.c
31 */
32#define MAGIC_CONTEXT_BASE 500
33
34#define CONFIG_LOOKUP_FILE ".crconfigs"
35
36#ifdef WINDOWS
37#define PYTHON_EXE "python.exe"
38#else
39#define PYTHON_EXE "python"
40#endif
41
42#ifdef WINDOWS
43static char* gsViewportHackApps[] = {"googleearth.exe", NULL};
44#endif
45
46static int stub_initialized = 0;
47
48/* NOTE: 'SPUDispatchTable glim' is declared in NULLfuncs.py now */
49/* NOTE: 'SPUDispatchTable stubThreadsafeDispatch' is declared in tsfuncs.c */
50Stub stub;
51
52
53static void stubInitNativeDispatch( void )
54{
55#define MAX_FUNCS 1000
56 SPUNamedFunctionTable gl_funcs[MAX_FUNCS];
57 int numFuncs;
58
59 numFuncs = crLoadOpenGL( &stub.wsInterface, gl_funcs );
60
61 stub.haveNativeOpenGL = (numFuncs > 0);
62
63 /* XXX call this after context binding */
64 numFuncs += crLoadOpenGLExtensions( &stub.wsInterface, gl_funcs + numFuncs );
65
66 CRASSERT(numFuncs < MAX_FUNCS);
67
68 crSPUInitDispatchTable( &stub.nativeDispatch );
69 crSPUInitDispatch( &stub.nativeDispatch, gl_funcs );
70 crSPUInitDispatchNops( &stub.nativeDispatch );
71#undef MAX_FUNCS
72}
73
74
75/** Pointer to the SPU's real glClear and glViewport functions */
76static ClearFunc_t origClear;
77static ViewportFunc_t origViewport;
78static SwapBuffersFunc_t origSwapBuffers;
79static DrawBufferFunc_t origDrawBuffer;
80static ScissorFunc_t origScissor;
81
82static void stubCheckWindowState(WindowInfo *window)
83{
84 bool bForceUpdate = false;
85
86#ifdef WINDOWS
87 /* @todo install hook and track for WM_DISPLAYCHANGE */
88 {
89 DEVMODE devMode;
90
91 devMode.dmSize = sizeof(DEVMODE);
92 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
93
94 if (devMode.dmPelsWidth!=window->dmPelsWidth || devMode.dmPelsHeight!=window->dmPelsHeight)
95 {
96 crDebug("Resolution changed(%d,%d), forcing window Pos/Size update", devMode.dmPelsWidth, devMode.dmPelsHeight);
97 window->dmPelsWidth = devMode.dmPelsWidth;
98 window->dmPelsHeight = devMode.dmPelsHeight;
99 bForceUpdate = true;
100 }
101 }
102#endif
103
104 stubUpdateWindowGeometry(window, bForceUpdate);
105
106#if defined(GLX) || defined (WINDOWS)
107 if (stub.trackWindowVisibleRgn)
108 {
109 stubUpdateWindowVisibileRegions(window);
110 }
111#endif
112
113 if (stub.trackWindowVisibility && window->type == CHROMIUM && window->drawable) {
114 const int mapped = stubIsWindowVisible(window);
115 if (mapped != window->mapped) {
116 crDebug("Dispatched: WindowShow(%i, %i)", window->spuWindow, mapped);
117 stub.spu->dispatch_table.WindowShow(window->spuWindow, mapped);
118 window->mapped = mapped;
119 }
120 }
121}
122
123static bool stubSystemWindowExist(WindowInfo *pWindow)
124{
125#ifdef WINDOWS
126 if (!WindowFromDC(pWindow->drawable))
127 {
128 return false;
129 }
130#else
131 Window root;
132 int x, y;
133 unsigned int border, depth, w, h;
134
135 if (!XGetGeometry(pWindow->dpy, pWindow->drawable, &root, &x, &y, &w, &h, &border, &depth))
136 {
137 return false;
138 }
139#endif
140
141 return true;
142}
143
144static void stubCheckWindowsCB(unsigned long key, void *data1, void *data2)
145{
146 WindowInfo *pWindow = (WindowInfo *) data1;
147 ContextInfo *pCtx = (ContextInfo *) data2;
148
149 if (pWindow == pCtx->currentDrawable
150 || pWindow->type!=CHROMIUM
151 || pWindow->pOwner!=pCtx)
152 {
153 return;
154 }
155
156 if (!stubSystemWindowExist(pWindow))
157 {
158#ifdef WINDOWS
159 crWindowDestroy((GLint)pWindow->hWnd);
160#else
161 crWindowDestroy((GLint)pWindow->drawable);
162#endif
163 return;
164 }
165
166 stubCheckWindowState(pWindow);
167}
168
169static void stubCheckWindowsState(void)
170{
171 CRASSERT(stub.trackWindowSize || stub.trackWindowPos);
172
173 if (!stub.currentContext)
174 return;
175
176 stubCheckWindowState(stub.currentContext->currentDrawable);
177
178 crHashtableWalk(stub.windowTable, stubCheckWindowsCB, stub.currentContext);
179}
180
181
182/**
183 * Override the head SPU's glClear function.
184 * We're basically trapping this function so that we can poll the
185 * application window size at a regular interval.
186 */
187static void SPU_APIENTRY trapClear(GLbitfield mask)
188{
189 stubCheckWindowsState();
190 /* call the original SPU glClear function */
191 origClear(mask);
192}
193
194/**
195 * As above, but for glViewport. Most apps call glViewport before
196 * glClear when a window is resized.
197 */
198static void SPU_APIENTRY trapViewport(GLint x, GLint y, GLsizei w, GLsizei h)
199{
200 stubCheckWindowsState();
201 /* call the original SPU glViewport function */
202 if (!stub.viewportHack)
203 {
204 origViewport(x, y, w, h);
205 }
206 else
207 {
208 int winX, winY;
209 unsigned int winW, winH;
210 WindowInfo *pWindow;
211 pWindow = stub.currentContext->currentDrawable;
212 stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
213 origViewport(0, 0, winW, winH);
214 }
215}
216
217static void SPU_APIENTRY trapSwapBuffers(GLint window, GLint flags)
218{
219 stubCheckWindowsState();
220 origSwapBuffers(window, flags);
221}
222
223static void SPU_APIENTRY trapDrawBuffer(GLenum buf)
224{
225 stubCheckWindowsState();
226 origDrawBuffer(buf);
227}
228
229static void SPU_APIENTRY trapScissor(GLint x, GLint y, GLsizei w, GLsizei h)
230{
231 int winX, winY;
232 unsigned int winW, winH;
233 WindowInfo *pWindow;
234 pWindow = stub.currentContext->currentDrawable;
235 stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
236 origScissor(0, 0, winW, winH);
237}
238
239/**
240 * Use the GL function pointers in <spu> to initialize the static glim
241 * dispatch table.
242 */
243static void stubInitSPUDispatch(SPU *spu)
244{
245 crSPUInitDispatchTable( &stub.spuDispatch );
246 crSPUCopyDispatchTable( &stub.spuDispatch, &(spu->dispatch_table) );
247
248 if (stub.trackWindowSize || stub.trackWindowPos || stub.trackWindowVisibleRgn) {
249 /* patch-in special glClear/Viewport function to track window sizing */
250 origClear = stub.spuDispatch.Clear;
251 origViewport = stub.spuDispatch.Viewport;
252 origSwapBuffers = stub.spuDispatch.SwapBuffers;
253 origDrawBuffer = stub.spuDispatch.DrawBuffer;
254 origScissor = stub.spuDispatch.Scissor;
255 stub.spuDispatch.Clear = trapClear;
256 stub.spuDispatch.Viewport = trapViewport;
257 if (stub.viewportHack)
258 stub.spuDispatch.Scissor = trapScissor;
259 /*stub.spuDispatch.SwapBuffers = trapSwapBuffers;
260 stub.spuDispatch.DrawBuffer = trapDrawBuffer;*/
261 }
262
263 crSPUCopyDispatchTable( &glim, &stub.spuDispatch );
264}
265
266// Callback function, used to destroy all created contexts
267static void hsWalkStubDestroyContexts(unsigned long key, void *data1, void *data2)
268{
269 stubDestroyContext(key);
270}
271
272/**
273 * This is called when we exit.
274 * We call all the SPU's cleanup functions.
275 */
276static void stubSPUTearDown(void)
277{
278 crDebug("stubSPUTearDown");
279 if (!stub_initialized) return;
280
281 stub_initialized = 0;
282
283#ifdef WINDOWS
284 stubUninstallWindowMessageHook();
285#endif
286
287 //delete all created contexts
288 stubMakeCurrent( NULL, NULL);
289 crHashtableWalk(stub.contextTable, hsWalkStubDestroyContexts, NULL);
290
291 /* shutdown, now trap any calls to a NULL dispatcher */
292 crSPUCopyDispatchTable(&glim, &stubNULLDispatch);
293
294 crSPUUnloadChain(stub.spu);
295 stub.spu = NULL;
296
297#ifndef Linux
298 crUnloadOpenGL();
299#endif
300
301 crNetTearDown();
302
303#ifdef GLX
304 if (stub.xshmSI.shmid>=0)
305 {
306 shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
307 shmdt(stub.xshmSI.shmaddr);
308 }
309 crFreeHashtable(stub.pGLXPixmapsHash, crFree);
310#endif
311
312 crFreeHashtable(stub.windowTable, crFree);
313 crFreeHashtable(stub.contextTable, NULL);
314
315 crMemset(&stub, 0, sizeof(stub) );
316}
317
318static void stubSPUSafeTearDown(void)
319{
320#ifdef CHROMIUM_THREADSAFE
321 CRmutex *mutex;
322#endif
323
324 if (!stub_initialized) return;
325 stub_initialized = 0;
326
327#ifdef CHROMIUM_THREADSAFE
328 mutex = &stub.mutex;
329 crLockMutex(mutex);
330#endif
331 crDebug("stubSPUSafeTearDown");
332 crNetTearDown();
333#ifdef WINDOWS
334 stubUninstallWindowMessageHook();
335#endif
336 crMemset(&stub, 0, sizeof(stub));
337#ifdef CHROMIUM_THREADSAFE
338 crUnlockMutex(mutex);
339 crFreeMutex(mutex);
340#endif
341}
342
343
344static void stubExitHandler(void)
345{
346 stubSPUSafeTearDown();
347}
348
349/**
350 * Called when we receive a SIGTERM signal.
351 */
352static void stubSignalHandler(int signo)
353{
354 stubSPUSafeTearDown();
355 exit(0); /* this causes stubExitHandler() to be called */
356}
357
358
359/**
360 * Init variables in the stub structure, install signal handler.
361 */
362static void stubInitVars(void)
363{
364 WindowInfo *defaultWin;
365
366#ifdef CHROMIUM_THREADSAFE
367 crInitMutex(&stub.mutex);
368#endif
369
370 /* At the very least we want CR_RGB_BIT. */
371 stub.haveNativeOpenGL = GL_FALSE;
372 stub.spu = NULL;
373 stub.appDrawCursor = 0;
374 stub.minChromiumWindowWidth = 0;
375 stub.minChromiumWindowHeight = 0;
376 stub.maxChromiumWindowWidth = 0;
377 stub.maxChromiumWindowHeight = 0;
378 stub.matchChromiumWindowCount = 0;
379 stub.matchChromiumWindowID = NULL;
380 stub.matchWindowTitle = NULL;
381 stub.ignoreFreeglutMenus = 0;
382 stub.threadSafe = GL_FALSE;
383 stub.trackWindowSize = 0;
384 stub.trackWindowPos = 0;
385 stub.trackWindowVisibility = 0;
386 stub.trackWindowVisibleRgn = 0;
387 stub.mothershipPID = 0;
388 stub.spu_dir = NULL;
389
390 stub.freeContextNumber = MAGIC_CONTEXT_BASE;
391 stub.contextTable = crAllocHashtable();
392 stub.currentContext = NULL;
393
394 stub.windowTable = crAllocHashtable();
395
396 defaultWin = (WindowInfo *) crCalloc(sizeof(WindowInfo));
397 defaultWin->type = CHROMIUM;
398 defaultWin->spuWindow = 0; /* window 0 always exists */
399#ifdef WINDOWS
400 defaultWin->hVisibleRegion = INVALID_HANDLE_VALUE;
401#elif defined(GLX)
402 defaultWin->pVisibleRegions = NULL;
403 defaultWin->cVisibleRegions = 0;
404#endif
405 crHashtableAdd(stub.windowTable, 0, defaultWin);
406
407#if 1
408 atexit(stubExitHandler);
409 signal(SIGTERM, stubSignalHandler);
410 signal(SIGINT, stubSignalHandler);
411#ifndef WINDOWS
412 signal(SIGPIPE, SIG_IGN); /* the networking code should catch this */
413#endif
414#else
415 (void) stubExitHandler;
416 (void) stubSignalHandler;
417#endif
418}
419
420
421/**
422 * Return a free port number for the mothership to use, or -1 if we
423 * can't find one.
424 */
425static int
426GenerateMothershipPort(void)
427{
428 const int MAX_PORT = 10100;
429 unsigned short port;
430
431 /* generate initial port number randomly */
432 crRandAutoSeed();
433 port = (unsigned short) crRandInt(10001, MAX_PORT);
434
435#ifdef WINDOWS
436 /* XXX should implement a free port check here */
437 return port;
438#else
439 /*
440 * See if this port number really is free, try another if needed.
441 */
442 {
443 struct sockaddr_in servaddr;
444 int so_reuseaddr = 1;
445 int sock, k;
446
447 /* create socket */
448 sock = socket(AF_INET, SOCK_STREAM, 0);
449 CRASSERT(sock > 2);
450
451 /* deallocate socket/port when we exit */
452 k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
453 (char *) &so_reuseaddr, sizeof(so_reuseaddr));
454 CRASSERT(k == 0);
455
456 /* initialize the servaddr struct */
457 crMemset(&servaddr, 0, sizeof(servaddr) );
458 servaddr.sin_family = AF_INET;
459 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
460
461 while (port < MAX_PORT) {
462 /* Bind to the given port number, return -1 if we fail */
463 servaddr.sin_port = htons((unsigned short) port);
464 k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
465 if (k) {
466 /* failed to create port. try next one. */
467 port++;
468 }
469 else {
470 /* free the socket/port now so mothership can make it */
471 close(sock);
472 return port;
473 }
474 }
475 }
476#endif /* WINDOWS */
477 return -1;
478}
479
480
481/**
482 * Try to determine which mothership configuration to use for this program.
483 */
484static char **
485LookupMothershipConfig(const char *procName)
486{
487 const int procNameLen = crStrlen(procName);
488 FILE *f;
489 const char *home;
490 char configPath[1000];
491
492 /* first, check if the CR_CONFIG env var is set */
493 {
494 const char *conf = crGetenv("CR_CONFIG");
495 if (conf && crStrlen(conf) > 0)
496 return crStrSplit(conf, " ");
497 }
498
499 /* second, look up config name from config file */
500 home = crGetenv("HOME");
501 if (home)
502 sprintf(configPath, "%s/%s", home, CONFIG_LOOKUP_FILE);
503 else
504 crStrcpy(configPath, CONFIG_LOOKUP_FILE); /* from current dir */
505 /* Check if the CR_CONFIG_PATH env var is set. */
506 {
507 const char *conf = crGetenv("CR_CONFIG_PATH");
508 if (conf)
509 crStrcpy(configPath, conf); /* from env var */
510 }
511
512 f = fopen(configPath, "r");
513 if (!f) {
514 return NULL;
515 }
516
517 while (!feof(f)) {
518 char line[1000];
519 char **args;
520 fgets(line, 999, f);
521 line[crStrlen(line) - 1] = 0; /* remove trailing newline */
522 if (crStrncmp(line, procName, procNameLen) == 0 &&
523 (line[procNameLen] == ' ' || line[procNameLen] == '\t'))
524 {
525 crWarning("Using Chromium configuration for %s from %s",
526 procName, configPath);
527 args = crStrSplit(line + procNameLen + 1, " ");
528 return args;
529 }
530 }
531 fclose(f);
532 return NULL;
533}
534
535
536static int Mothership_Awake = 0;
537
538
539/**
540 * Signal handler to determine when mothership is ready.
541 */
542static void
543MothershipPhoneHome(int signo)
544{
545 crDebug("Got signal %d: mothership is awake!", signo);
546 Mothership_Awake = 1;
547}
548
549void stubSetDefaultConfigurationOptions(void)
550{
551 unsigned char key[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
552
553 stub.appDrawCursor = 0;
554 stub.minChromiumWindowWidth = 0;
555 stub.minChromiumWindowHeight = 0;
556 stub.maxChromiumWindowWidth = 0;
557 stub.maxChromiumWindowHeight = 0;
558 stub.matchChromiumWindowID = NULL;
559 stub.numIgnoreWindowID = 0;
560 stub.matchWindowTitle = NULL;
561 stub.ignoreFreeglutMenus = 0;
562 stub.trackWindowSize = 1;
563 stub.trackWindowPos = 1;
564 stub.trackWindowVisibility = 1;
565 stub.trackWindowVisibleRgn = 1;
566 stub.matchChromiumWindowCount = 0;
567 stub.spu_dir = NULL;
568 crNetSetRank(0);
569 crNetSetContextRange(32, 35);
570 crNetSetNodeRange("iam0", "iamvis20");
571 crNetSetKey(key,sizeof(key));
572 stub.force_pbuffers = 0;
573 stub.viewportHack = 0;
574
575#ifdef WINDOWS
576 {
577 char name[1000];
578 int i;
579
580 /* Apply viewport hack only if we're running under wine */
581 if (NULL!=GetModuleHandle("wined3d.dll"))
582 {
583 crGetProcName(name, 1000);
584 for (i=0; gsViewportHackApps[i]; ++i)
585 {
586 if (!stricmp(name, gsViewportHackApps[i]))
587 {
588 stub.viewportHack = 1;
589 break;
590 }
591 }
592 }
593 }
594#endif
595}
596
597/**
598 * Do one-time initializations for the faker.
599 * Returns TRUE on success, FALSE otherwise.
600 */
601bool
602stubInit(void)
603{
604 /* Here is where we contact the mothership to find out what we're supposed
605 * to be doing. Networking code in a DLL initializer. I sure hope this
606 * works :)
607 *
608 * HOW can I pass the mothership address to this if I already know it?
609 */
610
611 CRConnection *conn = NULL;
612 char response[1024];
613 char **spuchain;
614 int num_spus;
615 int *spu_ids;
616 char **spu_names;
617 const char *app_id;
618 int i;
619
620 if (stub_initialized)
621 return true;
622
623 stubInitVars();
624
625 /* @todo check if it'd be of any use on other than guests, no use for windows */
626 app_id = crGetenv( "CR_APPLICATION_ID_NUMBER" );
627
628 crNetInit( NULL, NULL );
629
630#ifndef WINDOWS
631 {
632 CRNetServer ns;
633
634 ns.name = "vboxhgcm://host:0";
635 ns.buffer_size = 1024;
636 crNetServerConnect(&ns);
637 if (!ns.conn)
638 {
639 crWarning("Failed to connect to host. Make sure 3D acceleration is enabled for this VM.");
640 return false;
641 }
642 else
643 {
644 crNetFreeConnection(ns.conn);
645 }
646 }
647#endif
648
649 strcpy(response, "2 0 feedback 1 pack");
650 spuchain = crStrSplit( response, " " );
651 num_spus = crStrToInt( spuchain[0] );
652 spu_ids = (int *) crAlloc( num_spus * sizeof( *spu_ids ) );
653 spu_names = (char **) crAlloc( num_spus * sizeof( *spu_names ) );
654 for (i = 0 ; i < num_spus ; i++)
655 {
656 spu_ids[i] = crStrToInt( spuchain[2*i+1] );
657 spu_names[i] = crStrdup( spuchain[2*i+2] );
658 crDebug( "SPU %d/%d: (%d) \"%s\"", i+1, num_spus, spu_ids[i], spu_names[i] );
659 }
660
661 stubSetDefaultConfigurationOptions();
662
663 stub.spu = crSPULoadChain( num_spus, spu_ids, spu_names, stub.spu_dir, NULL );
664
665 crFree( spuchain );
666 crFree( spu_ids );
667 for (i = 0; i < num_spus; ++i)
668 crFree(spu_names[i]);
669 crFree( spu_names );
670
671 // spu chain load failed somewhere
672 if (!stub.spu) {
673 return false;
674 }
675
676 crSPUInitDispatchTable( &glim );
677
678 /* This is unlikely to change -- We still want to initialize our dispatch
679 * table with the functions of the first SPU in the chain. */
680 stubInitSPUDispatch( stub.spu );
681
682 /* we need to plug one special stub function into the dispatch table */
683 glim.GetChromiumParametervCR = stub_GetChromiumParametervCR;
684
685#if !defined(VBOX_NO_NATIVEGL)
686 /* Load pointers to native OpenGL functions into stub.nativeDispatch */
687 stubInitNativeDispatch();
688#endif
689
690/*crDebug("stub init");
691raise(SIGINT);*/
692
693#ifdef WINDOWS
694 stubInstallWindowMessageHook();
695#endif
696
697#ifdef GLX
698 stub.xshmSI.shmid = -1;
699 stub.bShmInitFailed = GL_FALSE;
700 stub.pGLXPixmapsHash = crAllocHashtable();
701#endif
702
703 stub_initialized = 1;
704 return true;
705}
706
707
708
709/* Sigh -- we can't do initialization at load time, since Windows forbids
710 * the loading of other libraries from DLLMain. */
711
712#ifdef LINUX
713/* GCC crap
714 *void (*stub_init_ptr)(void) __attribute__((section(".ctors"))) = __stubInit; */
715#endif
716
717#ifdef WINDOWS
718#define WIN32_LEAN_AND_MEAN
719#include <windows.h>
720
721/* Windows crap */
722BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
723{
724 (void) lpvReserved;
725
726 switch (fdwReason)
727 {
728 case DLL_PROCESS_ATTACH:
729 {
730 CRNetServer ns;
731
732 crNetInit(NULL, NULL);
733 ns.name = "vboxhgcm://host:0";
734 ns.buffer_size = 1024;
735 crNetServerConnect(&ns);
736 if (!ns.conn)
737 {
738 crDebug("Failed to connect to host (is guest 3d acceleration enabled?), aborting ICD load.");
739 return FALSE;
740 }
741 else
742 crNetFreeConnection(ns.conn);
743
744 break;
745 }
746
747 case DLL_PROCESS_DETACH:
748 {
749 stubSPUSafeTearDown();
750 break;
751 }
752
753 case DLL_THREAD_ATTACH:
754 break;
755
756 case DLL_THREAD_DETACH:
757 break;
758
759 default:
760 break;
761 }
762
763 return TRUE;
764}
765#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette