Add no_std support to wgpu-types (#6892)

* Initial Commit

* Remove now-redundant `format` import

* Update CHANGELOG.md

* Appropriately feature-gate `texture_format_serialize` test

* Remove `alloc` feature

Also fixed some documentation links and a Wasm `std` import

* Revert change to `Serialize` for `TextureFormat`

* Combine use statements

* Switch from `PathBuf` to `String`

* Consider environmental flags as unset on `no_std`

* Fix missing `format!`

* Add new CI tasks for `no_std` testing

* Comment out known failing CI matrix option

* Update all usage of `Dx12Compiler::DynamicDxc`

* Added comments to CI

* Update .github/workflows/ci.yml

* Update .github/workflows/ci.yml

* Update .github/workflows/ci.yml

* CI Touchups

---------

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Zachary Harrold 2025-01-12 16:03:47 +11:00 committed by GitHub
parent 4efc992d6d
commit 05e62f96f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 148 additions and 74 deletions

View File

@ -91,29 +91,34 @@ jobs:
- name: Windows x86_64
os: windows-2022
target: x86_64-pc-windows-msvc
tier: 1
kind: native
# Windows
- name: Windows aarch64
os: windows-2022
target: aarch64-pc-windows-msvc
tier: 2
kind: native
# MacOS
- name: MacOS x86_64
os: macos-14
target: x86_64-apple-darwin
tier: 1
kind: native
- name: MacOS aarch64
os: macos-14
target: aarch64-apple-darwin
tier: 1
kind: native
# IOS
- name: IOS aarch64
os: macos-14
target: aarch64-apple-ios
tier: 2
kind: native
# VisionOS
@ -121,36 +126,58 @@ jobs:
os: macos-14
target: aarch64-apple-visionos
kind: wgpu-only
toolchain: nightly
tier: 3
extra-flags: -Zbuild-std
# Linux
- name: Linux x86_64
os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
tier: 1
kind: native
- name: Linux aarch64
os: ubuntu-22.04
target: aarch64-unknown-linux-gnu
tier: 1
kind: native
# Android
- name: Android aarch64
os: ubuntu-22.04
target: aarch64-linux-android
tier: 2
kind: native
# WebGPU/WebGL
- name: WebAssembly
os: ubuntu-22.04
target: wasm32-unknown-unknown
tier: 2
kind: web
- name: Emscripten
os: ubuntu-22.04
target: wasm32-unknown-emscripten
tier: 2
kind: wgpu-only
# TODO: Uncomment once web-sys updates past 0.3.76
# See https://github.com/rustwasm/wasm-bindgen/pull/4378 for details
# - name: WebAssembly Core 1.0
# os: ubuntu-22.04
# target: wasm32v1-none
# tier: 2
# kind: no_std
# Bare-metal x86-64
# TODO: Remove once web-sys updates past 0.3.76
# Included while wasm32v1-none is failing to ensure `no_std` does not regress
- name: no_std x86_64
os: ubuntu-22.04
target: x86_64-unknown-none
tier: 2
kind: no_std
name: Clippy ${{ matrix.name }}
runs-on: ${{ matrix.os }}
@ -159,8 +186,8 @@ jobs:
- name: checkout repo
uses: actions/checkout@v4
- name: Install Toolchain (Repo MSRV)
if: matrix.toolchain != 'nightly'
- name: Install Toolchain (Repo MSRV - Tier 1 or 2)
if: matrix.tier == 1 || matrix.tier == 2
run: |
rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy
rustup target add ${{ matrix.target }} --toolchain ${{ env.REPO_MSRV }}
@ -178,8 +205,8 @@ jobs:
#
# RUSTC_BOOTSTRAP=1 is how the rust project builds itself when bootstrapping the compiler, so while not "stable"
# it has been around for many years and don't anticipate it going away any time soon.
- name: Install Toolchain (Repo MSRV - Pseudo Nightly)
if: matrix.toolchain == 'nightly'
- name: Install Toolchain (Repo MSRV - Tier 3)
if: matrix.tier == 3
run: |
rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy,rust-src
echo "RUSTC_BOOTSTRAP=1" >> "$GITHUB_ENV"
@ -246,6 +273,19 @@ jobs:
# Check with all features.
cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-hal --all-features
cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu --all-features
# Building for no_std platforms where every feature is enabled except "std".
- name: check no_std
if: matrix.kind == 'no_std'
shell: bash
run: |
set -e
# check with no features
cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-types --no-default-features
# Check with all features except "std".
cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-types --no-default-features --features strict_asserts,fragile-send-sync-non-atomic-wasm,serde,counters
# Building for native platforms with standard tests.
- name: check native

View File

@ -164,6 +164,7 @@ By @wumpf in [#6849](https://github.com/gfx-rs/wgpu/pull/6849).
- `DeviceType` and `AdapterInfo` now impl `Hash` by @cwfitzgerald in [#6868](https://github.com/gfx-rs/wgpu/pull/6868)
- Add build support for Apple Vision Pro. By @guusw in [#6611](https://github.com/gfx-rs/wgpu/pull/6611).
- Add `wgsl_language_features` for obtaining available WGSL language feature by @sagudev in [#6814](https://github.com/gfx-rs/wgpu/pull/6814)
- Add `no_std` support to `wgpu-types`. By @bushrat011899 in [#6892](https://github.com/gfx-rs/wgpu/pull/6892).
##### Vulkan

View File

@ -127,7 +127,7 @@ ron = "0.8"
# rustc-hash 2.0 is a completely different hasher with different performance characteristics
serde_json = "1.0.134"
rustc-hash = "1"
serde = "1"
serde = { version = "1", default-features = false }
smallvec = "1"
static_assertions = "1.1.0"
strum = { version = "0.26.0", features = ["derive"] }
@ -173,11 +173,11 @@ windows = { version = "0.58", default-features = false }
# wasm32 dependencies
console_error_panic_hook = "0.1.7"
console_log = "1"
js-sys = "0.3.70"
js-sys = { version = "0.3.70", default-features = false }
wasm-bindgen = "0.2.97"
wasm-bindgen-futures = "0.4.45"
wasm-bindgen-test = "0.3"
web-sys = "0.3.74"
web-sys = { version = "0.3.74", default-features = false }
web-time = "0.2.4"
# deno dependencies

View File

@ -16,4 +16,4 @@ anyhow.workspace = true
[dependencies.serde]
workspace = true
features = ["serde_derive"]
features = ["default", "serde_derive"]

View File

@ -78,7 +78,10 @@ log = "0.4"
strum.workspace = true
spirv = { version = "0.3", optional = true }
thiserror.workspace = true
serde = { version = "1.0.217", features = ["derive"], optional = true }
serde = { version = "1.0.217", features = [
"default",
"derive",
], optional = true }
# Hold on updating to 0.7 until https://github.com/petgraph/petgraph/pull/714 is on crates.io
petgraph = { version = "0.6", optional = true }
pp-rs = { version = "0.2.1", optional = true }
@ -101,5 +104,5 @@ hlsl-snapshots = { path = "./hlsl-snapshots" }
# incompatible with our tests because we do a syntactic diff and not a semantic one.
ron = "0.8.0"
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
serde = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["default", "derive"] }
spirv = { version = "0.3", features = ["deserialize"] }

View File

@ -127,7 +127,7 @@ profiling = { workspace = true, default-features = false }
raw-window-handle = { workspace = true, optional = true }
ron = { workspace = true, optional = true }
rustc-hash.workspace = true
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, features = ["default", "derive"], optional = true }
smallvec.workspace = true
thiserror.workspace = true

View File

@ -188,12 +188,13 @@ core-graphics-types.workspace = true
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
wasm-bindgen.workspace = true
web-sys = { workspace = true, features = [
"default",
"Window",
"HtmlCanvasElement",
"WebGl2RenderingContext",
"OffscreenCanvas",
] }
js-sys.workspace = true
js-sys = { workspace = true, features = ["default"] }
[target.'cfg(unix)'.dependencies]
libc.workspace = true

View File

@ -240,8 +240,8 @@ impl<A: hal::Api> Example<A> {
name: "example",
flags: wgt::InstanceFlags::default(),
dx12_shader_compiler: wgt::Dx12Compiler::DynamicDxc {
dxc_path: std::path::PathBuf::from("dxcompiler.dll"),
dxil_path: std::path::PathBuf::from("dxil.dll"),
dxc_path: "dxcompiler.dll".to_string(),
dxil_path: "dxil.dll".to_string(),
},
gles_minor_version: wgt::Gles3MinorVersion::default(),
};

View File

@ -72,14 +72,13 @@ impl crate::Instance for super::Instance {
dxil_path,
dxc_path,
} => {
let container =
super::shader_compilation::get_dynamic_dxc_container(dxc_path, dxil_path)
.map_err(|e| {
crate::InstanceError::with_source(
String::from("Failed to load dynamic DXC"),
e,
)
})?;
let container = super::shader_compilation::get_dynamic_dxc_container(
dxc_path.into(),
dxil_path.into(),
)
.map_err(|e| {
crate::InstanceError::with_source(String::from("Failed to load dynamic DXC"), e)
})?;
Some(Arc::new(container))
}

View File

@ -14,6 +14,6 @@ anyhow.workspace = true
bitflags.workspace = true
env_logger.workspace = true
pico-args.workspace = true
serde.workspace = true
serde = { workspace = true, features = ["default"] }
serde_json.workspace = true
wgpu = { workspace = true, features = ["serde", "dx12", "metal", "static-dxc"] }

View File

@ -28,6 +28,8 @@ targets = [
[lib]
[features]
default = ["std"]
std = ["js-sys/std", "web-sys/std"]
strict_asserts = []
fragile-send-sync-non-atomic-wasm = []
serde = ["dep:serde"]
@ -39,11 +41,14 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(web_sys_unstable_apis)'] }
[dependencies]
bitflags = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, default-features = false, features = [
"alloc",
"derive",
], optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys.workspace = true
web-sys = { workspace = true, features = [
js-sys = { workspace = true, default-features = false }
web-sys = { workspace = true, default-features = false, features = [
"ImageBitmap",
"ImageData",
"HtmlImageElement",
@ -56,3 +61,8 @@ web-sys = { workspace = true, features = [
[dev-dependencies]
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
[lints.clippy]
std_instead_of_core = "warn"
std_instead_of_alloc = "warn"
alloc_instead_of_core = "warn"

View File

@ -11,7 +11,7 @@
//! `wgpu-core`'s `"strict_asserts"` feature enables that validation
//! in both debug and release builds.
/// This is equivalent to [`std::assert`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert`].
/// This is equivalent to [`core::assert`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert`].
#[cfg(feature = "strict_asserts")]
#[macro_export]
macro_rules! strict_assert {
@ -20,7 +20,7 @@ macro_rules! strict_assert {
}
}
/// This is equivalent to [`std::assert_eq`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert_eq`].
/// This is equivalent to [`core::assert_eq`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert_eq`].
#[cfg(feature = "strict_asserts")]
#[macro_export]
macro_rules! strict_assert_eq {
@ -29,7 +29,7 @@ macro_rules! strict_assert_eq {
}
}
/// This is equivalent to [`std::assert_ne`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert_ne`].
/// This is equivalent to [`core::assert_ne`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert_ne`].
#[cfg(feature = "strict_asserts")]
#[macro_export]
macro_rules! strict_assert_ne {
@ -38,7 +38,7 @@ macro_rules! strict_assert_ne {
}
}
/// This is equivalent to [`std::assert`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert`]
/// This is equivalent to [`core::assert`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert`]
#[cfg(not(feature = "strict_asserts"))]
#[macro_export]
macro_rules! strict_assert {
@ -47,7 +47,7 @@ macro_rules! strict_assert {
};
}
/// This is equivalent to [`std::assert_eq`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert_eq`]
/// This is equivalent to [`core::assert_eq`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert_eq`]
#[cfg(not(feature = "strict_asserts"))]
#[macro_export]
macro_rules! strict_assert_eq {
@ -56,7 +56,7 @@ macro_rules! strict_assert_eq {
};
}
/// This is equivalent to [`std::assert_ne`] if the `strict_asserts` feature is activated, otherwise equal to [`std::debug_assert_ne`]
/// This is equivalent to [`core::assert_ne`] if the `strict_asserts` feature is activated, otherwise equal to [`core::debug_assert_ne`]
#[cfg(not(feature = "strict_asserts"))]
#[macro_export]
macro_rules! strict_assert_ne {

View File

@ -1,6 +1,7 @@
use alloc::{string::String, vec::Vec};
#[cfg(feature = "counters")]
use std::sync::atomic::{AtomicIsize, Ordering};
use std::{fmt, ops::Range};
use core::sync::atomic::{AtomicIsize, Ordering};
use core::{fmt, ops::Range};
/// An internal counter for debugging purposes
///
@ -95,8 +96,8 @@ impl Default for InternalCounter {
}
}
impl std::fmt::Debug for InternalCounter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl core::fmt::Debug for InternalCounter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.read().fmt(f)
}
}
@ -192,7 +193,7 @@ impl fmt::Debug for AllocationReport {
impl fmt::Debug for AllocatorReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut allocations = self.allocations.clone();
allocations.sort_by_key(|alloc| std::cmp::Reverse(alloc.size));
allocations.sort_by_key(|alloc| core::cmp::Reverse(alloc.size));
let max_num_allocations_to_print = f.precision().unwrap_or(usize::MAX);
allocations.truncate(max_num_allocations_to_print);
@ -200,7 +201,7 @@ impl fmt::Debug for AllocatorReport {
f.debug_struct("AllocatorReport")
.field(
"summary",
&std::format_args!(
&core::format_args!(
"{} / {}",
FmtBytes(self.total_allocated_bytes),
FmtBytes(self.total_reserved_bytes)

View File

@ -7,15 +7,26 @@
clippy::match_like_matches_macro,
)]
#![warn(clippy::ptr_as_ptr, missing_docs, unsafe_op_in_unsafe_fn)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
use alloc::{string::String, vec, vec::Vec};
use core::{
hash::{Hash, Hasher},
mem::size_of,
num::NonZeroU32,
ops::Range,
};
#[cfg(any(feature = "serde", test))]
use serde::Deserialize;
#[cfg(any(feature = "serde", test))]
use serde::Serialize;
use std::hash::{Hash, Hasher};
use std::mem::size_of;
use std::path::PathBuf;
use std::{num::NonZeroU32, ops::Range};
use {
alloc::format,
serde::{Deserialize, Serialize},
};
pub mod assertions;
mod counters;
@ -26,7 +37,7 @@ pub use counters::*;
/// Integral type used for buffer offsets.
pub type BufferAddress = u64;
/// Integral type used for buffer slice sizes.
pub type BufferSize = std::num::NonZeroU64;
pub type BufferSize = core::num::NonZeroU64;
/// Integral type used for binding locations in shaders.
pub type ShaderLocation = u32;
/// Integral type used for dynamic bind group offsets.
@ -88,8 +99,8 @@ impl Backend {
}
}
impl std::fmt::Display for Backend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl core::fmt::Display for Backend {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.to_str())
}
}
@ -1078,11 +1089,16 @@ impl InstanceFlags {
/// - WGPU_VALIDATION
#[must_use]
pub fn with_env(mut self) -> Self {
fn env(key: &str) -> Option<bool> {
std::env::var(key).ok().map(|s| match s.as_str() {
fn env(_key: &str) -> Option<bool> {
#[cfg(feature = "std")]
return std::env::var(_key).ok().map(|s| match s.as_str() {
"0" => false,
_ => true,
})
});
// Without access to std, environment variables are considered unset
#[cfg(not(feature = "std"))]
return None;
}
if let Some(bit) = env("WGPU_VALIDATION") {
@ -1479,7 +1495,7 @@ impl Limits {
fatal: bool,
mut fail_fn: impl FnMut(&'static str, u64, u64),
) {
use std::cmp::Ordering;
use core::cmp::Ordering;
macro_rules! compare {
($name:ident, $ordering:ident) => {
@ -2848,7 +2864,7 @@ impl<'de> Deserialize<'de> for TextureFormat {
impl de::Visitor<'_> for TextureFormatVisitor {
type Value = TextureFormat;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a valid texture format")
}
@ -4111,6 +4127,8 @@ impl TextureFormat {
#[test]
fn texture_format_serialize() {
use alloc::string::ToString;
assert_eq!(
serde_json::to_string(&TextureFormat::R8Unorm).unwrap(),
"\"r8unorm\"".to_string()
@ -5961,8 +5979,8 @@ impl Origin2d {
}
}
impl std::fmt::Debug for Origin2d {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl core::fmt::Debug for Origin2d {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.x, self.y).fmt(f)
}
}
@ -6004,8 +6022,8 @@ impl Default for Origin3d {
}
}
impl std::fmt::Debug for Origin3d {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl core::fmt::Debug for Origin3d {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.x, self.y, self.z).fmt(f)
}
}
@ -6028,8 +6046,8 @@ pub struct Extent3d {
pub depth_or_array_layers: u32,
}
impl std::fmt::Debug for Extent3d {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl core::fmt::Debug for Extent3d {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
(self.width, self.height, self.depth_or_array_layers).fmt(f)
}
}
@ -7221,7 +7239,7 @@ impl ExternalImageSource {
}
#[cfg(target_arch = "wasm32")]
impl std::ops::Deref for ExternalImageSource {
impl core::ops::Deref for ExternalImageSource {
type Target = js_sys::Object;
fn deref(&self) -> &Self::Target {
@ -7546,8 +7564,8 @@ impl DrawIndirectArgs {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
std::ptr::from_ref(self).cast::<u8>(),
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
@ -7577,8 +7595,8 @@ impl DrawIndexedIndirectArgs {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
std::ptr::from_ref(self).cast::<u8>(),
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
@ -7602,8 +7620,8 @@ impl DispatchIndirectArgs {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
std::ptr::from_ref(self).cast::<u8>(),
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
@ -7705,9 +7723,9 @@ pub enum Dx12Compiler {
/// It also requires WDDM 2.1 (Windows 10 version 1607).
DynamicDxc {
/// Path to `dxcompiler.dll`.
dxc_path: PathBuf,
dxc_path: String,
/// Path to `dxil.dll`.
dxil_path: PathBuf,
dxil_path: String,
},
/// The statically-linked variant of Dxc.
///

View File

@ -1,6 +1,6 @@
//! Utilitary math functions.
use std::ops::{Add, Rem, Sub};
use core::ops::{Add, Rem, Sub};
///
/// Aligns a `value` to an `alignment`.

View File

@ -193,7 +193,7 @@ log.workspace = true
parking_lot.workspace = true
profiling.workspace = true
raw-window-handle = { workspace = true, features = ["std"] }
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, features = ["default", "derive"], optional = true }
smallvec.workspace = true
static_assertions.workspace = true
@ -211,6 +211,7 @@ features = ["wgsl-in"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { workspace = true, features = [
"default",
"Document",
"Navigator",
"Node",
@ -227,6 +228,6 @@ web-sys = { workspace = true, features = [
"EventTarget",
] }
wasm-bindgen.workspace = true
js-sys.workspace = true
js-sys = { workspace = true, features = ["default"] }
wasm-bindgen-futures.workspace = true
parking_lot.workspace = true

View File

@ -108,8 +108,8 @@ pub fn dx12_shader_compiler_from_env() -> Option<wgt::Dx12Compiler> {
.as_deref()
{
Ok("dxc") => wgt::Dx12Compiler::DynamicDxc {
dxc_path: std::path::PathBuf::from("dxcompiler.dll"),
dxil_path: std::path::PathBuf::from("dxil.dll"),
dxc_path: "dxcompiler.dll".to_string(),
dxil_path: "dxil.dll".to_string(),
},
#[cfg(static_dxc)]
Ok("static-dxc") => wgt::Dx12Compiler::StaticDxc,