/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ device::{DeviceError, HostMap, MissingFeatures}, hub::Resource, id::{DeviceId, SwapChainId, TextureId}, memory_init_tracker::MemoryInitTracker, track::{TextureSelector, DUMMY_SELECTOR}, validation::MissingBufferUsageError, Label, LifeGuard, RefCount, Stored, }; use thiserror::Error; use std::{borrow::Borrow, num::NonZeroU8, ops::Range, ptr::NonNull}; #[repr(C)] #[derive(Debug)] pub enum BufferMapAsyncStatus { Success, Error, Aborted, Unknown, ContextLost, } #[derive(Debug)] pub(crate) enum BufferMapState { /// Mapped at creation. Init { ptr: NonNull, stage_buffer: A::Buffer, needs_flush: bool, }, /// Waiting for GPU to be done before mapping Waiting(BufferPendingMapping), /// Mapped Active { ptr: NonNull, range: hal::MemoryRange, host: HostMap, }, /// Not mapped Idle, } unsafe impl Send for BufferMapState {} unsafe impl Sync for BufferMapState {} pub type BufferMapCallback = unsafe extern "C" fn(status: BufferMapAsyncStatus, userdata: *mut u8); #[repr(C)] #[derive(Debug)] pub struct BufferMapOperation { pub host: HostMap, pub callback: BufferMapCallback, pub user_data: *mut u8, } //TODO: clarify if/why this is needed here unsafe impl Send for BufferMapOperation {} unsafe impl Sync for BufferMapOperation {} impl BufferMapOperation { pub(crate) fn call_error(self) { log::error!("wgpu_buffer_map_async failed: buffer mapping is pending"); unsafe { (self.callback)(BufferMapAsyncStatus::Error, self.user_data); } } } #[derive(Clone, Debug, Error)] pub enum BufferAccessError { #[error(transparent)] Device(#[from] DeviceError), #[error("buffer is invalid")] Invalid, #[error("buffer is destroyed")] Destroyed, #[error("buffer is already mapped")] AlreadyMapped, #[error(transparent)] MissingBufferUsage(#[from] MissingBufferUsageError), #[error("buffer is not mapped")] NotMapped, #[error( "buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`" )] UnalignedRange, #[error("buffer offset invalid: offset {offset} must be multiple of 8")] UnalignedOffset { offset: wgt::BufferAddress }, #[error("buffer range size invalid: range_size {range_size} must be multiple of 4")] UnalignedRangeSize { range_size: wgt::BufferAddress }, #[error("buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")] OutOfBoundsUnderrun { index: wgt::BufferAddress, min: wgt::BufferAddress, }, #[error( "buffer access out of bounds: last index {index} would overrun the buffer (limit: {max})" )] OutOfBoundsOverrun { index: wgt::BufferAddress, max: wgt::BufferAddress, }, } #[derive(Debug)] pub(crate) struct BufferPendingMapping { pub range: Range, pub op: BufferMapOperation, // hold the parent alive while the mapping is active pub parent_ref_count: RefCount, } pub type BufferDescriptor<'a> = wgt::BufferDescriptor>; #[derive(Debug)] pub struct Buffer { pub(crate) raw: Option, pub(crate) device_id: Stored, pub(crate) is_coherent: bool, pub(crate) usage: wgt::BufferUsage, pub(crate) size: wgt::BufferAddress, pub(crate) initialization_status: MemoryInitTracker, pub(crate) sync_mapped_writes: Option, pub(crate) life_guard: LifeGuard, pub(crate) map_state: BufferMapState, } #[derive(Clone, Debug, Error)] pub enum CreateBufferError { #[error(transparent)] Device(#[from] DeviceError), #[error("failed to map buffer while creating: {0}")] AccessError(#[from] BufferAccessError), #[error("buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")] UnalignedSize, #[error("Buffers cannot have empty usage flags")] EmptyUsage, #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")] UsageMismatch(wgt::BufferUsage), } impl Resource for Buffer { const TYPE: &'static str = "Buffer"; fn life_guard(&self) -> &LifeGuard { &self.life_guard } } impl Borrow<()> for Buffer { fn borrow(&self) -> &() { &DUMMY_SELECTOR } } pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; #[derive(Debug)] pub struct Texture { pub(crate) raw: Option, pub(crate) device_id: Stored, pub(crate) desc: wgt::TextureDescriptor<()>, pub(crate) format_features: wgt::TextureFormatFeatures, pub(crate) full_range: TextureSelector, pub(crate) life_guard: LifeGuard, } #[derive(Clone, Copy, Debug)] pub enum TextureErrorDimension { X, Y, Z, } #[derive(Clone, Debug, Error)] pub enum TextureDimensionError { #[error("Dimension {0:?} is zero")] Zero(TextureErrorDimension), #[error("Dimension {0:?} value {given} exceeds the limit of {limit}")] LimitExceeded { dim: TextureErrorDimension, given: u32, limit: u32, }, #[error("sample count {0} is invalid")] InvalidSampleCount(u32), } #[derive(Clone, Debug, Error)] pub enum CreateTextureError { #[error(transparent)] Device(#[from] DeviceError), #[error("D24Plus textures cannot be copied")] CannotCopyD24Plus, #[error("Textures cannot have empty usage flags")] EmptyUsage, #[error(transparent)] InvalidDimension(#[from] TextureDimensionError), #[error("texture descriptor mip level count ({0}) is invalid")] InvalidMipLevelCount(u32), #[error("The texture usages {0:?} are not allowed on a texture of type {1:?}")] InvalidUsages(wgt::TextureUsage, wgt::TextureFormat), #[error("Texture format {0:?} can't be used")] MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures), } impl Resource for Texture { const TYPE: &'static str = "Texture"; fn life_guard(&self) -> &LifeGuard { &self.life_guard } } impl Borrow for Texture { fn borrow(&self) -> &TextureSelector { &self.full_range } } /// Describes a [`TextureView`]. #[derive(Clone, Debug, Default, PartialEq)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize), serde(default))] pub struct TextureViewDescriptor<'a> { /// Debug label of the texture view. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// Format of the texture view, or `None` for the same format as the texture itself. /// At this time, it must be the same the underlying format of the texture. pub format: Option, /// The dimension of the texture view. For 1D textures, this must be `1D`. For 2D textures it must be one of /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `3D` pub dimension: Option, /// Range within the texture that is accessible via this view. pub range: wgt::ImageSubresourceRange, } #[derive(Debug)] pub(crate) enum TextureViewSource { Native(Stored), SwapChain(Stored), } #[derive(Debug)] pub(crate) struct HalTextureViewDescriptor { pub format: wgt::TextureFormat, pub dimension: wgt::TextureViewDimension, pub range: wgt::ImageSubresourceRange, } impl HalTextureViewDescriptor { pub fn aspects(&self) -> hal::FormatAspect { hal::FormatAspect::from(self.format) & hal::FormatAspect::from(self.range.aspect) } } #[derive(Debug)] pub struct TextureView { pub(crate) raw: A::TextureView, pub(crate) source: TextureViewSource, //TODO: store device_id for quick access? pub(crate) desc: HalTextureViewDescriptor, pub(crate) format_features: wgt::TextureFormatFeatures, pub(crate) extent: wgt::Extent3d, pub(crate) samples: u32, /// Internal use of this texture view when used as `BindingType::Texture`. pub(crate) sampled_internal_use: hal::TextureUse, pub(crate) selector: TextureSelector, pub(crate) life_guard: LifeGuard, } #[derive(Clone, Debug, Error)] pub enum CreateTextureViewError { #[error("parent texture is invalid or destroyed")] InvalidTexture, #[error("not enough memory left")] OutOfMemory, #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")] InvalidTextureViewDimension { view: wgt::TextureViewDimension, texture: wgt::TextureDimension, }, #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")] InvalidCubemapTextureDepth { depth: u32 }, #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")] InvalidCubemapArrayTextureDepth { depth: u32 }, #[error( "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}" )] TooManyMipLevels { requested: u32, total: u32 }, #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")] TooManyArrayLayers { requested: u32, total: u32 }, #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")] InvalidArrayLayerCount { requested: u32, dim: wgt::TextureViewDimension, }, #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")] InvalidAspect { texture_format: wgt::TextureFormat, requested_aspect: wgt::TextureAspect, }, #[error("Unable to view texture {texture:?} as {view:?}")] FormatReinterpretation { texture: wgt::TextureFormat, view: wgt::TextureFormat, }, } #[derive(Clone, Debug, Error)] pub enum TextureViewDestroyError { #[error("cannot destroy swap chain image")] SwapChainImage, } impl Resource for TextureView { const TYPE: &'static str = "TextureView"; fn life_guard(&self) -> &LifeGuard { &self.life_guard } } impl Borrow<()> for TextureView { fn borrow(&self) -> &() { &DUMMY_SELECTOR } } /// Describes a [`Sampler`] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct SamplerDescriptor<'a> { /// Debug label of the sampler. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// How to deal with out of bounds accesses in the u (i.e. x) direction pub address_modes: [wgt::AddressMode; 3], /// How to filter the texture when it needs to be magnified (made larger) pub mag_filter: wgt::FilterMode, /// How to filter the texture when it needs to be minified (made smaller) pub min_filter: wgt::FilterMode, /// How to filter between mip map levels pub mipmap_filter: wgt::FilterMode, /// Minimum level of detail (i.e. mip level) to use pub lod_min_clamp: f32, /// Maximum level of detail (i.e. mip level) to use pub lod_max_clamp: f32, /// If this is enabled, this is a comparison sampler using the given comparison function. pub compare: Option, /// Valid values: 1, 2, 4, 8, and 16. pub anisotropy_clamp: Option, /// Border color to use when address_mode is [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder) pub border_color: Option, } impl Default for SamplerDescriptor<'_> { fn default() -> Self { Self { label: None, address_modes: Default::default(), mag_filter: Default::default(), min_filter: Default::default(), mipmap_filter: Default::default(), lod_min_clamp: 0.0, lod_max_clamp: std::f32::MAX, compare: None, anisotropy_clamp: None, border_color: None, } } } #[derive(Debug)] pub struct Sampler { pub(crate) raw: A::Sampler, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, /// `true` if this is a comparison sampler pub(crate) comparison: bool, /// `true` if this is a filtering sampler pub(crate) filtering: bool, } #[derive(Clone, Debug, Error)] pub enum CreateSamplerError { #[error(transparent)] Device(#[from] DeviceError), #[error("invalid anisotropic clamp {0}, must be one of 1, 2, 4, 8 or 16")] InvalidClamp(u8), #[error("cannot create any more samplers")] TooManyObjects, /// AddressMode::ClampToBorder requires feature ADDRESS_MODE_CLAMP_TO_BORDER. #[error(transparent)] MissingFeatures(#[from] MissingFeatures), } impl Resource for Sampler { const TYPE: &'static str = "Sampler"; fn life_guard(&self) -> &LifeGuard { &self.life_guard } } impl Borrow<()> for Sampler { fn borrow(&self) -> &() { &DUMMY_SELECTOR } } #[derive(Clone, Debug, Error)] pub enum CreateQuerySetError { #[error(transparent)] Device(#[from] DeviceError), #[error("QuerySets cannot be made with zero queries")] ZeroCount, #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")] TooManyQueries { count: u32, maximum: u32 }, #[error(transparent)] MissingFeatures(#[from] MissingFeatures), } #[derive(Debug)] pub struct QuerySet { pub(crate) raw: A::QuerySet, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, pub(crate) desc: wgt::QuerySetDescriptor, } impl Resource for QuerySet { const TYPE: &'static str = "QuerySet"; fn life_guard(&self) -> &LifeGuard { &self.life_guard } } impl Borrow<()> for QuerySet { fn borrow(&self) -> &() { &DUMMY_SELECTOR } } #[derive(Clone, Debug, Error)] pub enum DestroyError { #[error("resource is invalid")] Invalid, #[error("resource is already destroyed")] AlreadyDestroyed, }