MetalHelper.mm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. #include "UnityTrampolineCompatibility.h"
  2. #include "UnityRendering.h"
  3. #if UNITY_CAN_USE_METAL
  4. #include "UnityMetalSupport.h"
  5. #include <QuartzCore/QuartzCore.h>
  6. #include <libkern/OSAtomic.h>
  7. #if UNITY_TRAMPOLINE_IN_USE
  8. #include "UnityAppController.h"
  9. #include "CVTextureCache.h"
  10. #endif
  11. #include "ObjCRuntime.h"
  12. #if UNITY_TRAMPOLINE_IN_USE
  13. static Class MTLTextureDescriptorClass;
  14. #else
  15. extern Class MTLTextureDescriptorClass;
  16. #endif
  17. extern "C" void UnityAddNewMetalAPIImplIfNeeded(id<MTLDevice> device)
  18. {
  19. #if UNITY_TRAMPOLINE_IN_USE
  20. MTLTextureDescriptorClass = [UnityGetMetalBundle() classNamed: @"MTLTextureDescriptor"];
  21. #endif
  22. #if !(TARGET_IPHONE_SIMULATOR && defined(__IPHONE_13_0)) && !(TARGET_TVOS_SIMULATOR && defined(__TVOS_13_0))
  23. if (![device respondsToSelector: @selector(supportsTextureSampleCount:)])
  24. {
  25. IMP MTLDevice_supportsTextureSampleCount_IMP = imp_implementationWithBlock(^BOOL(id _self, NSUInteger sampleCount) {
  26. return sampleCount == 1 || sampleCount == 4;
  27. });
  28. class_replaceMethod(object_getClass(device), @selector(supportsTextureSampleCount:), MTLDevice_supportsTextureSampleCount_IMP, MTLDevice_supportsTextureSampleCount_Enc);
  29. }
  30. // usage is dynamic property so there will be no selectors, that is why we check for property itself
  31. // if property is missed it is fine to add fake selectors
  32. if (class_getProperty(MTLTextureDescriptorClass, "usage") == 0)
  33. {
  34. IMP MTLTextureDescriptor_SetUsage_IMP = imp_implementationWithBlock(^void(id _self, MTLTextureUsage usage) {});
  35. class_replaceMethod(MTLTextureDescriptorClass, @selector(setUsage:), MTLTextureDescriptor_SetUsage_IMP, MTLTextureDescriptor_setUsage_Enc);
  36. IMP MTLTextureDescriptor_GetUsage_IMP = imp_implementationWithBlock(^MTLTextureUsage(id _self) { return MTLTextureUsageUnknown; });
  37. class_replaceMethod(MTLTextureDescriptorClass, @selector(usage), MTLTextureDescriptor_GetUsage_IMP, MTLTextureDescriptor_usage_Enc);
  38. }
  39. #endif
  40. }
  41. extern "C" void InitRenderingMTL()
  42. {
  43. #if UNITY_TRAMPOLINE_IN_USE
  44. extern bool _supportsMSAA;
  45. _supportsMSAA = true;
  46. #endif
  47. }
  48. static MTLPixelFormat GetColorFormatForSurface(const UnityDisplaySurfaceMTL* surface)
  49. {
  50. MTLPixelFormat colorFormat = surface->srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
  51. #if PLATFORM_IOS && UNITY_HAS_IOSSDK_10_0
  52. if (surface->wideColor)
  53. colorFormat = surface->srgb ? MTLPixelFormatBGR10_XR_sRGB : MTLPixelFormatBGR10_XR;
  54. #elif PLATFORM_OSX && __MAC_10_12
  55. if (surface->wideColor)
  56. colorFormat = MTLPixelFormatRGBA16Float;
  57. #endif
  58. return colorFormat;
  59. }
  60. extern "C" void CreateSystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  61. {
  62. DestroySystemRenderingSurfaceMTL(surface);
  63. MTLPixelFormat colorFormat = GetColorFormatForSurface(surface);
  64. surface->layer.presentsWithTransaction = NO;
  65. surface->layer.drawsAsynchronously = YES;
  66. #if PLATFORM_OSX
  67. MetalUpdateDisplaySync();
  68. #endif
  69. CGFloat backgroundColorValues[] = {0, 0, 0, 1};
  70. #if PLATFORM_IOS || PLATFORM_OSX
  71. CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
  72. #else
  73. CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
  74. #endif
  75. #if (PLATFORM_IOS && UNITY_HAS_IOSSDK_10_0) || (PLATFORM_OSX && __MAC_10_12)
  76. if (surface->wideColor)
  77. colorSpaceRef = CGColorSpaceCreateWithName(surface->srgb ? kCGColorSpaceExtendedLinearSRGB : kCGColorSpaceExtendedSRGB);
  78. #endif
  79. CGColorRef backgroundColorRef = CGColorCreate(colorSpaceRef, backgroundColorValues);
  80. surface->layer.backgroundColor = backgroundColorRef; // retained automatically
  81. #if PLATFORM_OSX && __MAC_10_12
  82. surface->layer.colorspace = colorSpaceRef;
  83. #endif
  84. CGColorRelease(backgroundColorRef);
  85. CGColorSpaceRelease(colorSpaceRef);
  86. surface->layer.device = surface->device;
  87. surface->layer.pixelFormat = colorFormat;
  88. surface->layer.framebufferOnly = (surface->framebufferOnly != 0);
  89. surface->colorFormat = colorFormat;
  90. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass texture2DDescriptorWithPixelFormat: colorFormat width: surface->systemW height: surface->systemH mipmapped: NO];
  91. #if PLATFORM_OSX
  92. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
  93. #endif
  94. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  95. @synchronized(surface->layer)
  96. {
  97. OSAtomicCompareAndSwap32Barrier(0, 0, &surface->bufferCompleted);
  98. OSAtomicCompareAndSwap32Barrier(0, 0, &surface->bufferSwap);
  99. for (int i = 0; i < kUnityNumOffscreenSurfaces; i++)
  100. {
  101. // Allocating a proxy texture is cheap until it's being rendered to and the GPU driver does allocation
  102. surface->drawableProxyRT[i] = [surface->device newTextureWithDescriptor: txDesc];
  103. surface->drawableProxyRT[i].label = @"DrawableProxy";
  104. // We mostly need the proxy for some of its state like width, height and pixelFormat (not the actual memory) before we can get the real drawable
  105. // Making it empty discards its backed memory/contents
  106. for (int i = 0; i < kUnityNumOffscreenSurfaces; ++i)
  107. [surface->drawableProxyRT[i] setPurgeableState: MTLPurgeableStateEmpty];
  108. }
  109. }
  110. }
  111. extern "C" void CreateRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  112. {
  113. DestroyRenderingSurfaceMTL(surface);
  114. MTLPixelFormat colorFormat = GetColorFormatForSurface(surface);
  115. const int w = surface->targetW, h = surface->targetH;
  116. if (w != surface->systemW || h != surface->systemH || surface->useCVTextureCache)
  117. {
  118. #if PLATFORM_IOS || PLATFORM_TVOS
  119. if (surface->useCVTextureCache)
  120. surface->cvTextureCache = CreateCVTextureCache();
  121. if (surface->cvTextureCache)
  122. {
  123. surface->cvTextureCacheTexture = CreateReadableRTFromCVTextureCache(surface->cvTextureCache, surface->targetW, surface->targetH, &surface->cvPixelBuffer);
  124. surface->targetColorRT = GetMetalTextureFromCVTextureCache(surface->cvTextureCacheTexture);
  125. }
  126. else
  127. #endif
  128. {
  129. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass new];
  130. txDesc.textureType = MTLTextureType2D;
  131. txDesc.width = w;
  132. txDesc.height = h;
  133. txDesc.depth = 1;
  134. txDesc.pixelFormat = colorFormat;
  135. txDesc.arrayLength = 1;
  136. txDesc.mipmapLevelCount = 1;
  137. #if PLATFORM_OSX
  138. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
  139. #endif
  140. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  141. surface->targetColorRT = [surface->device newTextureWithDescriptor: txDesc];
  142. }
  143. surface->targetColorRT.label = @"targetColorRT";
  144. }
  145. if (surface->msaaSamples > 1)
  146. {
  147. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass new];
  148. txDesc.textureType = MTLTextureType2DMultisample;
  149. txDesc.width = w;
  150. txDesc.height = h;
  151. txDesc.depth = 1;
  152. txDesc.pixelFormat = colorFormat;
  153. txDesc.arrayLength = 1;
  154. txDesc.mipmapLevelCount = 1;
  155. txDesc.sampleCount = surface->msaaSamples;
  156. #if PLATFORM_OSX
  157. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModePrivate;
  158. #endif
  159. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  160. if (![surface->device supportsTextureSampleCount: txDesc.sampleCount])
  161. txDesc.sampleCount = 4;
  162. surface->targetAAColorRT = [surface->device newTextureWithDescriptor: txDesc];
  163. surface->targetAAColorRT.label = @"targetAAColorRT";
  164. if (!surface->targetColorRT)
  165. {
  166. MTLTextureDescriptor* txDescResolve = [txDesc copyWithZone: nil];
  167. txDescResolve.textureType = MTLTextureType2D;
  168. txDescResolve.sampleCount = 1;
  169. surface->targetColorRT = [surface->device newTextureWithDescriptor: txDescResolve];
  170. }
  171. }
  172. }
  173. extern "C" void DestroyRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  174. {
  175. surface->targetColorRT = nil;
  176. surface->targetAAColorRT = nil;
  177. if (surface->cvTextureCacheTexture)
  178. CFRelease(surface->cvTextureCacheTexture);
  179. if (surface->cvPixelBuffer)
  180. CFRelease(surface->cvPixelBuffer);
  181. if (surface->cvTextureCache)
  182. CFRelease(surface->cvTextureCache);
  183. surface->cvTextureCache = 0;
  184. }
  185. extern "C" void CreateSharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  186. {
  187. DestroySharedDepthbufferMTL(surface);
  188. #if PLATFORM_OSX
  189. MTLPixelFormat pixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  190. #else
  191. MTLPixelFormat pixelFormat = MTLPixelFormatDepth32Float;
  192. #endif
  193. MTLTextureDescriptor* depthTexDesc = [MTLTextureDescriptorClass texture2DDescriptorWithPixelFormat: pixelFormat width: surface->targetW height: surface->targetH mipmapped: NO];
  194. #if PLATFORM_OSX
  195. depthTexDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModePrivate;
  196. #endif
  197. depthTexDesc.usage = MTLTextureUsageRenderTarget;
  198. if (surface->msaaSamples > 1)
  199. {
  200. depthTexDesc.textureType = MTLTextureType2DMultisample;
  201. depthTexDesc.sampleCount = surface->msaaSamples;
  202. if (![surface->device supportsTextureSampleCount: depthTexDesc.sampleCount])
  203. depthTexDesc.sampleCount = 4;
  204. }
  205. surface->depthRB = [surface->device newTextureWithDescriptor: depthTexDesc];
  206. #if PLATFORM_OSX
  207. surface->stencilRB = surface->depthRB;
  208. #else
  209. MTLTextureDescriptor* stencilTexDesc = [MTLTextureDescriptorClass texture2DDescriptorWithPixelFormat: MTLPixelFormatStencil8 width: surface->targetW height: surface->targetH mipmapped: NO];
  210. stencilTexDesc.usage = MTLTextureUsageRenderTarget;
  211. if (surface->msaaSamples > 1)
  212. {
  213. stencilTexDesc.textureType = MTLTextureType2DMultisample;
  214. stencilTexDesc.sampleCount = surface->msaaSamples;
  215. if (![surface->device supportsTextureSampleCount: stencilTexDesc.sampleCount])
  216. stencilTexDesc.sampleCount = 4;
  217. }
  218. surface->stencilRB = [surface->device newTextureWithDescriptor: stencilTexDesc];
  219. #endif
  220. }
  221. extern "C" void DestroySharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  222. {
  223. surface->depthRB = nil;
  224. surface->stencilRB = nil;
  225. }
  226. extern "C" void CreateUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  227. {
  228. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1 };
  229. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1 };
  230. surface->systemColorRB = surface->drawableProxyRT[0];
  231. if (surface->targetAAColorRT)
  232. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->targetAAColorRT, surface->targetColorRT, &tgt_desc, nil);
  233. else if (surface->targetColorRT)
  234. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->targetColorRT, nil, &tgt_desc, nil);
  235. else
  236. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->systemColorRB, nil, &tgt_desc, surface);
  237. surface->unityDepthBuffer = UnityCreateExternalDepthSurfaceMTL(surface->unityDepthBuffer, surface->depthRB, surface->stencilRB, &tgt_desc);
  238. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, surface->systemColorRB, nil, &sys_desc, surface);
  239. surface->systemDepthBuffer = UnityCreateDummySurface(surface->systemDepthBuffer, false, &sys_desc);
  240. }
  241. extern "C" void DestroySystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  242. {
  243. // before we needed to nil surface->systemColorRB (to release drawable we get from the view)
  244. // but after we switched to proxy rt this is no longer needed
  245. // even more it is harmful when running rendering on another thread (as is default now)
  246. // as on render thread we do StartFrameRenderingMTL/AcquireDrawableMTL/EndFrameRenderingMTL
  247. // and DestroySystemRenderingSurfaceMTL comes on main thread so we might end up with race condition for no reason
  248. }
  249. extern "C" void DestroyUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  250. {
  251. UnityDestroyExternalSurface(surface->unityColorBuffer);
  252. UnityDestroyExternalSurface(surface->systemColorBuffer);
  253. surface->unityColorBuffer = surface->systemColorBuffer = 0;
  254. UnityDestroyExternalSurface(surface->unityDepthBuffer);
  255. UnityDestroyExternalSurface(surface->systemDepthBuffer);
  256. surface->unityDepthBuffer = surface->systemDepthBuffer = 0;
  257. }
  258. extern "C" void PreparePresentMTL(UnityDisplaySurfaceMTL* surface)
  259. {
  260. if (surface->targetColorRT)
  261. UnityBlitToBackbuffer(surface->unityColorBuffer, surface->systemColorBuffer, surface->systemDepthBuffer);
  262. #if UNITY_TRAMPOLINE_IN_USE
  263. APP_CONTROLLER_RENDER_PLUGIN_METHOD(onFrameResolved);
  264. #endif
  265. }
  266. extern "C" void PresentMTL(UnityDisplaySurfaceMTL* surface)
  267. {
  268. if (surface->drawable)
  269. [UnityCurrentMTLCommandBuffer() presentDrawable: surface->drawable];
  270. }
  271. extern "C" MTLTextureRef AcquireDrawableMTL(UnityDisplaySurfaceMTL* surface)
  272. {
  273. if (!surface)
  274. return nil;
  275. if (!surface->drawable)
  276. surface->drawable = [surface->layer nextDrawable];
  277. // on A7 SoC nextDrawable may be nil before locking the screen
  278. if (!surface->drawable)
  279. return nil;
  280. surface->systemColorRB = [surface->drawable texture];
  281. return surface->systemColorRB;
  282. }
  283. extern "C" void StartFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  284. {
  285. // we will acquire drawable lazily in AcquireDrawableMTL
  286. surface->drawable = nil;
  287. #if PLATFORM_OSX
  288. bool bufferSwap = OSAtomicCompareAndSwap32Barrier(1, 0, &surface->bufferSwap);
  289. if (bufferSwap || surface->bufferCompleted == 1)
  290. {
  291. MTLTextureRef texture0 = surface->drawableProxyRT[0];
  292. MTLTextureRef texture1 = surface->drawableProxyRT[1];
  293. surface->drawableProxyRT[0] = texture1;
  294. surface->drawableProxyRT[1] = texture0;
  295. }
  296. #endif
  297. surface->systemColorRB = surface->drawableProxyRT[0];
  298. // screen disconnect notification comes asynchronously
  299. // even better when preparing render we might still have [UIScreen screens].count == 2, but drawable would be nil already
  300. if (surface->systemColorRB)
  301. {
  302. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1};
  303. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1};
  304. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, surface->systemColorRB, nil, &sys_desc, surface);
  305. if (surface->targetColorRT == nil)
  306. {
  307. if (surface->targetAAColorRT)
  308. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->targetAAColorRT, surface->systemColorRB, &tgt_desc, surface);
  309. else
  310. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->systemColorRB, nil, &tgt_desc, surface);
  311. }
  312. }
  313. else
  314. {
  315. UnityDisableRenderBuffers(surface->unityColorBuffer, surface->unityDepthBuffer);
  316. }
  317. }
  318. extern "C" void EndFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  319. {
  320. surface->systemColorRB = nil;
  321. surface->drawable = nil;
  322. }
  323. #else
  324. extern "C" void InitRenderingMTL() {}
  325. extern "C" void CreateSystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  326. extern "C" void CreateRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  327. extern "C" void DestroyRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  328. extern "C" void CreateSharedDepthbufferMTL(UnityDisplaySurfaceMTL*) {}
  329. extern "C" void DestroySharedDepthbufferMTL(UnityDisplaySurfaceMTL*) {}
  330. extern "C" void CreateUnityRenderBuffersMTL(UnityDisplaySurfaceMTL*) {}
  331. extern "C" void DestroySystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  332. extern "C" void DestroyUnityRenderBuffersMTL(UnityDisplaySurfaceMTL*) {}
  333. extern "C" void StartFrameRenderingMTL(UnityDisplaySurfaceMTL*) {}
  334. extern "C" void EndFrameRenderingMTL(UnityDisplaySurfaceMTL*) {}
  335. extern "C" void PreparePresentMTL(UnityDisplaySurfaceMTL*) {}
  336. extern "C" void PresentMTL(UnityDisplaySurfaceMTL*) {}
  337. extern "C" MTLTextureRef AcquireDrawableMTL(UnityDisplaySurfaceMTL*) { return nil; }
  338. extern "C" void UnityAddNewMetalAPIImplIfNeeded(id<MTLDevice> device) {}
  339. #endif