1 /* $Id: toglAGL.c,v 1.7 2009/10/22 00:06:41 gregcouch Exp $ */
2 
3 /* vi:set sw=4 expandtab: */
4 
5 /*
6  * Togl - a Tk OpenGL widget
7  *
8  * Copyright (C) 1996-2002  Brian Paul and Ben Bederson
9  * Copyright (C) 2005-2009  Greg Couch
10  * See the LICENSE file for copyright details.
11  */
12 
13 struct FBInfo
14 {
15     GLint   acceleration;
16     GLint   colors;
17     GLint   depth;
18     GLint   samples;
19     AGLPixelFormat pix;
20 };
21 typedef struct FBInfo FBInfo;
22 
23 static int
FBInfoCmp(const void * a,const void * b)24 FBInfoCmp(const void *a, const void *b)
25 {
26     /*
27      * 1. full acceleration is better
28      * 2. greater color bits is better
29      * 3. greater depth bits is better
30      * 4. more multisampling is better
31      */
32     const FBInfo *x = (const FBInfo *) a;
33     const FBInfo *y = (const FBInfo *) b;
34 
35     if (x->acceleration != y->acceleration)
36         return y->acceleration - x->acceleration;
37     if (x->colors != y->colors)
38         return y->colors - x->colors;
39     if (x->depth != y->depth)
40         return y->depth - x->depth;
41     if (x->samples != y->samples)
42         return y->samples - x->samples;
43     return 0;
44 }
45 
46 static AGLPixelFormat
togl_pixelFormat(Togl * togl)47 togl_pixelFormat(Togl *togl)
48 {
49     GLint   attribs[32];
50     int     na = 0;
51     AGLPixelFormat pix;
52     GDHandle display = NULL;
53     FBInfo *info = NULL;
54     int     count;
55 
56 #if 0
57     if (togl->MultisampleFlag && !hasMultisampling) {
58         Tcl_SetResult(togl->Interp,
59                 TCL_STUPID "multisampling not supported", TCL_STATIC);
60         return NULL;
61     }
62 #endif
63 
64     if (togl->PbufferFlag && !togl->RgbaFlag) {
65         Tcl_SetResult(togl->Interp,
66                 TCL_STUPID "puffer must be RGB[A]", TCL_STATIC);
67         return NULL;
68     }
69 
70     attribs[na++] = AGL_MINIMUM_POLICY;
71     /* ask for hardware-accelerated onscreen */
72     attribs[na++] = AGL_ACCELERATED;
73     attribs[na++] = AGL_NO_RECOVERY;
74     if (togl->RgbaFlag) {
75         /* RGB[A] mode */
76         attribs[na++] = AGL_RGBA;
77         attribs[na++] = AGL_RED_SIZE;
78         attribs[na++] = togl->RgbaRed;
79         attribs[na++] = AGL_GREEN_SIZE;
80         attribs[na++] = togl->RgbaGreen;
81         attribs[na++] = AGL_BLUE_SIZE;
82         attribs[na++] = togl->RgbaBlue;
83         if (togl->AlphaFlag) {
84             attribs[na++] = AGL_ALPHA_SIZE;
85             attribs[na++] = togl->AlphaSize;
86         }
87     } else {
88         /* Color index mode */
89         attribs[na++] = AGL_BUFFER_SIZE;
90         attribs[na++] = 8;
91     }
92     if (togl->DepthFlag) {
93         attribs[na++] = AGL_DEPTH_SIZE;
94         attribs[na++] = togl->DepthSize;
95     }
96     if (togl->DoubleFlag) {
97         attribs[na++] = AGL_DOUBLEBUFFER;
98     }
99     if (togl->StencilFlag) {
100         attribs[na++] = AGL_STENCIL_SIZE;
101         attribs[na++] = togl->StencilSize;
102     }
103     if (togl->AccumFlag) {
104         attribs[na++] = AGL_ACCUM_RED_SIZE;
105         attribs[na++] = togl->AccumRed;
106         attribs[na++] = AGL_ACCUM_GREEN_SIZE;
107         attribs[na++] = togl->AccumGreen;
108         attribs[na++] = AGL_ACCUM_BLUE_SIZE;
109         attribs[na++] = togl->AccumBlue;
110         if (togl->AlphaFlag) {
111             attribs[na++] = AGL_ACCUM_ALPHA_SIZE;
112             attribs[na++] = togl->AccumAlpha;
113         }
114     }
115     if (togl->MultisampleFlag) {
116         attribs[na++] = AGL_MULTISAMPLE;
117 #ifdef AGL_SAMPLES_ARB
118         /* OS X 10.2 and later */
119         attribs[na++] = AGL_SAMPLE_BUFFERS_ARB;
120         attribs[na++] = 1;
121         attribs[na++] = AGL_SAMPLES_ARB;
122         attribs[na++] = 2;
123 #endif
124     }
125     if (togl->AuxNumber != 0) {
126         attribs[na++] = AGL_AUX_BUFFERS;
127         attribs[na++] = togl->AuxNumber;
128     }
129     if (togl->Stereo == TOGL_STEREO_NATIVE) {
130         attribs[na++] = AGL_STEREO;
131     }
132     if (togl->FullscreenFlag) {
133         attribs[na++] = AGL_FULLSCREEN;
134         /* TODO: convert Tk screen to display device */
135         display = GetMainDevice();
136     }
137     attribs[na++] = AGL_NONE;
138 
139     if ((pix = aglChoosePixelFormat(&display, togl->FullscreenFlag ? 1 : 0,
140                             attribs)) == NULL) {
141         Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format",
142                 TCL_STATIC);
143         return NULL;
144     }
145 
146     /* TODO: since we aglDestroyPixelFormat elsewhere, this code may leak
147      * memory if the pixel format chosen is not the original (because
148      * aglDestroyPixelFormat will give an error). */
149     count = 0;
150     do {
151         info = (FBInfo *) realloc(info, (count + 1) * sizeof (FBInfo));
152         info[count].pix = pix;
153         aglDescribePixelFormat(pix, AGL_ACCELERATED, &info[count].acceleration);
154         aglDescribePixelFormat(pix, AGL_BUFFER_SIZE, &info[count].colors);
155         aglDescribePixelFormat(pix, AGL_DEPTH_SIZE, &info[count].depth);
156 #ifdef AGL_SAMPLES_ARB
157         aglDescribePixelFormat(pix, AGL_SAMPLES_ARB, &info[count].samples);
158 #else
159         info[count].samples = 0;
160 #endif
161         ++count;
162     } while (pix = aglNextPixelFormat(pix));
163     qsort(info, count, sizeof info[0], FBInfoCmp);
164     pix = info[0].pix;
165     free(info);
166     return pix;
167 }
168 
169 static int
togl_describePixelFormat(Togl * togl)170 togl_describePixelFormat(Togl *togl)
171 {
172     AGLPixelFormat pixelformat;
173 
174     /* fill in RgbaFlag, DoubleFlag, and Stereo */
175     pixelformat = (AGLPixelFormat) togl->PixelFormat;
176     GLint   has_rgba, has_doublebuf, has_depth, has_accum, has_alpha,
177             has_stencil, has_stereo, has_multisample;
178 
179     if (aglDescribePixelFormat(pixelformat, AGL_RGBA, &has_rgba)
180             && aglDescribePixelFormat(pixelformat, AGL_DOUBLEBUFFER,
181                     &has_doublebuf)
182             && aglDescribePixelFormat(pixelformat, AGL_DEPTH_SIZE, &has_depth)
183             && aglDescribePixelFormat(pixelformat, AGL_ACCUM_RED_SIZE,
184                     &has_accum)
185             && aglDescribePixelFormat(pixelformat, AGL_ALPHA_SIZE, &has_alpha)
186             && aglDescribePixelFormat(pixelformat, AGL_STENCIL_SIZE,
187                     &has_stencil)
188             && aglDescribePixelFormat(pixelformat, AGL_STEREO, &has_stereo)
189 #ifdef AGL_SAMPLES_ARB
190             && aglDescribePixelFormat(pixelformat, AGL_SAMPLES_ARB,
191                     &has_multisample)
192 #endif
193             ) {
194         togl->RgbaFlag = (has_rgba != 0);
195         togl->DoubleFlag = (has_doublebuf != 0);
196         togl->DepthFlag = (has_depth != 0);
197         togl->AccumFlag = (has_accum != 0);
198         togl->AlphaFlag = (has_alpha != 0);
199         togl->StencilFlag = (has_stencil != 0);
200         togl->Stereo = (has_stereo ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE);
201 #ifdef AGL_SAMPLES_ARB
202         togl->MultisampleFlag = (has_multisample != 0);
203 #else
204         togl->MultisampleFlag = False;
205 #endif
206         return True;
207     } else {
208         Tcl_SetResult(togl->Interp,
209                 TCL_STUPID "failed querying pixel format attributes",
210                 TCL_STATIC);
211         return False;
212     }
213 }
214 
215 #define isPow2(x) (((x) & ((x) - 1)) == 0)
216 
217 static AGLPbuffer
togl_createPbuffer(Togl * togl)218 togl_createPbuffer(Togl *togl)
219 {
220     GLint   min_size[2], max_size[2];
221     Bool    hasPbuffer;
222     const char *extensions;
223     GLboolean good;
224     GLint   target;
225     GLint   virtualScreen;
226     AGLPbuffer pbuf;
227 
228     extensions = (const char *) glGetString(GL_EXTENSIONS);
229     hasPbuffer = (strstr(extensions, "GL_APPLE_pixel_buffer") != NULL);
230     if (!hasPbuffer) {
231         Tcl_SetResult(togl->Interp,
232                 TCL_STUPID "pbuffers are not supported", TCL_STATIC);
233         return NULL;
234     }
235     glGetIntegerv(GL_MIN_PBUFFER_VIEWPORT_DIMS_APPLE, min_size);
236     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_size);
237     virtualScreen = aglGetVirtualScreen(togl->Ctx);
238     for (;;) {
239         /* make sure we don't exceed the maximum size because if we do,
240          * aglCreatePbuffer may succeed and later uses of the pbuffer fail */
241         if (togl->Width < min_size[0])
242             togl->Width = min_size[0];
243         else if (togl->Width > max_size[0]) {
244             if (togl->LargestPbufferFlag)
245                 togl->Width = max_size[0];
246             else {
247                 Tcl_SetResult(togl->Interp,
248                         TCL_STUPID "pbuffer too large", TCL_STATIC);
249                 return NULL;
250             }
251         }
252         if (togl->Height < min_size[1])
253             togl->Height = min_size[1];
254         else if (togl->Height > max_size[1]) {
255             if (togl->LargestPbufferFlag)
256                 togl->Height = max_size[1];
257             else {
258                 Tcl_SetResult(togl->Interp,
259                         TCL_STUPID "pbuffer too large", TCL_STATIC);
260                 return NULL;
261             }
262         }
263 
264         if (isPow2(togl->Width) && isPow2(togl->Height))
265             target = GL_TEXTURE_2D;
266         else
267             target = GL_TEXTURE_RECTANGLE_ARB;
268 
269         good = aglCreatePBuffer(togl->Width, togl->Height, target,
270                 togl->AlphaFlag ? GL_RGBA : GL_RGB, 0, &pbuf);
271         if (good) {
272             /* aglSetPbuffer allocates the framebuffer space */
273             if (aglSetPBuffer(togl->Ctx, pbuf, 0, 0, virtualScreen)) {
274                 return pbuf;
275             }
276         }
277         if (!togl->LargestPbufferFlag
278                 || togl->Width == min_size[0] || togl->Height == min_size[1]) {
279             Tcl_SetResult(togl->Interp,
280                     TCL_STUPID "unable to create pbuffer", TCL_STATIC);
281             return NULL;
282         }
283         /* largest unavailable, try something smaller */
284         togl->Width = togl->Width / 2 + togl->Width % 2;
285         togl->Height = togl->Width / 2 + togl->Height % 2;
286     }
287 }
288 
289 static void
togl_destroyPbuffer(Togl * togl)290 togl_destroyPbuffer(Togl *togl)
291 {
292     aglDestroyPBuffer(togl->pbuf);
293 }
294