diff options
Diffstat (limited to 'src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c')
-rw-r--r-- | src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c new file mode 100644 index 00000000..ec494a1f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c @@ -0,0 +1,623 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_mem.h" +#include "cr_spu.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_url.h" +#include "cr_environment.h" +#include "renderspu.h" +#include <stdio.h> + +#ifdef RT_OS_DARWIN +# include <iprt/semaphore.h> +#endif /* RT_OS_DARWIN */ + +static SPUNamedFunctionTable _cr_render_table[1000]; + +SPUFunctions render_functions = { + NULL, /* CHILD COPY */ + NULL, /* DATA */ + _cr_render_table /* THE ACTUAL FUNCTIONS */ +}; + +RenderSPU render_spu; +uint64_t render_spu_parent_window_id = 0; + +#ifdef CHROMIUM_THREADSAFE +CRtsd _RenderTSD; +#endif + +static void swapsyncConnect(void) +{ + char hostname[4096], protocol[4096]; + unsigned short port; + + crNetInit(NULL, NULL); + + if (!crParseURL( render_spu.swap_master_url, protocol, hostname, + &port, 9876)) + crError( "Bad URL: %s", render_spu.swap_master_url ); + + if (render_spu.is_swap_master) + { + int a; + + render_spu.swap_conns = (CRConnection **)crAlloc( + render_spu.num_swap_clients*sizeof(CRConnection *)); + for (a=0; a<render_spu.num_swap_clients; a++) + { + render_spu.swap_conns[a] = crNetAcceptClient( protocol, hostname, port, + render_spu.swap_mtu, 1); + } + } + else + { + render_spu.swap_conns = (CRConnection **)crAlloc(sizeof(CRConnection *)); + + render_spu.swap_conns[0] = crNetConnectToServer(render_spu.swap_master_url, + port, render_spu.swap_mtu, 1); + if (!render_spu.swap_conns[0]) + crError("Failed connection"); + } +} + +#ifdef RT_OS_WINDOWS +static DWORD WINAPI renderSPUWindowThreadProc(void* unused) +{ + MSG msg; + BOOL bRet; + + (void) unused; + + /* Force system to create the message queue. + * Else, there's a chance that render spu will issue PostThreadMessage + * before this thread calls GetMessage for first time. + */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + crDebug("RenderSPU: Window thread started (%x)", crThreadID()); + SetEvent(render_spu.hWinThreadReadyEvent); + + while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0) + { + if (bRet == -1) + { + crError("RenderSPU: Window thread GetMessage failed (%x)", GetLastError()); + break; + } + else + { + if (msg.message == WM_VBOX_RENDERSPU_CREATE_WINDOW) + { + LPCREATESTRUCT pCS = (LPCREATESTRUCT) msg.lParam; + HWND hWnd; + WindowInfo *pWindow = (WindowInfo *)pCS->lpCreateParams; + + CRASSERT(msg.lParam && !msg.wParam && pCS->lpCreateParams); + + hWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style, + pCS->x, pCS->y, pCS->cx, pCS->cy, + pCS->hwndParent, pCS->hMenu, pCS->hInstance, &render_spu); + + pWindow->hWnd = hWnd; + + SetEvent(render_spu.hWinThreadReadyEvent); + } + else if (msg.message == WM_VBOX_RENDERSPU_DESTROY_WINDOW) + { + CRASSERT(msg.lParam && !msg.wParam); + + DestroyWindow(((VBOX_RENDERSPU_DESTROY_WINDOW*) msg.lParam)->hWnd); + + SetEvent(render_spu.hWinThreadReadyEvent); + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + render_spu.dwWinThreadId = 0; + + crDebug("RenderSPU: Window thread stopped (%x)", crThreadID()); + SetEvent(render_spu.hWinThreadReadyEvent); + + return 0; +} +#endif + +int renderspuDefaultCtxInit() +{ + GLint defaultWin, defaultCtx; + WindowInfo *windowInfo; + + /* + * Create the default window and context. Their indexes are zero and + * a client can use them without calling CreateContext or WindowCreate. + */ + crDebug("Render SPU: Creating default window (visBits=0x%x, id=0)", + render_spu.default_visual); + defaultWin = renderspuWindowCreateEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_WINDOW_ID ); + if (defaultWin != CR_RENDER_DEFAULT_WINDOW_ID) { + crError("Render SPU: Couldn't get a double-buffered, RGB visual with Z!"); + return VERR_GENERAL_FAILURE; + } + crDebug( "Render SPU: WindowCreate returned %d (0=normal)", defaultWin ); + + crDebug("Render SPU: Creating default context, visBits=0x%x", + render_spu.default_visual ); + defaultCtx = renderspuCreateContextEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_CONTEXT_ID, 0 ); + if (defaultCtx != CR_RENDER_DEFAULT_CONTEXT_ID) { + crError("Render SPU: failed to create default context!"); + return VERR_GENERAL_FAILURE; + } + + renderspuMakeCurrent( defaultWin, 0, defaultCtx ); + + /* Get windowInfo for the default window */ + windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); + CRASSERT(windowInfo); + windowInfo->mapPending = GL_TRUE; + + return VINF_SUCCESS; +} + +static SPUFunctions * +renderSPUInit( int id, SPU *child, SPU *self, + unsigned int context_id, unsigned int num_contexts ) +{ + int numFuncs, numSpecial; + + const char * pcpwSetting; + int rc; + + (void) child; + (void) context_id; + (void) num_contexts; + + self->privatePtr = (void *) &render_spu; + +#ifdef CHROMIUM_THREADSAFE + crDebug("Render SPU: thread-safe"); + crInitTSD(&_RenderTSD); +#endif + + crMemZero(&render_spu, sizeof(render_spu)); + + render_spu.id = id; + renderspuSetVBoxConfiguration(&render_spu); + + if (render_spu.swap_master_url) + swapsyncConnect(); + + + /* Get our special functions. */ + numSpecial = renderspuCreateFunctions( _cr_render_table ); + +#ifdef RT_OS_WINDOWS + /* Start thread to create windows and process window messages */ + crDebug("RenderSPU: Starting windows serving thread"); + render_spu.hWinThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!render_spu.hWinThreadReadyEvent) + { + crError("RenderSPU: Failed to create WinThreadReadyEvent! (%x)", GetLastError()); + return NULL; + } + + if (!CreateThread(NULL, 0, renderSPUWindowThreadProc, 0, 0, &render_spu.dwWinThreadId)) + { + crError("RenderSPU: Failed to start windows thread! (%x)", GetLastError()); + return NULL; + } + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); +#endif + + /* Get the OpenGL functions. */ + numFuncs = crLoadOpenGL( &render_spu.ws, _cr_render_table + numSpecial ); + if (numFuncs == 0) { + crError("The render SPU was unable to load the native OpenGL library"); + return NULL; + } + + numFuncs += numSpecial; + + render_spu.contextTable = crAllocHashtableEx(1, INT32_MAX); + render_spu.windowTable = crAllocHashtableEx(1, INT32_MAX); + + render_spu.dummyWindowTable = crAllocHashtable(); + + pcpwSetting = crGetenv("CR_RENDER_ENABLE_SINGLE_PRESENT_CONTEXT"); + if (pcpwSetting) + { + if (pcpwSetting[0] == '0') + pcpwSetting = NULL; + } + + if (pcpwSetting) + { + /** @todo need proper blitter synchronization, do not use so far! + * the problem is that rendering can be done in multiple thread: the main command (hgcm) thread and the redraw thread + * we currently use per-window synchronization, while we'll need a per-blitter synchronization if one blitter is used for multiple windows + * this is not done currently */ + crWarning("TODO: need proper blitter synchronization, do not use so far!"); + render_spu.blitterTable = crAllocHashtable(); + CRASSERT(render_spu.blitterTable); + } + else + render_spu.blitterTable = NULL; + + CRASSERT(render_spu.default_visual & CR_RGB_BIT); + + rc = renderspu_SystemInit(); + if (!RT_SUCCESS(rc)) + { + crError("renderspu_SystemInit failed rc %d", rc); + return NULL; + } +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + if (!crLoadOSMesa(&render_spu.OSMesaCreateContext, + &render_spu.OSMesaMakeCurrent, + &render_spu.OSMesaDestroyContext)) { + crError("Unable to load OSMesa library"); + } + } +#endif + +#ifdef DARWIN +# ifdef VBOX_WITH_COCOA_QT +# else /* VBOX_WITH_COCOA_QT */ + render_spu.hRootVisibleRegion = 0; + render_spu.currentBufferName = 1; + render_spu.uiDockUpdateTS = 0; + /* Create a mutex for synchronizing events from the main Qt thread & this + thread */ + RTSemFastMutexCreate(&render_spu.syncMutex); + /* Create our window groups */ + CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pMasterGroup); + CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pParentGroup); + /* Make the correct z-layering */ + SendWindowGroupBehind (render_spu.pParentGroup, render_spu.pMasterGroup); + /* and set the gParentGroup as parent for gMasterGroup. */ + SetWindowGroupParent (render_spu.pMasterGroup, render_spu.pParentGroup); + /* Install the event handlers */ + EventTypeSpec eventList[] = + { + {kEventClassVBox, kEventVBoxUpdateContext}, /* Update the context after show/size/move events */ + {kEventClassVBox, kEventVBoxBoundsChanged} /* Clip/Pos the OpenGL windows when the main window is changed in pos/size */ + }; + /* We need to process events from our main window */ + render_spu.hParentEventHandler = NewEventHandlerUPP(windowEvtHndlr); + InstallApplicationEventHandler (render_spu.hParentEventHandler, + GetEventTypeCount(eventList), eventList, + NULL, NULL); + render_spu.fInit = true; +# endif /* VBOX_WITH_COCOA_QT */ +#endif /* DARWIN */ + + rc = renderspuDefaultCtxInit(); + if (!RT_SUCCESS(rc)) + { + WARN(("renderspuDefaultCtxInit failed %d", rc)); + return NULL; + } + + /* + * Get the OpenGL extension functions. + * SIGH -- we have to wait until the very bitter end to load the + * extensions, because the context has to be bound before + * wglGetProcAddress will work correctly. No such issue with GLX though. + */ + numFuncs += crLoadOpenGLExtensions( &render_spu.ws, _cr_render_table + numFuncs ); + CRASSERT(numFuncs < 1000); + +#ifdef WINDOWS + /* + * Same problem as above, these are extensions so we need to + * load them after a context has been bound. As they're WGL + * extensions too, we can't simply tag them into the spu_loader. + * So we do them here for now. + * Grrr, NVIDIA driver uses EXT for GetExtensionsStringEXT, + * but ARB for others. Need further testing here.... + */ + render_spu.ws.wglGetExtensionsStringEXT = + (wglGetExtensionsStringEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetExtensionsStringEXT" ); + render_spu.ws.wglChoosePixelFormatEXT = + (wglChoosePixelFormatEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglChoosePixelFormatARB" ); + render_spu.ws.wglGetPixelFormatAttribivEXT = + (wglGetPixelFormatAttribivEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribivARB" ); + render_spu.ws.wglGetPixelFormatAttribfvEXT = + (wglGetPixelFormatAttribfvEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribfvARB" ); + + if (render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("CopyTexSubImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glCopyTexSubImage3D function"); + } + + if (render_spu.ws.wglGetProcAddress("glDrawRangeElements")) + { + _cr_render_table[numFuncs].name = crStrdup("DrawRangeElements"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glDrawRangeElements"); + ++numFuncs; + crDebug("Render SPU: Found glDrawRangeElements function"); + } + + if (render_spu.ws.wglGetProcAddress("glTexSubImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("TexSubImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexSubImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glTexSubImage3D function"); + } + + if (render_spu.ws.wglGetProcAddress("glTexImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("TexImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glTexImage3D function"); + } + + if (render_spu.ws.wglGetExtensionsStringEXT) { + crDebug("WGL - found wglGetExtensionsStringEXT\n"); + } + if (render_spu.ws.wglChoosePixelFormatEXT) { + crDebug("WGL - found wglChoosePixelFormatEXT\n"); + } +#endif + + render_spu.barrierHash = crAllocHashtable(); + + render_spu.cursorX = 0; + render_spu.cursorY = 0; + render_spu.use_L2 = 0; + + render_spu.gather_conns = NULL; + + numFuncs = renderspu_SystemPostprocessFunctions(_cr_render_table, numFuncs, RT_ELEMENTS(_cr_render_table)); + + crDebug("Render SPU: ---------- End of Init -------------"); + + return &render_functions; +} + +static void renderSPUSelfDispatch(SPUDispatchTable *self) +{ + crSPUInitDispatchTable( &(render_spu.self) ); + crSPUCopyDispatchTable( &(render_spu.self), self ); + + crSPUInitDispatchTable( &(render_spu.blitterDispatch) ); + crSPUCopyDispatchTable( &(render_spu.blitterDispatch), self ); + + render_spu.server = (CRServer *)(self->server); + + { + GLfloat version; + version = crStrToFloat((const char *) render_spu.ws.glGetString(GL_VERSION)); + + if (version>=2.f || crStrstr((const char*)render_spu.ws.glGetString(GL_EXTENSIONS), "GL_ARB_vertex_shader")) + { + GLint mu=0; + render_spu.self.GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &mu); + crInfo("Render SPU: GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB=%i", mu); + } + } +} + + +static void DeleteContextCallback( void *data ) +{ + ContextInfo *context = (ContextInfo *) data; + renderspuContextMarkDeletedAndRelease(context); +} + +static void DeleteWindowCallback( void *data ) +{ + WindowInfo *window = (WindowInfo *) data; + renderspuWinTermOnShutdown(window); + renderspuWinRelease(window); +} + +static void DeleteBlitterCallback( void *data ) +{ + PCR_BLITTER pBlitter = (PCR_BLITTER) data; + CrBltTerm(pBlitter); + crFree(pBlitter); +} + +static void renderspuBlitterCleanupCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + CRASSERT(window); + + renderspuVBoxPresentBlitterCleanup( window ); +} + + +static void renderspuDeleteBlitterCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteBlitterCallback(data1); +} + + +static void renderspuDeleteWindowCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteWindowCallback(data1); +} + +static void renderspuDeleteBarierCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + crFree(data1); +} + + +static void renderspuDeleteContextCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteContextCallback(data1); +} + +void renderspuCleanupBase(bool fDeleteTables) +{ + renderspuVBoxCompositorClearAll(); + + if (render_spu.blitterTable) + { + if (fDeleteTables) + { + crFreeHashtable(render_spu.blitterTable, DeleteBlitterCallback); + render_spu.blitterTable = NULL; + } + else + { + crHashtableWalk(render_spu.blitterTable, renderspuDeleteBlitterCB, render_spu.contextTable); + } + } + else + { + crHashtableWalk(render_spu.windowTable, renderspuBlitterCleanupCB, NULL); + + crHashtableWalk(render_spu.dummyWindowTable, renderspuBlitterCleanupCB, NULL); + } + + renderspuSetDefaultSharedContext(NULL); + + if (fDeleteTables) + { + crFreeHashtable(render_spu.contextTable, DeleteContextCallback); + render_spu.contextTable = NULL; + crFreeHashtable(render_spu.windowTable, DeleteWindowCallback); + render_spu.windowTable = NULL; + crFreeHashtable(render_spu.dummyWindowTable, DeleteWindowCallback); + render_spu.dummyWindowTable = NULL; + crFreeHashtable(render_spu.barrierHash, crFree); + render_spu.barrierHash = NULL; + } + else + { + crHashtableWalk(render_spu.contextTable, renderspuDeleteContextCB, render_spu.contextTable); + crHashtableWalk(render_spu.windowTable, renderspuDeleteWindowCB, render_spu.windowTable); + crHashtableWalk(render_spu.dummyWindowTable, renderspuDeleteWindowCB, render_spu.dummyWindowTable); + crHashtableWalk(render_spu.barrierHash, renderspuDeleteBarierCB, render_spu.barrierHash); + } +} + +static int renderSPUCleanup(void) +{ + renderspuCleanupBase(true); + +#ifdef RT_OS_DARWIN +# ifndef VBOX_WITH_COCOA_QT + render_spu.fInit = false; + DisposeEventHandlerUPP(render_spu.hParentEventHandler); + ReleaseWindowGroup(render_spu.pMasterGroup); + ReleaseWindowGroup(render_spu.pParentGroup); + if (render_spu.hRootVisibleRegion) + { + DisposeRgn(render_spu.hRootVisibleRegion); + render_spu.hRootVisibleRegion = 0; + } + render_spu.currentBufferName = 1; + render_spu.uiDockUpdateTS = 0; + RTSemFastMutexDestroy(render_spu.syncMutex); +# else /* VBOX_WITH_COCOA_QT */ +# endif /* VBOX_WITH_COCOA_QT */ +#endif /* RT_OS_DARWIN */ + +#ifdef RT_OS_WINDOWS + if (render_spu.dwWinThreadId) + { + HANDLE hNative; + + hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE, + false, render_spu.dwWinThreadId); + if (!hNative) + { + crWarning("Failed to get handle for window thread(%#x)", GetLastError()); + } + + if (PostThreadMessage(render_spu.dwWinThreadId, WM_QUIT, 0, 0)) + { + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); + + /*wait for os thread to actually finish*/ + if (hNative && WaitForSingleObject(hNative, 3000)==WAIT_TIMEOUT) + { + crDebug("Wait failed, terminating"); + if (!TerminateThread(hNative, 1)) + { + crWarning("TerminateThread failed"); + } + } + } + + if (hNative) + { + CloseHandle(hNative); + } + } + CloseHandle(render_spu.hWinThreadReadyEvent); + render_spu.hWinThreadReadyEvent = NULL; +#endif + + crUnloadOpenGL(); + +#ifdef CHROMIUM_THREADSAFE + crFreeTSD(&_RenderTSD); +#endif + + return 1; +} + + +extern SPUOptions renderSPUOptions[]; + +int SPULoad( char **name, char **super, SPUInitFuncPtr *init, + SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup, + SPUOptionsPtr *options, int *flags ) +{ + *name = "render"; + *super = NULL; + *init = renderSPUInit; + *self = renderSPUSelfDispatch; + *cleanup = renderSPUCleanup; + *options = renderSPUOptions; + *flags = (SPU_NO_PACKER|SPU_IS_TERMINAL|SPU_MAX_SERVERS_ZERO); + + return 1; +} + +DECLEXPORT(void) renderspuSetWindowId(uint64_t winId) +{ + render_spu_parent_window_id = winId; + crDebug("Set new parent window %p (no actual reparent performed)", winId); +} |