gfxGLDevice.mac.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. // Don't include Apple's GL header
  23. #define __gl_h_
  24. // Include our GL header before Apple headers.
  25. #include "gfx/gl/ggl/ggl.h"
  26. #include "platform/tmm_off.h"
  27. #include <Cocoa/Cocoa.h>
  28. #include <OpenGL/OpenGL.h>
  29. #include "gfx/gl/gfxGLDevice.h"
  30. #include "platform/tmm_on.h"
  31. #include "gfx/gl/gfxGLTextureTarget.h"
  32. #include "gfx/gl/gfxGLCardProfiler.h"
  33. #include "gfx/gl/gfxGLAppleFence.h"
  34. #include "gfx/gl/gfxGLWindowTarget.h"
  35. #include "platformMac/macGLUtils.h"
  36. #include "windowManager/mac/macWindow.h"
  37. extern void loadGLCore();
  38. extern void loadGLExtensions(void* context);
  39. static String _getRendererForDisplay(CGDirectDisplayID display)
  40. {
  41. Vector<NSOpenGLPixelFormatAttribute> attributes = _createStandardPixelFormatAttributesForDisplay(display);
  42. NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()];
  43. AssertFatal(fmt, "_getRendererForDisplay - Unable to create a pixel format object");
  44. NSOpenGLContext* ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil];
  45. AssertFatal(ctx, "_getRendererForDisplay - Unable to create an OpenGL context");
  46. // Save the current context, just in case
  47. NSOpenGLContext* currCtx = [NSOpenGLContext currentContext];
  48. [ctx makeCurrentContext];
  49. // CodeReview [ags 12/19/07] This is a hack. We should call loadGLCore somewhere else, before we reach here.
  50. // On Macs we can safely assume access to the OpenGL framework at anytime, perhaps this should go in main()?
  51. loadGLCore();
  52. // get the renderer string
  53. String ret((const char*)glGetString(GL_RENDERER));
  54. // Restore our old context, release the context and pixel format.
  55. [currCtx makeCurrentContext];
  56. [ctx release];
  57. [fmt release];
  58. return ret;
  59. }
  60. static void _createInitialContextAndFormat(void* &ctx, void* &fmt)
  61. {
  62. AssertFatal(!fmt && !ctx, "_createInitialContextAndFormat - Already created initial context and format");
  63. fmt = _createStandardPixelFormat();
  64. AssertFatal(fmt, "_createInitialContextAndFormat - Unable to create an OpenGL pixel format");
  65. ctx = [[NSOpenGLContext alloc] initWithFormat: (NSOpenGLPixelFormat*)fmt shareContext: nil];
  66. AssertFatal(ctx, "_createInitialContextAndFormat - Unable to create an OpenGL context");
  67. }
  68. static NSOpenGLContext* _createContextForWindow(PlatformWindow *window, void* &context, void* &pixelFormat)
  69. {
  70. NSOpenGLView* view = (NSOpenGLView*)window->getPlatformDrawable();
  71. AssertFatal([view isKindOfClass:[NSOpenGLView class]], avar("_createContextForWindow - Supplied a %s instead of a NSOpenGLView", [[view className] UTF8String]));
  72. NSOpenGLContext* ctx = NULL;
  73. if(!context || !pixelFormat)
  74. {
  75. // Create the initial opengl context that the device and the first window will hold.
  76. _createInitialContextAndFormat(context, pixelFormat);
  77. ctx = (NSOpenGLContext*)context;
  78. }
  79. else
  80. {
  81. // Create a context which shares its resources with the device's initial context
  82. ctx = [[NSOpenGLContext alloc] initWithFormat: (NSOpenGLPixelFormat*)pixelFormat shareContext: (NSOpenGLContext*)context];
  83. AssertFatal(ctx, "Unable to create a shared OpenGL context");
  84. }
  85. [view setPixelFormat: (NSOpenGLPixelFormat*)pixelFormat];
  86. [view setOpenGLContext: ctx];
  87. return ctx;
  88. }
  89. void GFXGLDevice::init( const GFXVideoMode &mode, PlatformWindow *window )
  90. {
  91. if(mInitialized)
  92. return;
  93. NSOpenGLContext* ctx = _createContextForWindow(window, mContext, mPixelFormat);
  94. [ctx makeCurrentContext];
  95. loadGLCore();
  96. loadGLExtensions(ctx);
  97. initGLState();
  98. mInitialized = true;
  99. deviceInited();
  100. }
  101. void GFXGLDevice::enumerateAdapters( Vector<GFXAdapter*> &adapterList )
  102. {
  103. GFXAdapter *toAdd;
  104. Vector<GFXVideoMode> videoModes;
  105. CGDirectDisplayID display = CGMainDisplayID();
  106. // Enumerate all available resolutions:
  107. NSArray* modeArray = (NSArray*)CGDisplayAvailableModes(display);
  108. GFXVideoMode vmAdd;
  109. NSEnumerator* enumerator = [modeArray objectEnumerator];
  110. NSDictionary* mode;
  111. while((mode = [enumerator nextObject]))
  112. {
  113. vmAdd.resolution.x = [[mode valueForKey:@"Width"] intValue];
  114. vmAdd.resolution.y = [[mode valueForKey:@"Height"] intValue];
  115. vmAdd.bitDepth = [[mode valueForKey:@"BitsPerPixel"] intValue];
  116. vmAdd.refreshRate = [[mode valueForKey:@"RefreshRate"] intValue];
  117. vmAdd.fullScreen = false;
  118. // skip if mode claims to be 8bpp
  119. if( vmAdd.bitDepth == 8 )
  120. continue;
  121. // Only add this resolution if it is not already in the list:
  122. bool alreadyInList = false;
  123. for(Vector<GFXVideoMode>::iterator i = videoModes.begin(); i != videoModes.end(); i++)
  124. {
  125. if(vmAdd == *i)
  126. {
  127. alreadyInList = true;
  128. break;
  129. }
  130. }
  131. if( !alreadyInList )
  132. {
  133. videoModes.push_back( vmAdd );
  134. }
  135. }
  136. // Get number of displays
  137. CGDisplayCount dispCnt;
  138. CGGetActiveDisplayList(0, NULL, &dispCnt);
  139. // Take advantage of GNU-C
  140. CGDirectDisplayID displays[dispCnt];
  141. CGGetActiveDisplayList(dispCnt, displays, &dispCnt);
  142. for(U32 i = 0; i < dispCnt; i++)
  143. {
  144. toAdd = new GFXAdapter();
  145. toAdd->mType = OpenGL;
  146. toAdd->mIndex = (U32)displays[i];
  147. toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance;
  148. String renderer = _getRendererForDisplay(displays[i]);
  149. AssertFatal(dStrlen(renderer.c_str()) < GFXAdapter::MaxAdapterNameLen, "GFXGLDevice::enumerateAdapter - renderer name too long, increae the size of GFXAdapter::MaxAdapterNameLen (or use String!)");
  150. dStrncpy(toAdd->mName, renderer.c_str(), GFXAdapter::MaxAdapterNameLen);
  151. adapterList.push_back(toAdd);
  152. for (S32 j = videoModes.size() - 1 ; j >= 0 ; j--)
  153. toAdd->mAvailableModes.push_back(videoModes[j]);
  154. }
  155. }
  156. void GFXGLDevice::enumerateVideoModes()
  157. {
  158. mVideoModes.clear();
  159. CGDirectDisplayID display = CGMainDisplayID();
  160. // Enumerate all available resolutions:
  161. NSArray* modeArray = (NSArray*)CGDisplayAvailableModes(display);
  162. GFXVideoMode toAdd;
  163. NSEnumerator* enumerator = [modeArray objectEnumerator];
  164. NSDictionary* mode;
  165. while((mode = [enumerator nextObject]))
  166. {
  167. toAdd.resolution.x = [[mode valueForKey:@"Width"] intValue];
  168. toAdd.resolution.y = [[mode valueForKey:@"Height"] intValue];
  169. toAdd.bitDepth = [[mode valueForKey:@"BitsPerPixel"] intValue];
  170. toAdd.refreshRate = [[mode valueForKey:@"RefreshRate"] intValue];
  171. toAdd.fullScreen = false;
  172. // skip if mode claims to be 8bpp
  173. if( toAdd.bitDepth == 8 )
  174. continue;
  175. // Only add this resolution if it is not already in the list:
  176. bool alreadyInList = false;
  177. for(Vector<GFXVideoMode>::iterator i = mVideoModes.begin(); i != mVideoModes.end(); i++)
  178. {
  179. if(toAdd == *i)
  180. {
  181. alreadyInList = true;
  182. break;
  183. }
  184. }
  185. if( !alreadyInList )
  186. {
  187. mVideoModes.push_back( toAdd );
  188. }
  189. }
  190. }
  191. bool GFXGLDevice::beginSceneInternal()
  192. {
  193. // Nothing to do here for GL.
  194. mCanCurrentlyRender = true;
  195. return true;
  196. }
  197. U32 GFXGLDevice::getTotalVideoMemory()
  198. {
  199. // Convert our adapterIndex (i.e. our CGDirectDisplayID) into an OpenGL display mask
  200. GLuint display = CGDisplayIDToOpenGLDisplayMask((CGDirectDisplayID)mAdapterIndex);
  201. CGLRendererInfoObj rend;
  202. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  203. GLint nrend;
  204. #else
  205. long int nrend;
  206. #endif
  207. CGLQueryRendererInfo(display, &rend, &nrend);
  208. if(!nrend)
  209. return 0;
  210. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  211. GLint vidMem;
  212. #else
  213. long int vidMem;
  214. #endif
  215. CGLDescribeRenderer(rend, 0, kCGLRPVideoMemory, &vidMem);
  216. CGLDestroyRendererInfo(rend);
  217. // convert bytes to MB
  218. vidMem /= (1024 * 1024);
  219. return vidMem;
  220. }
  221. //-----------------------------------------------------------------------------
  222. GFXWindowTarget *GFXGLDevice::allocWindowTarget(PlatformWindow *window)
  223. {
  224. void *ctx = NULL;
  225. // Init if needed, or create a new context
  226. if(!mInitialized)
  227. init(window->getVideoMode(), window);
  228. else
  229. ctx = _createContextForWindow(window, mContext, mPixelFormat);
  230. // Allocate the wintarget and create a new context.
  231. GFXGLWindowTarget *gwt = new GFXGLWindowTarget(window, this);
  232. gwt->mContext = ctx ? ctx : mContext;
  233. // And return...
  234. return gwt;
  235. }
  236. GFXFence* GFXGLDevice::_createPlatformSpecificFence()
  237. {
  238. if(!mCardProfiler->queryProfile("GL::APPLE::suppFence"))
  239. return NULL;
  240. return new GFXGLAppleFence(this);
  241. }
  242. void GFXGLWindowTarget::makeActive()
  243. {
  244. // If we're supposed to be running fullscreen, but haven't yet set up for it,
  245. // do it now.
  246. if( !mFullscreenContext && mWindow->getVideoMode().fullScreen )
  247. {
  248. static_cast< GFXGLDevice* >( mDevice )->zombify();
  249. _setupNewMode();
  250. }
  251. mFullscreenContext ? [(NSOpenGLContext*)mFullscreenContext makeCurrentContext] : [(NSOpenGLContext*)mContext makeCurrentContext];
  252. }
  253. bool GFXGLWindowTarget::present()
  254. {
  255. GFX->updateStates();
  256. mFullscreenContext ? [(NSOpenGLContext*)mFullscreenContext flushBuffer] : [(NSOpenGLContext*)mContext flushBuffer];
  257. return true;
  258. }
  259. void GFXGLWindowTarget::_teardownCurrentMode()
  260. {
  261. GFX->setActiveRenderTarget(this);
  262. static_cast<GFXGLDevice*>(mDevice)->zombify();
  263. if(mFullscreenContext)
  264. {
  265. [NSOpenGLContext clearCurrentContext];
  266. [(NSOpenGLContext*)mFullscreenContext clearDrawable];
  267. }
  268. }
  269. void GFXGLWindowTarget::_setupNewMode()
  270. {
  271. if(mWindow->getVideoMode().fullScreen && !mFullscreenContext)
  272. {
  273. // We have to create a fullscreen context.
  274. Vector<NSOpenGLPixelFormatAttribute> attributes = _beginPixelFormatAttributesForDisplay(static_cast<MacWindow*>(mWindow)->getDisplay());
  275. _addColorAlphaDepthStencilAttributes(attributes, 24, 8, 24, 8);
  276. _addFullscreenAttributes(attributes);
  277. _endAttributeList(attributes);
  278. NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()];
  279. mFullscreenContext = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil];
  280. [fmt release];
  281. [(NSOpenGLContext*)mFullscreenContext setFullScreen];
  282. [(NSOpenGLContext*)mFullscreenContext makeCurrentContext];
  283. // Restore resources in new context
  284. static_cast<GFXGLDevice*>(mDevice)->resurrect();
  285. GFX->updateStates(true);
  286. }
  287. else if(!mWindow->getVideoMode().fullScreen && mFullscreenContext)
  288. {
  289. [(NSOpenGLContext*)mFullscreenContext release];
  290. mFullscreenContext = NULL;
  291. [(NSOpenGLContext*)mContext makeCurrentContext];
  292. GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(0, 0, 0), 1.0f, 0);
  293. static_cast<GFXGLDevice*>(mDevice)->resurrect();
  294. GFX->updateStates(true);
  295. }
  296. }