DisplayManager.mm 19 KB

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