VkGraphicsState.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Gr/Vulkan/VkGraphicsState.h>
  6. #include <AnKi/Gr/BackendCommon/Functions.h>
  7. #include <AnKi/Gr/Vulkan/VkGrManager.h>
  8. #include <AnKi/Gr/Vulkan/VkShaderProgram.h>
  9. #include <AnKi/Util/Filesystem.h>
  10. namespace anki {
  11. static VkViewport computeViewport(const U32 viewport[], U32 fbWidth, U32 fbHeight)
  12. {
  13. const U32 minx = viewport[0];
  14. const U32 miny = viewport[1];
  15. const U32 width = min<U32>(fbWidth, viewport[2]);
  16. const U32 height = min<U32>(fbHeight, viewport[3]);
  17. ANKI_ASSERT(width > 0 && height > 0);
  18. ANKI_ASSERT(minx + width <= fbWidth);
  19. ANKI_ASSERT(miny + height <= fbHeight);
  20. const VkViewport s = {.x = F32(minx), .y = F32(height + miny), .width = F32(width), .height = -F32(height), .minDepth = 0.0f, .maxDepth = 1.0f};
  21. return s;
  22. }
  23. static VkRect2D computeScissor(const U32 scissor[], U32 fbWidth, U32 fbHeight)
  24. {
  25. const U32 minx = scissor[0];
  26. const U32 miny = scissor[1];
  27. const U32 width = min<U32>(fbWidth, scissor[2]);
  28. const U32 height = min<U32>(fbHeight, scissor[3]);
  29. ANKI_ASSERT(width > 0 && height > 0);
  30. ANKI_ASSERT(minx + width <= fbWidth);
  31. ANKI_ASSERT(miny + height <= fbHeight);
  32. VkRect2D out = {};
  33. out.extent.width = width;
  34. out.extent.height = height;
  35. out.offset.x = minx;
  36. out.offset.y = miny;
  37. return out;
  38. }
  39. GraphicsPipelineFactory::~GraphicsPipelineFactory()
  40. {
  41. for(auto pso : m_map)
  42. {
  43. vkDestroyPipeline(getVkDevice(), pso, nullptr);
  44. }
  45. }
  46. void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, VkCommandBuffer& cmdb)
  47. {
  48. const GraphicsStateTracker::StaticState& staticState = state.m_staticState;
  49. GraphicsStateTracker::DynamicState& dynState = state.m_dynState;
  50. // Set dynamic state
  51. const auto& ss = staticState.m_stencil;
  52. const Bool stencilTestEnabled = anki::stencilTestEnabled(ss.m_face[0].m_fail, ss.m_face[0].m_stencilPassDepthFail,
  53. ss.m_face[0].m_stencilPassDepthPass, ss.m_face[0].m_compare)
  54. || anki::stencilTestEnabled(ss.m_face[1].m_fail, ss.m_face[1].m_stencilPassDepthFail,
  55. ss.m_face[1].m_stencilPassDepthPass, ss.m_face[1].m_compare);
  56. const Bool hasStencilRt =
  57. staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(staticState.m_misc.m_depthStencilFormat).isStencil();
  58. if(stencilTestEnabled && hasStencilRt && dynState.m_stencilCompareMaskDirty)
  59. {
  60. ANKI_ASSERT(dynState.m_stencilFaces[0].m_compareMask != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_compareMask != 0x5A5A5A5A);
  61. dynState.m_stencilCompareMaskDirty = false;
  62. if(dynState.m_stencilFaces[0].m_compareMask == dynState.m_stencilFaces[1].m_compareMask)
  63. {
  64. vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_compareMask);
  65. }
  66. else
  67. {
  68. vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_compareMask);
  69. vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_compareMask);
  70. }
  71. }
  72. if(stencilTestEnabled && hasStencilRt && dynState.m_stencilWriteMaskDirty)
  73. {
  74. ANKI_ASSERT(dynState.m_stencilFaces[0].m_writeMask != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_writeMask != 0x5A5A5A5A);
  75. dynState.m_stencilWriteMaskDirty = false;
  76. if(dynState.m_stencilFaces[0].m_writeMask == dynState.m_stencilFaces[1].m_writeMask)
  77. {
  78. vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_writeMask);
  79. }
  80. else
  81. {
  82. vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_writeMask);
  83. vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_writeMask);
  84. }
  85. }
  86. if(stencilTestEnabled && hasStencilRt && dynState.m_stencilRefDirty)
  87. {
  88. ANKI_ASSERT(dynState.m_stencilFaces[0].m_ref != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_ref != 0x5A5A5A5A);
  89. dynState.m_stencilRefDirty = false;
  90. if(dynState.m_stencilFaces[0].m_ref == dynState.m_stencilFaces[1].m_ref)
  91. {
  92. vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_ref);
  93. }
  94. else
  95. {
  96. vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_ref);
  97. vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_ref);
  98. }
  99. }
  100. const Bool hasDepthRt =
  101. staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(staticState.m_misc.m_depthStencilFormat).isDepth();
  102. const Bool depthTestEnabled = anki::depthTestEnabled(staticState.m_depth.m_compare, staticState.m_depth.m_writeEnabled);
  103. if(hasDepthRt && depthTestEnabled && dynState.m_depthBiasDirty)
  104. {
  105. dynState.m_depthBiasDirty = false;
  106. vkCmdSetDepthBias(cmdb, dynState.m_depthBiasConstantFactor, dynState.m_depthBiasClamp, dynState.m_depthBiasSlopeFactor);
  107. }
  108. if(dynState.m_viewportDirty)
  109. {
  110. ANKI_ASSERT(dynState.m_viewport[2] != 0 && dynState.m_viewport[3] != 0);
  111. dynState.m_viewportDirty = false;
  112. const VkViewport vp = computeViewport(dynState.m_viewport.getBegin(), state.m_rtsSize.x(), state.m_rtsSize.y());
  113. vkCmdSetViewport(cmdb, 0, 1, &vp);
  114. }
  115. if(dynState.m_scissorDirty)
  116. {
  117. dynState.m_scissorDirty = false;
  118. const VkRect2D rect = computeScissor(dynState.m_scissor.getBegin(), state.m_rtsSize.x(), state.m_rtsSize.y());
  119. vkCmdSetScissor(cmdb, 0, 1, &rect);
  120. }
  121. if(dynState.m_lineWidthDirty)
  122. {
  123. dynState.m_lineWidthDirty = false;
  124. vkCmdSetLineWidth(cmdb, dynState.m_lineWidth);
  125. }
  126. // Static state
  127. const Bool rebindPso = state.updateHashes();
  128. // Find the PSO
  129. VkPipeline pso = VK_NULL_HANDLE;
  130. {
  131. RLockGuard lock(m_mtx);
  132. auto it = m_map.find(state.m_globalHash);
  133. if(it != m_map.getEnd())
  134. {
  135. pso = *it;
  136. }
  137. }
  138. if(pso) [[likely]]
  139. {
  140. if(rebindPso)
  141. {
  142. vkCmdBindPipeline(cmdb, VK_PIPELINE_BIND_POINT_GRAPHICS, pso);
  143. }
  144. return;
  145. }
  146. // PSO not found, proactively create it WITHOUT a lock (we dont't want to serialize pipeline creation)
  147. const ShaderProgramImpl& prog = static_cast<const ShaderProgramImpl&>(*staticState.m_shaderProg);
  148. VkGraphicsPipelineCreateInfo ci = {};
  149. ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  150. if(staticState.m_misc.m_pipelineStatisticsEnabled)
  151. {
  152. ci.flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
  153. }
  154. ci.pStages = prog.getShaderCreateInfos(ci.stageCount);
  155. // Vertex stuff
  156. Array<VkVertexInputBindingDescription, U32(VertexAttributeSemantic::kCount)> vertBindings;
  157. Array<VkVertexInputAttributeDescription, U32(VertexAttributeSemantic::kCount)> attribs;
  158. VkPipelineVertexInputStateCreateInfo vertCi = {};
  159. vertCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  160. vertCi.pVertexAttributeDescriptions = &attribs[0];
  161. vertCi.pVertexBindingDescriptions = &vertBindings[0];
  162. BitSet<U32(VertexAttributeSemantic::kCount), U8> bindingSet = {false};
  163. for(VertexAttributeSemantic semantic : EnumBitsIterable<VertexAttributeSemantic, VertexAttributeSemanticBit>(staticState.m_vert.m_activeAttribs))
  164. {
  165. VkVertexInputAttributeDescription& attrib = attribs[vertCi.vertexAttributeDescriptionCount++];
  166. attrib.binding = staticState.m_vert.m_attribs[semantic].m_binding;
  167. attrib.format = convertFormat(staticState.m_vert.m_attribs[semantic].m_fmt);
  168. attrib.location = staticState.m_vert.m_attribs[semantic].m_semanticToVertexAttributeLocation;
  169. attrib.offset = staticState.m_vert.m_attribs[semantic].m_relativeOffset;
  170. if(!bindingSet.get(attrib.binding))
  171. {
  172. bindingSet.set(attrib.binding);
  173. VkVertexInputBindingDescription& binding = vertBindings[vertCi.vertexBindingDescriptionCount++];
  174. binding.binding = attrib.binding;
  175. binding.inputRate = convertVertexStepRate(staticState.m_vert.m_bindings[attrib.binding].m_stepRate);
  176. binding.stride = staticState.m_vert.m_bindings[attrib.binding].m_stride;
  177. }
  178. }
  179. ci.pVertexInputState = &vertCi;
  180. // IA
  181. VkPipelineInputAssemblyStateCreateInfo iaCi = {};
  182. iaCi.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  183. iaCi.primitiveRestartEnable = staticState.m_ia.m_primitiveRestartEnabled;
  184. iaCi.topology = convertTopology(staticState.m_ia.m_topology);
  185. ci.pInputAssemblyState = &iaCi;
  186. // Viewport
  187. VkPipelineViewportStateCreateInfo vpCi = {};
  188. vpCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  189. vpCi.scissorCount = 1;
  190. vpCi.viewportCount = 1;
  191. ci.pViewportState = &vpCi;
  192. // Raster
  193. VkPipelineRasterizationStateCreateInfo rastCi = {};
  194. rastCi.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  195. rastCi.depthClampEnable = false;
  196. rastCi.rasterizerDiscardEnable = false;
  197. rastCi.polygonMode = convertFillMode(staticState.m_rast.m_fillMode);
  198. rastCi.cullMode = convertCullMode(staticState.m_rast.m_cullMode);
  199. rastCi.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
  200. rastCi.depthBiasEnable = staticState.m_rast.m_depthBiasEnabled;
  201. rastCi.lineWidth = 1.0f;
  202. ci.pRasterizationState = &rastCi;
  203. // MS
  204. VkPipelineMultisampleStateCreateInfo msCi = {};
  205. msCi.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  206. msCi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  207. ci.pMultisampleState = &msCi;
  208. // Depth stencil
  209. VkPipelineDepthStencilStateCreateInfo dsCi;
  210. if(hasDepthRt || hasStencilRt)
  211. {
  212. dsCi = {};
  213. dsCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
  214. if(hasDepthRt)
  215. {
  216. dsCi.depthTestEnable = depthTestEnabled;
  217. dsCi.depthWriteEnable = staticState.m_depth.m_writeEnabled;
  218. dsCi.depthCompareOp = convertCompareOp(staticState.m_depth.m_compare);
  219. }
  220. if(hasStencilRt)
  221. {
  222. const auto& ss = staticState.m_stencil;
  223. dsCi.stencilTestEnable = stencilTestEnabled;
  224. dsCi.front.failOp = convertStencilOp(ss.m_face[0].m_fail);
  225. dsCi.front.passOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthPass);
  226. dsCi.front.depthFailOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthFail);
  227. dsCi.front.compareOp = convertCompareOp(ss.m_face[0].m_compare);
  228. dsCi.back.failOp = convertStencilOp(ss.m_face[1].m_fail);
  229. dsCi.back.passOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthPass);
  230. dsCi.back.depthFailOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthFail);
  231. dsCi.back.compareOp = convertCompareOp(ss.m_face[1].m_compare);
  232. }
  233. ci.pDepthStencilState = &dsCi;
  234. }
  235. // Color/blend
  236. Array<VkPipelineColorBlendAttachmentState, kMaxColorRenderTargets> colAttachments = {};
  237. VkPipelineColorBlendStateCreateInfo colCi = {};
  238. colCi.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  239. if(staticState.m_misc.m_colorRtMask.getAnySet())
  240. {
  241. colCi.attachmentCount = staticState.m_misc.m_colorRtMask.getSetBitCount();
  242. colCi.pAttachments = &colAttachments[0];
  243. for(U i = 0; i < colCi.attachmentCount; ++i)
  244. {
  245. VkPipelineColorBlendAttachmentState& out = colAttachments[i];
  246. const auto& in = staticState.m_blend.m_colorRts[i];
  247. out.blendEnable = blendingEnabled(in.m_srcRgb, in.m_dstRgb, in.m_srcA, in.m_dstA, in.m_funcRgb, in.m_funcA);
  248. out.srcColorBlendFactor = convertBlendFactor(in.m_srcRgb);
  249. out.dstColorBlendFactor = convertBlendFactor(in.m_dstRgb);
  250. out.srcAlphaBlendFactor = convertBlendFactor(in.m_srcA);
  251. out.dstAlphaBlendFactor = convertBlendFactor(in.m_dstA);
  252. out.colorBlendOp = convertBlendOperation(in.m_funcRgb);
  253. out.alphaBlendOp = convertBlendOperation(in.m_funcA);
  254. out.colorWriteMask = convertColorWriteMask(in.m_channelWriteMask);
  255. }
  256. ci.pColorBlendState = &colCi;
  257. }
  258. // Renderpass related (Dynamic rendering)
  259. Array<VkFormat, kMaxColorRenderTargets> dynamicRenderingAttachmentFormats = {};
  260. VkPipelineRenderingCreateInfoKHR dynRendering = {};
  261. dynRendering.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
  262. dynRendering.colorAttachmentCount = staticState.m_misc.m_colorRtMask.getSetBitCount();
  263. dynRendering.pColorAttachmentFormats = dynamicRenderingAttachmentFormats.getBegin();
  264. for(U i = 0; i < kMaxColorRenderTargets; ++i)
  265. {
  266. dynamicRenderingAttachmentFormats[i] =
  267. (staticState.m_misc.m_colorRtMask.get(i)) ? convertFormat(staticState.m_misc.m_colorRtFormats[i]) : VK_FORMAT_UNDEFINED;
  268. }
  269. if(hasDepthRt)
  270. {
  271. dynRendering.depthAttachmentFormat = convertFormat(staticState.m_misc.m_depthStencilFormat);
  272. }
  273. if(hasStencilRt)
  274. {
  275. dynRendering.stencilAttachmentFormat = convertFormat(staticState.m_misc.m_depthStencilFormat);
  276. }
  277. appendPNextList(ci, &dynRendering);
  278. // Almost all state is dynamic. Depth bias is static
  279. VkPipelineDynamicStateCreateInfo dynCi = {};
  280. dynCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
  281. static constexpr Array<VkDynamicState, 10> kDyn = {
  282. {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS,
  283. VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_LINE_WIDTH,
  284. VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR}};
  285. dynCi.dynamicStateCount = (getGrManagerImpl().getDeviceCapabilities().m_vrs) ? kDyn.getSize() : (kDyn.getSize() - 1);
  286. dynCi.pDynamicStates = &kDyn[0];
  287. ci.pDynamicState = &dynCi;
  288. // The rest
  289. ci.layout = prog.getPipelineLayout().getHandle();
  290. ci.subpass = 0;
  291. // Create the pipeline
  292. {
  293. ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
  294. #if ANKI_PLATFORM_MOBILE
  295. if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
  296. {
  297. PipelineCache::getSingleton().m_globalCreatePipelineMtx->lock();
  298. }
  299. #endif
  300. ANKI_VK_CHECKF(vkCreateGraphicsPipelines(getVkDevice(), PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr, &pso));
  301. getGrManagerImpl().printPipelineShaderInfo(pso, prog.getName());
  302. #if ANKI_PLATFORM_MOBILE
  303. if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
  304. {
  305. PipelineCache::getSingleton().m_globalCreatePipelineMtx->unlock();
  306. }
  307. #endif
  308. }
  309. // Now try to add the PSO to the hashmap
  310. {
  311. WLockGuard lock(m_mtx);
  312. auto it = m_map.find(state.m_globalHash);
  313. if(it == m_map.getEnd())
  314. {
  315. // Not found, add it
  316. m_map.emplace(state.m_globalHash, pso);
  317. }
  318. else
  319. {
  320. // Found, remove the PSO that was proactively created and use the old one
  321. vkDestroyPipeline(getVkDevice(), pso, nullptr);
  322. pso = *it;
  323. }
  324. }
  325. // Final thing, bind the PSO
  326. vkCmdBindPipeline(cmdb, VK_PIPELINE_BIND_POINT_GRAPHICS, pso);
  327. }
  328. Error PipelineCache::init(CString cacheDir)
  329. {
  330. ANKI_ASSERT(cacheDir);
  331. m_dumpSize = g_cvarGrDiskShaderCacheMaxSize;
  332. m_dumpFilename.sprintf("%s/VkPipelineCache", cacheDir.cstr());
  333. // Try read the pipeline cache file.
  334. GrDynamicArray<U8, PtrSize> diskDump;
  335. if(fileExists(m_dumpFilename.toCString()))
  336. {
  337. File file;
  338. ANKI_CHECK(file.open(m_dumpFilename.toCString(), FileOpenFlag::kBinary | FileOpenFlag::kRead));
  339. const PtrSize diskDumpSize = file.getSize();
  340. if(diskDumpSize <= sizeof(U8) * VK_UUID_SIZE)
  341. {
  342. ANKI_VK_LOGI("Pipeline cache dump appears to be empty: %s", &m_dumpFilename[0]);
  343. }
  344. else
  345. {
  346. // Get current pipeline UUID and compare it with the cache's
  347. VkPhysicalDeviceProperties props;
  348. vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
  349. Array<U8, VK_UUID_SIZE> cacheUuid;
  350. ANKI_CHECK(file.read(&cacheUuid[0], VK_UUID_SIZE));
  351. if(memcmp(&cacheUuid[0], &props.pipelineCacheUUID[0], VK_UUID_SIZE) != 0)
  352. {
  353. ANKI_VK_LOGI("Pipeline cache dump is not compatible with the current device: %s", &m_dumpFilename[0]);
  354. }
  355. else
  356. {
  357. diskDump.resize(diskDumpSize - VK_UUID_SIZE);
  358. ANKI_CHECK(file.read(&diskDump[0], diskDumpSize - VK_UUID_SIZE));
  359. }
  360. }
  361. }
  362. else
  363. {
  364. ANKI_VK_LOGI("Pipeline cache dump not found: %s", &m_dumpFilename[0]);
  365. }
  366. // Create the cache
  367. VkPipelineCacheCreateInfo ci = {};
  368. ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
  369. if(diskDump.getSize())
  370. {
  371. ANKI_VK_LOGI("Will load %zu bytes of pipeline cache", diskDump.getSize());
  372. ci.initialDataSize = diskDump.getSize();
  373. ci.pInitialData = &diskDump[0];
  374. }
  375. ANKI_VK_CHECK(vkCreatePipelineCache(getVkDevice(), &ci, nullptr, &m_cacheHandle));
  376. #if ANKI_PLATFORM_MOBILE
  377. ANKI_ASSERT(GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor != GpuVendor::kUnknown);
  378. if(GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor == GpuVendor::kQualcomm)
  379. {
  380. // Calling vkCreateGraphicsPipeline from multiple threads crashes qualcomm's compiler
  381. ANKI_VK_LOGI("Enabling workaround for vkCreateGraphicsPipeline crashing when called from multiple threads");
  382. m_globalCreatePipelineMtx = anki::newInstance<Mutex>(GrMemoryPool::getSingleton());
  383. }
  384. #endif
  385. return Error::kNone;
  386. }
  387. void PipelineCache::destroy()
  388. {
  389. const Error err = destroyInternal();
  390. if(err)
  391. {
  392. ANKI_VK_LOGE("An error occurred while storing the pipeline cache to disk. Will ignore");
  393. }
  394. m_dumpFilename.destroy();
  395. }
  396. Error PipelineCache::destroyInternal()
  397. {
  398. #if ANKI_PLATFORM_MOBILE
  399. deleteInstance(GrMemoryPool::getSingleton(), m_globalCreatePipelineMtx);
  400. #endif
  401. if(m_cacheHandle)
  402. {
  403. // Get size of cache
  404. size_t size = 0;
  405. ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, nullptr));
  406. size = min(size, m_dumpSize);
  407. if(size > 0)
  408. {
  409. // Read cache
  410. GrDynamicArray<U8, PtrSize> cacheData;
  411. cacheData.resize(size);
  412. ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, &cacheData[0]));
  413. // Write file
  414. File file;
  415. ANKI_CHECK(file.open(&m_dumpFilename[0], FileOpenFlag::kBinary | FileOpenFlag::kWrite));
  416. VkPhysicalDeviceProperties props;
  417. vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
  418. ANKI_CHECK(file.write(&props.pipelineCacheUUID[0], VK_UUID_SIZE));
  419. ANKI_CHECK(file.write(&cacheData[0], size));
  420. ANKI_VK_LOGI("Dumped %zu bytes of the pipeline cache", size);
  421. }
  422. // Destroy cache
  423. vkDestroyPipelineCache(getVkDevice(), m_cacheHandle, nullptr);
  424. m_cacheHandle = VK_NULL_HANDLE;
  425. }
  426. return Error::kNone;
  427. }
  428. } // end namespace anki