optimizing creation of glcontexts, less flicker
[wxWidgets.git] / src / osx / iphone / glcanvas.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/iphone/glcanvas.mm
3 // Purpose:     wxGLCanvas, for using OpenGL with wxWidgets under iPhone
4 // Author:      Stefan Csomor
5 // Modified by:
6 // Created:     1998-01-01
7 // RCS-ID:      $Id: glcanvas.cpp 54129 2008-06-11 19:30:52Z SC $
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/osx/private.h"
37
38 #import <OpenGLES/EAGL.h>
39 #import <OpenGLES/EAGLDrawable.h>
40 #import <QuartzCore/QuartzCore.h>
41
42 // a lot of the code is from the OpenGL ES Template
43
44 // can be turned on for ES 2.0 only
45 #define USE_DEPTH_BUFFER 0
46
47 @interface wxUICustomOpenGLView : UIView
48 {
49     CGRect oldRect;
50     EAGLContext* context;
51
52     /* The pixel dimensions of the backbuffer */
53     GLint backingWidth;
54     GLint backingHeight;
55
56     /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
57     GLuint viewRenderbuffer, viewFramebuffer;
58     
59     /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
60     GLuint depthRenderbuffer;
61 }
62
63 - (BOOL) createFramebuffer;
64 - (void) destroyFramebuffer;
65 - (id) initWithFrame:(CGRect) rect;
66
67 @end
68
69 @implementation wxUICustomOpenGLView
70
71 + (Class)layerClass {
72     return [CAEAGLLayer class];
73 }
74
75 + (void)initialize
76 {
77     static BOOL initialized = NO;
78     if (!initialized) 
79     {
80         initialized = YES;
81         wxOSXIPhoneClassAddWXMethods( self );
82     }
83 }
84
85 - (id) initWithFrame:(CGRect)rect
86 {
87     if ( !(self=[super initWithFrame:rect]) )
88         return nil;
89     
90     oldRect = rect;
91     return self;
92 }
93
94 - (BOOL)isOpaque
95 {
96     return YES;
97 }
98
99 - (BOOL)createFramebuffer {
100     
101     glGenFramebuffersOES(1, &viewFramebuffer);
102     glGenRenderbuffersOES(1, &viewRenderbuffer);
103     glEnable(GL_CULL_FACE);
104     
105     glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
106     glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
107     [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
108     glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
109     
110     glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
111     glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
112     
113     if (USE_DEPTH_BUFFER) {
114         glGenRenderbuffersOES(1, &depthRenderbuffer);
115         glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
116         glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
117         glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
118     }
119     
120     if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
121         NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
122         return NO;
123     }
124     
125     return YES;
126 }
127
128
129 - (void)destroyFramebuffer {
130     
131     glDeleteFramebuffersOES(1, &viewFramebuffer);
132     viewFramebuffer = 0;
133     glDeleteRenderbuffersOES(1, &viewRenderbuffer);
134     viewRenderbuffer = 0;
135     
136     if(depthRenderbuffer) {
137         glDeleteRenderbuffersOES(1, &depthRenderbuffer);
138         depthRenderbuffer = 0;
139     }
140 }
141
142 - (void) setContext:(EAGLContext*) ctx {
143     context = ctx;
144     [EAGLContext setCurrentContext:ctx];
145 #if 0
146     CGRect newRect = [self frame];
147     if ( /* (CGRectEqualToRect(newRect, oldRect) == NO && ![self isHidden] && newRect.size.width > 0 && newRect.size.height > 0 ) 
148         || */ viewFramebuffer == 0 )
149     {
150         [self destroyFramebuffer];
151         [self createFramebuffer];        
152     }
153 #endif
154     glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
155 }
156  
157 - (void) swapBuffers {
158     glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
159     [context presentRenderbuffer:GL_RENDERBUFFER_OES];
160 }
161
162 - (void)layoutSubviews {
163     [EAGLContext setCurrentContext:context];
164     [self destroyFramebuffer];
165     [self createFramebuffer];
166 }
167
168 @end
169
170
171 WXGLContext WXGLCreateContext( WXGLPixelFormat pixelFormat, WXGLContext shareContext )
172 {
173     WXGLContext context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
174     
175     // [[EAGLContext alloc] initWithFormat:pixelFormat shareContext: shareContext];
176     if ( !context )
177         wxFAIL_MSG("NSOpenGLContext creation failed");
178     return context ;
179 }
180
181 void WXGLDestroyContext( WXGLContext context )
182 {
183     if ( context )
184     {
185         [context release];
186     }
187 }
188
189 WXGLContext WXGLGetCurrentContext()
190 {
191     return [EAGLContext currentContext];
192 }
193
194 bool WXGLSetCurrentContext(WXGLContext context)
195 {
196     [EAGLContext setCurrentContext:context];
197     return true;
198 }
199
200 void WXGLDestroyPixelFormat( WXGLPixelFormat pixelFormat )
201 {
202 /*
203     if ( pixelFormat )
204     {
205         [pixelFormat release];
206     }
207 */
208 }
209
210
211 WXGLPixelFormat WXGLChoosePixelFormat(const int *attribList)
212 {
213 #if 0
214     NSOpenGLPixelFormatAttribute data[512];
215     const NSOpenGLPixelFormatAttribute defaultAttribs[] =
216     {
217         NSOpenGLPFADoubleBuffer,
218         NSOpenGLPFAMinimumPolicy,
219         NSOpenGLPFAColorSize,(NSOpenGLPixelFormatAttribute)8,
220         NSOpenGLPFAAlphaSize,(NSOpenGLPixelFormatAttribute)0,
221         NSOpenGLPFADepthSize,(NSOpenGLPixelFormatAttribute)8,
222         (NSOpenGLPixelFormatAttribute)nil
223     };
224
225     const NSOpenGLPixelFormatAttribute *attribs;
226     if ( !attribList )
227     {
228         attribs = defaultAttribs;
229     }
230     else
231     {
232         unsigned p = 0;
233         data[p++] = NSOpenGLPFAMinimumPolicy; // make _SIZE tags behave more like GLX
234
235         for ( unsigned arg = 0; attribList[arg] !=0 && p < WXSIZEOF(data); )
236         {
237             switch ( attribList[arg++] )
238             {
239                 case WX_GL_RGBA:
240                     //data[p++] = AGL_RGBA;
241                     break;
242
243                 case WX_GL_BUFFER_SIZE:
244                     //data[p++] = AGL_BUFFER_SIZE;
245                     //data[p++] = attribList[arg++];
246                     break;
247
248                 case WX_GL_LEVEL:
249                     //data[p++]=AGL_LEVEL;
250                     //data[p++]=attribList[arg++];
251                     break;
252
253                 case WX_GL_DOUBLEBUFFER:
254                     data[p++] = NSOpenGLPFADoubleBuffer;
255                     break;
256
257                 case WX_GL_STEREO:
258                     data[p++] = NSOpenGLPFAStereo;
259                     break;
260
261                 case WX_GL_AUX_BUFFERS:
262                     data[p++] = NSOpenGLPFAAuxBuffers;
263                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
264                     break;
265
266                 case WX_GL_MIN_RED:
267                     data[p++] = NSOpenGLPFAColorSize;
268                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
269                     break;
270
271                 case WX_GL_MIN_GREEN:
272                     //data[p++] = AGL_GREEN_SIZE;
273                     //data[p++] = attribList[arg++];
274                     break;
275
276                 case WX_GL_MIN_BLUE:
277                     //data[p++] = AGL_BLUE_SIZE;
278                     //data[p++] = attribList[arg++];
279                     break;
280
281                 case WX_GL_MIN_ALPHA:
282                     data[p++] = NSOpenGLPFAAlphaSize;
283                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
284                     break;
285
286                 case WX_GL_DEPTH_SIZE:
287                     data[p++] = NSOpenGLPFADepthSize;
288                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
289                     break;
290
291                 case WX_GL_STENCIL_SIZE:
292                     data[p++] = NSOpenGLPFAStencilSize;
293                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
294                     break;
295
296                 case WX_GL_MIN_ACCUM_RED:
297                     data[p++] = NSOpenGLPFAAccumSize;
298                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
299                     break;
300
301                 case WX_GL_MIN_ACCUM_GREEN:
302                     //data[p++] = AGL_ACCUM_GREEN_SIZE;
303                     //data[p++] = attribList[arg++];
304                     break;
305
306                 case WX_GL_MIN_ACCUM_BLUE:
307                     //data[p++] = AGL_ACCUM_BLUE_SIZE;
308                     //data[p++] = attribList[arg++];
309                     break;
310
311                 case WX_GL_MIN_ACCUM_ALPHA:
312                     //data[p++] = AGL_ACCUM_ALPHA_SIZE;
313                     //data[p++] = attribList[arg++];
314                     break;
315
316                 case WX_GL_SAMPLE_BUFFERS:
317                     if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
318                     {
319                         if ( !attribList[arg++] )
320                             break;
321
322                         return false;
323                     }
324
325                     data[p++] = NSOpenGLPFASampleBuffers;
326                     if ( (data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++]) == true )
327                     {
328                         // don't use software fallback
329                         data[p++] = NSOpenGLPFANoRecovery;
330                     }
331                     break;
332
333                 case WX_GL_SAMPLES:
334                     if ( !wxGLCanvas::IsAGLMultiSampleAvailable() )
335                     {
336                         if ( !attribList[arg++] )
337                             break;
338
339                         return false;
340                     }
341
342                     data[p++] = NSOpenGLPFASamples;
343                     data[p++] = (NSOpenGLPixelFormatAttribute) attribList[arg++];
344                     break;
345             }
346         }
347
348         data[p] = (NSOpenGLPixelFormatAttribute)nil;
349
350         attribs = data;
351     }
352
353     return [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*) attribs];
354 #endif
355     return NULL;
356 }
357
358 bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
359 {
360     if ( !m_glContext )
361         return false;  
362
363     wxUICustomOpenGLView* v = (wxUICustomOpenGLView*) win.GetPeer()->GetWXWidget();
364     [v setContext:m_glContext];
365     return true;
366 }
367
368 #define USE_SEPARATE_VIEW 1
369
370 bool wxGLCanvas::Create(wxWindow *parent,
371                         wxWindowID id,
372                         const wxPoint& pos,
373                         const wxSize& size,
374                         long style,
375                         const wxString& name,
376                         const int *attribList,
377                         const wxPalette& WXUNUSED(palette))
378 {
379 /*
380     m_glFormat = WXGLChoosePixelFormat(attribList);
381     if ( !m_glFormat )
382         return false;
383 */
384 #if USE_SEPARATE_VIEW
385     m_macIsUserPane = false ;
386 #endif
387
388     if ( !wxWindow::Create(parent, id, pos, size, style, name) )
389         return false;
390
391 #if USE_SEPARATE_VIEW
392     CGRect r = wxOSXGetFrameForControl( this, pos , size ) ;
393     wxUICustomOpenGLView* v = [[wxUICustomOpenGLView alloc] initWithFrame:r];
394     CAEAGLLayer* eaglLayer = (CAEAGLLayer*) v.layer;
395     eaglLayer.opaque = YES;
396     eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
397             [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, 
398             kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
399             
400     m_peer = new wxWidgetIPhoneImpl( this, v );
401
402     MacPostControlCreate(pos, size) ;
403 #endif
404     return true;
405 }
406
407 wxGLCanvas::~wxGLCanvas()
408 {
409     if ( m_glFormat )
410         WXGLDestroyPixelFormat(m_glFormat);
411 }
412
413 bool wxGLCanvas::SwapBuffers()
414 {
415     WXGLContext context = WXGLGetCurrentContext();
416     wxCHECK_MSG(context, false, wxT("should have current context"));
417
418     wxUICustomOpenGLView* v = (wxUICustomOpenGLView*) m_peer->GetWXWidget();
419     [v swapBuffers];
420     return true;
421 }
422
423
424 #endif // wxUSE_GLCANVAS