MetalHelper.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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 InitRenderingMTL()
  18. {
  19. #if UNITY_TRAMPOLINE_IN_USE
  20. MTLTextureDescriptorClass = NSClassFromString(@"MTLTextureDescriptor");
  21. #endif
  22. }
  23. static MTLPixelFormat GetColorFormatForSurface(const UnityDisplaySurfaceMTL* surface)
  24. {
  25. MTLPixelFormat colorFormat = surface->srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
  26. #if PLATFORM_IOS || PLATFORM_TVOS
  27. if (surface->wideColor && UnityIsWideColorSupported())
  28. colorFormat = surface->srgb ? MTLPixelFormatBGR10_XR_sRGB : MTLPixelFormatBGR10_XR;
  29. #elif PLATFORM_OSX
  30. if (surface->hdr)
  31. {
  32. if (@available(macOS 10.15, *))
  33. {
  34. colorFormat = UnityHDRSurfaceDepth() == 0 ? MTLPixelFormatBGR10A2Unorm : MTLPixelFormatRGBA16Float;
  35. }
  36. }
  37. else if (surface->wideColor)
  38. colorFormat = MTLPixelFormatRGBA16Float;
  39. #endif
  40. return colorFormat;
  41. }
  42. static uint32_t GetCVPixelFormatForSurface(const UnityDisplaySurfaceMTL* surface)
  43. {
  44. // this makes sense only for ios (at least we dont support this on macos)
  45. uint32_t colorFormat = kCVPixelFormatType_32BGRA;
  46. #if PLATFORM_IOS || PLATFORM_TVOS
  47. if (surface->wideColor && UnityIsWideColorSupported())
  48. colorFormat = kCVPixelFormatType_30RGB;
  49. #endif
  50. return colorFormat;
  51. }
  52. extern "C" void CreateSystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  53. {
  54. DestroySystemRenderingSurfaceMTL(surface);
  55. MTLPixelFormat colorFormat = GetColorFormatForSurface(surface);
  56. surface->layer.presentsWithTransaction = NO;
  57. surface->layer.drawsAsynchronously = YES;
  58. #if !PLATFORM_OSX
  59. if (UnityPreserveFramebufferAlpha())
  60. {
  61. const CGFloat components[] = {1.0f, 1.0f, 1.0f, 0.0f};
  62. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  63. CGColorRef color = CGColorCreate(colorSpace, components);
  64. surface->layer.opaque = NO;
  65. surface->layer.backgroundColor = color;
  66. CGColorRelease(color);
  67. CGColorSpaceRelease(colorSpace);
  68. }
  69. #endif
  70. #if PLATFORM_OSX
  71. surface->layer.opaque = YES;
  72. MetalUpdateDisplaySync();
  73. #endif
  74. #if PLATFORM_OSX
  75. CGColorSpaceRef colorSpaceRef = nil;
  76. if (surface->hdr)
  77. colorSpaceRef = UnityHDRSurfaceDepth() == 0 ? CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceITUR_2020_PQ_EOTF")) : CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceITUR_709"));
  78. else if (surface->wideColor)
  79. colorSpaceRef = CGColorSpaceCreateWithName(surface->srgb ? kCGColorSpaceExtendedLinearSRGB : kCGColorSpaceExtendedSRGB);
  80. else
  81. colorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
  82. surface->layer.colorspace = colorSpaceRef;
  83. CGColorSpaceRelease(colorSpaceRef);
  84. #endif
  85. // Update the native screen resolution
  86. UnityUpdateDrawableSize(surface);
  87. surface->layer.device = surface->device;
  88. surface->layer.pixelFormat = colorFormat;
  89. surface->layer.framebufferOnly = (surface->framebufferOnly != 0);
  90. surface->colorFormat = (unsigned)colorFormat;
  91. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass texture2DDescriptorWithPixelFormat: colorFormat width: surface->systemW height: surface->systemH mipmapped: NO];
  92. #if PLATFORM_OSX
  93. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
  94. #endif
  95. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  96. @synchronized(surface->layer)
  97. {
  98. #if PLATFORM_OSX
  99. OSAtomicCompareAndSwap32Barrier(0, 0, &surface->bufferCompleted);
  100. OSAtomicCompareAndSwap32Barrier(0, 0, &surface->bufferSwap);
  101. #endif
  102. for (int i = 0; i < kUnityNumOffscreenSurfaces; i++)
  103. {
  104. // Allocating a proxy texture is cheap until it's being rendered to and the GPU driver does allocation
  105. surface->drawableProxyRT[i] = [surface->device newTextureWithDescriptor: txDesc];
  106. surface->drawableProxyRT[i].label = @"DrawableProxy";
  107. // 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
  108. // Making it empty discards its backed memory/contents
  109. [surface->drawableProxyRT[i] setPurgeableState: MTLPurgeableStateEmpty];
  110. }
  111. }
  112. }
  113. extern "C" void CreateRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  114. {
  115. DestroyRenderingSurfaceMTL(surface);
  116. MTLPixelFormat colorFormat = GetColorFormatForSurface(surface);
  117. const int w = surface->targetW, h = surface->targetH;
  118. if (w != surface->systemW || h != surface->systemH || surface->useCVTextureCache)
  119. {
  120. #if PLATFORM_IOS || PLATFORM_TVOS
  121. if (surface->useCVTextureCache)
  122. surface->cvTextureCache = CreateCVTextureCache();
  123. if (surface->cvTextureCache)
  124. {
  125. surface->cvTextureCacheTexture = CreateReadableRTFromCVTextureCache2(surface->cvTextureCache, surface->targetW, surface->targetH,
  126. GetCVPixelFormatForSurface(surface), colorFormat, &surface->cvPixelBuffer);
  127. surface->targetColorRT = GetMetalTextureFromCVTextureCache(surface->cvTextureCacheTexture);
  128. }
  129. else
  130. #endif
  131. {
  132. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass new];
  133. txDesc.textureType = MTLTextureType2D;
  134. txDesc.width = w;
  135. txDesc.height = h;
  136. txDesc.depth = 1;
  137. txDesc.pixelFormat = colorFormat;
  138. txDesc.arrayLength = 1;
  139. txDesc.mipmapLevelCount = 1;
  140. #if PLATFORM_OSX
  141. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
  142. #endif
  143. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  144. surface->targetColorRT = [surface->device newTextureWithDescriptor: txDesc];
  145. }
  146. surface->targetColorRT.label = @"targetColorRT";
  147. }
  148. if (surface->msaaSamples > 1)
  149. {
  150. MTLTextureDescriptor* txDesc = [MTLTextureDescriptorClass new];
  151. txDesc.textureType = MTLTextureType2DMultisample;
  152. txDesc.width = w;
  153. txDesc.height = h;
  154. txDesc.depth = 1;
  155. txDesc.pixelFormat = colorFormat;
  156. txDesc.arrayLength = 1;
  157. txDesc.mipmapLevelCount = 1;
  158. txDesc.sampleCount = surface->msaaSamples;
  159. #if PLATFORM_OSX || (TARGET_IPHONE_SIMULATOR || TARGET_TVOS_SIMULATOR)
  160. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModePrivate;
  161. #endif
  162. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  163. if (![surface->device supportsTextureSampleCount: txDesc.sampleCount])
  164. txDesc.sampleCount = 4;
  165. surface->targetAAColorRT = [surface->device newTextureWithDescriptor: txDesc];
  166. surface->targetAAColorRT.label = @"targetAAColorRT";
  167. }
  168. }
  169. extern "C" void DestroyRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  170. {
  171. surface->targetColorRT = nil;
  172. surface->targetAAColorRT = nil;
  173. if (surface->cvTextureCacheTexture)
  174. CFRelease(surface->cvTextureCacheTexture);
  175. if (surface->cvPixelBuffer)
  176. CFRelease(surface->cvPixelBuffer);
  177. if (surface->cvTextureCache)
  178. CFRelease(surface->cvTextureCache);
  179. surface->cvTextureCache = 0;
  180. }
  181. extern "C" void CreateSharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  182. {
  183. DestroySharedDepthbufferMTL(surface);
  184. if (surface->disableDepthAndStencil)
  185. return;
  186. MTLPixelFormat pixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  187. MTLTextureDescriptor* depthTexDesc = [MTLTextureDescriptorClass texture2DDescriptorWithPixelFormat: pixelFormat width: surface->targetW height: surface->targetH mipmapped: NO];
  188. #if PLATFORM_OSX || (TARGET_IPHONE_SIMULATOR || TARGET_TVOS_SIMULATOR)
  189. depthTexDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModePrivate;
  190. #endif
  191. #if PLATFORM_IOS || PLATFORM_TVOS
  192. if (surface->memorylessDepth)
  193. depthTexDesc.storageMode = MTLStorageModeMemoryless;
  194. #endif
  195. depthTexDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  196. if (surface->msaaSamples > 1)
  197. {
  198. depthTexDesc.textureType = MTLTextureType2DMultisample;
  199. depthTexDesc.sampleCount = surface->msaaSamples;
  200. if (![surface->device supportsTextureSampleCount: depthTexDesc.sampleCount])
  201. depthTexDesc.sampleCount = 4;
  202. }
  203. surface->depthRB = [surface->device newTextureWithDescriptor: depthTexDesc];
  204. surface->stencilRB = surface->depthRB;
  205. }
  206. extern "C" void DestroySharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  207. {
  208. surface->depthRB = nil;
  209. surface->stencilRB = nil;
  210. }
  211. extern "C" void CreateUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  212. {
  213. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1 };
  214. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1 };
  215. // To avoid race condition with EndFrameRenderingMTL where systemColorRB is nulled we store it here
  216. MTLTextureRef systemColorRB = surface->drawableProxyRT[0];
  217. surface->systemColorRB = systemColorRB;
  218. // we could unify all of it with ugly chain of ternary operators but what if karma exists?
  219. if (surface->targetColorRT)
  220. {
  221. // render to interim RT: we do NOT need to request drawable
  222. MTLTextureRef texRender = surface->targetAAColorRT ? surface->targetAAColorRT : surface->targetColorRT;
  223. MTLTextureRef texResolve = surface->targetAAColorRT ? surface->targetColorRT : nil;
  224. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, texRender, texResolve, &tgt_desc, nil);
  225. }
  226. else
  227. {
  228. // render to backbuffer directly: we will request drawable hence we need to pass surface
  229. MTLTextureRef texRender = surface->targetAAColorRT ? surface->targetAAColorRT : systemColorRB;
  230. MTLTextureRef texResolve = surface->targetAAColorRT ? systemColorRB : nil;
  231. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, texRender, texResolve, &tgt_desc, surface);
  232. }
  233. if (surface->depthRB)
  234. surface->unityDepthBuffer = UnityCreateExternalDepthSurfaceMTL(surface->unityDepthBuffer, surface->depthRB, surface->stencilRB, &tgt_desc);
  235. else
  236. surface->unityDepthBuffer = UnityCreateDummySurface(surface->unityDepthBuffer, false, &tgt_desc);
  237. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, systemColorRB, nil, &sys_desc, surface);
  238. surface->systemDepthBuffer = UnityCreateDummySurface(surface->systemDepthBuffer, false, &sys_desc);
  239. }
  240. extern "C" void DestroySystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  241. {
  242. // before we needed to nil surface->systemColorRB (to release drawable we get from the view)
  243. // but after we switched to proxy rt this is no longer needed
  244. // even more it is harmful when running rendering on another thread (as is default now)
  245. // as on render thread we do StartFrameRenderingMTL/AcquireDrawableMTL/EndFrameRenderingMTL
  246. // and DestroySystemRenderingSurfaceMTL comes on main thread so we might end up with race condition for no reason
  247. }
  248. extern "C" void DestroyUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  249. {
  250. UnityDestroyExternalSurface(surface->unityColorBuffer);
  251. UnityDestroyExternalSurface(surface->systemColorBuffer);
  252. surface->unityColorBuffer = surface->systemColorBuffer = 0;
  253. UnityDestroyExternalSurface(surface->unityDepthBuffer);
  254. UnityDestroyExternalSurface(surface->systemDepthBuffer);
  255. surface->unityDepthBuffer = surface->systemDepthBuffer = 0;
  256. }
  257. extern "C" void PreparePresentMTL(UnityDisplaySurfaceMTL* surface)
  258. {
  259. if (surface->targetColorRT)
  260. UnityBlitToBackbuffer(surface->unityColorBuffer, surface->systemColorBuffer, surface->systemDepthBuffer);
  261. #if UNITY_TRAMPOLINE_IN_USE
  262. APP_CONTROLLER_RENDER_PLUGIN_METHOD(onFrameResolved);
  263. #endif
  264. }
  265. extern "C" void PresentMTL(UnityDisplaySurfaceMTL* surface)
  266. {
  267. if (surface->drawable)
  268. {
  269. // for some reason presentDrawable: afterMinimumDuration: is missing from simulator headers completely in xcode 12
  270. #if (PLATFORM_IOS || PLATFORM_TVOS) && !(TARGET_IPHONE_SIMULATOR || TARGET_TVOS_SIMULATOR)
  271. const int targetFPS = UnityGetTargetFPS(); assert(targetFPS > 0);
  272. [UnityCurrentMTLCommandBuffer() presentDrawable: surface->drawable afterMinimumDuration: 1.0 / targetFPS];
  273. return;
  274. #endif
  275. // note that we end up here if presentDrawable: afterMinimumDuration: is not supported
  276. [UnityCurrentMTLCommandBuffer() presentDrawable: surface->drawable];
  277. }
  278. }
  279. extern "C" MTLTextureRef AcquireDrawableMTL(UnityDisplaySurfaceMTL* surface)
  280. {
  281. if (!surface)
  282. return nil;
  283. if (!surface->drawable)
  284. surface->drawable = [surface->layer nextDrawable];
  285. // on A7 SoC nextDrawable may be nil before locking the screen
  286. if (!surface->drawable)
  287. return nil;
  288. surface->systemColorRB = [surface->drawable texture];
  289. return surface->systemColorRB;
  290. }
  291. extern "C" int UnityCommandQueueMaxCommandBufferCountMTL()
  292. {
  293. // customizable argument to pass towards [MTLDevice newCommandQueueWithMaxCommandBufferCount:],
  294. // the default value is 64 but with Parallel Render Encoder workloads, it might need to be increased
  295. return 256;
  296. }
  297. extern "C" void StartFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  298. {
  299. // we will acquire drawable lazily in AcquireDrawableMTL
  300. surface->drawable = nil;
  301. #if PLATFORM_OSX
  302. bool bufferSwap = OSAtomicCompareAndSwap32Barrier(1, 0, &surface->bufferSwap);
  303. if (bufferSwap || surface->bufferCompleted == 1)
  304. {
  305. MTLTextureRef texture0 = surface->drawableProxyRT[0];
  306. MTLTextureRef texture1 = surface->drawableProxyRT[1];
  307. surface->drawableProxyRT[0] = texture1;
  308. surface->drawableProxyRT[1] = texture0;
  309. }
  310. #endif
  311. surface->systemColorRB = surface->drawableProxyRT[0];
  312. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1};
  313. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1};
  314. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, surface->systemColorRB, nil, &sys_desc, surface);
  315. if (surface->targetColorRT == nil)
  316. {
  317. if (surface->targetAAColorRT)
  318. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->targetAAColorRT, surface->systemColorRB, &tgt_desc, surface);
  319. else
  320. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->systemColorRB, nil, &tgt_desc, surface);
  321. }
  322. }
  323. extern "C" void EndFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  324. {
  325. @autoreleasepool
  326. {
  327. if (surface->presentCB)
  328. {
  329. [surface->presentCB enqueue]; [surface->presentCB commit];
  330. surface->presentCB = nil;
  331. }
  332. surface->systemColorRB = nil;
  333. surface->drawable = nil;
  334. }
  335. }
  336. extern "C" void PreparePresentNonMainScreenMTL(UnityDisplaySurfaceMTL* surface)
  337. {
  338. if (surface->drawable)
  339. {
  340. surface->presentCB = [surface->drawableCommandQueue commandBuffer];
  341. [surface->presentCB presentDrawable: surface->drawable];
  342. }
  343. }
  344. extern "C" void SetDrawableSizeMTL(UnityDisplaySurfaceMTL* surface, int width, int height)
  345. {
  346. surface->layer.drawableSize = CGSizeMake(width, height);
  347. }
  348. #else
  349. extern "C" void InitRenderingMTL() {}
  350. extern "C" void CreateSystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  351. extern "C" void CreateRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  352. extern "C" void DestroyRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  353. extern "C" void CreateSharedDepthbufferMTL(UnityDisplaySurfaceMTL*) {}
  354. extern "C" void DestroySharedDepthbufferMTL(UnityDisplaySurfaceMTL*) {}
  355. extern "C" void CreateUnityRenderBuffersMTL(UnityDisplaySurfaceMTL*) {}
  356. extern "C" void DestroySystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL*) {}
  357. extern "C" void DestroyUnityRenderBuffersMTL(UnityDisplaySurfaceMTL*) {}
  358. extern "C" void StartFrameRenderingMTL(UnityDisplaySurfaceMTL*) {}
  359. extern "C" void EndFrameRenderingMTL(UnityDisplaySurfaceMTL*) {}
  360. extern "C" void PreparePresentMTL(UnityDisplaySurfaceMTL*) {}
  361. extern "C" void PresentMTL(UnityDisplaySurfaceMTL*) {}
  362. extern "C" int UnityCommandQueueMaxCommandBufferCountMTL() { return 0; }
  363. extern "C" void SetDrawableSizeMTL(UnityDisplaySurfaceMTL*, int, int) {}
  364. extern "C" MTLTextureRef AcquireDrawableMTL(UnityDisplaySurfaceMTL*) { return nil; }
  365. #endif