aboutsummaryrefslogtreecommitdiff
path: root/src/pipeline/compute_pipeline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pipeline/compute_pipeline.rs')
-rw-r--r--src/pipeline/compute_pipeline.rs523
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);
- }
-}