DisplayManager.mm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  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 with 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.hidden = NO;
  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 newCommandQueueWithMaxCommandBufferCount: UnityCommandQueueMaxCommandBufferCountMTL()];
  96. surf->drawableCommandQueue = [surf->device newCommandQueueWithMaxCommandBufferCount: UnityCommandQueueMaxCommandBufferCountMTL()];
  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. // On metal we depend on hardware screen compositor to handle upscaling this way avoiding additional blit
  113. CGSize layerSize = _view.layer.bounds.size;
  114. float scale = _view.contentScaleFactor;
  115. CGSize screenSize = CGSizeMake(layerSize.width * scale, layerSize.height * scale);
  116. // if we did request custom resolution we apply it here.
  117. // for metal we use hardware scaler which will be triggered exactly because our window is not of "native" size
  118. // but we also want to enforce native resolution as maximum, otherwise we might run out of memory vert fast
  119. // TODO: how about supersampling screenshots? maybe there are reasonable usecases
  120. if (UnitySelectedRenderingAPI() == apiMetal && params.renderW > 0 && params.renderH > 0)
  121. _screenSize = CGSizeMake(fminf(screenSize.width, params.renderW), fminf(screenSize.height, params.renderH));
  122. else
  123. _screenSize = screenSize;
  124. bool systemSizeChanged = _surface->systemW != _screenSize.width || _surface->systemH != _screenSize.height;
  125. bool msaaChanged = _supportsMSAA && (_surface->msaaSamples != params.msaaSampleCount);
  126. bool depthFmtChanged = _surface->disableDepthAndStencil != params.disableDepthAndStencil;
  127. bool cvCacheChanged = _surface->useCVTextureCache != params.useCVTextureCache;
  128. bool memorylessChanged = _surface->memorylessDepth != params.metalMemorylessDepth;
  129. bool renderSizeChanged = false;
  130. if ((params.renderW > 0 && _surface->targetW != params.renderW) // changed resolution
  131. || (params.renderH > 0 && _surface->targetH != params.renderH) // changed resolution
  132. || (params.renderW <= 0 && _surface->targetW != _surface->systemW) // no longer need intermediate fb
  133. || (params.renderH <= 0 && _surface->targetH != _surface->systemH) // no longer need intermediate fb
  134. )
  135. {
  136. renderSizeChanged = true;
  137. }
  138. bool recreateSystemSurface = systemSizeChanged;
  139. bool recreateRenderingSurface = systemSizeChanged || renderSizeChanged || msaaChanged || cvCacheChanged;
  140. bool recreateDepthbuffer = systemSizeChanged || renderSizeChanged || msaaChanged || depthFmtChanged || memorylessChanged;
  141. _surface->disableDepthAndStencil = params.disableDepthAndStencil;
  142. _surface->systemW = _screenSize.width;
  143. _surface->systemH = _screenSize.height;
  144. _surface->targetW = params.renderW > 0 ? params.renderW : _surface->systemW;
  145. _surface->targetH = params.renderH > 0 ? params.renderH : _surface->systemH;
  146. _surface->msaaSamples = _supportsMSAA ? params.msaaSampleCount : 0;
  147. _surface->srgb = params.srgb;
  148. _surface->wideColor = params.wideColor;
  149. _surface->useCVTextureCache = params.useCVTextureCache;
  150. _surface->memorylessDepth = params.metalMemorylessDepth;
  151. if (UnitySelectedRenderingAPI() == apiMetal)
  152. {
  153. recreateSystemSurface = recreateSystemSurface || self.surfaceMTL->systemColorRB == 0;
  154. self.surfaceMTL->framebufferOnly = params.metalFramebufferOnly;
  155. }
  156. else
  157. recreateSystemSurface = recreateSystemSurface || self.surfaceGLES->systemFB == 0;
  158. if (recreateSystemSurface)
  159. CreateSystemRenderingSurface(_surface);
  160. if (recreateRenderingSurface)
  161. CreateRenderingSurface(_surface);
  162. if (recreateDepthbuffer)
  163. CreateSharedDepthbuffer(_surface);
  164. if (recreateSystemSurface || recreateRenderingSurface || recreateDepthbuffer)
  165. CreateUnityRenderBuffers(_surface);
  166. UnityInvalidateDisplayDataCache((__bridge void*)_screen);
  167. }
  168. - (void)destroySurface
  169. {
  170. if (_surface)
  171. {
  172. DestroySystemRenderingSurface(_surface);
  173. DestroyRenderingSurface(_surface);
  174. DestroySharedDepthbuffer(_surface);
  175. DestroyUnityRenderBuffers(_surface);
  176. if (UnitySelectedRenderingAPI() == apiMetal)
  177. {
  178. self.surfaceMTL->device = nil;
  179. self.surfaceMTL->layer = nil;
  180. }
  181. else
  182. {
  183. self.surfaceGLES->context = nil;
  184. self.surfaceGLES->layer = nil;
  185. }
  186. }
  187. delete _surface;
  188. _surface = 0;
  189. }
  190. - (void)dealloc
  191. {
  192. NSAssert(_surface == 0, @"At this point surface should be already destroyed!");
  193. _view = nil;
  194. _window = nil;
  195. }
  196. - (void)present
  197. {
  198. PreparePresent(self.surface);
  199. Present(self.surface);
  200. if (_needRecreateSurface)
  201. {
  202. RenderingSurfaceParams params =
  203. {
  204. .msaaSampleCount = _surface->msaaSamples,
  205. .renderW = (int)_requestedRenderingSize.width,
  206. .renderH = (int)_requestedRenderingSize.height,
  207. .srgb = _surface->srgb,
  208. .wideColor = _surface->wideColor,
  209. .metalFramebufferOnly = 0,
  210. .metalMemorylessDepth = 0,
  211. .disableDepthAndStencil = _surface->disableDepthAndStencil,
  212. .useCVTextureCache = self.surface->cvTextureCache != 0,
  213. };
  214. [self recreateSurface: params];
  215. _needRecreateSurface = NO;
  216. _requestedRenderingSize = CGSizeMake(_surface->targetW, _surface->targetH);
  217. }
  218. }
  219. - (void)requestRenderingResolution:(CGSize)res
  220. {
  221. _requestedRenderingSize = res;
  222. _needRecreateSurface = YES;
  223. }
  224. @end
  225. @implementation DisplayManager
  226. {
  227. NSMapTable* _displayConnection;
  228. DisplayConnection* _mainDisplay;
  229. }
  230. @synthesize mainDisplay = _mainDisplay;
  231. @synthesize displayCount;
  232. - (NSUInteger)displayCount { return _displayConnection.count; }
  233. - (void)registerScreen:(UIScreen*)screen
  234. {
  235. [_displayConnection setObject: [[DisplayConnection alloc] init: screen] forKey: screen];
  236. }
  237. - (id)init
  238. {
  239. if ((self = [super init]))
  240. {
  241. [[NSNotificationCenter defaultCenter] addObserver: self
  242. selector: @selector(screenDidConnect:)
  243. name: UIScreenDidConnectNotification
  244. object: nil
  245. ];
  246. [[NSNotificationCenter defaultCenter] addObserver: self
  247. selector: @selector(screenDidDisconnect:)
  248. name: UIScreenDidDisconnectNotification
  249. object: nil
  250. ];
  251. _displayConnection = [NSMapTable
  252. mapTableWithKeyOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  253. valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  254. ];
  255. for (UIScreen* screen in [UIScreen screens])
  256. [self registerScreen: screen];
  257. _mainDisplay = self[[UIScreen mainScreen]];
  258. }
  259. return self;
  260. }
  261. - (void)dealloc
  262. {
  263. [[NSNotificationCenter defaultCenter] removeObserver: self];
  264. }
  265. - (BOOL)displayAvailable:(UIScreen*)targetScreen;
  266. {
  267. return self[targetScreen] != nil;
  268. }
  269. - (DisplayConnection*)display:(UIScreen*)targetScreen
  270. {
  271. return self[targetScreen];
  272. }
  273. - (id)objectForKeyedSubscript:(id)key
  274. {
  275. NSAssert([key isKindOfClass: [UIScreen class]], @"DisplayManager allows only UIScreen as subscript");
  276. return [_displayConnection objectForKey: (UIScreen*)key];
  277. }
  278. - (void)updateDisplayListCacheInUnity;
  279. {
  280. // [UIScreen screens] might be out of sync to what is indicated to the
  281. // application via UIScreenDidConnectNotification and UIScreenDidDisconnectNotification
  282. // notifications. For example, on disconnection [UIScreen screens] might still
  283. // have the screen that the display manager no longer knows about.
  284. const unsigned MAX_DISPLAYS_SUPPORTED = 8; // sync this to the value on Unity side
  285. void* screens[MAX_DISPLAYS_SUPPORTED];
  286. unsigned screenCount = 0;
  287. UIScreen* mainScreen = [UIScreen mainScreen];
  288. screens[screenCount++] = (__bridge void*)mainScreen;
  289. for (UIScreen* screen in _displayConnection)
  290. {
  291. if (screen == mainScreen)
  292. continue;
  293. screens[screenCount++] = (__bridge void*)screen;
  294. }
  295. UnityUpdateDisplayListCache(screens, screenCount);
  296. }
  297. - (void)enumerateDisplaysWithBlock:(void (^)(DisplayConnection* conn))block
  298. {
  299. for (UIScreen* screen in _displayConnection)
  300. {
  301. // if we want simple mirroring unity wont create rendering backing for display
  302. // in that case we dont want to touch Display
  303. DisplayConnection* conn = [_displayConnection objectForKey: screen];
  304. if (conn.surface != nil)
  305. block(conn);
  306. }
  307. }
  308. - (void)enumerateNonMainDisplaysWithBlock:(void (^)(DisplayConnection* conn))block
  309. {
  310. for (UIScreen* screen in _displayConnection)
  311. {
  312. DisplayConnection* conn = [_displayConnection objectForKey: screen];
  313. if (conn != _mainDisplay && conn.surface != nil)
  314. block(conn);
  315. }
  316. }
  317. - (void)startFrameRendering
  318. {
  319. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  320. StartFrameRendering(conn.surface);
  321. }];
  322. }
  323. - (void)endFrameRendering
  324. {
  325. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  326. EndFrameRendering(conn.surface);
  327. }];
  328. }
  329. - (void)present
  330. {
  331. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  332. [conn present];
  333. }];
  334. }
  335. - (void)screenDidConnect:(NSNotification*)notification
  336. {
  337. [self registerScreen: (UIScreen*)[notification object]];
  338. [self updateDisplayListCacheInUnity];
  339. }
  340. - (void)screenDidDisconnect:(NSNotification*)notification
  341. {
  342. UIScreen* screen = (UIScreen*)[notification object];
  343. DisplayConnection* conn = (DisplayConnection*)self[screen];
  344. if (conn != nil && conn.surface != nil)
  345. UnityDisableRenderBuffers(conn.surface->unityColorBuffer, conn.surface->unityDepthBuffer);
  346. [_displayConnection removeObjectForKey: screen];
  347. conn = nil;
  348. [self updateDisplayListCacheInUnity];
  349. }
  350. + (void)Initialize
  351. {
  352. NSAssert(_DisplayManager == nil, @"[DisplayManager Initialize] called after creating handler");
  353. if (!_DisplayManager)
  354. _DisplayManager = [[DisplayManager alloc] init];
  355. }
  356. + (DisplayManager*)Instance
  357. {
  358. if (!_DisplayManager)
  359. _DisplayManager = [[DisplayManager alloc] init];
  360. return _DisplayManager;
  361. }
  362. + (void)Destroy
  363. {
  364. _DisplayManager = nil;
  365. }
  366. @end
  367. //==============================================================================
  368. //
  369. // Unity Interface:
  370. static void EnsureDisplayIsInited(DisplayConnection* conn)
  371. {
  372. // main screen view will be created in AppController,
  373. // so we can assume that we need to init secondary display from script
  374. // meaning: gles + show right away
  375. if (conn.view == nil)
  376. [conn createView: YES];
  377. int api = UnitySelectedRenderingAPI();
  378. bool needRecreate = false;
  379. if (conn.surface == 0)
  380. needRecreate = true;
  381. else if (api == apiMetal)
  382. needRecreate = conn.surfaceMTL->layer == nil;
  383. else
  384. needRecreate = conn.surfaceGLES->systemFB == 0;
  385. if (needRecreate)
  386. {
  387. RenderingSurfaceParams params =
  388. {
  389. .msaaSampleCount = UnityGetDesiredMSAASampleCount(MSAA_DEFAULT_SAMPLE_COUNT),
  390. .renderW = -1, // native resolution at first (can be changed later)
  391. .renderH = -1, // native resolution at first (can be changed later)
  392. .srgb = UnityGetSRGBRequested(),
  393. .wideColor = 0, // i am not sure how to handle wide color here (and if it is even supported for airplay)
  394. .metalFramebufferOnly = UnityMetalFramebufferOnly(),
  395. .metalMemorylessDepth = UnityMetalMemorylessDepth(),
  396. .disableDepthAndStencil = UnityDisableDepthAndStencilBuffers(),
  397. .useCVTextureCache = 0,
  398. };
  399. [conn recreateSurface: params];
  400. {
  401. DisplayConnection* main = [DisplayManager Instance].mainDisplay;
  402. if (api != apiMetal)
  403. [EAGLContext setCurrentContext: UnityGetMainScreenContextGLES()];
  404. StartFrameRendering(main.surface);
  405. }
  406. }
  407. }
  408. #if !PLATFORM_TVOS
  409. extern "C" int UnityDisplayManager_DisplayCount()
  410. {
  411. return (int)[DisplayManager Instance].displayCount;
  412. }
  413. extern "C" bool UnityDisplayManager_DisplayAvailable(void* nativeDisplay)
  414. {
  415. if (nativeDisplay == NULL)
  416. return false;
  417. return [[DisplayManager Instance] displayAvailable: (__bridge UIScreen*)nativeDisplay];
  418. }
  419. extern "C" bool UnityDisplayManager_DisplayActive(void* nativeDisplay)
  420. {
  421. if (nativeDisplay == NULL)
  422. return false;
  423. return UnityDisplayManager_DisplayAvailable(nativeDisplay);
  424. }
  425. extern "C" void UnityDisplayManager_DisplaySystemResolution(void* nativeDisplay, int* w, int* h)
  426. {
  427. if (nativeDisplay == NULL)
  428. return;
  429. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  430. EnsureDisplayIsInited(conn);
  431. *w = (int)conn.surface->systemW;
  432. *h = (int)conn.surface->systemH;
  433. }
  434. extern "C" void UnityDisplayManager_DisplayRenderingResolution(void* nativeDisplay, int* w, int* h)
  435. {
  436. if (nativeDisplay == NULL)
  437. return;
  438. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  439. EnsureDisplayIsInited(conn);
  440. *w = (int)conn.surface->targetW;
  441. *h = (int)conn.surface->targetH;
  442. }
  443. extern "C" void UnityDisplayManager_DisplayRenderingBuffers(void* nativeDisplay, void** colorBuffer, void** depthBuffer)
  444. {
  445. if (nativeDisplay == NULL)
  446. return;
  447. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  448. EnsureDisplayIsInited(conn);
  449. if (colorBuffer)
  450. *colorBuffer = conn.surface->unityColorBuffer;
  451. if (depthBuffer)
  452. *depthBuffer = conn.surface->unityDepthBuffer;
  453. }
  454. extern "C" void UnityDisplayManager_SetRenderingResolution(void* nativeDisplay, int w, int h)
  455. {
  456. if (nativeDisplay == NULL)
  457. return;
  458. UIScreen* screen = (__bridge UIScreen*)nativeDisplay;
  459. DisplayConnection* conn = [DisplayManager Instance][screen];
  460. EnsureDisplayIsInited(conn);
  461. if (screen == [UIScreen mainScreen])
  462. UnityRequestRenderingResolution(w, h);
  463. else
  464. [conn requestRenderingResolution: CGSizeMake(w, h)];
  465. }
  466. extern "C" void UnityDisplayManager_ShouldShowWindowOnDisplay(void* nativeDisplay, bool show)
  467. {
  468. if (nativeDisplay == NULL)
  469. return;
  470. UIScreen* screen = (__bridge UIScreen*)nativeDisplay;
  471. DisplayConnection* conn = [DisplayManager Instance][screen];
  472. EnsureDisplayIsInited(conn);
  473. if (screen != [UIScreen mainScreen])
  474. [conn shouldShowWindow: show];
  475. }
  476. extern "C" int UnityDisplayManager_PrimaryDisplayIndex()
  477. {
  478. return 0;
  479. }
  480. #endif
  481. extern "C" EAGLContext* UnityGetMainScreenContextGLES()
  482. {
  483. return GetMainDisplay().surfaceGLES->context;
  484. }
  485. extern "C" EAGLContext* UnityGetContextEAGL()
  486. {
  487. return GetMainDisplay().surfaceGLES->context;
  488. }
  489. extern "C" float UnityScreenScaleFactor(UIScreen* screen)
  490. {
  491. // NOTE: All views handled by Unity have their contentScaleFactor initialized
  492. // to value returned by this function.
  493. // we should query nativeScale if available to get the true device resolution
  494. // this way we avoid unnecessarily large frame buffers and downscaling.
  495. // e.g. iPhone 6+ pretends to be a x3 device, while its physical screen is x2.6 something.
  496. // it is available on iOS 8.0+, tvOS 9.0+
  497. // for older ios versions we add this selector ourselves (AddNewAPIImplIfNeeded in UnityAppController.mm)
  498. // On AppleTV screen.nativeScale returns NaN when device is in sleep mode and starting from tvOS 10 (?) it returns 0.
  499. if (isnan(screen.nativeScale) || (screen.nativeScale == 0))
  500. return 1.0f;
  501. else
  502. {
  503. float scalingFactor = UnityCalculateScalingFactorFromTargetDPI(screen);
  504. if (scalingFactor > 0.0f)
  505. return scalingFactor;
  506. else
  507. return screen.nativeScale;
  508. }
  509. return screen.scale;
  510. }
  511. extern "C" int UnityMainScreenRefreshRate()
  512. {
  513. if (@available(iOS 10.3, tvOS 10.3, *))
  514. return (int)[UIScreen mainScreen].maximumFramesPerSecond;
  515. // this is backwards-compatible value
  516. return 30;
  517. }
  518. extern "C" void UnityStartFrameRendering()
  519. {
  520. [[DisplayManager Instance] startFrameRendering];
  521. }
  522. extern "C" void UnityDestroyUnityRenderSurfaces()
  523. {
  524. [[DisplayManager Instance] enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  525. [conn destroySurface];
  526. }];
  527. }
  528. extern "C" void UnitySetBrightness(float brightness)
  529. {
  530. #if !PLATFORM_TVOS
  531. brightness = (brightness > 1.0 ? 1.0 : brightness) < 0 ? 0.0 : brightness;
  532. UIScreen* screen = [UIScreen mainScreen];
  533. screen.brightness = brightness;
  534. #endif
  535. }
  536. extern "C" float UnityGetBrightness()
  537. {
  538. #if !PLATFORM_TVOS
  539. UIScreen* screen = [UIScreen mainScreen];
  540. return screen.brightness;
  541. #else
  542. return 1.0f;
  543. #endif
  544. }