VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c@ 71736

最後變更 在這個檔案從71736是 69390,由 vboxsync 提交於 7 年 前

HostServices/SharedOpenGL: scm updates

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 19.7 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
7#include "cr_mem.h"
8#include "cr_spu.h"
9#include "cr_error.h"
10#include "cr_string.h"
11#include "cr_url.h"
12#include "cr_environment.h"
13#include "renderspu.h"
14#include <stdio.h>
15
16#ifdef RT_OS_DARWIN
17# include <iprt/semaphore.h>
18#endif /* RT_OS_DARWIN */
19
20static SPUNamedFunctionTable _cr_render_table[1000];
21
22SPUFunctions render_functions = {
23 NULL, /* CHILD COPY */
24 NULL, /* DATA */
25 _cr_render_table /* THE ACTUAL FUNCTIONS */
26};
27
28RenderSPU render_spu;
29uint64_t render_spu_parent_window_id = 0;
30
31#ifdef CHROMIUM_THREADSAFE
32CRtsd _RenderTSD;
33#endif
34
35static void swapsyncConnect(void)
36{
37 char hostname[4096], protocol[4096];
38 unsigned short port;
39
40 crNetInit(NULL, NULL);
41
42 if (!crParseURL( render_spu.swap_master_url, protocol, hostname,
43 &port, 9876))
44 crError( "Bad URL: %s", render_spu.swap_master_url );
45
46 if (render_spu.is_swap_master)
47 {
48 int a;
49
50 render_spu.swap_conns = (CRConnection **)crAlloc(
51 render_spu.num_swap_clients*sizeof(CRConnection *));
52 for (a=0; a<render_spu.num_swap_clients; a++)
53 {
54 render_spu.swap_conns[a] = crNetAcceptClient( protocol, hostname, port,
55 render_spu.swap_mtu, 1);
56 }
57 }
58 else
59 {
60 render_spu.swap_conns = (CRConnection **)crAlloc(sizeof(CRConnection *));
61
62 render_spu.swap_conns[0] = crNetConnectToServer(render_spu.swap_master_url,
63 port, render_spu.swap_mtu, 1);
64 if (!render_spu.swap_conns[0])
65 crError("Failed connection");
66 }
67}
68
69#ifdef RT_OS_WINDOWS
70static DWORD WINAPI renderSPUWindowThreadProc(void* unused)
71{
72 MSG msg;
73 BOOL bRet;
74
75 (void) unused;
76
77 /* Force system to create the message queue.
78 * Else, there's a chance that render spu will issue PostThreadMessage
79 * before this thread calls GetMessage for first time.
80 */
81 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
82
83 crDebug("RenderSPU: Window thread started (%x)", crThreadID());
84 SetEvent(render_spu.hWinThreadReadyEvent);
85
86 while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
87 {
88 if (bRet == -1)
89 {
90 crError("RenderSPU: Window thread GetMessage failed (%x)", GetLastError());
91 break;
92 }
93 else
94 {
95 if (msg.message == WM_VBOX_RENDERSPU_CREATE_WINDOW)
96 {
97 LPCREATESTRUCT pCS = (LPCREATESTRUCT) msg.lParam;
98 HWND hWnd;
99 WindowInfo *pWindow = (WindowInfo *)pCS->lpCreateParams;
100
101 CRASSERT(msg.lParam && !msg.wParam && pCS->lpCreateParams);
102
103 hWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style,
104 pCS->x, pCS->y, pCS->cx, pCS->cy,
105 pCS->hwndParent, pCS->hMenu, pCS->hInstance, &render_spu);
106
107 pWindow->hWnd = hWnd;
108
109 SetEvent(render_spu.hWinThreadReadyEvent);
110 }
111 else if (msg.message == WM_VBOX_RENDERSPU_DESTROY_WINDOW)
112 {
113 CRASSERT(msg.lParam && !msg.wParam);
114
115 DestroyWindow(((VBOX_RENDERSPU_DESTROY_WINDOW*) msg.lParam)->hWnd);
116
117 SetEvent(render_spu.hWinThreadReadyEvent);
118 }
119 else
120 {
121 TranslateMessage(&msg);
122 DispatchMessage(&msg);
123 }
124 }
125 }
126
127 render_spu.dwWinThreadId = 0;
128
129 crDebug("RenderSPU: Window thread stopped (%x)", crThreadID());
130 SetEvent(render_spu.hWinThreadReadyEvent);
131
132 return 0;
133}
134#endif
135
136int renderspuDefaultCtxInit()
137{
138 GLint defaultWin, defaultCtx;
139 WindowInfo *windowInfo;
140
141 /*
142 * Create the default window and context. Their indexes are zero and
143 * a client can use them without calling CreateContext or WindowCreate.
144 */
145 crDebug("Render SPU: Creating default window (visBits=0x%x, id=0)",
146 render_spu.default_visual);
147 defaultWin = renderspuWindowCreateEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_WINDOW_ID );
148 if (defaultWin != CR_RENDER_DEFAULT_WINDOW_ID) {
149 crError("Render SPU: Couldn't get a double-buffered, RGB visual with Z!");
150 return VERR_GENERAL_FAILURE;
151 }
152 crDebug( "Render SPU: WindowCreate returned %d (0=normal)", defaultWin );
153
154 crDebug("Render SPU: Creating default context, visBits=0x%x",
155 render_spu.default_visual );
156 defaultCtx = renderspuCreateContextEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_CONTEXT_ID, 0 );
157 if (defaultCtx != CR_RENDER_DEFAULT_CONTEXT_ID) {
158 crError("Render SPU: failed to create default context!");
159 return VERR_GENERAL_FAILURE;
160 }
161
162 renderspuMakeCurrent( defaultWin, 0, defaultCtx );
163
164 /* Get windowInfo for the default window */
165 windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID);
166 CRASSERT(windowInfo);
167 windowInfo->mapPending = GL_TRUE;
168
169 return VINF_SUCCESS;
170}
171
172static SPUFunctions *
173renderSPUInit( int id, SPU *child, SPU *self,
174 unsigned int context_id, unsigned int num_contexts )
175{
176 int numFuncs, numSpecial;
177
178 const char * pcpwSetting;
179 int rc;
180
181 (void) child;
182 (void) context_id;
183 (void) num_contexts;
184
185 self->privatePtr = (void *) &render_spu;
186
187#ifdef CHROMIUM_THREADSAFE
188 crDebug("Render SPU: thread-safe");
189 crInitTSD(&_RenderTSD);
190#endif
191
192 crMemZero(&render_spu, sizeof(render_spu));
193
194 render_spu.id = id;
195 renderspuSetVBoxConfiguration(&render_spu);
196
197 if (render_spu.swap_master_url)
198 swapsyncConnect();
199
200
201 /* Get our special functions. */
202 numSpecial = renderspuCreateFunctions( _cr_render_table );
203
204#ifdef RT_OS_WINDOWS
205 /* Start thread to create windows and process window messages */
206 crDebug("RenderSPU: Starting windows serving thread");
207 render_spu.hWinThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
208 if (!render_spu.hWinThreadReadyEvent)
209 {
210 crError("RenderSPU: Failed to create WinThreadReadyEvent! (%x)", GetLastError());
211 return NULL;
212 }
213
214 if (!CreateThread(NULL, 0, renderSPUWindowThreadProc, 0, 0, &render_spu.dwWinThreadId))
215 {
216 crError("RenderSPU: Failed to start windows thread! (%x)", GetLastError());
217 return NULL;
218 }
219 WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE);
220#endif
221
222 /* Get the OpenGL functions. */
223 numFuncs = crLoadOpenGL( &render_spu.ws, _cr_render_table + numSpecial );
224 if (numFuncs == 0) {
225 crError("The render SPU was unable to load the native OpenGL library");
226 return NULL;
227 }
228
229 numFuncs += numSpecial;
230
231 render_spu.contextTable = crAllocHashtableEx(1, INT32_MAX);
232 render_spu.windowTable = crAllocHashtableEx(1, INT32_MAX);
233
234 render_spu.dummyWindowTable = crAllocHashtable();
235
236 pcpwSetting = crGetenv("CR_RENDER_ENABLE_SINGLE_PRESENT_CONTEXT");
237 if (pcpwSetting)
238 {
239 if (pcpwSetting[0] == '0')
240 pcpwSetting = NULL;
241 }
242
243 if (pcpwSetting)
244 {
245 /** @todo need proper blitter synchronization, do not use so far!
246 * the problem is that rendering can be done in multiple thread: the main command (hgcm) thread and the redraw thread
247 * we currently use per-window synchronization, while we'll need a per-blitter synchronization if one blitter is used for multiple windows
248 * this is not done currently */
249 crWarning("TODO: need proper blitter synchronization, do not use so far!");
250 render_spu.blitterTable = crAllocHashtable();
251 CRASSERT(render_spu.blitterTable);
252 }
253 else
254 render_spu.blitterTable = NULL;
255
256 CRASSERT(render_spu.default_visual & CR_RGB_BIT);
257
258 rc = renderspu_SystemInit();
259 if (!RT_SUCCESS(rc))
260 {
261 crError("renderspu_SystemInit failed rc %d", rc);
262 return NULL;
263 }
264#ifdef USE_OSMESA
265 if (render_spu.use_osmesa) {
266 if (!crLoadOSMesa(&render_spu.OSMesaCreateContext,
267 &render_spu.OSMesaMakeCurrent,
268 &render_spu.OSMesaDestroyContext)) {
269 crError("Unable to load OSMesa library");
270 }
271 }
272#endif
273
274#ifdef DARWIN
275# ifdef VBOX_WITH_COCOA_QT
276# else /* VBOX_WITH_COCOA_QT */
277 render_spu.hRootVisibleRegion = 0;
278 render_spu.currentBufferName = 1;
279 render_spu.uiDockUpdateTS = 0;
280 /* Create a mutex for synchronizing events from the main Qt thread & this
281 thread */
282 RTSemFastMutexCreate(&render_spu.syncMutex);
283 /* Create our window groups */
284 CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pMasterGroup);
285 CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pParentGroup);
286 /* Make the correct z-layering */
287 SendWindowGroupBehind (render_spu.pParentGroup, render_spu.pMasterGroup);
288 /* and set the gParentGroup as parent for gMasterGroup. */
289 SetWindowGroupParent (render_spu.pMasterGroup, render_spu.pParentGroup);
290 /* Install the event handlers */
291 EventTypeSpec eventList[] =
292 {
293 {kEventClassVBox, kEventVBoxUpdateContext}, /* Update the context after show/size/move events */
294 {kEventClassVBox, kEventVBoxBoundsChanged} /* Clip/Pos the OpenGL windows when the main window is changed in pos/size */
295 };
296 /* We need to process events from our main window */
297 render_spu.hParentEventHandler = NewEventHandlerUPP(windowEvtHndlr);
298 InstallApplicationEventHandler (render_spu.hParentEventHandler,
299 GetEventTypeCount(eventList), eventList,
300 NULL, NULL);
301 render_spu.fInit = true;
302# endif /* VBOX_WITH_COCOA_QT */
303#endif /* DARWIN */
304
305 rc = renderspuDefaultCtxInit();
306 if (!RT_SUCCESS(rc))
307 {
308 WARN(("renderspuDefaultCtxInit failed %d", rc));
309 return NULL;
310 }
311
312 /*
313 * Get the OpenGL extension functions.
314 * SIGH -- we have to wait until the very bitter end to load the
315 * extensions, because the context has to be bound before
316 * wglGetProcAddress will work correctly. No such issue with GLX though.
317 */
318 numFuncs += crLoadOpenGLExtensions( &render_spu.ws, _cr_render_table + numFuncs );
319 CRASSERT(numFuncs < 1000);
320
321#ifdef WINDOWS
322 /*
323 * Same problem as above, these are extensions so we need to
324 * load them after a context has been bound. As they're WGL
325 * extensions too, we can't simply tag them into the spu_loader.
326 * So we do them here for now.
327 * Grrr, NVIDIA driver uses EXT for GetExtensionsStringEXT,
328 * but ARB for others. Need further testing here....
329 */
330 render_spu.ws.wglGetExtensionsStringEXT =
331 (wglGetExtensionsStringEXTFunc_t)
332 render_spu.ws.wglGetProcAddress( "wglGetExtensionsStringEXT" );
333 render_spu.ws.wglChoosePixelFormatEXT =
334 (wglChoosePixelFormatEXTFunc_t)
335 render_spu.ws.wglGetProcAddress( "wglChoosePixelFormatARB" );
336 render_spu.ws.wglGetPixelFormatAttribivEXT =
337 (wglGetPixelFormatAttribivEXTFunc_t)
338 render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribivARB" );
339 render_spu.ws.wglGetPixelFormatAttribfvEXT =
340 (wglGetPixelFormatAttribfvEXTFunc_t)
341 render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribfvARB" );
342
343 if (render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D"))
344 {
345 _cr_render_table[numFuncs].name = crStrdup("CopyTexSubImage3D");
346 _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D");
347 ++numFuncs;
348 crDebug("Render SPU: Found glCopyTexSubImage3D function");
349 }
350
351 if (render_spu.ws.wglGetProcAddress("glDrawRangeElements"))
352 {
353 _cr_render_table[numFuncs].name = crStrdup("DrawRangeElements");
354 _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glDrawRangeElements");
355 ++numFuncs;
356 crDebug("Render SPU: Found glDrawRangeElements function");
357 }
358
359 if (render_spu.ws.wglGetProcAddress("glTexSubImage3D"))
360 {
361 _cr_render_table[numFuncs].name = crStrdup("TexSubImage3D");
362 _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexSubImage3D");
363 ++numFuncs;
364 crDebug("Render SPU: Found glTexSubImage3D function");
365 }
366
367 if (render_spu.ws.wglGetProcAddress("glTexImage3D"))
368 {
369 _cr_render_table[numFuncs].name = crStrdup("TexImage3D");
370 _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexImage3D");
371 ++numFuncs;
372 crDebug("Render SPU: Found glTexImage3D function");
373 }
374
375 if (render_spu.ws.wglGetExtensionsStringEXT) {
376 crDebug("WGL - found wglGetExtensionsStringEXT\n");
377 }
378 if (render_spu.ws.wglChoosePixelFormatEXT) {
379 crDebug("WGL - found wglChoosePixelFormatEXT\n");
380 }
381#endif
382
383 render_spu.barrierHash = crAllocHashtable();
384
385 render_spu.cursorX = 0;
386 render_spu.cursorY = 0;
387 render_spu.use_L2 = 0;
388
389 render_spu.gather_conns = NULL;
390
391 numFuncs = renderspu_SystemPostprocessFunctions(_cr_render_table, numFuncs, RT_ELEMENTS(_cr_render_table));
392
393 crDebug("Render SPU: ---------- End of Init -------------");
394
395 return &render_functions;
396}
397
398static void renderSPUSelfDispatch(SPUDispatchTable *self)
399{
400 crSPUInitDispatchTable( &(render_spu.self) );
401 crSPUCopyDispatchTable( &(render_spu.self), self );
402
403 crSPUInitDispatchTable( &(render_spu.blitterDispatch) );
404 crSPUCopyDispatchTable( &(render_spu.blitterDispatch), self );
405
406 render_spu.server = (CRServer *)(self->server);
407
408 {
409 GLfloat version;
410 version = crStrToFloat((const char *) render_spu.ws.glGetString(GL_VERSION));
411
412 if (version>=2.f || crStrstr((const char*)render_spu.ws.glGetString(GL_EXTENSIONS), "GL_ARB_vertex_shader"))
413 {
414 GLint mu=0;
415 render_spu.self.GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &mu);
416 crInfo("Render SPU: GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB=%i", mu);
417 }
418 }
419}
420
421
422static void DeleteContextCallback( void *data )
423{
424 ContextInfo *context = (ContextInfo *) data;
425 renderspuContextMarkDeletedAndRelease(context);
426}
427
428static void DeleteWindowCallback( void *data )
429{
430 WindowInfo *window = (WindowInfo *) data;
431 renderspuWinTermOnShutdown(window);
432 renderspuWinRelease(window);
433}
434
435static void DeleteBlitterCallback( void *data )
436{
437 PCR_BLITTER pBlitter = (PCR_BLITTER) data;
438 CrBltTerm(pBlitter);
439 crFree(pBlitter);
440}
441
442static void renderspuBlitterCleanupCB(unsigned long key, void *data1, void *data2)
443{
444 WindowInfo *window = (WindowInfo *) data1;
445 CRASSERT(window);
446
447 renderspuVBoxPresentBlitterCleanup( window );
448}
449
450
451static void renderspuDeleteBlitterCB(unsigned long key, void *data1, void *data2)
452{
453 CRHashTable *pTbl = (CRHashTable*)data2;
454
455 crHashtableDelete( pTbl, key, NULL );
456
457 DeleteBlitterCallback(data1);
458}
459
460
461static void renderspuDeleteWindowCB(unsigned long key, void *data1, void *data2)
462{
463 CRHashTable *pTbl = (CRHashTable*)data2;
464
465 crHashtableDelete( pTbl, key, NULL );
466
467 DeleteWindowCallback(data1);
468}
469
470static void renderspuDeleteBarierCB(unsigned long key, void *data1, void *data2)
471{
472 CRHashTable *pTbl = (CRHashTable*)data2;
473
474 crHashtableDelete( pTbl, key, NULL );
475
476 crFree(data1);
477}
478
479
480static void renderspuDeleteContextCB(unsigned long key, void *data1, void *data2)
481{
482 CRHashTable *pTbl = (CRHashTable*)data2;
483
484 crHashtableDelete( pTbl, key, NULL );
485
486 DeleteContextCallback(data1);
487}
488
489void renderspuCleanupBase(bool fDeleteTables)
490{
491 renderspuVBoxCompositorClearAll();
492
493 if (render_spu.blitterTable)
494 {
495 if (fDeleteTables)
496 {
497 crFreeHashtable(render_spu.blitterTable, DeleteBlitterCallback);
498 render_spu.blitterTable = NULL;
499 }
500 else
501 {
502 crHashtableWalk(render_spu.blitterTable, renderspuDeleteBlitterCB, render_spu.contextTable);
503 }
504 }
505 else
506 {
507 crHashtableWalk(render_spu.windowTable, renderspuBlitterCleanupCB, NULL);
508
509 crHashtableWalk(render_spu.dummyWindowTable, renderspuBlitterCleanupCB, NULL);
510 }
511
512 renderspuSetDefaultSharedContext(NULL);
513
514 if (fDeleteTables)
515 {
516 crFreeHashtable(render_spu.contextTable, DeleteContextCallback);
517 render_spu.contextTable = NULL;
518 crFreeHashtable(render_spu.windowTable, DeleteWindowCallback);
519 render_spu.windowTable = NULL;
520 crFreeHashtable(render_spu.dummyWindowTable, DeleteWindowCallback);
521 render_spu.dummyWindowTable = NULL;
522 crFreeHashtable(render_spu.barrierHash, crFree);
523 render_spu.barrierHash = NULL;
524 }
525 else
526 {
527 crHashtableWalk(render_spu.contextTable, renderspuDeleteContextCB, render_spu.contextTable);
528 crHashtableWalk(render_spu.windowTable, renderspuDeleteWindowCB, render_spu.windowTable);
529 crHashtableWalk(render_spu.dummyWindowTable, renderspuDeleteWindowCB, render_spu.dummyWindowTable);
530 crHashtableWalk(render_spu.barrierHash, renderspuDeleteBarierCB, render_spu.barrierHash);
531 }
532}
533
534static int renderSPUCleanup(void)
535{
536 renderspuCleanupBase(true);
537
538#ifdef RT_OS_DARWIN
539# ifndef VBOX_WITH_COCOA_QT
540 render_spu.fInit = false;
541 DisposeEventHandlerUPP(render_spu.hParentEventHandler);
542 ReleaseWindowGroup(render_spu.pMasterGroup);
543 ReleaseWindowGroup(render_spu.pParentGroup);
544 if (render_spu.hRootVisibleRegion)
545 {
546 DisposeRgn(render_spu.hRootVisibleRegion);
547 render_spu.hRootVisibleRegion = 0;
548 }
549 render_spu.currentBufferName = 1;
550 render_spu.uiDockUpdateTS = 0;
551 RTSemFastMutexDestroy(render_spu.syncMutex);
552# else /* VBOX_WITH_COCOA_QT */
553# endif /* VBOX_WITH_COCOA_QT */
554#endif /* RT_OS_DARWIN */
555
556#ifdef RT_OS_WINDOWS
557 if (render_spu.dwWinThreadId)
558 {
559 HANDLE hNative;
560
561 hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE,
562 false, render_spu.dwWinThreadId);
563 if (!hNative)
564 {
565 crWarning("Failed to get handle for window thread(%#x)", GetLastError());
566 }
567
568 if (PostThreadMessage(render_spu.dwWinThreadId, WM_QUIT, 0, 0))
569 {
570 WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE);
571
572 /*wait for os thread to actually finish*/
573 if (hNative && WaitForSingleObject(hNative, 3000)==WAIT_TIMEOUT)
574 {
575 crDebug("Wait failed, terminating");
576 if (!TerminateThread(hNative, 1))
577 {
578 crWarning("TerminateThread failed");
579 }
580 }
581 }
582
583 if (hNative)
584 {
585 CloseHandle(hNative);
586 }
587 }
588 CloseHandle(render_spu.hWinThreadReadyEvent);
589 render_spu.hWinThreadReadyEvent = NULL;
590#endif
591
592 crUnloadOpenGL();
593
594#ifdef CHROMIUM_THREADSAFE
595 crFreeTSD(&_RenderTSD);
596#endif
597
598 return 1;
599}
600
601
602extern SPUOptions renderSPUOptions[];
603
604int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
605 SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
606 SPUOptionsPtr *options, int *flags )
607{
608 *name = "render";
609 *super = NULL;
610 *init = renderSPUInit;
611 *self = renderSPUSelfDispatch;
612 *cleanup = renderSPUCleanup;
613 *options = renderSPUOptions;
614 *flags = (SPU_NO_PACKER|SPU_IS_TERMINAL|SPU_MAX_SERVERS_ZERO);
615
616 return 1;
617}
618
619DECLEXPORT(void) renderspuSetWindowId(uint64_t winId)
620{
621 render_spu_parent_window_id = winId;
622 crDebug("Set new parent window %p (no actual reparent performed)", winId);
623}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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