VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c@ 56276

最後變更 在這個檔案從56276是 53234,由 vboxsync 提交於 10 年 前

Host 3D: Chromium server: drop non-functional code.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id
檔案大小: 63.8 KB
 
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#if 00 /*TEMPORARY*/
7#include <unistd.h>
8#include "cr_rand.h"
9#endif
10
11#include <GL/glx.h>
12#include <X11/Xlib.h>
13#include <X11/Xutil.h>
14#include <X11/Xmu/StdCmap.h>
15#include <X11/Xatom.h>
16#include <X11/extensions/shape.h>
17#include <sys/time.h>
18#include <stdio.h>
19
20#include "cr_environment.h"
21#include "cr_error.h"
22#include "cr_string.h"
23#include "cr_mem.h"
24#include "cr_process.h"
25#include "renderspu.h"
26
27
28/*
29 * Stuff from MwmUtils.h
30 */
31typedef struct
32{
33 unsigned long flags;
34 unsigned long functions;
35 unsigned long decorations;
36 long inputMode;
37 unsigned long status;
38} PropMotifWmHints;
39
40#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
41#define MWM_HINTS_DECORATIONS (1L << 1)
42
43
44#define WINDOW_NAME window->title
45
46static Bool WindowExistsFlag;
47
48static int
49WindowExistsErrorHandler( Display *dpy, XErrorEvent *xerr )
50{
51 if (xerr->error_code == BadWindow)
52 {
53 WindowExistsFlag = GL_FALSE;
54 }
55 return 0;
56}
57
58static GLboolean
59WindowExists( Display *dpy, Window w )
60{
61 XWindowAttributes xwa;
62 int (*oldXErrorHandler)(Display *, XErrorEvent *);
63
64 WindowExistsFlag = GL_TRUE;
65 oldXErrorHandler = XSetErrorHandler(WindowExistsErrorHandler);
66 XGetWindowAttributes(dpy, w, &xwa); /* dummy request */
67 XSetErrorHandler(oldXErrorHandler);
68 return WindowExistsFlag;
69}
70
71static Colormap
72GetLUTColormap( Display *dpy, XVisualInfo *vi )
73{
74 int a;
75 XColor col;
76 Colormap cmap;
77
78#if defined(__cplusplus) || defined(c_plusplus)
79 int localclass = vi->c_class; /* C++ */
80#else
81 int localclass = vi->class; /* C */
82#endif
83
84 if ( localclass != DirectColor )
85 {
86 crError( "No support for non-DirectColor visuals with LUTs" );
87 }
88
89 cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen),
90 vi->visual, AllocAll );
91
92 for (a=0; a<256; a++)
93 {
94 col.red = render_spu.lut8[0][a]<<8;
95 col.green = col.blue = 0;
96 col.pixel = a<<16;
97 col.flags = DoRed;
98 XStoreColor(dpy, cmap, &col);
99 }
100
101 for (a=0; a<256; a++)
102 {
103 col.green = render_spu.lut8[1][a]<<8;
104 col.red = col.blue = 0;
105 col.pixel = a<<8;
106 col.flags = DoGreen;
107 XStoreColor(dpy, cmap, &col);
108 }
109
110 for (a=0; a<256; a++)
111 {
112 col.blue = render_spu.lut8[2][a]<<8;
113 col.red = col.green= 0;
114 col.pixel = a;
115 col.flags = DoBlue;
116 XStoreColor(dpy, cmap, &col);
117 }
118
119 return cmap;
120}
121
122static Colormap
123GetShareableColormap( Display *dpy, XVisualInfo *vi )
124{
125 Status status;
126 XStandardColormap *standardCmaps;
127 Colormap cmap;
128 int i, numCmaps;
129
130#if defined(__cplusplus) || defined(c_plusplus)
131 int localclass = vi->c_class; /* C++ */
132#else
133 int localclass = vi->class; /* C */
134#endif
135
136 if ( localclass != TrueColor )
137 {
138 crError( "No support for non-TrueColor visuals." );
139 }
140
141 status = XmuLookupStandardColormap( dpy, vi->screen, vi->visualid,
142 vi->depth, XA_RGB_DEFAULT_MAP,
143 False, True );
144
145 if ( status == 1 )
146 {
147 status = XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen),
148 &standardCmaps, &numCmaps,
149 XA_RGB_DEFAULT_MAP );
150 if ( status == 1 )
151 {
152 for (i = 0 ; i < numCmaps ; i++)
153 {
154 if (standardCmaps[i].visualid == vi->visualid)
155 {
156 cmap = standardCmaps[i].colormap;
157 XFree( standardCmaps);
158 return cmap;
159 }
160 }
161 }
162 }
163
164 cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen),
165 vi->visual, AllocNone );
166 return cmap;
167}
168
169
170static int
171WaitForMapNotify( Display *display, XEvent *event, char *arg )
172{
173 (void)display;
174 return ( event->type == MapNotify && event->xmap.window == (Window)arg );
175}
176
177
178/**
179 * Return the X Visual ID of the given window
180 */
181static int
182GetWindowVisualID( Display *dpy, Window w )
183{
184 XWindowAttributes attr;
185 int k = XGetWindowAttributes(dpy, w, &attr);
186 if (!k)
187 return -1;
188 return attr.visual->visualid;
189}
190
191
192/**
193 * Wrapper for glXGetConfig().
194 */
195static int
196Attrib( const VisualInfo *visual, int attrib )
197{
198 int value = 0;
199 render_spu.ws.glXGetConfig( visual->dpy, visual->visual, attrib, &value );
200 return value;
201}
202
203
204
205/**
206 * Find a visual with the specified attributes. If we fail, turn off an
207 * attribute (like multisample or stereo) and try again.
208 */
209static XVisualInfo *
210chooseVisualRetry( Display *dpy, int screen, GLbitfield visAttribs )
211{
212 while (1) {
213 XVisualInfo *vis = crChooseVisual(&render_spu.ws, dpy, screen,
214 (GLboolean) render_spu.use_lut8,
215 visAttribs);
216 if (vis)
217 return vis;
218
219 if (visAttribs & CR_MULTISAMPLE_BIT)
220 visAttribs &= ~CR_MULTISAMPLE_BIT;
221 else if (visAttribs & CR_OVERLAY_BIT)
222 visAttribs &= ~CR_OVERLAY_BIT;
223 else if (visAttribs & CR_STEREO_BIT)
224 visAttribs &= ~CR_STEREO_BIT;
225 else if (visAttribs & CR_ACCUM_BIT)
226 visAttribs &= ~CR_ACCUM_BIT;
227 else if (visAttribs & CR_ALPHA_BIT)
228 visAttribs &= ~CR_ALPHA_BIT;
229 else
230 return NULL;
231 }
232}
233
234
235/**
236 * Get an FBconfig for the specified attributes
237 */
238#ifdef GLX_VERSION_1_3
239static GLXFBConfig
240chooseFBConfig( Display *dpy, int screen, GLbitfield visAttribs )
241{
242 GLXFBConfig *fbconfig;
243 int attribs[1000], attrCount = 0, numConfigs;
244 int major, minor;
245
246 CRASSERT(visAttribs & CR_PBUFFER_BIT);
247
248 /* Make sure pbuffers are supported */
249 render_spu.ws.glXQueryVersion(dpy, &major, &minor);
250 if (major * 100 + minor < 103) {
251 crWarning("Render SPU: GLX %d.%d doesn't support pbuffers", major, minor);
252 return 0;
253 }
254
255 attribs[attrCount++] = GLX_DRAWABLE_TYPE;
256 attribs[attrCount++] = GLX_PBUFFER_BIT;
257
258 if (visAttribs & CR_RGB_BIT) {
259 attribs[attrCount++] = GLX_RENDER_TYPE;
260 attribs[attrCount++] = GLX_RGBA_BIT;
261 attribs[attrCount++] = GLX_RED_SIZE;
262 attribs[attrCount++] = 1;
263 attribs[attrCount++] = GLX_GREEN_SIZE;
264 attribs[attrCount++] = 1;
265 attribs[attrCount++] = GLX_BLUE_SIZE;
266 attribs[attrCount++] = 1;
267 if (visAttribs & CR_ALPHA_BIT) {
268 attribs[attrCount++] = GLX_ALPHA_SIZE;
269 attribs[attrCount++] = 1;
270 }
271 }
272
273 if (visAttribs & CR_DEPTH_BIT) {
274 attribs[attrCount++] = GLX_DEPTH_SIZE;
275 attribs[attrCount++] = 1;
276 }
277
278 if (visAttribs & CR_DOUBLE_BIT) {
279 attribs[attrCount++] = GLX_DOUBLEBUFFER;
280 attribs[attrCount++] = True;
281 }
282 else {
283 /* don't care */
284 }
285
286 if (visAttribs & CR_STENCIL_BIT) {
287 attribs[attrCount++] = GLX_STENCIL_SIZE;
288 attribs[attrCount++] = 1;
289 }
290
291 if (visAttribs & CR_ACCUM_BIT) {
292 attribs[attrCount++] = GLX_ACCUM_RED_SIZE;
293 attribs[attrCount++] = 1;
294 attribs[attrCount++] = GLX_ACCUM_GREEN_SIZE;
295 attribs[attrCount++] = 1;
296 attribs[attrCount++] = GLX_ACCUM_BLUE_SIZE;
297 attribs[attrCount++] = 1;
298 if (visAttribs & CR_ALPHA_BIT) {
299 attribs[attrCount++] = GLX_ACCUM_ALPHA_SIZE;
300 attribs[attrCount++] = 1;
301 }
302 }
303
304 if (visAttribs & CR_MULTISAMPLE_BIT) {
305 attribs[attrCount++] = GLX_SAMPLE_BUFFERS_SGIS;
306 attribs[attrCount++] = 1;
307 attribs[attrCount++] = GLX_SAMPLES_SGIS;
308 attribs[attrCount++] = 4;
309 }
310
311 if (visAttribs & CR_STEREO_BIT) {
312 attribs[attrCount++] = GLX_STEREO;
313 attribs[attrCount++] = 1;
314 }
315
316 /* terminate */
317 attribs[attrCount++] = 0;
318
319 fbconfig = render_spu.ws.glXChooseFBConfig(dpy, screen, attribs, &numConfigs);
320 if (!fbconfig || numConfigs == 0) {
321 /* no matches! */
322 return 0;
323 }
324 if (numConfigs == 1) {
325 /* one match */
326 return fbconfig[0];
327 }
328 else {
329 /* found several matches - try to find best one */
330 int i;
331
332 crDebug("Render SPU: glXChooseFBConfig found %d matches for visBits 0x%x",
333 numConfigs, visAttribs);
334 /* Skip/omit configs that have unneeded Z buffer or unneeded double
335 * buffering. Possible add other tests in the future.
336 */
337 for (i = 0; i < numConfigs; i++) {
338 int zBits, db;
339 render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i],
340 GLX_DEPTH_SIZE, &zBits);
341 if ((visAttribs & CR_DEPTH_BIT) == 0 && zBits > 0) {
342 /* omit fbconfig with unneeded Z */
343 continue;
344 }
345 render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i],
346 GLX_DOUBLEBUFFER, &db);
347 if ((visAttribs & CR_DOUBLE_BIT) == 0 && db) {
348 /* omit fbconfig with unneeded DB */
349 continue;
350 }
351
352 /* if we get here, use this config */
353 return fbconfig[i];
354 }
355
356 /* if we get here, we didn't find a better fbconfig */
357 return fbconfig[0];
358 }
359}
360#endif /* GLX_VERSION_1_3 */
361
362static const char * renderspuGetDisplayName()
363{
364 const char *dpyName;
365
366 if (render_spu.display_string[0])
367 dpyName = render_spu.display_string;
368 else
369 {
370 crWarning("Render SPU: no display..");
371 dpyName = NULL;
372 }
373 return dpyName;
374}
375
376static int renderspuWinCmdWinCreate(WindowInfo *pWindow)
377{
378 return VERR_NOT_IMPLEMENTED;
379}
380
381static int renderspuWinCmdWinDestroy(WindowInfo *pWindow)
382{
383 return VERR_NOT_IMPLEMENTED;
384}
385
386static int renderspuWinCmdInit()
387{
388 const char * dpyName;
389 int rc = VERR_GENERAL_FAILURE;
390
391 if (!crHashtableAllocRegisterKey(render_spu.windowTable, CR_RENDER_WINCMD_ID))
392 {
393 crError("CR_RENDER_WINCMD_ID %d is occupied already", CR_RENDER_WINCMD_ID);
394 return VERR_INVALID_STATE;
395 }
396
397 render_spu.pWinToInfoTable = crAllocHashtable();
398 if (render_spu.pWinToInfoTable)
399 {
400 dpyName = renderspuGetDisplayName();
401 if (dpyName)
402 {
403 GLboolean bRc = renderspuInitVisual(&render_spu.WinCmdVisual, dpyName, render_spu.default_visual);
404 if (bRc)
405 {
406 bRc = renderspuWinInitWithVisual(&render_spu.WinCmdWindow, &render_spu.WinCmdVisual, GL_FALSE, CR_RENDER_WINCMD_ID);
407 if (bRc)
408 {
409 XSelectInput(render_spu.WinCmdVisual.dpy, render_spu.WinCmdWindow.window, StructureNotifyMask);
410 render_spu.WinCmdAtom = XInternAtom(render_spu.WinCmdVisual.dpy, "VBoxWinCmd", False);
411 CRASSERT(render_spu.WinCmdAtom != None);
412 return VINF_SUCCESS;
413 }
414 else
415 {
416 crError("renderspuWinInitWithVisual failed");
417 }
418 /* there is no visual destroy impl currently
419 * @todo: implement */
420 }
421 else
422 {
423 crError("renderspuInitVisual failed");
424 }
425 }
426 else
427 {
428 crError("Render SPU: no display, aborting");
429 }
430 crFreeHashtable(render_spu.pWinToInfoTable, NULL);
431 render_spu.pWinToInfoTable = NULL;
432 }
433 else
434 {
435 crError("crAllocHashtable failed");
436 }
437 return rc;
438}
439
440static void renderspuWinCmdTerm()
441{
442 /* the window is not in the table, this will just ensure the key is freed */
443 crHashtableDelete(render_spu.windowTable, CR_RENDER_WINCMD_ID, NULL);
444 renderspuWinCleanup(&render_spu.WinCmdWindow);
445 crFreeHashtable(render_spu.pWinToInfoTable, NULL);
446 /* we do not have visual destroy functionality
447 * @todo implement */
448}
449
450
451static bool renderspuWinCmdProcess(CR_RENDER_WINCMD* pWinCmd)
452{
453 bool fExit = false;
454 /* process commands */
455 switch (pWinCmd->enmCmd)
456 {
457 case CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE:
458 crHashtableAdd(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, pWinCmd->pWindow);
459 XSelectInput(render_spu.WinCmdVisual.dpy, pWinCmd->pWindow->window, ExposureMask);
460 pWinCmd->rc = VINF_SUCCESS;
461 break;
462 case CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY:
463 crHashtableDelete(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, NULL);
464 pWinCmd->rc = VINF_SUCCESS;
465 break;
466 case CR_RENDER_WINCMD_TYPE_NOP:
467 pWinCmd->rc = VINF_SUCCESS;
468 break;
469 case CR_RENDER_WINCMD_TYPE_EXIT:
470 renderspuWinCmdTerm();
471 pWinCmd->rc = VINF_SUCCESS;
472 fExit = true;
473 pWinCmd->rc = VINF_SUCCESS;
474 break;
475 case CR_RENDER_WINCMD_TYPE_WIN_CREATE:
476 pWinCmd->rc = renderspuWinCmdWinCreate(pWinCmd->pWindow);
477 break;
478 case CR_RENDER_WINCMD_TYPE_WIN_DESTROY:
479 pWinCmd->rc = renderspuWinCmdWinDestroy(pWinCmd->pWindow);
480 break;
481 default:
482 crError("unknown WinCmd command! %d", pWinCmd->enmCmd);
483 pWinCmd->rc = VERR_INVALID_PARAMETER;
484 break;
485 }
486
487 RTSemEventSignal(render_spu.hWinCmdCompleteEvent);
488 return fExit;
489}
490
491static DECLCALLBACK(int) renderspuWinCmdThreadProc(RTTHREAD ThreadSelf, void *pvUser)
492{
493 int rc;
494 bool fExit = false;
495 crDebug("RenderSPU: Window thread started (%x)", crThreadID());
496
497 rc = renderspuWinCmdInit();
498
499 /* notify the main cmd thread that we have started */
500 RTSemEventSignal(render_spu.hWinCmdCompleteEvent);
501
502 if (!RT_SUCCESS(rc))
503 {
504 CRASSERT(!render_spu.pWinToInfoTable);
505 return rc;
506 }
507
508 do
509 {
510 XEvent event;
511 XNextEvent(render_spu.WinCmdVisual.dpy, &event);
512
513 switch (event.type)
514 {
515 case ClientMessage:
516 {
517 CRASSERT(event.xclient.window == render_spu.WinCmdWindow.window);
518 if (event.xclient.window == render_spu.WinCmdWindow.window)
519 {
520 if (render_spu.WinCmdAtom == event.xclient.message_type)
521 {
522 CR_RENDER_WINCMD *pWinCmd;
523 memcpy(&pWinCmd, event.xclient.data.b, sizeof (pWinCmd));
524 fExit = renderspuWinCmdProcess(pWinCmd);
525 }
526 }
527
528 break;
529 }
530 case Expose:
531 {
532 if (!event.xexpose.count)
533 {
534 WindowInfo *pWindow = (WindowInfo*)crHashtableSearch(render_spu.pWinToInfoTable, event.xexpose.window);
535 if (pWindow)
536 {
537 const struct VBOXVR_SCR_COMPOSITOR * pCompositor;
538
539 pCompositor = renderspuVBoxCompositorAcquire(pWindow);
540 if (pCompositor)
541 {
542 renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 0, false);
543 renderspuVBoxCompositorRelease(pWindow);
544 }
545 }
546 }
547 break;
548 }
549 default:
550 break;
551 }
552 } while (!fExit);
553
554 return 0;
555}
556
557static int renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE enmCmd, WindowInfo *pWindow)
558{
559 Status status;
560 XEvent event;
561 CR_RENDER_WINCMD WinCmd, *pWinCmd;
562 int rc;
563
564 pWinCmd = &WinCmd;
565 pWinCmd->enmCmd = enmCmd;
566 pWinCmd->rc = VERR_GENERAL_FAILURE;
567 pWinCmd->pWindow = pWindow;
568
569 memset(&event, 0, sizeof (event));
570 event.type = ClientMessage;
571 event.xclient.window = render_spu.WinCmdWindow.window;
572 event.xclient.message_type = render_spu.WinCmdAtom;
573 event.xclient.format = 8;
574 memcpy(event.xclient.data.b, &pWinCmd, sizeof (pWinCmd));
575
576 status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, StructureNotifyMask, &event);
577 if (!status)
578 {
579 Assert(0);
580 crWarning("XSendEvent returned null");
581 return VERR_GENERAL_FAILURE;
582 }
583
584 XFlush(render_spu.pCommunicationDisplay);
585 rc = RTSemEventWaitNoResume(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT);
586 if (!RT_SUCCESS(rc))
587 {
588 crWarning("RTSemEventWaitNoResume failed rc %d", rc);
589 return rc;
590 }
591 return pWinCmd->rc;
592}
593
594int renderspu_SystemInit()
595{
596 const char * dpyName;
597 int rc = VERR_GENERAL_FAILURE;
598
599 if (!render_spu.use_glxchoosevisual) {
600 /* sometimes want to set this option with ATI drivers */
601 render_spu.ws.glXChooseVisual = NULL;
602 }
603
604 /* setup communication display connection */
605 dpyName = renderspuGetDisplayName();
606 if (!dpyName)
607 {
608 crWarning("no display name, aborting");
609 return VERR_GENERAL_FAILURE;
610 }
611
612 render_spu.pCommunicationDisplay = XOpenDisplay(dpyName);
613 if (!render_spu.pCommunicationDisplay)
614 {
615 crWarning( "Couldn't open X display named '%s'", dpyName );
616 return VERR_GENERAL_FAILURE;
617 }
618
619 if ( !render_spu.ws.glXQueryExtension( render_spu.pCommunicationDisplay, NULL, NULL ) )
620 {
621 crWarning( "Render SPU: Display %s doesn't support GLX", dpyName );
622 return VERR_GENERAL_FAILURE;
623 }
624
625 rc = RTSemEventCreate(&render_spu.hWinCmdCompleteEvent);
626 if (RT_SUCCESS(rc))
627 {
628 rc = RTThreadCreate(&render_spu.hWinCmdThread, renderspuWinCmdThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxCrWinCmd");
629 if (RT_SUCCESS(rc))
630 {
631 rc = RTSemEventWait(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT);
632 if (RT_SUCCESS(rc))
633 {
634 return VINF_SUCCESS;
635 }
636 else
637 {
638 crWarning("RTSemEventWait failed rc %d", rc);
639 }
640
641 RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL);
642 }
643 else
644 {
645 crWarning("RTThreadCreate failed rc %d", rc);
646 }
647 RTSemEventDestroy(render_spu.hWinCmdCompleteEvent);
648 }
649 else
650 {
651 crWarning("RTSemEventCreate failed rc %d", rc);
652 }
653
654 return rc;
655}
656
657int renderspu_SystemTerm()
658{
659 int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_EXIT, NULL);
660 if (!RT_SUCCESS(rc))
661 {
662 crWarning("renderspuWinCmdSubmit EXIT failed rc %d", rc);
663 return rc;
664 }
665
666 RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL);
667 RTSemEventDestroy(render_spu.hWinCmdCompleteEvent);
668 return VINF_SUCCESS;
669}
670
671GLboolean
672renderspu_SystemInitVisual( VisualInfo *visual )
673{
674 const char *dpyName;
675 int screen;
676
677 CRASSERT(visual);
678
679#ifdef USE_OSMESA
680 if (render_spu.use_osmesa) {
681 /* A dummy visual - being non null is enough. */
682 visual->visual =(XVisualInfo *) "os";
683 return GL_TRUE;
684 }
685#endif
686
687 dpyName = renderspuGetDisplayName();
688 if (!dpyName)
689 {
690 crWarning("Render SPU: no display, aborting");
691 return GL_FALSE;
692 }
693
694
695 crInfo("Render SPU: Opening display %s", dpyName);
696
697 if (dpyName &&
698 (crStrncmp(dpyName, "localhost:11", 12) == 0 ||
699 crStrncmp(dpyName, "localhost:12", 12) == 0 ||
700 crStrncmp(dpyName, "localhost:13", 12) == 0)) {
701 /* Issue both debug and warning messages to make sure the
702 * message gets noticed!
703 */
704 crDebug("Render SPU: display string looks like a proxy X server!");
705 crDebug("Render SPU: This is usually a problem!");
706 crWarning("Render SPU: display string looks like a proxy X server!");
707 crWarning("Render SPU: This is usually a problem!");
708 }
709
710 visual->dpy = XOpenDisplay(dpyName);
711 if (!visual->dpy)
712 {
713 crWarning( "Couldn't open X display named '%s'", dpyName );
714 return GL_FALSE;
715 }
716
717 if ( !render_spu.ws.glXQueryExtension( visual->dpy, NULL, NULL ) )
718 {
719 crWarning( "Render SPU: Display %s doesn't support GLX", visual->displayName );
720 return GL_FALSE;
721 }
722
723 screen = DefaultScreen(visual->dpy);
724
725#ifdef GLX_VERSION_1_3
726 if (visual->visAttribs & CR_PBUFFER_BIT)
727 {
728 visual->fbconfig = chooseFBConfig(visual->dpy, screen, visual->visAttribs);
729 if (!visual->fbconfig) {
730 char s[1000];
731 renderspuMakeVisString( visual->visAttribs, s );
732 crWarning( "Render SPU: Display %s doesn't have the necessary fbconfig: %s",
733 dpyName, s );
734 XCloseDisplay(visual->dpy);
735 return GL_FALSE;
736 }
737 }
738 else
739#endif /* GLX_VERSION_1_3 */
740 {
741 visual->visual = chooseVisualRetry(visual->dpy, screen, visual->visAttribs);
742 if (!visual->visual) {
743 char s[1000];
744 renderspuMakeVisString( visual->visAttribs, s );
745 crWarning("Render SPU: Display %s doesn't have the necessary visual: %s",
746 dpyName, s );
747 XCloseDisplay(visual->dpy);
748 return GL_FALSE;
749 }
750 }
751
752 if ( render_spu.sync )
753 {
754 crDebug( "Render SPU: Turning on XSynchronize" );
755 XSynchronize( visual->dpy, True );
756 }
757
758 if (visual->visual) {
759 crDebug( "Render SPU: Choose visual id=0x%x: RGBA=(%d,%d,%d,%d) Z=%d"
760 " stencil=%d double=%d stereo=%d accum=(%d,%d,%d,%d)",
761 (int) visual->visual->visualid,
762 Attrib( visual, GLX_RED_SIZE ),
763 Attrib( visual, GLX_GREEN_SIZE ),
764 Attrib( visual, GLX_BLUE_SIZE ),
765 Attrib( visual, GLX_ALPHA_SIZE ),
766 Attrib( visual, GLX_DEPTH_SIZE ),
767 Attrib( visual, GLX_STENCIL_SIZE ),
768 Attrib( visual, GLX_DOUBLEBUFFER ),
769 Attrib( visual, GLX_STEREO ),
770 Attrib( visual, GLX_ACCUM_RED_SIZE ),
771 Attrib( visual, GLX_ACCUM_GREEN_SIZE ),
772 Attrib( visual, GLX_ACCUM_BLUE_SIZE ),
773 Attrib( visual, GLX_ACCUM_ALPHA_SIZE )
774 );
775 }
776 else if (visual->fbconfig) {
777 int id;
778 render_spu.ws.glXGetFBConfigAttrib(visual->dpy, visual->fbconfig,
779 GLX_FBCONFIG_ID, &id);
780 crDebug("Render SPU: Chose FBConfig 0x%x, visBits=0x%x",
781 id, visual->visAttribs);
782 }
783
784 return GL_TRUE;
785}
786
787
788/*
789 * Add a GLX window to a swap group for inter-machine SwapBuffer
790 * synchronization.
791 * Only supported on NVIDIA Quadro 3000G hardware.
792 */
793static void
794JoinSwapGroup(Display *dpy, int screen, Window window,
795 GLuint group, GLuint barrier)
796{
797 GLuint maxGroups, maxBarriers;
798 const char *ext;
799 Bool b;
800
801 /*
802 * XXX maybe query glXGetClientString() instead???
803 */
804 ext = render_spu.ws.glXQueryExtensionsString(dpy, screen);
805
806 if (!crStrstr(ext, "GLX_NV_swap_group") ||
807 !render_spu.ws.glXQueryMaxSwapGroupsNV ||
808 !render_spu.ws.glXJoinSwapGroupNV ||
809 !render_spu.ws.glXBindSwapBarrierNV) {
810 crWarning("Render SPU: nv_swap_group is set but GLX_NV_swap_group is not supported on this system!");
811 return;
812 }
813
814 b = render_spu.ws.glXQueryMaxSwapGroupsNV(dpy, screen,
815 &maxGroups, &maxBarriers);
816 if (!b)
817 crWarning("Render SPU: call to glXQueryMaxSwapGroupsNV() failed!");
818
819 if (group >= maxGroups) {
820 crWarning("Render SPU: nv_swap_group too large (%d > %d)",
821 group, (int) maxGroups);
822 return;
823 }
824 crDebug("Render SPU: max swap groups = %d, max barriers = %d",
825 maxGroups, maxBarriers);
826
827 /* add this window to the swap group */
828 b = render_spu.ws.glXJoinSwapGroupNV(dpy, window, group);
829 if (!b) {
830 crWarning("Render SPU: call to glXJoinSwapGroupNV() failed!");
831 return;
832 }
833 else {
834 crDebug("Render SPU: call to glXJoinSwapGroupNV() worked!");
835 }
836
837 /* ... and bind window to barrier of same ID */
838 b = render_spu.ws.glXBindSwapBarrierNV(dpy, group, barrier);
839 if (!b) {
840 crWarning("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) failed!", group, barrier);
841 return;
842 }
843 else {
844 crDebug("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) worked!", group, barrier);
845 }
846
847 crDebug("Render SPU: window has joined swap group %d", group);
848}
849
850
851
852static GLboolean
853createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
854{
855 Display *dpy;
856 Colormap cmap;
857 XSetWindowAttributes swa;
858 XSizeHints hints = {0};
859 XEvent event;
860 XTextProperty text_prop;
861 XClassHint *class_hints = NULL;
862 Window parent;
863 char *name;
864 unsigned long flags;
865 unsigned int vncWin;
866
867 CRASSERT(visual);
868 window->visual = visual;
869 window->nativeWindow = 0;
870
871#ifdef USE_OSMESA
872 if (render_spu.use_osmesa)
873 return GL_TRUE;
874#endif
875
876 dpy = visual->dpy;
877
878 if ( render_spu.use_L2 )
879 {
880 crWarning( "Render SPU: Going fullscreen because we think we're using Lightning-2." );
881 render_spu.fullscreen = 1;
882 }
883
884 /*
885 * Query screen size if we're going full-screen
886 */
887 if ( render_spu.fullscreen )
888 {
889 XWindowAttributes xwa;
890 Window root_window;
891
892 /* disable the screensaver */
893 XSetScreenSaver( dpy, 0, 0, PreferBlanking, AllowExposures );
894 crDebug( "Render SPU: Just turned off the screensaver" );
895
896 /* Figure out how big the screen is, and make the window that size */
897 root_window = DefaultRootWindow( dpy );
898 XGetWindowAttributes( dpy, root_window, &xwa );
899
900 crDebug( "Render SPU: root window size: %d x %d", xwa.width, xwa.height );
901
902 window->x = 0;
903 window->y = 0;
904 window->BltInfo.width = xwa.width;
905 window->BltInfo.height = xwa.height;
906 }
907
908 /* i've changed default window size to be 0,0 but X doesn't like it */
909 /*CRASSERT(window->BltInfo.width >= 1);
910 CRASSERT(window->BltInfo.height >= 1);*/
911 if (window->BltInfo.width < 1) window->BltInfo.width = 1;
912 if (window->BltInfo.height < 1) window->BltInfo.height = 1;
913
914 /*
915 * Get a colormap.
916 */
917 if (render_spu.use_lut8)
918 cmap = GetLUTColormap( dpy, visual->visual );
919 else
920 cmap = GetShareableColormap( dpy, visual->visual );
921 if ( !cmap ) {
922 crError( "Render SPU: Unable to get a colormap!" );
923 return GL_FALSE;
924 }
925
926 /* destroy existing window if there is one */
927 if (window->window) {
928 XDestroyWindow(dpy, window->window);
929 }
930
931 /*
932 * Create the window
933 *
934 * POSSIBLE OPTIMIZATION:
935 * If we're using the render_to_app_window or render_to_crut_window option
936 * (or DMX) we may never actually use this X window. So we could perhaps
937 * delay its creation until glXMakeCurrent is called. With NVIDIA's OpenGL
938 * driver, creating a lot of GLX windows can eat up a lot of VRAM and lead
939 * to slow, software-fallback rendering. Apps that use render_to_app_window,
940 * etc should set the default window size very small to avoid this.
941 * See dmx.conf for example.
942 */
943 swa.colormap = cmap;
944 swa.border_pixel = 0;
945 swa.event_mask = ExposureMask | StructureNotifyMask;
946 swa.override_redirect = 1;
947
948 flags = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
949
950 /*
951 * We pass the VNC's desktop windowID via an environment variable.
952 * If we don't find one, we're not on a 3D-capable vncviewer, or
953 * if we do find one, then create the renderspu subwindow as a
954 * child of the vncviewer's desktop window.
955 *
956 * This is purely for the replicateSPU.
957 *
958 * NOTE: This is crufty, and will do for now. FIXME.
959 */
960 vncWin = crStrToInt( crGetenv("CRVNCWINDOW") );
961 if (vncWin)
962 parent = (Window) vncWin;
963 else
964 parent = RootWindow(dpy, visual->visual->screen);
965
966 if (render_spu_parent_window_id>0)
967 {
968 crDebug("Render SPU: VBox parent window_id is: %x", render_spu_parent_window_id);
969 window->window = XCreateWindow(dpy, render_spu_parent_window_id,
970 window->x, window->y,
971 window->BltInfo.width, window->BltInfo.height,
972 0, visual->visual->depth, InputOutput,
973 visual->visual->visual, flags, &swa);
974 }
975 else
976 {
977 /* This should happen only at the call from crVBoxServerInit. At this point we don't
978 * know render_spu_parent_window_id yet, nor we need it for default window which is hidden.
979 */
980
981 crDebug("Render SPU: Creating global window, parent: %x", RootWindow(dpy, visual->visual->screen));
982 window->window = XCreateWindow(dpy, RootWindow(dpy, visual->visual->screen),
983 window->x, window->y,
984 window->BltInfo.width, window->BltInfo.height,
985 0, visual->visual->depth, InputOutput,
986 visual->visual->visual, flags, &swa);
987 }
988
989 if (!window->window) {
990 crWarning( "Render SPU: unable to create window" );
991 return GL_FALSE;
992 }
993
994 crDebug( "Render SPU: Created window 0x%x on display %s, Xvisual 0x%x",
995 (int) window->window,
996 DisplayString(visual->dpy),
997 (int) visual->visual->visual->visualid /* yikes */
998 );
999
1000 if (render_spu.fullscreen || render_spu.borderless)
1001 {
1002 /* Disable border/decorations with an MWM property (observed by most
1003 * modern window managers.
1004 */
1005 PropMotifWmHints motif_hints;
1006 Atom prop, proptype;
1007
1008 /* setup the property */
1009 motif_hints.flags = MWM_HINTS_DECORATIONS;
1010 motif_hints.decorations = 0; /* Turn off all decorations */
1011
1012 /* get the atom for the property */
1013 prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True );
1014 if (prop) {
1015 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
1016 proptype = prop;
1017 XChangeProperty( dpy, window->window, /* display, window */
1018 prop, proptype, /* property, type */
1019 32, /* format: 32-bit datums */
1020 PropModeReplace, /* mode */
1021 (unsigned char *) &motif_hints, /* data */
1022 PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */
1023 );
1024 }
1025 }
1026
1027 /* Make a clear cursor to get rid of the monitor cursor */
1028 if ( render_spu.fullscreen )
1029 {
1030 Pixmap pixmap;
1031 Cursor cursor;
1032 XColor colour;
1033 char clearByte = 0;
1034
1035 /* AdB - Only bother to create a 1x1 cursor (byte) */
1036 pixmap = XCreatePixmapFromBitmapData(dpy, window->window, &clearByte,
1037 1, 1, 1, 0, 1);
1038 if(!pixmap){
1039 crWarning("Unable to create clear cursor pixmap");
1040 return GL_FALSE;
1041 }
1042
1043 cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &colour, &colour, 0, 0);
1044 if(!cursor){
1045 crWarning("Unable to create clear cursor from zero byte pixmap");
1046 return GL_FALSE;
1047 }
1048 XDefineCursor(dpy, window->window, cursor);
1049 XFreePixmap(dpy, pixmap);
1050 }
1051
1052 hints.x = window->x;
1053 hints.y = window->y;
1054 hints.width = window->BltInfo.width;
1055 hints.height = window->BltInfo.height;
1056 hints.min_width = hints.width;
1057 hints.min_height = hints.height;
1058 hints.max_width = hints.width;
1059 hints.max_height = hints.height;
1060 if (render_spu.resizable)
1061 hints.flags = USPosition | USSize;
1062 else
1063 hints.flags = USPosition | USSize | PMinSize | PMaxSize;
1064 XSetStandardProperties( dpy, window->window,
1065 WINDOW_NAME, WINDOW_NAME,
1066 None, NULL, 0, &hints );
1067
1068 /* New item! This is needed so that the sgimouse server can find
1069 * the crDebug window.
1070 */
1071 name = WINDOW_NAME;
1072 XStringListToTextProperty( &name, 1, &text_prop );
1073 XSetWMName( dpy, window->window, &text_prop );
1074
1075 /* Set window name, resource class */
1076 class_hints = XAllocClassHint( );
1077 class_hints->res_name = crStrdup( "foo" );
1078 class_hints->res_class = crStrdup( "Chromium" );
1079 XSetClassHint( dpy, window->window, class_hints );
1080 crFree( class_hints->res_name );
1081 crFree( class_hints->res_class );
1082 XFree( class_hints );
1083
1084 if (showIt) {
1085 XMapWindow( dpy, window->window );
1086 XIfEvent( dpy, &event, WaitForMapNotify,
1087 (char *) window->window );
1088 }
1089
1090 if ((window->visual->visAttribs & CR_DOUBLE_BIT) && render_spu.nvSwapGroup) {
1091 /* NOTE:
1092 * If this SPU creates N windows we don't want to gang the N windows
1093 * together!
1094 * By adding the window ID to the nvSwapGroup ID we can be sure each
1095 * app window is in a separate swap group while all the back-end windows
1096 * which form a mural are in the same swap group.
1097 */
1098 GLuint group = 0; /*render_spu.nvSwapGroup + window->BltInfo.Base.id;*/
1099 GLuint barrier = 0;
1100 JoinSwapGroup(dpy, visual->visual->screen, window->window, group, barrier);
1101 }
1102
1103 /*
1104 * End GLX code
1105 */
1106 crDebug( "Render SPU: actual window x, y, width, height: %d, %d, %d, %d",
1107 window->x, window->y, window->BltInfo.width, window->BltInfo.height );
1108
1109 XSync(dpy, 0);
1110
1111 if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID)
1112 {
1113 int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, window);
1114 AssertRC(rc);
1115 }
1116
1117 return GL_TRUE;
1118}
1119
1120
1121static GLboolean
1122createPBuffer( VisualInfo *visual, WindowInfo *window )
1123{
1124 window->visual = visual;
1125 window->x = 0;
1126 window->y = 0;
1127 window->nativeWindow = 0;
1128
1129 CRASSERT(window->BltInfo.width > 0);
1130 CRASSERT(window->BltInfo.height > 0);
1131
1132#ifdef GLX_VERSION_1_3
1133 {
1134 int attribs[100], i = 0, w, h;
1135 CRASSERT(visual->fbconfig);
1136 w = window->BltInfo.width;
1137 h = window->BltInfo.height;
1138 attribs[i++] = GLX_PRESERVED_CONTENTS;
1139 attribs[i++] = True;
1140 attribs[i++] = GLX_PBUFFER_WIDTH;
1141 attribs[i++] = w;
1142 attribs[i++] = GLX_PBUFFER_HEIGHT;
1143 attribs[i++] = h;
1144 attribs[i++] = 0; /* terminator */
1145 window->window = render_spu.ws.glXCreatePbuffer(visual->dpy,
1146 visual->fbconfig, attribs);
1147 if (window->window) {
1148 crDebug("Render SPU: Allocated %d x %d pbuffer", w, h);
1149 return GL_TRUE;
1150 }
1151 else {
1152 crWarning("Render SPU: Failed to allocate %d x %d pbuffer", w, h);
1153 return GL_FALSE;
1154 }
1155 }
1156#endif /* GLX_VERSION_1_3 */
1157 return GL_FALSE;
1158}
1159
1160
1161GLboolean
1162renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
1163{
1164 if (visual->visAttribs & CR_PBUFFER_BIT) {
1165 window->BltInfo.width = render_spu.defaultWidth;
1166 window->BltInfo.height = render_spu.defaultHeight;
1167 return createPBuffer(visual, window);
1168 }
1169 else {
1170 return createWindow(visual, showIt, window);
1171 }
1172}
1173
1174GLboolean
1175renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
1176{
1177 return renderspu_SystemCreateWindow(visual, showIt, window);
1178}
1179
1180void
1181renderspu_SystemDestroyWindow( WindowInfo *window )
1182{
1183 CRASSERT(window);
1184 CRASSERT(window->visual);
1185
1186#ifdef USE_OSMESA
1187 if (render_spu.use_osmesa)
1188 {
1189 crFree(window->buffer);
1190 window->buffer = NULL;
1191 }
1192 else
1193#endif
1194 {
1195 if (window->visual->visAttribs & CR_PBUFFER_BIT) {
1196#ifdef GLX_VERSION_1_3
1197 render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window);
1198#endif
1199 }
1200 else {
1201 /* The value window->nativeWindow will only be non-NULL if the
1202 * render_to_app_window option is set to true. In this case, we
1203 * don't want to do anything, since we're not responsible for this
1204 * window. I know...personal responsibility and all...
1205 */
1206 if (!window->nativeWindow) {
1207 if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID)
1208 {
1209 int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, window);
1210 AssertRC(rc);
1211 }
1212 XDestroyWindow(window->visual->dpy, window->window);
1213 XSync(window->visual->dpy, 0);
1214 }
1215 }
1216 }
1217 window->visual = NULL;
1218 window->window = 0;
1219}
1220
1221
1222GLboolean
1223renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext )
1224{
1225 Bool is_direct;
1226 GLXContext sharedSystemContext = NULL;
1227
1228 CRASSERT(visual);
1229 CRASSERT(context);
1230
1231 context->visual = visual;
1232
1233 if (sharedContext != NULL) {
1234 sharedSystemContext = sharedContext->context;
1235 }
1236
1237
1238
1239#ifdef USE_OSMESA
1240 if (render_spu.use_osmesa) {
1241 context->context = (GLXContext) render_spu.OSMesaCreateContext(OSMESA_RGB, 0);
1242 if (context->context)
1243 return GL_TRUE;
1244 else
1245 return GL_FALSE;
1246 }
1247#endif
1248
1249#ifdef GLX_VERSION_1_3
1250 if (visual->visAttribs & CR_PBUFFER_BIT) {
1251 context->context = render_spu.ws.glXCreateNewContext( visual->dpy,
1252 visual->fbconfig,
1253 GLX_RGBA_TYPE,
1254 sharedSystemContext,
1255 render_spu.try_direct);
1256 }
1257 else
1258#endif
1259 {
1260 context->context = render_spu.ws.glXCreateContext( visual->dpy,
1261 visual->visual,
1262 sharedSystemContext,
1263 render_spu.try_direct);
1264 }
1265 if (!context->context) {
1266 crError( "Render SPU: Couldn't create rendering context" );
1267 return GL_FALSE;
1268 }
1269
1270 is_direct = render_spu.ws.glXIsDirect( visual->dpy, context->context );
1271 if (visual->visual)
1272 crDebug("Render SPU: Created %s context (%d) on display %s for visAttribs 0x%x",
1273 is_direct ? "DIRECT" : "INDIRECT",
1274 context->BltInfo.Base.id,
1275 DisplayString(visual->dpy),
1276 visual->visAttribs);
1277
1278 if ( render_spu.force_direct && !is_direct )
1279 {
1280 crError( "Render SPU: Direct rendering not possible." );
1281 return GL_FALSE;
1282 }
1283
1284 return GL_TRUE;
1285}
1286
1287
1288#define USE_GLX_COPYCONTEXT 0
1289
1290#if !USE_GLX_COPYCONTEXT
1291
1292/**
1293 * Unfortunately, glXCopyContext() is broken sometimes (NVIDIA 76.76 driver).
1294 * This bit of code gets and sets GL state we need to copy between contexts.
1295 */
1296struct saved_state
1297{
1298 /* XXX depending on the app, more state may be needed here */
1299 GLboolean Lighting;
1300 GLboolean LightEnabled[8];
1301 GLfloat LightPos[8][4];
1302 GLfloat LightAmbient[8][4];
1303 GLfloat LightDiffuse[8][4];
1304 GLfloat LightSpecular[8][4];
1305 GLboolean DepthTest;
1306};
1307
1308static struct saved_state SavedState;
1309
1310static void
1311get_state(struct saved_state *s)
1312{
1313 int i;
1314
1315 s->Lighting = render_spu.self.IsEnabled(GL_LIGHTING);
1316 for (i = 0; i < 8; i++) {
1317 s->LightEnabled[i] = render_spu.self.IsEnabled(GL_LIGHT0 + i);
1318 render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]);
1319 render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]);
1320 render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]);
1321 render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]);
1322 }
1323
1324 s->DepthTest = render_spu.self.IsEnabled(GL_DEPTH_TEST);
1325}
1326
1327static void
1328set_state(const struct saved_state *s)
1329{
1330 int i;
1331
1332 if (s->Lighting) {
1333 render_spu.self.Enable(GL_LIGHTING);
1334 }
1335 else {
1336 render_spu.self.Disable(GL_LIGHTING);
1337 }
1338
1339 for (i = 0; i < 8; i++) {
1340 if (s->LightEnabled[i]) {
1341 render_spu.self.Enable(GL_LIGHT0 + i);
1342 }
1343 else {
1344 render_spu.self.Disable(GL_LIGHT0 + i);
1345 }
1346 render_spu.self.Lightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]);
1347 render_spu.self.Lightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]);
1348 render_spu.self.Lightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]);
1349 render_spu.self.Lightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]);
1350 }
1351
1352 if (s->DepthTest)
1353 render_spu.self.Enable(GL_DEPTH_TEST);
1354 else
1355 render_spu.self.Disable(GL_DEPTH_TEST);
1356}
1357
1358#endif /* !USE_GLX_COPYCONTEXT */
1359
1360/**
1361 * Recreate the GLX context for ContextInfo. The new context will use the
1362 * visual specified by newVisualID.
1363 */
1364static void
1365renderspu_RecreateContext( ContextInfo *context, int newVisualID )
1366{
1367 XVisualInfo templateVis, *vis;
1368 long templateFlags;
1369 int screen = 0, count;
1370 GLXContext oldContext = context->context;
1371
1372 templateFlags = VisualScreenMask | VisualIDMask;
1373 templateVis.screen = screen;
1374 templateVis.visualid = newVisualID;
1375 vis = XGetVisualInfo(context->visual->dpy, templateFlags, &templateVis, &count);
1376 CRASSERT(vis);
1377 if (!vis)
1378 return;
1379
1380 /* create new context */
1381 crDebug("Render SPU: Creating new GLX context with visual 0x%x", newVisualID);
1382 context->context = render_spu.ws.glXCreateContext(context->visual->dpy,
1383 vis, NULL,
1384 render_spu.try_direct);
1385 CRASSERT(context->context);
1386
1387#if USE_GLX_COPYCONTEXT
1388 /* copy old context state to new context */
1389 render_spu.ws.glXCopyContext(context->visual->dpy,
1390 oldContext, context->context, ~0);
1391 crDebug("Render SPU: Done copying context state");
1392#endif
1393
1394 /* destroy old context */
1395 render_spu.ws.glXDestroyContext(context->visual->dpy, oldContext);
1396
1397 context->visual->visual = vis;
1398}
1399
1400
1401void
1402renderspu_SystemDestroyContext( ContextInfo *context )
1403{
1404#ifdef USE_OSMESA
1405 if (render_spu.use_osmesa)
1406 {
1407 render_spu.OSMesaDestroyContext( (OSMesaContext) context->context );
1408 }
1409 else
1410#endif
1411 {
1412#if 0
1413 /* XXX disable for now - causes segfaults w/ NVIDIA's driver */
1414 render_spu.ws.glXDestroyContext( context->visual->dpy, context->context );
1415#endif
1416 }
1417 context->visual = NULL;
1418 context->context = 0;
1419}
1420
1421
1422#ifdef USE_OSMESA
1423static void
1424check_buffer_size( WindowInfo *window )
1425{
1426 if (window->BltInfo.width != window->in_buffer_width
1427 || window->BltInfo.height != window->in_buffer_height
1428 || ! window->buffer) {
1429 crFree(window->buffer);
1430
1431 window->buffer = crCalloc(window->BltInfo.width * window->BltInfo.height
1432 * 4 * sizeof (GLubyte));
1433
1434 window->in_buffer_width = window->BltInfo.width;
1435 window->in_buffer_height = window->BltInfo.height;
1436
1437 crDebug("Render SPU: dimensions changed to %d x %d", window->BltInfo.width, window->BltInfo.height);
1438 }
1439}
1440#endif
1441
1442
1443void
1444renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow,
1445 ContextInfo *context )
1446{
1447 Bool b;
1448
1449 CRASSERT(render_spu.ws.glXMakeCurrent);
1450
1451 /*crDebug("%s nativeWindow=0x%x", __FUNCTION__, (int) nativeWindow);*/
1452
1453#ifdef USE_OSMESA
1454 if (render_spu.use_osmesa) {
1455 check_buffer_size(window);
1456 render_spu.OSMesaMakeCurrent( (OSMesaContext) context->context,
1457 window->buffer, GL_UNSIGNED_BYTE,
1458 window->BltInfo.width, window->BltInfo.height);
1459 return;
1460 }
1461#endif
1462
1463 nativeWindow = 0;
1464
1465 if (window && context) {
1466 window->appWindow = nativeWindow;
1467
1468 if (window->visual != context->visual) {
1469 crDebug("Render SPU: MakeCurrent visual mismatch (win(%d) bits:0x%x != ctx(%d) bits:0x%x); remaking window.",
1470 window->BltInfo.Base.id, window->visual->visAttribs,
1471 context->BltInfo.Base.id, context->visual->visAttribs);
1472 /*
1473 * XXX have to revisit this issue!!!
1474 *
1475 * But for now we destroy the current window
1476 * and re-create it with the context's visual abilities
1477 */
1478#ifndef SOLARIS_9_X_BUG
1479 /*
1480 I'm having some really weird issues if I destroy this window
1481 when I'm using the version of sunX that comes with Solaris 9.
1482 Subsiquent glX calls return GLXBadCurrentWindow error.
1483
1484 This is an issue even when running Linux version and using
1485 the Solaris 9 sunX as a display.
1486 -- jw
1487
1488 jw: we might have to call glXMakeCurrent(dpy, 0, 0) to unbind
1489 the context from the window before destroying it. -Brian
1490 */
1491 render_spu.ws.glXMakeCurrent(window->visual->dpy, 0, 0);
1492 renderspu_SystemDestroyWindow( window );
1493#endif
1494 renderspu_SystemCreateWindow( context->visual, window->visible, window );
1495 /*
1496 crError("In renderspu_SystemMakeCurrent() window and context"
1497 " weren't created with same visual!");
1498 */
1499 }
1500
1501 CRASSERT(context->context);
1502
1503#if 0
1504 if (render_spu.render_to_crut_window) {
1505 if (render_spu.crut_drawable == 0) {
1506 /* We don't know the X drawable ID yet. Ask mothership for it. */
1507 char response[8096];
1508 CRConnection *conn = crMothershipConnect();
1509 if (!conn)
1510 {
1511 crError("Couldn't connect to the mothership to get CRUT drawable-- "
1512 "I have no idea what to do!");
1513 }
1514 crMothershipGetParam( conn, "crut_drawable", response );
1515 render_spu.crut_drawable = crStrToInt(response);
1516 crMothershipDisconnect(conn);
1517
1518 crDebug("Render SPU: using CRUT drawable: 0x%x",
1519 render_spu.crut_drawable);
1520 if (!render_spu.crut_drawable) {
1521 crDebug("Render SPU: Crut drawable 0 is invalid");
1522 /* Continue with nativeWindow = 0; we'll render to the window that
1523 * we (the Render SPU) previously created.
1524 */
1525 }
1526 }
1527
1528 nativeWindow = render_spu.crut_drawable;
1529 }
1530#endif
1531
1532 if ((render_spu.render_to_crut_window || render_spu.render_to_app_window)
1533 && nativeWindow)
1534 {
1535 /* We're about to bind the rendering context to a window that we
1536 * (the Render SPU) did not create. The window was created by the
1537 * application or the CRUT server.
1538 * Make sure the window ID is valid and that the window's X visual is
1539 * the same as the rendering context's.
1540 */
1541 if (WindowExists(window->visual->dpy, nativeWindow))
1542 {
1543 int vid = GetWindowVisualID(window->visual->dpy, nativeWindow);
1544 GLboolean recreated = GL_FALSE;
1545
1546 /* check that the window's visual and context's visual match */
1547 if (vid != (int) context->visual->visual->visualid) {
1548 crWarning("Render SPU: Can't bind context %d to CRUT/native window "
1549 "0x%x because of different X visuals (0x%x != 0x%x)!",
1550 context->BltInfo.Base.id, (int) nativeWindow,
1551 vid, (int) context->visual->visual->visualid);
1552 crWarning("Render SPU: Trying to recreate GLX context to match.");
1553 /* Try to recreate the GLX context so that it uses the same
1554 * GLX visual as the window.
1555 */
1556#if !USE_GLX_COPYCONTEXT
1557 if (context->everCurrent) {
1558 get_state(&SavedState);
1559 }
1560#endif
1561 renderspu_RecreateContext(context, vid);
1562 recreated = GL_TRUE;
1563 }
1564
1565 /* OK, this should work */
1566 window->nativeWindow = (Window) nativeWindow;
1567 b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
1568 window->nativeWindow,
1569 context->context );
1570 CRASSERT(b);
1571#if !USE_GLX_COPYCONTEXT
1572 if (recreated) {
1573 set_state(&SavedState);
1574 }
1575#endif
1576 }
1577 else
1578 {
1579 crWarning("Render SPU: render_to_app/crut_window option is set but "
1580 "the window ID 0x%x is invalid on the display named %s",
1581 (unsigned int) nativeWindow,
1582 DisplayString(window->visual->dpy));
1583 CRASSERT(window->window);
1584 b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
1585 window->window, context->context );
1586 CRASSERT(b);
1587 }
1588 }
1589 else
1590 {
1591 /* This is the normal case - rendering to the render SPU's own window */
1592 CRASSERT(window->window);
1593#if 0
1594 crDebug("calling glXMakecurrent(%p, 0x%x, 0x%x)",
1595 window->visual->dpy,
1596 (int) window->window, (int) context->context );
1597#endif
1598 b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
1599 window->window, context->context );
1600 if (!b) {
1601 crWarning("glXMakeCurrent(%p, 0x%x, %p) failed! (winId %d, ctxId %d)",
1602 window->visual->dpy,
1603 (int) window->window, (void *) context->context,
1604 window->BltInfo.Base.id, context->BltInfo.Base.id );
1605 }
1606 /*CRASSERT(b);*/
1607 }
1608
1609 /* XXX this is a total hack to work around an NVIDIA driver bug */
1610#if 0
1611 if (render_spu.self.GetFloatv && context->haveWindowPosARB) {
1612 GLfloat f[4];
1613 render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f);
1614 if (!window->everCurrent || f[1] < 0.0) {
1615 crDebug("Render SPU: Resetting raster pos");
1616 render_spu.self.WindowPos2iARB(0, 0);
1617 }
1618 }
1619#endif
1620 }
1621 else
1622 {
1623 GET_CONTEXT(pCurCtx);
1624 if (pCurCtx)
1625 {
1626 b = render_spu.ws.glXMakeCurrent( pCurCtx->currentWindow->visual->dpy, None, NULL);
1627 if (!b) {
1628 crWarning("glXMakeCurrent(%p, None, NULL) failed!", pCurCtx->currentWindow->visual->dpy);
1629 }
1630 }
1631
1632 }
1633}
1634
1635
1636/**
1637 * Set window (or pbuffer) size.
1638 */
1639void
1640renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h )
1641{
1642#ifdef USE_OSMESA
1643 if (render_spu.use_osmesa) {
1644 window->BltInfo.width = w;
1645 window->BltInfo.height = h;
1646 check_buffer_size(window);
1647 return;
1648 }
1649#endif
1650
1651 CRASSERT(window);
1652 CRASSERT(window->visual);
1653 if (window->visual->visAttribs & CR_PBUFFER_BIT)
1654 {
1655 /* resizing a pbuffer */
1656 if (render_spu.pbufferWidth != 0 || render_spu.pbufferHeight != 0) {
1657 /* size limit check */
1658 if (w > render_spu.pbufferWidth || h > render_spu.pbufferHeight) {
1659 crWarning("Render SPU: Request for %d x %d pbuffer is larger than "
1660 "the configured size of %d x %d. ('pbuffer_size')",
1661 w, h, render_spu.pbufferWidth, render_spu.pbufferHeight);
1662 return;
1663 }
1664 /*
1665 * If the requested new pbuffer size is greater than 1/2 the size of
1666 * the max pbuffer, just use the max pbuffer size. This helps avoid
1667 * problems with VRAM memory fragmentation. If we run out of VRAM
1668 * for pbuffers, some drivers revert to software rendering. We want
1669 * to avoid that!
1670 */
1671 if (w * h >= render_spu.pbufferWidth * render_spu.pbufferHeight / 2) {
1672 /* increase the dimensions to the max pbuffer size now */
1673 w = render_spu.pbufferWidth;
1674 h = render_spu.pbufferHeight;
1675 }
1676 }
1677
1678 if (window->BltInfo.width != w || window->BltInfo.height != h) {
1679 /* Only resize if the new dimensions really are different */
1680#ifdef CHROMIUM_THREADSAFE
1681 ContextInfo *currentContext = (ContextInfo *) crGetTSD(&_RenderTSD);
1682#else
1683 ContextInfo *currentContext = render_spu.currentContext;
1684#endif
1685 /* Can't resize pbuffers, so destroy it and make a new one */
1686 render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window);
1687 window->BltInfo.width = w;
1688 window->BltInfo.height = h;
1689 crDebug("Render SPU: Creating new %d x %d PBuffer (id=%d)",
1690 w, h, window->BltInfo.Base.id);
1691 if (!createPBuffer(window->visual, window)) {
1692 crWarning("Render SPU: Unable to create PBuffer (out of VRAM?)!");
1693 }
1694 else if (currentContext && currentContext->currentWindow == window) {
1695 /* Determine if we need to bind the current context to new pbuffer */
1696 render_spu.ws.glXMakeCurrent(window->visual->dpy,
1697 window->window,
1698 currentContext->context );
1699 }
1700 }
1701 }
1702 else {
1703 if (!w || !h)
1704 {
1705 /* X can not handle zero sizes */
1706 if (window->visible)
1707 {
1708 renderspu_SystemShowWindow( window, GL_FALSE );
1709 }
1710 return;
1711 }
1712 /* Resize ordinary X window */
1713 /*
1714 * This is ugly, but it seems to be the only thing that works.
1715 * Basically, XResizeWindow() doesn't seem to always take effect
1716 * immediately.
1717 * Even after an XSync(), the GetWindowAttributes() call will sometimes
1718 * return the old window size. So, we use a loop to repeat the window
1719 * resize until it seems to take effect.
1720 */
1721 int attempt;
1722 crDebug("Render SPU: XResizeWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, w, h);
1723 XResizeWindow(window->visual->dpy, window->window, w, h);
1724 XSync(window->visual->dpy, 0);
1725
1726 if (!window->BltInfo.width || !window->BltInfo.height)
1727 {
1728 /* we have hidden the window instead of sizing it to (0;0) since X is unable to handle zero sizes */
1729 if (window->visible)
1730 {
1731 renderspu_SystemShowWindow( window, GL_TRUE );
1732 return;
1733 }
1734 }
1735#if 0
1736 for (attempt = 0; attempt < 3; attempt++) { /* try three times max */
1737 XWindowAttributes attribs;
1738 /* Now, query the window size */
1739 XGetWindowAttributes(window->visual->dpy, window->window, &attribs);
1740 if (attribs.width == w && attribs.height == h)
1741 break;
1742 /* sleep for a millisecond and try again */
1743 crMsleep(1);
1744 }
1745#endif
1746 }
1747}
1748
1749
1750void
1751renderspu_SystemGetWindowGeometry( WindowInfo *window,
1752 GLint *x, GLint *y, GLint *w, GLint *h )
1753{
1754#ifdef USE_OSMESA
1755 if (render_spu.use_osmesa) {
1756 *w = window->BltInfo.width;
1757 *h = window->BltInfo.height;
1758 return;
1759 }
1760#endif
1761
1762 CRASSERT(window);
1763 CRASSERT(window->visual);
1764 CRASSERT(window->window);
1765 if (window->visual->visAttribs & CR_PBUFFER_BIT)
1766 {
1767 *x = 0;
1768 *y = 0;
1769 *w = window->BltInfo.width;
1770 *h = window->BltInfo.height;
1771 }
1772 else
1773 {
1774 Window xw, child, root;
1775 unsigned int width, height, bw, d;
1776 int rx, ry;
1777
1778 if ((render_spu.render_to_app_window || render_spu.render_to_crut_window)
1779 && window->nativeWindow) {
1780 xw = window->nativeWindow;
1781 }
1782 else {
1783 xw = window->window;
1784 }
1785
1786 XGetGeometry(window->visual->dpy, xw, &root,
1787 x, y, &width, &height, &bw, &d);
1788
1789 /* translate x/y to screen coords */
1790 if (!XTranslateCoordinates(window->visual->dpy, xw, root,
1791 0, 0, &rx, &ry, &child)) {
1792 rx = ry = 0;
1793 }
1794 *x = rx;
1795 *y = ry;
1796 *w = (int) width;
1797 *h = (int) height;
1798 }
1799}
1800
1801
1802void
1803renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h )
1804{
1805 int scrn;
1806#ifdef USE_OSMESA
1807 if (render_spu.use_osmesa) {
1808 *w = 2048;
1809 *h = 2048;
1810 return;
1811 }
1812#endif
1813
1814 CRASSERT(window);
1815 CRASSERT(window->visual);
1816 CRASSERT(window->window);
1817
1818 scrn = DefaultScreen(window->visual->dpy);
1819 *w = DisplayWidth(window->visual->dpy, scrn);
1820 *h = DisplayHeight(window->visual->dpy, scrn);
1821}
1822
1823
1824void
1825renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y )
1826{
1827#ifdef USE_OSMESA
1828 if (render_spu.use_osmesa)
1829 return;
1830#endif
1831
1832 CRASSERT(window);
1833 CRASSERT(window->visual);
1834 if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
1835 {
1836 crDebug("Render SPU: XMoveWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, x, y);
1837 XMoveWindow(window->visual->dpy, window->window, x, y);
1838 XSync(window->visual->dpy, 0);
1839 }
1840}
1841
1842GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window)
1843{
1844 return GL_FALSE;
1845}
1846
1847void
1848renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, const GLint *pRects )
1849{
1850#ifdef USE_OSMESA
1851 if (render_spu.use_osmesa)
1852 return;
1853#endif
1854
1855 CRASSERT(window);
1856 CRASSERT(window->visual);
1857 if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
1858 {
1859 int evb, erb, i;
1860 XRectangle *pXRects;
1861
1862 if (!XShapeQueryExtension(window->visual->dpy, &evb, &erb))
1863 {
1864 crWarning("Render SPU: Display %s doesn't support SHAPE extension", window->visual->displayName);
1865 return;
1866 }
1867
1868 if (cRects>0)
1869 {
1870 pXRects = (XRectangle *) crAlloc(cRects * sizeof(XRectangle));
1871
1872 for (i=0; i<cRects; ++i)
1873 {
1874 pXRects[i].x = (short) pRects[4*i];
1875 pXRects[i].y = (short) pRects[4*i+1];
1876 pXRects[i].width = (unsigned short) (pRects[4*i+2]-pRects[4*i]);
1877 pXRects[i].height = (unsigned short) (pRects[4*i+3]-pRects[4*i+1]);
1878 }
1879 }
1880 else
1881 {
1882 pXRects = (XRectangle *) crAlloc(sizeof(XRectangle));
1883 pXRects[0].x = 0;
1884 pXRects[0].y = 0;
1885 pXRects[0].width = 0;
1886 pXRects[0].height = 0;
1887 cRects = 1;
1888 }
1889
1890 crDebug("Render SPU: XShapeCombineRectangles (%x, %x, cRects=%i)", window->visual->dpy, window->window, cRects);
1891
1892 XShapeCombineRectangles(window->visual->dpy, window->window, ShapeBounding, 0, 0,
1893 pXRects, cRects, ShapeSet, YXBanded);
1894 XSync(window->visual->dpy, 0);
1895 crFree(pXRects);
1896 }
1897}
1898
1899/* Either show or hide the render SPU's window. */
1900void
1901renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt )
1902{
1903#ifdef USE_OSMESA
1904 if (render_spu.use_osmesa)
1905 return;
1906#endif
1907
1908 if (window->visual->dpy && window->window &&
1909 (window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
1910 {
1911 if (showIt)
1912 {
1913 if (window->BltInfo.width && window->BltInfo.height)
1914 {
1915 XMapWindow( window->visual->dpy, window->window );
1916 XSync(window->visual->dpy, 0);
1917 }
1918 }
1919 else
1920 {
1921 XUnmapWindow( window->visual->dpy, window->window );
1922 XSync(window->visual->dpy, 0);
1923 }
1924 }
1925}
1926
1927#define CR_RENDER_FORCE_PRESENT_MAIN_THREAD
1928
1929void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
1930{
1931 /* the CR_RENDER_FORCE_PRESENT_MAIN_THREAD is actually inherited from cocoa backend impl,
1932 * here it forces rendering in WinCmd thread rather than a Main thread.
1933 * it is used for debugging only in any way actually.
1934 * @todo: change to some more generic macro name */
1935#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD
1936 const struct VBOXVR_SCR_COMPOSITOR *pCompositor;
1937 /* we do not want to be blocked with the GUI thread here, so only draw her eif we are really able to do that w/o bllocking */
1938 int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor);
1939 if (RT_SUCCESS(rc))
1940 {
1941 renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false);
1942 renderspuVBoxCompositorRelease(window);
1943 }
1944 else if (rc == VERR_SEM_BUSY)
1945#endif
1946 {
1947 Status status;
1948 XEvent event;
1949 render_spu.self.Flush();
1950// renderspuVBoxPresentBlitterEnsureCreated(window, 0);
1951
1952 crMemset(&event, 0, sizeof (event));
1953 event.type = Expose;
1954 event.xexpose.window = window->window;
1955 event.xexpose.width = window->BltInfo.width;
1956 event.xexpose.height = window->BltInfo.height;
1957 status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, 0, &event);
1958 if (!status)
1959 {
1960 WARN(("XSendEvent returned null"));
1961 }
1962 XFlush(render_spu.pCommunicationDisplay);
1963 }
1964#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD
1965 else
1966 {
1967 /* this is somewhat we do not expect */
1968 WARN(("renderspuVBoxCompositorTryAcquire failed rc %d", rc));
1969 }
1970#endif
1971}
1972
1973static void
1974MarkWindow(WindowInfo *w)
1975{
1976 static GC gc = 0; /* XXX per-window??? */
1977 if (!gc) {
1978 /* Create a GC for drawing invisible lines */
1979 XGCValues gcValues;
1980 gcValues.function = GXnoop;
1981 gc = XCreateGC(w->visual->dpy, w->nativeWindow, GCFunction, &gcValues);
1982 }
1983 XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->BltInfo.width, w->BltInfo.height);
1984}
1985
1986
1987void
1988renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags )
1989{
1990 CRASSERT(w);
1991
1992#if 00 /*TEMPORARY - FOR TESTING SWAP LOCK*/
1993 if (1) {
1994 /* random delay */
1995 int k = crRandInt(1000 * 100, 750*1000);
1996 static int first = 1;
1997 if (first) {
1998 crRandAutoSeed();
1999 first = 0;
2000 }
2001 usleep(k);
2002 }
2003#endif
2004
2005 /* render_to_app_window:
2006 * w->nativeWindow will only be non-zero if the
2007 * render_spu.render_to_app_window option is true and
2008 * MakeCurrent() recorded the nativeWindow handle in the WindowInfo
2009 * structure.
2010 */
2011 if (w->nativeWindow) {
2012 render_spu.ws.glXSwapBuffers( w->visual->dpy, w->nativeWindow );
2013#if 0
2014 MarkWindow(w);
2015#else
2016 (void) MarkWindow;
2017#endif
2018 }
2019 else {
2020 render_spu.ws.glXSwapBuffers( w->visual->dpy, w->window );
2021 }
2022}
2023
2024void renderspu_SystemReparentWindow(WindowInfo *window)
2025{
2026 Window parent;
2027
2028 parent = render_spu_parent_window_id>0 ? render_spu_parent_window_id :
2029 RootWindow(window->visual->dpy, window->visual->visual->screen);
2030
2031 XReparentWindow(window->visual->dpy, window->window, parent, window->x, window->y);
2032 XSync(window->visual->dpy, False);
2033}
2034
2035void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext)
2036{
2037
2038}
2039
2040uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable)
2041{
2042 return cFunctions;
2043}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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