DisplayManager.mm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. #include "DisplayManager.h"
  2. #include "EAGLContextHelper.h"
  3. #include "GlesHelper.h"
  4. #include "UI/UnityView.h"
  5. #include "UnityAppController.h"
  6. #include "UI/UnityAppController+ViewHandling.h"
  7. #import <QuartzCore/QuartzCore.h>
  8. #import <CoreGraphics/CoreGraphics.h>
  9. #include <OpenGLES/ES2/gl.h>
  10. #include <OpenGLES/ES2/glext.h>
  11. #include "UnityMetalSupport.h"
  12. static DisplayManager* _DisplayManager = nil;
  13. @interface DisplayConnection ()
  14. @property (readonly, nonatomic) UnityDisplaySurfaceGLES* surfaceGLES;
  15. @property (readonly, nonatomic) UnityDisplaySurfaceMTL* surfaceMTL;
  16. @end
  17. @implementation DisplayConnection
  18. {
  19. BOOL _needRecreateSurface;
  20. CGSize _requestedRenderingSize;
  21. UIScreen* _screen;
  22. UIWindow* _window;
  23. UIView* _view;
  24. CGSize _screenSize;
  25. UnityDisplaySurfaceBase* _surface;
  26. }
  27. @synthesize screen = _screen;
  28. @synthesize window = _window;
  29. @synthesize view = _view;
  30. @synthesize screenSize = _screenSize;
  31. @synthesize surface = _surface;
  32. @synthesize surfaceGLES;
  33. - (UnityDisplaySurfaceGLES*)surfaceGLES { assert(_surface->api != apiMetal); return (UnityDisplaySurfaceGLES*)_surface; }
  34. @synthesize surfaceMTL;
  35. - (UnityDisplaySurfaceMTL*)surfaceMTL { assert(_surface->api == apiMetal); return (UnityDisplaySurfaceMTL*)_surface; }
  36. - (id)init:(UIScreen*)targetScreen
  37. {
  38. if ((self = [super init]))
  39. {
  40. self->_screen = targetScreen;
  41. #if !PLATFORM_TVOS
  42. targetScreen.currentMode = targetScreen.preferredMode;
  43. #endif
  44. // UIScreenOverscanCompensationNone == UIScreenOverscanCompensationInsetApplicationFrame so it will work woth pre-ios9 just fine
  45. targetScreen.overscanCompensation = UIScreenOverscanCompensationNone;
  46. self->_screenSize = targetScreen.currentMode.size;
  47. self->_needRecreateSurface = NO;
  48. self->_requestedRenderingSize = CGSizeMake(-1, -1);
  49. }
  50. return self;
  51. }
  52. - (void)createWithWindow:(UIWindow*)window andView:(UIView*)view
  53. {
  54. _window = window;
  55. _view = view;
  56. CGSize layerSize = _view.layer.bounds.size;
  57. _screenSize = CGSizeMake(roundf(layerSize.width) * _view.contentScaleFactor, roundf(layerSize.height) * _view.contentScaleFactor);
  58. }
  59. - (void)createView:(BOOL)useForRendering
  60. {
  61. [self createView: useForRendering showRightAway: YES];
  62. }
  63. - (void)createView:(BOOL)useForRendering showRightAway:(BOOL)showRightAway;
  64. {
  65. NSAssert(_screen != [UIScreen mainScreen], @"DisplayConnection for mainScreen should be created with createWithWindow:andView:");
  66. if (_view == nil)
  67. {
  68. UIWindow* window = [[UIWindow alloc] initWithFrame: _screen.bounds];
  69. window.screen = _screen;
  70. UIView* view = [(useForRendering ? [UnityRenderingView alloc] : [UIView alloc]) initWithFrame: _screen.bounds];
  71. view.contentScaleFactor = UnityScreenScaleFactor(_screen);
  72. [self createWithWindow: window andView: view];
  73. if (showRightAway)
  74. {
  75. [window addSubview: view];
  76. [window makeKeyAndVisible];
  77. }
  78. }
  79. }
  80. - (void)shouldShowWindow:(BOOL)show
  81. {
  82. _window.hidden = show ? NO : YES;
  83. _window.screen = show ? _screen : nil;
  84. }
  85. - (void)initRendering
  86. {
  87. if (_surface == 0)
  88. {
  89. int api = UnitySelectedRenderingAPI();
  90. if (api == apiMetal)
  91. {
  92. UnityDisplaySurfaceMTL* surf = new UnityDisplaySurfaceMTL();
  93. surf->layer = (CAMetalLayer*)_view.layer;
  94. surf->device = UnityGetMetalDevice();
  95. surf->commandQueue = [surf->device newCommandQueue];
  96. surf->drawableCommandQueue = [surf->device newCommandQueue];
  97. _surface = surf;
  98. }
  99. else
  100. {
  101. UnityDisplaySurfaceGLES* surf = new UnityDisplaySurfaceGLES();
  102. surf->layer = (CAEAGLLayer*)_view.layer;
  103. surf->context = UnityCreateContextEAGL(UnityGetDataContextGLES(), 0);
  104. _surface = surf;
  105. }
  106. _surface->api = api;
  107. }
  108. }
  109. - (void)recreateSurface:(RenderingSurfaceParams)params
  110. {
  111. [self initRendering];
  112. CGSize layerSize = _view.layer.bounds.size;
  113. float scale = _view.contentScaleFactor;
  114. _screenSize = CGSizeMake(layerSize.width * scale, layerSize.height * scale);
  115. bool systemSizeChanged = _surface->systemW != _screenSize.width || _surface->systemH != _screenSize.height;
  116. bool msaaChanged = _supportsMSAA && (_surface->msaaSamples != params.msaaSampleCount);
  117. bool depthfmtChanged = _surface->disableDepthAndStencil != params.disableDepthAndStencil;
  118. bool cvCacheChanged = _surface->useCVTextureCache != params.useCVTextureCache;
  119. bool renderSizeChanged = false;
  120. if ((params.renderW > 0 && _surface->targetW != params.renderW) // changed resolution
  121. || (params.renderH > 0 && _surface->targetH != params.renderH) // changed resolution
  122. || (params.renderW <= 0 && _surface->targetW != _surface->systemW) // no longer need intermediate fb
  123. || (params.renderH <= 0 && _surface->targetH != _surface->systemH) // no longer need intermediate fb
  124. )
  125. {
  126. renderSizeChanged = true;
  127. }
  128. bool recreateSystemSurface = systemSizeChanged;
  129. bool recreateRenderingSurface = systemSizeChanged || renderSizeChanged || msaaChanged || cvCacheChanged;
  130. bool recreateDepthbuffer = systemSizeChanged || renderSizeChanged || msaaChanged || depthfmtChanged;
  131. _surface->disableDepthAndStencil = params.disableDepthAndStencil;
  132. _surface->systemW = _screenSize.width;
  133. _surface->systemH = _screenSize.height;
  134. _surface->targetW = params.renderW > 0 ? params.renderW : _surface->systemW;
  135. _surface->targetH = params.renderH > 0 ? params.renderH : _surface->systemH;
  136. _surface->msaaSamples = _supportsMSAA ? params.msaaSampleCount : 0;
  137. _surface->srgb = params.srgb;
  138. _surface->wideColor = params.wideColor;
  139. _surface->useCVTextureCache = params.useCVTextureCache;
  140. if (UnitySelectedRenderingAPI() == apiMetal)
  141. {
  142. recreateSystemSurface = recreateSystemSurface || self.surfaceMTL->systemColorRB == 0;
  143. self.surfaceMTL->framebufferOnly = params.metalFramebufferOnly;
  144. }
  145. else
  146. recreateSystemSurface = recreateSystemSurface || self.surfaceGLES->systemFB == 0;
  147. if (recreateSystemSurface)
  148. CreateSystemRenderingSurface(_surface);
  149. if (recreateRenderingSurface)
  150. CreateRenderingSurface(_surface);
  151. if (recreateDepthbuffer)
  152. CreateSharedDepthbuffer(_surface);
  153. if (recreateSystemSurface || recreateRenderingSurface)
  154. CreateUnityRenderBuffers(_surface);
  155. UnityInvalidateDisplayDataCache((__bridge void*)_screen);
  156. }
  157. - (void)dealloc
  158. {
  159. if (_surface)
  160. {
  161. DestroySystemRenderingSurface(_surface);
  162. DestroyRenderingSurface(_surface);
  163. DestroySharedDepthbuffer(_surface);
  164. DestroyUnityRenderBuffers(_surface);
  165. if (UnitySelectedRenderingAPI() == apiMetal)
  166. {
  167. self.surfaceMTL->device = nil;
  168. self.surfaceMTL->layer = nil;
  169. }
  170. else
  171. {
  172. self.surfaceGLES->context = nil;
  173. self.surfaceGLES->layer = nil;
  174. }
  175. }
  176. delete _surface;
  177. _surface = 0;
  178. _view = nil;
  179. _window = nil;
  180. }
  181. - (void)present
  182. {
  183. PreparePresent(self.surface);
  184. Present(self.surface);
  185. if (_needRecreateSurface)
  186. {
  187. RenderingSurfaceParams params =
  188. {
  189. _surface->msaaSamples, (int)_requestedRenderingSize.width, (int)_requestedRenderingSize.height,
  190. _surface->srgb,
  191. _surface->wideColor,
  192. false,
  193. _surface->disableDepthAndStencil, self.surface->cvTextureCache != 0
  194. };
  195. [self recreateSurface: params];
  196. _needRecreateSurface = NO;
  197. _requestedRenderingSize = CGSizeMake(_surface->targetW, _surface->targetH);
  198. }
  199. }
  200. - (void)requestRenderingResolution:(CGSize)res
  201. {
  202. _requestedRenderingSize = res;
  203. _needRecreateSurface = YES;
  204. }
  205. @end
  206. @implementation DisplayManager
  207. {
  208. NSMapTable* _displayConnection;
  209. DisplayConnection* _mainDisplay;
  210. }
  211. @synthesize mainDisplay = _mainDisplay;
  212. @synthesize displayCount;
  213. - (NSUInteger)displayCount { return _displayConnection.count; }
  214. - (void)registerScreen:(UIScreen*)screen
  215. {
  216. [_displayConnection setObject: [[DisplayConnection alloc] init: screen] forKey: screen];
  217. }
  218. - (id)init
  219. {
  220. if ((self = [super init]))
  221. {
  222. [[NSNotificationCenter defaultCenter] addObserver: self
  223. selector: @selector(screenDidConnect:)
  224. name: UIScreenDidConnectNotification
  225. object: nil
  226. ];
  227. [[NSNotificationCenter defaultCenter] addObserver: self
  228. selector: @selector(screenDidDisconnect:)
  229. name: UIScreenDidDisconnectNotification
  230. object: nil
  231. ];
  232. _displayConnection = [NSMapTable
  233. mapTableWithKeyOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  234. valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  235. ];
  236. for (UIScreen* screen in[UIScreen screens])
  237. [self registerScreen: screen];
  238. _mainDisplay = self[[UIScreen mainScreen]];
  239. }
  240. return self;
  241. }
  242. - (BOOL)displayAvailable:(UIScreen*)targetScreen;
  243. {
  244. return self[targetScreen] != nil;
  245. }
  246. - (DisplayConnection*)display:(UIScreen*)targetScreen
  247. {
  248. return self[targetScreen];
  249. }
  250. - (id)objectForKeyedSubscript:(id)key
  251. {
  252. NSAssert([key isKindOfClass: [UIScreen class]], @"DisplayManager allows only UIScreen as subscript");
  253. return [_displayConnection objectForKey: (UIScreen*)key];
  254. }
  255. - (void)updateDisplayListInUnity
  256. {
  257. // [UIScreen screens] might be out of sync to what is indicated to the
  258. // application via UIScreenDidConnectNotification and UIScreenDidDisconnectNotification
  259. // notifications. For example, on disconnection [UIScreen screens] might still
  260. // have the screen that the display manager no longer knows about.
  261. const unsigned MAX_DISPLAYS_SUPPORTED = 8; // sync this to the value on Unity side
  262. void* screens[MAX_DISPLAYS_SUPPORTED];
  263. unsigned screenCount = 0;
  264. UIScreen* mainScreen = [UIScreen mainScreen];
  265. screens[screenCount++] = (__bridge void*)mainScreen;
  266. for (UIScreen* screen in _displayConnection)
  267. {
  268. if (screen == mainScreen)
  269. continue;
  270. screens[screenCount++] = (__bridge void*)screen;
  271. }
  272. UnityUpdateDisplayList(screens, screenCount);
  273. }
  274. - (void)enumerateDisplaysWithBlock:(void (^)(DisplayConnection* conn))block
  275. {
  276. for (UIScreen* screen in _displayConnection)
  277. {
  278. // if we want simple mirroring unity wont create rendering backing for display
  279. // in that case we dont want to touch Display
  280. DisplayConnection* conn = [_displayConnection objectForKey: screen];
  281. if (conn.surface != nil)
  282. block(conn);
  283. }
  284. }
  285. - (void)startFrameRendering
  286. {
  287. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  288. StartFrameRendering(conn.surface);
  289. }];
  290. }
  291. - (void)endFrameRendering
  292. {
  293. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  294. EndFrameRendering(conn.surface);
  295. }];
  296. }
  297. - (void)present
  298. {
  299. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  300. [conn present];
  301. }];
  302. }
  303. - (void)screenDidConnect:(NSNotification*)notification
  304. {
  305. [self registerScreen: (UIScreen*)[notification object]];
  306. [self updateDisplayListInUnity];
  307. }
  308. - (void)screenDidDisconnect:(NSNotification*)notification
  309. {
  310. UIScreen* screen = (UIScreen*)[notification object];
  311. DisplayConnection* conn = (DisplayConnection*)self[screen];
  312. if (conn != nil && conn.surface != nil)
  313. UnityDisableRenderBuffers(conn.surface->unityColorBuffer, conn.surface->unityDepthBuffer);
  314. [_displayConnection removeObjectForKey: screen];
  315. conn = nil;
  316. [self updateDisplayListInUnity];
  317. }
  318. + (void)Initialize
  319. {
  320. NSAssert(_DisplayManager == nil, @"[DisplayManager Initialize] called after creating handler");
  321. if (!_DisplayManager)
  322. _DisplayManager = [[DisplayManager alloc] init];
  323. }
  324. + (DisplayManager*)Instance
  325. {
  326. if (!_DisplayManager)
  327. _DisplayManager = [[DisplayManager alloc] init];
  328. return _DisplayManager;
  329. }
  330. @end
  331. //==============================================================================
  332. //
  333. // Unity Interface:
  334. static void EnsureDisplayIsInited(DisplayConnection* conn)
  335. {
  336. // main screen view will be created in AppController,
  337. // so we can assume that we need to init secondary display from script
  338. // meaning: gles + show right away
  339. if (conn.view == nil)
  340. [conn createView: YES];
  341. int api = UnitySelectedRenderingAPI();
  342. bool needRecreate = false;
  343. if (conn.surface == 0)
  344. needRecreate = true;
  345. else if (api == apiMetal)
  346. needRecreate = conn.surfaceMTL->layer == nil;
  347. else
  348. needRecreate = conn.surfaceGLES->systemFB == 0;
  349. if (needRecreate)
  350. {
  351. RenderingSurfaceParams params = {0, -1, -1, 0, 0, 0, UnityDisableDepthAndStencilBuffers(), false};
  352. [conn recreateSurface: params];
  353. {
  354. DisplayConnection* main = [DisplayManager Instance].mainDisplay;
  355. if (api != apiMetal)
  356. [EAGLContext setCurrentContext: UnityGetMainScreenContextGLES()];
  357. StartFrameRendering(main.surface);
  358. }
  359. }
  360. }
  361. #if !PLATFORM_TVOS
  362. extern "C" int UnityDisplayManager_DisplayCount()
  363. {
  364. return (int)[DisplayManager Instance].displayCount;
  365. }
  366. extern "C" bool UnityDisplayManager_DisplayAvailable(void* nativeDisplay)
  367. {
  368. return [[DisplayManager Instance] displayAvailable: (__bridge UIScreen*)nativeDisplay];
  369. }
  370. extern "C" bool UnityDisplayManager_DisplayActive(void* nativeDisplay)
  371. {
  372. return UnityDisplayManager_DisplayAvailable(nativeDisplay);
  373. }
  374. extern "C" void UnityDisplayManager_DisplaySystemResolution(void* nativeDisplay, int* w, int* h)
  375. {
  376. if (nativeDisplay == NULL)
  377. return;
  378. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  379. EnsureDisplayIsInited(conn);
  380. *w = (int)conn.surface->systemW;
  381. *h = (int)conn.surface->systemH;
  382. }
  383. extern "C" void UnityDisplayManager_DisplayRenderingResolution(void* nativeDisplay, int* w, int* h)
  384. {
  385. if (nativeDisplay == NULL)
  386. return;
  387. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  388. EnsureDisplayIsInited(conn);
  389. *w = (int)conn.surface->targetW;
  390. *h = (int)conn.surface->targetH;
  391. }
  392. extern "C" void UnityDisplayManager_DisplayRenderingBuffers(void* nativeDisplay, void** colorBuffer, void** depthBuffer)
  393. {
  394. if (nativeDisplay == NULL)
  395. return;
  396. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  397. EnsureDisplayIsInited(conn);
  398. if (colorBuffer)
  399. *colorBuffer = conn.surface->unityColorBuffer;
  400. if (depthBuffer)
  401. *depthBuffer = conn.surface->unityDepthBuffer;
  402. }
  403. extern "C" void UnityDisplayManager_SetRenderingResolution(void* nativeDisplay, int w, int h)
  404. {
  405. if (nativeDisplay == NULL)
  406. return;
  407. UIScreen* screen = (__bridge UIScreen*)nativeDisplay;
  408. DisplayConnection* conn = [DisplayManager Instance][screen];
  409. EnsureDisplayIsInited(conn);
  410. if (screen == [UIScreen mainScreen])
  411. UnityRequestRenderingResolution(w, h);
  412. else
  413. [conn requestRenderingResolution: CGSizeMake(w, h)];
  414. }
  415. extern "C" void UnityDisplayManager_ShouldShowWindowOnDisplay(void* nativeDisplay, bool show)
  416. {
  417. if (nativeDisplay == NULL)
  418. return;
  419. UIScreen* screen = (__bridge UIScreen*)nativeDisplay;
  420. DisplayConnection* conn = [DisplayManager Instance][screen];
  421. EnsureDisplayIsInited(conn);
  422. if (screen != [UIScreen mainScreen])
  423. [conn shouldShowWindow: show];
  424. }
  425. extern "C" int UnityDisplayManager_PrimaryDisplayIndex()
  426. {
  427. return 0;
  428. }
  429. #endif
  430. extern "C" EAGLContext* UnityGetMainScreenContextGLES()
  431. {
  432. return GetMainDisplay().surfaceGLES->context;
  433. }
  434. extern "C" EAGLContext* UnityGetContextEAGL()
  435. {
  436. return GetMainDisplay().surfaceGLES->context;
  437. }
  438. extern "C" float UnityScreenScaleFactor(UIScreen* screen)
  439. {
  440. // NOTE: All views handled by Unity have their contentScaleFactor initialized
  441. // to value returned by this function.
  442. // we should query nativeScale if available to get the true device resolution
  443. // this way we avoid unnecessarily large frame buffers and downscaling.
  444. // e.g. iPhone 6+ pretends to be a x3 device, while its physical screen is x2.6 something.
  445. // it is available on iOS 8.0+, tvOS 9.0+
  446. // for older ios versions we add this selector ourselves (AddNewAPIImplIfNeeded in UnityAppController.mm)
  447. // On AppleTV screen.nativeScale returns NaN when device is in sleep mode and starting from tvOS 10 (?) it returns 0.
  448. if (isnan(screen.nativeScale) || (screen.nativeScale == 0))
  449. return 1.0f;
  450. else
  451. {
  452. float scalingFactor = UnityCalculateScalingFactorFromTargetDPI(screen);
  453. if (scalingFactor > 0.0f)
  454. return scalingFactor;
  455. else
  456. return screen.nativeScale;
  457. }
  458. return screen.scale;
  459. }
  460. extern "C" int UnityMainScreenRefreshRate()
  461. {
  462. if ([[UIScreen mainScreen] respondsToSelector: @selector(maximumFramesPerSecond)])
  463. return (int)[UIScreen mainScreen].maximumFramesPerSecond;
  464. // this is backwards-compatible value
  465. return 30;
  466. }
  467. extern "C" void UnityStartFrameRendering()
  468. {
  469. [[DisplayManager Instance] startFrameRendering];
  470. }
  471. extern "C" void UnityDestroyUnityRenderSurfaces()
  472. {
  473. [[DisplayManager Instance] enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  474. DestroyUnityRenderBuffers(conn.surface);
  475. }];
  476. }