diff options
Diffstat (limited to '')
13 files changed, 12056 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc new file mode 100644 index 00000000..2cfbc4d9 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxOGLrenderspu.rc $ */ +/** @file + * VBoxOGLrenderspu - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox crOpenGL ICD\0" + VALUE "InternalName", "VBoxOGLrenderspu\0" + VALUE "OriginalFilename", "VBoxOGLrenderspu.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedOpenGL/render/render.def b/src/VBox/HostServices/SharedOpenGL/render/render.def new file mode 100644 index 00000000..870d7494 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/render.def @@ -0,0 +1,7 @@ +; Copyright (c) 2001, Stanford University +; All rights reserved. +; +; See the file LICENSE.txt for information on redistributing this software. +EXPORTS +SPULoad +renderspuSetWindowId diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c new file mode 100644 index 00000000..52a1dfca --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c @@ -0,0 +1,1960 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_environment.h" +#include "cr_string.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_spu.h" +#include "cr_environment.h" +#include "renderspu.h" +#include "cr_extstring.h" + +#include <iprt/asm.h> + +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); + +static void +DoSync(void) +{ + CRMessage *in, out; + + out.header.type = CR_MESSAGE_OOB; + + if (render_spu.is_swap_master) + { + int a; + + for (a = 0; a < render_spu.num_swap_clients; a++) + { + crNetGetMessage( render_spu.swap_conns[a], &in ); + crNetFree( render_spu.swap_conns[a], in); + } + + for (a = 0; a < render_spu.num_swap_clients; a++) + crNetSend( render_spu.swap_conns[a], NULL, &out, sizeof(CRMessage)); + } + else + { + crNetSend( render_spu.swap_conns[0], NULL, &out, sizeof(CRMessage)); + + crNetGetMessage( render_spu.swap_conns[0], &in ); + crNetFree( render_spu.swap_conns[0], in); + } +} + + + +/* + * Visual functions + */ + +/** + * used for debugging and giving info to the user. + */ +void +renderspuMakeVisString( GLbitfield visAttribs, char *s ) +{ + s[0] = 0; + + if (visAttribs & CR_RGB_BIT) + crStrcat(s, "RGB"); + if (visAttribs & CR_ALPHA_BIT) + crStrcat(s, "A"); + if (visAttribs & CR_DOUBLE_BIT) + crStrcat(s, ", Doublebuffer"); + if (visAttribs & CR_STEREO_BIT) + crStrcat(s, ", Stereo"); + if (visAttribs & CR_DEPTH_BIT) + crStrcat(s, ", Z"); + if (visAttribs & CR_STENCIL_BIT) + crStrcat(s, ", Stencil"); + if (visAttribs & CR_ACCUM_BIT) + crStrcat(s, ", Accum"); + if (visAttribs & CR_MULTISAMPLE_BIT) + crStrcat(s, ", Multisample"); + if (visAttribs & CR_OVERLAY_BIT) + crStrcat(s, ", Overlay"); + if (visAttribs & CR_PBUFFER_BIT) + crStrcat(s, ", PBuffer"); +} + +GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs) +{ + pVisInfo->displayName = crStrdup(displayName); + pVisInfo->visAttribs = visAttribs; + return renderspu_SystemInitVisual(pVisInfo); +} + +/* + * Find a VisualInfo which matches the given display name and attribute + * bitmask, or return a pointer to a new visual. + */ +VisualInfo * +renderspuFindVisual(const char *displayName, GLbitfield visAttribs) +{ + int i; + + if (!displayName) + displayName = ""; + + /* first, try to find a match */ +#if defined(WINDOWS) || defined(DARWIN) + for (i = 0; i < render_spu.numVisuals; i++) { + if (visAttribs == render_spu.visuals[i].visAttribs) { + return &(render_spu.visuals[i]); + } + } +#elif defined(GLX) + for (i = 0; i < render_spu.numVisuals; i++) { + if (crStrcmp(displayName, render_spu.visuals[i].displayName) == 0 + && visAttribs == render_spu.visuals[i].visAttribs) { + return &(render_spu.visuals[i]); + } + } +#endif + + if (render_spu.numVisuals >= MAX_VISUALS) + { + crWarning("Render SPU: Couldn't create a visual, too many visuals already"); + return NULL; + } + + /* create a new visual */ + i = render_spu.numVisuals; + if (renderspuInitVisual(&(render_spu.visuals[i]), displayName, visAttribs)) { + render_spu.numVisuals++; + return &(render_spu.visuals[i]); + } + else { + crWarning("Render SPU: Couldn't get a visual, renderspu_SystemInitVisual failed"); + return NULL; + } +} + +static ContextInfo * renderspuCreateContextInternal(const char *dpyName, GLint visBits, GLint idCtx, ContextInfo * sharedContext) +{ + ContextInfo *context; + VisualInfo *visual; + + if (idCtx <= 0) + { + idCtx = (GLint)crHashtableAllocKeys(render_spu.contextTable, 1); + if (idCtx <= 0) + { + crWarning("failed to allocate context id"); + return NULL; + } + } + else + { + if (crHashtableIsKeyUsed(render_spu.contextTable, idCtx)) + { + crWarning("the specified ctx key %d is in use", idCtx); + return NULL; + } + } + + + if (!dpyName || crStrlen(render_spu.display_string)>0) + dpyName = render_spu.display_string; + + visual = renderspuFindVisual(dpyName, visBits); + if (!visual) + return NULL; + + context = (ContextInfo *) crCalloc(sizeof(ContextInfo)); + if (!context) + return NULL; + context->BltInfo.Base.id = idCtx; + context->shared = sharedContext; + if (!renderspu_SystemCreateContext(visual, context, sharedContext)) + return NULL; + + crHashtableAdd(render_spu.contextTable, idCtx, context); + + context->BltInfo.Base.visualBits = visual->visAttribs; + /* + crDebug("Render SPU: CreateContext(%s, 0x%x) returning %d", + dpyName, visBits, context->BltInfo.Base.id); + */ + + if (sharedContext) + renderspuContextRetain(sharedContext); + + context->cRefs = 1; + + return context; +} + +GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx) +{ + ContextInfo *context, *sharedContext = NULL; + + if (shareCtx) { + sharedContext + = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx); + CRASSERT(sharedContext); + } + + context = renderspuCreateContextInternal(dpyName, visBits, id, sharedContext); + if (context) + return context->BltInfo.Base.id; + return -1; +} + +/* + * Context functions + */ + +GLint RENDER_APIENTRY +renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx) +{ + return renderspuCreateContextEx(dpyName, visBits, 0, shareCtx); +} + +static void renderspuDestroyContextTerminate( ContextInfo *context ) +{ + CRASSERT(context->BltInfo.Base.id == -1); + renderspu_SystemDestroyContext( context ); + if (context->extensionString) { + crFree(context->extensionString); + context->extensionString = NULL; + } + + if (context->shared) + renderspuContextRelease( context->shared ); + + crFree(context); +} + +uint32_t renderspuContextRetain( ContextInfo *context ) +{ + Assert(context->cRefs); + return ASMAtomicIncU32(&context->cRefs); +} + +uint32_t renderspuContextRelease( ContextInfo *context ) +{ + uint32_t cRefs = ASMAtomicDecU32(&context->cRefs); + if (!cRefs) + renderspuDestroyContextTerminate( context ); + else + CRASSERT(cRefs < UINT32_MAX/2); + return cRefs; +} + +uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ) +{ + /* invalidate the context id to mark it as deleted */ + context->BltInfo.Base.id = -1; + + /* some drivers do not like when the base (shared) context is deleted before its referals, + * this is why we keep a context refference counting the base (shared) context will be destroyed as soon as*/ + return renderspuContextRelease( context ); +} + +ContextInfo * renderspuDefaultSharedContextAcquire() +{ + ContextInfo * pCtx = render_spu.defaultSharedContext; + if (!pCtx) + return NULL; + + renderspuContextRetain(pCtx); + return pCtx; +} + +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx) +{ + renderspuContextRelease(pCtx); +} + + +static void RENDER_APIENTRY +renderspuDestroyContext( GLint ctx ) +{ + ContextInfo *context, *curCtx; + + CRASSERT(ctx); + + if (ctx == CR_RENDER_DEFAULT_CONTEXT_ID) + { + crWarning("request to destroy a default context, ignoring"); + return; + } + + context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + + if (!context) + { + crWarning("request to delete inexistent context"); + return; + } + + if (render_spu.defaultSharedContext == context) + { + renderspuSetDefaultSharedContext(NULL); + } + + curCtx = GET_CONTEXT_VAL(); +// CRASSERT(curCtx); + if (curCtx == context) + { + renderspuMakeCurrent( CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID ); + curCtx = GET_CONTEXT_VAL(); + Assert(curCtx); + Assert(curCtx != context); + } + + crHashtableDelete(render_spu.contextTable, ctx, NULL); + + renderspuContextMarkDeletedAndRelease(context); +} + +WindowInfo* renderspuWinCreate(GLint visBits, GLint id) +{ + WindowInfo* window = (WindowInfo *)crAlloc(sizeof (*window)); + if (!window) + { + crWarning("crAlloc failed"); + return NULL; + } + + if (!renderspuWinInit(window, NULL, visBits, id)) + { + crWarning("renderspuWinInit failed"); + crFree(window); + return NULL; + } + + return window; +} + +void renderspuWinTermOnShutdown(WindowInfo *window) +{ + renderspuVBoxCompositorSet(window, NULL); + renderspuVBoxPresentBlitterCleanup(window); + window->BltInfo.Base.id = -1; + renderspu_SystemDestroyWindow( window ); +} + +static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, void *data2) +{ + ContextInfo *pCtx = (ContextInfo *) data1; + WindowInfo *pWindow = data2; + (void) key; + + if (pCtx->currentWindow==pWindow) + { + WindowInfo* pDummy = renderspuGetDummyWindow(pCtx->BltInfo.Base.visualBits); + if (pDummy) + { + renderspuPerformMakeCurrent(pDummy, 0, pCtx); + } + else + { + crWarning("failed to get dummy window"); + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, pCtx->BltInfo.Base.id); + } + } +} + +void renderspuWinTerm( WindowInfo *window ) +{ + if (!renderspuWinIsTermed(window)) + { + + GET_CONTEXT(pOldCtx); + WindowInfo * pOldWindow = pOldCtx ? pOldCtx->currentWindow : NULL; + CRASSERT(!pOldCtx == !pOldWindow); + /* ensure no concurrent draws can take place */ + renderspuWinTermOnShutdown(window); + /* check if this window is bound to some ctx. Note: window pointer is already freed here */ + crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window); + /* restore current context */ + { + GET_CONTEXT(pNewCtx); + WindowInfo * pNewWindow = pNewCtx ? pNewCtx->currentWindow : NULL; + CRASSERT(!pNewCtx == !pNewWindow); + + if (pOldWindow == window) + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + else if (pNewCtx != pOldCtx || pOldWindow != pNewWindow) + { + if (pOldCtx) + renderspuPerformMakeCurrent(pOldWindow, 0, pOldCtx); + else + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + } + } + + } +} + +void renderspuWinCleanup(WindowInfo *window) +{ + renderspuWinTerm( window ); + RTCritSectDelete(&window->CompositorLock); +} + +void renderspuWinDestroy(WindowInfo *window) +{ + renderspuWinCleanup(window); + crFree(window); +} + +WindowInfo* renderspuGetDummyWindow(GLint visBits) +{ + WindowInfo *window = (WindowInfo *) crHashtableSearch(render_spu.dummyWindowTable, visBits); + if (!window) + { + window = renderspuWinCreate(visBits, -1); + if (!window) + { + WARN(("renderspuWinCreate failed")); + return NULL; + } + + crHashtableAdd(render_spu.dummyWindowTable, visBits, window); + } + + return window; +} + +/* Check that OpenGL extensions listed in pszRequiredExts string also exist in the pszAvailableExts string. */ +static void renderCompareGLExtensions(const char *pszAvailableExts, const char *pszRequiredExts) +{ + unsigned char fPrintHeader = 1; + const char *pszExt = pszRequiredExts; + + for (;;) + { + const char *pszSrc = pszAvailableExts; + size_t offExtEnd; + + while (*pszExt == ' ') + ++pszExt; + + if (!*pszExt) + break; + + offExtEnd = RTStrOffCharOrTerm(pszExt, ' '); + + for (;;) + { + size_t offSrcEnd; + + while (*pszSrc == ' ') + ++pszSrc; + + if (!*pszSrc) + break; + + offSrcEnd = RTStrOffCharOrTerm(pszSrc, ' '); + + if ( offSrcEnd == offExtEnd + && memcmp(pszSrc, pszExt, offSrcEnd) == 0) + break; + + pszSrc += offSrcEnd; + } + + if (!*pszSrc) + { + if (fPrintHeader) + { + fPrintHeader = 0; + crInfo("Host does not support OpenGL extension(s):"); + } + crInfo(" %.*s", offExtEnd, pszExt); + } + + pszExt += offExtEnd; + } +} + +void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context) +{ + if (window && context) + { +#ifdef CHROMIUM_THREADSAFE + crSetTSD(&_RenderTSD, context); +#else + render_spu.currentContext = context; +#endif + context->currentWindow = window; + + renderspu_SystemMakeCurrent( window, nativeWindow, context ); + if (!context->everCurrent) { + static volatile uint32_t u32ExtCompared = 0; + /* print OpenGL info */ + const char *extString = (const char *) render_spu.ws.glGetString( GL_EXTENSIONS ); + /* + crDebug( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) ); + */ + crInfo( "Render SPU: GL_VENDOR: %s", render_spu.ws.glGetString( GL_VENDOR ) ); + crInfo( "Render SPU: GL_RENDERER: %s", render_spu.ws.glGetString( GL_RENDERER ) ); + crInfo( "Render SPU: GL_VERSION: %s", render_spu.ws.glGetString( GL_VERSION ) ); + crInfo( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) ); + + if (ASMAtomicCmpXchgU32(&u32ExtCompared, 1, 0)) + renderCompareGLExtensions(extString, crExtensions); + + if (crStrstr(extString, "GL_ARB_window_pos")) + context->haveWindowPosARB = GL_TRUE; + else + context->haveWindowPosARB = GL_FALSE; + context->everCurrent = GL_TRUE; + } + if (window->BltInfo.Base.id == CR_RENDER_DEFAULT_WINDOW_ID && window->mapPending && + !render_spu.render_to_app_window && !render_spu.render_to_crut_window) { + /* Window[CR_RENDER_DEFAULT_CONTEXT_ID] is special, it's the default window and normally hidden. + * If the mapPending flag is set, then we should now make the window + * visible. + */ + /*renderspu_SystemShowWindow( window, GL_TRUE );*/ + window->mapPending = GL_FALSE; + } + window->everCurrent = GL_TRUE; + } + else if (!window && !context) + { + renderspu_SystemMakeCurrent( NULL, 0, NULL ); +#ifdef CHROMIUM_THREADSAFE + crSetTSD(&_RenderTSD, NULL); +#else + render_spu.currentContext = NULL; +#endif + } + else + { + crError("renderspuMakeCurrent invalid ids: crWindow(%d), ctx(%d)", + window ? window->BltInfo.Base.id : 0, + context ? context->BltInfo.Base.id : 0); + } +} + +void RENDER_APIENTRY +renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) +{ + WindowInfo *window = NULL; + ContextInfo *context = NULL; + + /* + crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx); + */ + + if (crWindow) + { + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow); + if (!window) + { + crWarning("invalid window %d specified", crWindow); + return; + } + } + + if (ctx) + { + context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + if (!context) + { + crWarning("invalid context %d specified", ctx); + return; + } + } + + if (!context != !window) + { + crWarning("either window %d or context %d are zero", crWindow, ctx); + return; + } + + renderspuPerformMakeCurrent(window, nativeWindow, context); +} + +GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ) +{ + crMemset(window, 0, sizeof (*window)); + RTCritSectInit(&window->CompositorLock); + window->pCompositor = NULL; + + window->BltInfo.Base.id = id; + + window->x = render_spu.defaultX; + window->y = render_spu.defaultY; + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; + + /* Set window->title, replacing %i with the window ID number */ + { + const char *s = crStrstr(render_spu.window_title, "%i"); + if (s) { + int i, j, k; + window->title = crAlloc(crStrlen(render_spu.window_title) + 10); + for (i = 0; render_spu.window_title[i] != '%'; i++) + window->title[i] = render_spu.window_title[i]; + k = sprintf(window->title + i, "%d", window->BltInfo.Base.id); + CRASSERT(k < 10); + i++; /* skip the 'i' after the '%' */ + j = i + k; + for (; (window->title[j] = s[i]) != 0; i++, j++) + ; + } + else { + window->title = crStrdup(render_spu.window_title); + } + } + + window->BltInfo.Base.visualBits = visual->visAttribs; + + window->cRefs = 1; + + /* + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); + */ + /* Have GLX/WGL/AGL create the window */ + if (!renderspu_SystemVBoxCreateWindow( visual, showIt, window )) + { + crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); + return GL_FALSE; + } + + window->visible = !!showIt; + + CRASSERT(window->visual == visual); + return GL_TRUE; +} + +/* + * Window functions + */ +GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id) +{ + VisualInfo *visual; + + crMemset(pWindow, 0, sizeof (*pWindow)); + + if (!dpyName || crStrlen(render_spu.display_string) > 0) + dpyName = render_spu.display_string; + + visual = renderspuFindVisual( dpyName, visBits ); + if (!visual) + { + crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" ); + return GL_FALSE; + } + + /* + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); + */ + /* Have GLX/WGL/AGL create the window */ + if (!renderspuWinInitWithVisual( pWindow, visual, 0, id )) + { + crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ) +{ + WindowInfo *window; + + if (id <= 0) + { + id = (GLint)crHashtableAllocKeys(render_spu.windowTable, 1); + if (id <= 0) + { + crWarning("failed to allocate window id"); + return -1; + } + } + else + { + if (crHashtableIsKeyUsed(render_spu.windowTable, id)) + { + crWarning("the specified window key %d is in use", id); + return -1; + } + } + + /* Allocate WindowInfo */ + window = renderspuWinCreate(visBits, id); + + if (!window) + { + crWarning("renderspuWinCreate failed"); + crFree(window); + return -1; + } + + crHashtableAdd(render_spu.windowTable, id, window); + return window->BltInfo.Base.id; +} + +GLint RENDER_APIENTRY +renderspuWindowCreate( const char *dpyName, GLint visBits ) +{ + return renderspuWindowCreateEx( dpyName, visBits, 0 ); +} + +void renderspuWinReleaseCb(void*pvWindow) +{ + renderspuWinRelease((WindowInfo*)pvWindow); +} + +void +RENDER_APIENTRY renderspuWindowDestroy( GLint win ) +{ + WindowInfo *window; + + CRASSERT(win >= 0); + if (win == CR_RENDER_DEFAULT_WINDOW_ID) + { + crWarning("request to destroy a default mural, ignoring"); + return; + } + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + crDebug("Render SPU: Destroy window (%d)", win); + /* since os-specific backend can hold its own reference to the window object (e.g. on OSX), + * we need to explicitly issue a window destroy command + * this ensures the backend will eventually release the reference, + * the window object itself will remain valid until its ref count reaches zero */ + renderspuWinTerm( window ); + + /* remove window info from hash table, and free it */ + crHashtableDelete(render_spu.windowTable, win, renderspuWinReleaseCb); + + } + else { + crDebug("Render SPU: Attempt to destroy invalid window (%d)", win); + } +} + + +static void RENDER_APIENTRY +renderspuWindowSize( GLint win, GLint w, GLint h ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + if (w != window->BltInfo.width + || h != window->BltInfo.height) + { + /* window is resized, compositor data is no longer valid + * this set also ensures all redraw operations are done in the redraw thread + * and that no redraw is started until new Present request comes containing a valid presentation data */ + renderspuVBoxCompositorSet( window, NULL); + renderspu_SystemWindowSize( window, w, h ); + window->BltInfo.width = w; + window->BltInfo.height = h; + } + } + else { + WARN(("Render SPU: Attempt to resize invalid window (%d)", win)); + } +} + + +static void RENDER_APIENTRY +renderspuWindowPosition( GLint win, GLint x, GLint y ) +{ + if (!render_spu.ignore_window_moves) { + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + renderspu_SystemWindowPosition( window, x, y ); + window->x = x; + window->y = y; + } + else { + crDebug("Render SPU: Attempt to move invalid window (%d)", win); + } + } +} + +#ifdef DEBUG_misha +# define CR_DBG_DUMP_VISIBLE_REGIONS +#endif + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS +static void renderspuDbgDumpVisibleRegion(GLint win, GLint cRects, const GLint *pRects) +{ + GLint i; + const RTRECT *pRtRects = (const RTRECT *)((const void*)pRects); + + crInfo("Window %d, Vidible Regions%d", win, cRects); + for (i = 0; i < cRects; ++i) + { + crInfo("%d: (%d,%d), (%d,%d)", i, pRtRects[i].xLeft, pRtRects[i].yTop, pRtRects[i].xRight, pRtRects[i].yBottom); + } + crInfo("======"); +} +#endif + +static void RENDER_APIENTRY +renderspuWindowVisibleRegion(GLint win, GLint cRects, const GLint *pRects) +{ + WindowInfo *window; + CRASSERT(win >= 0); + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS + renderspuDbgDumpVisibleRegion(win, cRects, pRects); +#endif + + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + renderspu_SystemWindowVisibleRegion( window, cRects, pRects ); + } + else { + crWarning("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win); + } +} + +static void RENDER_APIENTRY +renderspuWindowShow( GLint win, GLint flag ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + GLboolean visible; + if (window->nativeWindow) { + /* We're rendering back to the native app window instead of the + * new window which we (the Render SPU) created earlier. + * So, we never want to show the Render SPU's window. + */ + flag = 0; + } + + visible = !!flag; + +// if (window->visible != visible) + { + renderspu_SystemShowWindow( window, visible ); + window->visible = visible; + } + } + else { + crDebug("Render SPU: Attempt to hide/show invalid window (%d)", win); + } +} + +static void RENDER_APIENTRY +renderspuVBoxPresentComposition( GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + if (renderspuVBoxCompositorSet(window, pCompositor)) + { + renderspu_SystemVBoxPresentComposition(window, pChangedEntry); + } + } + else { + crDebug("Render SPU: Attempt to PresentComposition for invalid window (%d)", win); + } +} + +void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + RTRECT DstRect; + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + DstRect.xLeft = paDstRegions[i].xLeft * scaleX; + DstRect.yTop = paDstRegions[i].yTop * scaleY; + DstRect.xRight = paDstRegions[i].xRight * scaleX; + DstRect.yBottom = paDstRegions[i].yBottom * scaleY; + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), &paSrcRegions[i], &DstRect, 1, fFlags); + } + } + else + { + crWarning("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), paSrcRegions, paDstRegions, cRegions, fFlags); + } + else + { + crWarning("Blit: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ) +{ + if (!window->pBlitter) + return; + + if (render_spu.blitterTable) + { + const CR_BLITTER_WINDOW * pBltInfo = CrBltMuralGetCurrentInfo(window->pBlitter); + if (pBltInfo && pBltInfo->Base.id == window->BltInfo.Base.id) + { + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + } + } + else + { + CRASSERT(CrBltMuralGetCurrentInfo(window->pBlitter)->Base.id == window->BltInfo.Base.id); + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + CrBltTerm(window->pBlitter); + } + window->pBlitter = NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ) +{ + PCR_BLITTER pBlitter = window->pBlitter; + if (!pBlitter) + { + if (render_spu.blitterTable) + { + crHashtableLock(render_spu.blitterTable); + pBlitter = (PCR_BLITTER)crHashtableSearch(render_spu.blitterTable, window->visual->visAttribs); + } + + if (!pBlitter) + { + int rc; + ContextInfo * pDefaultCtxInfo; + + pBlitter = (PCR_BLITTER)crCalloc(sizeof (*pBlitter)); + if (!pBlitter) + { + crWarning("failed to allocate blitter"); + return NULL; + } + + pDefaultCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pDefaultCtxInfo) + { + crWarning("no default ctx info!"); + crFree(pBlitter); + return NULL; + } + + rc = CrBltInit(pBlitter, &pDefaultCtxInfo->BltInfo, true, true, NULL, &render_spu.blitterDispatch); + + /* we can release it either way, since it will be retained when used as a shared context */ + renderspuDefaultSharedContextRelease(pDefaultCtxInfo); + + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltInit failed, rc %d", rc); + crFree(pBlitter); + return NULL; + } + + if (render_spu.blitterTable) + { + crHashtableAdd( render_spu.blitterTable, window->visual->visAttribs, pBlitter ); + } + } + + if (render_spu.blitterTable) + crHashtableUnlock(render_spu.blitterTable); + + Assert(pBlitter); + window->pBlitter = pBlitter; + } + + CrBltMuralSetCurrentInfo(pBlitter, &window->BltInfo); + return pBlitter; +} + +int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData) +{ + int rc; + + CrBltSetMakeCurrentUserData(pBlitter, i32MakeCurrentUserData); + + rc = CrBltEnter(pBlitter); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltEnter failed, rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw ) +{ + PCR_BLITTER pBlitter = fRedraw ? window->pBlitter : renderspuVBoxPresentBlitterGet(window); + if (pBlitter) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + return pBlitter; + } + } + return NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ) +{ + if (!window->pBlitter) + { + const struct VBOXVR_SCR_COMPOSITOR * pTmpCompositor; + /* just use compositor lock to synchronize */ + pTmpCompositor = renderspuVBoxCompositorAcquire(window); + CRASSERT(pTmpCompositor); + if (pTmpCompositor) + { + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet( window ); + if (pBlitter) + { + if (!CrBltIsEverEntered(pBlitter)) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + CrBltLeave(pBlitter); + } + else + { + crWarning("renderspuVBoxPresentBlitterEnter failed rc %d", rc); + } + } + } + else + { + crWarning("renderspuVBoxPresentBlitterGet failed"); + } + + renderspuVBoxCompositorRelease(window); + } + else + { + crWarning("renderspuVBoxCompositorAcquire failed"); + } + } + return window->pBlitter; +} + +void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData, + bool fRedraw ) +{ + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGetAndEnter(window, i32MakeCurrentUserData, fRedraw); + if (!pBlitter) + return; + + renderspuVBoxCompositorBlit(pCompositor, pBlitter); + + renderspu_SystemSwapBuffers(window, 0); + + CrBltLeave(pBlitter); +} + +GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor) +{ + int rc; + GLboolean fEmpty = pCompositor && CrVrScrCompositorIsEmpty(pCompositor); + GLboolean fNeedPresent; + + /* renderspuVBoxCompositorSet can be invoked from the chromium thread only and is not reentrant, + * no need to synch here + * the lock is actually needed to ensure we're in synch with the redraw thread */ + if (window->pCompositor == pCompositor && !fEmpty) + return !!pCompositor; + + rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + if (!fEmpty) + fNeedPresent = !!pCompositor; + else + { + fNeedPresent = renderspu_SystemWindowNeedEmptyPresent(window); + pCompositor = NULL; + } + + window->pCompositor = !fEmpty ? pCompositor : NULL; + RTCritSectLeave(&window->CompositorLock); + return fNeedPresent; + } + else + { + WARN(("RTCritSectEnter failed rc %d", rc)); + } + + return GL_FALSE; +} + +static void renderspuVBoxCompositorClearAllCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + renderspuVBoxCompositorSet(window, NULL); +} + +void renderspuVBoxCompositorClearAll() +{ + /* we need to clear window compositor, which is not that trivial though, + * since the lock order used in presentation thread is compositor lock() -> hash table lock (aquired for id->window resolution) + * this is why, to prevent potential deadlocks, we use crHashtableWalkUnlocked that does not hold the table lock + * we are can be sure noone will modify the table here since renderspuVBoxCompositorClearAll can be called in the command (hgcm) thread only, + * and the table can be modified from that thread only as well */ + crHashtableWalkUnlocked(render_spu.windowTable, renderspuVBoxCompositorClearAllCB, NULL); +} + +const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + const VBOXVR_SCR_COMPOSITOR * pCompositor = window->pCompositor; + if (pCompositor) + { + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + return pCompositor; + } + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + } + return NULL; +} + +int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + if (ppCompositor) + *ppCompositor = window->pCompositor; + } + else + WARN(("RTCritSectEnter failed %d", rc)); + return rc; +} + +int renderspuVBoxCompositorUnlock(WindowInfo *window) +{ + int rc = RTCritSectLeave(&window->CompositorLock); + AssertRC(rc); + return rc; +} + +int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor) +{ + int rc = RTCritSectTryEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + *ppCompositor = window->pCompositor; + if (*ppCompositor) + { + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + return VINF_SUCCESS; + } + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + rc = VERR_INVALID_STATE; + } + else + { + *ppCompositor = NULL; + } + return rc; +} + +void renderspuVBoxCompositorRelease( WindowInfo *window) +{ + int rc; + Assert(window->pCompositor); + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + rc = RTCritSectLeave(&window->CompositorLock); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectLeave failed rc %d", rc); + } +} + + +/* + * Set the current raster position to the given window coordinate. + */ +static void +SetRasterPos( GLint winX, GLint winY ) +{ + GLfloat fx, fy; + + /* Push current matrix mode and viewport attributes */ + render_spu.self.PushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT ); + + /* Setup projection parameters */ + render_spu.self.MatrixMode( GL_PROJECTION ); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.MatrixMode( GL_MODELVIEW ); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + + render_spu.self.Viewport( winX - 1, winY - 1, 2, 2 ); + + /* set the raster (window) position */ + /* huh ? */ + fx = (GLfloat) (winX - (int) winX); + fy = (GLfloat) (winY - (int) winY); + render_spu.self.RasterPos4f( fx, fy, 0.0, 1.0 ); + + /* restore matrices, viewport and matrix mode */ + render_spu.self.PopMatrix(); + render_spu.self.MatrixMode( GL_PROJECTION ); + render_spu.self.PopMatrix(); + + render_spu.self.PopAttrib(); +} + + +/* + * Draw the mouse pointer bitmap at (x,y) in window coords. + */ +static void DrawCursor( GLint x, GLint y ) +{ +#define POINTER_WIDTH 32 +#define POINTER_HEIGHT 32 + /* Somebody artistic could probably do better here */ + static const char *pointerImage[POINTER_HEIGHT] = + { + "XX..............................", + "XXXX............................", + ".XXXXX..........................", + ".XXXXXXX........................", + "..XXXXXXXX......................", + "..XXXXXXXXXX....................", + "...XXXXXXXXXXX..................", + "...XXXXXXXXXXXXX................", + "....XXXXXXXXXXXXXX..............", + "....XXXXXXXXXXXXXXXX............", + ".....XXXXXXXXXXXXXXXXX..........", + ".....XXXXXXXXXXXXXXXXXXX........", + "......XXXXXXXXXXXXXXXXXXXX......", + "......XXXXXXXXXXXXXXXXXXXXXX....", + ".......XXXXXXXXXXXXXXXXXXXXXXX..", + ".......XXXXXXXXXXXXXXXXXXXXXXXX.", + "........XXXXXXXXXXXXX...........", + "........XXXXXXXX.XXXXX..........", + ".........XXXXXX...XXXXX.........", + ".........XXXXX.....XXXXX........", + "..........XXX.......XXXXX.......", + "..........XX.........XXXXX......", + "......................XXXXX.....", + ".......................XXXXX....", + "........................XXX.....", + ".........................X......", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................" + + }; + static GLubyte pointerBitmap[POINTER_HEIGHT][POINTER_WIDTH / 8]; + static GLboolean firstCall = GL_TRUE; + GLboolean lighting, depthTest, scissorTest; + + if (firstCall) { + /* Convert pointerImage into pointerBitmap */ + GLint i, j; + for (i = 0; i < POINTER_HEIGHT; i++) { + for (j = 0; j < POINTER_WIDTH; j++) { + if (pointerImage[POINTER_HEIGHT - i - 1][j] == 'X') { + GLubyte bit = 128 >> (j & 0x7); + pointerBitmap[i][j / 8] |= bit; + } + } + } + firstCall = GL_FALSE; + } + + render_spu.self.GetBooleanv(GL_LIGHTING, &lighting); + render_spu.self.GetBooleanv(GL_DEPTH_TEST, &depthTest); + render_spu.self.GetBooleanv(GL_SCISSOR_TEST, &scissorTest); + render_spu.self.Disable(GL_LIGHTING); + render_spu.self.Disable(GL_DEPTH_TEST); + render_spu.self.Disable(GL_SCISSOR_TEST); + render_spu.self.PixelStorei(GL_UNPACK_ALIGNMENT, 1); + + render_spu.self.Color3f(1, 1, 1); + + /* save current raster pos */ + render_spu.self.PushAttrib(GL_CURRENT_BIT); + SetRasterPos(x, y); + render_spu.self.Bitmap(POINTER_WIDTH, POINTER_HEIGHT, 1.0, 31.0, 0, 0, + (const GLubyte *) pointerBitmap); + /* restore current raster pos */ + render_spu.self.PopAttrib(); + + if (lighting) + render_spu.self.Enable(GL_LIGHTING); + if (depthTest) + render_spu.self.Enable(GL_DEPTH_TEST); + if (scissorTest) + render_spu.self.Enable(GL_SCISSOR_TEST); +} + +void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags ) +{ + WindowInfo *w = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window); + + if (!w) + { + crDebug("Render SPU: SwapBuffers invalid window id: %d", window); + return; + } + + if (flags & CR_SUPPRESS_SWAP_BIT) + { + render_spu.self.Finish(); + return; + } + + if (render_spu.drawCursor) + DrawCursor( render_spu.cursorX, render_spu.cursorY ); + + if (render_spu.swap_master_url) + DoSync(); + + renderspu_SystemSwapBuffers( w, flags ); +} + + +/* + * Barrier functions + * Normally, we'll have a crserver somewhere that handles the barrier calls. + * However, if we're running the render SPU on the client node, then we + * should handle barriers here. The threadtest demo illustrates this. + * If we have N threads calling using this SPU we need these barrier + * functions to synchronize them. + */ + +static void RENDER_APIENTRY renderspuBarrierCreateCR( GLuint name, GLuint count ) +{ + Barrier *b; + + if (render_spu.ignore_papi) + return; + + b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name ); + if (b) { + /* HACK -- this allows everybody to create a barrier, and all + but the first creation are ignored, assuming the count + match. */ + if ( b->count != count ) { + crError( "Render SPU: Barrier name=%u created with count=%u, but already " + "exists with count=%u", name, count, b->count ); + } + } + else { + b = (Barrier *) crAlloc( sizeof(Barrier) ); + b->count = count; + crInitBarrier( &b->barrier, count ); + crHashtableAdd( render_spu.barrierHash, name, b ); + } +} + +static void RENDER_APIENTRY renderspuBarrierDestroyCR( GLuint name ) +{ + if (render_spu.ignore_papi) + return; + crHashtableDelete( render_spu.barrierHash, name, crFree ); +} + +static void RENDER_APIENTRY renderspuBarrierExecCR( GLuint name ) +{ + Barrier *b; + + if (render_spu.ignore_papi) + return; + + b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name ); + if (b) { + crWaitBarrier( &(b->barrier) ); + } + else { + crWarning("Render SPU: Bad barrier name %d in BarrierExec()", name); + } +} + + +/* + * Semaphore functions + * XXX we should probably implement these too, for the same reason as + * barriers (see above). + */ + +static void RENDER_APIENTRY renderspuSemaphoreCreateCR( GLuint name, GLuint count ) +{ + (void) name; + (void) count; +} + +static void RENDER_APIENTRY renderspuSemaphoreDestroyCR( GLuint name ) +{ + (void) name; +} + +static void RENDER_APIENTRY renderspuSemaphorePCR( GLuint name ) +{ + (void) name; +} + +static void RENDER_APIENTRY renderspuSemaphoreVCR( GLuint name ) +{ + (void) name; +} + + +/* + * Misc functions + */ +void renderspuSetDefaultSharedContext(ContextInfo *pCtx) +{ + if (pCtx == render_spu.defaultSharedContext) + return; + + renderspu_SystemDefaultSharedContextChanged(render_spu.defaultSharedContext, pCtx); + + if (render_spu.defaultSharedContext) + renderspuContextRelease(render_spu.defaultSharedContext); + + if (pCtx) + renderspuContextRetain(pCtx); + render_spu.defaultSharedContext = pCtx; +} + +static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint value) +{ + switch (target) + { + case GL_HH_SET_DEFAULT_SHARED_CTX: + { + ContextInfo * pCtx = NULL; + if (value) + pCtx = (ContextInfo *)crHashtableSearch(render_spu.contextTable, value); + else + crWarning("invalid default shared context id %d", value); + + renderspuSetDefaultSharedContext(pCtx); + break; + } + case GL_HH_RENDERTHREAD_INFORM: + { + if (value) + { + int rc = renderspuDefaultCtxInit(); + if (RT_FAILURE(rc)) + { + WARN(("renderspuDefaultCtxInit failed")); + break; + } + } + else + { + renderspuCleanupBase(false); + } + break; + } + default: +// crWarning("Unhandled target in renderspuChromiumParameteriCR()"); + break; + } +} + +static void RENDER_APIENTRY +renderspuChromiumParameterfCR(GLenum target, GLfloat value) +{ + (void) target; + (void) value; + +#if 0 + switch (target) { + default: + crWarning("Unhandled target in renderspuChromiumParameterfCR()"); + break; + } +#endif +} + +bool renderspuCalloutAvailable() +{ + return render_spu.pfnClientCallout != NULL; +} + +bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb) +{ + if (render_spu.pfnClientCallout) + { + render_spu.pfnClientCallout(pfnCb, pvCb); + return true; + } + return false; +} + +static void RENDER_APIENTRY +renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count, + const GLvoid *values) +{ + int client_num; + unsigned short port; + CRMessage *msg, pingback; + unsigned char *privbuf = NULL; + + switch (target) { + case GL_HH_SET_CLIENT_CALLOUT: + render_spu.pfnClientCallout = (PFNVCRSERVER_CLIENT_CALLOUT)values; + break; + case GL_GATHER_CONNECT_CR: + if (render_spu.gather_userbuf_size) + privbuf = (unsigned char *)crAlloc(1024*768*4); + + port = ((GLint *) values)[0]; + + if (render_spu.gather_conns == NULL) + render_spu.gather_conns = crAlloc(render_spu.server->numClients*sizeof(CRConnection *)); + else + { + crError("Oh bother! duplicate GL_GATHER_CONNECT_CR getting through"); + } + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + { + switch (render_spu.server->clients[client_num]->conn->type) + { + case CR_TCPIP: + crDebug("Render SPU: AcceptClient from %s on %d", + render_spu.server->clients[client_num]->conn->hostname, render_spu.gather_port); + render_spu.gather_conns[client_num] = + crNetAcceptClient("tcpip", NULL, port, 1024*1024, 1); + break; + + case CR_GM: + render_spu.gather_conns[client_num] = + crNetAcceptClient("gm", NULL, port, 1024*1024, 1); + break; + + default: + crError("Render SPU: Unknown Network Type to Open Gather Connection"); + } + + + if (render_spu.gather_userbuf_size) + { + render_spu.gather_conns[client_num]->userbuf = privbuf; + render_spu.gather_conns[client_num]->userbuf_len = render_spu.gather_userbuf_size; + } + else + { + render_spu.gather_conns[client_num]->userbuf = NULL; + render_spu.gather_conns[client_num]->userbuf_len = 0; + } + + if (render_spu.gather_conns[client_num]) + { + crDebug("Render SPU: success! from %s", render_spu.gather_conns[client_num]->hostname); + } + } + + break; + + case GL_GATHER_DRAWPIXELS_CR: + pingback.header.type = CR_MESSAGE_OOB; + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + { + crNetGetMessage(render_spu.gather_conns[client_num], &msg); + if (msg->header.type == CR_MESSAGE_GATHER) + { + crNetFree(render_spu.gather_conns[client_num], msg); + } + else + { + crError("Render SPU: expecting MESSAGE_GATHER. got crap! (%d of %d)", + client_num, render_spu.server->numClients-1); + } + } + + /* + * We're only hitting the case if we're not actually calling + * child.SwapBuffers from readback, so a switch about which + * call to DoSync() we really want [this one, or the one + * in SwapBuffers above] is not necessary -- karl + */ + + if (render_spu.swap_master_url) + DoSync(); + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + crNetSend(render_spu.gather_conns[client_num], NULL, &pingback, + sizeof(CRMessageHeader)); + + render_spu.self.RasterPos2i(((GLint *)values)[0], ((GLint *)values)[1]); + render_spu.self.DrawPixels( ((GLint *)values)[2], ((GLint *)values)[3], + ((GLint *)values)[4], ((GLint *)values)[5], + render_spu.gather_conns[0]->userbuf); + + + render_spu.self.SwapBuffers(((GLint *)values)[6], 0); + break; + + case GL_CURSOR_POSITION_CR: + if (type == GL_INT && count == 2) { + render_spu.cursorX = ((GLint *) values)[0]; + render_spu.cursorY = ((GLint *) values)[1]; + crDebug("Render SPU: GL_CURSOR_POSITION_CR (%d, %d)", render_spu.cursorX, render_spu.cursorY); + } + else { + crWarning("Render SPU: Bad type or count for ChromiumParametervCR(GL_CURSOR_POSITION_CR)"); + } + break; + + case GL_WINDOW_SIZE_CR: + /* XXX this is old code that should be removed. + * NOTE: we can only resize the default (id=CR_RENDER_DEFAULT_WINDOW_ID) window!!! + */ + { + GLint w, h; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + w = ((GLint*)values)[0]; + h = ((GLint*)values)[1]; + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); + if (window) + { + renderspu_SystemWindowSize(window, w, h); + } + } + break; + + case GL_HH_SET_TMPCTX_MAKE_CURRENT: + if (type == GL_BYTE && count == sizeof (void*)) + memcpy(&render_spu.blitterDispatch.MakeCurrent, values, count); + else + WARN(("unexpected type(%#x) - count(%d) pair", type, count)); + break; + + default: +#if 0 + WARN(("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target)); +#endif + break; + } +} + + +static void RENDER_APIENTRY +renderspuGetChromiumParametervCR(GLenum target, GLuint index, GLenum type, + GLsizei count, GLvoid *values) +{ + switch (target) { + case GL_WINDOW_SIZE_CR: + { + GLint x, y, w, h, *size = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + size[0] = size[1] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + size[0] = w; + size[1] = h; + } + } + break; + case GL_WINDOW_POSITION_CR: + /* return window position, as a screen coordinate */ + { + GLint *pos = (GLint *) values; + GLint x, y, w, h; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + pos[0] = pos[1] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + pos[0] = x;/*window->x;*/ + pos[1] = y;/*window->y;*/ + } + } + break; + case GL_MAX_WINDOW_SIZE_CR: + { + GLint *maxSize = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetMaxWindowSize(window, maxSize + 0, maxSize + 1); + } + } + break; + case GL_WINDOW_VISIBILITY_CR: + { + GLint *vis = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 1); + CRASSERT(values); + vis[0] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + vis[0] = window->visible; + } + } + break; + default: + ; /* nothing - silence compiler */ + } +} + + +static void RENDER_APIENTRY +renderspuBoundsInfoCR( CRrecti *bounds, GLbyte *payload, GLint len, + GLint num_opcodes ) +{ + (void) bounds; + (void) payload; + (void) len; + (void) num_opcodes; + /* draw the bounding box */ + if (render_spu.draw_bbox) { + GET_CONTEXT(context); + WindowInfo *window = context->currentWindow; + GLint x, y, w, h; + + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.MatrixMode(GL_PROJECTION); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.Ortho(0, w, 0, h, -1, 1); + render_spu.self.Color3f(1, 1, 1); + render_spu.self.Begin(GL_LINE_LOOP); + render_spu.self.Vertex2i(bounds->x1, bounds->y1); + render_spu.self.Vertex2i(bounds->x2, bounds->y1); + render_spu.self.Vertex2i(bounds->x2, bounds->y2); + render_spu.self.Vertex2i(bounds->x1, bounds->y2); + render_spu.self.End(); + render_spu.self.PopMatrix(); + render_spu.self.MatrixMode(GL_MODELVIEW); + render_spu.self.PopMatrix(); + } +} + + +static void RENDER_APIENTRY +renderspuWriteback( GLint *writeback ) +{ + (void) writeback; +} + + +static void +remove_trailing_space(char *s) +{ + int k = crStrlen(s); + while (k > 0 && s[k-1] == ' ') + k--; + s[k] = 0; +} + +static const GLubyte * RENDER_APIENTRY +renderspuGetString(GLenum pname) +{ + static char tempStr[1000]; + GET_CONTEXT(context); + + if (pname == GL_EXTENSIONS) + { + const char *nativeExt; + char *crExt, *s1, *s2; + + if (!render_spu.ws.glGetString) + return NULL; + + nativeExt = (const char *) render_spu.ws.glGetString(GL_EXTENSIONS); + if (!nativeExt) { + /* maybe called w/out current context. */ + return NULL; + } + + if (!context) + return (const GLubyte *)nativeExt; + + crExt = crStrjoin3(crExtensions, " ", crAppOnlyExtensions); + s1 = crStrIntersect(nativeExt, crExt); + remove_trailing_space(s1); + s2 = crStrjoin3(s1, " ", crChromiumExtensions); + remove_trailing_space(s2); + crFree(crExt); + crFree(s1); + if (context->extensionString) + crFree(context->extensionString); + context->extensionString = s2; + return (const GLubyte *) s2; + } + else if (pname == GL_VENDOR) + return (const GLubyte *) CR_VENDOR; + else if (pname == GL_VERSION) + return render_spu.ws.glGetString(GL_VERSION); + else if (pname == GL_RENDERER) { +#ifdef VBOX + snprintf(tempStr, sizeof(tempStr), "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER)); +#else + sprintf(tempStr, "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER)); +#endif + return (const GLubyte *) tempStr; + } +#ifdef CR_OPENGL_VERSION_2_0 + else if (pname == GL_SHADING_LANGUAGE_VERSION) + return render_spu.ws.glGetString(GL_SHADING_LANGUAGE_VERSION); +#endif +#ifdef GL_CR_real_vendor_strings + else if (pname == GL_REAL_VENDOR) + return render_spu.ws.glGetString(GL_VENDOR); + else if (pname == GL_REAL_VERSION) + return render_spu.ws.glGetString(GL_VERSION); + else if (pname == GL_REAL_RENDERER) + return render_spu.ws.glGetString(GL_RENDERER); + else if (pname == GL_REAL_EXTENSIONS) + return render_spu.ws.glGetString(GL_EXTENSIONS); +#endif + else + return NULL; +} + +static void renderspuReparentWindowCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *pWindow = (WindowInfo *)data1; + + renderspu_SystemReparentWindow(pWindow); +} + +DECLEXPORT(void) renderspuReparentWindow(GLint window) +{ + WindowInfo *pWindow; + CRASSERT(window >= 0); + + pWindow = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window); + + if (!pWindow) + { + crDebug("Render SPU: Attempt to reparent invalid window (%d)", window); + return; + } + + renderspu_SystemReparentWindow(pWindow); + + /* special case: reparent all internal windows as well */ + if (window == CR_RENDER_DEFAULT_WINDOW_ID) + { + crHashtableWalk(render_spu.dummyWindowTable, renderspuReparentWindowCB, NULL); + } +} + +DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable) +{ + render_spu.fUnscaledHiDPI = fEnable; +} + +#define FILLIN( NAME, FUNC ) \ + table[i].name = crStrdup(NAME); \ + table[i].fn = (SPUGenericFunction) FUNC; \ + i++; + + +/* These are the functions which the render SPU implements, not OpenGL. + */ +int +renderspuCreateFunctions(SPUNamedFunctionTable table[]) +{ + int i = 0; + FILLIN( "SwapBuffers", renderspuSwapBuffers ); + FILLIN( "CreateContext", renderspuCreateContext ); + FILLIN( "DestroyContext", renderspuDestroyContext ); + FILLIN( "MakeCurrent", renderspuMakeCurrent ); + FILLIN( "WindowCreate", renderspuWindowCreate ); + FILLIN( "WindowDestroy", renderspuWindowDestroy ); + FILLIN( "WindowSize", renderspuWindowSize ); + FILLIN( "WindowPosition", renderspuWindowPosition ); + FILLIN( "WindowVisibleRegion", renderspuWindowVisibleRegion ); + FILLIN( "WindowShow", renderspuWindowShow ); + FILLIN( "BarrierCreateCR", renderspuBarrierCreateCR ); + FILLIN( "BarrierDestroyCR", renderspuBarrierDestroyCR ); + FILLIN( "BarrierExecCR", renderspuBarrierExecCR ); + FILLIN( "BoundsInfoCR", renderspuBoundsInfoCR ); + FILLIN( "SemaphoreCreateCR", renderspuSemaphoreCreateCR ); + FILLIN( "SemaphoreDestroyCR", renderspuSemaphoreDestroyCR ); + FILLIN( "SemaphorePCR", renderspuSemaphorePCR ); + FILLIN( "SemaphoreVCR", renderspuSemaphoreVCR ); + FILLIN( "Writeback", renderspuWriteback ); + FILLIN( "ChromiumParameteriCR", renderspuChromiumParameteriCR ); + FILLIN( "ChromiumParameterfCR", renderspuChromiumParameterfCR ); + FILLIN( "ChromiumParametervCR", renderspuChromiumParametervCR ); + FILLIN( "GetChromiumParametervCR", renderspuGetChromiumParametervCR ); + FILLIN( "GetString", renderspuGetString ); + FILLIN( "VBoxPresentComposition", renderspuVBoxPresentComposition ); + return i; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h new file mode 100644 index 00000000..dfd591e8 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h @@ -0,0 +1,506 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifndef CR_RENDERSPU_H +#define CR_RENDERSPU_H + +#ifdef WINDOWS +#define WIN32_LEAN_AND_MEAN +#include <iprt/win/windows.h> +#define RENDER_APIENTRY __stdcall +#define snprintf _snprintf +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT +# include <AGL/AGL.h> +# else +# include "renderspu_cocoa_helper.h" +# endif +#define RENDER_APIENTRY +#else +#include <GL/glx.h> +#define RENDER_APIENTRY +#endif +#include "cr_threads.h" +#include "cr_spu.h" +#include "cr_hash.h" +#include "cr_server.h" +#include "cr_blitter.h" +#include "cr_compositor.h" + +#include <iprt/cdefs.h> +#include <iprt/critsect.h> +#if defined(GLX) /* @todo: unify windows and glx thread creation code */ +#include <iprt/thread.h> +#include <iprt/semaphore.h> + +/* special window id used for representing the command window CRWindowInfo */ +#define CR_RENDER_WINCMD_ID (INT32_MAX-2) +AssertCompile(CR_RENDER_WINCMD_ID != CR_RENDER_DEFAULT_WINDOW_ID); +/* CRHashTable is using unsigned long keys, we use it to trore X Window -> CRWindowInfo association */ +AssertCompile(sizeof (Window) == sizeof (unsigned long)); +#endif + + +#define MAX_VISUALS 32 + +#ifdef RT_OS_DARWIN +# ifndef VBOX_WITH_COCOA_QT +enum +{ + /* Event classes */ + kEventClassVBox = 'vbox', + /* Event kinds */ + kEventVBoxShowWindow = 'swin', + kEventVBoxHideWindow = 'hwin', + kEventVBoxMoveWindow = 'mwin', + kEventVBoxResizeWindow = 'rwin', + kEventVBoxDisposeWindow = 'dwin', + kEventVBoxUpdateDock = 'udck', + kEventVBoxUpdateContext = 'uctx', + kEventVBoxBoundsChanged = 'bchg' +}; +pascal OSStatus windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData); +# endif +#endif /* RT_OS_DARWIN */ + +/** + * Visual info + */ +typedef struct { + GLbitfield visAttribs; + const char *displayName; +#if defined(WINDOWS) +// HDC device_context; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + WindowRef window; +# endif +#elif defined(GLX) + Display *dpy; + XVisualInfo *visual; +#ifdef GLX_VERSION_1_3 + GLXFBConfig fbconfig; +#endif /* GLX_VERSION_1_3 */ +#endif +} VisualInfo; + +/** + * Window info + */ +typedef struct WindowInfo { + int x, y; +// int width, height; +// int id; /**< integer window ID */ + CR_BLITTER_WINDOW BltInfo; + + VisualInfo *visual; + + volatile uint32_t cRefs; + + GLboolean mapPending; + GLboolean visible; + GLboolean everCurrent; /**< has this window ever been bound? */ + char *title; + + const VBOXVR_SCR_COMPOSITOR *pCompositor; + /* the composotor lock is used to synchronize the current compositor access, + * i.e. the compositor can be accessed by a gui refraw thread, + * while chromium thread might try to set a new compositor + * note that the compositor internally has its own lock to be used for accessing its data + * see CrVrScrCompositorLock/Unlock; renderspu and crserverlib would use it for compositor data access */ + RTCRITSECT CompositorLock; + PCR_BLITTER pBlitter; +#if defined(WINDOWS) + HDC nativeWindow; /**< for render_to_app_window */ + HWND hWnd; + HDC device_context; + HDC redraw_device_context; + HRGN hRgn; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + WindowRef window; + WindowRef nativeWindow; /**< for render_to_app_window */ + WindowRef appWindow; + EventHandlerUPP event_handler; + GLint bufferName; + AGLContext dummyContext; + RgnHandle hVisibleRegion; + /* unsigned long context_ptr; */ +# else + NativeNSViewRef window; + NativeNSViewRef nativeWindow; /**< for render_to_app_window */ + NativeNSOpenGLContextRef *currentCtx; +# endif +#elif defined(GLX) + Window window; + Window nativeWindow; /**< for render_to_app_window */ + Window appWindow; /**< Same as nativeWindow but for garbage collections purposes */ +#endif + int nvSwapGroup; + +#ifdef USE_OSMESA + GLubyte *buffer; /**< for rendering to off screen buffer. */ + int in_buffer_width; + int in_buffer_height; +#endif + +} WindowInfo; + +/** + * Context Info + */ +typedef struct _ContextInfo { +// int id; /**< integer context ID */ + CR_BLITTER_CONTEXT BltInfo; + VisualInfo *visual; + GLboolean everCurrent; + GLboolean haveWindowPosARB; + WindowInfo *currentWindow; +#if defined(WINDOWS) + HGLRC hRC; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + AGLContext context; +# else + NativeNSOpenGLContextRef context; +# endif +#elif defined(GLX) + GLXContext context; +#endif + struct _ContextInfo *shared; + char *extensionString; + volatile uint32_t cRefs; +} ContextInfo; + +/** + * Barrier info + */ +typedef struct { + CRbarrier barrier; + GLuint count; +} Barrier; + +#ifdef GLX +typedef enum +{ + CR_RENDER_WINCMD_TYPE_UNDEFINED = 0, + /* create the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_CREATE, + /* destroy the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_DESTROY, + /* notify the WinCmd thread about window creation */ + CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, + /* notify the WinCmd thread about window destroy */ + CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, + /* nop used to synchronize with the WinCmd thread */ + CR_RENDER_WINCMD_TYPE_NOP, + /* exit Win Cmd thread */ + CR_RENDER_WINCMD_TYPE_EXIT, +} CR_RENDER_WINCMD_TYPE; + +typedef struct CR_RENDER_WINCMD +{ + /* command type */ + CR_RENDER_WINCMD_TYPE enmCmd; + /* command result */ + int rc; + /* valid for WIN_CREATE & WIN_DESTROY only */ + WindowInfo *pWindow; +} CR_RENDER_WINCMD, *PCR_RENDER_WINCMD; +#endif + +#ifdef RT_OS_DARWIN +typedef void (*PFNDELETE_OBJECT)(GLhandleARB obj); +typedef void (*PFNGET_ATTACHED_OBJECTS)( GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj ); +typedef GLhandleARB (*PFNGET_HANDLE)(GLenum pname); +typedef void (*PFNGET_INFO_LOG)( GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ); +typedef void (*PFNGET_OBJECT_PARAMETERFV)( GLhandleARB obj, GLenum pname, GLfloat * params ); +typedef void (*PFNGET_OBJECT_PARAMETERIV)( GLhandleARB obj, GLenum pname, GLint * params ); +#endif + +typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT_CB)(void *pvCb); +typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT)(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb); + + +/** + * Renderspu state info + */ +typedef struct { + SPUDispatchTable self; + int id; + + /** config options */ + /*@{*/ + char *window_title; + int defaultX, defaultY; + unsigned int defaultWidth, defaultHeight; + int default_visual; + int use_L2; + int fullscreen, ontop; + char display_string[100]; +#if defined(GLX) + int try_direct; + int force_direct; + int sync; +#endif + int force_present_main_thread; + int render_to_app_window; + int render_to_crut_window; + int crut_drawable; + int resizable; + int use_lut8, lut8[3][256]; + int borderless; + int nvSwapGroup; + int ignore_papi; + int ignore_window_moves; + int pbufferWidth, pbufferHeight; + int use_glxchoosevisual; + int draw_bbox; + /*@}*/ + + CRServer *server; + int gather_port; + int gather_userbuf_size; + CRConnection **gather_conns; + + GLint drawCursor; + GLint cursorX, cursorY; + + int numVisuals; + VisualInfo visuals[MAX_VISUALS]; + + CRHashTable *windowTable; + CRHashTable *contextTable; + + CRHashTable *dummyWindowTable; + + ContextInfo *defaultSharedContext; + +#ifndef CHROMIUM_THREADSAFE + ContextInfo *currentContext; +#endif + + crOpenGLInterface ws; /**< Window System interface */ + + CRHashTable *barrierHash; + + int is_swap_master, num_swap_clients; + int swap_mtu; + char *swap_master_url; + CRConnection **swap_conns; + + SPUDispatchTable blitterDispatch; + CRHashTable *blitterTable; + + PFNVCRSERVER_CLIENT_CALLOUT pfnClientCallout; + +#ifdef USE_OSMESA + /** Off screen rendering hooks. */ + int use_osmesa; + + OSMesaContext (*OSMesaCreateContext)( GLenum format, OSMesaContext sharelist ); + GLboolean (* OSMesaMakeCurrent)( OSMesaContext ctx, + GLubyte *buffer, + GLenum type, + GLsizei width, + GLsizei height ); + void (*OSMesaDestroyContext)( OSMesaContext ctx ); +#endif + +#if defined(GLX) + RTTHREAD hWinCmdThread; + VisualInfo WinCmdVisual; + WindowInfo WinCmdWindow; + RTSEMEVENT hWinCmdCompleteEvent; + /* display connection used to send data to the WinCmd thread */ + Display *pCommunicationDisplay; + Atom WinCmdAtom; + /* X Window -> CRWindowInfo table */ + CRHashTable *pWinToInfoTable; +#endif + +#ifdef RT_OS_WINDOWS + DWORD dwWinThreadId; + HANDLE hWinThreadReadyEvent; +#endif + +#ifdef RT_OS_DARWIN +# ifdef VBOX_WITH_COCOA_QT + PFNDELETE_OBJECT pfnDeleteObject; + PFNGET_ATTACHED_OBJECTS pfnGetAttachedObjects; + PFNGET_HANDLE pfnGetHandle; + PFNGET_INFO_LOG pfnGetInfoLog; + PFNGET_OBJECT_PARAMETERFV pfnGetObjectParameterfv; + PFNGET_OBJECT_PARAMETERIV pfnGetObjectParameteriv; + + CR_GLSL_CACHE GlobalShaders; +# else + RgnHandle hRootVisibleRegion; + RTSEMFASTMUTEX syncMutex; + EventHandlerUPP hParentEventHandler; + WindowGroupRef pParentGroup; + WindowGroupRef pMasterGroup; + GLint currentBufferName; + uint64_t uiDockUpdateTS; + bool fInit; +# endif +#endif /* RT_OS_DARWIN */ + /* If TRUE, render should tell window server to prevent artificial content + * up-scaling when displayed on HiDPI monitor. */ + bool fUnscaledHiDPI; +} RenderSPU; + +#ifdef RT_OS_WINDOWS + +/* Asks window thread to create new window. + msg.lParam - holds pointer to CREATESTRUCT structure + note that lpCreateParams is used to specify address to store handle of created window + msg.wParam - unused, should be NULL +*/ +#define WM_VBOX_RENDERSPU_CREATE_WINDOW (WM_APP+1) + +typedef struct _VBOX_RENDERSPU_DESTROY_WINDOW { + HWND hWnd; /* handle to window to destroy */ +} VBOX_RENDERSPU_DESTROY_WINDOW; + +/* Asks window thread to destroy previously created window. + msg.lParam - holds pointer to RENDERSPU_VBOX_WINDOW_DESTROY structure + msg.wParam - unused, should be NULL +*/ +#define WM_VBOX_RENDERSPU_DESTROY_WINDOW (WM_APP+2) + +#endif + +extern RenderSPU render_spu; + +/* @todo remove this hack */ +extern uint64_t render_spu_parent_window_id; + +#ifdef CHROMIUM_THREADSAFE +extern CRtsd _RenderTSD; +#define GET_CONTEXT_VAL() ((ContextInfo *) crGetTSD(&_RenderTSD)) +#define SET_CONTEXT_VAL(_v) do { \ + crSetTSD(&_RenderTSD, (_v)); \ + } while (0) +#else +#define GET_CONTEXT_VAL() (render_spu.currentContext) +#define SET_CONTEXT_VAL(_v) do { \ + render_spu.currentContext = (_v); \ + } while (0) + +#endif + +#define GET_CONTEXT(T) ContextInfo *T = GET_CONTEXT_VAL() + + +extern void renderspuSetDefaultSharedContext(ContextInfo *pCtx); +extern void renderspuSetVBoxConfiguration( RenderSPU *spu ); +extern void renderspuMakeVisString( GLbitfield visAttribs, char *s ); +extern VisualInfo *renderspuFindVisual(const char *displayName, GLbitfield visAttribs ); +extern GLboolean renderspu_SystemInitVisual( VisualInfo *visual ); +extern GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ); +extern void renderspu_SystemDestroyContext( ContextInfo *context ); +extern GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ); +extern GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ); +extern void renderspu_SystemDestroyWindow( WindowInfo *window ); +extern void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ); +extern void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h ); +extern void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ); +extern void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ); +extern void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects); +extern GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window); +extern int renderspu_SystemInit(); +extern int renderspu_SystemTerm(); +extern void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext); +extern void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ); +extern void renderspu_SystemMakeCurrent( WindowInfo *window, GLint windowInfor, ContextInfo *context ); +extern void renderspu_SystemSwapBuffers( WindowInfo *window, GLint flags ); +extern void renderspu_SystemReparentWindow(WindowInfo *window); +extern void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ); +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable); +extern void renderspu_GCWindow(void); +extern int renderspuCreateFunctions( SPUNamedFunctionTable table[] ); +extern GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor); +extern void renderspuVBoxCompositorClearAll(); +extern int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor); +extern int renderspuVBoxCompositorUnlock(WindowInfo *window); +extern const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window); +extern int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor); +extern void renderspuVBoxCompositorRelease( WindowInfo *window); +extern void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData, + bool fRedraw); +extern PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ); +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ); +extern int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData ); +extern PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw ); +extern PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ); +WindowInfo* renderspuWinCreate(GLint visBits, GLint id); +void renderspuWinTermOnShutdown(WindowInfo *window); +void renderspuWinTerm( WindowInfo *window ); +void renderspuWinCleanup(WindowInfo *window); +void renderspuWinDestroy(WindowInfo *window); +GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ); +GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id); + +DECLINLINE(void) renderspuWinRetain(WindowInfo *window) +{ + ASMAtomicIncU32(&window->cRefs); +} + +DECLINLINE(bool) renderspuWinIsTermed(WindowInfo *window) +{ + return window->BltInfo.Base.id < 0; +} + +DECLINLINE(void) renderspuWinRelease(WindowInfo *window) +{ + uint32_t cRefs = ASMAtomicDecU32(&window->cRefs); + if (!cRefs) + { + renderspuWinDestroy(window); + } +} + +extern WindowInfo* renderspuGetDummyWindow(GLint visBits); +extern void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context); +extern GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs); +extern void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter); +extern void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY); +extern GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx); +extern GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ); + +extern GLint RENDER_APIENTRY renderspuWindowCreate( const char *dpyName, GLint visBits ); +void RENDER_APIENTRY renderspuWindowDestroy( GLint win ); +extern GLint RENDER_APIENTRY renderspuCreateContext( const char *dpyname, GLint visBits, GLint shareCtx ); +extern void RENDER_APIENTRY renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx); +extern void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags ); + +extern uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ); + +int renderspuDefaultCtxInit(); +void renderspuCleanupBase(bool fDeleteTables); + +ContextInfo * renderspuDefaultSharedContextAcquire(); +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx); +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); + +bool renderspuCalloutAvailable(); +bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb); + + +#ifdef __cplusplus +extern "C" { +#endif +DECLEXPORT(void) renderspuSetWindowId(uint64_t winId); +DECLEXPORT(void) renderspuReparentWindow(GLint window); +DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable); +#ifdef __cplusplus +} +#endif + +#endif /* CR_RENDERSPU_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c new file mode 100644 index 00000000..de55160e --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c @@ -0,0 +1,907 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include <Carbon/Carbon.h> +#include <AGL/agl.h> +#include <OpenGL/OpenGL.h> + +#include <iprt/time.h> +#include <iprt/assert.h> +#include <iprt/semaphore.h> + +#include <stdio.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "renderspu.h" + +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ +# define renderspuSetWindowContext(w, c) \ + AssertFailed() +# define renderspuGetWindowContext(w) \ + ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) ) +#else +# define renderspuSetWindowContext(w, c) \ + ( SetWRefCon( (w), (unsigned long) (c) ) ) +# define renderspuGetWindowContext(w) \ + ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) ) +#endif + +/* Debug macros */ +#ifdef DEBUG_poetzsch +#define DEBUG_MSG_POETZSCH(text) \ + printf text +#else +#define DEBUG_MSG_POETZSCH(text) \ + do {} while (0) +#endif + +#define DEBUG_MSG_RESULT(result, text) \ + crDebug(text" (%d; %s:%d)", (int)(result), __FILE__, __LINE__) + +#define CHECK_CARBON_RC(result, text) \ + if((result) != noErr) \ + DEBUG_MSG_RESULT(result, text); + +#define CHECK_CARBON_RC_RETURN(result, text, ret) \ + if((result) != noErr) \ + { \ + DEBUG_MSG_RESULT(result, text); \ + return ret; \ + } + +#define CHECK_CARBON_RC_RETURN_VOID(result, text) \ + CHECK_CARBON_RC_RETURN(result, text,) + +#define CHECK_AGL_RC(result, text) \ + if(!(result)) \ + { \ + GLenum error = render_spu.ws.aglGetError(); \ + DEBUG_MSG_RESULT(result, text); \ + } + +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window); +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects); + +/* In some case (like compiz which doesn't provide us with clipping regions) we + * have to make sure that *all* open OpenGL windows are clipped to the main + * application window. This is done here when called from the event handler + * which monitor bounding changes of the main window. */ +static void crClipRootHelper(unsigned long key, void *data1, void *data2) +{ + /* The window with id zero is the base window, which isn't displayed at + * all. So ignore it. */ + if (key > 0) + { + /* Fetch the actually window info & the user data */ + WindowInfo *pWin = (WindowInfo *) data1; + /* We need to assign the context with this window */ + ContextInfo *context = renderspuGetWindowContext(pWin); + if (context && + context->context) + { + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(context->context); + CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): SetCurrentContext Failed"); + if (result) + { + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): UpdateContext Failed"); + /* Update the clipping region */ + renderspu_SystemWindowApplyVisibleRegion(pWin); + } + RTSemFastMutexRelease(render_spu.syncMutex); + /* Make sure that the position is updated relative to the Qt main + * view */ + renderspu_SystemWindowPosition(pWin, pWin->x, pWin->y); + } + } +} + +/* Window event handler */ +pascal OSStatus +windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData) +{ + WindowRef window = NULL; + OSStatus eventResult = eventNotHandledErr; + UInt32 class = GetEventClass (event); + UInt32 kind = GetEventKind (event); + + /* If we aren't initialized or even deinitialized already (as on VM + * shutdown) do nothing. */ + if (!render_spu.fInit) + return eventNotHandledErr; + + /* Fetch the sender of the event */ + GetEventParameter(event, kEventParamDirectObject, typeWindowRef, + NULL, sizeof(WindowRef), NULL, &window); + switch (class) + { + case kEventClassVBox: + { + switch (kind) + { + case kEventVBoxUpdateContext: + { +#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */ + WindowInfo *wi1; + GetEventParameter(event, kEventParamUserData, typeVoidPtr, + NULL, sizeof(wi1), NULL, &wi1); + ContextInfo *context = renderspuGetWindowContext(wi1); + if (context && + context->context) + { + AGLContext tmpContext = render_spu.ws.aglGetCurrentContext(); + DEBUG_MSG_POETZSCH (("kEventVBoxUpdateContext %x %x\n", wi1, context->context)); + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(context->context); + if (result) + { + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed"); + renderspu_SystemWindowApplyVisibleRegion(wi1); + /* Reapply the last active context */ + if (tmpContext) + { + result = render_spu.ws.aglSetCurrentContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed"); + if (result) + { + result = render_spu.ws.aglUpdateContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed"); + } + } + } + RTSemFastMutexRelease(render_spu.syncMutex); + } + eventResult = noErr; +#endif + break; + } + case kEventVBoxBoundsChanged: + { +#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */ + HIPoint p; + GetEventParameter(event, kEventParamOrigin, typeHIPoint, + NULL, sizeof(p), NULL, &p); + HISize s; + GetEventParameter(event, kEventParamDimensions, typeHISize, + NULL, sizeof(s), NULL, &s); + HIRect r = CGRectMake (0, 0, s.width, s.height); + DEBUG_MSG_POETZSCH (("kEventVBoxBoundsChanged %f %f %f %f\n", p.x, p.y, s.width, s.height)); + GLint l[4] = { 0, + 0, + r.size.width, + r.size.height }; + /* Update the root window clip region */ + renderspu_SystemSetRootVisibleRegion(1, l); + /* Temporary save the current active context */ + AGLContext tmpContext = render_spu.ws.aglGetCurrentContext(); + crHashtableWalk(render_spu.windowTable, crClipRootHelper, NULL); + /* Reapply the last active context */ + if (tmpContext) + { + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed"); + /* Doesn't work with DirectX; Anyway doesn't */ +/* if (result)*/ +/* {*/ +/* result = render_spu.ws.aglUpdateContext(tmpContext);*/ +/* CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed");*/ +/* }*/ + RTSemFastMutexRelease(render_spu.syncMutex); + } + eventResult = noErr; +#endif + break; + } + }; + break; + } + break; + }; + + return eventResult; +} + +GLboolean +renderspu_SystemInitVisual(VisualInfo *visual) +{ + if(visual->visAttribs & CR_PBUFFER_BIT) + crWarning("Render SPU (renderspu_SystemInitVisual): PBuffers not support on Darwin/AGL yet."); + + return GL_TRUE; +} + +GLboolean +renderspuChoosePixelFormat(ContextInfo *context, AGLPixelFormat *pix) +{ + GLbitfield visAttribs = context->visual->visAttribs; + GLint attribs[32]; + GLint ind = 0; + +#define ATTR_ADD(s) ( attribs[ind++] = (s) ) +#define ATTR_ADDV(s,v) ( ATTR_ADD((s)), ATTR_ADD((v)) ) + + CRASSERT(render_spu.ws.aglChoosePixelFormat); + + ATTR_ADD(AGL_RGBA); +/* ATTR_ADDV(AGL_RED_SIZE, 1); + ATTR_ADDV(AGL_GREEN_SIZE, 1); + ATTR_ADDV(AGL_BLUE_SIZE, 1); */ + +/* if( render_spu.fullscreen )*/ +/* ATTR_ADD(AGL_FULLSCREEN);*/ + + if( visAttribs & CR_ALPHA_BIT ) + ATTR_ADDV(AGL_ALPHA_SIZE, 1); + + if( visAttribs & CR_DOUBLE_BIT ) + ATTR_ADD(AGL_DOUBLEBUFFER); + + if( visAttribs & CR_STEREO_BIT ) + ATTR_ADD(AGL_STEREO); + + if( visAttribs & CR_DEPTH_BIT ) + ATTR_ADDV(AGL_DEPTH_SIZE, 1); + + if( visAttribs & CR_STENCIL_BIT ) + ATTR_ADDV(AGL_STENCIL_SIZE, 1); + + if( visAttribs & CR_ACCUM_BIT ) { + ATTR_ADDV(AGL_ACCUM_RED_SIZE, 1); + ATTR_ADDV(AGL_ACCUM_GREEN_SIZE, 1); + ATTR_ADDV(AGL_ACCUM_BLUE_SIZE, 1); + if( visAttribs & CR_ALPHA_BIT ) + ATTR_ADDV(AGL_ACCUM_ALPHA_SIZE, 1); + } + + if( visAttribs & CR_MULTISAMPLE_BIT ) { + ATTR_ADDV(AGL_SAMPLE_BUFFERS_ARB, 1); + ATTR_ADDV(AGL_SAMPLES_ARB, 4); + } + + if( visAttribs & CR_OVERLAY_BIT ) + ATTR_ADDV(AGL_LEVEL, 1); + + ATTR_ADD(AGL_NONE); + + *pix = render_spu.ws.aglChoosePixelFormat( NULL, 0, attribs ); + + return (*pix != NULL); +} + +void +renderspuDestroyPixelFormat(ContextInfo *context, AGLPixelFormat *pix) +{ + render_spu.ws.aglDestroyPixelFormat( *pix ); + *pix = NULL; +} + +GLboolean +renderspu_SystemCreateContext(VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext) +{ + AGLPixelFormat pix; + + (void) sharedContext; + CRASSERT(visual); + CRASSERT(context); + + context->visual = visual; + + if( !renderspuChoosePixelFormat(context, &pix) ) { + crError( "Render SPU (renderspu_SystemCreateContext): Unable to create pixel format" ); + return GL_FALSE; + } + + context->context = render_spu.ws.aglCreateContext( pix, NULL ); + renderspuDestroyPixelFormat( context, &pix ); + + if( !context->context ) { + crError( "Render SPU (renderspu_SystemCreateContext): Could not create rendering context" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +void +renderspu_SystemDestroyContext(ContextInfo *context) +{ + if(!context) + return; + + render_spu.ws.aglSetDrawable(context->context, NULL); + render_spu.ws.aglSetCurrentContext(NULL); + if(context->context) + { + render_spu.ws.aglDestroyContext(context->context); + context->context = NULL; + } + + context->visual = NULL; +} + +void +renderspuFullscreen(WindowInfo *window, GLboolean fullscreen) +{ + /* Real fullscreen isn't supported by VirtualBox */ +} + +GLboolean +renderspuWindowAttachContext(WindowInfo *wi, WindowRef window, + ContextInfo *context) +{ + GLboolean result; + + if(!context || !wi) + return render_spu.ws.aglSetCurrentContext( NULL ); + +/* DEBUG_MSG_POETZSCH (("WindowAttachContext %d\n", wi->BltInfo.Base.id));*/ + + /* Flush old context first */ + if (context->currentWindow->window != window) + render_spu.self.Flush(); + /* If the window buffer name is uninitialized we have to create a new + * dummy context. */ + if (wi->bufferName == -1) + { + DEBUG_MSG_POETZSCH (("WindowAttachContext: create context %d\n", wi->BltInfo.Base.id)); + /* Use the same visual bits as those in the context structure */ + AGLPixelFormat pix; + if( !renderspuChoosePixelFormat(context, &pix) ) + { + crError( "Render SPU (renderspuWindowAttachContext): Unable to create pixel format" ); + return GL_FALSE; + } + /* Create the dummy context */ + wi->dummyContext = render_spu.ws.aglCreateContext( pix, NULL ); + renderspuDestroyPixelFormat( context, &pix ); + if( !wi->dummyContext ) + { + crError( "Render SPU (renderspuWindowAttachContext): Could not create rendering context" ); + return GL_FALSE; + } + AGLDrawable drawable; +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + drawable = NULL; +#else + drawable = (AGLDrawable) GetWindowPort(window); +#endif + /* New global buffer name */ + wi->bufferName = render_spu.currentBufferName++; + /* Set the new buffer name to the dummy context. This enable the + * sharing of the same hardware buffer afterwards. */ + result = render_spu.ws.aglSetInteger(wi->dummyContext, AGL_BUFFER_NAME, &wi->bufferName); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed"); + /* Assign the dummy context to the window */ + result = render_spu.ws.aglSetDrawable(wi->dummyContext, drawable); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + } + + AGLDrawable oldDrawable; + AGLDrawable newDrawable; + + oldDrawable = render_spu.ws.aglGetDrawable(context->context); +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + newDrawable = oldDrawable; +#else + newDrawable = (AGLDrawable) GetWindowPort(window); +#endif + RTSemFastMutexRequest(render_spu.syncMutex); + /* Only switch the context if the drawable has changed */ + if (oldDrawable != newDrawable) + { + /* Reset the current context */ + result = render_spu.ws.aglSetDrawable(context->context, NULL); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + /* Set the buffer name of the dummy context to the current context + * also. After that both share the same hardware buffer. */ + render_spu.ws.aglSetInteger (context->context, AGL_BUFFER_NAME, &wi->bufferName); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed"); + /* Set the new drawable */ +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + result = -1; +#else + result = render_spu.ws.aglSetDrawable(context->context, newDrawable); +#endif + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + renderspuSetWindowContext(window, context); + } + result = render_spu.ws.aglSetCurrentContext(context->context); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetCurrentContext Failed"); + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): UpdateContext Failed"); + RTSemFastMutexRelease(render_spu.syncMutex); + + return result; +} + +GLboolean +renderspu_SystemCreateWindow(VisualInfo *visual, GLboolean showIt, + WindowInfo *window) +{ + return GL_TRUE; +} + +void renderspu_SystemReparentWindow(WindowInfo *) +{ + /* stub only */ +} + +void +renderspu_SystemDestroyWindow(WindowInfo *window) +{ + CRASSERT(window); + CRASSERT(window->visual); + + if(!window->nativeWindow) + { + EventRef evt; + OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): PostEventToQueue Failed"); + } + + /* Delete the dummy context */ + if(window->dummyContext) + { + render_spu.ws.aglSetDrawable(window->dummyContext, NULL); + render_spu.ws.aglDestroyContext(window->dummyContext); + window->dummyContext = NULL; + } + + /* Reset some values */ + window->bufferName = -1; + window->visual = NULL; + window->window = NULL; + + if (window->hVisibleRegion) + { + DisposeRgn(window->hVisibleRegion); + window->hVisibleRegion = 0; + } +} + +void +renderspu_SystemWindowPosition(WindowInfo *window, + GLint x, GLint y) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxMoveWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + HIPoint p = CGPointMake (x, y); + status = SetEventParameter(evt, kEventParamOrigin, typeHIPoint, sizeof (p), &p); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): PostEventToQueue Failed"); + + /* save the new pos */ + window->x = x; + window->y = y; +} + +void +renderspu_SystemWindowSize(WindowInfo *window, GLint w, GLint h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxResizeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): CreateEvent Failed "); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + HISize s = CGSizeMake (w, h); + status = SetEventParameter(evt, kEventParamDimensions, typeHISize, sizeof (s), &s); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SendEventToEventTarget Failed"); + + DEBUG_MSG_POETZSCH (("Size %d visible %d\n", window->BltInfo.Base.id, IsWindowVisible (window->window))); + /* save the new size */ + window->BltInfo.width = w; + window->BltInfo.height = h; +} + +void +renderspu_SystemGetWindowGeometry(WindowInfo *window, + GLint *x, GLint *y, + GLint *w, GLint *h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + Rect r; + status = GetWindowBounds(window->window, kWindowStructureRgn, &r); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetWindowGeometry): GetWindowBounds Failed"); + + *x = (int) r.left; + *y = (int) r.top; + *w = (int) (r.right - r.left); + *h = (int) (r.bottom - r.top); +} + +void +renderspu_SystemGetMaxWindowSize(WindowInfo *window, + GLint *w, GLint *h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + HISize s; +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + status = -1; +#else + status = GetWindowResizeLimits (window->window, NULL, &s); +#endif + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetMaxWindowSize): GetWindowResizeLimits Failed"); + + *w = s.width; + *h = s.height; +} + +/* Either show or hide the render SPU's window. */ +void +renderspu_SystemShowWindow(WindowInfo *window, GLboolean showIt) +{ + CRASSERT(window); + CRASSERT(window->window); + + if (!IsValidWindowPtr(window->window)) + return; + + if(showIt) + { + /* Force moving the win to the right position before we show it */ + renderspu_SystemWindowPosition (window, window->x, window->y); + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon + * aren't thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxShowWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowShow): SetEventParameter Failed"); + //status = SendEventToEventTarget (evt, GetWindowEventTarget (HIViewGetWindow ((HIViewRef)render_spu_parent_window_id))); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed"); + } + else + { + EventRef evt; + OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxHideWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed"); + } +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); +} + +void +renderspu_SystemMakeCurrent(WindowInfo *window, GLint nativeWindow, + ContextInfo *context) +{ + Boolean result; +/* DEBUG_MSG_POETZSCH (("makecurrent %d: \n", window->BltInfo.Base.id));*/ + + CRASSERT(render_spu.ws.aglSetCurrentContext); + //crDebug( "renderspu_SystemMakeCurrent( %x, %i, %x )", window, nativeWindow, context ); + + nativeWindow = 0; + + if(window && context) + { + CRASSERT(window->window); + CRASSERT(context->context); + + if(window->visual != context->visual) + { + crDebug("Render SPU (renderspu_SystemMakeCurrent): MakeCurrent visual mismatch (0x%x != 0x%x); remaking window.", + (uint)window->visual->visAttribs, (uint)context->visual->visAttribs); + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ + renderspu_SystemDestroyWindow(window); + renderspu_SystemCreateWindow(context->visual, window->visible, + window); + } + + /* This is the normal case: rendering to the render SPU's own window */ + result = renderspuWindowAttachContext(window, window->window, + context); + /* XXX this is a total hack to work around an NVIDIA driver bug */ + if(render_spu.self.GetFloatv && context->haveWindowPosARB) + { + GLfloat f[4]; + render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f); + if (!window->everCurrent || f[1] < 0.0) + { + crDebug("Render SPU (renderspu_SystemMakeCurrent): Resetting raster pos"); + render_spu.self.WindowPos2iARB(0, 0); + } + } + /* Reapply the visible regions */ + renderspu_SystemWindowApplyVisibleRegion(window); + } + else + renderspuWindowAttachContext (0, 0, 0); +} + +void +renderspu_SystemSwapBuffers(WindowInfo *window, GLint flags) +{ + CRASSERT(window); + CRASSERT(window->window); + + ContextInfo *context = renderspuGetWindowContext(window); + + if(!context) + crError("Render SPU (renderspu_SystemSwapBuffers): SwapBuffers got a null context from the window"); + + RTSemFastMutexRequest(render_spu.syncMutex); +// DEBUG_MSG_POETZSCH (("Swapped %d context %x visible: %d\n", window->BltInfo.Base.id, context->context, IsWindowVisible (window->window))); + if (context->visual && + context->visual->visAttribs & CR_DOUBLE_BIT) + render_spu.ws.aglSwapBuffers(context->context); + else + glFlush(); + RTSemFastMutexRelease(render_spu.syncMutex); + + /* This method seems called very often. To prevent the dock using all free + * resources we update the dock only two times per second. */ + uint64_t curTS = RTTimeMilliTS(); + if ((curTS - render_spu.uiDockUpdateTS) > 500) + { + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxUpdateDock, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): CreateEvent Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): PostEventToQueue Failed"); + + render_spu.uiDockUpdateTS = curTS; + } +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) +{ + CRASSERT(window); + CRASSERT(window->window); + + /* Remember any additional clipping stuff e.g. seamless regions */ + if (window->hVisibleRegion) + { + DisposeRgn(window->hVisibleRegion); + window->hVisibleRegion = 0; + } + + if (cRects>0) + { + int i; + /* Create some temporary regions */ + RgnHandle rgn = NewRgn(); + SetEmptyRgn (rgn); + RgnHandle tmpRgn = NewRgn(); + for (i=0; i<cRects; ++i) + { + SetRectRgn (tmpRgn, + pRects[4*i] , pRects[4*i+1], + pRects[4*i+2], pRects[4*i+3]); + //DEBUG_MSG_POETZSCH (("visible rect %d %d %d %d\n", pRects[4*i] , pRects[4*i+1], + // pRects[4*i+2], pRects[4*i+3])); + UnionRgn (rgn, tmpRgn, rgn); + } + DisposeRgn (tmpRgn); + window->hVisibleRegion = rgn; + } + + renderspu_SystemWindowApplyVisibleRegion(window); +} + +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects) +{ + /* Remember the visible region of the root window if there is one */ + if (render_spu.hRootVisibleRegion) + { + DisposeRgn(render_spu.hRootVisibleRegion); + render_spu.hRootVisibleRegion = 0; + } + + if (cRects>0) + { + int i; + render_spu.hRootVisibleRegion = NewRgn(); + SetEmptyRgn (render_spu.hRootVisibleRegion); + RgnHandle tmpRgn = NewRgn(); + for (i=0; i<cRects; ++i) + { + SetRectRgn (tmpRgn, + pRects[4*i] , pRects[4*i+1], + pRects[4*i+2], pRects[4*i+3]); + UnionRgn (render_spu.hRootVisibleRegion, tmpRgn, render_spu.hRootVisibleRegion); + } + DisposeRgn (tmpRgn); + } +} + +/*Assumes that all regions are in the guest coordinates system*/ +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) +{ + ContextInfo *c = renderspuGetWindowContext(window); + RgnHandle rgn; + GLboolean result = true; + + DEBUG_MSG_POETZSCH (("ApplyVisibleRegion %x\n", window)); + + if (!c || !c->context) return; + + rgn = NewRgn(); + SetEmptyRgn(rgn); + + if (render_spu.hRootVisibleRegion) + { + /* The render_spu.hRootVisibleRegion has coordinates from the root + * window. We intersect it with the rect of the OpenGL window we + * currently process. */ + SetRectRgn(rgn, + window->x, window->y, + window->x + window->BltInfo.width, + window->y + window->BltInfo.height); + SectRgn(render_spu.hRootVisibleRegion, rgn, rgn); + /* Because the clipping is done in the coordinate space of the OpenGL + * window we have to remove the x/y position from the newly created + * region. */ + OffsetRgn (rgn, -window->x, -window->y); + } + else + { + /* If there is not root clipping region is available, create a base + * region with the size of the target window. This covers all + * needed/possible space. */ + SetRectRgn(rgn, 0, 0, window->BltInfo.width, window->BltInfo.height); + } + + /* Now intersect the window clipping region with a additional region e.g. + * for the seamless mode. */ + if (window->hVisibleRegion) + SectRgn(rgn, window->hVisibleRegion, rgn); + + if (rgn && !EmptyRgn(rgn)) + { + /* Set the clip region to the context */ + result = render_spu.ws.aglSetInteger(c->context, AGL_CLIP_REGION, (const GLint*)rgn); + CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): SetInteger Failed"); + result = render_spu.ws.aglEnable(c->context, AGL_CLIP_REGION); + CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): Enable Failed"); + } + /* Clear the region structure */ + DisposeRgn (rgn); +} + +GLboolean +renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt, + WindowInfo *window) +{ + CRASSERT(visual); + CRASSERT(window); + + WindowAttributes winAttr = kWindowNoShadowAttribute | kWindowCompositingAttribute | kWindowIgnoreClicksAttribute | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute; + WindowClass winClass = kOverlayWindowClass; + Rect windowRect; + OSStatus status = noErr; + + window->visual = visual; + window->nativeWindow = NULL; + + if(window->window && IsValidWindowPtr(window->window)) + { + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateEvent Failed", false); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): SetEventParameter Failed", false); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): PostEventToQueue Failed", false); + } + + windowRect.left = window->x; + windowRect.top = window->y; + windowRect.right = window->x + window->BltInfo.width; + windowRect.bottom = window->y + window->BltInfo.height; + + status = CreateNewWindow(winClass, winAttr, &windowRect, &window->window); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateNewWindow Failed", GL_FALSE); + + /* We set a title for debugging purposes */ + CFStringRef title_string; + title_string = CFStringCreateWithCStringNoCopy(NULL, window->title, + kCFStringEncodingMacRoman, NULL); + SetWindowTitleWithCFString(window->BltInfo.window, title_string); + CFRelease(title_string); + + /* The parent has to be in its own group */ + WindowRef parent = NULL; + if (render_spu_parent_window_id) + { + parent = HIViewGetWindow ((HIViewRef)render_spu_parent_window_id); + SetWindowGroup (parent, render_spu.pParentGroup); + + } + + /* Add the new window to the master group */ + SetWindowGroup(window->window, render_spu.pMasterGroup); + + /* This will be initialized on the first attempt to attach the global + * context to this new window */ + window->bufferName = -1; + window->dummyContext = NULL; + window->hVisibleRegion = 0; + + if(showIt) + renderspu_SystemShowWindow(window, GL_TRUE); + + crDebug("Render SPU (renderspu_SystemVBoxCreateWindow): actual window (x, y, width, height): %d, %d, %d, %d", + window->x, window->y, window->BltInfo.width, window->BltInfo.height); + + return GL_TRUE; +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c new file mode 100644 index 00000000..583398a3 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c @@ -0,0 +1,479 @@ +/* $Id: renderspu_cocoa.c $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System implementation + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <OpenGL/OpenGL.h> + +#include "renderspu.h" +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/path.h> + +#include <cr_string.h> +#include <cr_mem.h> + +GLboolean renderspu_SystemInitVisual(VisualInfo *pVisInfo) +{ + CRASSERT(pVisInfo); + +/* cocoaGLVisualCreate(&pCtxInfo->context);*/ + + return GL_TRUE; +} + +GLboolean renderspu_SystemCreateContext(VisualInfo *pVisInfo, ContextInfo *pCtxInfo, ContextInfo *pSharedCtxInfo) +{ + CRASSERT(pVisInfo); + CRASSERT(pCtxInfo); + + pCtxInfo->currentWindow = NULL; + + cocoaGLCtxCreate(&pCtxInfo->context, pVisInfo->visAttribs, pSharedCtxInfo ? pSharedCtxInfo->context : NULL); + + return GL_TRUE; +} + +void renderspu_SystemDestroyContext(ContextInfo *pCtxInfo) +{ + if(!pCtxInfo) + return; + + if(pCtxInfo->context) + { + cocoaGLCtxDestroy(pCtxInfo->context); + pCtxInfo->context = NULL; + } +} + +void renderspuFullscreen(WindowInfo *pWinInfo, GLboolean fFullscreen) +{ + /* Real fullscreen isn't supported by VirtualBox */ +} + +GLboolean renderspu_SystemVBoxCreateWindow(VisualInfo *pVisInfo, GLboolean fShowIt, WindowInfo *pWinInfo) +{ + CRASSERT(pVisInfo); + CRASSERT(pWinInfo); + + /* VirtualBox is the only frontend which support 3D right now. */ + char pszName[256]; + if (RTProcGetExecutablePath(pszName, sizeof(pszName))) + /* Check for VirtualBox and VirtualBoxVM */ + if (RTStrNICmp(RTPathFilename(pszName), "VirtualBox", 10) != 0) + return GL_FALSE; + + pWinInfo->visual = pVisInfo; + pWinInfo->window = NULL; + pWinInfo->nativeWindow = NULL; + pWinInfo->currentCtx = NULL; + + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + + cocoaViewCreate(&pWinInfo->window, pWinInfo, pParentWin, pVisInfo->visAttribs); + + if (fShowIt) + renderspu_SystemShowWindow(pWinInfo, fShowIt); + + return GL_TRUE; +} + +void renderspu_SystemReparentWindow(WindowInfo *pWinInfo) +{ + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + cocoaViewReparent(pWinInfo->window, pParentWin); +} + +void renderspu_SystemDestroyWindow(WindowInfo *pWinInfo) +{ + CRASSERT(pWinInfo); + + cocoaViewDestroy(pWinInfo->window); +} + +void renderspu_SystemWindowPosition(WindowInfo *pWinInfo, GLint x, GLint y) +{ + CRASSERT(pWinInfo); + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + + /*pParentWin is unused in the call, otherwise it might hold incorrect value if for ex. last reparent call was for + a different screen*/ + cocoaViewSetPosition(pWinInfo->window, pParentWin, x, y); +} + +void renderspu_SystemWindowSize(WindowInfo *pWinInfo, GLint w, GLint h) +{ + CRASSERT(pWinInfo); + + cocoaViewSetSize(pWinInfo->window, w, h); +} + +void renderspu_SystemGetWindowGeometry(WindowInfo *pWinInfo, GLint *pX, GLint *pY, GLint *pW, GLint *pH) +{ + CRASSERT(pWinInfo); + + cocoaViewGetGeometry(pWinInfo->window, pX, pY, pW, pH); +} + +void renderspu_SystemGetMaxWindowSize(WindowInfo *pWinInfo, GLint *pW, GLint *pH) +{ + CRASSERT(pWinInfo); + + *pW = 10000; + *pH = 10000; +} + +void renderspu_SystemShowWindow(WindowInfo *pWinInfo, GLboolean fShowIt) +{ + CRASSERT(pWinInfo); + + cocoaViewShow(pWinInfo->window, fShowIt); +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + cocoaViewPresentComposition(window->window, pChangedEntry); +} + +void renderspu_SystemMakeCurrent(WindowInfo *pWinInfo, GLint nativeWindow, ContextInfo *pCtxInfo) +{ +/* if(pWinInfo->visual != pCtxInfo->visual)*/ +/* printf ("visual mismatch .....................\n");*/ + + nativeWindow = 0; + + if (pWinInfo && pCtxInfo) + cocoaViewMakeCurrentContext(pWinInfo->window, pCtxInfo->context); + else + cocoaViewMakeCurrentContext(NULL, NULL); +} + +void renderspu_SystemSwapBuffers(WindowInfo *pWinInfo, GLint flags) +{ + CRASSERT(pWinInfo); + + cocoaViewDisplay(pWinInfo->window); +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *pWinInfo) +{ + return cocoaViewNeedsEmptyPresent(pWinInfo->window); +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *pWinInfo, GLint cRects, const GLint* paRects) +{ + CRASSERT(pWinInfo); + + cocoaViewSetVisibleRegion(pWinInfo->window, cRects, paRects); +} + +void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *pWinInfo) +{ +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + CrGlslTerm(&render_spu.GlobalShaders); + return VINF_SUCCESS; +} + +static SPUNamedFunctionTable * renderspuFindEntry(SPUNamedFunctionTable *aFunctions, const char *pcszName) +{ + SPUNamedFunctionTable *pCur; + + for (pCur = aFunctions ; pCur->name != NULL ; pCur++) + { + if (!crStrcmp( pcszName, pCur->name ) ) + { + return pCur; + } + } + + AssertFailed(); + + return NULL; +} + +typedef struct CR_RENDER_CTX_INFO +{ + ContextInfo * pContext; + WindowInfo * pWindow; +} CR_RENDER_CTX_INFO; + +void renderspuCtxInfoInitCurrent(CR_RENDER_CTX_INFO *pInfo) +{ + GET_CONTEXT(pCurCtx); + pInfo->pContext = pCurCtx; + pInfo->pWindow = pCurCtx ? pCurCtx->currentWindow : NULL; +} + +void renderspuCtxInfoRestoreCurrent(CR_RENDER_CTX_INFO *pInfo) +{ + GET_CONTEXT(pCurCtx); + if (pCurCtx == pInfo->pContext && (!pCurCtx || pCurCtx->currentWindow == pInfo->pWindow)) + return; + renderspuPerformMakeCurrent(pInfo->pWindow, 0, pInfo->pContext); +} + +GLboolean renderspuCtxSetCurrentWithAnyWindow(ContextInfo * pContext, CR_RENDER_CTX_INFO *pInfo) +{ + WindowInfo * window; + renderspuCtxInfoInitCurrent(pInfo); + + if (pInfo->pContext == pContext) + return GL_TRUE; + + window = pContext->currentWindow; + if (!window) + { + window = renderspuGetDummyWindow(pContext->BltInfo.Base.visualBits); + if (!window) + { + WARN(("renderspuGetDummyWindow failed")); + return GL_FALSE; + } + } + + Assert(window); + + renderspuPerformMakeCurrent(window, 0, pContext); + return GL_TRUE; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + CRASSERT(fromContext != toContext); + + if (!CrGlslIsInited(&render_spu.GlobalShaders)) + { + CrGlslInit(&render_spu.GlobalShaders, &render_spu.blitterDispatch); + } + + if (fromContext) + { + if (CrGlslNeedsCleanup(&render_spu.GlobalShaders)) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(fromContext, &Info)) + { + CrGlslCleanup(&render_spu.GlobalShaders); + renderspuCtxInfoRestoreCurrent(&Info); + } + else + WARN(("renderspuCtxSetCurrentWithAnyWindow failed!")); + } + } + else + { + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + } + + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + + if (toContext) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(toContext, &Info)) + { + int rc = CrGlslProgGenAllNoAlpha(&render_spu.GlobalShaders); + if (!RT_SUCCESS(rc)) + WARN(("CrGlslProgGenAllNoAlpha failed, rc %d", rc)); + + renderspuCtxInfoRestoreCurrent(&Info); + } + else + crWarning("renderspuCtxSetCurrentWithAnyWindow failed!"); + } +} + +AssertCompile(sizeof (GLhandleARB) == sizeof (void*)); + +static VBoxGLhandleARB crHndlSearchVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); +} + +static GLhandleARB crHndlSearchNative(VBoxGLhandleARB hVBox) +{ + return (GLhandleARB)((uintptr_t)hVBox); +} + +static VBoxGLhandleARB crHndlAcquireVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); +} + +static GLhandleARB crHndlReleaseVBox(VBoxGLhandleARB hVBox) +{ + return (GLhandleARB)((uintptr_t)hVBox); +} + +static void SPU_APIENTRY renderspu_SystemDeleteObjectARB(VBoxGLhandleARB obj) +{ + GLhandleARB hNative = crHndlReleaseVBox(obj); + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + render_spu.pfnDeleteObject(hNative); +} + +static void SPU_APIENTRY renderspu_SystemGetAttachedObjectsARB( VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * pCount, VBoxGLhandleARB * obj ) +{ + GLhandleARB *paAttachments; + GLhandleARB hNative = crHndlSearchNative(containerObj); + GLsizei count, i; + + if (pCount) + *pCount = 0; + + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + paAttachments = crCalloc(maxCount * sizeof (*paAttachments)); + if (!paAttachments) + { + crWarning("crCalloc failed"); + return; + } + + render_spu.pfnGetAttachedObjects(hNative, maxCount, &count, paAttachments); + if (pCount) + *pCount = count; + if (count > maxCount) + { + crWarning("count too big"); + count = maxCount; + } + + for (i = 0; i < count; ++i) + { + obj[i] = crHndlSearchVBox(paAttachments[i]); + CRASSERT(obj[i]); + } + + crFree(paAttachments); +} + +static VBoxGLhandleARB SPU_APIENTRY renderspu_SystemGetHandleARB(GLenum pname) +{ + GLhandleARB hNative = render_spu.pfnGetHandle(pname); + VBoxGLhandleARB hVBox; + if (!hNative) + { + crWarning("pfnGetHandle failed"); + return 0; + } + hVBox = crHndlAcquireVBox(hNative); + CRASSERT(hVBox); + return hVBox; +} + +static void SPU_APIENTRY renderspu_SystemGetInfoLogARB( VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetInfoLog(hNative, maxLength, length, infoLog); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameterfv(hNative, pname, params); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameteriv(hNative, pname, params); +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + SPUNamedFunctionTable * pEntry; + + pEntry = renderspuFindEntry(aFunctions, "DeleteObjectARB"); + if (pEntry) + { + render_spu.pfnDeleteObject = (PFNDELETE_OBJECT)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemDeleteObjectARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetAttachedObjectsARB"); + if (pEntry) + { + render_spu.pfnGetAttachedObjects = (PFNGET_ATTACHED_OBJECTS)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetAttachedObjectsARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetHandleARB"); + if (pEntry) + { + render_spu.pfnGetHandle = (PFNGET_HANDLE)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetHandleARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetInfoLogARB"); + if (pEntry) + { + render_spu.pfnGetInfoLog = (PFNGET_INFO_LOG)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetInfoLogARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterfvARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameterfv = (PFNGET_OBJECT_PARAMETERFV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterfvARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterivARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameteriv = (PFNGET_OBJECT_PARAMETERIV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterivARB; + } + + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h new file mode 100644 index 00000000..11333083 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h @@ -0,0 +1,65 @@ +/* $Id: renderspu_cocoa_helper.h $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System Helper definition + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef ___renderspu_cocoa_helper_h +#define ___renderspu_cocoa_helper_h + +#include <iprt/cdefs.h> +#include <VBox/VBoxCocoa.h> +#include <OpenGL/OpenGL.h> +#ifdef IN_VMSVGA3D +# include "../../../GuestHost/OpenGL/include/cr_vreg.h" +# include "../../../GuestHost/OpenGL/include/cr_compositor.h" +#else +# include <cr_vreg.h> +# include <cr_compositor.h> +#endif + + +RT_C_DECLS_BEGIN + +struct WindowInfo; + +ADD_COCOA_NATIVE_REF(NSView); +ADD_COCOA_NATIVE_REF(NSOpenGLContext); + +/** @name OpenGL context management + * @{ */ +void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx); +void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx); +/** @} */ + +/** @name View management + * @{ */ +void cocoaViewCreate(NativeNSViewRef *ppView, struct WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams); +void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView); +void cocoaViewDestroy(NativeNSViewRef pView); +void cocoaViewDisplay(NativeNSViewRef pView); +void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt); +void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y); +void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy); +void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy); +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx); +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects); +GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView); +void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry); +/** @} */ + +RT_C_DECLS_END + +#endif /* !___renderspu_cocoa_helper_h */ + diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m new file mode 100644 index 00000000..6c643f9c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m @@ -0,0 +1,3283 @@ +/* $Id: renderspu_cocoa_helper.m $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System Helper Implementation. + * + * This source file is shared between the SharedOpenGL HGCM service and the + * SVGA3d emulation. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper + * + * How this works: + * In general it is not so easy like on the other platforms, cause Cocoa + * doesn't support any clipping of already painted stuff. In Mac OS X there is + * the concept of translucent canvas's e.g. windows and there it is just + * painted what should be visible to the user. Unfortunately this isn't the + * concept of chromium. Therefor I reroute all OpenGL operation from the guest + * to a frame buffer object (FBO). This is a OpenGL extension, which is + * supported by all OS X versions we support (AFAIC tell). Of course the guest + * doesn't know that and we have to make sure that the OpenGL state always is + * in the right state to paint into the FBO and not to the front/back buffer. + * Several functions below (like cocoaBindFramebufferEXT, cocoaGetIntegerv, + * ...) doing this. When a swap or finish is triggered by the guest, the + * content (which is already bound to an texture) is painted on the screen + * within a separate OpenGL context. This allows the usage of the same + * resources (texture ids, buffers ...) but at the same time having an + * different internal OpenGL state. Another advantage is that we can paint a + * thumbnail of the current output in a much more smaller (GPU accelerated + * scale) version on a third context and use glReadPixels to get the actual + * data. glReadPixels is a very slow operation, but as we just use a much more + * smaller image, we can handle it (anyway this is only done 5 times per + * second). + * + * Other things to know: + * - If the guest request double buffering, we have to make sure there are two + * buffers. We use the same FBO with 2 color attachments. Also glDrawBuffer + * and glReadBuffer is intercepted to make sure it is painted/read to/from + * the correct buffers. On swap our buffers are swapped and not the + * front/back buffer. + * - If the guest request a depth/stencil buffer, a combined render buffer for + * this is created. + * - If the size of the guest OpenGL window changes, all FBO's, textures, ... + * need to be recreated. + * - We need to track any changes to the parent window + * (create/destroy/move/resize). The various classes like OverlayHelperView, + * OverlayWindow, ... are there for. + * - The HGCM service runs on a other thread than the Main GUI. Keeps this + * always in mind (see e.g. performSelectorOnMainThread in renderFBOToView) + * - We make heavy use of late binding. We can not be sure that the GUI (or any + * other third party GUI), overwrite our NSOpenGLContext. So we always ask if + * this is our own one, before use. Really neat concept of Objective-C/Cocoa + * ;) + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef IN_VMSVGA3D +# define LOG_GROUP LOG_GROUP_DEV_VMSVGA +#endif +#include "renderspu_cocoa_helper.h" + +#import <Cocoa/Cocoa.h> +#undef PVM /* sys/param.h (included via Cocoa.h) pollutes the namespace with this define. */ + +#ifndef IN_VMSVGA3D +# include "chromium.h" /* For the visual bits of chromium */ +#endif + +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include <iprt/thread.h> + +#include <VBox/VBoxOGL.h> +#include <VBox/log.h> + +#ifdef IN_VMSVGA3D +# include "DevVGA-SVGA3d-cocoa.h" +# include <OpenGL/OpenGL.h> +# include <OpenGL/gl3.h> +# include <OpenGL/gl3ext.h> +# include <OpenGL/glext.h> +#else +# include <cr_vreg.h> +# include <cr_error.h> +# include <cr_blitter.h> +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +# include <cr_pixeldata.h> +# endif +# include "renderspu.h" +#endif + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* Debug macros */ +/** @def FBO + * Disable this to see how the output is without the FBO in the middle of the processing chain. */ +#define FBO 1 +/** @def SHOW_WINDOW_BACKGROUND + * Define this to see the window background even if the window is clipped. */ +/** @def DEBUG_VERBOSE + * Define this to get some debug info about the messages flow. */ +#if 0 || defined(DOXYGEN_RUNNING) +# define SHOW_WINDOW_BACKGROUND 1 +# define DEBUG_VERBOSE +#endif + +#ifdef DEBUG_VERBOSE +# error "should be disabled!" +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# else +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# endif + +# define DEBUG_MSG(text) do { LogRel(text); } while (0) +# define DEBUG_MSG_1(text) do { LogRel(text); } while (0) + +#else + +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); } while (0) +# else +# define DEBUG_INFO(text) do { crInfo text; } while (0) +# define DEBUG_WARN(text) do { crWarning text; } while (0) +#endif +# define DEBUG_MSG(text) do {} while (0) +# define DEBUG_MSG_1(text) do {} while (0) + +#endif +#ifdef IN_VMSVGA3D +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) do {} while (0) +# define COCOA_LOG_FLOW(a_TextArgs) LogFlow(a_TextArgs) +#else +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) DEBUG_MSG(a_TextArgs) +# define COCOA_LOG_FLOW(a_TextArgs) DEBUG_MSG(a_TextArgs) +#endif + + +#define DEBUG_FUNC_ENTER() DEBUG_MSG(("==>%s\n", __PRETTY_FUNCTION__)) +#define DEBUG_FUNC_LEAVE() DEBUG_MSG(("<==%s\n", __PRETTY_FUNCTION__)) + +#define DEBUG_GL_SAVE_STATE() \ + do { \ + glPushAttrib(GL_ALL_ATTRIB_BITS); \ + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); \ + glMatrixMode(GL_PROJECTION); \ + glPushMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPushMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPushMatrix(); \ + glMatrixMode(GL_MODELVIEW); \ + glPushMatrix(); \ + } while (0) + +#define DEBUG_GL_RESTORE_STATE() \ + do { \ + glMatrixMode(GL_MODELVIEW); \ + glPopMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPopMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPopMatrix(); \ + glMatrixMode(GL_PROJECTION); \ + glPopMatrix(); \ + glPopClientAttrib(); \ + glPopAttrib(); \ + } while (0) + +#ifdef VBOX_STRICT +# define DEBUG_CLEAR_GL_ERRORS() \ + do { \ + while (glGetError() != GL_NO_ERROR) \ + { /* nothing */ } \ + } while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) \ + do { \ + GLenum iGlCheckErr = glGetError(); \ + if (RT_UNLIKELY(iGlCheckErr != GL_NO_ERROR)) \ + AssertMsgFailed((a_szOp ": iGlCheckErr=%#x\n", iGlCheckErr)); \ + } while (0) +#else +# define DEBUG_CLEAR_GL_ERRORS() do {} while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) do {} while (0) +#endif + +/* Whether we control NSView automatic content zooming on Retina/HiDPI displays. */ +#define VBOX_WITH_CONFIGURABLE_HIDPI_SCALING 1 + + +#ifdef IN_VMSVGA3D + +/* + * VMSVGA3D compatibility glue. + */ +typedef struct WindowInfo WindowInfo; + +# define CR_RGB_BIT RT_BIT_32(0) + +# define CR_ALPHA_BIT RT_BIT_32(1) +# define CR_DEPTH_BIT RT_BIT_32(2) +# define CR_STENCIL_BIT RT_BIT_32(3) +# define CR_ACCUM_BIT RT_BIT_32(4) +# define CR_DOUBLE_BIT RT_BIT_32(5) +# define CR_STEREO_BIT RT_BIT_32(6) +# define CR_MULTISAMPLE_BIT RT_BIT_32(7) + +# define CR_OVERLAY_BIT RT_BIT_32(8) +# define CR_PBUFFER_BIT RT_BIT_32(9) +# define VMSVGA3D_NON_DEFAULT_PROFILE_BIT RT_BIT_32(31) +# define CR_ALL_BITS UINT32_C(0x800003ff) + +#endif /* IN_VMSVGA3D */ + + +/** + * Works functions that creates a NSOpenGLPixelFormat instance. + * + * @returns Instance. + * @param fVisParams Context flags. + */ +static NSOpenGLPixelFormat *vboxCreatePixelFormat(GLbitfield fVisParams) +{ + NSOpenGLPixelFormatAttribute attribs[24] = + { +#ifdef IN_VMSVGA3D + NSOpenGLPFAOpenGLProfile, (NSOpenGLPixelFormatAttribute)0, +#endif + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24 + }; +#ifndef IN_VMSVGA3D + int i = 3; +#else + int i = 3+2; + if (fVisParams & VMSVGA3D_NON_DEFAULT_PROFILE_BIT) + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersionLegacy : NSOpenGLProfileVersion3_2Core; + else + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy; +#endif + + if (fVisParams & CR_ALPHA_BIT) + { + COCOA_LOG_FLOW((" CR_ALPHA_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAlphaSize; + attribs[i++] = 8; + } + if (fVisParams & CR_DEPTH_BIT) + { + COCOA_LOG_FLOW((" CR_DEPTH_BIT requested\n")); + attribs[i++] = NSOpenGLPFADepthSize; + attribs[i++] = 24; + } + if (fVisParams & CR_STENCIL_BIT) + { + COCOA_LOG_FLOW((" CR_STENCIL_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStencilSize; + attribs[i++] = 8; + } + if (fVisParams & CR_ACCUM_BIT) + { + COCOA_LOG_FLOW((" CR_ACCUM_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAccumSize; + if (fVisParams & CR_ALPHA_BIT) + attribs[i++] = 32; + else + attribs[i++] = 24; + } + if (fVisParams & CR_MULTISAMPLE_BIT) + { + COCOA_LOG_FLOW((" CR_MULTISAMPLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFASampleBuffers; + attribs[i++] = 1; + attribs[i++] = NSOpenGLPFASamples; + attribs[i++] = 4; + } + if (fVisParams & CR_DOUBLE_BIT) + { + COCOA_LOG_FLOW((" CR_DOUBLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFADoubleBuffer; + } + if (fVisParams & CR_STEREO_BIT) + { + /* We don't support that. + COCOA_LOG_FLOW((" CR_STEREO_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStereo; + */ + } + + if (VBoxOglIsOfflineRenderingAppropriate()) + { + COCOA_LOG_FLOW((" Offline rendering is enabled\n")); + attribs[i++] = NSOpenGLPFAAllowOfflineRenderers; + } + + /* Mark the end */ + attribs[i++] = 0; + + /* Instantiate the pixel format object. */ + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; +} + + +static NSOpenGLContext *vboxCtxGetCurrent(void) +{ +#ifdef IN_VMSVGA3D + return [NSOpenGLContext currentContext]; +#else + GET_CONTEXT(pCtxInfo); + if (pCtxInfo) + { + Assert(pCtxInfo->context); + return pCtxInfo->context; + } + return nil; +#endif +} + +static bool vboxCtxSyncCurrentInfo(void) +{ +#ifdef IN_VMSVGA3D + return false; +#else + bool fAdjusted = false; + GET_CONTEXT(pCtxInfo); + NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; + NSView *pView = pCtx ? [pCtx view] : nil; + if (pCtxInfo) + { + WindowInfo *pWinInfo = pCtxInfo->currentWindow; + Assert(pWinInfo); + if ( pCtxInfo->context != pCtx + || pWinInfo->window != pView) + { + renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo); + fAdjusted = true; + } + } + else if (pCtx) + { + [NSOpenGLContext clearCurrentContext]; + fAdjusted = true; + } + + return fAdjusted; +#endif +} + + +/** + * State carrying structure for use with vboxCtxEnter and vboxCtxLeave + */ +typedef struct VBOX_CR_RENDER_CTX_INFO +{ + bool fIsValid; + NSOpenGLContext *pCtx; + NSView *pView; +} VBOX_CR_RENDER_CTX_INFO; +/** Pointer to render context info for use with vboxCtxEnter/Leave. */ +typedef VBOX_CR_RENDER_CTX_INFO *PVBOX_CR_RENDER_CTX_INFO; + +static void vboxCtxEnter(NSOpenGLContext *pNewCtx, PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + NSOpenGLContext *pOldCtx = vboxCtxGetCurrent(); + NSView *pOldView = pOldCtx ? [pOldCtx view] : nil; + NSView *pNewView = [pNewCtx view]; + + Assert(pNewCtx); + + if ( pOldCtx != pNewCtx + || pOldView != pNewView) + { + if (pOldCtx != nil) + glFlush(); + + DEBUG_CLEAR_GL_ERRORS(); + [pNewCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + + pCtxInfo->fIsValid = true; + pCtxInfo->pCtx = pOldCtx; + /** @todo r=bird: Why do we save the NEW VIEW here? vboxCtxLeave calls it 'pOldView'. Bug? */ + pCtxInfo->pView = pNewView; + } + else + { + /* No context switch necessary. */ + pCtxInfo->fIsValid = false; + } +} + +static void vboxCtxLeave(PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + if (pCtxInfo->fIsValid) + { + NSOpenGLContext *pOldCtx = pCtxInfo->pCtx; + NSView *pOldView = pCtxInfo->pView; + + glFlush(); + if (pOldCtx != nil) + { + /* vboxCtxEnter saves the new view, not the old. So, what we actually + do here is switching the view of the old context to that of the new + one (wrt vboxCtxEnter) before making it current. */ + /** @todo r=bird: Figure out what we really want to do here, and either rename + * pOldView or fix the code. */ + if ([pOldCtx view] != pOldView) + { + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx setView: pOldView]; + DEBUG_CHECK_GL_ERROR("setView"); + } + + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + +#ifdef VBOX_STRICT + { + NSOpenGLContext *pTstOldCtx = [NSOpenGLContext currentContext]; + NSView *pTstOldView = pTstOldCtx ? [pTstOldCtx view] : nil; + Assert(pTstOldCtx == pOldCtx); + Assert(pTstOldView == pOldView); + } +#endif + } + else + { + [NSOpenGLContext clearCurrentContext]; + } + } +} + + +/** + * Custom OpenGL context class. + * + * This implementation doesn't allow to set a view to the context, but save the + * view for later use. Also it saves a copy of the pixel format used to create + * that context for later use. + */ +@interface OverlayOpenGLContext: NSOpenGLContext +{ +@private + NSOpenGLPixelFormat *m_pPixelFormat; + NSView *m_pView; +} +- (NSOpenGLPixelFormat *)openGLPixelFormat; +@end + +/** + * Abstrack task class. + */ +@interface VBoxTask : NSObject +{ +} +- (void)run; +@end + +@implementation VBoxTask +/** Run method that the child classes must reimplement. + * This will abort the process. */ +- (void)run +{ + AssertReleaseFailed(); +} +@end + + +/** + * Generic task class for executing a given method select. + */ +@interface VBoxTaskPerformSelector : VBoxTask +{ +@private + id m_Object; + SEL m_Selector; + id m_Arg; +} +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskPerformSelector + +/** + * Initializes a VBoxTaskPerformSelector. + * + * @param aObject The object (reference not consumed). + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + self = [super init]; + if (self) + { + [aObject retain]; + m_Object = aObject; + m_Selector = aSelector; + if (aArg != nil) + [aArg retain]; + m_Arg = aArg; + } + + return self; +} + +- (void)run +{ + [m_Object performSelector:m_Selector withObject:m_Arg]; +} + +- (void)dealloc +{ + [m_Object release]; + if (m_Arg != nil) + [m_Arg release]; + + [super dealloc]; +} +@end + + +/** + * + */ +@interface VBoxTaskComposite : VBoxTask +{ +@private + NSUInteger m_CurIndex; + RTCRITSECT m_Lock; + NSMutableArray *m_pArray; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskComposite +- (id)init +{ + self = [super init]; + + if (self) + { + int rc = RTCritSectInit(&m_Lock); + if (!RT_SUCCESS(rc)) + { + DEBUG_WARN(("RTCritSectInit failed %d\n", rc)); + return nil; + } + + m_CurIndex = 0; + + m_pArray = [[NSMutableArray alloc] init]; + } + + return self; +} + +/** + * Adds a task to the composite task object. + * + * @param pTask Task to add. Reference is NOT consumed. + */ +- (void)add:(VBoxTask *)pTask +{ + [pTask retain]; + int rc = RTCritSectEnter(&m_Lock); + if (RT_SUCCESS(rc)) + { + [m_pArray addObject:pTask]; + RTCritSectLeave(&m_Lock); + } + else + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + [pTask release]; + } +} + +- (void)run +{ + for (;;) + { + /* + * Dequeue a task. + */ + int rc = RTCritSectEnter(&m_Lock); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + break; + } + + NSUInteger count = [m_pArray count]; + Assert(m_CurIndex <= count); + if (m_CurIndex == count) + { + [m_pArray removeAllObjects]; + m_CurIndex = 0; + RTCritSectLeave(&m_Lock); + break; + } + + VBoxTask *pTask = (VBoxTask *)[m_pArray objectAtIndex:m_CurIndex]; + Assert(pTask != nil); + + ++m_CurIndex; + + /* + * Remove the first 1025 empty entires. + */ + if (m_CurIndex > 1024) + { + NSRange range; + range.location = 0; + range.length = m_CurIndex; + [m_pArray removeObjectsInRange:range]; + m_CurIndex = 0; + } + RTCritSectLeave(&m_Lock); + + /* + * Run the task and release it. + */ + [pTask run]; + [pTask release]; + } +} + +- (void)dealloc +{ + NSUInteger count = [m_pArray count]; + for (;m_CurIndex < count; ++m_CurIndex) + { + VBoxTask *pTask = (VBoxTask*)[m_pArray objectAtIndex:m_CurIndex]; + DEBUG_WARN(("dealloc with non-empty tasks! %p\n", pTask)); + [pTask release]; + } + + [m_pArray release]; + RTCritSectDelete(&m_Lock); + + [super dealloc]; +} +@end + + +/** + * + * + */ +@interface VBoxMainThreadTaskRunner : NSObject +{ +@private + VBoxTaskComposite *m_pTasks; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)runTasks; +- (bool)runTasksSyncIfPossible; +- (void)dealloc; ++ (VBoxMainThreadTaskRunner *) globalInstance; +@end + +@implementation VBoxMainThreadTaskRunner +- (id)init +{ + self = [super init]; + if (self) + { + m_pTasks = [[VBoxTaskComposite alloc] init]; + } + + return self; +} + ++ (VBoxMainThreadTaskRunner *) globalInstance +{ + static dispatch_once_t s_DispatchOnce; + static VBoxMainThreadTaskRunner *s_pRunner = nil; + dispatch_once(&s_DispatchOnce, ^{ + s_pRunner = [[VBoxMainThreadTaskRunner alloc] init]; + }); + return s_pRunner; +} + +- (void)add:(VBoxTask *)pTask +{ + DEBUG_FUNC_ENTER(); + [m_pTasks add:pTask]; + /** @todo r=bird: Unbalanced [self retain]. */ + [self retain]; + + if (![self runTasksSyncIfPossible]) + { + DEBUG_MSG(("task will be processed async\n")); + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +/** + * Adds a task calling an object method (selector). + * + * @param aObject The object (reference not consumed).. + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + VBoxTaskPerformSelector *pSelTask = [[VBoxTaskPerformSelector alloc] initWithObject:aObject selector:aSelector arg:aArg]; + [self add:pSelTask]; + [pSelTask release]; +} + + +/** + * Internal method for running the pending tasks. + */ +- (void)runTasks +{ + if ([NSThread isMainThread]) + { + [m_pTasks run]; + /** @todo r=bird: This release and the retain in the add method aren't + * necessarily balanced if there are more than one call to add(). + * + * This could probably end up deleting the singleton prematurely and leave + * globalInstance() returning pointers to a stale object in freed memory, + * quite possibly causing crashes or/and heap corruption. */ + [self release]; + } + else + { + DEBUG_WARN(("run tasks called not on main thread!\n")); +#ifndef DEBUG_VERBOSE + AssertFailed(); +#endif + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:YES]; + } +} + +/** + * Callback for calling runTasks via renderspuCalloutClient. + * @param pvUser The VBoxMainThreadTaskRunner singleton. + */ +static DECLCALLBACK(void) VBoxMainThreadTaskRunner_RcdRunCallback(void *pvUser) +{ + DEBUG_FUNC_ENTER(); + VBoxMainThreadTaskRunner *pRunner = (VBoxMainThreadTaskRunner *)pvUser; + Assert(pRunner == [VBoxMainThreadTaskRunner globalInstance]); + [pRunner runTasks]; + DEBUG_FUNC_LEAVE(); +} + +/** + * Runs pending tasks synchronously, if possible in the current context. + * + * @returns true if executed tasks, false if not possible. + */ +- (bool)runTasksSyncIfPossible +{ +#ifndef IN_VMSVGA3D + /* + * Call on main thread (?) via renderspuCalloutClient (whatever that is). + */ + if (renderspuCalloutAvailable()) + { + Assert(![NSThread isMainThread]); + renderspuCalloutClient(VBoxMainThreadTaskRunner_RcdRunCallback, self); + return true; + } +#endif + + /* + * Run directly if on main thread. + */ + if ([NSThread isMainThread]) + { + [self runTasks]; + return true; + } + + /* Not possible. */ + return false; +} + +- (void)dealloc +{ + /** @todo r=bird: WTF is the point of the deallocator. The object is a singelton + * stored in an inaccessible static variable! */ + [m_pTasks release]; + m_pTasks = nil; + + [super dealloc]; +} + +@end + +#ifndef IN_VMSVGA3D +@class DockOverlayView; +#endif + +/** + * The custom view class. + * + * This is the main class of the cocoa OpenGL implementation. It manages a + * frame buffer object for the rendering of the guest applications. The guest + * applications render in this frame buffer which is bound to an OpenGL texture. + * To display the guest content, a secondary shared OpenGL context of the main + * OpenGL context is created. The secondary context is marked as non-opaque and + * the texture is displayed on an object which is composed out of the several + * visible region rectangles. + */ +@interface OverlayView : NSView +{ +@private + NSView *m_pParentView; + NSWindow *m_pOverlayWin; + + NSOpenGLContext *m_pGLCtx; + NSOpenGLContext *m_pSharedGLCtx; + RTTHREAD m_Thread; + + GLuint m_FBOId; + +#ifndef IN_VMSVGA3D + /** The corresponding dock tile view of this OpenGL view & all helper + * members. */ + DockOverlayView *m_DockTileView; + + GLfloat m_FBOThumbScaleX; + GLfloat m_FBOThumbScaleY; + uint64_t m_msDockUpdateTS; +#endif + + /** @name For clipping + * @remarks appears to be unused and a complete waste of time + heap. + * @{ */ + GLint m_cClipRects; + GLint *m_paClipRects; + /** @} */ + + /** @name Position/Size tracking + * @{ */ + NSPoint m_Pos; + NSSize m_Size; + /** @} */ + + /** This is necessary for clipping on the root window */ + NSRect m_RootRect; + float m_yInvRootOffset; + +#ifndef IN_VMSVGA3D + CR_BLITTER *m_pBlitter; + WindowInfo *m_pWinInfo; +#endif + bool m_fNeedViewportUpdate; + bool m_fNeedCtxUpdate; + bool m_fDataVisible; + bool m_fCleanupNeeded; + bool m_fEverSized; +} +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams; +- (void)setGLCtx:(NSOpenGLContext*)pCtx; +- (NSOpenGLContext *)glCtx; + +- (void)setParentView: (NSView *)view; +- (NSView *)parentView; +- (void)setOverlayWin: (NSWindow *)win; +- (NSWindow *)overlayWin; + +- (void)vboxSetPos:(NSPoint)pos; +- (void)vboxSetPosUI:(NSPoint)pos; +- (void)vboxSetPosUIObj:(NSValue *)pPos; +- (NSPoint)pos; +- (bool)isEverSized; +- (void)vboxDestroy; +- (void)vboxSetSizeUI:(NSSize)size; +- (void)vboxSetSizeUIObj:(NSValue *)pSize; +- (void)vboxSetSize:(NSSize)size; +- (NSSize)size; +- (void)updateViewportCS; +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect; +- (CGFloat)safeGetBackingScaleFactor; +#endif +- (NSRect)safeConvertToScreen:(NSRect *)pRect; +- (void)vboxReshapePerform; +- (void)vboxReshapeOnResizePerform; +- (void)vboxReshapeOnReparentPerform; + +#ifndef IN_VMSVGA3D +- (void)createDockTile; +- (void)deleteDockTile; +#endif + +- (void)makeCurrentFBO; +- (void)swapFBO; +- (void)vboxSetVisible:(GLboolean)fVisible; +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisible; +- (void)vboxSetVisibleUI:(GLboolean)fVisible; +- (void)vboxTryDraw; +- (void)vboxTryDrawUI; +- (void)vboxReparent:(NSView *)pParentView; +- (void)vboxReparentUI:(NSView *)pParentView; +- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#ifndef IN_VMSVGA3D +- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#endif +- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY *)pChangedEntry; +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow; +#endif + +- (void)clearVisibleRegions; +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects; +- (GLboolean)vboxNeedsEmptyPresent; + +#ifndef IN_VMSVGA3D +- (NSView *)dockTileScreen; +- (void)reshapeDockTile; +#endif +- (void)cleanupData; +@end + +/** + * Helper view. + * + * This view is added as a sub view of the parent view to track + * main window changes. Whenever the main window is changed + * (which happens on fullscreen/seamless entry/exit) the overlay + * window is informed & can add them self as a child window + * again. + */ +@class OverlayWindow; +@interface OverlayHelperView: NSView +{ +@private + OverlayWindow *m_pOverlayWindow; +} +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow; +@end + +/** + * Custom window class. + * + * This is the overlay window which contains our custom NSView. + * Its a direct child of the Qt Main window. It marks its background + * transparent & non opaque to make clipping possible. It also disable mouse + * events and handle frame change events of the parent view. + */ +@interface OverlayWindow : NSWindow +{ +@private + NSView *m_pParentView; + OverlayView *m_pOverlayView; + OverlayHelperView *m_pOverlayHelperView; + NSThread *m_Thread; +} +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView; +- (void)parentWindowFrameChanged:(NSNotification *)note; +- (void)parentWindowChanged:(NSWindow *)pWindow; +@end + + +#ifndef IN_VMSVGA3D +/** + * Dock overlay view class. + */ +@interface DockOverlayView: NSView +{ + NSBitmapImageRep *m_ThumbBitmap; + NSImage *m_ThumbImage; + NSLock *m_Lock; +} +- (void)dealloc; +- (void)cleanup; +- (void)lock; +- (void)unlock; +- (void)setFrame:(NSRect)frame; +- (void)drawRect:(NSRect)aRect; +- (NSBitmapImageRep *)thumbBitmap; +- (NSImage *)thumbImage; +@end + +@implementation DockOverlayView +- (id)init +{ + DEBUG_FUNC_ENTER(); + self = [super init]; + if (self) + { + /* + * We need a lock cause the thumb image could be accessed from the main + * thread when someone is calling display on the dock tile & from the + * OpenGL thread when the thumbnail is updated. + */ + m_Lock = [[NSLock alloc] init]; + } + + DEBUG_FUNC_LEAVE(); + + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + + [self cleanup]; + [m_Lock release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)cleanup +{ + DEBUG_FUNC_ENTER(); + + if (m_ThumbImage != nil) + { + [m_ThumbImage release]; + m_ThumbImage = nil; + } + + if (m_ThumbBitmap != nil) + { + [m_ThumbBitmap release]; + m_ThumbBitmap = nil; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)lock +{ + DEBUG_FUNC_ENTER(); + [m_Lock lock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)unlock +{ + DEBUG_FUNC_ENTER(); + [m_Lock unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)setFrame:(NSRect)frame +{ + DEBUG_FUNC_ENTER(); + [super setFrame:frame]; + + [self lock]; + [self cleanup]; + + if ( frame.size.width > 0 + && frame.size.height > 0) + { + /* Create a buffer for our thumbnail image. Its in the size of this view. */ + m_ThumbBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL + pixelsWide:frame.size.width + pixelsHigh:frame.size.height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:frame.size.width * 4 + bitsPerPixel:8 * 4 + ]; + m_ThumbImage = [[NSImage alloc] initWithSize:[m_ThumbBitmap size]]; + [m_ThumbImage addRepresentation:m_ThumbBitmap]; + } + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (BOOL)isFlipped +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return YES; +} + +- (void)drawRect:(NSRect)aRect +{ + NSRect frame; + DEBUG_FUNC_ENTER(); + [self lock]; + +#ifdef SHOW_WINDOW_BACKGROUND + [[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7] set]; + frame = [self frame]; + [NSBezierPath fillRect:NSMakeRect(0, 0, frame.size.width, frame.size.height)]; +#endif /* SHOW_WINDOW_BACKGROUND */ + if (m_ThumbImage != nil) + [m_ThumbImage drawAtPoint:NSMakePoint(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (NSBitmapImageRep *)thumbBitmap +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbBitmap; +} + +- (NSImage *)thumbImage +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbImage; +} +@end +#endif /* !IN_VMSVGA3D */ + + +/******************************************************************************** +* +* OverlayOpenGLContext class implementation +* +********************************************************************************/ +@implementation OverlayOpenGLContext + +-(id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share +{ + DEBUG_FUNC_ENTER(); + + m_pPixelFormat = NULL; + m_pView = NULL; + + self = [super initWithFormat:format shareContext:share]; + Assert(self != nil); + if (self) + m_pPixelFormat = format; + + DEBUG_MSG(("OCTX(%p): init OverlayOpenGLContext\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): dealloc OverlayOpenGLContext\n", (void *)self)); + + [m_pPixelFormat release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +-(bool)isDoubleBuffer +{ + DEBUG_FUNC_ENTER(); + + GLint val; + [m_pPixelFormat getValues:&val forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:0]; + + DEBUG_FUNC_LEAVE(); + return val == GL_TRUE ? YES : NO; +} + +-(void)setView:(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): setView: new view: %p\n", (void *)self, (void *)view)); + +#if 1 /* def FBO */ + m_pView = view;; +#else + [super setView: view]; +#endif + + DEBUG_FUNC_LEAVE(); +} + +-(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); +#if 1 /* def FBO */ + return m_pView; +#else + return [super view]; +#endif +} + +-(void)clearDrawable +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): clearDrawable\n", (void *)self)); + + m_pView = NULL;; + [super clearDrawable]; + + DEBUG_FUNC_LEAVE(); +} + +-(NSOpenGLPixelFormat *)openGLPixelFormat +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + + return m_pPixelFormat; +} + +@end /* @implementation OverlayOpenGLContext */ + + +/******************************************************************************** +* +* OverlayHelperView class implementation +* +********************************************************************************/ +@implementation OverlayHelperView + +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow +{ + DEBUG_FUNC_ENTER(); + + self = [super initWithFrame:frame]; +#ifdef IN_VMSVGA3D + self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; +#endif + + m_pOverlayWindow = pOverlayWindow; + + DEBUG_MSG(("OHVW(%p): init OverlayHelperView\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +-(void)viewDidMoveToWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OHVW(%p): viewDidMoveToWindow: new win: %p\n", (void *)self, (void *)[self window])); + + [m_pOverlayWindow parentWindowChanged:[self window]]; + + DEBUG_FUNC_LEAVE(); +} + +@end + + +/******************************************************************************** +* +* OverlayWindow class implementation +* +********************************************************************************/ +@implementation OverlayWindow + +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView +{ + DEBUG_FUNC_ENTER(); + NSWindow *pParentWin = nil; + + self = [super initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + if (self) + { + m_pParentView = pParentView; + m_pOverlayView = pOverlayView; + m_Thread = [NSThread currentThread]; + + [m_pOverlayView setOverlayWin: self]; + +#ifdef IN_VMSVGA3D + NSRect frame = [pParentView frame]; + frame.origin.x = frame.origin.x = 0; + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:frame + overlayWindow:self]; +#else + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:NSZeroRect + overlayWindow:self]; +#endif + + /* Add the helper view as a child of the parent view to get notifications */ + [pParentView addSubview:m_pOverlayHelperView]; + + /* Make sure this window is transparent */ +#ifdef SHOW_WINDOW_BACKGROUND + /* For debugging */ + [self setBackgroundColor:[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7]]; +#else + [self setBackgroundColor:[NSColor clearColor]]; +#endif + [self setOpaque:NO]; + [self setAlphaValue:.999]; + + /* Disable mouse events for this window */ + [self setIgnoresMouseEvents:YES]; + + pParentWin = [m_pParentView window]; + + /* Initial set the position to the parents view top/left (Compiz fix). */ + [self setFrameOrigin: + [pParentWin convertBaseToScreen: + [m_pParentView convertPoint:NSZeroPoint toView:nil]]]; + + /* Set the overlay view as our content view */ + [self setContentView:m_pOverlayView]; + + /* Add ourself as a child to the parent views window. Note: this has to + * be done last so that everything else is setup in + * parentWindowChanged. */ + [pParentWin addChildWindow:self ordered:NSWindowAbove]; + } + + DEBUG_MSG(("OWIN(%p): init OverlayWindow\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): dealloc OverlayWindow\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [m_pOverlayHelperView removeFromSuperview]; + [m_pOverlayHelperView release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowFrameChanged:(NSNotification *)pNote +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowFrameChanged\n", (void *)self)); + + /* + * Reposition this window with the help of the OverlayView. Perform the + * call in the OpenGL thread. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) onThread:m_Thread withObject:nil waitUntilDone:YES]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowChanged:(NSWindow *)pWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowChanged\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + if (pWindow != nil) + { + /* Ask to get notifications when our parent window frame changes. */ + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(parentWindowFrameChanged:) + name:NSWindowDidResizeNotification + object:pWindow]; + + /* Add us self as child window */ + [pWindow addChildWindow:self ordered:NSWindowAbove]; + + /* + * Reshape the overlay view after a short waiting time to let the main + * window resize itself properly. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) withObject:nil afterDelay:0.2]; + [NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(vboxReshapePerform) userInfo:nil repeats:NO]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + } + + DEBUG_FUNC_LEAVE(); +} + +@end /* @implementation OverlayWindow */ + + + +/******************************************************************************** +* +* OverlayView class implementation +* +********************************************************************************/ +@implementation OverlayView + +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams +{ + COCOA_LOG_FLOW(("%s: self=%p aThread=%p pParentView=%p pWinInfo=%p fVisParams=%#x\n", __PRETTY_FUNCTION__, (void *)self, + (void *)aThread, (void *)pParentView, (void *)pWinInfo, fVisParams)); + + m_pParentView = pParentView; + /* Make some reasonable defaults */ + m_pGLCtx = nil; + m_pSharedGLCtx = nil; + m_Thread = aThread; + m_FBOId = 0; + m_cClipRects = 0; + m_paClipRects = NULL; + m_Pos = NSZeroPoint; + m_Size = NSMakeSize(1, 1); + m_RootRect = NSMakeRect(0, 0, m_Size.width, m_Size.height); + m_yInvRootOffset = 0; +#ifndef IN_VMSVGA3D + m_pBlitter = nil; + m_pWinInfo = pWinInfo; +#endif + m_fNeedViewportUpdate = true; + m_fNeedCtxUpdate = true; + m_fDataVisible = false; + m_fCleanupNeeded = false; + m_fEverSized = false; + + self = [super initWithFrame:frame]; +#if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Always allocate HiDPI-ready backing store for NSView, so we will be able change HiDPI scaling option in runtime. */ + crDebug("HiDPI: Allocate big backing store for NSView. Up-scaling is currently %s.", render_spu.fUnscaledHiDPI ? "OFF" : "ON"); + [self performSelector:@selector(setWantsBestResolutionOpenGLSurface:) withObject: (id)YES]; +#endif + + COCOA_LOG_FLOW(("%s: returns self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + return self; +} + +- (void)cleanupData +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + +#ifndef IN_VMSVGA3D + [self deleteDockTile]; +#endif + + [self setGLCtx:nil]; + + if (m_pSharedGLCtx) + { + if ([m_pSharedGLCtx view] == self) + [m_pSharedGLCtx clearDrawable]; + + [m_pSharedGLCtx release]; + m_pSharedGLCtx = nil; + + +#ifndef IN_VMSVGA3D + CrBltTerm(m_pBlitter); + RTMemFree(m_pBlitter); + m_pBlitter = nil; +#endif + } + + [self clearVisibleRegions]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)dealloc +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self cleanupData]; + [super dealloc]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)drawRect:(NSRect)aRect +{ + COCOA_LOG_FLOW(("%s: self=%p aRect=%d,%d %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)aRect.origin.x, (int)aRect.origin.y, + (int)aRect.size.width, (int)aRect.size.height)); + + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setGLCtx:(NSOpenGLContext *)pCtx +{ + COCOA_LOG_FLOW(("%s: self=%p pCtx=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCtx, m_pGLCtx)); + + /* + * Only do something if the context changes. + */ + if (m_pGLCtx != pCtx) + { + /* Ensure the context drawable is cleared to avoid holding a reference to inexistent view. */ + if (m_pGLCtx) + { +#ifdef IN_VMSVGA3D + Assert(!pCtx); +#endif + [m_pGLCtx clearDrawable]; + [m_pGLCtx release]; + /*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + } + + m_pGLCtx = pCtx; + if (pCtx) + [pCtx retain]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSOpenGLContext *)glCtx +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + return m_pGLCtx; +} + +- (NSView *)parentView +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pParentView)); + return m_pParentView; +} + +- (void)setParentView:(NSView *)pView +{ + COCOA_LOG_FLOW(("%s: self=%p pView=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pView, m_pParentView)); + + m_pParentView = pView; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setOverlayWin:(NSWindow *)pWin +{ + COCOA_LOG_FLOW(("%s: self=%p pWin=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pWin, m_pOverlayWin)); + + m_pOverlayWin = pWin; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSWindow *)overlayWin +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pOverlayWin)); + return m_pOverlayWin; +} + +- (void)vboxSetPosUI:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + DEBUG_MSG(("vboxSetPosUI: [%d, %d].\n", (int)pos.x, (int)pos.y)); + + m_Pos = pos; + + if (m_fEverSized) + [self vboxReshapePerform]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPosUIObj:(NSValue *)pPos +{ + COCOA_LOG_FLOW(("%s: self=%p pPos=%p (%d,%d) (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, pPos, + (int)[pPos pointValue].x, (int)[pPos pointValue].y, (int)m_Pos.x, (int)m_Pos.y)); + + NSPoint pos = [pPos pointValue]; + [self vboxSetPosUI:pos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPos:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pPos = [NSValue valueWithPoint:pos]; + [pRunner addObj:self selector:@selector(vboxSetPosUIObj:) arg:pPos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Pos.x, (int)m_Pos.y)); + return m_Pos; +} + +- (bool)isEverSized +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d\n", __PRETTY_FUNCTION__, (void *)self, m_fEverSized)); + return m_fEverSized; +} + +- (void)vboxDestroy +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + BOOL fIsMain = [NSThread isMainThread]; + NSWindow *pWin = nil; + + Assert(fIsMain); + + /* Hide the view early. */ + [self setHidden: YES]; + + pWin = [self window]; + [[NSNotificationCenter defaultCenter] removeObserver:pWin]; + [pWin setContentView: nil]; + [[pWin parentWindow] removeChildWindow: pWin]; + + if (fIsMain) + [pWin release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO, + which should cause no harm. */ + [pWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + + [self cleanupData]; + + if (fIsMain) + [self release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO. + We need to avoid concurrency though, so we cleanup some data right away via a cleanupData call. */ + [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuWinRelease(m_pWinInfo); +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUIObj:(NSValue *)pSize +{ + COCOA_LOG_FLOW(("%s: self=%p pSize=%p (%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pSize, + (int)[pSize sizeValue].width, (int)[pSize sizeValue].height)); + + NSSize size = [pSize sizeValue]; + [self vboxSetSizeUI:size]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUI:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + m_Size = size; + m_fEverSized = true; + + DEBUG_MSG(("OVIW(%p): vboxSetSize: new size: %dx%d\n", (void *)self, (int)m_Size.width, (int)m_Size.height)); + [self vboxReshapeOnResizePerform]; + + /* ensure window contents is updated after that */ + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSize:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pSize = [NSValue valueWithSize:size]; + [pRunner addObj:self selector:@selector(vboxSetSizeUIObj:) arg:pSize]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Size.width, (int)m_Size.height)); + return m_Size; +} + +- (void)updateViewportCS +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + /* Update the viewport for our OpenGL view. */ + [m_pSharedGLCtx update]; + +#ifndef IN_VMSVGA3D + [self vboxBlitterSyncWindow]; +#endif + + /* Clear background to transparent. */ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnResizePerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + + /* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */ + m_fNeedViewportUpdate = true; +#if 0 + pCurCtx = [NSOpenGLContext currentContext]; + if (pCurCtx && pCurCtx == m_pGLCtx && (pCurView = [pCurCtx view]) == self) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + else + { + /* do it in a lazy way */ + m_fNeedCtxUpdate = true; + } +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnReparentPerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToBacking:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToBacking:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToBacking:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertRectToBacking: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertRectToBacking: parent widget has no window.\n")); + + resultingRect = *pRect; + + DEBUG_MSG(("safeConvertRectToBacking (reurn as is): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + + +- (CGFloat)safeGetBackingScaleFactor +{ + /* Assume its default value. */ + CGFloat backingScaleFactor = 1.; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + NSScreen *pScreen = [pWindow screen]; + if (pScreen) + { + if ([pScreen respondsToSelector:@selector(backingScaleFactor)]) + { + NSMethodSignature *pSignature = [pScreen methodSignatureForSelector:@selector(backingScaleFactor)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(backingScaleFactor)]; + [pInvocation setTarget:pScreen]; + [pInvocation invoke]; + [pInvocation getReturnValue:&backingScaleFactor]; + + DEBUG_MSG(("safeGetBackingScaleFactor: %d\n", (int)backingScaleFactor)); + + return backingScaleFactor; + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create invocation for backingScaleFactor method signature.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create method signature for backingScaleFactor selector.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: NSScreen does not respond to backingScaleFactor selector.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent window has no screen.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent widget has no window.\n")); + + return backingScaleFactor; +} + +#endif + + +- (NSRect)safeConvertToScreen:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToScreen:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToScreen:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToScreen:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertToScreen: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + + /* If we failed, let's use deprecated @selector(convertBaseToScreen:). It is a bit hacky, + * but what to do if we stick to SDK 10.6. */ + resultingRect.origin = [[m_pParentView window] convertBaseToScreen:pRect->origin]; + resultingRect.size = pRect->size; + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertToScreen: parent widget has no window.\n")); + + DEBUG_MSG(("safeConvertToScreen (deprecated method): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + +- (void)vboxReshapePerform +{ +#ifndef IN_VMSVGA3D + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); +#else + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); +#endif + + /* NOTE: Please consider the next naming convention for variables. + * + * Rectangle variables: + * + * <object to represent><coordinate system>: + * <object to represent>: + * parentFrame - a frame of the parent container (NSView) object + * childFrame - a frame required to display guest content + * windowFrame - resulting window frame constructed as an intersection of parentFrame and childFrame + * <coordinate system>: + * VCS - View Coordinate System + * WCS - Window Coordinate System + * SCS - Screen Coordinate System + * + * The same convention applied to offset variables naming as well which are of format: + * + * <object to represent><coordinate><coordinate system>. + * + * https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html + */ + + NSRect parentFrameVCS, parentFrameWCS, parentFrameSCS; + NSRect childFrameWCS, childFrameSCS; + NSRect windowFrameSCS; + + CGFloat childFrameXWCS, childFrameYWCS; + + /* We need to construct a new window frame (windowFrameSCS) for entire NSWindow object in + * screen coordinates. In order to make 3D overlay window to do not overlap Cocoa and Qt GUI elements (titlebar, + * Qt statusbar, scroll bars etc) let's do the next. Get parent view visible area (parentFrameSCS) in (NS)Screen + * coordinates. Then get the area required to diaplay guest content (childFrameSCS) in (NS)Screen coordinates as well. + * The intersection of these two areas in screen coordinates will be a new frame for entire NSWindow object. */ + + parentFrameVCS = [m_pParentView frame]; + parentFrameWCS = [m_pParentView convertRect:parentFrameVCS toView:nil]; + parentFrameSCS = [self safeConvertToScreen:&parentFrameWCS]; + + /* Choose childFrame origin in a bit special way. Its pop-left corner should stick to its parent top-left corner. */ + childFrameXWCS = parentFrameWCS.origin.x + m_Pos.x; + childFrameYWCS = parentFrameWCS.origin.y - m_Pos.y - (m_Size.height - parentFrameWCS.size.height); + childFrameWCS = NSMakeRect(childFrameXWCS, childFrameYWCS, m_Size.width, m_Size.height); + childFrameSCS = [self safeConvertToScreen:&childFrameWCS]; + + windowFrameSCS = NSIntersectionRect(parentFrameSCS, childFrameSCS); + + DEBUG_MSG(("vboxReshapePerform: a new overlay frame [%d, %d, %dx%d] has been constructed from intersection of window frame " + "[%d, %d, %dx%d] and guest content rectangle [%d, %d, %dx%d]; m_Pos=[%d, %d], m_Size=%dx%d.\n", + (int)windowFrameSCS.origin.x, (int)windowFrameSCS.origin.y, (int)windowFrameSCS.size.width, (int)windowFrameSCS.size.width, + (int)parentFrameSCS.origin.x, (int)parentFrameSCS.origin.y, (int)parentFrameSCS.size.width, (int)parentFrameSCS.size.width, + (int)childFrameSCS .origin.x, (int)childFrameSCS .origin.y, (int)childFrameSCS .size.width, (int)childFrameSCS .size.width, + (int)m_Pos.x, (int)m_Pos.y, (int)m_Size.width, (int)m_Size.height)); + + /** @todo galitsyn: drop this! + * Later we have to correct the texture position in the case the window is + * out of the parents window frame. So save the shift values for later use. */ + m_RootRect.origin.x = windowFrameSCS.origin.x - childFrameSCS.origin.x; + m_RootRect.origin.y = childFrameSCS.size.height + childFrameSCS.origin.y - (windowFrameSCS.size.height + windowFrameSCS.origin.y); + m_RootRect.size = windowFrameSCS.size; + m_yInvRootOffset = windowFrameSCS.origin.y - childFrameSCS.origin.y; + + DEBUG_MSG(("vboxReshapePerform: [%#p]: m_RootRect pos[%d : %d] size[%d : %d]\n", + (void *)self, (int)m_RootRect.origin.x, (int)m_RootRect.origin.y, (int)m_RootRect.size.width, (int)m_RootRect.size.height)); + + /* Set the new frame. */ + [[self window] setFrame:windowFrameSCS display:YES]; + +#ifndef IN_VMSVGA3D + /* Inform the dock tile view as well. */ + [self reshapeDockTile]; +#endif + + /* Make sure the context is updated accordingly. */ + /* [self updateViewport]; */ + if (m_pSharedGLCtx) + { + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self updateViewportCS]; + + vboxCtxLeave(&CtxInfo); + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (void)createDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pDockScreen = nil; + + [self deleteDockTile]; + + /* Is there a dock tile preview enabled in the GUI? If so setup a + * additional thumbnail view for the dock tile. */ + pDockScreen = [self dockTileScreen]; + if (pDockScreen) + { + m_DockTileView = [[DockOverlayView alloc] init]; + [self reshapeDockTile]; + [pDockScreen addSubview:m_DockTileView]; + } + + COCOA_LOG_FLOW(("%s: returns - m_DockTileView\n", __PRETTY_FUNCTION__, (void *)m_DockTileView)); +} + +- (void)deleteDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); + + if (m_DockTileView != nil) + { + [m_DockTileView removeFromSuperview]; + [m_DockTileView release]; + m_DockTileView = nil; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#endif /* !IN_VMSVGA3D */ + +- (void)makeCurrentFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p m_fNeedCtxUpdate=%d\n", __PRETTY_FUNCTION__, (void *)self, + (void *)m_pGLCtx, m_fNeedCtxUpdate)); + + if (m_pGLCtx) + { + NSOpenGLContext *pPrevCtx = [NSOpenGLContext currentContext]; + +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if (pPrevCtx != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + + if ([m_pGLCtx view] != self) + { +#ifndef IN_VMSVGA3D + /* We change the active view, so flush first */ + if (pPrevCtx != nil) + glFlush(); +#endif + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx setView: self]; + DEBUG_CHECK_GL_ERROR("setView"); + } + +#if 0 + if (pPrevCtx != m_pGLCtx) +#endif + { + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + Assert([NSOpenGLContext currentContext] == m_pGLCtx); + Assert([m_pGLCtx view] == self); + } + + if (m_fNeedCtxUpdate == true) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + + if (!m_FBOId) + { + glGenFramebuffersEXT(1, &m_FBOId); + Assert(m_FBOId); + } + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (bool)vboxSharedCtxCreate +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if (m_pSharedGLCtx) + { + COCOA_LOG_FLOW(("%s: returns true (m_pSharedGLCtx=%p)\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; + } + +#ifndef IN_VMSVGA3D + Assert(!m_pBlitter); + m_pBlitter = RTMemAlloc(sizeof(*m_pBlitter)); + if (RT_UNLIKELY(!m_pBlitter)) + { + DEBUG_WARN(("m_pBlitter allocation failed")); + COCOA_LOG_FLOW(("%s: returns false - m_pBlitter allocation failed\n", __PRETTY_FUNCTION__)); + return false; + } + + int rc = CrBltInit(m_pBlitter, NULL, false /*fCreateNewCtx*/, false /*fForceDrawBlt*/, + &render_spu.GlobalShaders, &render_spu.blitterDispatch); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("CrBltInit failed, rc %d", rc)); + RTMemFree(m_pBlitter); + m_pBlitter = NULL; + + COCOA_LOG_FLOW(("%s: returns false - CrBltInit failed with rc=%Rrc\n", __PRETTY_FUNCTION__, rc)); + return false; + } + + COCOA_LOG_FLOW(("%s: blitter (%p) created successfully for view 0x%p\n", (void *)m_pBlitter, (void *)self)); +#endif /* !IN_VMSVGA3D */ + + /* Create a shared context out of the main context. Use the same pixel format. */ + NSOpenGLPixelFormat *pPixelFormat = [(OverlayOpenGLContext *)m_pGLCtx openGLPixelFormat]; + NSOpenGLContext *pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:m_pGLCtx]; + + /* Set the new context as non opaque */ + GLint opaque = 0; + [pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + + /* Set this view as the drawable for the new context */ + [pSharedGLCtx setView:self]; + m_fNeedViewportUpdate = true; + + m_pSharedGLCtx = pSharedGLCtx; + + COCOA_LOG_FLOW(("%s: returns true - new m_pSharedGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; +} + +- (void)vboxTryDraw +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + glFlush(); + + /* Issue to the gui thread. */ + [self performSelectorOnMainThread:@selector(vboxTryDrawUI) withObject:nil waitUntilDone:NO]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisible:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSNumber *pVisObj = [NSNumber numberWithBool:fVisible]; + [pRunner addObj:self selector:@selector(vboxSetVisibleUIObj:) arg:pVisObj]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUI:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + [self setHidden: !fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisibleObj +{ + COCOA_LOG_FLOW(("%s: self=%p pVisibleObj=%p(%d)\n", __PRETTY_FUNCTION__, + (void *)self, (void *)pVisibleObj, [pVisibleObj boolValue])); + + BOOL fVisible = [pVisibleObj boolValue]; + [self vboxSetVisibleUI:fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparent:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:self selector:@selector(vboxReparentUI:) arg:pParentView]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparentUI:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + /* Make sure the window is removed from any previous parent window. */ + if ([[self overlayWin] parentWindow] != nil) + { + [[[self overlayWin] parentWindow] removeChildWindow:[self overlayWin]]; + } + + /* Set the new parent view */ + [self setParentView: pParentView]; + + /* Add the overlay window as a child to the new parent window */ + if (pParentView != nil) + { + [[pParentView window] addChildWindow:[self overlayWin] ordered:NSWindowAbove]; + if ([self isEverSized]) + [self vboxReshapeOnReparentPerform]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxTryDrawUI +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if ([self isHidden]) + { + COCOA_LOG_FLOW(("%s: returns - request to draw on a hidden view\n", __PRETTY_FUNCTION__)); + return; + } + + if ([[self overlayWin] parentWindow] == nil) + { + COCOA_LOG_FLOW(("%s: returns - request to draw a view w/o a parent\n", __PRETTY_FUNCTION__)); + return; + } + +#ifdef IN_VMSVGA3D + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + Assert(m_pSharedGLCtx); + } +#endif + + const VBOXVR_SCR_COMPOSITOR *pCompositor = NULL; +#ifndef IN_VMSVGA3D + int rc = renderspuVBoxCompositorLock(m_pWinInfo, &pCompositor); + if (RT_FAILURE(rc)) + { + COCOA_LOG_FLOW(("%s: returns - renderspuVBoxCompositorLock failed (%Rrc)\n", __PRETTY_FUNCTION__, rc)); + return; + } + + if (!pCompositor && !m_fCleanupNeeded) + { + renderspuVBoxCompositorUnlock(m_pWinInfo); + COCOA_LOG_FLOW(("%s: returns - noCompositorUI\n", __PRETTY_FUNCTION__)); + return; + } + + VBOXVR_SCR_COMPOSITOR TmpCompositor; + if (pCompositor) + { + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + renderspuVBoxCompositorRelease(m_pWinInfo); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + + Assert(m_pSharedGLCtx); + + pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo); + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (!pCompositor) + { + COCOA_LOG_FLOW(("%s: returns - Failed to reacquire compositor\n", __PRETTY_FUNCTION__)); + return; + } + } + } + else + { + DEBUG_MSG(("%s: NeedCleanup\n", __PRETTY_FUNCTION__)); + Assert(m_fCleanupNeeded); + CrVrScrCompositorInit(&TmpCompositor, NULL); + pCompositor = &TmpCompositor; + } +#endif /* !IN_VMSVGA3D */ + + + if ([self lockFocusIfCanDraw]) + { + COCOA_LOG_FLOW(("%s: Calling vboxPresent\n", __PRETTY_FUNCTION__)); + [self vboxPresent:pCompositor]; + [self unlockFocus]; + } +#ifndef IN_VMSVGA3D /** @todo VMSVGA3 */ + else if (!m_pWinInfo->visible) + { + COCOA_LOG_FLOW(("%s: NotVisible\n", __PRETTY_FUNCTION__)); + m_fCleanupNeeded = false; + } +#endif + else + { + COCOA_LOG_FLOW(("%s: Reschedule\n", __PRETTY_FUNCTION__)); + [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuVBoxCompositorUnlock(m_pWinInfo); +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)swapFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + [m_pGLCtx flushBuffer]; + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresent:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + /*DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void *)self));*/ +#ifndef IN_VMSVGA3D + AssertPtr(pCompositor); +#endif + + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self vboxPresentCS:pCompositor]; + + vboxCtxLeave(&CtxInfo); + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresentCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + if ([m_pSharedGLCtx view] != self) + { + COCOA_LOG_FLOW(("%s: Not current view of shared ctx! Switching... (self=%p, view=%p, m_pSharedGLCtx)\n", + __PRETTY_FUNCTION__, (void *)self, (void *)[m_pSharedGLCtx view], (void *)m_pSharedGLCtx)); + [m_pSharedGLCtx setView: self]; + m_fNeedViewportUpdate = true; + } + + if (m_fNeedViewportUpdate) + { + [self updateViewportCS]; + m_fNeedViewportUpdate = false; + } + + m_fCleanupNeeded = false; + +#ifndef IN_VMSVGA3D + /* Render FBO content to the dock tile when necessary. */ + [self vboxPresentToDockTileCS:pCompositor]; +#endif + + /* change to #if 0 to see thumbnail image */ +#if 1 + [self vboxPresentToViewCS:pCompositor]; +#else + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +DECLINLINE(void) vboxNSRectToRect(const NSRect *pR, RTRECT *pRect) +{ + pRect->xLeft = (int)pR->origin.x; + pRect->yTop = (int)pR->origin.y; + pRect->xRight = (int)(pR->origin.x + pR->size.width); + pRect->yBottom = (int)(pR->origin.y + pR->size.height); +} + +DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x / xStretch); + pRect->yTop = (int)(pR->origin.y / yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) / xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) / yStretch); +} + +DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x * xStretch); + pRect->yTop = (int)(pR->origin.y * yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) * xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) * yStretch); +} + +- (void)vboxPresentToViewCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + NSRect r = [self frame]; + COCOA_LOG_FLOW(("%s: self=%p - r={%d,%d %d,%d}\n", __PRETTY_FUNCTION__, (void *)self, + (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height)); + +#if 1 /* Set to 0 to see the docktile instead of the real output */ + float backingStretchFactor = 1.; +# if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Adjust viewport according to current NSView's backing store parameters. */ + if (render_spu.fUnscaledHiDPI) + { + /* Update stretch factor in order to satisfy current NSView's backing store parameters. */ + backingStretchFactor = [self safeGetBackingScaleFactor]; + } + + NSRect regularBounds = [self bounds]; + NSRect backingBounds = [self safeConvertRectToBacking:®ularBounds]; + glViewport(0, 0, backingBounds.size.width, backingBounds.size.height); + + //crDebug("HiDPI: vboxPresentToViewCS: up-scaling is %s (backingStretchFactor=%d).", + // render_spu.fUnscaledHiDPI ? "OFF" : "ON", (int)backingStretchFactor); +# endif + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + m_fDataVisible = false; + +# ifndef IN_VMSVGA3D + float xStretch; + float yStretch; + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + if (VBoxRectIsZero(&DstRect)) + continue; + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch / backingStretchFactor, yStretch / backingStretchFactor); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA); + + m_fDataVisible = true; + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + } + } +# endif /* !IN_VMSVGA3D */ +#endif + + /* + glFinish(); + */ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)presentComposition:(PCVBOXVR_SCR_COMPOSITOR_ENTRY)pChangedEntry +{ + COCOA_LOG_FLOW(("%s: self=%p pChangedEntry=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pChangedEntry)); + [self vboxTryDraw]; +} + +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + CR_BLITTER_WINDOW WinInfo; + NSRect r; + + if (!m_pBlitter) + return; + + RT_ZERO(WinInfo); + + r = [self frame]; + WinInfo.width = r.size.width; + WinInfo.height = r.size.height; + + Assert(WinInfo.width == m_RootRect.size.width); + Assert(WinInfo.height == m_RootRect.size.height); + + /*CrBltMuralSetCurrentInfo(m_pBlitter, NULL);*/ + + CrBltMuralSetCurrentInfo(m_pBlitter, &WinInfo); + CrBltCheckUpdateViewport(m_pBlitter); +} +#endif /* !IN_VMSVGA3D */ + +#ifndef IN_VMSVGA3D +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +static int g_cVBoxTgaCtr = 0; +# endif +- (void)vboxPresentToDockTileCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + NSRect r = [self frame]; + NSRect rr = NSZeroRect; + NSDockTile *pDT = nil; + float xStretch; + float yStretch; + + if ([m_DockTileView thumbBitmap] != nil) + { + /* + * Only update after at least 200 ms, cause glReadPixels is + * heavy performance wise. + */ + uint64_t msTS = RTTimeSystemMilliTS(); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + if (msTS - m_msDockUpdateTS > 200) + { + m_msDockUpdateTS = msTS; +# if 0 + /** @todo check this for optimization */ + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_STORAGE_HINT_APPLE, + GL_STORAGE_SHARED_APPLE); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, + sizex, sizey, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, myImagePtr); + glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, + 0, 0, 0, 0, 0, image_width, image_height); + glFlush(); + /* Do other work processing here, using a double or triple buffer */ + glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, pixels); +# endif + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + rr = [m_DockTileView frame]; + + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + PCRTRECT paSrcRegions; + PCRTRECT paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + VBoxRectScale(&DstRect, m_FBOThumbScaleX, m_FBOThumbScaleY); + + if (VBoxRectIsZero(&DstRect)) + continue; + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch, yStretch); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags); + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + } + } + + glFinish(); + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glReadBuffer(GL_BACK); + + /* Here the magic of reading the FBO content in our own buffer + * happens. We have to lock this access, in the case the dock + * is updated currently. */ + [m_DockTileView lock]; + glReadPixels(0, m_RootRect.size.height - rr.size.height, rr.size.width, rr.size.height, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, + [[m_DockTileView thumbBitmap] bitmapData]); + [m_DockTileView unlock]; + +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL + ++g_cVBoxTgaCtr; + crDumpNamedTGAF((GLint)rr.size.width, (GLint)rr.size.height, + [[m_DockTileView thumbBitmap] bitmapData], "/Users/leo/vboxdumps/dump%d.tga", g_cVBoxTgaCtr); +# endif + + pDT = [[NSApplication sharedApplication] dockTile]; + + /* Send a display message to the dock tile in the main thread */ + [[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil + waitUntilDone:NO]; + } + } +} +#endif /* !IN_VMSVGA3D */ + +- (void)clearVisibleRegions +{ + if (m_paClipRects) + { + RTMemFree(m_paClipRects); + m_paClipRects = NULL; + } + m_cClipRects = 0; +} + +- (GLboolean)vboxNeedsEmptyPresent +{ + if (m_fDataVisible) + { + m_fCleanupNeeded = true; + return GL_TRUE; + } + + return GL_FALSE; +} + +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects +{ + COCOA_LOG_FLOW(("%s: self=%p cRects=%d paRects=%p\n", __PRETTY_FUNCTION__, (void *)self, cRects, (void *)paRects)); + GLint cOldRects = m_cClipRects; + + [self clearVisibleRegions]; + + if (cRects > 0) + { +#ifdef DEBUG_poetzsch + int i = 0; + for (i = 0; i < cRects; ++i) + DEBUG_MSG_1(("OVIW(%p): setVisibleRegions: %d - %d %d %d %d\n", (void *)self, i, paRects[i * 4], paRects[i * 4 + 1], paRects[i * 4 + 2], paRects[i * 4 + 3])); +#endif + + m_paClipRects = (GLint *)RTMemDup(paRects, sizeof(GLint) * 4 * cRects); + m_cClipRects = cRects; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (NSView *)dockTileScreen +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pContentView = [[[NSApplication sharedApplication] dockTile] contentView]; + NSView *pScreenContent = nil; + + /* + * First try the new variant which checks if this window is within the + * screen which is previewed in the dock. + */ + if ([pContentView respondsToSelector:@selector(screenContentWithParentView:)]) + pScreenContent = [pContentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView]; + /* + * If it fails, fall back to the old variant (VBox...). + */ + else if ([pContentView respondsToSelector:@selector(screenContent)]) + pScreenContent = [pContentView performSelector:@selector(screenContent)]; + + COCOA_LOG_FLOW(("%s: returns %p (pContentView=%p)\n", __PRETTY_FUNCTION__, (void *)pScreenContent, (void *)pContentView)); + return pScreenContent; +} + +- (void)reshapeDockTile +{ + COCOA_LOG_FLOW(("%s:\n", __PRETTY_FUNCTION__)); + NSRect newFrame = NSZeroRect; + NSView *pView = [self dockTileScreen]; + if (pView != nil) + { + NSRect dockFrame = [pView frame]; + /** @todo This is not correct, we should use framebuffer size here, while + * parent view frame size may differ in case of scrolling. */ + NSRect parentFrame = [m_pParentView frame]; + + m_FBOThumbScaleX = (float)dockFrame.size.width / parentFrame.size.width; + m_FBOThumbScaleY = (float)dockFrame.size.height / parentFrame.size.height; + newFrame = NSMakeRect((int)(m_Pos.x * m_FBOThumbScaleX), + (int)(dockFrame.size.height - (m_Pos.y + m_Size.height - m_yInvRootOffset) * m_FBOThumbScaleY), + (int)(m_Size.width * m_FBOThumbScaleX), + (int)(m_Size.height * m_FBOThumbScaleY)); + /* + NSRect newFrame = NSMakeRect ((int)roundf(m_Pos.x * m_FBOThumbScaleX), (int)roundf(dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (int)roundf(m_Size.width * m_FBOThumbScaleX), (int)roundf(m_Size.height * m_FBOThumbScaleY)); + NSRect newFrame = NSMakeRect ((m_Pos.x * m_FBOThumbScaleX), (dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (m_Size.width * m_FBOThumbScaleX), (m_Size.height * m_FBOThumbScaleY)); + printf ("%f %f %f %f - %f %f\n", newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height, m_Size.height, m_FBOThumbScaleY); + */ + [m_DockTileView setFrame: newFrame]; + } + COCOA_LOG_FLOW(("%s: returns - newFrame={%d,%d %d,%d} pView=%d\n", __PRETTY_FUNCTION__, (int)newFrame.origin.x, + (int)newFrame.origin.y, (int)newFrame.size.width, (int)newFrame.size.height, (void *)pView)); +} + +#endif /* !IN_VMSVGA3D */ + +@end /* @implementation OverlayView */ + + +/******************************************************************************** +* +* OpenGL context management +* +********************************************************************************/ +void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxCreate: ppCtx=%p fVisParams=%#x pSharedCtx=%p\n", (void *)ppCtx, fVisParams, (void *)pSharedCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + NSOpenGLPixelFormat *pFmt = vboxCreatePixelFormat(fVisParams); + if (pFmt) + { + *ppCtx = [[OverlayOpenGLContext alloc] initWithFormat:pFmt shareContext:pSharedCtx]; + Assert(*ppCtx); + + /* Enable multi threaded OpenGL engine */ + /* + CGLContextObj cglCtx = [*ppCtx CGLContextObj]; + CGLError err = CGLEnable(cglCtx, kCGLCEMPEngine); + if (err != kCGLNoError) + printf ("Couldn't enable MT OpenGL engine!\n"); + */ + } + else + { + AssertFailed(); + *ppCtx = NULL; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxCreate: returns *ppCtx=%p\n", (void *)*ppCtx)); +} + +void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: pCtx=%p\n", (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [pCtx release]; + /*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: returns\n")); +} + +/******************************************************************************** +* +* View management +* +********************************************************************************/ +static OverlayView *vboxViewCreate(WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("vboxViewCreate: pWinInfo=%p pParentView=%p fVisParams=%#x\n", pWinInfo, (void *)pParentView, fVisParams)); + + /* Create our worker view. */ + OverlayView *pView = [[OverlayView alloc] initWithFrame:NSZeroRect + thread:RTThreadSelf() + parentView:pParentView + winInfo:pWinInfo + fVisParams:fVisParams]; + + if (pView) + { + /* We need a real window as container for the view */ + [[OverlayWindow alloc] initWithParentView:pParentView overlayView:pView]; + /* Return the freshly created overlay view */ + COCOA_LOG_FLOW(("vboxViewCreate: returns %p\n", (void *)pView)); + return pView; + } + + COCOA_LOG_FLOW(("vboxViewCreate: returns NULL\n")); + return NULL; +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_CREATEVIEW +{ + WindowInfo *pWinInfo; + NSView *pParentView; + GLbitfield fVisParams; + /* out */ + OverlayView *pView; +} CR_RCD_CREATEVIEW; + +static DECLCALLBACK(void) vboxRcdCreateView(void *pvCb) +{ + CR_RCD_CREATEVIEW *pCreateView = (CR_RCD_CREATEVIEW *)pvCb; + pCreateView->pView = vboxViewCreate(pCreateView->pWinInfo, pCreateView->pParentView, pCreateView->fVisParams); + COCOA_LOG_FLOW(("vboxRcdCreateView: returns pView=%p\n", (void *)pCreateView->pView)); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("cocoaViewCreate: ppView=%p pWinInfo=%p pParentView=%p fVisParams=%#x\n", + (void *)ppView, (void *)pWinInfo, (void *)pParentView, fVisParams)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + +#ifndef IN_VMSVGA3D + renderspuWinRetain(pWinInfo); + + if (renderspuCalloutAvailable()) + { + CR_RCD_CREATEVIEW CreateView; + CreateView.pWinInfo = pWinInfo; + CreateView.pParentView = pParentView; + CreateView.fVisParams = fVisParams; + CreateView.pView = NULL; + renderspuCalloutClient(vboxRcdCreateView, &CreateView); + *ppView = CreateView.pView; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on createWindow\n")); +#if 0 + dispatch_sync(dispatch_get_main_queue(), ^{ +#endif + *ppView = vboxViewCreate(pWinInfo, pParentView, fVisParams); +#if 0 + }); +#endif + } + +#ifndef IN_VMSVGA3D + if (!*ppView) + renderspuWinRelease(pWinInfo); +#endif + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewCreate: returns *ppView=%p\n", (void *)*ppView)); +} + +#ifndef IN_VMSVGA3D +void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView) +{ + COCOA_LOG_FLOW(("cocoaViewReparent: pView=%p pParentView=%p\n", (void *)pView, (void *)pParentView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + OverlayView *pOView = (OverlayView *)pView; + if (pOView) + [pOView vboxReparent:pParentView]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewReparent: returns\n")); +} +#endif /* !IN_VMSVGA3D */ + +void cocoaViewDestroy(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDestroy: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:pView selector:@selector(vboxDestroy) arg:nil]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDestroy: returns\n")); +} + +#ifndef IN_VMSVGA3D +void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt) +{ + COCOA_LOG_FLOW(("cocoaViewShow: pView=%p fShowIt=%d\n", (void *)pView, fShowIt)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetVisible:fShowIt]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewShow: returns\n")); +} +#endif /* IN_VMSVGA3D */ + +void cocoaViewDisplay(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDisplay: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + +#ifndef IN_VMSVGA3D + DEBUG_WARN(("cocoaViewDisplay should never happen!\n")); + DEBUG_MSG_1(("cocoaViewDisplay %p\n", (void *)pView)); +#endif + [(OverlayView *)pView swapFBO]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDisplay: returns\n")); +} + +void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + COCOA_LOG_FLOW(("cocoaViewSetPosition: pView=%p pParentView=%p x=%d y=%d\n", (void *)pView, (void *)pParentView, x, y)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetPos:NSMakePoint(x, y)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetPosition: returns\n")); +} + +void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy) +{ + COCOA_LOG_FLOW(("cocoaViewSetSize: pView=%p cx=%d cy=%d\n", (void *)pView, cx, cy)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + OverlayView *pOverlayView = (OverlayView *)pView; + + [pOverlayView vboxSetSize:NSMakeSize(cx, cy)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetSize: returns\n")); +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_GETGEOMETRY +{ + OverlayView *pView; + NSRect rect; +} CR_RCD_GETGEOMETRY; + +static DECLCALLBACK(void) vboxRcdGetGeomerty(void *pvUser) +{ + CR_RCD_GETGEOMETRY *pGetGeometry = (CR_RCD_GETGEOMETRY *)pvUser; + pGetGeometry->rect = [[pGetGeometry->pView window] frame]; + COCOA_LOG_FLOW(("vboxRcdGetGeomerty: (x,y)=(%d,%d) (cx,cy)=(%d,%d)\n", pGetGeometry->rect.origin.x, pGetGeometry->rect.origin.y, + pGetGeometry->rect.size.width, pGetGeometry->rect.size.height)); +} + +void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy) +{ + COCOA_LOG_FLOW(("cocoaViewGetGeometry: pView=%p px=%p py=%p pcx=%p pcy=%p\n", + (void *)pView, (void *)px, (void *)py, (void *)pcx, (void *)pcy)); + NSAutoreleasePool *pPool; + pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + + NSRect frame; +#ifndef IN_VMSVGA3D + if (renderspuCalloutAvailable()) + { + CR_RCD_GETGEOMETRY GetGeometry; + GetGeometry.pView = (OverlayView *)pView; + renderspuCalloutClient(vboxRcdGetGeomerty, &GetGeometry); + frame = GetGeometry.rect; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on getGeometry\n")); + frame = [[pView window] frame]; + } + + *px = frame.origin.x; + *py = frame.origin.y; + *pcx = frame.size.width; + *pcy = frame.size.height; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewGetGeometry: returns *px=%d, *py=%d, *pcx=%d, *pcy=%d\n", *px, *py, *pcx, *pcy)); +} + +void cocoaViewPresentComposition(NativeNSViewRef pView, PCVBOXVR_SCR_COMPOSITOR_ENTRY pChangedEntry) +{ + COCOA_LOG_FLOW(("cocoaViewPresentComposition: pView=%p pChangedEntry=%p\n", (void *)pView, (void *)pChangedEntry)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + NSOpenGLContext *pCtx; + +# ifdef IN_VMSVGA3D + Assert([(OverlayView *)pView glCtx]); + +# else + /* The view may not necesserily have a GL context set. */ + pCtx = [(OverlayView *)pView glCtx]; + if (!pCtx) + { + ContextInfo *pCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pCtxInfo) + { + DEBUG_WARN(("renderspuDefaultSharedContextAcquire returned NULL")); + + [pPool release]; + DEBUG_FUNC_LEAVE(); + return; + } + + pCtx = pCtxInfo->context; + + [(OverlayView *)pView setGLCtx:pCtx]; + } +# endif + + [(OverlayView *)pView presentComposition:pChangedEntry]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewPresentComposition: returns\n")); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: pView=%p pCtx=%p\n", (void *)pView, (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + if (pView) + { + [(OverlayView *)pView setGLCtx:pCtx]; + [(OverlayView *)pView makeCurrentFBO]; + } + else + { +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if ([NSOpenGLContext currentContext] != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + [NSOpenGLContext clearCurrentContext]; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: returns\n")); +} + +#ifndef IN_VMSVGA3D + +GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + GLboolean fNeedsPresent = [(OverlayView *)pView vboxNeedsEmptyPresent]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: returns %d\n", fNeedsPresent)); + return fNeedsPresent; +} + +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects) +{ + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: pView=%p cRects=%d paRects=%p)\n", (void *)pView, cRects, (void const *)paRects)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView setVisibleRegions:cRects paRects:paRects]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: returns\n")); +} + +#endif /* IN_VMSVGA3D */ + +#ifdef IN_VMSVGA3D +/* + * VMSVGA3D interface. + */ + +VMSVGA3DCOCOA_DECL(bool) vmsvga3dCocoaCreateViewAndContext(NativeNSViewRef *ppView, NativeNSOpenGLContextRef *ppCtx, + NativeNSViewRef pParentView, uint32_t cx, uint32_t cy, + NativeNSOpenGLContextRef pSharedCtx, bool fOtherProfile) +{ + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + GLbitfield fVisParams = CR_ALPHA_BIT | CR_DEPTH_BIT | CR_DOUBLE_BIT | (fOtherProfile ? VMSVGA3D_NON_DEFAULT_PROFILE_BIT : 0); + bool fRc = false; + + cocoaGLCtxCreate(ppCtx, fVisParams, pSharedCtx); + if (*ppCtx) + { + cocoaViewCreate(ppView, NULL, pParentView, fVisParams); + if (*ppView) + { + cocoaViewSetSize(*ppView, cx, cy); + [(OverlayView *)*ppView setGLCtx: *ppCtx]; + fRc = true; + } + else + [*ppCtx release]; + } + + [pPool release]; + return fRc; +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaDestroyViewAndContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + cocoaGLCtxDestroy(pCtx); + cocoaViewDestroy(pView); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + cocoaViewSetPosition(pView, pParentView, x, y); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetSize(NativeNSViewRef pView, int w, int h) +{ + cocoaViewSetSize(pView, w, h); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + Assert(!pView || [(OverlayView *)pView glCtx] == pCtx || [(OverlayView *)pView glCtx] == nil); + cocoaViewMakeCurrentContext(pView, pCtx); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaSwapBuffers(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ +# if 1 + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + cocoaViewDisplay(pView); +# else + DEBUG_FUNC_ENTER(); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + + [pCtx flushBuffer]; + + [pPool release]; + DEBUG_FUNC_LEAVE(); +# endif +} + +#endif /* IN_VMSVGA3D */ diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c new file mode 100644 index 00000000..624a051f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c @@ -0,0 +1,392 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "renderspu.h" + +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_error.h" +#include "cr_environment.h" +#include "cr_url.h" + + +static void set_window_geometry( RenderSPU *render_spu, const char *response ) +{ + int x, y, w, h; + CRASSERT(response[0] == '['); + sscanf( response, "[ %d, %d, %d, %d ]", &x, &y, &w, &h ); + render_spu->defaultX = (int) x; + render_spu->defaultY = (int) y; + render_spu->defaultWidth = (unsigned int) w; + render_spu->defaultHeight = (unsigned int) h; +} + +static void set_default_visual( RenderSPU *render_spu, const char *response ) +{ + if (crStrlen(response) > 0) { + if (crStrstr(response, "rgb")) + render_spu->default_visual |= CR_RGB_BIT; + if (crStrstr(response, "alpha")) + render_spu->default_visual |= CR_ALPHA_BIT; + if (crStrstr(response, "z") || crStrstr(response, "depth")) + render_spu->default_visual |= CR_DEPTH_BIT; + if (crStrstr(response, "stencil")) + render_spu->default_visual |= CR_STENCIL_BIT; + if (crStrstr(response, "accum")) + render_spu->default_visual |= CR_ACCUM_BIT; + if (crStrstr(response, "stereo")) + render_spu->default_visual |= CR_STEREO_BIT; + if (crStrstr(response, "multisample")) + render_spu->default_visual |= CR_MULTISAMPLE_BIT; + if (crStrstr(response, "double")) + render_spu->default_visual |= CR_DOUBLE_BIT; + if (crStrstr(response, "pbuffer")) + render_spu->default_visual |= CR_PBUFFER_BIT; + } +} + +static void set_display_string( RenderSPU *render_spu, const char *response ) +{ + if (!crStrcmp(response, "DEFAULT")) { + const char *display = crGetenv("DISPLAY"); + if (display) + crStrncpy(render_spu->display_string, + display, + sizeof(render_spu->display_string)); + else + crStrcpy(render_spu->display_string, ""); /* empty string */ + } + else { + crStrncpy(render_spu->display_string, + response, + sizeof(render_spu->display_string)); + } +} + +static void set_fullscreen( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->fullscreen) ); +} + +static void set_on_top( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->ontop) ); +} + +static void set_system_gl_path( RenderSPU *render_spu, const char *response ) +{ + if (crStrlen(response) > 0) + crSetenv( "CR_SYSTEM_GL_PATH", response ); +} + +static void set_title( RenderSPU *render_spu, const char *response ) +{ + crFree( render_spu->window_title ); + render_spu->window_title = crStrdup( response ); +} + +#if defined(GLX) +static void set_try_direct( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->try_direct) ); +} + +static void set_force_direct( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->force_direct) ); +} +#endif /* GLX */ + +static void render_to_app_window( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->render_to_app_window) ); +} + +static void render_to_crut_window( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->render_to_crut_window) ); +} + +static void resizable( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->resizable) ); +} + +static void set_borderless( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->borderless) ); +} + +static void set_cursor( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->drawCursor) ); +} + +static void gather_url( RenderSPU *render_spu, const char *response ) +{ + char protocol[4096], hostname[4096]; + unsigned short port; + + if (!crParseURL(response, protocol, hostname, &port, 0)) + { + crError( "Malformed URL: \"%s\"", response ); + } + + render_spu->gather_port = port; +} + +static void gather_userbuf( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->gather_userbuf_size) ); +} + +static void set_lut8( RenderSPU *render_spu, const char *response ) +{ + int a; + char **lut; + + if (!response[0]) return; + + lut = crStrSplit(response, ","); + if (!lut) return; + + for (a=0; a<256; a++) + { + render_spu->lut8[0][a] = crStrToInt(lut[a]); + render_spu->lut8[1][a] = crStrToInt(lut[256+a]); + render_spu->lut8[2][a] = crStrToInt(lut[512+a]); + } + + crFreeStrings(lut); + + render_spu->use_lut8 = 1; +} + +static void set_master_url ( RenderSPU *render_spu, char *response ) +{ + if (response[0]) + render_spu->swap_master_url = crStrdup( response ); + else + render_spu->swap_master_url = NULL; +} + +static void set_is_master ( RenderSPU *render_spu, char *response ) +{ + render_spu->is_swap_master = crStrToInt( response ); +} + +static void set_num_clients ( RenderSPU *render_spu, char *response ) +{ + render_spu->num_swap_clients = crStrToInt( response ); +} + +static void set_use_osmesa ( RenderSPU *render_spu, char *response ) +{ + int val = crStrToInt( response ); +#ifdef USE_OSMESA + render_spu->use_osmesa = val; +#else + if (val != 0) + crError( "renderspu with Conf(use_osmesa, 1) but not compiled with -DUSE_OSMESA"); +#endif +} + +static void set_nv_swap_group( RenderSPU *render_spu, char *response ) +{ + render_spu->nvSwapGroup = crStrToInt( response ); + if (render_spu->nvSwapGroup < 0) + render_spu->nvSwapGroup = 0; +} + +static void set_ignore_papi( RenderSPU *render_spu, char *response ) +{ + render_spu->ignore_papi = crStrToInt( response ); +} + +static void set_ignore_window_moves( RenderSPU *render_spu, char *response ) +{ + render_spu->ignore_window_moves = crStrToInt( response ); +} + +static void set_pbuffer_size( RenderSPU *render_spu, const char *response ) +{ + CRASSERT(response[0] == '['); + sscanf( response, "[ %d, %d ]", + &render_spu->pbufferWidth, &render_spu->pbufferHeight); +} + +static void set_use_glxchoosevisual( RenderSPU *render_spu, char *response ) +{ + render_spu->use_glxchoosevisual = crStrToInt( response ); +} + +static void set_draw_bbox( RenderSPU *render_spu, char *response ) +{ + render_spu->draw_bbox = crStrToInt( response ); +} + + + +/* option, type, nr, default, min, max, title, callback + */ +SPUOptions renderSPUOptions[] = { + { "title", CR_STRING, 1, "Chromium Render SPU", NULL, NULL, + "Window Title", (SPUOptionCB)set_title }, + + { "window_geometry", CR_INT, 4, "[0, 0, 256, 256]", "[0, 0, 1, 1]", NULL, + "Default Window Geometry (x,y,w,h)", (SPUOptionCB)set_window_geometry }, + + { "fullscreen", CR_BOOL, 1, "0", NULL, NULL, + "Full-screen Window", (SPUOptionCB)set_fullscreen }, + + { "resizable", CR_BOOL, 1, "0", NULL, NULL, + "Resizable Window", (SPUOptionCB)resizable }, + + { "on_top", CR_BOOL, 1, "0", NULL, NULL, + "Display on Top", (SPUOptionCB)set_on_top }, + + { "borderless", CR_BOOL, 1, "0", NULL, NULL, + "Borderless Window", (SPUOptionCB) set_borderless }, + + { "default_visual", CR_STRING, 1, "rgb, double, depth", NULL, NULL, + "Default GL Visual", (SPUOptionCB) set_default_visual }, + +#if defined(GLX) + { "try_direct", CR_BOOL, 1, "1", NULL, NULL, + "Try Direct Rendering", (SPUOptionCB)set_try_direct }, + + { "force_direct", CR_BOOL, 1, "0", NULL, NULL, + "Force Direct Rendering", (SPUOptionCB)set_force_direct }, +#endif + + { "render_to_app_window", CR_BOOL, 1, "0", NULL, NULL, + "Render to Application window", (SPUOptionCB)render_to_app_window }, + + { "render_to_crut_window", CR_BOOL, 1, "0", NULL, NULL, + "Render to CRUT window", (SPUOptionCB)render_to_crut_window }, + + { "show_cursor", CR_BOOL, 1, "0", NULL, NULL, + "Show Software Cursor", (SPUOptionCB) set_cursor }, + + { "system_gl_path", CR_STRING, 1, "", NULL, NULL, + "System GL Path", (SPUOptionCB)set_system_gl_path }, + + { "display_string", CR_STRING, 1, "DEFAULT", NULL, NULL, + "X Display String", (SPUOptionCB)set_display_string }, + + { "gather_url", CR_STRING, 1, "", NULL, NULL, + "Gatherer URL", (SPUOptionCB)gather_url}, + + { "gather_userbuf_size", CR_INT, 1, "0", NULL, NULL, + "Size of Buffer to Allocate for Gathering", (SPUOptionCB)gather_userbuf}, + + { "lut8", CR_STRING, 1, "", NULL, NULL, + "8 bit RGB LUT", (SPUOptionCB)set_lut8}, + + { "swap_master_url", CR_STRING, 1, "", NULL, NULL, + "The URL to the master swapper", (SPUOptionCB)set_master_url }, + + { "is_swap_master", CR_BOOL, 1, "0", NULL, NULL, + "Is this the swap master", (SPUOptionCB)set_is_master }, + + { "num_swap_clients", CR_INT, 1, "1", NULL, NULL, + "How many swaps to wait on", (SPUOptionCB)set_num_clients }, + + { "use_osmesa", CR_BOOL, 1, "0", NULL, NULL, + "Use offscreen rendering with Mesa", (SPUOptionCB)set_use_osmesa }, + + { "nv_swap_group", CR_INT, 1, "0", NULL, NULL, + "NVIDIA Swap Group Number", (SPUOptionCB) set_nv_swap_group }, + + { "ignore_papi", CR_BOOL, 1, "0", NULL, NULL, + "Ignore Barrier and Semaphore calls", (SPUOptionCB) set_ignore_papi }, + + { "ignore_window_moves", CR_BOOL, 1, "0", NULL, NULL, + "Ignore crWindowPosition calls", (SPUOptionCB) set_ignore_window_moves }, + + { "pbuffer_size", CR_INT, 2, "[0, 0]", "[0, 0]", NULL, + "Maximum PBuffer Size", (SPUOptionCB) set_pbuffer_size }, + + { "use_glxchoosevisual", CR_BOOL, 1, "1", NULL, NULL, + "Use glXChooseVisual", (SPUOptionCB) set_use_glxchoosevisual }, + + { "draw_bbox", CR_BOOL, 1, "0", NULL, NULL, + "Draw Bounding Boxes", (SPUOptionCB) set_draw_bbox }, + { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL }, +}; + + +void renderspuSetVBoxConfiguration( RenderSPU *render_spu ) +{ + int a; + + for (a=0; a<256; a++) + { + render_spu->lut8[0][a] = + render_spu->lut8[1][a] = + render_spu->lut8[2][a] = a; + } + render_spu->use_lut8 = 0; + + set_title(render_spu, "Chromium Render SPU"); + set_window_geometry(render_spu, "[0, 0, 0, 0]"); + set_fullscreen(render_spu, "0"); + resizable(render_spu, "0"); + set_on_top(render_spu, "1"); + set_borderless(render_spu, "1"); + set_default_visual(render_spu, "rgb, double, depth"); +#if defined(GLX) + set_try_direct(render_spu, "1"); + set_force_direct(render_spu, "0"); +#endif + render_to_app_window(render_spu, "0"); + render_to_crut_window(render_spu, "0"); + set_cursor(render_spu, "0"); + set_system_gl_path(render_spu, ""); + set_display_string(render_spu, "DEFAULT"); + gather_url(render_spu, ""); + gather_userbuf(render_spu, "0"); + set_lut8(render_spu, ""); + set_master_url(render_spu, ""); + set_is_master(render_spu, "0"); + set_num_clients(render_spu, "1"); + set_use_osmesa(render_spu, "0"); + set_nv_swap_group(render_spu, "0"); + set_ignore_papi(render_spu, "0"); + set_ignore_window_moves(render_spu, "0"); + set_pbuffer_size(render_spu, "[0, 0]"); + set_use_glxchoosevisual(render_spu, "1"); + set_draw_bbox(render_spu, "0"); + + render_spu->swap_mtu = 1024 * 500; + + /* Some initialization that doesn't really have anything to do + * with configuration but which was done here before: + */ + render_spu->use_L2 = 0; + render_spu->cursorX = 0; + render_spu->cursorY = 0; +#if defined(GLX) + render_spu->sync = 0; +#endif + + /* Config of "render force present main thread" (currently implemented by glx and wgl). */ + { + const char *forcePresent = crGetenv("CR_RENDER_FORCE_PRESENT_MAIN_THREAD"); + if (forcePresent) + render_spu->force_present_main_thread = crStrToInt(forcePresent) ? 1 : 0; + else + { +#if defined(GLX) + /* Customer needed this for avoiding system 3D driver bugs. */ + render_spu->force_present_main_thread = 1; +#else + render_spu->force_present_main_thread = 0; +#endif + } + } +} + diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c new file mode 100644 index 00000000..8a1a61ff --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c @@ -0,0 +1,2042 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ +#if 00 /*TEMPORARY*/ +#include <unistd.h> +#include "cr_rand.h" +#endif + +#include <GL/glx.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xmu/StdCmap.h> +#include <X11/Xatom.h> +#include <X11/extensions/shape.h> +#include <sys/time.h> +#include <stdio.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_process.h" +#include "renderspu.h" + + +/* + * Stuff from MwmUtils.h + */ +typedef struct +{ + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} PropMotifWmHints; + +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 +#define MWM_HINTS_DECORATIONS (1L << 1) + + +#define WINDOW_NAME window->title + +static Bool WindowExistsFlag; + +static int +WindowExistsErrorHandler( Display *dpy, XErrorEvent *xerr ) +{ + if (xerr->error_code == BadWindow) + { + WindowExistsFlag = GL_FALSE; + } + return 0; +} + +static GLboolean +WindowExists( Display *dpy, Window w ) +{ + XWindowAttributes xwa; + int (*oldXErrorHandler)(Display *, XErrorEvent *); + + WindowExistsFlag = GL_TRUE; + oldXErrorHandler = XSetErrorHandler(WindowExistsErrorHandler); + XGetWindowAttributes(dpy, w, &xwa); /* dummy request */ + XSetErrorHandler(oldXErrorHandler); + return WindowExistsFlag; +} + +static Colormap +GetLUTColormap( Display *dpy, XVisualInfo *vi ) +{ + int a; + XColor col; + Colormap cmap; + +#if defined(__cplusplus) || defined(c_plusplus) + int localclass = vi->c_class; /* C++ */ +#else + int localclass = vi->class; /* C */ +#endif + + if ( localclass != DirectColor ) + { + crError( "No support for non-DirectColor visuals with LUTs" ); + } + + cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocAll ); + + for (a=0; a<256; a++) + { + col.red = render_spu.lut8[0][a]<<8; + col.green = col.blue = 0; + col.pixel = a<<16; + col.flags = DoRed; + XStoreColor(dpy, cmap, &col); + } + + for (a=0; a<256; a++) + { + col.green = render_spu.lut8[1][a]<<8; + col.red = col.blue = 0; + col.pixel = a<<8; + col.flags = DoGreen; + XStoreColor(dpy, cmap, &col); + } + + for (a=0; a<256; a++) + { + col.blue = render_spu.lut8[2][a]<<8; + col.red = col.green= 0; + col.pixel = a; + col.flags = DoBlue; + XStoreColor(dpy, cmap, &col); + } + + return cmap; +} + +static Colormap +GetShareableColormap( Display *dpy, XVisualInfo *vi ) +{ + Status status; + XStandardColormap *standardCmaps; + Colormap cmap; + int i, numCmaps; + +#if defined(__cplusplus) || defined(c_plusplus) + int localclass = vi->c_class; /* C++ */ +#else + int localclass = vi->class; /* C */ +#endif + + if ( localclass != TrueColor ) + { + crError( "No support for non-TrueColor visuals." ); + } + + status = XmuLookupStandardColormap( dpy, vi->screen, vi->visualid, + vi->depth, XA_RGB_DEFAULT_MAP, + False, True ); + + if ( status == 1 ) + { + status = XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen), + &standardCmaps, &numCmaps, + XA_RGB_DEFAULT_MAP ); + if ( status == 1 ) + { + for (i = 0 ; i < numCmaps ; i++) + { + if (standardCmaps[i].visualid == vi->visualid) + { + cmap = standardCmaps[i].colormap; + XFree( standardCmaps); + return cmap; + } + } + } + } + + cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocNone ); + return cmap; +} + + +static int +WaitForMapNotify( Display *display, XEvent *event, char *arg ) +{ + (void)display; + return ( event->type == MapNotify && event->xmap.window == (Window)arg ); +} + + +/** + * Return the X Visual ID of the given window + */ +static int +GetWindowVisualID( Display *dpy, Window w ) +{ + XWindowAttributes attr; + int k = XGetWindowAttributes(dpy, w, &attr); + if (!k) + return -1; + return attr.visual->visualid; +} + + +/** + * Wrapper for glXGetConfig(). + */ +static int +Attrib( const VisualInfo *visual, int attrib ) +{ + int value = 0; + render_spu.ws.glXGetConfig( visual->dpy, visual->visual, attrib, &value ); + return value; +} + + + +/** + * Find a visual with the specified attributes. If we fail, turn off an + * attribute (like multisample or stereo) and try again. + */ +static XVisualInfo * +chooseVisualRetry( Display *dpy, int screen, GLbitfield visAttribs ) +{ + while (1) { + XVisualInfo *vis = crChooseVisual(&render_spu.ws, dpy, screen, + (GLboolean) render_spu.use_lut8, + visAttribs); + if (vis) + return vis; + + if (visAttribs & CR_MULTISAMPLE_BIT) + visAttribs &= ~CR_MULTISAMPLE_BIT; + else if (visAttribs & CR_OVERLAY_BIT) + visAttribs &= ~CR_OVERLAY_BIT; + else if (visAttribs & CR_STEREO_BIT) + visAttribs &= ~CR_STEREO_BIT; + else if (visAttribs & CR_ACCUM_BIT) + visAttribs &= ~CR_ACCUM_BIT; + else if (visAttribs & CR_ALPHA_BIT) + visAttribs &= ~CR_ALPHA_BIT; + else + return NULL; + } +} + + +/** + * Get an FBconfig for the specified attributes + */ +#ifdef GLX_VERSION_1_3 +static GLXFBConfig +chooseFBConfig( Display *dpy, int screen, GLbitfield visAttribs ) +{ + GLXFBConfig *fbconfig; + int attribs[1000], attrCount = 0, numConfigs; + int major, minor; + + CRASSERT(visAttribs & CR_PBUFFER_BIT); + + /* Make sure pbuffers are supported */ + render_spu.ws.glXQueryVersion(dpy, &major, &minor); + if (major * 100 + minor < 103) { + crWarning("Render SPU: GLX %d.%d doesn't support pbuffers", major, minor); + return 0; + } + + attribs[attrCount++] = GLX_DRAWABLE_TYPE; + attribs[attrCount++] = GLX_PBUFFER_BIT; + + if (visAttribs & CR_RGB_BIT) { + attribs[attrCount++] = GLX_RENDER_TYPE; + attribs[attrCount++] = GLX_RGBA_BIT; + attribs[attrCount++] = GLX_RED_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_GREEN_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_BLUE_SIZE; + attribs[attrCount++] = 1; + if (visAttribs & CR_ALPHA_BIT) { + attribs[attrCount++] = GLX_ALPHA_SIZE; + attribs[attrCount++] = 1; + } + } + + if (visAttribs & CR_DEPTH_BIT) { + attribs[attrCount++] = GLX_DEPTH_SIZE; + attribs[attrCount++] = 1; + } + + if (visAttribs & CR_DOUBLE_BIT) { + attribs[attrCount++] = GLX_DOUBLEBUFFER; + attribs[attrCount++] = True; + } + else { + /* don't care */ + } + + if (visAttribs & CR_STENCIL_BIT) { + attribs[attrCount++] = GLX_STENCIL_SIZE; + attribs[attrCount++] = 1; + } + + if (visAttribs & CR_ACCUM_BIT) { + attribs[attrCount++] = GLX_ACCUM_RED_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_ACCUM_GREEN_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_ACCUM_BLUE_SIZE; + attribs[attrCount++] = 1; + if (visAttribs & CR_ALPHA_BIT) { + attribs[attrCount++] = GLX_ACCUM_ALPHA_SIZE; + attribs[attrCount++] = 1; + } + } + + if (visAttribs & CR_MULTISAMPLE_BIT) { + attribs[attrCount++] = GLX_SAMPLE_BUFFERS_SGIS; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_SAMPLES_SGIS; + attribs[attrCount++] = 4; + } + + if (visAttribs & CR_STEREO_BIT) { + attribs[attrCount++] = GLX_STEREO; + attribs[attrCount++] = 1; + } + + /* terminate */ + attribs[attrCount++] = 0; + + fbconfig = render_spu.ws.glXChooseFBConfig(dpy, screen, attribs, &numConfigs); + if (!fbconfig || numConfigs == 0) { + /* no matches! */ + return 0; + } + if (numConfigs == 1) { + /* one match */ + return fbconfig[0]; + } + else { + /* found several matches - try to find best one */ + int i; + + crDebug("Render SPU: glXChooseFBConfig found %d matches for visBits 0x%x", + numConfigs, visAttribs); + /* Skip/omit configs that have unneeded Z buffer or unneeded double + * buffering. Possible add other tests in the future. + */ + for (i = 0; i < numConfigs; i++) { + int zBits, db; + render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i], + GLX_DEPTH_SIZE, &zBits); + if ((visAttribs & CR_DEPTH_BIT) == 0 && zBits > 0) { + /* omit fbconfig with unneeded Z */ + continue; + } + render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i], + GLX_DOUBLEBUFFER, &db); + if ((visAttribs & CR_DOUBLE_BIT) == 0 && db) { + /* omit fbconfig with unneeded DB */ + continue; + } + + /* if we get here, use this config */ + return fbconfig[i]; + } + + /* if we get here, we didn't find a better fbconfig */ + return fbconfig[0]; + } +} +#endif /* GLX_VERSION_1_3 */ + +static const char * renderspuGetDisplayName() +{ + const char *dpyName; + + if (render_spu.display_string[0]) + dpyName = render_spu.display_string; + else + { + crWarning("Render SPU: no display.."); + dpyName = NULL; + } + return dpyName; +} + +static int renderspuWinCmdWinCreate(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdWinDestroy(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!crHashtableAllocRegisterKey(render_spu.windowTable, CR_RENDER_WINCMD_ID)) + { + crError("CR_RENDER_WINCMD_ID %d is occupied already", CR_RENDER_WINCMD_ID); + return VERR_INVALID_STATE; + } + + render_spu.pWinToInfoTable = crAllocHashtable(); + if (render_spu.pWinToInfoTable) + { + dpyName = renderspuGetDisplayName(); + if (dpyName) + { + GLboolean bRc = renderspuInitVisual(&render_spu.WinCmdVisual, dpyName, render_spu.default_visual); + if (bRc) + { + bRc = renderspuWinInitWithVisual(&render_spu.WinCmdWindow, &render_spu.WinCmdVisual, GL_FALSE, CR_RENDER_WINCMD_ID); + if (bRc) + { + XSelectInput(render_spu.WinCmdVisual.dpy, render_spu.WinCmdWindow.window, StructureNotifyMask); + render_spu.WinCmdAtom = XInternAtom(render_spu.WinCmdVisual.dpy, "VBoxWinCmd", False); + CRASSERT(render_spu.WinCmdAtom != None); + return VINF_SUCCESS; + } + else + { + crError("renderspuWinInitWithVisual failed"); + } + /* there is no visual destroy impl currently + * @todo: implement */ + } + else + { + crError("renderspuInitVisual failed"); + } + } + else + { + crError("Render SPU: no display, aborting"); + } + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + render_spu.pWinToInfoTable = NULL; + } + else + { + crError("crAllocHashtable failed"); + } + return rc; +} + +static void renderspuWinCmdTerm() +{ + /* the window is not in the table, this will just ensure the key is freed */ + crHashtableDelete(render_spu.windowTable, CR_RENDER_WINCMD_ID, NULL); + renderspuWinCleanup(&render_spu.WinCmdWindow); + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + /* we do not have visual destroy functionality + * @todo implement */ +} + + +static bool renderspuWinCmdProcess(CR_RENDER_WINCMD* pWinCmd) +{ + bool fExit = false; + /* process commands */ + switch (pWinCmd->enmCmd) + { + case CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE: + crHashtableAdd(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, pWinCmd->pWindow); + XSelectInput(render_spu.WinCmdVisual.dpy, pWinCmd->pWindow->window, ExposureMask); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY: + crHashtableDelete(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, NULL); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_NOP: + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_EXIT: + renderspuWinCmdTerm(); + pWinCmd->rc = VINF_SUCCESS; + fExit = true; + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_CREATE: + pWinCmd->rc = renderspuWinCmdWinCreate(pWinCmd->pWindow); + break; + case CR_RENDER_WINCMD_TYPE_WIN_DESTROY: + pWinCmd->rc = renderspuWinCmdWinDestroy(pWinCmd->pWindow); + break; + default: + crError("unknown WinCmd command! %d", pWinCmd->enmCmd); + pWinCmd->rc = VERR_INVALID_PARAMETER; + break; + } + + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + return fExit; +} + +static DECLCALLBACK(int) renderspuWinCmdThreadProc(RTTHREAD ThreadSelf, void *pvUser) +{ + int rc; + bool fExit = false; + crDebug("RenderSPU: Window thread started (%x)", crThreadID()); + + rc = renderspuWinCmdInit(); + + /* notify the main cmd thread that we have started */ + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + + if (!RT_SUCCESS(rc)) + { + CRASSERT(!render_spu.pWinToInfoTable); + return rc; + } + + do + { + XEvent event; + XNextEvent(render_spu.WinCmdVisual.dpy, &event); + + switch (event.type) + { + case ClientMessage: + { + CRASSERT(event.xclient.window == render_spu.WinCmdWindow.window); + if (event.xclient.window == render_spu.WinCmdWindow.window) + { + if (render_spu.WinCmdAtom == event.xclient.message_type) + { + CR_RENDER_WINCMD *pWinCmd; + memcpy(&pWinCmd, event.xclient.data.b, sizeof (pWinCmd)); + fExit = renderspuWinCmdProcess(pWinCmd); + } + } + + break; + } + case Expose: + { + if (!event.xexpose.count) + { + WindowInfo *pWindow = (WindowInfo*)crHashtableSearch(render_spu.pWinToInfoTable, event.xexpose.window); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 0, false); + renderspuVBoxCompositorRelease(pWindow); + } + } + } + break; + } + default: + break; + } + } while (!fExit); + + return 0; +} + +static int renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE enmCmd, WindowInfo *pWindow) +{ + Status status; + XEvent event; + CR_RENDER_WINCMD WinCmd, *pWinCmd; + int rc; + + pWinCmd = &WinCmd; + pWinCmd->enmCmd = enmCmd; + pWinCmd->rc = VERR_GENERAL_FAILURE; + pWinCmd->pWindow = pWindow; + + memset(&event, 0, sizeof (event)); + event.type = ClientMessage; + event.xclient.window = render_spu.WinCmdWindow.window; + event.xclient.message_type = render_spu.WinCmdAtom; + event.xclient.format = 8; + memcpy(event.xclient.data.b, &pWinCmd, sizeof (pWinCmd)); + + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, StructureNotifyMask, &event); + if (!status) + { + Assert(0); + crWarning("XSendEvent returned null"); + return VERR_GENERAL_FAILURE; + } + + XFlush(render_spu.pCommunicationDisplay); + rc = RTSemEventWaitNoResume(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventWaitNoResume failed rc %d", rc); + return rc; + } + return pWinCmd->rc; +} + +int renderspu_SystemInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!render_spu.use_glxchoosevisual) { + /* sometimes want to set this option with ATI drivers */ + render_spu.ws.glXChooseVisual = NULL; + } + + /* setup communication display connection */ + dpyName = renderspuGetDisplayName(); + if (!dpyName) + { + crWarning("no display name, aborting"); + return VERR_GENERAL_FAILURE; + } + + render_spu.pCommunicationDisplay = XOpenDisplay(dpyName); + if (!render_spu.pCommunicationDisplay) + { + crWarning( "Couldn't open X display named '%s'", dpyName ); + return VERR_GENERAL_FAILURE; + } + + if ( !render_spu.ws.glXQueryExtension( render_spu.pCommunicationDisplay, NULL, NULL ) ) + { + crWarning( "Render SPU: Display %s doesn't support GLX", dpyName ); + return VERR_GENERAL_FAILURE; + } + + rc = RTSemEventCreate(&render_spu.hWinCmdCompleteEvent); + if (RT_SUCCESS(rc)) + { + rc = RTThreadCreate(&render_spu.hWinCmdThread, renderspuWinCmdThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxCrWinCmd"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + return VINF_SUCCESS; + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + } + else + { + crWarning("RTThreadCreate failed rc %d", rc); + } + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + + return rc; +} + +int renderspu_SystemTerm() +{ + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_EXIT, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("renderspuWinCmdSubmit EXIT failed rc %d", rc); + return rc; + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + return VINF_SUCCESS; +} + +GLboolean +renderspu_SystemInitVisual( VisualInfo *visual ) +{ + const char *dpyName; + int screen; + + CRASSERT(visual); + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + /* A dummy visual - being non null is enough. */ + visual->visual =(XVisualInfo *) "os"; + return GL_TRUE; + } +#endif + + dpyName = renderspuGetDisplayName(); + if (!dpyName) + { + crWarning("Render SPU: no display, aborting"); + return GL_FALSE; + } + + + crInfo("Render SPU: Opening display %s", dpyName); + + if (dpyName && + (crStrncmp(dpyName, "localhost:11", 12) == 0 || + crStrncmp(dpyName, "localhost:12", 12) == 0 || + crStrncmp(dpyName, "localhost:13", 12) == 0)) { + /* Issue both debug and warning messages to make sure the + * message gets noticed! + */ + crDebug("Render SPU: display string looks like a proxy X server!"); + crDebug("Render SPU: This is usually a problem!"); + crWarning("Render SPU: display string looks like a proxy X server!"); + crWarning("Render SPU: This is usually a problem!"); + } + + visual->dpy = XOpenDisplay(dpyName); + if (!visual->dpy) + { + crWarning( "Couldn't open X display named '%s'", dpyName ); + return GL_FALSE; + } + + if ( !render_spu.ws.glXQueryExtension( visual->dpy, NULL, NULL ) ) + { + crWarning( "Render SPU: Display %s doesn't support GLX", visual->displayName ); + return GL_FALSE; + } + + screen = DefaultScreen(visual->dpy); + +#ifdef GLX_VERSION_1_3 + if (visual->visAttribs & CR_PBUFFER_BIT) + { + visual->fbconfig = chooseFBConfig(visual->dpy, screen, visual->visAttribs); + if (!visual->fbconfig) { + char s[1000]; + renderspuMakeVisString( visual->visAttribs, s ); + crWarning( "Render SPU: Display %s doesn't have the necessary fbconfig: %s", + dpyName, s ); + XCloseDisplay(visual->dpy); + return GL_FALSE; + } + } + else +#endif /* GLX_VERSION_1_3 */ + { + visual->visual = chooseVisualRetry(visual->dpy, screen, visual->visAttribs); + if (!visual->visual) { + char s[1000]; + renderspuMakeVisString( visual->visAttribs, s ); + crWarning("Render SPU: Display %s doesn't have the necessary visual: %s", + dpyName, s ); + XCloseDisplay(visual->dpy); + return GL_FALSE; + } + } + + if ( render_spu.sync ) + { + crDebug( "Render SPU: Turning on XSynchronize" ); + XSynchronize( visual->dpy, True ); + } + + if (visual->visual) { + crDebug( "Render SPU: Choose visual id=0x%x: RGBA=(%d,%d,%d,%d) Z=%d" + " stencil=%d double=%d stereo=%d accum=(%d,%d,%d,%d)", + (int) visual->visual->visualid, + Attrib( visual, GLX_RED_SIZE ), + Attrib( visual, GLX_GREEN_SIZE ), + Attrib( visual, GLX_BLUE_SIZE ), + Attrib( visual, GLX_ALPHA_SIZE ), + Attrib( visual, GLX_DEPTH_SIZE ), + Attrib( visual, GLX_STENCIL_SIZE ), + Attrib( visual, GLX_DOUBLEBUFFER ), + Attrib( visual, GLX_STEREO ), + Attrib( visual, GLX_ACCUM_RED_SIZE ), + Attrib( visual, GLX_ACCUM_GREEN_SIZE ), + Attrib( visual, GLX_ACCUM_BLUE_SIZE ), + Attrib( visual, GLX_ACCUM_ALPHA_SIZE ) + ); + } + else if (visual->fbconfig) { + int id; + render_spu.ws.glXGetFBConfigAttrib(visual->dpy, visual->fbconfig, + GLX_FBCONFIG_ID, &id); + crDebug("Render SPU: Chose FBConfig 0x%x, visBits=0x%x", + id, visual->visAttribs); + } + + return GL_TRUE; +} + + +/* + * Add a GLX window to a swap group for inter-machine SwapBuffer + * synchronization. + * Only supported on NVIDIA Quadro 3000G hardware. + */ +static void +JoinSwapGroup(Display *dpy, int screen, Window window, + GLuint group, GLuint barrier) +{ + GLuint maxGroups, maxBarriers; + const char *ext; + Bool b; + + /* + * XXX maybe query glXGetClientString() instead??? + */ + ext = render_spu.ws.glXQueryExtensionsString(dpy, screen); + + if (!crStrstr(ext, "GLX_NV_swap_group") || + !render_spu.ws.glXQueryMaxSwapGroupsNV || + !render_spu.ws.glXJoinSwapGroupNV || + !render_spu.ws.glXBindSwapBarrierNV) { + crWarning("Render SPU: nv_swap_group is set but GLX_NV_swap_group is not supported on this system!"); + return; + } + + b = render_spu.ws.glXQueryMaxSwapGroupsNV(dpy, screen, + &maxGroups, &maxBarriers); + if (!b) + crWarning("Render SPU: call to glXQueryMaxSwapGroupsNV() failed!"); + + if (group >= maxGroups) { + crWarning("Render SPU: nv_swap_group too large (%d > %d)", + group, (int) maxGroups); + return; + } + crDebug("Render SPU: max swap groups = %d, max barriers = %d", + maxGroups, maxBarriers); + + /* add this window to the swap group */ + b = render_spu.ws.glXJoinSwapGroupNV(dpy, window, group); + if (!b) { + crWarning("Render SPU: call to glXJoinSwapGroupNV() failed!"); + return; + } + else { + crDebug("Render SPU: call to glXJoinSwapGroupNV() worked!"); + } + + /* ... and bind window to barrier of same ID */ + b = render_spu.ws.glXBindSwapBarrierNV(dpy, group, barrier); + if (!b) { + crWarning("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) failed!", group, barrier); + return; + } + else { + crDebug("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) worked!", group, barrier); + } + + crDebug("Render SPU: window has joined swap group %d", group); +} + + + +static GLboolean +createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + Display *dpy; + Colormap cmap; + XSetWindowAttributes swa; + XSizeHints hints = {0}; + XEvent event; + XTextProperty text_prop; + XClassHint *class_hints = NULL; + Window parent; + char *name; + unsigned long flags; + unsigned int vncWin; + + CRASSERT(visual); + window->visual = visual; + window->nativeWindow = 0; + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return GL_TRUE; +#endif + + dpy = visual->dpy; + + if ( render_spu.use_L2 ) + { + crWarning( "Render SPU: Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Query screen size if we're going full-screen + */ + if ( render_spu.fullscreen ) + { + XWindowAttributes xwa; + Window root_window; + + /* disable the screensaver */ + XSetScreenSaver( dpy, 0, 0, PreferBlanking, AllowExposures ); + crDebug( "Render SPU: Just turned off the screensaver" ); + + /* Figure out how big the screen is, and make the window that size */ + root_window = DefaultRootWindow( dpy ); + XGetWindowAttributes( dpy, root_window, &xwa ); + + crDebug( "Render SPU: root window size: %d x %d", xwa.width, xwa.height ); + + window->x = 0; + window->y = 0; + window->BltInfo.width = xwa.width; + window->BltInfo.height = xwa.height; + } + + /* i've changed default window size to be 0,0 but X doesn't like it */ + /*CRASSERT(window->BltInfo.width >= 1); + CRASSERT(window->BltInfo.height >= 1);*/ + if (window->BltInfo.width < 1) window->BltInfo.width = 1; + if (window->BltInfo.height < 1) window->BltInfo.height = 1; + + /* + * Get a colormap. + */ + if (render_spu.use_lut8) + cmap = GetLUTColormap( dpy, visual->visual ); + else + cmap = GetShareableColormap( dpy, visual->visual ); + if ( !cmap ) { + crError( "Render SPU: Unable to get a colormap!" ); + return GL_FALSE; + } + + /* destroy existing window if there is one */ + if (window->window) { + XDestroyWindow(dpy, window->window); + } + + /* + * Create the window + * + * POSSIBLE OPTIMIZATION: + * If we're using the render_to_app_window or render_to_crut_window option + * (or DMX) we may never actually use this X window. So we could perhaps + * delay its creation until glXMakeCurrent is called. With NVIDIA's OpenGL + * driver, creating a lot of GLX windows can eat up a lot of VRAM and lead + * to slow, software-fallback rendering. Apps that use render_to_app_window, + * etc should set the default window size very small to avoid this. + * See dmx.conf for example. + */ + swa.colormap = cmap; + swa.border_pixel = 0; + swa.event_mask = ExposureMask | StructureNotifyMask; + swa.override_redirect = 1; + + flags = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; + + /* + * We pass the VNC's desktop windowID via an environment variable. + * If we don't find one, we're not on a 3D-capable vncviewer, or + * if we do find one, then create the renderspu subwindow as a + * child of the vncviewer's desktop window. + * + * This is purely for the replicateSPU. + * + * NOTE: This is crufty, and will do for now. FIXME. + */ + vncWin = crStrToInt( crGetenv("CRVNCWINDOW") ); + if (vncWin) + parent = (Window) vncWin; + else + parent = RootWindow(dpy, visual->visual->screen); + + if (render_spu_parent_window_id>0) + { + crDebug("Render SPU: VBox parent window_id is: %x", render_spu_parent_window_id); + window->window = XCreateWindow(dpy, render_spu_parent_window_id, + window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + 0, visual->visual->depth, InputOutput, + visual->visual->visual, flags, &swa); + } + else + { + /* This should happen only at the call from crVBoxServerInit. At this point we don't + * know render_spu_parent_window_id yet, nor we need it for default window which is hidden. + */ + + crDebug("Render SPU: Creating global window, parent: %x", RootWindow(dpy, visual->visual->screen)); + window->window = XCreateWindow(dpy, RootWindow(dpy, visual->visual->screen), + window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + 0, visual->visual->depth, InputOutput, + visual->visual->visual, flags, &swa); + } + + if (!window->window) { + crWarning( "Render SPU: unable to create window" ); + return GL_FALSE; + } + + crDebug( "Render SPU: Created window 0x%x on display %s, Xvisual 0x%x", + (int) window->window, + DisplayString(visual->dpy), + (int) visual->visual->visual->visualid /* yikes */ + ); + + if (render_spu.fullscreen || render_spu.borderless) + { + /* Disable border/decorations with an MWM property (observed by most + * modern window managers. + */ + PropMotifWmHints motif_hints; + Atom prop, proptype; + + /* setup the property */ + motif_hints.flags = MWM_HINTS_DECORATIONS; + motif_hints.decorations = 0; /* Turn off all decorations */ + + /* get the atom for the property */ + prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); + if (prop) { + /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ + proptype = prop; + XChangeProperty( dpy, window->window, /* display, window */ + prop, proptype, /* property, type */ + 32, /* format: 32-bit datums */ + PropModeReplace, /* mode */ + (unsigned char *) &motif_hints, /* data */ + PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ + ); + } + } + + /* Make a clear cursor to get rid of the monitor cursor */ + if ( render_spu.fullscreen ) + { + Pixmap pixmap; + Cursor cursor; + XColor colour; + char clearByte = 0; + + /* AdB - Only bother to create a 1x1 cursor (byte) */ + pixmap = XCreatePixmapFromBitmapData(dpy, window->window, &clearByte, + 1, 1, 1, 0, 1); + if(!pixmap){ + crWarning("Unable to create clear cursor pixmap"); + return GL_FALSE; + } + + cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &colour, &colour, 0, 0); + if(!cursor){ + crWarning("Unable to create clear cursor from zero byte pixmap"); + return GL_FALSE; + } + XDefineCursor(dpy, window->window, cursor); + XFreePixmap(dpy, pixmap); + } + + hints.x = window->x; + hints.y = window->y; + hints.width = window->BltInfo.width; + hints.height = window->BltInfo.height; + hints.min_width = hints.width; + hints.min_height = hints.height; + hints.max_width = hints.width; + hints.max_height = hints.height; + if (render_spu.resizable) + hints.flags = USPosition | USSize; + else + hints.flags = USPosition | USSize | PMinSize | PMaxSize; + XSetStandardProperties( dpy, window->window, + WINDOW_NAME, WINDOW_NAME, + None, NULL, 0, &hints ); + + /* New item! This is needed so that the sgimouse server can find + * the crDebug window. + */ + name = WINDOW_NAME; + XStringListToTextProperty( &name, 1, &text_prop ); + XSetWMName( dpy, window->window, &text_prop ); + + /* Set window name, resource class */ + class_hints = XAllocClassHint( ); + class_hints->res_name = crStrdup( "foo" ); + class_hints->res_class = crStrdup( "Chromium" ); + XSetClassHint( dpy, window->window, class_hints ); + crFree( class_hints->res_name ); + crFree( class_hints->res_class ); + XFree( class_hints ); + + if (showIt) { + XMapWindow( dpy, window->window ); + XIfEvent( dpy, &event, WaitForMapNotify, + (char *) window->window ); + } + + if ((window->visual->visAttribs & CR_DOUBLE_BIT) && render_spu.nvSwapGroup) { + /* NOTE: + * If this SPU creates N windows we don't want to gang the N windows + * together! + * By adding the window ID to the nvSwapGroup ID we can be sure each + * app window is in a separate swap group while all the back-end windows + * which form a mural are in the same swap group. + */ + GLuint group = 0; /*render_spu.nvSwapGroup + window->BltInfo.Base.id;*/ + GLuint barrier = 0; + JoinSwapGroup(dpy, visual->visual->screen, window->window, group, barrier); + } + + /* + * End GLX code + */ + crDebug( "Render SPU: actual window x, y, width, height: %d, %d, %d, %d", + window->x, window->y, window->BltInfo.width, window->BltInfo.height ); + + XSync(dpy, 0); + + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, window); + AssertRC(rc); + } + + return GL_TRUE; +} + + +static GLboolean +createPBuffer( VisualInfo *visual, WindowInfo *window ) +{ + window->visual = visual; + window->x = 0; + window->y = 0; + window->nativeWindow = 0; + + CRASSERT(window->BltInfo.width > 0); + CRASSERT(window->BltInfo.height > 0); + +#ifdef GLX_VERSION_1_3 + { + int attribs[100], i = 0, w, h; + CRASSERT(visual->fbconfig); + w = window->BltInfo.width; + h = window->BltInfo.height; + attribs[i++] = GLX_PRESERVED_CONTENTS; + attribs[i++] = True; + attribs[i++] = GLX_PBUFFER_WIDTH; + attribs[i++] = w; + attribs[i++] = GLX_PBUFFER_HEIGHT; + attribs[i++] = h; + attribs[i++] = 0; /* terminator */ + window->window = render_spu.ws.glXCreatePbuffer(visual->dpy, + visual->fbconfig, attribs); + if (window->window) { + crDebug("Render SPU: Allocated %d x %d pbuffer", w, h); + return GL_TRUE; + } + else { + crWarning("Render SPU: Failed to allocate %d x %d pbuffer", w, h); + return GL_FALSE; + } + } +#endif /* GLX_VERSION_1_3 */ + return GL_FALSE; +} + + +GLboolean +renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + if (visual->visAttribs & CR_PBUFFER_BIT) { + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; + return createPBuffer(visual, window); + } + else { + return createWindow(visual, showIt, window); + } +} + +GLboolean +renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + return renderspu_SystemCreateWindow(visual, showIt, window); +} + +void +renderspu_SystemDestroyWindow( WindowInfo *window ) +{ + CRASSERT(window); + CRASSERT(window->visual); + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + { + crFree(window->buffer); + window->buffer = NULL; + } + else +#endif + { + if (window->visual->visAttribs & CR_PBUFFER_BIT) { +#ifdef GLX_VERSION_1_3 + render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window); +#endif + } + else { + /* The value window->nativeWindow will only be non-NULL if the + * render_to_app_window option is set to true. In this case, we + * don't want to do anything, since we're not responsible for this + * window. I know...personal responsibility and all... + */ + if (!window->nativeWindow) { + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, window); + AssertRC(rc); + } + XDestroyWindow(window->visual->dpy, window->window); + XSync(window->visual->dpy, 0); + } + } + } + window->visual = NULL; + window->window = 0; +} + + +GLboolean +renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ) +{ + Bool is_direct; + GLXContext sharedSystemContext = NULL; + + CRASSERT(visual); + CRASSERT(context); + + context->visual = visual; + + if (sharedContext != NULL) { + sharedSystemContext = sharedContext->context; + } + + + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + context->context = (GLXContext) render_spu.OSMesaCreateContext(OSMESA_RGB, 0); + if (context->context) + return GL_TRUE; + else + return GL_FALSE; + } +#endif + +#ifdef GLX_VERSION_1_3 + if (visual->visAttribs & CR_PBUFFER_BIT) { + context->context = render_spu.ws.glXCreateNewContext( visual->dpy, + visual->fbconfig, + GLX_RGBA_TYPE, + sharedSystemContext, + render_spu.try_direct); + } + else +#endif + { + context->context = render_spu.ws.glXCreateContext( visual->dpy, + visual->visual, + sharedSystemContext, + render_spu.try_direct); + } + if (!context->context) { + crError( "Render SPU: Couldn't create rendering context" ); + return GL_FALSE; + } + + is_direct = render_spu.ws.glXIsDirect( visual->dpy, context->context ); + if (visual->visual) + crDebug("Render SPU: Created %s context (%d) on display %s for visAttribs 0x%x", + is_direct ? "DIRECT" : "INDIRECT", + context->BltInfo.Base.id, + DisplayString(visual->dpy), + visual->visAttribs); + + if ( render_spu.force_direct && !is_direct ) + { + crError( "Render SPU: Direct rendering not possible." ); + return GL_FALSE; + } + + return GL_TRUE; +} + + +#define USE_GLX_COPYCONTEXT 0 + +#if !USE_GLX_COPYCONTEXT + +/** + * Unfortunately, glXCopyContext() is broken sometimes (NVIDIA 76.76 driver). + * This bit of code gets and sets GL state we need to copy between contexts. + */ +struct saved_state +{ + /* XXX depending on the app, more state may be needed here */ + GLboolean Lighting; + GLboolean LightEnabled[8]; + GLfloat LightPos[8][4]; + GLfloat LightAmbient[8][4]; + GLfloat LightDiffuse[8][4]; + GLfloat LightSpecular[8][4]; + GLboolean DepthTest; +}; + +static struct saved_state SavedState; + +static void +get_state(struct saved_state *s) +{ + int i; + + s->Lighting = render_spu.self.IsEnabled(GL_LIGHTING); + for (i = 0; i < 8; i++) { + s->LightEnabled[i] = render_spu.self.IsEnabled(GL_LIGHT0 + i); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]); + } + + s->DepthTest = render_spu.self.IsEnabled(GL_DEPTH_TEST); +} + +static void +set_state(const struct saved_state *s) +{ + int i; + + if (s->Lighting) { + render_spu.self.Enable(GL_LIGHTING); + } + else { + render_spu.self.Disable(GL_LIGHTING); + } + + for (i = 0; i < 8; i++) { + if (s->LightEnabled[i]) { + render_spu.self.Enable(GL_LIGHT0 + i); + } + else { + render_spu.self.Disable(GL_LIGHT0 + i); + } + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]); + } + + if (s->DepthTest) + render_spu.self.Enable(GL_DEPTH_TEST); + else + render_spu.self.Disable(GL_DEPTH_TEST); +} + +#endif /* !USE_GLX_COPYCONTEXT */ + +/** + * Recreate the GLX context for ContextInfo. The new context will use the + * visual specified by newVisualID. + */ +static void +renderspu_RecreateContext( ContextInfo *context, int newVisualID ) +{ + XVisualInfo templateVis, *vis; + long templateFlags; + int screen = 0, count; + GLXContext oldContext = context->context; + + templateFlags = VisualScreenMask | VisualIDMask; + templateVis.screen = screen; + templateVis.visualid = newVisualID; + vis = XGetVisualInfo(context->visual->dpy, templateFlags, &templateVis, &count); + CRASSERT(vis); + if (!vis) + return; + + /* create new context */ + crDebug("Render SPU: Creating new GLX context with visual 0x%x", newVisualID); + context->context = render_spu.ws.glXCreateContext(context->visual->dpy, + vis, NULL, + render_spu.try_direct); + CRASSERT(context->context); + +#if USE_GLX_COPYCONTEXT + /* copy old context state to new context */ + render_spu.ws.glXCopyContext(context->visual->dpy, + oldContext, context->context, ~0); + crDebug("Render SPU: Done copying context state"); +#endif + + /* destroy old context */ + render_spu.ws.glXDestroyContext(context->visual->dpy, oldContext); + + context->visual->visual = vis; +} + + +void +renderspu_SystemDestroyContext( ContextInfo *context ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + { + render_spu.OSMesaDestroyContext( (OSMesaContext) context->context ); + } + else +#endif + { +#if 0 + /* XXX disable for now - causes segfaults w/ NVIDIA's driver */ + render_spu.ws.glXDestroyContext( context->visual->dpy, context->context ); +#endif + } + context->visual = NULL; + context->context = 0; +} + + +#ifdef USE_OSMESA +static void +check_buffer_size( WindowInfo *window ) +{ + if (window->BltInfo.width != window->in_buffer_width + || window->BltInfo.height != window->in_buffer_height + || ! window->buffer) { + crFree(window->buffer); + + window->buffer = crCalloc(window->BltInfo.width * window->BltInfo.height + * 4 * sizeof (GLubyte)); + + window->in_buffer_width = window->BltInfo.width; + window->in_buffer_height = window->BltInfo.height; + + crDebug("Render SPU: dimensions changed to %d x %d", window->BltInfo.width, window->BltInfo.height); + } +} +#endif + + +void +renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, + ContextInfo *context ) +{ + Bool b; + + CRASSERT(render_spu.ws.glXMakeCurrent); + + /*crDebug("%s nativeWindow=0x%x", __FUNCTION__, (int) nativeWindow);*/ + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + check_buffer_size(window); + render_spu.OSMesaMakeCurrent( (OSMesaContext) context->context, + window->buffer, GL_UNSIGNED_BYTE, + window->BltInfo.width, window->BltInfo.height); + return; + } +#endif + + nativeWindow = 0; + + if (window && context) { + window->appWindow = nativeWindow; + + if (window->visual != context->visual) { + crDebug("Render SPU: MakeCurrent visual mismatch (win(%d) bits:0x%x != ctx(%d) bits:0x%x); remaking window.", + window->BltInfo.Base.id, window->visual->visAttribs, + context->BltInfo.Base.id, context->visual->visAttribs); + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ +#ifndef SOLARIS_9_X_BUG + /* + I'm having some really weird issues if I destroy this window + when I'm using the version of sunX that comes with Solaris 9. + Subsiquent glX calls return GLXBadCurrentWindow error. + + This is an issue even when running Linux version and using + the Solaris 9 sunX as a display. + -- jw + + jw: we might have to call glXMakeCurrent(dpy, 0, 0) to unbind + the context from the window before destroying it. -Brian + */ + render_spu.ws.glXMakeCurrent(window->visual->dpy, 0, 0); + renderspu_SystemDestroyWindow( window ); +#endif + renderspu_SystemCreateWindow( context->visual, window->visible, window ); + /* + crError("In renderspu_SystemMakeCurrent() window and context" + " weren't created with same visual!"); + */ + } + + CRASSERT(context->context); + +#if 0 + if (render_spu.render_to_crut_window) { + if (render_spu.crut_drawable == 0) { + /* We don't know the X drawable ID yet. Ask mothership for it. */ + char response[8096]; + CRConnection *conn = crMothershipConnect(); + if (!conn) + { + crError("Couldn't connect to the mothership to get CRUT drawable-- " + "I have no idea what to do!"); + } + crMothershipGetParam( conn, "crut_drawable", response ); + render_spu.crut_drawable = crStrToInt(response); + crMothershipDisconnect(conn); + + crDebug("Render SPU: using CRUT drawable: 0x%x", + render_spu.crut_drawable); + if (!render_spu.crut_drawable) { + crDebug("Render SPU: Crut drawable 0 is invalid"); + /* Continue with nativeWindow = 0; we'll render to the window that + * we (the Render SPU) previously created. + */ + } + } + + nativeWindow = render_spu.crut_drawable; + } +#endif + + if ((render_spu.render_to_crut_window || render_spu.render_to_app_window) + && nativeWindow) + { + /* We're about to bind the rendering context to a window that we + * (the Render SPU) did not create. The window was created by the + * application or the CRUT server. + * Make sure the window ID is valid and that the window's X visual is + * the same as the rendering context's. + */ + if (WindowExists(window->visual->dpy, nativeWindow)) + { + int vid = GetWindowVisualID(window->visual->dpy, nativeWindow); + GLboolean recreated = GL_FALSE; + + /* check that the window's visual and context's visual match */ + if (vid != (int) context->visual->visual->visualid) { + crWarning("Render SPU: Can't bind context %d to CRUT/native window " + "0x%x because of different X visuals (0x%x != 0x%x)!", + context->BltInfo.Base.id, (int) nativeWindow, + vid, (int) context->visual->visual->visualid); + crWarning("Render SPU: Trying to recreate GLX context to match."); + /* Try to recreate the GLX context so that it uses the same + * GLX visual as the window. + */ +#if !USE_GLX_COPYCONTEXT + if (context->everCurrent) { + get_state(&SavedState); + } +#endif + renderspu_RecreateContext(context, vid); + recreated = GL_TRUE; + } + + /* OK, this should work */ + window->nativeWindow = (Window) nativeWindow; + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->nativeWindow, + context->context ); + CRASSERT(b); +#if !USE_GLX_COPYCONTEXT + if (recreated) { + set_state(&SavedState); + } +#endif + } + else + { + crWarning("Render SPU: render_to_app/crut_window option is set but " + "the window ID 0x%x is invalid on the display named %s", + (unsigned int) nativeWindow, + DisplayString(window->visual->dpy)); + CRASSERT(window->window); + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->window, context->context ); + CRASSERT(b); + } + } + else + { + /* This is the normal case - rendering to the render SPU's own window */ + CRASSERT(window->window); +#if 0 + crDebug("calling glXMakecurrent(%p, 0x%x, 0x%x)", + window->visual->dpy, + (int) window->window, (int) context->context ); +#endif + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->window, context->context ); + if (!b) { + crWarning("glXMakeCurrent(%p, 0x%x, %p) failed! (winId %d, ctxId %d)", + window->visual->dpy, + (int) window->window, (void *) context->context, + window->BltInfo.Base.id, context->BltInfo.Base.id ); + } + /*CRASSERT(b);*/ + } + + /* XXX this is a total hack to work around an NVIDIA driver bug */ +#if 0 + if (render_spu.self.GetFloatv && context->haveWindowPosARB) { + GLfloat f[4]; + render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f); + if (!window->everCurrent || f[1] < 0.0) { + crDebug("Render SPU: Resetting raster pos"); + render_spu.self.WindowPos2iARB(0, 0); + } + } +#endif + } + else + { + GET_CONTEXT(pCurCtx); + if (pCurCtx) + { + b = render_spu.ws.glXMakeCurrent( pCurCtx->currentWindow->visual->dpy, None, NULL); + if (!b) { + crWarning("glXMakeCurrent(%p, None, NULL) failed!", pCurCtx->currentWindow->visual->dpy); + } + } + + } +} + + +/** + * Set window (or pbuffer) size. + */ +void +renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + window->BltInfo.width = w; + window->BltInfo.height = h; + check_buffer_size(window); + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if (window->visual->visAttribs & CR_PBUFFER_BIT) + { + /* resizing a pbuffer */ + if (render_spu.pbufferWidth != 0 || render_spu.pbufferHeight != 0) { + /* size limit check */ + if (w > render_spu.pbufferWidth || h > render_spu.pbufferHeight) { + crWarning("Render SPU: Request for %d x %d pbuffer is larger than " + "the configured size of %d x %d. ('pbuffer_size')", + w, h, render_spu.pbufferWidth, render_spu.pbufferHeight); + return; + } + /* + * If the requested new pbuffer size is greater than 1/2 the size of + * the max pbuffer, just use the max pbuffer size. This helps avoid + * problems with VRAM memory fragmentation. If we run out of VRAM + * for pbuffers, some drivers revert to software rendering. We want + * to avoid that! + */ + if (w * h >= render_spu.pbufferWidth * render_spu.pbufferHeight / 2) { + /* increase the dimensions to the max pbuffer size now */ + w = render_spu.pbufferWidth; + h = render_spu.pbufferHeight; + } + } + + if (window->BltInfo.width != w || window->BltInfo.height != h) { + /* Only resize if the new dimensions really are different */ +#ifdef CHROMIUM_THREADSAFE + ContextInfo *currentContext = (ContextInfo *) crGetTSD(&_RenderTSD); +#else + ContextInfo *currentContext = render_spu.currentContext; +#endif + /* Can't resize pbuffers, so destroy it and make a new one */ + render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window); + window->BltInfo.width = w; + window->BltInfo.height = h; + crDebug("Render SPU: Creating new %d x %d PBuffer (id=%d)", + w, h, window->BltInfo.Base.id); + if (!createPBuffer(window->visual, window)) { + crWarning("Render SPU: Unable to create PBuffer (out of VRAM?)!"); + } + else if (currentContext && currentContext->currentWindow == window) { + /* Determine if we need to bind the current context to new pbuffer */ + render_spu.ws.glXMakeCurrent(window->visual->dpy, + window->window, + currentContext->context ); + } + } + } + else { + if (!w || !h) + { + /* X can not handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_FALSE ); + } + return; + } + /* Resize ordinary X window */ + /* + * This is ugly, but it seems to be the only thing that works. + * Basically, XResizeWindow() doesn't seem to always take effect + * immediately. + * Even after an XSync(), the GetWindowAttributes() call will sometimes + * return the old window size. So, we use a loop to repeat the window + * resize until it seems to take effect. + */ + int attempt; + crDebug("Render SPU: XResizeWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, w, h); + XResizeWindow(window->visual->dpy, window->window, w, h); + XSync(window->visual->dpy, 0); + + if (!window->BltInfo.width || !window->BltInfo.height) + { + /* we have hidden the window instead of sizing it to (0;0) since X is unable to handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_TRUE ); + return; + } + } +#if 0 + for (attempt = 0; attempt < 3; attempt++) { /* try three times max */ + XWindowAttributes attribs; + /* Now, query the window size */ + XGetWindowAttributes(window->visual->dpy, window->window, &attribs); + if (attribs.width == w && attribs.height == h) + break; + /* sleep for a millisecond and try again */ + crMsleep(1); + } +#endif + } +} + + +void +renderspu_SystemGetWindowGeometry( WindowInfo *window, + GLint *x, GLint *y, GLint *w, GLint *h ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + *w = window->BltInfo.width; + *h = window->BltInfo.height; + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + CRASSERT(window->window); + if (window->visual->visAttribs & CR_PBUFFER_BIT) + { + *x = 0; + *y = 0; + *w = window->BltInfo.width; + *h = window->BltInfo.height; + } + else + { + Window xw, child, root; + unsigned int width, height, bw, d; + int rx, ry; + + if ((render_spu.render_to_app_window || render_spu.render_to_crut_window) + && window->nativeWindow) { + xw = window->nativeWindow; + } + else { + xw = window->window; + } + + XGetGeometry(window->visual->dpy, xw, &root, + x, y, &width, &height, &bw, &d); + + /* translate x/y to screen coords */ + if (!XTranslateCoordinates(window->visual->dpy, xw, root, + 0, 0, &rx, &ry, &child)) { + rx = ry = 0; + } + *x = rx; + *y = ry; + *w = (int) width; + *h = (int) height; + } +} + + +void +renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ) +{ + int scrn; +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + *w = 2048; + *h = 2048; + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + CRASSERT(window->window); + + scrn = DefaultScreen(window->visual->dpy); + *w = DisplayWidth(window->visual->dpy, scrn); + *h = DisplayHeight(window->visual->dpy, scrn); +} + + +void +renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + crDebug("Render SPU: XMoveWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, x, y); + XMoveWindow(window->visual->dpy, window->window, x, y); + XSync(window->visual->dpy, 0); + } +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void +renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, const GLint *pRects ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + int evb, erb, i; + XRectangle *pXRects; + + if (!XShapeQueryExtension(window->visual->dpy, &evb, &erb)) + { + crWarning("Render SPU: Display %s doesn't support SHAPE extension", window->visual->displayName); + return; + } + + if (cRects>0) + { + pXRects = (XRectangle *) crAlloc(cRects * sizeof(XRectangle)); + + for (i=0; i<cRects; ++i) + { + pXRects[i].x = (short) pRects[4*i]; + pXRects[i].y = (short) pRects[4*i+1]; + pXRects[i].width = (unsigned short) (pRects[4*i+2]-pRects[4*i]); + pXRects[i].height = (unsigned short) (pRects[4*i+3]-pRects[4*i+1]); + } + } + else + { + pXRects = (XRectangle *) crAlloc(sizeof(XRectangle)); + pXRects[0].x = 0; + pXRects[0].y = 0; + pXRects[0].width = 0; + pXRects[0].height = 0; + cRects = 1; + } + + crDebug("Render SPU: XShapeCombineRectangles (%x, %x, cRects=%i)", window->visual->dpy, window->window, cRects); + + XShapeCombineRectangles(window->visual->dpy, window->window, ShapeBounding, 0, 0, + pXRects, cRects, ShapeSet, YXBanded); + XSync(window->visual->dpy, 0); + crFree(pXRects); + } +} + +/* Either show or hide the render SPU's window. */ +void +renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + if (window->visual->dpy && window->window && + (window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + if (showIt) + { + if (window->BltInfo.width && window->BltInfo.height) + { + XMapWindow( window->visual->dpy, window->window ); + XSync(window->visual->dpy, 0); + } + } + else + { + XUnmapWindow( window->visual->dpy, window->window ); + XSync(window->visual->dpy, 0); + } + } +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* The !render_spu.force_present_main_thread code flow is actually inspired + * by cocoa backend impl, here it forces rendering in WinCmd thread rather + * than a Main thread. It defaults to 1, because otherwise there were + * 3D driver incompatibilities on some systems. Elsewhere it causes flicker + * on NVidia GPUs. In principle would need root cause investigation. */ + if (!render_spu.force_present_main_thread) + { + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); + renderspuVBoxCompositorRelease(window); + } + else if (rc != VERR_SEM_BUSY) + { + /* this is somewhat we do not expect */ + WARN(("renderspuVBoxCompositorTryAcquire failed rc %d", rc)); + return; + } + } + + { + Status status; + XEvent event; + render_spu.self.Flush(); +// renderspuVBoxPresentBlitterEnsureCreated(window, 0); + + crMemset(&event, 0, sizeof (event)); + event.type = Expose; + event.xexpose.window = window->window; + event.xexpose.width = window->BltInfo.width; + event.xexpose.height = window->BltInfo.height; + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, 0, &event); + if (!status) + { + WARN(("XSendEvent returned null")); + } + XFlush(render_spu.pCommunicationDisplay); + } +} + +static void +MarkWindow(WindowInfo *w) +{ + static GC gc = 0; /* XXX per-window??? */ + if (!gc) { + /* Create a GC for drawing invisible lines */ + XGCValues gcValues; + gcValues.function = GXnoop; + gc = XCreateGC(w->visual->dpy, w->nativeWindow, GCFunction, &gcValues); + } + XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->BltInfo.width, w->BltInfo.height); +} + + +void +renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) +{ + CRASSERT(w); + +#if 00 /*TEMPORARY - FOR TESTING SWAP LOCK*/ + if (1) { + /* random delay */ + int k = crRandInt(1000 * 100, 750*1000); + static int first = 1; + if (first) { + crRandAutoSeed(); + first = 0; + } + usleep(k); + } +#endif + + /* render_to_app_window: + * w->nativeWindow will only be non-zero if the + * render_spu.render_to_app_window option is true and + * MakeCurrent() recorded the nativeWindow handle in the WindowInfo + * structure. + */ + if (w->nativeWindow) { + render_spu.ws.glXSwapBuffers( w->visual->dpy, w->nativeWindow ); +#if 0 + MarkWindow(w); +#else + (void) MarkWindow; +#endif + } + else { + render_spu.ws.glXSwapBuffers( w->visual->dpy, w->window ); + } +} + +void renderspu_SystemReparentWindow(WindowInfo *window) +{ + Window parent; + + parent = render_spu_parent_window_id>0 ? render_spu_parent_window_id : + RootWindow(window->visual->dpy, window->visual->visual->screen); + + XReparentWindow(window->visual->dpy, window->window, parent, window->x, window->y); + XSync(window->visual->dpy, False); +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} 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); +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c new file mode 100644 index 00000000..5df60410 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c @@ -0,0 +1,1741 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + + +#define WIN32_LEAN_AND_MEAN +#include <iprt/win/windows.h> + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "renderspu.h" +#include "cr_mem.h" + + +/* IAT patcher stuff */ +#define RVA2PTR(_t, _base, _off) ((_t*)(((uint8_t*)(_base)) + (_off))) + +int renderspuIatPatcherGetImportAddress(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void** ppAdr) +{ + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS pNtHdr; + PIMAGE_IMPORT_DESCRIPTOR pImportDr; + DWORD rvaImport; + + crDebug("searching entry %s from %s", pszName, pszLib); + + *ppAdr = 0; + + if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE) + { + crWarning("invalid dos signature"); + return VERR_INVALID_HANDLE; + } + pNtHdr = RVA2PTR(IMAGE_NT_HEADERS, pDosHdr, pDosHdr->e_lfanew); + if (pNtHdr->Signature != IMAGE_NT_SIGNATURE) + { + crWarning("invalid nt signature"); + return VERR_INVALID_HANDLE; + } + rvaImport = pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if (!rvaImport) + { + crWarning("no imports found"); + return VERR_NOT_FOUND; + } + pImportDr = RVA2PTR(IMAGE_IMPORT_DESCRIPTOR, pDosHdr, rvaImport); + + for ( ;pImportDr->TimeDateStamp != 0 || pImportDr->Name != 0; ++pImportDr) + { + DWORD rvaINT, rvaIAT; + PIMAGE_THUNK_DATA pINT, pIAT; + LPCSTR pszLibCur = RVA2PTR(char, pDosHdr, pImportDr->Name); + if (stricmp(pszLibCur, pszLib)) + continue; + + /* got the necessary lib! */ + crDebug("got info for lib"); + + rvaINT = pImportDr->OriginalFirstThunk; + rvaIAT = pImportDr->FirstThunk; + + if (!rvaINT || !rvaIAT) + { + crWarning("either rvaINT(0x%x) or rvaIAT(0x%x) are NULL, nothing found!", rvaINT, rvaIAT); + return VERR_NOT_FOUND; + } + + pINT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaINT); + pIAT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaIAT); + + for ( ; pINT->u1.AddressOfData; ++pINT, ++pIAT) + { + PIMAGE_IMPORT_BY_NAME pIbn; + + if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)) + continue; + + pIbn = RVA2PTR(IMAGE_IMPORT_BY_NAME, pDosHdr, pINT->u1.AddressOfData); + + if (stricmp(pszName, (char*)pIbn->Name)) + continue; + + *ppAdr = &pIAT->u1.Function; + + crDebug("search succeeded!"); + return VINF_SUCCESS; + } + } + + crDebug("not found"); + return VERR_NOT_FOUND; +} + +int renderspuIatPatcherPatchEntry(void *pvEntry, void *pvValue, void **ppvOldVal) +{ + void **ppfn = (void**)pvEntry; + DWORD dwOldProtect = 0; + + if (!VirtualProtect(pvEntry, sizeof (pvEntry), PAGE_READWRITE, &dwOldProtect)) + { + crWarning("VirtualProtect 1 failed, %d", GetLastError()); + return VERR_ACCESS_DENIED; + } + + if (ppvOldVal) + *ppvOldVal = *ppfn; + *ppfn = pvValue; + + if (!VirtualProtect(pvEntry, sizeof (pvEntry), dwOldProtect, &dwOldProtect)) + { + crWarning("VirtualProtect 2 failed, %d.. ignoring", GetLastError()); + } + + return VINF_SUCCESS; +} + + +int renderspuIatPatcherPatchFunction(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void* pfn) +{ + void* pAdr; + int rc = renderspuIatPatcherGetImportAddress(hModule, pszLib, pszName, &pAdr); + if (RT_FAILURE(rc)) + { + crDebug("renderspuIatPatcherGetImportAddress failed, %d", rc); + return rc; + } + + rc = renderspuIatPatcherPatchEntry(pAdr, pfn, NULL); + if (RT_FAILURE(rc)) + { + crWarning("renderspuIatPatcherPatchEntry failed, %d", rc); + return rc; + } + + return VINF_SUCCESS; +} + +/* patch */ +static HWND __stdcall renderspuAtiQuirk_GetForegroundWindow() +{ + crDebug("renderspuAtiQuirk_GetForegroundWindow"); + return NULL; +} + + +#define CRREG_MAXKEYNAME 8 +static int renderspuAtiQuirk_GetICDDriverList(char *pBuf, DWORD cbBuf, DWORD *pcbResult) +{ + static LPCSTR aValueNames[] = {"OpenGLVendorName", "OpenGLDriverName"}; + char *pBufPos = pBuf; + DWORD cbBufRemain = cbBuf, cbTotal = 0; + HKEY hKey, hSubkey; + DWORD dwIndex = 0; + int i; + int rc = VINF_SUCCESS; + char NameBuf[CRREG_MAXKEYNAME]; + LONG lRc; + + if (pcbResult) + *pcbResult = 0; + + lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}", + 0, /* reserved*/ + KEY_READ, + &hKey); + if (ERROR_SUCCESS != lRc) + { + crDebug("RegOpenKeyEx 1 failed, %d", lRc); + return VERR_OPEN_FAILED; + } + + for ( ; ; ++dwIndex) + { + lRc = RegEnumKeyA(hKey, dwIndex, NameBuf, CRREG_MAXKEYNAME); + if (lRc == ERROR_NO_MORE_ITEMS) + break; + if (lRc == ERROR_MORE_DATA) + continue; + if (lRc != ERROR_SUCCESS) + { + crWarning("RegEnumKeyA failed, %d", lRc); + continue; + } + + lRc = RegOpenKeyEx(hKey, + NameBuf, + 0, /* reserved*/ + KEY_READ, + &hSubkey); + if (ERROR_SUCCESS != lRc) + { + crDebug("RegOpenKeyEx 2 failed, %d", lRc); + RegCloseKey(hKey); + return VERR_OPEN_FAILED; + } + + for (i = 0; i < RT_ELEMENTS(aValueNames); ++i) + { + DWORD cbCur = cbBufRemain; + DWORD type; + lRc = RegQueryValueExA(hSubkey, aValueNames[i], NULL, /* reserved*/ + &type, + (PBYTE)pBufPos, &cbCur); + /* exclude second null termination */ + --cbCur; + + if (ERROR_MORE_DATA == lRc) + { + if (REG_MULTI_SZ != type) + { + crWarning("unexpected data type! %d", type); + continue; + } + rc = VERR_BUFFER_OVERFLOW; + pBufPos = NULL; + cbBufRemain = 0; + CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2); + cbTotal += cbCur; + continue; + } + if (ERROR_SUCCESS != lRc) + { + crDebug("RegQueryValueExA failed, %d", lRc); + continue; + } + + if (REG_MULTI_SZ != type) + { + crWarning("unexpected data type! %d", type); + continue; + } + + /* succeeded */ + CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2); + pBufPos += cbCur; + cbBufRemain -= cbCur; + cbTotal += cbCur; + CRASSERT(cbBufRemain < UINT32_MAX/2); + } + + RegCloseKey(hSubkey); + } + + RegCloseKey(hKey); + + if (cbTotal) + { + /* include second null termination */ + CRASSERT(!pBufPos || pBufPos[0] == '\0'); + ++cbTotal; + } + + if (pcbResult) + *pcbResult = cbTotal; + + return rc; +} + +static int renderspuAtiQuirk_ApplyForModule(LPCSTR pszAtiDll) +{ + int rc; + HMODULE hAtiDll; + + crDebug("renderspuAtiQuirk_ApplyForModule (%s)", pszAtiDll); + + hAtiDll = GetModuleHandleA(pszAtiDll); + if (!hAtiDll) + { + crDebug("GetModuleHandle failed, %d", GetLastError()); + return VERR_NOT_FOUND; + } + + rc = renderspuIatPatcherPatchFunction(hAtiDll, "user32.dll", "GetForegroundWindow", (void*)renderspuAtiQuirk_GetForegroundWindow); + if (RT_FAILURE(rc)) + { + crDebug("renderspuIatPatcherPatchFunction failed, %d", rc); + return rc; + } + + crDebug("renderspuAtiQuirk_ApplyForModule SUCCEEDED!"); + crInfo("ATI Fullscreen quirk patch SUCCEEDED!"); + + return VINF_SUCCESS; +} + +static LPCSTR renderspuRegMultiSzNextVal(LPCSTR pszBuf) +{ + pszBuf += strlen(pszBuf) + sizeof (pszBuf[0]); + + if (pszBuf[0] == '\0') + return NULL; + + return pszBuf; +} + +static LPCSTR renderspuRegMultiSzCurVal(LPCSTR pszBuf) +{ + if (pszBuf[0] == '\0') + return NULL; + + return pszBuf; +} + + +static int renderspuAtiQuirk_Apply() +{ + char aBuf[4096]; + DWORD cbResult = 0; + LPCSTR pszVal; + int rc; + + crDebug("renderspuAtiQuirk_Apply.."); + + rc = renderspuAtiQuirk_GetICDDriverList(aBuf, sizeof (aBuf), &cbResult); + if (RT_FAILURE(rc)) + { + crDebug("renderspuAtiQuirk_GetICDDriverList failed, rc(%d)", rc); + return rc; + } + + for (pszVal = renderspuRegMultiSzCurVal(aBuf); + pszVal; + pszVal = renderspuRegMultiSzNextVal(pszVal)) + { + renderspuAtiQuirk_ApplyForModule(pszVal); + } + + return VINF_SUCCESS; +} + +static GLboolean renderspuAtiQuirk_Needed() +{ + const char * pszString = render_spu.ws.glGetString(GL_VENDOR); + if (pszString && strstr(pszString, "ATI")) + return GL_TRUE; + pszString = render_spu.ws.glGetString(GL_RENDERER); + if (pszString && strstr(pszString, "ATI")) + return GL_TRUE; + return GL_FALSE; +} + +static void renderspuAtiQuirk_ChkApply() +{ + static GLboolean fChecked = GL_FALSE; + if (fChecked) + return; + + fChecked = GL_TRUE; + if (!renderspuAtiQuirk_Needed()) + return; + + crInfo("This is an ATI card, taking care of fullscreen.."); + + /* + * ATI WDDM-based graphics have an issue with rendering fullscreen. + * See public tickets #9775 & #9267 . + * Namely ATI drivers check whether ogl window is foreground and fullscreen + * and if so - do D3DKMTSetDisplayMode for ogl surface, + * which prevented any other data from being displayed, no matter what. + * + * Here we check whether we're using an ATI card and if so, patch the ogl ICD driver's IAT + * to replace GetForegroundWindow reference with our renderspuAtiQuirk_GetForegroundWindow, + * which always returns NULL. + */ + renderspuAtiQuirk_Apply(); +} + +#define WINDOW_NAME window->title + +static BOOL +bSetupPixelFormat( HDC hdc, GLbitfield visAttribs ); + +GLboolean renderspu_SystemInitVisual( VisualInfo *visual ) +{ + if (visual->visAttribs & CR_PBUFFER_BIT) { + crWarning("Render SPU: PBuffers not support on Windows yet."); + } + + /* In the windows world, we need a window before a context. + * Use the device_context as a marker to do just that */ + + return TRUE; +} + +void renderspu_SystemDestroyWindow( WindowInfo *window ) +{ + VBOX_RENDERSPU_DESTROY_WINDOW vrdw; + + CRASSERT(window); + + /*DestroyWindow( window->hWnd );*/ + + vrdw.hWnd = window->hWnd; + + if (render_spu.dwWinThreadId) + { + PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_DESTROY_WINDOW, 0, (LPARAM) &vrdw); + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); + } + else + { + crError("Render SPU: window thread is not running"); + } + + window->hWnd = NULL; + window->visual = NULL; + if (window->hRgn) + { + DeleteObject(window->hRgn); + window->hRgn = NULL; + } +} + +static LONG WINAPI +MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + /* int w,h; */ + + switch ( uMsg ) { + case WM_PAINT: + { + WindowInfo *pWindow = (WindowInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + HDC hDC; + PAINTSTRUCT Paint; + + Assert(pWindow->device_context); + hDC = BeginPaint(pWindow->hWnd, &Paint); + if (hDC) + { + BOOL bRc; + pWindow->redraw_device_context = hDC; + + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 1, true); + + bRc = EndPaint(pWindow->hWnd, &Paint); + + pWindow->redraw_device_context = NULL; + + renderspuVBoxCompositorRelease(pWindow); + + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("EndPaint failed, winEr %d", winEr); + } + } + else + { + DWORD winEr = GetLastError(); + crWarning("BeginPaint failed, winEr %d", winEr); + } + } + } + break; + } + case WM_SIZE: + /* w = LOWORD( lParam ); + * h = HIWORD( lParam ); */ + + /* glViewport( 0, 0, w, h ); */ +#if 0 + glViewport( -render_spu.mural_x, -render_spu.mural_y, + render_spu.mural_width, render_spu.mural_height ); + glScissor( -render_spu.mural_x, -render_spu.mural_y, + render_spu.mural_width, render_spu.mural_height ); +#endif + break; + + case WM_CLOSE: + crWarning( "Render SPU: caught WM_CLOSE -- quitting." ); + exit( 0 ); + break; + + case WM_DESTROY: + crDebug("Render SPU: caught WM_DESTROY for our window %x", hWnd); + break; + + case WM_NCHITTEST: + crDebug("WM_NCHITTEST"); + return HTNOWHERE; + } + + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + +static BOOL +bSetupPixelFormatEXT( HDC hdc, GLbitfield visAttribs) +{ + PIXELFORMATDESCRIPTOR ppfd; + int pixelFormat; + int attribList[100]; + float fattribList[] = { 0.0, 0.0 }; + int numFormats; + int i = 0; + BOOL vis; + + CRASSERT(visAttribs & CR_RGB_BIT); /* anybody need color index */ + + crWarning("Render SPU: Using WGL_EXT_pixel_format to select visual ID."); + + attribList[i++] = WGL_DRAW_TO_WINDOW_EXT; + attribList[i++] = GL_TRUE; + attribList[i++] = WGL_ACCELERATION_EXT; + attribList[i++] = WGL_FULL_ACCELERATION_EXT; + attribList[i++] = WGL_COLOR_BITS_EXT; + attribList[i++] = 24; + attribList[i++] = WGL_RED_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_GREEN_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_BLUE_BITS_EXT; + attribList[i++] = 1; + + crWarning("Render SPU: Visual chosen is... RGB"); + + if (visAttribs & CR_ALPHA_BIT) + { + attribList[i++] = WGL_ALPHA_BITS_EXT; + attribList[i++] = 1; + crWarning("A"); + } + + crWarning(", "); + + if (visAttribs & CR_DOUBLE_BIT) { + attribList[i++] = WGL_DOUBLE_BUFFER_EXT; + attribList[i++] = GL_TRUE; + crWarning("DB, "); + } + + if (visAttribs & CR_STEREO_BIT) { + attribList[i++] = WGL_STEREO_EXT; + attribList[i++] = GL_TRUE; + crWarning("Stereo, "); + } + + if (visAttribs & CR_DEPTH_BIT) + { + attribList[i++] = WGL_DEPTH_BITS_EXT; + attribList[i++] = 1; + crWarning("Z, "); + } + + if (visAttribs & CR_STENCIL_BIT) + { + attribList[i++] = WGL_STENCIL_BITS_EXT; + attribList[i++] = 1; + crWarning("Stencil, "); + } + + if (visAttribs & CR_ACCUM_BIT) + { + attribList[i++] = WGL_ACCUM_RED_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_ACCUM_GREEN_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_ACCUM_BLUE_BITS_EXT; + attribList[i++] = 1; + crWarning("Accum, "); + if (visAttribs & CR_ALPHA_BIT) + { + attribList[i++] = WGL_ACCUM_ALPHA_BITS_EXT; + attribList[i++] = 1; + crWarning("Accum Alpha, "); + } + } + + if (visAttribs & CR_MULTISAMPLE_BIT) + { + attribList[i++] = WGL_SAMPLE_BUFFERS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_SAMPLES_EXT; + attribList[i++] = 4; + crWarning("Multisample, "); + } + + crWarning("\n"); + + /* End the list */ + attribList[i++] = 0; + attribList[i++] = 0; + + vis = render_spu.ws.wglChoosePixelFormatEXT( hdc, attribList, fattribList, 1, &pixelFormat, &numFormats); + + crDebug("Render SPU: wglChoosePixelFormatEXT (vis 0x%x, LastError 0x%x, pixelFormat 0x%x", vis, GetLastError(), pixelFormat); + +#ifdef VBOX_CR_SERVER_FORCE_WGL + render_spu.ws.wglSetPixelFormat( hdc, pixelFormat, &ppfd ); +#else + SetPixelFormat( hdc, pixelFormat, &ppfd ); +#endif + + crDebug("Render SPU: wglSetPixelFormat (Last error 0x%x)", GetLastError()); + + return vis; +} + +static BOOL +bSetupPixelFormatNormal( HDC hdc, GLbitfield visAttribs ) +{ + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */ + 1, /* version number */ + PFD_DRAW_TO_WINDOW | /* support window */ + PFD_SUPPORT_OPENGL, /* support OpenGL */ + PFD_TYPE_RGBA, /* RGBA type */ + 24, /* 24-bit color depth */ + 0, 0, 0, 0, 0, 0, /* color bits ignored */ + 0, /* no alpha buffer */ + 0, /* shift bit ignored */ + 0, /* no accumulation buffer */ + 0, 0, 0, 0, /* accum bits ignored */ + 0, /* set depth buffer */ + 0, /* set stencil buffer */ + 0, /* no auxiliary buffer */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, 0, 0 /* layer masks ignored */ + }; + PIXELFORMATDESCRIPTOR *ppfd = &pfd; + char s[1000]; + GLbitfield b = 0; + int pixelformat; + + renderspuMakeVisString( visAttribs, s ); + + crDebug( "Render SPU: WGL wants these visual capabilities: %s", s); + + /* These really come into play with sort-last configs */ + if (visAttribs & CR_DEPTH_BIT) + ppfd->cDepthBits = 24; + if (visAttribs & CR_ACCUM_BIT) + ppfd->cAccumBits = 16; + if (visAttribs & CR_RGB_BIT) + ppfd->cColorBits = 24; + if (visAttribs & CR_STENCIL_BIT) + ppfd->cStencilBits = 8; + if (visAttribs & CR_ALPHA_BIT) + ppfd->cAlphaBits = 8; + if (visAttribs & CR_DOUBLE_BIT) + ppfd->dwFlags |= PFD_DOUBLEBUFFER; + if (visAttribs & CR_STEREO_BIT) + ppfd->dwFlags |= PFD_STEREO; + + /* + * We call the wgl functions directly if the SPU was loaded + * by our faker library, otherwise we have to call the GDI + * versions. + */ +#ifdef VBOX_CR_SERVER_FORCE_WGL + if (crGetenv( "CR_WGL_DO_NOT_USE_GDI" ) != NULL) + { + pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd ); + /* doing this twice is normal Win32 magic */ + pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd ); + if ( pixelformat == 0 ) + { + crError( "render_spu.ws.wglChoosePixelFormat failed" ); + } + if ( !render_spu.ws.wglSetPixelFormat( hdc, pixelformat, ppfd ) ) + { + crError( "render_spu.ws.wglSetPixelFormat failed" ); + } + + render_spu.ws.wglDescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd ); + } + else +#endif + { + /* Okay, we were loaded manually. Call the GDI functions. */ + pixelformat = ChoosePixelFormat( hdc, ppfd ); + /* doing this twice is normal Win32 magic */ + pixelformat = ChoosePixelFormat( hdc, ppfd ); + if ( pixelformat == 0 ) + { + crError( "ChoosePixelFormat failed" ); + } + if ( !SetPixelFormat( hdc, pixelformat, ppfd ) ) + { + crError( "SetPixelFormat failed (Error 0x%x)", GetLastError() ); + } + + DescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd ); + } + + + if (ppfd->cDepthBits > 0) + b |= CR_DEPTH_BIT; + if (ppfd->cAccumBits > 0) + b |= CR_ACCUM_BIT; + if (ppfd->cColorBits > 8) + b |= CR_RGB_BIT; + if (ppfd->cStencilBits > 0) + b |= CR_STENCIL_BIT; + if (ppfd->cAlphaBits > 0) + b |= CR_ALPHA_BIT; + if (ppfd->dwFlags & PFD_DOUBLEBUFFER) + b |= CR_DOUBLE_BIT; + if (ppfd->dwFlags & PFD_STEREO) + b |= CR_STEREO_BIT; + + renderspuMakeVisString( b, s ); + + crDebug( "Render SPU: WGL chose these visual capabilities: %s", s); + return TRUE; +} + +static BOOL +bSetupPixelFormat( HDC hdc, GLbitfield visAttribs ) +{ + /* According to http://www.opengl.org/resources/faq/technical/mswindows.htm + we shouldn't be using wgl functions to setup pixel formats unless we're loading ICD driver. + In particular, bSetupPixelFormatEXT bugs with Intel drivers. + */ + return bSetupPixelFormatNormal(hdc, visAttribs); +} + +GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + HDESK desktop; + HINSTANCE hinstance; + WNDCLASS wc; + DWORD window_style; + int window_plus_caption_width; + int window_plus_caption_height; + + window->hRgn = NULL; + window->visual = visual; + window->nativeWindow = 0; + + if ( render_spu.use_L2 ) + { + crWarning( "Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Begin Windows / WGL code + */ + + hinstance = GetModuleHandle( NULL ); + if (!hinstance) + { + crError( "Render SPU: Couldn't get a handle to my module." ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the module handle: 0x%x", hinstance ); + + /* If we were launched from a service, telnet, or rsh, we need to + * get the input desktop. */ + + desktop = OpenInputDesktop( 0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE ); + + if ( !desktop ) + { + crError( "Render SPU: Couldn't acquire input desktop" ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the desktop: 0x%x", desktop ); + + if ( !SetThreadDesktop( desktop ) ) + { + /* If this function fails, it's probably because + * it's already been called (i.e., the render SPU + * is bolted to an application?) */ + + /*crError( "Couldn't set thread to input desktop" ); */ + } + crDebug( "Render SPU: Set the thread desktop -- this might have failed." ); + + if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) ) + { + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinstance; + wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = WINDOW_NAME; + + if ( !RegisterClass( &wc ) ) + { + crError( "Render SPU: Couldn't register window class -- you're not trying " + "to do multi-pipe stuff on windows, are you?\n\nNote --" + "This error message is from 1997 and probably doesn't make" + "any sense any more, but it's nostalgic for Humper." ); + return GL_FALSE; + } + crDebug( "Render SPU: Registered the class" ); + } + crDebug( "Render SPU: Got the class information" ); + + /* Full screen window should be a popup (undecorated) window */ +#if 1 + window_style = ( render_spu.fullscreen ? WS_POPUP : WS_CAPTION ); +#else + window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); + window_style |= WS_SYSMENU; +#endif + + crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no"); + + if ( render_spu.fullscreen ) + { +#if 0 + + int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; + int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); + + window->x = render_spu->defaultX - smCxFixedFrame - 1; + window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + +#else + /* Since it's undecorated, we don't have to do anything fancy + * with these parameters. */ + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + window->x = 0; + window->y = 0; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; + +#endif + } + else + { + /* CreateWindow takes the size of the entire window, so we add + * in the size necessary for the frame and the caption. */ + + int smCxFixedFrame, smCyFixedFrame, smCyCaption; + smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + crDebug( "Render SPU: Got the X fixed frame" ); + smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME ); + crDebug( "Render SPU: Got the Y fixed frame" ); + smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + crDebug( "Render SPU: Got the Caption " ); + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + + window->x = render_spu.defaultX - smCxFixedFrame; + window->y = render_spu.defaultY - smCyFixedFrame - smCyCaption; + } + + crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height ); + window->hWnd = CreateWindow( WINDOW_NAME, WINDOW_NAME, + window_style, + window->x, window->y, + window_plus_caption_width, + window_plus_caption_height, + NULL, NULL, hinstance, &render_spu ); + + if ( !window->hWnd ) + { + crError( "Render SPU: Create Window failed! That's almost certainly terrible." ); + return GL_FALSE; + } + + window->visible = showIt; + + if (!showIt) + { + renderspu_SystemShowWindow( window, 0 ); + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) + { + renderspu_SystemWindowSize(window, + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); + } + } + else + { + crDebug( "Render SPU: Showing the window" ); + crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd); + } + + CRASSERT(!window->visible == !showIt); + + /* Intel drivers require a window to be visible for proper 3D rendering, + * so set it visible and handle the visibility with visible regions (see below) */ + ShowWindow( window->hWnd, SW_SHOWNORMAL ); + + SetForegroundWindow( window->hWnd ); + + SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y, + window_plus_caption_width, window_plus_caption_height, + ( render_spu.fullscreen ? (SWP_SHOWWINDOW | + SWP_NOSENDCHANGING | + SWP_NOREDRAW | + SWP_NOACTIVATE ) : + 0 ) ); + + if ( render_spu.fullscreen ) + ShowCursor( FALSE ); + + window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } + + crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); + + if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) ) + { + crError( "Render SPU: Couldn't set up the device context! Yikes!" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ +#if 0 + HDESK desktop; +#endif + HINSTANCE hinstance; + WNDCLASS wc; + DWORD window_style; + int window_plus_caption_width; + int window_plus_caption_height; + + window->hRgn = NULL; + window->visual = visual; + window->nativeWindow = 0; + + if ( render_spu.use_L2 ) + { + crWarning( "Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Begin Windows / WGL code + */ + + hinstance = GetModuleHandle( NULL ); + if (!hinstance) + { + crError( "Render SPU: Couldn't get a handle to my module." ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the module handle: 0x%x", hinstance ); + +#if 0 + /* If we were launched from a service, telnet, or rsh, we need to + * get the input desktop. */ + + desktop = OpenInputDesktop( 0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE ); + + if ( !desktop ) + { + crError( "Render SPU: Couldn't acquire input desktop" ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the desktop: 0x%x", desktop ); + + if ( !SetThreadDesktop( desktop ) ) + { + /* If this function fails, it's probably because + * it's already been called (i.e., the render SPU + * is bolted to an application?) */ + + /*crError( "Couldn't set thread to input desktop" ); */ + } + crDebug( "Render SPU: Set the thread desktop -- this might have failed." ); +#endif + + if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) ) + { + wc.style = CS_OWNDC; // | CS_PARENTDC; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinstance; + wc.hIcon = NULL; //LoadIcon( NULL, IDI_APPLICATION ); + wc.hCursor = NULL; //LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = WINDOW_NAME; + + if ( !RegisterClass( &wc ) ) + { + crError( "Render SPU: Couldn't register window class -- you're not trying " + "to do multi-pipe stuff on windows, are you?\n\nNote --" + "This error message is from 1997 and probably doesn't make" + "any sense any more, but it's nostalgic for Humper." ); + return GL_FALSE; + } + crDebug( "Render SPU: Registered the class" ); + } + crDebug( "Render SPU: Got the class information" ); + + /* Full screen window should be a popup (undecorated) window */ +#if 1 + window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED; + if (render_spu_parent_window_id) + { + window_style |= WS_CHILD; + } +#else + window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); + window_style |= WS_SYSMENU; +#endif + + crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no"); + + if ( render_spu.fullscreen ) + { +#if 0 + + int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; + int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); + + window->x = render_spu->defaultX - smCxFixedFrame - 1; + window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + +#else + /* Since it's undecorated, we don't have to do anything fancy + * with these parameters. */ + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + window->x = 0; + window->y = 0; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; + +#endif + } + else + { + /* CreateWindow takes the size of the entire window, so we add + * in the size necessary for the frame and the caption. */ + int smCxFixedFrame, smCyFixedFrame, smCyCaption; + smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + crDebug( "Render SPU: Got the X fixed frame" ); + smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME ); + crDebug( "Render SPU: Got the Y fixed frame" ); + smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + crDebug( "Render SPU: Got the Caption " ); + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + + window->x = render_spu.defaultX; + window->y = render_spu.defaultY; + } + + crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height ); + /*window->hWnd = CreateWindowEx( WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, + WINDOW_NAME, WINDOW_NAME, + window_style, + window->x, window->y, + window->BltInfo.width, + window->BltInfo.height, + (void*) render_spu_parent_window_id, NULL, hinstance, &render_spu );*/ + { + CREATESTRUCT cs; + + cs.lpCreateParams = window; + + cs.dwExStyle = WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY; + cs.lpszName = WINDOW_NAME; + cs.lpszClass = WINDOW_NAME; + cs.style = window_style; + cs.x = window->x; + cs.y = window->y; + cs.cx = window->BltInfo.width; + cs.cy = window->BltInfo.height; + cs.hwndParent = (void*) render_spu_parent_window_id; + cs.hMenu = NULL; + cs.hInstance = hinstance; + + if (render_spu.dwWinThreadId) + { + DWORD res; + int cnt=0; + + if (!PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_CREATE_WINDOW, 0, (LPARAM) &cs)) + { + crError("Render SPU: PostThreadMessage failed with %i", GetLastError()); + return GL_FALSE; + } + + do + { + res = WaitForSingleObject(render_spu.hWinThreadReadyEvent, 1000); + cnt++; + } + while ((res!=WAIT_OBJECT_0) && (cnt<10)); + + crDebug("Render SPU: window thread waited %i secs", cnt); + + if (res!=WAIT_OBJECT_0) + { + crError("Render SPU: window thread not responded after %i tries", cnt); + return GL_FALSE; + } + } + else + { + crError("Render SPU: window thread is not running"); + return GL_FALSE; + } + } + + if ( !window->hWnd ) + { + crError( "Render SPU: Create Window failed! That's almost certainly terrible." ); + return GL_FALSE; + } + + window->visible = 1; + + if (!showIt) + { + renderspu_SystemShowWindow( window, 0 ); + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) + { + renderspu_SystemWindowSize(window, + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); + } + } + else + { +#ifdef DEBUG_misha + crWarning( "Render SPU: Showing the window" ); +#else + crDebug( "Render SPU: Showing the window" ); +#endif + crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd); + } + + CRASSERT(!window->visible == !showIt); + + /* Intel drivers require a window to be visible for proper 3D rendering, + * so set it visible and handle the visibility with visible regions (see below) */ + if (window->BltInfo.Base.id != CR_RENDER_DEFAULT_WINDOW_ID) + { + ShowWindow( window->hWnd, SW_SHOWNORMAL ); + } + else + { + CRASSERT(!showIt); + /* dummy window is always hidden in any way */ + } + + //SetForegroundWindow( visual->hWnd ); + + SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + ( render_spu.fullscreen ? + (SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOACTIVATE ) : SWP_NOACTIVATE + ) ); + crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, + window->x, window->y, window->BltInfo.width, window->BltInfo.height); + + if ( render_spu.fullscreen ) + ShowCursor( FALSE ); + + window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } + + crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); + + if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) ) + { + crError( "Render SPU: Couldn't set up the device context! Yikes!" ); + return GL_FALSE; + } + + /* set the window pointer data at the last step to ensure our WM_PAINT callback does not do anything until we are fully initialized */ +#ifdef RT_STRICT + SetLastError(NO_ERROR); +#endif + { + LONG_PTR oldVal = SetWindowLongPtr(window->hWnd, GWLP_USERDATA, (LONG_PTR)window); + Assert(!oldVal && GetLastError() == NO_ERROR); RT_NOREF_PV(oldVal); + } + + return GL_TRUE; +} + +static void renderspuWindowRgnApply(WindowInfo *window) +{ + HRGN hRgn = window->hRgn; + if (hRgn) + { + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that. + * this is why we need to make a copy of the regions to be passed in */ + + int result; + hRgn = CreateRectRgn(0, 0, 0, 0); + if (!hRgn) + { + WARN(("CreateRectRgn failed")); + return; + } + + result = CombineRgn(hRgn, window->hRgn, NULL, RGN_COPY); + if (result == ERROR) + { + WARN(("CombineRgn failed")); + return; + } + } + + SetWindowRgn(window->hWnd, hRgn, true); +} + +/* Either show or hide the render SPU's window. */ +void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) +{ + if (showIt) + { + crDebug("SHOW renderspu_SystemShowWindow: %x", window->hWnd); + renderspuWindowRgnApply(window); + } + else + { + HRGN hRgn; + crDebug("HIDE renderspu_SystemShowWindow: %x", window->hWnd); + hRgn = CreateRectRgn(0, 0, 0, 0); + SetWindowRgn(window->hWnd, hRgn, true); + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that */ + } + window->visible = showIt; +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* The !render_spu.force_present_main_thread code flow is actually inspired + * by cocoa backend impl, here it forces rendering in WndProc, i.e. main + * thread. It defaults to 0, because it is for debugging and working around + * bugs. In principle would need root cause investigation. */ + if (!render_spu.force_present_main_thread) + { + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); + renderspuVBoxCompositorRelease(window); + } + else if (rc != VERR_SEM_BUSY) + { + /* this is somewhat we do not expect */ + crWarning("renderspuVBoxCompositorTryAcquire failed rc %d", rc); + } + } + + { + render_spu.self.Flush(); + renderspuVBoxPresentBlitterEnsureCreated(window, 0); + RedrawWindow(window->hWnd, NULL, NULL, RDW_INTERNALPAINT); + } +} + +GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ) +{ + (void) sharedContext; + context->visual = visual; + + /* Found a visual, so we're o.k. to create the context now */ + if (0/*visual->device_context*/) { + + //crDebug( "Render SPU: Using the DC: 0x%x", visual->device_context ); + + //context->hRC = render_spu.ws.wglCreateContext( visual->device_context ); + if (!context->hRC) + { + crError( "Render SPU: wglCreateContext failed (error 0x%x)", GetLastError() ); + return GL_FALSE; + } + } else { + crDebug( "Render SPU: Delaying DC creation " ); + context->hRC = NULL; /* create it later in makecurrent */ + } + + + return GL_TRUE; +} + +void renderspu_SystemDestroyContext( ContextInfo *context ) +{ + render_spu.ws.wglDeleteContext( context->hRC ); + context->hRC = NULL; +} + +static GLboolean renderspuChkActivateSharedContext(ContextInfo *sharedContext) +{ + WindowInfo *window; + + if (sharedContext->hRC) + return GL_TRUE; + + CRASSERT(sharedContext->BltInfo.Base.id); + + if (sharedContext->shared) + renderspuChkActivateSharedContext(sharedContext->shared); + + window = renderspuGetDummyWindow(sharedContext->visual->visAttribs); + if (!window) + { + crError("renderspuChkActivateSharedContext: renderspuGetDummyWindow failed!"); + return GL_FALSE; + } + + CRASSERT(window->device_context); + + crDebug( "Render SPU: renderspuChkActivateSharedContext: made the DC: 0x%x", window->device_context ); + + sharedContext->hRC = render_spu.ws.wglCreateContext(window->device_context); + if (!sharedContext->hRC) + { + crError( "Render SPU: (renderspuChkActivateSharedContext) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + return GL_FALSE; + } + + return GL_TRUE; +} + +void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, ContextInfo *context ) +{ + CRASSERT(render_spu.ws.wglMakeCurrent); + + if (context && window) { + if (window->visual != context->visual) { + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ + + /** @todo Chromium has no correct code to remove window ids and associated info from + * various tables. This is hack which just hides the root case. + */ + crWarning("Recreating window in renderspu_SystemMakeCurrent\n"); + renderspu_SystemDestroyWindow( window ); + renderspu_SystemVBoxCreateWindow( context->visual, window->visible, window ); + } + + if (0/*render_spu.render_to_app_window && nativeWindow*/) + { + /* The render_to_app_window option + * is set and we've got a nativeWindow + * handle, save the handle for + * later calls to swapbuffers(). + * + * NOTE: This doesn't work, except + * for software driven Mesa. + * We'd need to object link the + * crappfaker and crserver to be able to share + * the HDC values between processes.. FIXME! + */ + if (context->shared) + { + /* first make sure we have shared context created */ + renderspuChkActivateSharedContext(context->shared); + } + + window->nativeWindow = (HDC) nativeWindow; + if (context->hRC == 0) { + context->hRC = render_spu.ws.wglCreateContext( window->nativeWindow ); + if (!context->hRC) + { + crError( "(MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + } + } + + if (context->shared + && context->shared->hRC + && context->hRC) + { + /* share lists */ + render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + } + + render_spu.ws.wglMakeCurrent( window->nativeWindow, context->hRC ); + } + else + { + if (!context->hRC) { + CRASSERT(!nativeWindow); + if (context->shared) + { + /* first make sure we have shared context created */ + renderspuChkActivateSharedContext(context->shared); + } + + context->hRC = render_spu.ws.wglCreateContext(window->device_context); + if (!context->hRC) + { + crError( "Render SPU: (MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + } + + if (context->shared + && context->shared->hRC + && context->hRC) + { + /* share lists */ + BOOL bRc = render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("wglShareLists failed, winEr %d", winEr); + } + } + + /*Requery ext function pointers, we skip dummy ctx as it should never be used with ext functions*/ + if (0 && context->BltInfo.Base.id) + { + int numFuncs, i; + SPUNamedFunctionTable ext_table[1000]; + + + crDebug("Default server ctx created, requerying ext functions"); + /*requery ext functions*/ + numFuncs = renderspuCreateFunctions(ext_table); + numFuncs += crLoadOpenGLExtensions( &render_spu.ws, ext_table+numFuncs); + CRASSERT(numFuncs < 1000); + + /*change spu dispatch*/ + crSPUChangeDispatch(&render_spu.self, ext_table); + + + /*cleanup temp table*/ + for (i=0; i<numFuncs; ++i) + { + if (ext_table[i].name) crFree(ext_table[i].name); + } + } + } + + /*crDebug("MakeCurrent 0x%x, 0x%x", window->device_context, context->hRC);*/ + if (!render_spu.ws.wglMakeCurrent(!nativeWindow ? window->device_context : window->redraw_device_context, context->hRC)) + { + DWORD err = GetLastError(); + crError("Render SPU: (MakeCurrent) failed to make 0x%x, 0x%x current with 0x%x error.", window->device_context, context->hRC, err); + } + } + + renderspuAtiQuirk_ChkApply(); + } + else { + render_spu.ws.wglMakeCurrent( 0, 0 ); + } +} + +void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) +{ + int winprop; + CRASSERT(window); + CRASSERT(window->visual); + if ( render_spu.fullscreen ) + winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING | + SWP_NOREDRAW | SWP_NOACTIVATE; + else + winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW; + + /*SetWindowRgn(window->hWnd, NULL, false);*/ + + if (!SetWindowPos( window->hWnd, HWND_TOP, + window->x, window->y, w, h, winprop )) { + crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h); + } else { + crDebug("Render SPU: SetWindowSize (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h); + } + /* save the new size */ + window->BltInfo.width = w; + window->BltInfo.height = h; +} + + +void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h ) +{ + RECT rect; + + CRASSERT(window); + CRASSERT(window->visual); + + GetClientRect( window->hWnd, &rect ); + *x = rect.left; + *y = rect.top; + *w = rect.right - rect.left; + *h = rect.bottom - rect.top; +} + + +void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ) +{ + /* XXX fix this */ + (void) window; + *w = 1600; + *h = 1200; +} + + +void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) +{ + int winprop; + CRASSERT(window); + CRASSERT(window->visual); + if ( render_spu.fullscreen ) + winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING | + SWP_NOREDRAW | SWP_NOACTIVATE; + else + winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW; + + /*SetWindowRgn(window->visual->hWnd, NULL, false);*/ + + if (!SetWindowPos( window->hWnd, HWND_TOP, + x, y, window->BltInfo.width, window->BltInfo.height, winprop )) { + crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, x, y, window->BltInfo.width, window->BltInfo.height); + } else { + crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, + x, y, window->BltInfo.width, window->BltInfo.height); + } + /* save the new position */ + window->x = x; + window->y = y; +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) +{ + GLint i; + HRGN hRgn, hTmpRgn; + RECT rectRgnBound; + + CRASSERT(window); + CRASSERT(window->visual); + + if (window->hRgn) + { + DeleteObject(window->hRgn); + window->hRgn = NULL; + } + + hRgn = CreateRectRgn(0, 0, 0, 0); + + for (i = 0; i < cRects; i++) + { + crDebug("Render SPU: CreateRectRgn #%d: (%d, %d)-(%d, %d)", i, + pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]); + + hTmpRgn = CreateRectRgn(pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]); + CombineRgn(hRgn, hRgn, hTmpRgn, RGN_OR); + DeleteObject(hTmpRgn); + } + + if (GetRgnBox(hRgn, &rectRgnBound)) + { + crDebug("Render SPU: Rgn bounding box (%d, %d)-(%d, %d)", + rectRgnBound.left, rectRgnBound.top, rectRgnBound.right, rectRgnBound.bottom); + } + + window->hRgn = hRgn; + + if (window->visible) + renderspuWindowRgnApply(window); + + crDebug("Render SPU: SetWindowRgn (%x, cRects=%i)", window->hWnd, cRects); +} + +static void renderspuHandleWindowMessages( HWND hWnd ) +{ + MSG msg; + while ( PeekMessage( &msg, hWnd, 0, 0xffffffff, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + //BringWindowToTop( hWnd ); +} + +void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) +{ + int return_value; + + /* peek at the windows message queue */ +// renderspuHandleWindowMessages( w->hWnd ); + + /* render_to_app_window: + * w->nativeWindow will only be non-zero if the + * render_spu.render_to_app_window option is true and + * MakeCurrent() recorded the nativeWindow handle in the WindowInfo + * structure. + */ + if (render_spu.render_to_app_window && w->nativeWindow) { +#ifdef VBOX_CR_SERVER_FORCE_WGL + return_value = render_spu.ws.wglSwapBuffers( w->nativeWindow ); +#else + return_value = SwapBuffers( w->nativeWindow ); +#endif + } else { + /* + HRGN hRgn1, hRgn2, hRgn3; + HWND hWndParent; + LONG ws; + + hRgn1 = CreateRectRgn(0, 0, w->BltInfo.width, w->BltInfo.height); + hRgn2 = CreateRectRgn(50, 50, 100, 100); + hRgn3 = CreateRectRgn(0, 0, 0, 0); + CombineRgn(hRgn3, hRgn1, hRgn2, RGN_DIFF); + SetWindowRgn(w->visual->hWnd, hRgn3, true); + DeleteObject(hRgn1); + DeleteObject(hRgn2); + + hWndParent = GetParent(w->visual->hWnd); + ws = GetWindowLong(hWndParent, GWL_STYLE); + ws &= ~WS_CLIPCHILDREN; + SetWindowLong(hWndParent, GWL_STYLE, ws); + + RECT rcClip; + + rcClip.left = 50; + rcClip.top = 50; + rcClip.right = 100; + rcClip.bottom = 100; + ValidateRect(w->visual->hWnd, &rcClip); + + return_value = GetClipBox(w->visual->device_context, &rcClip); + crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + + crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom); + + return_value = ExcludeClipRect(w->visual->device_context, 50, 50, 100, 100); + crDebug("ExcludeClipRect returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + + return_value = GetClipBox(w->visual->device_context, &rcClip); + crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom); + */ +#ifdef VBOX_CR_SERVER_FORCE_WGL + return_value = render_spu.ws.wglSwapBuffers( w->device_context ); +#else + return_value = SwapBuffers( w->device_context ); +#endif + } + if (!return_value) + { + /* GOD DAMN IT. The latest versions of the NVIDIA drivers + * return failure from wglSwapBuffers, but it works just fine. + * WHAT THE HELL?! */ + + crWarning( "wglSwapBuffers failed: return value of %d!", return_value); + } +} + +void renderspu_SystemReparentWindow(WindowInfo *window) +{ + SetParent(window->hWnd, (HWND)render_spu_parent_window_id); +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} |