Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DisplayManager.mm 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. #include "DisplayManager.h"
  2. #include "UnityAppController.h"
  3. #include "UI/UnityView.h"
  4. #include "UI/UnityAppController+ViewHandling.h"
  5. #import <CoreGraphics/CoreGraphics.h>
  6. #import <Metal/Metal.h>
  7. #import <QuartzCore/QuartzCore.h>
  8. #if !(defined(__IPHONE_16_0) || defined(__TVOS_16_0))
  9. @interface CAMetalLayer (UnityForSdk16)
  10. {
  11. }
  12. @property BOOL wantsExtendedDynamicRangeContent API_AVAILABLE(macos(10.11), ios(16.0), macCatalyst(16.0)) API_UNAVAILABLE(tvos, watchos);
  13. @end
  14. @interface UIScreen (UnityForSdk16)
  15. {
  16. }
  17. @property CGFloat potentialEDRHeadroom API_AVAILABLE(macos(10.11), ios(16.0), tvos(16.0), macCatalyst(16.0)) API_UNAVAILABLE(watchos);
  18. @property CGFloat currentEDRHeadroom API_AVAILABLE(macos(10.11), ios(16.0), tvos(16.0), macCatalyst(16.0)) API_UNAVAILABLE(watchos);
  19. @end
  20. #endif
  21. static DisplayManager* _DisplayManager = nil;
  22. @interface DisplayConnection ()
  23. @property (readonly, nonatomic) UnityDisplaySurfaceMTL* surfaceMTL;
  24. @end
  25. @implementation DisplayConnection
  26. {
  27. BOOL _needRecreateSurface;
  28. CGSize _requestedRenderingSize;
  29. #if !PLATFORM_VISIONOS
  30. UIScreen* _screen;
  31. #endif
  32. UIWindow* _window;
  33. UIView* _view;
  34. CGSize _screenSize;
  35. UnityDisplaySurfaceBase* _surface;
  36. }
  37. #if !PLATFORM_VISIONOS
  38. @synthesize screen = _screen;
  39. #endif
  40. @synthesize window = _window;
  41. @synthesize view = _view;
  42. @synthesize screenSize = _screenSize;
  43. @synthesize surface = _surface;
  44. @synthesize surfaceMTL;
  45. - (UnityDisplaySurfaceMTL*)surfaceMTL
  46. {
  47. assert(_surface->api == apiMetal);
  48. return (UnityDisplaySurfaceMTL*)_surface;
  49. }
  50. #if !PLATFORM_VISIONOS
  51. - (id)init:(UIScreen*)targetScreen
  52. {
  53. if ((self = [super init]))
  54. {
  55. self->_screen = targetScreen;
  56. #if !PLATFORM_TVOS
  57. targetScreen.currentMode = targetScreen.preferredMode;
  58. #endif
  59. // UIScreenOverscanCompensationNone == UIScreenOverscanCompensationInsetApplicationFrame so it will work with pre-ios9 just fine
  60. targetScreen.overscanCompensation = UIScreenOverscanCompensationNone;
  61. self->_screenSize = targetScreen.currentMode.size;
  62. self->_needRecreateSurface = NO;
  63. self->_requestedRenderingSize = CGSizeMake(-1, -1);
  64. }
  65. return self;
  66. }
  67. #else
  68. - (id)init
  69. {
  70. if ((self = [super init]))
  71. {
  72. self->_screenSize = CGSizeMake(1920, 1080);
  73. self->_needRecreateSurface = NO;
  74. self->_requestedRenderingSize = CGSizeMake(-1, -1);
  75. }
  76. return self;
  77. }
  78. #endif
  79. - (void)createWithWindow:(UIWindow*)window andView:(UIView*)view
  80. {
  81. _window = window;
  82. _view = view;
  83. CGSize layerSize = _view.layer.bounds.size;
  84. _screenSize = CGSizeMake(roundf(layerSize.width) * _view.contentScaleFactor, roundf(layerSize.height) * _view.contentScaleFactor);
  85. }
  86. - (void)createView:(BOOL)useForRendering
  87. {
  88. [self createView: useForRendering showRightAway: YES];
  89. }
  90. - (void)createView:(BOOL)useForRendering showRightAway:(BOOL)showRightAway;
  91. {
  92. #if !PLATFORM_VISIONOS
  93. NSAssert(_screen != [UIScreen mainScreen], @"DisplayConnection for mainScreen should be created with createWithWindow:andView:");
  94. #endif
  95. if (_view == nil)
  96. {
  97. #if !PLATFORM_VISIONOS
  98. UIWindow* window = [[UIWindow alloc] initWithFrame: _screen.bounds];
  99. #pragma clang diagnostic push
  100. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  101. // [UIWindow setScreen:] is deprecated in favor of [UIWindow setWindowScene:], but we are not yet scenes based
  102. // this API works perfectly fine for now, so we use it until we rewrite/modernize trampoline to be Scene-based
  103. window.screen = _screen;
  104. #pragma clang diagnostic pop
  105. UIView* view = [(useForRendering ? [UnityRenderingView alloc] : [UIView alloc]) initWithFrame: _screen.bounds];
  106. view.contentScaleFactor = UnityScreenScaleFactor(_screen);
  107. #else
  108. UIWindow* window = [[UIWindow alloc] init];
  109. UIView* view = [(useForRendering ? [UnityRenderingView alloc] : [UIView alloc]) init];
  110. #endif
  111. [self createWithWindow: window andView: view];
  112. if (showRightAway)
  113. {
  114. [window addSubview: view];
  115. window.hidden = NO;
  116. }
  117. }
  118. }
  119. - (void)shouldShowWindow:(BOOL)show
  120. {
  121. _window.hidden = show ? NO : YES;
  122. #if !PLATFORM_VISIONOS
  123. #pragma clang diagnostic push
  124. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  125. // [UIWindow setScreen:] is deprecated in favor of [UIWindow setWindowScene:], but we are not yet scenes based
  126. // this API works perfectly fine for now, so we use it until we rewrite/modernize trampoline to be Scene-based
  127. _window.screen = show ? _screen : nil;
  128. #pragma clang diagnostic pop
  129. #endif
  130. }
  131. - (UnityDisplaySurfaceBase*)initRendering
  132. {
  133. if (_surface)
  134. return _surface;
  135. UnityDisplaySurfaceBase* ret = NULL;
  136. const int api = UnitySelectedRenderingAPI();
  137. if (api == apiMetal)
  138. {
  139. UnityDisplaySurfaceMTL* surf = new UnityDisplaySurfaceMTL();
  140. surf->layer = (CAMetalLayer*)_view.layer;
  141. surf->device = UnityGetMetalDevice();
  142. ret = surf;
  143. }
  144. else
  145. ret = new UnityDisplaySurfaceBase();
  146. ret->api = api;
  147. return ret;
  148. }
  149. - (void)recreateSurface:(RenderingSurfaceParams)params
  150. {
  151. UnityDisplaySurfaceBase* surface = [self initRendering];
  152. // On metal we depend on hardware screen compositor to handle upscaling this way avoiding additional blit
  153. CGSize layerSize = _view.layer.bounds.size;
  154. float scale = _view.contentScaleFactor;
  155. CGSize screenSize = CGSizeMake(layerSize.width * scale, layerSize.height * scale);
  156. // if we did request custom resolution we apply it here.
  157. // for metal we use hardware scaler which will be triggered exactly because our window is not of "native" size
  158. // but we also want to enforce native resolution as maximum, otherwise we might run out of memory vert fast
  159. // TODO: how about supersampling screenshots? maybe there are reasonable usecases
  160. if (UnitySelectedRenderingAPI() == apiMetal && params.renderW > 0 && params.renderH > 0)
  161. _screenSize = CGSizeMake(fminf(screenSize.width, params.renderW), fminf(screenSize.height, params.renderH));
  162. else
  163. _screenSize = screenSize;
  164. bool hdrChanged = surface->hdr != params.hdr;
  165. bool systemSizeChanged = surface->systemW != _screenSize.width || surface->systemH != _screenSize.height;
  166. bool msaaChanged = surface->msaaSamples != params.msaaSampleCount;
  167. bool depthFmtChanged = surface->disableDepthAndStencil != params.disableDepthAndStencil;
  168. bool cvCacheChanged = surface->useCVTextureCache != params.useCVTextureCache;
  169. bool memorylessChanged = surface->memorylessDepth != params.metalMemorylessDepth;
  170. bool renderSizeChanged = false;
  171. if ((params.renderW > 0 && surface->targetW != params.renderW) // changed resolution
  172. || (params.renderH > 0 && surface->targetH != params.renderH) // changed resolution
  173. || (params.renderW <= 0 && surface->targetW != surface->systemW) // no longer need intermediate fb
  174. || (params.renderH <= 0 && surface->targetH != surface->systemH) // no longer need intermediate fb
  175. )
  176. {
  177. renderSizeChanged = true;
  178. }
  179. bool recreateSystemSurface = systemSizeChanged || hdrChanged;
  180. bool recreateRenderingSurface = systemSizeChanged || renderSizeChanged || msaaChanged || cvCacheChanged || hdrChanged;
  181. bool recreateDepthbuffer = systemSizeChanged || renderSizeChanged || msaaChanged || depthFmtChanged || memorylessChanged;
  182. surface->disableDepthAndStencil = params.disableDepthAndStencil;
  183. surface->systemW = (unsigned)_screenSize.width;
  184. surface->systemH = (unsigned)_screenSize.height;
  185. surface->targetW = params.renderW > 0 ? params.renderW : surface->systemW;
  186. surface->targetH = params.renderH > 0 ? params.renderH : surface->systemH;
  187. surface->msaaSamples = params.msaaSampleCount;
  188. surface->srgb = params.srgb;
  189. surface->wideColor = params.wideColor;
  190. surface->hdr = params.hdr;
  191. surface->useCVTextureCache = params.useCVTextureCache;
  192. surface->memorylessDepth = params.metalMemorylessDepth;
  193. const int api = UnitySelectedRenderingAPI();
  194. if (api == apiMetal)
  195. {
  196. UnityDisplaySurfaceMTL* mtlSurf = (UnityDisplaySurfaceMTL*)surface;
  197. recreateSystemSurface = recreateSystemSurface || mtlSurf->systemColorRB == 0;
  198. mtlSurf->framebufferOnly = params.metalFramebufferOnly;
  199. }
  200. if (recreateSystemSurface)
  201. CreateSystemRenderingSurface(surface);
  202. if (recreateRenderingSurface)
  203. CreateRenderingSurface(surface);
  204. if (recreateDepthbuffer)
  205. CreateSharedDepthbuffer(surface);
  206. if (recreateSystemSurface || recreateRenderingSurface || recreateDepthbuffer)
  207. CreateUnityRenderBuffers(surface);
  208. if (recreateSystemSurface || recreateRenderingSurface)
  209. {
  210. UnityDisplaySurfaceMTL* mtlSurf = (UnityDisplaySurfaceMTL*)surface;
  211. #if !PLATFORM_TVOS
  212. if (@available(iOS 16.0, *))
  213. {
  214. mtlSurf->layer.wantsExtendedDynamicRangeContent = surface->hdr != 0;
  215. }
  216. #endif
  217. UnitySetHDRMode(surface->hdr);
  218. }
  219. _surface = surface;
  220. #if !PLATFORM_VISIONOS
  221. UnityInvalidateDisplayDataCache((__bridge void*)_screen);
  222. #endif
  223. }
  224. - (void)destroySurface
  225. {
  226. if (_surface)
  227. {
  228. DestroySystemRenderingSurface(_surface);
  229. DestroyRenderingSurface(_surface);
  230. DestroySharedDepthbuffer(_surface);
  231. DestroyUnityRenderBuffers(_surface);
  232. const int api = UnitySelectedRenderingAPI();
  233. if (api == apiMetal)
  234. {
  235. self.surfaceMTL->device = nil;
  236. self.surfaceMTL->layer = nil;
  237. }
  238. }
  239. delete _surface;
  240. _surface = 0;
  241. }
  242. - (void)dealloc
  243. {
  244. NSAssert(_surface == 0, @"At this point surface should be already destroyed!");
  245. _view = nil;
  246. _window = nil;
  247. }
  248. - (void)present
  249. {
  250. #if !PLATFORM_VISIONOS
  251. CGFloat maxEDR = 1.f;
  252. CGFloat currentEDR = 1.f;
  253. if (_screen.captured)
  254. {
  255. maxEDR = 1.f;
  256. currentEDR = 1.f;
  257. }
  258. else
  259. {
  260. if (@available(iOS 16.0, tvOS 16.0, *))
  261. {
  262. maxEDR = _screen.potentialEDRHeadroom;
  263. currentEDR = _screen.currentEDRHeadroom;
  264. }
  265. }
  266. UnitySetEDRValues(maxEDR, currentEDR);
  267. #endif
  268. PreparePresent(self.surface);
  269. Present(self.surface);
  270. if (_needRecreateSurface)
  271. {
  272. RenderingSurfaceParams params =
  273. {
  274. .msaaSampleCount = _surface->msaaSamples,
  275. .renderW = (int)_requestedRenderingSize.width,
  276. .renderH = (int)_requestedRenderingSize.height,
  277. .srgb = _surface->srgb,
  278. .wideColor = _surface->wideColor,
  279. .hdr = _surface->hdr,
  280. .metalFramebufferOnly = 0,
  281. .metalMemorylessDepth = 0,
  282. .disableDepthAndStencil = _surface->disableDepthAndStencil,
  283. .useCVTextureCache = self.surface->cvTextureCache != 0,
  284. };
  285. [self recreateSurface: params];
  286. _needRecreateSurface = NO;
  287. _requestedRenderingSize = CGSizeMake(_surface->targetW, _surface->targetH);
  288. }
  289. }
  290. - (void)requestRenderingResolution:(CGSize)res
  291. {
  292. _requestedRenderingSize = res;
  293. _needRecreateSurface = YES;
  294. }
  295. @end
  296. #if !PLATFORM_VISIONOS
  297. @implementation DisplayManager
  298. {
  299. NSMapTable* _displayConnection;
  300. DisplayConnection* _mainDisplay;
  301. }
  302. @synthesize mainDisplay = _mainDisplay;
  303. @synthesize displayCount;
  304. - (NSUInteger)displayCount { return _displayConnection.count; }
  305. - (void)registerScreen:(UIScreen*)screen
  306. {
  307. [_displayConnection setObject: [[DisplayConnection alloc] init: screen] forKey: screen];
  308. }
  309. - (id)init
  310. {
  311. if ((self = [super init]))
  312. {
  313. [[NSNotificationCenter defaultCenter] addObserver: self
  314. selector: @selector(screenDidConnect:)
  315. name: UIScreenDidConnectNotification
  316. object: nil
  317. ];
  318. [[NSNotificationCenter defaultCenter] addObserver: self
  319. selector: @selector(screenDidDisconnect:)
  320. name: UIScreenDidDisconnectNotification
  321. object: nil
  322. ];
  323. _displayConnection = [NSMapTable
  324. mapTableWithKeyOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  325. valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality
  326. ];
  327. for (UIScreen* screen in [UIScreen screens])
  328. [self registerScreen: screen];
  329. _mainDisplay = self[[UIScreen mainScreen]];
  330. }
  331. return self;
  332. }
  333. - (void)dealloc
  334. {
  335. [[NSNotificationCenter defaultCenter] removeObserver: self];
  336. }
  337. - (BOOL)displayAvailable:(UIScreen*)targetScreen;
  338. {
  339. return self[targetScreen] != nil;
  340. }
  341. - (DisplayConnection*)display:(UIScreen*)targetScreen
  342. {
  343. return self[targetScreen];
  344. }
  345. - (id)objectForKeyedSubscript:(id)key
  346. {
  347. NSAssert([key isKindOfClass: [UIScreen class]], @"DisplayManager allows only UIScreen as subscript");
  348. return [_displayConnection objectForKey: (UIScreen*)key];
  349. }
  350. - (void)updateDisplayListCacheInUnity
  351. {
  352. // [UIScreen screens] might be out of sync to what is indicated to the
  353. // application via UIScreenDidConnectNotification and UIScreenDidDisconnectNotification
  354. // notifications. For example, on disconnection [UIScreen screens] might still
  355. // have the screen that the display manager no longer knows about.
  356. const unsigned MAX_DISPLAYS_SUPPORTED = 8; // sync this to the value on Unity side
  357. void* screens[MAX_DISPLAYS_SUPPORTED];
  358. unsigned screenCount = 0;
  359. UIScreen* mainScreen = [UIScreen mainScreen];
  360. screens[screenCount++] = (__bridge void*)mainScreen;
  361. for (UIScreen* screen in _displayConnection)
  362. {
  363. if (screen == mainScreen)
  364. continue;
  365. screens[screenCount++] = (__bridge void*)screen;
  366. }
  367. UnityUpdateDisplayListCache(screens, screenCount);
  368. }
  369. - (void)enumerateDisplaysWithBlock:(void (^)(DisplayConnection* conn))block
  370. {
  371. for (UIScreen* screen in _displayConnection)
  372. {
  373. // if we want simple mirroring unity wont create rendering backing for display
  374. // in that case we dont want to touch Display
  375. DisplayConnection* conn = [_displayConnection objectForKey: screen];
  376. if (conn.surface != nil)
  377. block(conn);
  378. }
  379. }
  380. - (void)enumerateNonMainDisplaysWithBlock:(void (^)(DisplayConnection* conn))block
  381. {
  382. for (UIScreen* screen in _displayConnection)
  383. {
  384. DisplayConnection* conn = [_displayConnection objectForKey: screen];
  385. if (conn != _mainDisplay && conn.surface != nil)
  386. block(conn);
  387. }
  388. }
  389. - (void)startFrameRendering
  390. {
  391. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  392. StartFrameRendering(conn.surface);
  393. }];
  394. }
  395. - (void)endFrameRendering
  396. {
  397. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  398. EndFrameRendering(conn.surface);
  399. }];
  400. }
  401. - (void)present
  402. {
  403. [self enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  404. [conn present];
  405. }];
  406. }
  407. - (void)screenDidConnect:(NSNotification*)notification
  408. {
  409. [self registerScreen: (UIScreen*)[notification object]];
  410. [self updateDisplayListCacheInUnity];
  411. }
  412. - (void)screenDidDisconnect:(NSNotification*)notification
  413. {
  414. UIScreen* screen = (UIScreen*)[notification object];
  415. DisplayConnection* conn = (DisplayConnection*)self[screen];
  416. if (conn != nil && conn.surface != nil)
  417. UnityDisableRenderBuffers(conn.surface->unityColorBuffer, conn.surface->unityDepthBuffer);
  418. [conn destroySurface];
  419. conn = nil;
  420. [_displayConnection removeObjectForKey: screen];
  421. [self updateDisplayListCacheInUnity];
  422. }
  423. + (void)Initialize
  424. {
  425. NSAssert(_DisplayManager == nil, @"[DisplayManager Initialize] called after creating handler");
  426. if (!_DisplayManager)
  427. _DisplayManager = [[DisplayManager alloc] init];
  428. }
  429. + (DisplayManager*)Instance
  430. {
  431. if (!_DisplayManager)
  432. _DisplayManager = [[DisplayManager alloc] init];
  433. return _DisplayManager;
  434. }
  435. + (void)Destroy
  436. {
  437. _DisplayManager = nil;
  438. }
  439. @end
  440. #else
  441. // xros DisplayManager
  442. @implementation DisplayManager
  443. {
  444. DisplayConnection* _mainDisplay;
  445. }
  446. @synthesize mainDisplay = _mainDisplay;
  447. @synthesize displayCount;
  448. - (NSUInteger)displayCount { return 1; }
  449. - (id)init
  450. {
  451. if ((self = [super init]))
  452. {
  453. _mainDisplay = [[DisplayConnection alloc] init];
  454. // Unity needs a non-zero screen in order for renderloop to run
  455. const int screenCount = 1;
  456. void* screens[screenCount] = {(void*)0x1};
  457. UnityUpdateDisplayListCache(screens, screenCount);
  458. }
  459. return self;
  460. }
  461. - (void)dealloc
  462. {
  463. }
  464. - (void)startFrameRendering
  465. {
  466. StartFrameRendering(_mainDisplay.surface);
  467. }
  468. - (void)endFrameRendering
  469. {
  470. EndFrameRendering(_mainDisplay.surface);
  471. }
  472. - (void)present
  473. {
  474. [_mainDisplay present];
  475. }
  476. + (void)Initialize
  477. {
  478. NSAssert(_DisplayManager == nil, @"[DisplayManager Initialize] called after creating handler");
  479. if (!_DisplayManager)
  480. _DisplayManager = [[DisplayManager alloc] init];
  481. }
  482. + (DisplayManager*)Instance
  483. {
  484. if (!_DisplayManager)
  485. _DisplayManager = [[DisplayManager alloc] init];
  486. return _DisplayManager;
  487. }
  488. @end
  489. #endif
  490. //==============================================================================
  491. //
  492. // Unity Interface:
  493. static void EnsureDisplayIsInited(DisplayConnection* conn)
  494. {
  495. // main screen view will be created in AppController,
  496. // so we can assume that we need to init secondary display from script
  497. // meaning: gles + show right away
  498. if (conn.view == nil)
  499. [conn createView: YES];
  500. int api = UnitySelectedRenderingAPI();
  501. bool needRecreate = false;
  502. if (conn.surface == 0)
  503. needRecreate = true;
  504. else if (api == apiMetal)
  505. needRecreate = conn.surfaceMTL->layer == nil;
  506. if (needRecreate)
  507. {
  508. RenderingSurfaceParams params =
  509. {
  510. .msaaSampleCount = UnityGetDesiredMSAASampleCount(1),
  511. .renderW = -1, // native resolution at first (can be changed later)
  512. .renderH = -1, // native resolution at first (can be changed later)
  513. .srgb = UnityGetSRGBRequested(),
  514. .wideColor = 0, // i am not sure how to handle wide color here (and if it is even supported for airplay)
  515. .hdr = 0,
  516. .metalFramebufferOnly = UnityMetalFramebufferOnly(),
  517. .metalMemorylessDepth = UnityMetalMemorylessDepth(),
  518. .disableDepthAndStencil = UnityDisableDepthAndStencilBuffers(),
  519. .useCVTextureCache = 0,
  520. };
  521. [conn recreateSurface: params];
  522. {
  523. DisplayConnection* main = [DisplayManager Instance].mainDisplay;
  524. StartFrameRendering(main.surface);
  525. }
  526. }
  527. }
  528. #if !PLATFORM_TVOS
  529. extern "C" int UnityDisplayManager_DisplayCount()
  530. {
  531. return (int)[DisplayManager Instance].displayCount;
  532. }
  533. extern "C" bool UnityDisplayManager_DisplayAvailable(void* nativeDisplay)
  534. {
  535. #if !PLATFORM_VISIONOS
  536. if (nativeDisplay == NULL)
  537. return false;
  538. return [[DisplayManager Instance] displayAvailable: (__bridge UIScreen*)nativeDisplay];
  539. #else
  540. return false;
  541. #endif
  542. }
  543. extern "C" bool UnityDisplayManager_DisplayActive(void* nativeDisplay)
  544. {
  545. #if !PLATFORM_VISIONOS
  546. return UnityDisplayManager_DisplayAvailable(nativeDisplay);
  547. #else
  548. return true;
  549. #endif
  550. }
  551. extern "C" void UnityDisplayManager_DisplaySystemResolution(void* nativeDisplay, int* w, int* h)
  552. {
  553. #if !PLATFORM_VISIONOS
  554. if (nativeDisplay == NULL)
  555. return;
  556. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  557. EnsureDisplayIsInited(conn);
  558. // CODE ARCHEOLOGY: We were creating full-screen buffer for "system" RT, and (possibly) resized for "target" RT,
  559. // CODE ARCHEOLOGY: and if the resolution was changed we were blitting target to system to rescale the image.
  560. // CODE ARCHEOLOGY: Since then we have switched to using OS scaling mechanism, i.e. we are creating window
  561. // CODE ARCHEOLOGY: with rendering extents and let iOS do the upscale
  562. // CODE ARCHEOLOGY: We should revisit the rendering code to possibly get rid of the code needed for this setup
  563. // CODE ARCHEOLOGY: but for now we do the minimal change to unbreak native resolution query
  564. // note that we mimic size calculation that we use in other places, i.e. using screen bounds + content scale factor
  565. // we should also note that we tweak CAMetalLayer.drawableSize (well, that's the way to trigger native upscale)
  566. // so we have CAMetalLayer.drawableSize = rendering size
  567. // and CAMetalLayer.size = system (native) size
  568. const CGSize layerSize = conn.view.layer.bounds.size; const float scale = conn.view.contentScaleFactor;
  569. *w = (int)(layerSize.width * scale);
  570. *h = (int)(layerSize.height * scale);
  571. #endif
  572. }
  573. extern "C" void UnityDisplayManager_DisplayRenderingResolution(void* nativeDisplay, int* w, int* h)
  574. {
  575. #if !PLATFORM_VISIONOS
  576. if (nativeDisplay == NULL)
  577. return;
  578. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  579. EnsureDisplayIsInited(conn);
  580. *w = (int)conn.surface->targetW;
  581. *h = (int)conn.surface->targetH;
  582. #endif
  583. }
  584. extern "C" void UnityDisplayManager_DisplayRenderingBuffers(void* nativeDisplay, void** colorBuffer, void** depthBuffer)
  585. {
  586. if (nativeDisplay == NULL)
  587. return;
  588. #if !PLATFORM_VISIONOS
  589. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  590. #else
  591. DisplayConnection* conn = [DisplayManager Instance].mainDisplay;
  592. #endif
  593. EnsureDisplayIsInited(conn);
  594. if (colorBuffer)
  595. *colorBuffer = conn.surface->unityColorBuffer;
  596. if (depthBuffer)
  597. *depthBuffer = conn.surface->unityDepthBuffer;
  598. }
  599. extern "C" void UnityDisplayManager_SetRenderingResolution(void* nativeDisplay, int w, int h)
  600. {
  601. #if !PLATFORM_VISIONOS
  602. if (nativeDisplay == NULL)
  603. return;
  604. UIScreen* screen = (__bridge UIScreen*)nativeDisplay;
  605. DisplayConnection* conn = [DisplayManager Instance][screen];
  606. EnsureDisplayIsInited(conn);
  607. if (screen == [UIScreen mainScreen])
  608. UnityRequestRenderingResolution(w, h);
  609. else
  610. [conn requestRenderingResolution: CGSizeMake(w, h)];
  611. #endif
  612. }
  613. extern "C" int UnityDisplayManager_PrimaryDisplayIndex()
  614. {
  615. return 0;
  616. }
  617. #endif
  618. extern "C" void UnityActivateScreenForRendering(void* nativeDisplay)
  619. {
  620. if (nativeDisplay == NULL)
  621. return;
  622. #if !PLATFORM_VISIONOS
  623. DisplayConnection* conn = [DisplayManager Instance][(__bridge UIScreen*)nativeDisplay];
  624. #else
  625. DisplayConnection* conn = [DisplayManager Instance].mainDisplay;
  626. #endif
  627. EnsureDisplayIsInited(conn);
  628. [conn shouldShowWindow: YES];
  629. }
  630. #if !PLATFORM_VISIONOS
  631. extern "C" float UnityScreenScaleFactor(UIScreen* screen)
  632. {
  633. // NOTE: All views handled by Unity have their contentScaleFactor initialized
  634. // to value returned by this function.
  635. // we should query nativeScale if available to get the true device resolution
  636. // this way we avoid unnecessarily large frame buffers and downscaling.
  637. // e.g. iPhone 6+ pretends to be a x3 device, while its physical screen is x2.6 something.
  638. // it is available on iOS 8.0+, tvOS 9.0+
  639. // for older ios versions we add this selector ourselves (AddNewAPIImplIfNeeded in UnityAppController.mm)
  640. // On AppleTV screen.nativeScale returns NaN when device is in sleep mode and starting from tvOS 10 (?) it returns 0.
  641. if (isnan(screen.nativeScale) || (screen.nativeScale == 0))
  642. return 1.0f;
  643. else
  644. {
  645. float scalingFactor = UnityCalculateScalingFactorFromTargetDPI(screen);
  646. if (scalingFactor > 0.0f)
  647. return scalingFactor;
  648. else
  649. return screen.nativeScale;
  650. }
  651. return screen.scale;
  652. }
  653. extern "C" int UnityMainScreenRefreshRate()
  654. {
  655. return (int)[UIScreen mainScreen].maximumFramesPerSecond;
  656. }
  657. extern "C" void UnityStartFrameRendering()
  658. {
  659. [[DisplayManager Instance] startFrameRendering];
  660. }
  661. extern "C" void UnityDestroyUnityRenderSurfaces()
  662. {
  663. [[DisplayManager Instance] enumerateDisplaysWithBlock:^(DisplayConnection* conn) {
  664. [conn destroySurface];
  665. }];
  666. }
  667. extern "C" void UnitySetBrightness(float brightness)
  668. {
  669. #if !PLATFORM_TVOS
  670. brightness = (brightness > 1.0 ? 1.0 : brightness) < 0 ? 0.0 : brightness;
  671. UIScreen* screen = [UIScreen mainScreen];
  672. screen.brightness = brightness;
  673. #endif
  674. }
  675. extern "C" float UnityGetBrightness()
  676. {
  677. #if !PLATFORM_TVOS
  678. UIScreen* screen = [UIScreen mainScreen];
  679. return screen.brightness;
  680. #else
  681. return 1.0f;
  682. #endif
  683. }
  684. extern "C" bool UnityIsFullscreen()
  685. {
  686. CGSize screenSize = [[[[DisplayManager Instance] mainDisplay] screen] bounds].size;
  687. CGSize viewSize = [[[[DisplayManager Instance] mainDisplay] view] bounds].size;
  688. return screenSize.width == viewSize.width && screenSize.height == viewSize.height;
  689. }
  690. #else
  691. extern "C" int UnityMainScreenRefreshRate()
  692. {
  693. return 90;
  694. }
  695. extern "C" void UnityStartFrameRendering()
  696. {
  697. [[DisplayManager Instance] startFrameRendering];
  698. }
  699. extern "C" void UnityDestroyUnityRenderSurfaces()
  700. {
  701. [[DisplayManager Instance].mainDisplay destroySurface];
  702. }
  703. extern "C" void UnitySetBrightness(float brightness)
  704. {
  705. }
  706. extern "C" float UnityGetBrightness()
  707. {
  708. return 1.0f;
  709. }
  710. extern "C" bool UnityIsFullscreen()
  711. {
  712. return false;
  713. }
  714. #endif