説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

MetalHelper.mm 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. #include "UnityTrampolineCompatibility.h"
  2. #include "UnityRendering.h"
  3. #import <Metal/Metal.h>
  4. #import <QuartzCore/QuartzCore.h>
  5. #if UNITY_TRAMPOLINE_IN_USE
  6. #include "UnityAppController.h"
  7. #include "CVTextureCache.h"
  8. #endif
  9. #include "ObjCRuntime.h"
  10. #include <libkern/OSAtomic.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_IOS || PLATFORM_VISIONOS || PLATFORM_OSX
  19. if (surface->hdr)
  20. {
  21. // 0 = 10 bit, 1 = 16bit
  22. if (@available(iOS 16.0, *))
  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->hdr)
  73. {
  74. if (@available(iOS 16.0, *))
  75. colorSpaceRef = UnityHDRSurfaceDepth() == 0 ? CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceITUR_2100_PQ")) : CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceExtendedLinearITUR_2020"));
  76. #if PLATFORM_OSX
  77. if(colorSpaceRef == nil)
  78. colorSpaceRef = UnityHDRSurfaceDepth() == 0 ? CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceITUR_2020_PQ_EOTF")) : CGColorSpaceCreateWithName(CFSTR("kCGColorSpaceExtendedLinearITUR_2020"));
  79. #endif
  80. }
  81. if(colorSpaceRef == nil)
  82. {
  83. if (surface->wideColor)
  84. colorSpaceRef = CGColorSpaceCreateWithName(surface->srgb ? kCGColorSpaceExtendedLinearSRGB : kCGColorSpaceExtendedSRGB);
  85. else
  86. colorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
  87. }
  88. surface->layer.colorspace = colorSpaceRef;
  89. CGColorSpaceRelease(colorSpaceRef);
  90. // Update the native screen resolution
  91. UnityUpdateDrawableSize(surface);
  92. surface->layer.device = surface->device;
  93. surface->layer.pixelFormat = colorFormat;
  94. surface->layer.framebufferOnly = (surface->framebufferOnly != 0);
  95. surface->colorFormat = (unsigned)colorFormat;
  96. MTLTextureDescriptor* txDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: colorFormat width: surface->systemW height: surface->systemH mipmapped: NO];
  97. MTLResourceOptions storageModeOptions = MTLResourceStorageModeShared;
  98. #if PLATFORM_OSX
  99. storageModeOptions = MTLResourceStorageModeManaged;
  100. if ([surface->device supportsFamily: MTLGPUFamilyApple6])
  101. storageModeOptions = MTLResourceStorageModeShared;
  102. #endif
  103. txDesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | storageModeOptions;
  104. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  105. @synchronized(surface->layer)
  106. {
  107. #if PLATFORM_OSX
  108. surface->proxySwaps = 0;
  109. surface->proxyReady = 0;
  110. surface->calledPresentDrawable = 0;
  111. surface->vsync = 1; // by default, vsync is enabled for all surfaces
  112. #endif
  113. for (int i = 0; i < kUnityNumOffscreenSurfaces; i++)
  114. {
  115. UnityUnregisterMetalTextureForMemoryProfiler(surface->drawableProxyRT[i]);
  116. // Allocating a proxy texture is cheap until it's being rendered to and the GPU driver does allocation
  117. surface->drawableProxyRT[i] = [surface->device newTextureWithDescriptor: txDesc];
  118. surface->drawableProxyRT[i].label = @"DrawableProxy";
  119. #if PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS
  120. [surface->drawableProxyRT[i] setPurgeableState: MTLPurgeableStateEmpty];
  121. #endif
  122. // Mark each drawableProxy surface as needing a clear load action when next rendered to as its contents are undefined.
  123. surface->drawableProxyNeedsClear[i] = true;
  124. UnityRegisterExternalRenderSurfaceTextureForMemoryProfiler(surface->drawableProxyRT[i]);
  125. }
  126. }
  127. }
  128. extern "C" void CreateRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  129. {
  130. DestroyRenderingSurfaceMTL(surface);
  131. MTLPixelFormat colorFormat = GetColorFormatForSurface(surface);
  132. const int w = surface->targetW, h = surface->targetH;
  133. if (w != surface->systemW || h != surface->systemH || surface->useCVTextureCache)
  134. {
  135. #if PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS
  136. if (surface->useCVTextureCache)
  137. surface->cvTextureCache = CreateCVTextureCache();
  138. if (surface->cvTextureCache)
  139. {
  140. surface->cvTextureCacheTexture = CreateReadableRTFromCVTextureCache2(surface->cvTextureCache, surface->targetW, surface->targetH,
  141. GetCVPixelFormatForSurface(surface), colorFormat, &surface->cvPixelBuffer);
  142. surface->targetColorRT = GetMetalTextureFromCVTextureCache(surface->cvTextureCacheTexture);
  143. }
  144. else
  145. #endif
  146. {
  147. MTLTextureDescriptor* txDesc = [MTLTextureDescriptor new];
  148. txDesc.textureType = MTLTextureType2D;
  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. #if PLATFORM_OSX
  156. txDesc.resourceOptions = MTLResourceStorageModeManaged;
  157. #endif
  158. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  159. surface->targetColorRT = [surface->device newTextureWithDescriptor: txDesc];
  160. }
  161. surface->targetColorRT.label = @"targetColorRT";
  162. UnityRegisterExternalRenderSurfaceTextureForMemoryProfiler(surface->targetColorRT);
  163. }
  164. if (surface->msaaSamples > 1)
  165. {
  166. MTLTextureDescriptor* txDesc = [MTLTextureDescriptor new];
  167. txDesc.textureType = MTLTextureType2DMultisample;
  168. txDesc.width = w;
  169. txDesc.height = h;
  170. txDesc.depth = 1;
  171. txDesc.pixelFormat = colorFormat;
  172. txDesc.arrayLength = 1;
  173. txDesc.mipmapLevelCount = 1;
  174. txDesc.sampleCount = surface->msaaSamples;
  175. txDesc.resourceOptions = MTLResourceStorageModePrivate;
  176. txDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  177. if (![surface->device supportsTextureSampleCount: txDesc.sampleCount])
  178. txDesc.sampleCount = 4;
  179. surface->targetAAColorRT = [surface->device newTextureWithDescriptor: txDesc];
  180. surface->targetAAColorRT.label = @"targetAAColorRT";
  181. UnityRegisterExternalRenderSurfaceTextureForMemoryProfiler(surface->targetAAColorRT);
  182. }
  183. }
  184. extern "C" void DestroyRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  185. {
  186. UnityUnregisterMetalTextureForMemoryProfiler(surface->targetColorRT);
  187. surface->targetColorRT = nil;
  188. UnityUnregisterMetalTextureForMemoryProfiler(surface->targetAAColorRT);
  189. surface->targetAAColorRT = nil;
  190. if (surface->cvTextureCacheTexture)
  191. CFRelease(surface->cvTextureCacheTexture);
  192. if (surface->cvPixelBuffer)
  193. CFRelease(surface->cvPixelBuffer);
  194. if (surface->cvTextureCache)
  195. CFRelease(surface->cvTextureCache);
  196. surface->cvTextureCache = 0;
  197. }
  198. extern "C" void CreateSharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  199. {
  200. DestroySharedDepthbufferMTL(surface);
  201. if (surface->disableDepthAndStencil)
  202. return;
  203. MTLPixelFormat pixelFormat = MTLPixelFormatDepth32Float_Stencil8;
  204. MTLTextureDescriptor* depthTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: pixelFormat width: surface->targetW height: surface->targetH mipmapped: NO];
  205. depthTexDesc.resourceOptions = MTLResourceStorageModePrivate;
  206. #if PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_VISIONOS
  207. if (surface->memorylessDepth)
  208. depthTexDesc.storageMode = MTLStorageModeMemoryless;
  209. #endif
  210. depthTexDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
  211. if (surface->msaaSamples > 1)
  212. {
  213. depthTexDesc.textureType = MTLTextureType2DMultisample;
  214. depthTexDesc.sampleCount = surface->msaaSamples;
  215. if (![surface->device supportsTextureSampleCount: depthTexDesc.sampleCount])
  216. depthTexDesc.sampleCount = 4;
  217. }
  218. surface->depthRB = [surface->device newTextureWithDescriptor: depthTexDesc];
  219. surface->stencilRB = surface->depthRB;
  220. UnityRegisterExternalRenderSurfaceTextureForMemoryProfiler(surface->depthRB);
  221. }
  222. extern "C" void DestroySharedDepthbufferMTL(UnityDisplaySurfaceMTL* surface)
  223. {
  224. UnityUnregisterMetalTextureForMemoryProfiler(surface->depthRB);
  225. surface->depthRB = nil;
  226. surface->stencilRB = nil;
  227. }
  228. extern "C" void CreateUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  229. {
  230. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1 };
  231. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1 };
  232. // note that StartFrameRenderingMTL/AcquireDrawableMTL/EndFrameRenderingMTL happen on the render thread
  233. // while CreateUnityRenderBuffersMTL/DestroyUnityRenderBuffersMTL happen on main
  234. MTLTextureRef systemColorRB = nil;
  235. @synchronized(surface->layer)
  236. {
  237. for (unsigned i = 0; i < kUnityNumOffscreenSurfaces; ++i)
  238. surface->drawableProxyRS[i] = UnityCreateExternalColorSurfaceMTL(surface->drawableProxyRS[i], surface->drawableProxyRT[i], nil, &sys_desc, surface);
  239. systemColorRB = surface->drawableProxyRT[0];
  240. }
  241. surface->systemColorRB = systemColorRB;
  242. // we could unify all of it with ugly chain of ternary operators but what if karma exists?
  243. if (surface->targetColorRT)
  244. {
  245. // render to interim RT: we do NOT need to request drawable
  246. MTLTextureRef texRender = surface->targetAAColorRT ? surface->targetAAColorRT : surface->targetColorRT;
  247. MTLTextureRef texResolve = surface->targetAAColorRT ? surface->targetColorRT : nil;
  248. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, texRender, texResolve, &tgt_desc, nil);
  249. }
  250. else
  251. {
  252. // render to backbuffer directly: we will request drawable hence we need to pass surface
  253. MTLTextureRef texRender = surface->targetAAColorRT ? surface->targetAAColorRT : systemColorRB;
  254. MTLTextureRef texResolve = surface->targetAAColorRT ? systemColorRB : nil;
  255. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, texRender, texResolve, &tgt_desc, surface);
  256. }
  257. if (surface->depthRB)
  258. surface->unityDepthBuffer = UnityCreateExternalDepthSurfaceMTL(surface->unityDepthBuffer, surface->depthRB, surface->stencilRB, &tgt_desc);
  259. else
  260. surface->unityDepthBuffer = UnityCreateDummySurface(surface->unityDepthBuffer, false, &tgt_desc);
  261. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, systemColorRB, nil, &sys_desc, surface);
  262. surface->systemDepthBuffer = UnityCreateDummySurface(surface->systemDepthBuffer, false, &sys_desc);
  263. }
  264. extern "C" void DestroySystemRenderingSurfaceMTL(UnityDisplaySurfaceMTL* surface)
  265. {
  266. // before we needed to nil surface->systemColorRB (to release drawable we get from the view)
  267. // but after we switched to proxy rt this is no longer needed
  268. // even more it is harmful when running rendering on another thread (as is default now)
  269. // as on render thread we do StartFrameRenderingMTL/AcquireDrawableMTL/EndFrameRenderingMTL
  270. // and DestroySystemRenderingSurfaceMTL comes on main thread so we might end up with race condition for no reason
  271. }
  272. extern "C" void DestroyUnityRenderBuffersMTL(UnityDisplaySurfaceMTL* surface)
  273. {
  274. UnityDestroyExternalSurface(surface->unityColorBuffer);
  275. UnityDestroyExternalSurface(surface->systemColorBuffer);
  276. surface->unityColorBuffer = surface->systemColorBuffer = 0;
  277. UnityDestroyExternalSurface(surface->unityDepthBuffer);
  278. UnityDestroyExternalSurface(surface->systemDepthBuffer);
  279. surface->unityDepthBuffer = surface->systemDepthBuffer = 0;
  280. @synchronized(surface->layer)
  281. {
  282. for (unsigned i = 0; i < kUnityNumOffscreenSurfaces; ++i)
  283. {
  284. UnityDestroyExternalSurface(surface->drawableProxyRS[i]);
  285. surface->drawableProxyRS[i] = 0;
  286. }
  287. }
  288. }
  289. extern "C" void PreparePresentMTL(UnityDisplaySurfaceMTL* surface)
  290. {
  291. if (surface->targetColorRT)
  292. UnityBlitToBackbuffer(surface->unityColorBuffer, surface->systemColorBuffer, surface->systemDepthBuffer);
  293. #if UNITY_TRAMPOLINE_IN_USE
  294. APP_CONTROLLER_RENDER_PLUGIN_METHOD(onFrameResolved);
  295. #endif
  296. }
  297. extern "C" void PresentMTL(UnityDisplaySurfaceMTL* surface)
  298. {
  299. //ARCHEOLOGY: we used to present using [MTLCommandBuffer presentDrawable:afterMinimumDuration:]
  300. //however that was found to sometimes cause 0.5s-1s hangs when acquiring drawable after surface rebuild, or presenting hanging completely (UUM-9480)
  301. //after some further investigation we found that using the more complex present logic didn't actually yield much benefit
  302. //current implementation is made to align with our macOS present logic
  303. if (surface->drawable)
  304. {
  305. id<CAMetalDrawable> drawable = surface->drawable;
  306. [UnityCurrentMTLCommandBuffer() addScheduledHandler:^(id<MTLCommandBuffer> commandBuffer) {
  307. [drawable present];
  308. }];
  309. }
  310. surface->calledPresentDrawable = 1;
  311. }
  312. extern "C" MTLTextureRef AcquireDrawableMTL(UnityDisplaySurfaceMTL* surface)
  313. {
  314. if (!surface)
  315. return nil;
  316. if (!surface->drawable)
  317. surface->drawable = [surface->layer nextDrawable];
  318. // on A7 SoC nextDrawable may be nil before locking the screen
  319. if (!surface->drawable)
  320. return nil;
  321. if (surface->drawableTex)
  322. return surface->drawableTex;
  323. id<MTLTexture> drawableTex = [surface->drawable texture];
  324. UnityUnregisterMetalTextureForMemoryProfiler(surface->drawableTex);
  325. surface->drawableTex = surface->systemColorRB = drawableTex;
  326. UnityRegisterExternalRenderSurfaceTextureForMemoryProfiler(drawableTex);
  327. return drawableTex;
  328. }
  329. extern "C" int UnityCommandQueueMaxCommandBufferCountMTL()
  330. {
  331. // customizable argument to pass towards [MTLDevice newCommandQueueWithMaxCommandBufferCount:],
  332. // the default value is 64 but with Parallel Render Encoder workloads, it might need to be increased
  333. return 256;
  334. }
  335. extern "C" void StartFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  336. {
  337. // we will acquire drawable lazily in AcquireDrawableMTL
  338. if (surface->drawableTex)
  339. UnityUnregisterMetalTextureForMemoryProfiler(surface->drawableTex);
  340. surface->drawable = nil;
  341. surface->drawableTex = nil;
  342. surface->systemColorRB = surface->drawableProxyRT[0];
  343. UnityRenderBufferDesc sys_desc = { surface->systemW, surface->systemH, 1, 1, 1};
  344. UnityRenderBufferDesc tgt_desc = { surface->targetW, surface->targetH, 1, (unsigned int)surface->msaaSamples, 1};
  345. surface->systemColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->systemColorBuffer, surface->systemColorRB, nil, &sys_desc, surface);
  346. if (surface->targetColorRT == nil)
  347. {
  348. if (surface->targetAAColorRT)
  349. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->targetAAColorRT, surface->systemColorRB, &tgt_desc, surface);
  350. else
  351. surface->unityColorBuffer = UnityCreateExternalColorSurfaceMTL(surface->unityColorBuffer, surface->systemColorRB, nil, &tgt_desc, surface);
  352. }
  353. }
  354. extern "C" void EndFrameRenderingMTL(UnityDisplaySurfaceMTL* surface)
  355. {
  356. @autoreleasepool
  357. {
  358. if (surface->drawableTex)
  359. UnityUnregisterMetalTextureForMemoryProfiler(surface->drawableTex);
  360. surface->drawable = nil;
  361. surface->drawableTex = nil;
  362. surface->systemColorRB = surface->drawableProxyRT[0];
  363. }
  364. #if PLATFORM_OSX
  365. @synchronized(surface->layer)
  366. {
  367. if (!surface->calledPresentDrawable)
  368. return;
  369. surface->calledPresentDrawable = 0;
  370. std::swap(surface->drawableProxyRT[0], surface->drawableProxyRT[1]);
  371. std::swap(surface->drawableProxyRS[0], surface->drawableProxyRS[1]);
  372. surface->proxySwaps++;
  373. surface->proxyReady = 1;
  374. // Swap the needs clear state of the swapped proxy buffers, to ensure that each surface
  375. // will get cleared at least once when the proxy buffer surfaces are recreated.
  376. std::swap(surface->drawableProxyNeedsClear[0],
  377. surface->drawableProxyNeedsClear[1]);
  378. }
  379. #endif
  380. }
  381. extern "C" void PreparePresentNonMainScreenMTL(UnityDisplaySurfaceMTL* surface)
  382. {
  383. if (surface->drawable)
  384. [UnityCurrentMTLCommandBuffer() presentDrawable: surface->drawable];
  385. }
  386. extern "C" void SetDrawableSizeMTL(UnityDisplaySurfaceMTL* surface, int width, int height)
  387. {
  388. surface->layer.drawableSize = CGSizeMake(width, height);
  389. }