renderspu_cocoa_helper.m revision 07557d07616212d7ba6e7ab3059e85cb14633775
/* $Id$ */
/** @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-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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
* 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
* the correct buffers. On swap our buffers are swapped and not the
* 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
* 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 *
*******************************************************************************/
#include "renderspu_cocoa_helper.h"
#import <Cocoa/Cocoa.h>
# include "DevVGA-SVGA3d-cocoa.h"
# include <OpenGL/gl3.h>
#else
# include <cr_blitter.h>
# include <cr_pixeldata.h>
# endif
# include "renderspu.h"
/*******************************************************************************
* 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. */
/** @def CR_RENDER_FORCE_PRESENT_MAIN_THREAD
* Force present schedule to main thread. */
/** @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. */
# error "should be disabled!"
# else
# endif
#else
# else
#else
do { \
glPushMatrix(); \
glPushMatrix(); \
glMatrixMode(GL_COLOR); \
glPushMatrix(); \
glPushMatrix(); \
} while (0)
do { \
glPopMatrix(); \
glMatrixMode(GL_COLOR); \
glPopMatrix(); \
glPopMatrix(); \
glPopMatrix(); \
glPopClientAttrib(); \
glPopAttrib(); \
} while (0)
{
if (uGlErr != GL_NO_ERROR)
{
const char *errStr;
switch (uGlErr)
{
default: errStr = "UNKNOWN"; break;
}
}
}
#else
/*
* VMSVGA3D compatibility glue.
*/
{
//NativeNSViewRef window;
//NativeNSViewRef nativeWindow; /**< for render_to_app_window */
} WindowInfo;
{
/** @todo */
}
{
}
{
if (!cRefs)
}
{
if (ppCompositor)
return VINF_SUCCESS;
}
{
return rc;
}
{
if (pCompositor)
{
return pCompositor;
}
/* if no compositor is set, release the lock and return */
return NULL;
}
{
}
#endif /* IN_VMSVGA3D */
static NSOpenGLContext *vboxCtxGetCurrent(void)
{
return [NSOpenGLContext currentContext];
#else
if (pCtxInfo)
{
}
return nil;
}
static bool vboxCtxSyncCurrentInfo(void)
{
return false;
#else
if (pCtxInfo)
{
{
fAdjusted = true;
}
}
else if (pCtx)
{
fAdjusted = true;
}
return fAdjusted;
}
/**
* State carrying structure for use with vboxCtxEnter and vboxCtxLeave
*/
{
/** Pointer to render context info for use with vboxCtxEnter/Leave. */
{
{
glFlush();
/** @todo r=bird: Why do we save the NEW VIEW here? vboxCtxLeave calls it 'pOldView'. Bug? */
}
else
{
/* No context switch necessary. */
}
}
{
{
glFlush();
{
/* 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. */
{
}
{
}
}
else
{
}
}
}
/**
* 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
}
@end
/**
* Abstrack task class.
*/
{
}
- (void)run;
@end
/** Run method that the child classes must reimplement.
* This will abort the process. */
- (void)run
{
}
@end
/**
* Generic task class for executing a given method select.
*/
@interface VBoxTaskPerformSelector : VBoxTask
{
@private
}
- (void)run;
- (void)dealloc;
@end
/**
* Initializes a VBoxTaskPerformSelector.
*
* @param aObject The object (reference not consumed).
* @param aSelector The method selector.
* @param aArg The method argument (reference not consumed).
*/
{
if (self)
{
}
return self;
}
- (void)run
{
}
- (void)dealloc
{
[super dealloc];
}
@end
/**
*
*/
@interface VBoxTaskComposite : VBoxTask
{
@private
}
- (void)run;
- (void)dealloc;
@end
{
if (self)
{
if (!RT_SUCCESS(rc))
{
return nil;
}
m_CurIndex = 0;
}
return self;
}
/**
* Adds a task to the composite task object.
*
* @param pTask Task to add. Reference is NOT consumed.
*/
{
if (RT_SUCCESS(rc))
{
}
else
{
}
}
- (void)run
{
for (;;)
{
/*
* Dequeue a task.
*/
if (RT_FAILURE(rc))
{
break;
}
if (m_CurIndex == count)
{
m_CurIndex = 0;
break;
}
++m_CurIndex;
/*
* Remove the first 1025 empty entires.
*/
if (m_CurIndex > 1024)
{
m_CurIndex = 0;
}
/*
* Run the task and release it.
*/
}
}
- (void)dealloc
{
{
}
[super dealloc];
}
@end
/**
*
*
*/
@interface VBoxMainThreadTaskRunner : NSObject
{
@private
}
- (void)runTasks;
- (void)dealloc;
@end
{
if (self)
{
}
return self;
}
{
static dispatch_once_t s_DispatchOnce;
});
return s_pRunner;
}
{
/** @todo r=bird: Unbalanced [self retain]. */
if (![self runTasksSyncIfPossible])
{
DEBUG_MSG(("task will be processed async\n"));
}
}
/**
* 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).
*/
{
VBoxTaskPerformSelector *pSelTask = [[VBoxTaskPerformSelector alloc] initWithObject:aObject selector:aSelector arg:aArg];
}
/**
* Internal method for running the pending tasks.
*/
- (void)runTasks
{
if ([NSThread isMainThread])
{
/** @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,
}
else
{
DEBUG_WARN(("run tasks called not on main thread!\n"));
AssertFailed();
}
}
/**
* Callback for calling runTasks via renderspuCalloutClient.
* @param pvUser The VBoxMainThreadTaskRunner singleton.
*/
{
}
/**
* Runs pending tasks synchronously, if possible in the current context.
*
* @returns true if executed tasks, false if not possible.
*/
{
/*
* Call on main thread (?) via renderspuCalloutClient (whatever that is).
*/
if (renderspuCalloutAvailable())
{
return true;
}
/*
* Run directly if on main thread.
*/
if ([NSThread isMainThread])
{
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! */
[super dealloc];
}
@end
@class DockOverlayView;
/**
* 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
/** The corresponding dock tile view of this OpenGL view & all helper
* members. */
/** @name For clipping
* @remarks appears to be unused and a complete waste of time + heap.
* @{ */
/** @} */
* @{ */
/** @} */
/** This is necessary for clipping on the root window */
float m_yInvRootOffset;
}
- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo;
- (NSOpenGLContext *)glCtx;
- (NSView *)parentView;
- (NSWindow *)overlayWin;
- (bool)isEverSized;
- (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;
- (NSView *)dockTileScreen;
- (void)reshapeDockTile;
- (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
}
@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
}
@end
/**
* Dock overlay view class.
*/
@interface DockOverlayView: NSView
{
}
- (void)dealloc;
- (void)cleanup;
- (void)lock;
- (void)unlock;
- (NSBitmapImageRep *)thumbBitmap;
- (NSImage *)thumbImage;
@end
{
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.
*/
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)cleanup
{
if (m_ThumbImage != nil)
{
m_ThumbImage = nil;
}
if (m_ThumbBitmap != nil)
{
m_ThumbBitmap = nil;
}
}
- (void)lock
{
}
- (void)unlock
{
}
{
{
/* Create a buffer for our thumbnail image. Its in the size of this view. */
];
}
}
{
return YES;
}
{
#endif /* SHOW_WINDOW_BACKGROUND */
if (m_ThumbImage != nil)
[m_ThumbImage drawAtPoint:NSMakePoint(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
{
return m_ThumbBitmap;
}
- (NSImage *)thumbImage
{
return m_ThumbImage;
}
@end
/********************************************************************************
*
* OverlayOpenGLContext class implementation
*
********************************************************************************/
{
if (self)
return self;
}
- (void)dealloc
{
[super dealloc];
}
{
}
{
#if 1 /* def FBO */
#else
}
{
#if 1 /* def FBO */
return m_pView;
#else
return [super view];
}
-(void)clearDrawable
{
[super clearDrawable];
}
{
return m_pPixelFormat;
}
@end /* @implementation OverlayOpenGLContext */
/********************************************************************************
*
* OverlayHelperView class implementation
*
********************************************************************************/
{
return self;
}
-(void)viewDidMoveToWindow
{
}
@end
/********************************************************************************
*
* OverlayWindow class implementation
*
********************************************************************************/
{
self = [super initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self)
{
/* Add the helper view as a child of the parent view to get notifications */
/* Make sure this window is transparent */
/* For debugging */
#else
/* Disable mouse events for this window */
/* Set the overlay view as our content view */
/* 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. */
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
{
/*
* 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])
else
}
}
{
{
/* Ask to get notifications when our parent window frame changes. */
/* Add us self as child window */
/*
* 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])
else
}
}
}
@end /* @implementation OverlayWindow */
/********************************************************************************
*
* OverlayView class implementation
*
********************************************************************************/
- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo
{
COCOA_LOG_FLOW(("%s: self=%p aThread=%p pParentView=%p pWinInfo=%p\n", __PRETTY_FUNCTION__, (void *)self,
/* Make some reasonable defaults */
m_FBOId = 0;
m_cClipRects = 0;
m_Pos = NSZeroPoint;
m_yInvRootOffset = 0;
m_pBlitter = nil;
m_fNeedViewportUpdate = true;
m_fNeedCtxUpdate = true;
m_fDataVisible = false;
m_fCleanupNeeded = false;
m_fEverSized = false;
return self;
}
- (void)cleanupData
{
if (m_pSharedGLCtx)
{
m_pBlitter = nil;
}
}
- (void)dealloc
{
[self cleanupData];
[super dealloc];
}
{
COCOA_LOG_FLOW(("%s: self=%p aRect=%d,%d %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)aRect.origin.x, (int)aRect.origin.y,
}
{
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.
*/
{
/* Ensure the context drawable is cleared to avoid holding a reference to inexistent view. */
if (m_pGLCtx)
{
/*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/
}
if (pCtx)
}
}
- (NSOpenGLContext *)glCtx
{
return m_pGLCtx;
}
- (NSView *)parentView
{
COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pParentView));
return m_pParentView;
}
{
COCOA_LOG_FLOW(("%s: self=%p pView=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pView, m_pParentView));
}
{
COCOA_LOG_FLOW(("%s: self=%p pWin=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pWin, m_pOverlayWin));
}
- (NSWindow *)overlayWin
{
COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pOverlayWin));
return m_pOverlayWin;
}
{
COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y,
if (m_fEverSized)
}
{
COCOA_LOG_FLOW(("%s: self=%p pPos=%p (%d,%d) (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, pPos,
}
{
COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y,
}
{
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
{
return m_fEverSized;
}
- (void)vboxDestroy
{
/* Hide the view early. */
if (fIsMain)
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. */
}
[self cleanupData];
if (fIsMain)
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. */
}
}
{
}
{
COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height));
m_fEverSized = true;
DEBUG_MSG(("OVIW(%p): vboxSetSize: new size: %dx%d\n", (void *)self, (int)m_Size.width, (int)m_Size.height));
/* ensure window contents is updated after that */
}
{
COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height));
}
{
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
{
/* Update the viewport for our OpenGL view. */
/* Clear background to transparent. */
}
- (void)vboxReshapeOnResizePerform
{
/* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */
m_fNeedViewportUpdate = true;
#if 0
{
m_fNeedCtxUpdate = false;
}
else
{
/* do it in a lazy way */
m_fNeedCtxUpdate = true;
}
}
- (void)vboxReshapeOnReparentPerform
{
}
- (void)vboxReshapePerform
{
COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView));
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));
/* We have to make sure that the overlay window will not be displayed out
* of the parent window. So intersect both frames & use the result as the new
* frame for the window. */
DEBUG_MSG(("[%#p]: parentFrame pos[%f : %f] size[%f : %f]\n",
(void *)self, parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height));
DEBUG_MSG(("[%#p]: childFrame pos[%f : %f] size[%f : %f]\n",
(void *)self, childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height));
DEBUG_MSG(("[%#p]: newFrame pos[%f : %f] size[%f : %f]\n",
/* 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.y = childFrame.size.height + childFrame.origin.y - (newFrame.size.height + newFrame.origin.y);
DEBUG_MSG(("[%#p]: m_RootRect pos[%f : %f] size[%f : %f]\n",
(void *)self, m_RootRect.origin.x, m_RootRect.origin.y, m_RootRect.size.width, m_RootRect.size.height));
/*
NSScrollView *pScrollView = [[[m_pParentView window] contentView] enclosingScrollView];
if (pScrollView)
{
NSRect scrollRect = [pScrollView documentVisibleRect];
NSRect scrollRect = [m_pParentView visibleRect];
printf ("sc rect: %d %d %d %d\n", (int) scrollRect.origin.x,(int) scrollRect.origin.y,(int) scrollRect.size.width,(int) scrollRect.size.height);
NSRect b = [[m_pParentView superview] bounds];
printf ("bound rect: %d %d %d %d\n", (int) b.origin.x,(int) b.origin.y,(int) b.size.width,(int) b.size.height);
newFrame.origin.x += scrollRect.origin.x;
newFrame.origin.y += scrollRect.origin.y;
}
*/
/* Set the new frame. */
/* Inform the dock tile view as well. */
/* Make sure the context is updated accordingly. */
/* [self updateViewport]; */
if (m_pSharedGLCtx)
{
}
}
- (void)createDockTile
{
/* Is there a dock tile preview enabled in the GUI? If so setup a
* additional thumbnail view for the dock tile. */
if (pDockScreen)
{
}
}
- (void)deleteDockTile
{
COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView));
if (m_DockTileView != nil)
{
}
}
- (void)makeCurrentFBO
{
if (m_pGLCtx)
{
{
/* We change the active view, so flush first */
glFlush();
}
/*
if ([NSOpenGLContext currentContext] != m_pGLCtx)
*/
{
if (m_fNeedCtxUpdate == true)
{
m_fNeedCtxUpdate = false;
}
}
if (!m_FBOId)
{
}
}
}
{
if (m_pSharedGLCtx)
{
COCOA_LOG_FLOW(("%s: returns true (m_pSharedGLCtx=%p)\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx));
return true;
}
Assert(!m_pBlitter);
if (RT_UNLIKELY(!m_pBlitter))
{
DEBUG_WARN(("m_pBlitter allocation failed"));
return false;
}
#else
);
if (RT_FAILURE(rc))
{
m_pBlitter = NULL;
return false;
}
COCOA_LOG_FLOW(("%s: blitter (%p) created successfully for view 0x%p\n", (void *)m_pBlitter, (void *)self));
/* Create a shared context out of the main context. Use the same pixel format. */
NSOpenGLContext *pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:m_pGLCtx];
/* Set the new context as non opaque */
/* Set this view as the drawable for the new context */
m_fNeedViewportUpdate = true;
COCOA_LOG_FLOW(("%s: returns true - new m_pSharedGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx));
return true;
}
- (void)vboxTryDraw
{
glFlush();
/* Issue to the gui thread. */
}
{
}
{
}
{
}
{
COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)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. */
{
}
/* Set the new parent view */
/* Add the overlay window as a child to the new parent window */
if (pParentView != nil)
{
if ([self isEverSized])
}
}
- (void)vboxTryDrawUI
{
{
return;
}
{
return;
}
if (!m_pSharedGLCtx)
{
if (![self vboxSharedCtxCreate])
{
return;
}
}
const VBOXVR_SCR_COMPOSITOR *pCompositor;
if (RT_FAILURE(rc))
{
COCOA_LOG_FLOW(("%s: returns - renderspuVBoxCompositorLock failed (%Rrc)\n", __PRETTY_FUNCTION__, rc));
return;
}
if (!pCompositor && !m_fCleanupNeeded)
{
return;
}
if (pCompositor)
{
if (!m_pSharedGLCtx)
{
if (![self vboxSharedCtxCreate])
{
return;
}
if (!pCompositor)
{
return;
}
}
}
else
{
}
if ([self lockFocusIfCanDraw])
{
[self unlockFocus];
}
else if (!m_pWinInfo->visible)
{
m_fCleanupNeeded = false;
}
else
{
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO];
}
}
- (void)swapFBO
{
COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx));
}
{
COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor));
/*DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void *)self));*/
}
{
COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor));
{
COCOA_LOG_FLOW(("%s: Not current view of shared ctx! Switching... (self=%p, view=%p, m_pSharedGLCtx)\n",
m_fNeedViewportUpdate = true;
}
{
m_fNeedViewportUpdate = false;
}
m_fCleanupNeeded = false;
/* Render FBO content to the dock tile when necessary. */
/* change to #if 0 to see thumbnail image */
#if 1
#else
}
{
}
DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch)
{
}
DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch)
{
}
{
#if 1 /* Set to 0 to see the docktile instead of the real output */
const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
/* Clear background to transparent */
m_fDataVisible = false;
float xStretch;
float yStretch;
{
int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
uint32_t i;
for (i = 0; i < cRegions; ++i)
{
const CR_TEXDATA *pTexData;
if (VBoxRectIsZero(&DstRect))
continue;
if (VBoxRectIsZero(&SrcRect))
continue;
CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA);
m_fDataVisible = true;
}
}
else
{
# endif
}
}
else
{
}
}
/*
glFinish();
*/
}
{
COCOA_LOG_FLOW(("%s: self=%p pChangedEntry=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pChangedEntry));
[self vboxTryDraw];
}
- (void)vboxBlitterSyncWindow
{
NSRect r;
if (!m_pBlitter)
return;
/*CrBltMuralSetCurrentInfo(m_pBlitter, NULL);*/
}
static int g_cVBoxTgaCtr = 0;
{
COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor));
float xStretch;
float yStretch;
{
/*
* Only update after at least 200 ms, cause glReadPixels is
* heavy performance wise.
*/
const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
{
#if 0
/* todo: check this for optimization */
glFlush();
/* Do other work processing here, using a double or triple buffer */
/* Clear background to transparent */
{
int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
uint32_t i;
for (i = 0; i < cRegions; ++i)
{
const CR_TEXDATA *pTexData;
if (VBoxRectIsZero(&DstRect))
continue;
if (VBoxRectIsZero(&SrcRect))
continue;
}
}
else
{
}
}
else
{
}
}
glFinish();
/* 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. */
/* Send a display message to the dock tile in the main thread */
[[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil
}
}
}
- (void)clearVisibleRegions
{
if (m_paClipRects)
{
}
m_cClipRects = 0;
}
{
if (m_fDataVisible)
{
m_fCleanupNeeded = true;
return GL_TRUE;
}
return GL_FALSE;
}
{
COCOA_LOG_FLOW(("%s: self=%p cRects=%d paRects=%p\n", __PRETTY_FUNCTION__, (void *)self, cRects, (void *)paRects));
if (cRects > 0)
{
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]));
}
}
- (NSView *)dockTileScreen
{
/*
* First try the new variant which checks if this window is within the
* screen which is previewed in the dock.
*/
pScreenContent = [pContentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView];
/*
* If it fails, fall back to the old variant (VBox...).
*/
COCOA_LOG_FLOW(("%s: returns %p (pContentView=%p)\n", __PRETTY_FUNCTION__, (void *)pScreenContent, (void *)pContentView));
return pScreenContent;
}
- (void)reshapeDockTile
{
{
/** @todo This is not correct, we should use framebuffer size here, while
* parent view frame size may differ in case of scrolling. */
/*
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);
*/
}
COCOA_LOG_FLOW(("%s: returns - newFrame={%d,%d %d,%d} pView=%d\n", __PRETTY_FUNCTION__, (int)newFrame.origin.x,
}
@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));
{
};
int i = 3;
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;
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;
*/
}
{
COCOA_LOG_FLOW((" Offline rendering is enabled\n"));
}
/* Mark the end */
attribs[i++] = 0;
/* Choose a pixel format */
if (pFmt)
{
/* 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();
}
}
{
/*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/
COCOA_LOG_FLOW(("cocoaGLCtxDestroy: returns\n"));
}
/********************************************************************************
*
* View management
*
********************************************************************************/
{
/* Create our worker view. */
if (pView)
{
/* We need a real window as container for the view */
/* Return the freshly created overlay view */
return pView;
}
COCOA_LOG_FLOW(("vboxViewCreate: returns NULL\n"));
return NULL;
}
{
/* out */
{
}
#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",
/* make sure all tasks are run, to preserve the order */
if (renderspuCalloutAvailable())
{
}
else
{
DEBUG_MSG_NOT_VMSVGA3D(("no callout available on createWindow\n"));
#if 0
#if 0
});
}
if (!*ppView)
}
{
COCOA_LOG_FLOW(("cocoaViewReparent: pView=%p pParentView=%p\n", (void *)pView, (void *)pParentView));
if (pOView)
COCOA_LOG_FLOW(("cocoaViewReparent: returns\n"));
}
{
COCOA_LOG_FLOW(("cocoaViewDestroy: returns\n"));
}
{
COCOA_LOG_FLOW(("cocoaViewShow: returns\n"));
}
{
DEBUG_WARN(("cocoaViewDisplay should never happen!\n"));
COCOA_LOG_FLOW(("cocoaViewDisplay: returns\n"));
}
{
COCOA_LOG_FLOW(("cocoaViewSetPosition: pView=%p pParentView=%p x=%d y=%d\n", (void *)pView, (void *)pParentView, x, y));
COCOA_LOG_FLOW(("cocoaViewSetPosition: returns\n"));
}
{
COCOA_LOG_FLOW(("cocoaViewSetSize: returns\n"));
}
{
{
COCOA_LOG_FLOW(("vboxRcdGetGeomerty: (x,y)=(%d,%d) (cx,cy)=(%d,%d)\n", pGetGeometry->rect.origin.x, pGetGeometry->rect.origin.y,
}
#endif /* !IN_VMSVGA3D */
{
COCOA_LOG_FLOW(("cocoaViewGetGeometry: pView=%p px=%p py=%p pcx=%p pcy=%p\n",
/* make sure all tasks are run, to preserve the order */
if (renderspuCalloutAvailable())
{
}
else
{
DEBUG_MSG_NOT_VMSVGA3D(("no callout available on getGeometry\n"));
}
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));
/* view should not necesserily have a context set */
if (!pCtx)
{
#else
if (!pCtxInfo)
{
DEBUG_WARN(("renderspuDefaultSharedContextAcquire returned NULL"));
return;
}
}
COCOA_LOG_FLOW(("cocoaViewPresentComposition: returns\n"));
}
{
if (pView)
{
}
else
{
}
COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: returns\n"));
}
{
return fNeedsPresent;
}
{
COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: pView=%p cRects=%d paRects=%p)\n", (void *)pView, cRects, (void const *)paRects));
COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: returns\n"));
}
/*
* VMSVGA3D interface.
*/
VMSVGA3D_DECL(void) vmsvga3dCocoaCreateContext(NativeNSOpenGLContextRef *ppCtx, NativeNSOpenGLContextRef pSharedCtx,
{
cocoaGLCtxCreate(ppCtx, CR_ALPHA_BIT | CR_DEPTH_BIT | CR_DOUBLE_BIT | (fOtherProfile ? VMSVGA3D_NON_DEFAULT_PROFILE_BIT : 0),
}
{
}
{
/** @todo share WinInfo with caller and maintain it better. */
}
{
}
VMSVGA3D_DECL(void) vmsvga3dCocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y)
{
}
{
cocoaViewSetSize(pView, w, h);
}
VMSVGA3D_DECL(void) vmsvga3dCocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx)
{
}
{
# if 1
# else
[pCtx flushBuffer];
# endif
}
#endif /* IN_VMSVGA3D */