summaryrefslogtreecommitdiffstats
path: root/gfx/wgpu/wgpu-core/src/resource.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wgpu/wgpu-core/src/resource.rs')
-rw-r--r--gfx/wgpu/wgpu-core/src/resource.rs447
1 files changed, 447 insertions, 0 deletions
diff --git a/gfx/wgpu/wgpu-core/src/resource.rs b/gfx/wgpu/wgpu-core/src/resource.rs
new file mode 100644
index 0000000000..16319dd27e
--- /dev/null
+++ b/gfx/wgpu/wgpu-core/src/resource.rs
@@ -0,0 +1,447 @@
+/* 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::{alloc::MemoryBlock, DeviceError, HostMap},
+ hub::Resource,
+ id::{DeviceId, SwapChainId, TextureId},
+ track::{TextureSelector, DUMMY_SELECTOR},
+ validation::MissingBufferUsageError,
+ Label, LifeGuard, RefCount, Stored,
+};
+
+use thiserror::Error;
+
+use std::{
+ borrow::Borrow,
+ num::{NonZeroU32, NonZeroU8},
+ ops::Range,
+ ptr::NonNull,
+};
+
+bitflags::bitflags! {
+ /// The internal enum mirrored from `BufferUsage`. The values don't have to match!
+ pub struct BufferUse: u32 {
+ const EMPTY = 0;
+ const MAP_READ = 1;
+ const MAP_WRITE = 2;
+ const COPY_SRC = 4;
+ const COPY_DST = 8;
+ const INDEX = 16;
+ const VERTEX = 32;
+ const UNIFORM = 64;
+ const STORAGE_LOAD = 128;
+ const STORAGE_STORE = 256;
+ const INDIRECT = 512;
+ /// The combination of all read-only usages.
+ const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits |
+ Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits |
+ Self::STORAGE_LOAD.bits | Self::INDIRECT.bits;
+ /// The combination of all write-only and read-write usages.
+ const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits;
+ /// The combination of all usages that the are guaranteed to be be ordered by the hardware.
+ /// If a usage is not ordered, then even if it doesn't change between draw calls, there
+ /// still need to be pipeline barriers inserted for synchronization.
+ const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits;
+ }
+}
+
+bitflags::bitflags! {
+ /// The internal enum mirrored from `TextureUsage`. The values don't have to match!
+ pub struct TextureUse: u32 {
+ const EMPTY = 0;
+ const COPY_SRC = 1;
+ const COPY_DST = 2;
+ const SAMPLED = 4;
+ const ATTACHMENT_READ = 8;
+ const ATTACHMENT_WRITE = 16;
+ const STORAGE_LOAD = 32;
+ const STORAGE_STORE = 48;
+ /// The combination of all read-only usages.
+ const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::ATTACHMENT_READ.bits | Self::STORAGE_LOAD.bits;
+ /// The combination of all write-only and read-write usages.
+ const WRITE_ALL = Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits | Self::STORAGE_STORE.bits;
+ /// The combination of all usages that the are guaranteed to be be ordered by the hardware.
+ /// If a usage is not ordered, then even if it doesn't change between draw calls, there
+ /// still need to be pipeline barriers inserted for synchronization.
+ const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits;
+ const UNINITIALIZED = 0xFFFF;
+ }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub enum BufferMapAsyncStatus {
+ Success,
+ Error,
+ Unknown,
+ ContextLost,
+}
+
+#[derive(Debug)]
+pub(crate) enum BufferMapState<B: hal::Backend> {
+ /// Mapped at creation.
+ Init {
+ ptr: NonNull<u8>,
+ stage_buffer: B::Buffer,
+ stage_memory: MemoryBlock<B>,
+ needs_flush: bool,
+ },
+ /// Waiting for GPU to be done before mapping
+ Waiting(BufferPendingMapping),
+ /// Mapped
+ Active {
+ ptr: NonNull<u8>,
+ sub_range: hal::buffer::SubRange,
+ host: HostMap,
+ },
+ /// Not mapped
+ Idle,
+}
+
+unsafe impl<B: hal::Backend> Send for BufferMapState<B> {}
+unsafe impl<B: hal::Backend> Sync for BufferMapState<B> {}
+
+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) {
+ tracing::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 does not respect `COPY_BUFFER_ALIGNMENT`")]
+ UnalignedRange,
+}
+
+#[derive(Debug)]
+pub(crate) struct BufferPendingMapping {
+ pub range: Range<wgt::BufferAddress>,
+ pub op: BufferMapOperation,
+ // hold the parent alive while the mapping is active
+ pub parent_ref_count: RefCount,
+}
+
+pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
+
+#[derive(Debug)]
+pub struct Buffer<B: hal::Backend> {
+ pub(crate) raw: Option<(B::Buffer, MemoryBlock<B>)>,
+ pub(crate) device_id: Stored<DeviceId>,
+ pub(crate) usage: wgt::BufferUsage,
+ pub(crate) size: wgt::BufferAddress,
+ pub(crate) full_range: (),
+ pub(crate) sync_mapped_writes: Option<hal::memory::Segment>,
+ pub(crate) life_guard: LifeGuard,
+ pub(crate) map_state: BufferMapState<B>,
+}
+
+#[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("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
+ UsageMismatch(wgt::BufferUsage),
+}
+
+impl<B: hal::Backend> Resource for Buffer<B> {
+ const TYPE: &'static str = "Buffer";
+
+ fn life_guard(&self) -> &LifeGuard {
+ &self.life_guard
+ }
+}
+
+impl<B: hal::Backend> Borrow<()> for Buffer<B> {
+ fn borrow(&self) -> &() {
+ &DUMMY_SELECTOR
+ }
+}
+
+pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
+
+#[derive(Debug)]
+pub struct Texture<B: hal::Backend> {
+ pub(crate) raw: Option<(B::Image, MemoryBlock<B>)>,
+ pub(crate) device_id: Stored<DeviceId>,
+ pub(crate) usage: wgt::TextureUsage,
+ pub(crate) aspects: hal::format::Aspects,
+ pub(crate) dimension: wgt::TextureDimension,
+ pub(crate) kind: hal::image::Kind,
+ pub(crate) format: wgt::TextureFormat,
+ pub(crate) full_range: TextureSelector,
+ pub(crate) life_guard: LifeGuard,
+}
+
+#[derive(Clone, Debug)]
+pub enum TextureErrorDimension {
+ X,
+ Y,
+ Z,
+}
+
+#[derive(Clone, Debug, Error)]
+pub enum TextureDimensionError {
+ #[error("Dimension {0:?} is zero")]
+ Zero(TextureErrorDimension),
+ #[error("1D textures must have height set to 1")]
+ InvalidHeight,
+ #[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(transparent)]
+ InvalidDimension(#[from] TextureDimensionError),
+ #[error("texture descriptor mip level count ({0}) is invalid")]
+ InvalidMipLevelCount(u32),
+ #[error("Feature {0:?} must be enabled to create a texture of type {1:?}")]
+ MissingFeature(wgt::Features, wgt::TextureFormat),
+}
+
+impl<B: hal::Backend> Resource for Texture<B> {
+ const TYPE: &'static str = "Texture";
+
+ fn life_guard(&self) -> &LifeGuard {
+ &self.life_guard
+ }
+}
+
+impl<B: hal::Backend> Borrow<TextureSelector> for Texture<B> {
+ 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<wgt::TextureFormat>,
+ /// 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<wgt::TextureViewDimension>,
+ /// Aspect of the texture. Color textures must be [`TextureAspect::All`].
+ pub aspect: wgt::TextureAspect,
+ /// Base mip level.
+ pub base_mip_level: u32,
+ /// Mip level count.
+ /// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
+ /// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
+ pub level_count: Option<NonZeroU32>,
+ /// Base array layer.
+ pub base_array_layer: u32,
+ /// Layer count.
+ /// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
+ /// If `None`, considered to include the rest of the array layers, but at least 1 in total.
+ pub array_layer_count: Option<NonZeroU32>,
+}
+
+#[derive(Debug)]
+pub(crate) enum TextureViewInner<B: hal::Backend> {
+ Native {
+ raw: B::ImageView,
+ source_id: Stored<TextureId>,
+ },
+ SwapChain {
+ image: <B::Surface as hal::window::PresentationSurface<B>>::SwapchainImage,
+ source_id: Stored<SwapChainId>,
+ },
+}
+
+#[derive(Debug)]
+pub struct TextureView<B: hal::Backend> {
+ pub(crate) inner: TextureViewInner<B>,
+ //TODO: store device_id for quick access?
+ pub(crate) aspects: hal::format::Aspects,
+ pub(crate) format: wgt::TextureFormat,
+ pub(crate) extent: hal::image::Extent,
+ pub(crate) samples: hal::image::NumSamples,
+ 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 `{image:?}`")]
+ InvalidTextureViewDimension {
+ view: wgt::TextureViewDimension,
+ image: wgt::TextureDimension,
+ },
+ #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")]
+ InvalidCubemapTextureDepth { depth: u16 },
+ #[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: u16 },
+ #[error(
+ "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}"
+ )]
+ TooManyMipLevels { requested: u32, total: u8 },
+ #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")]
+ TooManyArrayLayers { requested: u32, total: u16 },
+ #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
+ InvalidArrayLayerCount {
+ requested: u32,
+ dim: wgt::TextureViewDimension,
+ },
+ #[error("Aspect {requested:?} is not in the source texture ({total:?})")]
+ InvalidAspect {
+ requested: hal::format::Aspects,
+ total: hal::format::Aspects,
+ },
+}
+
+#[derive(Clone, Debug, Error)]
+pub enum TextureViewDestroyError {
+ #[error("cannot destroy swap chain image")]
+ SwapChainImage,
+}
+
+impl<B: hal::Backend> Resource for TextureView<B> {
+ const TYPE: &'static str = "TextureView";
+
+ fn life_guard(&self) -> &LifeGuard {
+ &self.life_guard
+ }
+}
+
+impl<B: hal::Backend> Borrow<()> for TextureView<B> {
+ 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<wgt::CompareFunction>,
+ /// Valid values: 1, 2, 4, 8, and 16.
+ pub anisotropy_clamp: Option<NonZeroU8>,
+ /// Border color to use when address_mode is [`AddressMode::ClampToBorder`]
+ pub border_color: Option<wgt::SamplerBorderColor>,
+}
+
+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<B: hal::Backend> {
+ pub(crate) raw: B::Sampler,
+ pub(crate) device_id: Stored<DeviceId>,
+ pub(crate) life_guard: LifeGuard,
+ /// `true` if this is a comparison sampler
+ pub(crate) comparison: 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("Feature {0:?} must be enabled")]
+ MissingFeature(wgt::Features),
+}
+
+impl<B: hal::Backend> Resource for Sampler<B> {
+ const TYPE: &'static str = "Sampler";
+
+ fn life_guard(&self) -> &LifeGuard {
+ &self.life_guard
+ }
+}
+
+impl<B: hal::Backend> Borrow<()> for Sampler<B> {
+ fn borrow(&self) -> &() {
+ &DUMMY_SELECTOR
+ }
+}
+
+#[derive(Clone, Debug, Error)]
+pub enum DestroyError {
+ #[error("resource is invalid")]
+ Invalid,
+ #[error("resource is already destroyed")]
+ AlreadyDestroyed,
+}