]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/glcanvas.cpp
added multisampling (anti-aliasing) support to wxGLCanvas (#9145)
[wxWidgets.git] / src / mac / carbon / glcanvas.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWidgets under Macintosh
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #if defined(__BORLANDC__)
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_GLCANVAS
27
28 #include "wx/glcanvas.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/frame.h"
32 #include "wx/log.h"
33 #include "wx/settings.h"
34 #endif
35
36 #include "wx/mac/uma.h"
37
38 #include "wx/mac/private.h"
39
40 // ----------------------------------------------------------------------------
41 // helper functions
42 // ----------------------------------------------------------------------------
43
44 static void wxLogAGLError(const char *func)
45 {
46 const int err = aglGetError();
47
48 wxLogError(_("OpenGL function \"%s\" failed: %s (error %d)"),
49 func, aglErrorString(err), err);
50 }
51
52 // ============================================================================
53 // implementation
54 // ============================================================================
55
56 // ----------------------------------------------------------------------------
57 // wxGLContext
58 // ----------------------------------------------------------------------------
59
60 wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext *other)
61 {
62 m_aglContext = aglCreateContext(win->GetAGLPixelFormat(),
63 other ? other->m_aglContext : NULL);
64 if ( !m_aglContext )
65 wxLogAGLError("aglCreateContext");
66 }
67
68 wxGLContext::~wxGLContext()
69 {
70 if ( m_aglContext )
71 {
72 // it's ok to pass the current context to this function
73 if ( !aglDestroyContext(m_aglContext) )
74 wxLogAGLError("aglDestroyContext");
75 }
76 }
77
78 bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
79 {
80 if ( !m_aglContext )
81 return false;
82
83 AGLDrawable drawable = (AGLDrawable)GetWindowPort(
84 MAC_WXHWND(win.MacGetTopLevelWindowRef()));
85 if ( !aglSetDrawable(m_aglContext, drawable) )
86 {
87 wxLogAGLError("aglSetDrawable");
88 return false;
89 }
90
91 if ( !aglSetCurrentContext(m_aglContext) )
92 {
93 wxLogAGLError("aglSetCurrentContext");
94 return false;
95 }
96
97 wx_const_cast(wxGLCanvas&, win).SetViewport();
98 return true;
99 }
100
101 // ----------------------------------------------------------------------------
102 // wxGLCanvas
103 // ----------------------------------------------------------------------------
104
105 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
106
107 BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow)
108 EVT_SIZE(wxGLCanvas::OnSize)
109 END_EVENT_TABLE()
110
111 wxGLCanvas::wxGLCanvas(wxWindow *parent,
112 wxWindowID id,
113 const int *attribList,
114 const wxPoint& pos,
115 const wxSize& size,
116 long style,
117 const wxString& name,
118 const wxPalette& palette)
119 {
120 Create(parent, id, pos, size, style, name, attribList, palette);
121 }
122
123 #if WXWIN_COMPATIBILITY_2_8
124
125 wxGLCanvas::wxGLCanvas(wxWindow *parent,
126 wxWindowID id,
127 const wxPoint& pos,
128 const wxSize& size,
129 long style,
130 const wxString& name,
131 const int *attribList,
132 const wxPalette& palette)
133 {
134 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
135 m_glContext = new wxGLContext(this);
136 }
137
138 wxGLCanvas::wxGLCanvas(wxWindow *parent,
139 const wxGLContext *shared,
140 wxWindowID id,
141 const wxPoint& pos,
142 const wxSize& size,
143 long style,
144 const wxString& name,
145 const int *attribList,
146 const wxPalette& palette)
147 {
148 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
149 m_glContext = new wxGLContext(this, shared);
150 }
151
152 wxGLCanvas::wxGLCanvas(wxWindow *parent,
153 const wxGLCanvas *shared,
154 wxWindowID id,
155 const wxPoint& pos,
156 const wxSize& size,
157 long style,
158 const wxString& name,
159 const int *attribList,
160 const wxPalette& palette)
161 {
162 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
163 m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
164 }
165
166 #endif // WXWIN_COMPATIBILITY_2_8
167
168 /* static */
169 bool wxGLCanvasBase::IsExtensionSupported(const char *extension)
170 {
171 // we need a valid context to query for extensions.
172 const GLint defaultAttribs[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_NONE };
173 AGLPixelFormat fmt = aglChoosePixelFormat(NULL, 0, defaultAttribs);
174 AGLContext ctx = aglCreateContext(fmt, NULL);
175 if ( !ctx )
176 return false;
177
178 wxString extensions = wxString::FromAscii(glGetString(GL_EXTENSIONS));
179
180 aglDestroyPixelFormat(fmt);
181 aglDestroyContext(ctx);
182
183 return IsExtensionInList(extensions, extension);
184 }
185
186 /* static */
187 bool wxGLCanvas::IsAGLMultiSampleAvailable()
188 {
189 static int s_isMultiSampleAvailable = -1;
190 if ( s_isMultiSampleAvailable == -1 )
191 s_isMultiSampleAvailable = IsExtensionSupported("GL_ARB_multisample");
192
193 return s_isMultiSampleAvailable != 0;
194 }
195
196 static AGLPixelFormat ChoosePixelFormat(const int *attribList)
197 {
198 GLint data[512];
199 const GLint defaultAttribs[] =
200 {
201 AGL_RGBA,
202 AGL_DOUBLEBUFFER,
203 AGL_MINIMUM_POLICY, // never choose less than requested
204 AGL_DEPTH_SIZE, 1, // use largest available depth buffer
205 AGL_RED_SIZE, 1,
206 AGL_GREEN_SIZE, 1,
207 AGL_BLUE_SIZE, 1,
208 AGL_ALPHA_SIZE, 0,
209 AGL_NONE
210 };
211
212 const GLint *attribs;
213 if ( !attribList )
214 {
215 attribs = defaultAttribs;
216 }
217 else
218 {
219 unsigned p = 0;
220 data[p++] = AGL_MINIMUM_POLICY; // make _SIZE tags behave more like GLX
221
222 for ( unsigned arg = 0; attribList[arg] !=0 && p < WXSIZEOF(data); )
223 {
224 switch ( attribList[arg++] )
225 {
226 case WX_GL_RGBA:
227 data[p++] = AGL_RGBA;
228 break;
229
230 case WX_GL_BUFFER_SIZE:
231 data[p++] = AGL_BUFFER_SIZE;
232 data[p++] = attribList[arg++];
233 break;
234
235 case WX_GL_LEVEL:
236 data[p++]=AGL_LEVEL;
237 data[p++]=attribList[arg++];
238 break;
239
240 case WX_GL_DOUBLEBUFFER:
241 data[p++] = AGL_DOUBLEBUFFER;
242 break;
243
244 case WX_GL_STEREO:
245 data[p++] = AGL_STEREO;
246 break;
247
248 case WX_GL_AUX_BUFFERS:
249 data[p++] = AGL_AUX_BUFFERS;
250 data[p++] = attribList[arg++];
251 break;
252
253 case WX_GL_MIN_RED:
254 data[p++] = AGL_RED_SIZE;
255 data[p++] = attribList[arg++];
256 break;
257
258 case WX_GL_MIN_GREEN:
259 data[p++] = AGL_GREEN_SIZE;
260 data[p++] = attribList[arg++];
261 break;
262
263 case WX_GL_MIN_BLUE:
264 data[p++] = AGL_BLUE_SIZE;
265 data[p++] = attribList[arg++];
266 break;
267
268 case WX_GL_MIN_ALPHA:
269 data[p++] = AGL_ALPHA_SIZE;
270 data[p++] = attribList[arg++];
271 break;
272
273 case WX_GL_DEPTH_SIZE:
274 data[p++] = AGL_DEPTH_SIZE;
275 data[p++] = attribList[arg++];
276 break;
277
278 case WX_GL_STENCIL_SIZE:
279 data[p++] = AGL_STENCIL_SIZE;
280 data[p++] = attribList[arg++];
281 break;
282
283 case WX_GL_MIN_ACCUM_RED:
284 data[p++] = AGL_ACCUM_RED_SIZE;
285 data[p++] = attribList[arg++];
286 break;
287
288 case WX_GL_MIN_ACCUM_GREEN:
289 data[p++] = AGL_ACCUM_GREEN_SIZE;
290 data[p++] = attribList[arg++];
291 break;
292
293 case WX_GL_MIN_ACCUM_BLUE:
294 data[p++] = AGL_ACCUM_BLUE_SIZE;
295 data[p++] = attribList[arg++];
296 break;
297
298 case WX_GL_MIN_ACCUM_ALPHA:
299 data[p++] = AGL_ACCUM_ALPHA_SIZE;
300 data[p++] = attribList[arg++];
301 break;
302
303 case WX_GL_SAMPLE_BUFFERS:
304 if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
305 {
306 if ( !attribList[arg++] )
307 break;
308
309 return false;
310 }
311
312 data[p++] = AGL_SAMPLE_BUFFERS_ARB;
313 if ( (data[p++] = attribList[arg++]) == true )
314 {
315 // don't use software fallback
316 data[p++] = AGL_NO_RECOVERY;
317 }
318 break;
319
320 case WX_GL_SAMPLES:
321 if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
322 {
323 if ( !attribList[arg++] )
324 break;
325
326 return false;
327 }
328
329 data[p++] = AGL_SAMPLES_ARB;
330 data[p++] = attribList[arg++];
331 break;
332 }
333 }
334
335 data[p] = AGL_NONE;
336
337 attribs = data;
338 }
339
340 return aglChoosePixelFormat(NULL, 0, attribs);
341 }
342
343 bool wxGLCanvas::Create(wxWindow *parent,
344 wxWindowID id,
345 const wxPoint& pos,
346 const wxSize& size,
347 long style,
348 const wxString& name,
349 const int *attribList,
350 const wxPalette& WXUNUSED(palette))
351 {
352 m_needsUpdate = false;
353 m_macCanvasIsShown = false;
354
355 m_aglFormat = ChoosePixelFormat(attribList);
356 if ( !m_aglFormat )
357 return false;
358
359 if ( !wxWindow::Create(parent, id, pos, size, style, name) )
360 return false;
361
362 m_macCanvasIsShown = true;
363
364 return true;
365 }
366
367 wxGLCanvas::~wxGLCanvas()
368 {
369 if ( m_aglFormat )
370 aglDestroyPixelFormat(m_aglFormat);
371 }
372
373 /* static */
374 bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
375 {
376 AGLPixelFormat aglFormat = ChoosePixelFormat(attribList);
377
378 if ( !aglFormat )
379 return false;
380
381 aglDestroyPixelFormat(aglFormat);
382
383 return true;
384 }
385
386 bool wxGLCanvas::SwapBuffers()
387 {
388 AGLContext context = aglGetCurrentContext();
389 wxCHECK_MSG(context, false, _T("should have current context"));
390
391 aglSwapBuffers(context);
392 return true;
393 }
394
395 void wxGLCanvas::SetViewport()
396 {
397 if ( !m_needsUpdate )
398 return;
399
400 m_needsUpdate = false;
401
402 AGLContext context = aglGetCurrentContext();
403 if ( !context )
404 return;
405
406 // viewport is initially set to entire port, adjust it to just this window
407 int x = 0,
408 y = 0;
409 MacClientToRootWindow(&x , &y);
410
411 int width, height;
412 GetClientSize(&width, &height);
413
414 Rect bounds;
415 GetWindowPortBounds(MAC_WXHWND(MacGetTopLevelWindowRef()) , &bounds);
416
417 #if 0
418 // TODO in case we adopt point vs pixel coordinates, this will make the conversion
419 HIRect hiRect = CGRectMake( x, y, width, height );
420 HIRectConvert( &hiRect, kHICoordSpace72DPIGlobal, NULL, kHICoordSpaceScreenPixel, NULL);
421 HIRect hiBounds = CGRectMake( 0, 0, bounds.right - bounds.left , bounds.bottom - bounds.top );
422 HIRectConvert( &hiBounds, kHICoordSpace72DPIGlobal, NULL, kHICoordSpaceScreenPixel, NULL);
423 GLint parms[4];
424 parms[0] = hiRect.origin.x;
425 parms[1] = hiBounds.size.height - (hiRect.origin.y + hiRect.size.height);
426 parms[2] = hiRect.size.width;
427 parms[3] = hiRect.size.height;
428 #else
429 GLint parms[4];
430 parms[0] = x;
431 parms[1] = bounds.bottom - bounds.top - ( y + height );
432 parms[2] = width;
433 parms[3] = height;
434 #endif
435
436 // move the buffer rect out of sight if we're hidden
437 if ( !m_macCanvasIsShown )
438 parms[0] += 20000;
439
440 if ( !aglSetInteger(context, AGL_BUFFER_RECT, parms) )
441 wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)");
442
443 if ( !aglEnable(context, AGL_BUFFER_RECT) )
444 wxLogAGLError("aglEnable(AGL_BUFFER_RECT)");
445
446 if ( !aglUpdateContext(context) )
447 wxLogAGLError("aglUpdateContext");
448 }
449
450 void wxGLCanvas::OnSize(wxSizeEvent& event)
451 {
452 MacUpdateView();
453 event.Skip();
454 }
455
456 void wxGLCanvas::MacUpdateView()
457 {
458 m_needsUpdate = true;
459 Refresh(false);
460 }
461
462 void wxGLCanvas::MacSuperChangedPosition()
463 {
464 MacUpdateView();
465 wxWindow::MacSuperChangedPosition();
466 }
467
468 void wxGLCanvas::MacTopLevelWindowChangedPosition()
469 {
470 MacUpdateView();
471 wxWindow::MacTopLevelWindowChangedPosition();
472 }
473
474 void wxGLCanvas::MacVisibilityChanged()
475 {
476 if ( IsShownOnScreen() != m_macCanvasIsShown )
477 {
478 m_macCanvasIsShown = !m_macCanvasIsShown;
479 MacUpdateView();
480 }
481
482 wxWindowMac::MacVisibilityChanged();
483 }
484
485 // ----------------------------------------------------------------------------
486 // wxGLApp
487 // ----------------------------------------------------------------------------
488
489 bool wxGLApp::InitGLVisual(const int *attribList)
490 {
491 AGLPixelFormat fmt = ChoosePixelFormat(attribList);
492 if ( !fmt )
493 return false;
494
495 aglDestroyPixelFormat(fmt);
496 return true;
497 }
498
499 #endif // wxUSE_GLCANVAS