diff options
Diffstat (limited to 'src/pipeline/compute_pipeline.rs')
-rw-r--r-- | src/pipeline/compute_pipeline.rs | 523 |
1 files changed, 0 insertions, 523 deletions
diff --git a/src/pipeline/compute_pipeline.rs b/src/pipeline/compute_pipeline.rs deleted file mode 100644 index 0601374..0000000 --- a/src/pipeline/compute_pipeline.rs +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT -// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -use crate::check_errors; -use crate::descriptor_set::layout::DescriptorSetLayout; -use crate::device::Device; -use crate::device::DeviceOwned; -use crate::pipeline::cache::PipelineCache; -use crate::pipeline::layout::PipelineLayout; -use crate::pipeline::layout::PipelineLayoutCreationError; -use crate::pipeline::layout::PipelineLayoutSupersetError; -use crate::pipeline::shader::EntryPointAbstract; -use crate::pipeline::shader::SpecializationConstants; -use crate::Error; -use crate::OomError; -use crate::SafeDeref; -use crate::VulkanObject; -use std::error; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::mem::MaybeUninit; -use std::ptr; -use std::sync::Arc; - -/// A pipeline object that describes to the Vulkan implementation how it should perform compute -/// operations. -/// -/// The template parameter contains the descriptor set to use with this pipeline. -/// -/// All compute pipeline objects implement the `ComputePipelineAbstract` trait. You can turn any -/// `Arc<ComputePipeline>` into an `Arc<ComputePipelineAbstract>` if necessary. -/// -/// Pass an optional `Arc` to a `PipelineCache` to enable pipeline caching. The vulkan -/// implementation will handle the `PipelineCache` and check if it is available. -/// Check the documentation of the `PipelineCache` for more information. -pub struct ComputePipeline { - inner: Inner, - pipeline_layout: Arc<PipelineLayout>, -} - -struct Inner { - pipeline: ash::vk::Pipeline, - device: Arc<Device>, -} - -impl ComputePipeline { - /// Builds a new `ComputePipeline`. - pub fn new<Cs, Css>( - device: Arc<Device>, - shader: &Cs, - spec_constants: &Css, - cache: Option<Arc<PipelineCache>>, - ) -> Result<ComputePipeline, ComputePipelineCreationError> - where - Cs: EntryPointAbstract, - Css: SpecializationConstants, - { - unsafe { - let descriptor_set_layouts = shader - .descriptor_set_layout_descs() - .iter() - .map(|desc| { - Ok(Arc::new(DescriptorSetLayout::new( - device.clone(), - desc.clone(), - )?)) - }) - .collect::<Result<Vec<_>, OomError>>()?; - let pipeline_layout = Arc::new(PipelineLayout::new( - device.clone(), - descriptor_set_layouts, - shader.push_constant_range().iter().cloned(), - )?); - ComputePipeline::with_unchecked_pipeline_layout( - device, - shader, - spec_constants, - pipeline_layout, - cache, - ) - } - } - - /// Builds a new `ComputePipeline` with a specific pipeline layout. - /// - /// An error will be returned if the pipeline layout isn't a superset of what the shader - /// uses. - pub fn with_pipeline_layout<Cs, Css>( - device: Arc<Device>, - shader: &Cs, - spec_constants: &Css, - pipeline_layout: Arc<PipelineLayout>, - cache: Option<Arc<PipelineCache>>, - ) -> Result<ComputePipeline, ComputePipelineCreationError> - where - Cs: EntryPointAbstract, - Css: SpecializationConstants, - { - if Css::descriptors() != shader.spec_constants() { - return Err(ComputePipelineCreationError::IncompatibleSpecializationConstants); - } - - unsafe { - pipeline_layout.ensure_superset_of( - shader.descriptor_set_layout_descs(), - shader.push_constant_range(), - )?; - ComputePipeline::with_unchecked_pipeline_layout( - device, - shader, - spec_constants, - pipeline_layout, - cache, - ) - } - } - - /// Same as `with_pipeline_layout`, but doesn't check whether the pipeline layout is a - /// superset of what the shader expects. - pub unsafe fn with_unchecked_pipeline_layout<Cs, Css>( - device: Arc<Device>, - shader: &Cs, - spec_constants: &Css, - pipeline_layout: Arc<PipelineLayout>, - cache: Option<Arc<PipelineCache>>, - ) -> Result<ComputePipeline, ComputePipelineCreationError> - where - Cs: EntryPointAbstract, - Css: SpecializationConstants, - { - let fns = device.fns(); - - let pipeline = { - let spec_descriptors = Css::descriptors(); - let specialization = ash::vk::SpecializationInfo { - map_entry_count: spec_descriptors.len() as u32, - p_map_entries: spec_descriptors.as_ptr() as *const _, - data_size: mem::size_of_val(spec_constants), - p_data: spec_constants as *const Css as *const _, - }; - - let stage = ash::vk::PipelineShaderStageCreateInfo { - flags: ash::vk::PipelineShaderStageCreateFlags::empty(), - stage: ash::vk::ShaderStageFlags::COMPUTE, - module: shader.module().internal_object(), - p_name: shader.name().as_ptr(), - p_specialization_info: if specialization.data_size == 0 { - ptr::null() - } else { - &specialization - }, - ..Default::default() - }; - - let infos = ash::vk::ComputePipelineCreateInfo { - flags: ash::vk::PipelineCreateFlags::empty(), - stage, - layout: pipeline_layout.internal_object(), - base_pipeline_handle: ash::vk::Pipeline::null(), - base_pipeline_index: 0, - ..Default::default() - }; - - let cache_handle = match cache { - Some(ref cache) => cache.internal_object(), - None => ash::vk::PipelineCache::null(), - }; - - let mut output = MaybeUninit::uninit(); - check_errors(fns.v1_0.create_compute_pipelines( - device.internal_object(), - cache_handle, - 1, - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; - - Ok(ComputePipeline { - inner: Inner { - device: device.clone(), - pipeline: pipeline, - }, - pipeline_layout: pipeline_layout, - }) - } -} - -impl fmt::Debug for ComputePipeline { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "<Vulkan compute pipeline {:?}>", self.inner.pipeline) - } -} - -impl ComputePipeline { - /// Returns the `Device` this compute pipeline was created with. - #[inline] - pub fn device(&self) -> &Arc<Device> { - &self.inner.device - } -} - -/// Trait implemented on all compute pipelines. -pub unsafe trait ComputePipelineAbstract: DeviceOwned { - /// Returns an opaque object that represents the inside of the compute pipeline. - fn inner(&self) -> ComputePipelineSys; - - /// Returns the pipeline layout used in this compute pipeline. - fn layout(&self) -> &Arc<PipelineLayout>; -} - -unsafe impl ComputePipelineAbstract for ComputePipeline { - #[inline] - fn inner(&self) -> ComputePipelineSys { - ComputePipelineSys(self.inner.pipeline, PhantomData) - } - - #[inline] - fn layout(&self) -> &Arc<PipelineLayout> { - &self.pipeline_layout - } -} - -unsafe impl<T> ComputePipelineAbstract for T -where - T: SafeDeref, - T::Target: ComputePipelineAbstract, -{ - #[inline] - fn inner(&self) -> ComputePipelineSys { - (**self).inner() - } - - #[inline] - fn layout(&self) -> &Arc<PipelineLayout> { - (**self).layout() - } -} - -/// Opaque object that represents the inside of the compute pipeline. Can be made into a trait -/// object. -#[derive(Debug, Copy, Clone)] -pub struct ComputePipelineSys<'a>(ash::vk::Pipeline, PhantomData<&'a ()>); - -unsafe impl<'a> VulkanObject for ComputePipelineSys<'a> { - type Object = ash::vk::Pipeline; - - #[inline] - fn internal_object(&self) -> ash::vk::Pipeline { - self.0 - } -} - -unsafe impl DeviceOwned for ComputePipeline { - #[inline] - fn device(&self) -> &Arc<Device> { - self.device() - } -} - -unsafe impl VulkanObject for ComputePipeline { - type Object = ash::vk::Pipeline; - - #[inline] - fn internal_object(&self) -> ash::vk::Pipeline { - self.inner.pipeline - } -} - -impl Drop for Inner { - #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.v1_0 - .destroy_pipeline(self.device.internal_object(), self.pipeline, ptr::null()); - } - } -} - -/// Error that can happen when creating a compute pipeline. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ComputePipelineCreationError { - /// Not enough memory. - OomError(OomError), - /// Error while creating the pipeline layout object. - PipelineLayoutCreationError(PipelineLayoutCreationError), - /// The pipeline layout is not compatible with what the shader expects. - IncompatiblePipelineLayout(PipelineLayoutSupersetError), - /// The provided specialization constants are not compatible with what the shader expects. - IncompatibleSpecializationConstants, -} - -impl error::Error for ComputePipelineCreationError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - ComputePipelineCreationError::OomError(ref err) => Some(err), - ComputePipelineCreationError::PipelineLayoutCreationError(ref err) => Some(err), - ComputePipelineCreationError::IncompatiblePipelineLayout(ref err) => Some(err), - ComputePipelineCreationError::IncompatibleSpecializationConstants => None, - } - } -} - -impl fmt::Display for ComputePipelineCreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - ComputePipelineCreationError::OomError(_) => "not enough memory available", - ComputePipelineCreationError::PipelineLayoutCreationError(_) => { - "error while creating the pipeline layout object" - } - ComputePipelineCreationError::IncompatiblePipelineLayout(_) => { - "the pipeline layout is not compatible with what the shader expects" - } - ComputePipelineCreationError::IncompatibleSpecializationConstants => { - "the provided specialization constants are not compatible with what the shader expects" - } - } - ) - } -} - -impl From<OomError> for ComputePipelineCreationError { - #[inline] - fn from(err: OomError) -> ComputePipelineCreationError { - ComputePipelineCreationError::OomError(err) - } -} - -impl From<PipelineLayoutCreationError> for ComputePipelineCreationError { - #[inline] - fn from(err: PipelineLayoutCreationError) -> ComputePipelineCreationError { - ComputePipelineCreationError::PipelineLayoutCreationError(err) - } -} - -impl From<PipelineLayoutSupersetError> for ComputePipelineCreationError { - #[inline] - fn from(err: PipelineLayoutSupersetError) -> ComputePipelineCreationError { - ComputePipelineCreationError::IncompatiblePipelineLayout(err) - } -} - -impl From<Error> for ComputePipelineCreationError { - #[inline] - fn from(err: Error) -> ComputePipelineCreationError { - match err { - err @ Error::OutOfHostMemory => { - ComputePipelineCreationError::OomError(OomError::from(err)) - } - err @ Error::OutOfDeviceMemory => { - ComputePipelineCreationError::OomError(OomError::from(err)) - } - _ => panic!("unexpected error: {:?}", err), - } - } -} - -#[cfg(test)] -mod tests { - use crate::buffer::BufferUsage; - use crate::buffer::CpuAccessibleBuffer; - use crate::command_buffer::AutoCommandBufferBuilder; - use crate::command_buffer::CommandBufferUsage; - use crate::descriptor_set::layout::DescriptorBufferDesc; - use crate::descriptor_set::layout::DescriptorDesc; - use crate::descriptor_set::layout::DescriptorDescTy; - use crate::descriptor_set::layout::DescriptorSetDesc; - use crate::descriptor_set::PersistentDescriptorSet; - use crate::pipeline::shader::ShaderModule; - use crate::pipeline::shader::ShaderStages; - use crate::pipeline::shader::SpecializationConstants; - use crate::pipeline::shader::SpecializationMapEntry; - use crate::pipeline::ComputePipeline; - use crate::pipeline::ComputePipelineAbstract; - use crate::sync::now; - use crate::sync::GpuFuture; - use std::ffi::CStr; - use std::sync::Arc; - - // TODO: test for basic creation - // TODO: test for pipeline layout error - - #[test] - fn spec_constants() { - // This test checks whether specialization constants work. - // It executes a single compute shader (one invocation) that writes the value of a spec. - // constant to a buffer. The buffer content is then checked for the right value. - - let (device, queue) = gfx_dev_and_queue!(); - - let module = unsafe { - /* - #version 450 - - layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - - layout(constant_id = 83) const int VALUE = 0xdeadbeef; - - layout(set = 0, binding = 0) buffer Output { - int write; - } write; - - void main() { - write.write = VALUE; - } - */ - const MODULE: [u8; 480] = [ - 3, 2, 35, 7, 0, 0, 1, 0, 1, 0, 8, 0, 14, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 0, - 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0, - 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, - 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, - 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, - 109, 97, 105, 110, 0, 0, 0, 0, 5, 0, 4, 0, 7, 0, 0, 0, 79, 117, 116, 112, 117, 116, - 0, 0, 6, 0, 5, 0, 7, 0, 0, 0, 0, 0, 0, 0, 119, 114, 105, 116, 101, 0, 0, 0, 5, 0, - 4, 0, 9, 0, 0, 0, 119, 114, 105, 116, 101, 0, 0, 0, 5, 0, 4, 0, 11, 0, 0, 0, 86, - 65, 76, 85, 69, 0, 0, 0, 72, 0, 5, 0, 7, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, - 0, 71, 0, 3, 0, 7, 0, 0, 0, 3, 0, 0, 0, 71, 0, 4, 0, 9, 0, 0, 0, 34, 0, 0, 0, 0, 0, - 0, 0, 71, 0, 4, 0, 9, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 11, 0, 0, 0, - 1, 0, 0, 0, 83, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, - 0, 21, 0, 4, 0, 6, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 30, 0, 3, 0, 7, 0, 0, 0, 6, 0, - 0, 0, 32, 0, 4, 0, 8, 0, 0, 0, 2, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0, 8, 0, 0, 0, 9, - 0, 0, 0, 2, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 50, 0, 4, 0, - 6, 0, 0, 0, 11, 0, 0, 0, 239, 190, 173, 222, 32, 0, 4, 0, 12, 0, 0, 0, 2, 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, 0, 248, 0, 2, - 0, 5, 0, 0, 0, 65, 0, 5, 0, 12, 0, 0, 0, 13, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 62, - 0, 3, 0, 13, 0, 0, 0, 11, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, - ]; - ShaderModule::new(device.clone(), &MODULE).unwrap() - }; - - let shader = unsafe { - static NAME: [u8; 5] = [109, 97, 105, 110, 0]; // "main" - module.compute_entry_point( - CStr::from_ptr(NAME.as_ptr() as *const _), - [DescriptorSetDesc::new([Some(DescriptorDesc { - ty: DescriptorDescTy::Buffer(DescriptorBufferDesc { - dynamic: Some(false), - storage: true, - }), - array_count: 1, - stages: ShaderStages { - compute: true, - ..ShaderStages::none() - }, - readonly: true, - })])], - None, - SpecConsts::descriptors(), - ) - }; - - #[derive(Debug, Copy, Clone)] - #[allow(non_snake_case)] - #[repr(C)] - struct SpecConsts { - VALUE: i32, - } - unsafe impl SpecializationConstants for SpecConsts { - fn descriptors() -> &'static [SpecializationMapEntry] { - static DESCRIPTORS: [SpecializationMapEntry; 1] = [SpecializationMapEntry { - constant_id: 83, - offset: 0, - size: 4, - }]; - &DESCRIPTORS - } - } - - let pipeline = Arc::new( - ComputePipeline::new( - device.clone(), - &shader, - &SpecConsts { VALUE: 0x12345678 }, - None, - ) - .unwrap(), - ); - - let data_buffer = - CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap(); - let layout = pipeline.layout().descriptor_set_layouts().get(0).unwrap(); - let set = PersistentDescriptorSet::start(layout.clone()) - .add_buffer(data_buffer.clone()) - .unwrap() - .build() - .unwrap(); - - let mut cbb = AutoCommandBufferBuilder::primary( - device.clone(), - queue.family(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - cbb.dispatch([1, 1, 1], pipeline.clone(), set, ()).unwrap(); - let cb = cbb.build().unwrap(); - - let future = now(device.clone()) - .then_execute(queue.clone(), cb) - .unwrap() - .then_signal_fence_and_flush() - .unwrap(); - future.wait(None).unwrap(); - - let data_buffer_content = data_buffer.read().unwrap(); - assert_eq!(*data_buffer_content, 0x12345678); - } -} |