blob: 4431e1bfcf0879f423b657fab708533893f0a9b8 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2011 VMware, Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
**************************************************************************/
/**
* Minimal Cocoa integration.
*
* See also:
* - http://developer.apple.com/library/mac/#samplecode/CocoaGL/Introduction/Intro.html
* - http://developer.apple.com/library/mac/#samplecode/Cocoa_With_Carbon_or_CPP/Introduction/Intro.html
* - http://developer.apple.com/library/mac/#samplecode/glut/Introduction/Intro.html
* - http://developer.apple.com/library/mac/#samplecode/GLEssentials/Introduction/Intro.html
* - http://www.glfw.org/
* - http://cocoasamurai.blogspot.co.uk/2008/04/guide-to-threading-on-leopard.html
* - http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html
*/
#include "glproc.hpp"
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <dlfcn.h>
#include <Cocoa/Cocoa.h>
#include "glws.hpp"
/**
* Dummy thread to force Cocoa to enter multithreading mode.
*
* See also:
* - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
*/
@interface DummyThread : NSObject
+ (void)enterMultiThreaded;
+ (void)dummyThreadMethod:(id)unused;
@end
@implementation DummyThread
+ (void)dummyThreadMethod:(id)unused {
(void)unused;
}
+ (void)enterMultiThreaded {
[NSThread detachNewThreadSelector:@selector(dummyThreadMethod:)
toTarget:self
withObject:nil];
}
@end
namespace glws {
static __thread NSAutoreleasePool *
autoreleasePool = nil;
class CocoaVisual : public Visual
{
public:
NSOpenGLPixelFormat *pixelFormat;
CocoaVisual(Profile prof, NSOpenGLPixelFormat *pf) :
Visual(prof),
pixelFormat(pf)
{}
~CocoaVisual() {
[pixelFormat release];
}
};
class CocoaDrawable : public Drawable
{
public:
NSWindow *window;
NSOpenGLView *view;
NSOpenGLContext *currentContext;
CocoaDrawable(const Visual *vis, int w, int h, const pbuffer_info *info) :
Drawable(vis, w, h, info),
currentContext(nil)
{
NSOpenGLPixelFormat *pixelFormat = static_cast<const CocoaVisual *>(visual)->pixelFormat;
NSRect winRect = NSMakeRect(0, 0, w, h);
window = [[NSWindow alloc]
initWithContentRect:winRect
styleMask:NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable
backing:NSBackingStoreBuffered
defer:NO];
assert(window != nil);
view = [[NSOpenGLView alloc]
initWithFrame:winRect
pixelFormat:pixelFormat];
assert(view != nil);
[window setContentView:view];
[window setTitle:@"glretrace"];
}
~CocoaDrawable() {
[view release];
[window close];
}
void
resize(int w, int h) {
if (w == width && h == height) {
return;
}
[window setContentSize:NSMakeSize(w, h)];
processEvents();
if (currentContext != nil) {
[currentContext update];
[window makeKeyAndOrderFront:nil];
[view setOpenGLContext:currentContext];
[currentContext makeCurrentContext];
}
Drawable::resize(w, h);
}
void show(void) {
if (visible) {
return;
}
// TODO
Drawable::show();
}
void swapBuffers(void) {
if (currentContext != nil) {
[currentContext flushBuffer];
}
}
};
class CocoaContext : public Context
{
public:
NSOpenGLContext *context;
CocoaContext(const Visual *vis, NSOpenGLContext *ctx) :
Context(vis),
context(ctx)
{}
~CocoaContext() {
[context release];
}
};
static inline void
initThread(void) {
if (autoreleasePool == nil) {
autoreleasePool = [[NSAutoreleasePool alloc] init];
}
}
static GLint gRendererID = 0;
void
init(void) {
// Prevent glproc to load system's OpenGL, so that we can trace glretrace.
_libGlHandle = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LOCAL | RTLD_NOW | RTLD_FIRST);
assert(_libGlHandle);
initThread();
[DummyThread enterMultiThreaded];
bool isMultiThreaded = [NSThread isMultiThreaded];
if (!isMultiThreaded) {
std::cerr << "error: failed to enable Cocoa multi-threading\n";
exit(1);
}
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp finishLaunching];
[NSApp activateIgnoringOtherApps:YES];
// Allow to pick a specific renderer via a command line option
const char *vendor = getenv("VENDOR");
if (vendor != nullptr) {
CGLRendererInfoObj renderer_info = NULL;
GLint num_renderers = 0;
if (CGLQueryRendererInfo(~GLuint(0),
&renderer_info,
&num_renderers) == kCGLNoError) {
for (GLint i = 0; i < num_renderers; ++i) {
GLint rendererID = 0;
if (CGLDescribeRenderer(renderer_info,
i,
kCGLRPRendererID,
&rendererID) != kCGLNoError) {
continue;
}
GLint vendorID = rendererID & kCGLRendererIDMatchingMask & ~0xfff;
const char *name;
switch (vendorID) {
case 0x00020000:
name = "Software";
break;
case 0x00021000:
name = "AMD";
break;
case 0x00022000:
name = "NVIDIA";
break;
case 0x00024000:
name = "Intel";
break;
default:
name = "Unknown";
break;
}
if (gRendererID == 0 &&
strcasecmp(vendor, name) == 0) {
gRendererID = rendererID;
}
GLint accelerated = 0;
if (CGLDescribeRenderer(renderer_info,
i,
kCGLRPAccelerated,
&accelerated) == kCGLNoError) {
}
std::cerr << "info: found " << name
<< " renderer (0x" << std::hex << rendererID << std::dec << ") "
<< (accelerated ? "accelerated" : "unacellerated")
<< std::endl;
}
}
}
}
void
cleanup(void) {
[autoreleasePool release];
}
Visual *
createVisual(bool doubleBuffer, unsigned samples, Profile profile) {
initThread();
Attributes<NSOpenGLPixelFormatAttribute> attribs;
attribs.add(NSOpenGLPFAAlphaSize, (NSOpenGLPixelFormatAttribute)8);
attribs.add(NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24);
if (doubleBuffer) {
attribs.add(NSOpenGLPFADoubleBuffer);
}
attribs.add(NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)24);
attribs.add(NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)8);
if (profile.api != glfeatures::API_GL) {
return NULL;
}
// We snap 3.1 to 3.2 core, as allowed by GLX/WGL_ARB_create_context
if (profile.versionGreaterOrEqual(3, 1) || profile.core) {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
/*
* kCGLOGLPVersion_GL4_Core doesn't seem to work as expected. The
* recommended approach is to use NSOpenGLProfileVersion3_2Core and
* then check the OpenGL version.
*/
attribs.add(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
#else
return NULL;
#endif
} else if (profile.versionGreaterOrEqual(3, 0)) {
// Compatibility profile is not supported
return NULL;
}
if (gRendererID) {
attribs.add(NSOpenGLPFARendererID, gRendererID); // kCGLRendererGenericID
}
// Force hardware acceleration
// https://developer.apple.com/library/mac/qa/qa1502/_index.html
if (0) {
attribs.add(NSOpenGLPFAAccelerated);
attribs.add(NSOpenGLPFANoRecovery);
}
// Tell this context is offline renderer aware
// https://developer.apple.com/library/content/technotes/tn2229/_index.html
// TODO: override NSOpenGLView::update to receive notifications
attribs.add(NSOpenGLPFAAllowOfflineRenderers);
attribs.end();
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc]
initWithAttributes:attribs];
return new CocoaVisual(profile, pixelFormat);
}
Drawable *
createDrawable(const Visual *visual, int width, int height,
const pbuffer_info *info)
{
initThread();
return new CocoaDrawable(visual, width, height, info);
}
Context *
createContext(const Visual *visual, Context *shareContext, bool debug)
{
initThread();
NSOpenGLPixelFormat *pixelFormat = static_cast<const CocoaVisual *>(visual)->pixelFormat;
NSOpenGLContext *share_context = nil;
NSOpenGLContext *context;
if (shareContext) {
share_context = static_cast<CocoaContext*>(shareContext)->context;
}
context = [[NSOpenGLContext alloc]
initWithFormat:pixelFormat
shareContext:share_context];
assert(context != nil);
return new CocoaContext(visual, context);
}
bool
makeCurrentInternal(Drawable *drawable, Drawable *readable, Context *context)
{
// NOTE: the 'readable' argument is ignored.
initThread();
if (!drawable || !context) {
[NSOpenGLContext clearCurrentContext];
} else {
CocoaDrawable *cocoaDrawable = static_cast<CocoaDrawable *>(drawable);
CocoaContext *cocoaContext = static_cast<CocoaContext *>(context);
[cocoaDrawable->window makeKeyAndOrderFront:nil];
[cocoaDrawable->view setOpenGLContext:cocoaContext->context];
[cocoaContext->context makeCurrentContext];
cocoaDrawable->currentContext = cocoaContext->context;
}
return TRUE;
}
bool
processEvents(void) {
initThread();
if (![NSThread isMainThread]) {
return true;
}
NSEvent* event;
do {
event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event)
[NSApp sendEvent:event];
} while (event);
return true;
}
bool
bindTexImage(Drawable *pBuffer, int iBuffer) {
std::cerr << "error: Cocoa::wglBindTexImageARB not implemented.\n";
assert(pBuffer->pbuffer);
return true;
}
bool
releaseTexImage(Drawable *pBuffer, int iBuffer) {
std::cerr << "error: Cocoa::wglReleaseTexImageARB not implemented.\n";
assert(pBuffer->pbuffer);
return true;
}
bool
setPbufferAttrib(Drawable *pBuffer, const int *attribList) {
// nothing to do here.
assert(pBuffer->pbuffer);
return true;
}
} /* namespace glws */