Bug 1868143 - Update wgpu to revision 767ac03245ee937d3dc552edc13fe7ab0a860eec. r=webgpu-reviewers,supply-chain-reviewers,ErichDonGubler

# Changelog

 * #4708 Fix max_vertex_buffers validation
   By nical in https://github.com/gfx-rs/wgpu/pull/4708
 * #4729 Remove expected failure on AMD/DX12 from msaa example
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4729
 * #4711 [naga] Add `Literal::I64`, for signed 64-bit integer literals.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4711
 * #4736 Bump serde from 1.0.192 to 1.0.193
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4736
 * #4735 [naga]: Let `TypeInner::Matrix` hold a `Scalar`, not just a width.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4735
 * #4741 [naga] Fix type error in test.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4741
 * #4742 [naga]: Make snapshot tests include paths in errors.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4742
 * #4723 Test And Normalize Vertex Behavior on All Backends
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4723
 * #4746 Skip Flaky Test
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4746
 * #4744 Ensure DeviceLostClosureC callbacks have null-terminated message strings
   By bradwerth in https://github.com/gfx-rs/wgpu/pull/4744
 * #4745 [naga wgsl-in] Test hex float suffix handling corner case.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4745
 * #4737 Make the command_encoder_clear_buffer's size an Option<BufferAddress>
   By nical in https://github.com/gfx-rs/wgpu/pull/4737
 * #4701 [naga msl-out] Emit and init `struct` member padding always
   By ErichDonGubler in https://github.com/gfx-rs/wgpu/pull/4701
 * #4701 [naga msl-out] Emit and init `struct` member padding always
   By ErichDonGubler in https://github.com/gfx-rs/wgpu/pull/4701
 * #4701 [naga msl-out] Emit and init `struct` member padding always
   By ErichDonGubler in https://github.com/gfx-rs/wgpu/pull/4701
 * #4701 [naga msl-out] Emit and init `struct` member padding always
   By ErichDonGubler in https://github.com/gfx-rs/wgpu/pull/4701
 * #4733 [naga wgsl-in] Preserve type names in `alias` declarations.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4733
 * #4734 [naga] Make compaction preserve named types, even if unused.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4734
 * #4752 Logging cleanups in device/global.rs
   By nical in https://github.com/gfx-rs/wgpu/pull/4752
 * #4753 Fix Mac Event Loop
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4753
 * #4754 wgpu-hal(vk): Add WGPU_ALLOW_NONCOMPLIANT_ADAPTER
   By i509VCB in https://github.com/gfx-rs/wgpu/pull/4754
 * #4748 Allow Tests to Expect Certain Panic or Validation Messages
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4748
 * #4756 Move to A Single Example Executable
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4756
 * #4747 [naga wgsl-in] Experimental 64-bit floating-point literals.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4747
 * #4747 [naga wgsl-in] Experimental 64-bit floating-point literals.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4747
 * #4761 [naga] Make the `example_wgsl` test build without `wgsl-in` feature.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4761
 * #4769 Conditionally lift API logging from trace to info level
   By nical in https://github.com/gfx-rs/wgpu/pull/4769
 * #4771 Downgrade some of wgpu_core's logging level from info to trace and debug
   By nical in https://github.com/gfx-rs/wgpu/pull/4771
 * #4760 Rename `ALLOW_NONCOMPLIANT_ADAPTER` to `ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER`
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4760
 * #4772 Downgrade resource lifetime management log level to trace.
   By nical in https://github.com/gfx-rs/wgpu/pull/4772
 * #4765 Revamp Examples to Match Website
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4765
 * #4774 Update examples readme files
   By roffs in https://github.com/gfx-rs/wgpu/pull/4774
 * #4781 remove_abandoned fix
   By gents83 in https://github.com/gfx-rs/wgpu/pull/4781
 * #4777 Bump web-sys to 0.3.65
   By torokati44 in https://github.com/gfx-rs/wgpu/pull/4777
 * #4780 Fixes for Skybox and All Examples
   By cwfitzgerald in https://github.com/gfx-rs/wgpu/pull/4780
 * #4782 Remove resources ONLY when needed inside wgpu and not in user land
   By gents83 in https://github.com/gfx-rs/wgpu/pull/4782
 * #4786 Don't keep a strong ref in storage for destroyed resources
   By gents83 in https://github.com/gfx-rs/wgpu/pull/4786
 * #4795 Fix Arcanization changelog
   By a1phyr in https://github.com/gfx-rs/wgpu/pull/4795
 * #4794 Delete/rename now-unused gpu.rs
   By exrook in https://github.com/gfx-rs/wgpu/pull/4794
 * #4794 Delete/rename now-unused gpu.rs
   By exrook in https://github.com/gfx-rs/wgpu/pull/4794
 * #4573 Support nv12 texture format
   By xiaopengli89 in https://github.com/gfx-rs/wgpu/pull/4573
 * #4789 Bump js-sys from 0.3.65 to 0.3.66
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4789
 * #4784 Bump winit from 0.29.3 to 0.29.4
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4784
 * #4798 Bump JamesIves/github-pages-deploy-action from 4.4.3 to 4.5.0
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4798
 * #4800 Bump web-sys from 0.3.65 to 0.3.66
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4800
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4743 Implement WGSL abstract types for global `const` declarations and constructor calls.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4743
 * #4799 Bump wasm-bindgen-futures from 0.4.38 to 0.4.39
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4799
 * #4796 Remove surface extent validation (and thus fix the annoying `Requested size ... is outside of the supported range` warning)
   By Wumpf in https://github.com/gfx-rs/wgpu/pull/4796
 * #4804 Add space to a comment inside make_spirv_raw
   By ComfyFluffy in https://github.com/gfx-rs/wgpu/pull/4804
 * #4803 `features`/`limits` refactors
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4803
 * #4803 `features`/`limits` refactors
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4803
 * #4803 `features`/`limits` refactors
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4803
 * #4803 `features`/`limits` refactors
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4803
 * #4803 `features`/`limits` refactors
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4803
 * #4805 [naga] Improve ConstantEvaluatorError::InvalidCastArg message.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4805
 * #4801 update deno
   By crowlKats in https://github.com/gfx-rs/wgpu/pull/4801
 * #4806 Fixes and changes to the documentation for increasing clarity
   By Blatko1 in https://github.com/gfx-rs/wgpu/pull/4806
 * #4809 [naga wgsl-in] Use a better span for errors in constructors.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4809
 * #4808 [naga wgsl-in] Drop spanless labels from front-end error messages.
   By jimblandy in https://github.com/gfx-rs/wgpu/pull/4808
 * #4822 Fix expected error message
   By teoxoy in https://github.com/gfx-rs/wgpu/pull/4822
 * #4759 Add feature float32-filterable
   By almarklein in https://github.com/gfx-rs/wgpu/pull/4759
 * #4813 Bump core-graphics-types from 0.1.2 to 0.1.3
   By dependabot[bot] in https://github.com/gfx-rs/wgpu/pull/4813

Differential Revision: https://phabricator.services.mozilla.com/D195438
This commit is contained in:
Nicolas Silva 2023-12-04 22:30:03 +00:00
parent 4faadd5909
commit 0a15b03add
99 changed files with 2509 additions and 1039 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=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"]
[source."git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec"]
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
replace-with = "vendored-sources"
[source."git+https://github.com/glandium/warp?rev=4af45fae95bc98b0eba1ef0db17e1dac471bb23d"]

10
Cargo.lock generated
View File

@ -1157,7 +1157,7 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.7.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee#a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
source = "git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec#767ac03245ee937d3dc552edc13fe7ab0a860eec"
dependencies = [
"bitflags 2.4.0",
"libloading",
@ -3793,7 +3793,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
version = "0.14.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee#a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
source = "git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec#767ac03245ee937d3dc552edc13fe7ab0a860eec"
dependencies = [
"bit-set",
"bitflags 2.4.0",
@ -6395,7 +6395,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.18.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee#a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
source = "git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec#767ac03245ee937d3dc552edc13fe7ab0a860eec"
dependencies = [
"arrayvec",
"bit-vec",
@ -6418,7 +6418,7 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.18.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee#a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
source = "git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec#767ac03245ee937d3dc552edc13fe7ab0a860eec"
dependencies = [
"android_system_properties",
"arrayvec",
@ -6455,7 +6455,7 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.18.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=a820a3ffba468cbb87c2a7e7bbe37065ed5207ee#a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
source = "git+https://github.com/gfx-rs/wgpu?rev=767ac03245ee937d3dc552edc13fe7ab0a860eec#767ac03245ee937d3dc552edc13fe7ab0a860eec"
dependencies = [
"bitflags 2.4.0",
"js-sys",

View File

@ -130,10 +130,7 @@ static Maybe<ffi::WGPUFeatures> ToWGPUFeatures(
return Some(WGPUFeatures_BGRA8UNORM_STORAGE);
case dom::GPUFeatureName::Float32_filterable:
#ifdef WGPUFeatures_FLOAT32_FILTERABLE
# error fix todo
#endif
return Nothing(); // TODO
return Some(WGPUFeatures_FLOAT32_FILTERABLE);
case dom::GPUFeatureName::EndGuard_:
break;
@ -454,15 +451,16 @@ already_AddRefed<dom::Promise> Adapter::RequestDevice(
// -
ffi::WGPUDeviceDescriptor ffiDesc = {};
ffiDesc.features = *MakeFeatureBits(aDesc.mRequiredFeatures);
ffiDesc.limits = deviceLimits;
ffiDesc.required_features = *MakeFeatureBits(aDesc.mRequiredFeatures);
ffiDesc.required_limits = deviceLimits;
auto request = mBridge->AdapterRequestDevice(mId, ffiDesc);
if (!request) {
promise->MaybeRejectWithNotSupportedError(
"Unable to instantiate a Device");
return;
}
RefPtr<Device> device = new Device(this, request->mId, ffiDesc.limits);
RefPtr<Device> device =
new Device(this, request->mId, ffiDesc.required_limits);
for (const auto& feature : aDesc.mRequiredFeatures) {
device->mFeatures->Add(feature, aRv);
}

View File

@ -17,7 +17,7 @@ default = []
[dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
#Note: "replay" shouldn't ideally be needed,
# but it allows us to serialize everything across IPC.
features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl"]
@ -27,36 +27,36 @@ features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl"]
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
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 = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
features = ["dx11", "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 = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
features = ["vulkan"]
[dependencies.wgt]
package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
[dependencies.wgh]
package = "wgpu-hal"
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
[target.'cfg(windows)'.dependencies.d3d12]
git = "https://github.com/gfx-rs/wgpu"
rev = "a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
rev = "767ac03245ee937d3dc552edc13fe7ab0a860eec"
[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: commit a820a3ffba468cbb87c2a7e7bbe37065ed5207ee
release: commit 767ac03245ee937d3dc552edc13fe7ab0a860eec
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
revision: a820a3ffba468cbb87c2a7e7bbe37065ed5207ee
revision: 767ac03245ee937d3dc552edc13fe7ab0a860eec
license: ['MIT', 'Apache-2.0']

View File

@ -580,6 +580,7 @@ pub extern "C" fn wgpu_client_create_texture_view(
base_array_layer: desc.base_array_layer,
array_layer_count: desc.array_layer_count.map(|ptr| *ptr),
},
plane: None,
};
let action = TextureAction::CreateView(id, wgpu_desc);

View File

@ -1201,6 +1201,11 @@ who = "Erich Gubler <egubler@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.7.0@git:6e21f7a9291db4395192d6b510d906978ae2d251 -> 0.7.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
[[audits.d3d12]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.7.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee -> 0.7.0@git:767ac03245ee937d3dc552edc13fe7ab0a860eec"
[[audits.d3d12]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
@ -2492,6 +2497,11 @@ who = "Erich Gubler <egubler@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.14.0@git:6e21f7a9291db4395192d6b510d906978ae2d251 -> 0.14.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
[[audits.naga]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.14.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee -> 0.14.0@git:767ac03245ee937d3dc552edc13fe7ab0a860eec"
[[audits.naga]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
@ -4255,6 +4265,11 @@ who = "Erich Gubler <egubler@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:6e21f7a9291db4395192d6b510d906978ae2d251 -> 0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
[[audits.wgpu-core]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee -> 0.18.0@git:767ac03245ee937d3dc552edc13fe7ab0a860eec"
[[audits.wgpu-core]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
@ -4323,6 +4338,11 @@ who = "Erich Gubler <egubler@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:6e21f7a9291db4395192d6b510d906978ae2d251 -> 0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
[[audits.wgpu-hal]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee -> 0.18.0@git:767ac03245ee937d3dc552edc13fe7ab0a860eec"
[[audits.wgpu-hal]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
@ -4391,6 +4411,11 @@ who = "Erich Gubler <egubler@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:6e21f7a9291db4395192d6b510d906978ae2d251 -> 0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee"
[[audits.wgpu-types]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.18.0@git:a820a3ffba468cbb87c2a7e7bbe37065ed5207ee -> 0.18.0@git:767ac03245ee937d3dc552edc13fe7ab0a860eec"
[[audits.wgpu-types]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"

View File

@ -1 +1 @@
{"files":{"CHANGELOG.md":"45fa76b0e5bc51721887147000e9e78a5934cb04d1ad628e501ef2082763d353","Cargo.toml":"1b0564721518684589ff305dd77017193ef116cd29c19eb6d92c81a9f9962a4c","README.md":"76cee3209f773a62535de6c9724b53f158406359f35b4d48b17ac3747b6c102e","src/com.rs":"cfc8f5692162b4cbf9bf370959ad0be08a40a7e9026b5511ca0065ffe33f72ab","src/command_allocator.rs":"ef01059a661749470f3772d188fe0fab0f002e1d154facdab4b9b2932f4b2d93","src/command_list.rs":"beb49cf5a4e53e3e5b121ea7c3247fc8e74eaba67f1fb7dee3416174d5a9fa07","src/debug.rs":"67c8eb966cf349038f79c691e42b5d86cd8fc63c40d10cc279172400d56db6dc","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":"ff61a2b76a108afc1f082724bb9b07ac8b52afbe97356e0fcf6df0ff7e53e07d","src/queue.rs":"bd32813d0b8a3bedf3223b69ade9f9c799a138a9e27d970f86435d9ce32d1557","src/resource.rs":"8989cdb7c3ee0687c826047f39f85148459d9219754f20a970bf8aaa09b96e27","src/sync.rs":"5c287fb7498242a397eb1f08887be9cff9b48dc7cb13af5792cce5f7182b55f8"},"package":null}
{"files":{"CHANGELOG.md":"45fa76b0e5bc51721887147000e9e78a5934cb04d1ad628e501ef2082763d353","Cargo.toml":"1b0564721518684589ff305dd77017193ef116cd29c19eb6d92c81a9f9962a4c","README.md":"76cee3209f773a62535de6c9724b53f158406359f35b4d48b17ac3747b6c102e","src/com.rs":"cfc8f5692162b4cbf9bf370959ad0be08a40a7e9026b5511ca0065ffe33f72ab","src/command_allocator.rs":"ef01059a661749470f3772d188fe0fab0f002e1d154facdab4b9b2932f4b2d93","src/command_list.rs":"8723f3b755b721e0dbb234bd604956c1b7922a2368231197495daa3fa6548e63","src/debug.rs":"67c8eb966cf349038f79c691e42b5d86cd8fc63c40d10cc279172400d56db6dc","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":"ff61a2b76a108afc1f082724bb9b07ac8b52afbe97356e0fcf6df0ff7e53e07d","src/queue.rs":"bd32813d0b8a3bedf3223b69ade9f9c799a138a9e27d970f86435d9ce32d1557","src/resource.rs":"8989cdb7c3ee0687c826047f39f85148459d9219754f20a970bf8aaa09b96e27","src/sync.rs":"5c287fb7498242a397eb1f08887be9cff9b48dc7cb13af5792cce5f7182b55f8"},"package":null}

View File

@ -213,11 +213,11 @@ impl GraphicsCommandList {
&self,
num_vertices: VertexCount,
num_instances: InstanceCount,
start_vertex: VertexCount,
start_instance: InstanceCount,
first_vertex: VertexCount,
first_instance: InstanceCount,
) {
unsafe {
self.DrawInstanced(num_vertices, num_instances, start_vertex, start_instance);
self.DrawInstanced(num_vertices, num_instances, first_vertex, first_instance);
}
}
@ -225,17 +225,17 @@ impl GraphicsCommandList {
&self,
num_indices: IndexCount,
num_instances: InstanceCount,
start_index: IndexCount,
first_index: IndexCount,
base_vertex: VertexOffset,
start_instance: InstanceCount,
first_instance: InstanceCount,
) {
unsafe {
self.DrawIndexedInstanced(
num_indices,
num_instances,
start_index,
first_index,
base_vertex,
start_instance,
first_instance,
);
}
}

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@ exclude = [
"Cargo.lock",
"target/**/*",
]
autotests = false
description = "Shader translation infrastructure"
readme = "README.md"
keywords = [
@ -36,6 +37,10 @@ resolver = "2"
[package.metadata.docs.rs]
all-features = true
[[test]]
name = "naga-test"
path = "tests/root.rs"
[[bench]]
name = "criterion"
harness = false
@ -143,6 +148,7 @@ spv-out = ["spirv"]
wgsl-in = [
"hexf-parse",
"unicode-xid",
"compact",
]
wgsl-out = []

View File

@ -33,7 +33,7 @@ DOT (GraphViz) | :ok: | dot-out | Not a shading language |
## Conversion tool
Naga can be used as a CLI, which allows to test the conversion of different code paths.
Naga can be used as a CLI, which allows testing the conversion of different code paths.
First, install `naga-cli` from crates.io or directly from GitHub.
@ -54,7 +54,7 @@ naga my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Meta
naga my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile
```
As naga includes a default binary target, you can also use `cargo run` without installation. This is useful when you develop naga itself, or investigate the behavior of naga at a specific commit (e.g. [wgpu](https://github.com/gfx-rs/wgpu) might pin a different version of naga than the `HEAD` of this repository).
As naga includes a default binary target, you can also use `cargo run` without installation. This is useful when you develop naga itself or investigate the behavior of naga at a specific commit (e.g. [wgpu](https://github.com/gfx-rs/wgpu) might pin a different version of naga than the `HEAD` of this repository).
```bash
cargo run my_shader.wgsl
@ -63,7 +63,7 @@ cargo run my_shader.wgsl
## Development workflow
The main instrument aiding the development is the good old `cargo test --all-features --workspace`,
which will run the unit tests, and also update all the snapshots. You'll see these
which will run the unit tests and also update all the snapshots. You'll see these
changes in git before committing the code.
If working on a particular front-end or back-end, it may be convenient to
@ -71,7 +71,7 @@ enable the relevant features in `Cargo.toml`, e.g.
```toml
default = ["spv-out"] #TEMP!
```
This allows IDE basic checks to report errors there, unless your IDE is sufficiently configurable already.
This allows IDE basic checks to report errors there unless your IDE is sufficiently configurable already.
Finally, when changes to the snapshots are made, we should verify that the produced shaders
are indeed valid for the target platforms they are compiled for:

View File

@ -1,5 +1,6 @@
use super::{BackendResult, Error, Version, Writer};
use crate::{
back::glsl::{Options, WriterFlags},
AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation, Sampling,
Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner,
};
@ -43,6 +44,10 @@ bitflags::bitflags! {
const IMAGE_SIZE = 1 << 20;
/// Dual source blending
const DUAL_SOURCE_BLENDING = 1 << 21;
/// Instance index
///
/// We can always support this, either through the language or a polyfill
const INSTANCE_INDEX = 1 << 22;
}
}
@ -63,6 +68,11 @@ impl FeaturesManager {
self.0 |= features
}
/// Checks if the list of features [`Features`] contains the specified [`Features`]
pub fn contains(&mut self, features: Features) -> bool {
self.0.contains(features)
}
/// Checks that all required [`Features`] are available for the specified
/// [`Version`] otherwise returns an [`Error::MissingFeatures`].
pub fn check_availability(&self, version: Version) -> BackendResult {
@ -129,13 +139,13 @@ impl FeaturesManager {
/// # Notes
/// This won't check for feature availability so it might output extensions that aren't even
/// supported.[`check_availability`](Self::check_availability) will check feature availability
pub fn write(&self, version: Version, mut out: impl Write) -> BackendResult {
if self.0.contains(Features::COMPUTE_SHADER) && !version.is_es() {
pub fn write(&self, options: &Options, mut out: impl Write) -> BackendResult {
if self.0.contains(Features::COMPUTE_SHADER) && !options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_compute_shader.txt
writeln!(out, "#extension GL_ARB_compute_shader : require")?;
}
if self.0.contains(Features::BUFFER_STORAGE) && !version.is_es() {
if self.0.contains(Features::BUFFER_STORAGE) && !options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_storage_buffer_object.txt
writeln!(
out,
@ -143,22 +153,22 @@ impl FeaturesManager {
)?;
}
if self.0.contains(Features::DOUBLE_TYPE) && version < Version::Desktop(400) {
if self.0.contains(Features::DOUBLE_TYPE) && options.version < Version::Desktop(400) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gpu_shader_fp64.txt
writeln!(out, "#extension GL_ARB_gpu_shader_fp64 : require")?;
}
if self.0.contains(Features::CUBE_TEXTURES_ARRAY) {
if version.is_es() {
if options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_cube_map_array.txt
writeln!(out, "#extension GL_EXT_texture_cube_map_array : require")?;
} else if version < Version::Desktop(400) {
} else if options.version < Version::Desktop(400) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_cube_map_array.txt
writeln!(out, "#extension GL_ARB_texture_cube_map_array : require")?;
}
}
if self.0.contains(Features::MULTISAMPLED_TEXTURE_ARRAYS) && version.is_es() {
if self.0.contains(Features::MULTISAMPLED_TEXTURE_ARRAYS) && options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_texture_storage_multisample_2d_array.txt
writeln!(
out,
@ -166,49 +176,49 @@ impl FeaturesManager {
)?;
}
if self.0.contains(Features::ARRAY_OF_ARRAYS) && version < Version::Desktop(430) {
if self.0.contains(Features::ARRAY_OF_ARRAYS) && options.version < Version::Desktop(430) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_arrays_of_arrays.txt
writeln!(out, "#extension ARB_arrays_of_arrays : require")?;
}
if self.0.contains(Features::IMAGE_LOAD_STORE) {
if self.0.contains(Features::FULL_IMAGE_FORMATS) && version.is_es() {
if self.0.contains(Features::FULL_IMAGE_FORMATS) && options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/NV/NV_image_formats.txt
writeln!(out, "#extension GL_NV_image_formats : require")?;
}
if version < Version::Desktop(420) {
if options.version < Version::Desktop(420) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_image_load_store.txt
writeln!(out, "#extension GL_ARB_shader_image_load_store : require")?;
}
}
if self.0.contains(Features::CONSERVATIVE_DEPTH) {
if version.is_es() {
if options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_conservative_depth.txt
writeln!(out, "#extension GL_EXT_conservative_depth : require")?;
}
if version < Version::Desktop(420) {
if options.version < Version::Desktop(420) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt
writeln!(out, "#extension GL_ARB_conservative_depth : require")?;
}
}
if (self.0.contains(Features::CLIP_DISTANCE) || self.0.contains(Features::CULL_DISTANCE))
&& version.is_es()
&& options.version.is_es()
{
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_clip_cull_distance.txt
writeln!(out, "#extension GL_EXT_clip_cull_distance : require")?;
}
if self.0.contains(Features::SAMPLE_VARIABLES) && version.is_es() {
if self.0.contains(Features::SAMPLE_VARIABLES) && options.version.is_es() {
// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_sample_variables.txt
writeln!(out, "#extension GL_OES_sample_variables : require")?;
}
if self.0.contains(Features::MULTI_VIEW) {
if let Version::Embedded { is_webgl: true, .. } = version {
if let Version::Embedded { is_webgl: true, .. } = options.version {
// https://www.khronos.org/registry/OpenGL/extensions/OVR/OVR_multiview2.txt
writeln!(out, "#extension GL_OVR_multiview2 : require")?;
} else {
@ -225,15 +235,22 @@ impl FeaturesManager {
)?;
}
if self.0.contains(Features::TEXTURE_LEVELS) && version < Version::Desktop(430) {
if self.0.contains(Features::TEXTURE_LEVELS) && options.version < Version::Desktop(430) {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt
writeln!(out, "#extension GL_ARB_texture_query_levels : require")?;
}
if self.0.contains(Features::DUAL_SOURCE_BLENDING) && version.is_es() {
if self.0.contains(Features::DUAL_SOURCE_BLENDING) && options.version.is_es() {
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_blend_func_extended.txt
writeln!(out, "#extension GL_EXT_blend_func_extended : require")?;
}
if self.0.contains(Features::INSTANCE_INDEX) {
if options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS) {
// https://registry.khronos.org/OpenGL/extensions/ARB/ARB_shader_draw_parameters.txt
writeln!(out, "#extension GL_ARB_shader_draw_parameters : require")?;
}
}
Ok(())
}
}
@ -490,6 +507,9 @@ impl<'a, W> Writer<'a, W> {
crate::BuiltIn::ViewIndex => {
self.features.request(Features::MULTI_VIEW)
}
crate::BuiltIn::InstanceIndex => {
self.features.request(Features::INSTANCE_INDEX)
}
_ => {}
},
Binding::Location {

View File

@ -480,4 +480,5 @@ pub const RESERVED_KEYWORDS: &[&str] = &[
// Naga utilities:
super::MODF_FUNCTION,
super::FREXP_FUNCTION,
super::FIRST_INSTANCE_BINDING,
];

View File

@ -76,6 +76,9 @@ const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
// Must match code in glsl_built_in
pub const FIRST_INSTANCE_BINDING: &str = "naga_vs_first_instance";
/// Mapping between resources and bindings.
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>;
@ -235,17 +238,20 @@ bitflags::bitflags! {
/// Supports GL_EXT_texture_shadow_lod on the host, which provides
/// additional functions on shadows and arrays of shadows.
const TEXTURE_SHADOW_LOD = 0x2;
/// Supports ARB_shader_draw_parameters on the host, which provides
/// support for `gl_BaseInstanceARB`, `gl_BaseVertexARB`, and `gl_DrawIDARB`.
const DRAW_PARAMETERS = 0x4;
/// Include unused global variables, constants and functions. By default the output will exclude
/// global variables that are not used in the specified entrypoint (including indirect use),
/// all constant declarations, and functions that use excluded global variables.
const INCLUDE_UNUSED_ITEMS = 0x4;
const INCLUDE_UNUSED_ITEMS = 0x10;
/// Emit `PointSize` output builtin to vertex shaders, which is
/// required for drawing with `PointList` topology.
///
/// https://registry.khronos.org/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.html#built-in-language-variables
/// The variable gl_PointSize is intended for a shader to write the size of the point to be rasterized. It is measured in pixels.
/// If gl_PointSize is not written to, its value is undefined in subsequent pipe stages.
const FORCE_POINT_SIZE = 0x10;
const FORCE_POINT_SIZE = 0x20;
}
}
@ -388,6 +394,24 @@ impl IdGenerator {
}
}
/// Assorted options needed for generting varyings.
#[derive(Clone, Copy)]
struct VaryingOptions {
output: bool,
targetting_webgl: bool,
draw_parameters: bool,
}
impl VaryingOptions {
const fn from_writer_options(options: &Options, output: bool) -> Self {
Self {
output,
targetting_webgl: options.version.is_webgl(),
draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS),
}
}
}
/// Helper wrapper used to get a name for a varying
///
/// Varying have different naming schemes depending on their binding:
@ -398,8 +422,7 @@ impl IdGenerator {
struct VaryingName<'a> {
binding: &'a crate::Binding,
stage: ShaderStage,
output: bool,
targetting_webgl: bool,
options: VaryingOptions,
}
impl fmt::Display for VaryingName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -411,7 +434,7 @@ impl fmt::Display for VaryingName<'_> {
write!(f, "_fs2p_location1",)
}
crate::Binding::Location { location, .. } => {
let prefix = match (self.stage, self.output) {
let prefix = match (self.stage, self.options.output) {
(ShaderStage::Compute, _) => unreachable!(),
// pipeline to vertex
(ShaderStage::Vertex, false) => "p2vs",
@ -423,11 +446,7 @@ impl fmt::Display for VaryingName<'_> {
write!(f, "_{prefix}_location{location}",)
}
crate::Binding::BuiltIn(built_in) => {
write!(
f,
"{}",
glsl_built_in(built_in, self.output, self.targetting_webgl)
)
write!(f, "{}", glsl_built_in(built_in, self.options))
}
}
}
@ -569,7 +588,11 @@ impl<'a, W: Write> Writer<'a, W> {
keywords::RESERVED_KEYWORDS,
&[],
&[],
&["gl_"],
&[
"gl_", // all GL built-in variables
"_group", // all normal bindings
"_push_constant_binding_", // all push constant bindings
],
&mut names,
);
@ -621,7 +644,7 @@ impl<'a, W: Write> Writer<'a, W> {
// writing the module saving some loops but some older versions (420 or less) required the
// extensions to appear before being used, even though extensions are part of the
// preprocessor not the processor ¯\_(ツ)_/¯
self.features.write(self.options.version, &mut self.out)?;
self.features.write(self.options, &mut self.out)?;
// Write the additional extensions
if self
@ -652,6 +675,17 @@ impl<'a, W: Write> Writer<'a, W> {
writeln!(self.out)?;
}
if self.entry_point.stage == ShaderStage::Vertex
&& !self
.options
.writer_flags
.contains(WriterFlags::DRAW_PARAMETERS)
&& self.features.contains(Features::INSTANCE_INDEX)
{
writeln!(self.out, "uniform uint {FIRST_INSTANCE_BINDING};")?;
writeln!(self.out)?;
}
// Enable early depth tests if needed
if let Some(depth_test) = self.entry_point.early_depth_test {
// If early depth test is supported for this version of GLSL
@ -1422,7 +1456,10 @@ impl<'a, W: Write> Writer<'a, W> {
writeln!(
self.out,
"invariant {};",
glsl_built_in(built_in, output, self.options.version.is_webgl())
glsl_built_in(
built_in,
VaryingOptions::from_writer_options(self.options, output)
)
)?;
}
}
@ -1499,8 +1536,7 @@ impl<'a, W: Write> Writer<'a, W> {
second_blend_source,
},
stage: self.entry_point.stage,
output,
targetting_webgl: self.options.version.is_webgl(),
options: VaryingOptions::from_writer_options(self.options, output),
};
writeln!(self.out, " {vname};")?;
@ -1661,8 +1697,7 @@ impl<'a, W: Write> Writer<'a, W> {
let varying_name = VaryingName {
binding: member.binding.as_ref().unwrap(),
stage,
output: false,
targetting_webgl: self.options.version.is_webgl(),
options: VaryingOptions::from_writer_options(self.options, false),
};
if index != 0 {
write!(self.out, ", ")?;
@ -1675,8 +1710,7 @@ impl<'a, W: Write> Writer<'a, W> {
let varying_name = VaryingName {
binding: arg.binding.as_ref().unwrap(),
stage,
output: false,
targetting_webgl: self.options.version.is_webgl(),
options: VaryingOptions::from_writer_options(self.options, false),
};
writeln!(self.out, "{varying_name};")?;
}
@ -2170,8 +2204,10 @@ impl<'a, W: Write> Writer<'a, W> {
let varying_name = VaryingName {
binding: member.binding.as_ref().unwrap(),
stage: ep.stage,
output: true,
targetting_webgl: self.options.version.is_webgl(),
options: VaryingOptions::from_writer_options(
self.options,
true,
),
};
write!(self.out, "{varying_name} = ")?;
@ -2195,8 +2231,10 @@ impl<'a, W: Write> Writer<'a, W> {
let name = VaryingName {
binding: result.binding.as_ref().unwrap(),
stage: ep.stage,
output: true,
targetting_webgl: self.options.version.is_webgl(),
options: VaryingOptions::from_writer_options(
self.options,
true,
),
};
write!(self.out, "{name} = ")?;
self.write_expr(value, ctx)?;
@ -2413,6 +2451,11 @@ impl<'a, W: Write> Writer<'a, W> {
crate::Literal::I64(_) => {
return Err(Error::Custom("GLSL has no 64-bit integer type".into()));
}
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".into(),
));
}
}
}
Expression::Constant(handle) => {
@ -3517,6 +3560,9 @@ impl<'a, W: Write> Writer<'a, W> {
(Sk::Sint | Sk::Uint | Sk::Float, Sk::Bool, None) => {
write!(self.out, "bool")?
}
(Sk::AbstractInt | Sk::AbstractFloat, _, _)
| (_, Sk::AbstractInt | Sk::AbstractFloat, _) => unreachable!(),
};
write!(self.out, "(")?;
@ -4079,6 +4125,11 @@ impl<'a, W: Write> Writer<'a, W> {
crate::ScalarKind::Uint => write!(self.out, "0u")?,
crate::ScalarKind::Float => write!(self.out, "0.0")?,
crate::ScalarKind::Sint => write!(self.out, "0")?,
crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".to_string(),
))
}
}
Ok(())
@ -4307,33 +4358,39 @@ const fn glsl_scalar(scalar: crate::Scalar) -> Result<ScalarString<'static>, Err
prefix: "b",
full: "bool",
},
Sk::AbstractInt | Sk::AbstractFloat => {
return Err(Error::UnsupportedScalar(scalar));
}
})
}
/// Helper function that returns the glsl variable name for a builtin
const fn glsl_built_in(
built_in: crate::BuiltIn,
output: bool,
targetting_webgl: bool,
) -> &'static str {
const fn glsl_built_in(built_in: crate::BuiltIn, options: VaryingOptions) -> &'static str {
use crate::BuiltIn as Bi;
match built_in {
Bi::Position { .. } => {
if output {
if options.output {
"gl_Position"
} else {
"gl_FragCoord"
}
}
Bi::ViewIndex if targetting_webgl => "int(gl_ViewID_OVR)",
Bi::ViewIndex if options.targetting_webgl => "int(gl_ViewID_OVR)",
Bi::ViewIndex => "gl_ViewIndex",
// vertex
Bi::BaseInstance => "uint(gl_BaseInstance)",
Bi::BaseVertex => "uint(gl_BaseVertex)",
Bi::ClipDistance => "gl_ClipDistance",
Bi::CullDistance => "gl_CullDistance",
Bi::InstanceIndex => "uint(gl_InstanceID)",
Bi::InstanceIndex => {
if options.draw_parameters {
"(uint(gl_InstanceID) + uint(gl_BaseInstanceARB))"
} else {
// Must match FISRT_INSTANCE_BINDING
"(uint(gl_InstanceID) + naga_vs_first_instance)"
}
}
Bi::PointSize => "gl_PointSize",
Bi::VertexIndex => "uint(gl_VertexID)",
// fragment
@ -4343,7 +4400,7 @@ const fn glsl_built_in(
Bi::PrimitiveIndex => "uint(gl_PrimitiveID)",
Bi::SampleIndex => "gl_SampleID",
Bi::SampleMask => {
if output {
if options.output {
"gl_SampleMask"
} else {
"gl_SampleMaskIn"

View File

@ -10,7 +10,7 @@ impl crate::ScalarKind {
Self::Float => "asfloat",
Self::Sint => "asint",
Self::Uint => "asuint",
Self::Bool => unreachable!(),
Self::Bool | Self::AbstractInt | Self::AbstractFloat => unreachable!(),
}
}
}
@ -30,6 +30,9 @@ impl crate::Scalar {
_ => Err(Error::UnsupportedScalar(self)),
},
crate::ScalarKind::Bool => Ok("bool"),
crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
Err(Error::UnsupportedScalar(self))
}
}
}
}

View File

@ -13,8 +13,8 @@ use std::{fmt, mem};
const LOCATION_SEMANTIC: &str = "LOC";
const SPECIAL_CBUF_TYPE: &str = "NagaConstants";
const SPECIAL_CBUF_VAR: &str = "_NagaConstants";
const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance";
const SPECIAL_FIRST_VERTEX: &str = "first_vertex";
const SPECIAL_FIRST_INSTANCE: &str = "first_instance";
const SPECIAL_OTHER: &str = "other";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
@ -189,8 +189,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
// Write special constants, if needed
if let Some(ref bt) = self.options.special_constants_binding {
writeln!(self.out, "struct {SPECIAL_CBUF_TYPE} {{")?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_VERTEX)?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_INSTANCE)?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_VERTEX)?;
writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_INSTANCE)?;
writeln!(self.out, "{}uint {};", back::INDENT, SPECIAL_OTHER)?;
writeln!(self.out, "}};")?;
write!(
@ -2040,6 +2040,11 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
crate::Literal::I32(value) => write!(self.out, "{}", value)?,
crate::Literal::I64(value) => write!(self.out, "{}L", value)?,
crate::Literal::Bool(value) => write!(self.out, "{}", value)?,
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".into(),
));
}
},
Expression::Constant(handle) => {
let constant = &module.constants[handle];
@ -2102,7 +2107,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
) -> BackendResult {
use crate::Expression;
// Handle the special semantics for base vertex/instance
// Handle the special semantics of vertex_index/instance_index
let ff_input = if self.options.special_constants_binding.is_some() {
func_ctx.is_fixed_function_input(expr, module)
} else {
@ -2110,20 +2115,20 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
};
let closing_bracket = match ff_input {
Some(crate::BuiltIn::VertexIndex) => {
write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_BASE_VERTEX} + ")?;
write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX} + ")?;
")"
}
Some(crate::BuiltIn::InstanceIndex) => {
write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_BASE_INSTANCE} + ",)?;
write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE} + ",)?;
")"
}
Some(crate::BuiltIn::NumWorkGroups) => {
//Note: despite their names (`BASE_VERTEX` and `BASE_INSTANCE`),
// Note: despite their names (`FIRST_VERTEX` and `FIRST_INSTANCE`),
// in compute shaders the special constants contain the number
// of workgroups, which we are using here.
write!(
self.out,
"uint3({SPECIAL_CBUF_VAR}.{SPECIAL_BASE_VERTEX}, {SPECIAL_CBUF_VAR}.{SPECIAL_BASE_INSTANCE}, {SPECIAL_CBUF_VAR}.{SPECIAL_OTHER})",
"uint3({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX}, {SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE}, {SPECIAL_CBUF_VAR}.{SPECIAL_OTHER})",
)?;
return Ok(());
}

View File

@ -338,6 +338,10 @@ impl crate::Scalar {
kind: Sk::Bool,
width: _,
} => "bool",
Self {
kind: Sk::AbstractInt | Sk::AbstractFloat,
width: _,
} => unreachable!(),
}
}
}
@ -357,11 +361,6 @@ fn should_pack_struct_member(
module: &crate::Module,
) -> Option<crate::Scalar> {
let member = &members[index];
//Note: this is imperfect - the same structure can be used for host-shared
// things, where packed float would matter.
if member.binding.is_some() {
return None;
}
let ty_inner = &module.types[member.ty].inner;
let last_offset = member.offset + ty_inner.size(module.to_ctx());
@ -375,7 +374,7 @@ fn should_pack_struct_member(
crate::TypeInner::Vector {
size: crate::VectorSize::Tri,
scalar: scalar @ crate::Scalar { width: 4, .. },
} if member.offset & 0xF != 0 || is_tight => Some(scalar),
} if is_tight => Some(scalar),
_ => None,
}
}
@ -1280,6 +1279,9 @@ impl<W: Write> Writer<W> {
crate::Literal::Bool(value) => {
write!(self.out, "{value}")?;
}
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Validation);
}
},
crate::Expression::Constant(handle) => {
let constant = &module.constants[handle];
@ -3307,8 +3309,7 @@ impl<W: Write> Writer<W> {
writeln!(self.out, "struct {name} {{")?;
let mut last_offset = 0;
for (index, member) in members.iter().enumerate() {
// quick and dirty way to figure out if we need this...
if member.binding.is_none() && member.offset > last_offset {
if member.offset > last_offset {
self.struct_member_pads.insert((handle, index as u32));
let pad = member.offset - last_offset;
writeln!(self.out, "{}char _pad{}[{}];", back::INDENT, index, pad)?;
@ -4275,6 +4276,13 @@ impl<W: Write> Writer<W> {
if member_index != 0 {
write!(self.out, ", ")?;
}
// insert padding initialization, if needed
if self
.struct_member_pads
.contains(&(arg.ty, member_index as u32))
{
write!(self.out, "{{}}, ")?;
}
if let Some(crate::Binding::Location { .. }) = member.binding {
write!(self.out, "{varyings_member_name}.")?;
}

View File

@ -1175,7 +1175,7 @@ impl<'w> BlockContext<'w> {
let op = match src_scalar.kind {
Sk::Sint | Sk::Uint => spirv::Op::INotEqual,
Sk::Float => spirv::Op::FUnordNotEqual,
Sk::Bool => unreachable!(),
Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => unreachable!(),
};
let zero_scalar_id =
self.writer.get_constant_scalar_with(0, src_scalar)?;

View File

@ -334,6 +334,10 @@ impl<'w> BlockContext<'w> {
(_, crate::ScalarKind::Bool | crate::ScalarKind::Float) => {
unreachable!("we don't allow bool or float for array index")
}
(crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat, _)
| (_, crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat) => {
unreachable!("abstract types should never reach backends")
}
};
let reconciled_array_index_id = if let Some(cast) = cast {
let component_ty_id = self.get_type_id(LookupType::Local(LocalType::Value {

View File

@ -824,6 +824,9 @@ impl Writer {
Instruction::type_float(id, bits)
}
Sk::Bool => Instruction::type_bool(id),
Sk::AbstractInt | Sk::AbstractFloat => {
unreachable!("abstract types should never reach the backend");
}
}
}
@ -1184,6 +1187,9 @@ impl Writer {
}
crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
unreachable!("Abstract types should not appear in IR presented to backends");
}
};
instruction.to_words(&mut self.logical_layout.declarations);
@ -1591,6 +1597,11 @@ impl Writer {
| crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
Sk::Uint | Sk::Sint | Sk::Bool => true,
Sk::Float => false,
Sk::AbstractInt | Sk::AbstractFloat => {
return Err(Error::Validation(
"Abstract types should not appear in IR presented to backends",
))
}
},
_ => false,
};

View File

@ -1095,12 +1095,15 @@ impl<W: Write> Writer<W> {
crate::Literal::U32(value) => write!(self.out, "{}u", value)?,
crate::Literal::I32(value) => write!(self.out, "{}", value)?,
crate::Literal::Bool(value) => write!(self.out, "{}", value)?,
crate::Literal::F64(_) => {
return Err(Error::Custom("unsupported f64 literal".to_string()));
}
crate::Literal::F64(value) => write!(self.out, "{:?}lf", value)?,
crate::Literal::I64(_) => {
return Err(Error::Custom("unsupported i64 literal".to_string()));
}
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".into(),
));
}
}
}
Expression::Constant(handle) => {

View File

@ -100,6 +100,14 @@ pub fn compact(module: &mut crate::Module) {
}
}
// Treat all named types as used.
for (handle, ty) in module.types.iter() {
log::trace!("tracing type {:?}, name {:?}", handle, ty.name);
if ty.name.is_some() {
module_tracer.types_used.insert(handle);
}
}
// Propagate usage through types.
module_tracer.as_type().trace_types();

View File

@ -205,7 +205,7 @@ pub const fn type_power(scalar: Scalar) -> Option<u32> {
ScalarKind::Uint => 1,
ScalarKind::Float if scalar.width == 4 => 2,
ScalarKind::Float => 3,
ScalarKind::Bool => return None,
ScalarKind::Bool | ScalarKind::AbstractInt | ScalarKind::AbstractFloat => return None,
})
}

View File

@ -34,9 +34,9 @@ impl ParseError {
.with_labels(
self.labels
.iter()
.map(|label| {
Label::primary((), label.0.to_range().unwrap())
.with_message(label.1.to_string())
.filter_map(|label| label.0.to_range().map(|range| (label, range)))
.map(|(label, range)| {
Label::primary((), range).with_message(label.1.to_string())
})
.collect(),
)
@ -251,6 +251,12 @@ pub enum Error<'a> {
ExpectedPositiveArrayLength(Span),
MissingWorkgroupSize(Span),
ConstantEvaluatorError(ConstantEvaluatorError, Span),
AutoConversion {
dest_span: Span,
dest_type: String,
source_span: Span,
source_type: String,
},
}
impl<'a> Error<'a> {
@ -712,6 +718,20 @@ impl<'a> Error<'a> {
)],
notes: vec![],
},
Error::AutoConversion { dest_span, ref dest_type, source_span, ref source_type } => ParseError {
message: format!("automatic conversions cannot convert `{source_type}` to `{dest_type}`"),
labels: vec![
(
dest_span,
format!("a value of type {dest_type} is required here").into(),
),
(
source_span,
format!("this expression has type {source_type}").into(),
)
],
notes: vec![],
}
}
}
}

View File

@ -5,6 +5,7 @@ use crate::{Handle, Span};
use crate::front::wgsl::error::Error;
use crate::front::wgsl::lower::{ExpressionContext, Lowerer};
use crate::front::wgsl::Scalar;
/// A cooked form of `ast::ConstructorType` that uses Naga types whenever
/// possible.
@ -80,7 +81,6 @@ enum Components<'a> {
Many {
components: Vec<Handle<crate::Expression>>,
spans: Vec<Span>,
first_component_ty_inner: &'a crate::TypeInner,
},
}
@ -116,13 +116,15 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
components: &[Handle<ast::Expression<'source>>],
ctx: &mut ExpressionContext<'source, '_, '_>,
) -> Result<Handle<crate::Expression>, Error<'source>> {
use crate::proc::TypeResolution as Tr;
let constructor_h = self.constructor(constructor, ctx)?;
let components = match *components {
[] => Components::None,
[component] => {
let span = ctx.ast_expressions.get_span(component);
let component = self.expression(component, ctx)?;
let component = self.expression_for_abstract(component, ctx)?;
let ty_inner = super::resolve_inner!(ctx, component);
Components::One {
@ -131,30 +133,21 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
ty_inner,
}
}
[component, ref rest @ ..] => {
let span = ctx.ast_expressions.get_span(component);
let component = self.expression(component, ctx)?;
let components = std::iter::once(Ok(component))
.chain(
rest.iter()
.map(|&component| self.expression(component, ctx)),
)
ref ast_components @ [_, _, ..] => {
let components = ast_components
.iter()
.map(|&expr| self.expression_for_abstract(expr, ctx))
.collect::<Result<_, _>>()?;
let spans = std::iter::once(span)
.chain(
rest.iter()
.map(|&component| ctx.ast_expressions.get_span(component)),
)
let spans = ast_components
.iter()
.map(|&expr| ctx.ast_expressions.get_span(expr))
.collect();
let first_component_ty_inner = super::resolve_inner!(ctx, component);
Components::Many {
components,
spans,
first_component_ty_inner,
for &component in &components {
ctx.grow_types(component)?;
}
Components::Many { components, spans }
}
};
@ -163,7 +156,8 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
// above can have mutable access to the type arena.
let constructor = constructor_h.borrow_inner(ctx.module);
let expr = match (components, constructor) {
let expr;
match (components, constructor) {
// Empty constructor
(Components::None, dst_ty) => match dst_ty {
Constructor::Type((result_ty, _)) => {
@ -186,11 +180,13 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
..
},
Constructor::Type((_, &crate::TypeInner::Scalar(scalar))),
) => crate::Expression::As {
expr: component,
kind: scalar.kind,
convert: Some(scalar.width),
},
) => {
expr = crate::Expression::As {
expr: component,
kind: scalar.kind,
convert: Some(scalar.width),
};
}
// Vector conversion (vector -> vector)
(
@ -206,11 +202,13 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
scalar: dst_scalar,
},
)),
) if dst_size == src_size => crate::Expression::As {
expr: component,
kind: dst_scalar.kind,
convert: Some(dst_scalar.width),
},
) if dst_size == src_size => {
expr = crate::Expression::As {
expr: component,
kind: dst_scalar.kind,
convert: Some(dst_scalar.width),
};
}
// Vector conversion (vector -> vector) - partial
(
@ -247,11 +245,13 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
scalar: dst_scalar,
},
)),
) if dst_columns == src_columns && dst_rows == src_rows => crate::Expression::As {
expr: component,
kind: crate::ScalarKind::Float,
convert: Some(dst_scalar.width),
},
) if dst_columns == src_columns && dst_rows == src_rows => {
expr = crate::Expression::As {
expr: component,
kind: dst_scalar.kind,
convert: Some(dst_scalar.width),
};
}
// Matrix conversion (matrix -> matrix) - partial
(
@ -284,69 +284,99 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
..
},
Constructor::PartialVector { size },
) => crate::Expression::Splat {
size,
value: component,
},
) => {
expr = crate::Expression::Splat {
size,
value: component,
};
}
// Vector constructor (splat)
(
Components::One {
component,
ty_inner: &crate::TypeInner::Scalar(src_scalar),
..
},
Constructor::Type((
_,
&crate::TypeInner::Vector {
size,
scalar: dst_scalar,
},
)),
) if dst_scalar == src_scalar => crate::Expression::Splat {
size,
value: component,
},
// Vector constructor (by elements)
(
Components::Many {
components,
first_component_ty_inner:
&crate::TypeInner::Scalar(scalar) | &crate::TypeInner::Vector { scalar, .. },
..
},
Constructor::PartialVector { size },
)
| (
Components::Many {
components,
first_component_ty_inner:
&crate::TypeInner::Scalar { .. } | &crate::TypeInner::Vector { .. },
mut component,
ty_inner: &crate::TypeInner::Scalar(_),
..
},
Constructor::Type((_, &crate::TypeInner::Vector { size, scalar })),
) => {
let inner = crate::TypeInner::Vector { size, scalar };
let ty = ctx.ensure_type_exists(inner);
crate::Expression::Compose { ty, components }
ctx.convert_slice_to_common_scalar(std::slice::from_mut(&mut component), scalar)?;
expr = crate::Expression::Splat {
size,
value: component,
};
}
// Matrix constructor (by elements)
// Vector constructor (by elements), partial
(
Components::Many {
components,
first_component_ty_inner: &crate::TypeInner::Scalar(scalar),
..
mut components,
spans,
},
Constructor::PartialVector { size },
) => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
let inner = consensus_scalar.to_inner_vector(size);
let ty = ctx.ensure_type_exists(inner);
expr = crate::Expression::Compose { ty, components };
}
// Vector constructor (by elements), full type given
(
Components::Many { mut components, .. },
Constructor::Type((ty, &crate::TypeInner::Vector { scalar, .. })),
) => {
ctx.try_automatic_conversions_for_vector(&mut components, scalar, ty_span)?;
expr = crate::Expression::Compose { ty, components };
}
// Matrix constructor (by elements), partial
(
Components::Many {
mut components,
spans,
},
Constructor::PartialMatrix { columns, rows },
)
| (
Components::Many {
components,
first_component_ty_inner: &crate::TypeInner::Scalar { .. },
..
},
) if components.len() == columns as usize * rows as usize => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
// We actually only accept floating-point elements.
let consensus_scalar = consensus_scalar
.automatic_conversion_combine(crate::Scalar::ABSTRACT_FLOAT)
.unwrap_or(consensus_scalar);
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
let vec_ty = ctx.ensure_type_exists(consensus_scalar.to_inner_vector(rows));
let components = components
.chunks(rows as usize)
.map(|vec_components| {
ctx.append_expression(
crate::Expression::Compose {
ty: vec_ty,
components: Vec::from(vec_components),
},
Default::default(),
)
})
.collect::<Result<Vec<_>, _>>()?;
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
columns,
rows,
scalar: consensus_scalar,
});
expr = crate::Expression::Compose { ty, components };
}
// Matrix constructor (by elements), type given
(
Components::Many { mut components, .. },
Constructor::Type((
_,
&crate::TypeInner::Matrix {
@ -355,9 +385,10 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
scalar,
},
)),
) => {
let vec_ty =
ctx.ensure_type_exists(crate::TypeInner::Vector { scalar, size: rows });
) if components.len() == columns as usize * rows as usize => {
let element = Tr::Value(crate::TypeInner::Scalar(scalar));
ctx.try_automatic_conversions_slice(&mut components, &element, ty_span)?;
let vec_ty = ctx.ensure_type_exists(scalar.to_inner_vector(rows));
let components = components
.chunks(rows as usize)
@ -377,44 +408,74 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
rows,
scalar,
});
crate::Expression::Compose { ty, components }
expr = crate::Expression::Compose { ty, components };
}
// Matrix constructor (by columns)
// Matrix constructor (by columns), partial
(
Components::Many {
components,
first_component_ty_inner: &crate::TypeInner::Vector { scalar, .. },
..
mut components,
spans,
},
Constructor::PartialMatrix { columns, rows },
)
| (
Components::Many {
components,
first_component_ty_inner: &crate::TypeInner::Vector { .. },
..
},
) => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
columns,
rows,
scalar: consensus_scalar,
});
expr = crate::Expression::Compose { ty, components };
}
// Matrix constructor (by columns), type given
(
Components::Many { mut components, .. },
Constructor::Type((
_,
ty,
&crate::TypeInner::Matrix {
columns,
columns: _,
rows,
scalar,
},
)),
) => {
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
columns,
rows,
scalar,
});
crate::Expression::Compose { ty, components }
let component_ty = crate::TypeInner::Vector { size: rows, scalar };
ctx.try_automatic_conversions_slice(
&mut components,
&Tr::Value(component_ty),
ty_span,
)?;
expr = crate::Expression::Compose { ty, components };
}
// Array constructor - infer type
(components, Constructor::PartialArray) => {
let components = components.into_components_vec();
let mut components = components.into_components_vec();
if let Ok(consensus_scalar) = automatic_conversion_consensus(&components, ctx) {
// Note that this will *not* necessarily convert all the
// components to the same type! The `automatic_conversion_consensus`
// function only considers the parameters' leaf scalar
// types; the parameters themselves could be any mix of
// vectors, matrices, and scalars.
//
// But *if* it is possible for this array construction
// expression to be well-typed at all, then all the
// parameters must have the same type constructors (vec,
// matrix, scalar) applied to their leaf scalars, so
// reconciling their scalars is always the right thing to
// do. And if this array construction is not well-typed,
// these conversions will not make it so, and we can let
// validation catch the error.
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
} else {
// There's no consensus scalar. Emit the `Compose`
// expression anyway, and let validation catch the problem.
}
let base = ctx.register_type(components[0])?;
@ -430,19 +491,34 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
};
let ty = ctx.ensure_type_exists(inner);
crate::Expression::Compose { ty, components }
expr = crate::Expression::Compose { ty, components };
}
// Array or Struct constructor
// Array constructor, explicit type
(components, Constructor::Type((ty, &crate::TypeInner::Array { base, .. }))) => {
let mut components = components.into_components_vec();
ctx.try_automatic_conversions_slice(&mut components, &Tr::Handle(base), ty_span)?;
expr = crate::Expression::Compose { ty, components };
}
// Struct constructor
(
components,
Constructor::Type((
ty,
&crate::TypeInner::Array { .. } | &crate::TypeInner::Struct { .. },
)),
Constructor::Type((ty, &crate::TypeInner::Struct { ref members, .. })),
) => {
let components = components.into_components_vec();
crate::Expression::Compose { ty, components }
let mut components = components.into_components_vec();
let struct_ty_span = ctx.module.types.get_span(ty);
// Make a vector of the members' type handles in advance, to
// avoid borrowing `members` from `ctx` while we generate
// new code.
let members: Vec<Handle<crate::Type>> = members.iter().map(|m| m.ty).collect();
for (component, &ty) in components.iter_mut().zip(&members) {
*component =
ctx.try_automatic_conversions(*component, &Tr::Handle(ty), struct_ty_span)?;
}
expr = crate::Expression::Compose { ty, components };
}
// ERRORS
@ -466,22 +542,9 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
return Err(Error::UnexpectedComponents(span));
}
// Parameters are of the wrong type for vector or matrix constructor
(
Components::Many { spans, .. },
Constructor::Type((
_,
&crate::TypeInner::Vector { .. } | &crate::TypeInner::Matrix { .. },
))
| Constructor::PartialVector { .. }
| Constructor::PartialMatrix { .. },
) => {
return Err(Error::InvalidConstructorComponentType(spans[0], 0));
}
// Other types can't be constructed
_ => return Err(Error::TypeNotConstructible(ty_span)),
};
}
let expr = ctx.append_expression(expr, span)?;
Ok(expr)
@ -546,3 +609,50 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
Ok(handle)
}
}
/// Find the consensus scalar of `components` under WGSL's automatic
/// conversions.
///
/// If `components` can all be converted to any common scalar via
/// WGSL's automatic conversions, return the best such scalar.
///
/// The `components` slice must not be empty. All elements' types must
/// have been resolved.
///
/// If `components` are definitely not acceptable as arguments to such
/// constructors, return `Err(i)`, where `i` is the index in
/// `components` of some problematic argument.
///
/// This function doesn't fully type-check the arguments - it only
/// considers their leaf scalar types. This means it may return `Ok`
/// even when the Naga validator will reject the resulting
/// construction expression later.
fn automatic_conversion_consensus(
components: &[Handle<crate::Expression>],
ctx: &ExpressionContext<'_, '_, '_>,
) -> Result<Scalar, usize> {
let types = &ctx.module.types;
let mut inners = components
.iter()
.map(|&c| ctx.typifier()[c].inner_with(types));
log::debug!(
"wgsl automatic_conversion_consensus: {:?}",
inners
.clone()
.map(|inner| inner.to_wgsl(&ctx.module.to_ctx()))
.collect::<Vec<String>>()
);
let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
for (inner, i) in inners.zip(1..) {
let scalar = inner.scalar().ok_or(i)?;
match best.automatic_conversion_combine(scalar) {
Some(new_best) => {
best = new_best;
}
None => return Err(i),
}
}
log::debug!(" consensus: {:?}", best.to_wgsl());
Ok(best)
}

View File

@ -0,0 +1,365 @@
//! WGSL's automatic conversions for abstract types.
use crate::{Handle, Span};
impl<'source, 'temp, 'out> super::ExpressionContext<'source, 'temp, 'out> {
/// Try to use WGSL's automatic conversions to convert `expr` to `goal_ty`.
///
/// If no conversions are necessary, return `expr` unchanged.
///
/// If automatic conversions cannot convert `expr` to `goal_ty`, return an
/// [`AutoConversion`] error.
///
/// Although the Load Rule is one of the automatic conversions, this
/// function assumes it has already been applied if appropriate, as
/// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.
///
/// [`AutoConversion`]: super::Error::AutoConversion
pub fn try_automatic_conversions(
&mut self,
expr: Handle<crate::Expression>,
goal_ty: &crate::proc::TypeResolution,
goal_span: Span,
) -> Result<Handle<crate::Expression>, super::Error<'source>> {
let expr_span = self.get_expression_span(expr);
// Keep the TypeResolution so we can get type names for
// structs in error messages.
let expr_resolution = super::resolve!(self, expr);
let types = &self.module.types;
let expr_inner = expr_resolution.inner_with(types);
let goal_inner = goal_ty.inner_with(types);
// If `expr` already has the requested type, we're done.
if expr_inner.equivalent(goal_inner, types) {
return Ok(expr);
}
let (_expr_scalar, goal_scalar) =
match expr_inner.automatically_converts_to(goal_inner, types) {
Some(scalars) => scalars,
None => {
let gctx = &self.module.to_ctx();
let source_type = expr_resolution.to_wgsl(gctx);
let dest_type = goal_ty.to_wgsl(gctx);
return Err(super::Error::AutoConversion {
dest_span: goal_span,
dest_type,
source_span: expr_span,
source_type,
});
}
};
let converted = if let crate::TypeInner::Array { .. } = *goal_inner {
let span = self.get_expression_span(expr);
self.as_const_evaluator()
.cast_array(expr, goal_scalar, span)
.map_err(|err| super::Error::ConstantEvaluatorError(err, span))?
} else {
let cast = crate::Expression::As {
expr,
kind: goal_scalar.kind,
convert: Some(goal_scalar.width),
};
self.append_expression(cast, expr_span)?
};
Ok(converted)
}
/// Try to convert `exprs` to `goal_ty` using WGSL's automatic conversions.
pub fn try_automatic_conversions_slice(
&mut self,
exprs: &mut [Handle<crate::Expression>],
goal_ty: &crate::proc::TypeResolution,
goal_span: Span,
) -> Result<(), super::Error<'source>> {
for expr in exprs.iter_mut() {
*expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
}
Ok(())
}
/// Apply WGSL's automatic conversions to a vector constructor's arguments.
///
/// When calling a vector constructor like `vec3<f32>(...)`, the parameters
/// can be a mix of scalars and vectors, with the latter being spread out to
/// contribute each of their components as a component of the new value.
/// When the element type is explicit, as with `<f32>` in the example above,
/// WGSL's automatic conversions should convert abstract scalar and vector
/// parameters to the constructor's required scalar type.
pub fn try_automatic_conversions_for_vector(
&mut self,
exprs: &mut [Handle<crate::Expression>],
goal_scalar: crate::Scalar,
goal_span: Span,
) -> Result<(), super::Error<'source>> {
use crate::proc::TypeResolution as Tr;
use crate::TypeInner as Ti;
let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
for (i, expr) in exprs.iter_mut().enumerate() {
// Keep the TypeResolution so we can get full type names
// in error messages.
let expr_resolution = super::resolve!(self, *expr);
let types = &self.module.types;
let expr_inner = expr_resolution.inner_with(types);
match *expr_inner {
Ti::Scalar(_) => {
*expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
}
Ti::Vector { size, scalar: _ } => {
let goal_vector_res = Tr::Value(Ti::Vector {
size,
scalar: goal_scalar,
});
*expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
}
_ => {
let span = self.get_expression_span(*expr);
return Err(super::Error::InvalidConstructorComponentType(
span, i as i32,
));
}
}
}
Ok(())
}
/// Convert all expressions in `exprs` to a common scalar type.
///
/// Note that the caller is responsible for making sure these
/// conversions are actually justified. This function simply
/// generates `As` expressions, regardless of whether they are
/// permitted WGSL automatic conversions. Callers intending to
/// implement automatic conversions need to determine for
/// themselves whether the casts we we generate are justified,
/// perhaps by calling `TypeInner::automatically_converts_to` or
/// `Scalar::automatic_conversion_combine`.
pub fn convert_slice_to_common_scalar(
&mut self,
exprs: &mut [Handle<crate::Expression>],
goal: crate::Scalar,
) -> Result<(), super::Error<'source>> {
for expr in exprs.iter_mut() {
let inner = super::resolve_inner!(self, *expr);
// Do nothing if `inner` doesn't even have leaf scalars;
// it's a type error that validation will catch.
if inner.scalar() != Some(goal) {
let cast = crate::Expression::As {
expr: *expr,
kind: goal.kind,
convert: Some(goal.width),
};
let expr_span = self.get_expression_span(*expr);
*expr = self.append_expression(cast, expr_span)?;
}
}
Ok(())
}
/// Return an expression for the concretized value of `expr`.
///
/// If `expr` is already concrete, return it unchanged.
pub fn concretize(
&mut self,
mut expr: Handle<crate::Expression>,
) -> Result<Handle<crate::Expression>, super::Error<'source>> {
let inner = super::resolve_inner!(self, expr);
if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
let concretized = scalar.concretize();
if concretized != scalar {
let span = self.get_expression_span(expr);
expr = self
.as_const_evaluator()
.cast_array(expr, concretized, span)
.map_err(|err| super::Error::ConstantEvaluatorError(err, span))?;
}
}
Ok(expr)
}
}
impl crate::TypeInner {
/// Determine whether `self` automatically converts to `goal`.
///
/// If WGSL's automatic conversions (excluding the Load Rule) will
/// convert `self` to `goal`, then return a pair `(from, to)`,
/// where `from` and `to` are the scalar types of the leaf values
/// of `self` and `goal`.
///
/// This function assumes that `self` and `goal` are different
/// types. Callers should first check whether any conversion is
/// needed at all.
///
/// If the automatic conversions cannot convert `self` to `goal`,
/// return `None`.
fn automatically_converts_to(
&self,
goal: &Self,
types: &crate::UniqueArena<crate::Type>,
) -> Option<(crate::Scalar, crate::Scalar)> {
use crate::ScalarKind as Sk;
use crate::TypeInner as Ti;
// Automatic conversions only change the scalar type of a value's leaves
// (e.g., `vec4<AbstractFloat>` to `vec4<f32>`), never the type
// constructors applied to those scalar types (e.g., never scalar to
// `vec4`, or `vec2` to `vec3`). So first we check that the type
// constructors match, extracting the leaf scalar types in the process.
let expr_scalar;
let goal_scalar;
match (self, goal) {
(&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
expr_scalar = expr;
goal_scalar = goal;
}
(
&Ti::Vector {
size: expr_size,
scalar: expr,
},
&Ti::Vector {
size: goal_size,
scalar: goal,
},
) if expr_size == goal_size => {
expr_scalar = expr;
goal_scalar = goal;
}
(
&Ti::Matrix {
rows: expr_rows,
columns: expr_columns,
scalar: expr,
},
&Ti::Matrix {
rows: goal_rows,
columns: goal_columns,
scalar: goal,
},
) if expr_rows == goal_rows && expr_columns == goal_columns => {
expr_scalar = expr;
goal_scalar = goal;
}
(
&Ti::Array {
base: expr_base,
size: expr_size,
stride: _,
},
&Ti::Array {
base: goal_base,
size: goal_size,
stride: _,
},
) if expr_size == goal_size => {
return types[expr_base]
.inner
.automatically_converts_to(&types[goal_base].inner, types);
}
_ => return None,
}
match (expr_scalar.kind, goal_scalar.kind) {
(Sk::AbstractFloat, Sk::Float) => {}
(Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
_ => return None,
}
log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
Some((expr_scalar, goal_scalar))
}
fn automatically_convertible_scalar(
&self,
types: &crate::UniqueArena<crate::Type>,
) -> Option<crate::Scalar> {
use crate::TypeInner as Ti;
match *self {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
Some(scalar)
}
Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
Ti::Atomic(_)
| Ti::Pointer { .. }
| Ti::ValuePointer { .. }
| Ti::Struct { .. }
| Ti::Image { .. }
| Ti::Sampler { .. }
| Ti::AccelerationStructure
| Ti::RayQuery
| Ti::BindingArray { .. } => None,
}
}
}
impl crate::Scalar {
/// Find the common type of `self` and `other` under WGSL's
/// automatic conversions.
///
/// If there are any scalars to which WGSL's automatic conversions
/// will convert both `self` and `other`, return the best such
/// scalar. Otherwise, return `None`.
pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
use crate::ScalarKind as Sk;
match (self.kind, other.kind) {
// When the kinds match...
(Sk::AbstractFloat, Sk::AbstractFloat)
| (Sk::AbstractInt, Sk::AbstractInt)
| (Sk::Sint, Sk::Sint)
| (Sk::Uint, Sk::Uint)
| (Sk::Float, Sk::Float)
| (Sk::Bool, Sk::Bool) => {
if self.width == other.width {
// ... either no conversion is necessary ...
Some(self)
} else {
// ... or no conversion is possible.
// We never convert concrete to concrete, and
// abstract types should have only one size.
None
}
}
// AbstractInt converts to AbstractFloat.
(Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
(Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
// AbstractFloat converts to Float.
(Sk::AbstractFloat, Sk::Float) => Some(other),
(Sk::Float, Sk::AbstractFloat) => Some(self),
// AbstractInt converts to concrete integer or float.
(Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
(Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
// AbstractFloat can't be reconciled with concrete integer types.
(Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
None
}
// Nothing can be reconciled with `bool`.
(Sk::Bool, _) | (_, Sk::Bool) => None,
// Different concrete types cannot be reconciled.
(Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
}
}
const fn concretize(self) -> Self {
use crate::ScalarKind as Sk;
match self.kind {
Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
Sk::AbstractInt => Self::I32,
Sk::AbstractFloat => Self::F32,
}
}
}

View File

@ -11,6 +11,7 @@ use crate::proc::{
use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span};
mod construction;
mod conversion;
/// Resolves the inner type of a given expression.
///
@ -66,6 +67,7 @@ macro_rules! resolve {
&$ctx.typifier()[$expr]
}};
}
pub(super) use resolve;
/// State for constructing a `crate::Module`.
pub struct GlobalContext<'source, 'temp, 'out> {
@ -98,10 +100,14 @@ impl<'source> GlobalContext<'source, '_, '_> {
}
}
fn ensure_type_exists(&mut self, inner: crate::TypeInner) -> Handle<crate::Type> {
fn ensure_type_exists(
&mut self,
name: Option<String>,
inner: crate::TypeInner,
) -> Handle<crate::Type> {
self.module
.types
.insert(crate::Type { inner, name: None }, Span::UNDEFINED)
.insert(crate::Type { inner, name }, Span::UNDEFINED)
}
}
@ -635,7 +641,7 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
}
fn ensure_type_exists(&mut self, inner: crate::TypeInner) -> Handle<crate::Type> {
self.as_global().ensure_type_exists(inner)
self.as_global().ensure_type_exists(None, inner)
}
}
@ -899,29 +905,39 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
}
ast::GlobalDeclKind::Const(ref c) => {
let mut ectx = ctx.as_const();
let init = self.expression(c.init, &mut ectx)?;
let inferred_type = ectx.register_type(init)?;
let mut init = self.expression_for_abstract(c.init, &mut ectx)?;
let explicit_ty =
c.ty.map(|ty| self.resolve_ast_type(ty, &mut ctx))
.transpose()?;
if let Some(explicit) = explicit_ty {
if explicit != inferred_type {
let gctx = ctx.module.to_ctx();
return Err(Error::InitializationTypeMismatch {
name: c.name.span,
expected: explicit.to_wgsl(&gctx),
got: inferred_type.to_wgsl(&gctx),
});
}
let ty;
if let Some(explicit_ty) = c.ty {
let explicit_ty =
self.resolve_ast_type(explicit_ty, &mut ectx.as_global())?;
let explicit_ty_res = crate::proc::TypeResolution::Handle(explicit_ty);
init = ectx
.try_automatic_conversions(init, &explicit_ty_res, c.name.span)
.map_err(|error| match error {
Error::AutoConversion {
dest_span: _,
dest_type,
source_span: _,
source_type,
} => Error::InitializationTypeMismatch {
name: c.name.span,
expected: dest_type,
got: source_type,
},
other => other,
})?;
ty = explicit_ty;
} else {
init = ectx.concretize(init)?;
ty = ectx.register_type(init)?;
}
let handle = ctx.module.constants.append(
crate::Constant {
name: Some(c.name.name.to_string()),
r#override: crate::Override::None,
ty: inferred_type,
ty,
init,
},
span,
@ -936,13 +952,22 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
.insert(s.name.name, LoweredGlobalDecl::Type(handle));
}
ast::GlobalDeclKind::Type(ref alias) => {
let ty = self.resolve_ast_type(alias.ty, &mut ctx)?;
let ty = self.resolve_named_ast_type(
alias.ty,
Some(alias.name.name.to_string()),
&mut ctx,
)?;
ctx.globals
.insert(alias.name.name, LoweredGlobalDecl::Type(ty));
}
}
}
// Constant evaluation may leave abstract-typed literals and
// compositions in expression arenas, so we need to compact the module
// to remove unused expressions and types.
crate::compact::compact(&mut module);
Ok(module)
}
@ -1441,10 +1466,25 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
}
/// Lower `expr` and apply the Load Rule if possible.
///
/// For the time being, this concretizes abstract values, to support
/// consumers that haven't been adapted to consume them yet. Consumers
/// prepared for abstract values can call [`expression_for_abstract`].
///
/// [`expression_for_abstract`]: Lowerer::expression_for_abstract
fn expression(
&mut self,
expr: Handle<ast::Expression<'source>>,
ctx: &mut ExpressionContext<'source, '_, '_>,
) -> Result<Handle<crate::Expression>, Error<'source>> {
let expr = self.expression_for_abstract(expr, ctx)?;
ctx.concretize(expr)
}
fn expression_for_abstract(
&mut self,
expr: Handle<ast::Expression<'source>>,
ctx: &mut ExpressionContext<'source, '_, '_>,
) -> Result<Handle<crate::Expression>, Error<'source>> {
let expr = self.expression_for_reference(expr, ctx)?;
ctx.apply_load_rule(expr)
@ -1464,8 +1504,10 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
ast::Literal::Number(Number::F32(f)) => crate::Literal::F32(f),
ast::Literal::Number(Number::I32(i)) => crate::Literal::I32(i),
ast::Literal::Number(Number::U32(u)) => crate::Literal::U32(u),
ast::Literal::Number(_) => {
unreachable!("got abstract numeric type when not expected");
ast::Literal::Number(Number::F64(f)) => crate::Literal::F64(f),
ast::Literal::Number(Number::AbstractInt(i)) => crate::Literal::AbstractInt(i),
ast::Literal::Number(Number::AbstractFloat(f)) => {
crate::Literal::AbstractFloat(f)
}
ast::Literal::Bool(b) => crate::Literal::Bool(b),
};
@ -2513,10 +2555,19 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
})
}
/// Return a Naga `Handle<Type>` representing the front-end type `handle`.
fn resolve_ast_type(
/// Build the Naga equivalent of a named AST type.
///
/// Return a Naga `Handle<Type>` representing the front-end type
/// `handle`, which should be named `name`, if given.
///
/// If `handle` refers to a type cached in [`SpecialTypes`],
/// `name` may be ignored.
///
/// [`SpecialTypes`]: crate::SpecialTypes
fn resolve_named_ast_type(
&mut self,
handle: Handle<ast::Type<'source>>,
name: Option<String>,
ctx: &mut GlobalContext<'source, '_, '_>,
) -> Result<Handle<crate::Type>, Error<'source>> {
let inner = match ctx.types[handle] {
@ -2577,7 +2628,16 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
}
};
Ok(ctx.ensure_type_exists(inner))
Ok(ctx.ensure_type_exists(name, inner))
}
/// Return a Naga `Handle<Type>` representing the front-end type `handle`.
fn resolve_ast_type(
&mut self,
handle: Handle<ast::Type<'source>>,
ctx: &mut GlobalContext<'source, '_, '_>,
) -> Result<Handle<crate::Type>, Error<'source>> {
self.resolve_named_ast_type(handle, None, ctx)
}
fn binding(

View File

@ -448,6 +448,7 @@ impl<'a> Lexer<'a> {
}
#[cfg(test)]
#[track_caller]
fn sub_test(source: &str, expected_tokens: &[Token]) {
let mut lex = Lexer::new(source);
for &token in expected_tokens {
@ -464,13 +465,13 @@ fn test_numbers() {
sub_test(
"0x123 0X123u 1u 123 0 0i 0x3f",
&[
Token::Number(Ok(Number::I32(291))),
Token::Number(Ok(Number::AbstractInt(291))),
Token::Number(Ok(Number::U32(291))),
Token::Number(Ok(Number::U32(1))),
Token::Number(Ok(Number::I32(123))),
Token::Number(Ok(Number::AbstractInt(123))),
Token::Number(Ok(Number::AbstractInt(0))),
Token::Number(Ok(Number::I32(0))),
Token::Number(Ok(Number::I32(0))),
Token::Number(Ok(Number::I32(63))),
Token::Number(Ok(Number::AbstractInt(63))),
],
);
// decimal floating point
@ -478,17 +479,17 @@ fn test_numbers() {
"0.e+4f 01. .01 12.34 .0f 0h 1e-3 0xa.fp+2 0x1P+4f 0X.3 0x3p+2h 0X1.fp-4 0x3.2p+2h",
&[
Token::Number(Ok(Number::F32(0.))),
Token::Number(Ok(Number::F32(1.))),
Token::Number(Ok(Number::F32(0.01))),
Token::Number(Ok(Number::F32(12.34))),
Token::Number(Ok(Number::AbstractFloat(1.))),
Token::Number(Ok(Number::AbstractFloat(0.01))),
Token::Number(Ok(Number::AbstractFloat(12.34))),
Token::Number(Ok(Number::F32(0.))),
Token::Number(Err(NumberError::UnimplementedF16)),
Token::Number(Ok(Number::F32(0.001))),
Token::Number(Ok(Number::F32(43.75))),
Token::Number(Ok(Number::AbstractFloat(0.001))),
Token::Number(Ok(Number::AbstractFloat(43.75))),
Token::Number(Ok(Number::F32(16.))),
Token::Number(Ok(Number::F32(0.1875))),
Token::Number(Ok(Number::AbstractFloat(0.1875))),
Token::Number(Err(NumberError::UnimplementedF16)),
Token::Number(Ok(Number::F32(0.12109375))),
Token::Number(Ok(Number::AbstractFloat(0.12109375))),
Token::Number(Err(NumberError::UnimplementedF16)),
],
);
@ -624,18 +625,37 @@ fn test_numbers() {
);
}
#[test]
fn double_floats() {
sub_test(
"0x1.2p4lf 0x1p8lf 0.0625lf 625e-4lf 10lf 10l",
&[
Token::Number(Ok(Number::F64(18.0))),
Token::Number(Ok(Number::F64(256.0))),
Token::Number(Ok(Number::F64(0.0625))),
Token::Number(Ok(Number::F64(0.0625))),
Token::Number(Ok(Number::F64(10.0))),
Token::Number(Ok(Number::AbstractInt(10))),
Token::Word("l"),
],
)
}
#[test]
fn test_tokens() {
sub_test("id123_OK", &[Token::Word("id123_OK")]);
sub_test(
"92No",
&[Token::Number(Ok(Number::I32(92))), Token::Word("No")],
&[
Token::Number(Ok(Number::AbstractInt(92))),
Token::Word("No"),
],
);
sub_test(
"2u3o",
&[
Token::Number(Ok(Number::U32(2))),
Token::Number(Ok(Number::I32(3))),
Token::Number(Ok(Number::AbstractInt(3))),
Token::Word("o"),
],
);
@ -643,7 +663,7 @@ fn test_tokens() {
"2.4f44po",
&[
Token::Number(Ok(Number::F32(2.4))),
Token::Number(Ok(Number::I32(44))),
Token::Number(Ok(Number::AbstractInt(44))),
Token::Word("po"),
],
);
@ -674,6 +694,24 @@ fn test_tokens() {
Token::Operation('/'),
],
);
// Type suffixes are only allowed on hex float literals
// if you provided an exponent.
sub_test(
"0x1.2f 0x1.2f 0x1.2h 0x1.2H 0x1.2lf",
&[
// The 'f' suffixes are taken as a hex digit:
// the fractional part is 0x2f / 256.
Token::Number(Ok(Number::AbstractFloat(1.0 + 0x2f as f64 / 256.0))),
Token::Number(Ok(Number::AbstractFloat(1.0 + 0x2f as f64 / 256.0))),
Token::Number(Ok(Number::AbstractFloat(1.125))),
Token::Word("h"),
Token::Number(Ok(Number::AbstractFloat(1.125))),
Token::Word("H"),
Token::Number(Ok(Number::AbstractFloat(1.125))),
Token::Word("lf"),
],
)
}
#[test]
@ -684,7 +722,7 @@ fn test_variable_decl() {
Token::Attribute,
Token::Word("group"),
Token::Paren('('),
Token::Number(Ok(Number::I32(0))),
Token::Number(Ok(Number::AbstractInt(0))),
Token::Paren(')'),
Token::Word("var"),
Token::Paren('<'),

View File

@ -16,39 +16,15 @@ pub enum Number {
U32(u32),
/// Concrete f32
F32(f32),
}
impl Number {
/// Convert abstract numbers to a plausible concrete counterpart.
///
/// Return concrete numbers unchanged. If the conversion would be
/// lossy, return an error.
fn abstract_to_concrete(self) -> Result<Number, NumberError> {
match self {
Number::AbstractInt(num) => i32::try_from(num)
.map(Number::I32)
.map_err(|_| NumberError::NotRepresentable),
Number::AbstractFloat(num) => {
let num = num as f32;
if num.is_finite() {
Ok(Number::F32(num))
} else {
Err(NumberError::NotRepresentable)
}
}
num => Ok(num),
}
}
/// Concrete f64
F64(f64),
}
// TODO: when implementing Creation-Time Expressions, remove the ability to match the minus sign
pub(in crate::front::wgsl) fn consume_number(input: &str) -> (Token<'_>, &str) {
let (result, rest) = parse(input);
(
Token::Number(result.and_then(Number::abstract_to_concrete)),
rest,
)
(Token::Number(result), rest)
}
enum Kind {
@ -61,9 +37,11 @@ enum IntKind {
U32,
}
#[derive(Debug)]
enum FloatKind {
F32,
F16,
F32,
F64,
}
// The following regexes (from the WGSL spec) will be matched:
@ -104,9 +82,9 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
/// if one of the given patterns are found at the start of the buffer
/// returning the corresponding expr for the matched pattern
macro_rules! consume_map {
($bytes:ident, [$($pattern:pat_param => $to:expr),*]) => {
($bytes:ident, [$( $($pattern:pat_param),* => $to:expr),* $(,)?]) => {
match $bytes {
$( &[$pattern, ref rest @ ..] => { $bytes = rest; Some($to) }, )*
$( &[ $($pattern),*, ref rest @ ..] => { $bytes = rest; Some($to) }, )*
_ => None,
}
};
@ -136,6 +114,16 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
}};
}
macro_rules! consume_float_suffix {
($bytes:ident) => {
consume_map!($bytes, [
b'h' => FloatKind::F16,
b'f' => FloatKind::F32,
b'l', b'f' => FloatKind::F64,
])
};
}
/// maps the given `&[u8]` (tail of the initial `input: &str`) to a `&str`
macro_rules! rest_to_str {
($bytes:ident) => {
@ -190,7 +178,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let number = general_extract.end(bytes);
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
let kind = consume_float_suffix!(bytes);
(parse_hex_float(number, kind), rest_to_str!(bytes))
} else {
@ -219,7 +207,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let exponent = exp_extract.end(bytes);
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
let kind = consume_float_suffix!(bytes);
(
parse_hex_float_missing_period(significand, exponent, kind),
@ -257,7 +245,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let number = general_extract.end(bytes);
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
let kind = consume_float_suffix!(bytes);
(parse_dec_float(number, kind), rest_to_str!(bytes))
} else {
@ -275,7 +263,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let number = general_extract.end(bytes);
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
let kind = consume_float_suffix!(bytes);
(parse_dec_float(number, kind), rest_to_str!(bytes))
} else {
@ -289,8 +277,9 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let kind = consume_map!(bytes, [
b'i' => Kind::Int(IntKind::I32),
b'u' => Kind::Int(IntKind::U32),
b'h' => Kind::Float(FloatKind::F16),
b'f' => Kind::Float(FloatKind::F32),
b'h' => Kind::Float(FloatKind::F16)
b'l', b'f' => Kind::Float(FloatKind::F64),
]);
(
@ -382,12 +371,17 @@ fn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, Numbe
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
_ => Err(NumberError::NotRepresentable),
},
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
Some(FloatKind::F32) => match hexf_parse::parse_hexf32(input, false) {
Ok(num) => Ok(Number::F32(num)),
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
_ => Err(NumberError::NotRepresentable),
},
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
Some(FloatKind::F64) => match hexf_parse::parse_hexf64(input, false) {
Ok(num) => Ok(Number::F64(num)),
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
_ => Err(NumberError::NotRepresentable),
},
}
}
@ -407,6 +401,12 @@ fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, Numbe
.then_some(Number::F32(num))
.ok_or(NumberError::NotRepresentable)
}
Some(FloatKind::F64) => {
let num = input.parse::<f64>().unwrap(); // will never fail
num.is_finite()
.then_some(Number::F64(num))
.ok_or(NumberError::NotRepresentable)
}
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
}
}

View File

@ -76,7 +76,7 @@ fn parse_type_cast() {
assert!(parse_str(
"
fn main() {
let x: vec2<f32> = vec2<f32>(0);
let x: vec2<f32> = vec2<f32>(0i, 0i);
}
",
)
@ -313,7 +313,7 @@ fn parse_texture_load() {
"
var t: texture_3d<u32>;
fn foo() {
let r: vec4<u32> = textureLoad(t, vec3<u32>(0.0, 1.0, 2.0), 1);
let r: vec4<u32> = textureLoad(t, vec3<u32>(0u, 1u, 2u), 1);
}
",
)

View File

@ -140,6 +140,8 @@ impl crate::Scalar {
crate::ScalarKind::Uint => "u",
crate::ScalarKind::Float => "f",
crate::ScalarKind::Bool => return "bool".to_string(),
crate::ScalarKind::AbstractInt => return "{AbstractInt}".to_string(),
crate::ScalarKind::AbstractFloat => return "{AbstractFloat}".to_string(),
};
format!("{}{}", prefix, self.width * 8)
}

View File

@ -300,6 +300,9 @@ use serde::Serialize;
/// Width of a boolean type, in bytes.
pub const BOOL_WIDTH: Bytes = 1;
/// Width of abstract types, in bytes.
pub const ABSTRACT_WIDTH: Bytes = 8;
/// Hash map that is faster but not resilient to DoS attacks.
pub type FastHashMap<K, T> = rustc_hash::FxHashMap<K, T>;
/// Hash set that is faster but not resilient to DoS attacks.
@ -470,6 +473,16 @@ pub enum ScalarKind {
Float,
/// Boolean type.
Bool,
/// WGSL abstract integer type.
///
/// These are forbidden by validation, and should never reach backends.
AbstractInt,
/// Abstract floating-point type.
///
/// These are forbidden by validation, and should never reach backends.
AbstractFloat,
}
/// Characteristics of a scalar type.
@ -871,6 +884,8 @@ pub enum Literal {
I32(i32),
I64(i64),
Bool(bool),
AbstractInt(i64),
AbstractFloat(f64),
}
#[derive(Debug, PartialEq)]

View File

@ -141,8 +141,8 @@ pub enum ConstantEvaluatorError {
InvalidAccessIndexTy,
#[error("Constants don't support array length expressions")]
ArrayLength,
#[error("Cannot cast type")]
InvalidCastArg,
#[error("Cannot cast type `{from}` to `{to}`")]
InvalidCastArg { from: String, to: String },
#[error("Cannot apply the unary op to the argument")]
InvalidUnaryOpArg,
#[error("Cannot apply the binary op to the arguments")]
@ -167,6 +167,15 @@ pub enum ConstantEvaluatorError {
NotImplemented(String),
#[error("{0} operation overflowed")]
Overflow(String),
#[error(
"the concrete type `{to_type}` cannot represent the abstract value `{value}` accurately"
)]
AutomaticConversionLossy {
value: String,
to_type: &'static str,
},
#[error("abstract floating-point values cannot be automatically converted to integers")]
AutomaticConversionFloatToInt { to_type: &'static str },
#[error("Division by zero")]
DivisionByZero,
#[error("Remainder by zero")]
@ -268,6 +277,17 @@ impl<'a> ConstantEvaluator<'a> {
}
}
pub fn to_ctx(&self) -> crate::proc::GlobalCtx {
crate::proc::GlobalCtx {
types: self.types,
constants: self.constants,
const_expressions: match self.function_local_data {
Some(ref data) => data.const_expressions,
None => self.expressions,
},
}
}
fn check(&self, expr: Handle<Expression>) -> Result<(), ConstantEvaluatorError> {
if let Some(ref function_local_data) = self.function_local_data {
if !function_local_data.expression_constness.is_const(expr) {
@ -968,6 +988,22 @@ impl<'a> ConstantEvaluator<'a> {
let expr = self.eval_zero_value(expr, span)?;
let make_error = || -> Result<_, ConstantEvaluatorError> {
let ty = self.resolve_type(expr)?;
#[cfg(feature = "wgsl-in")]
let from = ty.to_wgsl(&self.to_ctx());
#[cfg(feature = "wgsl-in")]
let to = target.to_wgsl();
#[cfg(not(feature = "wgsl-in"))]
let from = format!("{ty:?}");
#[cfg(not(feature = "wgsl-in"))]
let to = format!("{target:?}");
Err(ConstantEvaluatorError::InvalidCastArg { from, to })
};
let expr = match self.expressions[expr] {
Expression::Literal(literal) => {
let literal = match target {
@ -977,8 +1013,10 @@ impl<'a> ConstantEvaluator<'a> {
Literal::F32(v) => v as i32,
Literal::Bool(v) => v as i32,
Literal::F64(_) | Literal::I64(_) => {
return Err(ConstantEvaluatorError::InvalidCastArg)
return make_error();
}
Literal::AbstractInt(v) => i32::try_from_abstract(v)?,
Literal::AbstractFloat(v) => i32::try_from_abstract(v)?,
}),
Sc::U32 => Literal::U32(match literal {
Literal::I32(v) => v as u32,
@ -986,8 +1024,10 @@ impl<'a> ConstantEvaluator<'a> {
Literal::F32(v) => v as u32,
Literal::Bool(v) => v as u32,
Literal::F64(_) | Literal::I64(_) => {
return Err(ConstantEvaluatorError::InvalidCastArg)
return make_error();
}
Literal::AbstractInt(v) => u32::try_from_abstract(v)?,
Literal::AbstractFloat(v) => u32::try_from_abstract(v)?,
}),
Sc::F32 => Literal::F32(match literal {
Literal::I32(v) => v as f32,
@ -995,19 +1035,48 @@ impl<'a> ConstantEvaluator<'a> {
Literal::F32(v) => v,
Literal::Bool(v) => v as u32 as f32,
Literal::F64(_) | Literal::I64(_) => {
return Err(ConstantEvaluatorError::InvalidCastArg)
return make_error();
}
Literal::AbstractInt(v) => f32::try_from_abstract(v)?,
Literal::AbstractFloat(v) => f32::try_from_abstract(v)?,
}),
Sc::F64 => Literal::F64(match literal {
Literal::I32(v) => v as f64,
Literal::U32(v) => v as f64,
Literal::F32(v) => v as f64,
Literal::F64(v) => v,
Literal::Bool(v) => v as u32 as f64,
Literal::I64(_) => return make_error(),
Literal::AbstractInt(v) => f64::try_from_abstract(v)?,
Literal::AbstractFloat(v) => f64::try_from_abstract(v)?,
}),
Sc::BOOL => Literal::Bool(match literal {
Literal::I32(v) => v != 0,
Literal::U32(v) => v != 0,
Literal::F32(v) => v != 0.0,
Literal::Bool(v) => v,
Literal::F64(_) | Literal::I64(_) => {
return Err(ConstantEvaluatorError::InvalidCastArg)
Literal::F64(_)
| Literal::I64(_)
| Literal::AbstractInt(_)
| Literal::AbstractFloat(_) => {
return make_error();
}
}),
_ => return Err(ConstantEvaluatorError::InvalidCastArg),
Sc::ABSTRACT_FLOAT => Literal::AbstractFloat(match literal {
Literal::AbstractInt(v) => {
// Overflow is forbidden, but inexact conversions
// are fine. The range of f64 is far larger than
// that of i64, so we don't have to check anything
// here.
v as f64
}
Literal::AbstractFloat(v) => v,
_ => return make_error(),
}),
_ => {
log::debug!("Constant evaluator refused to convert value to {target:?}");
return make_error();
}
};
Expression::Literal(literal)
}
@ -1025,7 +1094,7 @@ impl<'a> ConstantEvaluator<'a> {
rows,
scalar: target,
},
_ => return Err(ConstantEvaluatorError::InvalidCastArg),
_ => return make_error(),
};
let mut components = src_components.clone();
@ -1051,12 +1120,70 @@ impl<'a> ConstantEvaluator<'a> {
value: cast_value,
}
}
_ => return Err(ConstantEvaluatorError::InvalidCastArg),
_ => return make_error(),
};
self.register_evaluated_expr(expr, span)
}
/// Convert the scalar leaves of `expr` to `target`, handling arrays.
///
/// `expr` must be a `Compose` expression whose type is a scalar, vector,
/// matrix, or nested arrays of such.
///
/// This is basically the same as the [`cast`] method, except that that
/// should only handle Naga [`As`] expressions, which cannot convert arrays.
///
/// Treat `span` as the location of the resulting expression.
///
/// [`cast`]: ConstantEvaluator::cast
/// [`As`]: crate::Expression::As
pub fn cast_array(
&mut self,
expr: Handle<Expression>,
target: crate::Scalar,
span: Span,
) -> Result<Handle<Expression>, ConstantEvaluatorError> {
let Expression::Compose { ty, ref components } = self.expressions[expr] else {
return self.cast(expr, target, span);
};
let crate::TypeInner::Array { base: _, size, stride: _ } = self.types[ty].inner else {
return self.cast(expr, target, span);
};
let mut components = components.clone();
for component in &mut components {
*component = self.cast_array(*component, target, span)?;
}
let first = components.first().unwrap();
let new_base = match self.resolve_type(*first)? {
crate::proc::TypeResolution::Handle(ty) => ty,
crate::proc::TypeResolution::Value(inner) => {
self.types.insert(Type { name: None, inner }, span)
}
};
let new_base_stride = self.types[new_base].inner.size(self.to_ctx());
let new_array_ty = self.types.insert(
Type {
name: None,
inner: TypeInner::Array {
base: new_base,
size,
stride: new_base_stride,
},
},
span,
);
let compose = Expression::Compose {
ty: new_array_ty,
components,
};
self.register_evaluated_expr(compose, span)
}
fn unary_op(
&mut self,
op: UnaryOperator,
@ -1311,6 +1438,28 @@ impl<'a> ConstantEvaluator<'a> {
Ok(self.expressions.append(expr, span))
}
}
fn resolve_type(
&self,
expr: Handle<Expression>,
) -> Result<crate::proc::TypeResolution, ConstantEvaluatorError> {
use crate::proc::TypeResolution as Tr;
use crate::Expression as Ex;
let resolution = match self.expressions[expr] {
Ex::Literal(ref literal) => Tr::Value(literal.ty_inner()),
Ex::Constant(c) => Tr::Handle(self.constants[c].ty),
Ex::ZeroValue(ty) | Ex::Compose { ty, .. } => Tr::Handle(ty),
Ex::Splat { size, value } => {
let Tr::Value(TypeInner::Scalar(scalar)) = self.resolve_type(value)? else {
return Err(ConstantEvaluatorError::SplatScalarOnly);
};
Tr::Value(TypeInner::Vector { scalar, size })
}
_ => return Err(ConstantEvaluatorError::SubexpressionsAreNotConstant),
};
Ok(resolution)
}
}
#[cfg(test)]
@ -1820,3 +1969,92 @@ mod tests {
}
}
}
/// Trait for conversions of abstract values to concrete types.
trait TryFromAbstract<T>: Sized {
/// Convert an abstract literal `value` to `Self`.
///
/// Since Naga's `AbstractInt` and `AbstractFloat` exist to support
/// WGSL, we follow WGSL's conversion rules here:
///
/// - WGSL §6.1.2. Conversion Rank says that automatic conversions
/// to integers are either lossless or an error.
///
/// - WGSL §14.6.4 Floating Point Conversion says that conversions
/// to floating point in constant expressions and override
/// expressions are errors if the value is out of range for the
/// destination type, but rounding is okay.
///
/// [`AbstractInt`]: crate::Literal::AbstractInt
/// [`Float`]: crate::Literal::Float
fn try_from_abstract(value: T) -> Result<Self, ConstantEvaluatorError>;
}
impl TryFromAbstract<i64> for i32 {
fn try_from_abstract(value: i64) -> Result<i32, ConstantEvaluatorError> {
i32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
value: format!("{value:?}"),
to_type: "i32",
})
}
}
impl TryFromAbstract<i64> for u32 {
fn try_from_abstract(value: i64) -> Result<u32, ConstantEvaluatorError> {
u32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
value: format!("{value:?}"),
to_type: "u32",
})
}
}
impl TryFromAbstract<i64> for f32 {
fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
let f = value as f32;
// The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
// `f32` is roughly ±3.4 × 10³⁸, so there's no opportunity for
// overflow here.
Ok(f)
}
}
impl TryFromAbstract<f64> for f32 {
fn try_from_abstract(value: f64) -> Result<f32, ConstantEvaluatorError> {
let f = value as f32;
if f.is_infinite() {
return Err(ConstantEvaluatorError::AutomaticConversionLossy {
value: format!("{value:?}"),
to_type: "f32",
});
}
Ok(f)
}
}
impl TryFromAbstract<i64> for f64 {
fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
let f = value as f64;
// The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
// `f64` is roughly ±1.8 × 10³⁰⁸, so there's no opportunity for
// overflow here.
Ok(f)
}
}
impl TryFromAbstract<f64> for f64 {
fn try_from_abstract(value: f64) -> Result<f64, ConstantEvaluatorError> {
Ok(value)
}
}
impl TryFromAbstract<f64> for i32 {
fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i32" })
}
}
impl TryFromAbstract<f64> for u32 {
fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u32" })
}
}

View File

@ -71,7 +71,11 @@ impl From<super::StorageFormat> for super::ScalarKind {
impl super::ScalarKind {
pub const fn is_numeric(self) -> bool {
match self {
crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float => true,
crate::ScalarKind::Sint
| crate::ScalarKind::Uint
| crate::ScalarKind::Float
| crate::ScalarKind::AbstractInt
| crate::ScalarKind::AbstractFloat => true,
crate::ScalarKind::Bool => false,
}
}
@ -102,6 +106,14 @@ impl super::Scalar {
kind: crate::ScalarKind::Bool,
width: crate::BOOL_WIDTH,
};
pub const ABSTRACT_INT: Self = Self {
kind: crate::ScalarKind::AbstractInt,
width: crate::ABSTRACT_WIDTH,
};
pub const ABSTRACT_FLOAT: Self = Self {
kind: crate::ScalarKind::AbstractFloat,
width: crate::ABSTRACT_WIDTH,
};
/// Construct a float `Scalar` with the given width.
///
@ -144,7 +156,7 @@ impl Eq for crate::Literal {}
impl std::hash::Hash for crate::Literal {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
match *self {
Self::F64(v) => {
Self::F64(v) | Self::AbstractFloat(v) => {
hasher.write_u8(0);
v.to_bits().hash(hasher);
}
@ -164,7 +176,7 @@ impl std::hash::Hash for crate::Literal {
hasher.write_u8(4);
v.hash(hasher);
}
Self::I64(v) => {
Self::I64(v) | Self::AbstractInt(v) => {
hasher.write_u8(5);
v.hash(hasher);
}
@ -198,7 +210,8 @@ impl crate::Literal {
match *self {
Self::F64(_) | Self::I64(_) => 8,
Self::F32(_) | Self::U32(_) | Self::I32(_) => 4,
Self::Bool(_) => 1,
Self::Bool(_) => crate::BOOL_WIDTH,
Self::AbstractInt(_) | Self::AbstractFloat(_) => crate::ABSTRACT_WIDTH,
}
}
pub const fn scalar(&self) -> crate::Scalar {
@ -209,6 +222,8 @@ impl crate::Literal {
Self::I32(_) => crate::Scalar::I32,
Self::I64(_) => crate::Scalar::I64,
Self::Bool(_) => crate::Scalar::BOOL,
Self::AbstractInt(_) => crate::Scalar::ABSTRACT_INT,
Self::AbstractFloat(_) => crate::Scalar::ABSTRACT_FLOAT,
}
}
pub const fn scalar_kind(&self) -> crate::ScalarKind {
@ -222,6 +237,10 @@ impl crate::Literal {
pub const POINTER_SPAN: u32 = 4;
impl super::TypeInner {
/// Return the scalar type of `self`.
///
/// If `inner` is a scalar, vector, or matrix type, return
/// its scalar type. Otherwise, return `None`.
pub const fn scalar(&self) -> Option<super::Scalar> {
use crate::TypeInner as Ti;
match *self {

View File

@ -671,7 +671,7 @@ impl super::Validator {
Bo::Add | Bo::Subtract => match *left_inner {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
Sk::Bool => false,
Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
},
Ti::Matrix { .. } => left_inner == right_inner,
_ => false,
@ -679,14 +679,14 @@ impl super::Validator {
Bo::Divide | Bo::Modulo => match *left_inner {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
Sk::Bool => false,
Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
},
_ => false,
},
Bo::Multiply => {
let kind_allowed = match left_inner.scalar_kind() {
Some(Sk::Uint | Sk::Sint | Sk::Float) => true,
Some(Sk::Bool) | None => false,
Some(Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat) | None => false,
};
let types_match = match (left_inner, right_inner) {
// Straight scalar and mixed scalar/vector.
@ -763,7 +763,7 @@ impl super::Validator {
match *left_inner {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
Sk::Bool => false,
Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
},
ref other => {
log::error!("Op {:?} left type {:?}", op, other);
@ -785,7 +785,7 @@ impl super::Validator {
Bo::And | Bo::InclusiveOr => match *left_inner {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
Sk::Bool | Sk::Sint | Sk::Uint => left_inner == right_inner,
Sk::Float => false,
Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
},
ref other => {
log::error!("Op {:?} left type {:?}", op, other);
@ -795,7 +795,7 @@ impl super::Validator {
Bo::ExclusiveOr => match *left_inner {
Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
Sk::Sint | Sk::Uint => left_inner == right_inner,
Sk::Bool | Sk::Float => false,
Sk::Bool | Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
},
ref other => {
log::error!("Op {:?} left type {:?}", op, other);
@ -824,7 +824,7 @@ impl super::Validator {
};
match base_scalar.kind {
Sk::Sint | Sk::Uint => base_size.is_ok() && base_size == shift_size,
Sk::Float | Sk::Bool => false,
Sk::Float | Sk::AbstractInt | Sk::AbstractFloat | Sk::Bool => false,
}
}
};

View File

@ -112,7 +112,7 @@ pub enum FunctionError {
InvalidStorePointer(Handle<crate::Expression>),
#[error("The value {0:?} can not be stored")]
InvalidStoreValue(Handle<crate::Expression>),
#[error("Store of {value:?} into {pointer:?} doesn't have matching types")]
#[error("The type of {value:?} doesn't match the type stored in {pointer:?}")]
InvalidStoreTypes {
pointer: Handle<crate::Expression>,
value: Handle<crate::Expression>,

View File

@ -143,6 +143,9 @@ pub enum WidthError {
#[error("64-bit integers are not yet supported")]
Unsupported64Bit,
#[error("Abstract types may only appear in constant expressions")]
Abstract,
}
// Only makes sense if `flags.contains(HOST_SHAREABLE)`
@ -248,6 +251,9 @@ impl super::Validator {
}
scalar.width == 4
}
crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
return Err(WidthError::Abstract);
}
};
if good {
Ok(())
@ -325,7 +331,10 @@ impl super::Validator {
}
Ti::Atomic(crate::Scalar { kind, width }) => {
let good = match kind {
crate::ScalarKind::Bool | crate::ScalarKind::Float => false,
crate::ScalarKind::Bool
| crate::ScalarKind::Float
| crate::ScalarKind::AbstractInt
| crate::ScalarKind::AbstractFloat => false,
crate::ScalarKind::Sint | crate::ScalarKind::Uint => width == 4,
};
if !good {

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"bde42e04a69f912ac871368e202df8af116ea0ef010a2f08a62388b6985ca61f","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/any_surface.rs":"1afed4e5e2cc1726c9887bfbf8805f9141f859615c1eaf90f4ef3e49850caf06","src/binding_model.rs":"d7add33189aa453ad19acd8ac38465f44e233d0e8da42be5a3aadfc50a87a491","src/command/bind.rs":"85bbab812222f9bc11893059304cac850616818b00857fadac4885b978e4cfe2","src/command/bundle.rs":"7836b3740ad32168fdfb4241dbc91839d695c019abd3c38e3decec332b7e82c2","src/command/clear.rs":"65499812d269e23efb12a12a455f58f52f3014f8db224d02951b7331638f6ad4","src/command/compute.rs":"296864d4f9e9a98f368d64910146480e38e2f895eee98a97d947dd593033f87c","src/command/draw.rs":"3687cbde422a29f28c1c3d17e132d912b3b4b2bcc98efca68d1ee0d563a5bf56","src/command/memory_init.rs":"ffe5c301f19a17285523ee8fd5e7bf5abd5e50e9a3716f5713ac99ab135d9f5e","src/command/mod.rs":"e7c5e83a1b6398e68279b10910f426d7dde1ce3c800752bb88688b37f69b7872","src/command/query.rs":"f31db3f1282109baa59e6dcd7b2e674c7858a2c64f58fc6eb3a4d0c546935af5","src/command/render.rs":"0b9a8804de0a85d3b4c593df2da78ce5d6c3e53169b05ca9babd3ec984cbe9f7","src/command/transfer.rs":"49dd1261e3ad1effc4ebdad1bc3061f4330f991b8291df75591a1e2bc5eaa257","src/conv.rs":"7e3ffe33b47a6fd3617aabf9f11cc68f1ccbee2c7343b8dbbcd0e8f3447e1ad8","src/device/any_device.rs":"2cb2be0fd078d65039692d309d8688cf4a02fb768579cf22c93cfa514d20ad7f","src/device/global.rs":"3be88717a7d164ea73842f90b93c359f8ea06bbae253af9a195734640d5a830d","src/device/life.rs":"6c2b2e7f98f71231c97f19cf19c4d8ff254ee9862a8422f973270656658aed96","src/device/mod.rs":"9bae0d30eaf51af5545eea10d5c8af6ca3ced2518d24c42880ec3c8f1f7664b2","src/device/queue.rs":"aec77faebaa750f843f2f04ea9a2021598098783f16f95e739bc64fabcc29619","src/device/resource.rs":"0e707723d58cecfb402fe35eb9944278de96a95496bd2e62b50a71ef3e44d079","src/device/trace.rs":"21408dfd2c99e3ce36a77d08ba86cf52f32bb376ed82690bbbf74937bfd42cbe","src/error.rs":"32680e922acfb1f1d6842177179768d365c575a8baa402da9d5a43a2357b0dbf","src/global.rs":"fd2a7995bdb64f1186fd71c4f6b59f34c543ee695912b1fad7931c88ec024584","src/hal_api.rs":"bb380df266fa9754c93d55c24b1a3535629710eb04bc6858a40c38a5f02aae68","src/hub.rs":"d9435f5b12f47e0b57599dce1d38e6eb4ef2477ab634806cfccefa4c1541f87b","src/id.rs":"4684c40d56ad8f49f36455ea84f2901df587fc3574274ac132b8236ece6926a3","src/identity.rs":"0701f6f41e754dde2bebc567a87c25b353dfab40b79a322990dbfa477739ab8c","src/init_tracker/buffer.rs":"61eb9cfaa312135b7a937ff6a3117f531b5b7323fae6553a41d6de9bc106d7e0","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"030fd594bf9948fad391390d85c5e1fec7eaf67b6e812c60f2dd59bc4fda8fd5","src/instance.rs":"643ce09d9a623f00e66e1eb50b2facc4d072f4149c9953343c1b0e8c4a5f6915","src/lib.rs":"23ac375edd02c7808ccb901b392bb34960339027b2e068b9009f89d413946515","src/pipeline.rs":"e09adc811a7553daa4d826fd6001be14a7d21222fc8ba76040d4fd5c0f017787","src/present.rs":"5a15e583ee2b4c1afcf596c8429437a50e9fd1e17f8fbf4fafb482928940815e","src/registry.rs":"52a52b0ed41abbed3c6a84f0fb01c5f45c75953bacaa7a9b9bdb95a8825eb573","src/resource.rs":"14a3fedcf422688dc484c91abc5b2522c388fd5587c61e11a4b0bd882284fb24","src/storage.rs":"343811d28ed0926ef2d7f116d5ad8b4b6b6f530d7dfb5b9afbb90f2bb3ccfbc1","src/track/buffer.rs":"2e37fdae4e3b81f4e9d4bc35d71849ce82846afbb55c3de75c5db101c888e216","src/track/metadata.rs":"8ef7af1d0152a3a8d706402a416c2e74c9282354cad055d2c0c527486cb3019e","src/track/mod.rs":"028b04676f8a42a1f67bb9f6ffb809f56fa6349596b52561bce5b78c176e51e7","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"57f13386f0829cd6e1a894e454f69929eea7494565e530ed9f80bd7d50a4ba2d","src/track/texture.rs":"ea8ba2089e72401dcd37738d0583c411ec100a7436626f738d8d81bc188cbd99","src/validation.rs":"0ca7fe772c06f788bd758e720d35e5664ffe69188922c7a1981f3855206e200b"},"package":null}
{"files":{"Cargo.toml":"5119648ea52e97ad3fa2f0f24cc30e0b305fe81f710a33396bdadaf7397d41b9","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/any_surface.rs":"1afed4e5e2cc1726c9887bfbf8805f9141f859615c1eaf90f4ef3e49850caf06","src/binding_model.rs":"b709658ed6b9af2ed07ff77b8da64912cd26a9d7caaecee26c77a92efdc615d4","src/command/bind.rs":"85bbab812222f9bc11893059304cac850616818b00857fadac4885b978e4cfe2","src/command/bundle.rs":"7836b3740ad32168fdfb4241dbc91839d695c019abd3c38e3decec332b7e82c2","src/command/clear.rs":"8929095abd1508fdbc4b51db1bcfe513f77954fcfb269888c91b47c90b943c03","src/command/compute.rs":"296864d4f9e9a98f368d64910146480e38e2f895eee98a97d947dd593033f87c","src/command/draw.rs":"3687cbde422a29f28c1c3d17e132d912b3b4b2bcc98efca68d1ee0d563a5bf56","src/command/memory_init.rs":"ffe5c301f19a17285523ee8fd5e7bf5abd5e50e9a3716f5713ac99ab135d9f5e","src/command/mod.rs":"9ffa26b0306067ad63fc61fbde985ad98d495a2ad739f51bf54a39f702f45d3a","src/command/query.rs":"f31db3f1282109baa59e6dcd7b2e674c7858a2c64f58fc6eb3a4d0c546935af5","src/command/render.rs":"61a9f34d813700606b49224d9de19773c49eb3dd42fa4027357a334dc63e2579","src/command/transfer.rs":"60969f020bddfb88a0c3b459b72b94d3a9a96cc635d20e88d016765d0fb94ba5","src/conv.rs":"7e3ffe33b47a6fd3617aabf9f11cc68f1ccbee2c7343b8dbbcd0e8f3447e1ad8","src/device/any_device.rs":"2cb2be0fd078d65039692d309d8688cf4a02fb768579cf22c93cfa514d20ad7f","src/device/global.rs":"33c47e5aaa44ad4a485a996af52247e765bedfa18724ab7662ce34c78754448b","src/device/life.rs":"bbda212777bf5156feb9ee4c46d7c7621888c11544903a26bf86e7b708590f5a","src/device/mod.rs":"4ec88e037307e597fc661c85c9fa28f2d70a82a222a5fec5c97c450d33224f37","src/device/queue.rs":"9525b939e49b4c2860928c04a660a4556b34bfbed23e00318635a5ad46bce061","src/device/resource.rs":"a47f4d8b605c477fc6a63058e1340872590631c9a1a7f4e454bbbbe01aada757","src/device/trace.rs":"9a8ec674567a8866a6bd1ed2ad06e474bd2504ed91f228d3040cb6db18fe5f2b","src/error.rs":"32680e922acfb1f1d6842177179768d365c575a8baa402da9d5a43a2357b0dbf","src/global.rs":"9aa9448791477007b850c5eb1584e7c74c14fe43ac47ab3b5f29373850f463ee","src/hal_api.rs":"bb380df266fa9754c93d55c24b1a3535629710eb04bc6858a40c38a5f02aae68","src/hub.rs":"d9435f5b12f47e0b57599dce1d38e6eb4ef2477ab634806cfccefa4c1541f87b","src/id.rs":"0aa4258f93e05f6991385980a553e67892a0f1769e37624ae0466f83161af697","src/identity.rs":"0701f6f41e754dde2bebc567a87c25b353dfab40b79a322990dbfa477739ab8c","src/init_tracker/buffer.rs":"61eb9cfaa312135b7a937ff6a3117f531b5b7323fae6553a41d6de9bc106d7e0","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"030fd594bf9948fad391390d85c5e1fec7eaf67b6e812c60f2dd59bc4fda8fd5","src/instance.rs":"46b13911e15310a7ced4e5f13eeaa5d2cde7762b8ffa282a28d0c812e7036458","src/lib.rs":"9408b33250c7062599045cc11dd1f52d6036415f0e5c568b37c73348b330bb7c","src/pipeline.rs":"1d615e582254cfa108dd1925df7b6b0ae0c9845714b1351b8bcd4c7aa4b4dd69","src/present.rs":"1c28d6dbc3125b5eebd12265013618d8585d5c90f3fd1d3529c20e41e1ddecb3","src/registry.rs":"e93369ae372b133649b554a3d0338d74823cfded8b7e84d01bf1f000b670f471","src/resource.rs":"be6b9b648911c2277d9f88ca9e9a2811ec28fdf8118e009a47de0c16179f28aa","src/storage.rs":"6ba6416522ba2f1d2a279c220960aabdfb071c4e0ade888c8cbc2b91325c4203","src/track/buffer.rs":"a6762fad8bf5b257d37fb8844f0ee44ecb04f052c58655404b8af0036ac929e5","src/track/metadata.rs":"f8994ad91db7f3bb1b7f8b8b0f6884753d733ce28238b36f31da2230706f848e","src/track/mod.rs":"5fdef0bcf20999fda97342e261341809394dcad3ee97e2bc2429c5a1e41d48d5","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"65142de8e8425eee26e32bfef8169af3ed0e98455d5e470517717a3c15e8036c","src/track/texture.rs":"d4e92ef3400cf48b2e76d8b7c1e4c6a1cef0d3d060c6abdb46b9a3b386bc9dc9","src/validation.rs":"4bd36b7b02ad0122fda719790d40cc68058e0f8e3ba3cd622f1d76db377320f9"},"package":null}

View File

@ -81,6 +81,7 @@ path = "../wgpu-types"
package = "wgpu-types"
[features]
api_log_info = []
default = ["link"]
dx11 = ["hal/dx11"]
dx12 = ["hal/dx12"]
@ -99,6 +100,7 @@ replay = [
"arrayvec/serde",
"naga/deserialize",
]
resource_log_info = []
serial-pass = [
"serde",
"wgt/serde",
@ -116,7 +118,7 @@ vulkan = ["hal/vulkan"]
wgsl = ["naga/wgsl-in"]
[target."cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))".dependencies.web-sys]
version = "0.3.64"
version = "0.3.66"
features = [
"HtmlCanvasElement",
"OffscreenCanvas",

View File

@ -8,6 +8,7 @@ use crate::{
},
init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
resource::{Resource, ResourceInfo, ResourceType},
resource_log,
track::{BindGroupStates, UsageConflict},
validation::{MissingBufferUsageError, MissingTextureUsageError},
FastHashMap, Label,
@ -465,8 +466,8 @@ pub struct BindGroupLayout<A: HalApi> {
impl<A: HalApi> Drop for BindGroupLayout<A> {
fn drop(&mut self) {
log::info!("Destroying BindGroupLayout {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw BindGroupLayout {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_bind_group_layout(raw);
@ -606,8 +607,8 @@ pub struct PipelineLayout<A: HalApi> {
impl<A: HalApi> Drop for PipelineLayout<A> {
fn drop(&mut self) {
log::info!("Destroying PipelineLayout {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw PipelineLayout {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_pipeline_layout(raw);
@ -827,8 +828,8 @@ pub struct BindGroup<A: HalApi> {
impl<A: HalApi> Drop for BindGroup<A> {
fn drop(&mut self) {
log::info!("Destroying BindGroup {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw BindGroup {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_bind_group(raw);

View File

@ -3,6 +3,7 @@ use std::{ops::Range, sync::Arc};
#[cfg(feature = "trace")]
use crate::device::trace::Command as TraceCommand;
use crate::{
api_log,
command::CommandBuffer,
get_lowest_common_denom,
global::Global,
@ -16,9 +17,7 @@ use crate::{
use hal::CommandEncoder as _;
use thiserror::Error;
use wgt::{
math::align_to, BufferAddress, BufferSize, BufferUsages, ImageSubresourceRange, TextureAspect,
};
use wgt::{math::align_to, BufferAddress, BufferUsages, ImageSubresourceRange, TextureAspect};
/// Error encountered while attempting a clear.
#[derive(Clone, Debug, Error)]
@ -37,7 +36,7 @@ pub enum ClearError {
#[error("Texture {0:?} can not be cleared")]
NoValidTextureClearMode(TextureId),
#[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
UnalignedFillSize(BufferSize),
UnalignedFillSize(BufferAddress),
#[error("Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
UnalignedBufferOffset(BufferAddress),
#[error("Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")]
@ -75,10 +74,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
command_encoder_id: CommandEncoderId,
dst: BufferId,
offset: BufferAddress,
size: Option<BufferSize>,
size: Option<BufferAddress>,
) -> Result<(), ClearError> {
profiling::scope!("CommandEncoder::clear_buffer");
log::trace!("CommandEncoder::clear_buffer {dst:?}");
api_log!("CommandEncoder::clear_buffer {dst:?}");
let hub = A::hub(self);
@ -116,10 +115,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return Err(ClearError::UnalignedBufferOffset(offset));
}
if let Some(size) = size {
if size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 {
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
return Err(ClearError::UnalignedFillSize(size));
}
let destination_end_offset = offset + size.get();
let destination_end_offset = offset + size;
if destination_end_offset > dst_buffer.size {
return Err(ClearError::BufferOverrun {
start_offset: offset,
@ -130,7 +129,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
let end = match size {
Some(size) => offset + size.get(),
Some(size) => offset + size,
None => dst_buffer.size,
};
if offset == end {
@ -163,7 +162,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
subresource_range: &ImageSubresourceRange,
) -> Result<(), ClearError> {
profiling::scope!("CommandEncoder::clear_texture");
log::trace!("CommandEncoder::clear_texture {dst:?}");
api_log!("CommandEncoder::clear_texture {dst:?}");
let hub = A::hub(self);
@ -338,6 +337,11 @@ fn clear_texture_via_buffer_copies<A: HalApi>(
hal::FormatAspects::COLOR
);
if texture_desc.format == wgt::TextureFormat::NV12 {
// TODO: Currently COPY_DST for NV12 textures is unsupported.
return;
}
// Gather list of zero_buffer copies and issue a single command then to perform them
let mut zero_buffer_copy_regions = Vec::new();
let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;

View File

@ -26,7 +26,10 @@ use crate::id::CommandBufferId;
use crate::init_tracker::BufferInitTrackerAction;
use crate::resource::{Resource, ResourceInfo, ResourceType};
use crate::track::{Tracker, UsageScope};
use crate::{global::Global, hal_api::HalApi, id, identity::GlobalIdentityHandlerFactory, Label};
use crate::{
api_log, global::Global, hal_api::HalApi, id, identity::GlobalIdentityHandlerFactory,
resource_log, Label,
};
use hal::CommandEncoder as _;
use parking_lot::Mutex;
@ -135,7 +138,7 @@ impl<A: HalApi> Drop for CommandBuffer<A> {
if self.data.lock().is_none() {
return;
}
log::info!("Destroying CommandBuffer {:?}", self.info.label());
resource_log!("resource::CommandBuffer::drop {}", self.info.label());
let mut baked = self.extract_baked_commands();
unsafe {
baked.encoder.reset_all(baked.list.into_iter());
@ -253,7 +256,7 @@ impl<A: HalApi> CommandBuffer<A> {
}
pub(crate) fn extract_baked_commands(&mut self) -> BakedCommands<A> {
log::info!(
log::trace!(
"Extracting BakedCommands from CommandBuffer {:?}",
self.info.label()
);
@ -435,7 +438,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
label: &str,
) -> Result<(), CommandEncoderError> {
profiling::scope!("CommandEncoder::push_debug_group");
log::trace!("CommandEncoder::push_debug_group {label}");
api_log!("CommandEncoder::push_debug_group {label}");
let hub = A::hub(self);
@ -466,7 +469,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
label: &str,
) -> Result<(), CommandEncoderError> {
profiling::scope!("CommandEncoder::insert_debug_marker");
log::trace!("CommandEncoder::insert_debug_marker {label}");
api_log!("CommandEncoder::insert_debug_marker {label}");
let hub = A::hub(self);
@ -497,7 +500,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
encoder_id: id::CommandEncoderId,
) -> Result<(), CommandEncoderError> {
profiling::scope!("CommandEncoder::pop_debug_marker");
log::trace!("CommandEncoder::pop_debug_group");
api_log!("CommandEncoder::pop_debug_group");
let hub = A::hub(self);

View File

@ -1,5 +1,6 @@
use crate::resource::Resource;
use crate::{
api_log,
binding_model::BindError,
command::{
self,
@ -1410,7 +1411,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
num_dynamic_offsets,
bind_group_id,
} => {
log::trace!("RenderPass::set_bind_group {index} {bind_group_id:?}");
api_log!("RenderPass::set_bind_group {index} {bind_group_id:?}");
let scope = PassErrorScope::SetBindGroup(bind_group_id);
let max_bind_groups = device.limits.max_bind_groups;
@ -1493,7 +1494,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
RenderCommand::SetPipeline(pipeline_id) => {
log::trace!("RenderPass::set_pipeline {pipeline_id:?}");
api_log!("RenderPass::set_pipeline {pipeline_id:?}");
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
state.pipeline = Some(pipeline_id);
@ -1621,7 +1622,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
offset,
size,
} => {
log::trace!("RenderPass::set_index_buffer {buffer_id:?}");
api_log!("RenderPass::set_index_buffer {buffer_id:?}");
let scope = PassErrorScope::SetIndexBuffer(buffer_id);
let buffer = info
@ -1674,7 +1675,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
offset,
size,
} => {
log::trace!("RenderPass::set_vertex_buffer {slot} {buffer_id:?}");
api_log!("RenderPass::set_vertex_buffer {slot} {buffer_id:?}");
let scope = PassErrorScope::SetVertexBuffer(buffer_id);
let buffer = info
@ -1737,7 +1738,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
state.vertex.update_limits();
}
RenderCommand::SetBlendConstant(ref color) => {
log::trace!("RenderPass::set_blend_constant");
api_log!("RenderPass::set_blend_constant");
state.blend_constant = OptionalState::Set;
let array = [
@ -1751,7 +1752,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
RenderCommand::SetStencilReference(value) => {
log::trace!("RenderPass::set_stencil_reference {value}");
api_log!("RenderPass::set_stencil_reference {value}");
state.stencil_reference = value;
if state
@ -1768,7 +1769,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
depth_min,
depth_max,
} => {
log::trace!("RenderPass::set_viewport {rect:?}");
api_log!("RenderPass::set_viewport {rect:?}");
let scope = PassErrorScope::SetViewport;
if rect.x < 0.0
@ -1806,7 +1807,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size_bytes,
values_offset,
} => {
log::trace!("RenderPass::set_push_constants");
api_log!("RenderPass::set_push_constants");
let scope = PassErrorScope::SetPushConstant;
let values_offset = values_offset
@ -1841,7 +1842,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
RenderCommand::SetScissor(ref rect) => {
log::trace!("RenderPass::set_scissor_rect {rect:?}");
api_log!("RenderPass::set_scissor_rect {rect:?}");
let scope = PassErrorScope::SetScissorRect;
if rect.x + rect.w > info.extent.width
@ -1866,7 +1867,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
first_vertex,
first_instance,
} => {
log::trace!(
api_log!(
"RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}"
);
@ -1910,7 +1911,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
base_vertex,
first_instance,
} => {
log::trace!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
let indexed = true;
let scope = PassErrorScope::Draw {
@ -1958,7 +1959,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
count,
indexed,
} => {
log::trace!("RenderPass::draw_indirect (indexed:{indexed}) {buffer_id:?} {offset} {count:?}");
api_log!("RenderPass::draw_indirect (indexed:{indexed}) {buffer_id:?} {offset} {count:?}");
let scope = PassErrorScope::Draw {
indexed,
@ -2032,7 +2033,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
max_count,
indexed,
} => {
log::trace!("RenderPass::multi_draw_indirect_count (indexed:{indexed}) {buffer_id:?} {offset} {count_buffer_id:?} {count_buffer_offset:?} {max_count:?}");
api_log!("RenderPass::multi_draw_indirect_count (indexed:{indexed}) {buffer_id:?} {offset} {count_buffer_id:?} {count_buffer_offset:?} {max_count:?}");
let scope = PassErrorScope::Draw {
indexed,
@ -2148,7 +2149,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
)
.unwrap();
log::trace!("RenderPass::push_debug_group {label:?}");
api_log!("RenderPass::push_debug_group {label:?}");
unsafe {
raw.begin_debug_marker(label);
}
@ -2156,7 +2157,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
string_offset += len;
}
RenderCommand::PopDebugGroup => {
log::trace!("RenderPass::pop_debug_group");
api_log!("RenderPass::pop_debug_group");
let scope = PassErrorScope::PopDebugGroup;
if state.debug_scope_depth == 0 {
@ -2176,7 +2177,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&base.string_data[string_offset..string_offset + len],
)
.unwrap();
log::trace!("RenderPass::insert_debug_marker {label:?}");
api_log!("RenderPass::insert_debug_marker {label:?}");
unsafe {
raw.insert_debug_marker(label);
}
@ -2187,7 +2188,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
query_set_id,
query_index,
} => {
log::trace!("RenderPass::write_timestamps {query_set_id:?} {query_index}");
api_log!("RenderPass::write_timestamps {query_set_id:?} {query_index}");
let scope = PassErrorScope::WriteTimestamp;
device
@ -2210,7 +2211,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_pass_err(scope)?;
}
RenderCommand::BeginOcclusionQuery { query_index } => {
log::trace!("RenderPass::begin_occlusion_query {query_index}");
api_log!("RenderPass::begin_occlusion_query {query_index}");
let scope = PassErrorScope::BeginOcclusionQuery;
let query_set_id = occlusion_query_set_id
@ -2234,7 +2235,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_pass_err(scope)?;
}
RenderCommand::EndOcclusionQuery => {
log::trace!("RenderPass::end_occlusion_query");
api_log!("RenderPass::end_occlusion_query");
let scope = PassErrorScope::EndOcclusionQuery;
end_occlusion_query(raw, &*query_set_guard, &mut active_query)
@ -2244,7 +2245,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
query_set_id,
query_index,
} => {
log::trace!("RenderPass::begin_pipeline_statistics_query {query_set_id:?} {query_index}");
api_log!("RenderPass::begin_pipeline_statistics_query {query_set_id:?} {query_index}");
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
let query_set = tracker
@ -2264,14 +2265,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_pass_err(scope)?;
}
RenderCommand::EndPipelineStatisticsQuery => {
log::trace!("RenderPass::end_pipeline_statistics_query");
api_log!("RenderPass::end_pipeline_statistics_query");
let scope = PassErrorScope::EndPipelineStatisticsQuery;
end_pipeline_statistics_query(raw, &*query_set_guard, &mut active_query)
.map_pass_err(scope)?;
}
RenderCommand::ExecuteBundle(bundle_id) => {
log::trace!("RenderPass::execute_bundle {bundle_id:?}");
api_log!("RenderPass::execute_bundle {bundle_id:?}");
let scope = PassErrorScope::ExecuteBundle;
let bundle: &command::RenderBundle<A> = tracker
.bundles

View File

@ -1,6 +1,7 @@
#[cfg(feature = "trace")]
use crate::device::trace::Command as TraceCommand;
use crate::{
api_log,
command::{clear_texture, CommandBuffer, CommandEncoderError},
conv,
device::{Device, MissingDownlevelFlags},
@ -567,6 +568,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size: BufferAddress,
) -> Result<(), CopyError> {
profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
api_log!(
"CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes"
);
if source == destination {
return Err(TransferError::SameSourceDestinationBuffer.into());
@ -727,6 +731,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size: &Extent3d,
) -> Result<(), CopyError> {
profiling::scope!("CommandEncoder::copy_buffer_to_texture");
api_log!(
"CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
source.buffer,
destination.texture
);
let hub = A::hub(self);
@ -885,6 +894,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size: &Extent3d,
) -> Result<(), CopyError> {
profiling::scope!("CommandEncoder::copy_texture_to_buffer");
api_log!(
"CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
source.texture,
destination.buffer
);
let hub = A::hub(self);
@ -1055,6 +1069,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size: &Extent3d,
) -> Result<(), CopyError> {
profiling::scope!("CommandEncoder::copy_texture_to_texture");
api_log!(
"CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
source.texture,
destination.texture
);
let hub = A::hub(self);

View File

@ -1,7 +1,7 @@
#[cfg(feature = "trace")]
use crate::device::trace;
use crate::{
binding_model, command, conv,
api_log, binding_model, command, conv,
device::{
life::WaitIdleError, map_buffer, queue, DeviceError, DeviceLostClosure, HostMap,
IMPLICIT_FAILURE,
@ -185,7 +185,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(buffer);
log::info!("Created Buffer {:?} with {:?}", id, desc);
api_log!("Device::create_buffer({desc:?}) -> {id:?}");
let buffer_use = if !desc.mapped_at_creation {
hal::BufferUses::empty()
@ -481,10 +481,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_id: id::BufferId,
) -> Result<(), resource::DestroyError> {
profiling::scope!("Buffer::destroy");
api_log!("Buffer::destroy {buffer_id:?}");
let hub = A::hub(self);
log::debug!("Buffer {:?} is asked to be dropped", buffer_id);
let mut buffer_guard = hub.buffers.write();
let buffer = buffer_guard
.get_and_mark_destroyed(buffer_id)
@ -494,8 +494,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn buffer_drop<A: HalApi>(&self, buffer_id: id::BufferId, wait: bool) {
profiling::scope!("Buffer::drop");
log::debug!("Buffer {:?} is asked to be dropped", buffer_id);
api_log!("Buffer::drop {buffer_id:?}");
let hub = A::hub(self);
@ -564,7 +563,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(texture);
log::info!("Created Texture {:?} with {:?}", id, desc);
api_log!("Device::create_texture({desc:?}) -> {id:?}");
device.trackers.lock().textures.insert_single(
id,
@ -575,6 +574,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return (id, None);
};
log::error!("Device::create_texture error {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -636,7 +637,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
RwLock::new(TextureInitTracker::new(desc.mip_level_count, 0));
let (id, resource) = fid.assign(texture);
log::info!("Created Texture {:?} with {:?}", id, desc);
api_log!("Device::create_texture -> {id:?}");
device.trackers.lock().textures.insert_single(
id,
@ -647,6 +648,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return (id, None);
};
log::error!("Device::create_texture error {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -688,7 +691,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let buffer = device.create_buffer_from_hal(hal_buffer, desc);
let (id, buffer) = fid.assign(buffer);
log::info!("Created buffer {:?} with {:?}", id, desc);
api_log!("Device::create_buffer -> {id:?}");
device
.trackers
@ -699,6 +702,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return (id, None);
};
log::error!("Device::create_buffer error {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -712,11 +717,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
texture_id: id::TextureId,
) -> Result<(), resource::DestroyError> {
profiling::scope!("Texture::destroy");
log::trace!("Texture::destroy {texture_id:?}");
api_log!("Texture::destroy {texture_id:?}");
let hub = A::hub(self);
log::debug!("Texture {:?} is destroyed", texture_id);
let mut texture_guard = hub.textures.write();
let texture = texture_guard
.get_and_mark_destroyed(texture_id)
@ -754,8 +758,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn texture_drop<A: HalApi>(&self, texture_id: id::TextureId, wait: bool) {
profiling::scope!("Texture::drop");
log::debug!("Texture {:?} is asked to be dropped", texture_id);
api_log!("Texture::drop {texture_id:?}");
let hub = A::hub(self);
@ -827,12 +830,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(view);
log::info!("Created TextureView {:?}", id);
api_log!("Texture::create_view({texture_id:?}) -> {id:?}");
device.trackers.lock().views.insert_single(id, resource);
return (id, None);
};
log::error!("Texture::create_view {:?} error {:?}", texture_id, error);
log::error!("Texture::create_view({texture_id:?}) error {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -847,8 +850,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
wait: bool,
) -> Result<(), resource::TextureViewDestroyError> {
profiling::scope!("TextureView::drop");
log::debug!("TextureView {:?} is asked to be dropped", texture_view_id);
api_log!("TextureView::drop {texture_view_id:?}");
let hub = A::hub(self);
@ -905,7 +907,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(sampler);
log::info!("Created Sampler {:?}", id);
api_log!("Device::create_sampler -> {id:?}");
device.trackers.lock().samplers.insert_single(id, resource);
return (id, None);
@ -921,7 +923,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn sampler_drop<A: HalApi>(&self, sampler_id: id::SamplerId) {
profiling::scope!("Sampler::drop");
log::debug!("Sampler {:?} is asked to be dropped", sampler_id);
api_log!("Sampler::drop {sampler_id:?}");
let hub = A::hub(self);
@ -981,7 +983,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let bgl_guard = hub.bind_group_layouts.read();
device.deduplicate_bind_group_layout(&entry_map, &*bgl_guard)
} {
log::info!("Reusing BindGroupLayout {layout:?} -> {:?}", id);
api_log!("Reusing BindGroupLayout {layout:?} -> {:?}", id);
let id = fid.assign_existing(&layout);
return (id, None);
}
@ -992,7 +994,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, _layout) = fid.assign(layout);
log::info!("Created BindGroupLayout {:?}", id);
api_log!("Device::create_bind_group_layout -> {id:?}");
return (id, None);
};
@ -1007,11 +1009,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn bind_group_layout_drop<A: HalApi>(&self, bind_group_layout_id: id::BindGroupLayoutId) {
profiling::scope!("BindGroupLayout::drop");
log::debug!(
"BindGroupLayout {:?} is asked to be dropped",
bind_group_layout_id
);
api_log!("BindGroupLayout::drop {bind_group_layout_id:?}");
let hub = A::hub(self);
@ -1061,7 +1059,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, _) = fid.assign(layout);
log::info!("Created PipelineLayout {:?}", id);
api_log!("Device::create_pipeline_layout -> {id:?}");
return (id, None);
};
@ -1075,11 +1073,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn pipeline_layout_drop<A: HalApi>(&self, pipeline_layout_id: id::PipelineLayoutId) {
profiling::scope!("PipelineLayout::drop");
log::debug!(
"PipelineLayout {:?} is asked to be dropped",
pipeline_layout_id
);
api_log!("PipelineLayout::drop {pipeline_layout_id:?}");
let hub = A::hub(self);
if let Some(layout) = hub.pipeline_layouts.unregister(pipeline_layout_id) {
@ -1132,7 +1126,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(bind_group);
log::info!("Created BindGroup {:?}", id,);
api_log!("Device::create_bind_group -> {id:?}");
device
.trackers
@ -1152,8 +1146,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn bind_group_drop<A: HalApi>(&self, bind_group_id: id::BindGroupId) {
profiling::scope!("BindGroup::drop");
log::debug!("BindGroup {:?} is asked to be dropped", bind_group_id);
api_log!("BindGroup::drop {bind_group_id:?}");
let hub = A::hub(self);
@ -1220,10 +1213,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, _) = fid.assign(shader);
log::info!("Created ShaderModule {:?} with {:?}", id, desc);
api_log!("Device::create_shader_module -> {id:?}");
return (id, None);
};
log::error!("Device::create_shader_module error: {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -1275,10 +1270,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Err(e) => break e,
};
let (id, _) = fid.assign(shader);
log::info!("Created ShaderModule {:?} with {:?}", id, desc);
api_log!("Device::create_shader_module_spirv -> {id:?}");
return (id, None);
};
log::error!("Device::create_shader_module_spirv error: {error:?}");
let id = fid.assign_error(desc.label.borrow_or_default());
(id, Some(error))
}
@ -1289,8 +1286,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn shader_module_drop<A: HalApi>(&self, shader_module_id: id::ShaderModuleId) {
profiling::scope!("ShaderModule::drop");
log::debug!("ShaderModule {:?} is asked to be dropped", shader_module_id);
api_log!("ShaderModule::drop {shader_module_id:?}");
let hub = A::hub(self);
hub.shader_modules.unregister(shader_module_id);
@ -1340,7 +1336,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
let (id, _) = fid.assign(command_buffer);
log::info!("Created CommandBuffer {:?} with {:?}", id, desc);
api_log!("Device::create_command_encoder -> {id:?}");
return (id, None);
};
@ -1354,11 +1350,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn command_encoder_drop<A: HalApi>(&self, command_encoder_id: id::CommandEncoderId) {
profiling::scope!("CommandEncoder::drop");
log::debug!(
"CommandEncoder {:?} is asked to be dropped",
command_encoder_id
);
api_log!("CommandEncoder::drop {command_encoder_id:?}");
let hub = A::hub(self);
@ -1371,11 +1363,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn command_buffer_drop<A: HalApi>(&self, command_buffer_id: id::CommandBufferId) {
profiling::scope!("CommandBuffer::drop");
log::debug!(
"CommandBuffer {:?} is asked to be dropped",
command_buffer_id
);
api_log!("CommandBuffer::drop {command_buffer_id:?}");
self.command_encoder_drop::<A>(command_buffer_id)
}
@ -1388,7 +1376,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Option<command::CreateRenderBundleError>,
) {
profiling::scope!("Device::create_render_bundle_encoder");
log::trace!("Device::device_create_render_bundle_encoder");
api_log!("Device::device_create_render_bundle_encoder");
let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id, None) {
Ok(encoder) => (encoder, None),
Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)),
@ -1437,7 +1425,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(render_bundle);
log::info!("Created RenderBundle {:?}", id);
api_log!("RenderBundleEncoder::finish -> {id:?}");
device.trackers.lock().bundles.insert_single(id, resource);
return (id, None);
};
@ -1452,8 +1440,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn render_bundle_drop<A: HalApi>(&self, render_bundle_id: id::RenderBundleId) {
profiling::scope!("RenderBundle::drop");
log::debug!("RenderBundle {:?} is asked to be dropped", render_bundle_id);
api_log!("RenderBundle::drop {render_bundle_id:?}");
let hub = A::hub(self);
@ -1500,7 +1487,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(query_set);
log::info!("Created QuerySet {:?}", id);
api_log!("Device::create_query_set -> {id:?}");
device
.trackers
.lock()
@ -1516,8 +1503,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn query_set_drop<A: HalApi>(&self, query_set_id: id::QuerySetId) {
profiling::scope!("QuerySet::drop");
log::debug!("QuerySet {:?} is asked to be dropped", query_set_id);
api_log!("QuerySet::drop {query_set_id:?}");
let hub = A::hub(self);
@ -1582,7 +1568,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(pipeline);
log::info!("Created RenderPipeline {:?} with {:?}", id, desc);
api_log!("Device::create_render_pipeline -> {id:?}");
device
.trackers
@ -1612,6 +1598,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
log::error!("Device::create_render_pipeline error {error:?}");
(id, Some(error))
}
@ -1656,11 +1644,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn render_pipeline_drop<A: HalApi>(&self, render_pipeline_id: id::RenderPipelineId) {
profiling::scope!("RenderPipeline::drop");
log::debug!(
"RenderPipeline {:?} is asked to be dropped",
render_pipeline_id
);
api_log!("RenderPipeline::drop {render_pipeline_id:?}");
let hub = A::hub(self);
@ -1719,7 +1703,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(pipeline);
log::info!("Created ComputePipeline {:?} with {:?}", id, desc);
api_log!("Device::create_compute_pipeline -> {id:?}");
device
.trackers
@ -1795,11 +1779,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn compute_pipeline_drop<A: HalApi>(&self, compute_pipeline_id: id::ComputePipelineId) {
profiling::scope!("ComputePipeline::drop");
log::debug!(
"ComputePipeline {:?} is asked to be dropped",
compute_pipeline_id
);
api_log!("ComputePipeline::drop {compute_pipeline_id:?}");
let hub = A::hub(self);
@ -1829,21 +1809,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
fn validate_surface_configuration(
config: &mut hal::SurfaceConfiguration,
caps: &hal::SurfaceCapabilities,
max_texture_dimension_2d: u32,
) -> Result<(), E> {
let width = config.extent.width;
let height = config.extent.height;
if width < caps.extents.start().width
|| width > caps.extents.end().width
|| height < caps.extents.start().height
|| height > caps.extents.end().height
{
log::warn!(
"Requested size {}x{} is outside of the supported range: {:?}",
if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
return Err(E::TooLarge {
width,
height,
caps.extents
);
max_texture_dimension_2d,
});
}
if !caps.present_modes.contains(&config.present_mode) {
let new_mode = 'b: loop {
// Automatic present mode checks.
@ -1876,7 +1854,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
unreachable!("Fallback system failed to choose present mode. This is a bug. Mode: {:?}, Options: {:?}", config.present_mode, &caps.present_modes);
};
log::info!(
api_log!(
"Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
config.present_mode
);
@ -1920,7 +1898,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
};
log::info!(
api_log!(
"Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
config.composite_alpha_mode
);
@ -2017,14 +1995,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
view_formats: hal_view_formats,
};
if let Err(error) = validate_surface_configuration(&mut hal_config, &caps) {
if let Err(error) = validate_surface_configuration(
&mut hal_config,
&caps,
device.limits.max_texture_dimension_2d,
) {
break error;
}
// Wait for all work to finish before configuring the surface.
let fence = device.fence.read();
let fence = fence.as_ref().unwrap();
match device.maintain(hub, fence, wgt::Maintain::Wait) {
match device.maintain(fence, wgt::Maintain::Wait) {
Ok((closures, _)) => {
user_callbacks = closures;
}
@ -2094,7 +2076,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return Err(InvalidDevice);
}
device.lock_life().triage_suspected(
hub,
&device.trackers,
#[cfg(feature = "trace")]
None,
@ -2110,7 +2091,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: DeviceId,
maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
) -> Result<bool, WaitIdleError> {
log::trace!("Device::poll");
api_log!("Device::poll");
let (closures, queue_empty) = {
if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
@ -2129,7 +2110,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(|_| DeviceError::Invalid)?;
let fence = device.fence.read();
let fence = fence.as_ref().unwrap();
device.maintain(hub, fence, maintain)?
device.maintain(fence, maintain)?
};
closures.fire();
@ -2163,7 +2144,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let fence = device.fence.read();
let fence = fence.as_ref().unwrap();
let (cbs, queue_empty) = device.maintain(hub, fence, maintain)?;
let (cbs, queue_empty) = device.maintain(fence, maintain)?;
all_queue_empty = all_queue_empty && queue_empty;
closures.extend(cbs);
@ -2180,6 +2161,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
/// Return `all_queue_empty` indicating whether there are more queue
/// submissions still in flight.
pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
api_log!("poll_all_devices");
let mut closures = UserClosures::default();
let mut all_queue_empty = true;
@ -2219,7 +2201,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub fn device_start_capture<A: HalApi>(&self, id: DeviceId) {
log::trace!("Device::start_capture");
api_log!("Device::start_capture");
let hub = A::hub(self);
@ -2232,7 +2214,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub fn device_stop_capture<A: HalApi>(&self, id: DeviceId) {
log::trace!("Device::stop_capture");
api_log!("Device::stop_capture");
let hub = A::hub(self);
@ -2246,7 +2228,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn device_drop<A: HalApi>(&self, device_id: DeviceId) {
profiling::scope!("Device::drop");
log::debug!("Device {:?} is asked to be dropped", device_id);
api_log!("Device::drop {device_id:?}");
let hub = A::hub(self);
if let Some(device) = hub.devices.unregister(device_id) {
@ -2279,7 +2261,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub fn device_destroy<A: HalApi>(&self, device_id: DeviceId) {
log::trace!("Device::destroy {device_id:?}");
api_log!("Device::destroy {device_id:?}");
let hub = A::hub(self);
@ -2305,7 +2287,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub fn device_mark_lost<A: HalApi>(&self, device_id: DeviceId, message: &str) {
log::trace!("Device::mark_lost {device_id:?}");
api_log!("Device::mark_lost {device_id:?}");
let hub = A::hub(self);
@ -2316,7 +2298,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn queue_drop<A: HalApi>(&self, queue_id: QueueId) {
profiling::scope!("Queue::drop");
log::debug!("Queue {:?} is asked to be dropped", queue_id);
api_log!("Queue::drop {queue_id:?}");
let hub = A::hub(self);
if let Some(queue) = hub.queues.unregister(queue_id) {
@ -2330,7 +2312,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
range: Range<BufferAddress>,
op: BufferMapOperation,
) -> BufferAccessResult {
log::trace!("Buffer::map_async {buffer_id:?}");
api_log!("Buffer::map_async {buffer_id:?}");
// User callbacks must not be called while holding buffer_map_async_inner's locks, so we
// defer the error callback if it needs to be called immediately (typically when running
@ -2339,6 +2321,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if let Some(callback) = operation.callback.take() {
callback.call(Err(err.clone()));
}
log::error!("Buffer::map_async error {err:?}");
return Err(err);
}
@ -2425,7 +2408,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
};
}
log::debug!("Buffer {:?} map state -> Waiting", buffer_id);
{
let mut trackers = buffer.device.as_ref().trackers.lock();
@ -2449,7 +2431,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size: Option<BufferAddress>,
) -> Result<(*mut u8, u64), BufferAccessError> {
profiling::scope!("Buffer::get_mapped_range");
log::trace!("Buffer::get_mapped_range {buffer_id:?}");
api_log!("Buffer::get_mapped_range {buffer_id:?}");
let hub = A::hub(self);
@ -2511,7 +2493,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub fn buffer_unmap<A: HalApi>(&self, buffer_id: id::BufferId) -> BufferAccessResult {
profiling::scope!("unmap", "Buffer");
log::trace!("Buffer::unmap {buffer_id:?}");
api_log!("Buffer::unmap {buffer_id:?}");
let closure;
{

View File

@ -8,14 +8,12 @@ use crate::{
DeviceError, DeviceLostClosure,
},
hal_api::HalApi,
hub::Hub,
id::{
self, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, PipelineLayoutId,
QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, StagingBufferId, TextureId,
TextureViewId,
},
pipeline::{ComputePipeline, RenderPipeline},
registry::Registry,
resource::{
self, Buffer, QuerySet, Resource, ResourceType, Sampler, StagingBuffer, Texture,
TextureView,
@ -77,16 +75,6 @@ impl ResourceMaps {
self.maps.insert(R::TYPE, Box::new(map));
self
}
fn map<Id, R>(&self) -> &FastHashMap<Id, Arc<R>>
where
Id: id::TypedId,
R: Resource<Id>,
{
let map = self.maps.get(R::TYPE).unwrap();
let any_map = map.as_ref().as_any();
let map = any_map.downcast_ref::<FastHashMap<Id, Arc<R>>>().unwrap();
map
}
fn map_mut<Id, R>(&mut self) -> &mut FastHashMap<Id, Arc<R>>
where
Id: id::TypedId,
@ -132,13 +120,6 @@ impl ResourceMaps {
self.map_mut().insert(id, r);
self
}
pub(crate) fn contains<Id, R>(&mut self, id: &Id) -> bool
where
Id: id::TypedId,
R: Resource<Id>,
{
self.map::<Id, R>().contains_key(id)
}
}
/// Resources used by a queue submission, and work to be done once it completes.
@ -369,7 +350,7 @@ 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::info!("Active submission {} is done", a.index);
log::debug!("Active submission {} is done", a.index);
self.free_resources.extend(a.last_resources);
self.ready_to_map.extend(a.mapped);
for encoder in a.encoders {
@ -424,35 +405,27 @@ impl<A: HalApi> LifetimeTracker<A> {
}
impl<A: HalApi> LifetimeTracker<A> {
fn triage_resources<Id, R, F, T>(
fn triage_resources<Id, R, T>(
resources_map: &mut FastHashMap<Id, Arc<R>>,
active: &mut [ActiveSubmission<A>],
free_resources: &mut ResourceMaps,
trackers: &mut impl ResourceTracker<Id, R>,
registry: &Registry<Id, R>,
count_fn: F,
mut on_remove: T,
) -> Vec<Arc<R>>
where
Id: id::TypedId,
R: Resource<Id>,
F: Fn(u64, &[ActiveSubmission<A>], &Id) -> usize,
T: FnMut(&Id, &Arc<R>),
{
let mut removed_resources = Vec::new();
resources_map.retain(|&id, resource| {
let submit_index = resource.as_info().submission_index();
let mut count = 1;
count += count_fn(submit_index, active, &id);
count += registry.contains(id) as usize;
let non_referenced_resources = active
.iter_mut()
.find(|a| a.index == submit_index)
.map_or(&mut *free_resources, |a| &mut a.last_resources);
count += non_referenced_resources.contains::<Id, R>(&id) as usize;
let is_removed = trackers.remove_abandoned(id, count);
let is_removed = trackers.remove_abandoned(id);
if is_removed {
on_remove(&id, resource);
removed_resources.push(resource.clone());
@ -465,7 +438,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_render_bundles(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -476,8 +448,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.bundles,
&hub.render_bundles,
|_submit_index, _active, _id| 0,
|_bundle_id, _bundle| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -507,7 +477,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_bind_groups(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -518,8 +487,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.bind_groups,
&hub.bind_groups,
|_submit_index, _active, _id| 0,
|_bind_group_id, _bind_group| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -553,7 +520,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_texture_views(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -564,8 +530,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.views,
&hub.texture_views,
|_submit_index, _active, _id| 0,
|_texture_view_id, _texture_view| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -585,7 +549,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_textures(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -596,8 +559,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.textures,
&hub.textures,
|_submit_index, _active, _id| 0,
|_texture_id, _texture| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -610,7 +571,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_samplers(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -621,8 +581,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.samplers,
&hub.samplers,
|_submit_index, _active, _id| 0,
|_sampler_id, _sampler| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -635,7 +593,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_buffers(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -646,20 +603,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.buffers,
&hub.buffers,
|submit_index, active, buffer_id| {
let mut count = 0;
let mapped = active
.iter()
.find(|a| a.index == submit_index)
.map_or(&self.mapped, |a| &a.mapped);
mapped.iter().for_each(|b| {
if b.as_info().id() == *buffer_id {
count += 1;
}
});
count
},
|_buffer_id, _buffer| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -681,7 +624,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_compute_pipelines(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -692,8 +634,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.compute_pipelines,
&hub.compute_pipelines,
|_submit_index, _active, _id| 0,
|_compute_pipeline_id, _compute_pipeline| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -712,7 +652,6 @@ impl<A: HalApi> LifetimeTracker<A> {
fn triage_suspected_render_pipelines(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: &mut Option<&mut trace::Trace>,
) -> &mut Self {
@ -723,8 +662,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.render_pipelines,
&hub.render_pipelines,
|_submit_index, _active, _id| 0,
|_render_pipeline_id, _render_pipeline| {
#[cfg(feature = "trace")]
if let Some(ref mut t) = *trace {
@ -787,11 +724,7 @@ impl<A: HalApi> LifetimeTracker<A> {
self
}
fn triage_suspected_query_sets(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
) -> &mut Self {
fn triage_suspected_query_sets(&mut self, trackers: &Mutex<Tracker<A>>) -> &mut Self {
let mut trackers = trackers.lock();
let resource_map = self.suspected_resources.map_mut();
Self::triage_resources(
@ -799,8 +732,6 @@ impl<A: HalApi> LifetimeTracker<A> {
self.active.as_mut_slice(),
&mut self.free_resources,
&mut trackers.query_sets,
&hub.query_sets,
|_submit_index, _active, _id| 0,
|_query_set_id, _query_set| {},
);
self
@ -858,7 +789,6 @@ impl<A: HalApi> LifetimeTracker<A> {
/// [`self.free_resources`]: LifetimeTracker::free_resources
pub(crate) fn triage_suspected(
&mut self,
hub: &Hub<A>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] mut trace: Option<&mut trace::Trace>,
) {
@ -866,25 +796,21 @@ impl<A: HalApi> LifetimeTracker<A> {
//NOTE: the order is important to release resources that depends between each other!
self.triage_suspected_render_bundles(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_compute_pipelines(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_render_pipelines(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_bind_groups(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
@ -897,28 +823,24 @@ impl<A: HalApi> LifetimeTracker<A> {
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_query_sets(hub, trackers);
self.triage_suspected_query_sets(trackers);
self.triage_suspected_samplers(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_staging_buffers();
self.triage_suspected_texture_views(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_textures(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
);
self.triage_suspected_buffers(
hub,
trackers,
#[cfg(feature = "trace")]
&mut trace,
@ -959,7 +881,6 @@ impl<A: HalApi> LifetimeTracker<A> {
#[must_use]
pub(crate) fn handle_mapping(
&mut self,
hub: &Hub<A>,
raw: &A::Device,
trackers: &Mutex<Tracker<A>>,
) -> Vec<super::BufferMapPendingClosure> {
@ -973,13 +894,11 @@ impl<A: HalApi> LifetimeTracker<A> {
let buffer_id = buffer.info.id();
let is_removed = {
let mut trackers = trackers.lock();
let mut count = 1;
count += hub.buffers.contains(buffer_id) as usize;
trackers.buffers.remove_abandoned(buffer_id, count)
trackers.buffers.remove_abandoned(buffer_id)
};
if is_removed {
*buffer.map_state.lock() = resource::BufferMapState::Idle;
log::info!("Buffer ready to map {:?} is not tracked anymore", buffer_id);
log::trace!("Buffer ready to map {:?} is not tracked anymore", buffer_id);
self.free_resources.insert(buffer_id, buffer.clone());
} else {
let mapping = match std::mem::replace(

View File

@ -6,7 +6,7 @@ use crate::{
identity::{GlobalIdentityHandlerFactory, Input},
resource::{Buffer, BufferAccessResult},
resource::{BufferAccessError, BufferMapOperation},
Label, DOWNLEVEL_ERROR_MESSAGE,
resource_log, Label, DOWNLEVEL_ERROR_MESSAGE,
};
use arrayvec::ArrayVec;
@ -273,19 +273,15 @@ impl DeviceLostClosure {
}
}
#[allow(trivial_casts)]
pub(crate) fn call(self, reason: DeviceLostReason, message: String) {
match self.inner {
DeviceLostClosureInner::Rust { callback } => callback(reason, message),
// SAFETY: the contract of the call to from_c says that this unsafe is sound.
DeviceLostClosureInner::C { inner } => unsafe {
// We need to pass message as a c_char typed pointer. To avoid trivial
// conversion warnings on some platforms, we use the allow lint.
(inner.callback)(
inner.user_data,
reason as u8,
message.as_ptr() as *const c_char,
)
// Ensure message is structured as a null-terminated C string. It only
// needs to live as long as the callback invocation.
let message = std::ffi::CString::new(message).unwrap();
(inner.callback)(inner.user_data, reason as u8, message.as_ptr())
},
}
}
@ -376,7 +372,10 @@ impl<A: HalApi> CommandAllocator<A> {
}
fn dispose(self, device: &A::Device) {
log::info!("Destroying {} command encoders", self.free_encoders.len());
resource_log!(
"CommandAllocator::dispose encoders {}",
self.free_encoders.len()
);
for cmd_encoder in self.free_encoders {
unsafe {
device.destroy_command_encoder(cmd_encoder);

View File

@ -1,6 +1,7 @@
#[cfg(feature = "trace")]
use crate::device::trace::Action;
use crate::{
api_log,
command::{
extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
ClearError, CommandBuffer, CopySide, ImageCopyTexture, TransferError,
@ -18,7 +19,7 @@ use crate::{
Buffer, BufferAccessError, BufferMapState, Resource, ResourceInfo, ResourceType,
StagingBuffer, Texture, TextureInner,
},
track, FastHashMap, SubmissionIndex,
resource_log, track, FastHashMap, SubmissionIndex,
};
use hal::{CommandEncoder as _, Device as _, Queue as _};
@ -385,6 +386,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
data: &[u8],
) -> Result<(), QueueWriteError> {
profiling::scope!("Queue::write_buffer");
api_log!("Queue::write_buffer {buffer_id:?} {}bytes", data.len());
let hub = A::hub(self);
@ -466,7 +468,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let fid = hub.staging_buffers.prepare::<G>(id_in);
let (id, _) = fid.assign(staging_buffer);
log::info!("Created StagingBuffer {:?}", id);
resource_log!("Queue::create_staging_buffer {id:?}");
Ok((id, staging_buffer_ptr))
}
@ -649,6 +651,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size: &wgt::Extent3d,
) -> Result<(), QueueWriteError> {
profiling::scope!("Queue::write_texture");
api_log!("Queue::write_texture {:?} {size:?}", destination.texture);
let hub = A::hub(self);
@ -1115,7 +1118,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
command_buffer_ids: &[id::CommandBufferId],
) -> Result<WrappedSubmissionIndex, QueueSubmitError> {
profiling::scope!("Queue::submit");
log::trace!("Queue::submit {queue_id:?}");
api_log!("Queue::submit {queue_id:?}");
let (submit_index, callbacks) = {
let hub = A::hub(self);
@ -1484,7 +1487,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// This will schedule destruction of all resources that are no longer needed
// by the user but used in the command stream, among other things.
let (closures, _) = match device.maintain(hub, fence, wgt::Maintain::Poll) {
let (closures, _) = match device.maintain(fence, wgt::Maintain::Poll) {
Ok(closures) => closures,
Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
@ -1524,7 +1527,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
queue_id: QueueId,
closure: SubmittedWorkDoneClosure,
) -> Result<(), InvalidQueue> {
log::trace!("Queue::on_submitted_work_done {queue_id:?}");
api_log!("Queue::on_submitted_work_done {queue_id:?}");
//TODO: flush pending writes
let hub = A::hub(self);

View File

@ -25,6 +25,7 @@ use crate::{
self, Buffer, QuerySet, Resource, ResourceType, Sampler, Texture, TextureView,
TextureViewNotRenderableReason,
},
resource_log,
storage::Storage,
track::{BindGroupStates, TextureSelector, Tracker},
validation::{self, check_buffer_usage, check_texture_usage},
@ -140,7 +141,7 @@ impl<A: HalApi> std::fmt::Debug for Device<A> {
impl<A: HalApi> Drop for Device<A> {
fn drop(&mut self) {
log::info!("Destroying Device {:?}", self.info.label());
resource_log!("Destroy raw Device {}", self.info.label());
let raw = self.raw.take().unwrap();
let pending_writes = self.pending_writes.lock().take().unwrap();
pending_writes.dispose(&raw);
@ -191,8 +192,6 @@ impl<A: HalApi> Device<A> {
raw_device: A::Device,
raw_queue: &A::Queue,
adapter: &Arc<Adapter<A>>,
alignments: hal::Alignments,
downlevel: wgt::DownlevelCapabilities,
desc: &DeviceDescriptor,
trace_path: Option<&std::path::Path>,
instance_flags: wgt::InstanceFlags,
@ -242,6 +241,9 @@ impl<A: HalApi> Device<A> {
}));
}
let alignments = adapter.raw.capabilities.alignments.clone();
let downlevel = adapter.raw.capabilities.downlevel.clone();
Ok(Self {
raw: Some(raw_device),
adapter: adapter.clone(),
@ -271,8 +273,8 @@ impl<A: HalApi> Device<A> {
}
})),
alignments,
limits: desc.limits.clone(),
features: desc.features,
limits: desc.required_limits.clone(),
features: desc.required_features,
downlevel,
instance_flags,
pending_writes: Mutex::new(Some(pending_writes)),
@ -306,7 +308,6 @@ impl<A: HalApi> Device<A> {
/// return it to our callers.)
pub(crate) fn maintain<'this>(
&'this self,
hub: &Hub<A>,
fence: &A::Fence,
maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
) -> Result<(UserClosures, bool), WaitIdleError> {
@ -327,7 +328,6 @@ impl<A: HalApi> Device<A> {
life_tracker.suspected_resources.extend(temp_suspected);
life_tracker.triage_suspected(
hub,
&self.trackers,
#[cfg(feature = "trace")]
self.trace.lock().as_mut(),
@ -367,7 +367,7 @@ impl<A: HalApi> Device<A> {
last_done_index,
self.command_allocator.lock().as_mut().unwrap(),
);
let mapping_closures = life_tracker.handle_mapping(hub, self.raw(), &self.trackers);
let mapping_closures = life_tracker.handle_mapping(self.raw(), &self.trackers);
//Cleaning up resources and released all unused suspected ones
life_tracker.cleanup();
@ -751,7 +751,8 @@ impl<A: HalApi> Device<A> {
if desc.format == *format {
continue;
}
if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
if !check_texture_view_format_compatible(desc.format, *format) {
return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
}
hal_view_formats.push(*format);
@ -804,23 +805,35 @@ impl<A: HalApi> Device<A> {
let mut clear_views = SmallVec::new();
for mip_level in 0..desc.mip_level_count {
for array_layer in 0..desc.size.depth_or_array_layers {
let desc = hal::TextureViewDescriptor {
label: clear_label,
format: desc.format,
dimension,
usage,
range: wgt::ImageSubresourceRange {
aspect: wgt::TextureAspect::All,
base_mip_level: mip_level,
mip_level_count: Some(1),
base_array_layer: array_layer,
array_layer_count: Some(1),
},
};
clear_views.push(Some(
unsafe { self.raw().create_texture_view(&raw_texture, &desc) }
.map_err(DeviceError::from)?,
));
macro_rules! push_clear_view {
($format:expr, $plane:expr) => {
let desc = hal::TextureViewDescriptor {
label: clear_label,
format: $format,
dimension,
usage,
range: wgt::ImageSubresourceRange {
aspect: wgt::TextureAspect::All,
base_mip_level: mip_level,
mip_level_count: Some(1),
base_array_layer: array_layer,
array_layer_count: Some(1),
},
plane: $plane,
};
clear_views.push(Some(
unsafe { self.raw().create_texture_view(&raw_texture, &desc) }
.map_err(DeviceError::from)?,
));
};
}
if desc.format == wgt::TextureFormat::NV12 {
push_clear_view!(wgt::TextureFormat::R8Unorm, Some(0));
push_clear_view!(wgt::TextureFormat::Rg8Unorm, Some(1));
} else {
push_clear_view!(desc.format, None);
}
}
}
resource::TextureClearMode::RenderPass {
@ -1015,6 +1028,8 @@ impl<A: HalApi> Device<A> {
});
};
validate_texture_view_plane(texture.desc.format, resolved_format, desc.plane)?;
// https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
let render_extent = 'b: loop {
if !texture
@ -1106,6 +1121,7 @@ impl<A: HalApi> Device<A> {
dimension: resolved_dimension,
usage,
range: resolved_range,
plane: desc.plane,
};
let raw = unsafe {
@ -2048,7 +2064,7 @@ impl<A: HalApi> Device<A> {
.views
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) = Self::texture_use_parameters(
let (pub_usage, internal_use) = self.texture_use_parameters(
binding,
decl,
view,
@ -2079,7 +2095,7 @@ impl<A: HalApi> Device<A> {
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) =
Self::texture_use_parameters(binding, decl, view,
self.texture_use_parameters(binding, decl, view,
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
Self::create_texture_binding(
view,
@ -2181,6 +2197,7 @@ impl<A: HalApi> Device<A> {
}
pub(crate) fn texture_use_parameters(
self: &Arc<Self>,
binding: u32,
decl: &wgt::BindGroupLayoutEntry,
view: &TextureView<A>,
@ -2211,7 +2228,7 @@ impl<A: HalApi> Device<A> {
let compat_sample_type = view
.desc
.format
.sample_type(Some(view.desc.range.aspect))
.sample_type(Some(view.desc.range.aspect), Some(self.features))
.unwrap();
match (sample_type, compat_sample_type) {
(Tst::Uint, Tst::Uint) |
@ -2389,7 +2406,7 @@ impl<A: HalApi> Device<A> {
.collect::<Vec<_>>();
let hal_desc = hal::PipelineLayoutDescriptor {
label: desc.label.to_hal(self.instance_flags),
flags: hal::PipelineLayoutFlags::BASE_VERTEX_INSTANCE,
flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE,
bind_group_layouts: &bgl_vec,
push_constant_ranges: desc.push_constant_ranges.as_ref(),
};
@ -3182,6 +3199,24 @@ impl<A: HalApi> Device<A> {
Ok(pipeline)
}
pub(crate) fn get_texture_format_features(
&self,
adapter: &Adapter<A>,
format: TextureFormat,
) -> wgt::TextureFormatFeatures {
// Variant of adapter.get_texture_format_features that takes device features into account
use wgt::TextureFormatFeatureFlags as tfsc;
let mut format_features = adapter.get_texture_format_features(format);
if (format == TextureFormat::R32Float
|| format == TextureFormat::Rg32Float
|| format == TextureFormat::Rgba32Float)
&& !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
{
format_features.flags.set(tfsc::FILTERABLE, false);
}
format_features
}
pub(crate) fn describe_format_features(
&self,
adapter: &Adapter<A>,
@ -3197,7 +3232,7 @@ impl<A: HalApi> Device<A> {
let downlevel = !self.downlevel.is_webgpu_compliant();
if using_device_features || downlevel {
Ok(adapter.get_texture_format_features(format))
Ok(self.get_texture_format_features(adapter, format))
} else {
Ok(format.guaranteed_format_features(self.features))
}
@ -3349,3 +3384,39 @@ impl<A: HalApi> Resource<DeviceId> for Device<A> {
&mut self.info
}
}
fn check_texture_view_format_compatible(
texture_format: TextureFormat,
view_format: TextureFormat,
) -> bool {
use TextureFormat::*;
match (texture_format, view_format) {
(NV12, R8Unorm | R8Uint | Rg8Unorm | Rg8Uint) => true,
_ => texture_format.remove_srgb_suffix() == view_format.remove_srgb_suffix(),
}
}
fn validate_texture_view_plane(
texture_format: TextureFormat,
view_format: TextureFormat,
plane: Option<u32>,
) -> Result<(), resource::CreateTextureViewError> {
use TextureFormat::*;
match (texture_format, view_format, plane) {
(NV12, R8Unorm | R8Uint, Some(0)) => Ok(()),
(NV12, Rg8Unorm | Rg8Uint, Some(1)) => Ok(()),
(NV12, _, _) => {
Err(resource::CreateTextureViewError::InvalidTextureViewPlane { plane, view_format })
}
(_, _, Some(_)) => Err(
resource::CreateTextureViewError::InvalidTextureViewPlaneOnNonplanarTexture {
plane,
texture_format,
},
),
_ => Ok(()),
}
}

View File

@ -154,7 +154,7 @@ pub enum Command {
ClearBuffer {
dst: id::BufferId,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
size: Option<wgt::BufferAddress>,
},
ClearTexture {
dst: id::TextureId,

View File

@ -9,6 +9,7 @@ use crate::{
identity::GlobalIdentityHandlerFactory,
instance::{Instance, Surface},
registry::{Registry, RegistryReport},
resource_log,
storage::Element,
};
@ -150,7 +151,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
impl<G: GlobalIdentityHandlerFactory> Drop for Global<G> {
fn drop(&mut self) {
profiling::scope!("Global::drop");
log::info!("Destroying Global");
resource_log!("Global::drop");
let mut surfaces_locked = self.surfaces.write();
// destroy hubs before the instance gets dropped

View File

@ -155,12 +155,17 @@ where
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let (index, epoch, backend) = self.unzip();
formatter
.debug_struct("Id")
.field("index", &index)
.field("epoch", &epoch)
.field("backend", &backend)
.finish()
let backend = match backend {
Backend::Vulkan => "vk",
Backend::Metal => "mtl",
Backend::Dx12 => "d3d12",
Backend::Dx11 => "d3d11",
Backend::Gl => "gl",
Backend::BrowserWebGpu => "webgpu",
Backend::Empty => "_",
};
write!(formatter, "Id({index},{epoch},{backend})")?;
Ok(())
}
}

View File

@ -2,6 +2,7 @@ use std::sync::Arc;
use crate::{
any_surface::AnySurface,
api_log,
device::{queue::Queue, resource::Device, DeviceDescriptor},
global::Global,
hal_api::HalApi,
@ -9,7 +10,7 @@ use crate::{
identity::{GlobalIdentityHandlerFactory, Input},
present::Presentation,
resource::{Resource, ResourceInfo, ResourceType},
LabelHelpers, DOWNLEVEL_WARNING_MESSAGE,
resource_log, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE,
};
use parking_lot::Mutex;
@ -306,15 +307,12 @@ impl<A: HalApi> Adapter<A> {
instance_flags: wgt::InstanceFlags,
trace_path: Option<&std::path::Path>,
) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
log::info!("Adapter::create_device");
api_log!("Adapter::create_device");
let caps = &self.raw.capabilities;
if let Ok(device) = Device::new(
hal_device.device,
&hal_device.queue,
self,
caps.alignments.clone(),
caps.downlevel.clone(),
desc,
trace_path,
instance_flags,
@ -336,9 +334,9 @@ impl<A: HalApi> Adapter<A> {
trace_path: Option<&std::path::Path>,
) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
// Verify all features were exposed by the adapter
if !self.raw.features.contains(desc.features) {
if !self.raw.features.contains(desc.required_features) {
return Err(RequestDeviceError::UnsupportedFeature(
desc.features - self.raw.features,
desc.required_features - self.raw.features,
));
}
@ -357,7 +355,7 @@ impl<A: HalApi> Adapter<A> {
// Verify feature preconditions
if desc
.features
.required_features
.contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
&& self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
{
@ -371,17 +369,20 @@ impl<A: HalApi> Adapter<A> {
//TODO
}
if let Some(failed) = check_limits(&desc.limits, &caps.limits).pop() {
if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
return Err(RequestDeviceError::LimitsExceeded(failed));
}
let open = unsafe { self.raw.adapter.open(desc.features, &desc.limits) }.map_err(
|err| match err {
hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
hal::DeviceError::ResourceCreationFailed => RequestDeviceError::Internal,
},
)?;
let open = unsafe {
self.raw
.adapter
.open(desc.required_features, &desc.required_limits)
}
.map_err(|err| match err {
hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
hal::DeviceError::ResourceCreationFailed => RequestDeviceError::Internal,
})?;
self.create_device_and_queue_from_hal(open, desc, instance_flags, trace_path)
}
@ -762,7 +763,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn surface_drop(&self, id: SurfaceId) {
profiling::scope!("Surface::drop");
log::info!("Surface::drop {id:?}");
api_log!("Surface::drop {id:?}");
fn unconfigure<G: GlobalIdentityHandlerFactory, A: HalApi>(
global: &Global<G>,
@ -828,7 +829,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
profiling::scope!("Instance::enumerate_adapters");
log::trace!("Instance::enumerate_adapters");
api_log!("Instance::enumerate_adapters");
let mut adapters = Vec::new();
@ -884,8 +885,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
desc: &RequestAdapterOptions,
inputs: AdapterInputs<Input<G, AdapterId>>,
) -> Result<AdapterId, RequestAdapterError> {
profiling::scope!("Instance::pick_adapter");
log::trace!("Instance::pick_adapter");
profiling::scope!("Instance::request_adapter");
api_log!("Instance::request_adapter");
fn gather<A: HalApi, I: Copy>(
_: A,
@ -1081,7 +1082,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Backend::Gl => fid.assign(Adapter::new(hal_adapter)),
_ => unreachable!(),
};
log::info!("Created Adapter {:?}", id);
resource_log!("Created Adapter {:?}", id);
id
}
@ -1159,7 +1160,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn adapter_drop<A: HalApi>(&self, adapter_id: AdapterId) {
profiling::scope!("Adapter::drop");
log::trace!("Adapter::drop {adapter_id:?}");
api_log!("Adapter::drop {adapter_id:?}");
let hub = A::hub(self);
let mut adapters_locked = hub.adapters.write();
@ -1185,7 +1186,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
queue_id_in: Input<G, QueueId>,
) -> (DeviceId, QueueId, Option<RequestDeviceError>) {
profiling::scope!("Adapter::request_device");
log::trace!("Adapter::request_device");
api_log!("Adapter::request_device");
let hub = A::hub(self);
let device_fid = hub.devices.prepare::<G>(device_id_in);
@ -1202,13 +1203,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Err(e) => break e,
};
let (device_id, _) = device_fid.assign(device);
log::info!("Created Device {:?}", device_id);
resource_log!("Created Device {:?}", device_id);
let device = hub.devices.get(device_id).unwrap();
queue.device = Some(device.clone());
let (queue_id, _) = queue_fid.assign(queue);
log::info!("Created Queue {:?}", queue_id);
resource_log!("Created Queue {:?}", queue_id);
device.queue_id.write().replace(queue_id);
@ -1254,13 +1255,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Err(e) => break e,
};
let (device_id, _) = devices_fid.assign(device);
log::info!("Created Device {:?}", device_id);
resource_log!("Created Device {:?}", device_id);
let device = hub.devices.get(device_id).unwrap();
queue.device = Some(device.clone());
let (queue_id, _) = queues_fid.assign(queue);
log::info!("Created Queue {:?}", queue_id);
resource_log!("Created Queue {:?}", queue_id);
device.queue_id.write().replace(queue_id);

View File

@ -279,6 +279,26 @@ macro_rules! gfx_select {
};
}
#[cfg(feature = "api_log_info")]
macro_rules! api_log {
($($arg:tt)+) => (log::info!($($arg)+))
}
#[cfg(not(feature = "api_log_info"))]
macro_rules! api_log {
($($arg:tt)+) => (log::trace!($($arg)+))
}
pub(crate) use api_log;
#[cfg(feature = "resource_log_info")]
macro_rules! resource_log {
($($arg:tt)+) => (log::info!($($arg)+))
}
#[cfg(not(feature = "resource_log_info"))]
macro_rules! resource_log {
($($arg:tt)+) => (log::trace!($($arg)+))
}
pub(crate) use resource_log;
/// Fast hash map used internally.
type FastHashMap<K, V> =
std::collections::HashMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;

View File

@ -7,7 +7,7 @@ use crate::{
hal_api::HalApi,
id::{ComputePipelineId, PipelineLayoutId, RenderPipelineId, ShaderModuleId},
resource::{Resource, ResourceInfo, ResourceType},
validation, Label,
resource_log, validation, Label,
};
use arrayvec::ArrayVec;
use std::{borrow::Cow, error::Error, fmt, marker::PhantomData, num::NonZeroU32, sync::Arc};
@ -54,8 +54,8 @@ pub struct ShaderModule<A: HalApi> {
impl<A: HalApi> Drop for ShaderModule<A> {
fn drop(&mut self) {
log::info!("Destroying ShaderModule {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw ShaderModule {}", self.info.label());
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *self.device.trace.lock() {
trace.add(trace::Action::DestroyShaderModule(self.info.id()));
@ -253,8 +253,8 @@ pub struct ComputePipeline<A: HalApi> {
impl<A: HalApi> Drop for ComputePipeline<A> {
fn drop(&mut self) {
log::info!("Destroying ComputePipeline {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw ComputePipeline {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_compute_pipeline(raw);
@ -494,8 +494,8 @@ pub struct RenderPipeline<A: HalApi> {
impl<A: HalApi> Drop for RenderPipeline<A> {
fn drop(&mut self) {
log::info!("Destroying RenderPipeline {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw RenderPipeline {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_render_pipeline(raw);

View File

@ -77,6 +77,12 @@ pub enum ConfigureSurfaceError {
PreviousOutputExists,
#[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
ZeroArea,
#[error("`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, height), maximum extent is {max_texture_dimension_2d}.")]
TooLarge {
width: u32,
height: u32,
max_texture_dimension_2d: u32,
},
#[error("Surface does not support the adapter's queue family")]
UnsupportedQueueFamily,
#[error("Requested format {requested:?} is not in list of supported formats: {available:?}")]
@ -196,6 +202,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let clear_view = unsafe {
hal::Device::create_texture_view(
@ -230,7 +237,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (id, resource) = fid.assign(texture);
log::info!("Created CURRENT Surface Texture {:?}", id);
log::debug!("Created CURRENT Surface Texture {:?}", id);
{
// register it in the device tracker as uninitialized

View File

@ -122,9 +122,6 @@ impl<I: id::TypedId, T: Resource<I>> Registry<I, T> {
data: &self.storage,
}
}
pub(crate) fn contains(&self, id: I) -> bool {
self.read().contains(id)
}
pub(crate) fn try_get(&self, id: I) -> Result<Option<Arc<T>>, InvalidId> {
self.read().try_get(id).map(|o| o.cloned())
}

View File

@ -13,7 +13,7 @@ use crate::{
},
identity::{GlobalIdentityHandlerFactory, IdentityManager},
init_tracker::{BufferInitTracker, TextureInitTracker},
resource,
resource, resource_log,
track::TextureSelector,
validation::MissingBufferUsageError,
Label, SubmissionIndex,
@ -79,7 +79,6 @@ impl<Id: TypedId> Drop for ResourceInfo<Id> {
if let Some(identity) = self.identity.as_ref() {
let id = self.id.as_ref().unwrap();
identity.free(*id);
log::info!("Freeing {:?}", self.label());
}
}
}
@ -418,8 +417,8 @@ pub struct Buffer<A: HalApi> {
impl<A: HalApi> Drop for Buffer<A> {
fn drop(&mut self) {
log::info!("Destroying Buffer {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw Buffer {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_buffer(raw);
@ -639,8 +638,8 @@ pub struct StagingBuffer<A: HalApi> {
impl<A: HalApi> Drop for StagingBuffer<A> {
fn drop(&mut self) {
log::info!("Destroying StagingBuffer {:?}", self.info.label());
if let Some(raw) = self.raw.lock().take() {
resource_log!("Destroy raw StagingBuffer {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_buffer(raw);
@ -722,7 +721,7 @@ pub struct Texture<A: HalApi> {
impl<A: HalApi> Drop for Texture<A> {
fn drop(&mut self) {
log::info!("Destroying Texture {:?}", self.info.label());
resource_log!("Destroy raw Texture {}", self.info.label());
use hal::Device;
let mut clear_mode = self.clear_mode.write();
let clear_mode = &mut *clear_mode;
@ -989,6 +988,8 @@ pub struct TextureViewDescriptor<'a> {
pub dimension: Option<wgt::TextureViewDimension>,
/// Range within the texture that is accessible via this view.
pub range: wgt::ImageSubresourceRange,
/// The plane of the texture view.
pub plane: Option<u32>,
}
#[derive(Debug)]
@ -1038,8 +1039,8 @@ pub struct TextureView<A: HalApi> {
impl<A: HalApi> Drop for TextureView<A> {
fn drop(&mut self) {
log::info!("Destroying TextureView {:?}", self.info.label());
if let Some(raw) = self.raw.take() {
resource_log!("Destroy raw TextureView {}", self.info.label());
unsafe {
use hal::Device;
self.device.raw().destroy_texture_view(raw);
@ -1099,6 +1100,16 @@ pub enum CreateTextureViewError {
texture: wgt::TextureFormat,
view: wgt::TextureFormat,
},
#[error("Invalid texture view plane `{plane:?}` with view format `{view_format:?}`")]
InvalidTextureViewPlane {
plane: Option<u32>,
view_format: wgt::TextureFormat,
},
#[error("Invalid texture view plane `{plane:?}` on non-planar texture `{texture_format:?}`")]
InvalidTextureViewPlaneOnNonplanarTexture {
plane: Option<u32>,
texture_format: wgt::TextureFormat,
},
}
#[derive(Clone, Debug, Error)]
@ -1160,7 +1171,7 @@ pub struct Sampler<A: HalApi> {
impl<A: HalApi> Drop for Sampler<A> {
fn drop(&mut self) {
log::info!("Destroying Sampler {:?}", self.info.label());
resource_log!("Destroy raw Sampler {}", self.info.label());
if let Some(raw) = self.raw.take() {
unsafe {
use hal::Device;
@ -1257,7 +1268,7 @@ pub struct QuerySet<A: HalApi> {
impl<A: HalApi> Drop for QuerySet<A> {
fn drop(&mut self) {
log::info!("Destroying QuerySet {:?}", self.info.label());
resource_log!("Destroy raw QuerySet {}", self.info.label());
if let Some(raw) = self.raw.take() {
unsafe {
use hal::Device;

View File

@ -16,7 +16,7 @@ pub(crate) enum Element<T> {
/// Like `Occupied`, but the resource has been marked as destroyed
/// and hasn't been dropped yet.
Destroyed(Arc<T>, Epoch),
Destroyed(Epoch),
/// Like `Occupied`, but an error occurred when creating the
/// resource.
@ -80,7 +80,7 @@ where
Some(&Element::Vacant) => false,
Some(
&Element::Occupied(_, storage_epoch)
| &Element::Destroyed(_, storage_epoch)
| &Element::Destroyed(storage_epoch)
| &Element::Error(storage_epoch, _),
) => storage_epoch == epoch,
None => false,
@ -145,7 +145,7 @@ where
}
match std::mem::replace(&mut self.map[index], element) {
Element::Vacant => {}
Element::Destroyed(_, storage_epoch) => {
Element::Destroyed(storage_epoch) => {
assert_ne!(
epoch,
storage_epoch,
@ -173,13 +173,13 @@ where
}
pub(crate) fn insert(&mut self, id: I, value: Arc<T>) {
log::info!("User is inserting {}{:?}", T::TYPE, id);
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: I, label: &str) {
log::info!("User is insering as error {}{:?}", T::TYPE, id);
log::trace!("User is insering as error {}{:?}", T::TYPE, id);
let (index, epoch, _) = id.unzip();
self.insert_impl(
index as usize,
@ -208,36 +208,35 @@ where
let slot = &mut self.map[index as usize];
// borrowck dance: we have to move the element out before we can replace it
// with another variant with the same value.
if let &mut Element::Occupied(..) = slot {
if let &mut Element::Occupied(_, e) = slot {
if let Element::Occupied(value, storage_epoch) =
std::mem::replace(slot, Element::Vacant)
std::mem::replace(slot, Element::Destroyed(e))
{
debug_assert_eq!(storage_epoch, epoch);
*slot = Element::Destroyed(value, storage_epoch);
return Ok(value);
}
}
if let Element::Destroyed(ref value, ..) = *slot {
Ok(value.clone())
} else {
Err(InvalidId)
}
Err(InvalidId)
}
pub(crate) fn force_replace(&mut self, id: I, value: T) {
log::info!("User is replacing {}{:?}", T::TYPE, id);
log::trace!("User is replacing {}{:?}", T::TYPE, id);
let (index, epoch, _) = id.unzip();
self.map[index as usize] = Element::Occupied(Arc::new(value), epoch);
}
pub(crate) fn remove(&mut self, id: I) -> Option<Arc<T>> {
log::info!("User is removing {}{:?}", T::TYPE, id);
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) | Element::Destroyed(value, storage_epoch) => {
Element::Occupied(value, storage_epoch) => {
assert_eq!(epoch, storage_epoch);
Some(value)
}
Element::Destroyed(storage_epoch) => {
assert_eq!(epoch, storage_epoch);
None
}
Element::Error(..) => None,
Element::Vacant => panic!("Cannot remove a vacant resource"),
}

View File

@ -293,7 +293,7 @@ pub(crate) struct BufferTracker<A: HalApi> {
}
impl<A: HalApi> ResourceTracker<BufferId, Buffer<A>> for BufferTracker<A> {
/// Removes the buffer `id` from this tracker if it is otherwise unused.
/// Try to remove the buffer `id` from this tracker if it is otherwise unused.
///
/// A buffer is 'otherwise unused' when the only references to it are:
///
@ -308,13 +308,12 @@ impl<A: HalApi> ResourceTracker<BufferId, Buffer<A>> for BufferTracker<A> {
/// `triage_suspected` will remove 3), leaving 1) as the sole
/// remaining reference.
///
/// Return `true` if this tracker contained the buffer `id`. This
/// implies that we removed it.
/// Returns true if the resource was removed or if not existing in metadata.
///
/// [`Device::trackers`]: crate::device::Device
/// [`self.metadata`]: BufferTracker::metadata
/// [`Hub::buffers`]: crate::hub::Hub::buffers
fn remove_abandoned(&mut self, id: BufferId, external_count: usize) -> bool {
fn remove_abandoned(&mut self, id: BufferId) -> bool {
let index = id.unzip().0 as usize;
if index > self.metadata.size() {
@ -326,24 +325,23 @@ impl<A: HalApi> ResourceTracker<BufferId, Buffer<A>> for BufferTracker<A> {
unsafe {
if self.metadata.contains_unchecked(index) {
let existing_ref_count = self.metadata.get_ref_count_unchecked(index);
//2 ref count if only in Device Tracker and suspected resource itself and already released from user
//so not appearing in Registry
let min_ref_count = 1 + external_count;
if existing_ref_count <= min_ref_count {
//RefCount 2 means that resource is hold just by DeviceTracker and this suspected resource itself
//so it's already been released from user and so it's not inside Registry\Storage
if existing_ref_count <= 2 {
self.metadata.remove(index);
log::info!("Buffer {:?} is not tracked anymore", id,);
log::trace!("Buffer {:?} is not tracked anymore", id,);
return true;
} else {
log::info!(
log::trace!(
"Buffer {:?} is still referenced from {}",
id,
existing_ref_count
);
return false;
}
}
}
false
true
}
}

View File

@ -122,14 +122,7 @@ impl<A: HalApi, I: TypedId, T: Resource<I>> ResourceMetadata<A, I, T> {
/// existing tables. See `tracker_assert_in_bounds`.
#[inline(always)]
pub(super) unsafe fn get_ref_count_unchecked(&self, index: usize) -> usize {
unsafe {
Arc::strong_count(
self.resources
.get_unchecked(index)
.as_ref()
.unwrap_unchecked(),
)
}
unsafe { Arc::strong_count(self.get_resource_unchecked(index)) }
}
/// Returns an iterator over the resources owned by `self`.

View File

@ -489,7 +489,7 @@ where
Id: TypedId,
R: resource::Resource<Id>,
{
fn remove_abandoned(&mut self, id: Id, external_count: usize) -> bool;
fn remove_abandoned(&mut self, id: Id) -> bool;
}
/// A full double sided tracker used by CommandBuffers and the Device.

View File

@ -9,7 +9,8 @@ use std::{marker::PhantomData, sync::Arc};
use parking_lot::Mutex;
use crate::{
hal_api::HalApi, id::TypedId, resource::Resource, storage::Storage, track::ResourceMetadata,
hal_api::HalApi, id::TypedId, resource::Resource, resource_log, storage::Storage,
track::ResourceMetadata,
};
use super::ResourceTracker;
@ -77,44 +78,45 @@ pub(crate) struct StatelessTracker<A: HalApi, Id: TypedId, T: Resource<Id>> {
impl<A: HalApi, Id: TypedId, T: Resource<Id>> ResourceTracker<Id, T>
for StatelessTracker<A, Id, T>
{
/// Removes the given resource from the tracker iff we have the last reference to the
/// Try to remove the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
/// Returns true if the resource was removed.
/// Returns true if the resource was removed or if not existing in metadata.
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
fn remove_abandoned(&mut self, id: Id, external_count: usize) -> bool {
fn remove_abandoned(&mut self, id: Id) -> bool {
let index = id.unzip().0 as usize;
if index > self.metadata.size() {
return false;
}
resource_log!("StatelessTracker::remove_abandoned {id:?}");
self.tracker_assert_in_bounds(index);
unsafe {
if self.metadata.contains_unchecked(index) {
let existing_ref_count = self.metadata.get_ref_count_unchecked(index);
//2 ref count if only in Device Tracker and suspected resource itself and already released from user
//so not appearing in Registry
let min_ref_count = 1 + external_count;
if existing_ref_count <= min_ref_count {
//RefCount 2 means that resource is hold just by DeviceTracker and this suspected resource itself
//so it's already been released from user and so it's not inside Registry\Storage
if existing_ref_count <= 2 {
self.metadata.remove(index);
log::info!("{} {:?} is not tracked anymore", T::TYPE, id,);
log::trace!("{} {:?} is not tracked anymore", T::TYPE, id,);
return true;
} else {
log::info!(
log::trace!(
"{} {:?} is still referenced from {}",
T::TYPE,
id,
existing_ref_count
);
return false;
}
}
}
false
true
}
}

View File

@ -394,14 +394,14 @@ pub(crate) struct TextureTracker<A: HalApi> {
}
impl<A: HalApi> ResourceTracker<TextureId, Texture<A>> for TextureTracker<A> {
/// Removes the given resource from the tracker iff we have the last reference to the
/// Try to remove the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
/// Returns true if the resource was removed.
/// Returns true if the resource was removed or if not existing in metadata.
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
fn remove_abandoned(&mut self, id: TextureId, external_count: usize) -> bool {
fn remove_abandoned(&mut self, id: TextureId) -> bool {
let index = id.unzip().0 as usize;
if index > self.metadata.size() {
@ -413,26 +413,25 @@ impl<A: HalApi> ResourceTracker<TextureId, Texture<A>> for TextureTracker<A> {
unsafe {
if self.metadata.contains_unchecked(index) {
let existing_ref_count = self.metadata.get_ref_count_unchecked(index);
//2 ref count if only in Device Tracker and suspected resource itself and already released from user
//so not appearing in Registry
let min_ref_count = 1 + external_count;
if existing_ref_count <= min_ref_count {
//RefCount 2 means that resource is hold just by DeviceTracker and this suspected resource itself
//so it's already been released from user and so it's not inside Registry\Storage
if existing_ref_count <= 2 {
self.start_set.complex.remove(&index);
self.end_set.complex.remove(&index);
self.metadata.remove(index);
log::info!("Texture {:?} is not tracked anymore", id,);
log::trace!("Texture {:?} is not tracked anymore", id,);
return true;
} else {
log::info!(
log::trace!(
"Texture {:?} is still referenced from {}",
id,
existing_ref_count
);
return false;
}
}
}
false
true
}
}

View File

@ -560,7 +560,9 @@ impl Resource {
}
naga::ScalarKind::Sint => wgt::TextureSampleType::Sint,
naga::ScalarKind::Uint => wgt::TextureSampleType::Uint,
naga::ScalarKind::Bool => unreachable!(),
naga::ScalarKind::AbstractInt
| naga::ScalarKind::AbstractFloat
| naga::ScalarKind::Bool => unreachable!(),
},
view_dimension,
multisampled: multi,
@ -690,6 +692,7 @@ impl NumericType {
| Tf::Depth24PlusStencil8 => {
panic!("Unexpected depth format")
}
Tf::NV12 => panic!("Unexpected nv12 format"),
Tf::Rgb9e5Ufloat => (NumericDimension::Vector(Vs::Tri), Scalar::F32),
Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb

File diff suppressed because one or more lines are too long

View File

@ -88,7 +88,7 @@ path = "../naga"
features = ["wgsl-in"]
[dev-dependencies.winit]
version = "0.29.2"
version = "0.29.4"
features = ["android-native-activity"]
[features]
@ -146,11 +146,11 @@ vulkan = [
windows_rs = ["gpu-allocator"]
[target."cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))".dependencies]
js-sys = "0.3.65"
js-sys = "0.3.66"
wasm-bindgen = "0.2.87"
[target."cfg(all(target_arch = \"wasm32\", not(target_os = \"emscripten\")))".dependencies.web-sys]
version = "0.3.64"
version = "0.3.66"
features = [
"Window",
"HtmlCanvasElement",

View File

@ -15,7 +15,7 @@ such as running out-of-memory, or losing the device.
For the counter-example, there is no error for mapping a buffer that's not mappable.
As the buffer creator, the user should already know if they can map it.
The API accept iterators in order to avoid forcing the user to store data in particular containers. The implementation doesn't guarantee that any of the iterators are drained, unless stated otherwise by the function documentation.
The API accepts iterators in order to avoid forcing the user to store data in particular containers. The implementation doesn't guarantee that any of the iterators are drained, unless stated otherwise by the function documentation.
For this reason, we recommend that iterators don't do any mutating work.
# Debugging

View File

@ -422,6 +422,7 @@ impl<A: hal::Api> Example<A> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::RESOURCE,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let texture_view = unsafe { device.create_texture_view(&texture, &view_desc).unwrap() };
@ -658,6 +659,7 @@ impl<A: hal::Api> Example<A> {
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
};
let surface_tex_view = unsafe {
self.device

View File

@ -142,6 +142,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
dimension: wgt::TextureViewDimension::D2,
usage: hal::TextureUses::COLOR_TARGET,
range: wgt::ImageSubresourceRange::default(),
plane: None,
},
)
.unwrap()

View File

@ -62,6 +62,7 @@ pub fn map_texture_format_failable(format: wgt::TextureFormat) -> Option<dxgifor
Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,
Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT,
Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
Tf::NV12 => DXGI_FORMAT_NV12,
Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM,
Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB,
Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM,

View File

@ -98,7 +98,7 @@ unsafe extern "system" fn output_debug_string_handler(
if cfg!(debug_assertions) && level == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.set();
crate::VALIDATION_CANARY.add(message.to_string());
}
excpt::EXCEPTION_CONTINUE_EXECUTION

View File

@ -155,6 +155,10 @@ impl super::Adapter {
// bgra8unorm-storage is never supported on dx11 according to:
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-11-0-feature-level-hardware#dxgi_format_b8g8r8a8_unormfcs-87
// float32-filterable should always be available on dx11
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-11-0-feature-level-hardware#dxgi_format_r32g32b32a32_floatfcs-2
features.set(wgt::Features::FLOAT32_FILTERABLE, true);
//
// Fill out limits and alignments
//

View File

@ -187,9 +187,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
todo!()
@ -197,10 +197,10 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
todo!()

View File

@ -249,7 +249,8 @@ impl super::Adapter {
| wgt::Features::PUSH_CONSTANTS
| wgt::Features::SHADER_PRIMITIVE_INDEX
| wgt::Features::RG11B10UFLOAT_RENDERABLE
| wgt::Features::DUAL_SOURCE_BLENDING;
| wgt::Features::DUAL_SOURCE_BLENDING
| wgt::Features::TEXTURE_FORMAT_NV12;
//TODO: in order to expose this, we need to run a compute shader
// that extract the necessary statistics out of the D3D12 result.
@ -293,11 +294,19 @@ impl super::Adapter {
bgra8unorm_storage_supported,
);
// float32-filterable should always be available on d3d12
features.set(wgt::Features::FLOAT32_FILTERABLE, true);
// TODO: Determine if IPresentationManager is supported
let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();
let base = wgt::Limits::default();
let mut downlevel = wgt::DownlevelCapabilities::default();
// https://github.com/gfx-rs/wgpu/issues/2471
downlevel.flags -=
wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
Some(crate::ExposedAdapter {
adapter: super::Adapter {
raw: adapter,
@ -392,7 +401,7 @@ impl super::Adapter {
)
.unwrap(),
},
downlevel: wgt::DownlevelCapabilities::default(),
downlevel,
},
})
}
@ -620,16 +629,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
// we currently use a flip effect which supports 2..=16 buffers
swap_chain_sizes: 2..=16,
current_extent,
// TODO: figure out the exact bounds
extents: wgt::Extent3d {
width: 16,
height: 16,
depth_or_array_layers: 1,
}..=wgt::Extent3d {
width: 4096,
height: 4096,
depth_or_array_layers: 1,
},
usage: crate::TextureUses::COLOR_TARGET
| crate::TextureUses::COPY_SRC
| crate::TextureUses::COPY_DST,

View File

@ -85,7 +85,7 @@ impl super::CommandEncoder {
self.pass.clear();
}
unsafe fn prepare_draw(&mut self, base_vertex: i32, base_instance: u32) {
unsafe fn prepare_draw(&mut self, first_vertex: i32, first_instance: u32) {
while self.pass.dirty_vertex_buffers != 0 {
let list = self.list.as_ref().unwrap();
let index = self.pass.dirty_vertex_buffers.trailing_zeros();
@ -101,18 +101,18 @@ impl super::CommandEncoder {
if let Some(root_index) = self.pass.layout.special_constants_root_index {
let needs_update = match self.pass.root_elements[root_index as usize] {
super::RootElement::SpecialConstantBuffer {
base_vertex: other_vertex,
base_instance: other_instance,
first_vertex: other_vertex,
first_instance: other_instance,
other: _,
} => base_vertex != other_vertex || base_instance != other_instance,
} => first_vertex != other_vertex || first_instance != other_instance,
_ => true,
};
if needs_update {
self.pass.dirty_root_elements |= 1 << root_index;
self.pass.root_elements[root_index as usize] =
super::RootElement::SpecialConstantBuffer {
base_vertex,
base_instance,
first_vertex,
first_instance,
other: 0,
};
}
@ -124,18 +124,18 @@ impl super::CommandEncoder {
if let Some(root_index) = self.pass.layout.special_constants_root_index {
let needs_update = match self.pass.root_elements[root_index as usize] {
super::RootElement::SpecialConstantBuffer {
base_vertex,
base_instance,
first_vertex,
first_instance,
other,
} => [base_vertex as u32, base_instance, other] != count,
} => [first_vertex as u32, first_instance, other] != count,
_ => true,
};
if needs_update {
self.pass.dirty_root_elements |= 1 << root_index;
self.pass.root_elements[root_index as usize] =
super::RootElement::SpecialConstantBuffer {
base_vertex: count[0] as i32,
base_instance: count[1],
first_vertex: count[0] as i32,
first_instance: count[1],
other: count[2],
};
}
@ -168,17 +168,17 @@ impl super::CommandEncoder {
}
}
super::RootElement::SpecialConstantBuffer {
base_vertex,
base_instance,
first_vertex,
first_instance,
other,
} => match self.pass.kind {
Pk::Render => {
list.set_graphics_root_constant(index, base_vertex as u32, 0);
list.set_graphics_root_constant(index, base_instance, 1);
list.set_graphics_root_constant(index, first_vertex as u32, 0);
list.set_graphics_root_constant(index, first_instance, 1);
}
Pk::Compute => {
list.set_compute_root_constant(index, base_vertex as u32, 0);
list.set_compute_root_constant(index, base_instance, 1);
list.set_compute_root_constant(index, first_vertex as u32, 0);
list.set_compute_root_constant(index, first_instance, 1);
list.set_compute_root_constant(index, other, 2);
}
Pk::Transfer => (),
@ -220,8 +220,8 @@ impl super::CommandEncoder {
if let Some(root_index) = layout.special_constants_root_index {
self.pass.root_elements[root_index as usize] =
super::RootElement::SpecialConstantBuffer {
base_vertex: 0,
base_instance: 0,
first_vertex: 0,
first_instance: 0,
other: 0,
};
}
@ -1031,34 +1031,34 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
unsafe { self.prepare_draw(start_vertex as i32, start_instance) };
unsafe { self.prepare_draw(first_vertex as i32, first_instance) };
self.list.as_ref().unwrap().draw(
vertex_count,
instance_count,
start_vertex,
start_instance,
first_vertex,
first_instance,
);
}
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
unsafe { self.prepare_draw(base_vertex, start_instance) };
unsafe { self.prepare_draw(base_vertex, first_instance) };
self.list.as_ref().unwrap().draw_indexed(
index_count,
instance_count,
start_index,
first_index,
base_vertex,
start_instance,
first_instance,
);
}
unsafe fn draw_indirect(

View File

@ -467,7 +467,11 @@ impl crate::Device<super::Api> for super::Device {
aspects: view_desc.aspects,
target_base: (
texture.resource.clone(),
texture.calc_subresource(desc.range.base_mip_level, desc.range.base_array_layer, 0),
texture.calc_subresource(
desc.range.base_mip_level,
desc.range.base_array_layer,
desc.plane.unwrap_or(0),
),
),
handle_srv: if desc.usage.intersects(crate::TextureUses::RESOURCE) {
let raw_desc = unsafe { view_desc.to_srv() };
@ -983,7 +987,7 @@ impl crate::Device<super::Api> for super::Device {
debug_assert_eq!(ranges.len(), total_non_dynamic_entries);
let (special_constants_root_index, special_constants_binding) = if desc.flags.intersects(
crate::PipelineLayoutFlags::BASE_VERTEX_INSTANCE
crate::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
| crate::PipelineLayoutFlags::NUM_WORK_GROUPS,
) {
let parameter_index = parameters.len();
@ -991,7 +995,7 @@ impl crate::Device<super::Api> for super::Device {
parameters.push(d3d12::RootParameter::constants(
d3d12::ShaderVisibility::All, // really needed for VS and CS only
native_binding(&bind_cbv),
3, // 0 = base vertex, 1 = base instance, 2 = other
3, // 0 = first_vertex, 1 = first_instance, 2 = other
));
let binding = bind_cbv.clone();
bind_cbv.register += 1;

View File

@ -291,8 +291,8 @@ enum RootElement {
Empty,
Constant,
SpecialConstantBuffer {
base_vertex: i32,
base_instance: u32,
first_vertex: i32,
first_instance: u32,
other: u32,
},
/// Descriptor table.

View File

@ -14,6 +14,7 @@ pub(super) struct ViewDescriptor {
array_layer_count: u32,
mip_level_base: u32,
mip_level_count: u32,
plane: u32,
}
impl crate::TextureViewDescriptor<'_> {
@ -30,6 +31,7 @@ impl crate::TextureViewDescriptor<'_> {
mip_level_count: self.range.mip_level_count.unwrap_or(!0),
array_layer_base: self.range.base_array_layer,
array_layer_count: self.range.array_layer_count.unwrap_or(!0),
plane: self.plane.unwrap_or(0),
}
}
}
@ -79,7 +81,7 @@ impl ViewDescriptor {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_SRV {
MostDetailedMip: self.mip_level_base,
MipLevels: self.mip_level_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
ResourceMinLODClamp: 0.0,
}
}
@ -103,7 +105,7 @@ impl ViewDescriptor {
MipLevels: self.mip_level_count,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
ResourceMinLODClamp: 0.0,
}
}
@ -179,7 +181,7 @@ impl ViewDescriptor {
unsafe {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_UAV {
MipSlice: self.mip_level_base,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -190,7 +192,7 @@ impl ViewDescriptor {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -250,7 +252,7 @@ impl ViewDescriptor {
unsafe {
*desc.u.Texture2D_mut() = d3d12_ty::D3D12_TEX2D_RTV {
MipSlice: self.mip_level_base,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}
@ -272,7 +274,7 @@ impl ViewDescriptor {
MipSlice: self.mip_level_base,
FirstArraySlice: self.array_layer_base,
ArraySize: self.array_layer_count,
PlaneSlice: 0,
PlaneSlice: self.plane,
}
}
}

View File

@ -353,18 +353,18 @@ impl crate::CommandEncoder<Api> for Encoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
}
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
}

View File

@ -379,7 +379,8 @@ impl super::Adapter {
let mut downlevel_flags = wgt::DownlevelFlags::empty()
| wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
| wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
| wgt::DownlevelFlags::COMPARISON_SAMPLERS;
| wgt::DownlevelFlags::COMPARISON_SAMPLERS
| wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
downlevel_flags.set(
wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
@ -389,8 +390,6 @@ impl super::Adapter {
wgt::DownlevelFlags::INDIRECT_EXECUTION,
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_multi_draw_indirect"),
);
//TODO: we can actually support positive `base_vertex` in the same way
// as we emulate the `start_instance`. But we can't deal with negatives...
downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
downlevel_flags.set(
wgt::DownlevelFlags::INDEPENDENT_BLEND,
@ -550,6 +549,13 @@ impl super::Adapter {
);
}
features.set(
wgt::Features::FLOAT32_FILTERABLE,
extensions.contains("GL_ARB_color_buffer_float")
|| extensions.contains("GL_EXT_color_buffer_float")
|| extensions.contains("OES_texture_float_linear"),
);
// We *might* be able to emulate bgra8unorm-storage but currently don't attempt to.
let mut private_caps = super::PrivateCapabilities::empty();
@ -595,15 +601,8 @@ impl super::Adapter {
super::PrivateCapabilities::COLOR_BUFFER_FLOAT,
color_buffer_float,
);
private_caps.set(
super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR,
if full_ver.is_some() {
color_buffer_float
} else {
extensions.contains("OES_texture_float_linear")
},
);
private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);
private_caps.set(super::PrivateCapabilities::QUERY_64BIT, full_ver.is_some());
private_caps.set(
super::PrivateCapabilities::TEXTURE_STORAGE,
supported((3, 0), (4, 2)),
@ -616,6 +615,21 @@ impl super::Adapter {
super::PrivateCapabilities::INVALIDATE_FRAMEBUFFER,
supported((3, 0), (4, 3)),
);
if let Some(full_ver) = full_ver {
let supported =
full_ver >= (4, 2) && extensions.contains("GL_ARB_shader_draw_parameters");
private_caps.set(
super::PrivateCapabilities::FULLY_FEATURED_INSTANCING,
supported,
);
// Desktop 4.2 and greater specify the first instance parameter.
//
// For all other versions, the behavior is undefined.
//
// We only support indirect first instance when we also have ARB_shader_draw_parameters as
// that's the only way to get gl_InstanceID to work correctly.
features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported);
}
let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
@ -772,7 +786,7 @@ impl super::Adapter {
// Drop the GL guard so we can move the context into AdapterShared
// ( on Wasm the gl handle is just a ref so we tell clippy to allow
// dropping the ref )
#[cfg_attr(target_arch = "wasm32", allow(clippy::drop_ref))]
#[cfg_attr(target_arch = "wasm32", allow(dropping_references))]
drop(gl);
Some(crate::ExposedAdapter {
@ -783,7 +797,6 @@ impl super::Adapter {
workarounds,
features,
shading_language_version,
max_texture_size,
next_shader_id: Default::default(),
program_cache: Default::default(),
es: es_ver.is_some(),
@ -1008,8 +1021,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tfc::MULTISAMPLE_RESOLVE,
);
let texture_float_linear =
private_caps_fn(super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, filterable);
let texture_float_linear = feature_fn(wgt::Features::FLOAT32_FILTERABLE, filterable);
match format {
Tf::R8Unorm => filterable_renderable,
@ -1058,6 +1070,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tf::Depth32FloatStencil8
| Tf::Depth24Plus
| Tf::Depth24PlusStencil8 => depth,
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => filterable,
Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb
@ -1129,15 +1142,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
swap_chain_sizes: 2..=2,
current_extent: None,
extents: wgt::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
}..=wgt::Extent3d {
width: self.shared.max_texture_size,
height: self.shared.max_texture_size,
depth_or_array_layers: 1,
},
usage: crate::TextureUses::COLOR_TARGET,
})
} else {

View File

@ -29,6 +29,7 @@ pub(super) struct State {
instance_vbuf_mask: usize,
dirty_vbuf_mask: usize,
active_first_instance: u32,
first_instance_location: Option<glow::UniformLocation>,
push_constant_descs: ArrayVec<super::PushConstantDesc, { super::MAX_PUSH_CONSTANT_COMMANDS }>,
// The current state of the push constant data block.
current_push_constant_data: [u32; super::MAX_PUSH_CONSTANTS],
@ -57,6 +58,7 @@ impl Default for State {
instance_vbuf_mask: Default::default(),
dirty_vbuf_mask: Default::default(),
active_first_instance: Default::default(),
first_instance_location: Default::default(),
push_constant_descs: Default::default(),
current_push_constant_data: [0; super::MAX_PUSH_CONSTANTS],
end_of_pass_timestamp: Default::default(),
@ -193,19 +195,32 @@ impl super::CommandEncoder {
}
fn prepare_draw(&mut self, first_instance: u32) {
if first_instance != self.state.active_first_instance {
// If we support fully featured instancing, we want to bind everything as normal
// and let the draw call sort it out.
let emulated_first_instance_value = if self
.private_caps
.contains(super::PrivateCapabilities::FULLY_FEATURED_INSTANCING)
{
0
} else {
first_instance
};
if emulated_first_instance_value != self.state.active_first_instance {
// rebind all per-instance buffers on first-instance change
self.state.dirty_vbuf_mask |= self.state.instance_vbuf_mask;
self.state.active_first_instance = first_instance;
self.state.active_first_instance = emulated_first_instance_value;
}
if self.state.dirty_vbuf_mask != 0 {
self.rebind_vertex_data(first_instance);
self.rebind_vertex_data(emulated_first_instance_value);
}
}
#[allow(clippy::clone_on_copy)] // False positive when cloning glow::UniformLocation
fn set_pipeline_inner(&mut self, inner: &super::PipelineInner) {
self.cmd_buffer.commands.push(C::SetProgram(inner.program));
self.state.first_instance_location = inner.first_instance_location.clone();
self.state.push_constant_descs = inner.push_constant_descs.clone();
// rebind textures, if needed
@ -590,7 +605,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
if !cat.ops.contains(crate::AttachmentOps::LOAD) {
let c = &cat.clear_value;
self.cmd_buffer.commands.push(
match cat.target.view.format.sample_type(None).unwrap() {
match cat.target.view.format.sample_type(None, None).unwrap() {
wgt::TextureSampleType::Float { .. } => C::ClearColorF {
draw_buffer: i as u32,
color: [c.r as f32, c.g as f32, c.b as f32, c.a as f32],
@ -1002,40 +1017,46 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
self.prepare_draw(start_instance);
self.prepare_draw(first_instance);
#[allow(clippy::clone_on_copy)] // False positive when cloning glow::UniformLocation
self.cmd_buffer.commands.push(C::Draw {
topology: self.state.topology,
start_vertex,
first_vertex,
vertex_count,
first_instance,
instance_count,
first_instance_location: self.state.first_instance_location.clone(),
});
}
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
self.prepare_draw(start_instance);
self.prepare_draw(first_instance);
let (index_size, index_type) = match self.state.index_format {
wgt::IndexFormat::Uint16 => (2, glow::UNSIGNED_SHORT),
wgt::IndexFormat::Uint32 => (4, glow::UNSIGNED_INT),
};
let index_offset = self.state.index_offset + index_size * start_index as wgt::BufferAddress;
let index_offset = self.state.index_offset + index_size * first_index as wgt::BufferAddress;
#[allow(clippy::clone_on_copy)] // False positive when cloning glow::UniformLocation
self.cmd_buffer.commands.push(C::DrawIndexed {
topology: self.state.topology,
index_type,
index_offset,
index_count,
base_vertex,
first_instance,
instance_count,
first_instance_location: self.state.first_instance_location.clone(),
});
}
unsafe fn draw_indirect(
@ -1048,10 +1069,12 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
for draw in 0..draw_count as wgt::BufferAddress {
let indirect_offset =
offset + draw * mem::size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
#[allow(clippy::clone_on_copy)] // False positive when cloning glow::UniformLocation
self.cmd_buffer.commands.push(C::DrawIndirect {
topology: self.state.topology,
indirect_buf: buffer.raw.unwrap(),
indirect_offset,
first_instance_location: self.state.first_instance_location.clone(),
});
}
}
@ -1069,11 +1092,13 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
for draw in 0..draw_count as wgt::BufferAddress {
let indirect_offset = offset
+ draw * mem::size_of::<wgt::DrawIndexedIndirectArgs>() as wgt::BufferAddress;
#[allow(clippy::clone_on_copy)] // False positive when cloning glow::UniformLocation
self.cmd_buffer.commands.push(C::DrawIndexedIndirect {
topology: self.state.topology,
index_type,
indirect_buf: buffer.raw.unwrap(),
indirect_offset,
first_instance_location: self.state.first_instance_location.clone(),
});
}
}

View File

@ -87,6 +87,7 @@ impl super::AdapterShared {
glow::DEPTH_STENCIL,
glow::UNSIGNED_INT_24_8,
),
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => (glow::RGB9_E5, glow::RGB, glow::UNSIGNED_INT_5_9_9_9_REV),
Tf::Bc1RgbaUnorm => (glow::COMPRESSED_RGBA_S3TC_DXT1_EXT, glow::RGBA, 0),
Tf::Bc1RgbaUnormSrgb => (glow::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, glow::RGBA, 0),

View File

@ -467,9 +467,17 @@ impl super::Device {
}
}
let first_instance_location = if has_stages.contains(wgt::ShaderStages::VERTEX) {
// If this returns none (the uniform isn't active), that's fine, we just won't set it.
unsafe { gl.get_uniform_location(program, naga::back::glsl::FIRST_INSTANCE_BINDING) }
} else {
None
};
Ok(Arc::new(super::PipelineInner {
program,
sampler_map,
first_instance_location,
push_constant_descs: uniforms,
}))
}
@ -738,7 +746,7 @@ impl crate::Device<super::Api> for super::Device {
unsafe { gl.bind_texture(target, Some(raw)) };
//Note: this has to be done before defining the storage!
match desc.format.sample_type(None) {
match desc.format.sample_type(None, Some(self.shared.features)) {
Some(
wgt::TextureSampleType::Float { filterable: false }
| wgt::TextureSampleType::Uint
@ -1082,6 +1090,12 @@ impl crate::Device<super::Api> for super::Device {
.private_caps
.contains(super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD),
);
writer_flags.set(
glsl::WriterFlags::DRAW_PARAMETERS,
self.shared
.private_caps
.contains(super::PrivateCapabilities::FULLY_FEATURED_INSTANCING),
);
// We always force point size to be written and it will be ignored by the driver if it's not a point list primitive.
// https://github.com/gfx-rs/wgpu/pull/3440/files#r1095726950
writer_flags.set(glsl::WriterFlags::FORCE_POINT_SIZE, true);
@ -1343,31 +1357,18 @@ impl crate::Device<super::Api> for super::Device {
desc: &wgt::QuerySetDescriptor<crate::Label>,
) -> Result<super::QuerySet, crate::DeviceError> {
let gl = &self.shared.context.lock();
let mut temp_string = String::new();
let mut queries = Vec::with_capacity(desc.count as usize);
for i in 0..desc.count {
for _ in 0..desc.count {
let query =
unsafe { gl.create_query() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
#[cfg(not(target_arch = "wasm32"))]
if gl.supports_debug() {
use std::fmt::Write;
// Initialize the query so we can label it
match desc.ty {
wgt::QueryType::Timestamp => unsafe {
gl.query_counter(query, glow::TIMESTAMP)
},
_ => (),
}
// We aren't really able to, in general, label queries.
//
// We could take a timestamp here to "initialize" the query,
// but that's a bit of a hack, and we don't want to insert
// random timestamps into the command stream of we don't have to.
if let Some(label) = desc.label {
temp_string.clear();
let _ = write!(temp_string, "{label}[{i}]");
let name = unsafe { mem::transmute(query) };
unsafe { gl.object_label(glow::QUERY, name, Some(&temp_string)) };
}
}
queries.push(query);
}

View File

@ -34,7 +34,7 @@ will be minimal, if any.
Generally, vertex buffers are marked as dirty and lazily bound on draw.
GLES3 doesn't support "base instance" semantics. However, it's easy to support,
GLES3 doesn't support `first_instance` semantics. However, it's easy to support,
since we are forced to do late binding anyway. We just adjust the offsets
into the vertex data.
@ -51,9 +51,34 @@ from the vertex data itself. This mostly matches WebGPU, however there is a catc
`stride` needs to be specified with the data, not as a part of the layout.
To address this, we invalidate the vertex buffers based on:
- whether or not `start_instance` is used
- whether or not `first_instance` is used
- stride has changed
## Handling of `base_vertex`, `first_instance`, and `first_vertex`
Between indirect, the lack of `first_instance` semantics, and the availability of `gl_BaseInstance`
in shaders, getting buffers and builtins to work correctly is a bit tricky.
We never emulate `base_vertex` and gl_VertexID behaves as `@builtin(vertex_index)` does, so we
never need to do anything about that.
We always advertise support for `VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW`.
### GL 4.2+ with ARB_shader_draw_parameters
- `@builtin(instance_index)` translates to `gl_InstanceID + gl_BaseInstance`
- We bind instance buffers without any offset emulation.
- We advertise support for the `INDIRECT_FIRST_INSTANCE` feature.
While we can theoretically have a card with 4.2+ support but without ARB_shader_draw_parameters,
we don't bother with that combination.
### GLES & GL 4.1
- `@builtin(instance_index)` translates to `gl_InstanceID + naga_vs_first_instance`
- We bind instance buffers with offset emulation.
- We _do not_ advertise support for `INDIRECT_FIRST_INSTANCE` and cpu-side pretend the `first_instance` is 0 on indirect calls.
*/
///cbindgen:ignore
@ -161,16 +186,20 @@ bitflags::bitflags! {
const COLOR_BUFFER_HALF_FLOAT = 1 << 8;
/// Supports `f11/f10` and `f32` color buffers
const COLOR_BUFFER_FLOAT = 1 << 9;
/// Supports linear flitering `f32` textures.
const TEXTURE_FLOAT_LINEAR = 1 << 10;
/// Supports query buffer objects.
const QUERY_BUFFERS = 1 << 11;
/// Supports 64 bit queries via `glGetQueryObjectui64v`
const QUERY_64BIT = 1 << 12;
/// Supports `glTexStorage2D`, etc.
const TEXTURE_STORAGE = 1 << 12;
const TEXTURE_STORAGE = 1 << 13;
/// Supports `push_debug_group`, `pop_debug_group` and `debug_message_insert`.
const DEBUG_FNS = 1 << 13;
const DEBUG_FNS = 1 << 14;
/// Supports framebuffer invalidation.
const INVALIDATE_FRAMEBUFFER = 1 << 14;
const INVALIDATE_FRAMEBUFFER = 1 << 15;
/// Indicates support for `glDrawElementsInstancedBaseVertexBaseInstance` and `ARB_shader_draw_parameters`
///
/// When this is true, instance offset emulation via vertex buffer rebinding and a shader uniform will be disabled.
const FULLY_FEATURED_INSTANCING = 1 << 16;
}
}
@ -218,7 +247,6 @@ struct AdapterShared {
features: wgt::Features,
workarounds: Workarounds,
shading_language_version: naga::back::glsl::Version,
max_texture_size: u32,
next_shader_id: AtomicU32,
program_cache: Mutex<ProgramCache>,
es: bool,
@ -516,6 +544,7 @@ type SamplerBindMap = [Option<u8>; MAX_TEXTURE_SLOTS];
struct PipelineInner {
program: glow::Program,
sampler_map: SamplerBindMap,
first_instance_location: Option<glow::UniformLocation>,
push_constant_descs: ArrayVec<PushConstantDesc, MAX_PUSH_CONSTANT_COMMANDS>,
}
@ -715,9 +744,11 @@ type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }
enum Command {
Draw {
topology: u32,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
first_instance: u32,
instance_count: u32,
first_instance_location: Option<glow::UniformLocation>,
},
DrawIndexed {
topology: u32,
@ -725,18 +756,22 @@ enum Command {
index_count: u32,
index_offset: wgt::BufferAddress,
base_vertex: i32,
first_instance: u32,
instance_count: u32,
first_instance_location: Option<glow::UniformLocation>,
},
DrawIndirect {
topology: u32,
indirect_buf: glow::Buffer,
indirect_offset: wgt::BufferAddress,
first_instance_location: Option<glow::UniformLocation>,
},
DrawIndexedIndirect {
topology: u32,
index_type: u32,
indirect_buf: glow::Buffer,
indirect_offset: wgt::BufferAddress,
first_instance_location: Option<glow::UniformLocation>,
},
Dispatch([u32; 3]),
DispatchIndirect {
@ -914,6 +949,19 @@ impl fmt::Debug for CommandBuffer {
}
}
#[cfg(all(
target_arch = "wasm32",
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Sync for CommandBuffer {}
#[cfg(all(
target_arch = "wasm32",
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Send for CommandBuffer {}
//TODO: we would have something like `Arc<typed_arena::Arena>`
// here and in the command buffers. So that everything grows
// inside the encoder and stays there until `reset_all`.
@ -932,6 +980,19 @@ impl fmt::Debug for CommandEncoder {
}
}
#[cfg(all(
target_arch = "wasm32",
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Sync for CommandEncoder {}
#[cfg(all(
target_arch = "wasm32",
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Send for CommandEncoder {}
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
let source_str = match source {
@ -978,6 +1039,6 @@ fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, m
if cfg!(debug_assertions) && log_severity == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.set();
crate::VALIDATION_CANARY.add(message.to_string());
}
}

View File

@ -160,20 +160,43 @@ impl super::Queue {
match *command {
C::Draw {
topology,
start_vertex,
first_vertex,
vertex_count,
instance_count,
first_instance,
ref first_instance_location,
} => {
// Don't use `gl.draw_arrays` for `instance_count == 1`.
// Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `draw_arrays`.
// See https://github.com/gfx-rs/wgpu/issues/3578
unsafe {
gl.draw_arrays_instanced(
topology,
start_vertex as i32,
vertex_count as i32,
instance_count as i32,
)
let supports_full_instancing = self
.shared
.private_caps
.contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING);
if supports_full_instancing {
unsafe {
gl.draw_arrays_instanced_base_instance(
topology,
first_vertex as i32,
vertex_count as i32,
instance_count as i32,
first_instance,
)
}
} else {
unsafe {
gl.uniform_1_u32(first_instance_location.as_ref(), first_instance);
}
// Don't use `gl.draw_arrays` for `instance_count == 1`.
// Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `draw_arrays`.
// See https://github.com/gfx-rs/wgpu/issues/3578
unsafe {
gl.draw_arrays_instanced(
topology,
first_vertex as i32,
vertex_count as i32,
instance_count as i32,
)
}
};
}
C::DrawIndexed {
@ -182,38 +205,75 @@ impl super::Queue {
index_count,
index_offset,
base_vertex,
first_instance,
instance_count,
ref first_instance_location,
} => {
match base_vertex {
// Don't use `gl.draw_elements`/`gl.draw_elements_base_vertex` for `instance_count == 1`.
// Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `gl.draw_elements`/`gl.draw_elements_base_vertex`.
// See https://github.com/gfx-rs/wgpu/issues/3578
0 => unsafe {
gl.draw_elements_instanced(
topology,
index_count as i32,
index_type,
index_offset as i32,
instance_count as i32,
)
},
_ => unsafe {
gl.draw_elements_instanced_base_vertex(
topology,
index_count as _,
index_type,
index_offset as i32,
instance_count as i32,
base_vertex,
)
},
0 => {
unsafe {
gl.uniform_1_u32(first_instance_location.as_ref(), first_instance)
};
unsafe {
// Don't use `gl.draw_elements`/`gl.draw_elements_base_vertex` for `instance_count == 1`.
// Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `gl.draw_elements`/`gl.draw_elements_base_vertex`.
// See https://github.com/gfx-rs/wgpu/issues/3578
gl.draw_elements_instanced(
topology,
index_count as i32,
index_type,
index_offset as i32,
instance_count as i32,
)
}
}
_ => {
let supports_full_instancing = self
.shared
.private_caps
.contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING);
if supports_full_instancing {
unsafe {
gl.draw_elements_instanced_base_vertex_base_instance(
topology,
index_count as i32,
index_type,
index_offset as i32,
instance_count as i32,
base_vertex,
first_instance,
)
}
} else {
unsafe {
gl.uniform_1_u32(first_instance_location.as_ref(), first_instance)
};
// If we've gotten here, wgpu-core has already validated that this function exists via the DownlevelFlags::BASE_VERTEX feature.
unsafe {
gl.draw_elements_instanced_base_vertex(
topology,
index_count as _,
index_type,
index_offset as i32,
instance_count as i32,
base_vertex,
)
}
}
}
}
}
C::DrawIndirect {
topology,
indirect_buf,
indirect_offset,
ref first_instance_location,
} => {
unsafe { gl.uniform_1_u32(first_instance_location.as_ref(), 0) };
unsafe { gl.bind_buffer(glow::DRAW_INDIRECT_BUFFER, Some(indirect_buf)) };
unsafe { gl.draw_arrays_indirect_offset(topology, indirect_offset as i32) };
}
@ -222,7 +282,10 @@ impl super::Queue {
index_type,
indirect_buf,
indirect_offset,
ref first_instance_location,
} => {
unsafe { gl.uniform_1_u32(first_instance_location.as_ref(), 0) };
unsafe { gl.bind_buffer(glow::DRAW_INDIRECT_BUFFER, Some(indirect_buf)) };
unsafe {
gl.draw_elements_indirect_offset(topology, index_type, indirect_offset as i32)
@ -877,12 +940,21 @@ impl super::Queue {
{
let mut result: u64 = 0;
unsafe {
let result: *mut u64 = &mut result;
gl.get_query_parameter_u64_with_offset(
query,
glow::QUERY_RESULT,
result as usize,
)
if self
.shared
.private_caps
.contains(PrivateCapabilities::QUERY_64BIT)
{
let result: *mut u64 = &mut result;
gl.get_query_parameter_u64_with_offset(
query,
glow::QUERY_RESULT,
result as usize,
)
} else {
result =
gl.get_query_parameter_u32(query, glow::QUERY_RESULT) as u64;
}
};
temp_query_results.push(result);
}

View File

@ -90,10 +90,11 @@ use std::{
num::NonZeroU32,
ops::{Range, RangeInclusive},
ptr::NonNull,
sync::{atomic::AtomicBool, Arc},
sync::Arc,
};
use bitflags::bitflags;
use parking_lot::Mutex;
use thiserror::Error;
use wgt::WasmNotSendSync;
@ -565,17 +566,17 @@ pub trait CommandEncoder<A: Api>: WasmNotSendSync + fmt::Debug {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
);
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
);
unsafe fn draw_indirect(
@ -623,8 +624,8 @@ bitflags!(
/// Pipeline layout creation flags.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PipelineLayoutFlags: u32 {
/// Include support for base vertex/instance drawing.
const BASE_VERTEX_INSTANCE = 1 << 0;
/// Include support for `first_vertex` / `first_instance` drawing.
const FIRST_VERTEX_INSTANCE = 1 << 0;
/// Include support for num work groups builtin.
const NUM_WORK_GROUPS = 1 << 1;
}
@ -881,11 +882,6 @@ pub struct SurfaceCapabilities {
/// Current extent of the surface, if known.
pub current_extent: Option<wgt::Extent3d>,
/// Range of supported extents.
///
/// `current_extent` must be inside this range.
pub extents: RangeInclusive<wgt::Extent3d>,
/// Supported texture usage flags.
///
/// Must have at least `TextureUses::COLOR_TARGET`
@ -980,6 +976,7 @@ pub struct TextureViewDescriptor<'a> {
pub dimension: wgt::TextureViewDimension,
pub usage: TextureUses,
pub range: wgt::ImageSubresourceRange,
pub plane: Option<u32>,
}
#[derive(Clone, Debug)]
@ -1384,8 +1381,11 @@ pub struct ComputePassDescriptor<'a, A: Api> {
pub timestamp_writes: Option<ComputePassTimestampWrites<'a, A>>,
}
/// Stores if any API validation error has occurred in this process
/// since it was last reset.
/// Stores the text of any validation errors that have occurred since
/// the last call to `get_and_reset`.
///
/// Each value is a validation error and a message associated with it,
/// or `None` if the error has no message from the api.
///
/// This is used for internal wgpu testing only and _must not_ be used
/// as a way to check for errors.
@ -1396,24 +1396,24 @@ pub struct ComputePassDescriptor<'a, A: Api> {
/// This prevents the issue of one validation error terminating the
/// entire process.
pub static VALIDATION_CANARY: ValidationCanary = ValidationCanary {
inner: AtomicBool::new(false),
inner: Mutex::new(Vec::new()),
};
/// Flag for internal testing.
pub struct ValidationCanary {
inner: AtomicBool,
inner: Mutex<Vec<String>>,
}
impl ValidationCanary {
#[allow(dead_code)] // in some configurations this function is dead
fn set(&self) {
self.inner.store(true, std::sync::atomic::Ordering::SeqCst);
fn add(&self, msg: String) {
self.inner.lock().push(msg);
}
/// Returns true if any API validation error has occurred in this process
/// Returns any API validation errors that hav occurred in this process
/// since the last call to this function.
pub fn get_and_reset(&self) -> bool {
self.inner.swap(false, std::sync::atomic::Ordering::SeqCst)
pub fn get_and_reset(&self) -> Vec<String> {
self.inner.lock().drain(..).collect()
}
}

View File

@ -232,6 +232,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
}
flags
}
Tf::NV12 => return Tfc::empty(),
Tf::Rgb9e5Ufloat => {
if pc.msaa_apple3 {
all_caps
@ -338,15 +339,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
],
current_extent,
extents: wgt::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
}..=wgt::Extent3d {
width: pc.max_texture_size as u32,
height: pc.max_texture_size as u32,
depth_or_array_layers: 1,
},
usage: crate::TextureUses::COLOR_TARGET | crate::TextureUses::COPY_DST, //TODO: expose more
})
}
@ -427,17 +419,17 @@ const BGR10A2_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const BASE_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
/// "Indirect draw & dispatch arguments" in the Metal feature set tables
const INDIRECT_DRAW_DISPATCH_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
];
const BASE_VERTEX_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
];
/// "Base vertex/instance drawing" in the Metal feature set tables
///
/// in our terms, `base_vertex` and `first_instance` must be 0
const BASE_VERTEX_FIRST_INSTANCE_SUPPORT: &[MTLFeatureSet] = INDIRECT_DRAW_DISPATCH_SUPPORT;
const TEXTURE_CUBE_ARRAY_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily4_v1,
@ -600,8 +592,11 @@ impl super::PrivateCapabilities {
MUTABLE_COMPARISON_SAMPLER_SUPPORT,
),
sampler_clamp_to_border: Self::supports_any(device, SAMPLER_CLAMP_TO_BORDER_SUPPORT),
base_instance: Self::supports_any(device, BASE_INSTANCE_SUPPORT),
base_vertex_instance_drawing: Self::supports_any(device, BASE_VERTEX_INSTANCE_SUPPORT),
indirect_draw_dispatch: Self::supports_any(device, INDIRECT_DRAW_DISPATCH_SUPPORT),
base_vertex_first_instance_drawing: Self::supports_any(
device,
BASE_VERTEX_FIRST_INSTANCE_SUPPORT,
),
dual_source_blending: Self::supports_any(device, DUAL_SOURCE_BLEND_SUPPORT),
low_power: !os_is_mac || device.is_low_power(),
headless: os_is_mac && device.is_headless(),
@ -609,6 +604,9 @@ impl super::PrivateCapabilities {
function_specialization: Self::supports_any(device, FUNCTION_SPECIALIZATION_SUPPORT),
depth_clip_mode: Self::supports_any(device, DEPTH_CLIP_MODE),
texture_cube_array: Self::supports_any(device, TEXTURE_CUBE_ARRAY_SUPPORT),
supports_float_filtering: os_is_mac
|| (version.at_least((11, 0), (14, 0), os_is_mac)
&& device.supports_32bit_float_filtering()),
format_depth24_stencil8: os_is_mac && device.d24_s8_supported(),
format_depth32_stencil8_filter: os_is_mac,
format_depth32_stencil8_none: !os_is_mac,
@ -815,7 +813,6 @@ impl super::PrivateCapabilities {
use wgt::Features as F;
let mut features = F::empty()
| F::INDIRECT_FIRST_INSTANCE
| F::MAPPABLE_PRIMARY_BUFFERS
| F::VERTEX_WRITABLE_STORAGE
| F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
@ -825,9 +822,13 @@ impl super::PrivateCapabilities {
| F::TEXTURE_FORMAT_16BIT_NORM
| F::SHADER_F16
| F::DEPTH32FLOAT_STENCIL8
| F::MULTI_DRAW_INDIRECT
| F::BGRA8UNORM_STORAGE;
features.set(F::FLOAT32_FILTERABLE, self.supports_float_filtering);
features.set(
F::INDIRECT_FIRST_INSTANCE | F::MULTI_DRAW_INDIRECT,
self.indirect_draw_dispatch,
);
features.set(
F::TIMESTAMP_QUERY,
self.timestamp_query_support
@ -891,11 +892,20 @@ impl super::PrivateCapabilities {
wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
self.texture_cube_array,
);
//TODO: separate the mutable comparisons from immutable ones
// TODO: separate the mutable comparisons from immutable ones
downlevel.flags.set(
wgt::DownlevelFlags::COMPARISON_SAMPLERS,
self.mutable_comparison_samplers,
);
downlevel.flags.set(
wgt::DownlevelFlags::INDIRECT_EXECUTION,
self.indirect_draw_dispatch,
);
// TODO: add another flag for `first_instance`
downlevel.flags.set(
wgt::DownlevelFlags::BASE_VERTEX,
self.base_vertex_first_instance_drawing,
);
downlevel
.flags
.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, true);
@ -1008,6 +1018,7 @@ impl super::PrivateCapabilities {
Depth32Float_Stencil8
}
}
Tf::NV12 => unreachable!(),
Tf::Rgb9e5Ufloat => RGB9E5Float,
Tf::Bc1RgbaUnorm => BC1_RGBA,
Tf::Bc1RgbaUnormSrgb => BC1_RGBA_sRGB,

View File

@ -965,31 +965,31 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
let encoder = self.state.render.as_ref().unwrap();
if start_instance != 0 {
if first_instance != 0 {
encoder.draw_primitives_instanced_base_instance(
self.state.raw_primitive_type,
start_vertex as _,
first_vertex as _,
vertex_count as _,
instance_count as _,
start_instance as _,
first_instance as _,
);
} else if instance_count != 1 {
encoder.draw_primitives_instanced(
self.state.raw_primitive_type,
start_vertex as _,
first_vertex as _,
vertex_count as _,
instance_count as _,
);
} else {
encoder.draw_primitives(
self.state.raw_primitive_type,
start_vertex as _,
first_vertex as _,
vertex_count as _,
);
}
@ -997,16 +997,16 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
let encoder = self.state.render.as_ref().unwrap();
let index = self.state.index.as_ref().unwrap();
let offset = index.offset + index.stride * start_index as wgt::BufferAddress;
if base_vertex != 0 || start_instance != 0 {
let offset = index.offset + index.stride * first_index as wgt::BufferAddress;
if base_vertex != 0 || first_instance != 0 {
encoder.draw_indexed_primitives_instanced_base_instance(
self.state.raw_primitive_type,
index_count as _,
@ -1015,7 +1015,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
offset,
instance_count as _,
base_vertex as _,
start_instance as _,
first_instance as _,
);
} else if instance_count != 1 {
encoder.draw_indexed_primitives_instanced(

View File

@ -182,8 +182,8 @@ struct PrivateCapabilities {
shared_textures: bool,
mutable_comparison_samplers: bool,
sampler_clamp_to_border: bool,
base_instance: bool,
base_vertex_instance_drawing: bool,
indirect_draw_dispatch: bool,
base_vertex_first_instance_drawing: bool,
dual_source_blending: bool,
low_power: bool,
headless: bool,
@ -191,6 +191,7 @@ struct PrivateCapabilities {
function_specialization: bool,
depth_clip_mode: bool,
texture_cube_array: bool,
supports_float_filtering: bool,
format_depth24_stencil8: bool,
format_depth32_stencil8_filter: bool,
format_depth32_stencil8_none: bool,

View File

@ -301,6 +301,7 @@ impl PhysicalDeviceFeatures {
fn to_wgpu(
&self,
adapter_info: &wgt::AdapterInfo,
instance: &ash::Instance,
phd: vk::PhysicalDevice,
caps: &PhysicalDeviceCapabilities,
@ -331,7 +332,8 @@ impl PhysicalDeviceFeatures {
| Df::INDIRECT_EXECUTION
| Df::VIEW_FORMATS
| Df::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES
| Df::NONBLOCKING_QUERY_RESOLVE;
| Df::NONBLOCKING_QUERY_RESOLVE
| Df::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
dl_flags.set(
Df::SURFACE_VIEW_FORMATS,
@ -534,6 +536,26 @@ impl PhysicalDeviceFeatures {
supports_bgra8unorm_storage(instance, phd, caps.device_api_version),
);
features.set(
F::FLOAT32_FILTERABLE,
is_float32_filterable_supported(instance, phd),
);
features.set(
F::TEXTURE_FORMAT_NV12,
(caps.device_api_version >= vk::API_VERSION_1_1
|| caps.supports_extension(vk::KhrSamplerYcbcrConversionFn::name()))
&& supports_format(
instance,
phd,
vk::Format::G8_B8R8_2PLANE_420_UNORM,
vk::ImageTiling::OPTIMAL,
vk::FormatFeatureFlags::SAMPLED_IMAGE
| vk::FormatFeatureFlags::TRANSFER_SRC
| vk::FormatFeatureFlags::TRANSFER_DST,
)
&& !adapter_info.driver.contains("MoltenVK"),
);
(features, dl_flags)
}
@ -968,7 +990,7 @@ impl super::Instance {
};
let (available_features, downlevel_flags) =
phd_features.to_wgpu(&self.shared.raw, phd, &phd_capabilities);
phd_features.to_wgpu(&info, &self.shared.raw, phd, &phd_capabilities);
let mut workarounds = super::Workarounds::empty();
{
// see https://github.com/gfx-rs/gfx/issues/1930
@ -992,11 +1014,21 @@ impl super::Instance {
if let Some(driver) = phd_capabilities.driver {
if driver.conformance_version.major == 0 {
log::warn!(
"Adapter is not Vulkan compliant, hiding adapter: {}",
info.name
);
return None;
if driver.driver_id == ash::vk::DriverId::MOLTENVK {
log::debug!("Adapter is not Vulkan compliant, but is MoltenVK, continuing");
} else if self
.shared
.flags
.contains(wgt::InstanceFlags::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER)
{
log::warn!("Adapter is not Vulkan compliant: {}", info.name);
} else {
log::warn!(
"Adapter is not Vulkan compliant, hiding adapter: {}",
info.name
);
return None;
}
}
}
if phd_capabilities.device_api_version == vk::API_VERSION_1_0
@ -1540,14 +1572,14 @@ impl crate::Adapter<super::Api> for super::Adapter {
.framebuffer_stencil_sample_counts
.min(limits.sampled_image_stencil_sample_counts)
} else {
match format.sample_type(None).unwrap() {
wgt::TextureSampleType::Float { filterable: _ } => limits
match format.sample_type(None, None) {
Some(wgt::TextureSampleType::Float { .. }) => limits
.framebuffer_color_sample_counts
.min(limits.sampled_image_color_sample_counts),
wgt::TextureSampleType::Sint | wgt::TextureSampleType::Uint => {
Some(wgt::TextureSampleType::Sint) | Some(wgt::TextureSampleType::Uint) => {
limits.sampled_image_integer_sample_counts
}
_ => unreachable!(),
_ => vk::SampleCountFlags::TYPE_1,
}
};
@ -1631,18 +1663,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
None
};
let min_extent = wgt::Extent3d {
width: caps.min_image_extent.width,
height: caps.min_image_extent.height,
depth_or_array_layers: 1,
};
let max_extent = wgt::Extent3d {
width: caps.max_image_extent.width,
height: caps.max_image_extent.height,
depth_or_array_layers: caps.max_image_array_layers,
};
let raw_present_modes = {
profiling::scope!("vkGetPhysicalDeviceSurfacePresentModesKHR");
match unsafe {
@ -1681,7 +1701,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
formats,
swap_chain_sizes: caps.min_image_count..=max_image_count,
current_extent,
extents: min_extent..=max_extent,
usage: conv::map_vk_image_usage(caps.supported_usage_flags),
present_modes: raw_present_modes
.into_iter()
@ -1745,6 +1764,21 @@ fn is_format_16bit_norm_supported(instance: &ash::Instance, phd: vk::PhysicalDev
r16unorm && r16snorm && rg16unorm && rg16snorm && rgba16unorm && rgba16snorm
}
fn is_float32_filterable_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
let tiling = vk::ImageTiling::OPTIMAL;
let features = vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR;
let r_float = supports_format(instance, phd, vk::Format::R32_SFLOAT, tiling, features);
let rg_float = supports_format(instance, phd, vk::Format::R32G32_SFLOAT, tiling, features);
let rgba_float = supports_format(
instance,
phd,
vk::Format::R32G32B32A32_SFLOAT,
tiling,
features,
);
r_float && rg_float && rgba_float
}
fn supports_format(
instance: &ash::Instance,
phd: vk::PhysicalDevice,

View File

@ -722,9 +722,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn draw(
&mut self,
start_vertex: u32,
first_vertex: u32,
vertex_count: u32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
unsafe {
@ -732,17 +732,17 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
self.active,
vertex_count,
instance_count,
start_vertex,
start_instance,
first_vertex,
first_instance,
)
};
}
unsafe fn draw_indexed(
&mut self,
start_index: u32,
first_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
first_instance: u32,
instance_count: u32,
) {
unsafe {
@ -750,9 +750,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
self.active,
index_count,
instance_count,
start_index,
first_index,
base_vertex,
start_instance,
first_instance,
)
};
}

View File

@ -74,6 +74,7 @@ impl super::PrivateCapabilities {
}
}
Tf::Depth16Unorm => F::D16_UNORM,
Tf::NV12 => F::G8_B8R8_2PLANE_420_UNORM,
Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32,
Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK,
Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK,
@ -199,7 +200,7 @@ impl crate::ColorAttachment<'_, super::Api> {
.view
.attachment
.view_format
.sample_type(None)
.sample_type(None, None)
.unwrap()
{
wgt::TextureSampleType::Float { .. } => vk::ClearColorValue {
@ -401,10 +402,18 @@ pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> vk::Format {
}
}
pub fn map_aspects(aspects: crate::FormatAspects) -> vk::ImageAspectFlags {
pub fn map_aspects(aspects: crate::FormatAspects, plane: Option<u32>) -> vk::ImageAspectFlags {
let mut flags = vk::ImageAspectFlags::empty();
if aspects.contains(crate::FormatAspects::COLOR) {
flags |= vk::ImageAspectFlags::COLOR;
match plane {
Some(0) => flags |= vk::ImageAspectFlags::PLANE_0,
Some(1) => flags |= vk::ImageAspectFlags::PLANE_1,
Some(2) => flags |= vk::ImageAspectFlags::PLANE_2,
Some(plane) => panic!("Unexpected plane {}", plane),
None => {
if aspects.contains(crate::FormatAspects::COLOR) {
flags |= vk::ImageAspectFlags::COLOR;
}
}
}
if aspects.contains(crate::FormatAspects::DEPTH) {
flags |= vk::ImageAspectFlags::DEPTH;
@ -586,9 +595,10 @@ pub fn map_copy_extent(extent: &crate::CopyExtent) -> vk::Extent3D {
pub fn map_subresource_range(
range: &wgt::ImageSubresourceRange,
format: wgt::TextureFormat,
plane: Option<u32>,
) -> vk::ImageSubresourceRange {
vk::ImageSubresourceRange {
aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect)),
aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect), plane),
base_mip_level: range.base_mip_level,
level_count: range.mip_level_count.unwrap_or(vk::REMAINING_MIP_LEVELS),
base_array_layer: range.base_array_layer,
@ -605,7 +615,7 @@ pub(super) fn map_subresource_range_combined_aspect(
format: wgt::TextureFormat,
private_caps: &super::PrivateCapabilities,
) -> vk::ImageSubresourceRange {
let mut range = map_subresource_range(range, format);
let mut range = map_subresource_range(range, format, None);
if !private_caps.texture_s8 && format == wgt::TextureFormat::Stencil8 {
range.aspect_mask |= vk::ImageAspectFlags::DEPTH;
}
@ -621,7 +631,7 @@ pub fn map_subresource_layers(
z: base.origin.z as i32,
};
let subresource = vk::ImageSubresourceLayers {
aspect_mask: map_aspects(base.aspect),
aspect_mask: map_aspects(base.aspect, None),
mip_level: base.mip_level,
base_array_layer: base.array_layer,
layer_count: 1,

View File

@ -1062,7 +1062,7 @@ impl crate::Device<super::Api> for super::Device {
texture: &super::Texture,
desc: &crate::TextureViewDescriptor,
) -> Result<super::TextureView, crate::DeviceError> {
let subresource_range = conv::map_subresource_range(&desc.range, desc.format);
let subresource_range = conv::map_subresource_range(&desc.range, desc.format, desc.plane);
let mut vk_info = vk::ImageViewCreateInfo::builder()
.flags(vk::ImageViewCreateFlags::empty())
.image(texture.raw)

View File

@ -41,10 +41,11 @@ unsafe extern "system" fn debug_utils_messenger_callback(
}
}
// Silence Vulkan Validation error "VUID-VkSwapchainCreateInfoKHR-imageExtent-01274"
// - it's a false positive due to the inherent racy-ness of surface resizing
const VUID_VKSWAPCHAINCREATEINFOKHR_IMAGEEXTENT_01274: i32 = 0x7cd0911d;
if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_IMAGEEXTENT_01274 {
// Silence Vulkan Validation error "VUID-VkSwapchainCreateInfoKHR-pNext-07781"
// This happens when a surface is configured with a size outside the allowed extent.
// It's s false positive due to the inherent racy-ness of surface resizing.
const VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781: i32 = 0x4c8929c1;
if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781 {
return vk::FALSE;
}
@ -146,7 +147,7 @@ unsafe extern "system" fn debug_utils_messenger_callback(
if cfg!(debug_assertions) && level == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.set();
crate::VALIDATION_CANARY.add(message.to_string());
}
vk::FALSE

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"e45ee369c8f91526056ba7f46504e270da1d8b2de65431bbaf36426bcd54da68","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"6c3886f27653eee4359a8d150a6091a7d6e0ff1abab44c9afff2d54a0d0a474b","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null}
{"files":{"Cargo.toml":"18549fb7d7de2ea2481f30292dca63889856a33bd1b3698e16cee6631ab65df4","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"61f4f46533468f82e3d22e42d6e8b3d8b68504ccd4f9b4b9ea67b0664c42a34e","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null}

View File

@ -58,10 +58,10 @@ strict_asserts = []
trace = ["serde"]
[target."cfg(target_arch = \"wasm32\")".dependencies]
js-sys = "0.3.65"
js-sys = "0.3.66"
[target."cfg(target_arch = \"wasm32\")".dependencies.web-sys]
version = "0.3.64"
version = "0.3.66"
features = [
"ImageBitmap",
"HtmlVideoElement",

View File

@ -280,12 +280,21 @@ bitflags::bitflags! {
///
/// This is a web and native feature.
const TIMESTAMP_QUERY = 1 << 1;
/// Allows non-zero value for the "first instance" in indirect draw calls.
/// Allows non-zero value for the `first_instance` member in indirect draw calls.
///
/// If this feature is not enabled, and the `first_instance` member is non-zero, the behavior may be:
/// - The draw call is ignored.
/// - The draw call is executed as if the `first_instance` is zero.
/// - The draw call is executed with the correct `first_instance` value.
///
/// Supported Platforms:
/// - Vulkan (mostly)
/// - DX12
/// - Metal
/// - Metal on Apple3+ or Mac1+
/// - OpenGL (Desktop 4.2+ with ARB_shader_draw_parameters only)
///
/// Not Supported:
/// - OpenGL ES / WebGL
///
/// This is a web and native feature.
const INDIRECT_FIRST_INSTANCE = 1 << 2;
@ -328,7 +337,18 @@ bitflags::bitflags! {
// ? const NORM16_FILTERABLE = 1 << 17; (https://github.com/gpuweb/gpuweb/issues/3839)
// ? const NORM16_RESOLVE = 1 << 18; (https://github.com/gpuweb/gpuweb/issues/3839)
// TODO const FLOAT32_FILTERABLE = 1 << 19;
/// Allows textures with formats "r32float", "rg32float", and "rgba32float" to be filterable.
///
/// Supported Platforms:
/// - Vulkan (mainly on Desktop GPUs)
/// - DX12
/// - Metal on macOS or Apple9+ GPUs, optional on iOS/iPadOS with Apple7/8 GPUs
/// - GL with one of `GL_ARB_color_buffer_float`/`GL_EXT_color_buffer_float`/`OES_texture_float_linear`
///
/// This is a web and native feature.
const FLOAT32_FILTERABLE = 1 << 19;
// ? const FLOAT32_BLENDABLE = 1 << 20; (https://github.com/gpuweb/gpuweb/issues/3556)
// ? const 32BIT_FORMAT_MULTISAMPLE = 1 << 21; (https://github.com/gpuweb/gpuweb/issues/3844)
// ? const 32BIT_FORMAT_RESOLVE = 1 << 22; (https://github.com/gpuweb/gpuweb/issues/3844)
@ -613,7 +633,7 @@ bitflags::bitflags! {
/// Supported platforms:
/// - DX12
/// - Vulkan
/// - Metal (Emulated on top of `draw_indirect` and `draw_indexed_indirect`)
/// - Metal on Apple3+ or Mac1+ (Emulated on top of `draw_indirect` and `draw_indexed_indirect`)
///
/// This is a native only feature.
///
@ -762,7 +782,16 @@ bitflags::bitflags! {
/// - OpenGL
const SHADER_UNUSED_VERTEX_OUTPUT = 1 << 54;
// 54..59 available
/// Allows for creation of textures of format [`TextureFormat::NV12`]
///
/// Supported platforms:
/// - DX12
/// - Vulkan
///
/// This is a native only feature.
const TEXTURE_FORMAT_NV12 = 1 << 55;
// 55..59 available
// Shader:
@ -849,6 +878,16 @@ bitflags::bitflags! {
const VALIDATION = 1 << 1;
/// Don't pass labels to wgpu-hal.
const DISCARD_HAL_LABELS = 1 << 2;
/// Whether wgpu should expose adapters that run on top of non-compliant adapters.
///
/// Turning this on might mean that some of the functionality provided by the wgpu
/// adapter/device is not working or is broken. It could be that all the functionality
/// wgpu currently exposes works but we can't tell for sure since we have no additional
/// transparency into what is working and what is not on the underlying adapter.
///
/// This mainly applies to a Vulkan driver's compliance version. If the major compliance version
/// is `0`, then the driver is ignored. This flag allows that driver to be enabled for testing.
const ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER = 1 << 3;
}
}
@ -901,6 +940,9 @@ impl InstanceFlags {
if let Some(bit) = env("WGPU_DEBUG") {
self.set(Self::DEBUG, bit);
}
if let Some(bit) = env("WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER") {
self.set(Self::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER, bit);
}
self
}
@ -1385,9 +1427,18 @@ bitflags::bitflags! {
const FRAGMENT_WRITABLE_STORAGE = 1 << 1;
/// Supports indirect drawing and dispatching.
///
/// DX11 on FL10 level hardware, WebGL2, and GLES 3.0 devices do not support indirect.
/// DX11 on FL10 level hardware, WebGL2, GLES 3.0, and Metal on Apple1/Apple2 GPUs do not support indirect.
const INDIRECT_EXECUTION = 1 << 2;
/// Supports non-zero `base_vertex` parameter to indexed draw calls.
/// Supports non-zero `base_vertex` parameter to direct indexed draw calls.
///
/// Indirect calls, if supported, always support non-zero `base_vertex`.
///
/// Supported by:
/// - Vulkan
/// - DX12
/// - Metal on Apple3+ or Mac1+
/// - OpenGL 3.2+
/// - OpenGL ES 3.2
const BASE_VERTEX = 1 << 3;
/// Supports reading from a depth/stencil texture while using it as a read-only
/// depth/stencil attachment.
@ -1486,6 +1537,34 @@ bitflags::bitflags! {
/// Not Supported by:
/// - GL ES / WebGL
const NONBLOCKING_QUERY_RESOLVE = 1 << 22;
/// If this is true, use of `@builtin(vertex_index)` and `@builtin(instance_index)` will properly take into consideration
/// the `first_vertex` and `first_instance` parameters of indirect draw calls.
///
/// If this is false, `@builtin(vertex_index)` and `@builtin(instance_index)` will start by counting from 0, ignoring the
/// `first_vertex` and `first_instance` parameters.
///
/// For example, if you had a draw call like this:
/// - `first_vertex: 4,`
/// - `vertex_count: 12,`
///
/// When this flag is present, `@builtin(vertex_index)` will start at 4 and go up to 15 (12 invocations).
///
/// When this flag is not present, `@builtin(vertex_index)` will start at 0 and go up to 11 (12 invocations).
///
/// This only affects the builtins in the shaders,
/// vertex buffers and instance rate vertex buffers will behave like expected with this flag disabled.
///
/// See also [`Features::`]
///
/// Supported By:
/// - Vulkan
/// - Metal
/// - OpenGL
///
/// Will be implemented in the future by:
/// - DX12 ([#2471](https://github.com/gfx-rs/wgpu/issues/2471))
const VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW = 1 << 23;
}
}
@ -1583,12 +1662,18 @@ pub struct AdapterInfo {
pub struct DeviceDescriptor<L> {
/// Debug label for the device.
pub label: L,
/// Features that the device should support. If any feature is not supported by
/// the adapter, creating a device will panic.
pub features: Features,
/// Limits that the device should support. If any limit is "better" than the limit exposed by
/// the adapter, creating a device will panic.
pub limits: Limits,
/// Specifies the features that are required by the device request.
/// The request will fail if the adapter cannot provide these features.
///
/// Exactly the specified set of features, and no more or less,
/// will be allowed in validation of API calls on the resulting device.
pub required_features: Features,
/// Specifies the limits that are required by the device request.
/// The request will fail if the adapter cannot provide these limits.
///
/// Exactly the specified limits, and no better or worse,
/// will be allowed in validation of API calls on the resulting device.
pub required_limits: Limits,
}
impl<L> DeviceDescriptor<L> {
@ -1596,8 +1681,8 @@ impl<L> DeviceDescriptor<L> {
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> DeviceDescriptor<K> {
DeviceDescriptor {
label: fun(&self.label),
features: self.features,
limits: self.limits.clone(),
required_features: self.required_features,
required_limits: self.required_limits.clone(),
}
}
}
@ -2318,6 +2403,21 @@ pub enum TextureFormat {
/// [`Features::DEPTH32FLOAT_STENCIL8`] must be enabled to use this texture format.
Depth32FloatStencil8,
/// YUV 4:2:0 chroma subsampled format.
///
/// Contains two planes:
/// - 0: Single 8 bit channel luminance.
/// - 1: Dual 8 bit channel chrominance at half width and half height.
///
/// Valid view formats for luminance are [`TextureFormat::R8Unorm`] and [`TextureFormat::R8Uint`].
///
/// Valid view formats for chrominance are [`TextureFormat::Rg8Unorm`] and [`TextureFormat::Rg8Uint`].
///
/// Width and height must be even.
///
/// [`Features::TEXTURE_FORMAT_NV12`] must be enabled to use this texture format.
NV12,
// Compressed textures usable with `TEXTURE_COMPRESSION_BC` feature.
/// 4x4 block compressed texture. 8 bytes per block (4 bit/px). 4 color + alpha pallet. 5 bit R + 6 bit G + 5 bit B + 1 bit alpha.
/// [0, 63] ([0, 1] for alpha) converted to/from float [0, 1] in shader.
@ -2547,6 +2647,7 @@ impl<'de> Deserialize<'de> for TextureFormat {
"depth16unorm" => TextureFormat::Depth16Unorm,
"depth24plus" => TextureFormat::Depth24Plus,
"depth24plus-stencil8" => TextureFormat::Depth24PlusStencil8,
"nv12" => TextureFormat::NV12,
"rgb9e5ufloat" => TextureFormat::Rgb9e5Ufloat,
"bc1-rgba-unorm" => TextureFormat::Bc1RgbaUnorm,
"bc1-rgba-unorm-srgb" => TextureFormat::Bc1RgbaUnormSrgb,
@ -2674,6 +2775,7 @@ impl Serialize for TextureFormat {
TextureFormat::Depth32FloatStencil8 => "depth32float-stencil8",
TextureFormat::Depth24Plus => "depth24plus",
TextureFormat::Depth24PlusStencil8 => "depth24plus-stencil8",
TextureFormat::NV12 => "nv12",
TextureFormat::Rgb9e5Ufloat => "rgb9e5ufloat",
TextureFormat::Bc1RgbaUnorm => "bc1-rgba-unorm",
TextureFormat::Bc1RgbaUnormSrgb => "bc1-rgba-unorm-srgb",
@ -2868,6 +2970,8 @@ impl TextureFormat {
| Self::Depth32Float
| Self::Depth32FloatStencil8 => (1, 1),
Self::NV12 => (2, 2),
Self::Bc1RgbaUnorm
| Self::Bc1RgbaUnormSrgb
| Self::Bc2RgbaUnorm
@ -2966,6 +3070,8 @@ impl TextureFormat {
Self::Depth32FloatStencil8 => Features::DEPTH32FLOAT_STENCIL8,
Self::NV12 => Features::TEXTURE_FORMAT_NV12,
Self::R16Unorm
| Self::R16Snorm
| Self::Rg16Unorm
@ -3020,6 +3126,7 @@ impl TextureFormat {
TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING;
let attachment = basic | TextureUsages::RENDER_ATTACHMENT;
let storage = basic | TextureUsages::STORAGE_BINDING;
let binding = TextureUsages::TEXTURE_BINDING;
let all_flags = TextureUsages::all();
let rg11b10f = if device_features.contains(Features::RG11B10UFLOAT_RENDERABLE) {
attachment
@ -3081,6 +3188,9 @@ impl TextureFormat {
Self::Depth32Float => ( msaa, attachment),
Self::Depth32FloatStencil8 => ( msaa, attachment),
// We only support sampling nv12 textures until we implement transfer plane data.
Self::NV12 => ( noaa, binding),
Self::R16Unorm => ( msaa, storage),
Self::R16Snorm => ( msaa, storage),
Self::Rg16Unorm => ( msaa, storage),
@ -3119,10 +3229,16 @@ impl TextureFormat {
Self::Astc { .. } => ( noaa, basic),
};
let is_filterable =
self.sample_type(None) == Some(TextureSampleType::Float { filterable: true });
// Get whether the format is filterable, taking features into account
let sample_type1 = self.sample_type(None, Some(device_features));
let is_filterable = sample_type1 == Some(TextureSampleType::Float { filterable: true });
// Features that enable filtering don't affect blendability
let sample_type2 = self.sample_type(None, None);
let is_blendable = sample_type2 == Some(TextureSampleType::Float { filterable: true });
flags.set(TextureFormatFeatureFlags::FILTERABLE, is_filterable);
flags.set(TextureFormatFeatureFlags::BLENDABLE, is_filterable);
flags.set(TextureFormatFeatureFlags::BLENDABLE, is_blendable);
TextureFormatFeatures {
allowed_usages,
@ -3134,9 +3250,17 @@ impl TextureFormat {
///
/// Returns `None` only if the format is combined depth-stencil
/// and `TextureAspect::All` or no `aspect` was provided
pub fn sample_type(&self, aspect: Option<TextureAspect>) -> Option<TextureSampleType> {
pub fn sample_type(
&self,
aspect: Option<TextureAspect>,
device_features: Option<Features>,
) -> Option<TextureSampleType> {
let float = TextureSampleType::Float { filterable: true };
let unfilterable_float = TextureSampleType::Float { filterable: false };
let float32_sample_type = TextureSampleType::Float {
filterable: device_features
.unwrap_or(Features::empty())
.contains(Features::FLOAT32_FILTERABLE),
};
let depth = TextureSampleType::Depth;
let uint = TextureSampleType::Uint;
let sint = TextureSampleType::Sint;
@ -3157,7 +3281,7 @@ impl TextureFormat {
| Self::Rgb10a2Unorm
| Self::Rg11b10Float => Some(float),
Self::R32Float | Self::Rg32Float | Self::Rgba32Float => Some(unfilterable_float),
Self::R32Float | Self::Rg32Float | Self::Rgba32Float => Some(float32_sample_type),
Self::R8Uint
| Self::Rg8Uint
@ -3188,6 +3312,8 @@ impl TextureFormat {
Some(TextureAspect::StencilOnly) => Some(uint),
},
Self::NV12 => None,
Self::R16Unorm
| Self::R16Snorm
| Self::Rg16Unorm
@ -3304,6 +3430,8 @@ impl TextureFormat {
Some(TextureAspect::StencilOnly) => Some(1),
},
Self::NV12 => None,
Self::Bc1RgbaUnorm | Self::Bc1RgbaUnormSrgb | Self::Bc4RUnorm | Self::Bc4RSnorm => {
Some(8)
}
@ -3395,6 +3523,8 @@ impl TextureFormat {
TextureAspect::DepthOnly | TextureAspect::StencilOnly => 1,
},
Self::NV12 => 3,
Self::Bc4RUnorm | Self::Bc4RSnorm => 1,
Self::Bc5RgUnorm | Self::Bc5RgSnorm => 2,
Self::Bc6hRgbUfloat | Self::Bc6hRgbFloat => 3,
@ -6507,44 +6637,84 @@ impl_bitflags!(PipelineStatisticsTypes);
/// Argument buffer layout for draw_indirect commands.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Copy, Clone, Debug, Default)]
pub struct DrawIndirectArgs {
/// The number of vertices to draw.
pub vertex_count: u32,
/// The number of instances to draw.
pub instance_count: u32,
/// Offset into the vertex buffers, in vertices, to begin drawing from.
/// The Index of the first vertex to draw.
pub first_vertex: u32,
/// First instance to draw.
/// The instance ID of the first instance to draw.
///
/// Has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`](crate::Features::INDIRECT_FIRST_INSTANCE) is enabled.
pub first_instance: u32,
}
impl DrawIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written in a buffer.
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
self as *const _ as *const u8,
std::mem::size_of::<Self>(),
))
}
}
}
/// Argument buffer layout for draw_indexed_indirect commands.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Copy, Clone, Debug, Default)]
pub struct DrawIndexedIndirectArgs {
/// The number of indices to draw.
pub index_count: u32,
/// The number of instances to draw.
pub instance_count: u32,
/// Offset into the index buffer, in indices, begin drawing from.
/// The first index within the index buffer.
pub first_index: u32,
/// Added to each index value before indexing into the vertex buffers.
/// The value added to the vertex index before indexing into the vertex buffer.
pub base_vertex: i32,
/// First instance to draw.
/// The instance ID of the first instance to draw.
///
/// Has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`](crate::Features::INDIRECT_FIRST_INSTANCE) is enabled.
pub first_instance: u32,
}
impl DrawIndexedIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written in a buffer.
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
self as *const _ as *const u8,
std::mem::size_of::<Self>(),
))
}
}
}
/// Argument buffer layout for dispatch_indirect commands.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Copy, Clone, Debug, Default)]
pub struct DispatchIndirectArgs {
/// X dimension of the grid of workgroups to dispatch.
pub group_size_x: u32,
/// Y dimension of the grid of workgroups to dispatch.
pub group_size_y: u32,
/// Z dimension of the grid of workgroups to dispatch.
pub group_size_z: u32,
/// The number of work groups in X dimension.
pub x: u32,
/// The number of work groups in Y dimension.
pub y: u32,
/// The number of work groups in Z dimension.
pub z: u32,
}
impl DispatchIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written into a buffer.
pub fn as_bytes(&self) -> &[u8] {
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
self as *const _ as *const u8,
std::mem::size_of::<Self>(),
))
}
}
}
/// Describes how shader bound checks should be performed.