ClangFormat: apply to source, most of intern
[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 NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil;
40 int GHOST_ContextCGL::s_sharedCount = 0;
41
42 GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
43                                    GHOST_TUns16 numOfAASamples,
44                                    NSWindow *window,
45                                    NSOpenGLView *openGLView,
46                                    int contextProfileMask,
47                                    int contextMajorVersion,
48                                    int contextMinorVersion,
49                                    int contextFlags,
50                                    int contextResetNotificationStrategy)
51     : GHOST_Context(stereoVisual, numOfAASamples),
52       m_openGLView(openGLView),
53       m_openGLContext(nil),
54       m_debug(contextFlags)
55 {
56   // for now be very strict about OpenGL version requested
57   switch (contextMajorVersion) {
58     case 2:
59       assert(contextMinorVersion == 1);
60       assert(contextProfileMask == 0);
61       m_coreProfile = false;
62       break;
63     case 3:
64       // Apple didn't implement 3.0 or 3.1
65       assert(contextMinorVersion == 3);
66       assert(contextProfileMask == GL_CONTEXT_CORE_PROFILE_BIT);
67       m_coreProfile = true;
68       break;
69     default:
70       assert(false);
71   }
72 }
73
74 GHOST_ContextCGL::~GHOST_ContextCGL()
75 {
76   if (m_openGLContext != nil) {
77     if (m_openGLContext == [NSOpenGLContext currentContext]) {
78       [NSOpenGLContext clearCurrentContext];
79
80       if (m_openGLView) {
81         [m_openGLView clearGLContext];
82       }
83     }
84
85     if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) {
86       assert(s_sharedCount > 0);
87
88       s_sharedCount--;
89
90       if (s_sharedCount == 0)
91         s_sharedOpenGLContext = nil;
92
93       [m_openGLContext release];
94     }
95   }
96 }
97
98 GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
99 {
100   if (m_openGLContext != nil) {
101     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
102     [m_openGLContext flushBuffer];
103     [pool drain];
104     return GHOST_kSuccess;
105   }
106   else {
107     return GHOST_kFailure;
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 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 getValues:&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 GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
145 {
146   if (m_openGLContext != nil) {
147     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
148     [m_openGLContext makeCurrentContext];
149     [pool drain];
150     return GHOST_kSuccess;
151   }
152   else {
153     return GHOST_kFailure;
154   }
155 }
156
157 GHOST_TSuccess GHOST_ContextCGL::releaseDrawingContext()
158 {
159   if (m_openGLContext != nil) {
160     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
161     [NSOpenGLContext clearCurrentContext];
162     [pool drain];
163     return GHOST_kSuccess;
164   }
165   else {
166     return GHOST_kFailure;
167   }
168 }
169
170 GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
171 {
172   if (m_openGLContext != nil) {
173     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
174     [m_openGLContext update];
175     [pool drain];
176     return GHOST_kSuccess;
177   }
178   else {
179     return GHOST_kFailure;
180   }
181 }
182
183 static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
184                            bool coreProfile,
185                            bool stereoVisual,
186                            int numOfAASamples,
187                            bool needAlpha,
188                            bool needStencil,
189                            bool softwareGL)
190 {
191   attribs.clear();
192
193   attribs.push_back(NSOpenGLPFAOpenGLProfile);
194   attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
195
196   // Pixel Format Attributes for the windowed NSOpenGLContext
197   attribs.push_back(NSOpenGLPFADoubleBuffer);
198
199   if (softwareGL) {
200     attribs.push_back(NSOpenGLPFARendererID);
201     attribs.push_back(kCGLRendererGenericFloatID);
202   }
203   else {
204     attribs.push_back(NSOpenGLPFAAccelerated);
205     attribs.push_back(NSOpenGLPFANoRecovery);
206   }
207
208   attribs.push_back(NSOpenGLPFAAllowOfflineRenderers);  // for automatic GPU switching
209
210   attribs.push_back(NSOpenGLPFADepthSize);
211   attribs.push_back((NSOpenGLPixelFormatAttribute)32);
212
213   if (stereoVisual)
214     attribs.push_back(NSOpenGLPFAStereo);
215
216   if (needAlpha) {
217     attribs.push_back(NSOpenGLPFAAlphaSize);
218     attribs.push_back((NSOpenGLPixelFormatAttribute)8);
219   }
220
221   if (needStencil) {
222     attribs.push_back(NSOpenGLPFAStencilSize);
223     attribs.push_back((NSOpenGLPixelFormatAttribute)8);
224   }
225
226   if (numOfAASamples > 0) {
227     // Multisample anti-aliasing
228     attribs.push_back(NSOpenGLPFAMultisample);
229
230     attribs.push_back(NSOpenGLPFASampleBuffers);
231     attribs.push_back((NSOpenGLPixelFormatAttribute)1);
232
233     attribs.push_back(NSOpenGLPFASamples);
234     attribs.push_back((NSOpenGLPixelFormatAttribute)numOfAASamples);
235   }
236
237   attribs.push_back((NSOpenGLPixelFormatAttribute)0);
238 }
239
240 // TODO(merwin): make this available to all platforms
241 static void getVersion(int *major, int *minor)
242 {
243 #if 1  // legacy GL
244   sscanf((const char *)glGetString(GL_VERSION), "%d.%d", major, minor);
245 #else  // 3.0+
246   glGetIntegerv(GL_MAJOR_VERSION, major);
247   glGetIntegerv(GL_MINOR_VERSION, minor);
248 #endif
249 }
250
251 GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
252 {
253   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
254
255   std::vector<NSOpenGLPixelFormatAttribute> attribs;
256   attribs.reserve(40);
257
258   NSOpenGLContext *prev_openGLContext = (m_openGLView) ? [m_openGLView openGLContext] : NULL;
259
260 #ifdef GHOST_OPENGL_ALPHA
261   static const bool needAlpha = true;
262 #else
263   static const bool needAlpha = false;
264 #endif
265
266 #ifdef GHOST_OPENGL_STENCIL
267   static const bool needStencil = true;
268 #else
269   static const bool needStencil = false;
270 #endif
271
272   static bool softwareGL = getenv("BLENDER_SOFTWAREGL");  // command-line argument would be better
273   GLint major = 0, minor = 0;
274   NSOpenGLPixelFormat *pixelFormat;
275   // TODO: keep pixel format for subsequent windows/contexts instead of recreating each time
276
277   makeAttribList(attribs,
278                  m_coreProfile,
279                  m_stereoVisual,
280                  m_numOfAASamples,
281                  needAlpha,
282                  needStencil,
283                  softwareGL);
284
285   pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
286
287   // Fall back to no multisampling if Antialiasing init failed
288   if (m_numOfAASamples > 0 && pixelFormat == nil) {
289     // XXX jwilkins: Does CGL only succeed when it makes an exact match on the number of samples?
290     // Does this need to explicitly try for a lesser match before giving up?
291     // (Now that I think about it, does WGL really require the code that it has for finding a lesser match?)
292
293     attribs.clear();
294     makeAttribList(attribs, m_coreProfile, m_stereoVisual, 0, needAlpha, needStencil, softwareGL);
295     pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
296   }
297
298   if (pixelFormat == nil)
299     goto error;
300
301   if (m_numOfAASamples > 0) {  //Set m_numOfAASamples to the actual value
302     GLint actualSamples;
303     [pixelFormat getValues:&actualSamples forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
304
305     if (m_numOfAASamples != (GHOST_TUns16)actualSamples) {
306       fprintf(
307           stderr,
308           "Warning! Unable to find a multisample pixel format that supports exactly %d samples. "
309           "Substituting one that uses %d samples.\n",
310           m_numOfAASamples,
311           actualSamples);
312
313       m_numOfAASamples = (GHOST_TUns16)actualSamples;
314     }
315   }
316
317   m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
318                                                shareContext:s_sharedOpenGLContext];
319   [pixelFormat release];
320
321   [m_openGLContext makeCurrentContext];
322
323   getVersion(&major, &minor);
324   if (m_debug) {
325     fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
326     fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
327   }
328
329   if (major < 2 || (major == 2 && minor < 1)) {
330     // fall back to software renderer if GL < 2.1
331     fprintf(stderr, "OpenGL 2.1 is not supported on your hardware, falling back to software");
332     softwareGL = true;
333
334     // discard hardware GL context
335     [NSOpenGLContext clearCurrentContext];
336     [m_openGLContext release];
337
338     // create software GL context
339     makeAttribList(attribs,
340                    m_coreProfile,
341                    m_stereoVisual,
342                    m_numOfAASamples,
343                    needAlpha,
344                    needStencil,
345                    softwareGL);
346     pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
347     m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
348                                                  shareContext:s_sharedOpenGLContext];
349     [pixelFormat release];
350
351     [m_openGLContext makeCurrentContext];
352
353     getVersion(&major, &minor);
354     if (m_debug) {
355       fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
356       fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
357     }
358   }
359
360 #ifdef GHOST_MULTITHREADED_OPENGL
361   //Switch openGL to multhreaded mode
362   if (CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine) == kCGLNoError)
363     if (m_debug)
364       fprintf(stderr, "\nSwitched OpenGL to multithreaded mode\n");
365 #endif
366
367 #ifdef GHOST_WAIT_FOR_VSYNC
368   {
369     GLint swapInt = 1;
370     /* wait for vsync, to avoid tearing artifacts */
371     [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
372   }
373 #endif
374
375   initContextGLEW();
376
377   if (m_openGLView) {
378     [m_openGLView setOpenGLContext:m_openGLContext];
379     [m_openGLContext setView:m_openGLView];
380   }
381
382   if (s_sharedCount == 0)
383     s_sharedOpenGLContext = m_openGLContext;
384
385   s_sharedCount++;
386
387   initClearGL();
388   [m_openGLContext flushBuffer];
389
390   [pool drain];
391
392   return GHOST_kSuccess;
393
394 error:
395
396   if (m_openGLView) {
397     [m_openGLView setOpenGLContext:prev_openGLContext];
398   }
399
400   [pixelFormat release];
401
402   [pool drain];
403
404   return GHOST_kFailure;
405 }
406
407 GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
408 {
409   m_openGLContext = NULL;
410   m_openGLView = NULL;
411
412   return GHOST_kSuccess;
413 }