Repository: gfx-rs/wgpu
Pull request #5509 (2024-04-09 00:25:31)

[core] Replace id `transmute` method with explicit functions.
Replace the `wgpu_core::id::Id::transmute` method, the `transmute` private module, and the `Transmute` sealed trait with some associated functions with obvious names.


jimblandy: (No change in behavior, just less rigamarole.)

Patch:
diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs
index 7e3cbad11..5c438dd20 100644
--- a/player/src/bin/play.rs
+++ b/player/src/bin/play.rs
@@ -87,7 +87,7 @@ fn main() {
                 &desc,
                 None,
                 Some(id),
-                Some(id.transmute())
+                Some(id.into_queue_id())
             ));
             if let Some(e) = error {
                 panic!("{:?}", e);
diff --git a/player/src/lib.rs b/player/src/lib.rs
index eb89e43f7..ca3a4b6a5 100644
--- a/player/src/lib.rs
+++ b/player/src/lib.rs
@@ -336,7 +336,7 @@ impl GlobalPlay for wgc::global::Global {
                 let bin = std::fs::read(dir.join(data)).unwrap();
                 let size = (range.end - range.start) as usize;
                 if queued {
-                    self.queue_write_buffer::<A>(device.transmute(), id, range.start, &bin)
+                    self.queue_write_buffer::<A>(device.into_queue_id(), id, range.start, &bin)
                         .unwrap();
                 } else {
                     self.device_wait_for_buffer::<A>(device, id).unwrap();
@@ -351,23 +351,27 @@ impl GlobalPlay for wgc::global::Global {
                 size,
             } => {
                 let bin = std::fs::read(dir.join(data)).unwrap();
-                self.queue_write_texture::<A>(device.transmute(), &to, &bin, &layout, &size)
+                self.queue_write_texture::<A>(device.into_queue_id(), &to, &bin, &layout, &size)
                     .unwrap();
             }
             Action::Submit(_index, ref commands) if commands.is_empty() => {
-                self.queue_submit::<A>(device.transmute(), &[]).unwrap();
+                self.queue_submit::<A>(device.into_queue_id(), &[]).unwrap();
             }
             Action::Submit(_index, commands) => {
                 let (encoder, error) = self.device_create_command_encoder::<A>(
                     device,
                     &wgt::CommandEncoderDescriptor { label: None },
-                    Some(comb_manager.process(device.backend()).transmute()),
+                    Some(
+                        comb_manager
+                            .process(device.backend())
+                            .into_command_encoder_id(),
+                    ),
                 );
                 if let Some(e) = error {
                     panic!("{e}");
                 }
                 let cmdbuf = self.encode_commands::<A>(encoder, commands);
-                self.queue_submit::<A>(device.transmute(), &[cmdbuf])
+                self.queue_submit::<A>(device.into_queue_id(), &[cmdbuf])
                     .unwrap();
             }
         }
diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs
index ab413db73..e2e8f7411 100644
--- a/wgpu-core/src/command/mod.rs
+++ b/wgpu-core/src/command/mod.rs
@@ -253,7 +253,7 @@ impl<A: HalApi> CommandBuffer<A> {
         id: id::CommandEncoderId,
     ) -> Result<Arc<Self>, CommandEncoderError> {
         let storage = hub.command_buffers.read();
-        match storage.get(id.transmute()) {
+        match storage.get(id.into_command_buffer_id()) {
             Ok(cmd_buf) => match cmd_buf.data.lock().as_ref().unwrap().status {
                 CommandEncoderStatus::Recording => Ok(cmd_buf.clone()),
                 CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording),
@@ -418,7 +418,7 @@ impl Global {
 
         let hub = A::hub(self);
 
-        let error = match hub.command_buffers.get(encoder_id.transmute()) {
+        let error = match hub.command_buffers.get(encoder_id.into_command_buffer_id()) {
             Ok(cmd_buf) => {
                 let mut cmd_buf_data = cmd_buf.data.lock();
                 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
@@ -444,7 +444,7 @@ impl Global {
             Err(_) => Some(CommandEncoderError::Invalid),
         };
 
-        (encoder_id.transmute(), error)
+        (encoder_id.into_command_buffer_id(), error)
     }
 
     pub fn command_encoder_push_debug_group<A: HalApi>(
@@ -700,7 +700,7 @@ impl PrettyError for PassErrorScope {
         // This error is not in the error chain, only notes are needed
         match *self {
             Self::Pass(id) => {
-                fmt.command_buffer_label(&id.transmute());
+                fmt.command_buffer_label(&id.into_command_buffer_id());
             }
             Self::SetBindGroup(id) => {
                 fmt.bind_group_label(&id);
diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs
index 7e859e3cc..4e887fea2 100644
--- a/wgpu-core/src/command/render.rs
+++ b/wgpu-core/src/command/render.rs
@@ -2409,7 +2409,10 @@ impl Global {
             (trackers, pending_discard_init_fixups)
         };
 
-        let cmd_buf = hub.command_buffers.get(encoder_id.transmute()).unwrap();
+        let cmd_buf = hub
+            .command_buffers
+            .get(encoder_id.into_command_buffer_id())
+            .unwrap();
         let mut cmd_buf_data = cmd_buf.data.lock();
         let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
 
diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs
index 7f682f555..9c54dfc19 100644
--- a/wgpu-core/src/device/global.rs
+++ b/wgpu-core/src/device/global.rs
@@ -1334,7 +1334,9 @@ impl Global {
         profiling::scope!("Device::create_command_encoder");
 
         let hub = A::hub(self);
-        let fid = hub.command_buffers.prepare(id_in.map(|id| id.transmute()));
+        let fid = hub
+            .command_buffers
+            .prepare(id_in.map(|id| id.into_command_buffer_id()));
 
         let error = loop {
             let device = match hub.devices.get(device_id) {
@@ -1369,11 +1371,11 @@ impl Global {
 
             let (id, _) = fid.assign(Arc::new(command_buffer));
             api_log!("Device::create_command_encoder -> {id:?}");
-            return (id.transmute(), None);
+            return (id.into_command_encoder_id(), None);
         };
 
         let id = fid.assign_error(desc.label.borrow_or_default());
-        (id.transmute(), Some(error))
+        (id.into_command_encoder_id(), Some(error))
     }
 
     pub fn command_buffer_label<A: HalApi>(&self, id: id::CommandBufferId) -> String {
@@ -1388,7 +1390,7 @@ impl Global {
 
         if let Some(cmd_buf) = hub
             .command_buffers
-            .unregister(command_encoder_id.transmute())
+            .unregister(command_encoder_id.into_command_buffer_id())
         {
             cmd_buf.data.lock().as_mut().unwrap().encoder.discard();
             cmd_buf
@@ -1400,7 +1402,7 @@ impl Global {
     pub fn command_buffer_drop<A: HalApi>(&self, command_buffer_id: id::CommandBufferId) {
         profiling::scope!("CommandBuffer::drop");
         api_log!("CommandBuffer::drop {command_buffer_id:?}");
-        self.command_encoder_drop::<A>(command_buffer_id.transmute())
+        self.command_encoder_drop::<A>(command_buffer_id.into_command_encoder_id())
     }
 
     pub fn device_create_render_bundle_encoder(
@@ -2121,7 +2123,7 @@ impl Global {
             .map_err(|_| DeviceError::Invalid)?;
 
         if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
-            if submission_index.queue_id != device_id.transmute() {
+            if submission_index.queue_id != device_id.into_queue_id() {
                 return Err(WaitIdleError::WrongSubmissionIndex(
                     submission_index.queue_id,
                     device_id,
diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs
index 26d6c8b51..0e91408b6 100644
--- a/wgpu-core/src/device/queue.rs
+++ b/wgpu-core/src/device/queue.rs
@@ -707,7 +707,7 @@ impl Global {
             .get(destination.texture)
             .map_err(|_| TransferError::InvalidTexture(destination.texture))?;
 
-        if dst.device.as_info().id() != queue_id.transmute() {
+        if dst.device.as_info().id().into_queue_id() != queue_id {
             return Err(DeviceError::WrongDevice.into());
         }
 
@@ -1191,7 +1191,7 @@ impl Global {
                             Err(_) => continue,
                         };
 
-                        if cmdbuf.device.as_info().id() != queue_id.transmute() {
+                        if cmdbuf.device.as_info().id().into_queue_id() != queue_id {
                             return Err(DeviceError::WrongDevice.into());
                         }
 
diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs
index 72b74218d..c901a97db 100644
--- a/wgpu-core/src/id.rs
+++ b/wgpu-core/src/id.rs
@@ -182,15 +182,6 @@ where
         self.0.backend()
     }
 
-    /// Transmute this identifier to one with a different marker trait.
-    ///
-    /// Legal use is governed through a sealed trait, however it's correctness
-    /// depends on the current implementation of `wgpu-core`.
-    #[inline]
-    pub const fn transmute<U: self::transmute::Transmute<T>>(self) -> Id<U> {
-        Id(self.0, PhantomData)
-    }
-
     #[inline]
     pub fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self {
         Id(RawId::zip(index, epoch, backend), PhantomData)
@@ -202,20 +193,6 @@ where
     }
 }
 
-pub(crate) mod transmute {
-    // This trait is effectively sealed to prevent illegal transmutes.
-    pub trait Transmute<U>: super::Marker {}
-
-    // Self-transmute is always legal.
-    impl<T> Transmute<T> for T where T: super::Marker {}
-
-    // TODO: Remove these once queues have their own identifiers.
-    impl Transmute<super::markers::Queue> for super::markers::Device {}
-    impl Transmute<super::markers::Device> for super::markers::Queue {}
-    impl Transmute<super::markers::CommandBuffer> for super::markers::CommandEncoder {}
-    impl Transmute<super::markers::CommandEncoder> for super::markers::CommandBuffer {}
-}
-
 impl<T> Copy for Id<T> where T: Marker {}
 
 impl<T> Clone for Id<T>
@@ -349,6 +326,24 @@ ids! {
     pub type QuerySetId QuerySet;
 }
 
+impl CommandEncoderId {
+    pub fn into_command_buffer_id(self) -> CommandBufferId {
+        Id(self.0, PhantomData)
+    }
+}
+
+impl CommandBufferId {
+    pub fn into_command_encoder_id(self) -> CommandEncoderId {
+        Id(self.0, PhantomData)
+    }
+}
+
+impl DeviceId {
+    pub fn into_queue_id(self) -> QueueId {
+        Id(self.0, PhantomData)
+    }
+}
+
 #[test]
 fn test_id_backend() {
     for &b in &[
diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs
index 8256b9553..62335304c 100644
--- a/wgpu-core/src/resource.rs
+++ b/wgpu-core/src/resource.rs
@@ -1043,7 +1043,10 @@ impl Global {
         profiling::scope!("CommandEncoder::as_hal");
 
         let hub = A::hub(self);
-        let cmd_buf = hub.command_buffers.get(id.transmute()).unwrap();
+        let cmd_buf = hub
+            .command_buffers
+            .get(id.into_command_buffer_id())
+            .unwrap();
         let mut cmd_buf_data = cmd_buf.data.lock();
         let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
         let cmd_buf_raw = cmd_buf_data.encoder.open().ok();
diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs
index c73b0fbd1..edc24cee3 100644
--- a/wgpu/src/backend/wgpu_core.rs
+++ b/wgpu/src/backend/wgpu_core.rs
@@ -631,7 +631,7 @@ impl crate::Context for ContextWgpuCore {
             id: queue_id,
             error_sink,
         };
-        ready(Ok((device_id, device, device_id.transmute(), queue)))
+        ready(Ok((device_id, device, device_id.into_queue_id(), queue)))
     }
 
     fn instance_poll_all_devices(&self, force_wait: bool) -> bool {
@@ -1839,8 +1839,7 @@ impl crate::Context for ContextWgpuCore {
         if let Err(cause) = wgc::gfx_select!(
             encoder => self.0.command_encoder_run_compute_pass(*encoder, pass_data)
         ) {
-            let name =
-                wgc::gfx_select!(encoder => self.0.command_buffer_label(encoder.transmute()));
+            let name = wgc::gfx_select!(encoder => self.0.command_buffer_label(encoder.into_command_buffer_id()));
             self.handle_error(
                 &encoder_data.error_sink,
                 cause,
@@ -1923,8 +1922,7 @@ impl crate::Context for ContextWgpuCore {
         if let Err(cause) =
             wgc::gfx_select!(encoder => self.0.command_encoder_run_render_pass(*encoder, pass_data))
         {
-            let name =
-                wgc::gfx_select!(encoder => self.0.command_buffer_label(encoder.transmute()));
+            let name = wgc::gfx_select!(encoder => self.0.command_buffer_label(encoder.into_command_buffer_id()));
             self.handle_error(
                 &encoder_data.error_sink,
                 cause,


Test patch:
diff --git a/player/tests/test.rs b/player/tests/test.rs
index c04622b42..a6c7222b6 100644
--- a/player/tests/test.rs
+++ b/player/tests/test.rs
@@ -115,7 +115,7 @@ impl Test<'_> {
             },
             None,
             Some(device_id),
-            Some(device_id.transmute())
+            Some(device_id.into_queue_id())
         ));
         if let Some(e) = error {
             panic!("{:?}", e);