UnityAppController+Rendering.mm 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #include "UnityAppController+Rendering.h"
  2. #include "UnityAppController+ViewHandling.h"
  3. #include "Unity/InternalProfiler.h"
  4. #include "Unity/UnityMetalSupport.h"
  5. #include "Unity/DisplayManager.h"
  6. #include "UI/UnityView.h"
  7. #include <dlfcn.h>
  8. // On some devices presenting render buffer may sporadically take long time to complete even with very simple scenes.
  9. // In these cases display link still fires at steady frame rate but input processing becomes stuttering.
  10. // As a workaround this switch disables display link during rendering a frame.
  11. // If you are running a GPU bound scene and experience frame drop you may want to disable this switch.
  12. #define ENABLE_DISPLAY_LINK_PAUSING 1
  13. #define ENABLE_RUNLOOP_ACCEPT_INPUT 1
  14. // _glesContextCreated was renamed to _renderingInited
  15. extern bool _renderingInited;
  16. extern bool _unityAppReady;
  17. extern bool _skipPresent;
  18. extern bool _didResignActive;
  19. static int _renderingAPI = 0;
  20. static int SelectRenderingAPIImpl();
  21. static bool _enableRunLoopAcceptInput = false;
  22. @implementation UnityAppController (Rendering)
  23. - (void)createDisplayLink
  24. {
  25. _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(repaintDisplayLink)];
  26. [self callbackFramerateChange: -1];
  27. [_displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
  28. }
  29. - (void)destroyDisplayLink
  30. {
  31. [_displayLink invalidate];
  32. _displayLink = nil;
  33. }
  34. - (void)processTouchEvents
  35. {
  36. // On multicore devices running at 60 FPS some touch event delivery isn't properly interleaved with graphical frames.
  37. // Running additional run loop here improves event handling in those cases.
  38. // Passing here an NSDate from the past invokes run loop only once.
  39. #if ENABLE_RUNLOOP_ACCEPT_INPUT
  40. // We get "NSInternalInconsistencyException: unexpected start state" exception if there are events queued and app is
  41. // going to background at the same time. This happens when we render additional frame after receiving
  42. // applicationWillResignActive. So check if we are supposed to ignore input.
  43. bool ignoreInput = [[UIApplication sharedApplication] isIgnoringInteractionEvents];
  44. if (!ignoreInput && _enableRunLoopAcceptInput)
  45. {
  46. static NSDate* past = [NSDate dateWithTimeIntervalSince1970: 0]; // the oldest date we can get
  47. [[NSRunLoop currentRunLoop] acceptInputForMode: NSDefaultRunLoopMode beforeDate: past];
  48. }
  49. #endif
  50. }
  51. - (void)repaintDisplayLink
  52. {
  53. #if ENABLE_DISPLAY_LINK_PAUSING
  54. _displayLink.paused = YES;
  55. #endif
  56. if (!_didResignActive)
  57. {
  58. UnityDisplayLinkCallback(_displayLink.timestamp);
  59. [self repaint];
  60. [self processTouchEvents];
  61. }
  62. #if ENABLE_DISPLAY_LINK_PAUSING
  63. _displayLink.paused = NO;
  64. #endif
  65. }
  66. - (void)repaint
  67. {
  68. #if UNITY_SUPPORT_ROTATION
  69. [self checkOrientationRequest];
  70. #endif
  71. [_unityView recreateRenderingSurfaceIfNeeded];
  72. [_unityView processKeyboard];
  73. UnityDeliverUIEvents();
  74. if (!UnityIsPaused())
  75. UnityRepaint();
  76. }
  77. - (void)callbackGfxInited
  78. {
  79. InitRendering();
  80. _renderingInited = true;
  81. [self shouldAttachRenderDelegate];
  82. [_unityView recreateRenderingSurface];
  83. [_renderDelegate mainDisplayInited: _mainDisplay.surface];
  84. _mainDisplay.surface->allowScreenshot = 1;
  85. }
  86. - (void)callbackPresent:(const UnityFrameStats*)frameStats
  87. {
  88. if (_skipPresent || _didResignActive)
  89. return;
  90. // metal needs special processing, because in case of airplay we need extra command buffers to present non-main screen drawables
  91. if (UnitySelectedRenderingAPI() == apiMetal)
  92. {
  93. #if UNITY_CAN_USE_METAL
  94. [[DisplayManager Instance].mainDisplay present];
  95. [[DisplayManager Instance] enumerateNonMainDisplaysWithBlock:^(DisplayConnection* conn) {
  96. PreparePresentNonMainScreenMTL((UnityDisplaySurfaceMTL*)conn.surface);
  97. }];
  98. #endif
  99. }
  100. else
  101. {
  102. [[DisplayManager Instance] present];
  103. }
  104. Profiler_FramePresent(frameStats);
  105. }
  106. - (void)callbackFramerateChange:(int)targetFPS
  107. {
  108. int maxFPS = (int)[UIScreen mainScreen].maximumFramesPerSecond;
  109. if (targetFPS <= 0)
  110. targetFPS = UnityGetTargetFPS();
  111. if (targetFPS > maxFPS)
  112. {
  113. targetFPS = maxFPS;
  114. UnitySetTargetFPS(targetFPS);
  115. return;
  116. }
  117. _enableRunLoopAcceptInput = (targetFPS == maxFPS && UnityDeviceCPUCount() > 1);
  118. _displayLink.preferredFramesPerSecond = targetFPS;
  119. }
  120. - (void)selectRenderingAPI
  121. {
  122. NSAssert(_renderingAPI == 0, @"[UnityAppController selectRenderingApi] called twice");
  123. _renderingAPI = SelectRenderingAPIImpl();
  124. }
  125. - (UnityRenderingAPI)renderingAPI
  126. {
  127. NSAssert(_renderingAPI != 0, @"[UnityAppController renderingAPI] called before [UnityAppController selectRenderingApi]");
  128. return (UnityRenderingAPI)_renderingAPI;
  129. }
  130. @end
  131. extern "C" void UnityGfxInitedCallback()
  132. {
  133. [GetAppController() callbackGfxInited];
  134. }
  135. extern "C" void UnityPresentContextCallback(struct UnityFrameStats const* unityFrameStats)
  136. {
  137. [GetAppController() callbackPresent: unityFrameStats];
  138. }
  139. extern "C" void UnityFramerateChangeCallback(int targetFPS)
  140. {
  141. [GetAppController() callbackFramerateChange: targetFPS];
  142. }
  143. extern "C" void UnityInitMainScreenRenderingCallback()
  144. {
  145. [GetAppController().mainDisplay initRendering];
  146. }
  147. static NSBundle* _MetalBundle = nil;
  148. static id<MTLDevice> _MetalDevice = nil;
  149. static bool IsMetalSupported(int /*api*/)
  150. {
  151. _MetalBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Metal.framework"];
  152. if (_MetalBundle)
  153. {
  154. [_MetalBundle load];
  155. _MetalDevice = ((MTLCreateSystemDefaultDeviceFunc)::dlsym(dlopen(0, RTLD_LOCAL | RTLD_LAZY), "MTLCreateSystemDefaultDevice"))();
  156. if (_MetalDevice)
  157. return true;
  158. }
  159. [_MetalBundle unload];
  160. return false;
  161. }
  162. static int SelectRenderingAPIImpl()
  163. {
  164. const int api = UnityGetRenderingAPI();
  165. if (api == apiMetal && IsMetalSupported(0))
  166. return api;
  167. #if TARGET_IPHONE_SIMULATOR || TARGET_TVOS_SIMULATOR
  168. return apiNoGraphics;
  169. #else
  170. assert(false);
  171. return 0;
  172. #endif
  173. }
  174. extern "C" NSBundle* UnityGetMetalBundle()
  175. {
  176. return _MetalBundle;
  177. }
  178. extern "C" MTLDeviceRef UnityGetMetalDevice() { return _MetalDevice; }
  179. extern "C" MTLCommandQueueRef UnityGetMetalCommandQueue() { return ((UnityDisplaySurfaceMTL*)GetMainDisplaySurface())->commandQueue; }
  180. extern "C" MTLCommandQueueRef UnityGetMetalDrawableCommandQueue() { return ((UnityDisplaySurfaceMTL*)GetMainDisplaySurface())->drawableCommandQueue; }
  181. extern "C" int UnitySelectedRenderingAPI() { return _renderingAPI; }
  182. extern "C" UnityRenderBufferHandle UnityBackbufferColor() { return GetMainDisplaySurface()->unityColorBuffer; }
  183. extern "C" UnityRenderBufferHandle UnityBackbufferDepth() { return GetMainDisplaySurface()->unityDepthBuffer; }
  184. extern "C" void DisplayManagerEndFrameRendering() { [[DisplayManager Instance] endFrameRendering]; }
  185. extern "C" void UnityPrepareScreenshot() { UnitySetRenderTarget(GetMainDisplaySurface()->unityColorBuffer, GetMainDisplaySurface()->unityDepthBuffer); }
  186. extern "C" void UnityRepaint()
  187. {
  188. @autoreleasepool
  189. {
  190. Profiler_FrameStart();
  191. UnityPlayerLoop();
  192. Profiler_FrameEnd();
  193. }
  194. }