1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25 
26 
27 #include <string.h>
28 
29 #include "glproc.hpp"
30 #include "retrace.hpp"
31 #include "retrace_swizzle.hpp"
32 #include "glretrace.hpp"
33 
34 
35 #define CGL_PBUFFER_HEIGHT 0x8040
36 #define CGL_PBUFFER_WIDTH 0x8041
37 
38 #define kCGLNoError 0
39 
40 #define kCGLPFAAllRenderers            1
41 #define kCGLPFATripleBuffer            3
42 #define kCGLPFADoubleBuffer            5
43 #define kCGLPFAStereo                  6
44 #define kCGLPFAAuxBuffers              7
45 #define kCGLPFAColorSize               8
46 #define kCGLPFAAlphaSize              11
47 #define kCGLPFADepthSize              12
48 #define kCGLPFAStencilSize            13
49 #define kCGLPFAAccumSize              14
50 #define kCGLPFAMinimumPolicy          51
51 #define kCGLPFAMaximumPolicy          52
52 #define kCGLPFAOffScreen              53
53 #define kCGLPFAFullScreen             54
54 #define kCGLPFASampleBuffers          55
55 #define kCGLPFASamples                56
56 #define kCGLPFAAuxDepthStencil        57
57 #define kCGLPFAColorFloat             58
58 #define kCGLPFAMultisample            59
59 #define kCGLPFASupersample            60
60 #define kCGLPFASampleAlpha            61
61 #define kCGLPFARendererID             70
62 #define kCGLPFASingleRenderer         71
63 #define kCGLPFANoRecovery             72
64 #define kCGLPFAAccelerated            73
65 #define kCGLPFAClosestPolicy          74
66 #define kCGLPFARobust                 75
67 #define kCGLPFABackingStore           76
68 #define kCGLPFABackingVolatile        77
69 #define kCGLPFAMPSafe                 78
70 #define kCGLPFAWindow                 80
71 #define kCGLPFAMultiScreen            81
72 #define kCGLPFACompliant              83
73 #define kCGLPFADisplayMask            84
74 #define kCGLPFAPBuffer                90
75 #define kCGLPFARemotePBuffer          91
76 #define kCGLPFAAllowOfflineRenderers  96
77 #define kCGLPFAAcceleratedCompute     97
78 #define kCGLPFAOpenGLProfile          99
79 #define kCGLPFASupportsAutomaticGraphicsSwitching  101
80 #define kCGLPFAVirtualScreenCount    128
81 
82 #define kCGLOGLPVersion_Legacy   0x1000
83 #define kCGLOGLPVersion_3_2_Core 0x3200
84 #define kCGLOGLPVersion_GL3_Core 0x3200
85 #define kCGLOGLPVersion_GL4_Core 0x4100
86 
87 
88 using namespace glretrace;
89 
90 
91 typedef std::map<unsigned long long, glws::Drawable *> DrawableMap;
92 typedef std::map<unsigned long long, Context *> ContextMap;
93 
94 // sid -> Drawable* map
95 static DrawableMap drawable_map;
96 
97 static DrawableMap pbuffer_map;
98 
99 // ctx -> Context* map
100 static ContextMap context_map;
101 
102 static Context *sharedContext = NULL;
103 
104 
105 struct PixelFormat
106 {
107     glfeatures::Profile profile;
108 
PixelFormatPixelFormat109     PixelFormat() :
110         profile(glfeatures::API_GL, 1, 0)
111     {}
112 };
113 
114 
115 static glws::Drawable *
getDrawable(unsigned long drawable_id,glfeatures::Profile profile)116 getDrawable(unsigned long drawable_id, glfeatures::Profile profile) {
117     if (drawable_id == 0) {
118         return NULL;
119     }
120 
121     DrawableMap::const_iterator it;
122     it = drawable_map.find(drawable_id);
123     if (it == drawable_map.end()) {
124         return (drawable_map[drawable_id] = glretrace::createDrawable(profile));
125     }
126 
127     return it->second;
128 }
129 
130 static glws::Drawable *
getDrawable(unsigned long long hdc)131 getDrawable(unsigned long long hdc) {
132     if (hdc == 0) {
133         return NULL;
134     }
135 
136     DrawableMap::const_iterator it;
137     it = drawable_map.find(hdc);
138     if (it == drawable_map.end()) {
139         return (drawable_map[hdc] = glretrace::createDrawable());
140     }
141 
142     return it->second;
143 }
144 
145 static Context *
getContext(unsigned long long ctx)146 getContext(unsigned long long ctx) {
147     if (ctx == 0) {
148         return NULL;
149     }
150 
151     ContextMap::const_iterator it;
152     it = context_map.find(ctx);
153     if (it == context_map.end()) {
154         Context *context;
155         context_map[ctx] = context = glretrace::createContext(sharedContext);
156         if (!sharedContext) {
157             sharedContext = context;
158         }
159         return context;
160     }
161 
162     return it->second;
163 }
164 
165 
retrace_CGLChoosePixelFormat(trace::Call & call)166 static void retrace_CGLChoosePixelFormat(trace::Call &call) {
167     if (call.ret->toUInt() != kCGLNoError) {
168         return;
169     }
170 
171     bool singleBuffer = true;
172     int profile = 0;
173 
174     const trace::Array * attribs = call.arg(0).toArray();
175     if (attribs) {
176         size_t i = 0;
177         while (i < attribs->values.size()) {
178             int param = attribs->values[i++]->toSInt();
179             if (param == 0) {
180                 break;
181             }
182 
183             switch (param) {
184 
185             // Boolean attributes
186 
187             case kCGLPFAAllRenderers:
188             case kCGLPFAStereo:
189             case kCGLPFAMinimumPolicy:
190             case kCGLPFAMaximumPolicy:
191             case kCGLPFAOffScreen:
192             case kCGLPFAFullScreen:
193             case kCGLPFAAuxDepthStencil:
194             case kCGLPFAColorFloat:
195             case kCGLPFAMultisample:
196             case kCGLPFASupersample:
197             case kCGLPFASampleAlpha:
198             case kCGLPFASingleRenderer:
199             case kCGLPFANoRecovery:
200             case kCGLPFAAccelerated:
201             case kCGLPFAClosestPolicy:
202             case kCGLPFARobust:
203             case kCGLPFABackingStore:
204             case kCGLPFABackingVolatile:
205             case kCGLPFAMPSafe:
206             case kCGLPFAWindow:
207             case kCGLPFAMultiScreen:
208             case kCGLPFACompliant:
209             case kCGLPFAPBuffer:
210             case kCGLPFARemotePBuffer:
211             case kCGLPFAAllowOfflineRenderers:
212             case kCGLPFAAcceleratedCompute:
213             case kCGLPFASupportsAutomaticGraphicsSwitching:
214                 break;
215 
216             case kCGLPFADoubleBuffer:
217             case kCGLPFATripleBuffer:
218                 singleBuffer = false;
219                 break;
220 
221             // Valued attributes
222 
223             case kCGLPFAColorSize:
224             case kCGLPFAAlphaSize:
225             case kCGLPFADepthSize:
226             case kCGLPFAStencilSize:
227             case kCGLPFAAuxBuffers:
228             case kCGLPFAAccumSize:
229             case kCGLPFASampleBuffers:
230             case kCGLPFASamples:
231             case kCGLPFARendererID:
232             case kCGLPFADisplayMask:
233             case kCGLPFAVirtualScreenCount:
234                 ++i;
235                 break;
236 
237             case kCGLPFAOpenGLProfile:
238                 profile = attribs->values[i++]->toSInt();
239                 break;
240 
241             default:
242                 retrace::warning(call) << "unexpected attribute " << param << "\n";
243                 break;
244             }
245         }
246     }
247 
248     trace::Value & pix = call.argByName("pix")[0];
249 
250     PixelFormat *pixelFormat = new PixelFormat;
251 
252     // TODO: Do this on a per visual basis
253     switch (profile) {
254     case 0:
255         break;
256     case kCGLOGLPVersion_Legacy:
257         pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 1, 0);
258         break;
259     case kCGLOGLPVersion_GL3_Core:
260         pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 3, 2, true, true);
261         break;
262     case kCGLOGLPVersion_GL4_Core:
263         pixelFormat->profile = glfeatures::Profile(glfeatures::API_GL, 4, 1, true, true);
264         break;
265     default:
266         retrace::warning(call) << "unexpected opengl profile " << std::hex << profile << std::dec << "\n";
267         break;
268     }
269 
270     // XXX: Generalize this, don't override command line options.
271     retrace::doubleBuffer = !singleBuffer;
272 
273     retrace::addObj(call, pix, pixelFormat);
274 }
275 
276 
retrace_CGLDestroyPixelFormat(trace::Call & call)277 static void retrace_CGLDestroyPixelFormat(trace::Call &call) {
278     if (call.ret->toUInt() != kCGLNoError) {
279         return;
280     }
281 
282     trace::Value & pix = call.argByName("pix");
283 
284     PixelFormat *pixelFormat = retrace::asObjPointer<PixelFormat>(call, pix);
285     delete pixelFormat;
286 
287     retrace::delObj(pix);
288 }
289 
290 
retrace_CGLCreateContext(trace::Call & call)291 static void retrace_CGLCreateContext(trace::Call &call) {
292     if (call.ret->toUInt() != kCGLNoError) {
293         return;
294     }
295 
296     trace::Value & pix = call.argByName("pix");
297     const PixelFormat *pixelFormat = retrace::asObjPointer<PixelFormat>(call, pix);
298     glfeatures::Profile profile = pixelFormat ? pixelFormat->profile : glretrace::defaultProfile;
299 
300     unsigned long long share = call.arg(1).toUIntPtr();
301     Context *sharedContext = getContext(share);
302 
303     const trace::Array *ctx_ptr = call.arg(2).toArray();
304     assert(ctx_ptr);
305     unsigned long long ctx = ctx_ptr->values[0]->toUIntPtr();
306 
307     Context *context = glretrace::createContext(sharedContext, profile);
308     context_map[ctx] = context;
309 }
310 
311 
retrace_CGLDestroyContext(trace::Call & call)312 static void retrace_CGLDestroyContext(trace::Call &call) {
313     if (call.ret->toUInt() != kCGLNoError) {
314         return;
315     }
316 
317     unsigned long long ctx = call.arg(0).toUIntPtr();
318 
319     ContextMap::iterator it;
320     it = context_map.find(ctx);
321     if (it == context_map.end()) {
322         return;
323     }
324 
325     it->second->release();
326 
327     context_map.erase(it);
328 }
329 
330 /*
331   CGLError CGLCreatePBuffer (long width,
332     long height,
333     unsigned long target,
334     unsigned long internalFormat,
335     long max_level,
336     CGLPBufferObj *pbuffer);
337 */
retrace_CGLCreatePBuffer(trace::Call & call)338 static void retrace_CGLCreatePBuffer(trace::Call &call) {
339     if (call.ret->toUInt() != kCGLNoError) {
340         return;
341     }
342 
343     int width = (call.arg(0)).toSInt();
344     int height = (call.arg(1)).toSInt();
345     int target = (call.arg(2)).toUInt();
346     int internalFormat = (call.arg(3)).toUInt();
347     int max_level = (call.arg(4)).toSInt();
348     unsigned long long pb = call.arg(5).toUIntPtr();
349 
350     glws::pbuffer_info pbInfo = {0, 0, false};
351     pbInfo.texFormat = internalFormat;
352     pbInfo.texTarget = target;
353 
354     glws::Drawable *drawable = glretrace::createPbuffer(width, height, &pbInfo);
355 
356     drawable->cubeFace   = 0;
357     drawable->mipmapLevel = max_level;
358 
359     pbuffer_map[pb]  = drawable;
360     drawable_map[pb] = drawable;
361 }
362 
retrace_CGLDestroyPBuffer(trace::Call & call)363 static void retrace_CGLDestroyPBuffer(trace::Call &call) {
364     glws::Drawable *drawable = getDrawable(call.arg(0).toUInt());
365 
366     if (!drawable) {
367         return;
368     }
369 
370     delete drawable;
371 }
372 
373 /*
374   CGLError CGLGetPBuffer(CGLContextObj ctx,
375     CGLPBufferObj *pbuffer,
376     unsigned long *face,
377     long *level,
378     long *screen);
379 */
retrace_CGLGetPBuffer(trace::Call & call)380 static void retrace_CGLGetPBuffer(trace::Call &call) {
381     if (call.ret->toUInt() != kCGLNoError) {
382         return;
383     }
384 
385     unsigned long long ctx = call.arg(0).toUIntPtr();
386     unsigned long long pbuffer = call.arg(1).toUIntPtr();
387 
388     glws::Drawable *drawable = pbuffer_map[ctx];
389 
390     if (!drawable) {
391         return;
392     }
393 
394     drawable_map[pbuffer] = drawable;
395 }
396 
397 /*
398 CGLSetPBuffer
399 Attaches a pixel buffer object to a rendering context.
400 CGLError CGLSetPBuffer(CGLContextObj ctx,
401 CGLPBufferObj pbuffer,
402 unsigned long face,
403 long level,
404 long screen);
405 */
retrace_CGLSetPBuffer(trace::Call & call)406 static void retrace_CGLSetPBuffer(trace::Call &call) {
407     if (call.ret->toUInt() != kCGLNoError) {
408         return;
409     }
410 
411     unsigned long long ctx = call.arg(0).toUIntPtr();
412     unsigned long long pbuffer = call.arg(1).toUIntPtr();
413 
414     glws::Drawable *drawable = pbuffer_map[ctx];
415 
416     if (!drawable) {
417         glws::pbuffer_info pbInfo = {0, 0, false};
418 
419         drawable = glretrace::createPbuffer(CGL_PBUFFER_WIDTH, CGL_PBUFFER_HEIGHT, &pbInfo);
420     }
421 
422     drawable->cubeFace = (call.arg(2)).toUInt();
423     drawable->mipmapLevel = (call.arg(3)).toSInt();
424 
425     pbuffer_map[ctx] = drawable;
426 
427     drawable_map[pbuffer] = drawable;
428 }
429 
retrace_CGLSetSurface(trace::Call & call)430 static void retrace_CGLSetSurface(trace::Call &call) {
431     if (call.ret->toUInt() != kCGLNoError) {
432         return;
433     }
434 
435     unsigned long long ctx = call.arg(0).toUIntPtr();
436     unsigned long long cid = call.arg(1).toUInt();
437     int wid = call.arg(2).toUInt();
438     int sid = call.arg(3).toUInt();
439 
440     (void)cid;
441     (void)wid;
442 
443     Context *context = getContext(ctx);
444     if (context) {
445         glws::Drawable *drawable = getDrawable(sid, context->profile());
446         context->drawable = drawable;
447     }
448 }
449 
450 
retrace_CGLClearDrawable(trace::Call & call)451 static void retrace_CGLClearDrawable(trace::Call &call) {
452     if (call.ret->toUInt() != kCGLNoError) {
453         return;
454     }
455 
456     unsigned long long ctx = call.arg(0).toUIntPtr();
457     Context *context = getContext(ctx);
458     if (context) {
459         context->drawable = NULL;
460     }
461 }
462 
463 
retrace_CGLSetCurrentContext(trace::Call & call)464 static void retrace_CGLSetCurrentContext(trace::Call &call) {
465     if (call.ret->toUInt() != kCGLNoError) {
466         return;
467     }
468 
469     unsigned long long ctx = call.arg(0).toUIntPtr();
470 
471     Context *new_context = getContext(ctx);
472     glws::Drawable *new_drawable = NULL;
473     if (new_context) {
474         if (!new_context->drawable) {
475             glfeatures::Profile profile = new_context->profile();
476             new_context->drawable = glretrace::createDrawable(profile);
477         }
478         new_drawable = new_context->drawable;
479     }
480 
481     glretrace::makeCurrent(call, new_drawable, new_context);
482 }
483 
484 
retrace_CGLFlushDrawable(trace::Call & call)485 static void retrace_CGLFlushDrawable(trace::Call &call) {
486     if (call.ret->toUInt() != kCGLNoError) {
487         return;
488     }
489 
490     unsigned long long ctx = call.arg(0).toUIntPtr();
491     Context *context = getContext(ctx);
492 
493     if (context) {
494         glws::Drawable *drawable = context->drawable;
495         if (drawable) {
496             if (retrace::doubleBuffer) {
497                 drawable->swapBuffers();
498             } else {
499                 glFlush();
500             }
501             frame_complete(call);
502         } else {
503             if (retrace::debug > 0) {
504                 retrace::warning(call) << "context has no drawable\n";
505             }
506         }
507     }
508 }
509 
510 
retrace_CGLSetVirtualScreen(trace::Call & call)511 static void retrace_CGLSetVirtualScreen(trace::Call &call) {
512     if (call.ret->toUInt() != kCGLNoError) {
513         return;
514     }
515 
516     GLint screen = call.arg(1).toSInt();
517     if (screen != 0) {
518         retrace::warning(call) << "multiple virtual screens unsupported\n";
519     }
520 }
521 
522 
523 /**
524  * We can't fully reimplement CGLTexImageIOSurface2D, as external IOSurface are
525  * no longer present.  Simply emit a glTexImage2D to ensure the texture storage
526  * is present.
527  *
528  * See also:
529  * - /System/Library/Frameworks/OpenGL.framework/Headers/CGLIOSurface.h
530  */
retrace_CGLTexImageIOSurface2D(trace::Call & call)531 static void retrace_CGLTexImageIOSurface2D(trace::Call &call) {
532     if (call.ret->toUInt() != kCGLNoError) {
533         return;
534     }
535 
536     if (retrace::debug > 0) {
537         retrace::warning(call) << "external IOSurface not supported\n";
538     }
539 
540     unsigned long long ctx = call.arg(0).toUIntPtr();
541     Context *context = getContext(ctx);
542 
543     GLenum target;
544     target = static_cast<GLenum>((call.arg(1)).toSInt());
545 
546     GLint level = 0;
547 
548     GLint internalformat;
549     internalformat = static_cast<GLenum>((call.arg(2)).toSInt());
550 
551     GLsizei width;
552     width = (call.arg(3)).toSInt();
553 
554     GLsizei height;
555     height = (call.arg(4)).toSInt();
556 
557     GLint border = 0;
558 
559     GLenum format;
560     format = static_cast<GLenum>((call.arg(5)).toSInt());
561 
562     GLenum type;
563     type = static_cast<GLenum>((call.arg(6)).toSInt());
564 
565     GLvoid * pixels = NULL;
566 
567     glretrace::Context *currentContext = glretrace::getCurrentContext();
568     if (retrace::debug > 0 && currentContext != context) {
569         retrace::warning(call) << "current context mismatch\n";
570     }
571 
572     glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
573 
574     if (retrace::debug > 0 && currentContext && !currentContext->insideBeginEnd) {
575         glretrace::checkGlError(call);
576     }
577 }
578 
579 
580 const retrace::Entry glretrace::cgl_callbacks[] = {
581     {"CGLChoosePixelFormat", &retrace_CGLChoosePixelFormat},
582     {"CGLClearDrawable", &retrace_CGLClearDrawable},
583     {"CGLCreateContext", &retrace_CGLCreateContext},
584     {"CGLCreatePBuffer", &retrace_CGLCreatePBuffer},
585     {"CGLDescribePixelFormat", &retrace::ignore},
586     {"CGLDescribeRenderer", &retrace::ignore},
587     {"CGLDestroyContext", &retrace_CGLDestroyContext},
588     {"CGLDestroyPBuffer", &retrace_CGLDestroyPBuffer},
589     {"CGLDestroyPixelFormat", &retrace_CGLDestroyPixelFormat},
590     {"CGLDisable", &retrace::ignore},
591     {"CGLEnable", &retrace::ignore},
592     {"CGLErrorString", &retrace::ignore},
593     {"CGLFlushDrawable", &retrace_CGLFlushDrawable},
594     {"CGLGetCurrentContext", &retrace::ignore},
595     {"CGLGetGlobalOption", &retrace::ignore},
596     {"CGLGetOption", &retrace::ignore},
597     {"CGLGetParameter", &retrace::ignore},
598     {"CGLGetPBuffer", &retrace_CGLGetPBuffer},
599     {"CGLGetPixelFormat", &retrace::ignore},
600     {"CGLGetSurface", &retrace::ignore},
601     {"CGLGetVersion", &retrace::ignore},
602     {"CGLGetVirtualScreen", &retrace::ignore},
603     {"CGLIsEnabled", &retrace::ignore},
604     {"CGLLockContext", &retrace::ignore},
605     {"CGLSetCurrentContext", &retrace_CGLSetCurrentContext},
606     {"CGLSetGlobalOption", &retrace::ignore},
607     {"CGLSetOption", &retrace::ignore},
608     {"CGLSetPBuffer", &retrace_CGLSetPBuffer},
609     {"CGLSetParameter", &retrace::ignore},
610     {"CGLSetSurface", &retrace_CGLSetSurface},
611     {"CGLSetVirtualScreen", &retrace_CGLSetVirtualScreen},
612     {"CGLTexImageIOSurface2D", &retrace_CGLTexImageIOSurface2D},
613     {"CGLUnlockContext", &retrace::ignore},
614     {"CGLUpdateContext", &retrace::ignore},
615     {NULL, NULL},
616 };
617 
618