| /************************************************************************** |
| * |
| * 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 */ |