cart_tmp_wgc/
binding_model.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::{
6    id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId},
7    track::{TrackerSet, DUMMY_SELECTOR},
8    FastHashMap, LifeGuard, MultiRefCount, RefCount, Stored, MAX_BIND_GROUPS,
9};
10
11use arrayvec::ArrayVec;
12use cart_tmp_gdesc::{DescriptorCounts, DescriptorSet};
13
14#[cfg(feature = "replay")]
15use serde::Deserialize;
16#[cfg(feature = "trace")]
17use serde::Serialize;
18use std::borrow::Borrow;
19
20#[derive(Clone, Debug)]
21pub enum BindGroupLayoutError {
22    ConflictBinding(u32),
23    MissingFeature(wgt::Features),
24    /// Arrays of bindings can't be 0 elements long
25    ZeroCount,
26    /// Arrays of bindings unsupported for this type of binding
27    ArrayUnsupported,
28}
29
30#[derive(Clone, Debug)]
31pub enum BindGroupError {
32    /// Number of bindings in bind group descriptor does not match
33    /// the number of bindings defined in the bind group layout.
34    BindingsNumMismatch { actual: usize, expected: usize },
35    /// Unable to find a corresponding declaration for the given binding,
36    MissingBindingDeclaration(u32),
37    /// The given binding has a different type than the one in the layout.
38    WrongBindingType {
39        // Index of the binding
40        binding: u32,
41        // The type given to the function
42        actual: wgt::BindingType,
43        // Human-readable description of expected types
44        expected: &'static str,
45    },
46    /// The given sampler is/is not a comparison sampler,
47    /// while the layout type indicates otherwise.
48    WrongSamplerComparison,
49}
50
51pub(crate) type BindEntryMap = FastHashMap<u32, wgt::BindGroupLayoutEntry>;
52
53#[derive(Debug)]
54pub struct BindGroupLayout<B: hal::Backend> {
55    pub(crate) raw: B::DescriptorSetLayout,
56    pub(crate) device_id: Stored<DeviceId>,
57    pub(crate) multi_ref_count: MultiRefCount,
58    pub(crate) entries: BindEntryMap,
59    pub(crate) desc_counts: DescriptorCounts,
60    pub(crate) dynamic_count: usize,
61}
62
63#[repr(C)]
64#[derive(Debug)]
65pub struct PipelineLayoutDescriptor {
66    pub bind_group_layouts: *const BindGroupLayoutId,
67    pub bind_group_layouts_length: usize,
68}
69
70#[derive(Clone, Debug)]
71pub enum PipelineLayoutError {
72    TooManyGroups(usize),
73}
74
75#[derive(Debug)]
76pub struct PipelineLayout<B: hal::Backend> {
77    pub(crate) raw: B::PipelineLayout,
78    pub(crate) device_id: Stored<DeviceId>,
79    pub(crate) life_guard: LifeGuard,
80    pub(crate) bind_group_layout_ids: ArrayVec<[Stored<BindGroupLayoutId>; MAX_BIND_GROUPS]>,
81}
82
83#[repr(C)]
84#[derive(Clone, Debug, Hash, PartialEq)]
85#[cfg_attr(feature = "trace", derive(Serialize))]
86#[cfg_attr(feature = "replay", derive(Deserialize))]
87pub struct BufferBinding {
88    pub buffer_id: BufferId,
89    pub offset: wgt::BufferAddress,
90    pub size: Option<wgt::BufferSize>,
91}
92
93// Note: Duplicated in wgpu-rs as BindingResource
94#[derive(Debug)]
95pub enum BindingResource<'a> {
96    Buffer(BufferBinding),
97    Sampler(SamplerId),
98    TextureView(TextureViewId),
99    TextureViewArray(&'a [TextureViewId]),
100}
101
102// Note: Duplicated in wgpu-rs as Binding
103#[derive(Debug)]
104pub struct BindGroupEntry<'a> {
105    pub binding: u32,
106    pub resource: BindingResource<'a>,
107}
108
109// Note: Duplicated in wgpu-rs as BindGroupDescriptor
110#[derive(Debug)]
111pub struct BindGroupDescriptor<'a> {
112    pub label: Option<&'a str>,
113    pub layout: BindGroupLayoutId,
114    pub bindings: &'a [BindGroupEntry<'a>],
115}
116
117#[derive(Debug)]
118pub enum BindError {
119    /// Number of dynamic offsets doesn't match the number of dynamic bindings
120    /// in the bind group layout.
121    MismatchedDynamicOffsetCount { actual: usize, expected: usize },
122    /// Expected dynamic binding alignment was not met.
123    UnalignedDynamicBinding { idx: usize },
124    /// Dynamic offset would cause buffer overrun.
125    DynamicBindingOutOfBounds { idx: usize },
126}
127
128#[derive(Debug)]
129pub struct BindGroupDynamicBindingData {
130    /// The maximum value the dynamic offset can have before running off the end of the buffer.
131    pub(crate) maximum_dynamic_offset: wgt::BufferAddress,
132}
133
134#[derive(Debug)]
135pub struct BindGroup<B: hal::Backend> {
136    pub(crate) raw: DescriptorSet<B>,
137    pub(crate) device_id: Stored<DeviceId>,
138    pub(crate) layout_id: BindGroupLayoutId,
139    pub(crate) life_guard: LifeGuard,
140    pub(crate) used: TrackerSet,
141    pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
142}
143
144impl<B: hal::Backend> BindGroup<B> {
145    pub(crate) fn validate_dynamic_bindings(
146        &self,
147        offsets: &[wgt::DynamicOffset],
148    ) -> Result<(), BindError> {
149        if self.dynamic_binding_info.len() != offsets.len() {
150            log::error!(
151                "BindGroup has {} dynamic bindings, but {} dynamic offsets were provided",
152                self.dynamic_binding_info.len(),
153                offsets.len()
154            );
155            return Err(BindError::MismatchedDynamicOffsetCount {
156                expected: self.dynamic_binding_info.len(),
157                actual: offsets.len(),
158            });
159        }
160
161        for (idx, (info, &offset)) in self
162            .dynamic_binding_info
163            .iter()
164            .zip(offsets.iter())
165            .enumerate()
166        {
167            if offset as wgt::BufferAddress % wgt::BIND_BUFFER_ALIGNMENT != 0 {
168                log::error!(
169                    "Dynamic buffer offset index {}: {} needs to be aligned as a multiple of {}",
170                    idx,
171                    offset,
172                    wgt::BIND_BUFFER_ALIGNMENT
173                );
174                return Err(BindError::UnalignedDynamicBinding { idx });
175            }
176
177            if offset as wgt::BufferAddress > info.maximum_dynamic_offset {
178                log::error!(
179                    "Dynamic offset index {} with value {} overruns underlying buffer. Dynamic offset must be no more than {}",
180                    idx,
181                    offset,
182                    info.maximum_dynamic_offset,
183                );
184                return Err(BindError::DynamicBindingOutOfBounds { idx });
185            }
186        }
187
188        Ok(())
189    }
190}
191
192impl<B: hal::Backend> Borrow<RefCount> for BindGroup<B> {
193    fn borrow(&self) -> &RefCount {
194        self.life_guard.ref_count.as_ref().unwrap()
195    }
196}
197
198impl<B: hal::Backend> Borrow<()> for BindGroup<B> {
199    fn borrow(&self) -> &() {
200        &DUMMY_SELECTOR
201    }
202}