renderspu_cocoa_helper.m revision ae2a9b93d772d52146af2c010701ead81e4fb348
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * VirtualBox OpenGL Cocoa Window System Helper Implementation.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Copyright (C) 2009-2014 Oracle Corporation
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * available from http://www.virtualbox.org. This file is free software;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * you can redistribute it and/or modify it under the terms of the GNU
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * General Public License (GPL) as published by the Free Software
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * How this works:
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * In general it is not so easy like on the other platforms, cause Cocoa
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * doesn't support any clipping of already painted stuff. In Mac OS X there is
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * the concept of translucent canvas's e.g. windows and there it is just
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * painted what should be visible to the user. Unfortunately this isn't the
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * concept of chromium. Therefor I reroute all OpenGL operation from the guest
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * to a frame buffer object (FBO). This is a OpenGL extension, which is
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * supported by all OS X versions we support (AFAIC tell). Of course the guest
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * doesn't know that and we have to make sure that the OpenGL state always is
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * in the right state to paint into the FBO and not to the front/back buffer.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Several functions below (like cocoaBindFramebufferEXT, cocoaGetIntegerv,
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * ...) doing this. When a swap or finish is triggered by the guest, the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * content (which is already bound to an texture) is painted on the screen
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * within a separate OpenGL context. This allows the usage of the same
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * resources (texture ids, buffers ...) but at the same time having an
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * different internal OpenGL state. Another advantage is that we can paint a
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * thumbnail of the current output in a much more smaller (GPU accelerated
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * scale) version on a third context and use glReadPixels to get the actual
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * data. glReadPixels is a very slow operation, but as we just use a much more
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * smaller image, we can handle it (anyway this is only done 5 times per
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Other things to know:
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the guest request double buffering, we have to make sure there are two
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * buffers. We use the same FBO with 2 color attachments. Also glDrawBuffer
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * and glReadBuffer is intercepted to make sure it is painted/read to/from
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * the correct buffers. On swap our buffers are swapped and not the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the guest request a depth/stencil buffer, a combined render buffer for
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * this is created.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the size of the guest OpenGL window changes, all FBO's, textures, ...
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * need to be recreated.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - We need to track any changes to the parent window
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * (create/destroy/move/resize). The various classes like OverlayHelperView,
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * OverlayWindow, ... are there for.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - The HGCM service runs on a other thread than the Main GUI. Keeps this
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * always in mind (see e.g. performSelectorOnMainThread in renderFBOToView)
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * - We make heavy use of late binding. We can not be sure that the GUI (or any
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * other third party GUI), overwrite our NSOpenGLContext. So we always ask if
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * this is our own one, before use. Really neat concept of Objective-C/Cocoa
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/*******************************************************************************
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync* Header Files *
99be02f9e15a3ca61b6a7c207cc7eb68dbd04817vboxsync*******************************************************************************/
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#undef PVM /* sys/param.h (included via Cocoa.h) pollutes the namespace with this define. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include "chromium.h" /* For the visual bits of chromium */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/*******************************************************************************
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync* Defined Constants And Macros *
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync*******************************************************************************/
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/* Debug macros */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def FBO
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Disable this to see how the output is without the FBO in the middle of the processing chain. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def CR_RENDER_FORCE_PRESENT_MAIN_THREAD
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Force present schedule to main thread. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def SHOW_WINDOW_BACKGROUND
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Define this to see the window background even if the window is clipped. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def DEBUG_VERBOSE
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Define this to get some debug info about the messages flow. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG(text) do { printf text; } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG_1(text) do { printf text; } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define DEBUG_FUNC_ENTER() DEBUG_MSG(("==>%s\n", __PRETTY_FUNCTION__))
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define DEBUG_FUNC_LEAVE() DEBUG_MSG(("<==%s\n", __PRETTY_FUNCTION__))
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_CHECK_GL_ERROR() do { checkGLError(__FILE__, __LINE__); } while (0);
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync const char *errStr;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_ENUM: errStr = "GL_INVALID_ENUM"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_VALUE: errStr = "GL_INVALID_VALUE"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_OPERATION: errStr = "GL_INVALID_OPERATION"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_STACK_OVERFLOW: errStr = "GL_STACK_OVERFLOW"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_STACK_UNDERFLOW: errStr = "GL_STACK_UNDERFLOW"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_OUT_OF_MEMORY: errStr = "GL_OUT_OF_MEMORY"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_TABLE_TOO_LARGE: errStr = "GL_TABLE_TOO_LARGE"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync DEBUG_MSG(("%s:%d: glError %d (%s)\n", pszFile, iLine, uGlErr, errStr));
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync NSOpenGLContext *pCtx = [NSOpenGLContext currentContext];
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo);
return fAdjusted;
/** Pointer to render context info for use with vboxCtxEnter/Leave. */
glFlush();
glFlush();
@end
- (void)run;
@end
- (void)run
@end
- (void)run;
- (void)dealloc;
@end
if (self)
return self;
- (void)run
- (void)dealloc
[super dealloc];
@end
- (void)run;
- (void)dealloc;
@end
if (self)
return nil;
return self;
- (void)run
++m_CurIndex;
- (void)dealloc
[super dealloc];
@end
- (void)runTasks;
- (void)dealloc;
@end
if (self)
return self;
return s_pRunner;
VBoxTaskPerformSelector *pSelTask = [[VBoxTaskPerformSelector alloc] initWithObject:aObject selector:aSelector arg:aArg];
- (void)runTasks
AssertFailed();
if (renderspuCalloutAvailable())
- (void)dealloc
[super dealloc];
@end
@class DockOverlayView;
float m_yInvRootOffset;
- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo;
- (void)vboxDestroy;
- (void)updateViewportCS;
- (void)vboxReshapePerform;
- (void)vboxReshapeOnResizePerform;
- (void)vboxReshapeOnReparentPerform;
- (void)createDockTile;
- (void)deleteDockTile;
- (void)makeCurrentFBO;
- (void)swapFBO;
- (void)vboxTryDraw;
- (void)vboxTryDrawUI;
- (void)vboxBlitterSyncWindow;
- (void)clearVisibleRegions;
- (void)reshapeDockTile;
- (void)cleanupData;
@end
* (which happens on fullscreen/seamless entry/exit) the overlay
@class OverlayWindow;
@end
@end
- (void)dealloc;
- (void)cleanup;
- (void)lock;
- (void)unlock;
@end
if (self)
return self;
- (void)dealloc
[super dealloc];
- (void)cleanup
- (void)lock
- (void)unlock
return YES;
[m_ThumbImage drawAtPoint:NSMakePoint(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
return m_ThumbBitmap;
return m_ThumbImage;
@end
if (self)
return self;
- (void)dealloc
[super dealloc];
return m_pView;
return [super view];
-(void)clearDrawable
[super clearDrawable];
return m_pPixelFormat;
return self;
-(void)viewDidMoveToWindow
@end
self = [super initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self)
return self;
- (void)dealloc
[super dealloc];
[m_pOverlayView performSelector:@selector(vboxReshapePerform) onThread:m_Thread withObject:nil waitUntilDone:YES];
[NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(vboxReshapePerform) userInfo:nil repeats:NO];
- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo
m_fNeedViewportUpdate = true;
m_fNeedCtxUpdate = true;
m_fDataVisible = false;
m_fCleanupNeeded = false;
m_fEverSized = false;
return self;
- (void)cleanupData
if (m_pSharedGLCtx)
- (void)dealloc
[super dealloc];
if (m_pGLCtx)
if (pCtx)
return m_pGLCtx;
return m_pParentView;
return m_pOverlayWin;
if (m_fEverSized)
return m_Pos;
return m_fEverSized;
- (void)vboxDestroy
if (fIsMain)
if (fIsMain)
m_fEverSized = true;
DEBUG_MSG(("OVIW(%p): vboxSetSize: new size: %dx%d\n", (void *)self, (int)m_Size.width, (int)m_Size.height));
return m_Size;
- (void)updateViewportCS
- (void)vboxReshapeOnResizePerform
m_fNeedViewportUpdate = true;
m_fNeedCtxUpdate = false;
m_fNeedCtxUpdate = true;
- (void)vboxReshapeOnReparentPerform
- (void)vboxReshapePerform
DEBUG_MSG(("FIXED parentFrame [%f:%f], [%f:%f]\n", parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height));
DEBUG_MSG(("FIXED childFrame [%f:%f], [%f:%f]\n", childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height));
(void *)self, parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height));
(void *)self, childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height));
m_RootRect.origin.y = childFrame.size.height + childFrame.origin.y - (newFrame.size.height + newFrame.origin.y);
(void *)self, m_RootRect.origin.x, m_RootRect.origin.y, m_RootRect.size.width, m_RootRect.size.height));
printf ("sc rect: %d %d %d %d\n", (int) scrollRect.origin.x,(int) scrollRect.origin.y,(int) scrollRect.size.width,(int) scrollRect.size.height);
printf ("bound rect: %d %d %d %d\n", (int) b.origin.x,(int) b.origin.y,(int) b.size.width,(int) b.size.height);
if (m_pSharedGLCtx)
- (void)createDockTile
if (pDockScreen)
- (void)deleteDockTile
- (void)makeCurrentFBO
if (m_pGLCtx)
glFlush();
if (m_fNeedCtxUpdate == true)
m_fNeedCtxUpdate = false;
if (!m_FBOId)
if (m_pSharedGLCtx)
int rc = CrBltInit(m_pBlitter, NULL, false, false, &render_spu.GlobalShaders, &render_spu.blitterDispatch);
m_fNeedViewportUpdate = true;
- (void)vboxTryDraw
glFlush();
- (void)vboxTryDrawUI
if (pCompositor)
if (!m_pSharedGLCtx)
if (!pCompositor)
m_fCleanupNeeded = false;
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO];
- (void)swapFBO
m_fNeedViewportUpdate = true;
m_fNeedViewportUpdate = false;
m_fCleanupNeeded = false;
DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch)
DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch)
float xStretch;
float yStretch;
DEBUG_MSG(("OVIW(%p): rF2V frame: [%i, %i, %i, %i]\n", (void *)self, (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height));
m_fDataVisible = false;
int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
uint32_t i;
CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA);
m_fDataVisible = true;
# endif
- (void)vboxBlitterSyncWindow
NSRect r;
if (!m_pBlitter)
float xStretch;
float yStretch;
glFlush();
int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
uint32_t i;
glFinish();
[[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil
- (void)clearVisibleRegions
if (m_paClipRects)
if (m_fDataVisible)
m_fCleanupNeeded = true;
return GL_TRUE;
return GL_FALSE;
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]));
screenContent = [contentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView];
return screenContent;
- (void)reshapeDockTile
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);
void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx)
if (pFmt)
OverlayView* pView = [[OverlayView alloc] initWithFrame:NSZeroRect thread:RTThreadSelf() parentView:pParentView winInfo:pWinInfo];
if (pView)
return pView;
return NULL;
void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams)
if (renderspuCalloutAvailable())
if (!*ppView)
if (pOView)
if (renderspuCalloutAvailable())
void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry)
if (!pCtx)
if (!pCtxInfo)
if (pView)
return fNeedsPresent;