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