1use crate::{
17 device::{Device, DeviceOwned},
18 instance::InstanceOwnedDebugWrapper,
19 macros::{impl_id_counter, vulkan_bitflags},
20 Validated, ValidationError, VulkanError, VulkanObject,
21};
22use smallvec::SmallVec;
23use std::{mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
24
25#[derive(Debug)]
29pub struct PipelineCache {
30 device: InstanceOwnedDebugWrapper<Arc<Device>>,
31 handle: ash::vk::PipelineCache,
32 id: NonZeroU64,
33
34 flags: PipelineCacheCreateFlags,
35}
36
37impl PipelineCache {
38 #[inline]
84 pub unsafe fn new(
85 device: Arc<Device>,
86 create_info: PipelineCacheCreateInfo,
87 ) -> Result<Arc<PipelineCache>, Validated<VulkanError>> {
88 Self::validate_new(&device, &create_info)?;
89
90 Ok(unsafe { Self::new_unchecked(device, create_info) }?)
91 }
92
93 fn validate_new(
94 device: &Device,
95 create_info: &PipelineCacheCreateInfo,
96 ) -> Result<(), Box<ValidationError>> {
97 create_info
98 .validate(device)
99 .map_err(|err| err.add_context("create_info"))?;
100
101 Ok(())
102 }
103
104 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
105 pub unsafe fn new_unchecked(
106 device: Arc<Device>,
107 create_info: PipelineCacheCreateInfo,
108 ) -> Result<Arc<PipelineCache>, VulkanError> {
109 let create_info_vk = create_info.to_vk();
110
111 let handle = {
112 let fns = device.fns();
113 let mut output = MaybeUninit::uninit();
114 unsafe {
115 (fns.v1_0.create_pipeline_cache)(
116 device.handle(),
117 &create_info_vk,
118 ptr::null(),
119 output.as_mut_ptr(),
120 )
121 }
122 .result()
123 .map_err(VulkanError::from)?;
124 unsafe { output.assume_init() }
125 };
126
127 Ok(unsafe { Self::from_handle(device, handle, create_info) })
128 }
129
130 pub unsafe fn from_handle(
137 device: Arc<Device>,
138 handle: ash::vk::PipelineCache,
139 create_info: PipelineCacheCreateInfo,
140 ) -> Arc<PipelineCache> {
141 let PipelineCacheCreateInfo {
142 flags,
143 initial_data: _,
144 _ne: _,
145 } = create_info;
146
147 Arc::new(PipelineCache {
148 device: InstanceOwnedDebugWrapper(device),
149 handle,
150 id: Self::next_id(),
151
152 flags,
153 })
154 }
155
156 pub fn flags(&self) -> PipelineCacheCreateFlags {
158 self.flags
159 }
160
161 #[inline]
185 pub fn get_data(&self) -> Result<Vec<u8>, VulkanError> {
186 let fns = self.device.fns();
187
188 let data = loop {
189 let mut count = 0;
190 unsafe {
191 (fns.v1_0.get_pipeline_cache_data)(
192 self.device.handle(),
193 self.handle,
194 &mut count,
195 ptr::null_mut(),
196 )
197 }
198 .result()
199 .map_err(VulkanError::from)?;
200
201 let mut data: Vec<u8> = Vec::with_capacity(count);
202 let result = unsafe {
203 (fns.v1_0.get_pipeline_cache_data)(
204 self.device.handle(),
205 self.handle,
206 &mut count,
207 data.as_mut_ptr().cast(),
208 )
209 };
210
211 match result {
212 ash::vk::Result::SUCCESS => {
213 unsafe { data.set_len(count) };
214 break data;
215 }
216 ash::vk::Result::INCOMPLETE => (),
217 err => return Err(VulkanError::from(err)),
218 }
219 };
220
221 Ok(data)
222 }
223
224 pub fn merge<'a>(
230 &self,
231 src_caches: impl IntoIterator<Item = &'a PipelineCache>,
232 ) -> Result<(), Validated<VulkanError>> {
233 let src_caches: SmallVec<[_; 8]> = src_caches.into_iter().collect();
234 self.validate_merge(&src_caches)?;
235
236 Ok(unsafe { self.merge_unchecked(src_caches) }?)
237 }
238
239 fn validate_merge(&self, src_caches: &[&PipelineCache]) -> Result<(), Box<ValidationError>> {
240 for (index, &src_cache) in src_caches.iter().enumerate() {
241 if src_cache == self {
242 return Err(Box::new(ValidationError {
243 context: format!("src_caches[{}]", index).into(),
244 problem: "equals `self`".into(),
245 vuids: &["VUID-vkMergePipelineCaches-dstCache-00770"],
246 ..Default::default()
247 }));
248 }
249 }
250
251 Ok(())
252 }
253
254 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
255 pub unsafe fn merge_unchecked<'a>(
256 &self,
257 src_caches: impl IntoIterator<Item = &'a PipelineCache>,
258 ) -> Result<(), VulkanError> {
259 let src_caches_vk: SmallVec<[_; 8]> =
260 src_caches.into_iter().map(VulkanObject::handle).collect();
261
262 let fns = self.device.fns();
263 unsafe {
264 (fns.v1_0.merge_pipeline_caches)(
265 self.device.handle(),
266 self.handle,
267 src_caches_vk.len() as u32,
268 src_caches_vk.as_ptr(),
269 )
270 }
271 .result()
272 .map_err(VulkanError::from)?;
273
274 Ok(())
275 }
276}
277
278impl Drop for PipelineCache {
279 #[inline]
280 fn drop(&mut self) {
281 let fns = self.device.fns();
282 unsafe {
283 (fns.v1_0.destroy_pipeline_cache)(self.device.handle(), self.handle, ptr::null())
284 };
285 }
286}
287
288unsafe impl VulkanObject for PipelineCache {
289 type Handle = ash::vk::PipelineCache;
290
291 #[inline]
292 fn handle(&self) -> Self::Handle {
293 self.handle
294 }
295}
296
297unsafe impl DeviceOwned for PipelineCache {
298 #[inline]
299 fn device(&self) -> &Arc<Device> {
300 &self.device
301 }
302}
303
304impl_id_counter!(PipelineCache);
305
306#[derive(Clone, Debug)]
308pub struct PipelineCacheCreateInfo {
309 pub flags: PipelineCacheCreateFlags,
313
314 pub initial_data: Vec<u8>,
325
326 pub _ne: crate::NonExhaustive,
327}
328
329impl Default for PipelineCacheCreateInfo {
330 #[inline]
331 fn default() -> Self {
332 Self {
333 flags: PipelineCacheCreateFlags::empty(),
334 initial_data: Vec::new(),
335 _ne: crate::NonExhaustive(()),
336 }
337 }
338}
339
340impl PipelineCacheCreateInfo {
341 pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
342 let &Self {
343 flags,
344 initial_data: _,
345 _ne,
346 } = self;
347
348 flags.validate_device(device).map_err(|err| {
349 err.add_context("flags")
350 .set_vuids(&["VUID-VkPipelineCacheCreateInfo-flags-parameter"])
351 })?;
352
353 Ok(())
354 }
355
356 pub(crate) fn to_vk(&self) -> ash::vk::PipelineCacheCreateInfo<'_> {
357 let &Self {
358 flags,
359 ref initial_data,
360 _ne: _,
361 } = self;
362
363 let mut val_vk = ash::vk::PipelineCacheCreateInfo::default().flags(flags.into());
364
365 if !initial_data.is_empty() {
366 val_vk = val_vk.initial_data(initial_data);
367 }
368
369 val_vk
370 }
371}
372
373vulkan_bitflags! {
374 #[non_exhaustive]
375
376 PipelineCacheCreateFlags = PipelineCacheCreateFlags(u32);
378
379 }
387
388#[cfg(test)]
389mod tests {
390 use crate::{
391 pipeline::{
392 cache::PipelineCache, compute::ComputePipelineCreateInfo,
393 layout::PipelineDescriptorSetLayoutCreateInfo, ComputePipeline, PipelineLayout,
394 PipelineShaderStageCreateInfo,
395 },
396 shader::{ShaderModule, ShaderModuleCreateInfo},
397 };
398
399 #[test]
400 fn merge_self_forbidden() {
401 let (device, _queue) = gfx_dev_and_queue!();
402 let pipeline = unsafe { PipelineCache::new(device, Default::default()) }.unwrap();
403 match pipeline.merge([pipeline.as_ref()]) {
404 Err(_) => (),
405 Ok(_) => panic!(),
406 }
407 }
408
409 #[test]
410 fn cache_returns_same_data() {
411 let (device, _queue) = gfx_dev_and_queue!();
412
413 let cache = unsafe { PipelineCache::new(device.clone(), Default::default()) }.unwrap();
414
415 let cs = {
416 const MODULE: [u32; 48] = [
422 119734787, 65536, 524298, 6, 0, 131089, 1, 393227, 1, 1280527431, 1685353262,
423 808793134, 0, 196622, 0, 1, 327695, 5, 4, 1852399981, 0, 393232, 4, 17, 1, 1, 1,
424 196611, 2, 450, 262149, 4, 1852399981, 0, 131091, 2, 196641, 3, 2, 327734, 2, 4, 0,
425 3, 131320, 5, 65789, 65592,
426 ];
427 let module =
428 unsafe { ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&MODULE)) }
429 .unwrap();
430 module.entry_point("main").unwrap()
431 };
432
433 let _pipeline = {
434 let stage = PipelineShaderStageCreateInfo::new(cs);
435 let layout = PipelineLayout::new(
436 device.clone(),
437 PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
438 .into_pipeline_layout_create_info(device.clone())
439 .unwrap(),
440 )
441 .unwrap();
442 ComputePipeline::new(
443 device,
444 Some(cache.clone()),
445 ComputePipelineCreateInfo::stage_layout(stage, layout),
446 )
447 .unwrap()
448 };
449
450 let cache_data = cache.get_data().unwrap();
451 let second_data = cache.get_data().unwrap();
452
453 assert_eq!(cache_data, second_data);
454 }
455
456 #[test]
457 fn cache_returns_different_data() {
458 let (device, _queue) = gfx_dev_and_queue!();
459
460 let cache = unsafe { PipelineCache::new(device.clone(), Default::default()) }.unwrap();
461
462 let _first_pipeline = {
463 let cs = {
464 const MODULE: [u32; 48] = [
470 119734787, 65536, 524298, 6, 0, 131089, 1, 393227, 1, 1280527431, 1685353262,
471 808793134, 0, 196622, 0, 1, 327695, 5, 4, 1852399981, 0, 393232, 4, 17, 1, 1,
472 1, 196611, 2, 450, 262149, 4, 1852399981, 0, 131091, 2, 196641, 3, 2, 327734,
473 2, 4, 0, 3, 131320, 5, 65789, 65592,
474 ];
475 let module = unsafe {
476 ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&MODULE))
477 }
478 .unwrap();
479 module.entry_point("main").unwrap()
480 };
481
482 let stage = PipelineShaderStageCreateInfo::new(cs);
483 let layout = PipelineLayout::new(
484 device.clone(),
485 PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
486 .into_pipeline_layout_create_info(device.clone())
487 .unwrap(),
488 )
489 .unwrap();
490 ComputePipeline::new(
491 device.clone(),
492 Some(cache.clone()),
493 ComputePipelineCreateInfo::stage_layout(stage, layout),
494 )
495 .unwrap()
496 };
497
498 let cache_data = cache.get_data().unwrap();
499
500 let _second_pipeline = {
501 let cs = {
502 const MODULE: [u32; 108] = [
510 119734787, 65536, 524298, 16, 0, 131089, 1, 393227, 1, 1280527431, 1685353262,
511 808793134, 0, 196622, 0, 1, 393231, 5, 4, 1852399981, 0, 11, 393232, 4, 17, 1,
512 1, 1, 196611, 2, 450, 262149, 4, 1852399981, 0, 196613, 8, 7890025, 524293, 11,
513 1197436007, 1633841004, 1986939244, 1952539503, 1231974249, 68, 262215, 11, 11,
514 28, 131091, 2, 196641, 3, 2, 262165, 6, 32, 0, 262176, 7, 7, 6, 262167, 9, 6,
515 3, 262176, 10, 1, 9, 262203, 10, 11, 1, 262187, 6, 12, 0, 262176, 13, 1, 6,
516 327734, 2, 4, 0, 3, 131320, 5, 262203, 7, 8, 7, 327745, 13, 14, 11, 12, 262205,
517 6, 15, 14, 196670, 8, 15, 65789, 65592,
518 ];
519 let module = unsafe {
520 ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&MODULE))
521 }
522 .unwrap();
523 module.entry_point("main").unwrap()
524 };
525
526 let stage = PipelineShaderStageCreateInfo::new(cs);
527 let layout = PipelineLayout::new(
528 device.clone(),
529 PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
530 .into_pipeline_layout_create_info(device.clone())
531 .unwrap(),
532 )
533 .unwrap();
534 ComputePipeline::new(
535 device,
536 Some(cache.clone()),
537 ComputePipelineCreateInfo::stage_layout(stage, layout),
538 )
539 .unwrap()
540 };
541
542 let second_data = cache.get_data().unwrap();
543
544 if cache_data.is_empty() {
545 assert_eq!(cache_data, second_data);
546 } else {
547 assert_ne!(cache_data, second_data);
548 }
549 }
550
551 #[test]
552 fn cache_data_does_not_change() {
553 let (device, _queue) = gfx_dev_and_queue!();
554
555 let cache = unsafe { PipelineCache::new(device.clone(), Default::default()) }.unwrap();
556
557 let cs = {
558 const MODULE: [u32; 48] = [
564 119734787, 65536, 524298, 6, 0, 131089, 1, 393227, 1, 1280527431, 1685353262,
565 808793134, 0, 196622, 0, 1, 327695, 5, 4, 1852399981, 0, 393232, 4, 17, 1, 1, 1,
566 196611, 2, 450, 262149, 4, 1852399981, 0, 131091, 2, 196641, 3, 2, 327734, 2, 4, 0,
567 3, 131320, 5, 65789, 65592,
568 ];
569 let module =
570 unsafe { ShaderModule::new(device.clone(), ShaderModuleCreateInfo::new(&MODULE)) }
571 .unwrap();
572 module.entry_point("main").unwrap()
573 };
574
575 let _first_pipeline = {
576 let stage = PipelineShaderStageCreateInfo::new(cs.clone());
577 let layout = PipelineLayout::new(
578 device.clone(),
579 PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
580 .into_pipeline_layout_create_info(device.clone())
581 .unwrap(),
582 )
583 .unwrap();
584 ComputePipeline::new(
585 device.clone(),
586 Some(cache.clone()),
587 ComputePipelineCreateInfo::stage_layout(stage, layout),
588 )
589 .unwrap()
590 };
591
592 let cache_data = cache.get_data().unwrap();
593
594 let _second_pipeline = {
595 let stage = PipelineShaderStageCreateInfo::new(cs);
596 let layout = PipelineLayout::new(
597 device.clone(),
598 PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
599 .into_pipeline_layout_create_info(device.clone())
600 .unwrap(),
601 )
602 .unwrap();
603 ComputePipeline::new(
604 device,
605 Some(cache.clone()),
606 ComputePipelineCreateInfo::stage_layout(stage, layout),
607 )
608 .unwrap()
609 };
610
611 let second_data = cache.get_data().unwrap();
612
613 assert_eq!(cache_data, second_data);
614 }
615}