wgpu_core/command/
transition_resources.rs

1use alloc::{sync::Arc, vec::Vec};
2
3use thiserror::Error;
4use wgt::error::{ErrorType, WebGpuError};
5
6use crate::{
7    command::{encoder::EncodingState, ArcCommand, CommandEncoder, EncoderStateError},
8    device::DeviceError,
9    global::Global,
10    id::{BufferId, CommandEncoderId, TextureId},
11    resource::{Buffer, InvalidResourceError, ParentDevice, Texture},
12    track::ResourceUsageCompatibilityError,
13};
14
15impl CommandEncoder {
16    pub fn transition_resources(
17        self: &Arc<Self>,
18        buffer_transitions: impl Iterator<Item = wgt::BufferTransition<Arc<Buffer>>>,
19        texture_transitions: impl Iterator<Item = wgt::TextureTransition<Arc<Texture>>>,
20    ) -> Result<(), EncoderStateError> {
21        profiling::scope!("CommandEncoder::transition_resources");
22
23        // Lock command encoder for recording
24        let mut cmd_buf_data = self.data.lock();
25        cmd_buf_data.push_with(|| -> Result<_, TransitionResourcesError> {
26            Ok(ArcCommand::TransitionResources {
27                buffer_transitions: buffer_transitions
28                    .map(|t| {
29                        t.buffer.check_is_valid()?;
30                        Ok(wgt::BufferTransition {
31                            buffer: t.buffer,
32                            state: t.state,
33                        })
34                    })
35                    .collect::<Result<_, TransitionResourcesError>>()?,
36                texture_transitions: texture_transitions
37                    .map(|t| {
38                        t.texture.check_valid()?;
39                        Ok(wgt::TextureTransition {
40                            texture: t.texture,
41                            selector: t.selector,
42                            state: t.state,
43                        })
44                    })
45                    .collect::<Result<_, TransitionResourcesError>>()?,
46            })
47        })
48    }
49}
50
51impl Global {
52    pub fn command_encoder_transition_resources(
53        &self,
54        command_encoder_id: CommandEncoderId,
55        buffer_transitions: impl Iterator<Item = wgt::BufferTransition<BufferId>>,
56        texture_transitions: impl Iterator<Item = wgt::TextureTransition<TextureId>>,
57    ) -> Result<(), EncoderStateError> {
58        let hub = &self.hub;
59
60        let cmd_enc = hub.command_encoders.get(command_encoder_id);
61        let buffer_transitions = buffer_transitions
62            .map(|t| {
63                let buffer = hub.buffers.get(t.buffer);
64                wgt::BufferTransition {
65                    buffer,
66                    state: t.state,
67                }
68            })
69            .collect::<Vec<_>>();
70        let texture_transitions = texture_transitions
71            .map(|t| {
72                let texture = hub.textures.get(t.texture);
73                wgt::TextureTransition {
74                    texture,
75                    selector: t.selector,
76                    state: t.state,
77                }
78            })
79            .collect::<Vec<_>>();
80        cmd_enc.transition_resources(
81            buffer_transitions.into_iter(),
82            texture_transitions.into_iter(),
83        )
84    }
85}
86
87pub(crate) fn transition_resources(
88    state: &mut EncodingState,
89    buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
90    texture_transitions: Vec<wgt::TextureTransition<Arc<Texture>>>,
91) -> Result<(), TransitionResourcesError> {
92    let mut usage_scope = state.device.new_usage_scope();
93    let indices = &state.device.tracker_indices;
94    usage_scope.buffers.set_size(indices.buffers.size());
95    usage_scope.textures.set_size(indices.textures.size());
96
97    // Process buffer transitions
98    for buffer_transition in buffer_transitions {
99        buffer_transition.buffer.same_device(state.device)?;
100
101        usage_scope
102            .buffers
103            .merge_single(&buffer_transition.buffer, buffer_transition.state)?;
104    }
105
106    // Process texture transitions
107    for texture_transition in texture_transitions {
108        texture_transition.texture.same_device(state.device)?;
109
110        unsafe {
111            usage_scope.textures.merge_single(
112                &texture_transition.texture,
113                texture_transition.selector,
114                texture_transition.state,
115            )
116        }?;
117    }
118
119    // Record any needed barriers based on tracker data
120    CommandEncoder::insert_barriers_from_scope(
121        state.raw_encoder,
122        state.tracker,
123        &usage_scope,
124        state.snatch_guard,
125    );
126    Ok(())
127}
128
129/// Error encountered while attempting to perform [`CommandEncoder::transition_resources`].
130#[derive(Clone, Debug, Error)]
131#[non_exhaustive]
132pub enum TransitionResourcesError {
133    #[error(transparent)]
134    Device(#[from] DeviceError),
135    #[error(transparent)]
136    EncoderState(#[from] EncoderStateError),
137    #[error(transparent)]
138    InvalidResource(#[from] InvalidResourceError),
139    #[error(transparent)]
140    ResourceUsage(#[from] ResourceUsageCompatibilityError),
141}
142
143impl WebGpuError for TransitionResourcesError {
144    fn webgpu_error_type(&self) -> ErrorType {
145        match self {
146            Self::Device(e) => e.webgpu_error_type(),
147            Self::EncoderState(e) => e.webgpu_error_type(),
148            Self::InvalidResource(e) => e.webgpu_error_type(),
149            Self::ResourceUsage(e) => e.webgpu_error_type(),
150        }
151    }
152}