Ghost Context Refactor
[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                 if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
83                         assert(s_sharedCount > 0);
84
85                         s_sharedCount--;
86
87                         if (s_sharedCount == 0)
88                                 s_sharedOpenGLContext = nil;
89
90                         [m_openGLContext release];
91                 }
92         }
93 }
94
95
96 GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
97 {
98         if (m_openGLContext != nil) {
99                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
100                 [m_openGLContext flushBuffer];
101                 [pool drain];
102                 return GHOST_kSuccess;
103         }
104         else {
105                 return GHOST_kFailure;
106         }
107 }
108
109
110 GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval)
111 {
112         if (m_openGLContext != nil) {
113                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
114                 [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
115                 [pool drain];
116                 return GHOST_kSuccess;
117         }
118         else {
119                 return GHOST_kFailure;
120         }
121 }
122
123
124 GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut)
125 {
126         if (m_openGLContext != nil) {
127                 GLint interval;
128
129                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
130
131                 [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval];
132
133                 [pool drain];
134
135                 intervalOut = static_cast<int>(interval);
136
137                 return GHOST_kSuccess;
138         }
139         else {
140                 return GHOST_kFailure;
141         }
142 }
143
144
145 GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
146 {
147         if (m_openGLContext != nil) {
148                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
149                 [m_openGLContext makeCurrentContext];
150
151                 activateGLEW();
152
153                 [pool drain];
154                 return GHOST_kSuccess;
155         }
156         else {
157                 return GHOST_kFailure;
158         }
159 }
160
161
162 GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
163 {
164         if (m_openGLContext != nil) {
165                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
166                 [m_openGLContext update];
167                 [pool drain];
168                 return GHOST_kSuccess;
169         }
170         else {
171                 return GHOST_kFailure;
172         }
173 }
174
175
176 static void makeAttribList(
177         std::vector<NSOpenGLPixelFormatAttribute>& attribs,
178         bool stereoVisual,
179         int numOfAASamples,
180         bool needAlpha,
181         bool needStencil)
182 {
183         // Pixel Format Attributes for the windowed NSOpenGLContext
184         attribs.push_back(NSOpenGLPFADoubleBuffer);
185
186         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
187         // needed for 'Draw Overlap' drawing method
188         attribs.push_back(NSOpenGLPFABackingStore);
189
190         // Force software OpenGL, for debugging
191         /* XXX jwilkins: fixed this to work on Intel macs? useful feature for Windows and Linux too?
192          * Maybe a command line flag is better... */
193         if (getenv("BLENDER_SOFTWAREGL")) {
194                 attribs.push_back(NSOpenGLPFARendererID);
195 #if defined(__ppc__) || defined(__ppc64__)
196                 attribs.push_back(kCGLRendererAppleSWID);
197 #else
198                 attribs.push_back(kCGLRendererGenericFloatID);
199 #endif
200         }
201         else {
202                 attribs.push_back(NSOpenGLPFAAccelerated);
203         }
204
205         /* Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway */
206         //attribs.push_back(NSOpenGLPFAAllowOfflineRenderers);
207
208         attribs.push_back(NSOpenGLPFADepthSize);
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];
299
300         if (m_openGLContext == nil)
301                 goto error;
302
303         if (s_sharedCount == 0)
304                 s_sharedOpenGLContext = m_openGLContext;
305
306         s_sharedCount++;
307
308 #ifdef GHOST_MULTITHREADED_OPENGL
309         //Switch openGL to multhreaded mode
310         CGLContextObj cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
311         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
312                 printf("\nSwitched openGL to multithreaded mode\n");
313 #endif
314
315 #ifdef GHOST_WAIT_FOR_VSYNC
316         {
317                 GLint swapInt = 1;
318                 /* wait for vsync, to avoid tearing artifacts */
319                 [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
320         }
321 #endif
322
323         [m_openGLView setOpenGLContext:m_openGLContext];
324         [m_openGLContext setView:m_openGLView];
325
326         initContextGLEW();
327
328         initClearGL();
329         [m_openGLContext flushBuffer];
330
331         [pool drain];
332
333         return GHOST_kSuccess;
334
335 error:
336
337         [m_openGLView setOpenGLContext:prev_openGLContext];
338
339         [pool drain];
340
341         return GHOST_kFailure;
342 }
343
344
345 GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
346 {
347         m_openGLContext = NULL;
348         m_openGLView    = NULL;
349
350         return GHOST_kSuccess;
351 }