MetalHelper.mm 17 KB

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