1/*
2 * Copyright (c) 2008 Apple Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <assert.h>
26#include <OpenGL/OpenGL.h>
27#include <OpenGL/gl.h>
28#include <OpenGL/glu.h>
29#include <OpenGL/glext.h>
30#include <ApplicationServices/ApplicationServices.h>
31
32#include "capabilities.h"
33
34#define Cursor X_Cursor
35#include "os.h"
36#undef Cursor
37
38static void handleBufferModes(struct glCapabilitiesConfig *c, GLint bufferModes) {
39    if(bufferModes & kCGLStereoscopicBit) {
40	c->stereo = true;
41    }
42
43    if(bufferModes & kCGLDoubleBufferBit) {
44	c->buffers = 2;
45    } else {
46	c->buffers = 1;
47    }
48}
49
50static void handleStencilModes(struct glCapabilitiesConfig *c, GLint smodes) {
51    int offset = 0;
52
53    if(kCGL0Bit & smodes)
54	c->stencil_bit_depths[offset++] = 0;
55
56    if(kCGL1Bit & smodes)
57	c->stencil_bit_depths[offset++] = 1;
58
59    if(kCGL2Bit & smodes)
60	c->stencil_bit_depths[offset++] = 2;
61
62    if(kCGL3Bit & smodes)
63	c->stencil_bit_depths[offset++] = 3;
64
65    if(kCGL4Bit & smodes)
66	c->stencil_bit_depths[offset++] = 4;
67
68    if(kCGL5Bit & smodes)
69	c->stencil_bit_depths[offset++] = 5;
70
71    if(kCGL6Bit & smodes)
72	c->stencil_bit_depths[offset++] = 6;
73
74    if(kCGL8Bit & smodes)
75	c->stencil_bit_depths[offset++] = 8;
76
77    if(kCGL10Bit & smodes)
78	c->stencil_bit_depths[offset++] = 10;
79
80    if(kCGL12Bit & smodes)
81	c->stencil_bit_depths[offset++] = 12;
82
83    if(kCGL16Bit & smodes)
84	c->stencil_bit_depths[offset++] = 16;
85
86    if(kCGL24Bit & smodes)
87	c->stencil_bit_depths[offset++] = 24;
88
89    if(kCGL32Bit & smodes)
90	c->stencil_bit_depths[offset++] = 32;
91
92    if(kCGL48Bit & smodes)
93	c->stencil_bit_depths[offset++] = 48;
94
95    if(kCGL64Bit & smodes)
96	c->stencil_bit_depths[offset++] = 64;
97
98    if(kCGL96Bit & smodes)
99	c->stencil_bit_depths[offset++] = 96;
100
101    if(kCGL128Bit & smodes)
102	c->stencil_bit_depths[offset++] = 128;
103
104    assert(offset < GLCAPS_STENCIL_BIT_DEPTH_BUFFERS);
105
106    c->total_stencil_bit_depths = offset;
107}
108
109static int handleColorAndAccumulation(struct glColorBufCapabilities *c,
110				       GLint cmodes, int forAccum) {
111    int offset = 0;
112
113    /*1*/
114    if(kCGLRGB444Bit & cmodes) {
115	c[offset].r = 4;
116	c[offset].g = 4;
117	c[offset].b = 4;
118	++offset;
119    }
120
121    /*2*/
122    if(kCGLARGB4444Bit & cmodes) {
123	c[offset].a = 4;
124	c[offset].r = 4;
125	c[offset].g = 4;
126	c[offset].b = 4;
127	c[offset].is_argb = true;
128	++offset;
129    }
130
131    /*3*/
132    if(kCGLRGB444A8Bit & cmodes) {
133	c[offset].r = 4;
134	c[offset].g = 4;
135	c[offset].b = 4;
136	c[offset].a = 8;
137	++offset;
138    }
139
140    /*4*/
141    if(kCGLRGB555Bit & cmodes) {
142	c[offset].r = 5;
143	c[offset].g = 5;
144	c[offset].b = 5;
145	++offset;
146    }
147
148    /*5*/
149    if(kCGLARGB1555Bit & cmodes) {
150	c[offset].a = 1;
151	c[offset].r = 5;
152	c[offset].g = 5;
153	c[offset].b = 5;
154	c[offset].is_argb = true;
155	++offset;
156    }
157
158    /*6*/
159    if(kCGLRGB555A8Bit & cmodes) {
160	c[offset].r = 5;
161	c[offset].g = 5;
162	c[offset].b = 5;
163	c[offset].a = 8;
164	++offset;
165    }
166
167    /*7*/
168    if(kCGLRGB565Bit & cmodes) {
169	c[offset].r = 5;
170	c[offset].g = 6;
171	c[offset].b = 5;
172	++offset;
173    }
174
175    /*8*/
176    if(kCGLRGB565A8Bit & cmodes) {
177	c[offset].r = 5;
178	c[offset].g = 6;
179	c[offset].b = 5;
180	c[offset].a = 8;
181	++offset;
182    }
183
184    /*9*/
185    if(kCGLRGB888Bit & cmodes) {
186	c[offset].r = 8;
187	c[offset].g = 8;
188	c[offset].b = 8;
189	++offset;
190    }
191
192    /*10*/
193    if(kCGLARGB8888Bit & cmodes) {
194	c[offset].a = 8;
195	c[offset].r = 8;
196	c[offset].g = 8;
197	c[offset].b = 8;
198	c[offset].is_argb = true;
199	++offset;
200    }
201
202    /*11*/
203    if(kCGLRGB888A8Bit & cmodes) {
204	c[offset].r = 8;
205	c[offset].g = 8;
206	c[offset].b = 8;
207	c[offset].a = 8;
208	++offset;
209    }
210
211    if(forAccum) {
212//#if 0
213    /* FIXME
214     * Disable this path, because some part of libGL, X, or Xplugin
215     * doesn't work with sizes greater than 8.
216     * When this is enabled and visuals are chosen using depths
217     * such as 16, the result is that the windows don't redraw
218     * and are often white, until a resize.
219     */
220
221    /*12*/
222    if(kCGLRGB101010Bit & cmodes) {
223	c[offset].r = 10;
224	c[offset].g = 10;
225	c[offset].b = 10;
226	++offset;
227    }
228
229    /*13*/
230    if(kCGLARGB2101010Bit & cmodes) {
231	c[offset].a = 2;
232	c[offset].r = 10;
233	c[offset].g = 10;
234	c[offset].b = 10;
235	c[offset].is_argb = true;
236	++offset;
237    }
238
239    /*14*/
240    if(kCGLRGB101010_A8Bit & cmodes) {
241	c[offset].r = 10;
242	c[offset].g = 10;
243	c[offset].b = 10;
244	c[offset].a = 8;
245	++offset;
246    }
247
248    /*15*/
249    if(kCGLRGB121212Bit & cmodes) {
250	c[offset].r = 12;
251	c[offset].g = 12;
252	c[offset].b = 12;
253	++offset;
254    }
255
256    /*16*/
257    if(kCGLARGB12121212Bit & cmodes) {
258	c[offset].a = 12;
259	c[offset].r = 12;
260	c[offset].g = 12;
261	c[offset].b = 12;
262	c[offset].is_argb = true;
263	++offset;
264    }
265
266    /*17*/
267    if(kCGLRGB161616Bit & cmodes) {
268	c[offset].r = 16;
269	c[offset].g = 16;
270	c[offset].b = 16;
271	++offset;
272    }
273
274    /*18*/
275    if(kCGLRGBA16161616Bit & cmodes) {
276	c[offset].r = 16;
277	c[offset].g = 16;
278	c[offset].b = 16;
279	c[offset].a = 16;
280	++offset;
281    }
282    }
283//#endif
284
285    /* FIXME should we handle the floating point color modes, and if so, how? */
286
287    return offset;
288}
289
290
291static void handleColorModes(struct glCapabilitiesConfig *c, GLint cmodes) {
292    c->total_color_buffers = handleColorAndAccumulation(c->color_buffers,
293							cmodes, 0);
294
295    assert(c->total_color_buffers < GLCAPS_COLOR_BUFFERS);
296}
297
298static void handleAccumulationModes(struct glCapabilitiesConfig *c, GLint cmodes) {
299    c->total_accum_buffers = handleColorAndAccumulation(c->accum_buffers,
300							cmodes, 1);
301    assert(c->total_accum_buffers < GLCAPS_COLOR_BUFFERS);
302}
303
304static void handleDepthModes(struct glCapabilitiesConfig *c, GLint dmodes) {
305    int offset = 0;
306#define DEPTH(flag,value) do { \
307	if(dmodes & flag) { \
308	    c->depth_buffers[offset++] = value; \
309	} \
310    } while(0)
311
312    /*1*/
313    DEPTH(kCGL0Bit, 0);
314    /*2*/
315    DEPTH(kCGL1Bit, 1);
316    /*3*/
317    DEPTH(kCGL2Bit, 2);
318    /*4*/
319    DEPTH(kCGL3Bit, 3);
320    /*5*/
321    DEPTH(kCGL4Bit, 4);
322    /*6*/
323    DEPTH(kCGL5Bit, 5);
324    /*7*/
325    DEPTH(kCGL6Bit, 6);
326    /*8*/
327    DEPTH(kCGL8Bit, 8);
328    /*9*/
329    DEPTH(kCGL10Bit, 10);
330    /*10*/
331    DEPTH(kCGL12Bit, 12);
332    /*11*/
333    DEPTH(kCGL16Bit, 16);
334    /*12*/
335    DEPTH(kCGL24Bit, 24);
336    /*13*/
337    DEPTH(kCGL32Bit, 32);
338    /*14*/
339    DEPTH(kCGL48Bit, 48);
340    /*15*/
341    DEPTH(kCGL64Bit, 64);
342    /*16*/
343    DEPTH(kCGL96Bit, 96);
344    /*17*/
345    DEPTH(kCGL128Bit, 128);
346
347#undef DEPTH
348
349    c->total_depth_buffer_depths = offset;
350    assert(c->total_depth_buffer_depths < GLCAPS_DEPTH_BUFFERS);
351}
352
353/* Return non-zero if an error occured. */
354static CGLError handleRendererDescriptions(CGLRendererInfoObj info, GLint r,
355					   struct glCapabilitiesConfig *c) {
356    CGLError err;
357    GLint accelerated = 0, flags = 0, aux = 0, samplebufs = 0, samples = 0;
358
359    err = CGLDescribeRenderer (info, r, kCGLRPAccelerated, &accelerated);
360
361    if(err)
362	return err;
363
364    c->accelerated = accelerated;
365
366    /* Buffering modes: single/double, stereo */
367    err = CGLDescribeRenderer(info, r, kCGLRPBufferModes, &flags);
368
369    if(err)
370	return err;
371
372    handleBufferModes(c, flags);
373
374    /* AUX buffers */
375    err = CGLDescribeRenderer(info, r, kCGLRPMaxAuxBuffers, &aux);
376
377    if(err)
378	return err;
379
380    c->aux_buffers = aux;
381
382
383    /* Depth buffer size */
384    err = CGLDescribeRenderer(info, r, kCGLRPDepthModes, &flags);
385
386    if(err)
387	return err;
388
389    handleDepthModes(c, flags);
390
391
392    /* Multisample buffers */
393    err = CGLDescribeRenderer(info, r, kCGLRPMaxSampleBuffers, &samplebufs);
394
395    if(err)
396	return err;
397
398    c->multisample_buffers = samplebufs;
399
400
401    /* Multisample samples per multisample buffer */
402    err = CGLDescribeRenderer(info, r, kCGLRPMaxSamples, &samples);
403
404    if(err)
405	return err;
406
407    c->multisample_samples = samples;
408
409
410    /* Stencil bit depths */
411    err = CGLDescribeRenderer(info, r, kCGLRPStencilModes, &flags);
412
413    if(err)
414	return err;
415
416    handleStencilModes(c, flags);
417
418
419    /* Color modes (RGB/RGBA depths supported */
420    err = CGLDescribeRenderer(info, r, kCGLRPColorModes, &flags);
421
422    if(err)
423	return err;
424
425    handleColorModes(c, flags);
426
427    err = CGLDescribeRenderer(info, r, kCGLRPAccumModes, &flags);
428
429    if(err)
430	return err;
431
432    handleAccumulationModes(c, flags);
433
434    return kCGLNoError;
435}
436
437static void initCapabilities(struct glCapabilities *cap) {
438    cap->configurations = NULL;
439    cap->total_configurations = 0;
440}
441
442static void initConfig(struct glCapabilitiesConfig *c) {
443    int i;
444
445    c->accelerated = false;
446    c->stereo = false;
447    c->aux_buffers = 0;
448    c->buffers = 0;
449
450    c->total_depth_buffer_depths = 0;
451
452    for(i = 0; i < GLCAPS_DEPTH_BUFFERS; ++i) {
453	c->depth_buffers[i] = GLCAPS_INVALID_DEPTH_VALUE;
454    }
455
456    c->multisample_buffers = 0;
457    c->multisample_samples = 0;
458
459    c->total_stencil_bit_depths = 0;
460
461    for(i = 0; i < GLCAPS_STENCIL_BIT_DEPTH_BUFFERS; ++i) {
462	c->stencil_bit_depths[i] = GLCAPS_INVALID_STENCIL_DEPTH;
463    }
464
465    c->total_color_buffers = 0;
466
467    for(i = 0; i < GLCAPS_COLOR_BUFFERS; ++i) {
468	c->color_buffers[i].r =	c->color_buffers[i].g =
469	    c->color_buffers[i].b = c->color_buffers[i].a =
470	    GLCAPS_COLOR_BUF_INVALID_VALUE;
471	c->color_buffers[i].is_argb = false;
472     }
473
474    c->total_accum_buffers = 0;
475
476    for(i = 0; i < GLCAPS_COLOR_BUFFERS; ++i) {
477	c->accum_buffers[i].r = c->accum_buffers[i].g =
478	    c->accum_buffers[i].b = c->accum_buffers[i].a =
479	    GLCAPS_COLOR_BUF_INVALID_VALUE;
480	c->accum_buffers[i].is_argb = false;
481    }
482
483    c->next = NULL;
484}
485
486void freeGlCapabilities(struct glCapabilities *cap) {
487    struct glCapabilitiesConfig *conf, *next;
488
489    conf = cap->configurations;
490
491    while(conf) {
492	next = conf->next;
493	free(conf);
494	conf = next;
495    }
496
497    cap->configurations = NULL;
498}
499
500/*Return true if an error occured. */
501bool getGlCapabilities(struct glCapabilities *cap) {
502	CGLRendererInfoObj info;
503    CGLError err;
504	GLint numRenderers = 0, r;
505
506    initCapabilities(cap);
507
508	err = CGLQueryRendererInfo((GLuint)-1, &info, &numRenderers);
509    if(err) {
510	    fprintf(stderr, "CGLQueryRendererInfo error: %s\n", CGLErrorString(err));
511        return err;
512	}
513
514	for(r = 0; r < numRenderers; r++) {
515	    struct glCapabilitiesConfig tmpconf, *conf;
516
517	    initConfig(&tmpconf);
518
519	    err = handleRendererDescriptions(info, r, &tmpconf);
520	    if(err) {
521            fprintf(stderr, "handleRendererDescriptions returned error: %s\n", CGLErrorString(err));
522            fprintf(stderr, "trying to continue...\n");
523            continue;
524	    }
525
526	    conf = malloc(sizeof(*conf));
527	    if(NULL == conf) {
528                FatalError("Unable to allocate memory for OpenGL capabilities\n");
529	    }
530
531	    /* Copy the struct. */
532	    *conf = tmpconf;
533
534	    /* Now link the configuration into the list. */
535	    conf->next = cap->configurations;
536	    cap->configurations = conf;
537	}
538
539    CGLDestroyRendererInfo(info);
540
541    /* No error occured.  We are done. */
542    return kCGLNoError;
543}
544