Bug 1910150: Update wgpu to 9c6ae1be (2024-7-31) r=webgpu-reviewers,supply-chain-reviewers,ErichDonGubler

Update the following crates in `third_party/rust` to commit 9c6ae1be
from `github.com/gfx-rs/wgpu`:

- d3d12
- naga
- wgpu-core
- wgpu-hal
- wgpu-types

Update dependencies:

- bit-set 0.8.0
- bit-vec 0.8.0
- gpu-allocator 0.27

Make necessary minor changes to `gfx/wgpu_bindings`.

Add appropriate audits to `supply-chain/audits.toml`.

Differential Revision: https://phabricator.services.mozilla.com/D218695
This commit is contained in:
Jim Blandy
2024-08-07 16:00:47 +00:00
parent 4d7ca2a448
commit 8aa54bb539
128 changed files with 2984 additions and 2226 deletions

View File

@@ -25,9 +25,9 @@ git = "https://github.com/franziskuskiefer/cose-rust"
rev = "43c22248d136c8b38fe42ea709d08da6355cf04b"
replace-with = "vendored-sources"
[source."git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4"]
[source."git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8"]
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
replace-with = "vendored-sources"
[source."git+https://github.com/hsivonen/any_all_workaround?rev=7fb1b7034c9f172aade21ee1c8554e8d8a48af80"]

32
Cargo.lock generated
View File

@@ -460,18 +460,18 @@ dependencies = [
[[package]]
name = "bit-set"
version = "0.6.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
@@ -1241,8 +1241,8 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4#aeb2067e8120c1ff480625c00b9571db8d01d5a4"
version = "22.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8#9c6ae1beae2490ce44d99034f7f1faada936f3d8"
dependencies = [
"bitflags 2.6.0",
"libloading",
@@ -2553,9 +2553,9 @@ dependencies = [
[[package]]
name = "gpu-allocator"
version = "0.26.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7"
checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
dependencies = [
"log",
"presser",
@@ -4022,8 +4022,8 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4#aeb2067e8120c1ff480625c00b9571db8d01d5a4"
version = "22.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8#9c6ae1beae2490ce44d99034f7f1faada936f3d8"
dependencies = [
"arrayvec",
"bit-set",
@@ -6768,8 +6768,8 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4#aeb2067e8120c1ff480625c00b9571db8d01d5a4"
version = "22.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8#9c6ae1beae2490ce44d99034f7f1faada936f3d8"
dependencies = [
"arrayvec",
"bit-vec",
@@ -6793,8 +6793,8 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4#aeb2067e8120c1ff480625c00b9571db8d01d5a4"
version = "22.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8#9c6ae1beae2490ce44d99034f7f1faada936f3d8"
dependencies = [
"android_system_properties",
"arrayvec",
@@ -6832,8 +6832,8 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=aeb2067e8120c1ff480625c00b9571db8d01d5a4#aeb2067e8120c1ff480625c00b9571db8d01d5a4"
version = "22.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=9c6ae1beae2490ce44d99034f7f1faada936f3d8#9c6ae1beae2490ce44d99034f7f1faada936f3d8"
dependencies = [
"bitflags 2.6.0",
"js-sys",

View File

@@ -17,7 +17,7 @@ default = []
[dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
# TODO: remove the replay feature on the next update containing https://github.com/gfx-rs/wgpu/pull/5182
features = ["serde", "replay", "trace", "strict_asserts", "wgsl", "api_log_info"]
@@ -26,37 +26,37 @@ features = ["serde", "replay", "trace", "strict_asserts", "wgsl", "api_log_info"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows.
[target.'cfg(windows)'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
features = ["dx12"]
# We want the wgpu-core Vulkan backend on Linux and Windows.
[target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
features = ["vulkan"]
[dependencies.wgt]
package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
[dependencies.wgh]
package = "wgpu-hal"
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
features = ["windows_rs", "oom_panic", "device_lost_panic", "internal_error_panic"]
[target.'cfg(windows)'.dependencies.d3d12]
git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
rev = "9c6ae1beae2490ce44d99034f7f1faada936f3d8"
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

View File

@@ -20,11 +20,11 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: aeb2067e8120c1ff480625c00b9571db8d01d5a4 (2024-07-17T23:11:04Z).
release: 9c6ae1beae2490ce44d99034f7f1faada936f3d8 (2024-7-31).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
revision: aeb2067e8120c1ff480625c00b9571db8d01d5a4
revision: 9c6ae1beae2490ce44d99034f7f1faada936f3d8
license: ['MIT', 'Apache-2.0']

View File

@@ -69,7 +69,6 @@ impl ProgrammableStageDescriptor {
entry_point: cow_label(&self.entry_point),
constants: Cow::Owned(constants),
zero_initialize_workgroup_memory: true,
vertex_pulling_transform: true,
}
}
}

View File

@@ -586,7 +586,7 @@ pub extern "C" fn wgpu_server_buffer_destroy(global: &Global, self_id: id::Buffe
#[no_mangle]
pub extern "C" fn wgpu_server_buffer_drop(global: &Global, self_id: id::BufferId) {
gfx_select!(self_id => global.buffer_drop(self_id, false));
gfx_select!(self_id => global.buffer_drop(self_id));
}
#[allow(unused_variables)]
@@ -1317,12 +1317,12 @@ pub extern "C" fn wgpu_server_texture_destroy(global: &Global, self_id: id::Text
#[no_mangle]
pub extern "C" fn wgpu_server_texture_drop(global: &Global, self_id: id::TextureId) {
gfx_select!(self_id => global.texture_drop(self_id, false));
gfx_select!(self_id => global.texture_drop(self_id));
}
#[no_mangle]
pub extern "C" fn wgpu_server_texture_view_drop(global: &Global, self_id: id::TextureViewId) {
gfx_select!(self_id => global.texture_view_drop(self_id, false)).unwrap();
gfx_select!(self_id => global.texture_view_drop(self_id)).unwrap();
}
#[no_mangle]

View File

@@ -870,6 +870,11 @@ who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.5.3 -> 0.6.0"
[[audits.bit-set]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.6.0 -> 0.8.0"
[[audits.bit-vec]]
who = "Aria Beingessner <a.beingessner@gmail.com>"
criteria = "safe-to-deploy"
@@ -881,6 +886,11 @@ who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.6.3 -> 0.7.0"
[[audits.bit-vec]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.7.0 -> 0.8.0"
[[audits.bitflags]]
who = "Alex Franchuk <afranchuk@mozilla.com>"
criteria = "safe-to-deploy"
@@ -1477,13 +1487,14 @@ criteria = "safe-to-deploy"
delta = "0.19.0 -> 0.20.0"
[[audits.d3d12]]
who = [
"Jim Blandy <jimb@red-bean.com>",
"Teodor Tanasoaia <ttanasoaia@mozilla.com>",
"Erich Gubler <erichdongubler@gmail.com>",
]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.20.0 -> 0.20.0@git:aeb2067e8120c1ff480625c00b9571db8d01d5a4"
delta = "0.20.0 -> 22.0.0"
[[audits.d3d12]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "22.0.0 -> 22.0.0@git:9c6ae1beae2490ce44d99034f7f1faada936f3d8"
importable = false
[[audits.darling]]
@@ -2268,6 +2279,11 @@ criteria = "safe-to-deploy"
delta = "0.25.0 -> 0.26.0"
notes = "New Metal backend is written with no `unsafe`. New `unsafe` usage of DX12's platform APIs appear correct and safe. Otherwise, minimal changes."
[[audits.gpu-allocator]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.26.0 -> 0.27.0"
[[audits.gpu-descriptor]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
@@ -3084,13 +3100,14 @@ criteria = "safe-to-deploy"
delta = "0.19.2 -> 0.20.0"
[[audits.naga]]
who = [
"Jim Blandy <jimb@red-bean.com>",
"Teodor Tanasoaia <ttanasoaia@mozilla.com>",
"Erich Gubler <erichdongubler@gmail.com>",
]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.20.0 -> 0.20.0@git:aeb2067e8120c1ff480625c00b9571db8d01d5a4"
delta = "0.20.0 -> 22.0.0"
[[audits.naga]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "22.0.0 -> 22.0.0@git:9c6ae1beae2490ce44d99034f7f1faada936f3d8"
importable = false
[[audits.net2]]
@@ -5043,13 +5060,14 @@ criteria = "safe-to-deploy"
delta = "0.19.3 -> 0.20.0"
[[audits.wgpu-core]]
who = [
"Jim Blandy <jimb@red-bean.com>",
"Teodor Tanasoaia <ttanasoaia@mozilla.com>",
"Erich Gubler <erichdongubler@gmail.com>",
]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.20.0 -> 0.20.0@git:aeb2067e8120c1ff480625c00b9571db8d01d5a4"
delta = "0.20.0 -> 22.0.0"
[[audits.wgpu-core]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "22.0.0 -> 22.0.0@git:9c6ae1beae2490ce44d99034f7f1faada936f3d8"
importable = false
[[audits.wgpu-hal]]
@@ -5111,13 +5129,14 @@ criteria = "safe-to-deploy"
delta = "0.19.3 -> 0.20.0"
[[audits.wgpu-hal]]
who = [
"Jim Blandy <jimb@red-bean.com>",
"Teodor Tanasoaia <ttanasoaia@mozilla.com>",
"Erich Gubler <erichdongubler@gmail.com>",
]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.20.0 -> 0.20.0@git:aeb2067e8120c1ff480625c00b9571db8d01d5a4"
delta = "0.20.0 -> 22.0.0"
[[audits.wgpu-hal]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "22.0.0 -> 22.0.0@git:9c6ae1beae2490ce44d99034f7f1faada936f3d8"
importable = false
[[audits.wgpu-types]]
@@ -5179,13 +5198,14 @@ criteria = "safe-to-deploy"
delta = "0.19.2 -> 0.20.0"
[[audits.wgpu-types]]
who = [
"Jim Blandy <jimb@red-bean.com>",
"Teodor Tanasoaia <ttanasoaia@mozilla.com>",
"Erich Gubler <erichdongubler@gmail.com>",
]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "0.20.0 -> 0.20.0@git:aeb2067e8120c1ff480625c00b9571db8d01d5a4"
delta = "0.20.0 -> 22.0.0"
[[audits.wgpu-types]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"
delta = "22.0.0 -> 22.0.0@git:9c6ae1beae2490ce44d99034f7f1faada936f3d8"
importable = false
[[audits.whatsys]]

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"072bebcdc135cd08bf1fc8b1d6098fde8174868fbfe316261150861748a4bea0","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"3722445d55d05fa0095b68ccce1b17972584ce8bfcddb911a7e1071cdcb0ef77","deploy-docs.sh":"7b66111b124c1c7e59cb84cf110d98b5cb783bd35a676e970d9b3035e55f7dfd","src/lib.rs":"33dd2f06f955acede0dd632b11e622eba87b84264cd43381d4a6099d45255a9e"},"package":"f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"}
{"files":{"Cargo.toml":"f718d1e3b585881b737a689fdcc34b7df64818b14093907d5c4546e29692ea8f","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"b2301e8a7953cc006062459041fc9edaa9e31ad5fc2aa2009ed742380624e4c5","RELEASES.md":"78721919b25f13b36004ff40acde09fb1beaaa4e1d721fe30c7b1eb6f9325190","benches/bench.rs":"b044b6aa529e73d921ade54b85dd1c07543a588999ba69192714774abf80edbf","src/lib.rs":"c090ea3ef7cd0367c8e157418c5d51072a474f660b1c74c94fa68be696a079e0"},"package":"08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"}

View File

@@ -10,8 +10,9 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2015"
name = "bit-set"
version = "0.6.0"
version = "0.8.0"
authors = ["Alexis Beingessner <a.beingessner@gmail.com>"]
build = false
autobins = false
@@ -20,27 +21,42 @@ autotests = false
autobenches = false
description = "A set of bits"
homepage = "https://github.com/contain-rs/bit-set"
documentation = "https://contain-rs.github.io/bit-set/bit_set"
documentation = "https://docs.rs/bit-set/"
readme = "README.md"
keywords = [
"data-structures",
"bitset",
]
license = "MIT/Apache-2.0"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/contain-rs/bit-set"
[lib]
name = "bit_set"
path = "src/lib.rs"
[[bench]]
name = "bench"
path = "benches/bench.rs"
[dependencies.bit-vec]
version = "0.7.0"
version = "0.8.0"
default-features = false
[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true
[dev-dependencies.rand]
version = "0.8"
[dev-dependencies.serde_json]
version = "1.0"
[features]
bench = []
default = ["std"]
serde = [
"dep:serde",
"bit-vec/serde",
]
std = ["bit-vec/std"]

View File

@@ -19,13 +19,13 @@
[crates.io shield]: https://img.shields.io/crates/v/bit-set?label=latest
[crates.io link]: https://crates.io/crates/bit-set
[docs.rs badge]: https://docs.rs/bit-set/badge.svg?version=0.6.0
[docs.rs link]: https://docs.rs/bit-set/0.6.0/bit_set/
[docs.rs badge]: https://docs.rs/bit-set/badge.svg?version=0.8.0
[docs.rs link]: https://docs.rs/bit-set/0.8.0/bit_set/
[github ci badge]: https://github.com/contain-rs/linked-hash-map/workflows/Rust/badge.svg?branch=master
[rustc 1.0+]: https://img.shields.io/badge/rustc-1.0%2B-blue.svg
[Rust 1.0]: https://blog.rust-lang.org/2015/05/15/Rust-1.0.html
[deps.rs status]: https://deps.rs/crate/bit-set/0.6.0/status.svg
[deps.rs link]: https://deps.rs/crate/bit-set/0.6.0
[deps.rs status]: https://deps.rs/crate/bit-set/0.8.0/status.svg
[deps.rs link]: https://deps.rs/crate/bit-set/0.8.0
[shields.io download count]: https://img.shields.io/crates/d/bit-set.svg
## Usage
@@ -34,7 +34,7 @@ Add this to your Cargo.toml:
```toml
[dependencies]
bit-set = "0.5"
bit-set = "0.8"
```
Since Rust 2018, `extern crate` is no longer mandatory. If your edition is old (Rust 2015),
@@ -44,11 +44,18 @@ add this to your crate root:
extern crate bit_set;
```
If you want to use `serde`, enable it with the `serde` feature:
```toml
[dependencies]
bit-set = { version = "0.8", features = ["serde"] }
```
If you want to use bit-set in a program that has `#![no_std]`, just drop default features:
```toml
[dependencies]
bit-set = { version = "0.5", default-features = false }
bit-set = { version = "0.8", default-features = false }
```
<!-- cargo-rdme start -->

10
third_party/rust/bit-set/RELEASES.md vendored Normal file
View File

@@ -0,0 +1,10 @@
Version 0.7.0 (not yet released) (ZERO BREAKING CHANGES)
========================================================
<a id="v0.7.0"></a>
- `serde::Serialize`, `Deserialize` is derived under the `serde` optional feature
- `impl Display` is implemented
- `impl Debug` has different output (we do not promise stable `Debug` output)
- `fn truncate` is implemented
- `fn get_mut` is implemented

View File

@@ -0,0 +1,65 @@
// Copyright 2012-2024 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(test)]
extern crate bit_set;
extern crate bit_vec;
extern crate rand;
extern crate test;
use bit_set::BitSet;
use bit_vec::BitVec;
use rand::{rngs::ThreadRng, thread_rng, RngCore};
use test::{black_box, Bencher};
const BENCH_BITS: usize = 1 << 14;
const BITS: usize = 32;
fn rng() -> ThreadRng {
thread_rng()
}
#[bench]
fn bench_bit_vecset_small(b: &mut Bencher) {
let mut r = rng();
let mut bit_vec = BitSet::new();
b.iter(|| {
for _ in 0..100 {
bit_vec.insert((r.next_u32() as usize) % BITS);
}
black_box(&bit_vec);
});
}
#[bench]
fn bench_bit_vecset_big(b: &mut Bencher) {
let mut r = rng();
let mut bit_vec = BitSet::new();
b.iter(|| {
for _ in 0..100 {
bit_vec.insert((r.next_u32() as usize) % BENCH_BITS);
}
black_box(&bit_vec);
});
}
#[bench]
fn bench_bit_vecset_iter(b: &mut Bencher) {
let bit_vec = BitSet::from_bit_vec(BitVec::from_fn(BENCH_BITS, |idx| idx % 3 == 0));
b.iter(|| {
let mut sum = 0;
for idx in &bit_vec {
sum += idx as usize;
}
sum
})
}

View File

@@ -1,20 +0,0 @@
#!/bin/bash
set -o errexit -o nounset
rev=$(git rev-parse --short HEAD)
cd target/doc
git init
git config user.email 'FlashCat@users.noreply.github.com'
git config user.name 'FlashCat'
git remote add upstream "https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git"
git fetch upstream gh-pages
git reset upstream/gh-pages
touch .
git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

View File

@@ -48,15 +48,13 @@
//! let bv = s.into_bit_vec();
//! assert!(bv[3]);
//! ```
#![doc(html_root_url = "https://docs.rs/bit-set/0.8.0")]
#![no_std]
#![cfg_attr(feature = "bench", feature(test))]
extern crate bit_vec;
#[cfg(test)]
extern crate rand;
#[cfg(feature = "bench")]
extern crate test;
#[cfg(feature = "serde")]
extern crate serde;
#[cfg(any(test, feature = "std"))]
extern crate std;
@@ -119,6 +117,7 @@ fn match_words<'a, 'b, B: BitBlock>(
}
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct BitSet<B = u32> {
bit_vec: BitVec<B>,
}
@@ -341,10 +340,10 @@ impl<B: BitBlock> BitSet<B> {
/// ```
/// use bit_set::BitSet;
///
/// let mut s = BitSet::new();
/// s.insert(0);
/// let mut set = BitSet::new();
/// set.insert(0);
///
/// let bv = s.get_ref();
/// let bv = set.get_ref();
/// assert_eq!(bv[0], true);
/// ```
#[inline]
@@ -352,6 +351,31 @@ impl<B: BitBlock> BitSet<B> {
&self.bit_vec
}
/// Returns a mutable reference to the underlying bit vector.
///
/// # Examples
///
/// ```
/// use bit_set::BitSet;
///
/// let mut set = BitSet::new();
/// set.insert(0);
/// set.insert(3);
///
/// {
/// let bv = set.get_mut();
/// bv.set(1, true);
/// }
///
/// assert!(set.contains(0));
/// assert!(set.contains(1));
/// assert!(set.contains(3));
/// ```
#[inline]
pub fn get_mut(&mut self) -> &mut BitVec<B> {
&mut self.bit_vec
}
#[inline]
fn other_op<F>(&mut self, other: &Self, mut f: F)
where
@@ -421,8 +445,8 @@ impl<B: BitBlock> BitSet<B> {
unsafe {
bit_vec.storage_mut().truncate(trunc_len);
bit_vec.set_len(trunc_len * B::bits());
bit_vec.shrink_to_fit();
}
bit_vec.shrink_to_fit();
}
/// Iterator over each usize stored in the `BitSet`.
@@ -445,7 +469,7 @@ impl<B: BitBlock> BitSet<B> {
}
/// Iterator over each usize stored in `self` union `other`.
/// See [union_with](#method.union_with) for an efficient in-place version.
/// See [`union_with`] for an efficient in-place version.
///
/// # Examples
///
@@ -460,6 +484,8 @@ impl<B: BitBlock> BitSet<B> {
/// println!("{}", x);
/// }
/// ```
///
/// [`union_with`]: Self::union_with
#[inline]
pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, B> {
fn or<B: BitBlock>(w1: B, w2: B) -> B {
@@ -474,7 +500,7 @@ impl<B: BitBlock> BitSet<B> {
}
/// Iterator over each usize stored in `self` intersect `other`.
/// See [intersect_with](#method.intersect_with) for an efficient in-place version.
/// See [`intersect_with`] for an efficient in-place version.
///
/// # Examples
///
@@ -489,6 +515,8 @@ impl<B: BitBlock> BitSet<B> {
/// println!("{}", x);
/// }
/// ```
///
/// [`intersect_with`]: Self::intersect_with
#[inline]
pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, B> {
fn bitand<B: BitBlock>(w1: B, w2: B) -> B {
@@ -496,18 +524,18 @@ impl<B: BitBlock> BitSet<B> {
}
let min = cmp::min(self.bit_vec.len(), other.bit_vec.len());
Intersection(
BlockIter::from_blocks(TwoBitPositions {
Intersection {
iter: BlockIter::from_blocks(TwoBitPositions {
set: self.bit_vec.blocks(),
other: other.bit_vec.blocks(),
merge: bitand,
})
.take(min),
)
}),
n: min,
}
}
/// Iterator over each usize stored in the `self` setminus `other`.
/// See [difference_with](#method.difference_with) for an efficient in-place version.
/// See [`difference_with`] for an efficient in-place version.
///
/// # Examples
///
@@ -529,6 +557,8 @@ impl<B: BitBlock> BitSet<B> {
/// println!("{}", x);
/// }
/// ```
///
/// [`difference_with`]: Self::difference_with
#[inline]
pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, B> {
fn diff<B: BitBlock>(w1: B, w2: B) -> B {
@@ -543,8 +573,7 @@ impl<B: BitBlock> BitSet<B> {
}
/// Iterator over each usize stored in the symmetric difference of `self` and `other`.
/// See [symmetric_difference_with](#method.symmetric_difference_with) for
/// an efficient in-place version.
/// See [`symmetric_difference_with`] for an efficient in-place version.
///
/// # Examples
///
@@ -559,6 +588,8 @@ impl<B: BitBlock> BitSet<B> {
/// println!("{}", x);
/// }
/// ```
///
/// [`symmetric_difference_with`]: Self::symmetric_difference_with
#[inline]
pub fn symmetric_difference<'a>(&'a self, other: &'a Self) -> SymmetricDifference<'a, B> {
fn bitxor<B: BitBlock>(w1: B, w2: B) -> B {
@@ -815,7 +846,7 @@ impl<B: BitBlock> BitSet<B> {
// Ensure we have enough space to hold the new element
let len = self.bit_vec.len();
if value >= len {
self.bit_vec.grow(value - len + 1, false)
self.bit_vec.grow(value - len + 1, false);
}
self.bit_vec.set(value, true);
@@ -833,6 +864,11 @@ impl<B: BitBlock> BitSet<B> {
true
}
/// Excludes `element` and all greater elements from the `BitSet`.
pub fn truncate(&mut self, element: usize) {
self.bit_vec.truncate(element);
}
}
impl<B: BitBlock> fmt::Debug for BitSet<B> {
@@ -884,7 +920,14 @@ pub struct Iter<'a, B: 'a>(BlockIter<Blocks<'a, B>, B>);
#[derive(Clone)]
pub struct Union<'a, B: 'a>(BlockIter<TwoBitPositions<'a, B>, B>);
#[derive(Clone)]
pub struct Intersection<'a, B: 'a>(Take<BlockIter<TwoBitPositions<'a, B>, B>>);
pub struct Intersection<'a, B: 'a> {
iter: BlockIter<TwoBitPositions<'a, B>, B>,
// as an optimization, we compute the maximum possible
// number of elements in the intersection, and count it
// down as we return elements. If we reach zero, we can
// stop.
n: usize,
}
#[derive(Clone)]
pub struct Difference<'a, B: 'a>(BlockIter<TwoBitPositions<'a, B>, B>);
#[derive(Clone)]
@@ -916,10 +959,14 @@ where
Some(self.head_offset + (B::count_ones(k)))
}
fn count(self) -> usize {
self.head.count_ones() + self.tail.map(|block| block.count_ones()).sum::<usize>()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self.tail.size_hint() {
(_, Some(h)) => (0, Some(1 + h * B::bits())),
(_, Some(h)) => (0, Some((1 + h) * B::bits())),
_ => (0, None),
}
}
@@ -962,6 +1009,10 @@ impl<'a, B: BitBlock> Iterator for Iter<'a, B> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize {
self.0.count()
}
}
impl<'a, B: BitBlock> Iterator for Union<'a, B> {
@@ -975,6 +1026,10 @@ impl<'a, B: BitBlock> Iterator for Union<'a, B> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize {
self.0.count()
}
}
impl<'a, B: BitBlock> Iterator for Intersection<'a, B> {
@@ -982,11 +1037,25 @@ impl<'a, B: BitBlock> Iterator for Intersection<'a, B> {
#[inline]
fn next(&mut self) -> Option<usize> {
self.0.next()
if self.n != 0 {
self.n -= 1;
self.iter.next()
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
// We could invoke self.iter.size_hint() and incorporate that into the hint.
// In practice, that does not seem worthwhile because the lower bound will
// always be zero and the upper bound could only possibly less then n in a
// partially iterated iterator. However, it makes little sense ask for size_hint
// in a partially iterated iterator, so it did not seem worthwhile.
(0, Some(self.n))
}
#[inline]
fn count(self) -> usize {
self.iter.count()
}
}
@@ -1001,6 +1070,10 @@ impl<'a, B: BitBlock> Iterator for Difference<'a, B> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize {
self.0.count()
}
}
impl<'a, B: BitBlock> Iterator for SymmetricDifference<'a, B> {
@@ -1014,6 +1087,10 @@ impl<'a, B: BitBlock> Iterator for SymmetricDifference<'a, B> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize {
self.0.count()
}
}
impl<'a, B: BitBlock> IntoIterator for &'a BitSet<B> {
@@ -1061,12 +1138,14 @@ mod tests {
let idxs: Vec<_> = bit_vec.iter().collect();
assert_eq!(idxs, [0, 2, 3]);
assert_eq!(bit_vec.iter().count(), 3);
let long: BitSet = (0..10000).filter(|&n| n % 2 == 0).collect();
let real: Vec<_> = (0..10000 / 2).map(|x| x * 2).collect();
let idxs: Vec<_> = long.iter().collect();
assert_eq!(idxs, real);
assert_eq!(long.iter().count(), real.len());
}
#[test]
@@ -1132,6 +1211,7 @@ mod tests {
let expected = [3, 5, 11, 77];
let actual: Vec<_> = a.intersection(&b).collect();
assert_eq!(actual, expected);
assert_eq!(a.intersection(&b).count(), expected.len());
}
#[test]
@@ -1151,6 +1231,7 @@ mod tests {
let expected = [1, 5, 500];
let actual: Vec<_> = a.difference(&b).collect();
assert_eq!(actual, expected);
assert_eq!(a.difference(&b).count(), expected.len());
}
#[test]
@@ -1172,6 +1253,7 @@ mod tests {
let expected = [1, 5, 11, 14, 220];
let actual: Vec<_> = a.symmetric_difference(&b).collect();
assert_eq!(actual, expected);
assert_eq!(a.symmetric_difference(&b).count(), expected.len());
}
#[test]
@@ -1197,6 +1279,7 @@ mod tests {
let expected = [1, 3, 5, 9, 11, 13, 19, 24, 160, 200];
let actual: Vec<_> = a.union(&b).collect();
assert_eq!(actual, expected);
assert_eq!(a.union(&b).count(), expected.len());
}
#[test]
@@ -1494,6 +1577,45 @@ mod tests {
assert!(b.contains(1000));
}
#[test]
fn test_truncate() {
let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
let mut s = BitSet::from_bytes(&bytes);
s.truncate(5 * 8);
assert_eq!(s, BitSet::from_bytes(&bytes[..5]));
assert_eq!(s.len(), 5 * 8);
s.truncate(4 * 8);
assert_eq!(s, BitSet::from_bytes(&bytes[..4]));
assert_eq!(s.len(), 4 * 8);
// Truncating to a size > s.len() should be a noop
s.truncate(5 * 8);
assert_eq!(s, BitSet::from_bytes(&bytes[..4]));
assert_eq!(s.len(), 4 * 8);
s.truncate(8);
assert_eq!(s, BitSet::from_bytes(&bytes[..1]));
assert_eq!(s.len(), 8);
s.truncate(0);
assert_eq!(s, BitSet::from_bytes(&[]));
assert_eq!(s.len(), 0);
}
#[cfg(feature = "serde")]
#[test]
fn test_serialization() {
let bset: BitSet = BitSet::new();
let serialized = serde_json::to_string(&bset).unwrap();
let unserialized: BitSet = serde_json::from_str(&serialized).unwrap();
assert_eq!(bset, unserialized);
let elems: Vec<usize> = vec![11, 42, 100, 101];
let bset: BitSet = elems.iter().map(|n| *n).collect();
let serialized = serde_json::to_string(&bset).unwrap();
let unserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(bset, unserialized);
}
/*
#[test]
fn test_bit_set_append() {
@@ -1557,55 +1679,3 @@ mod tests {
}
*/
}
#[cfg(feature = "bench")]
mod bench {
use super::BitSet;
use bit_vec::BitVec;
use rand::{rngs::ThreadRng, thread_rng, RngCore};
use test::{black_box, Bencher};
const BENCH_BITS: usize = 1 << 14;
const BITS: usize = 32;
fn rng() -> ThreadRng {
thread_rng()
}
#[bench]
fn bench_bit_vecset_small(b: &mut Bencher) {
let mut r = rng();
let mut bit_vec = BitSet::new();
b.iter(|| {
for _ in 0..100 {
bit_vec.insert((r.next_u32() as usize) % BITS);
}
black_box(&bit_vec);
});
}
#[bench]
fn bench_bit_vecset_big(b: &mut Bencher) {
let mut r = rng();
let mut bit_vec = BitSet::new();
b.iter(|| {
for _ in 0..100 {
bit_vec.insert((r.next_u32() as usize) % BENCH_BITS);
}
black_box(&bit_vec);
});
}
#[bench]
fn bench_bit_vecset_iter(b: &mut Bencher) {
let bit_vec = BitSet::from_bit_vec(BitVec::from_fn(BENCH_BITS, |idx| idx % 3 == 0));
b.iter(|| {
let mut sum = 0;
for idx in &bit_vec {
sum += idx as usize;
}
sum
})
}
}

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"4b0b92b08b6e3ccc92da0a88ae49203b43429879f83c37a79cbeb17a9cd9fd2e","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"115294791cc85dd20010ce6854e230f585a87992e8e76c7af2a9b7a28b2cac8b","benches/bench.rs":"b0f3cd80ea37456a4ba7dee46f3aef0a143c7ab88418b8ca8e0661b9bb741d2a","crusader.sh":"e656dcb62d5122a64d55f837992e63cfd3beee37cf74c5ab6ff178a3c7ef943e","src/lib.rs":"2176d15b2dac5eb53d835705512d787dac646b2fa824b6bbff672e9ab7c7aec6"},"package":"d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"}
{"files":{"Cargo.toml":"3ffdb0eaead44d902d6db061fec67c139107b79a1044b974fa56560644cb3a92","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"5fc245f9be5f4c99931ca018b09603d29f9e376d8f9bc77cb7b156a4bdc7926a","RELEASES.md":"19717f09fe2af669be80801a5702ecd166e6001194c935e81669f72619e4144a","benches/bench.rs":"b0f3cd80ea37456a4ba7dee46f3aef0a143c7ab88418b8ca8e0661b9bb741d2a","crusader.sh":"e656dcb62d5122a64d55f837992e63cfd3beee37cf74c5ab6ff178a3c7ef943e","src/lib.rs":"2c570ee7e33315cb8f1cbb33bbb91aee9b4b9dc8521f488837414e890a149084"},"package":"5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"}

View File

@@ -12,7 +12,7 @@
[package]
edition = "2015"
name = "bit-vec"
version = "0.7.0"
version = "0.8.0"
authors = ["Alexis Beingessner <a.beingessner@gmail.com>"]
build = false
autobins = false
@@ -21,7 +21,7 @@ autotests = false
autobenches = false
description = "A vector of bits"
homepage = "https://github.com/contain-rs/bit-vec"
documentation = "https://contain-rs.github.io/bit-vec/bit_vec"
documentation = "https://docs.rs/bit-vec/"
readme = "README.md"
keywords = [
"data-structures",
@@ -30,7 +30,7 @@ keywords = [
"bitmap",
"bit",
]
license = "MIT/Apache-2.0"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/contain-rs/bit-vec"
[package.metadata.docs.rs]
@@ -50,7 +50,7 @@ name = "bench"
path = "benches/bench.rs"
[dependencies.borsh]
version = "1.5.0"
version = "1.5"
features = ["derive"]
optional = true
default-features = false

View File

@@ -20,15 +20,15 @@
[crates.io shield]: https://img.shields.io/crates/v/bit-vec?label=latest
[crates.io link]: https://crates.io/crates/bit-vec
[docs.rs badge]: https://docs.rs/bit-vec/badge.svg?version=0.7.0
[docs.rs link]: https://docs.rs/bit-vec/0.7.0/bit_vec/
[docs.rs badge]: https://docs.rs/bit-vec/badge.svg?version=0.8.0
[docs.rs link]: https://docs.rs/bit-vec/0.8.0/bit_vec/
[github ci badge]: https://github.com/contain-rs/linked-hash-map/workflows/Rust/badge.svg?branch=master
[rustc 1.0+]: https://img.shields.io/badge/rustc-1.0%2B-blue.svg
[serde_derive: rustc 1.31+]: https://img.shields.io/badge/serde_derive-rustc_1.31+-lightgray.svg
[Rust 1.0]: https://blog.rust-lang.org/2015/05/15/Rust-1.0.html
[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
[deps.rs status]: https://deps.rs/crate/bit-vec/0.7.0/status.svg
[deps.rs link]: https://deps.rs/crate/bit-vec/0.7.0
[deps.rs status]: https://deps.rs/crate/bit-vec/0.8.0/status.svg
[deps.rs link]: https://deps.rs/crate/bit-vec/0.8.0
[shields.io download count]: https://img.shields.io/crates/d/bit-vec.svg
## Usage
@@ -37,7 +37,7 @@ Add this to your Cargo.toml:
```toml
[dependencies]
bit-vec = "0.6"
bit-vec = "0.8"
```
Since Rust 2018, `extern crate` is no longer mandatory. If your edition is old (Rust 2015),
@@ -51,28 +51,28 @@ If you want [serde](https://github.com/serde-rs/serde) support, include the feat
```toml
[dependencies]
bit-vec = { version = "0.6", features = ["serde"] }
bit-vec = { version = "0.8", features = ["serde"] }
```
If you want to use bit-vec in a program that has `#![no_std]`, just drop default features:
```toml
[dependencies]
bit-vec = { version = "0.6", default-features = false }
bit-vec = { version = "0.8", default-features = false }
```
If you want to use serde with the alloc crate instead of std, just use the `serde_no_std` feature:
```toml
[dependencies]
bit-vec = { version = "0.6", default-features = false, features = ["serde", "serde_no_std"] }
bit-vec = { version = "0.8", default-features = false, features = ["serde", "serde_no_std"] }
```
If you want [borsh-rs](https://github.com/near/borsh-rs) support, include it like this:
```toml
[dependencies]
bit-vec = { version = "0.6", features = ["borsh"] }
bit-vec = { version = "0.8", features = ["borsh"] }
```
Other available serialization libraries can be enabled with the

8
third_party/rust/bit-vec/RELEASES.md vendored Normal file
View File

@@ -0,0 +1,8 @@
Version 0.8.0 (2024-07-16)
==========================
<a id="v0.8.0"></a>
- `fn insert` is implemented
- `impl Display` is implemented
- `impl Debug` has different output

View File

@@ -82,7 +82,7 @@
//! assert_eq!(num_primes, 1_229);
//! ```
#![doc(html_root_url = "https://docs.rs/bit-vec/0.6.3")]
#![doc(html_root_url = "https://docs.rs/bit-vec/0.8.0")]
#![no_std]
#[cfg(any(test, feature = "std"))]
@@ -91,6 +91,8 @@ extern crate std;
#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(feature = "std")]
use std::string::String;
#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(feature = "serde")]
@@ -112,12 +114,14 @@ extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::rc::Rc;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cell::RefCell;
use core::cmp;
use core::cmp::Ordering;
use core::fmt;
use core::fmt::{self, Write};
use core::hash;
use core::iter::repeat;
use core::iter::FromIterator;
@@ -447,19 +451,19 @@ impl<B: BitBlock> BitVec<B> {
}
}
/// Exposes the raw block storage of this BitVec
/// Exposes the raw block storage of this `BitVec`.
///
/// Only really intended for BitSet.
/// Only really intended for `BitSet`.
#[inline]
pub fn storage(&self) -> &[B] {
&self.storage
}
/// Exposes the raw block storage of this BitVec
/// Exposes the raw block storage of this `BitVec`.
///
/// # Safety
///
/// Can probably cause unsafety. Only really intended for BitSet.
/// Can probably cause unsafety. Only really intended for `BitSet`.
#[inline]
pub unsafe fn storage_mut(&mut self) -> &mut Vec<B> {
&mut self.storage
@@ -1521,7 +1525,7 @@ impl<B: BitBlock> BitVec<B> {
self.fix_last_block();
}
/// Removes the last bit from the BitVec, and returns it. Returns None if the BitVec is empty.
/// Removes the last bit from the `BitVec`, and returns it. Returns `None` if the `BitVec` is empty.
///
/// # Examples
///
@@ -1581,11 +1585,11 @@ impl<B: BitBlock> BitVec<B> {
self.nbits
}
/// Sets the number of bits that this BitVec considers initialized.
/// Sets the number of bits that this `BitVec` considers initialized.
///
/// # Safety
///
/// Almost certainly can cause bad stuff. Only really intended for BitSet.
/// Almost certainly can cause bad stuff. Only really intended for `BitSet`.
#[inline]
pub unsafe fn set_len(&mut self, len: usize) {
self.nbits = len;
@@ -1615,6 +1619,61 @@ impl<B: BitBlock> BitVec<B> {
pub fn shrink_to_fit(&mut self) {
self.storage.shrink_to_fit();
}
/// Inserts a given bit at index `at`, shifting all bits after by one
///
/// # Panics
/// Panics if `at` is out of bounds for `BitVec`'s length (that is, if `at > BitVec::len()`)
///
/// # Examples
///```
/// use bit_vec::BitVec;
///
/// let mut b = BitVec::new();
///
/// b.push(true);
/// b.push(true);
/// b.insert(1, false);
///
/// assert!(b.eq_vec(&[true, false, true]));
///```
///
/// # Time complexity
/// Takes O([`len`]) time. All items after the insertion index must be
/// shifted to the right. In the worst case, all elements are shifted when
/// the insertion index is 0.
///
/// [`len`]: Self::len
pub fn insert(&mut self, at: usize, bit: bool) {
assert!(
at <= self.nbits,
"insertion index (is {at}) should be <= nbits (is {nbits})",
nbits = self.nbits
);
let last_block_bits = self.nbits % B::bits();
let block_at = at / B::bits(); // needed block
let bit_at = at % B::bits(); // index within the block
if last_block_bits == 0 {
self.storage.push(B::zero());
}
self.nbits += 1;
let mut carry = self.storage[block_at] >> (B::bits() - 1);
let lsbits_mask = (B::one() << bit_at) - B::one();
let set_bit = if bit { B::one() } else { B::zero() } << bit_at;
self.storage[block_at] = (self.storage[block_at] & lsbits_mask)
| ((self.storage[block_at] & !lsbits_mask) << 1)
| set_bit;
for block_ref in &mut self.storage[block_at + 1..] {
let curr_carry = *block_ref >> (B::bits() - 1);
*block_ref = *block_ref << 1 | carry;
carry = curr_carry;
}
}
}
impl<B: BitBlock> Default for BitVec<B> {
@@ -1695,16 +1754,33 @@ impl<B: BitBlock> Ord for BitVec<B> {
}
}
impl<B: BitBlock> fmt::Debug for BitVec<B> {
impl<B: BitBlock> fmt::Display for BitVec<B> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.ensure_invariant();
for bit in self {
write!(fmt, "{}", if bit { 1 } else { 0 })?;
fmt.write_char(if bit { '1' } else { '0' })?;
}
Ok(())
}
}
impl<B: BitBlock> fmt::Debug for BitVec<B> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.ensure_invariant();
let mut storage = String::with_capacity(self.len() + self.len() / B::bits());
for (i, bit) in self.iter().enumerate() {
if i != 0 && i % B::bits() == 0 {
storage.push(' ');
}
storage.push(if bit { '1' } else { '0' });
}
fmt.debug_struct("BitVec")
.field("storage", &storage)
.field("nbits", &self.nbits)
.finish()
}
}
impl<B: BitBlock> hash::Hash for BitVec<B> {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
@@ -1925,12 +2001,37 @@ mod tests {
const U32_BITS: usize = 32;
#[test]
fn test_to_str() {
let zerolen = BitVec::new();
assert_eq!(format!("{:?}", zerolen), "");
fn test_display_output() {
assert_eq!(format!("{}", BitVec::new()), "");
assert_eq!(format!("{}", BitVec::from_elem(1, true)), "1");
assert_eq!(format!("{}", BitVec::from_elem(8, false)), "00000000")
}
let eightbits = BitVec::from_elem(8, false);
assert_eq!(format!("{:?}", eightbits), "00000000")
#[test]
fn test_debug_output() {
assert_eq!(
format!("{:?}", BitVec::new()),
"BitVec { storage: \"\", nbits: 0 }"
);
assert_eq!(
format!("{:?}", BitVec::from_elem(1, true)),
"BitVec { storage: \"1\", nbits: 1 }"
);
assert_eq!(
format!("{:?}", BitVec::from_elem(8, false)),
"BitVec { storage: \"00000000\", nbits: 8 }"
);
assert_eq!(
format!("{:?}", BitVec::from_elem(33, true)),
"BitVec { storage: \"11111111111111111111111111111111 1\", nbits: 33 }"
);
assert_eq!(
format!(
"{:?}",
BitVec::from_bytes(&[0b111, 0b000, 0b1110, 0b0001, 0b11111111, 0b00000000])
),
"BitVec { storage: \"00000111000000000000111000000001 1111111100000000\", nbits: 48 }"
)
}
#[test]
@@ -1956,7 +2057,7 @@ mod tests {
let mut b = BitVec::from_elem(2, false);
b.set(0, true);
b.set(1, false);
assert_eq!(format!("{:?}", b), "10");
assert_eq!(format!("{}", b), "10");
assert!(!b.none() && !b.all());
}
@@ -2306,7 +2407,7 @@ mod tests {
fn test_from_bytes() {
let bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111]);
let str = concat!("10110110", "00000000", "11111111");
assert_eq!(format!("{:?}", bit_vec), str);
assert_eq!(format!("{}", bit_vec), str);
}
#[test]
@@ -2325,7 +2426,7 @@ mod tests {
fn test_from_bools() {
let bools = [true, false, true, true];
let bit_vec: BitVec = bools.iter().copied().collect();
assert_eq!(format!("{:?}", bit_vec), "1011");
assert_eq!(format!("{}", bit_vec), "1011");
}
#[test]
@@ -3007,4 +3108,79 @@ mod tests {
});
assert!(a.eq_vec(&[false, true, false, true, false, true, false, true]));
}
#[test]
fn test_insert_at_zero() {
let mut v = BitVec::new();
v.insert(0, false);
v.insert(0, true);
v.insert(0, false);
v.insert(0, true);
v.insert(0, false);
v.insert(0, true);
assert_eq!(v.len(), 6);
assert_eq!(v.storage().len(), 1);
assert!(v.eq_vec(&[true, false, true, false, true, false]));
}
#[test]
fn test_insert_at_end() {
let mut v = BitVec::new();
v.insert(v.len(), true);
v.insert(v.len(), false);
v.insert(v.len(), true);
v.insert(v.len(), false);
v.insert(v.len(), true);
v.insert(v.len(), false);
assert_eq!(v.storage().len(), 1);
assert_eq!(v.len(), 6);
assert!(v.eq_vec(&[true, false, true, false, true, false]));
}
#[test]
fn test_insert_at_block_boundaries() {
let mut v = BitVec::from_elem(32, false);
assert_eq!(v.storage().len(), 1);
v.insert(31, true);
assert_eq!(v.len(), 33);
assert!(matches!(v.get(31), Some(true)));
assert!(v.eq_vec(&[
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, true, false
]));
assert_eq!(v.storage().len(), 2);
}
#[test]
fn test_insert_at_block_boundaries_1() {
let mut v = BitVec::from_elem(64, false);
assert_eq!(v.storage().len(), 2);
v.insert(63, true);
assert_eq!(v.len(), 65);
assert!(matches!(v.get(63), Some(true)));
assert!(v.eq_vec(&[
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, true, false
]));
assert_eq!(v.storage().len(), 3);
}
}

View File

@@ -1 +1 @@
{"files":{"CHANGELOG.md":"45fa76b0e5bc51721887147000e9e78a5934cb04d1ad628e501ef2082763d353","Cargo.toml":"9938addd7ce2c7785a9ca11eb0049271317f9b05fdf0d7330d4a80f0e07ab500","README.md":"76cee3209f773a62535de6c9724b53f158406359f35b4d48b17ac3747b6c102e","src/com.rs":"cfd6556a7abf38cba57559038f9f2cf86274418448fb2745436c251a99575e05","src/command_allocator.rs":"ef01059a661749470f3772d188fe0fab0f002e1d154facdab4b9b2932f4b2d93","src/command_list.rs":"8723f3b755b721e0dbb234bd604956c1b7922a2368231197495daa3fa6548e63","src/debug.rs":"aa33b98f7c3e71cba75fc42c6ca9af72d96b45122422c16e48525e24590c57bf","src/descriptor.rs":"fea0b820de1566b54d17d8d0c67e6f5a2126eda19526397eb710ff7d6db9db9e","src/device.rs":"c1dd479aabd22bced0d407523d60629ad1da439fb47ad89fe7b48bae1c4b23e5","src/dxgi.rs":"1516186845b91bf3df813a29b4a0e00a85ca5649fb7a2755da43fba984c41a42","src/heap.rs":"dae2380684896c97e97ed022929f79ce2cc4f5418a3ec34883086f7c88f423d0","src/lib.rs":"612e2f471b84502d219da3fb86ee13f3cbd6faf17d77407bab6c84e51ec424d0","src/pso.rs":"ff819c321536695e34a3be9a6051cf3e57765049a4a2035db6ab27add5a7978a","src/query.rs":"b046b922f48e817fe252d9b2f859c036f54635779e84103ca53d1b2ca9c18e02","src/queue.rs":"bd32813d0b8a3bedf3223b69ade9f9c799a138a9e27d970f86435d9ce32d1557","src/resource.rs":"931c255c845eb621fc1b9e807b0effd92a2cd20e624c2beaa88506019a7a43a4","src/sync.rs":"5c287fb7498242a397eb1f08887be9cff9b48dc7cb13af5792cce5f7182b55f8"},"package":null}
{"files":{"CHANGELOG.md":"45fa76b0e5bc51721887147000e9e78a5934cb04d1ad628e501ef2082763d353","Cargo.toml":"18e881193dc6cc4c61d3fe0c2938867da2801f93e650d2c7ee12d9d465edd909","README.md":"76cee3209f773a62535de6c9724b53f158406359f35b4d48b17ac3747b6c102e","src/com.rs":"cfd6556a7abf38cba57559038f9f2cf86274418448fb2745436c251a99575e05","src/command_allocator.rs":"ef01059a661749470f3772d188fe0fab0f002e1d154facdab4b9b2932f4b2d93","src/command_list.rs":"8723f3b755b721e0dbb234bd604956c1b7922a2368231197495daa3fa6548e63","src/debug.rs":"aa33b98f7c3e71cba75fc42c6ca9af72d96b45122422c16e48525e24590c57bf","src/descriptor.rs":"fea0b820de1566b54d17d8d0c67e6f5a2126eda19526397eb710ff7d6db9db9e","src/device.rs":"c1dd479aabd22bced0d407523d60629ad1da439fb47ad89fe7b48bae1c4b23e5","src/dxgi.rs":"1516186845b91bf3df813a29b4a0e00a85ca5649fb7a2755da43fba984c41a42","src/heap.rs":"dae2380684896c97e97ed022929f79ce2cc4f5418a3ec34883086f7c88f423d0","src/lib.rs":"612e2f471b84502d219da3fb86ee13f3cbd6faf17d77407bab6c84e51ec424d0","src/pso.rs":"ff819c321536695e34a3be9a6051cf3e57765049a4a2035db6ab27add5a7978a","src/query.rs":"b046b922f48e817fe252d9b2f859c036f54635779e84103ca53d1b2ca9c18e02","src/queue.rs":"bd32813d0b8a3bedf3223b69ade9f9c799a138a9e27d970f86435d9ce32d1557","src/resource.rs":"931c255c845eb621fc1b9e807b0effd92a2cd20e624c2beaa88506019a7a43a4","src/sync.rs":"5c287fb7498242a397eb1f08887be9cff9b48dc7cb13af5792cce5f7182b55f8"},"package":null}

View File

@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "d3d12"
version = "0.20.0"
version = "22.0.0"
authors = ["gfx-rs developers"]
description = "Low level D3D12 API wrapper"
documentation = "https://docs.rs/d3d12"

View File

@@ -1 +1 @@
{"files":{"Cargo.lock":"7dffc5e93a0c3ff20a743e47d81da0baea4436153d5700be93805f3e0e982deb","Cargo.toml":"0b3005bccdea36f58aa260d17393d97fcdc2d80b86d39af2b7dd6828777524af","LICENSE-APACHE":"0178e21322b0e88aa3aeb3146f6a9611bc1f8df6d98bdfb34be28b9dd56a8107","LICENSE-MIT":"ad41be6cc6538b29b9346648f41432b5e460bad6be073b5eeaa41320ea2921dc","README.md":"0830d3914152fa9afa9ffe48651cbbbb31d34fe330785dee98ef747078a92079","examples/d3d12-buffer-winrs.rs":"c2c66ace6e41d302f244b95dbe38f2272ac59357a1c717a767dac3df89ea4718","examples/d3d12-buffer.rs":"abd9acc233a45c44f421dcaa4109f5a408de5335979654b1acbb55c73197ed5b","examples/metal-buffer.rs":"0fbd65a5a8381013199ff98f76c3830b96eb5e46591d4043f54614d582f26523","examples/vulkan-buffer.rs":"8097b361074302cf294d09a309e0cb8bfa8fbc586096f77a1cca9f7ba1f735dc","src/allocator/dedicated_block_allocator/mod.rs":"184ab11fcb52d3438586d10909277b54f1dbec9aeafb29ea338fd4df0e025e76","src/allocator/dedicated_block_allocator/visualizer.rs":"5b9019dd73ebe7bb9e9d103c48368014b73cdd4ae7f36a706ae047919f56fac6","src/allocator/free_list_allocator/mod.rs":"cab1d7e451c08978e67163ad45006cf8fdf2bdbb40e205c04bfec17490ca605f","src/allocator/free_list_allocator/visualizer.rs":"46214d07285d72a0a29c8d7e76322243853eba7d25d87ebfbb17c75e7815d07f","src/allocator/mod.rs":"09a560d3f4b0ba174bceea6d71bc201afcbea2b8170430c21365cbea8ca315f9","src/d3d12/mod.rs":"8fddd0755bd804cfcedf89c9a0a1f9686b6f0f728ff094cf3cb42a2ba12dc3d6","src/d3d12/visualizer.rs":"955c587a0d676e7e67fe02a3c3d4a59e8857e323c316ab7c352ef8d1a247193d","src/lib.rs":"4e19cd56ceaeb38f3fcaa71619c0fb180306cd79b25db583a05c8954b766c3da","src/metal/mod.rs":"5d156324aaaa054745054ae612056da5e854f18893756d86563b0f05ecd98bb2","src/result.rs":"6c7d85ee13afbd0b17c1b81ed0b6d7094247dd693444b62c28daf4d9f2248846","src/visualizer/allocation_reports.rs":"8e848f56503ee9b1a0b744804ac68a415dd5d761b20b0be0b15f9d4a5299607b","src/visualizer/memory_chunks.rs":"7d07c01f1471d25ff5702b53f5ccda09d6135888f6a77d1eaf06c541b4cafd5e","src/visualizer/mod.rs":"7d56c956abba968400aa6794e399db4b7ec10135a948beef21ea13ba3bd1fd9e","src/vulkan/mod.rs":"e14cfa043c938d9da3171bf9e9cbe7e4c0132e02c5c20af2726307ad40179491","src/vulkan/visualizer.rs":"7d6c113c70fa36f1a85a989d7020bd8e9814584f11c43bc151606be643a07c6c"},"package":"fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7"}
{"files":{"Cargo.lock":"3ca79349a9d9a4636c763d35a0caad53c655a13ff0612020ae8ec8fe3b42b4ff","Cargo.toml":"4d6d3bb1f8fe80fe67f31bf41d3469cc311e14daa295f42d0e7944b287b9f8d3","LICENSE-APACHE":"0178e21322b0e88aa3aeb3146f6a9611bc1f8df6d98bdfb34be28b9dd56a8107","LICENSE-MIT":"ad41be6cc6538b29b9346648f41432b5e460bad6be073b5eeaa41320ea2921dc","README.md":"d51d23364f3944098ca4b6118d46b5b353c2bfcd0cce0b3ade317537bdf69143","examples/d3d12-buffer-winrs.rs":"e7bb5565a26c1608ed57bcf5895e7e45bed5af85040f5a572049e6b74c99631c","examples/d3d12-buffer.rs":"c84cfdeae3a347fe561529b60963daaccc10880a9aeb64ae992689c30e16ea11","examples/metal-buffer.rs":"0fbd65a5a8381013199ff98f76c3830b96eb5e46591d4043f54614d582f26523","examples/vulkan-buffer.rs":"49f57f1f4542126047e217c81083b08ed798637fd90d6b7560bc9fab21732953","src/allocator/dedicated_block_allocator/mod.rs":"ec52728fb0c9d40173472640f8005ee7eca450170b7c3113adfd2e887e387f29","src/allocator/dedicated_block_allocator/visualizer.rs":"5b9019dd73ebe7bb9e9d103c48368014b73cdd4ae7f36a706ae047919f56fac6","src/allocator/free_list_allocator/mod.rs":"afe3417f40cdf71ec99c5885066d715e56d8d6c19662b17846cc8ce54222b1e2","src/allocator/free_list_allocator/visualizer.rs":"46214d07285d72a0a29c8d7e76322243853eba7d25d87ebfbb17c75e7815d07f","src/allocator/mod.rs":"86a95a58a30ec59aa7a9bc5822deca4c2a82c737e5633008400c2b291683a024","src/d3d12/mod.rs":"3cb8289c2effe1430c70cbb87b705fcaca7915d36b441f7d8a9a591bca18be25","src/d3d12/visualizer.rs":"41d1b5b897ff7b0a3fda359cee3f6b5c921617a4e1fefe7ddd2341bb1da87691","src/lib.rs":"36c8bf74f77da2113651e4e13d2bd9dbb7f6c854c089573461aaea8378c4d02c","src/metal/mod.rs":"f55592a96135da25785df21d8057fe5c1843b38f1a99d817cadba17d9d036ab7","src/result.rs":"6c7d85ee13afbd0b17c1b81ed0b6d7094247dd693444b62c28daf4d9f2248846","src/visualizer/allocation_reports.rs":"441a85fd68a8903fd9e1413756730e3e5cf9aa61803983e61f7cbca27ee39071","src/visualizer/memory_chunks.rs":"f521a4ce056d610d095c7fd65b110b8c046e84850746ec38b4d66f27b0ec70ae","src/visualizer/mod.rs":"7d56c956abba968400aa6794e399db4b7ec10135a948beef21ea13ba3bd1fd9e","src/vulkan/mod.rs":"e6dd3e67aea9f321e7c53bf87448a645bc83e3bbc92e6aef76223b18dd610fe5","src/vulkan/visualizer.rs":"6357703e89e3f5e9b78649eb16af907c107d0d121c23a4094dc0794a38fd4929"},"package":"c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"}

View File

@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "ab_glyph"
version = "0.2.25"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f90148830dac590fac7ccfe78ec4a8ea404c60f75a24e16407a71f0f40de775"
checksum = "79faae4620f45232f599d9bc7b290f88247a0834162c4495ab2f02d60004adfb"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
@@ -52,18 +52,18 @@ dependencies = [
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
version = "0.38.0+1.3.281"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
dependencies = [
"libloading",
]
[[package]]
name = "autocfg"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
@@ -73,9 +73,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block"
@@ -83,12 +83,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "cc"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -260,7 +254,7 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "gpu-allocator"
version = "0.26.0"
version = "0.27.0"
dependencies = [
"ash",
"egui",
@@ -299,18 +293,18 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.154"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
version = "0.7.4"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
"winapi",
"windows-targets",
]
[[package]]
@@ -325,9 +319,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "malloc_buf"
@@ -340,17 +334,17 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.2"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "metal"
version = "0.27.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"block",
"core-graphics-types",
"dispatch",
@@ -373,16 +367,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
"objc_exception",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
dependencies = [
"cc",
]
[[package]]
@@ -393,18 +377,18 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "owned_ttf_parser"
version = "0.20.0"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
checksum = "490d3a563d3122bf7c911a59b0add9389e5ec0f5f0c3ac6b91ff235a0e6a7f90"
dependencies = [
"ttf-parser",
]
[[package]]
name = "parking_lot"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -425,9 +409,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "presser"
@@ -437,9 +421,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "proc-macro2"
version = "1.0.81"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@@ -455,18 +439,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.1"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
@@ -476,9 +460,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
@@ -487,9 +471,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "scopeguard"
@@ -499,18 +483,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.199"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.199"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
@@ -525,9 +509,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.60"
version = "2.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
dependencies = [
"proc-macro2",
"quote",
@@ -545,18 +529,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.59"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.59"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
@@ -565,9 +549,9 @@ dependencies = [
[[package]]
name = "ttf-parser"
version = "0.20.0"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
checksum = "8686b91785aff82828ed725225925b33b4fde44c4bb15876e5f7c832724c420a"
[[package]]
name = "unicode-ident"
@@ -614,9 +598,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.52.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core",
"windows-targets",
@@ -624,10 +608,55 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]
@@ -642,9 +671,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -658,66 +687,66 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -11,9 +11,9 @@
[package]
edition = "2021"
rust-version = "1.65"
rust-version = "1.70"
name = "gpu-allocator"
version = "0.26.0"
version = "0.27.0"
authors = ["Traverse Research <opensource@traverseresearch.nl>"]
include = [
"/README.md",
@@ -63,7 +63,7 @@ name = "metal-buffer"
required-features = ["metal"]
[dependencies.ash]
version = ">=0.34, <=0.37"
version = "0.38"
features = ["debug"]
optional = true
default-features = false
@@ -88,7 +88,7 @@ version = "0.3"
version = "1.0"
[dev-dependencies.ash]
version = ">=0.34,<=0.37"
version = "0.38"
features = [
"debug",
"loaded",
@@ -113,7 +113,7 @@ visualizer = [
vulkan = ["dep:ash"]
[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.metal]
version = "0.27.0"
version = "0.29.0"
features = [
"link",
"dispatch",
@@ -132,13 +132,9 @@ features = [
optional = true
[target."cfg(windows)".dependencies.windows]
version = ">=0.51,<=0.52"
version = ">=0.53,<=0.58"
features = [
"Win32_Foundation",
"Win32_Graphics",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D12",
"Win32_Graphics_Dxgi",
"Win32_Graphics_Dxgi_Common",
]
optional = true
@@ -158,12 +154,9 @@ features = [
]
[target."cfg(windows)".dev-dependencies.windows]
version = ">=0.51,<=0.52"
version = "0.58"
features = [
"Win32_Foundation",
"Win32_Graphics",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D12",
"Win32_Graphics_Dxgi",
"Win32_Graphics_Dxgi_Common",
]

View File

@@ -6,13 +6,13 @@
[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT)
[![LICENSE](https://img.shields.io/badge/license-apache-blue.svg?logo=apache)](LICENSE-APACHE)
[![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../main/CODE_OF_CONDUCT.md)
[![MSRV](https://img.shields.io/badge/rustc-1.65.0+-ab6000.svg)](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)
[![MSRV](https://img.shields.io/badge/rustc-1.70.0+-ab6000.svg)](https://blog.rust-lang.org/2023/06/01/Rust-1.70.0.html)
[![Banner](banner.png)](https://traverseresearch.nl)
```toml
[dependencies]
gpu-allocator = "0.26.0"
gpu-allocator = "0.27.0"
```
![Visualizer](visualizer.png)
@@ -48,7 +48,7 @@ use gpu_allocator::vulkan::*;
use gpu_allocator::MemoryLocation;
// Setup vulkan info
let vk_info = vk::BufferCreateInfo::builder()
let vk_info = vk::BufferCreateInfo::default()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER);
@@ -163,7 +163,7 @@ allocator.free(&allocation).unwrap();
## Minimum Supported Rust Version
The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust 1.65. Any other features such as the `visualizer` (with all the `egui` dependencies) may have a higher requirement and are not tested in our CI.
The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust 1.70. Any other features such as the `visualizer` (with all the `egui` dependencies) may have a higher requirement and are not tested in our CI.
## License

View File

@@ -1,24 +1,28 @@
//! Example showcasing [`gpu-allocator`] with types and functions from the [`windows`] crate.
use gpu_allocator::d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ID3D12DeviceVersion, ResourceCategory,
use gpu_allocator::{
d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ID3D12DeviceVersion, ResourceCategory,
},
MemoryLocation,
};
use gpu_allocator::MemoryLocation;
use log::*;
use windows::core::{ComInterface, Result};
use windows::Win32::{
Foundation::E_NOINTERFACE,
Graphics::{
Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0},
Direct3D12::{
D3D12CreateDevice, ID3D12Device, ID3D12Resource,
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON,
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
},
Dxgi::{
Common::{DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC},
CreateDXGIFactory2, IDXGIAdapter4, IDXGIFactory6, DXGI_ADAPTER_FLAG3_SOFTWARE,
DXGI_ERROR_NOT_FOUND,
use windows::{
core::{Interface, Result},
Win32::{
Foundation::E_NOINTERFACE,
Graphics::{
Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0},
Direct3D12::{
D3D12CreateDevice, ID3D12Device, ID3D12Resource,
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_FLAG_NONE,
D3D12_RESOURCE_STATE_COMMON, D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
},
Dxgi::{
Common::{DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC},
CreateDXGIFactory2, IDXGIAdapter4, IDXGIFactory6, DXGI_ADAPTER_FLAG3_SOFTWARE,
DXGI_ERROR_NOT_FOUND,
},
},
},
};
@@ -33,8 +37,7 @@ fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option<ID3D12Device> {
};
let adapter4: IDXGIAdapter4 = adapter1.cast().unwrap();
let mut desc = Default::default();
unsafe { adapter4.GetDesc3(&mut desc) }.unwrap();
let desc = unsafe { adapter4.GetDesc3() }.unwrap();
// Skip software adapters
// Vote for https://github.com/microsoft/windows-rs/issues/793!
if (desc.Flags & DXGI_ADAPTER_FLAG3_SOFTWARE) == DXGI_ADAPTER_FLAG3_SOFTWARE {
@@ -82,7 +85,9 @@ fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option<ID3D12Device> {
fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let dxgi_factory = unsafe { CreateDXGIFactory2(0) }?;
let dxgi_factory = unsafe {
CreateDXGIFactory2(windows::Win32::Graphics::Dxgi::DXGI_CREATE_FACTORY_FLAGS::default())
}?;
let device = create_d3d12_device(&dxgi_factory).expect("Failed to create D3D12 device.");

View File

@@ -1,19 +1,22 @@
//! Example showcasing [`winapi`] interop with [`gpu-allocator`] which is driven by the [`windows`] crate.
use winapi::shared::{dxgiformat, winerror};
use winapi::um::{d3d12, d3dcommon};
use winapi::Interface;
use winapi::{
shared::{dxgiformat, winerror},
um::{d3d12, d3dcommon},
Interface,
};
mod all_dxgi {
pub use winapi::shared::{dxgi1_3::*, dxgi1_6::*, dxgitype::*};
}
use log::*;
use gpu_allocator::d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ID3D12DeviceVersion, ResourceCategory,
ToWinapi, ToWindows,
use gpu_allocator::{
d3d12::{
AllocationCreateDesc, Allocator, AllocatorCreateDesc, ID3D12DeviceVersion,
ResourceCategory, ToWinapi, ToWindows,
},
MemoryLocation,
};
use gpu_allocator::MemoryLocation;
use log::*;
fn create_d3d12_device(
dxgi_factory: *mut all_dxgi::IDXGIFactory6,
@@ -68,11 +71,11 @@ fn create_d3d12_device(
)
};
match hr {
winapi::shared::winerror::S_OK => {
winerror::S_OK => {
info!("Using D3D12 feature level: {}.", feature_level_name);
Some(device)
}
winapi::shared::winerror::E_NOINTERFACE => {
winerror::E_NOINTERFACE => {
error!("ID3D12Device interface not supported.");
None
}
@@ -106,11 +109,7 @@ fn main() {
)
};
assert_eq!(
hr,
winapi::shared::winerror::S_OK,
"Failed to create DXGI factory",
);
assert_eq!(hr, winerror::S_OK, "Failed to create DXGI factory");
dxgi_factory
};

View File

@@ -1,13 +1,11 @@
use std::default::Default;
use std::ffi::CStr;
use ash::vk;
use log::info;
use gpu_allocator::vulkan::{
AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
use gpu_allocator::{
vulkan::{AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc},
MemoryLocation,
};
use gpu_allocator::MemoryLocation;
use log::info;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
@@ -16,20 +14,18 @@ fn main() {
// Create Vulkan instance
let instance = {
let app_name = CStr::from_bytes_with_nul(b"Vulkan gpu-allocator test\0").unwrap();
let app_name = c"Vulkan gpu-allocator test";
let appinfo = vk::ApplicationInfo::builder()
let appinfo = vk::ApplicationInfo::default()
.application_name(app_name)
.application_version(0)
.engine_name(app_name)
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 0, 0));
let layer_names_raw = [CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0")
.unwrap()
.as_ptr()];
let layer_names_raw = [c"VK_LAYER_KHRONOS_validation".as_ptr()];
let create_info = vk::InstanceCreateInfo::builder()
let create_info = vk::InstanceCreateInfo::default()
.application_info(&appinfo)
.enabled_layer_names(&layer_names_raw);
@@ -74,11 +70,11 @@ fn main() {
};
let priorities = [1.0];
let queue_info = vk::DeviceQueueCreateInfo::builder()
let queue_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family_index as u32)
.queue_priorities(&priorities);
let create_info = vk::DeviceCreateInfo::builder()
let create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(std::slice::from_ref(&queue_info))
.enabled_extension_names(&device_extension_names_raw)
.enabled_features(&features);
@@ -99,7 +95,7 @@ fn main() {
// Test allocating Gpu Only memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
let test_buffer_info = vk::BufferCreateInfo::default()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
@@ -132,7 +128,7 @@ fn main() {
// Test allocating Cpu to Gpu memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
let test_buffer_info = vk::BufferCreateInfo::default()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
@@ -165,7 +161,7 @@ fn main() {
// Test allocating Gpu to Cpu memory
{
let test_buffer_info = vk::BufferCreateInfo::builder()
let test_buffer_info = vk::BufferCreateInfo::default()
.size(512)
.usage(vk::BufferUsageFlags::STORAGE_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);

View File

@@ -116,16 +116,13 @@ impl SubAllocator for DedicatedBlockAllocator {
.name
.clone()
.unwrap_or_else(|| "<Unnamed Dedicated allocation>".to_owned()),
offset: 0,
size: self.size,
#[cfg(feature = "visualizer")]
backtrace: self.backtrace.clone(),
}]
}
fn size(&self) -> u64 {
self.size
}
fn allocated(&self) -> u64 {
self.allocated
}

View File

@@ -398,6 +398,7 @@ impl SubAllocator for FreeListAllocator {
.name
.clone()
.unwrap_or_else(|| "<Unnamed FreeList allocation>".to_owned()),
offset: chunk.offset,
size: chunk.size,
#[cfg(feature = "visualizer")]
backtrace: chunk.backtrace.clone(),
@@ -405,10 +406,6 @@ impl SubAllocator for FreeListAllocator {
.collect::<Vec<_>>()
}
fn size(&self) -> u64 {
self.size
}
fn allocated(&self) -> u64 {
self.allocated
}

View File

@@ -1,4 +1,4 @@
use std::{backtrace::Backtrace, sync::Arc};
use std::{backtrace::Backtrace, fmt, ops::Range, sync::Arc};
use log::*;
@@ -29,20 +29,83 @@ impl AllocationType {
}
}
/// Describes an allocation in the [`AllocatorReport`].
#[derive(Clone)]
pub(crate) struct AllocationReport {
pub(crate) name: String,
pub(crate) size: u64,
pub struct AllocationReport {
/// The name provided to the `allocate()` function.
pub name: String,
/// The offset in bytes of the allocation in its memory block.
pub offset: u64,
/// The size in bytes of the allocation.
pub size: u64,
#[cfg(feature = "visualizer")]
pub(crate) backtrace: Arc<Backtrace>,
}
/// Describes a memory block in the [`AllocatorReport`].
#[derive(Clone)]
pub struct MemoryBlockReport {
/// The size in bytes of this memory block.
pub size: u64,
/// The range of allocations in [`AllocatorReport::allocations`] that are associated
/// to this memory block.
pub allocations: Range<usize>,
}
/// A report that can be generated for informational purposes using `Allocator::generate_report()`.
#[derive(Clone)]
pub struct AllocatorReport {
/// All live allocations, sub-allocated from memory blocks.
pub allocations: Vec<AllocationReport>,
/// All memory blocks.
pub blocks: Vec<MemoryBlockReport>,
/// Sum of the memory used by all allocations, in bytes.
pub total_allocated_bytes: u64,
/// Sum of the memory reserved by all memory blocks including unallocated regions, in bytes.
pub total_reserved_bytes: u64,
}
impl fmt::Debug for AllocationReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = if !self.name.is_empty() {
self.name.as_str()
} else {
"--"
};
write!(f, "{name:?}: {}", fmt_bytes(self.size))
}
}
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));
let max_num_allocations_to_print = f.precision().unwrap_or(usize::MAX);
allocations.truncate(max_num_allocations_to_print);
f.debug_struct("AllocatorReport")
.field(
"summary",
&std::format_args!(
"{} / {}",
fmt_bytes(self.total_allocated_bytes),
fmt_bytes(self.total_reserved_bytes)
),
)
.field("blocks", &self.blocks.len())
.field("allocations", &self.allocations.len())
.field("largest", &allocations.as_slice())
.finish()
}
}
#[cfg(feature = "visualizer")]
pub(crate) trait SubAllocatorBase: crate::visualizer::SubAllocatorVisualizer {}
#[cfg(not(feature = "visualizer"))]
pub(crate) trait SubAllocatorBase {}
pub(crate) trait SubAllocator: SubAllocatorBase + std::fmt::Debug + Sync + Send {
pub(crate) trait SubAllocator: SubAllocatorBase + fmt::Debug + Sync + Send {
fn allocate(
&mut self,
size: u64,
@@ -73,16 +136,8 @@ pub(crate) trait SubAllocator: SubAllocatorBase + std::fmt::Debug + Sync + Send
#[must_use]
fn supports_general_allocations(&self) -> bool;
#[must_use]
fn size(&self) -> u64;
#[must_use]
fn allocated(&self) -> u64;
/// Helper function: reports how much memory is available in this suballocator
#[must_use]
fn available_memory(&self) -> u64 {
self.size() - self.allocated()
}
/// Helper function: reports if the suballocator is empty (meaning, having no allocations).
#[must_use]
fn is_empty(&self) -> bool {
@@ -90,8 +145,6 @@ pub(crate) trait SubAllocator: SubAllocatorBase + std::fmt::Debug + Sync + Send
}
}
pub(crate) const VISUALIZER_TABLE_MAX_ENTRY_NAME_LEN: usize = 40;
pub(crate) fn fmt_bytes(mut amount: u64) -> String {
const SUFFIX: [&str; 5] = ["B", "KB", "MB", "GB", "TB"];

View File

@@ -1,9 +1,6 @@
#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)]
use std::{backtrace::Backtrace, fmt, sync::Arc};
use log::{debug, warn, Level};
use windows::Win32::{
Foundation::E_OUTOFMEMORY,
Graphics::{Direct3D12::*, Dxgi::Common::DXGI_FORMAT},
@@ -11,9 +8,10 @@ use windows::Win32::{
#[cfg(feature = "public-winapi")]
mod public_winapi {
use super::*;
pub use winapi::um::d3d12 as winapi_d3d12;
use super::*;
/// Trait similar to [`AsRef`]/[`AsMut`],
pub trait ToWinapi<T> {
fn as_winapi(&self) -> *const T;
@@ -84,12 +82,10 @@ mod visualizer;
#[cfg(feature = "visualizer")]
pub use visualizer::AllocatorVisualizer;
use super::allocator;
use super::allocator::AllocationType;
use super::{allocator, allocator::AllocationType};
use crate::{
allocator::fmt_bytes, AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation,
Result,
allocator::{AllocatorReport, MemoryBlockReport},
AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, Result,
};
/// [`ResourceCategory`] is used for supporting [`D3D12_RESOURCE_HEAP_TIER_1`].
@@ -197,10 +193,12 @@ impl<'a> AllocationCreateDesc<'a> {
desc: &winapi_d3d12::D3D12_RESOURCE_DESC,
name: &'a str,
location: MemoryLocation,
) -> AllocationCreateDesc<'a> {
) -> Self {
let device = device.as_windows();
// Raw structs are binary-compatible
let desc = unsafe { std::mem::transmute(desc) };
let desc = unsafe {
std::mem::transmute::<&winapi_d3d12::D3D12_RESOURCE_DESC, &D3D12_RESOURCE_DESC>(desc)
};
let allocation_info =
unsafe { device.GetResourceAllocationInfo(0, std::slice::from_ref(desc)) };
let resource_category: ResourceCategory = desc.into();
@@ -223,7 +221,7 @@ impl<'a> AllocationCreateDesc<'a> {
desc: &D3D12_RESOURCE_DESC,
name: &'a str,
location: MemoryLocation,
) -> AllocationCreateDesc<'a> {
) -> Self {
let allocation_info =
unsafe { device.GetResourceAllocationInfo(0, std::slice::from_ref(desc)) };
let resource_category: ResourceCategory = desc.into();
@@ -256,9 +254,8 @@ impl std::ops::Deref for ID3D12DeviceVersion {
fn deref(&self) -> &Self::Target {
match self {
Self::Device(device) => device,
// Windows-rs hides CanInto, we know that Device10/Device12 is a subclass of Device but there's not even a Deref.
Self::Device10(device10) => windows::core::CanInto::can_into(device10),
Self::Device12(device12) => windows::core::CanInto::can_into(device12),
Self::Device10(device10) => device10.into(),
Self::Device12(device12) => device12.into(),
}
}
}
@@ -271,12 +268,16 @@ pub struct AllocatorCreateDesc {
}
pub enum ResourceType<'a> {
/// Allocation equivalent to Dx12's CommittedResource.
/// Create a D3D12 [`CommittedResource`].
///
/// [`CommittedResource`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommittedresource
Committed {
heap_properties: &'a D3D12_HEAP_PROPERTIES,
heap_flags: D3D12_HEAP_FLAGS,
},
/// Allocation equivalent to Dx12's PlacedResource.
/// Create a D3D12 [`PlacedResource`].
///
/// [`PlacedResource`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource
Placed,
}
@@ -1099,50 +1100,38 @@ impl Allocator {
Ok(())
}
}
pub fn generate_report(&self) -> AllocatorReport {
let mut allocations = vec![];
let mut blocks = vec![];
let mut total_reserved_bytes = 0;
for memory_type in &self.memory_types {
for block in memory_type.memory_blocks.iter().flatten() {
total_reserved_bytes += block.size;
let first_allocation = allocations.len();
allocations.extend(block.sub_allocator.report_allocations());
blocks.push(MemoryBlockReport {
size: block.size,
allocations: first_allocation..allocations.len(),
});
}
}
let total_allocated_bytes = allocations.iter().map(|report| report.size).sum();
AllocatorReport {
allocations,
blocks,
total_allocated_bytes,
total_reserved_bytes,
}
}
}
impl fmt::Debug for Allocator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut allocation_report = vec![];
let mut total_reserved_size_in_bytes = 0;
for memory_type in &self.memory_types {
for block in memory_type.memory_blocks.iter().flatten() {
total_reserved_size_in_bytes += block.size;
allocation_report.extend(block.sub_allocator.report_allocations())
}
}
let total_used_size_in_bytes = allocation_report.iter().map(|report| report.size).sum();
allocation_report.sort_by_key(|alloc| std::cmp::Reverse(alloc.size));
writeln!(
f,
"================================================================",
)?;
writeln!(
f,
"ALLOCATION BREAKDOWN ({} / {})",
fmt_bytes(total_used_size_in_bytes),
fmt_bytes(total_reserved_size_in_bytes),
)?;
let max_num_allocations_to_print = f.precision().map_or(usize::MAX, |n| n);
for (idx, alloc) in allocation_report.iter().enumerate() {
if idx >= max_num_allocations_to_print {
break;
}
writeln!(
f,
"{:max_len$.max_len$}\t- {}",
alloc.name,
fmt_bytes(alloc.size),
max_len = allocator::VISUALIZER_TABLE_MAX_ENTRY_NAME_LEN,
)?;
}
Ok(())
self.generate_report().fmt(f)
}
}

View File

@@ -1,13 +1,13 @@
#![allow(clippy::new_without_default)]
use windows::Win32::Graphics::Direct3D12::*;
use super::Allocator;
use crate::visualizer::{
render_allocation_reports_ui, AllocationReportVisualizeSettings, ColorScheme,
MemoryChunksVisualizationSettings,
};
use windows::Win32::Graphics::Direct3D12::*;
struct AllocatorVisualizerBlockWindow {
memory_type_index: usize,
block_index: usize,
@@ -92,7 +92,7 @@ impl AllocatorVisualizer {
let mut total_allocated = 0;
for block in mem_type.memory_blocks.iter().flatten() {
total_block_size += block.sub_allocator.size();
total_block_size += block.size;
total_allocated += block.sub_allocator.allocated();
}
@@ -134,10 +134,7 @@ impl AllocatorVisualizer {
let Some(block) = block else { continue };
ui.collapsing(format!("Block: {}", block_idx), |ui| {
ui.label(format!(
"size: {} KiB",
block.sub_allocator.size() / 1024
));
ui.label(format!("size: {} KiB", block.size / 1024));
ui.label(format!(
"allocated: {} KiB",
block.sub_allocator.allocated() / 1024
@@ -147,7 +144,7 @@ impl AllocatorVisualizer {
if block.sub_allocator.supports_visualization()
&& ui.button("visualize").clicked()
&& !self.selected_blocks.iter().enumerate().any(|(_, x)| {
&& !self.selected_blocks.iter().any(|x| {
x.memory_type_index == mem_type_idx
&& x.block_index == block_idx
})
@@ -205,7 +202,7 @@ impl AllocatorVisualizer {
"Memory type {}, Memory block {}, Block size: {} KiB",
window.memory_type_index,
window.block_index,
memblock.sub_allocator.size() / 1024
memblock.size / 1024
));
window

View File

@@ -52,7 +52,7 @@
//! # }).unwrap();
//!
//! // Setup vulkan info
//! let vk_info = vk::BufferCreateInfo::builder()
//! let vk_info = vk::BufferCreateInfo::default()
//! .size(512)
//! .usage(vk::BufferUsageFlags::STORAGE_BUFFER);
//!
@@ -212,6 +212,8 @@ pub use result::*;
pub(crate) mod allocator;
pub use allocator::{AllocationReport, AllocatorReport, MemoryBlockReport};
#[cfg(feature = "visualizer")]
pub mod visualizer;

View File

@@ -1,10 +1,12 @@
#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)]
use std::{backtrace::Backtrace, sync::Arc};
use log::debug;
use crate::{
allocator, AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, Result,
allocator::{self, AllocatorReport, MemoryBlockReport},
AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, Result,
};
use log::{debug, Level};
fn memory_location_to_metal(location: MemoryLocation) -> metal::MTLResourceOptions {
match location {
@@ -15,6 +17,7 @@ fn memory_location_to_metal(location: MemoryLocation) -> metal::MTLResourceOptio
}
}
#[derive(Debug)]
pub struct Allocation {
chunk_id: Option<std::num::NonZeroU64>,
offset: u64,
@@ -69,6 +72,7 @@ impl Allocation {
}
}
#[derive(Clone, Debug)]
pub struct AllocationCreateDesc<'a> {
/// Name of the allocation, for tracking and debugging purposes
pub name: &'a str,
@@ -84,7 +88,7 @@ impl<'a> AllocationCreateDesc<'a> {
name: &'a str,
length: u64,
location: MemoryLocation,
) -> AllocationCreateDesc<'a> {
) -> Self {
let size_and_align =
device.heap_buffer_size_and_align(length, memory_location_to_metal(location));
Self {
@@ -95,11 +99,7 @@ impl<'a> AllocationCreateDesc<'a> {
}
}
pub fn texture(
device: &metal::Device,
name: &'a str,
desc: &metal::TextureDescriptor,
) -> AllocationCreateDesc<'a> {
pub fn texture(device: &metal::Device, name: &'a str, desc: &metal::TextureDescriptor) -> Self {
let size_and_align = device.heap_texture_size_and_align(desc);
Self {
name,
@@ -119,7 +119,7 @@ impl<'a> AllocationCreateDesc<'a> {
name: &'a str,
size: u64,
location: MemoryLocation,
) -> AllocationCreateDesc<'a> {
) -> Self {
let size_and_align = device.heap_acceleration_structure_size_and_align_with_size(size);
Self {
name,
@@ -129,24 +129,31 @@ impl<'a> AllocationCreateDesc<'a> {
}
}
}
pub struct Allocator {
device: Arc<metal::Device>,
debug_settings: AllocatorDebugSettings,
memory_types: Vec<MemoryType>,
allocation_sizes: AllocationSizes,
}
#[derive(Debug)]
pub struct AllocatorCreateDesc {
pub device: Arc<metal::Device>,
pub debug_settings: AllocatorDebugSettings,
pub allocation_sizes: AllocationSizes,
}
#[derive(Debug)]
pub struct CommittedAllocationStatistics {
pub num_allocations: usize,
pub total_size: u64,
}
#[derive(Debug)]
struct MemoryBlock {
heap: Arc<metal::Heap>,
_size: u64,
size: u64,
sub_allocator: Box<dyn allocator::SubAllocator>,
}
@@ -156,10 +163,12 @@ impl MemoryBlock {
size: u64,
heap_descriptor: &metal::HeapDescriptor,
dedicated: bool,
memory_location: MemoryLocation,
) -> Result<Self> {
heap_descriptor.set_size(size);
let heap = Arc::new(device.new_heap(heap_descriptor));
heap.set_label(&format!("MemoryBlock {memory_location:?}"));
let sub_allocator: Box<dyn allocator::SubAllocator> = if dedicated {
Box::new(allocator::DedicatedBlockAllocator::new(size))
@@ -169,12 +178,13 @@ impl MemoryBlock {
Ok(Self {
heap,
_size: size,
size,
sub_allocator,
})
}
}
#[derive(Debug)]
struct MemoryType {
memory_blocks: Vec<Option<MemoryBlock>>,
_committed_allocations: CommittedAllocationStatistics,
@@ -206,7 +216,13 @@ impl MemoryType {
// Create a dedicated block for large memory allocations
if size > memblock_size {
let mem_block = MemoryBlock::new(device, size, &self.heap_properties, true)?;
let mem_block = MemoryBlock::new(
device,
size,
&self.heap_properties,
true,
self.memory_location,
)?;
let block_index = self.memory_blocks.iter().position(|block| block.is_none());
let block_index = match block_index {
@@ -276,8 +292,13 @@ impl MemoryType {
}
}
let new_memory_block =
MemoryBlock::new(device, memblock_size, &self.heap_properties, false)?;
let new_memory_block = MemoryBlock::new(
device,
memblock_size,
&self.heap_properties,
false,
self.memory_location,
)?;
let new_block_index = if let Some(block_index) = empty_block_index {
self.memory_blocks[block_index] = Some(new_memory_block);
@@ -355,14 +376,7 @@ impl MemoryType {
}
}
pub struct ResourceCreateDesc {}
pub struct Resource {}
impl Allocator {
pub fn device(&self) -> &metal::Device {
todo!()
}
pub fn new(desc: &AllocatorCreateDesc) -> Result<Self> {
let heap_types = [
(MemoryLocation::GpuOnly, {
@@ -389,7 +403,7 @@ impl Allocator {
];
let memory_types = heap_types
.iter()
.into_iter()
.enumerate()
.map(|(i, (memory_location, heap_descriptor))| MemoryType {
memory_blocks: vec![],
@@ -397,8 +411,8 @@ impl Allocator {
num_allocations: 0,
total_size: 0,
},
memory_location: *memory_location,
heap_properties: heap_descriptor.clone(),
memory_location,
heap_properties: heap_descriptor,
memory_type_index: i,
active_general_blocks: 0,
})
@@ -479,10 +493,30 @@ impl Allocator {
heaps
}
pub fn rename_allocation(&mut self, _allocation: &mut Allocation, _name: &str) -> Result<()> {
todo!()
}
pub fn report_memory_leaks(&self, _log_level: Level) {
todo!()
pub fn generate_report(&self) -> AllocatorReport {
let mut allocations = vec![];
let mut blocks = vec![];
let mut total_reserved_bytes = 0;
for memory_type in &self.memory_types {
for block in memory_type.memory_blocks.iter().flatten() {
total_reserved_bytes += block.size;
let first_allocation = allocations.len();
allocations.extend(block.sub_allocator.report_allocations());
blocks.push(MemoryBlockReport {
size: block.size,
allocations: first_allocation..allocations.len(),
});
}
}
let total_allocated_bytes = allocations.iter().map(|report| report.size).sum();
AllocatorReport {
allocations,
blocks,
total_allocated_bytes,
total_reserved_bytes,
}
}
}

View File

@@ -115,6 +115,7 @@ pub(crate) fn render_allocation_reports_ui(
name,
size,
backtrace,
..
} = alloc;
row.col(|ui| {

View File

@@ -2,9 +2,8 @@ use std::backtrace::BacktraceStatus;
use egui::{Color32, DragValue, Rect, ScrollArea, Sense, Ui, Vec2};
use crate::allocator::free_list_allocator::MemoryChunk;
use super::ColorScheme;
use crate::allocator::free_list_allocator::MemoryChunk;
pub(crate) struct MemoryChunksVisualizationSettings {
pub width_in_bytes: u64,

View File

@@ -2,18 +2,17 @@
#[cfg(feature = "visualizer")]
mod visualizer;
#[cfg(feature = "visualizer")]
pub use visualizer::AllocatorVisualizer;
use std::{backtrace::Backtrace, fmt, marker::PhantomData, sync::Arc};
use ash::vk;
use log::{debug, Level};
#[cfg(feature = "visualizer")]
pub use visualizer::AllocatorVisualizer;
use super::allocator;
use crate::{
allocator::fmt_bytes, AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation,
Result,
allocator::{AllocatorReport, MemoryBlockReport},
AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, Result,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -58,7 +57,7 @@ unsafe impl Sync for SendSyncPtr {}
pub struct AllocatorCreateDesc {
pub instance: ash::Instance,
pub device: ash::Device,
pub physical_device: ash::vk::PhysicalDevice,
pub physical_device: vk::PhysicalDevice,
pub debug_settings: AllocatorDebugSettings,
pub buffer_device_address: bool,
pub allocation_sizes: AllocationSizes,
@@ -105,7 +104,7 @@ pub struct AllocatorCreateDesc {
/// let my_gpu_data: Vec<MyGpuData> = make_vertex_data();
/// ```
///
/// Depending on how the data we're copying will be used, the vulkan device may have a minimum
/// Depending on how the data we're copying will be used, the Vulkan device may have a minimum
/// alignment requirement for that data:
///
/// ```ignore
@@ -180,7 +179,7 @@ impl Allocation {
///
/// [`Slab`]: presser::Slab
// best to be explicit where the lifetime is coming from since we're doing unsafe things
// and relying on an inferred liftime type in the PhantomData below
// and relying on an inferred lifetime type in the PhantomData below
#[allow(clippy::needless_lifetimes)]
pub fn try_as_mapped_slab<'a>(&'a mut self) -> Option<MappedAllocationSlab<'a>> {
let mapped_ptr = self.mapped_ptr()?.cast().as_ptr();
@@ -352,12 +351,12 @@ impl MemoryBlock {
requires_personal_block: bool,
) -> Result<Self> {
let device_memory = {
let alloc_info = vk::MemoryAllocateInfo::builder()
let alloc_info = vk::MemoryAllocateInfo::default()
.allocation_size(size)
.memory_type_index(mem_type_index as u32);
let allocation_flags = vk::MemoryAllocateFlags::DEVICE_ADDRESS;
let mut flags_info = vk::MemoryAllocateFlagsInfo::builder().flags(allocation_flags);
let mut flags_info = vk::MemoryAllocateFlagsInfo::default().flags(allocation_flags);
// TODO(manon): Test this based on if the device has this feature enabled or not
let alloc_info = if buffer_device_address {
alloc_info.push_next(&mut flags_info)
@@ -366,7 +365,7 @@ impl MemoryBlock {
};
// Flag the memory as dedicated if required.
let mut dedicated_memory_info = vk::MemoryDedicatedAllocateInfo::builder();
let mut dedicated_memory_info = vk::MemoryDedicatedAllocateInfo::default();
let alloc_info = match allocation_scheme {
AllocationScheme::DedicatedBuffer(buffer) => {
dedicated_memory_info = dedicated_memory_info.buffer(buffer);
@@ -691,53 +690,13 @@ pub struct Allocator {
impl fmt::Debug for Allocator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut allocation_report = vec![];
let mut total_reserved_size_in_bytes = 0;
for memory_type in &self.memory_types {
for block in memory_type.memory_blocks.iter().flatten() {
total_reserved_size_in_bytes += block.size;
allocation_report.extend(block.sub_allocator.report_allocations())
}
}
let total_used_size_in_bytes = allocation_report.iter().map(|report| report.size).sum();
allocation_report.sort_by_key(|alloc| std::cmp::Reverse(alloc.size));
writeln!(
f,
"================================================================",
)?;
writeln!(
f,
"ALLOCATION BREAKDOWN ({} / {})",
fmt_bytes(total_used_size_in_bytes),
fmt_bytes(total_reserved_size_in_bytes),
)?;
let max_num_allocations_to_print = f.precision().map_or(usize::MAX, |n| n);
for (idx, alloc) in allocation_report.iter().enumerate() {
if idx >= max_num_allocations_to_print {
break;
}
writeln!(
f,
"{:max_len$.max_len$}\t- {}",
alloc.name,
fmt_bytes(alloc.size),
max_len = allocator::VISUALIZER_TABLE_MAX_ENTRY_NAME_LEN,
)?;
}
Ok(())
self.generate_report().fmt(f)
}
}
impl Allocator {
pub fn new(desc: &AllocatorCreateDesc) -> Result<Self> {
if desc.physical_device == ash::vk::PhysicalDevice::null() {
if desc.physical_device == vk::PhysicalDevice::null() {
return Err(AllocationError::InvalidAllocatorCreateDesc(
"AllocatorCreateDesc field `physical_device` is null.".into(),
));
@@ -748,8 +707,8 @@ impl Allocator {
.get_physical_device_memory_properties(desc.physical_device)
};
let memory_types = &mem_props.memory_types[..mem_props.memory_type_count as _];
let memory_heaps = mem_props.memory_heaps[..mem_props.memory_heap_count as _].to_vec();
let memory_types = &mem_props.memory_types_as_slice();
let memory_heaps = mem_props.memory_heaps_as_slice().to_vec();
if desc.debug_settings.log_memory_information {
debug!("memory type count: {}", mem_props.memory_type_count);
@@ -972,6 +931,33 @@ impl Allocator {
})
.map(|memory_type| memory_type.memory_type_index as _)
}
pub fn generate_report(&self) -> AllocatorReport {
let mut allocations = vec![];
let mut blocks = vec![];
let mut total_reserved_bytes = 0;
for memory_type in &self.memory_types {
for block in memory_type.memory_blocks.iter().flatten() {
total_reserved_bytes += block.size;
let first_allocation = allocations.len();
allocations.extend(block.sub_allocator.report_allocations());
blocks.push(MemoryBlockReport {
size: block.size,
allocations: first_allocation..allocations.len(),
});
}
}
let total_allocated_bytes = allocations.iter().map(|report| report.size).sum();
AllocatorReport {
allocations,
blocks,
total_allocated_bytes,
total_reserved_bytes,
}
}
}
impl Drop for Allocator {

View File

@@ -121,7 +121,7 @@ impl AllocatorVisualizer {
if block.sub_allocator.supports_visualization()
&& ui.button("visualize").clicked()
&& !self.selected_blocks.iter().enumerate().any(|(_, x)| {
&& !self.selected_blocks.iter().any(|x| {
x.memory_type_index == mem_type_idx
&& x.block_index == block_idx
})

File diff suppressed because one or more lines are too long

View File

@@ -81,6 +81,7 @@ For changelogs after v0.14, see [the wgpu changelog](../CHANGELOG.md).
- Make varyings' struct members unique. ([#2521](https://github.com/gfx-rs/naga/pull/2521)) **@evahop**
- Add experimental vertex pulling transform flag. ([#5254](https://github.com/gfx-rs/wgpu/pull/5254)) **@bradwerth**
- Fixup some generated MSL for vertex buffer unpack functions. ([#5829](https://github.com/gfx-rs/wgpu/pull/5829)) **@bradwerth**
- Make vertex pulling transform on by default. ([#5773](https://github.com/gfx-rs/wgpu/pull/5773)) **@bradwerth**
#### GLSL-OUT

View File

@@ -11,9 +11,9 @@
[package]
edition = "2021"
rust-version = "1.74"
rust-version = "1.76"
name = "naga"
version = "0.20.0"
version = "22.0.0"
authors = ["gfx-rs developers"]
exclude = [
"bin/**/*",
@@ -43,11 +43,12 @@ path = "tests/root.rs"
[dependencies]
arrayvec = "0.7"
bit-set = "0.6"
bit-set = "0.8"
bitflags = "2.6"
indexmap = "2"
log = "0.4"
rustc-hash = "1.1.0"
thiserror = "1.0.62"
thiserror = "1.0.63"
[dependencies.arbitrary]
version = "1.3"
@@ -61,10 +62,6 @@ version = "0.11.0"
version = "0.2.1"
optional = true
[dependencies.indexmap]
version = "2"
features = ["std"]
[dependencies.petgraph]
version = "0.6"
optional = true
@@ -103,7 +100,7 @@ git = "https://github.com/gfx-rs/rspirv"
rev = "b969f175d5663258b4891e44b76c1544da9661ab"
[dev-dependencies.serde]
version = "1.0"
version = "1"
features = ["derive"]
[dev-dependencies.spirv]

View File

@@ -4,7 +4,7 @@
[![Crates.io](https://img.shields.io/crates/v/naga.svg?label=naga)](https://crates.io/crates/naga)
[![Docs.rs](https://docs.rs/naga/badge.svg)](https://docs.rs/naga)
[![Build Status](https://github.com/gfx-rs/naga/workflows/pipeline/badge.svg)](https://github.com/gfx-rs/naga/actions)
![MSRV](https://img.shields.io/badge/rustc-1.74+-blue.svg)
![MSRV](https://img.shields.io/badge/rustc-1.76+-blue.svg)
[![codecov.io](https://codecov.io/gh/gfx-rs/naga/branch/master/graph/badge.svg?token=9VOKYO8BM2)](https://codecov.io/gh/gfx-rs/naga)
The shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu).

View File

@@ -0,0 +1,311 @@
//! Workarounds for platform bugs and limitations in switches and loops.
//!
//! In these docs, we use CamelCase links for Naga IR concepts, and ordinary
//! `code` formatting for HLSL or GLSL concepts.
//!
//! ## Avoiding `continue` within `switch`
//!
//! As described in <https://github.com/gfx-rs/wgpu/issues/4485>, the FXC HLSL
//! compiler doesn't allow `continue` statements within `switch` statements, but
//! Naga IR does. We work around this by introducing synthetic boolean local
//! variables and branches.
//!
//! Specifically:
//!
//! - We generate code for [`Continue`] statements within [`SwitchCase`]s that
//! sets an introduced `bool` local to `true` and does a `break`, jumping to
//! immediately after the generated `switch`.
//!
//! - When generating code for a [`Switch`] statement, we conservatively assume
//! it might contain such a [`Continue`] statement, so:
//!
//! - If it's the outermost such [`Switch`] within a [`Loop`], we declare the
//! `bool` local ahead of the switch, initialized to `false`. Immediately
//! after the `switch`, we check the local and do a `continue` if it's set.
//!
//! - If the [`Switch`] is nested within other [`Switch`]es, then after the
//! generated `switch`, we check the local (which we know was declared
//! before the surrounding `switch`) and do a `break` if it's set.
//!
//! - As an optimization, we only generate the check of the local if a
//! [`Continue`] statement is encountered within the [`Switch`]. This may
//! help drivers more easily identify that the `bool` is unused.
//!
//! So while we "weaken" the [`Continue`] statement by rendering it as a `break`
//! statement, we also place checks immediately at the locations to which those
//! `break` statements will jump, until we can be sure we've reached the
//! intended target of the original [`Continue`].
//!
//! In the case of nested [`Loop`] and [`Switch`] statements, there may be
//! multiple introduced `bool` locals in scope, but there's no problem knowing
//! which one to operate on. At any point, there is at most one [`Loop`]
//! statement that could be targeted by a [`Continue`] statement, so the correct
//! `bool` local to set and test is always the one introduced for the innermost
//! enclosing [`Loop`]'s outermost [`Switch`].
//!
//! # Avoiding single body `switch` statements
//!
//! As described in <https://github.com/gfx-rs/wgpu/issues/4514>, some language
//! front ends miscompile `switch` statements where all cases branch to the same
//! body. Our HLSL and GLSL backends render [`Switch`] statements with a single
//! [`SwitchCase`] as `do {} while(false);` loops.
//!
//! However, this rewriting introduces a new loop that could "capture"
//! `continue` statements in its body. To avoid doing so, we apply the
//! [`Continue`]-to-`break` transformation described above.
//!
//! [`Continue`]: crate::Statement::Continue
//! [`Loop`]: crate::Statement::Loop
//! [`Switch`]: crate::Statement::Switch
//! [`SwitchCase`]: crate::SwitchCase
use crate::proc::Namer;
use std::rc::Rc;
/// A summary of the code surrounding a statement.
enum Nesting {
/// Currently nested in at least one [`Loop`] statement.
///
/// [`Continue`] should apply to the innermost loop.
///
/// When this entry is on the top of the stack:
///
/// * When entering an inner [`Loop`] statement, push a [`Loop`][nl] state
/// onto the stack.
///
/// * When entering a nested [`Switch`] statement, push a [`Switch`][ns]
/// state onto the stack with a new variable name. Before the generated
/// `switch`, introduce a `bool` local with that name, initialized to
/// `false`.
///
/// When exiting the [`Loop`] for which this entry was pushed, pop it from
/// the stack.
///
/// [`Continue`]: crate::Statement::Continue
/// [`Loop`]: crate::Statement::Loop
/// [`Switch`]: crate::Statement::Switch
/// [ns]: Nesting::Switch
/// [nl]: Nesting::Loop
Loop,
/// Currently nested in at least one [`Switch`] that may need to forward
/// [`Continue`]s.
///
/// This includes [`Switch`]es rendered as `do {} while(false)` loops, but
/// doesn't need to include regular [`Switch`]es in backends that can
/// support `continue` within switches.
///
/// [`Continue`] should be forwarded to the innermost surrounding [`Loop`].
///
/// When this entry is on the top of the stack:
///
/// * When entering a nested [`Loop`], push a [`Loop`][nl] state onto the
/// stack.
///
/// * When entering a nested [`Switch`], push a [`Switch`][ns] state onto
/// the stack with a clone of the introduced `bool` variable's name.
///
/// * When encountering a [`Continue`] statement, render it as code to set
/// the introduced `bool` local (whose name is held in [`variable`]) to
/// `true`, and then `break`. Set [`continue_encountered`] to `true` to
/// record that the [`Switch`] contains a [`Continue`].
///
/// * When exiting this [`Switch`], pop its entry from the stack. If
/// [`continue_encountered`] is set, then we have rendered [`Continue`]
/// statements as `break` statements that jump to this point. Generate
/// code to check `variable`, and if it is `true`:
///
/// * If there is another [`Switch`][ns] left on top of the stack, set
/// its `continue_encountered` flag, and generate a `break`. (Both
/// [`Switch`][ns]es are within the same [`Loop`] and share the same
/// introduced variable, so there's no need to set another flag to
/// continue to exit the `switch`es.)
///
/// * Otherwise, `continue`.
///
/// When we exit the [`Switch`] for which this entry was pushed, pop it.
///
/// [`Continue`]: crate::Statement::Continue
/// [`Loop`]: crate::Statement::Loop
/// [`Switch`]: crate::Statement::Switch
/// [`variable`]: Nesting::Switch::variable
/// [`continue_encountered`]: Nesting::Switch::continue_encountered
/// [ns]: Nesting::Switch
/// [nl]: Nesting::Loop
Switch {
variable: Rc<String>,
/// Set if we've generated code for a [`Continue`] statement with this
/// entry on the top of the stack.
///
/// If this is still clear when we finish rendering the [`Switch`], then
/// we know we don't need to generate branch forwarding code. Omitting
/// that may make it easier for drivers to tell that the `bool` we
/// introduced ahead of the [`Switch`] is actually unused.
///
/// [`Continue`]: crate::Statement::Continue
/// [`Switch`]: crate::Statement::Switch
continue_encountered: bool,
},
}
/// A micro-IR for code a backend should generate after a [`Switch`].
///
/// [`Switch`]: crate::Statement::Switch
pub(super) enum ExitControlFlow {
None,
/// Emit `if (continue_variable) { continue; }`
Continue {
variable: Rc<String>,
},
/// Emit `if (continue_variable) { break; }`
///
/// Used after a [`Switch`] to exit from an enclosing [`Switch`].
///
/// After the enclosing switch, its associated check will consult this same
/// variable, see that it is set, and exit early.
///
/// [`Switch`]: crate::Statement::Switch
Break {
variable: Rc<String>,
},
}
/// Utility for tracking nesting of loops and switches to orchestrate forwarding
/// of continue statements inside of a switch to the enclosing loop.
///
/// See [module docs](self) for why we need this.
#[derive(Default)]
pub(super) struct ContinueCtx {
stack: Vec<Nesting>,
}
impl ContinueCtx {
/// Resets internal state.
///
/// Use this to reuse memory between writing sessions.
pub fn clear(&mut self) {
self.stack.clear();
}
/// Updates internal state to record entering a [`Loop`] statement.
///
/// [`Loop`]: crate::Statement::Loop
pub fn enter_loop(&mut self) {
self.stack.push(Nesting::Loop);
}
/// Updates internal state to record exiting a [`Loop`] statement.
///
/// [`Loop`]: crate::Statement::Loop
pub fn exit_loop(&mut self) {
if !matches!(self.stack.pop(), Some(Nesting::Loop)) {
unreachable!("ContinueCtx stack out of sync");
}
}
/// Updates internal state to record entering a [`Switch`] statement.
///
/// Return `Some(variable)` if this [`Switch`] is nested within a [`Loop`],
/// and the caller should introcue a new `bool` local variable named
/// `variable` above the `switch`, for forwarding [`Continue`] statements.
///
/// `variable` is guaranteed not to conflict with any names used by the
/// program itself.
///
/// [`Continue`]: crate::Statement::Continue
/// [`Loop`]: crate::Statement::Loop
/// [`Switch`]: crate::Statement::Switch
pub fn enter_switch(&mut self, namer: &mut Namer) -> Option<Rc<String>> {
match self.stack.last() {
// If the stack is empty, we are not in loop, so we don't need to
// forward continue statements within this `Switch`. We can leave
// the stack empty.
None => None,
Some(&Nesting::Loop { .. }) => {
let variable = Rc::new(namer.call("should_continue"));
self.stack.push(Nesting::Switch {
variable: Rc::clone(&variable),
continue_encountered: false,
});
Some(variable)
}
Some(&Nesting::Switch { ref variable, .. }) => {
self.stack.push(Nesting::Switch {
variable: Rc::clone(variable),
continue_encountered: false,
});
// We have already declared the variable before some enclosing
// `Switch`.
None
}
}
}
/// Update internal state to record leaving a [`Switch`] statement.
///
/// Return an [`ExitControlFlow`] value indicating what code should be
/// introduced after the generated `switch` to forward continues.
///
/// [`Switch`]: crate::Statement::Switch
pub fn exit_switch(&mut self) -> ExitControlFlow {
match self.stack.pop() {
// This doesn't indicate a problem: we don't start pushing entries
// for `Switch` statements unless we have an enclosing `Loop`.
None => ExitControlFlow::None,
Some(Nesting::Loop { .. }) => {
unreachable!("Unexpected loop state when exiting switch");
}
Some(Nesting::Switch {
variable,
continue_encountered: inner_continue,
}) => {
if !inner_continue {
// No `Continue` statement was encountered, so we didn't
// introduce any `break`s jumping to this point.
ExitControlFlow::None
} else if let Some(&mut Nesting::Switch {
continue_encountered: ref mut outer_continue,
..
}) = self.stack.last_mut()
{
// This is nested in another `Switch`. Propagate upwards
// that there is a continue statement present.
*outer_continue = true;
ExitControlFlow::Break { variable }
} else {
ExitControlFlow::Continue { variable }
}
}
}
}
/// Determine what to generate for a [`Continue`] statement.
///
/// If we can generate an ordinary `continue` statement, return `None`.
///
/// Otherwise, we're enclosed by a [`Switch`] that is itself enclosed by a
/// [`Loop`]. Return `Some(variable)` to indicate that the [`Continue`]
/// should be rendered as setting `variable` to `true`, and then doing a
/// `break`.
///
/// This also notes that we've encountered a [`Continue`] statement, so that
/// we can generate the right code to forward the branch following the
/// enclosing `switch`.
///
/// [`Continue`]: crate::Statement::Continue
/// [`Loop`]: crate::Statement::Loop
/// [`Switch`]: crate::Statement::Switch
pub fn continue_encountered(&mut self) -> Option<&str> {
if let Some(&mut Nesting::Switch {
ref variable,
ref mut continue_encountered,
}) = self.stack.last_mut()
{
*continue_encountered = true;
Some(variable)
} else {
None
}
}
}

View File

@@ -447,7 +447,7 @@ impl<'a, W> Writer<'a, W> {
..
} = self;
// Loop trough all expressions in both functions and the entry point
// Loop through all expressions in both functions and the entry point
// to check for needed features
for (expressions, info) in module
.functions

View File

@@ -545,6 +545,11 @@ pub struct Writer<'a, W> {
named_expressions: crate::NamedExpressions,
/// Set of expressions that need to be baked to avoid unnecessary repetition in output
need_bake_expressions: back::NeedBakeExpressions,
/// Information about nesting of loops and switches.
///
/// Used for forwarding continue statements in switches that have been
/// transformed to `do {} while(false);` loops.
continue_ctx: back::continue_forward::ContinueCtx,
/// How many views to render to, if doing multiview rendering.
multiview: Option<std::num::NonZeroU32>,
/// Mapping of varying variables to their location. Needed for reflections.
@@ -619,6 +624,7 @@ impl<'a, W: Write> Writer<'a, W> {
block_id: IdGenerator::default(),
named_expressions: Default::default(),
need_bake_expressions: Default::default(),
continue_ctx: back::continue_forward::ContinueCtx::default(),
varying: Default::default(),
};
@@ -1869,7 +1875,7 @@ impl<'a, W: Write> Writer<'a, W> {
// with different precedences from applying earlier.
write!(self.out, "(")?;
// Cycle trough all the components of the vector
// Cycle through all the components of the vector
for index in 0..size {
let component = back::COMPONENTS[index];
// Write the addition to the previous product
@@ -2082,42 +2088,94 @@ impl<'a, W: Write> Writer<'a, W> {
selector,
ref cases,
} => {
// Start the switch
write!(self.out, "{level}")?;
write!(self.out, "switch(")?;
self.write_expr(selector, ctx)?;
writeln!(self.out, ") {{")?;
// Write all cases
let l2 = level.next();
for case in cases {
match case.value {
crate::SwitchValue::I32(value) => write!(self.out, "{l2}case {value}:")?,
crate::SwitchValue::U32(value) => write!(self.out, "{l2}case {value}u:")?,
crate::SwitchValue::Default => write!(self.out, "{l2}default:")?,
// Some GLSL consumers may not handle switches with a single
// body correctly: See wgpu#4514. Write such switch statements
// as a `do {} while(false);` loop instead.
//
// Since doing so may inadvertently capture `continue`
// statements in the switch body, we must apply continue
// forwarding. See the `naga::back::continue_forward` module
// docs for details.
let one_body = cases
.iter()
.rev()
.skip(1)
.all(|case| case.fall_through && case.body.is_empty());
if one_body {
// Unlike HLSL, in GLSL `continue_ctx` only needs to know
// about [`Switch`] statements that are being rendered as
// `do-while` loops.
if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {
writeln!(self.out, "{level}bool {variable} = false;",)?;
};
writeln!(self.out, "{level}do {{")?;
// Note: Expressions have no side-effects so we don't need to emit selector expression.
// Body
if let Some(case) = cases.last() {
for sta in case.body.iter() {
self.write_stmt(sta, ctx, l2)?;
}
}
// End do-while
writeln!(self.out, "{level}}} while(false);")?;
// Handle any forwarded continue statements.
use back::continue_forward::ExitControlFlow;
let op = match self.continue_ctx.exit_switch() {
ExitControlFlow::None => None,
ExitControlFlow::Continue { variable } => Some(("continue", variable)),
ExitControlFlow::Break { variable } => Some(("break", variable)),
};
if let Some((control_flow, variable)) = op {
writeln!(self.out, "{level}if ({variable}) {{")?;
writeln!(self.out, "{l2}{control_flow};")?;
writeln!(self.out, "{level}}}")?;
}
} else {
// Start the switch
write!(self.out, "{level}")?;
write!(self.out, "switch(")?;
self.write_expr(selector, ctx)?;
writeln!(self.out, ") {{")?;
// Write all cases
for case in cases {
match case.value {
crate::SwitchValue::I32(value) => {
write!(self.out, "{l2}case {value}:")?
}
crate::SwitchValue::U32(value) => {
write!(self.out, "{l2}case {value}u:")?
}
crate::SwitchValue::Default => write!(self.out, "{l2}default:")?,
}
let write_block_braces = !(case.fall_through && case.body.is_empty());
if write_block_braces {
writeln!(self.out, " {{")?;
} else {
writeln!(self.out)?;
}
for sta in case.body.iter() {
self.write_stmt(sta, ctx, l2.next())?;
}
if !case.fall_through
&& case.body.last().map_or(true, |s| !s.is_terminator())
{
writeln!(self.out, "{}break;", l2.next())?;
}
if write_block_braces {
writeln!(self.out, "{l2}}}")?;
}
}
let write_block_braces = !(case.fall_through && case.body.is_empty());
if write_block_braces {
writeln!(self.out, " {{")?;
} else {
writeln!(self.out)?;
}
for sta in case.body.iter() {
self.write_stmt(sta, ctx, l2.next())?;
}
if !case.fall_through && case.body.last().map_or(true, |s| !s.is_terminator()) {
writeln!(self.out, "{}break;", l2.next())?;
}
if write_block_braces {
writeln!(self.out, "{l2}}}")?;
}
writeln!(self.out, "{level}}}")?
}
writeln!(self.out, "{level}}}")?
}
// Loops in naga IR are based on wgsl loops, glsl can emulate the behaviour by using a
// while true loop and appending the continuing block to the body resulting on:
@@ -2134,6 +2192,7 @@ impl<'a, W: Write> Writer<'a, W> {
ref continuing,
break_if,
} => {
self.continue_ctx.enter_loop();
if !continuing.is_empty() || break_if.is_some() {
let gate_name = self.namer.call("loop_init");
writeln!(self.out, "{level}bool {gate_name} = true;")?;
@@ -2159,7 +2218,8 @@ impl<'a, W: Write> Writer<'a, W> {
for sta in body {
self.write_stmt(sta, ctx, level.next())?;
}
writeln!(self.out, "{level}}}")?
writeln!(self.out, "{level}}}")?;
self.continue_ctx.exit_loop();
}
// Break, continue and return as written as in C
// `break;`
@@ -2169,8 +2229,14 @@ impl<'a, W: Write> Writer<'a, W> {
}
// `continue;`
Statement::Continue => {
write!(self.out, "{level}")?;
writeln!(self.out, "continue;")?
// Sometimes we must render a `Continue` statement as a `break`.
// See the docs for the `back::continue_forward` module.
if let Some(variable) = self.continue_ctx.continue_encountered() {
writeln!(self.out, "{level}{variable} = true;",)?;
writeln!(self.out, "{level}break;")?
} else {
writeln!(self.out, "{level}continue;")?
}
}
// `return expr;`, `expr` is optional
Statement::Return { value } => {
@@ -3581,8 +3647,8 @@ impl<'a, W: Write> Writer<'a, W> {
return Ok(());
}
Mf::FindLsb => "findLSB",
Mf::FindMsb => "findMSB",
Mf::FirstTrailingBit => "findLSB",
Mf::FirstLeadingBit => "findMSB",
// data packing
Mf::Pack4x8snorm => "packSnorm4x8",
Mf::Pack4x8unorm => "packUnorm4x8",
@@ -3656,8 +3722,10 @@ impl<'a, W: Write> Writer<'a, W> {
// Some GLSL functions always return signed integers (like findMSB),
// so they need to be cast to uint if the argument is also an uint.
let ret_might_need_int_to_uint =
matches!(fun, Mf::FindLsb | Mf::FindMsb | Mf::CountOneBits | Mf::Abs);
let ret_might_need_int_to_uint = matches!(
fun,
Mf::FirstTrailingBit | Mf::FirstLeadingBit | Mf::CountOneBits | Mf::Abs
);
// Some GLSL functions only accept signed integers (like abs),
// so they need their argument cast from uint to int.

View File

@@ -327,6 +327,7 @@ pub struct Writer<'a, W> {
/// Set of expressions that have associated temporary variables
named_expressions: crate::NamedExpressions,
wrapped: Wrapped,
continue_ctx: back::continue_forward::ContinueCtx,
/// A reference to some part of a global variable, lowered to a series of
/// byte offset calculations.

View File

@@ -104,6 +104,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
entry_point_io: Vec::new(),
named_expressions: crate::NamedExpressions::default(),
wrapped: super::Wrapped::default(),
continue_ctx: back::continue_forward::ContinueCtx::default(),
temp_access_chain: Vec::new(),
need_bake_expressions: Default::default(),
}
@@ -122,6 +123,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
self.entry_point_io.clear();
self.named_expressions.clear();
self.wrapped.clear();
self.continue_ctx.clear();
self.need_bake_expressions.clear();
}
@@ -1439,6 +1441,151 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
self.write_barrier(crate::Barrier::WORK_GROUP, level)
}
/// Helper method used to write switches
fn write_switch(
&mut self,
module: &Module,
func_ctx: &back::FunctionCtx<'_>,
level: back::Level,
selector: Handle<crate::Expression>,
cases: &[crate::SwitchCase],
) -> BackendResult {
// Write all cases
let indent_level_1 = level.next();
let indent_level_2 = indent_level_1.next();
// See docs of `back::continue_forward` module.
if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {
writeln!(self.out, "{level}bool {variable} = false;",)?;
};
// Check if there is only one body, by seeing if all except the last case are fall through
// with empty bodies. FXC doesn't handle these switches correctly, so
// we generate a `do {} while(false);` loop instead. There must be a default case, so there
// is no need to check if one of the cases would have matched.
let one_body = cases
.iter()
.rev()
.skip(1)
.all(|case| case.fall_through && case.body.is_empty());
if one_body {
// Start the do-while
writeln!(self.out, "{level}do {{")?;
// Note: Expressions have no side-effects so we don't need to emit selector expression.
// Body
if let Some(case) = cases.last() {
for sta in case.body.iter() {
self.write_stmt(module, sta, func_ctx, indent_level_1)?;
}
}
// End do-while
writeln!(self.out, "{level}}} while(false);")?;
} else {
// Start the switch
write!(self.out, "{level}")?;
write!(self.out, "switch(")?;
self.write_expr(module, selector, func_ctx)?;
writeln!(self.out, ") {{")?;
for (i, case) in cases.iter().enumerate() {
match case.value {
crate::SwitchValue::I32(value) => {
write!(self.out, "{indent_level_1}case {value}:")?
}
crate::SwitchValue::U32(value) => {
write!(self.out, "{indent_level_1}case {value}u:")?
}
crate::SwitchValue::Default => write!(self.out, "{indent_level_1}default:")?,
}
// The new block is not only stylistic, it plays a role here:
// We might end up having to write the same case body
// multiple times due to FXC not supporting fallthrough.
// Therefore, some `Expression`s written by `Statement::Emit`
// will end up having the same name (`_expr<handle_index>`).
// So we need to put each case in its own scope.
let write_block_braces = !(case.fall_through && case.body.is_empty());
if write_block_braces {
writeln!(self.out, " {{")?;
} else {
writeln!(self.out)?;
}
// Although FXC does support a series of case clauses before
// a block[^yes], it does not support fallthrough from a
// non-empty case block to the next[^no]. If this case has a
// non-empty body with a fallthrough, emulate that by
// duplicating the bodies of all the cases it would fall
// into as extensions of this case's own body. This makes
// the HLSL output potentially quadratic in the size of the
// Naga IR.
//
// [^yes]: ```hlsl
// case 1:
// case 2: do_stuff()
// ```
// [^no]: ```hlsl
// case 1: do_this();
// case 2: do_that();
// ```
if case.fall_through && !case.body.is_empty() {
let curr_len = i + 1;
let end_case_idx = curr_len
+ cases
.iter()
.skip(curr_len)
.position(|case| !case.fall_through)
.unwrap();
let indent_level_3 = indent_level_2.next();
for case in &cases[i..=end_case_idx] {
writeln!(self.out, "{indent_level_2}{{")?;
let prev_len = self.named_expressions.len();
for sta in case.body.iter() {
self.write_stmt(module, sta, func_ctx, indent_level_3)?;
}
// Clear all named expressions that were previously inserted by the statements in the block
self.named_expressions.truncate(prev_len);
writeln!(self.out, "{indent_level_2}}}")?;
}
let last_case = &cases[end_case_idx];
if last_case.body.last().map_or(true, |s| !s.is_terminator()) {
writeln!(self.out, "{indent_level_2}break;")?;
}
} else {
for sta in case.body.iter() {
self.write_stmt(module, sta, func_ctx, indent_level_2)?;
}
if !case.fall_through && case.body.last().map_or(true, |s| !s.is_terminator()) {
writeln!(self.out, "{indent_level_2}break;")?;
}
}
if write_block_braces {
writeln!(self.out, "{indent_level_1}}}")?;
}
}
writeln!(self.out, "{level}}}")?;
}
// Handle any forwarded continue statements.
use back::continue_forward::ExitControlFlow;
let op = match self.continue_ctx.exit_switch() {
ExitControlFlow::None => None,
ExitControlFlow::Continue { variable } => Some(("continue", variable)),
ExitControlFlow::Break { variable } => Some(("break", variable)),
};
if let Some((control_flow, variable)) = op {
writeln!(self.out, "{level}if ({variable}) {{")?;
writeln!(self.out, "{indent_level_1}{control_flow};")?;
writeln!(self.out, "{level}}}")?;
}
Ok(())
}
/// Helper method used to write statements
///
/// # Notes
@@ -1882,6 +2029,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
ref continuing,
break_if,
} => {
self.continue_ctx.enter_loop();
let l2 = level.next();
if !continuing.is_empty() || break_if.is_some() {
let gate_name = self.namer.call("loop_init");
@@ -1908,10 +2056,18 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
for sta in body.iter() {
self.write_stmt(module, sta, func_ctx, l2)?;
}
writeln!(self.out, "{level}}}")?
writeln!(self.out, "{level}}}")?;
self.continue_ctx.exit_loop();
}
Statement::Break => writeln!(self.out, "{level}break;")?,
Statement::Continue => writeln!(self.out, "{level}continue;")?,
Statement::Continue => {
if let Some(variable) = self.continue_ctx.continue_encountered() {
writeln!(self.out, "{level}{variable} = true;")?;
writeln!(self.out, "{level}break;")?
} else {
writeln!(self.out, "{level}continue;")?
}
}
Statement::Barrier(barrier) => {
self.write_barrier(barrier, level)?;
}
@@ -2063,100 +2219,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
selector,
ref cases,
} => {
// Start the switch
write!(self.out, "{level}")?;
write!(self.out, "switch(")?;
self.write_expr(module, selector, func_ctx)?;
writeln!(self.out, ") {{")?;
// Write all cases
let indent_level_1 = level.next();
let indent_level_2 = indent_level_1.next();
for (i, case) in cases.iter().enumerate() {
match case.value {
crate::SwitchValue::I32(value) => {
write!(self.out, "{indent_level_1}case {value}:")?
}
crate::SwitchValue::U32(value) => {
write!(self.out, "{indent_level_1}case {value}u:")?
}
crate::SwitchValue::Default => {
write!(self.out, "{indent_level_1}default:")?
}
}
// The new block is not only stylistic, it plays a role here:
// We might end up having to write the same case body
// multiple times due to FXC not supporting fallthrough.
// Therefore, some `Expression`s written by `Statement::Emit`
// will end up having the same name (`_expr<handle_index>`).
// So we need to put each case in its own scope.
let write_block_braces = !(case.fall_through && case.body.is_empty());
if write_block_braces {
writeln!(self.out, " {{")?;
} else {
writeln!(self.out)?;
}
// Although FXC does support a series of case clauses before
// a block[^yes], it does not support fallthrough from a
// non-empty case block to the next[^no]. If this case has a
// non-empty body with a fallthrough, emulate that by
// duplicating the bodies of all the cases it would fall
// into as extensions of this case's own body. This makes
// the HLSL output potentially quadratic in the size of the
// Naga IR.
//
// [^yes]: ```hlsl
// case 1:
// case 2: do_stuff()
// ```
// [^no]: ```hlsl
// case 1: do_this();
// case 2: do_that();
// ```
if case.fall_through && !case.body.is_empty() {
let curr_len = i + 1;
let end_case_idx = curr_len
+ cases
.iter()
.skip(curr_len)
.position(|case| !case.fall_through)
.unwrap();
let indent_level_3 = indent_level_2.next();
for case in &cases[i..=end_case_idx] {
writeln!(self.out, "{indent_level_2}{{")?;
let prev_len = self.named_expressions.len();
for sta in case.body.iter() {
self.write_stmt(module, sta, func_ctx, indent_level_3)?;
}
// Clear all named expressions that were previously inserted by the statements in the block
self.named_expressions.truncate(prev_len);
writeln!(self.out, "{indent_level_2}}}")?;
}
let last_case = &cases[end_case_idx];
if last_case.body.last().map_or(true, |s| !s.is_terminator()) {
writeln!(self.out, "{indent_level_2}break;")?;
}
} else {
for sta in case.body.iter() {
self.write_stmt(module, sta, func_ctx, indent_level_2)?;
}
if !case.fall_through
&& case.body.last().map_or(true, |s| !s.is_terminator())
{
writeln!(self.out, "{indent_level_2}break;")?;
}
}
if write_block_braces {
writeln!(self.out, "{indent_level_1}}}")?;
}
}
writeln!(self.out, "{level}}}")?
self.write_switch(module, func_ctx, level, selector, cases)?;
}
Statement::RayQuery { .. } => unreachable!(),
Statement::SubgroupBallot { result, predicate } => {
@@ -3000,8 +3063,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Mf::CountLeadingZeros => Function::CountLeadingZeros,
Mf::CountOneBits => Function::MissingIntOverload("countbits"),
Mf::ReverseBits => Function::MissingIntOverload("reversebits"),
Mf::FindLsb => Function::MissingIntReturnType("firstbitlow"),
Mf::FindMsb => Function::MissingIntReturnType("firstbithigh"),
Mf::FirstTrailingBit => Function::MissingIntReturnType("firstbitlow"),
Mf::FirstLeadingBit => Function::MissingIntReturnType("firstbithigh"),
Mf::ExtractBits => Function::Regular(EXTRACT_BITS_FUNCTION),
Mf::InsertBits => Function::Regular(INSERT_BITS_FUNCTION),
// Data Packing

View File

@@ -19,6 +19,9 @@ pub mod wgsl;
#[cfg(any(hlsl_out, msl_out, spv_out, glsl_out))]
pub mod pipeline_constants;
#[cfg(any(hlsl_out, glsl_out))]
mod continue_forward;
/// Names of vector components.
pub const COMPONENTS: &[char] = &['x', 'y', 'z', 'w'];
/// Indent for backends.

View File

@@ -354,7 +354,9 @@ pub struct PipelineOptions {
/// to receive the vertex buffers, lengths, and vertex id as args,
/// and bounds-check the vertex id and use the index into the
/// vertex buffers to access attributes, rather than using Metal's
/// [[stage-in]] assembled attribute data.
/// [[stage-in]] assembled attribute data. This is true by default,
/// but remains configurable for use by tests via deserialization
/// of this struct. There is no user-facing way to set this value.
pub vertex_pulling_transform: bool,
/// vertex_buffer_mappings are used during shader translation to

View File

@@ -1063,43 +1063,6 @@ impl<W: Write> Writer<W> {
address: &TexelAddress,
value: Handle<crate::Expression>,
context: &StatementContext,
) -> BackendResult {
match context.expression.policies.image_store {
proc::BoundsCheckPolicy::Restrict => {
// We don't have a restricted level value, because we don't
// support writes to mipmapped textures.
debug_assert!(address.level.is_none());
write!(self.out, "{level}")?;
self.put_expression(image, &context.expression, false)?;
write!(self.out, ".write(")?;
self.put_expression(value, &context.expression, true)?;
write!(self.out, ", ")?;
self.put_restricted_texel_address(image, address, &context.expression)?;
writeln!(self.out, ");")?;
}
proc::BoundsCheckPolicy::ReadZeroSkipWrite => {
write!(self.out, "{level}if (")?;
self.put_image_access_bounds_check(image, address, &context.expression)?;
writeln!(self.out, ") {{")?;
self.put_unchecked_image_store(level.next(), image, address, value, context)?;
writeln!(self.out, "{level}}}")?;
}
proc::BoundsCheckPolicy::Unchecked => {
self.put_unchecked_image_store(level, image, address, value, context)?;
}
}
Ok(())
}
fn put_unchecked_image_store(
&mut self,
level: back::Level,
image: Handle<crate::Expression>,
address: &TexelAddress,
value: Handle<crate::Expression>,
context: &StatementContext,
) -> BackendResult {
write!(self.out, "{level}")?;
self.put_expression(image, &context.expression, false)?;
@@ -1235,7 +1198,7 @@ impl<W: Write> Writer<W> {
// with different precedences from applying earlier.
write!(self.out, "(")?;
// Cycle trough all the components of the vector
// Cycle through all the components of the vector
for index in 0..size {
let component = back::COMPONENTS[index];
// Write the addition to the previous product
@@ -1875,8 +1838,8 @@ impl<W: Write> Writer<W> {
Mf::ReverseBits => "reverse_bits",
Mf::ExtractBits => "",
Mf::InsertBits => "",
Mf::FindLsb => "",
Mf::FindMsb => "",
Mf::FirstTrailingBit => "",
Mf::FirstLeadingBit => "",
// data packing
Mf::Pack4x8snorm => "pack_float_to_snorm4x8",
Mf::Pack4x8unorm => "pack_float_to_unorm4x8",
@@ -1920,7 +1883,7 @@ impl<W: Write> Writer<W> {
self.put_expression(arg1.unwrap(), context, false)?;
write!(self.out, ")")?;
}
Mf::FindLsb => {
Mf::FirstTrailingBit => {
let scalar = context.resolve_type(arg).scalar().unwrap();
let constant = scalar.width * 8 + 1;
@@ -1928,7 +1891,7 @@ impl<W: Write> Writer<W> {
self.put_expression(arg, context, true)?;
write!(self.out, ") + 1) % {constant}) - 1)")?;
}
Mf::FindMsb => {
Mf::FirstLeadingBit => {
let inner = context.resolve_type(arg);
let scalar = inner.scalar().unwrap();
let constant = scalar.width * 8 - 1;
@@ -2702,7 +2665,7 @@ impl<W: Write> Writer<W> {
}
}
}
crate::MathFunction::FindMsb
crate::MathFunction::FirstLeadingBit
| crate::MathFunction::Pack4xI8
| crate::MathFunction::Pack4xU8
| crate::MathFunction::Unpack4xI8
@@ -3953,8 +3916,8 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::float2((float(b0) - 128.0f) / 255.0f, \
(float(b1) - 128.0f) / 255.0f);",
"{}return metal::float2(metal::max(-1.0f, as_type<char>(b0) / 127.0f), \
metal::max(-1.0f, as_type<char>(b1) / 127.0f));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -3971,10 +3934,10 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::float4((float(b0) - 128.0f) / 255.0f, \
(float(b1) - 128.0f) / 255.0f, \
(float(b2) - 128.0f) / 255.0f, \
(float(b3) - 128.0f) / 255.0f);",
"{}return metal::float4(metal::max(-1.0f, as_type<char>(b0) / 127.0f), \
metal::max(-1.0f, as_type<char>(b1) / 127.0f), \
metal::max(-1.0f, as_type<char>(b2) / 127.0f), \
metal::max(-1.0f, as_type<char>(b3) / 127.0f));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4033,8 +3996,8 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::int2(as_type<metal::short>(b1 << 8 | b0), \
as_type<metal::short>(b3 << 8 | b2));",
"{}return metal::int2(as_type<short>(metal::ushort(b1 << 8 | b0)), \
as_type<short>(metal::ushort(b3 << 8 | b2)));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4055,10 +4018,10 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::int4(as_type<metal::short>(b1 << 8 | b0), \
as_type<metal::short>(b3 << 8 | b2), \
as_type<metal::short>(b5 << 8 | b4), \
as_type<metal::short>(b7 << 8 | b6));",
"{}return metal::int4(as_type<short>(metal::ushort(b1 << 8 | b0)), \
as_type<short>(metal::ushort(b3 << 8 | b2)), \
as_type<short>(metal::ushort(b5 << 8 | b4)), \
as_type<short>(metal::ushort(b7 << 8 | b6)));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4117,8 +4080,7 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::float2((float(b1 << 8 | b0) - 32767.0f) / 65535.0f, \
(float(b3 << 8 | b2) - 32767.0f) / 65535.0f);",
"{}return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2);",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4139,10 +4101,8 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::float4((float(b1 << 8 | b0) - 32767.0f) / 65535.0f, \
(float(b3 << 8 | b2) - 32767.0f) / 65535.0f, \
(float(b5 << 8 | b4) - 32767.0f) / 65535.0f, \
(float(b7 << 8 | b6) - 32767.0f) / 65535.0f);",
"{}return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), \
metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4159,8 +4119,8 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::float2(as_type<metal::half>(b1 << 8 | b0), \
as_type<metal::half>(b3 << 8 | b2));",
"{}return metal::float2(as_type<half>(metal::ushort(b1 << 8 | b0)), \
as_type<half>(metal::ushort(b3 << 8 | b2)));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4170,7 +4130,7 @@ impl<W: Write> Writer<W> {
let name = self.namer.call("unpackFloat16x4");
writeln!(
self.out,
"metal::int4 {name}(metal::ushort b0, \
"metal::float4 {name}(metal::ushort b0, \
metal::ushort b1, \
metal::ushort b2, \
metal::ushort b3, \
@@ -4181,10 +4141,10 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return metal::int4(as_type<metal::half>(b1 << 8 | b0), \
as_type<metal::half>(b3 << 8 | b2), \
as_type<metal::half>(b5 << 8 | b4), \
as_type<metal::half>(b7 << 8 | b6));",
"{}return metal::float4(as_type<half>(metal::ushort(b1 << 8 | b0)), \
as_type<half>(metal::ushort(b3 << 8 | b2)), \
as_type<half>(metal::ushort(b5 << 8 | b4)), \
as_type<half>(metal::ushort(b7 << 8 | b6)));",
back::INDENT
)?;
writeln!(self.out, "}}")?;
@@ -4390,10 +4350,10 @@ impl<W: Write> Writer<W> {
let name = self.namer.call("unpackSint32");
writeln!(
self.out,
"metal::int {name}(uint b0, \
uint b1, \
uint b2, \
uint b3) {{"
"int {name}(uint b0, \
uint b1, \
uint b2, \
uint b3) {{"
)?;
writeln!(
self.out,
@@ -4495,7 +4455,18 @@ impl<W: Write> Writer<W> {
)?;
writeln!(
self.out,
"{}return unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);",
// The following is correct for RGBA packing, but our format seems to
// match ABGR, which can be fed into the Metal builtin function
// unpack_unorm10a2_to_float.
/*
"{}uint v = (b3 << 24 | b2 << 16 | b1 << 8 | b0); \
uint r = (v & 0xFFC00000) >> 22; \
uint g = (v & 0x003FF000) >> 12; \
uint b = (v & 0x00000FFC) >> 2; \
uint a = (v & 0x00000003); \
return metal::float4(float(r) / 1023.0f, float(g) / 1023.0f, float(b) / 1023.0f, float(a) / 3.0f);",
*/
"{}return metal::unpack_unorm10a2_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);",
back::INDENT
)?;
writeln!(self.out, "}}")?;

View File

@@ -1183,13 +1183,13 @@ impl<'w> BlockContext<'w> {
count_id,
))
}
Mf::FindLsb => MathOp::Ext(spirv::GLOp::FindILsb),
Mf::FindMsb => {
Mf::FirstTrailingBit => MathOp::Ext(spirv::GLOp::FindILsb),
Mf::FirstLeadingBit => {
if arg_ty.scalar_width() == Some(4) {
let thing = match arg_scalar_kind {
Some(crate::ScalarKind::Uint) => spirv::GLOp::FindUMsb,
Some(crate::ScalarKind::Sint) => spirv::GLOp::FindSMsb,
other => unimplemented!("Unexpected findMSB({:?})", other),
other => unimplemented!("Unexpected firstLeadingBit({:?})", other),
};
MathOp::Ext(thing)
} else {

View File

@@ -1178,32 +1178,13 @@ impl<'w> BlockContext<'w> {
_ => {}
}
match self.writer.bounds_check_policies.image_store {
crate::proc::BoundsCheckPolicy::Restrict => {
let (coords, _, _) =
self.write_restricted_coordinates(image_id, coordinates, None, None, block)?;
write.generate(&mut self.writer.id_gen, coords, None, None, block);
}
crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => {
self.write_conditional_image_access(
image_id,
coordinates,
None,
None,
block,
&write,
)?;
}
crate::proc::BoundsCheckPolicy::Unchecked => {
write.generate(
&mut self.writer.id_gen,
coordinates.value_id,
None,
None,
block,
);
}
}
write.generate(
&mut self.writer.id_gen,
coordinates.value_id,
None,
None,
block,
);
Ok(())
}

View File

@@ -1710,8 +1710,8 @@ impl<W: Write> Writer<W> {
Mf::ReverseBits => Function::Regular("reverseBits"),
Mf::ExtractBits => Function::Regular("extractBits"),
Mf::InsertBits => Function::Regular("insertBits"),
Mf::FindLsb => Function::Regular("firstTrailingBit"),
Mf::FindMsb => Function::Regular("firstLeadingBit"),
Mf::FirstTrailingBit => Function::Regular("firstTrailingBit"),
Mf::FirstLeadingBit => Function::Regular("firstLeadingBit"),
// data packing
Mf::Pack4x8snorm => Function::Regular("pack4x8snorm"),
Mf::Pack4x8unorm => Function::Regular("pack4x8unorm"),

View File

@@ -3,7 +3,6 @@ use crate::arena::{Arena, Handle};
pub struct ExpressionTracer<'tracer> {
pub constants: &'tracer Arena<crate::Constant>,
pub overrides: &'tracer Arena<crate::Override>,
/// The arena in which we are currently tracing expressions.
pub expressions: &'tracer Arena<crate::Expression>,

View File

@@ -4,7 +4,6 @@ use super::{FunctionMap, ModuleMap};
pub struct FunctionTracer<'a> {
pub function: &'a crate::Function,
pub constants: &'a crate::Arena<crate::Constant>,
pub overrides: &'a crate::Arena<crate::Override>,
pub types_used: &'a mut HandleSet<crate::Type>,
pub constants_used: &'a mut HandleSet<crate::Constant>,
@@ -48,7 +47,6 @@ impl<'a> FunctionTracer<'a> {
fn as_expression(&mut self) -> super::expressions::ExpressionTracer {
super::expressions::ExpressionTracer {
constants: self.constants,
overrides: self.overrides,
expressions: &self.function.expressions,
types_used: self.types_used,

View File

@@ -253,7 +253,6 @@ impl<'module> ModuleTracer<'module> {
expressions::ExpressionTracer {
expressions: &self.module.global_expressions,
constants: &self.module.constants,
overrides: &self.module.overrides,
types_used: &mut self.types_used,
constants_used: &mut self.constants_used,
expressions_used: &mut self.global_expressions_used,
@@ -268,7 +267,6 @@ impl<'module> ModuleTracer<'module> {
FunctionTracer {
function,
constants: &self.module.constants,
overrides: &self.module.overrides,
types_used: &mut self.types_used,
constants_used: &mut self.constants_used,
global_expressions_used: &mut self.global_expressions_used,

View File

@@ -646,8 +646,8 @@ fn inject_standard_builtins(
"bitfieldReverse" => MathFunction::ReverseBits,
"bitfieldExtract" => MathFunction::ExtractBits,
"bitfieldInsert" => MathFunction::InsertBits,
"findLSB" => MathFunction::FindLsb,
"findMSB" => MathFunction::FindMsb,
"findLSB" => MathFunction::FirstTrailingBit,
"findMSB" => MathFunction::FirstLeadingBit,
_ => unreachable!(),
};
@@ -695,8 +695,12 @@ fn inject_standard_builtins(
// we need to cast the return type of findLsb / findMsb
let mc = if scalar.kind == Sk::Uint {
match mc {
MacroCall::MathFunction(MathFunction::FindLsb) => MacroCall::FindLsbUint,
MacroCall::MathFunction(MathFunction::FindMsb) => MacroCall::FindMsbUint,
MacroCall::MathFunction(MathFunction::FirstTrailingBit) => {
MacroCall::FindLsbUint
}
MacroCall::MathFunction(MathFunction::FirstLeadingBit) => {
MacroCall::FindMsbUint
}
mc => mc,
}
} else {
@@ -1787,8 +1791,8 @@ impl MacroCall {
)?,
mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => {
let fun = match mc {
MacroCall::FindLsbUint => MathFunction::FindLsb,
MacroCall::FindMsbUint => MathFunction::FindMsb,
MacroCall::FindLsbUint => MathFunction::FirstTrailingBit,
MacroCall::FindMsbUint => MathFunction::FirstLeadingBit,
_ => unreachable!(),
};
let res = ctx.add_expression(

View File

@@ -275,7 +275,7 @@ where
Name: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
// Iterate backwards trough the scopes and try to find the variable
// Iterate backwards through the scopes and try to find the variable
for scope in self.scopes[..self.cursor].iter().rev() {
if let Some(var) = scope.get(name) {
return Some(var);

View File

@@ -3026,8 +3026,8 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
Glo::UnpackHalf2x16 => Mf::Unpack2x16float,
Glo::UnpackUnorm2x16 => Mf::Unpack2x16unorm,
Glo::UnpackSnorm2x16 => Mf::Unpack2x16snorm,
Glo::FindILsb => Mf::FindLsb,
Glo::FindUMsb | Glo::FindSMsb => Mf::FindMsb,
Glo::FindILsb => Mf::FirstTrailingBit,
Glo::FindUMsb | Glo::FindSMsb => Mf::FirstLeadingBit,
// TODO: https://github.com/gfx-rs/naga/issues/2526
Glo::Modf | Glo::Frexp => return Err(Error::UnsupportedExtInst(inst_id)),
Glo::IMix
@@ -3460,7 +3460,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
.insert(target, (case_body_idx, vec![literal as i32]));
}
// Loop trough the collected target blocks creating a new case for each
// Loop through the collected target blocks creating a new case for each
// literal pointing to it, only one case will have the true body and all the
// others will be empty fallthrough so that they all execute the same body
// without duplicating code.

View File

@@ -117,33 +117,6 @@ pub struct Function<'a> {
pub name: Ident<'a>,
pub arguments: Vec<FunctionArgument<'a>>,
pub result: Option<FunctionResult<'a>>,
/// Local variable and function argument arena.
///
/// Note that the `Local` here is actually a zero-sized type. The AST keeps
/// all the detailed information about locals - names, types, etc. - in
/// [`LocalDecl`] statements. For arguments, that information is kept in
/// [`arguments`]. This `Arena`'s only role is to assign a unique `Handle`
/// to each of them, and track their definitions' spans for use in
/// diagnostics.
///
/// In the AST, when an [`Ident`] expression refers to a local variable or
/// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this
/// arena.
///
/// During lowering, [`LocalDecl`] statements add entries to a per-function
/// table that maps `Handle<Local>` values to their Naga representations,
/// accessed via [`StatementContext::local_table`] and
/// [`RuntimeExpressionContext::local_table`]. This table is then consulted when
/// lowering subsequent [`Ident`] expressions.
///
/// [`LocalDecl`]: StatementKind::LocalDecl
/// [`arguments`]: Function::arguments
/// [`Ident`]: Expression::Ident
/// [`StatementContext::local_table`]: StatementContext::local_table
/// [`RuntimeExpressionContext::local_table`]: RuntimeExpressionContext::local_table
pub locals: Arena<Local>,
pub body: Block<'a>,
}

View File

@@ -235,8 +235,8 @@ pub fn map_standard_fun(word: &str) -> Option<crate::MathFunction> {
"reverseBits" => Mf::ReverseBits,
"extractBits" => Mf::ExtractBits,
"insertBits" => Mf::InsertBits,
"firstTrailingBit" => Mf::FindLsb,
"firstLeadingBit" => Mf::FindMsb,
"firstTrailingBit" => Mf::FirstTrailingBit,
"firstLeadingBit" => Mf::FirstLeadingBit,
// data packing
"pack4x8snorm" => Mf::Pack4x8snorm,
"pack4x8unorm" => Mf::Pack4x8unorm,

View File

@@ -37,9 +37,30 @@ struct ExpressionContext<'input, 'temp, 'out> {
/// [`Function::locals`]: ast::Function::locals
local_table: &'temp mut SymbolTable<&'input str, Handle<ast::Local>>,
/// The [`Function::locals`] arena for the function we're building.
/// Local variable and function argument arena for the function we're building.
///
/// [`Function::locals`]: ast::Function::locals
/// Note that the `Local` here is actually a zero-sized type. The AST keeps
/// all the detailed information about locals - names, types, etc. - in
/// [`LocalDecl`] statements. For arguments, that information is kept in
/// [`arguments`]. This `Arena`'s only role is to assign a unique `Handle`
/// to each of them, and track their definitions' spans for use in
/// diagnostics.
///
/// In the AST, when an [`Ident`] expression refers to a local variable or
/// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this
/// arena.
///
/// During lowering, [`LocalDecl`] statements add entries to a per-function
/// table that maps `Handle<Local>` values to their Naga representations,
/// accessed via [`StatementContext::local_table`] and
/// [`RuntimeExpressionContext::local_table`]. This table is then consulted when
/// lowering subsequent [`Ident`] expressions.
///
/// [`LocalDecl`]: StatementKind::LocalDecl
/// [`arguments`]: Function::arguments
/// [`Ident`]: Expression::Ident
/// [`StatementContext::local_table`]: StatementContext::local_table
/// [`RuntimeExpressionContext::local_table`]: RuntimeExpressionContext::local_table
locals: &'out mut Arena<ast::Local>,
/// Identifiers used by the current global declaration that have no local definition.
@@ -2158,7 +2179,6 @@ impl Parser {
arguments,
result,
body,
locals,
};
// done

View File

@@ -873,7 +873,7 @@ pub enum Literal {
}
/// Pipeline-overridable constant.
#[derive(Debug, Clone)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
@@ -891,8 +891,7 @@ pub struct Override {
}
/// Constant value.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
@@ -954,7 +953,7 @@ pub struct ResourceBinding {
}
/// Variable defined at module level.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
@@ -1198,8 +1197,8 @@ pub enum MathFunction {
ReverseBits,
ExtractBits,
InsertBits,
FindLsb,
FindMsb,
FirstTrailingBit,
FirstLeadingBit,
// data packing
Pack4x8snorm,
Pack4x8unorm,
@@ -1337,7 +1336,7 @@ bitflags::bitflags! {
const STORAGE = 1 << 0;
/// Barrier affects all [`AddressSpace::WorkGroup`] accesses.
const WORK_GROUP = 1 << 1;
/// Barrier synchronizes execution across all invocations within a subgroup that exectue this instruction.
/// Barrier synchronizes execution across all invocations within a subgroup that execute this instruction.
const SUB_GROUP = 1 << 2;
}
}
@@ -1354,8 +1353,7 @@ bitflags::bitflags! {
///
/// [`Constant`]: Expression::Constant
/// [`Override`]: Expression::Override
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]

View File

@@ -27,6 +27,8 @@ macro_rules! gen_component_wise_extractor {
scalar_kinds: [$( $scalar_kind:ident ),* $(,)?],
) => {
/// A subset of [`Literal`]s intended to be used for implementing numeric built-ins.
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
enum $target<const N: usize> {
$(
#[doc = concat!(
@@ -1231,6 +1233,12 @@ impl<'a> ConstantEvaluator<'a> {
crate::MathFunction::ReverseBits => {
component_wise_concrete_int!(self, span, [arg], |e| { Ok([e.reverse_bits()]) })
}
crate::MathFunction::FirstTrailingBit => {
component_wise_concrete_int(self, span, [arg], |ci| Ok(first_trailing_bit(ci)))
}
crate::MathFunction::FirstLeadingBit => {
component_wise_concrete_int(self, span, [arg], |ci| Ok(first_leading_bit(ci)))
}
fun => Err(ConstantEvaluatorError::NotImplemented(format!(
"{fun:?} built-in function"
@@ -2096,6 +2104,174 @@ impl<'a> ConstantEvaluator<'a> {
}
}
fn first_trailing_bit(concrete_int: ConcreteInt<1>) -> ConcreteInt<1> {
// NOTE: Bit indices for this built-in start at 0 at the "right" (or LSB). For example, a value
// of 1 means the least significant bit is set. Therefore, an input of `0x[80 00…]` would
// return a right-to-left bit index of 0.
let trailing_zeros_to_bit_idx = |e: u32| -> u32 {
match e {
idx @ 0..=31 => idx,
32 => u32::MAX,
_ => unreachable!(),
}
};
match concrete_int {
ConcreteInt::U32([e]) => ConcreteInt::U32([trailing_zeros_to_bit_idx(e.trailing_zeros())]),
ConcreteInt::I32([e]) => {
ConcreteInt::I32([trailing_zeros_to_bit_idx(e.trailing_zeros()) as i32])
}
}
}
#[test]
fn first_trailing_bit_smoke() {
assert_eq!(
first_trailing_bit(ConcreteInt::I32([0])),
ConcreteInt::I32([-1])
);
assert_eq!(
first_trailing_bit(ConcreteInt::I32([1])),
ConcreteInt::I32([0])
);
assert_eq!(
first_trailing_bit(ConcreteInt::I32([2])),
ConcreteInt::I32([1])
);
assert_eq!(
first_trailing_bit(ConcreteInt::I32([-1])),
ConcreteInt::I32([0]),
);
assert_eq!(
first_trailing_bit(ConcreteInt::I32([i32::MIN])),
ConcreteInt::I32([31]),
);
assert_eq!(
first_trailing_bit(ConcreteInt::I32([i32::MAX])),
ConcreteInt::I32([0]),
);
for idx in 0..32 {
assert_eq!(
first_trailing_bit(ConcreteInt::I32([1 << idx])),
ConcreteInt::I32([idx])
)
}
assert_eq!(
first_trailing_bit(ConcreteInt::U32([0])),
ConcreteInt::U32([u32::MAX])
);
assert_eq!(
first_trailing_bit(ConcreteInt::U32([1])),
ConcreteInt::U32([0])
);
assert_eq!(
first_trailing_bit(ConcreteInt::U32([2])),
ConcreteInt::U32([1])
);
assert_eq!(
first_trailing_bit(ConcreteInt::U32([1 << 31])),
ConcreteInt::U32([31]),
);
assert_eq!(
first_trailing_bit(ConcreteInt::U32([u32::MAX])),
ConcreteInt::U32([0]),
);
for idx in 0..32 {
assert_eq!(
first_trailing_bit(ConcreteInt::U32([1 << idx])),
ConcreteInt::U32([idx])
)
}
}
fn first_leading_bit(concrete_int: ConcreteInt<1>) -> ConcreteInt<1> {
// NOTE: Bit indices for this built-in start at 0 at the "right" (or LSB). For example, 1 means
// the least significant bit is set. Therefore, an input of 1 would return a right-to-left bit
// index of 0.
let rtl_to_ltr_bit_idx = |e: u32| -> u32 {
match e {
idx @ 0..=31 => 31 - idx,
32 => u32::MAX,
_ => unreachable!(),
}
};
match concrete_int {
ConcreteInt::I32([e]) => ConcreteInt::I32([{
let rtl_bit_index = if e.is_negative() {
e.leading_ones()
} else {
e.leading_zeros()
};
rtl_to_ltr_bit_idx(rtl_bit_index) as i32
}]),
ConcreteInt::U32([e]) => ConcreteInt::U32([rtl_to_ltr_bit_idx(e.leading_zeros())]),
}
}
#[test]
fn first_leading_bit_smoke() {
assert_eq!(
first_leading_bit(ConcreteInt::I32([-1])),
ConcreteInt::I32([-1])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([0])),
ConcreteInt::I32([-1])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([1])),
ConcreteInt::I32([0])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([-2])),
ConcreteInt::I32([0])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([1234 + 4567])),
ConcreteInt::I32([12])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([i32::MAX])),
ConcreteInt::I32([30])
);
assert_eq!(
first_leading_bit(ConcreteInt::I32([i32::MIN])),
ConcreteInt::I32([30])
);
// NOTE: Ignore the sign bit, which is a separate (above) case.
for idx in 0..(32 - 1) {
assert_eq!(
first_leading_bit(ConcreteInt::I32([1 << idx])),
ConcreteInt::I32([idx])
);
}
for idx in 1..(32 - 1) {
assert_eq!(
first_leading_bit(ConcreteInt::I32([-(1 << idx)])),
ConcreteInt::I32([idx - 1])
);
}
assert_eq!(
first_leading_bit(ConcreteInt::U32([0])),
ConcreteInt::U32([u32::MAX])
);
assert_eq!(
first_leading_bit(ConcreteInt::U32([1])),
ConcreteInt::U32([0])
);
assert_eq!(
first_leading_bit(ConcreteInt::U32([u32::MAX])),
ConcreteInt::U32([31])
);
for idx in 0..32 {
assert_eq!(
first_leading_bit(ConcreteInt::U32([1 << idx])),
ConcreteInt::U32([idx])
)
}
}
/// Trait for conversions of abstract values to concrete types.
trait TryFromAbstract<T>: Sized {
/// Convert an abstract literal `value` to `Self`.

View File

@@ -112,22 +112,16 @@ pub struct BoundsCheckPolicies {
/// This controls the behavior of [`ImageLoad`] expressions when a coordinate,
/// texture array index, level of detail, or multisampled sample number is out of range.
///
/// There is no corresponding policy for [`ImageStore`] statements. All the
/// platforms we support already discard out-of-bounds image stores,
/// effectively implementing the "skip write" part of [`ReadZeroSkipWrite`].
///
/// [`ImageLoad`]: crate::Expression::ImageLoad
/// [`ImageStore`]: crate::Statement::ImageStore
/// [`ReadZeroSkipWrite`]: BoundsCheckPolicy::ReadZeroSkipWrite
#[cfg_attr(feature = "deserialize", serde(default))]
pub image_load: BoundsCheckPolicy,
/// How should the generated code handle image texel stores that are out
/// of range?
///
/// This controls the behavior of [`ImageStore`] statements when a coordinate,
/// texture array index, level of detail, or multisampled sample number is out of range.
///
/// This policy should't be needed since all backends should ignore OOB writes.
///
/// [`ImageStore`]: crate::Statement::ImageStore
#[cfg_attr(feature = "deserialize", serde(default))]
pub image_store: BoundsCheckPolicy,
/// How should the generated code handle binding array indexes that are out of bounds.
#[cfg_attr(feature = "deserialize", serde(default))]
pub binding_array: BoundsCheckPolicy,
@@ -173,10 +167,7 @@ impl BoundsCheckPolicies {
/// Return `true` if any of `self`'s policies are `policy`.
pub fn contains(&self, policy: BoundsCheckPolicy) -> bool {
self.index == policy
|| self.buffer == policy
|| self.image_load == policy
|| self.image_store == policy
self.index == policy || self.buffer == policy || self.image_load == policy
}
}

View File

@@ -484,8 +484,8 @@ impl super::MathFunction {
Self::ReverseBits => 1,
Self::ExtractBits => 3,
Self::InsertBits => 4,
Self::FindLsb => 1,
Self::FindMsb => 1,
Self::FirstTrailingBit => 1,
Self::FirstLeadingBit => 1,
// data packing
Self::Pack4x8snorm => 1,
Self::Pack4x8unorm => 1,

View File

@@ -788,8 +788,8 @@ impl<'a> ResolveContext<'a> {
Mf::ReverseBits |
Mf::ExtractBits |
Mf::InsertBits |
Mf::FindLsb |
Mf::FindMsb => match *res_arg.inner_with(types) {
Mf::FirstTrailingBit |
Mf::FirstLeadingBit => match *res_arg.inner_with(types) {
Ti::Scalar(scalar @ crate::Scalar {
kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
..

View File

@@ -1350,8 +1350,8 @@ impl super::Validator {
| Mf::CountTrailingZeros
| Mf::CountOneBits
| Mf::ReverseBits
| Mf::FindMsb
| Mf::FindLsb => {
| Mf::FirstLeadingBit
| Mf::FirstTrailingBit => {
if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun));
}

File diff suppressed because one or more lines are too long

View File

@@ -11,9 +11,9 @@
[package]
edition = "2021"
rust-version = "1.74"
rust-version = "1.76"
name = "wgpu-core"
version = "0.20.0"
version = "22.0.0"
authors = ["gfx-rs developers"]
description = "WebGPU core logic on wgpu-hal"
homepage = "https://wgpu.rs/"
@@ -41,29 +41,30 @@ targets = [
[dependencies]
arrayvec = "0.7"
bit-vec = "0.7"
bitflags = "2"
bit-vec = "0.8"
bitflags = "2.6"
document-features = "0.2.10"
indexmap = "2"
log = "0.4"
once_cell = "1"
once_cell = "1.19.0"
parking_lot = ">=0.11, <0.13"
rustc-hash = "1.1"
rustc-hash = "1.1.0"
smallvec = "1"
thiserror = "1"
thiserror = "1.0.63"
[dependencies.bytemuck]
version = "1.16"
features = ["derive"]
optional = true
[dependencies.hal]
version = "0.20.0"
version = "22.0.0"
path = "../wgpu-hal"
default-features = false
package = "wgpu-hal"
[dependencies.naga]
version = "0.20.0"
version = "22.0.0"
path = "../naga"
[dependencies.profiling]
@@ -84,7 +85,7 @@ features = ["derive"]
optional = true
[dependencies.wgt]
version = "0.20.0"
version = "22.0.0"
path = "../wgpu-types"
package = "wgpu-types"

View File

@@ -142,43 +142,38 @@ mod compat {
let mut errors = Vec::new();
let mut expected_bgl_entries = expected_bgl.entries.iter();
let mut assigned_bgl_entries = assigned_bgl.entries.iter();
let zipped = crate::utils::ZipWithProperAdvance::new(
&mut expected_bgl_entries,
&mut assigned_bgl_entries,
);
for ((&binding, expected_entry), (_, assigned_entry)) in zipped {
if assigned_entry.visibility != expected_entry.visibility {
errors.push(EntryError::Visibility {
binding,
expected: expected_entry.visibility,
assigned: assigned_entry.visibility,
});
}
if assigned_entry.ty != expected_entry.ty {
errors.push(EntryError::Type {
binding,
expected: expected_entry.ty,
assigned: assigned_entry.ty,
});
}
if assigned_entry.count != expected_entry.count {
errors.push(EntryError::Count {
binding,
expected: expected_entry.count,
assigned: assigned_entry.count,
});
for (&binding, expected_entry) in expected_bgl.entries.iter() {
if let Some(assigned_entry) = assigned_bgl.entries.get(binding) {
if assigned_entry.visibility != expected_entry.visibility {
errors.push(EntryError::Visibility {
binding,
expected: expected_entry.visibility,
assigned: assigned_entry.visibility,
});
}
if assigned_entry.ty != expected_entry.ty {
errors.push(EntryError::Type {
binding,
expected: expected_entry.ty,
assigned: assigned_entry.ty,
});
}
if assigned_entry.count != expected_entry.count {
errors.push(EntryError::Count {
binding,
expected: expected_entry.count,
assigned: assigned_entry.count,
});
}
} else {
errors.push(EntryError::ExtraExpected { binding });
}
}
for (&binding, _) in expected_bgl_entries {
errors.push(EntryError::ExtraExpected { binding });
}
for (&binding, _) in assigned_bgl_entries {
errors.push(EntryError::ExtraAssigned { binding });
for (&binding, _) in assigned_bgl.entries.iter() {
if !expected_bgl.entries.contains_key(binding) {
errors.push(EntryError::ExtraAssigned { binding });
}
}
Err(Error::Incompatible {
@@ -253,6 +248,7 @@ mod compat {
.filter_map(|(i, e)| if e.is_active() { Some(i) } else { None })
}
#[allow(clippy::result_large_err)]
pub fn get_invalid(&self) -> Result<(), (usize, Error)> {
for (index, entry) in self.entries.iter().enumerate() {
entry.check().map_err(|e| (index, e))?;
@@ -387,8 +383,6 @@ impl<A: HalApi> Binder<A> {
bind_group: &Arc<BindGroup<A>>,
offsets: &[wgt::DynamicOffset],
) -> &'a [EntryPayload<A>] {
log::trace!("\tBinding [{}] = group {}", index, bind_group.error_ident());
let payload = &mut self.payloads[index];
payload.group = Some(bind_group.clone());
payload.dynamic_offsets.clear();

View File

@@ -269,8 +269,6 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder, A: HalApi>
.set_and_remove_from_usage_scope_sparse(&mut self.scope.buffers, indirect_buffer);
}
log::trace!("Encoding dispatch barriers");
CommandBuffer::drain_barriers(
self.raw_encoder,
&mut self.intermediate_trackers,
@@ -321,10 +319,6 @@ impl Global {
);
};
if let Err(e) = query_set.same_device_as(cmd_buf.as_ref()) {
return make_err(e.into(), arc_desc);
}
Some(ArcPassTimestampWrites {
query_set,
beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
@@ -501,6 +495,10 @@ impl Global {
state.tracker.query_sets.set_size(indices.query_sets.size());
let timestamp_writes = if let Some(tw) = timestamp_writes.take() {
tw.query_set
.same_device_as(cmd_buf)
.map_pass_err(pass_scope)?;
let query_set = state.tracker.query_sets.insert_single(tw.query_set);
// Unlike in render passes we can't delay resetting the query sets since

View File

@@ -506,7 +506,6 @@ impl<A: HalApi> CommandBuffer<A> {
}
pub(crate) fn extract_baked_commands(&mut self) -> BakedCommands<A> {
log::trace!("Extracting BakedCommands from {}", self.error_ident());
let data = self.data.lock().take().unwrap();
BakedCommands {
encoder: data.encoder.raw,
@@ -626,7 +625,6 @@ impl Global {
cmd_buf_data.status = CommandEncoderStatus::Finished;
//Note: if we want to stop tracking the swapchain texture view,
// this is the place to do it.
log::trace!("Command buffer {:?}", encoder_id);
None
}
}

View File

@@ -817,7 +817,7 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
}
fn start(
device: &'d Device<A>,
device: &'d Arc<Device<A>>,
hal_label: Option<&str>,
color_attachments: ArrayVec<
Option<ArcRenderPassColorAttachment<A>>,
@@ -919,6 +919,7 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
if let Some(at) = depth_stencil_attachment.as_ref() {
let view = &at.view;
view.same_device(device)?;
check_multiview(view)?;
add_view(view, AttachmentErrorLocation::Depth)?;
@@ -1049,6 +1050,7 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
continue;
};
let color_view: &TextureView<A> = &at.view;
color_view.same_device(device)?;
check_multiview(color_view)?;
add_view(
color_view,
@@ -1079,6 +1081,7 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
let mut hal_resolve_target = None;
if let Some(resolve_view) = &at.resolve_target {
resolve_view.same_device(device)?;
check_multiview(resolve_view)?;
let resolve_location = AttachmentErrorLocation::Color {
@@ -1178,8 +1181,9 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
multiview,
};
let timestamp_writes_hal = timestamp_writes.as_ref().map(|tw| {
let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {
let query_set = &tw.query_set;
query_set.same_device(device)?;
if let Some(index) = tw.beginning_of_pass_write_index {
pending_query_resets.use_query_set(query_set, index);
@@ -1188,16 +1192,21 @@ impl<'d, A: HalApi> RenderPassInfo<'d, A> {
pending_query_resets.use_query_set(query_set, index);
}
hal::RenderPassTimestampWrites {
Some(hal::RenderPassTimestampWrites {
query_set: query_set.raw.as_ref().unwrap(),
beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
end_of_pass_write_index: tw.end_of_pass_write_index,
}
});
})
} else {
None
};
let occlusion_query_set_hal = occlusion_query_set
.as_ref()
.map(|query_set| query_set.raw.as_ref().unwrap());
let occlusion_query_set_hal = if let Some(query_set) = occlusion_query_set.as_ref() {
query_set.same_device(device)?;
Some(query_set.raw.as_ref().unwrap())
} else {
None
};
let hal_desc = hal::RenderPassDescriptor {
label: hal_label,
@@ -1331,7 +1340,6 @@ impl Global {
) -> (RenderPass<A>, Option<CommandEncoderError>) {
fn fill_arc_desc<A: HalApi>(
hub: &crate::hub::Hub<A>,
device: &Arc<Device<A>>,
desc: &RenderPassDescriptor<'_>,
arc_desc: &mut ArcRenderPassDescriptor<A>,
) -> Result<(), CommandEncoderError> {
@@ -1348,13 +1356,11 @@ impl Global {
let view = texture_views
.get_owned(*view_id)
.map_err(|_| CommandEncoderError::InvalidAttachmentId(*view_id))?;
view.same_device(device)?;
let resolve_target = if let Some(resolve_target_id) = resolve_target {
let rt_arc = texture_views.get_owned(*resolve_target_id).map_err(|_| {
CommandEncoderError::InvalidResolveTargetId(*resolve_target_id)
})?;
rt_arc.same_device(device)?;
Some(rt_arc)
} else {
@@ -1382,7 +1388,6 @@ impl Global {
depth_stencil_attachment.view,
)
})?;
view.same_device(device)?;
Some(ArcRenderPassDepthStencilAttachment {
view,
@@ -1397,7 +1402,6 @@ impl Global {
let query_set = query_sets.get_owned(tw.query_set).map_err(|_| {
CommandEncoderError::InvalidTimestampWritesQuerySetId(tw.query_set)
})?;
query_set.same_device(device)?;
Some(ArcPassTimestampWrites {
query_set,
@@ -1413,7 +1417,6 @@ impl Global {
let query_set = query_sets.get_owned(occlusion_query_set).map_err(|_| {
CommandEncoderError::InvalidOcclusionQuerySetId(occlusion_query_set)
})?;
query_set.same_device(device)?;
Some(query_set)
} else {
@@ -1444,7 +1447,7 @@ impl Global {
Err(e) => return make_err(e, arc_desc),
};
let err = fill_arc_desc(hub, &cmd_buf.device, desc, &mut arc_desc).err();
let err = fill_arc_desc(hub, desc, &mut arc_desc).err();
(RenderPass::new(Some(cmd_buf), arc_desc), err)
}
@@ -1563,7 +1566,7 @@ impl Global {
profiling::scope!(
"CommandEncoder::run_render_pass {}",
base.label.unwrap_or("")
base.label.as_deref().unwrap_or("")
);
let Some(cmd_buf) = pass.parent.as_ref() else {
@@ -1597,8 +1600,6 @@ impl Global {
*status = CommandEncoderStatus::Error;
encoder.open_pass(hal_label).map_pass_err(pass_scope)?;
log::trace!("Encoding render pass begin in {}", cmd_buf.error_ident());
let info = RenderPassInfo::start(
device,
hal_label,
@@ -1788,7 +1789,7 @@ impl Global {
},
indexed,
};
multi_draw_indirect(&mut state, buffer, offset, count, indexed)
multi_draw_indirect(&mut state, cmd_buf, buffer, offset, count, indexed)
.map_pass_err(scope)?;
}
ArcRenderCommand::MultiDrawIndirectCount {
@@ -1805,6 +1806,7 @@ impl Global {
};
multi_draw_indirect_count(
&mut state,
cmd_buf,
buffer,
offset,
count_buffer,
@@ -1831,6 +1833,7 @@ impl Global {
let scope = PassErrorScope::WriteTimestamp;
write_timestamp(
&mut state,
cmd_buf,
&mut cmd_buf_data.pending_query_resets,
query_set,
query_index,
@@ -1902,7 +1905,6 @@ impl Global {
}
}
log::trace!("Merging renderpass into {}", cmd_buf.error_ident());
let (trackers, pending_discard_init_fixups) = state
.info
.finish(state.raw_encoder, state.snatch_guard)
@@ -2445,6 +2447,7 @@ fn draw_indexed<A: HalApi>(
fn multi_draw_indirect<A: HalApi>(
state: &mut State<A>,
cmd_buf: &Arc<CommandBuffer<A>>,
indirect_buffer: Arc<crate::resource::Buffer<A>>,
offset: u64,
count: Option<NonZeroU32>,
@@ -2471,6 +2474,8 @@ fn multi_draw_indirect<A: HalApi>(
.device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
indirect_buffer.same_device_as(cmd_buf.as_ref())?;
state
.info
.usage_scope
@@ -2517,6 +2522,7 @@ fn multi_draw_indirect<A: HalApi>(
fn multi_draw_indirect_count<A: HalApi>(
state: &mut State<A>,
cmd_buf: &Arc<CommandBuffer<A>>,
indirect_buffer: Arc<crate::resource::Buffer<A>>,
offset: u64,
count_buffer: Arc<crate::resource::Buffer<A>>,
@@ -2544,6 +2550,9 @@ fn multi_draw_indirect_count<A: HalApi>(
.device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
indirect_buffer.same_device_as(cmd_buf.as_ref())?;
count_buffer.same_device_as(cmd_buf.as_ref())?;
state
.info
.usage_scope
@@ -2674,6 +2683,7 @@ fn insert_debug_marker<A: HalApi>(state: &mut State<A>, string_data: &[u8], len:
fn write_timestamp<A: HalApi>(
state: &mut State<A>,
cmd_buf: &CommandBuffer<A>,
pending_query_resets: &mut QueryResetMap<A>,
query_set: Arc<QuerySet<A>>,
query_index: u32,
@@ -2683,6 +2693,8 @@ fn write_timestamp<A: HalApi>(
query_set.error_ident()
);
query_set.same_device_as(cmd_buf)?;
state
.device
.require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES)?;

View File

@@ -225,7 +225,7 @@ pub(crate) fn validate_linear_texture_data(
// the copy size before calling this function (for example via `validate_texture_copy_range`).
let copy_width = copy_size.width as BufferAddress;
let copy_height = copy_size.height as BufferAddress;
let copy_depth = copy_size.depth_or_array_layers as BufferAddress;
let depth_or_array_layers = copy_size.depth_or_array_layers as BufferAddress;
let offset = layout.offset;
@@ -253,19 +253,19 @@ pub(crate) fn validate_linear_texture_data(
}
bytes_per_row
} else {
if copy_depth > 1 || height_in_blocks > 1 {
if depth_or_array_layers > 1 || height_in_blocks > 1 {
return Err(TransferError::UnspecifiedBytesPerRow);
}
0
};
let block_rows_per_image = if let Some(rows_per_image) = layout.rows_per_image {
let rows_per_image = if let Some(rows_per_image) = layout.rows_per_image {
let rows_per_image = rows_per_image as BufferAddress;
if rows_per_image < height_in_blocks {
return Err(TransferError::InvalidRowsPerImage);
}
rows_per_image
} else {
if copy_depth > 1 {
if depth_or_array_layers > 1 {
return Err(TransferError::UnspecifiedRowsPerImage);
}
0
@@ -287,12 +287,12 @@ pub(crate) fn validate_linear_texture_data(
}
}
let bytes_per_image = bytes_per_row * block_rows_per_image;
let bytes_per_image = bytes_per_row * rows_per_image;
let required_bytes_in_copy = if copy_depth == 0 {
let required_bytes_in_copy = if depth_or_array_layers == 0 {
0
} else {
let mut required_bytes_in_copy = bytes_per_image * (copy_depth - 1);
let mut required_bytes_in_copy = bytes_per_image * (depth_or_array_layers - 1);
if height_in_blocks > 0 {
required_bytes_in_copy += bytes_per_row * (height_in_blocks - 1) + bytes_in_last_row;
}

View File

@@ -34,7 +34,7 @@ impl AnyDevice {
unsafe fn drop_glue<A: HalApi>(ptr: *mut ()) {
// Drop the arc this instance is holding.
unsafe {
_ = Arc::from_raw(ptr.cast::<A::Device>());
_ = Arc::from_raw(ptr.cast::<Device<A>>());
}
}

View File

@@ -126,4 +126,9 @@ impl EntryMap {
self.sorted = false;
self.inner.entry(key)
}
pub fn sort(&mut self) {
self.inner.sort_unstable_keys();
self.sorted = true;
}
}

View File

@@ -19,7 +19,6 @@ use crate::{
present,
resource::{
self, BufferAccessError, BufferAccessResult, BufferMapOperation, CreateBufferError,
Trackable,
},
storage::Storage,
Label,
@@ -31,8 +30,7 @@ use wgt::{BufferAddress, TextureFormat};
use std::{
borrow::Cow,
iter,
ptr::{self, NonNull},
ptr::NonNull,
sync::{atomic::Ordering, Arc},
};
@@ -253,120 +251,46 @@ impl Global {
}
#[cfg(feature = "replay")]
pub fn device_wait_for_buffer<A: HalApi>(
pub fn device_set_buffer_data<A: HalApi>(
&self,
device_id: DeviceId,
buffer_id: id::BufferId,
) -> Result<(), WaitIdleError> {
let hub = A::hub(self);
let last_submission = match hub.buffers.read().get(buffer_id) {
Ok(buffer) => buffer.submission_index(),
Err(_) => return Ok(()),
};
hub.devices
.get(device_id)
.map_err(|_| DeviceError::InvalidDeviceId)?
.wait_for_submit(last_submission)
}
#[doc(hidden)]
pub fn device_set_buffer_sub_data<A: HalApi>(
&self,
device_id: DeviceId,
buffer_id: id::BufferId,
offset: BufferAddress,
data: &[u8],
) -> BufferAccessResult {
profiling::scope!("Device::set_buffer_sub_data");
let hub = A::hub(self);
let device = hub
.devices
.get(device_id)
.map_err(|_| DeviceError::InvalidDeviceId)?;
let buffer = hub
.buffers
.get(buffer_id)
.map_err(|_| BufferAccessError::InvalidBufferId(buffer_id))?;
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *device.trace.lock() {
let data_path = trace.make_binary("bin", data);
trace.add(trace::Action::WriteBuffer {
id: buffer_id,
data: data_path,
range: offset..offset + data.len() as BufferAddress,
queued: false,
});
}
let device = &buffer.device;
device.check_is_valid()?;
buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;
//assert!(buffer isn't used by the GPU);
let snatch_guard = device.snatchable_lock.read();
let raw_buf = buffer.try_raw(&snatch_guard)?;
unsafe {
let mapping = device
.raw()
.map_buffer(raw_buf, offset..offset + data.len() as u64)
.map_err(DeviceError::from)?;
ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len());
if !mapping.is_coherent {
device
.raw()
.flush_mapped_ranges(raw_buf, iter::once(offset..offset + data.len() as u64));
}
device.raw().unmap_buffer(raw_buf);
let last_submission = device
.lock_life()
.get_buffer_latest_submission_index(&buffer);
if let Some(last_submission) = last_submission {
device.wait_for_submit(last_submission)?;
}
Ok(())
}
#[doc(hidden)]
pub fn device_get_buffer_sub_data<A: HalApi>(
&self,
device_id: DeviceId,
buffer_id: id::BufferId,
offset: BufferAddress,
data: &mut [u8],
) -> BufferAccessResult {
profiling::scope!("Device::get_buffer_sub_data");
let hub = A::hub(self);
let device = hub
.devices
.get(device_id)
.map_err(|_| DeviceError::InvalidDeviceId)?;
device.check_is_valid()?;
let snatch_guard = device.snatchable_lock.read();
let buffer = hub
.buffers
.get(buffer_id)
.map_err(|_| BufferAccessError::InvalidBufferId(buffer_id))?;
buffer.check_usage(wgt::BufferUsages::MAP_READ)?;
//assert!(buffer isn't used by the GPU);
let raw_buf = buffer.try_raw(&snatch_guard)?;
unsafe {
let mapping = device
.raw()
.map_buffer(raw_buf, offset..offset + data.len() as u64)
.map_err(DeviceError::from)?;
std::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len());
if !mapping.is_coherent {
device.raw().invalidate_mapped_ranges(
device.raw().flush_mapped_ranges(
raw_buf,
iter::once(offset..offset + data.len() as u64),
std::iter::once(offset..offset + data.len() as u64),
);
}
ptr::copy_nonoverlapping(mapping.ptr.as_ptr(), data.as_mut_ptr(), data.len());
device.raw().unmap_buffer(raw_buf);
}
@@ -400,7 +324,7 @@ impl Global {
buffer.destroy()
}
pub fn buffer_drop<A: HalApi>(&self, buffer_id: id::BufferId, wait: bool) {
pub fn buffer_drop<A: HalApi>(&self, buffer_id: id::BufferId) {
profiling::scope!("Buffer::drop");
api_log!("Buffer::drop {buffer_id:?}");
@@ -422,14 +346,6 @@ impl Global {
#[cfg(feature = "trace")]
buffer_id,
);
if wait {
let last_submit_index = buffer.submission_index();
match buffer.device.wait_for_submit(last_submit_index) {
Ok(()) => (),
Err(e) => log::error!("Failed to wait for buffer {:?}: {}", buffer_id, e),
}
}
}
pub fn device_create_texture<A: HalApi>(
@@ -586,25 +502,17 @@ impl Global {
texture.destroy()
}
pub fn texture_drop<A: HalApi>(&self, texture_id: id::TextureId, wait: bool) {
pub fn texture_drop<A: HalApi>(&self, texture_id: id::TextureId) {
profiling::scope!("Texture::drop");
api_log!("Texture::drop {texture_id:?}");
let hub = A::hub(self);
if let Some(texture) = hub.textures.unregister(texture_id) {
if let Some(_texture) = hub.textures.unregister(texture_id) {
#[cfg(feature = "trace")]
if let Some(t) = texture.device.trace.lock().as_mut() {
if let Some(t) = _texture.device.trace.lock().as_mut() {
t.add(trace::Action::DestroyTexture(texture_id));
}
if wait {
let last_submit_index = texture.submission_index();
match texture.device.wait_for_submit(last_submit_index) {
Ok(()) => (),
Err(e) => log::error!("Failed to wait for texture {texture_id:?}: {e}"),
}
}
}
}
@@ -658,28 +566,17 @@ impl Global {
pub fn texture_view_drop<A: HalApi>(
&self,
texture_view_id: id::TextureViewId,
wait: bool,
) -> Result<(), resource::TextureViewDestroyError> {
profiling::scope!("TextureView::drop");
api_log!("TextureView::drop {texture_view_id:?}");
let hub = A::hub(self);
if let Some(view) = hub.texture_views.unregister(texture_view_id) {
if let Some(_view) = hub.texture_views.unregister(texture_view_id) {
#[cfg(feature = "trace")]
if let Some(t) = view.device.trace.lock().as_mut() {
if let Some(t) = _view.device.trace.lock().as_mut() {
t.add(trace::Action::DestroyTextureView(texture_view_id));
}
if wait {
let last_submit_index = view.submission_index();
match view.device.wait_for_submit(last_submit_index) {
Ok(()) => (),
Err(e) => {
log::error!("Failed to wait for texture view {texture_view_id:?}: {e}")
}
}
}
}
Ok(())
}
@@ -1143,7 +1040,7 @@ impl Global {
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *device.trace.lock() {
let data = trace.make_binary("spv", unsafe {
std::slice::from_raw_parts(source.as_ptr() as *const u8, source.len() * 4)
std::slice::from_raw_parts(source.as_ptr().cast::<u8>(), source.len() * 4)
});
trace.add(trace::Action::CreateShaderModule {
id: fid.id(),
@@ -1457,7 +1354,6 @@ impl Global {
.vertex
.stage
.zero_initialize_workgroup_memory,
vertex_pulling_transform: desc.vertex.stage.vertex_pulling_transform,
};
ResolvedVertexState {
stage,
@@ -1484,7 +1380,6 @@ impl Global {
.vertex
.stage
.zero_initialize_workgroup_memory,
vertex_pulling_transform: state.stage.vertex_pulling_transform,
};
Some(ResolvedFragmentState {
stage,
@@ -1693,7 +1588,6 @@ impl Global {
entry_point: desc.stage.entry_point.clone(),
constants: desc.stage.constants.clone(),
zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
vertex_pulling_transform: desc.stage.vertex_pulling_transform,
};
let desc = ResolvedComputePipelineDescriptor {
@@ -1985,7 +1879,10 @@ impl Global {
config.composite_alpha_mode = new_alpha_mode;
}
if !caps.usage.contains(config.usage) {
return Err(E::UnsupportedUsage);
return Err(E::UnsupportedUsage {
requested: config.usage,
available: caps.usage,
});
}
if width == 0 || height == 0 {
return Err(E::ZeroArea);
@@ -2347,11 +2244,7 @@ impl Global {
// need to wait for submissions or triage them. We know we were
// just polled, so `life_tracker.free_resources` is empty.
debug_assert!(device.lock_life().queue_empty());
{
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
pending_writes.deactivate();
}
device.pending_writes.lock().deactivate();
drop(device);
}
@@ -2435,6 +2328,17 @@ impl Global {
}
}
pub fn device_generate_allocator_report<A: HalApi>(
&self,
device_id: DeviceId,
) -> Option<wgt::AllocatorReport> {
let hub = A::hub(self);
hub.devices
.get(device_id)
.ok()
.and_then(|device| device.generate_allocator_report())
}
pub fn queue_drop<A: HalApi>(&self, queue_id: QueueId) {
profiling::scope!("Queue::drop");
api_log!("Queue::drop {queue_id:?}");

View File

@@ -5,7 +5,7 @@ use crate::{
},
hal_api::HalApi,
id,
resource::{self, Buffer, Labeled, Trackable},
resource::{self, Buffer, Texture, Trackable},
snatch::SnatchGuard,
SubmissionIndex,
};
@@ -55,6 +55,58 @@ struct ActiveSubmission<A: HalApi> {
work_done_closures: SmallVec<[SubmittedWorkDoneClosure; 1]>,
}
impl<A: HalApi> ActiveSubmission<A> {
/// Returns true if this submission contains the given buffer.
///
/// This only uses constant-time operations.
pub fn contains_buffer(&self, buffer: &Buffer<A>) -> bool {
for encoder in &self.encoders {
// The ownership location of buffers depends on where the command encoder
// came from. If it is the staging command encoder on the queue, it is
// in the pending buffer list. If it came from a user command encoder,
// it is in the tracker.
if encoder.trackers.buffers.contains(buffer) {
return true;
}
if encoder
.pending_buffers
.contains_key(&buffer.tracker_index())
{
return true;
}
}
false
}
/// Returns true if this submission contains the given texture.
///
/// This only uses constant-time operations.
pub fn contains_texture(&self, texture: &Texture<A>) -> bool {
for encoder in &self.encoders {
// The ownership location of textures depends on where the command encoder
// came from. If it is the staging command encoder on the queue, it is
// in the pending buffer list. If it came from a user command encoder,
// it is in the tracker.
if encoder.trackers.textures.contains(texture) {
return true;
}
if encoder
.pending_textures
.contains_key(&texture.tracker_index())
{
return true;
}
}
false
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum WaitIdleError {
@@ -165,6 +217,40 @@ impl<A: HalApi> LifetimeTracker<A> {
self.mapped.push(value.clone());
}
/// Returns the submission index of the most recent submission that uses the
/// given buffer.
pub fn get_buffer_latest_submission_index(
&self,
buffer: &Buffer<A>,
) -> Option<SubmissionIndex> {
// We iterate in reverse order, so that we can bail out early as soon
// as we find a hit.
self.active.iter().rev().find_map(|submission| {
if submission.contains_buffer(buffer) {
Some(submission.index)
} else {
None
}
})
}
/// Returns the submission index of the most recent submission that uses the
/// given texture.
pub fn get_texture_latest_submission_index(
&self,
texture: &Texture<A>,
) -> Option<SubmissionIndex> {
// We iterate in reverse order, so that we can bail out early as soon
// as we find a hit.
self.active.iter().rev().find_map(|submission| {
if submission.contains_texture(texture) {
Some(submission.index)
} else {
None
}
})
}
/// Sort out the consequences of completed submissions.
///
/// Assume that all submissions up through `last_done` have completed.
@@ -197,7 +283,6 @@ impl<A: HalApi> LifetimeTracker<A> {
let mut work_done_closures: SmallVec<_> = self.work_done_closures.drain(..).collect();
for a in self.active.drain(..done_count) {
log::debug!("Active submission {} is done", a.index);
self.ready_to_map.extend(a.mapped);
for encoder in a.encoders {
let raw = unsafe { encoder.land() };
@@ -236,9 +321,7 @@ impl<A: HalApi> LifetimeTracker<A> {
}
}
}
}
impl<A: HalApi> LifetimeTracker<A> {
/// Determine which buffers are ready to map, and which must wait for the
/// GPU.
///
@@ -249,17 +332,13 @@ impl<A: HalApi> LifetimeTracker<A> {
}
for buffer in self.mapped.drain(..) {
let submit_index = buffer.submission_index();
log::trace!(
"Mapping of {} at submission {:?} gets assigned to active {:?}",
buffer.error_ident(),
submit_index,
self.active.iter().position(|a| a.index == submit_index)
);
self.active
let submission = self
.active
.iter_mut()
.find(|a| a.index == submit_index)
.rev()
.find(|a| a.contains_buffer(&buffer));
submission
.map_or(&mut self.ready_to_map, |a| &mut a.mapped)
.push(buffer);
}
@@ -283,8 +362,6 @@ impl<A: HalApi> LifetimeTracker<A> {
Vec::with_capacity(self.ready_to_map.len());
for buffer in self.ready_to_map.drain(..) {
let tracker_index = buffer.tracker_index();
// This _cannot_ be inlined into the match. If it is, the lock will be held
// open through the whole match, resulting in a deadlock when we try to re-lock
// the buffer back to active.
@@ -305,7 +382,6 @@ impl<A: HalApi> LifetimeTracker<A> {
_ => panic!("No pending mapping."),
};
let status = if pending_mapping.range.start != pending_mapping.range.end {
log::debug!("Buffer {tracker_index:?} map state -> Active");
let host = pending_mapping.op.host;
let size = pending_mapping.range.end - pending_mapping.range.start;
match super::map_buffer(

View File

@@ -364,6 +364,7 @@ fn map_buffer<A: HalApi>(
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeviceMismatch {
pub(super) res: ResourceErrorIdent,
pub(super) res_device: ResourceErrorIdent,
@@ -388,6 +389,7 @@ impl std::fmt::Display for DeviceMismatch {
impl std::error::Error for DeviceMismatch {}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum DeviceError {
#[error("{0} is invalid.")]

View File

@@ -30,7 +30,7 @@ use smallvec::SmallVec;
use std::{
iter,
mem::{self},
mem::{self, ManuallyDrop},
ptr::NonNull,
sync::{atomic::Ordering, Arc},
};
@@ -39,10 +39,23 @@ use thiserror::Error;
use super::Device;
pub struct Queue<A: HalApi> {
pub(crate) raw: Option<A::Queue>,
raw: ManuallyDrop<A::Queue>,
pub(crate) device: Arc<Device<A>>,
}
impl<A: HalApi> Queue<A> {
pub(crate) fn new(device: Arc<Device<A>>, raw: A::Queue) -> Self {
Queue {
raw: ManuallyDrop::new(raw),
device,
}
}
pub(crate) fn raw(&self) -> &A::Queue {
&self.raw
}
}
crate::impl_resource_type!(Queue);
// TODO: https://github.com/gfx-rs/wgpu/issues/4014
impl<A: HalApi> Labeled for Queue<A> {
@@ -56,7 +69,8 @@ crate::impl_storage_item!(Queue);
impl<A: HalApi> Drop for Queue<A> {
fn drop(&mut self) {
resource_log!("Drop {}", self.error_ident());
let queue = self.raw.take().unwrap();
// SAFETY: we never access `self.raw` beyond this point.
let queue = unsafe { ManuallyDrop::take(&mut self.raw) };
self.device.release_queue(queue);
}
}
@@ -149,12 +163,12 @@ pub enum TempResource<A: HalApi> {
pub(crate) struct EncoderInFlight<A: HalApi> {
raw: A::CommandEncoder,
cmd_buffers: Vec<A::CommandBuffer>,
trackers: Tracker<A>,
pub(crate) trackers: Tracker<A>,
/// These are the buffers that have been tracked by `PendingWrites`.
pending_buffers: Vec<Arc<Buffer<A>>>,
pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer<A>>>,
/// These are the textures that have been tracked by `PendingWrites`.
pending_textures: Vec<Arc<Texture<A>>>,
pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture<A>>>,
}
impl<A: HalApi> EncoderInFlight<A> {
@@ -268,8 +282,8 @@ impl<A: HalApi> PendingWrites<A> {
queue: &A::Queue,
) -> Result<Option<EncoderInFlight<A>>, DeviceError> {
if self.is_recording {
let pending_buffers = self.dst_buffers.drain().map(|(_, b)| b).collect();
let pending_textures = self.dst_textures.drain().map(|(_, t)| t).collect();
let pending_buffers = mem::take(&mut self.dst_buffers);
let pending_textures = mem::take(&mut self.dst_textures);
let cmd_buf = unsafe { self.command_encoder.end_encoding()? };
self.is_recording = false;
@@ -407,7 +421,6 @@ impl Global {
// `device.pending_writes.consume`.
let mut staging_buffer = StagingBuffer::new(device, data_size)?;
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
let staging_buffer = {
profiling::scope!("copy");
@@ -418,7 +431,7 @@ impl Global {
let result = self.queue_write_staging_buffer_impl(
&queue,
device,
pending_writes,
&mut pending_writes,
&staging_buffer,
buffer_id,
buffer_offset,
@@ -478,7 +491,6 @@ impl Global {
.ok_or_else(|| QueueWriteError::Transfer(TransferError::InvalidBufferId(buffer_id)))?;
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
// At this point, we have taken ownership of the staging_buffer from the
// user. Platform validation requires that the staging buffer always
@@ -489,7 +501,7 @@ impl Global {
let result = self.queue_write_staging_buffer_impl(
&queue,
device,
pending_writes,
&mut pending_writes,
&staging_buffer,
buffer_id,
buffer_offset,
@@ -572,8 +584,6 @@ impl Global {
self.queue_validate_write_buffer_impl(&dst, buffer_offset, staging_buffer.size)?;
dst.use_at(device.active_submission_index.load(Ordering::Relaxed) + 1);
let region = hal::BufferCopy {
src_offset: 0,
dst_offset: buffer_offset,
@@ -670,7 +680,7 @@ impl Global {
// Note: `_source_bytes_per_array_layer` is ignored since we
// have a staging copy, and it can have a different value.
let (_, _source_bytes_per_array_layer) = validate_linear_texture_data(
let (required_bytes_in_copy, _source_bytes_per_array_layer) = validate_linear_texture_data(
data_layout,
dst.desc.format,
destination.aspect,
@@ -686,34 +696,7 @@ impl Global {
.map_err(TransferError::from)?;
}
let (block_width, block_height) = dst.desc.format.block_dimensions();
let width_blocks = size.width / block_width;
let height_blocks = size.height / block_height;
let block_rows_per_image = data_layout.rows_per_image.unwrap_or(
// doesn't really matter because we need this only if we copy
// more than one layer, and then we validate for this being not
// None
height_blocks,
);
let block_size = dst
.desc
.format
.block_copy_size(Some(destination.aspect))
.unwrap();
let bytes_per_row_alignment =
get_lowest_common_denom(device.alignments.buffer_copy_pitch.get() as u32, block_size);
let stage_bytes_per_row =
wgt::math::align_to(block_size * width_blocks, bytes_per_row_alignment);
let block_rows_in_copy =
(size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
let stage_size =
wgt::BufferSize::new(stage_bytes_per_row as u64 * block_rows_in_copy as u64).unwrap();
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
let encoder = pending_writes.activate();
// If the copy does not fully cover the layers, we need to initialize to
@@ -765,37 +748,50 @@ impl Global {
// call above. Since we've held `texture_guard` the whole time, we know
// the texture hasn't gone away in the mean time, so we can unwrap.
let dst = hub.textures.get(destination.texture).unwrap();
dst.use_at(device.active_submission_index.load(Ordering::Relaxed) + 1);
let dst_raw = dst.try_raw(&snatch_guard)?;
let bytes_per_row = data_layout
.bytes_per_row
.unwrap_or(width_blocks * block_size);
let (block_width, block_height) = dst.desc.format.block_dimensions();
let width_in_blocks = size.width / block_width;
let height_in_blocks = size.height / block_height;
let block_size = dst
.desc
.format
.block_copy_size(Some(destination.aspect))
.unwrap();
let bytes_in_last_row = width_in_blocks * block_size;
let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
let bytes_per_row_alignment =
get_lowest_common_denom(device.alignments.buffer_copy_pitch.get() as u32, block_size);
let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
// Platform validation requires that the staging buffer always be
// freed, even if an error occurs. All paths from here must call
// `device.pending_writes.consume`.
let mut staging_buffer = StagingBuffer::new(device, stage_size)?;
if stage_bytes_per_row == bytes_per_row {
let staging_buffer = if stage_bytes_per_row == bytes_per_row {
profiling::scope!("copy aligned");
// Fast path if the data is already being aligned optimally.
unsafe {
staging_buffer.write_with_offset(
data,
data_layout.offset as isize,
0,
(data.len() as u64 - data_layout.offset) as usize,
);
}
let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
let mut staging_buffer = StagingBuffer::new(device, stage_size)?;
staging_buffer.write(&data[data_layout.offset as usize..]);
staging_buffer
} else {
profiling::scope!("copy chunked");
// Copy row by row into the optimal alignment.
let block_rows_in_copy =
(size.depth_or_array_layers - 1) * rows_per_image + height_in_blocks;
let stage_size =
wgt::BufferSize::new(stage_bytes_per_row as u64 * block_rows_in_copy as u64)
.unwrap();
let mut staging_buffer = StagingBuffer::new(device, stage_size)?;
let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
for layer in 0..size.depth_or_array_layers {
let rows_offset = layer * block_rows_per_image;
for row in rows_offset..rows_offset + height_blocks {
let rows_offset = layer * rows_per_image;
for row in rows_offset..rows_offset + height_in_blocks {
let src_offset = data_layout.offset as u32 + row * bytes_per_row;
let dst_offset = row * stage_bytes_per_row;
unsafe {
@@ -808,20 +804,21 @@ impl Global {
}
}
}
}
staging_buffer
};
let staging_buffer = staging_buffer.flush();
let regions = (0..array_layer_count).map(|rel_array_layer| {
let regions = (0..array_layer_count).map(|array_layer_offset| {
let mut texture_base = dst_base.clone();
texture_base.array_layer += rel_array_layer;
texture_base.array_layer += array_layer_offset;
hal::BufferTextureCopy {
buffer_layout: wgt::ImageDataLayout {
offset: rel_array_layer as u64
* block_rows_per_image as u64
offset: array_layer_offset as u64
* rows_per_image as u64
* stage_bytes_per_row as u64,
bytes_per_row: Some(stage_bytes_per_row),
rows_per_image: Some(block_rows_per_image),
rows_per_image: Some(rows_per_image),
},
texture_base,
size: hal_copy_size,
@@ -967,7 +964,7 @@ impl Global {
extract_texture_selector(&destination.to_untagged(), &size, &dst)?;
let mut pending_writes = device.pending_writes.lock();
let encoder = pending_writes.as_mut().unwrap().activate();
let encoder = pending_writes.activate();
// If the copy does not fully cover the layers, we need to initialize to
// zero *first* as we don't keep track of partial texture layer inits.
@@ -1010,7 +1007,6 @@ impl Global {
.drain(init_layer_range);
}
}
dst.use_at(device.active_submission_index.load(Ordering::Relaxed) + 1);
let snatch_guard = device.snatchable_lock.read();
let dst_raw = dst.try_raw(&snatch_guard)?;
@@ -1129,7 +1125,7 @@ impl Global {
}
{
profiling::scope!("update submission ids");
profiling::scope!("check resource state");
let cmd_buf_data = cmdbuf.data.lock();
let cmd_buf_trackers = &cmd_buf_data.as_ref().unwrap().trackers;
@@ -1139,7 +1135,6 @@ impl Global {
profiling::scope!("buffers");
for buffer in cmd_buf_trackers.buffers.used_resources() {
buffer.check_destroyed(&snatch_guard)?;
buffer.use_at(submit_index);
match *buffer.map_state.lock() {
BufferMapState::Idle => (),
@@ -1166,7 +1161,6 @@ impl Global {
true
}
};
texture.use_at(submit_index);
if should_extend {
unsafe {
used_surface_textures
@@ -1180,69 +1174,6 @@ impl Global {
}
}
}
{
profiling::scope!("views");
for texture_view in cmd_buf_trackers.views.used_resources() {
texture_view.use_at(submit_index);
}
}
{
profiling::scope!("bind groups (+ referenced views/samplers)");
for bg in cmd_buf_trackers.bind_groups.used_resources() {
bg.use_at(submit_index);
// We need to update the submission indices for the contained
// state-less (!) resources as well, so that they don't get
// deleted too early if the parent bind group goes out of scope.
for view in bg.used.views.used_resources() {
view.use_at(submit_index);
}
for sampler in bg.used.samplers.used_resources() {
sampler.use_at(submit_index);
}
}
}
{
profiling::scope!("compute pipelines");
for compute_pipeline in
cmd_buf_trackers.compute_pipelines.used_resources()
{
compute_pipeline.use_at(submit_index);
}
}
{
profiling::scope!("render pipelines");
for render_pipeline in
cmd_buf_trackers.render_pipelines.used_resources()
{
render_pipeline.use_at(submit_index);
}
}
{
profiling::scope!("query sets");
for query_set in cmd_buf_trackers.query_sets.used_resources() {
query_set.use_at(submit_index);
}
}
{
profiling::scope!(
"render bundles (+ referenced pipelines/query sets)"
);
for bundle in cmd_buf_trackers.bundles.used_resources() {
bundle.use_at(submit_index);
// We need to update the submission indices for the contained
// state-less (!) resources as well, excluding the bind groups.
// They don't get deleted too early if the bundle goes out of scope.
for render_pipeline in
bundle.used.render_pipelines.read().used_resources()
{
render_pipeline.use_at(submit_index);
}
for query_set in bundle.used.query_sets.read().used_resources()
{
query_set.use_at(submit_index);
}
}
}
}
let mut baked = cmdbuf.from_arc_into_baked();
@@ -1256,7 +1187,6 @@ impl Global {
))
.map_err(DeviceError::from)?
};
log::trace!("Stitching command buffer {:?} before submission", cmb_id);
//Note: locking the trackers has to be done after the storages
let mut trackers = device.trackers.lock();
@@ -1306,17 +1236,14 @@ impl Global {
raw: baked.encoder,
cmd_buffers: baked.list,
trackers: baked.trackers,
pending_buffers: Vec::new(),
pending_textures: Vec::new(),
pending_buffers: FastHashMap::default(),
pending_textures: FastHashMap::default(),
});
}
log::trace!("Device after submission {}", submit_index);
}
}
let mut pending_writes_guard = device.pending_writes.lock();
let pending_writes = pending_writes_guard.as_mut().unwrap();
let mut pending_writes = device.pending_writes.lock();
{
used_surface_textures.set_size(hub.textures.read().len());
@@ -1356,11 +1283,9 @@ impl Global {
}
}
if let Some(pending_execution) = pending_writes.pre_submit(
&device.command_allocator,
device.raw(),
queue.raw.as_ref().unwrap(),
)? {
if let Some(pending_execution) =
pending_writes.pre_submit(&device.command_allocator, device.raw(), queue.raw())?
{
active_executions.insert(0, pending_execution);
}
@@ -1382,9 +1307,7 @@ impl Global {
unsafe {
queue
.raw
.as_ref()
.unwrap()
.raw()
.submit(
&hal_command_buffers,
&submit_surface_textures,
@@ -1402,17 +1325,12 @@ impl Global {
profiling::scope!("cleanup");
// this will register the new submission to the life time tracker
let mut pending_write_resources = mem::take(&mut pending_writes.temp_resources);
device.lock_life().track_submission(
submit_index,
pending_write_resources.drain(..),
pending_writes.temp_resources.drain(..),
active_executions,
);
// pending_write_resources has been drained, so it's empty, but we
// want to retain its heap allocation.
pending_writes.temp_resources = pending_write_resources;
drop(pending_writes_guard);
drop(pending_writes);
// This will schedule destruction of all resources that are no longer needed
// by the user but used in the command stream, among other things.
@@ -1445,7 +1363,7 @@ impl Global {
) -> Result<f32, InvalidQueue> {
let hub = A::hub(self);
match hub.queues.get(queue_id) {
Ok(queue) => Ok(unsafe { queue.raw.as_ref().unwrap().get_timestamp_period() }),
Ok(queue) => Ok(unsafe { queue.raw().get_timestamp_period() }),
Err(_) => Err(InvalidQueue),
}
}

View File

@@ -32,7 +32,7 @@ use crate::{
UsageScopePool,
},
validation::{self, validate_color_attachment_bytes_per_sample},
FastHashMap, LabelHelpers as _, SubmissionIndex,
FastHashMap, LabelHelpers as _, PreHashedKey, PreHashedMap,
};
use arrayvec::ArrayVec;
@@ -46,6 +46,7 @@ use wgt::{DeviceLostReason, TextureFormat, TextureSampleType, TextureViewDimensi
use std::{
borrow::Cow,
iter,
mem::ManuallyDrop,
num::NonZeroU32,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
@@ -142,7 +143,7 @@ pub struct Device<A: HalApi> {
pub(crate) features: wgt::Features,
pub(crate) downlevel: wgt::DownlevelCapabilities,
pub(crate) instance_flags: wgt::InstanceFlags,
pub(crate) pending_writes: Mutex<Option<PendingWrites<A>>>,
pub(crate) pending_writes: Mutex<ManuallyDrop<PendingWrites<A>>>,
pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy<A>>>,
#[cfg(feature = "trace")]
pub(crate) trace: Mutex<Option<trace::Trace>>,
@@ -169,7 +170,8 @@ impl<A: HalApi> Drop for Device<A> {
fn drop(&mut self) {
resource_log!("Drop {}", self.error_ident());
let raw = self.raw.take().unwrap();
let pending_writes = self.pending_writes.lock().take().unwrap();
// SAFETY: We are in the Drop impl and we don't use self.pending_writes anymore after this point.
let pending_writes = unsafe { ManuallyDrop::take(&mut self.pending_writes.lock()) };
pending_writes.dispose(&raw);
self.command_allocator.dispose(&raw);
unsafe {
@@ -307,7 +309,10 @@ impl<A: HalApi> Device<A> {
features: desc.required_features,
downlevel,
instance_flags,
pending_writes: Mutex::new(rank::DEVICE_PENDING_WRITES, Some(pending_writes)),
pending_writes: Mutex::new(
rank::DEVICE_PENDING_WRITES,
ManuallyDrop::new(pending_writes),
),
deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
})
@@ -438,7 +443,7 @@ impl<A: HalApi> Device<A> {
.map_err(DeviceError::from)?
};
}
log::info!("Device::maintain: waiting for submission index {submission_index}");
log::trace!("Device::maintain: waiting for submission index {submission_index}");
let mut life_tracker = self.lock_life();
let submission_closures =
@@ -991,6 +996,8 @@ impl<A: HalApi> Device<A> {
texture: &Arc<Texture<A>>,
desc: &resource::TextureViewDescriptor,
) -> Result<Arc<TextureView<A>>, resource::CreateTextureViewError> {
self.check_is_valid()?;
let snatch_guard = texture.device.snatchable_lock.read();
let texture_raw = texture.try_raw(&snatch_guard)?;
@@ -1223,12 +1230,6 @@ impl<A: HalApi> Device<A> {
texture.hal_usage & mask_copy & mask_dimension & mask_mip_level
};
log::debug!(
"Create view for {} filters usages to {:?}",
texture.error_ident(),
usage
);
// use the combined depth-stencil format for the view
let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
texture.desc.format
@@ -1594,7 +1595,7 @@ impl<A: HalApi> Device<A> {
let encoder = self
.command_allocator
.acquire_encoder(self.raw(), queue.raw.as_ref().unwrap())?;
.acquire_encoder(self.raw(), queue.raw())?;
Ok(command::CommandBuffer::new(
encoder,
@@ -2069,8 +2070,6 @@ impl<A: HalApi> Device<A> {
used.textures
.add_single(texture, Some(view.selector.clone()), internal_use);
texture.same_device_as(view.as_ref())?;
texture.check_usage(pub_usage)?;
used_texture_ranges.push(TextureInitTrackerAction {
@@ -2587,11 +2586,29 @@ impl<A: HalApi> Device<A> {
derived_group_layouts.pop();
}
let mut unique_bind_group_layouts = PreHashedMap::default();
let bind_group_layouts = derived_group_layouts
.into_iter()
.map(|bgl_entry_map| {
self.create_bind_group_layout(&None, bgl_entry_map, bgl::Origin::Derived)
.map(Arc::new)
.map(|mut bgl_entry_map| {
bgl_entry_map.sort();
match unique_bind_group_layouts.entry(PreHashedKey::from_key(&bgl_entry_map)) {
std::collections::hash_map::Entry::Occupied(v) => Ok(Arc::clone(v.get())),
std::collections::hash_map::Entry::Vacant(e) => {
match self.create_bind_group_layout(
&None,
bgl_entry_map,
bgl::Origin::Derived,
) {
Ok(bgl) => {
let bgl = Arc::new(bgl);
e.insert(bgl.clone());
Ok(bgl)
}
Err(e) => Err(e),
}
}
}
})
.collect::<Result<Vec<_>, _>>()?;
@@ -2689,7 +2706,6 @@ impl<A: HalApi> Device<A> {
entry_point: final_entry_point_name.as_ref(),
constants: desc.stage.constants.as_ref(),
zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
vertex_pulling_transform: false,
},
cache: cache.as_ref().and_then(|it| it.raw.as_ref()),
};
@@ -2726,11 +2742,12 @@ impl<A: HalApi> Device<A> {
if is_auto_layout {
for bgl in pipeline.layout.bind_group_layouts.iter() {
bgl.exclusive_pipeline
// `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
let _ = bgl
.exclusive_pipeline
.set(binding_model::ExclusivePipeline::Compute(Arc::downgrade(
&pipeline,
)))
.unwrap();
)));
}
}
@@ -2773,7 +2790,6 @@ impl<A: HalApi> Device<A> {
.iter()
.any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
} {
log::debug!("Color targets: {:?}", color_targets);
self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
}
}
@@ -3109,7 +3125,6 @@ impl<A: HalApi> Device<A> {
entry_point: &vertex_entry_point_name,
constants: stage_desc.constants.as_ref(),
zero_initialize_workgroup_memory: stage_desc.zero_initialize_workgroup_memory,
vertex_pulling_transform: stage_desc.vertex_pulling_transform,
}
};
@@ -3119,6 +3134,7 @@ impl<A: HalApi> Device<A> {
let stage = wgt::ShaderStages::FRAGMENT;
let shader_module = &fragment_state.stage.module;
shader_module.same_device(self)?;
let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
@@ -3165,7 +3181,6 @@ impl<A: HalApi> Device<A> {
zero_initialize_workgroup_memory: fragment_state
.stage
.zero_initialize_workgroup_memory,
vertex_pulling_transform: false,
})
}
None => None,
@@ -3352,11 +3367,12 @@ impl<A: HalApi> Device<A> {
if is_auto_layout {
for bgl in pipeline.layout.bind_group_layouts.iter() {
bgl.exclusive_pipeline
// `bind_group_layouts` might contain duplicate entries, so we need to ignore the result.
let _ = bgl
.exclusive_pipeline
.set(binding_model::ExclusivePipeline::Render(Arc::downgrade(
&pipeline,
)))
.unwrap();
)));
}
}
@@ -3451,27 +3467,20 @@ impl<A: HalApi> Device<A> {
}
}
#[cfg(feature = "replay")]
pub(crate) fn wait_for_submit(
&self,
submission_index: SubmissionIndex,
) -> Result<(), WaitIdleError> {
submission_index: crate::SubmissionIndex,
) -> Result<(), DeviceError> {
let guard = self.fence.read();
let fence = guard.as_ref().unwrap();
let last_done_index = unsafe {
self.raw
.as_ref()
.unwrap()
.get_fence_value(fence)
.map_err(DeviceError::from)?
};
let last_done_index = unsafe { self.raw.as_ref().unwrap().get_fence_value(fence)? };
if last_done_index < submission_index {
log::info!("Waiting for submission {:?}", submission_index);
unsafe {
self.raw
.as_ref()
.unwrap()
.wait(fence, submission_index, !0)
.map_err(DeviceError::from)?
.wait(fence, submission_index, !0)?
};
drop(guard);
let closures = self
@@ -3592,6 +3601,13 @@ impl<A: HalApi> Device<A> {
.map(|raw| raw.get_internal_counters())
.unwrap_or_default()
}
pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
self.raw
.as_ref()
.map(|raw| raw.generate_allocator_report())
.unwrap_or_default()
}
}
impl<A: HalApi> Device<A> {
@@ -3610,7 +3626,7 @@ impl<A: HalApi> Device<A> {
/// Wait for idle and remove resources that we can, before we die.
pub(crate) fn prepare_to_die(&self) {
self.pending_writes.lock().as_mut().unwrap().deactivate();
self.pending_writes.lock().deactivate();
let current_index = self
.last_successful_submission_index
.load(Ordering::Acquire);

View File

@@ -1,5 +1,3 @@
use std::sync::Arc;
use wgt::Backend;
use crate::{
@@ -8,7 +6,6 @@ use crate::{
instance::{Instance, Surface},
registry::{Registry, RegistryReport},
resource_log,
storage::Element,
};
#[derive(Debug, PartialEq, Eq)]
@@ -90,13 +87,6 @@ impl Global {
}
}
pub fn clear_backend<A: HalApi>(&self, _dummy: ()) {
let hub = A::hub(self);
let surfaces_locked = self.surfaces.read();
// this is used for tests, which keep the adapter
hub.clear(&surfaces_locked, false);
}
pub fn generate_report(&self) -> GlobalReport {
GlobalReport {
surfaces: self.surfaces.generate_report(),
@@ -137,29 +127,22 @@ impl Drop for Global {
// destroy hubs before the instance gets dropped
#[cfg(vulkan)]
{
self.hubs.vulkan.clear(&surfaces_locked, true);
self.hubs.vulkan.clear(&surfaces_locked);
}
#[cfg(metal)]
{
self.hubs.metal.clear(&surfaces_locked, true);
self.hubs.metal.clear(&surfaces_locked);
}
#[cfg(dx12)]
{
self.hubs.dx12.clear(&surfaces_locked, true);
self.hubs.dx12.clear(&surfaces_locked);
}
#[cfg(gles)]
{
self.hubs.gl.clear(&surfaces_locked, true);
self.hubs.gl.clear(&surfaces_locked);
}
// destroy surfaces
for element in surfaces_locked.map.drain(..) {
if let Element::Occupied(arc_surface, _) = element {
let surface = Arc::into_inner(arc_surface)
.expect("Surface cannot be destroyed because is still in use");
self.instance.destroy_surface(surface);
}
}
surfaces_locked.map.clear();
}
}

View File

@@ -214,10 +214,7 @@ impl<A: HalApi> Hub<A> {
}
}
//TODO: instead of having a hacky `with_adapters` parameter,
// we should have `clear_device(device_id)` that specifically destroys
// everything related to a logical device.
pub(crate) fn clear(&self, surface_guard: &Storage<Surface>, with_adapters: bool) {
pub(crate) fn clear(&self, surface_guard: &Storage<Surface>) {
use hal::Surface;
let mut devices = self.devices.write();
@@ -248,7 +245,6 @@ impl<A: HalApi> Hub<A> {
let suf = A::surface_as_hal(surface);
unsafe {
suf.unwrap().unconfigure(device.raw());
//TODO: we could destroy the surface here
}
}
}
@@ -258,17 +254,8 @@ impl<A: HalApi> Hub<A> {
self.queues.write().map.clear();
devices.map.clear();
if with_adapters {
drop(devices);
self.adapters.write().map.clear();
}
}
pub(crate) fn surface_unconfigure(&self, device: &Device<A>, surface: &A::Surface) {
unsafe {
use hal::Surface;
surface.unconfigure(device.raw());
}
drop(devices);
self.adapters.write().map.clear();
}
pub fn generate_report(&self) -> HubReport {

View File

@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::sync::Arc;
use std::{borrow::Cow, collections::HashMap};
use crate::{
api_log,
@@ -23,9 +23,10 @@ type HalInstance<A> = <A as hal::Api>::Instance;
type HalSurface<A> = <A as hal::Api>::Surface;
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
pub struct FailedLimit {
name: &'static str,
name: Cow<'static, str>,
requested: u64,
allowed: u64,
}
@@ -35,7 +36,7 @@ fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLim
requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {
failed.push(FailedLimit {
name,
name: Cow::Borrowed(name),
requested,
allowed,
})
@@ -111,24 +112,6 @@ impl Instance {
flags: instance_desc.flags,
}
}
pub(crate) fn destroy_surface(&self, surface: Surface) {
fn destroy<A: HalApi>(instance: &Option<A::Instance>, mut surface: Option<HalSurface<A>>) {
if let Some(surface) = surface.take() {
unsafe {
instance.as_ref().unwrap().destroy_surface(surface);
}
}
}
#[cfg(vulkan)]
destroy::<hal::api::Vulkan>(&self.vulkan, surface.vulkan);
#[cfg(metal)]
destroy::<hal::api::Metal>(&self.metal, surface.metal);
#[cfg(dx12)]
destroy::<hal::api::Dx12>(&self.dx12, surface.dx12);
#[cfg(gles)]
destroy::<hal::api::Gles>(&self.gl, surface.gl);
}
}
pub struct Surface {
@@ -292,11 +275,7 @@ impl<A: HalApi> Adapter<A> {
instance_flags,
) {
let device = Arc::new(device);
let queue = Queue {
device: device.clone(),
raw: Some(hal_device.queue),
};
let queue = Arc::new(queue);
let queue = Arc::new(Queue::new(device.clone(), hal_device.queue));
device.set_queue(&queue);
return Ok((device, queue));
}
@@ -342,10 +321,6 @@ impl<A: HalApi> Adapter<A> {
);
}
if let Some(_) = desc.label {
//TODO
}
if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
return Err(RequestDeviceError::LimitsExceeded(failed));
}
@@ -391,6 +366,7 @@ pub enum GetSurfaceSupportError {
}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Error when requesting a device from the adaptor
#[non_exhaustive]
pub enum RequestDeviceError {
@@ -435,6 +411,7 @@ impl<M: Marker> AdapterInputs<'_, M> {
pub struct InvalidAdapter;
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum RequestAdapterError {
#[error("No suitable adapter found")]
@@ -577,9 +554,20 @@ impl Global {
metal: Some(self.instance.metal.as_ref().map_or(
Err(CreateSurfaceError::BackendNotEnabled(Backend::Metal)),
|inst| {
// we don't want to link to metal-rs for this
#[allow(clippy::transmute_ptr_to_ref)]
Ok(inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) }))
let layer = layer.cast();
// SAFETY: We do this cast and deref. (rather than using `metal` to get the
// object we want) to avoid direct coupling on the `metal` crate.
//
// To wit, this pointer…
//
// - …is properly aligned.
// - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal`
// field.
// - …points to an _initialized_ `MetalLayerRef`.
// - …is only ever aliased via an immutable reference that lives within this
// lexical scope.
let layer = unsafe { &*layer };
Ok(inst.create_surface_from_layer(layer))
},
)?),
#[cfg(dx12)]
@@ -631,7 +619,7 @@ impl Global {
) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_visual");
self.instance_create_surface_dx12(id_in, |inst| unsafe {
inst.create_surface_from_visual(visual as _)
inst.create_surface_from_visual(visual.cast())
})
}
@@ -661,7 +649,7 @@ impl Global {
) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
self.instance_create_surface_dx12(id_in, |inst| unsafe {
inst.create_surface_from_swap_chain_panel(swap_chain_panel as _)
inst.create_surface_from_swap_chain_panel(swap_chain_panel.cast())
})
}
@@ -670,15 +658,11 @@ impl Global {
api_log!("Surface::drop {id:?}");
fn unconfigure<A: HalApi>(
global: &Global,
surface: &Option<HalSurface<A>>,
present: &Presentation,
) {
fn unconfigure<A: HalApi>(surface: &Option<HalSurface<A>>, present: &Presentation) {
if let Some(surface) = surface {
let hub = HalApi::hub(global);
if let Some(device) = present.device.downcast_ref::<A>() {
hub.surface_unconfigure(device, surface);
use hal::Surface;
unsafe { surface.unconfigure(device.raw()) };
}
}
}
@@ -689,15 +673,15 @@ impl Global {
if let Some(present) = surface.presentation.lock().take() {
#[cfg(vulkan)]
unconfigure::<hal::api::Vulkan>(self, &surface.vulkan, &present);
unconfigure::<hal::api::Vulkan>(&surface.vulkan, &present);
#[cfg(metal)]
unconfigure::<hal::api::Metal>(self, &surface.metal, &present);
unconfigure::<hal::api::Metal>(&surface.metal, &present);
#[cfg(dx12)]
unconfigure::<hal::api::Dx12>(self, &surface.dx12, &present);
unconfigure::<hal::api::Dx12>(&surface.dx12, &present);
#[cfg(gles)]
unconfigure::<hal::api::Gles>(self, &surface.gl, &present);
unconfigure::<hal::api::Gles>(&surface.gl, &present);
}
self.instance.destroy_surface(surface);
drop(surface)
}
fn enumerate<A: HalApi>(

View File

@@ -2,6 +2,20 @@
//! It is designed for integration into browsers, as well as wrapping
//! into other language-specific user-friendly libraries.
//!
#![cfg_attr(
not(any(not(doc), wgpu_core_doc)),
doc = r#"\
## Documentation hidden
As a workaround for [an issue in rustdoc](https://github.com/rust-lang/rust/issues/114891)
that [affects `wgpu-core` documentation builds \
severely](https://github.com/gfx-rs/wgpu/issues/4905),
the documentation for `wgpu-core` is empty unless built with
`RUSTFLAGS="--cfg wgpu_core_doc"`, which may take a very long time.
"#
)]
#![cfg(any(not(doc), wgpu_core_doc))]
//!
//! ## Feature flags
#![doc = document_features::document_features!()]
//!
@@ -41,6 +55,7 @@
rustdoc::private_intra_doc_links
)]
#![warn(
clippy::ptr_as_ptr,
trivial_casts,
trivial_numeric_casts,
unsafe_op_in_unsafe_fn,
@@ -71,7 +86,6 @@ pub mod resource;
mod snatch;
pub mod storage;
mod track;
mod utils;
// This is public for users who pre-compile shaders while still wanting to
// preserve all run-time checks that `wgpu-core` does.
// See <https://github.com/gfx-rs/wgpu/issues/3103>, after which this can be

View File

@@ -147,8 +147,6 @@ pub struct ProgrammableStageDescriptor<'a> {
/// This is required by the WebGPU spec, but may have overhead which can be avoided
/// for cross-platform applications
pub zero_initialize_workgroup_memory: bool,
/// Should the pipeline attempt to transform vertex shaders to use vertex pulling.
pub vertex_pulling_transform: bool,
}
/// Describes a programmable pipeline stage.
@@ -176,8 +174,6 @@ pub struct ResolvedProgrammableStageDescriptor<'a, A: HalApi> {
/// This is required by the WebGPU spec, but may have overhead which can be avoided
/// for cross-platform applications
pub zero_initialize_workgroup_memory: bool,
/// Should the pipeline attempt to transform vertex shaders to use vertex pulling.
pub vertex_pulling_transform: bool,
}
/// Number of implicit bind groups derived at pipeline creation.

View File

@@ -89,8 +89,11 @@ pub enum ConfigureSurfaceError {
requested: wgt::CompositeAlphaMode,
available: Vec<wgt::CompositeAlphaMode>,
},
#[error("Requested usage is not supported")]
UnsupportedUsage,
#[error("Requested usage {requested:?} is not in the list of supported usages: {available:?}")]
UnsupportedUsage {
requested: hal::TextureUses,
available: hal::TextureUses,
},
#[error("Gpu got stuck :(")]
StuckGpu,
}
@@ -227,7 +230,6 @@ impl Global {
.insert_single(&texture, hal::TextureUses::UNINITIALIZED);
let id = fid.assign(texture);
log::debug!("Created CURRENT Surface Texture {:?}", id);
if present.acquired_texture.is_some() {
return Err(SurfaceError::AlreadyAcquired);
@@ -298,10 +300,6 @@ impl Global {
// The texture ID got added to the device tracker by `submit()`,
// and now we are moving it away.
log::debug!(
"Removing swapchain texture {:?} from the device tracker",
texture_id
);
let texture = hub.textures.unregister(texture_id);
if let Some(texture) = texture {
device
@@ -323,13 +321,7 @@ impl Global {
log::error!("Presented frame is from a different surface");
Err(hal::SurfaceError::Lost)
} else {
unsafe {
queue
.raw
.as_ref()
.unwrap()
.present(suf.unwrap(), raw.take().unwrap())
}
unsafe { queue.raw().present(suf.unwrap(), raw.take().unwrap()) }
}
}
_ => unreachable!(),
@@ -339,8 +331,6 @@ impl Global {
}
};
log::debug!("Presented. End of Frame");
match result {
Ok(()) => Ok(Status::Good),
Err(err) => match err {
@@ -390,11 +380,6 @@ impl Global {
// The texture ID got added to the device tracker by `submit()`,
// and now we are moving it away.
log::debug!(
"Removing swapchain texture {:?} from the device tracker",
texture_id
);
let texture = hub.textures.unregister(texture_id);
if let Some(texture) = texture {

View File

@@ -14,7 +14,7 @@ use crate::{
resource_log,
snatch::{ExclusiveSnatchGuard, SnatchGuard, Snatchable},
track::{SharedTrackerIndexAllocator, TextureSelector, TrackerIndex},
Label, LabelHelpers, SubmissionIndex,
Label, LabelHelpers,
};
use hal::CommandEncoder;
@@ -22,13 +22,13 @@ use smallvec::SmallVec;
use thiserror::Error;
use std::{
borrow::Borrow,
borrow::{Borrow, Cow},
fmt::Debug,
iter,
mem::{self, ManuallyDrop},
ops::Range,
ptr::NonNull,
sync::{atomic::Ordering, Arc, Weak},
sync::{Arc, Weak},
};
/// Information about the wgpu-core resource.
@@ -54,14 +54,6 @@ use std::{
pub(crate) struct TrackingData {
tracker_index: TrackerIndex,
tracker_indices: Arc<SharedTrackerIndexAllocator>,
/// The index of the last queue submission in which the resource
/// was used.
///
/// Each queue submission is fenced and assigned an index number
/// sequentially. Thus, when a queue submission completes, we know any
/// resources used in that submission and any lower-numbered submissions are
/// no longer in use by the GPU.
submission_index: hal::AtomicFenceValue,
}
impl Drop for TrackingData {
@@ -75,28 +67,18 @@ impl TrackingData {
Self {
tracker_index: tracker_indices.alloc(),
tracker_indices,
submission_index: hal::AtomicFenceValue::new(0),
}
}
pub(crate) fn tracker_index(&self) -> TrackerIndex {
self.tracker_index
}
/// Record that this resource will be used by the queue submission with the
/// given index.
pub(crate) fn use_at(&self, submit_index: SubmissionIndex) {
self.submission_index.store(submit_index, Ordering::Release);
}
pub(crate) fn submission_index(&self) -> SubmissionIndex {
self.submission_index.load(Ordering::Acquire)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ResourceErrorIdent {
r#type: &'static str,
r#type: Cow<'static, str>,
label: String,
}
@@ -174,7 +156,7 @@ pub(crate) trait Labeled: ResourceType {
fn error_ident(&self) -> ResourceErrorIdent {
ResourceErrorIdent {
r#type: Self::TYPE,
r#type: Cow::Borrowed(Self::TYPE),
label: self.label().to_owned(),
}
}
@@ -193,10 +175,6 @@ macro_rules! impl_labeled {
pub(crate) trait Trackable: Labeled {
fn tracker_index(&self) -> TrackerIndex;
/// Record that this resource will be used by the queue submission with the
/// given index.
fn use_at(&self, submit_index: SubmissionIndex);
fn submission_index(&self) -> SubmissionIndex;
}
#[macro_export]
@@ -206,12 +184,6 @@ macro_rules! impl_trackable {
fn tracker_index(&self) -> $crate::track::TrackerIndex {
self.tracking_data.tracker_index()
}
fn use_at(&self, submit_index: $crate::SubmissionIndex) {
self.tracking_data.use_at(submit_index)
}
fn submission_index(&self) -> $crate::SubmissionIndex {
self.tracking_data.submission_index()
}
}
};
}
@@ -370,6 +342,7 @@ pub struct BufferMapOperation {
}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum BufferAccessError {
#[error(transparent)]
@@ -418,6 +391,7 @@ pub enum BufferAccessError {
}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
pub struct MissingBufferUsageError {
pub(crate) res: ResourceErrorIdent,
@@ -434,6 +408,7 @@ pub struct MissingTextureUsageError {
}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("{0} has been destroyed")]
pub struct DestroyedResourceError(pub ResourceErrorIdent);
@@ -642,7 +617,6 @@ impl<A: HalApi> Buffer<A> {
let device = &self.device;
let snatch_guard = device.snatchable_lock.read();
let raw_buf = self.try_raw(&snatch_guard)?;
log::debug!("{} map state -> Idle", self.error_ident());
match mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle) {
BufferMapState::Init { staging_buffer } => {
#[cfg(feature = "trace")]
@@ -657,11 +631,9 @@ impl<A: HalApi> Buffer<A> {
}
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
let staging_buffer = staging_buffer.flush();
self.use_at(device.active_submission_index.load(Ordering::Relaxed) + 1);
let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {
src_offset: 0,
dst_offset: 0,
@@ -738,7 +710,7 @@ impl<A: HalApi> Buffer<A> {
};
queue::TempResource::DestroyedBuffer(DestroyedBuffer {
raw: Some(raw),
raw: ManuallyDrop::new(raw),
device: Arc::clone(&self.device),
label: self.label().to_owned(),
bind_groups,
@@ -746,14 +718,14 @@ impl<A: HalApi> Buffer<A> {
};
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
if pending_writes.contains_buffer(self) {
pending_writes.consume_temp(temp);
} else {
let last_submit_index = self.submission_index();
device
.lock_life()
.schedule_resource_destruction(temp, last_submit_index);
let mut life_lock = device.lock_life();
let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
if let Some(last_submit_index) = last_submit_index {
life_lock.schedule_resource_destruction(temp, last_submit_index);
}
}
Ok(())
@@ -788,7 +760,7 @@ crate::impl_trackable!(Buffer);
/// A buffer that has been marked as destroyed and is staged for actual deletion soon.
#[derive(Debug)]
pub struct DestroyedBuffer<A: HalApi> {
raw: Option<A::Buffer>,
raw: ManuallyDrop<A::Buffer>,
device: Arc<Device<A>>,
label: String,
bind_groups: Vec<Weak<BindGroup<A>>>,
@@ -808,13 +780,12 @@ impl<A: HalApi> Drop for DestroyedBuffer<A> {
}
drop(deferred);
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
unsafe {
use hal::Device;
self.device.raw().destroy_buffer(raw);
}
resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
// SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
unsafe {
use hal::Device;
self.device.raw().destroy_buffer(raw);
}
}
}
@@ -1201,7 +1172,7 @@ impl<A: HalApi> Texture<A> {
};
queue::TempResource::DestroyedTexture(DestroyedTexture {
raw: Some(raw),
raw: ManuallyDrop::new(raw),
views,
bind_groups,
device: Arc::clone(&self.device),
@@ -1210,14 +1181,14 @@ impl<A: HalApi> Texture<A> {
};
let mut pending_writes = device.pending_writes.lock();
let pending_writes = pending_writes.as_mut().unwrap();
if pending_writes.contains_texture(self) {
pending_writes.consume_temp(temp);
} else {
let last_submit_index = self.submission_index();
device
.lock_life()
.schedule_resource_destruction(temp, last_submit_index);
let mut life_lock = device.lock_life();
let last_submit_index = life_lock.get_texture_latest_submission_index(self);
if let Some(last_submit_index) = last_submit_index {
life_lock.schedule_resource_destruction(temp, last_submit_index);
}
}
Ok(())
@@ -1390,7 +1361,7 @@ impl Global {
/// A texture that has been marked as destroyed and is staged for actual deletion soon.
#[derive(Debug)]
pub struct DestroyedTexture<A: HalApi> {
raw: Option<A::Texture>,
raw: ManuallyDrop<A::Texture>,
views: Vec<Weak<TextureView<A>>>,
bind_groups: Vec<Weak<BindGroup<A>>>,
device: Arc<Device<A>>,
@@ -1416,13 +1387,12 @@ impl<A: HalApi> Drop for DestroyedTexture<A> {
}
drop(deferred);
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
unsafe {
use hal::Device;
self.device.raw().destroy_texture(raw);
}
resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
// SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
unsafe {
use hal::Device;
self.device.raw().destroy_texture(raw);
}
}
}
@@ -1632,6 +1602,8 @@ impl<A: HalApi> TextureView<A> {
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum CreateTextureViewError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error("TextureId {0:?} is invalid")]
InvalidTextureId(TextureId),
#[error(transparent)]

View File

@@ -119,13 +119,11 @@ where
}
pub(crate) fn insert(&mut self, id: Id<T::Marker>, value: Arc<T>) {
log::trace!("User is inserting {}{:?}", T::TYPE, id);
let (index, epoch, _backend) = id.unzip();
self.insert_impl(index as usize, epoch, Element::Occupied(value, epoch))
}
pub(crate) fn insert_error(&mut self, id: Id<T::Marker>) {
log::trace!("User is inserting as error {}{:?}", T::TYPE, id);
let (index, epoch, _) = id.unzip();
self.insert_impl(index as usize, epoch, Element::Error(epoch))
}
@@ -143,7 +141,6 @@ where
}
pub(crate) fn remove(&mut self, id: Id<T::Marker>) -> Option<Arc<T>> {
log::trace!("User is removing {}{:?}", T::TYPE, id);
let (index, epoch, _) = id.unzip();
match std::mem::replace(&mut self.map[index as usize], Element::Vacant) {
Element::Occupied(value, storage_epoch) => {

View File

@@ -1,9 +1,8 @@
/*! Buffer Trackers
*
* Buffers are represented by a single state for the whole resource,
* a 16 bit bitflag of buffer usages. Because there is only ever
* one subresource, they have no selector.
!*/
//! Buffer Trackers
//!
//! Buffers are represented by a single state for the whole resource,
//! a 16 bit bitflag of buffer usages. Because there is only ever
//! one subresource, they have no selector.
use std::sync::{Arc, Weak};
@@ -277,6 +276,11 @@ impl<A: HalApi> BufferTracker<A> {
}
}
/// Returns true if the given buffer is tracked.
pub fn contains(&self, buffer: &Buffer<A>) -> bool {
self.metadata.contains(buffer.tracker_index().as_usize())
}
/// Returns a list of all buffers tracked.
pub fn used_resources(&self) -> impl Iterator<Item = Arc<Buffer<A>>> + '_ {
self.metadata.owned_resources()
@@ -731,8 +735,6 @@ unsafe fn insert<T: Clone>(
strict_assert_eq!(invalid_resource_state(new_start_state), false);
strict_assert_eq!(invalid_resource_state(new_end_state), false);
log::trace!("\tbuf {index}: insert {new_start_state:?}..{new_end_state:?}");
unsafe {
if let Some(&mut ref mut start_state) = start_states {
*start_state.get_unchecked_mut(index) = new_start_state;
@@ -747,7 +749,7 @@ unsafe fn insert<T: Clone>(
#[inline(always)]
unsafe fn merge<A: HalApi>(
current_states: &mut [BufferUses],
index32: u32,
_index32: u32,
index: usize,
state_provider: BufferStateProvider<'_>,
metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer<A>>>,
@@ -765,8 +767,6 @@ unsafe fn merge<A: HalApi>(
));
}
log::trace!("\tbuf {index32}: merge {current_state:?} + {new_state:?}");
*current_state = merged_state;
Ok(())
@@ -791,8 +791,6 @@ unsafe fn barrier(
selector: (),
usage: current_state..new_state,
});
log::trace!("\tbuf {index}: transition {current_state:?} -> {new_state:?}");
}
#[inline(always)]

View File

@@ -67,7 +67,7 @@ impl<T: Clone> ResourceMetadata<T> {
/// Returns true if the set contains the resource with the given index.
pub(super) fn contains(&self, index: usize) -> bool {
self.owned[index]
self.owned.get(index).unwrap_or(false)
}
/// Returns true if the set contains the resource with the given index.

View File

@@ -1,8 +1,7 @@
/*! Stateless Trackers
*
* Stateless trackers don't have any state, so make no
* distinction between a usage scope and a full tracker.
!*/
//! Stateless Trackers
//!
//! Stateless trackers don't have any state, so make no
//! distinction between a usage scope and a full tracker.
use std::sync::Arc;
@@ -34,12 +33,6 @@ impl<T: Trackable> StatelessBindGroupState<T> {
resources.sort_unstable_by_key(|resource| resource.tracker_index());
}
/// Returns a list of all resources tracked. May contain duplicates.
pub fn used_resources(&self) -> impl Iterator<Item = Arc<T>> + '_ {
let resources = self.resources.lock();
resources.iter().cloned().collect::<Vec<_>>().into_iter()
}
/// Adds the given resource.
pub fn add_single(&self, resource: &Arc<T>) {
let mut resources = self.resources.lock();
@@ -79,11 +72,6 @@ impl<T: Trackable> StatelessTracker<T> {
}
}
/// Returns a list of all resources tracked.
pub fn used_resources(&self) -> impl Iterator<Item = Arc<T>> + '_ {
self.metadata.owned_resources()
}
/// Inserts a single resource into the resource tracker.
///
/// If the resource already exists in the tracker, it will be overwritten.

View File

@@ -1,23 +1,22 @@
/*! Texture Trackers
*
* Texture trackers are significantly more complicated than
* the buffer trackers because textures can be in a "complex"
* state where each individual subresource can potentially be
* in a different state from every other subtresource. These
* complex states are stored separately from the simple states
* because they are signifignatly more difficult to track and
* most resources spend the vast majority of their lives in
* simple states.
*
* There are two special texture usages: `UNKNOWN` and `UNINITIALIZED`.
* - `UNKNOWN` is only used in complex states and is used to signify
* that the complex state does not know anything about those subresources.
* It cannot leak into transitions, it is invalid to transition into UNKNOWN
* state.
* - `UNINITIALIZED` is used in both simple and complex states to mean the texture
* is known to be in some undefined state. Any transition away from UNINITIALIZED
* will treat the contents as junk.
!*/
//! Texture Trackers
//!
//! Texture trackers are significantly more complicated than
//! the buffer trackers because textures can be in a "complex"
//! state where each individual subresource can potentially be
//! in a different state from every other subtresource. These
//! complex states are stored separately from the simple states
//! because they are signifignatly more difficult to track and
//! most resources spend the vast majority of their lives in
//! simple states.
//!
//! There are two special texture usages: `UNKNOWN` and `UNINITIALIZED`.
//! - `UNKNOWN` is only used in complex states and is used to signify
//! that the complex state does not know anything about those subresources.
//! It cannot leak into transitions, it is invalid to transition into UNKNOWN
//! state.
//! - `UNINITIALIZED` is used in both simple and complex states to mean the texture
//! is known to be in some undefined state. Any transition away from UNINITIALIZED
//! will treat the contents as junk.
use super::{range::RangedStates, PendingTransition, PendingTransitionList, TrackerIndex};
use crate::{
@@ -446,6 +445,11 @@ impl<A: HalApi> TextureTracker<A> {
}
}
/// Returns true if the tracker owns the given texture.
pub fn contains(&self, texture: &Texture<A>) -> bool {
self.metadata.contains(texture.tracker_index().as_usize())
}
/// Returns a list of all textures tracked.
pub fn used_resources(&self) -> impl Iterator<Item = Arc<Texture<A>>> + '_ {
self.metadata.owned_resources()
@@ -1120,8 +1124,6 @@ unsafe fn insert<T: Clone>(
// check that resource states don't have any conflicts.
strict_assert_eq!(invalid_resource_state(state), false);
log::trace!("\ttex {index}: insert start {state:?}");
if let Some(start_state) = start_state {
unsafe { *start_state.simple.get_unchecked_mut(index) = state };
}
@@ -1137,8 +1139,6 @@ unsafe fn insert<T: Clone>(
let complex =
unsafe { ComplexTextureState::from_selector_state_iter(full_range, state_iter) };
log::trace!("\ttex {index}: insert start {complex:?}");
if let Some(start_state) = start_state {
unsafe { *start_state.simple.get_unchecked_mut(index) = TextureUses::COMPLEX };
start_state.complex.insert(index, complex.clone());
@@ -1159,8 +1159,6 @@ unsafe fn insert<T: Clone>(
// check that resource states don't have any conflicts.
strict_assert_eq!(invalid_resource_state(state), false);
log::trace!("\ttex {index}: insert end {state:?}");
// We only need to insert into the end, as there is guaranteed to be
// a start state provider.
unsafe { *end_state.simple.get_unchecked_mut(index) = state };
@@ -1172,8 +1170,6 @@ unsafe fn insert<T: Clone>(
ComplexTextureState::from_selector_state_iter(full_range, state_iter)
};
log::trace!("\ttex {index}: insert end {complex:?}");
// We only need to insert into the end, as there is guaranteed to be
// a start state provider.
unsafe { *end_state.simple.get_unchecked_mut(index) = TextureUses::COMPLEX };
@@ -1211,8 +1207,6 @@ unsafe fn merge<A: HalApi>(
(SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => {
let merged_state = *current_simple | new_simple;
log::trace!("\ttex {index}: merge simple {current_simple:?} + {new_simple:?}");
if invalid_resource_state(merged_state) {
return Err(ResourceUsageCompatibilityError::from_texture(
unsafe { metadata_provider.get(index) },
@@ -1238,8 +1232,6 @@ unsafe fn merge<A: HalApi>(
for (selector, new_state) in new_many {
let merged_state = *current_simple | new_state;
log::trace!("\ttex {index}: merge {selector:?} {current_simple:?} + {new_state:?}");
if invalid_resource_state(merged_state) {
return Err(ResourceUsageCompatibilityError::from_texture(
unsafe { metadata_provider.get(index) },
@@ -1276,11 +1268,6 @@ unsafe fn merge<A: HalApi>(
// simple states are never unknown.
let merged_state = merged_state - TextureUses::UNKNOWN;
log::trace!(
"\ttex {index}: merge mip {mip_id} layers {layers:?} \
{current_layer_state:?} + {new_simple:?}"
);
if invalid_resource_state(merged_state) {
return Err(ResourceUsageCompatibilityError::from_texture(
unsafe { metadata_provider.get(index) },
@@ -1317,11 +1304,6 @@ unsafe fn merge<A: HalApi>(
continue;
}
log::trace!(
"\ttex {index}: merge mip {mip_id} layers {layers:?} \
{current_layer_state:?} + {new_state:?}"
);
if invalid_resource_state(merged_state) {
return Err(ResourceUsageCompatibilityError::from_texture(
unsafe { metadata_provider.get(index) },
@@ -1369,8 +1351,6 @@ unsafe fn barrier(
return;
}
log::trace!("\ttex {index}: transition simple {current_simple:?} -> {new_simple:?}");
barriers.push(PendingTransition {
id: index as _,
selector: texture_selector.clone(),
@@ -1387,10 +1367,6 @@ unsafe fn barrier(
continue;
}
log::trace!(
"\ttex {index}: transition {selector:?} {current_simple:?} -> {new_state:?}"
);
barriers.push(PendingTransition {
id: index as _,
selector,
@@ -1411,11 +1387,6 @@ unsafe fn barrier(
continue;
}
log::trace!(
"\ttex {index}: transition mip {mip_id} layers {layers:?} \
{current_layer_state:?} -> {new_simple:?}"
);
barriers.push(PendingTransition {
id: index as _,
selector: TextureSelector {
@@ -1445,11 +1416,6 @@ unsafe fn barrier(
continue;
}
log::trace!(
"\ttex {index}: transition mip {mip_id} layers {layers:?} \
{current_layer_state:?} -> {new_state:?}"
);
barriers.push(PendingTransition {
id: index as _,
selector: TextureSelector {

View File

@@ -1,54 +0,0 @@
/// If the first iterator is longer than the second, the zip implementation
/// in the standard library will still advance the the first iterator before
/// realizing that the second iterator has finished.
///
/// This implementation will advance the shorter iterator first avoiding
/// the issue above.
///
/// If you can guarantee that the first iterator is always shorter than the
/// second, you should use the zip impl in stdlib.
pub(crate) struct ZipWithProperAdvance<
A: ExactSizeIterator<Item = IA>,
B: ExactSizeIterator<Item = IB>,
IA,
IB,
> {
a: A,
b: B,
iter_a_first: bool,
}
impl<A: ExactSizeIterator<Item = IA>, B: ExactSizeIterator<Item = IB>, IA, IB>
ZipWithProperAdvance<A, B, IA, IB>
{
pub(crate) fn new(a: A, b: B) -> Self {
let iter_a_first = a.len() <= b.len();
Self { a, b, iter_a_first }
}
}
impl<A: ExactSizeIterator<Item = IA>, B: ExactSizeIterator<Item = IB>, IA, IB> Iterator
for ZipWithProperAdvance<A, B, IA, IB>
{
type Item = (IA, IB);
fn next(&mut self) -> Option<Self::Item> {
if self.iter_a_first {
let a = self.a.next()?;
let b = self.b.next()?;
Some((a, b))
} else {
let b = self.b.next()?;
let a = self.a.next()?;
Some((a, b))
}
}
}
impl<A: ExactSizeIterator<Item = IA>, B: ExactSizeIterator<Item = IB>, IA, IB> ExactSizeIterator
for ZipWithProperAdvance<A, B, IA, IB>
{
fn len(&self) -> usize {
self.a.len().min(self.b.len())
}
}

File diff suppressed because one or more lines are too long

View File

@@ -11,9 +11,9 @@
[package]
edition = "2021"
rust-version = "1.74"
rust-version = "1.76"
name = "wgpu-hal"
version = "0.20.0"
version = "22.0.0"
authors = ["gfx-rs developers"]
description = "WebGPU hardware abstraction layer"
homepage = "https://wgpu.rs/"
@@ -53,20 +53,20 @@ required-features = ["gles"]
[dependencies]
arrayvec = "0.7"
bitflags = "2"
bitflags = "2.6"
log = "0.4"
once_cell = "1.19.0"
parking_lot = ">=0.11, <0.13"
raw-window-handle = "0.6"
rustc-hash = "1.1"
thiserror = "1"
rustc-hash = "1.1.0"
thiserror = "1.0.63"
[dependencies.glow]
version = "0.13.1"
version = "0.14.0"
optional = true
[dependencies.naga]
version = "0.20.0"
version = "22.0.0"
path = "../naga"
[dependencies.profiling]
@@ -74,7 +74,7 @@ version = "1"
default-features = false
[dependencies.wgt]
version = "0.20.0"
version = "22.0.0"
path = "../wgpu-types"
package = "wgpu-types"
@@ -84,7 +84,7 @@ env_logger = "0.11"
glam = "0.28"
[dev-dependencies.naga]
version = "0.20.0"
version = "22.0.0"
path = "../naga"
features = ["wgsl-in"]
@@ -120,7 +120,10 @@ gles = [
"dep:khronos-egl",
"dep:libloading",
"dep:ndk-sys",
"winapi/libloaderapi",
"windows/Win32_Graphics_OpenGL",
"windows/Win32_Graphics_Gdi",
"windows/Win32_System_LibraryLoader",
"windows/Win32_UI_WindowsAndMessaging",
]
internal_error_panic = []
metal = [
@@ -223,11 +226,11 @@ optional = true
libc = "0.2"
[target."cfg(windows)".dependencies.bit-set]
version = "0.6"
version = "0.8"
optional = true
[target."cfg(windows)".dependencies.d3d12]
version = "0.20.0"
version = "22.0.0"
path = "../d3d12/"
features = ["libloading"]
optional = true
@@ -237,7 +240,7 @@ version = "0.6"
optional = true
[target."cfg(windows)".dependencies.gpu-allocator]
version = "0.26"
version = "0.27"
features = [
"d3d12",
"public-winapi",
@@ -246,7 +249,7 @@ optional = true
default-features = false
[target."cfg(windows)".dependencies.hassle-rs]
version = "0.11"
version = "0.11.0"
optional = true
[target."cfg(windows)".dependencies.range-alloc]
@@ -261,3 +264,8 @@ features = [
"winuser",
"dcomp",
]
[target."cfg(windows)".dependencies.windows]
version = "0.58"
optional = true
default-features = false

View File

@@ -257,7 +257,6 @@ impl<A: hal::Api> Example<A> {
entry_point: "vs_main",
constants: &constants,
zero_initialize_workgroup_memory: true,
vertex_pulling_transform: false,
},
vertex_buffers: &[],
fragment_stage: Some(hal::ProgrammableStage {
@@ -265,7 +264,6 @@ impl<A: hal::Api> Example<A> {
entry_point: "fs_main",
constants: &constants,
zero_initialize_workgroup_memory: true,
vertex_pulling_transform: false,
}),
primitive: wgt::PrimitiveState {
topology: wgt::PrimitiveTopology::TriangleStrip,
@@ -580,7 +578,7 @@ impl<A: hal::Api> Example<A> {
self.surface.unconfigure(&self.device);
self.device.exit(self.queue);
self.instance.destroy_surface(self.surface);
drop(self.surface);
drop(self.adapter);
}
}

View File

@@ -379,7 +379,6 @@ impl<A: hal::Api> Example<A> {
entry_point: "main",
constants: &Default::default(),
zero_initialize_workgroup_memory: true,
vertex_pulling_transform: false,
},
cache: None,
})
@@ -1040,7 +1039,7 @@ impl<A: hal::Api> Example<A> {
self.surface.unconfigure(&self.device);
self.device.exit(self.queue);
self.instance.destroy_surface(self.surface);
drop(self.surface);
drop(self.adapter);
}
}

Some files were not shown because too many files have changed in this diff Show More