MTLObjectCache.mm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. // Copyright 2022 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoBackends/Metal/MTLObjectCache.h"
  4. #include <map>
  5. #include <mutex>
  6. #include <optional>
  7. #include "Common/Assert.h"
  8. #include "Common/MsgHandler.h"
  9. #include "VideoBackends/Metal/MTLPipeline.h"
  10. #include "VideoBackends/Metal/MTLUtil.h"
  11. #include "VideoBackends/Metal/MTLVertexFormat.h"
  12. #include "VideoCommon/AbstractPipeline.h"
  13. #include "VideoCommon/NativeVertexFormat.h"
  14. #include "VideoCommon/VertexShaderGen.h"
  15. #include "VideoCommon/VideoBackendBase.h"
  16. #include "VideoCommon/VideoConfig.h"
  17. MRCOwned<id<MTLDevice>> Metal::g_device;
  18. MRCOwned<id<MTLCommandQueue>> Metal::g_queue;
  19. std::unique_ptr<Metal::ObjectCache> Metal::g_object_cache;
  20. static void SetupDepthStencil(
  21. MRCOwned<id<MTLDepthStencilState>> (&dss)[Metal::DepthStencilSelector::N_VALUES]);
  22. Metal::ObjectCache::ObjectCache()
  23. {
  24. m_internal = std::make_unique<Internal>();
  25. SetupDepthStencil(m_dss);
  26. }
  27. Metal::ObjectCache::~ObjectCache()
  28. {
  29. }
  30. void Metal::ObjectCache::Initialize(MRCOwned<id<MTLDevice>> device)
  31. {
  32. g_device = std::move(device);
  33. g_queue = MRCTransfer([g_device newCommandQueue]);
  34. g_object_cache = std::unique_ptr<ObjectCache>(new ObjectCache);
  35. }
  36. void Metal::ObjectCache::Shutdown()
  37. {
  38. g_object_cache.reset();
  39. g_queue = nullptr;
  40. g_device = nullptr;
  41. }
  42. // MARK: Depth Stencil State
  43. // clang-format off
  44. static MTLCompareFunction Convert(CompareMode mode)
  45. {
  46. const bool invert_depth = !g_backend_info.bSupportsReversedDepthRange;
  47. switch (mode)
  48. {
  49. case CompareMode::Never: return MTLCompareFunctionNever;
  50. case CompareMode::Less: return invert_depth ? MTLCompareFunctionGreater
  51. : MTLCompareFunctionLess;
  52. case CompareMode::Equal: return MTLCompareFunctionEqual;
  53. case CompareMode::LEqual: return invert_depth ? MTLCompareFunctionGreaterEqual
  54. : MTLCompareFunctionLessEqual;
  55. case CompareMode::Greater: return invert_depth ? MTLCompareFunctionLess
  56. : MTLCompareFunctionGreater;
  57. case CompareMode::NEqual: return MTLCompareFunctionNotEqual;
  58. case CompareMode::GEqual: return invert_depth ? MTLCompareFunctionLessEqual
  59. : MTLCompareFunctionGreaterEqual;
  60. case CompareMode::Always: return MTLCompareFunctionAlways;
  61. }
  62. }
  63. static const char* to_string(MTLCompareFunction compare)
  64. {
  65. switch (compare)
  66. {
  67. case MTLCompareFunctionNever: return "Never";
  68. case MTLCompareFunctionGreater: return "Greater";
  69. case MTLCompareFunctionEqual: return "Equal";
  70. case MTLCompareFunctionGreaterEqual: return "GEqual";
  71. case MTLCompareFunctionLess: return "Less";
  72. case MTLCompareFunctionNotEqual: return "NEqual";
  73. case MTLCompareFunctionLessEqual: return "LEqual";
  74. case MTLCompareFunctionAlways: return "Always";
  75. }
  76. }
  77. // clang-format on
  78. static void
  79. SetupDepthStencil(MRCOwned<id<MTLDepthStencilState>> (&dss)[Metal::DepthStencilSelector::N_VALUES])
  80. {
  81. auto desc = MRCTransfer([MTLDepthStencilDescriptor new]);
  82. Metal::DepthStencilSelector sel;
  83. for (size_t i = 0; i < std::size(dss); ++i)
  84. {
  85. sel.value = i;
  86. MTLCompareFunction mcompare = Convert(sel.CompareMode());
  87. [desc setDepthWriteEnabled:sel.UpdateEnable()];
  88. [desc setDepthCompareFunction:mcompare];
  89. [desc setLabel:[NSString stringWithFormat:@"DSS %s%s", to_string(mcompare),
  90. sel.UpdateEnable() ? "+Write" : ""]];
  91. dss[i] = MRCTransfer([Metal::g_device newDepthStencilStateWithDescriptor:desc]);
  92. }
  93. }
  94. // MARK: Samplers
  95. // clang-format off
  96. static MTLSamplerMinMagFilter ConvertMinMag(FilterMode filter)
  97. {
  98. switch (filter)
  99. {
  100. case FilterMode::Linear: return MTLSamplerMinMagFilterLinear;
  101. case FilterMode::Near: return MTLSamplerMinMagFilterNearest;
  102. }
  103. }
  104. static MTLSamplerMipFilter ConvertMip(FilterMode filter)
  105. {
  106. switch (filter)
  107. {
  108. case FilterMode::Linear: return MTLSamplerMipFilterLinear;
  109. case FilterMode::Near: return MTLSamplerMipFilterNearest;
  110. }
  111. }
  112. static MTLSamplerAddressMode Convert(WrapMode wrap)
  113. {
  114. switch (wrap)
  115. {
  116. case WrapMode::Clamp: return MTLSamplerAddressModeClampToEdge;
  117. case WrapMode::Mirror: return MTLSamplerAddressModeMirrorRepeat;
  118. case WrapMode::Repeat: return MTLSamplerAddressModeRepeat;
  119. }
  120. }
  121. static const char* to_string(FilterMode filter)
  122. {
  123. switch (filter)
  124. {
  125. case FilterMode::Linear: return "Ln";
  126. case FilterMode::Near: return "Pt";
  127. }
  128. }
  129. static const char* to_string(WrapMode wrap)
  130. {
  131. switch (wrap)
  132. {
  133. case WrapMode::Clamp: return "C";
  134. case WrapMode::Mirror: return "M";
  135. case WrapMode::Repeat: return "R";
  136. }
  137. }
  138. // clang-format on
  139. MRCOwned<id<MTLSamplerState>> Metal::ObjectCache::CreateSampler(SamplerSelector sel)
  140. {
  141. @autoreleasepool
  142. {
  143. auto desc = MRCTransfer([MTLSamplerDescriptor new]);
  144. [desc setMinFilter:ConvertMinMag(sel.MinFilter())];
  145. [desc setMagFilter:ConvertMinMag(sel.MagFilter())];
  146. [desc setMipFilter:ConvertMip(sel.MipFilter())];
  147. [desc setSAddressMode:Convert(sel.WrapU())];
  148. [desc setTAddressMode:Convert(sel.WrapV())];
  149. [desc setMaxAnisotropy:1 << sel.AnisotropicFiltering()];
  150. [desc setLabel:MRCTransfer([[NSString alloc]
  151. initWithFormat:@"%s%s%s %s%s%d", to_string(sel.MinFilter()),
  152. to_string(sel.MagFilter()), to_string(sel.MipFilter()),
  153. to_string(sel.WrapU()), to_string(sel.WrapV()),
  154. 1 << sel.AnisotropicFiltering()])];
  155. return MRCTransfer([Metal::g_device newSamplerStateWithDescriptor:desc]);
  156. }
  157. }
  158. void Metal::ObjectCache::ReloadSamplers()
  159. {
  160. for (auto& sampler : m_samplers)
  161. sampler = nullptr;
  162. }
  163. // MARK: Pipelines
  164. static MTLPrimitiveTopologyClass GetClass(PrimitiveType prim)
  165. {
  166. switch (prim)
  167. {
  168. case PrimitiveType::Points:
  169. return MTLPrimitiveTopologyClassPoint;
  170. case PrimitiveType::Lines:
  171. return MTLPrimitiveTopologyClassLine;
  172. case PrimitiveType::Triangles:
  173. case PrimitiveType::TriangleStrip:
  174. return MTLPrimitiveTopologyClassTriangle;
  175. }
  176. }
  177. static MTLPrimitiveType Convert(PrimitiveType prim)
  178. {
  179. // clang-format off
  180. switch (prim)
  181. {
  182. case PrimitiveType::Points: return MTLPrimitiveTypePoint;
  183. case PrimitiveType::Lines: return MTLPrimitiveTypeLine;
  184. case PrimitiveType::Triangles: return MTLPrimitiveTypeTriangle;
  185. case PrimitiveType::TriangleStrip: return MTLPrimitiveTypeTriangleStrip;
  186. }
  187. // clang-format on
  188. }
  189. static MTLCullMode Convert(CullMode cull)
  190. {
  191. switch (cull)
  192. {
  193. case CullMode::None:
  194. case CullMode::All: // Handled by VertexLoaderManager::RunVertices
  195. return MTLCullModeNone;
  196. case CullMode::Front:
  197. return MTLCullModeFront;
  198. case CullMode::Back:
  199. return MTLCullModeBack;
  200. }
  201. }
  202. static MTLBlendFactor Convert(DstBlendFactor factor, bool usedualsrc)
  203. {
  204. // clang-format off
  205. switch (factor)
  206. {
  207. case DstBlendFactor::Zero: return MTLBlendFactorZero;
  208. case DstBlendFactor::One: return MTLBlendFactorOne;
  209. case DstBlendFactor::SrcClr: return MTLBlendFactorSourceColor;
  210. case DstBlendFactor::InvSrcClr: return MTLBlendFactorOneMinusSourceColor;
  211. case DstBlendFactor::SrcAlpha: return usedualsrc ? MTLBlendFactorSource1Alpha
  212. : MTLBlendFactorSourceAlpha;
  213. case DstBlendFactor::InvSrcAlpha: return usedualsrc ? MTLBlendFactorOneMinusSource1Alpha
  214. : MTLBlendFactorOneMinusSourceAlpha;
  215. case DstBlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha;
  216. case DstBlendFactor::InvDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha;
  217. }
  218. // clang-format on
  219. }
  220. static MTLBlendFactor Convert(SrcBlendFactor factor, bool usedualsrc)
  221. {
  222. // clang-format off
  223. switch (factor)
  224. {
  225. case SrcBlendFactor::Zero: return MTLBlendFactorZero;
  226. case SrcBlendFactor::One: return MTLBlendFactorOne;
  227. case SrcBlendFactor::DstClr: return MTLBlendFactorDestinationColor;
  228. case SrcBlendFactor::InvDstClr: return MTLBlendFactorOneMinusDestinationColor;
  229. case SrcBlendFactor::SrcAlpha: return usedualsrc ? MTLBlendFactorSource1Alpha
  230. : MTLBlendFactorSourceAlpha;
  231. case SrcBlendFactor::InvSrcAlpha: return usedualsrc ? MTLBlendFactorOneMinusSource1Alpha
  232. : MTLBlendFactorOneMinusSourceAlpha;
  233. case SrcBlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha;
  234. case SrcBlendFactor::InvDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha;
  235. }
  236. // clang-format on
  237. }
  238. class Metal::ObjectCache::Internal
  239. {
  240. public:
  241. using StoredPipeline = std::pair<MRCOwned<id<MTLRenderPipelineState>>, PipelineReflection>;
  242. /// Holds only the things that are actually used in a Metal pipeline
  243. struct PipelineID
  244. {
  245. struct VertexAttribute
  246. {
  247. // Just hold the things that might differ while using the same shader
  248. // (Really only a thing for ubershaders)
  249. u8 offset : 6;
  250. u8 components : 2;
  251. VertexAttribute() = default;
  252. explicit VertexAttribute(AttributeFormat format)
  253. : offset(format.offset), components(format.components - 1)
  254. {
  255. if (!format.enable)
  256. offset = 0x3F; // Set it to something unlikely
  257. }
  258. };
  259. template <size_t N>
  260. static void CopyAll(std::array<VertexAttribute, N>& output,
  261. const std::array<AttributeFormat, N>& input)
  262. {
  263. for (size_t i = 0; i < N; ++i)
  264. output[i] = VertexAttribute(input[i]);
  265. }
  266. PipelineID(const AbstractPipelineConfig& cfg)
  267. {
  268. memset(this, 0, sizeof(*this));
  269. if (const NativeVertexFormat* v = cfg.vertex_format)
  270. {
  271. const PortableVertexDeclaration& decl = v->GetVertexDeclaration();
  272. v_stride = v->GetVertexStride();
  273. v_position = VertexAttribute(decl.position);
  274. CopyAll(v_normals, decl.normals);
  275. CopyAll(v_colors, decl.colors);
  276. CopyAll(v_texcoords, decl.texcoords);
  277. v_posmtx = VertexAttribute(decl.posmtx);
  278. }
  279. vertex_shader = static_cast<const Shader*>(cfg.vertex_shader);
  280. fragment_shader = static_cast<const Shader*>(cfg.pixel_shader);
  281. framebuffer.color_texture_format = cfg.framebuffer_state.color_texture_format.Value();
  282. framebuffer.depth_texture_format = cfg.framebuffer_state.depth_texture_format.Value();
  283. framebuffer.samples = cfg.framebuffer_state.samples.Value();
  284. framebuffer.additional_color_attachment_count =
  285. cfg.framebuffer_state.additional_color_attachment_count.Value();
  286. blend.colorupdate = cfg.blending_state.colorupdate.Value();
  287. blend.alphaupdate = cfg.blending_state.alphaupdate.Value();
  288. if (cfg.blending_state.blendenable)
  289. {
  290. // clang-format off
  291. blend.blendenable = true;
  292. blend.usedualsrc = cfg.blending_state.usedualsrc.Value();
  293. blend.srcfactor = cfg.blending_state.srcfactor.Value();
  294. blend.dstfactor = cfg.blending_state.dstfactor.Value();
  295. blend.srcfactoralpha = cfg.blending_state.srcfactoralpha.Value();
  296. blend.dstfactoralpha = cfg.blending_state.dstfactoralpha.Value();
  297. blend.subtract = cfg.blending_state.subtract.Value();
  298. blend.subtractAlpha = cfg.blending_state.subtractAlpha.Value();
  299. // clang-format on
  300. }
  301. if (cfg.usage != AbstractPipelineUsage::GXUber)
  302. {
  303. if (cfg.rasterization_state.primitive == PrimitiveType::Points)
  304. is_points = true;
  305. else if (cfg.rasterization_state.primitive == PrimitiveType::Lines)
  306. is_lines = true;
  307. }
  308. }
  309. PipelineID() { memset(this, 0, sizeof(*this)); }
  310. PipelineID(const PipelineID& other) { memcpy(this, &other, sizeof(*this)); }
  311. PipelineID& operator=(const PipelineID& other)
  312. {
  313. memcpy(this, &other, sizeof(*this));
  314. return *this;
  315. }
  316. bool operator<(const PipelineID& other) const
  317. {
  318. return memcmp(this, &other, sizeof(*this)) < 0;
  319. }
  320. bool operator==(const PipelineID& other) const
  321. {
  322. return memcmp(this, &other, sizeof(*this)) == 0;
  323. }
  324. u8 v_stride;
  325. VertexAttribute v_position;
  326. std::array<VertexAttribute, 3> v_normals;
  327. std::array<VertexAttribute, 2> v_colors;
  328. std::array<VertexAttribute, 8> v_texcoords;
  329. VertexAttribute v_posmtx;
  330. const Shader* vertex_shader;
  331. const Shader* fragment_shader;
  332. union
  333. {
  334. BlendingState blend;
  335. // Throw extras in bits we don't otherwise use
  336. BitField<30, 1, bool, u32> is_points;
  337. BitField<31, 1, bool, u32> is_lines;
  338. };
  339. FramebufferState framebuffer;
  340. };
  341. std::mutex m_mtx;
  342. std::condition_variable m_cv;
  343. std::map<PipelineID, StoredPipeline> m_pipelines;
  344. std::map<const Shader*, std::vector<PipelineID>> m_shaders;
  345. std::array<u32, 3> m_pipeline_counter;
  346. StoredPipeline CreatePipeline(const AbstractPipelineConfig& config)
  347. {
  348. @autoreleasepool
  349. {
  350. ASSERT(!config.geometry_shader);
  351. auto desc = MRCTransfer([MTLRenderPipelineDescriptor new]);
  352. [desc setVertexFunction:static_cast<const Shader*>(config.vertex_shader)->GetShader()];
  353. [desc setFragmentFunction:static_cast<const Shader*>(config.pixel_shader)->GetShader()];
  354. if (config.usage == AbstractPipelineUsage::GXUber)
  355. [desc setLabel:[NSString stringWithFormat:@"GX Uber Pipeline %d", m_pipeline_counter[0]++]];
  356. else if (config.usage == AbstractPipelineUsage::GX)
  357. [desc setLabel:[NSString stringWithFormat:@"GX Pipeline %d", m_pipeline_counter[1]++]];
  358. else
  359. [desc setLabel:[NSString stringWithFormat:@"Utility Pipeline %d", m_pipeline_counter[2]++]];
  360. if (config.vertex_format)
  361. [desc setVertexDescriptor:static_cast<const VertexFormat*>(config.vertex_format)->Get()];
  362. RasterizationState rs = config.rasterization_state;
  363. if (config.usage != AbstractPipelineUsage::GXUber)
  364. [desc setInputPrimitiveTopology:GetClass(rs.primitive)];
  365. MTLRenderPipelineColorAttachmentDescriptor* color0 =
  366. [[desc colorAttachments] objectAtIndexedSubscript:0];
  367. BlendingState bs = config.blending_state;
  368. MTLColorWriteMask mask = MTLColorWriteMaskNone;
  369. if (bs.colorupdate)
  370. mask |= MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue;
  371. if (bs.alphaupdate)
  372. mask |= MTLColorWriteMaskAlpha;
  373. [color0 setWriteMask:mask];
  374. if (bs.blendenable)
  375. {
  376. // clang-format off
  377. [color0 setBlendingEnabled:YES];
  378. [color0 setSourceRGBBlendFactor: Convert(bs.srcfactor, bs.usedualsrc)];
  379. [color0 setSourceAlphaBlendFactor: Convert(bs.srcfactoralpha, bs.usedualsrc)];
  380. [color0 setDestinationRGBBlendFactor: Convert(bs.dstfactor, bs.usedualsrc)];
  381. [color0 setDestinationAlphaBlendFactor:Convert(bs.dstfactoralpha, bs.usedualsrc)];
  382. [color0 setRgbBlendOperation: bs.subtract ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
  383. [color0 setAlphaBlendOperation:bs.subtractAlpha ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
  384. // clang-format on
  385. }
  386. FramebufferState fs = config.framebuffer_state;
  387. if (fs.color_texture_format == AbstractTextureFormat::Undefined &&
  388. fs.depth_texture_format == AbstractTextureFormat::Undefined)
  389. {
  390. // Intel HD 4000's Metal driver asserts if you try to make one of these
  391. PanicAlertFmt("Attempted to create pipeline with no render targets!");
  392. }
  393. [desc setRasterSampleCount:fs.samples];
  394. [color0 setPixelFormat:Util::FromAbstract(fs.color_texture_format)];
  395. if (u32 cnt = fs.additional_color_attachment_count)
  396. {
  397. for (u32 i = 0; i < cnt; i++)
  398. [[desc colorAttachments] setObject:color0 atIndexedSubscript:i + 1];
  399. }
  400. [desc setDepthAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
  401. if (Util::HasStencil(fs.depth_texture_format))
  402. [desc setStencilAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
  403. NSError* err = nullptr;
  404. MTLRenderPipelineReflection* reflection = nullptr;
  405. id<MTLRenderPipelineState> pipe =
  406. [g_device newRenderPipelineStateWithDescriptor:desc
  407. options:MTLPipelineOptionArgumentInfo
  408. reflection:&reflection
  409. error:&err];
  410. if (err)
  411. {
  412. static int counter;
  413. std::string filename = VideoBackendBase::BadShaderFilename("pipeline", counter++);
  414. FILE* file = fopen(filename.c_str(), "w");
  415. if (file)
  416. {
  417. fmt::println(file, "=============== Error ===============");
  418. fmt::println(file, "{}", [[err localizedDescription] UTF8String]);
  419. fmt::println(file, "============== Pipeline =============");
  420. fmt::println(file, "VS: {}", [[[desc vertexFunction] label] UTF8String]);
  421. fmt::println(file, "PS: {}", [[[desc fragmentFunction] label] UTF8String]);
  422. fmt::println(file, "Color Format: {}", static_cast<u32>(fs.color_texture_format.Value()));
  423. fmt::println(file, "Depth Format: {}", static_cast<u32>(fs.depth_texture_format.Value()));
  424. fmt::println(file, "Sample Count: {}", fs.samples);
  425. if (u32 cnt = fs.additional_color_attachment_count)
  426. fmt::println(file, "Additional Color Attachments: {}", cnt);
  427. if (bs.colorupdate && bs.alphaupdate)
  428. fmt::println(file, "Write Color, Alpha");
  429. else if (bs.colorupdate)
  430. fmt::println(file, "Write Color");
  431. else if (bs.alphaupdate)
  432. fmt::println(file, "Write Alpha");
  433. else
  434. fmt::println(file, "Write None");
  435. if (bs.blendenable)
  436. {
  437. auto print_blend = [file](const char* name, SrcBlendFactor src, DstBlendFactor dst,
  438. bool subtract) {
  439. if (subtract)
  440. fmt::println(file, "{}: dst * {} - src * {}", name, dst, src);
  441. else
  442. fmt::println(file, "{}: src * {} + dst * {}", name, src, dst);
  443. };
  444. print_blend("Color Blend", bs.srcfactor, bs.dstfactor, bs.subtract);
  445. print_blend("Alpha Blend", bs.srcfactoralpha, bs.dstfactoralpha, bs.subtractAlpha);
  446. fmt::println(file, "Blend Dual Source: {}", bs.usedualsrc ? "true" : "false");
  447. }
  448. else
  449. {
  450. fmt::println(file, "Blend Disabled");
  451. }
  452. if (const Shader* vs = static_cast<const Shader*>(config.vertex_shader))
  453. {
  454. fmt::println(file, "========= Vertex Shader MSL =========");
  455. fmt::println(file, "{}", vs->GetMSL());
  456. }
  457. if (const Shader* ps = static_cast<const Shader*>(config.pixel_shader))
  458. {
  459. fmt::println(file, "========== Pixel Shader MSL =========");
  460. fmt::println(file, "{}", ps->GetMSL());
  461. }
  462. fclose(file);
  463. }
  464. std::string file_msg = file ? fmt::format("Details were written to {}", filename) :
  465. "Failed to write detailed info";
  466. PanicAlertFmt("Failed to compile pipeline for {} and {}: {}\n{}",
  467. [[[desc vertexFunction] label] UTF8String],
  468. [[[desc fragmentFunction] label] UTF8String],
  469. [[err localizedDescription] UTF8String], file_msg);
  470. return std::make_pair(nullptr, PipelineReflection());
  471. }
  472. return std::make_pair(MRCTransfer(pipe), PipelineReflection(reflection));
  473. }
  474. }
  475. StoredPipeline GetOrCreatePipeline(const AbstractPipelineConfig& config)
  476. {
  477. std::unique_lock<std::mutex> lock(m_mtx);
  478. PipelineID pid(config);
  479. auto it = m_pipelines.find(pid);
  480. if (it != m_pipelines.end())
  481. {
  482. while (!it->second.first && !it->second.second.textures)
  483. m_cv.wait(lock); // Wait for whoever's already compiling this
  484. return it->second;
  485. }
  486. // Reserve the spot now, so other threads know we're making it
  487. it = m_pipelines.insert({pid, {nullptr, PipelineReflection()}}).first;
  488. lock.unlock();
  489. StoredPipeline pipe = CreatePipeline(config);
  490. lock.lock();
  491. if (pipe.first)
  492. it->second = pipe;
  493. else
  494. it->second.second.textures = 1; // Abuse this as a "failed to create pipeline" flag
  495. m_shaders[pid.vertex_shader].push_back(pid);
  496. m_shaders[pid.fragment_shader].push_back(pid);
  497. lock.unlock();
  498. m_cv.notify_all(); // Wake up anyone who might be waiting
  499. return pipe;
  500. }
  501. void ShaderDestroyed(const Shader* shader)
  502. {
  503. std::lock_guard<std::mutex> lock(m_mtx);
  504. auto it = m_shaders.find(shader);
  505. if (it == m_shaders.end())
  506. return;
  507. // It's unlikely, but if a shader is destroyed, a new one could be made with the same address
  508. // (Also, we know it won't be used anymore, so there's no reason to keep these around)
  509. for (const PipelineID& pid : it->second)
  510. m_pipelines.erase(pid);
  511. m_shaders.erase(it);
  512. }
  513. };
  514. std::unique_ptr<AbstractPipeline>
  515. Metal::ObjectCache::CreatePipeline(const AbstractPipelineConfig& config)
  516. {
  517. Internal::StoredPipeline pipeline = m_internal->GetOrCreatePipeline(config);
  518. if (!pipeline.first)
  519. return nullptr;
  520. return std::make_unique<Pipeline>(config, std::move(pipeline.first), pipeline.second,
  521. Convert(config.rasterization_state.primitive),
  522. Convert(config.rasterization_state.cullmode),
  523. config.depth_state, config.usage);
  524. }
  525. void Metal::ObjectCache::ShaderDestroyed(const Shader* shader)
  526. {
  527. m_internal->ShaderDestroyed(shader);
  528. }