Repository: gfx-rs/wgpu (language: rust)

Issue:
Cannot build wgpu-core for PowerPC 32-bit (`AtomicU64` does not exist in 32-bit PPC)
<!-- Thank you for filing this! Please read the [debugging tips](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications).
That may let you investigate on your own, or provide additional information that helps us to assist.-->

**Description**
Currently `wgpu-core/src/device/resource.rs` uses `AtomicU64`.  This prevents successful compilation and use of Ruffle on it.
I wanted to make sure this is known and see if there was some simple workaround for it so I can use it on PowerPC 32-bit and MIPS.

**Repro steps**
I tried to build ruffle with `cargo build --target=powerpc-unknown-linux-gnu`

**Expected vs observed behavior**
````
error[E0432]: unresolved import `std::sync::atomic::AtomicU64`
  --> /path/to/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-core
56 |         atomic::{AtomicBool, AtomicU64, Ordering},
   |                              ^^^^^^^^^
   |                              |
   |                              no `AtomicU64` in `sync::atomic`
````

This is not the sole blocker for building ruffle, but it is the first I come across (the second is `ring`).

**Platform**
Build host is amd64 debian Linux, i5-3470 with 16GB RAM, not that I think it matters for this.
I am not using debian's rust packages; I am using the generic ones from https://sh.rustup.rs .


Discussion / hints:
I don't think anyone looked into getting wgpu to run on any target that doesn't have 64bit atomic support. But if someone were able to pull it off without drastic changes to the codebase + set up a ci job that ensures this doesn't rot, I don't see why we shouldn't have this support :)

Quick grep on `AtomicU64` only shows up the submission index and that could be encapsulated to fall back to 32bit if necessary. Big question though if that's the only thing that would blocking running on powerpc.
Note atomic submission index is going away soon-ish as it doesn't help us, it's just going to be a u64 behind a mutex
> Note atomic submission index is going away soon-ish as it doesn't help us, it's just going to be a u64 behind a mutex

This is #5978.
Note this also affects armel Linux, and any target without `cfg(target_has_atomic = "64")` https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic
Confirmed this is still a problem

```
error[E0412]: cannot find type `AtomicU64` in module `std::sync::atomic`
    --> wgpu-hal\src\lib.rs:304:48
     |
304  |   pub type AtomicFenceValue = std::sync::atomic::AtomicU64;
     |                                                  ^^^^^^^^^ help: a struct with a similar name exists: `AtomicU16`
     |
    ::: C:\Users\conno\.rustup\toolchains\1.83-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library/core/src/sync/atomic.rs:3101:1
     |
3101 | / atomic_int! {
3102 | |     cfg(target_has_atomic = "16"),
3103 | |     cfg(target_has_atomic_equal_alignment = "16"),
3104 | |     stable(feature = "integer_atomics_stable", since = "1.34.0"),
...    |
3116 | |     u16 AtomicU16
3117 | | }
     | |_- similarly named struct `AtomicU16` defined here
```

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a1495e081b..a932721b72 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -181,6 +181,14 @@ jobs:
             tier: 2
             kind: no_std
 
+          # 32-bit PowerPC Linux
+          # Included to test support for `portable-atomic`
+          - name: Linux ppc32
+            os: ubuntu-22.04
+            target: powerpc-unknown-linux-gnu
+            tier: 2
+            kind: wgpu-only
+
     name: Clippy ${{ matrix.name }}
     runs-on: ${{ matrix.os }}
 
diff --git a/Cargo.lock b/Cargo.lock
index 1198532882..e440ef3367 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1305,7 +1305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
 dependencies = [
  "libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2004,7 +2004,7 @@ checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
 dependencies = [
  "hermit-abi",
  "libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2134,7 +2134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
 dependencies = [
  "cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -3043,6 +3043,12 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
 
+[[package]]
+name = "portable-atomic"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
+
 [[package]]
 name = "pp-rs"
 version = "0.2.1"
@@ -3336,7 +3342,7 @@ dependencies = [
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -4005,7 +4011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f"
 dependencies = [
  "cc",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -4507,6 +4513,7 @@ dependencies = [
  "log",
  "naga",
  "parking_lot",
+ "portable-atomic",
  "profiling",
  "raw-window-handle 0.6.2",
  "smallvec",
@@ -4551,6 +4558,7 @@ dependencies = [
  "naga",
  "once_cell",
  "parking_lot",
+ "portable-atomic",
  "profiling",
  "raw-window-handle 0.6.2",
  "ron",
@@ -4681,6 +4689,7 @@ dependencies = [
  "once_cell",
  "ordered-float",
  "parking_lot",
+ "portable-atomic",
  "profiling",
  "range-alloc",
  "raw-window-handle 0.5.2",
@@ -4804,7 +4813,7 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 38a0b60ce6..f0608b43cc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -135,6 +135,7 @@ pico-args = { version = "0.5.0", features = [
 ] }
 png = "0.17.16"
 pollster = "0.4"
+portable-atomic = "1"
 profiling = { version = "1", default-features = false }
 raw-window-handle = { version = "0.6", default-features = false }
 rayon = "1"
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index 863769913d..bf8e84eefe 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -114,6 +114,9 @@ fragile-send-sync-non-atomic-wasm = [
 ## Enable using the `mach-dxcompiler-rs` crate to compile DX12 shaders.
 static-dxc = ["wgpu-hal/static-dxc"]
 
+## Enable portable atomics on platforms that do not support 64bit atomics.
+portable-atomic = ["dep:portable-atomic", "wgpu-hal/portable-atomic"]
+
 #! ### Target Conditional Features
 # --------------------------------------------------------------------
 # Look to wgpu-hal's Cargo.toml for explaination how these features and the wgpu-core
@@ -179,5 +182,8 @@ serde = { workspace = true, features = ["default", "derive"], optional = true }
 smallvec.workspace = true
 thiserror.workspace = true
 
+[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
+portable-atomic = { workspace = true, optional = true }
+
 [build-dependencies]
 cfg_aliases.workspace = true
diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs
index 55c43fba20..fde109a7fa 100644
--- a/wgpu-core/build.rs
+++ b/wgpu-core/build.rs
@@ -18,5 +18,7 @@ fn main() {
             all(target_vendor = "apple", feature = "vulkan-portability") // Vulkan Portability on Apple
         ) },
         metal: { all(target_vendor = "apple", feature = "metal") },
+
+        supports_64bit_atomics: { target_has_atomic = "64" }
     }
 }
diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs
index 036140e31b..69a62ab119 100644
--- a/wgpu-core/src/device/resource.rs
+++ b/wgpu-core/src/device/resource.rs
@@ -9,7 +9,7 @@ use core::{
     fmt,
     mem::{self, ManuallyDrop},
     num::NonZeroU32,
-    sync::atomic::{AtomicBool, AtomicU64, Ordering},
+    sync::atomic::{AtomicBool, Ordering},
 };
 use std::sync::OnceLock;
 
@@ -57,6 +57,11 @@ use super::{
     ENTRYPOINT_FAILURE_ERROR, ZERO_BUFFER_SIZE,
 };
 
+#[cfg(supports_64bit_atomics)]
+use core::sync::atomic::AtomicU64;
+#[cfg(not(supports_64bit_atomics))]
+use portable_atomic::AtomicU64;
+
 /// Structure describing a logical device. Some members are internally mutable,
 /// stored behind mutexes.
 pub struct Device {
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index 9255d507a7..dedf4e1014 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -155,6 +155,7 @@ renderdoc = ["dep:libloading", "dep:renderdoc-sys", "dep:log"]
 fragile-send-sync-non-atomic-wasm = [
     "wgpu-types/fragile-send-sync-non-atomic-wasm",
 ]
+portable-atomic = ["dep:portable-atomic"]
 
 ###################################
 ### Internal Debugging Features ###
@@ -300,6 +301,9 @@ khronos-egl = { workspace = true, optional = true, features = [
 # Note: it's unused by emscripten, but we keep it to have single code base in egl.rs
 libloading = { workspace = true, optional = true }
 
+[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
+portable-atomic = { workspace = true, optional = true }
+
 [build-dependencies]
 cfg_aliases.workspace = true
 
diff --git a/wgpu-hal/build.rs b/wgpu-hal/build.rs
index 9a648c60ea..d67321a72d 100644
--- a/wgpu-hal/build.rs
+++ b/wgpu-hal/build.rs
@@ -12,6 +12,7 @@ fn main() {
         metal: { all(target_vendor = "apple", feature = "metal") },
         vulkan: { all(not(target_arch = "wasm32"), feature = "vulkan") },
         // ⚠️ Keep in sync with target.cfg() definition in Cargo.toml and cfg_alias in `wgpu` crate ⚠️
-        static_dxc: { all(target_os = "windows", feature = "static-dxc", not(target_arch = "aarch64")) }
+        static_dxc: { all(target_os = "windows", feature = "static-dxc", not(target_arch = "aarch64")) },
+        supports_64bit_atomics: { target_has_atomic = "64" }
     }
 }
diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs
index 3bfc5dab3a..cd7139b27d 100644
--- a/wgpu-hal/src/lib.rs
+++ b/wgpu-hal/src/lib.rs
@@ -317,7 +317,10 @@ pub const QUERY_SIZE: wgt::BufferAddress = 8;
 pub type Label<'a> = Option<&'a str>;
 pub type MemoryRange = Range<wgt::BufferAddress>;
 pub type FenceValue = u64;
+#[cfg(supports_64bit_atomics)]
 pub type AtomicFenceValue = core::sync::atomic::AtomicU64;
+#[cfg(not(supports_64bit_atomics))]
+pub type AtomicFenceValue = portable_atomic::AtomicU64;
 
 /// A callback to signal that wgpu is no longer using a resource.
 #[cfg(any(gles, vulkan))]
diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs
index 2e1f8f665c..b9ddb242cb 100644
--- a/wgpu-hal/src/noop/mod.rs
+++ b/wgpu-hal/src/noop/mod.rs
@@ -1,11 +1,12 @@
 #![allow(unused_variables)]
 
 use alloc::{string::String, vec, vec::Vec};
-use core::{
-    ptr,
-    sync::atomic::{AtomicU64, Ordering},
-    time::Duration,
-};
+use core::{ptr, sync::atomic::Ordering, time::Duration};
+
+#[cfg(supports_64bit_atomics)]
+use core::sync::atomic::AtomicU64;
+#[cfg(not(supports_64bit_atomics))]
+use portable_atomic::AtomicU64;
 
 use crate::TlasInstance;
 
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index aa6f25d05c..c79f31aeba 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -179,6 +179,7 @@ wgpu-core = { workspace = true, features = [
     "raw-window-handle",
     "renderdoc",
     "indirect-validation",
+    "portable-atomic",
 ] }
 wgpu-hal.workspace = true
 
@@ -216,5 +217,8 @@ wgpu-hal.workspace = true
 
 smallvec.workspace = true
 
+[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
+portable-atomic.workspace = true
+
 [build-dependencies]
 cfg_aliases.workspace = true
diff --git a/wgpu/build.rs b/wgpu/build.rs
index d853281dfa..787e4691fb 100644
--- a/wgpu/build.rs
+++ b/wgpu/build.rs
@@ -16,5 +16,6 @@ fn main() {
         naga: { any(feature = "naga-ir", feature = "spirv", feature = "glsl") },
         // ⚠️ Keep in sync with target.cfg() definition in wgpu-hal/Cargo.toml and cfg_alias in `wgpu-hal` crate ⚠️
         static_dxc: { all(target_os = "windows", feature = "static-dxc", not(target_arch = "aarch64")) },
+        supports_64bit_atomics: { target_has_atomic = "64" }
     }
 }
diff --git a/wgpu/src/cmp.rs b/wgpu/src/cmp.rs
index 64fc73f115..60829cfde8 100644
--- a/wgpu/src/cmp.rs
+++ b/wgpu/src/cmp.rs
@@ -4,10 +4,12 @@
 //!
 //! For types (like WebGPU) that don't have such a property, we generate an identifier and use that.
 
-use core::{
-    num::NonZeroU64,
-    sync::atomic::{AtomicU64, Ordering},
-};
+#[cfg(supports_64bit_atomics)]
+pub use core::sync::atomic::AtomicU64;
+#[cfg(not(supports_64bit_atomics))]
+pub use portable_atomic::AtomicU64;
+
+use core::{num::NonZeroU64, sync::atomic::Ordering};
 
 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
 
diff --git a/xtask/src/check_feature_dependencies.rs b/xtask/src/check_feature_dependencies.rs
index 3a1e4c3f0c..70451df024 100644
--- a/xtask/src/check_feature_dependencies.rs
+++ b/xtask/src/check_feature_dependencies.rs
@@ -155,6 +155,22 @@ pub fn check_feature_dependencies(shell: Shell, arguments: Arguments) -> anyhow:
             default_features: false,
             search_terms: &[Search::Positive("glow")],
         },
+        Requirement {
+            human_readable_name: "x86-64 does not depend on portable-atomic",
+            target: "x86_64-unknown-linux-gnu",
+            packages: &["wgpu"],
+            features: &[],
+            default_features: false,
+            search_terms: &[Search::Negative("portable-atomic")],
+        },
+        Requirement {
+            human_readable_name: "ppc32 does depend on portable-atomic",
+            target: "powerpc-unknown-linux-gnu",
+            packages: &["wgpu"],
+            features: &[],
+            default_features: false,
+            search_terms: &[Search::Positive("portable-atomic")],
+        },
     ];
 
     let mut any_failures = false;


Test patch: