• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 //! Cache the pipeline objects to disk for faster reloads.
11 //!
12 //! A pipeline cache is an opaque type that allow you to cache your graphics and compute
13 //! pipelines on the disk.
14 //!
15 //! You can create either an empty cache or a cache from some initial data. Whenever you create a
16 //! graphics or compute pipeline, you have the possibility to pass a reference to that cache.
17 //! The Vulkan implementation will then look in the cache for an existing entry, or add one if it
18 //! doesn't exist.
19 //!
20 //! Once that is done, you can extract the data from the cache and store it. See the documentation
21 //! of [`get_data`](struct.PipelineCache.html#method.get_data) for example of how to store the data
22 //! on the disk, and [`with_data`](struct.PipelineCache.html#method.with_data) for how to reload it.
23 
24 use crate::check_errors;
25 use crate::device::Device;
26 use crate::OomError;
27 use crate::VulkanObject;
28 use std::mem::MaybeUninit;
29 use std::ptr;
30 use std::sync::Arc;
31 
32 /// Opaque cache that contains pipeline objects.
33 ///
34 /// See [the documentation of the module](index.html) for more info.
35 pub struct PipelineCache {
36     device: Arc<Device>,
37     cache: ash::vk::PipelineCache,
38 }
39 
40 impl PipelineCache {
41     /// Builds a new pipeline cache from existing data. The data must have been previously obtained
42     /// with [`get_data`](#method.get_data).
43     ///
44     /// The data passed to this function will most likely be blindly trusted by the Vulkan
45     /// implementation. Therefore you can easily crash your application or the system by passing
46     /// wrong data. Hence why this function is unsafe.
47     ///
48     /// # Example
49     ///
50     /// This example loads a cache from a file, if it exists.
51     /// See [`get_data`](#method.get_data) for how to store the data in a file.
52     /// TODO: there's a header in the cached data that must be checked ; talk about this
53     ///
54     /// ```
55     /// # use std::sync::Arc;
56     /// # use vulkano::device::Device;
57     /// use std::fs::File;
58     /// use std::io::Read;
59     /// use vulkano::pipeline::cache::PipelineCache;
60     /// # let device: Arc<Device> = return;
61     ///
62     /// let data = {
63     ///     let file = File::open("pipeline_cache.bin");
64     ///     if let Ok(mut file) = file {
65     ///         let mut data = Vec::new();
66     ///         if let Ok(_) = file.read_to_end(&mut data) {
67     ///             Some(data)
68     ///         } else { None }
69     ///     } else { None }
70     /// };
71     ///
72     /// let cache = if let Some(data) = data {
73     ///     // This is unsafe because there is no way to be sure that the file contains valid data.
74     ///     unsafe { PipelineCache::with_data(device.clone(), &data).unwrap() }
75     /// } else {
76     ///     PipelineCache::empty(device.clone()).unwrap()
77     /// };
78     /// ```
79     #[inline]
with_data( device: Arc<Device>, initial_data: &[u8], ) -> Result<Arc<PipelineCache>, OomError>80     pub unsafe fn with_data(
81         device: Arc<Device>,
82         initial_data: &[u8],
83     ) -> Result<Arc<PipelineCache>, OomError> {
84         PipelineCache::new_impl(device, Some(initial_data))
85     }
86 
87     /// Builds a new empty pipeline cache.
88     ///
89     /// # Example
90     ///
91     /// ```
92     /// # use std::sync::Arc;
93     /// # use vulkano::device::Device;
94     /// use vulkano::pipeline::cache::PipelineCache;
95     /// # let device: Arc<Device> = return;
96     /// let cache = PipelineCache::empty(device.clone()).unwrap();
97     /// ```
98     #[inline]
empty(device: Arc<Device>) -> Result<Arc<PipelineCache>, OomError>99     pub fn empty(device: Arc<Device>) -> Result<Arc<PipelineCache>, OomError> {
100         unsafe { PipelineCache::new_impl(device, None) }
101     }
102 
103     // Actual implementation of the constructor.
new_impl( device: Arc<Device>, initial_data: Option<&[u8]>, ) -> Result<Arc<PipelineCache>, OomError>104     unsafe fn new_impl(
105         device: Arc<Device>,
106         initial_data: Option<&[u8]>,
107     ) -> Result<Arc<PipelineCache>, OomError> {
108         let fns = device.fns();
109 
110         let cache = {
111             let infos = ash::vk::PipelineCacheCreateInfo {
112                 flags: ash::vk::PipelineCacheCreateFlags::empty(),
113                 initial_data_size: initial_data.map(|d| d.len()).unwrap_or(0),
114                 p_initial_data: initial_data
115                     .map(|d| d.as_ptr() as *const _)
116                     .unwrap_or(ptr::null()),
117                 ..Default::default()
118             };
119 
120             let mut output = MaybeUninit::uninit();
121             check_errors(fns.v1_0.create_pipeline_cache(
122                 device.internal_object(),
123                 &infos,
124                 ptr::null(),
125                 output.as_mut_ptr(),
126             ))?;
127             output.assume_init()
128         };
129 
130         Ok(Arc::new(PipelineCache {
131             device: device.clone(),
132             cache: cache,
133         }))
134     }
135 
136     /// Merges other pipeline caches into this one.
137     ///
138     /// It is `self` that is modified here. The pipeline caches passed as parameter are untouched.
139     ///
140     /// # Panic
141     ///
142     /// - Panics if `self` is included in the list of other pipelines.
143     ///
144     // FIXME: vkMergePipelineCaches is not thread safe for the destination cache
145     // TODO: write example
merge<'a, I>(&self, pipelines: I) -> Result<(), OomError> where I: IntoIterator<Item = &'a &'a Arc<PipelineCache>>,146     pub fn merge<'a, I>(&self, pipelines: I) -> Result<(), OomError>
147     where
148         I: IntoIterator<Item = &'a &'a Arc<PipelineCache>>,
149     {
150         unsafe {
151             let fns = self.device.fns();
152 
153             let pipelines = pipelines
154                 .into_iter()
155                 .map(|pipeline| {
156                     assert!(&***pipeline as *const _ != &*self as *const _);
157                     pipeline.cache
158                 })
159                 .collect::<Vec<_>>();
160 
161             check_errors(fns.v1_0.merge_pipeline_caches(
162                 self.device.internal_object(),
163                 self.cache,
164                 pipelines.len() as u32,
165                 pipelines.as_ptr(),
166             ))?;
167 
168             Ok(())
169         }
170     }
171 
172     /// Obtains the data from the cache.
173     ///
174     /// This data can be stored and then reloaded and passed to `PipelineCache::with_data`.
175     ///
176     /// # Example
177     ///
178     /// This example stores the data of a pipeline cache on the disk.
179     /// See [`with_data`](#method.with_data) for how to reload it.
180     ///
181     /// ```
182     /// use std::fs;
183     /// use std::fs::File;
184     /// use std::io::Write;
185     /// # use std::sync::Arc;
186     /// # use vulkano::pipeline::cache::PipelineCache;
187     ///
188     /// # let cache: Arc<PipelineCache> = return;
189     /// // If an error happens (eg. no permission for the file) we simply skip storing the cache.
190     /// if let Ok(data) = cache.get_data() {
191     ///     if let Ok(mut file) = File::create("pipeline_cache.bin.tmp") {
192     ///         if let Ok(_) = file.write_all(&data) {
193     ///             let _ = fs::rename("pipeline_cache.bin.tmp", "pipeline_cache.bin");
194     ///         } else {
195     ///             let _ = fs::remove_file("pipeline_cache.bin.tmp");
196     ///         }
197     ///     }
198     /// }
199     /// ```
get_data(&self) -> Result<Vec<u8>, OomError>200     pub fn get_data(&self) -> Result<Vec<u8>, OomError> {
201         unsafe {
202             let fns = self.device.fns();
203 
204             let mut num = 0;
205             check_errors(fns.v1_0.get_pipeline_cache_data(
206                 self.device.internal_object(),
207                 self.cache,
208                 &mut num,
209                 ptr::null_mut(),
210             ))?;
211 
212             let mut data: Vec<u8> = Vec::with_capacity(num as usize);
213             check_errors(fns.v1_0.get_pipeline_cache_data(
214                 self.device.internal_object(),
215                 self.cache,
216                 &mut num,
217                 data.as_mut_ptr() as *mut _,
218             ))?;
219             data.set_len(num as usize);
220 
221             Ok(data)
222         }
223     }
224 }
225 
226 unsafe impl VulkanObject for PipelineCache {
227     type Object = ash::vk::PipelineCache;
228 
229     #[inline]
internal_object(&self) -> ash::vk::PipelineCache230     fn internal_object(&self) -> ash::vk::PipelineCache {
231         self.cache
232     }
233 }
234 
235 impl Drop for PipelineCache {
236     #[inline]
drop(&mut self)237     fn drop(&mut self) {
238         unsafe {
239             let fns = self.device.fns();
240             fns.v1_0
241                 .destroy_pipeline_cache(self.device.internal_object(), self.cache, ptr::null());
242         }
243     }
244 }
245 
246 #[cfg(test)]
247 mod tests {
248     use crate::pipeline::cache::PipelineCache;
249     use crate::pipeline::shader::ShaderModule;
250     use crate::pipeline::shader::SpecializationConstants;
251     use crate::pipeline::ComputePipeline;
252     use std::{ffi::CStr, sync::Arc};
253 
254     #[test]
merge_self_forbidden()255     fn merge_self_forbidden() {
256         let (device, queue) = gfx_dev_and_queue!();
257         let pipeline = PipelineCache::empty(device).unwrap();
258         assert_should_panic!({
259             pipeline.merge(&[&pipeline]).unwrap();
260         });
261     }
262 
263     #[test]
cache_returns_same_data()264     fn cache_returns_same_data() {
265         let (device, queue) = gfx_dev_and_queue!();
266 
267         let cache = PipelineCache::empty(device.clone()).unwrap();
268 
269         let module = unsafe {
270             /*
271              * #version 450
272              * void main() {
273              * }
274              */
275             const MODULE: [u8; 192] = [
276                 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 6, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 0,
277                 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0,
278                 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 5, 0, 5, 0, 0, 0, 4, 0, 0, 0,
279                 109, 97, 105, 110, 0, 0, 0, 0, 16, 0, 6, 0, 4, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 1,
280                 0, 0, 0, 1, 0, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 194, 1, 0, 0, 5, 0, 4, 0, 4, 0, 0, 0,
281                 109, 97, 105, 110, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2,
282                 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0,
283                 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
284             ];
285             ShaderModule::new(device.clone(), &MODULE).unwrap()
286         };
287 
288         let shader = unsafe {
289             static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main"
290             module.compute_entry_point(
291                 CStr::from_ptr(NAME.as_ptr() as *const _),
292                 [],
293                 None,
294                 <()>::descriptors(),
295             )
296         };
297 
298         let pipeline = Arc::new(
299             ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone())).unwrap(),
300         );
301 
302         let cache_data = cache.get_data().unwrap();
303         let second_data = cache.get_data().unwrap();
304 
305         assert_eq!(cache_data, second_data);
306     }
307 
308     #[test]
cache_returns_different_data()309     fn cache_returns_different_data() {
310         let (device, queue) = gfx_dev_and_queue!();
311 
312         let cache = PipelineCache::empty(device.clone()).unwrap();
313 
314         let first_module = unsafe {
315             /*
316              * #version 450
317              * void main() {
318              * }
319              */
320             const MODULE: [u8; 192] = [
321                 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 6, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 0,
322                 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0,
323                 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 5, 0, 5, 0, 0, 0, 4, 0, 0, 0,
324                 109, 97, 105, 110, 0, 0, 0, 0, 16, 0, 6, 0, 4, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 1,
325                 0, 0, 0, 1, 0, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 194, 1, 0, 0, 5, 0, 4, 0, 4, 0, 0, 0,
326                 109, 97, 105, 110, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2,
327                 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0,
328                 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
329             ];
330             ShaderModule::new(device.clone(), &MODULE).unwrap()
331         };
332 
333         let first_shader = unsafe {
334             static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main"
335             first_module.compute_entry_point(
336                 CStr::from_ptr(NAME.as_ptr() as *const _),
337                 [],
338                 None,
339                 <()>::descriptors(),
340             )
341         };
342 
343         let second_module = unsafe {
344             /*
345              * #version 450
346              *
347              * void main() {
348              *     uint idx = gl_GlobalInvocationID.x;
349              * }
350              */
351             const SECOND_MODULE: [u8; 432] = [
352                 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 16, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0,
353                 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48,
354                 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 6, 0, 5, 0, 0, 0, 4, 0, 0,
355                 0, 109, 97, 105, 110, 0, 0, 0, 0, 11, 0, 0, 0, 16, 0, 6, 0, 4, 0, 0, 0, 17, 0, 0,
356                 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 194, 1, 0, 0, 5, 0,
357                 4, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 5, 0, 3, 0, 8, 0, 0, 0, 105, 100,
358                 120, 0, 5, 0, 8, 0, 11, 0, 0, 0, 103, 108, 95, 71, 108, 111, 98, 97, 108, 73, 110,
359                 118, 111, 99, 97, 116, 105, 111, 110, 73, 68, 0, 0, 0, 71, 0, 4, 0, 11, 0, 0, 0,
360                 11, 0, 0, 0, 28, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0,
361                 0, 0, 21, 0, 4, 0, 6, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 7, 0, 0, 0, 7,
362                 0, 0, 0, 6, 0, 0, 0, 23, 0, 4, 0, 9, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0,
363                 10, 0, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 59, 0, 4, 0, 10, 0, 0, 0, 11, 0, 0, 0, 1, 0,
364                 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 13, 0, 0, 0,
365                 1, 0, 0, 0, 6, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0,
366                 0, 248, 0, 2, 0, 5, 0, 0, 0, 59, 0, 4, 0, 7, 0, 0, 0, 8, 0, 0, 0, 7, 0, 0, 0, 65,
367                 0, 5, 0, 13, 0, 0, 0, 14, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0,
368                 0, 15, 0, 0, 0, 14, 0, 0, 0, 62, 0, 3, 0, 8, 0, 0, 0, 15, 0, 0, 0, 253, 0, 1, 0,
369                 56, 0, 1, 0,
370             ];
371             ShaderModule::new(device.clone(), &SECOND_MODULE).unwrap()
372         };
373 
374         let second_shader = unsafe {
375             static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main"
376             second_module.compute_entry_point(
377                 CStr::from_ptr(NAME.as_ptr() as *const _),
378                 [],
379                 None,
380                 <()>::descriptors(),
381             )
382         };
383 
384         let pipeline = Arc::new(
385             ComputePipeline::new(device.clone(), &first_shader, &(), Some(cache.clone())).unwrap(),
386         );
387 
388         let cache_data = cache.get_data().unwrap();
389 
390         let second_pipeline = Arc::new(
391             ComputePipeline::new(device.clone(), &second_shader, &(), Some(cache.clone())).unwrap(),
392         );
393 
394         let second_data = cache.get_data().unwrap();
395 
396         if cache_data.is_empty() {
397             assert_eq!(cache_data, second_data);
398         } else {
399             assert_ne!(cache_data, second_data);
400         }
401     }
402 
403     #[test]
cache_data_does_not_change()404     fn cache_data_does_not_change() {
405         let (device, queue) = gfx_dev_and_queue!();
406 
407         let cache = PipelineCache::empty(device.clone()).unwrap();
408 
409         let module = unsafe {
410             /*
411              * #version 450
412              * void main() {
413              * }
414              */
415             const MODULE: [u8; 192] = [
416                 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 6, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 0,
417                 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0,
418                 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 5, 0, 5, 0, 0, 0, 4, 0, 0, 0,
419                 109, 97, 105, 110, 0, 0, 0, 0, 16, 0, 6, 0, 4, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 1,
420                 0, 0, 0, 1, 0, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 194, 1, 0, 0, 5, 0, 4, 0, 4, 0, 0, 0,
421                 109, 97, 105, 110, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2,
422                 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0,
423                 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0,
424             ];
425             ShaderModule::new(device.clone(), &MODULE).unwrap()
426         };
427 
428         let shader = unsafe {
429             static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main"
430             module.compute_entry_point(
431                 CStr::from_ptr(NAME.as_ptr() as *const _),
432                 [],
433                 None,
434                 <()>::descriptors(),
435             )
436         };
437 
438         let pipeline = Arc::new(
439             ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone())).unwrap(),
440         );
441 
442         let cache_data = cache.get_data().unwrap();
443 
444         let second_pipeline = Arc::new(
445             ComputePipeline::new(device.clone(), &shader, &(), Some(cache.clone())).unwrap(),
446         );
447 
448         let second_data = cache.get_data().unwrap();
449 
450         assert_eq!(cache_data, second_data);
451     }
452 }
453