| 1 | /* |
| 2 | SDL - Simple DirectMedia Layer |
| 3 | Copyright (C) 1997-2009 Sam Lantinga |
| 4 | |
| 5 | This library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Library General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2 of the License, or (at your option) any later version. |
| 9 | |
| 10 | This library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Library General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Library General Public |
| 16 | License along with this library; if not, write to the Free |
| 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 18 | |
| 19 | Sam Lantinga |
| 20 | slouken@libsdl.org |
| 21 | */ |
| 22 | #include "SDL_config.h" |
| 23 | |
| 24 | #include "SDL_QuartzVideo.h" |
| 25 | |
| 26 | /* |
| 27 | * GL_ARB_Multisample is supposed to be available in 10.1, according to Apple: |
| 28 | * |
| 29 | * http://developer.apple.com/graphicsimaging/opengl/extensions.html#GL_ARB_multisample |
| 30 | * |
| 31 | * ...but it isn't in the system headers, according to Sam: |
| 32 | * |
| 33 | * http://lists.libsdl.org/pipermail/sdl-libsdl.org/2003-December/039794.html |
| 34 | * |
| 35 | * These are normally enums and not #defines in the system headers. |
| 36 | * |
| 37 | * --ryan. |
| 38 | */ |
| 39 | #if (MAC_OS_X_VERSION_MAX_ALLOWED < 1020) |
| 40 | #define NSOpenGLPFASampleBuffers ((NSOpenGLPixelFormatAttribute) 55) |
| 41 | #define NSOpenGLPFASamples ((NSOpenGLPixelFormatAttribute) 56) |
| 42 | #endif |
| 43 | |
| 44 | #ifdef __powerpc__ /* we lost this in 10.6, which has no PPC support. */ |
| 45 | @implementation NSOpenGLContext (CGLContextAccess) |
| 46 | - (CGLContextObj) cglContext; |
| 47 | { |
| 48 | return _contextAuxiliary; |
| 49 | } |
| 50 | @end |
| 51 | CGLContextObj QZ_GetCGLContextObj(NSOpenGLContext *nsctx) |
| 52 | { |
| 53 | return [nsctx cglContext]; |
| 54 | } |
| 55 | #else |
| 56 | CGLContextObj QZ_GetCGLContextObj(NSOpenGLContext *nsctx) |
| 57 | { |
| 58 | return (CGLContextObj) [nsctx CGLContextObj]; |
| 59 | } |
| 60 | #endif |
| 61 | |
| 62 | |
| 63 | /* OpenGL helper functions (used internally) */ |
| 64 | |
| 65 | int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags) { |
| 66 | |
| 67 | NSOpenGLPixelFormatAttribute attr[32]; |
| 68 | NSOpenGLPixelFormat *fmt; |
| 69 | int i = 0; |
| 70 | int colorBits = bpp; |
| 71 | |
| 72 | /* if a GL library hasn't been loaded at this point, load the default. */ |
| 73 | if (!this->gl_config.driver_loaded) { |
| 74 | if (QZ_GL_LoadLibrary(this, NULL) == -1) |
| 75 | return 0; |
| 76 | } |
| 77 | |
| 78 | if ( flags & SDL_FULLSCREEN ) { |
| 79 | |
| 80 | attr[i++] = NSOpenGLPFAFullScreen; |
| 81 | } |
| 82 | /* In windowed mode, the OpenGL pixel depth must match device pixel depth */ |
| 83 | else if ( colorBits != device_bpp ) { |
| 84 | |
| 85 | colorBits = device_bpp; |
| 86 | } |
| 87 | |
| 88 | attr[i++] = NSOpenGLPFAColorSize; |
| 89 | attr[i++] = colorBits; |
| 90 | |
| 91 | attr[i++] = NSOpenGLPFADepthSize; |
| 92 | attr[i++] = this->gl_config.depth_size; |
| 93 | |
| 94 | if ( this->gl_config.double_buffer ) { |
| 95 | attr[i++] = NSOpenGLPFADoubleBuffer; |
| 96 | } |
| 97 | |
| 98 | if ( this->gl_config.stereo ) { |
| 99 | attr[i++] = NSOpenGLPFAStereo; |
| 100 | } |
| 101 | |
| 102 | if ( this->gl_config.stencil_size != 0 ) { |
| 103 | attr[i++] = NSOpenGLPFAStencilSize; |
| 104 | attr[i++] = this->gl_config.stencil_size; |
| 105 | } |
| 106 | |
| 107 | if ( (this->gl_config.accum_red_size + |
| 108 | this->gl_config.accum_green_size + |
| 109 | this->gl_config.accum_blue_size + |
| 110 | this->gl_config.accum_alpha_size) > 0 ) { |
| 111 | attr[i++] = NSOpenGLPFAAccumSize; |
| 112 | attr[i++] = this->gl_config.accum_red_size + this->gl_config.accum_green_size + this->gl_config.accum_blue_size + this->gl_config.accum_alpha_size; |
| 113 | } |
| 114 | |
| 115 | if ( this->gl_config.multisamplebuffers != 0 ) { |
| 116 | attr[i++] = NSOpenGLPFASampleBuffers; |
| 117 | attr[i++] = this->gl_config.multisamplebuffers; |
| 118 | } |
| 119 | |
| 120 | if ( this->gl_config.multisamplesamples != 0 ) { |
| 121 | attr[i++] = NSOpenGLPFASamples; |
| 122 | attr[i++] = this->gl_config.multisamplesamples; |
| 123 | attr[i++] = NSOpenGLPFANoRecovery; |
| 124 | } |
| 125 | |
| 126 | if ( this->gl_config.accelerated > 0 ) { |
| 127 | attr[i++] = NSOpenGLPFAAccelerated; |
| 128 | } |
| 129 | |
| 130 | attr[i++] = NSOpenGLPFAScreenMask; |
| 131 | attr[i++] = CGDisplayIDToOpenGLDisplayMask (display_id); |
| 132 | attr[i] = 0; |
| 133 | |
| 134 | fmt = [ [ NSOpenGLPixelFormat alloc ] initWithAttributes:attr ]; |
| 135 | if (fmt == nil) { |
| 136 | SDL_SetError ("Failed creating OpenGL pixel format"); |
| 137 | return 0; |
| 138 | } |
| 139 | |
| 140 | gl_context = [ [ NSOpenGLContext alloc ] initWithFormat:fmt |
| 141 | shareContext:nil]; |
| 142 | |
| 143 | [ fmt release ]; |
| 144 | |
| 145 | if (gl_context == nil) { |
| 146 | SDL_SetError ("Failed creating OpenGL context"); |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | /* Synchronize QZ_GL_SwapBuffers() to vertical retrace. |
| 151 | * (Apple's documentation is not completely clear about what this setting |
| 152 | * exactly does, IMHO - for a detailed explanation see |
| 153 | * http://lists.apple.com/archives/mac-opengl/2006/Jan/msg00080.html ) |
| 154 | */ |
| 155 | if ( this->gl_config.swap_control >= 0 ) { |
| 156 | long value; |
| 157 | value = this->gl_config.swap_control; |
| 158 | [ gl_context setValues: &value forParameter: NSOpenGLCPSwapInterval ]; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | * Wisdom from Apple engineer in reference to UT2003's OpenGL performance: |
| 163 | * "You are blowing a couple of the internal OpenGL function caches. This |
| 164 | * appears to be happening in the VAO case. You can tell OpenGL to up |
| 165 | * the cache size by issuing the following calls right after you create |
| 166 | * the OpenGL context. The default cache size is 16." --ryan. |
| 167 | */ |
| 168 | |
| 169 | #ifndef GLI_ARRAY_FUNC_CACHE_MAX |
| 170 | #define GLI_ARRAY_FUNC_CACHE_MAX 284 |
| 171 | #endif |
| 172 | |
| 173 | #ifndef GLI_SUBMIT_FUNC_CACHE_MAX |
| 174 | #define GLI_SUBMIT_FUNC_CACHE_MAX 280 |
| 175 | #endif |
| 176 | |
| 177 | { |
| 178 | long cache_max = 64; |
| 179 | CGLContextObj ctx = QZ_GetCGLContextObj(gl_context); |
| 180 | CGLSetParameter (ctx, GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max); |
| 181 | CGLSetParameter (ctx, GLI_ARRAY_FUNC_CACHE_MAX, &cache_max); |
| 182 | } |
| 183 | |
| 184 | /* End Wisdom from Apple Engineer section. --ryan. */ |
| 185 | |
| 186 | return 1; |
| 187 | } |
| 188 | |
| 189 | void QZ_TearDownOpenGL (_THIS) { |
| 190 | |
| 191 | [ NSOpenGLContext clearCurrentContext ]; |
| 192 | [ gl_context clearDrawable ]; |
| 193 | [ gl_context release ]; |
| 194 | } |
| 195 | |
| 196 | |
| 197 | /* SDL OpenGL functions */ |
| 198 | static const char *DEFAULT_OPENGL_LIB_NAME = |
| 199 | "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"; |
| 200 | |
| 201 | int QZ_GL_LoadLibrary (_THIS, const char *location) { |
| 202 | if ( gl_context != NULL ) { |
| 203 | SDL_SetError("OpenGL context already created"); |
| 204 | return -1; |
| 205 | } |
| 206 | |
| 207 | if (opengl_library != NULL) |
| 208 | SDL_UnloadObject(opengl_library); |
| 209 | |
| 210 | if (location == NULL) |
| 211 | location = DEFAULT_OPENGL_LIB_NAME; |
| 212 | |
| 213 | opengl_library = SDL_LoadObject(location); |
| 214 | if (opengl_library != NULL) { |
| 215 | this->gl_config.driver_loaded = 1; |
| 216 | return 0; |
| 217 | } |
| 218 | |
| 219 | this->gl_config.driver_loaded = 0; |
| 220 | return -1; |
| 221 | } |
| 222 | |
| 223 | void* QZ_GL_GetProcAddress (_THIS, const char *proc) { |
| 224 | return SDL_LoadFunction(opengl_library, proc); |
| 225 | } |
| 226 | |
| 227 | int QZ_GL_GetAttribute (_THIS, SDL_GLattr attrib, int* value) { |
| 228 | |
| 229 | GLenum attr = 0; |
| 230 | |
| 231 | QZ_GL_MakeCurrent (this); |
| 232 | |
| 233 | switch (attrib) { |
| 234 | case SDL_GL_RED_SIZE: attr = GL_RED_BITS; break; |
| 235 | case SDL_GL_BLUE_SIZE: attr = GL_BLUE_BITS; break; |
| 236 | case SDL_GL_GREEN_SIZE: attr = GL_GREEN_BITS; break; |
| 237 | case SDL_GL_ALPHA_SIZE: attr = GL_ALPHA_BITS; break; |
| 238 | case SDL_GL_DOUBLEBUFFER: attr = GL_DOUBLEBUFFER; break; |
| 239 | case SDL_GL_DEPTH_SIZE: attr = GL_DEPTH_BITS; break; |
| 240 | case SDL_GL_STENCIL_SIZE: attr = GL_STENCIL_BITS; break; |
| 241 | case SDL_GL_ACCUM_RED_SIZE: attr = GL_ACCUM_RED_BITS; break; |
| 242 | case SDL_GL_ACCUM_GREEN_SIZE: attr = GL_ACCUM_GREEN_BITS; break; |
| 243 | case SDL_GL_ACCUM_BLUE_SIZE: attr = GL_ACCUM_BLUE_BITS; break; |
| 244 | case SDL_GL_ACCUM_ALPHA_SIZE: attr = GL_ACCUM_ALPHA_BITS; break; |
| 245 | case SDL_GL_STEREO: attr = GL_STEREO; break; |
| 246 | case SDL_GL_MULTISAMPLEBUFFERS: attr = GL_SAMPLE_BUFFERS_ARB; break; |
| 247 | case SDL_GL_MULTISAMPLESAMPLES: attr = GL_SAMPLES_ARB; break; |
| 248 | case SDL_GL_BUFFER_SIZE: |
| 249 | { |
| 250 | GLint bits = 0; |
| 251 | GLint component; |
| 252 | |
| 253 | /* there doesn't seem to be a single flag in OpenGL for this! */ |
| 254 | glGetIntegerv (GL_RED_BITS, &component); bits += component; |
| 255 | glGetIntegerv (GL_GREEN_BITS,&component); bits += component; |
| 256 | glGetIntegerv (GL_BLUE_BITS, &component); bits += component; |
| 257 | glGetIntegerv (GL_ALPHA_BITS, &component); bits += component; |
| 258 | |
| 259 | *value = bits; |
| 260 | return 0; |
| 261 | } |
| 262 | case SDL_GL_ACCELERATED_VISUAL: |
| 263 | { |
| 264 | long val; |
| 265 | /* FIXME: How do we get this information here? |
| 266 | [fmt getValues: &val forAttribute: NSOpenGLPFAAccelerated attr forVirtualScreen: 0]; |
| 267 | */ |
| 268 | val = (this->gl_config.accelerated != 0);; |
| 269 | *value = val; |
| 270 | return 0; |
| 271 | } |
| 272 | case SDL_GL_SWAP_CONTROL: |
| 273 | { |
| 274 | long val; |
| 275 | [ gl_context getValues: &val forParameter: NSOpenGLCPSwapInterval ]; |
| 276 | *value = val; |
| 277 | return 0; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | glGetIntegerv (attr, (GLint *)value); |
| 282 | return 0; |
| 283 | } |
| 284 | |
| 285 | int QZ_GL_MakeCurrent (_THIS) { |
| 286 | [ gl_context makeCurrentContext ]; |
| 287 | return 0; |
| 288 | } |
| 289 | |
| 290 | void QZ_GL_SwapBuffers (_THIS) { |
| 291 | [ gl_context flushBuffer ]; |
| 292 | } |