UnityView.mm 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #include "UnityView.h"
  2. #include "UnityAppController.h"
  3. #include "UnityAppController+Rendering.h"
  4. #include "OrientationSupport.h"
  5. #include "Unity/DisplayManager.h"
  6. #include "Unity/UnityMetalSupport.h"
  7. #include "Unity/ObjCRuntime.h"
  8. extern bool _renderingInited;
  9. extern bool _unityAppReady;
  10. extern bool _skipPresent;
  11. @implementation UnityView
  12. {
  13. CGSize _surfaceSize;
  14. }
  15. @synthesize contentOrientation = _curOrientation;
  16. - (void)onUpdateSurfaceSize:(CGSize)size
  17. {
  18. _surfaceSize = size;
  19. CGSize systemRenderSize = CGSizeMake(size.width * self.contentScaleFactor, size.height * self.contentScaleFactor);
  20. _curOrientation = (ScreenOrientation)UnityReportResizeView((unsigned)systemRenderSize.width, (unsigned)systemRenderSize.height, _curOrientation);
  21. ReportSafeAreaChangeForView(self);
  22. }
  23. - (void)boundsUpdated
  24. {
  25. [self onUpdateSurfaceSize: self.bounds.size];
  26. }
  27. - (void)initImpl:(CGRect)frame scaleFactor:(CGFloat)scale
  28. {
  29. #if !PLATFORM_TVOS
  30. self.multipleTouchEnabled = YES;
  31. self.exclusiveTouch = YES;
  32. #endif
  33. self.contentScaleFactor = scale;
  34. self.isAccessibilityElement = TRUE;
  35. self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
  36. #if UNITY_TVOS
  37. _curOrientation = UNITY_TVOS_ORIENTATION;
  38. #endif
  39. [self onUpdateSurfaceSize: frame.size];
  40. }
  41. - (id)initWithFrame:(CGRect)frame scaleFactor:(CGFloat)scale;
  42. {
  43. if ((self = [super initWithFrame: frame]))
  44. [self initImpl: frame scaleFactor: scale];
  45. return self;
  46. }
  47. - (id)initWithFrame:(CGRect)frame
  48. {
  49. if ((self = [super initWithFrame: frame]))
  50. [self initImpl: frame scaleFactor: 1.0f];
  51. return self;
  52. }
  53. - (id)initFromMainScreen
  54. {
  55. CGRect frame = [UIScreen mainScreen].bounds;
  56. CGFloat scale = UnityScreenScaleFactor([UIScreen mainScreen]);
  57. if ((self = [super initWithFrame: frame]))
  58. [self initImpl: frame scaleFactor: scale];
  59. return self;
  60. }
  61. - (void)layoutSubviews
  62. {
  63. if (_surfaceSize.width != self.bounds.size.width || _surfaceSize.height != self.bounds.size.height)
  64. _shouldRecreateView = YES;
  65. [self onUpdateSurfaceSize: self.bounds.size];
  66. for (UIView* subView in self.subviews)
  67. {
  68. if ([subView respondsToSelector: @selector(onUnityUpdateViewLayout)])
  69. [subView performSelector: @selector(onUnityUpdateViewLayout)];
  70. }
  71. [super layoutSubviews];
  72. }
  73. - (void)safeAreaInsetsDidChange
  74. {
  75. ReportSafeAreaChangeForView(self);
  76. }
  77. - (void)recreateRenderingSurfaceIfNeeded
  78. {
  79. float requestedContentScaleFactor = UnityScreenScaleFactor([UIScreen mainScreen]);
  80. if (abs(requestedContentScaleFactor - self.contentScaleFactor) > FLT_EPSILON)
  81. {
  82. self.contentScaleFactor = requestedContentScaleFactor;
  83. [self onUpdateSurfaceSize: self.bounds.size];
  84. }
  85. unsigned requestedW, requestedH; UnityGetRenderingResolution(&requestedW, &requestedH);
  86. int requestedMSAA = UnityGetDesiredMSAASampleCount(1);
  87. int requestedSRGB = UnityGetSRGBRequested();
  88. int requestedWideColor = UnityGetWideColorRequested();
  89. int requestedHDR = UnityGetHDRModeRequested();
  90. int requestedMemorylessDepth = UnityMetalMemorylessDepth();
  91. UnityDisplaySurfaceBase* surf = GetMainDisplaySurface();
  92. if (_shouldRecreateView == YES
  93. || surf->targetW != requestedW || surf->targetH != requestedH
  94. || surf->disableDepthAndStencil != UnityDisableDepthAndStencilBuffers()
  95. || surf->msaaSamples != requestedMSAA
  96. || surf->srgb != requestedSRGB
  97. || surf->wideColor != requestedWideColor
  98. || surf->hdr != requestedHDR
  99. || surf->memorylessDepth != requestedMemorylessDepth
  100. )
  101. {
  102. [self recreateRenderingSurface];
  103. }
  104. }
  105. - (void)recreateRenderingSurface
  106. {
  107. if (_renderingInited)
  108. {
  109. unsigned requestedW, requestedH;
  110. UnityGetRenderingResolution(&requestedW, &requestedH);
  111. RenderingSurfaceParams params =
  112. {
  113. .msaaSampleCount = UnityGetDesiredMSAASampleCount(1),
  114. .renderW = (int)requestedW,
  115. .renderH = (int)requestedH,
  116. .srgb = UnityGetSRGBRequested(),
  117. .wideColor = UnityGetWideColorRequested(),
  118. .hdr = UnityGetHDRModeRequested(),
  119. .metalFramebufferOnly = UnityMetalFramebufferOnly(),
  120. .metalMemorylessDepth = UnityMetalMemorylessDepth(),
  121. .disableDepthAndStencil = UnityDisableDepthAndStencilBuffers(),
  122. .useCVTextureCache = 0,
  123. };
  124. APP_CONTROLLER_RENDER_PLUGIN_METHOD_ARG(onBeforeMainDisplaySurfaceRecreate, &params);
  125. [GetMainDisplay() recreateSurface: params];
  126. // actually poke unity about updated back buffer and notify that extents were changed
  127. UnityReportBackbufferChange(GetMainDisplaySurface()->unityColorBuffer, GetMainDisplaySurface()->unityDepthBuffer);
  128. APP_CONTROLLER_RENDER_PLUGIN_METHOD(onAfterMainDisplaySurfaceRecreate);
  129. if (_unityAppReady)
  130. {
  131. // seems like ios sometimes got confused about abrupt swap chain destroy
  132. // draw 2 times to fill "both" buffers (we assume double buffering)
  133. // present only once to make sure correct image goes to CA
  134. // if we are calling this from inside repaint, second draw and present will be done automatically
  135. // please note that we still need to pretend we did come from displaylink to make sure vsync magic works
  136. // NOTE: unity does handle "draw frame with exact same timestamp" just fine
  137. _skipPresent = true;
  138. if (!UnityIsPaused())
  139. {
  140. UnityDisplayLinkCallback(GetAppController().unityDisplayLink.timestamp);
  141. UnityRepaint();
  142. // we are not inside repaint so we need to draw second time ourselves
  143. if (_viewIsRotating)
  144. {
  145. UnityDisplayLinkCallback(GetAppController().unityDisplayLink.timestamp);
  146. UnityRepaint();
  147. }
  148. }
  149. _skipPresent = false;
  150. }
  151. }
  152. _shouldRecreateView = NO;
  153. }
  154. @end
  155. @implementation UnityView (Deprecated)
  156. - (void)recreateGLESSurfaceIfNeeded { [self recreateRenderingSurfaceIfNeeded]; }
  157. - (void)recreateGLESSurface { [self recreateRenderingSurface]; }
  158. @end
  159. static Class UnityRenderingView_LayerClassMTL(id self_, SEL _cmd)
  160. {
  161. return NSClassFromString(@"CAMetalLayer");
  162. }
  163. static Class UnityRenderingView_LayerClassNULL(id self_, SEL _cmd)
  164. {
  165. return NSClassFromString(@"CALayer");
  166. }
  167. @implementation UnityRenderingView
  168. + (Class)layerClass
  169. {
  170. return nil;
  171. }
  172. + (void)InitializeForAPI:(UnityRenderingAPI)api
  173. {
  174. IMP layerClassImpl = api == apiMetal ? (IMP)UnityRenderingView_LayerClassMTL : (IMP)UnityRenderingView_LayerClassNULL;
  175. class_replaceMethod(object_getClass([UnityRenderingView class]), @selector(layerClass), layerClassImpl, UIView_LayerClass_Enc);
  176. }
  177. @end
  178. void ReportSafeAreaChangeForView(UIView* view)
  179. {
  180. CGRect safeArea = ComputeSafeArea(view);
  181. UnityReportSafeAreaChange(safeArea.origin.x, safeArea.origin.y,
  182. safeArea.size.width, safeArea.size.height);
  183. switch (UnityDeviceGeneration())
  184. {
  185. case deviceiPhoneXR:
  186. case deviceiPhone11:
  187. {
  188. const float x = 184, y = 1726, w = 460, h = 66;
  189. UnityReportDisplayCutouts(&x, &y, &w, &h, 1);
  190. break;
  191. }
  192. case deviceiPhoneX:
  193. case deviceiPhoneXS:
  194. case deviceiPhone11Pro:
  195. {
  196. const float x = 250, y = 2346, w = 625, h = 90;
  197. UnityReportDisplayCutouts(&x, &y, &w, &h, 1);
  198. break;
  199. }
  200. case deviceiPhoneXSMax:
  201. case deviceiPhone11ProMax:
  202. {
  203. const float x = 308, y = 2598, w = 626, h = 90;
  204. UnityReportDisplayCutouts(&x, &y, &w, &h, 1);
  205. break;
  206. }
  207. default:
  208. UnityReportDisplayCutouts(nullptr, nullptr, nullptr, nullptr, 0);
  209. }
  210. }
  211. CGRect ComputeSafeArea(UIView* view)
  212. {
  213. CGSize screenSize = view.bounds.size;
  214. CGRect screenRect = CGRectMake(0, 0, screenSize.width, screenSize.height);
  215. UIEdgeInsets insets = [view safeAreaInsets];
  216. screenRect.origin.x += insets.left;
  217. screenRect.origin.y += insets.bottom; // Unity uses bottom left as the origin
  218. screenRect.size.width -= insets.left + insets.right;
  219. screenRect.size.height -= insets.top + insets.bottom;
  220. float scale = view.contentScaleFactor;
  221. // Truncate safe area size because in some cases (for example when Display zoom is turned on)
  222. // it might become larger than Screen.width/height which are returned as ints.
  223. screenRect.origin.x = (unsigned)(screenRect.origin.x * scale);
  224. screenRect.origin.y = (unsigned)(screenRect.origin.y * scale);
  225. screenRect.size.width = (unsigned)(screenRect.size.width * scale);
  226. screenRect.size.height = (unsigned)(screenRect.size.height * scale);
  227. return screenRect;
  228. }