51895e5fa1aab8dd233309f80378ed8cd6b21945
[blender-staging.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 #ifdef GHOST_MULTITHREADED_OPENGL
39 #include <OpenGL/OpenGL.h>
40 #endif
41
42 #include <vector>
43 #include <cassert>
44
45
46 NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil;
47 int              GHOST_ContextCGL::s_sharedCount         = 0;
48
49
50 GHOST_ContextCGL::GHOST_ContextCGL(
51         bool stereoVisual,
52         GHOST_TUns16  numOfAASamples,
53         NSWindow *window,
54         NSOpenGLView *openGLView,
55         int contextProfileMask,
56         int contextMajorVersion,
57         int contextMinorVersion,
58         int contextFlags,
59         int contextResetNotificationStrategy)
60     : GHOST_Context(stereoVisual, numOfAASamples),
61       m_window(window),
62       m_openGLView(openGLView),
63       m_contextProfileMask(contextProfileMask),
64       m_contextMajorVersion(contextMajorVersion),
65       m_contextMinorVersion(contextMinorVersion),
66       m_contextFlags(contextFlags),
67       m_contextResetNotificationStrategy(contextResetNotificationStrategy),
68       m_openGLContext(nil)
69 {
70         assert(window != nil);
71         assert(openGLView != nil);
72 }
73
74
75 GHOST_ContextCGL::~GHOST_ContextCGL()
76 {
77         if (m_openGLContext != nil) {
78                 if (m_openGLContext == [NSOpenGLContext currentContext]) {
79                         [NSOpenGLContext clearCurrentContext];
80                         [m_openGLView clearGLContext];
81                 }
82
83                 if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
84                         assert(s_sharedCount > 0);
85
86                         s_sharedCount--;
87
88                         if (s_sharedCount == 0)
89                                 s_sharedOpenGLContext = nil;
90
91                         [m_openGLContext release];
92                 }
93         }
94 }
95
96
97 GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
98 {
99         if (m_openGLContext != nil) {
100                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
101                 [m_openGLContext flushBuffer];
102                 [pool drain];
103                 return GHOST_kSuccess;
104         }
105         else {
106                 return GHOST_kFailure;
107         }
108 }
109
110
111 GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval)
112 {
113         if (m_openGLContext != nil) {
114                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
115                 [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
116                 [pool drain];
117                 return GHOST_kSuccess;
118         }
119         else {
120                 return GHOST_kFailure;
121         }
122 }
123
124
125 GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut)
126 {
127         if (m_openGLContext != nil) {
128                 GLint interval;
129
130                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
131
132                 [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
133
134                 [pool drain];
135
136                 intervalOut = static_cast<int>(interval);
137
138                 return GHOST_kSuccess;
139         }
140         else {
141                 return GHOST_kFailure;
142         }
143 }
144
145
146 GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
147 {
148         if (m_openGLContext != nil) {
149                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
150                 [m_openGLContext makeCurrentContext];
151
152                 activateGLEW();
153
154                 [pool drain];
155                 return GHOST_kSuccess;
156         }
157         else {
158                 return GHOST_kFailure;
159         }
160 }
161
162
163 GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
164 {
165         if (m_openGLContext != nil) {
166                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
167                 [m_openGLContext update];
168                 [pool drain];
169                 return GHOST_kSuccess;
170         }
171         else {
172                 return GHOST_kFailure;
173         }
174 }
175
176
177 static void makeAttribList(
178         std::vector<NSOpenGLPixelFormatAttribute>& attribs,
179         bool stereoVisual,
180         int numOfAASamples,
181         bool needAlpha,
182         bool needStencil)
183 {
184         // Pixel Format Attributes for the windowed NSOpenGLContext
185         attribs.push_back(NSOpenGLPFADoubleBuffer);
186
187         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
188         // needed for 'Draw Overlap' drawing method
189         attribs.push_back(NSOpenGLPFABackingStore);
190
191         // Force software OpenGL, for debugging
192         /* XXX jwilkins: fixed this to work on Intel macs? useful feature for Windows and Linux too?
193          * Maybe a command line flag is better... */
194         if (getenv("BLENDER_SOFTWAREGL")) {
195                 attribs.push_back(NSOpenGLPFARendererID);
196                 attribs.push_back(kCGLRendererGenericFloatID);
197         }
198         else {
199                 attribs.push_back(NSOpenGLPFAAccelerated);
200         }
201
202         /* Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway */
203         //attribs.push_back(NSOpenGLPFAAllowOfflineRenderers);
204
205         attribs.push_back(NSOpenGLPFADepthSize);
206         attribs.push_back((NSOpenGLPixelFormatAttribute) 32);
207
208         attribs.push_back(NSOpenGLPFAAccumSize);
209         attribs.push_back((NSOpenGLPixelFormatAttribute) 32);
210
211         if (stereoVisual)
212                 attribs.push_back(NSOpenGLPFAStereo);
213
214         if (needAlpha) {
215                 attribs.push_back(NSOpenGLPFAAlphaSize);
216                 attribs.push_back((NSOpenGLPixelFormatAttribute) 8);
217         }
218
219         if (needStencil) {
220                 attribs.push_back(NSOpenGLPFAStencilSize);
221                 attribs.push_back((NSOpenGLPixelFormatAttribute) 8);
222         }
223
224         if (numOfAASamples > 0) {
225                 // Multisample anti-aliasing
226                 attribs.push_back(NSOpenGLPFAMultisample);
227
228                 attribs.push_back(NSOpenGLPFASampleBuffers);
229                 attribs.push_back((NSOpenGLPixelFormatAttribute) 1);
230
231                 attribs.push_back(NSOpenGLPFASamples);
232                 attribs.push_back((NSOpenGLPixelFormatAttribute) numOfAASamples);
233
234                 attribs.push_back(NSOpenGLPFANoRecovery);
235         }
236
237         attribs.push_back((NSOpenGLPixelFormatAttribute) 0);
238 }
239
240
241 GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
242 {
243         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
244
245         std::vector<NSOpenGLPixelFormatAttribute> attribs;
246         attribs.reserve(40);
247
248         NSOpenGLContext *prev_openGLContext = [m_openGLView openGLContext];
249
250 #ifdef GHOST_OPENGL_ALPHA
251         static const bool needAlpha   = true;
252 #else
253         static const bool needAlpha   = false;
254 #endif
255
256 #ifdef GHOST_OPENGL_STENCIL
257         static const bool needStencil = true;
258 #else
259         static const bool needStencil = false;
260 #endif
261
262         makeAttribList(attribs, m_stereoVisual, m_numOfAASamples, needAlpha, needStencil);
263
264         NSOpenGLPixelFormat *pixelFormat;
265
266         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
267
268         // Fall back to no multisampling if Antialiasing init failed
269         if (m_numOfAASamples > 0 && pixelFormat == nil) {
270                 // XXX jwilkins: Does CGL only succeed when it makes an exact match on the number of samples?
271                 // Does this need to explicitly try for a lesser match before giving up?
272                 // (Now that I think about it, does WGL really require the code that it has for finding a lesser match?)
273
274                 attribs.clear();
275                 makeAttribList(attribs, m_stereoVisual, 0, needAlpha, needStencil);
276                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
277         }
278
279         if (pixelFormat == nil)
280                 goto error;
281
282         if (m_numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
283                 GLint actualSamples;
284                 [pixelFormat getValues:&actualSamples forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
285
286                 if (m_numOfAASamples != (GHOST_TUns16)actualSamples) {
287                         fprintf(stderr,
288                                 "Warning! Unable to find a multisample pixel format that supports exactly %d samples. "
289                                 "Substituting one that uses %d samples.\n",
290                                 m_numOfAASamples, actualSamples);
291
292                         m_numOfAASamples = (GHOST_TUns16)actualSamples;
293                 }
294         }
295
296         [m_openGLView setPixelFormat:pixelFormat];
297
298         m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext]; // +1 refCount to pixelFormat
299
300         if (m_openGLContext == nil)
301                 goto error;
302
303         if (s_sharedCount == 0)
304                 s_sharedOpenGLContext = m_openGLContext;
305         
306         [pixelFormat release]; // -1 refCount to pixelFormat
307         
308         s_sharedCount++;
309
310 #ifdef GHOST_MULTITHREADED_OPENGL
311         //Switch openGL to multhreaded mode
312         CGLContextObj cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
313         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
314                 printf("\nSwitched openGL to multithreaded mode\n");
315 #endif
316
317 #ifdef GHOST_WAIT_FOR_VSYNC
318         {
319                 GLint swapInt = 1;
320                 /* wait for vsync, to avoid tearing artifacts */
321                 [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
322         }
323 #endif
324
325         [m_openGLView setOpenGLContext:m_openGLContext];
326         [m_openGLContext setView:m_openGLView];
327
328         initContextGLEW();
329
330         initClearGL();
331         [m_openGLContext flushBuffer];
332
333         [pool drain];
334
335         return GHOST_kSuccess;
336
337 error:
338
339         [m_openGLView setOpenGLContext:prev_openGLContext];
340         [pixelFormat release];
341
342         [pool drain];
343
344         return GHOST_kFailure;
345 }
346
347
348 GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
349 {
350         m_openGLContext = NULL;
351         m_openGLView    = NULL;
352
353         return GHOST_kSuccess;
354 }