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