vulkano 0.29.0

Safe wrapper for the Vulkan graphics API
Documentation
// 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::buffer::TypedBufferAccess;
use crate::device::physical::QueueFamily;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::query::GetResultsError;
use crate::query::QueryControlFlags;
use crate::query::QueryPool;
use crate::query::QueryResultElement;
use crate::query::QueryResultFlags;
use crate::query::QueryType;
use crate::sync::PipelineStage;
use crate::DeviceSize;
use crate::VulkanObject;
use std::error;
use std::fmt;
use std::ops::Range;

/// Checks whether a `begin_query` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_begin_query(
    device: &Device,
    query_pool: &QueryPool,
    query: u32,
    flags: QueryControlFlags,
) -> Result<(), CheckBeginQueryError> {
    assert_eq!(
        device.internal_object(),
        query_pool.device().internal_object(),
    );
    query_pool
        .query(query)
        .ok_or(CheckBeginQueryError::OutOfRange)?;

    match query_pool.query_type() {
        QueryType::Occlusion => {
            if flags.precise && !device.enabled_features().occlusion_query_precise {
                return Err(CheckBeginQueryError::OcclusionQueryPreciseFeatureNotEnabled);
            }
        }
        QueryType::PipelineStatistics(_) => {
            if flags.precise {
                return Err(CheckBeginQueryError::InvalidFlags);
            }
        }
        QueryType::Timestamp => return Err(CheckBeginQueryError::NotPermitted),
    }

    Ok(())
}

/// Error that can happen from `check_begin_query`.
#[derive(Debug, Copy, Clone)]
pub enum CheckBeginQueryError {
    /// The provided flags are not allowed for this type of query.
    InvalidFlags,
    /// This operation is not permitted on this query type.
    NotPermitted,
    /// `QueryControlFlags::precise` was requested, but the `occlusion_query_precise` feature was not enabled.
    OcclusionQueryPreciseFeatureNotEnabled,
    /// The provided query index is not valid for this pool.
    OutOfRange,
}

impl error::Error for CheckBeginQueryError {}

impl fmt::Display for CheckBeginQueryError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                Self::InvalidFlags => {
                    "the provided flags are not allowed for this type of query"
                }
                Self::NotPermitted => {
                    "this operation is not permitted on this query type"
                }
                Self::OcclusionQueryPreciseFeatureNotEnabled => {
                    "QueryControlFlags::precise was requested, but the occlusion_query_precise feature was not enabled"
                }
                Self::OutOfRange => {
                    "the provided query index is not valid for this pool"
                }
            }
        )
    }
}

/// Checks whether a `end_query` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_end_query(
    device: &Device,
    query_pool: &QueryPool,
    query: u32,
) -> Result<(), CheckEndQueryError> {
    assert_eq!(
        device.internal_object(),
        query_pool.device().internal_object(),
    );

    query_pool
        .query(query)
        .ok_or(CheckEndQueryError::OutOfRange)?;

    Ok(())
}

/// Error that can happen from `check_end_query`.
#[derive(Debug, Copy, Clone)]
pub enum CheckEndQueryError {
    /// The provided query index is not valid for this pool.
    OutOfRange,
}

impl error::Error for CheckEndQueryError {}

impl fmt::Display for CheckEndQueryError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                Self::OutOfRange => {
                    "the provided query index is not valid for this pool"
                }
            }
        )
    }
}

/// Checks whether a `write_timestamp` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_write_timestamp(
    device: &Device,
    queue_family: QueueFamily,
    query_pool: &QueryPool,
    query: u32,
    stage: PipelineStage,
) -> Result<(), CheckWriteTimestampError> {
    assert_eq!(
        device.internal_object(),
        query_pool.device().internal_object(),
    );

    if !matches!(query_pool.query_type(), QueryType::Timestamp) {
        return Err(CheckWriteTimestampError::NotPermitted);
    }

    query_pool
        .query(query)
        .ok_or(CheckWriteTimestampError::OutOfRange)?;

    if queue_family.timestamp_valid_bits().is_none() {
        return Err(CheckWriteTimestampError::NoTimestampValidBits);
    }

    if !queue_family.supports_stage(stage) {
        return Err(CheckWriteTimestampError::StageNotSupported);
    }

    match stage {
        PipelineStage::GeometryShader => {
            if !device.enabled_features().geometry_shader {
                return Err(CheckWriteTimestampError::GeometryShaderFeatureNotEnabled);
            }
        }
        PipelineStage::TessellationControlShader | PipelineStage::TessellationEvaluationShader => {
            if !device.enabled_features().tessellation_shader {
                return Err(CheckWriteTimestampError::TessellationShaderFeatureNotEnabled);
            }
        }
        _ => (),
    }

    Ok(())
}

/// Error that can happen from `check_write_timestamp`.
#[derive(Debug, Copy, Clone)]
pub enum CheckWriteTimestampError {
    /// The geometry shader stage was requested, but the `geometry_shader` feature was not enabled.
    GeometryShaderFeatureNotEnabled,
    /// The queue family's `timestamp_valid_bits` value is `None`.
    NoTimestampValidBits,
    /// This operation is not permitted on this query type.
    NotPermitted,
    /// The provided query index is not valid for this pool.
    OutOfRange,
    /// The provided stage is not supported by the queue family.
    StageNotSupported,
    /// A tessellation shader stage was requested, but the `tessellation_shader` feature was not enabled.
    TessellationShaderFeatureNotEnabled,
}

impl error::Error for CheckWriteTimestampError {}

impl fmt::Display for CheckWriteTimestampError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                Self::GeometryShaderFeatureNotEnabled => {
                    "the geometry shader stage was requested, but the geometry_shader feature was not enabled"
                }
                Self::NoTimestampValidBits => {
                    "the queue family's timestamp_valid_bits value is None"
                }
                Self::NotPermitted => {
                    "this operation is not permitted on this query type"
                }
                Self::OutOfRange => {
                    "the provided query index is not valid for this pool"
                }
                Self::StageNotSupported => {
                    "the provided stage is not supported by the queue family"
                }
                Self::TessellationShaderFeatureNotEnabled => {
                    "a tessellation shader stage was requested, but the tessellation_shader feature was not enabled"
                }
            }
        )
    }
}

/// Checks whether a `copy_query_pool_results` command is valid.
///
/// # Panic
///
/// - Panics if the query pool or buffer was not created with `device`.
pub fn check_copy_query_pool_results<D, T>(
    device: &Device,
    query_pool: &QueryPool,
    queries: Range<u32>,
    destination: &D,
    flags: QueryResultFlags,
) -> Result<DeviceSize, CheckCopyQueryPoolResultsError>
where
    D: ?Sized + TypedBufferAccess<Content = [T]>,
    T: QueryResultElement,
{
    let buffer_inner = destination.inner();
    assert_eq!(
        device.internal_object(),
        buffer_inner.buffer.device().internal_object(),
    );
    assert_eq!(
        device.internal_object(),
        query_pool.device().internal_object(),
    );

    if !buffer_inner.buffer.usage().transfer_destination {
        return Err(CheckCopyQueryPoolResultsError::DestinationMissingTransferUsage);
    }

    let queries_range = query_pool
        .queries_range(queries)
        .ok_or(CheckCopyQueryPoolResultsError::OutOfRange)?;

    Ok(queries_range.check_query_pool_results::<T>(
        buffer_inner.offset,
        destination.len(),
        flags,
    )?)
}

/// Error that can happen from `check_copy_query_pool_results`.
#[derive(Debug, Copy, Clone)]
pub enum CheckCopyQueryPoolResultsError {
    /// The buffer is too small for the copy operation.
    BufferTooSmall {
        /// Required number of elements in the buffer.
        required_len: DeviceSize,
        /// Actual number of elements in the buffer.
        actual_len: DeviceSize,
    },
    /// The destination buffer is missing the transfer destination usage.
    DestinationMissingTransferUsage,
    /// The provided flags are not allowed for this type of query.
    InvalidFlags,
    /// The provided queries range is not valid for this pool.
    OutOfRange,
}

impl From<GetResultsError> for CheckCopyQueryPoolResultsError {
    #[inline]
    fn from(value: GetResultsError) -> Self {
        match value {
            GetResultsError::BufferTooSmall {
                required_len,
                actual_len,
            } => CheckCopyQueryPoolResultsError::BufferTooSmall {
                required_len,
                actual_len,
            },
            GetResultsError::InvalidFlags => CheckCopyQueryPoolResultsError::InvalidFlags,
            GetResultsError::DeviceLost | GetResultsError::OomError(_) => unreachable!(),
        }
    }
}

impl error::Error for CheckCopyQueryPoolResultsError {}

impl fmt::Display for CheckCopyQueryPoolResultsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                Self::BufferTooSmall { .. } => {
                    "the buffer is too small for the copy operation"
                }
                Self::DestinationMissingTransferUsage => {
                    "the destination buffer is missing the transfer destination usage"
                }
                Self::InvalidFlags => {
                    "the provided flags are not allowed for this type of query"
                }
                Self::OutOfRange => {
                    "the provided queries range is not valid for this pool"
                }
            }
        )
    }
}

/// Checks whether a `reset_query_pool` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_reset_query_pool(
    device: &Device,
    query_pool: &QueryPool,
    queries: Range<u32>,
) -> Result<(), CheckResetQueryPoolError> {
    assert_eq!(
        device.internal_object(),
        query_pool.device().internal_object(),
    );
    query_pool
        .queries_range(queries)
        .ok_or(CheckResetQueryPoolError::OutOfRange)?;
    Ok(())
}

/// Error that can happen from `check_reset_query_pool`.
#[derive(Debug, Copy, Clone)]
pub enum CheckResetQueryPoolError {
    /// The provided queries range is not valid for this pool.
    OutOfRange,
}

impl error::Error for CheckResetQueryPoolError {}

impl fmt::Display for CheckResetQueryPoolError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                Self::OutOfRange => {
                    "the provided queries range is not valid for this pool"
                }
            }
        )
    }
}