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