1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
/* 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("Buffers cannot have empty usage flags")]
    EmptyUsage,
    #[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) format_features: wgt::TextureFormatFeatures,
    pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
    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("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("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`](wgt::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) format_features: wgt::TextureFormatFeatures,
    pub(crate) extent: wgt::Extent3d,
    pub(crate) samples: hal::image::NumSamples,
    pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
    /// Internal use of this texture view when used as `BindingType::Texture`.
    pub(crate) sampled_internal_use: 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 `{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`](wgt::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 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("Feature {0:?} must be enabled")]
    MissingFeature(wgt::Features),
}

#[derive(Debug)]
pub struct QuerySet<B: hal::Backend> {
    pub(crate) raw: B::QueryPool,
    pub(crate) device_id: Stored<DeviceId>,
    pub(crate) life_guard: LifeGuard,
    /// Amount of queries in the query set.
    pub(crate) desc: wgt::QuerySetDescriptor,
    /// Amount of numbers in each query (i.e. a pipeline statistics query for two attributes will have this number be two)
    pub(crate) elements: u32,
}

impl<B: hal::Backend> Resource for QuerySet<B> {
    const TYPE: &'static str = "QuerySet";

    fn life_guard(&self) -> &LifeGuard {
        &self.life_guard
    }
}

impl<B: hal::Backend> Borrow<()> for QuerySet<B> {
    fn borrow(&self) -> &() {
        &DUMMY_SELECTOR
    }
}

#[derive(Clone, Debug, Error)]
pub enum DestroyError {
    #[error("resource is invalid")]
    Invalid,
    #[error("resource is already destroyed")]
    AlreadyDestroyed,
}