| /************************************************************************** |
| * |
| * Copyright 2011 Jose Fonseca |
| * 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. |
| * |
| **************************************************************************/ |
| |
| |
| #include <string.h> |
| |
| #include "glproc.hpp" |
| #include "retrace.hpp" |
| #include "retrace_swizzle.hpp" |
| #include "glretrace.hpp" |
| |
| |
| #define CGL_PBUFFER_HEIGHT 0x8040 |
| #define CGL_PBUFFER_WIDTH 0x8041 |
| |
| #define kCGLNoError 0 |
| |
| #define kCGLPFAAllRenderers 1 |
| #define kCGLPFATripleBuffer 3 |
| #define kCGLPFADoubleBuffer 5 |
| #define kCGLPFAStereo 6 |
| #define kCGLPFAAuxBuffers 7 |
| #define kCGLPFAColorSize 8 |
| #define kCGLPFAAlphaSize 11 |
| #define kCGLPFADepthSize 12 |
| #define kCGLPFAStencilSize 13 |
| #define kCGLPFAAccumSize 14 |
| #define kCGLPFAMinimumPolicy 51 |
| #define kCGLPFAMaximumPolicy 52 |
| #define kCGLPFAOffScreen 53 |
| #define kCGLPFAFullScreen 54 |
| #define kCGLPFASampleBuffers 55 |
| #define kCGLPFASamples 56 |
| #define kCGLPFAAuxDepthStencil 57 |
| #define kCGLPFAColorFloat 58 |
| #define kCGLPFAMultisample 59 |
| #define kCGLPFASupersample 60 |
| #define kCGLPFASampleAlpha 61 |
| #define kCGLPFARendererID 70 |
| #define kCGLPFASingleRenderer 71 |
| #define kCGLPFANoRecovery 72 |
| #define kCGLPFAAccelerated 73 |
| #define kCGLPFAClosestPolicy 74 |
| #define kCGLPFARobust 75 |
| #define kCGLPFABackingStore 76 |
| #define kCGLPFABackingVolatile 77 |
| #define kCGLPFAMPSafe 78 |
| #define kCGLPFAWindow 80 |
| #define kCGLPFAMultiScreen 81 |
| #define kCGLPFACompliant 83 |
| #define kCGLPFADisplayMask 84 |
| #define kCGLPFAPBuffer 90 |
| #define kCGLPFARemotePBuffer 91 |
| #define kCGLPFAAllowOfflineRenderers 96 |
| #define kCGLPFAAcceleratedCompute 97 |
| #define kCGLPFAOpenGLProfile 99 |
| #define kCGLPFASupportsAutomaticGraphicsSwitching 101 |
| #define kCGLPFAVirtualScreenCount 128 |
| |
| #define kCGLOGLPVersion_Legacy 0x1000 |
| #define kCGLOGLPVersion_3_2_Core 0x3200 |
| #define kCGLOGLPVersion_GL3_Core 0x3200 |
| #define kCGLOGLPVersion_GL4_Core 0x4100 |
| |
| |
| using namespace glretrace; |
| |
| |
| typedef std::map<unsigned long long, glws::Drawable *> DrawableMap; |
| typedef std::map<unsigned long long, Context *> ContextMap; |
| |
| // sid -> Drawable* map |
| static DrawableMap drawable_map; |
| |
| static DrawableMap pbuffer_map; |
| |
| // ctx -> Context* map |
| static ContextMap context_map; |
| |
| static Context *sharedContext = NULL; |
| |
| |
| struct PixelFormat |
| { |
| glfeatures::Profile profile; |
| |
| PixelFormat() : |
| profile(glfeatures::API_GL, 1, 0) |
| {} |
| }; |
| |
| |
| static glws::Drawable * |
| getDrawable(unsigned long drawable_id, glfeatures::Profile profile) { |
| if (drawable_id == 0) { |
| return NULL; |
| } |
| |
| DrawableMap::const_iterator it; |
| it = drawable_map.find(drawable_id); |
| if (it == drawable_map.end()) { |
| return (drawable_map[drawable_id] = glretrace::createDrawable(profile)); |
| } |
| |
| return it->second; |
| } |
| |
| static glws::Drawable * |
| getDrawable(unsigned long long hdc) { |
| if (hdc == 0) { |
| return NULL; |
| } |
| |
| DrawableMap::const_iterator it; |
| it = drawable_map.find(hdc); |
| if (it == drawable_map.end()) { |
| return (drawable_map[hdc] = glretrace::createDrawable()); |
| } |
| |
| return it->second; |
| } |
| |
| static Context * |
| getContext(unsigned long long ctx) { |
| if (ctx == 0) { |
| return NULL; |
| } |
| |
| ContextMap::const_iterator it; |
| it = context_map.find(ctx); |
| if (it == context_map.end()) { |
| Context *context; |
| context_map[ctx] = context = glretrace::createContext(sharedContext); |
| if (!sharedContext) { |
| sharedContext = context; |
| } |
| return context; |
| } |
| |
| return it->second; |
| } |
| |
| |
| static void retrace_CGLChoosePixelFormat(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| bool singleBuffer = true; |
| int profile = 0; |
| |
| const trace::Array * attribs = call.arg(0).toArray(); |
| if (attribs) { |
| size_t i = 0; |
| while (i < attribs->values.size()) { |
| int param = attribs->values[i++]->toSInt(); |
| if (param == 0) { |
| break; |
| } |
| |
| switch (param) { |
| |
| // Boolean attributes |
| |
| case kCGLPFAAllRenderers: |
| case kCGLPFAStereo: |
| case kCGLPFAMinimumPolicy: |
| case kCGLPFAMaximumPolicy: |
| case kCGLPFAOffScreen: |
| case kCGLPFAFullScreen: |
| case kCGLPFAAuxDepthStencil: |
| case kCGLPFAColorFloat: |
| case kCGLPFAMultisample: |
| case kCGLPFASupersample: |
| case kCGLPFASampleAlpha: |
| case kCGLPFASingleRenderer: |
| case kCGLPFANoRecovery: |
| case kCGLPFAAccelerated: |
| case kCGLPFAClosestPolicy: |
| case kCGLPFARobust: |
| case kCGLPFABackingStore: |
| case kCGLPFABackingVolatile: |
| case kCGLPFAMPSafe: |
| case kCGLPFAWindow: |
| case kCGLPFAMultiScreen: |
| case kCGLPFACompliant: |
| case kCGLPFAPBuffer: |
| case kCGLPFARemotePBuffer: |
| case kCGLPFAAllowOfflineRenderers: |
| case kCGLPFAAcceleratedCompute: |
| case kCGLPFASupportsAutomaticGraphicsSwitching: |
| break; |
| |
| case kCGLPFADoubleBuffer: |
| case kCGLPFATripleBuffer: |
| singleBuffer = false; |
| break; |
| |
| // Valued attributes |
| |
| case kCGLPFAColorSize: |
| case kCGLPFAAlphaSize: |
| case kCGLPFADepthSize: |
| case kCGLPFAStencilSize: |
| case kCGLPFAAuxBuffers: |
| case kCGLPFAAccumSize: |
| case kCGLPFASampleBuffers: |
| case kCGLPFASamples: |
| case kCGLPFARendererID: |
| case kCGLPFADisplayMask: |
| case kCGLPFAVirtualScreenCount: |
| ++i; |
| break; |
| |
| case kCGLPFAOpenGLProfile: |
| profile = attribs->values[i++]->toSInt(); |
| break; |
| |
| default: |
| retrace::warning(call) << "unexpected attribute " << param << "\n"; |
| break; |
| } |
| } |
| } |
| |
| trace::Value & pix = call.argByName("pix")[0]; |
| |
| PixelFormat *pixelFormat = new PixelFormat; |
| |
| // TODO: Do this on a per visual basis |
| switch (profile) { |
| case 0: |
| break; |
| case kCGLOGLPVersion_Legacy: |
| pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 1, 0); |
| break; |
| case kCGLOGLPVersion_GL3_Core: |
| pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 3, 2, true, true); |
| break; |
| case kCGLOGLPVersion_GL4_Core: |
| pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 4, 1, true, true); |
| break; |
| default: |
| retrace::warning(call) << "unexpected opengl profile " << std::hex << profile << std::dec << "\n"; |
| break; |
| } |
| |
| // XXX: Generalize this, don't override command line options. |
| retrace::doubleBuffer = !singleBuffer; |
| |
| retrace::addObj(call, pix, pixelFormat); |
| } |
| |
| |
| static void retrace_CGLDestroyPixelFormat(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| trace::Value & pix = call.argByName("pix"); |
| |
| PixelFormat *pixelFormat = retrace::asObjPointer<PixelFormat>(call, pix); |
| delete pixelFormat; |
| |
| retrace::delObj(pix); |
| } |
| |
| |
| static void retrace_CGLCreateContext(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| trace::Value & pix = call.argByName("pix"); |
| const PixelFormat *pixelFormat = retrace::asObjPointer<PixelFormat>(call, pix); |
| glfeatures::Profile profile = pixelFormat ? pixelFormat->profile : glretrace::defaultProfile; |
| |
| unsigned long long share = call.arg(1).toUIntPtr(); |
| Context *sharedContext = getContext(share); |
| |
| const trace::Array *ctx_ptr = call.arg(2).toArray(); |
| assert(ctx_ptr); |
| unsigned long long ctx = ctx_ptr->values[0]->toUIntPtr(); |
| |
| Context *context = glretrace::createContext(sharedContext, profile); |
| context_map[ctx] = context; |
| } |
| |
| |
| static void retrace_CGLDestroyContext(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| |
| ContextMap::iterator it; |
| it = context_map.find(ctx); |
| if (it == context_map.end()) { |
| return; |
| } |
| |
| it->second->release(); |
| |
| context_map.erase(it); |
| } |
| |
| /* |
| CGLError CGLCreatePBuffer (long width, |
| long height, |
| unsigned long target, |
| unsigned long internalFormat, |
| long max_level, |
| CGLPBufferObj *pbuffer); |
| */ |
| static void retrace_CGLCreatePBuffer(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| int width = (call.arg(0)).toSInt(); |
| int height = (call.arg(1)).toSInt(); |
| int target = (call.arg(2)).toUInt(); |
| int internalFormat = (call.arg(3)).toUInt(); |
| int max_level = (call.arg(4)).toSInt(); |
| unsigned long long pb = call.arg(5).toUIntPtr(); |
| |
| glws::pbuffer_info pbInfo = {0, 0, false}; |
| pbInfo.texFormat = internalFormat; |
| pbInfo.texTarget = target; |
| |
| glws::Drawable *drawable = glretrace::createPbuffer(width, height, &pbInfo); |
| |
| drawable->cubeFace = 0; |
| drawable->mipmapLevel = max_level; |
| |
| pbuffer_map[pb] = drawable; |
| drawable_map[pb] = drawable; |
| } |
| |
| static void retrace_CGLDestroyPBuffer(trace::Call &call) { |
| glws::Drawable *drawable = getDrawable(call.arg(0).toUInt()); |
| |
| if (!drawable) { |
| return; |
| } |
| |
| delete drawable; |
| } |
| |
| /* |
| CGLError CGLGetPBuffer(CGLContextObj ctx, |
| CGLPBufferObj *pbuffer, |
| unsigned long *face, |
| long *level, |
| long *screen); |
| */ |
| static void retrace_CGLGetPBuffer(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| unsigned long long pbuffer = call.arg(1).toUIntPtr(); |
| |
| glws::Drawable *drawable = pbuffer_map[ctx]; |
| |
| if (!drawable) { |
| return; |
| } |
| |
| drawable_map[pbuffer] = drawable; |
| } |
| |
| /* |
| CGLSetPBuffer |
| Attaches a pixel buffer object to a rendering context. |
| CGLError CGLSetPBuffer(CGLContextObj ctx, |
| CGLPBufferObj pbuffer, |
| unsigned long face, |
| long level, |
| long screen); |
| */ |
| static void retrace_CGLSetPBuffer(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| unsigned long long pbuffer = call.arg(1).toUIntPtr(); |
| |
| glws::Drawable *drawable = pbuffer_map[ctx]; |
| |
| if (!drawable) { |
| glws::pbuffer_info pbInfo = {0, 0, false}; |
| |
| drawable = glretrace::createPbuffer(CGL_PBUFFER_WIDTH, CGL_PBUFFER_HEIGHT, &pbInfo); |
| } |
| |
| drawable->cubeFace = (call.arg(2)).toUInt(); |
| drawable->mipmapLevel = (call.arg(3)).toSInt(); |
| |
| pbuffer_map[ctx] = drawable; |
| |
| drawable_map[pbuffer] = drawable; |
| } |
| |
| static void retrace_CGLSetSurface(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| unsigned long long cid = call.arg(1).toUInt(); |
| int wid = call.arg(2).toUInt(); |
| int sid = call.arg(3).toUInt(); |
| |
| (void)cid; |
| (void)wid; |
| |
| Context *context = getContext(ctx); |
| if (context) { |
| glws::Drawable *drawable = getDrawable(sid, context->profile()); |
| context->drawable = drawable; |
| } |
| } |
| |
| |
| static void retrace_CGLClearDrawable(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| Context *context = getContext(ctx); |
| if (context) { |
| context->drawable = NULL; |
| } |
| } |
| |
| |
| static void retrace_CGLSetCurrentContext(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| |
| Context *new_context = getContext(ctx); |
| glws::Drawable *new_drawable = NULL; |
| if (new_context) { |
| if (!new_context->drawable) { |
| glfeatures::Profile profile = new_context->profile(); |
| new_context->drawable = glretrace::createDrawable(profile); |
| } |
| new_drawable = new_context->drawable; |
| } |
| |
| glretrace::makeCurrent(call, new_drawable, new_context); |
| } |
| |
| |
| static void retrace_CGLFlushDrawable(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| Context *context = getContext(ctx); |
| |
| if (context) { |
| glws::Drawable *drawable = context->drawable; |
| if (drawable) { |
| if (retrace::doubleBuffer) { |
| drawable->swapBuffers(); |
| } else { |
| glFlush(); |
| } |
| frame_complete(call); |
| } else { |
| if (retrace::debug > 0) { |
| retrace::warning(call) << "context has no drawable\n"; |
| } |
| } |
| } |
| } |
| |
| |
| static void retrace_CGLSetVirtualScreen(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| GLint screen = call.arg(1).toSInt(); |
| if (screen != 0) { |
| retrace::warning(call) << "multiple virtual screens unsupported\n"; |
| } |
| } |
| |
| |
| /** |
| * We can't fully reimplement CGLTexImageIOSurface2D, as external IOSurface are |
| * no longer present. Simply emit a glTexImage2D to ensure the texture storage |
| * is present. |
| * |
| * See also: |
| * - /System/Library/Frameworks/OpenGL.framework/Headers/CGLIOSurface.h |
| */ |
| static void retrace_CGLTexImageIOSurface2D(trace::Call &call) { |
| if (call.ret->toUInt() != kCGLNoError) { |
| return; |
| } |
| |
| if (retrace::debug > 0) { |
| retrace::warning(call) << "external IOSurface not supported\n"; |
| } |
| |
| unsigned long long ctx = call.arg(0).toUIntPtr(); |
| Context *context = getContext(ctx); |
| |
| GLenum target; |
| target = static_cast<GLenum>((call.arg(1)).toSInt()); |
| |
| GLint level = 0; |
| |
| GLint internalformat; |
| internalformat = static_cast<GLenum>((call.arg(2)).toSInt()); |
| |
| GLsizei width; |
| width = (call.arg(3)).toSInt(); |
| |
| GLsizei height; |
| height = (call.arg(4)).toSInt(); |
| |
| GLint border = 0; |
| |
| GLenum format; |
| format = static_cast<GLenum>((call.arg(5)).toSInt()); |
| |
| GLenum type; |
| type = static_cast<GLenum>((call.arg(6)).toSInt()); |
| |
| GLvoid * pixels = NULL; |
| |
| glretrace::Context *currentContext = glretrace::getCurrentContext(); |
| if (retrace::debug > 0 && currentContext != context) { |
| retrace::warning(call) << "current context mismatch\n"; |
| } |
| |
| glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); |
| |
| if (retrace::debug > 0 && currentContext && !currentContext->insideBeginEnd) { |
| glretrace::checkGlError(call); |
| } |
| } |
| |
| |
| const retrace::Entry glretrace::cgl_callbacks[] = { |
| {"CGLChoosePixelFormat", &retrace_CGLChoosePixelFormat}, |
| {"CGLClearDrawable", &retrace_CGLClearDrawable}, |
| {"CGLCreateContext", &retrace_CGLCreateContext}, |
| {"CGLCreatePBuffer", &retrace_CGLCreatePBuffer}, |
| {"CGLDescribePixelFormat", &retrace::ignore}, |
| {"CGLDescribeRenderer", &retrace::ignore}, |
| {"CGLDestroyContext", &retrace_CGLDestroyContext}, |
| {"CGLDestroyPBuffer", &retrace_CGLDestroyPBuffer}, |
| {"CGLDestroyPixelFormat", &retrace_CGLDestroyPixelFormat}, |
| {"CGLDisable", &retrace::ignore}, |
| {"CGLEnable", &retrace::ignore}, |
| {"CGLErrorString", &retrace::ignore}, |
| {"CGLFlushDrawable", &retrace_CGLFlushDrawable}, |
| {"CGLGetCurrentContext", &retrace::ignore}, |
| {"CGLGetGlobalOption", &retrace::ignore}, |
| {"CGLGetOption", &retrace::ignore}, |
| {"CGLGetParameter", &retrace::ignore}, |
| {"CGLGetPBuffer", &retrace_CGLGetPBuffer}, |
| {"CGLGetPixelFormat", &retrace::ignore}, |
| {"CGLGetSurface", &retrace::ignore}, |
| {"CGLGetVersion", &retrace::ignore}, |
| {"CGLGetVirtualScreen", &retrace::ignore}, |
| {"CGLIsEnabled", &retrace::ignore}, |
| {"CGLLockContext", &retrace::ignore}, |
| {"CGLSetCurrentContext", &retrace_CGLSetCurrentContext}, |
| {"CGLSetGlobalOption", &retrace::ignore}, |
| {"CGLSetOption", &retrace::ignore}, |
| {"CGLSetPBuffer", &retrace_CGLSetPBuffer}, |
| {"CGLSetParameter", &retrace::ignore}, |
| {"CGLSetSurface", &retrace_CGLSetSurface}, |
| {"CGLSetVirtualScreen", &retrace_CGLSetVirtualScreen}, |
| {"CGLTexImageIOSurface2D", &retrace_CGLTexImageIOSurface2D}, |
| {"CGLUnlockContext", &retrace::ignore}, |
| {"CGLUpdateContext", &retrace::ignore}, |
| {NULL, NULL}, |
| }; |
| |