Merge branch 'blender2.7'
[blender.git] / intern / ghost / intern / GHOST_ContextCGL.mm
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2013 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Jason Wilkins
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file ghost/intern/GHOST_ContextCGL.mm
29  *  \ingroup GHOST
30  *
31  * Definition of GHOST_ContextCGL class.
32  */
33
34 #include "GHOST_ContextCGL.h"
35
36 #include <Cocoa/Cocoa.h>
37
38 //#define GHOST_MULTITHREADED_OPENGL
39
40 #ifdef GHOST_MULTITHREADED_OPENGL
41 #include <OpenGL/OpenGL.h>
42 #endif
43
44 #include <vector>
45 #include <cassert>
46
47
48 NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil;
49 int              GHOST_ContextCGL::s_sharedCount         = 0;
50
51
52 GHOST_ContextCGL::GHOST_ContextCGL(
53         bool stereoVisual,
54         GHOST_TUns16  numOfAASamples,
55         NSWindow *window,
56         NSOpenGLView *openGLView,
57         int contextProfileMask,
58         int contextMajorVersion,
59         int contextMinorVersion,
60         int contextFlags,
61         int contextResetNotificationStrategy)
62     : GHOST_Context(stereoVisual, numOfAASamples),
63       m_openGLView(openGLView),
64       m_openGLContext(nil),
65       m_debug(contextFlags)
66 {
67         // for now be very strict about OpenGL version requested
68         switch (contextMajorVersion) {
69                 case 2:
70                         assert(contextMinorVersion == 1);
71                         assert(contextProfileMask == 0);
72                         m_coreProfile = false;
73                         break;
74                 case 3:
75                         // Apple didn't implement 3.0 or 3.1
76                         assert(contextMinorVersion == 3);
77                         assert(contextProfileMask == GL_CONTEXT_CORE_PROFILE_BIT);
78                         m_coreProfile = true;
79                         break;
80                 default:
81                         assert(false);
82         }
83 }
84
85
86 GHOST_ContextCGL::~GHOST_ContextCGL()
87 {
88         if (m_openGLContext != nil) {
89                 if (m_openGLContext == [NSOpenGLContext currentContext]) {
90                         [NSOpenGLContext clearCurrentContext];
91
92                         if(m_openGLView) {
93                                 [m_openGLView clearGLContext];
94                         }
95                 }
96
97                 if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
98                         assert(s_sharedCount > 0);
99
100                         s_sharedCount--;
101
102                         if (s_sharedCount == 0)
103                                 s_sharedOpenGLContext = nil;
104
105                         [m_openGLContext release];
106                 }
107         }
108 }
109
110
111 GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
112 {
113         if (m_openGLContext != nil) {
114                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
115                 [m_openGLContext flushBuffer];
116                 [pool drain];
117                 return GHOST_kSuccess;
118         }
119         else {
120                 return GHOST_kFailure;
121         }
122 }
123
124
125 GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval)
126 {
127         if (m_openGLContext != nil) {
128                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
129                 [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
130                 [pool drain];
131                 return GHOST_kSuccess;
132         }
133         else {
134                 return GHOST_kFailure;
135         }
136 }
137
138
139 GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut)
140 {
141         if (m_openGLContext != nil) {
142                 GLint interval;
143
144                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
145
146                 [m_openGLContext getValues:&interval forParameter:NSOpenGLCPSwapInterval];
147
148                 [pool drain];
149
150                 intervalOut = static_cast<int>(interval);
151
152                 return GHOST_kSuccess;
153         }
154         else {
155                 return GHOST_kFailure;
156         }
157 }
158
159
160 GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
161 {
162         if (m_openGLContext != nil) {
163                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
164                 [m_openGLContext makeCurrentContext];
165                 [pool drain];
166                 return GHOST_kSuccess;
167         }
168         else {
169                 return GHOST_kFailure;
170         }
171 }
172
173 GHOST_TSuccess GHOST_ContextCGL::releaseDrawingContext()
174 {
175         if (m_openGLContext != nil) {
176                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
177                 [NSOpenGLContext clearCurrentContext];
178                 [pool drain];
179                 return GHOST_kSuccess;
180         }
181         else {
182                 return GHOST_kFailure;
183         }
184 }
185
186 GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
187 {
188         if (m_openGLContext != nil) {
189                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
190                 [m_openGLContext update];
191                 [pool drain];
192                 return GHOST_kSuccess;
193         }
194         else {
195                 return GHOST_kFailure;
196         }
197 }
198
199
200 static void makeAttribList(
201         std::vector<NSOpenGLPixelFormatAttribute>& attribs,
202         bool coreProfile, 
203         bool stereoVisual,
204         int numOfAASamples,
205         bool needAlpha,
206         bool needStencil,
207         bool softwareGL)
208 {
209         attribs.clear();
210
211         attribs.push_back(NSOpenGLPFAOpenGLProfile);
212         attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
213         
214         // Pixel Format Attributes for the windowed NSOpenGLContext
215         attribs.push_back(NSOpenGLPFADoubleBuffer);
216
217         if (softwareGL) {
218                 attribs.push_back(NSOpenGLPFARendererID);
219                 attribs.push_back(kCGLRendererGenericFloatID);
220         }
221         else {
222                 attribs.push_back(NSOpenGLPFAAccelerated);
223                 attribs.push_back(NSOpenGLPFANoRecovery);
224         }
225
226         attribs.push_back(NSOpenGLPFAAllowOfflineRenderers); // for automatic GPU switching
227
228         attribs.push_back(NSOpenGLPFADepthSize);
229         attribs.push_back((NSOpenGLPixelFormatAttribute) 32);
230
231         if (stereoVisual)
232                 attribs.push_back(NSOpenGLPFAStereo);
233
234         if (needAlpha) {
235                 attribs.push_back(NSOpenGLPFAAlphaSize);
236                 attribs.push_back((NSOpenGLPixelFormatAttribute) 8);
237         }
238
239         if (needStencil) {
240                 attribs.push_back(NSOpenGLPFAStencilSize);
241                 attribs.push_back((NSOpenGLPixelFormatAttribute) 8);
242         }
243
244         if (numOfAASamples > 0) {
245                 // Multisample anti-aliasing
246                 attribs.push_back(NSOpenGLPFAMultisample);
247
248                 attribs.push_back(NSOpenGLPFASampleBuffers);
249                 attribs.push_back((NSOpenGLPixelFormatAttribute) 1);
250
251                 attribs.push_back(NSOpenGLPFASamples);
252                 attribs.push_back((NSOpenGLPixelFormatAttribute) numOfAASamples);
253         }
254
255         attribs.push_back((NSOpenGLPixelFormatAttribute) 0);
256 }
257
258 // TODO(merwin): make this available to all platforms
259 static void getVersion(int *major, int *minor)
260 {
261 #if 1 // legacy GL
262         sscanf((const char*)glGetString(GL_VERSION), "%d.%d", major, minor);
263 #else // 3.0+
264         glGetIntegerv(GL_MAJOR_VERSION, major);
265         glGetIntegerv(GL_MINOR_VERSION, minor);
266 #endif
267 }
268
269 GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
270 {
271         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
272
273         std::vector<NSOpenGLPixelFormatAttribute> attribs;
274         attribs.reserve(40);
275
276         NSOpenGLContext *prev_openGLContext = (m_openGLView) ? [m_openGLView openGLContext] : NULL;
277
278 #ifdef GHOST_OPENGL_ALPHA
279         static const bool needAlpha   = true;
280 #else
281         static const bool needAlpha   = false;
282 #endif
283
284 #ifdef GHOST_OPENGL_STENCIL
285         static const bool needStencil = true;
286 #else
287         static const bool needStencil = false;
288 #endif
289
290         static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); // command-line argument would be better
291         GLint major = 0, minor = 0;
292         NSOpenGLPixelFormat *pixelFormat;
293         // TODO: keep pixel format for subsequent windows/contexts instead of recreating each time
294
295         makeAttribList(attribs, m_coreProfile, m_stereoVisual, m_numOfAASamples, needAlpha, needStencil, softwareGL);
296
297         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
298
299         // Fall back to no multisampling if Antialiasing init failed
300         if (m_numOfAASamples > 0 && pixelFormat == nil) {
301                 // XXX jwilkins: Does CGL only succeed when it makes an exact match on the number of samples?
302                 // Does this need to explicitly try for a lesser match before giving up?
303                 // (Now that I think about it, does WGL really require the code that it has for finding a lesser match?)
304
305                 attribs.clear();
306                 makeAttribList(attribs, m_coreProfile, m_stereoVisual, 0, needAlpha, needStencil, softwareGL);
307                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
308         }
309
310         if (pixelFormat == nil)
311                 goto error;
312
313         if (m_numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
314                 GLint actualSamples;
315                 [pixelFormat getValues:&actualSamples forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
316
317                 if (m_numOfAASamples != (GHOST_TUns16)actualSamples) {
318                         fprintf(stderr,
319                                 "Warning! Unable to find a multisample pixel format that supports exactly %d samples. "
320                                 "Substituting one that uses %d samples.\n",
321                                 m_numOfAASamples, actualSamples);
322
323                         m_numOfAASamples = (GHOST_TUns16)actualSamples;
324                 }
325         }
326
327         m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext];
328         [pixelFormat release];
329
330         [m_openGLContext makeCurrentContext];
331
332         getVersion(&major, &minor);
333         if (m_debug) {
334                 fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
335                 fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
336         }
337
338         if (major < 2 || (major == 2 && minor < 1)) {
339                 // fall back to software renderer if GL < 2.1
340                 fprintf(stderr, "OpenGL 2.1 is not supported on your hardware, falling back to software");
341                 softwareGL = true;
342
343                 // discard hardware GL context
344                 [NSOpenGLContext clearCurrentContext];
345                 [m_openGLContext release];
346
347                 // create software GL context
348                 makeAttribList(attribs, m_coreProfile, m_stereoVisual, m_numOfAASamples, needAlpha, needStencil, softwareGL);
349                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
350                 m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext];
351                 [pixelFormat release];
352
353                 [m_openGLContext makeCurrentContext];
354
355                 getVersion(&major, &minor);
356                 if (m_debug) {
357                         fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
358                         fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
359                 }
360         }
361
362 #ifdef GHOST_MULTITHREADED_OPENGL
363         //Switch openGL to multhreaded mode
364         if (CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine) == kCGLNoError)
365                 if (m_debug)
366                         fprintf(stderr, "\nSwitched OpenGL to multithreaded mode\n");
367 #endif
368
369 #ifdef GHOST_WAIT_FOR_VSYNC
370         {
371                 GLint swapInt = 1;
372                 /* wait for vsync, to avoid tearing artifacts */
373                 [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
374         }
375 #endif
376
377         initContextGLEW();
378
379         if (m_openGLView) {
380                 [m_openGLView setOpenGLContext:m_openGLContext];
381                 [m_openGLContext setView:m_openGLView];
382         }
383
384         if (s_sharedCount == 0)
385                 s_sharedOpenGLContext = m_openGLContext;
386         
387         s_sharedCount++;
388
389
390         initClearGL();
391         [m_openGLContext flushBuffer];
392
393         [pool drain];
394
395         return GHOST_kSuccess;
396
397 error:
398
399         if (m_openGLView) {
400                 [m_openGLView setOpenGLContext:prev_openGLContext];
401         }
402
403         [pixelFormat release];
404
405         [pool drain];
406
407         return GHOST_kFailure;
408 }
409
410
411 GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
412 {
413         m_openGLContext = NULL;
414         m_openGLView    = NULL;
415
416         return GHOST_kSuccess;
417 }