зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1622846 - Update WebGPU API with wgpu r=jgilbert,webidl,smaug
This is another WebGPU API update, it picks up a lot of changes that were made recently: - new bind group layout - new render pipeline descriptor - new vertex formats - limits - compressed texture formats - index format - query sets - and more small ones! It also brings in the updated `gfx/wgpu` to support these API changes. Differential Revision: https://phabricator.services.mozilla.com/D107013
This commit is contained in:
Родитель
210cd0a19d
Коммит
65199ec16a
|
@ -2,16 +2,6 @@
|
|||
# It was generated by `mach vendor rust`.
|
||||
# Please do not edit.
|
||||
|
||||
[source."https://github.com/zakarumych/gpu-descriptor"]
|
||||
git = "https://github.com/zakarumych/gpu-descriptor"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "831460c4b5120d9a74744d542f39a95b9816b5ab"
|
||||
|
||||
[source."https://github.com/zakarumych/gpu-alloc"]
|
||||
git = "https://github.com/zakarumych/gpu-alloc"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "d07be73f9439a37c89f5b72f2500cbf0eb4ff613"
|
||||
|
||||
[source."https://github.com/shravanrn/nix/"]
|
||||
git = "https://github.com/shravanrn/nix/"
|
||||
replace-with = "vendored-sources"
|
||||
|
@ -73,7 +63,7 @@ replace-with = "vendored-sources"
|
|||
rev = "d5d8c00ebd3281d12e0be5dfddbb69f791f836f1"
|
||||
|
||||
[source."https://github.com/kvark/spirv_cross"]
|
||||
branch = "wgpu4"
|
||||
branch = "wgpu5"
|
||||
git = "https://github.com/kvark/spirv_cross"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
|
@ -105,12 +95,22 @@ rev = "fd4ed671ef495af4dcda4c4cba3ef8d426db8af1"
|
|||
[source."https://github.com/gfx-rs/naga"]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "96c80738650822de35f77ab6a589f309460c8f39"
|
||||
tag = "gfx-12"
|
||||
|
||||
[source."https://github.com/gfx-rs/metal-rs"]
|
||||
git = "https://github.com/gfx-rs/metal-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "439c986eb7a9b91e88b61def2daa66e4043fcbef"
|
||||
|
||||
[source."https://github.com/gfx-rs/gfx"]
|
||||
git = "https://github.com/gfx-rs/gfx"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
|
||||
[source."https://github.com/gfx-rs/d3d12-rs"]
|
||||
git = "https://github.com/gfx-rs/d3d12-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "be19a243b86e0bafb9937d661fc8eabb3e42b44e"
|
||||
|
||||
[source."https://github.com/badboy/failure"]
|
||||
git = "https://github.com/badboy/failure"
|
||||
|
|
|
@ -1061,12 +1061,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "d3d12"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc7ed48e89905e5e146bcc1951cc3facb9e44aea9adf5dc01078cda1bd24b662"
|
||||
version = "0.3.2"
|
||||
source = "git+https://github.com/gfx-rs/d3d12-rs?rev=be19a243b86e0bafb9937d661fc8eabb3e42b44e#be19a243b86e0bafb9937d661fc8eabb3e42b44e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libloading 0.5.2",
|
||||
"libloading 0.7.0",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
|
@ -1852,8 +1851,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-auxil"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"fxhash",
|
||||
"gfx-hal",
|
||||
|
@ -1862,14 +1861,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx11"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"gfx-auxil",
|
||||
"gfx-hal",
|
||||
"libloading 0.6.2",
|
||||
"libloading 0.7.0",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"range-alloc",
|
||||
|
@ -1883,8 +1882,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx12"
|
||||
version = "0.6.2"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
|
@ -1903,8 +1902,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-empty"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"gfx-hal",
|
||||
"log",
|
||||
|
@ -1913,8 +1912,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-metal"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
|
@ -1922,23 +1921,22 @@ dependencies = [
|
|||
"cocoa-foundation",
|
||||
"copyless",
|
||||
"foreign-types",
|
||||
"gfx-auxil",
|
||||
"fxhash",
|
||||
"gfx-hal",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"metal",
|
||||
"naga",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"range-alloc",
|
||||
"raw-window-handle",
|
||||
"spirv_cross",
|
||||
"storage-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gfx-backend-vulkan"
|
||||
version = "0.6.5"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"ash",
|
||||
|
@ -1946,9 +1944,10 @@ dependencies = [
|
|||
"core-graphics-types",
|
||||
"gfx-hal",
|
||||
"inplace_it",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"naga",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"raw-window-handle",
|
||||
"smallvec",
|
||||
"winapi 0.3.9",
|
||||
|
@ -1956,11 +1955,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-hal"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"naga",
|
||||
"raw-window-handle",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2184,35 +2185,41 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gpu-alloc"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/zakarumych/gpu-alloc?rev=d07be73f9439a37c89f5b72f2500cbf0eb4ff613#d07be73f9439a37c89f5b72f2500cbf0eb4ff613"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7724b9aef57ea36d70faf54e0ee6265f86e41de16bed8333efdeab5b00e16b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gpu-alloc-types",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpu-alloc-types"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zakarumych/gpu-alloc?rev=d07be73f9439a37c89f5b72f2500cbf0eb4ff613#d07be73f9439a37c89f5b72f2500cbf0eb4ff613"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpu-descriptor"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gpu-descriptor-types",
|
||||
"hashbrown",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpu-descriptor-types"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -2477,9 +2484,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "inplace_it"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd01a2a73f2f399df96b22dc88ea687ef4d76226284e7531ae3c7ee1dc5cb534"
|
||||
checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
|
@ -2780,6 +2787,16 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.20.1"
|
||||
|
@ -3050,9 +3067,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "metal"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c4e8a431536529327e28c9ba6992f2cb0c15d4222f0602a16e6d7695ff3bccf"
|
||||
version = "0.21.0"
|
||||
source = "git+https://github.com/gfx-rs/metal-rs?rev=439c986eb7a9b91e88b61def2daa66e4043fcbef#439c986eb7a9b91e88b61def2daa66e4043fcbef"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
|
@ -3325,9 +3341,10 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
|
|||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=96c80738650822de35f77ab6a589f309460c8f39#96c80738650822de35f77ab6a589f309460c8f39"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/gfx-rs/naga?tag=gfx-12#fa7d4d8b51d4eeffe9f648d285466637f733a4a1"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"fxhash",
|
||||
"log",
|
||||
|
@ -4155,8 +4172,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "range-alloc"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/gfx-rs/gfx?rev=0a201d1c406b5119ec11068293a40e50ec0be4c8#0a201d1c406b5119ec11068293a40e50ec0be4c8"
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
|
@ -4780,24 +4797,24 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "spirv-cross-internal"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/kvark/spirv_cross?branch=wgpu4#e51babbf00427984fe343e48493d8a9339fec473"
|
||||
source = "git+https://github.com/kvark/spirv_cross?branch=wgpu5#a5a90d38ab1f82ad8327b48e161dbfe556ef6c6e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv_cross"
|
||||
version = "0.22.0"
|
||||
source = "git+https://github.com/kvark/spirv_cross?branch=wgpu4#e51babbf00427984fe343e48493d8a9339fec473"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/kvark/spirv_cross?branch=wgpu5#a5a90d38ab1f82ad8327b48e161dbfe556ef6c6e"
|
||||
dependencies = [
|
||||
"spirv-cross-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv_headers"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f1418983d16481227ffa3ab3cf44ef92eebc9a76c092fbcd4c51a64ff032622"
|
||||
checksum = "1f5b132530b1ac069df335577e3581765995cba5a13995cdbbdbc8fb057c532c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"num-traits",
|
||||
|
@ -5441,9 +5458,21 @@ checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
|
|||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"pin-project-lite 0.1.4",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.17"
|
||||
|
@ -5897,7 +5926,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
|
@ -5924,7 +5953,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"serde",
|
||||
|
|
|
@ -73,7 +73,7 @@ libudev-sys = { path = "dom/webauthn/libudev-sys" }
|
|||
packed_simd = { git = "https://github.com/hsivonen/packed_simd", rev="0917fe780032a6bbb23d71be545f9c1834128d75" }
|
||||
rlbox_lucet_sandbox = { git = "https://github.com/PLSysSec/rlbox_lucet_sandbox/", rev="f3cace4fb8b53db0849c62af4fa62bade5a620f7" }
|
||||
nix = { git = "https://github.com/shravanrn/nix/", rev="4af6c367603869a30fddb5ffb0aba2b9477ba92e" }
|
||||
spirv_cross = { git = "https://github.com/kvark/spirv_cross", branch = "wgpu4" }
|
||||
spirv_cross = { git = "https://github.com/kvark/spirv_cross", branch = "wgpu5" }
|
||||
# failure's backtrace feature might break our builds, see bug 1608157.
|
||||
failure = { git = "https://github.com/badboy/failure", rev = "64af847bc5fdcb6d2438bec8a6030812a80519a5" }
|
||||
failure_derive = { git = "https://github.com/badboy/failure", rev = "64af847bc5fdcb6d2438bec8a6030812a80519a5" }
|
||||
|
|
|
@ -1326,6 +1326,12 @@ DOMInterfaces = {
|
|||
'GPUAdapter': {
|
||||
'nativeType': 'mozilla::webgpu::Adapter',
|
||||
},
|
||||
'GPUAdapterFeatures': {
|
||||
'nativeType': 'mozilla::webgpu::AdapterFeatures',
|
||||
},
|
||||
'GPUAdapterLimits': {
|
||||
'nativeType': 'mozilla::webgpu::AdapterLimits',
|
||||
},
|
||||
'GPUBindGroup': {
|
||||
'nativeType': 'mozilla::webgpu::BindGroup',
|
||||
},
|
||||
|
@ -1345,6 +1351,12 @@ DOMInterfaces = {
|
|||
'GPUCommandEncoder': {
|
||||
'nativeType': 'mozilla::webgpu::CommandEncoder',
|
||||
},
|
||||
'GPUCompilationInfo': {
|
||||
'nativeType': 'mozilla::webgpu::CompilationInfo',
|
||||
},
|
||||
'GPUCompilationMessage': {
|
||||
'nativeType': 'mozilla::webgpu::CompilationMessage',
|
||||
},
|
||||
'GPUComputePassEncoder': {
|
||||
'nativeType': 'mozilla::webgpu::ComputePassEncoder',
|
||||
},
|
||||
|
@ -1357,15 +1369,15 @@ DOMInterfaces = {
|
|||
'GPUDeviceLostInfo': {
|
||||
'nativeType': 'mozilla::webgpu::DeviceLostInfo',
|
||||
},
|
||||
'GPUFence': {
|
||||
'nativeType': 'mozilla::webgpu::Fence',
|
||||
},
|
||||
'GPUOutOfMemoryError': {
|
||||
'nativeType': 'mozilla::webgpu::OutOfMemoryError',
|
||||
},
|
||||
'GPUPipelineLayout': {
|
||||
'nativeType': 'mozilla::webgpu::PipelineLayout',
|
||||
},
|
||||
'GPUQuerySet': {
|
||||
'nativeType': 'mozilla::webgpu::QuerySet',
|
||||
},
|
||||
'GPUQueue': {
|
||||
'nativeType': 'mozilla::webgpu::Queue',
|
||||
},
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
#include "Adapter.h"
|
||||
|
||||
#include "AdapterFeatures.h"
|
||||
#include "AdapterLimits.h"
|
||||
#include "Device.h"
|
||||
#include "Instance.h"
|
||||
#include "ipc/WebGPUChild.h"
|
||||
|
@ -29,6 +31,9 @@ void Adapter::Cleanup() {
|
|||
}
|
||||
}
|
||||
|
||||
const RefPtr<AdapterFeatures>& Adapter::Features() const { return mFeatures; }
|
||||
const RefPtr<AdapterLimits>& Adapter::Limits() const { return mLimits; }
|
||||
|
||||
already_AddRefed<dom::Promise> Adapter::RequestDevice(
|
||||
const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) {
|
||||
RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
|
||||
|
|
|
@ -21,6 +21,8 @@ struct GPUFeatures;
|
|||
} // namespace dom
|
||||
|
||||
namespace webgpu {
|
||||
class AdapterFeatures;
|
||||
class AdapterLimits;
|
||||
class Device;
|
||||
class Instance;
|
||||
class WebGPUChild;
|
||||
|
@ -33,16 +35,19 @@ class Adapter final : public ObjectBase, public ChildOf<Instance> {
|
|||
RefPtr<WebGPUChild> mBridge;
|
||||
|
||||
private:
|
||||
Adapter() = delete;
|
||||
~Adapter();
|
||||
void Cleanup();
|
||||
|
||||
const RawId mId;
|
||||
const nsString mName;
|
||||
RefPtr<AdapterFeatures> mFeatures;
|
||||
RefPtr<AdapterLimits> mLimits;
|
||||
|
||||
public:
|
||||
explicit Adapter(Instance* const aParent, RawId aId);
|
||||
Adapter(Instance* const aParent, RawId aId);
|
||||
void GetName(nsString& out) const { out = mName; }
|
||||
const RefPtr<AdapterFeatures>& Features() const;
|
||||
const RefPtr<AdapterLimits>& Limits() const;
|
||||
|
||||
already_AddRefed<dom::Promise> RequestDevice(
|
||||
const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AdapterFeatures.h"
|
||||
#include "Adapter.h"
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(AdapterFeatures, mParent)
|
||||
GPU_IMPL_JS_WRAP(AdapterFeatures)
|
||||
|
||||
AdapterFeatures::AdapterFeatures(Adapter* const aParent) : ChildOf(aParent) {}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GPU_AdapterFeatures_H_
|
||||
#define GPU_AdapterFeatures_H_
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "ObjectModel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
class Adapter;
|
||||
|
||||
class AdapterFeatures final : public nsWrapperCache, public ChildOf<Adapter> {
|
||||
public:
|
||||
GPU_DECL_CYCLE_COLLECTION(AdapterFeatures)
|
||||
GPU_DECL_JS_WRAP(AdapterFeatures)
|
||||
|
||||
private:
|
||||
explicit AdapterFeatures(Adapter* const aParent);
|
||||
~AdapterFeatures() = default;
|
||||
void Cleanup() {}
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GPU_AdapterFeatures_H_
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AdapterLimits.h"
|
||||
#include "Adapter.h"
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
#include "mozilla/webgpu/ffi/wgpu.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(AdapterLimits, mParent)
|
||||
GPU_IMPL_JS_WRAP(AdapterLimits)
|
||||
|
||||
AdapterLimits::AdapterLimits(Adapter* const aParent,
|
||||
const ffi::WGPULimits& aLimits)
|
||||
: ChildOf(aParent), mLimits(new ffi::WGPULimits(aLimits)) {}
|
||||
|
||||
AdapterLimits::~AdapterLimits() = default;
|
||||
|
||||
uint32_t AdapterLimits::MaxTextureDimension1D() const {
|
||||
return mLimits->max_texture_dimension_1d;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxTextureDimension2D() const {
|
||||
return mLimits->max_texture_dimension_2d;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxTextureDimension3D() const {
|
||||
return mLimits->max_texture_dimension_3d;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxTextureArrayLayers() const {
|
||||
return mLimits->max_texture_array_layers;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxBindGroups() const {
|
||||
return mLimits->max_bind_groups;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxDynamicUniformBuffersPerPipelineLayout() const {
|
||||
return mLimits->max_dynamic_uniform_buffers_per_pipeline_layout;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxDynamicStorageBuffersPerPipelineLayout() const {
|
||||
return mLimits->max_dynamic_storage_buffers_per_pipeline_layout;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxSampledTexturesPerShaderStage() const {
|
||||
return mLimits->max_sampled_textures_per_shader_stage;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxSamplersPerShaderStage() const {
|
||||
return mLimits->max_samplers_per_shader_stage;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxStorageBuffersPerShaderStage() const {
|
||||
return mLimits->max_storage_buffers_per_shader_stage;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxStorageTexturesPerShaderStage() const {
|
||||
return mLimits->max_storage_textures_per_shader_stage;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxUniformBuffersPerShaderStage() const {
|
||||
return mLimits->max_uniform_buffers_per_shader_stage;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxUniformBufferBindingSize() const {
|
||||
return mLimits->max_uniform_buffer_binding_size;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxStorageBufferBindingSize() const {
|
||||
return mLimits->max_storage_buffer_binding_size;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxVertexBuffers() const {
|
||||
return mLimits->max_vertex_buffers;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxVertexAttributes() const {
|
||||
return mLimits->max_vertex_attributes;
|
||||
}
|
||||
uint32_t AdapterLimits::MaxVertexBufferArrayStride() const {
|
||||
return mLimits->max_vertex_buffer_array_stride;
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GPU_AdapterLimitss_H_
|
||||
#define GPU_AdapterLimitss_H_
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "ObjectModel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
namespace ffi {
|
||||
struct WGPULimits;
|
||||
}
|
||||
class Adapter;
|
||||
|
||||
class AdapterLimits final : public nsWrapperCache, public ChildOf<Adapter> {
|
||||
const UniquePtr<ffi::WGPULimits> mLimits;
|
||||
|
||||
public:
|
||||
GPU_DECL_CYCLE_COLLECTION(AdapterLimits)
|
||||
GPU_DECL_JS_WRAP(AdapterLimits)
|
||||
|
||||
uint32_t MaxTextureDimension1D() const;
|
||||
uint32_t MaxTextureDimension2D() const;
|
||||
uint32_t MaxTextureDimension3D() const;
|
||||
uint32_t MaxTextureArrayLayers() const;
|
||||
uint32_t MaxBindGroups() const;
|
||||
uint32_t MaxDynamicUniformBuffersPerPipelineLayout() const;
|
||||
uint32_t MaxDynamicStorageBuffersPerPipelineLayout() const;
|
||||
uint32_t MaxSampledTexturesPerShaderStage() const;
|
||||
uint32_t MaxSamplersPerShaderStage() const;
|
||||
uint32_t MaxStorageBuffersPerShaderStage() const;
|
||||
uint32_t MaxStorageTexturesPerShaderStage() const;
|
||||
uint32_t MaxUniformBuffersPerShaderStage() const;
|
||||
uint32_t MaxUniformBufferBindingSize() const;
|
||||
uint32_t MaxStorageBufferBindingSize() const;
|
||||
uint32_t MaxVertexBuffers() const;
|
||||
uint32_t MaxVertexAttributes() const;
|
||||
uint32_t MaxVertexBufferArrayStride() const;
|
||||
|
||||
private:
|
||||
AdapterLimits(Adapter* const aParent, const ffi::WGPULimits& aLimits);
|
||||
~AdapterLimits();
|
||||
void Cleanup() {}
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GPU_AdapterLimitss_H_
|
|
@ -66,6 +66,11 @@ bool CanvasContext::UpdateWebRenderCanvasData(
|
|||
return true;
|
||||
}
|
||||
|
||||
dom::GPUTextureFormat CanvasContext::GetSwapChainPreferredFormat(
|
||||
Adapter&) const {
|
||||
return dom::GPUTextureFormat::Bgra8unorm;
|
||||
}
|
||||
|
||||
RefPtr<SwapChain> CanvasContext::ConfigureSwapChain(
|
||||
const dom::GPUSwapChainDescriptor& aDesc, ErrorResult& aRv) {
|
||||
Cleanup();
|
||||
|
@ -86,7 +91,7 @@ RefPtr<SwapChain> CanvasContext::ConfigureSwapChain(
|
|||
dom::GPUExtent3DDict extent;
|
||||
extent.mWidth = mWidth;
|
||||
extent.mHeight = mHeight;
|
||||
extent.mDepth = 1;
|
||||
extent.mDepthOrArrayLayers = 1;
|
||||
mSwapChain = new SwapChain(aDesc, extent, mExternalImageId, format);
|
||||
|
||||
// Force a new frame to be built, which will execute the
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Promise;
|
||||
enum class GPUTextureFormat : uint8_t;
|
||||
} // namespace dom
|
||||
namespace layers {
|
||||
class WebRenderLocalCanvasData;
|
||||
};
|
||||
namespace webgpu {
|
||||
class Device;
|
||||
class Adapter;
|
||||
class SwapChain;
|
||||
class Texture;
|
||||
|
||||
|
@ -97,6 +98,7 @@ class CanvasContext final : public nsICanvasRenderingContextInternal,
|
|||
bool IsContextCleanForFrameCapture() override { return false; }
|
||||
|
||||
public:
|
||||
dom::GPUTextureFormat GetSwapChainPreferredFormat(Adapter& aAdapter) const;
|
||||
RefPtr<SwapChain> ConfigureSwapChain(const dom::GPUSwapChainDescriptor& aDesc,
|
||||
ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -60,12 +60,12 @@ void CommandEncoder::ConvertExtent3DToFFI(const dom::GPUExtent3D& aExtent,
|
|||
const auto& seq = aExtent.GetAsRangeEnforcedUnsignedLongSequence();
|
||||
aExtentFFI->width = seq.Length() > 0 ? seq[0] : 0;
|
||||
aExtentFFI->height = seq.Length() > 1 ? seq[1] : 0;
|
||||
aExtentFFI->depth = seq.Length() > 2 ? seq[2] : 0;
|
||||
aExtentFFI->depth_or_array_layers = seq.Length() > 2 ? seq[2] : 0;
|
||||
} else if (aExtent.IsGPUExtent3DDict()) {
|
||||
const auto& dict = aExtent.GetAsGPUExtent3DDict();
|
||||
aExtentFFI->width = dict.mWidth;
|
||||
aExtentFFI->height = dict.mHeight;
|
||||
aExtentFFI->depth = dict.mDepth;
|
||||
aExtentFFI->depth_or_array_layers = dict.mDepthOrArrayLayers;
|
||||
} else {
|
||||
MOZ_CRASH("Unexptected extent type");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CompilationInfo.h"
|
||||
#include "CompilationMessage.h"
|
||||
#include "ShaderModule.h"
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(CompilationInfo, mParent)
|
||||
GPU_IMPL_JS_WRAP(CompilationInfo)
|
||||
|
||||
CompilationInfo::CompilationInfo(ShaderModule* const aParent)
|
||||
: ChildOf(aParent) {}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GPU_CompilationInfo_H_
|
||||
#define GPU_CompilationInfo_H_
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "ObjectModel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
class ShaderModule;
|
||||
|
||||
class CompilationInfo final : public nsWrapperCache,
|
||||
public ChildOf<ShaderModule> {
|
||||
public:
|
||||
GPU_DECL_CYCLE_COLLECTION(CompilationInfo)
|
||||
GPU_DECL_JS_WRAP(CompilationInfo)
|
||||
|
||||
private:
|
||||
explicit CompilationInfo(ShaderModule* const aParent);
|
||||
~CompilationInfo() = default;
|
||||
void Cleanup() {}
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GPU_CompilationInfo_H_
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CompilationMessage.h"
|
||||
#include "CompilationInfo.h"
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(CompilationMessage, mParent)
|
||||
GPU_IMPL_JS_WRAP(CompilationMessage)
|
||||
|
||||
CompilationMessage::CompilationMessage(CompilationInfo* const aParent)
|
||||
: ChildOf(aParent) {}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GPU_CompilationMessage_H_
|
||||
#define GPU_CompilationMessage_H_
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "ObjectModel.h"
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class DOMString;
|
||||
} // namespace dom
|
||||
namespace webgpu {
|
||||
class CompilationInfo;
|
||||
|
||||
class CompilationMessage final : public nsWrapperCache,
|
||||
public ChildOf<CompilationInfo> {
|
||||
dom::GPUCompilationMessageType mType = dom::GPUCompilationMessageType::Error;
|
||||
uint64_t mLineNum = 0;
|
||||
uint64_t mLinePos = 0;
|
||||
|
||||
public:
|
||||
GPU_DECL_CYCLE_COLLECTION(CompilationMessage)
|
||||
GPU_DECL_JS_WRAP(CompilationMessage)
|
||||
|
||||
void GetMessage(dom::DOMString& aMessage) {}
|
||||
dom::GPUCompilationMessageType Type() const { return mType; }
|
||||
uint64_t LineNum() const { return mLineNum; }
|
||||
uint64_t LinePos() const { return mLinePos; }
|
||||
|
||||
private:
|
||||
explicit CompilationMessage(CompilationInfo* const aParent);
|
||||
~CompilationMessage() = default;
|
||||
void Cleanup() {}
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GPU_CompilationMessage_H_
|
|
@ -52,7 +52,7 @@ Device::Device(Adapter* const aParent, RawId aId)
|
|||
: DOMEventTargetHelper(aParent->GetParentObject()),
|
||||
mId(aId),
|
||||
mBridge(aParent->mBridge),
|
||||
mQueue(new Queue(this, aParent->mBridge, aId)) {
|
||||
mQueue(new class Queue(this, aParent->mBridge, aId)) {
|
||||
mBridge->RegisterDevice(mId, this);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ void Device::Cleanup() {
|
|||
void Device::GetLabel(nsAString& aValue) const { aValue = mLabel; }
|
||||
void Device::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
|
||||
|
||||
Queue* Device::DefaultQueue() const { return mQueue; }
|
||||
Queue* Device::Queue() const { return mQueue; }
|
||||
|
||||
already_AddRefed<Buffer> Device::CreateBuffer(
|
||||
const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
|
||||
|
@ -98,14 +98,12 @@ already_AddRefed<Buffer> Device::CreateBuffer(
|
|||
// If the buffer is not mapped at creation, and it has Shmem, we send it
|
||||
// to the GPU process. Otherwise, we keep it.
|
||||
RawId id = mBridge->DeviceCreateBuffer(mId, aDesc);
|
||||
if (hasMapFlags && !aDesc.mMappedAtCreation) {
|
||||
mBridge->SendBufferReturnShmem(id, std::move(shmem));
|
||||
}
|
||||
RefPtr<Buffer> buffer = new Buffer(this, id, aDesc.mSize);
|
||||
|
||||
if (aDesc.mMappedAtCreation) {
|
||||
buffer->SetMapped(std::move(shmem),
|
||||
!(aDesc.mUsage & dom::GPUBufferUsage_Binding::MAP_READ));
|
||||
} else if (hasMapFlags) {
|
||||
mBridge->SendBufferReturnShmem(id, std::move(shmem));
|
||||
}
|
||||
|
||||
return buffer.forget();
|
||||
|
@ -187,11 +185,8 @@ already_AddRefed<BindGroup> Device::CreateBindGroup(
|
|||
}
|
||||
|
||||
already_AddRefed<ShaderModule> Device::CreateShaderModule(
|
||||
const dom::GPUShaderModuleDescriptor& aDesc) {
|
||||
if (aDesc.mCode.IsString()) {
|
||||
// we don't yet support WGSL
|
||||
return nullptr;
|
||||
}
|
||||
JSContext* aCx, const dom::GPUShaderModuleDescriptor& aDesc) {
|
||||
Unused << aCx;
|
||||
RawId id = mBridge->DeviceCreateShaderModule(mId, aDesc);
|
||||
RefPtr<ShaderModule> object = new ShaderModule(this, id);
|
||||
return object.forget();
|
||||
|
@ -240,5 +235,9 @@ already_AddRefed<Texture> Device::InitSwapChain(
|
|||
return CreateTexture(desc);
|
||||
}
|
||||
|
||||
void Device::Destroy() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -105,7 +105,7 @@ class Device final : public DOMEventTargetHelper {
|
|||
void GetLabel(nsAString& aValue) const;
|
||||
void SetLabel(const nsAString& aLabel);
|
||||
|
||||
Queue* DefaultQueue() const;
|
||||
Queue* Queue() const;
|
||||
|
||||
already_AddRefed<Buffer> CreateBuffer(const dom::GPUBufferDescriptor& aDesc,
|
||||
ErrorResult& aRv);
|
||||
|
@ -126,12 +126,14 @@ class Device final : public DOMEventTargetHelper {
|
|||
const dom::GPUBindGroupDescriptor& aDesc);
|
||||
|
||||
already_AddRefed<ShaderModule> CreateShaderModule(
|
||||
const dom::GPUShaderModuleDescriptor& aDesc);
|
||||
JSContext* aCx, const dom::GPUShaderModuleDescriptor& aDesc);
|
||||
already_AddRefed<ComputePipeline> CreateComputePipeline(
|
||||
const dom::GPUComputePipelineDescriptor& aDesc);
|
||||
already_AddRefed<RenderPipeline> CreateRenderPipeline(
|
||||
const dom::GPURenderPipelineDescriptor& aDesc);
|
||||
|
||||
void Destroy();
|
||||
|
||||
IMPL_EVENT_HANDLER(uncapturederror)
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "ObjectModel.h"
|
||||
|
||||
#include "Adapter.h"
|
||||
#include "ShaderModule.h"
|
||||
#include "CompilationInfo.h"
|
||||
#include "Device.h"
|
||||
#include "CommandEncoder.h"
|
||||
#include "Instance.h"
|
||||
|
@ -29,6 +31,8 @@ void ObjectBase::GetLabel(nsAString& aValue) const { aValue = mLabel; }
|
|||
void ObjectBase::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
|
||||
|
||||
template class ChildOf<Adapter>;
|
||||
template class ChildOf<ShaderModule>;
|
||||
template class ChildOf<CompilationInfo>;
|
||||
template class ChildOf<CommandEncoder>;
|
||||
template class ChildOf<Device>;
|
||||
template class ChildOf<Instance>;
|
||||
|
|
|
@ -3,15 +3,22 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Fence.h"
|
||||
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
#include "QuerySet.h"
|
||||
|
||||
#include "Device.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(Fence, mParent)
|
||||
GPU_IMPL_JS_WRAP(Fence)
|
||||
QuerySet::~QuerySet() = default;
|
||||
|
||||
GPU_IMPL_CYCLE_COLLECTION(QuerySet, mParent)
|
||||
GPU_IMPL_JS_WRAP(QuerySet)
|
||||
|
||||
void QuerySet::Destroy() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
|
@ -3,34 +3,31 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GPU_Fence_H_
|
||||
#define GPU_Fence_H_
|
||||
#ifndef GPU_QuerySet_H_
|
||||
#define GPU_QuerySet_H_
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "ObjectModel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Promise;
|
||||
} // namespace dom
|
||||
namespace webgpu {
|
||||
|
||||
class Device;
|
||||
|
||||
class Fence final : public ObjectBase, public ChildOf<Device> {
|
||||
class QuerySet final : public ObjectBase, public ChildOf<Device> {
|
||||
public:
|
||||
GPU_DECL_CYCLE_COLLECTION(Fence)
|
||||
GPU_DECL_JS_WRAP(Fence)
|
||||
GPU_DECL_CYCLE_COLLECTION(QuerySet)
|
||||
GPU_DECL_JS_WRAP(QuerySet)
|
||||
|
||||
QuerySet() = delete;
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
Fence() = delete;
|
||||
~Fence() = default;
|
||||
virtual ~QuerySet();
|
||||
void Cleanup() {}
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GPU_Fence_H_
|
||||
#endif // GPU_QuerySet_H_
|
|
@ -4,6 +4,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "Queue.h"
|
||||
|
||||
#include "CommandBuffer.h"
|
||||
|
@ -36,20 +37,36 @@ void Queue::Submit(
|
|||
}
|
||||
|
||||
void Queue::WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
||||
const dom::ArrayBuffer& aData, uint64_t aDataOffset,
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||
uint64_t aDataOffset,
|
||||
const dom::Optional<uint64_t>& aSize,
|
||||
ErrorResult& aRv) {
|
||||
aData.ComputeState();
|
||||
const auto checkedSize =
|
||||
aSize.WasPassed() ? CheckedInt<size_t>(aSize.Value())
|
||||
: CheckedInt<size_t>(aData.Length()) - aDataOffset;
|
||||
uint64_t length = 0;
|
||||
uint8_t* data = nullptr;
|
||||
if (aData.IsArrayBufferView()) {
|
||||
const auto& view = aData.GetAsArrayBufferView();
|
||||
view.ComputeState();
|
||||
length = view.Length();
|
||||
data = view.Data();
|
||||
}
|
||||
if (aData.IsArrayBuffer()) {
|
||||
const auto& ab = aData.GetAsArrayBuffer();
|
||||
ab.ComputeState();
|
||||
length = ab.Length();
|
||||
data = ab.Data();
|
||||
}
|
||||
MOZ_ASSERT(data != nullptr);
|
||||
|
||||
const auto checkedSize = aSize.WasPassed()
|
||||
? CheckedInt<size_t>(aSize.Value())
|
||||
: CheckedInt<size_t>(length) - aDataOffset;
|
||||
if (!checkedSize.isValid()) {
|
||||
aRv.ThrowRangeError("Mapped size is too large");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& size = checkedSize.value();
|
||||
if (aDataOffset + size > aData.Length()) {
|
||||
if (aDataOffset + size > length) {
|
||||
aRv.ThrowAbortError(nsPrintfCString("Wrong data size %" PRIuPTR, size));
|
||||
return;
|
||||
}
|
||||
|
@ -62,13 +79,13 @@ void Queue::WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
|||
return;
|
||||
}
|
||||
|
||||
memcpy(shmem.get<uint8_t>(), aData.Data() + aDataOffset, size);
|
||||
memcpy(shmem.get<uint8_t>(), data + aDataOffset, size);
|
||||
mBridge->SendQueueWriteBuffer(mId, aBuffer.mId, aBufferOffset,
|
||||
std::move(shmem));
|
||||
}
|
||||
|
||||
void Queue::WriteTexture(const dom::GPUTextureCopyView& aDestination,
|
||||
const dom::ArrayBuffer& aData,
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||
const dom::GPUTextureDataLayout& aDataLayout,
|
||||
const dom::GPUExtent3D& aSize, ErrorResult& aRv) {
|
||||
ffi::WGPUTextureCopyView copyView = {};
|
||||
|
@ -79,21 +96,37 @@ void Queue::WriteTexture(const dom::GPUTextureCopyView& aDestination,
|
|||
ffi::WGPUExtent3d extent = {};
|
||||
CommandEncoder::ConvertExtent3DToFFI(aSize, &extent);
|
||||
|
||||
uint64_t availableSize = 0;
|
||||
uint8_t* data = nullptr;
|
||||
if (aData.IsArrayBufferView()) {
|
||||
const auto& view = aData.GetAsArrayBufferView();
|
||||
view.ComputeState();
|
||||
availableSize = view.Length();
|
||||
data = view.Data();
|
||||
}
|
||||
if (aData.IsArrayBuffer()) {
|
||||
const auto& ab = aData.GetAsArrayBuffer();
|
||||
ab.ComputeState();
|
||||
availableSize = ab.Length();
|
||||
data = ab.Data();
|
||||
}
|
||||
MOZ_ASSERT(data != nullptr);
|
||||
|
||||
const auto bpb = aDestination.mTexture->mBytesPerBlock;
|
||||
if (!bpb) {
|
||||
aRv.ThrowAbortError(nsPrintfCString("Invalid texture format"));
|
||||
return;
|
||||
}
|
||||
if (extent.width == 0 || extent.height == 0 || extent.depth == 0) {
|
||||
if (extent.width == 0 || extent.height == 0 ||
|
||||
extent.depth_or_array_layers == 0) {
|
||||
aRv.ThrowAbortError(nsPrintfCString("Invalid copy size"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: support block-compressed formats
|
||||
aData.ComputeState();
|
||||
const auto fullRows =
|
||||
(CheckedInt<size_t>(extent.depth - 1) * aDataLayout.mRowsPerImage +
|
||||
extent.height - 1);
|
||||
const auto fullRows = (CheckedInt<size_t>(extent.depth_or_array_layers - 1) *
|
||||
aDataLayout.mRowsPerImage +
|
||||
extent.height - 1);
|
||||
const auto checkedSize = fullRows * aDataLayout.mBytesPerRow +
|
||||
CheckedInt<size_t>(extent.width) * bpb.value();
|
||||
if (!checkedSize.isValid()) {
|
||||
|
@ -102,7 +135,6 @@ void Queue::WriteTexture(const dom::GPUTextureCopyView& aDestination,
|
|||
}
|
||||
|
||||
const auto& size = checkedSize.value();
|
||||
auto availableSize = aData.Length();
|
||||
if (availableSize < aDataLayout.mOffset ||
|
||||
size > (availableSize - aDataLayout.mOffset)) {
|
||||
aRv.ThrowAbortError(nsPrintfCString("Wrong data size %" PRIuPTR, size));
|
||||
|
@ -117,7 +149,7 @@ void Queue::WriteTexture(const dom::GPUTextureCopyView& aDestination,
|
|||
return;
|
||||
}
|
||||
|
||||
memcpy(shmem.get<uint8_t>(), aData.Data() + aDataLayout.mOffset, size);
|
||||
memcpy(shmem.get<uint8_t>(), data + aDataLayout.mOffset, size);
|
||||
mBridge->SendQueueWriteTexture(mId, copyView, std::move(shmem), dataLayout,
|
||||
extent);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace mozilla {
|
|||
class ErrorResult;
|
||||
namespace dom {
|
||||
class RangeEnforcedUnsignedLongSequenceOrGPUExtent3DDict;
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
template <typename T>
|
||||
class Optional;
|
||||
template <typename T>
|
||||
|
@ -40,16 +41,16 @@ class Queue final : public ObjectBase, public ChildOf<Device> {
|
|||
const dom::Sequence<OwningNonNull<CommandBuffer>>& aCommandBuffers);
|
||||
|
||||
void WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
||||
const dom::ArrayBuffer& adata, uint64_t aDataOffset,
|
||||
const dom::Optional<uint64_t>& aSize, ErrorResult& aRv);
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||
uint64_t aDataOffset, const dom::Optional<uint64_t>& aSize,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void WriteTexture(const dom::GPUTextureCopyView& aDestination,
|
||||
const dom::ArrayBuffer& aData,
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||
const dom::GPUTextureDataLayout& aDataLayout,
|
||||
const dom::GPUExtent3D& aSize, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
Queue() = delete;
|
||||
virtual ~Queue();
|
||||
void Cleanup() {}
|
||||
|
||||
|
|
|
@ -162,11 +162,16 @@ void RenderPassEncoder::SetPipeline(const RenderPipeline& aPipeline) {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderPassEncoder::SetIndexBuffer(const Buffer& aBuffer, uint64_t aOffset,
|
||||
uint64_t aSize) {
|
||||
void RenderPassEncoder::SetIndexBuffer(const Buffer& aBuffer,
|
||||
const dom::GPUIndexFormat& aIndexFormat,
|
||||
uint64_t aOffset, uint64_t aSize) {
|
||||
if (mValid) {
|
||||
mUsedBuffers.AppendElement(&aBuffer);
|
||||
ffi::wgpu_render_pass_set_index_buffer(mPass, aBuffer.mId, aOffset, aSize);
|
||||
const auto iformat = aIndexFormat == dom::GPUIndexFormat::Uint32
|
||||
? ffi::WGPUIndexFormat_Uint32
|
||||
: ffi::WGPUIndexFormat_Uint16;
|
||||
ffi::wgpu_render_pass_set_index_buffer(mPass, aBuffer.mId, iformat, aOffset,
|
||||
aSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ class RenderPassEncoder final : public ObjectBase,
|
|||
void SetBindGroup(uint32_t aSlot, const BindGroup& aBindGroup,
|
||||
const dom::Sequence<uint32_t>& aDynamicOffsets);
|
||||
void SetPipeline(const RenderPipeline& aPipeline);
|
||||
void SetIndexBuffer(const Buffer& aBuffer, uint64_t aOffset, uint64_t aSize);
|
||||
void SetIndexBuffer(const Buffer& aBuffer,
|
||||
const dom::GPUIndexFormat& aIndexFormat, uint64_t aOffset,
|
||||
uint64_t aSize);
|
||||
void SetVertexBuffer(uint32_t aSlot, const Buffer& aBuffer, uint64_t aOffset,
|
||||
uint64_t aSize);
|
||||
void Draw(uint32_t aVertexCount, uint32_t aInstanceCount,
|
||||
|
|
|
@ -59,10 +59,32 @@ static Maybe<uint8_t> GetBytesPerBlock(dom::GPUTextureFormat format) {
|
|||
case dom::GPUTextureFormat::Rgba32sint:
|
||||
case dom::GPUTextureFormat::Rgba32float:
|
||||
return Some<uint8_t>(16u);
|
||||
case dom::GPUTextureFormat::Stencil8:
|
||||
return Some<uint8_t>(1u);
|
||||
case dom::GPUTextureFormat::Depth16unorm:
|
||||
return Some<uint8_t>(2u);
|
||||
case dom::GPUTextureFormat::Depth32float:
|
||||
return Some<uint8_t>(4u);
|
||||
case dom::GPUTextureFormat::Bc1_rgba_unorm:
|
||||
case dom::GPUTextureFormat::Bc1_rgba_unorm_srgb:
|
||||
case dom::GPUTextureFormat::Bc4_r_unorm:
|
||||
case dom::GPUTextureFormat::Bc4_r_snorm:
|
||||
return Some<uint8_t>(8u);
|
||||
case dom::GPUTextureFormat::Bc2_rgba_unorm:
|
||||
case dom::GPUTextureFormat::Bc2_rgba_unorm_srgb:
|
||||
case dom::GPUTextureFormat::Bc3_rgba_unorm:
|
||||
case dom::GPUTextureFormat::Bc3_rgba_unorm_srgb:
|
||||
case dom::GPUTextureFormat::Bc5_rg_unorm:
|
||||
case dom::GPUTextureFormat::Bc5_rg_snorm:
|
||||
case dom::GPUTextureFormat::Bc6h_rgb_ufloat:
|
||||
case dom::GPUTextureFormat::Bc6h_rgb_float:
|
||||
case dom::GPUTextureFormat::Bc7_rgba_unorm:
|
||||
case dom::GPUTextureFormat::Bc7_rgba_unorm_srgb:
|
||||
return Some<uint8_t>(16u);
|
||||
case dom::GPUTextureFormat::Depth24plus:
|
||||
case dom::GPUTextureFormat::Depth24plus_stencil8:
|
||||
case dom::GPUTextureFormat::Depth24unorm_stencil8:
|
||||
case dom::GPUTextureFormat::Depth32float_stencil8:
|
||||
case dom::GPUTextureFormat::EndGuard_:
|
||||
return Nothing();
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ using wr::ExternalImageId from "mozilla/webrender/WebRenderAPI.h";
|
|||
using RawId from "mozilla/webgpu/WebGPUTypes.h";
|
||||
using BufferAddress from "mozilla/webgpu/WebGPUTypes.h";
|
||||
using dom::GPURequestAdapterOptions from "mozilla/dom/WebGPUBinding.h";
|
||||
using dom::GPUDeviceDescriptor from "mozilla/dom/WebGPUBinding.h";
|
||||
using dom::GPUCommandBufferDescriptor from "mozilla/dom/WebGPUBinding.h";
|
||||
using webgpu::ffi::WGPUDeviceDescriptor from "mozilla/webgpu/ffi/wgpu.h";
|
||||
using webgpu::ffi::WGPUTextureDataLayout from "mozilla/webgpu/ffi/wgpu.h";
|
||||
using webgpu::ffi::WGPUTextureCopyView from "mozilla/webgpu/ffi/wgpu.h";
|
||||
using webgpu::ffi::WGPUExtent3d from "mozilla/webgpu/ffi/wgpu.h";
|
||||
|
@ -41,7 +41,7 @@ parent:
|
|||
async BumpImplicitBindGroupLayout(RawId pipelineId, bool isCompute, uint32_t index, RawId assignId);
|
||||
|
||||
async InstanceRequestAdapter(GPURequestAdapterOptions options, RawId[] ids) returns (RawId adapterId);
|
||||
async AdapterRequestDevice(RawId selfId, GPUDeviceDescriptor desc, RawId newId);
|
||||
async AdapterRequestDevice(RawId selfId, ByteBuf buf, RawId newId);
|
||||
async AdapterDestroy(RawId selfId);
|
||||
async BufferReturnShmem(RawId selfId, Shmem shmem);
|
||||
async BufferMap(RawId selfId, WGPUHostMap hostMap, uint64_t offset, uint64_t size) returns (Shmem sm);
|
||||
|
|
|
@ -64,7 +64,35 @@ RefPtr<RawIdPromise> WebGPUChild::InstanceRequestAdapter(
|
|||
Maybe<RawId> WebGPUChild::AdapterRequestDevice(
|
||||
RawId aSelfId, const dom::GPUDeviceDescriptor& aDesc) {
|
||||
RawId id = ffi::wgpu_client_make_device_id(mClient, aSelfId);
|
||||
if (SendAdapterRequestDevice(aSelfId, aDesc, id)) {
|
||||
|
||||
ffi::WGPUDeviceDescriptor desc = {};
|
||||
ffi::wgpu_client_fill_default_limits(&desc.limits);
|
||||
|
||||
if (aDesc.mNonGuaranteedLimits.WasPassed()) {
|
||||
for (const auto& entry : aDesc.mNonGuaranteedLimits.Value().Entries()) {
|
||||
Unused << entry; // TODO
|
||||
}
|
||||
/*desc.limits.max_bind_groups = lim.mMaxBindGroups;
|
||||
desc.limits.max_dynamic_uniform_buffers_per_pipeline_layout =
|
||||
lim.mMaxDynamicUniformBuffersPerPipelineLayout;
|
||||
desc.limits.max_dynamic_storage_buffers_per_pipeline_layout =
|
||||
lim.mMaxDynamicStorageBuffersPerPipelineLayout;
|
||||
desc.limits.max_sampled_textures_per_shader_stage =
|
||||
lim.mMaxSampledTexturesPerShaderStage;
|
||||
desc.limits.max_samplers_per_shader_stage = lim.mMaxSamplersPerShaderStage;
|
||||
desc.limits.max_storage_buffers_per_shader_stage =
|
||||
lim.mMaxStorageBuffersPerShaderStage;
|
||||
desc.limits.max_storage_textures_per_shader_stage =
|
||||
lim.mMaxStorageTexturesPerShaderStage;
|
||||
desc.limits.max_uniform_buffers_per_shader_stage =
|
||||
lim.mMaxUniformBuffersPerShaderStage;
|
||||
desc.limits.max_uniform_buffer_binding_size =
|
||||
lim.mMaxUniformBufferBindingSize;*/
|
||||
}
|
||||
|
||||
ByteBuf bb;
|
||||
ffi::wgpu_client_serialize_device_descriptor(&desc, ToFFI(&bb));
|
||||
if (SendAdapterRequestDevice(aSelfId, std::move(bb), id)) {
|
||||
return Some(id);
|
||||
}
|
||||
ffi::wgpu_client_kill_device_id(mClient, id);
|
||||
|
@ -104,12 +132,12 @@ RawId WebGPUChild::DeviceCreateTexture(RawId aSelfId,
|
|||
const auto& seq = aDesc.mSize.GetAsRangeEnforcedUnsignedLongSequence();
|
||||
desc.size.width = seq.Length() > 0 ? seq[0] : 1;
|
||||
desc.size.height = seq.Length() > 1 ? seq[1] : 1;
|
||||
desc.size.depth = seq.Length() > 2 ? seq[2] : 1;
|
||||
desc.size.depth_or_array_layers = seq.Length() > 2 ? seq[2] : 1;
|
||||
} else if (aDesc.mSize.IsGPUExtent3DDict()) {
|
||||
const auto& dict = aDesc.mSize.GetAsGPUExtent3DDict();
|
||||
desc.size.width = dict.mWidth;
|
||||
desc.size.height = dict.mHeight;
|
||||
desc.size.depth = dict.mDepth;
|
||||
desc.size.depth_or_array_layers = dict.mDepthOrArrayLayers;
|
||||
} else {
|
||||
MOZ_CRASH("Unexpected union");
|
||||
}
|
||||
|
@ -241,30 +269,33 @@ RawId WebGPUChild::DeviceCreateBindGroupLayout(
|
|||
nsTArray<OptionalData> optional(aDesc.mEntries.Length());
|
||||
for (const auto& entry : aDesc.mEntries) {
|
||||
OptionalData data = {};
|
||||
if (entry.mViewDimension.WasPassed()) {
|
||||
data.dim = ffi::WGPUTextureViewDimension(entry.mViewDimension.Value());
|
||||
}
|
||||
if (entry.mTextureComponentType.WasPassed()) {
|
||||
switch (entry.mTextureComponentType.Value()) {
|
||||
case dom::GPUTextureComponentType::Float:
|
||||
if (entry.mTexture.WasPassed()) {
|
||||
const auto& texture = entry.mTexture.Value();
|
||||
data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
|
||||
switch (texture.mSampleType) {
|
||||
case dom::GPUTextureSampleType::Float:
|
||||
data.type = ffi::WGPURawTextureSampleType_Float;
|
||||
break;
|
||||
case dom::GPUTextureComponentType::Uint:
|
||||
case dom::GPUTextureSampleType::Unfilterable_float:
|
||||
data.type = ffi::WGPURawTextureSampleType_UnfilterableFloat;
|
||||
break;
|
||||
case dom::GPUTextureSampleType::Uint:
|
||||
data.type = ffi::WGPURawTextureSampleType_Uint;
|
||||
break;
|
||||
case dom::GPUTextureComponentType::Sint:
|
||||
case dom::GPUTextureSampleType::Sint:
|
||||
data.type = ffi::WGPURawTextureSampleType_Sint;
|
||||
break;
|
||||
case dom::GPUTextureComponentType::Depth_comparison:
|
||||
case dom::GPUTextureSampleType::Depth:
|
||||
data.type = ffi::WGPURawTextureSampleType_Depth;
|
||||
break;
|
||||
default:
|
||||
case dom::GPUTextureSampleType::EndGuard_:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entry.mStorageTextureFormat.WasPassed()) {
|
||||
data.format = ffi::WGPUTextureFormat(entry.mStorageTextureFormat.Value());
|
||||
if (entry.mStorageTexture.WasPassed()) {
|
||||
const auto& texture = entry.mStorageTexture.Value();
|
||||
data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
|
||||
data.format = ffi::WGPUTextureFormat(texture.mFormat);
|
||||
}
|
||||
optional.AppendElement(data);
|
||||
}
|
||||
|
@ -275,18 +306,51 @@ RawId WebGPUChild::DeviceCreateBindGroupLayout(
|
|||
ffi::WGPUBindGroupLayoutEntry e = {};
|
||||
e.binding = entry.mBinding;
|
||||
e.visibility = entry.mVisibility;
|
||||
e.ty = ffi::WGPURawBindingType(entry.mType);
|
||||
e.multisampled = entry.mMultisampled;
|
||||
e.has_dynamic_offset = entry.mHasDynamicOffset;
|
||||
if (entry.mViewDimension.WasPassed()) {
|
||||
if (entry.mBuffer.WasPassed()) {
|
||||
switch (entry.mBuffer.Value().mType) {
|
||||
case dom::GPUBufferBindingType::Uniform:
|
||||
e.ty = ffi::WGPURawBindingType_UniformBuffer;
|
||||
break;
|
||||
case dom::GPUBufferBindingType::Storage:
|
||||
e.ty = ffi::WGPURawBindingType_StorageBuffer;
|
||||
break;
|
||||
case dom::GPUBufferBindingType::Read_only_storage:
|
||||
e.ty = ffi::WGPURawBindingType_ReadonlyStorageBuffer;
|
||||
break;
|
||||
case dom::GPUBufferBindingType::EndGuard_:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
}
|
||||
e.has_dynamic_offset = entry.mBuffer.Value().mHasDynamicOffset;
|
||||
}
|
||||
if (entry.mTexture.WasPassed()) {
|
||||
e.ty = ffi::WGPURawBindingType_SampledTexture;
|
||||
e.view_dimension = &optional[i].dim;
|
||||
}
|
||||
if (entry.mTextureComponentType.WasPassed()) {
|
||||
e.texture_sample_type = &optional[i].type;
|
||||
e.multisampled = entry.mTexture.Value().mMultisampled;
|
||||
}
|
||||
if (entry.mStorageTextureFormat.WasPassed()) {
|
||||
if (entry.mStorageTexture.WasPassed()) {
|
||||
e.ty = entry.mStorageTexture.Value().mAccess ==
|
||||
dom::GPUStorageTextureAccess::Write_only
|
||||
? ffi::WGPURawBindingType_WriteonlyStorageTexture
|
||||
: ffi::WGPURawBindingType_ReadonlyStorageTexture;
|
||||
e.view_dimension = &optional[i].dim;
|
||||
e.storage_texture_format = &optional[i].format;
|
||||
}
|
||||
if (entry.mSampler.WasPassed()) {
|
||||
e.ty = ffi::WGPURawBindingType_Sampler;
|
||||
switch (entry.mSampler.Value().mType) {
|
||||
case dom::GPUSamplerBindingType::Filtering:
|
||||
e.sampler_filter = true;
|
||||
break;
|
||||
case dom::GPUSamplerBindingType::Non_filtering:
|
||||
break;
|
||||
case dom::GPUSamplerBindingType::Comparison:
|
||||
e.sampler_compare = true;
|
||||
break;
|
||||
case dom::GPUSamplerBindingType::EndGuard_:
|
||||
MOZ_ASSERT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
entries.AppendElement(e);
|
||||
}
|
||||
|
||||
|
@ -379,8 +443,8 @@ RawId WebGPUChild::DeviceCreateShaderModule(
|
|||
ffi::WGPUShaderModuleDescriptor desc = {};
|
||||
|
||||
nsCString wgsl;
|
||||
if (aDesc.mCode.IsString()) {
|
||||
LossyCopyUTF16toASCII(aDesc.mCode.GetAsString(), wgsl);
|
||||
if (aDesc.mCode.IsUSVString()) {
|
||||
LossyCopyUTF16toASCII(aDesc.mCode.GetAsUSVString(), wgsl);
|
||||
desc.wgsl_chars = wgsl.get();
|
||||
} else {
|
||||
const auto& code = aDesc.mCode.GetAsUint32Array();
|
||||
|
@ -410,9 +474,9 @@ RawId WebGPUChild::DeviceCreateComputePipeline(
|
|||
if (aDesc.mLayout.WasPassed()) {
|
||||
desc.layout = aDesc.mLayout.Value().mId;
|
||||
}
|
||||
desc.compute_stage.module = aDesc.mComputeStage.mModule->mId;
|
||||
LossyCopyUTF16toASCII(aDesc.mComputeStage.mEntryPoint, entryPoint);
|
||||
desc.compute_stage.entry_point = entryPoint.get();
|
||||
desc.stage.module = aDesc.mCompute.mModule->mId;
|
||||
LossyCopyUTF16toASCII(aDesc.mCompute.mEntryPoint, entryPoint);
|
||||
desc.stage.entry_point = entryPoint.get();
|
||||
|
||||
ByteBuf bb;
|
||||
RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {};
|
||||
|
@ -429,39 +493,27 @@ RawId WebGPUChild::DeviceCreateComputePipeline(
|
|||
return id;
|
||||
}
|
||||
|
||||
static ffi::WGPURasterizationStateDescriptor ConvertRasterizationDescriptor(
|
||||
const dom::GPURasterizationStateDescriptor& aDesc) {
|
||||
ffi::WGPURasterizationStateDescriptor desc = {};
|
||||
desc.front_face = ffi::WGPUFrontFace(aDesc.mFrontFace);
|
||||
desc.cull_mode = ffi::WGPUCullMode(aDesc.mCullMode);
|
||||
desc.depth_bias = aDesc.mDepthBias;
|
||||
desc.depth_bias_slope_scale = aDesc.mDepthBiasSlopeScale;
|
||||
desc.depth_bias_clamp = aDesc.mDepthBiasClamp;
|
||||
static ffi::WGPUMultisampleState ConvertMultisampleState(
|
||||
const dom::GPUMultisampleState& aDesc) {
|
||||
ffi::WGPUMultisampleState desc = {};
|
||||
desc.count = aDesc.mCount;
|
||||
desc.mask = aDesc.mMask;
|
||||
desc.alpha_to_coverage_enabled = aDesc.mAlphaToCoverageEnabled;
|
||||
return desc;
|
||||
}
|
||||
|
||||
static ffi::WGPUBlendDescriptor ConvertBlendDescriptor(
|
||||
const dom::GPUBlendDescriptor& aDesc) {
|
||||
ffi::WGPUBlendDescriptor desc = {};
|
||||
static ffi::WGPUBlendComponent ConvertBlendComponent(
|
||||
const dom::GPUBlendComponent& aDesc) {
|
||||
ffi::WGPUBlendComponent desc = {};
|
||||
desc.src_factor = ffi::WGPUBlendFactor(aDesc.mSrcFactor);
|
||||
desc.dst_factor = ffi::WGPUBlendFactor(aDesc.mDstFactor);
|
||||
desc.operation = ffi::WGPUBlendOperation(aDesc.mOperation);
|
||||
return desc;
|
||||
}
|
||||
|
||||
static ffi::WGPUColorStateDescriptor ConvertColorDescriptor(
|
||||
const dom::GPUColorStateDescriptor& aDesc) {
|
||||
ffi::WGPUColorStateDescriptor desc = {};
|
||||
desc.format = ffi::WGPUTextureFormat(aDesc.mFormat);
|
||||
desc.alpha_blend = ConvertBlendDescriptor(aDesc.mAlphaBlend);
|
||||
desc.color_blend = ConvertBlendDescriptor(aDesc.mColorBlend);
|
||||
desc.write_mask = aDesc.mWriteMask;
|
||||
return desc;
|
||||
}
|
||||
|
||||
static ffi::WGPUStencilStateFaceDescriptor ConvertStencilFaceDescriptor(
|
||||
const dom::GPUStencilStateFaceDescriptor& aDesc) {
|
||||
ffi::WGPUStencilStateFaceDescriptor desc = {};
|
||||
static ffi::WGPUStencilFaceState ConvertStencilFaceState(
|
||||
const dom::GPUStencilFaceState& aDesc) {
|
||||
ffi::WGPUStencilFaceState desc = {};
|
||||
desc.compare = ConvertCompareFunction(aDesc.mCompare);
|
||||
desc.fail_op = ffi::WGPUStencilOperation(aDesc.mFailOp);
|
||||
desc.depth_fail_op = ffi::WGPUStencilOperation(aDesc.mDepthFailOp);
|
||||
|
@ -469,26 +521,36 @@ static ffi::WGPUStencilStateFaceDescriptor ConvertStencilFaceDescriptor(
|
|||
return desc;
|
||||
}
|
||||
|
||||
static ffi::WGPUDepthStencilStateDescriptor ConvertDepthStencilDescriptor(
|
||||
const dom::GPUDepthStencilStateDescriptor& aDesc) {
|
||||
ffi::WGPUDepthStencilStateDescriptor desc = {};
|
||||
static ffi::WGPUDepthStencilState ConvertDepthStencilState(
|
||||
const dom::GPUDepthStencilState& aDesc) {
|
||||
ffi::WGPUDepthStencilState desc = {};
|
||||
desc.format = ffi::WGPUTextureFormat(aDesc.mFormat);
|
||||
desc.depth_write_enabled = aDesc.mDepthWriteEnabled;
|
||||
desc.depth_compare = ConvertCompareFunction(aDesc.mDepthCompare);
|
||||
desc.stencil.front = ConvertStencilFaceDescriptor(aDesc.mStencilFront);
|
||||
desc.stencil.back = ConvertStencilFaceDescriptor(aDesc.mStencilBack);
|
||||
desc.stencil.front = ConvertStencilFaceState(aDesc.mStencilFront);
|
||||
desc.stencil.back = ConvertStencilFaceState(aDesc.mStencilBack);
|
||||
desc.stencil.read_mask = aDesc.mStencilReadMask;
|
||||
desc.stencil.write_mask = aDesc.mStencilWriteMask;
|
||||
desc.bias.constant = aDesc.mDepthBias;
|
||||
desc.bias.slope_scale = aDesc.mDepthBiasSlopeScale;
|
||||
desc.bias.clamp = aDesc.mDepthBiasClamp;
|
||||
return desc;
|
||||
}
|
||||
|
||||
RawId WebGPUChild::DeviceCreateRenderPipeline(
|
||||
RawId aSelfId, const dom::GPURenderPipelineDescriptor& aDesc,
|
||||
nsTArray<RawId>* const aImplicitBindGroupLayoutIds) {
|
||||
// A bunch of stack locals that we can have pointers into
|
||||
nsTArray<ffi::WGPUVertexBufferLayout> vertexBuffers;
|
||||
nsTArray<ffi::WGPUVertexAttribute> vertexAttributes;
|
||||
ffi::WGPURenderPipelineDescriptor desc = {};
|
||||
nsCString label, vsEntry, fsEntry;
|
||||
ffi::WGPUProgrammableStageDescriptor vertexStage = {};
|
||||
ffi::WGPUProgrammableStageDescriptor fragmentStage = {};
|
||||
ffi::WGPUIndexFormat stripIndexFormat = ffi::WGPUIndexFormat_Uint16;
|
||||
ffi::WGPUFace cullFace = ffi::WGPUFace_Front;
|
||||
ffi::WGPUVertexState vertexState = {};
|
||||
ffi::WGPUFragmentState fragmentState = {};
|
||||
nsTArray<ffi::WGPUColorTargetState> colorStates;
|
||||
nsTArray<ffi::WGPUBlendState> blendStates;
|
||||
|
||||
if (aDesc.mLabel.WasPassed()) {
|
||||
LossyCopyUTF16toASCII(aDesc.mLabel.Value(), label);
|
||||
|
@ -498,73 +560,95 @@ RawId WebGPUChild::DeviceCreateRenderPipeline(
|
|||
desc.layout = aDesc.mLayout.Value().mId;
|
||||
}
|
||||
|
||||
vertexStage.module = aDesc.mVertexStage.mModule->mId;
|
||||
LossyCopyUTF16toASCII(aDesc.mVertexStage.mEntryPoint, vsEntry);
|
||||
vertexStage.entry_point = vsEntry.get();
|
||||
desc.vertex_stage = &vertexStage;
|
||||
{
|
||||
const auto& stage = aDesc.mVertex;
|
||||
vertexState.stage.module = stage.mModule->mId;
|
||||
LossyCopyUTF16toASCII(stage.mEntryPoint, vsEntry);
|
||||
vertexState.stage.entry_point = vsEntry.get();
|
||||
|
||||
if (aDesc.mFragmentStage.WasPassed()) {
|
||||
const auto& stage = aDesc.mFragmentStage.Value();
|
||||
fragmentStage.module = stage.mModule->mId;
|
||||
for (const auto& vertex_desc : stage.mBuffers) {
|
||||
ffi::WGPUVertexBufferLayout vb_desc = {};
|
||||
if (!vertex_desc.IsNull()) {
|
||||
const auto& vd = vertex_desc.Value();
|
||||
vb_desc.array_stride = vd.mArrayStride;
|
||||
vb_desc.step_mode = ffi::WGPUInputStepMode(vd.mStepMode);
|
||||
// Note: we are setting the length but not the pointer
|
||||
vb_desc.attributes_length = vd.mAttributes.Length();
|
||||
for (const auto& vat : vd.mAttributes) {
|
||||
ffi::WGPUVertexAttribute ad = {};
|
||||
ad.offset = vat.mOffset;
|
||||
ad.format = ffi::WGPUVertexFormat(vat.mFormat);
|
||||
ad.shader_location = vat.mShaderLocation;
|
||||
vertexAttributes.AppendElement(ad);
|
||||
}
|
||||
}
|
||||
vertexBuffers.AppendElement(vb_desc);
|
||||
}
|
||||
// Now patch up all the pointers to attribute lists.
|
||||
size_t numAttributes = 0;
|
||||
for (auto& vb_desc : vertexBuffers) {
|
||||
vb_desc.attributes = vertexAttributes.Elements() + numAttributes;
|
||||
numAttributes += vb_desc.attributes_length;
|
||||
}
|
||||
|
||||
vertexState.buffers = vertexBuffers.Elements();
|
||||
vertexState.buffers_length = vertexBuffers.Length();
|
||||
desc.vertex = &vertexState;
|
||||
}
|
||||
|
||||
if (aDesc.mFragment.WasPassed()) {
|
||||
const auto& stage = aDesc.mFragment.Value();
|
||||
fragmentState.stage.module = stage.mModule->mId;
|
||||
LossyCopyUTF16toASCII(stage.mEntryPoint, fsEntry);
|
||||
fragmentStage.entry_point = fsEntry.get();
|
||||
desc.fragment_stage = &fragmentStage;
|
||||
}
|
||||
fragmentState.stage.entry_point = fsEntry.get();
|
||||
|
||||
desc.primitive_topology =
|
||||
ffi::WGPUPrimitiveTopology(aDesc.mPrimitiveTopology);
|
||||
const auto rasterization =
|
||||
ConvertRasterizationDescriptor(aDesc.mRasterizationState);
|
||||
desc.rasterization_state = &rasterization;
|
||||
|
||||
nsTArray<ffi::WGPUColorStateDescriptor> colorStates;
|
||||
for (const auto& colorState : aDesc.mColorStates) {
|
||||
colorStates.AppendElement(ConvertColorDescriptor(colorState));
|
||||
}
|
||||
desc.color_states = colorStates.Elements();
|
||||
desc.color_states_length = colorStates.Length();
|
||||
|
||||
ffi::WGPUDepthStencilStateDescriptor depthStencilState = {};
|
||||
if (aDesc.mDepthStencilState.WasPassed()) {
|
||||
depthStencilState =
|
||||
ConvertDepthStencilDescriptor(aDesc.mDepthStencilState.Value());
|
||||
desc.depth_stencil_state = &depthStencilState;
|
||||
}
|
||||
|
||||
desc.vertex_state.index_format =
|
||||
ffi::WGPUIndexFormat(aDesc.mVertexState.mIndexFormat);
|
||||
nsTArray<ffi::WGPUVertexBufferDescriptor> vertexBuffers;
|
||||
nsTArray<ffi::WGPUVertexAttributeDescriptor> vertexAttributes;
|
||||
for (const auto& vertex_desc : aDesc.mVertexState.mVertexBuffers) {
|
||||
ffi::WGPUVertexBufferDescriptor vb_desc = {};
|
||||
if (!vertex_desc.IsNull()) {
|
||||
const auto& vd = vertex_desc.Value();
|
||||
vb_desc.stride = vd.mArrayStride;
|
||||
vb_desc.step_mode = ffi::WGPUInputStepMode(vd.mStepMode);
|
||||
// Note: we are setting the length but not the pointer
|
||||
vb_desc.attributes_length = vd.mAttributes.Length();
|
||||
for (const auto& vat : vd.mAttributes) {
|
||||
ffi::WGPUVertexAttributeDescriptor ad = {};
|
||||
ad.offset = vat.mOffset;
|
||||
ad.format = ffi::WGPUVertexFormat(vat.mFormat);
|
||||
ad.shader_location = vat.mShaderLocation;
|
||||
vertexAttributes.AppendElement(ad);
|
||||
// Note: we pre-collect the blend states into a different array
|
||||
// so that we can have non-stale pointers into it.
|
||||
for (const auto& colorState : stage.mTargets) {
|
||||
ffi::WGPUColorTargetState desc = {};
|
||||
desc.format = ffi::WGPUTextureFormat(colorState.mFormat);
|
||||
desc.write_mask = colorState.mWriteMask;
|
||||
colorStates.AppendElement(desc);
|
||||
ffi::WGPUBlendState bs = {};
|
||||
if (colorState.mBlend.WasPassed()) {
|
||||
const auto& blend = colorState.mBlend.Value();
|
||||
bs.alpha = ConvertBlendComponent(blend.mAlpha);
|
||||
bs.color = ConvertBlendComponent(blend.mColor);
|
||||
}
|
||||
blendStates.AppendElement(bs);
|
||||
}
|
||||
for (size_t i = 0; i < colorStates.Length(); ++i) {
|
||||
if (stage.mTargets[i].mBlend.WasPassed()) {
|
||||
colorStates[i].blend = &blendStates[i];
|
||||
}
|
||||
}
|
||||
vertexBuffers.AppendElement(vb_desc);
|
||||
}
|
||||
// Now patch up all the pointers to attribute lists.
|
||||
size_t numAttributes = 0;
|
||||
for (auto& vb_desc : vertexBuffers) {
|
||||
vb_desc.attributes = vertexAttributes.Elements() + numAttributes;
|
||||
numAttributes += vb_desc.attributes_length;
|
||||
|
||||
fragmentState.targets = colorStates.Elements();
|
||||
fragmentState.targets_length = colorStates.Length();
|
||||
desc.fragment = &fragmentState;
|
||||
}
|
||||
|
||||
desc.vertex_state.vertex_buffers = vertexBuffers.Elements();
|
||||
desc.vertex_state.vertex_buffers_length = vertexBuffers.Length();
|
||||
desc.sample_count = aDesc.mSampleCount;
|
||||
desc.sample_mask = aDesc.mSampleMask;
|
||||
desc.alpha_to_coverage_enabled = aDesc.mAlphaToCoverageEnabled;
|
||||
{
|
||||
const auto& prim = aDesc.mPrimitive;
|
||||
desc.primitive.topology = ffi::WGPUPrimitiveTopology(prim.mTopology);
|
||||
if (prim.mStripIndexFormat.WasPassed()) {
|
||||
stripIndexFormat = ffi::WGPUIndexFormat(prim.mStripIndexFormat.Value());
|
||||
desc.primitive.strip_index_format = &stripIndexFormat;
|
||||
}
|
||||
desc.primitive.front_face = ffi::WGPUFrontFace(prim.mFrontFace);
|
||||
if (prim.mCullMode != dom::GPUCullMode::None) {
|
||||
cullFace = prim.mCullMode == dom::GPUCullMode::Front ? ffi::WGPUFace_Front
|
||||
: ffi::WGPUFace_Back;
|
||||
desc.primitive.cull_mode = &cullFace;
|
||||
}
|
||||
}
|
||||
desc.multisample = ConvertMultisampleState(aDesc.mMultisample);
|
||||
|
||||
ffi::WGPUDepthStencilState depthStencilState = {};
|
||||
if (aDesc.mDepthStencil.WasPassed()) {
|
||||
depthStencilState = ConvertDepthStencilState(aDesc.mDepthStencil.Value());
|
||||
desc.depth_stencil = &depthStencilState;
|
||||
}
|
||||
|
||||
ByteBuf bb;
|
||||
RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {};
|
||||
|
|
|
@ -245,35 +245,10 @@ ipc::IPCResult WebGPUParent::RecvInstanceRequestAdapter(
|
|||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvAdapterRequestDevice(
|
||||
RawId aSelfId, const dom::GPUDeviceDescriptor& aDesc, RawId aNewId) {
|
||||
ffi::WGPUDeviceDescriptor desc = {};
|
||||
desc.shader_validation = true; // required for implicit pipeline layouts
|
||||
|
||||
if (aDesc.mLimits.WasPassed()) {
|
||||
const auto& lim = aDesc.mLimits.Value();
|
||||
desc.limits.max_bind_groups = lim.mMaxBindGroups;
|
||||
desc.limits.max_dynamic_uniform_buffers_per_pipeline_layout =
|
||||
lim.mMaxDynamicUniformBuffersPerPipelineLayout;
|
||||
desc.limits.max_dynamic_storage_buffers_per_pipeline_layout =
|
||||
lim.mMaxDynamicStorageBuffersPerPipelineLayout;
|
||||
desc.limits.max_sampled_textures_per_shader_stage =
|
||||
lim.mMaxSampledTexturesPerShaderStage;
|
||||
desc.limits.max_samplers_per_shader_stage = lim.mMaxSamplersPerShaderStage;
|
||||
desc.limits.max_storage_buffers_per_shader_stage =
|
||||
lim.mMaxStorageBuffersPerShaderStage;
|
||||
desc.limits.max_storage_textures_per_shader_stage =
|
||||
lim.mMaxStorageTexturesPerShaderStage;
|
||||
desc.limits.max_uniform_buffers_per_shader_stage =
|
||||
lim.mMaxUniformBuffersPerShaderStage;
|
||||
desc.limits.max_uniform_buffer_binding_size =
|
||||
lim.mMaxUniformBufferBindingSize;
|
||||
} else {
|
||||
ffi::wgpu_server_fill_default_limits(&desc.limits);
|
||||
}
|
||||
|
||||
RawId aSelfId, const ipc::ByteBuf& aByteBuf, RawId aNewId) {
|
||||
ErrorBuffer error;
|
||||
ffi::wgpu_server_adapter_request_device(mContext, aSelfId, &desc, aNewId,
|
||||
error.ToFFI());
|
||||
ffi::wgpu_server_adapter_request_device(mContext, aSelfId, ToFFI(&aByteBuf),
|
||||
aNewId, error.ToFFI());
|
||||
error.CheckAndForward(this, 0);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
const nsTArray<RawId>& aTargetIds,
|
||||
InstanceRequestAdapterResolver&& resolver);
|
||||
ipc::IPCResult RecvAdapterRequestDevice(RawId aSelfId,
|
||||
const dom::GPUDeviceDescriptor& aDesc,
|
||||
const ipc::ByteBuf& aByteBuf,
|
||||
RawId aNewId);
|
||||
ipc::IPCResult RecvAdapterDestroy(RawId aSelfId);
|
||||
ipc::IPCResult RecvDeviceDestroy(RawId aSelfId);
|
||||
|
|
|
@ -32,12 +32,8 @@ DEFINE_IPC_SERIALIZER_WITHOUT_FIELDS(mozilla::dom::GPUCommandBufferDescriptor);
|
|||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::GPURequestAdapterOptions,
|
||||
mPowerPreference);
|
||||
DEFINE_IPC_SERIALIZER_WITHOUT_FIELDS(mozilla::dom::GPUExtensions);
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::GPULimits, mMaxBindGroups);
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::GPUDeviceDescriptor,
|
||||
mExtensions, mLimits);
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::webgpu::ffi::WGPUExtent3d, width,
|
||||
height, depth);
|
||||
height, depth_or_array_layers);
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::webgpu::ffi::WGPUOrigin3d, x, y, z);
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::webgpu::ffi::WGPUTextureDataLayout,
|
||||
|
|
|
@ -16,9 +16,9 @@ const func = async function() {
|
|||
const buffer = device.createBuffer({size:16, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.VERTEX});
|
||||
const arrayBuf = new ArrayBuffer(16);
|
||||
(new Int32Array(arrayBuf)).fill(5)
|
||||
device.defaultQueue.writeBuffer(buffer, 0, arrayBuf, 0);
|
||||
device.queue.writeBuffer(buffer, 0, arrayBuf, 0);
|
||||
const texture = device.createTexture({size: [2,2,1], dimension: "2d", format: "rgba8unorm", usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC });
|
||||
device.defaultQueue.writeTexture({ texture }, arrayBuf, { bytesPerRow:8 }, [2,2,1]);
|
||||
device.queue.writeTexture({ texture }, arrayBuf, { bytesPerRow:8 }, [2,2,1]);
|
||||
// this isn't a process check, we need to read back the contents and verify the writes happened
|
||||
ok(device !== undefined, '');
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ const func = async function() {
|
|||
const pass = encoder.beginComputePass();
|
||||
pass.endPass();
|
||||
const command_buffer = encoder.finish();
|
||||
device.defaultQueue.submit([command_buffer]);
|
||||
device.queue.submit([command_buffer]);
|
||||
ok(command_buffer !== undefined, 'command_buffer !== undefined');
|
||||
};
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ const func = async function() {
|
|||
});
|
||||
pass.endPass();
|
||||
const command_buffer = encoder.finish();
|
||||
device.defaultQueue.submit([command_buffer]);
|
||||
device.queue.submit([command_buffer]);
|
||||
ok(command_buffer !== undefined, 'command_buffer !== undefined');
|
||||
};
|
||||
|
||||
|
|
|
@ -16,21 +16,25 @@ DIRS += []
|
|||
|
||||
h_and_cpp = [
|
||||
"Adapter",
|
||||
"AdapterFeatures",
|
||||
"AdapterLimits",
|
||||
"BindGroup",
|
||||
"BindGroupLayout",
|
||||
"Buffer",
|
||||
"CanvasContext",
|
||||
"CommandBuffer",
|
||||
"CommandEncoder",
|
||||
"CompilationInfo",
|
||||
"CompilationMessage",
|
||||
"ComputePassEncoder",
|
||||
"ComputePipeline",
|
||||
"Device",
|
||||
"DeviceLostInfo",
|
||||
"Fence",
|
||||
"Instance",
|
||||
"ObjectModel",
|
||||
"OutOfMemoryError",
|
||||
"PipelineLayout",
|
||||
"QuerySet",
|
||||
"Queue",
|
||||
"RenderBundle",
|
||||
"RenderBundleEncoder",
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
|
||||
typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
|
||||
typedef [EnforceRange] unsigned long long GPUFenceValue;
|
||||
typedef [EnforceRange] unsigned long GPUStencilValue;
|
||||
typedef [EnforceRange] unsigned long GPUSampleMask;
|
||||
typedef [EnforceRange] long GPUDepthBias;
|
||||
|
@ -41,8 +40,8 @@ dictionary GPUOrigin3DDict {
|
|||
|
||||
dictionary GPUExtent3DDict {
|
||||
required GPUIntegerCoordinate width;
|
||||
required GPUIntegerCoordinate height;
|
||||
required GPUIntegerCoordinate depth;
|
||||
GPUIntegerCoordinate height = 1;
|
||||
GPUIntegerCoordinate depthOrArrayLayers = 1;
|
||||
};
|
||||
|
||||
typedef (sequence<double> or GPUColorDict) GPUColor;
|
||||
|
@ -51,11 +50,11 @@ typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
|
|||
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
|
||||
|
||||
interface mixin GPUObjectBase {
|
||||
attribute DOMString? label;
|
||||
attribute USVString? label;
|
||||
};
|
||||
|
||||
dictionary GPUObjectDescriptorBase {
|
||||
DOMString? label;
|
||||
USVString label;
|
||||
};
|
||||
|
||||
// ****************************************************************************
|
||||
|
@ -69,7 +68,7 @@ dictionary GPUObjectDescriptorBase {
|
|||
interface GPU {
|
||||
// May reject with DOMException
|
||||
[NewObject]
|
||||
Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options = {});
|
||||
Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
|
||||
};
|
||||
|
||||
// Add a "webgpu" member to Navigator/Worker that contains the global instance of a "WebGPU"
|
||||
|
@ -86,43 +85,70 @@ dictionary GPURequestAdapterOptions {
|
|||
GPUPowerPreference powerPreference;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUAdapterFeatures {
|
||||
readonly setlike<GPUFeatureName>;
|
||||
};
|
||||
|
||||
dictionary GPUDeviceDescriptor {
|
||||
sequence<GPUFeatureName> nonGuaranteedFeatures = [];
|
||||
record<DOMString, GPUSize32> nonGuaranteedLimits;
|
||||
};
|
||||
|
||||
enum GPUFeatureName {
|
||||
"depth-clamping",
|
||||
"depth24unorm-stencil8",
|
||||
"depth32float-stencil8",
|
||||
"pipeline-statistics-query",
|
||||
"texture-compression-bc",
|
||||
"timestamp-query",
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUAdapterLimits {
|
||||
readonly attribute unsigned long maxTextureDimension1D;
|
||||
readonly attribute unsigned long maxTextureDimension2D;
|
||||
readonly attribute unsigned long maxTextureDimension3D;
|
||||
readonly attribute unsigned long maxTextureArrayLayers;
|
||||
readonly attribute unsigned long maxBindGroups;
|
||||
readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
|
||||
readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
|
||||
readonly attribute unsigned long maxSampledTexturesPerShaderStage;
|
||||
readonly attribute unsigned long maxSamplersPerShaderStage;
|
||||
readonly attribute unsigned long maxStorageBuffersPerShaderStage;
|
||||
readonly attribute unsigned long maxStorageTexturesPerShaderStage;
|
||||
readonly attribute unsigned long maxUniformBuffersPerShaderStage;
|
||||
readonly attribute unsigned long maxUniformBufferBindingSize;
|
||||
readonly attribute unsigned long maxStorageBufferBindingSize;
|
||||
readonly attribute unsigned long maxVertexBuffers;
|
||||
readonly attribute unsigned long maxVertexAttributes;
|
||||
readonly attribute unsigned long maxVertexBufferArrayStride;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUAdapter {
|
||||
readonly attribute DOMString name;
|
||||
//GPUExtensions getExtensions();
|
||||
//readonly attribute GPULimits limits; Don't expose higher limits for now.
|
||||
[SameObject] readonly attribute GPUAdapterFeatures features;
|
||||
[SameObject] readonly attribute GPUAdapterLimits limits;
|
||||
|
||||
// May reject with DOMException
|
||||
[NewObject]
|
||||
Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
|
||||
};
|
||||
GPUAdapter includes GPUObjectBase;
|
||||
|
||||
dictionary GPUExtensions {
|
||||
};
|
||||
|
||||
dictionary GPULimits {
|
||||
GPUSize32 maxBindGroups = 4;
|
||||
GPUSize32 maxDynamicUniformBuffersPerPipelineLayout = 8;
|
||||
GPUSize32 maxDynamicStorageBuffersPerPipelineLayout = 4;
|
||||
GPUSize32 maxSampledTexturesPerShaderStage = 16;
|
||||
GPUSize32 maxSamplersPerShaderStage = 16;
|
||||
GPUSize32 maxStorageBuffersPerShaderStage = 4;
|
||||
GPUSize32 maxStorageTexturesPerShaderStage = 4;
|
||||
GPUSize32 maxUniformBuffersPerShaderStage = 12;
|
||||
GPUSize32 maxUniformBufferBindingSize = 16384;
|
||||
};
|
||||
|
||||
// Device
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUDevice {
|
||||
//GPUExtensions getExtensions();
|
||||
//GPULimits getLimits();
|
||||
//readonly attribute GPUAdapter adapter;
|
||||
interface GPUDevice: EventTarget {
|
||||
//[SameObject] readonly attribute GPUAdapter adapter;
|
||||
//readonly attribute FrozenArray<GPUFeatureName> features;
|
||||
//readonly attribute object limits;
|
||||
|
||||
[SameObject] readonly attribute GPUQueue defaultQueue;
|
||||
[SameObject] readonly attribute GPUQueue queue;
|
||||
|
||||
void destroy();
|
||||
|
||||
[NewObject, Throws]
|
||||
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
|
||||
|
@ -139,19 +165,18 @@ interface GPUDevice {
|
|||
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
|
||||
GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
|
||||
|
||||
//Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
|
||||
//Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);
|
||||
|
||||
[NewObject]
|
||||
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
|
||||
//[NewObject]
|
||||
//GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
|
||||
//[NewObject]
|
||||
//GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
|
||||
};
|
||||
GPUDevice includes GPUObjectBase;
|
||||
|
||||
dictionary GPUDeviceDescriptor {
|
||||
GPUExtensions extensions;
|
||||
GPULimits limits;
|
||||
|
||||
// TODO are other things configurable like queues?
|
||||
};
|
||||
|
||||
|
||||
// ****************************************************************************
|
||||
// ERROR HANDLING
|
||||
|
@ -197,7 +222,7 @@ partial interface GPUDevice {
|
|||
// ****************************************************************************
|
||||
|
||||
// Buffer
|
||||
typedef unsigned long GPUBufferUsageFlags;
|
||||
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUBufferUsage {
|
||||
|
@ -219,7 +244,7 @@ dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
|
|||
boolean mappedAtCreation = false;
|
||||
};
|
||||
|
||||
typedef unsigned long GPUMapModeFlags;
|
||||
typedef [EnforceRange] unsigned long GPUMapModeFlags;
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
|
@ -300,12 +325,37 @@ enum GPUTextureFormat {
|
|||
"rgba32float",
|
||||
|
||||
// Depth and stencil formats
|
||||
"depth32float",
|
||||
"stencil8",
|
||||
"depth16unorm",
|
||||
"depth24plus",
|
||||
"depth24plus-stencil8"
|
||||
"depth24plus-stencil8",
|
||||
"depth32float",
|
||||
|
||||
// BC compressed formats usable if "texture-compression-bc" is both
|
||||
// supported by the device/user agent and enabled in requestDevice.
|
||||
"bc1-rgba-unorm",
|
||||
"bc1-rgba-unorm-srgb",
|
||||
"bc2-rgba-unorm",
|
||||
"bc2-rgba-unorm-srgb",
|
||||
"bc3-rgba-unorm",
|
||||
"bc3-rgba-unorm-srgb",
|
||||
"bc4-r-unorm",
|
||||
"bc4-r-snorm",
|
||||
"bc5-rg-unorm",
|
||||
"bc5-rg-snorm",
|
||||
"bc6h-rgb-ufloat",
|
||||
"bc6h-rgb-float",
|
||||
"bc7-rgba-unorm",
|
||||
"bc7-rgba-unorm-srgb",
|
||||
|
||||
// "depth24unorm-stencil8" feature
|
||||
"depth24unorm-stencil8",
|
||||
|
||||
// "depth32float-stencil8" feature
|
||||
"depth32float-stencil8",
|
||||
};
|
||||
|
||||
typedef unsigned long GPUTextureUsageFlags;
|
||||
typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUTextureUsage {
|
||||
|
@ -398,8 +448,9 @@ dictionary GPUSamplerDescriptor : GPUObjectDescriptorBase {
|
|||
GPUFilterMode minFilter = "nearest";
|
||||
GPUFilterMode mipmapFilter = "nearest";
|
||||
float lodMinClamp = 0;
|
||||
float lodMaxClamp = 1000.0; //TODO?
|
||||
float lodMaxClamp = 1000.0; // TODO: What should this be?
|
||||
GPUCompareFunction compare;
|
||||
[Clamp] unsigned short maxAnisotropy = 1;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
|
@ -431,7 +482,7 @@ interface GPUPipelineLayout {
|
|||
GPUPipelineLayout includes GPUObjectBase;
|
||||
|
||||
// BindGroupLayout
|
||||
typedef unsigned long GPUShaderStageFlags;
|
||||
typedef [EnforceRange] unsigned long GPUShaderStageFlags;
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUShaderStage {
|
||||
|
@ -440,26 +491,60 @@ interface GPUShaderStage {
|
|||
const GPUShaderStageFlags COMPUTE = 4;
|
||||
};
|
||||
|
||||
enum GPUBindingType {
|
||||
"uniform-buffer",
|
||||
"storage-buffer",
|
||||
"readonly-storage-buffer",
|
||||
"sampler",
|
||||
"comparison-sampler",
|
||||
"sampled-texture",
|
||||
"readonly-storage-texture",
|
||||
"writeonly-storage-texture",
|
||||
enum GPUBufferBindingType {
|
||||
"uniform",
|
||||
"storage",
|
||||
"read-only-storage",
|
||||
};
|
||||
|
||||
dictionary GPUBufferBindingLayout {
|
||||
GPUBufferBindingType type = "uniform";
|
||||
boolean hasDynamicOffset = false;
|
||||
GPUSize64 minBindingSize = 0;
|
||||
};
|
||||
|
||||
enum GPUSamplerBindingType {
|
||||
"filtering",
|
||||
"non-filtering",
|
||||
"comparison",
|
||||
};
|
||||
|
||||
dictionary GPUSamplerBindingLayout {
|
||||
GPUSamplerBindingType type = "filtering";
|
||||
};
|
||||
|
||||
enum GPUTextureSampleType {
|
||||
"float",
|
||||
"unfilterable-float",
|
||||
"depth",
|
||||
"sint",
|
||||
"uint",
|
||||
};
|
||||
|
||||
dictionary GPUTextureBindingLayout {
|
||||
GPUTextureSampleType sampleType = "float";
|
||||
GPUTextureViewDimension viewDimension = "2d";
|
||||
boolean multisampled = false;
|
||||
};
|
||||
|
||||
enum GPUStorageTextureAccess {
|
||||
"read-only",
|
||||
"write-only",
|
||||
};
|
||||
|
||||
dictionary GPUStorageTextureBindingLayout {
|
||||
required GPUStorageTextureAccess access;
|
||||
required GPUTextureFormat format;
|
||||
GPUTextureViewDimension viewDimension = "2d";
|
||||
};
|
||||
|
||||
dictionary GPUBindGroupLayoutEntry {
|
||||
required GPUIndex32 binding;
|
||||
required GPUShaderStageFlags visibility;
|
||||
required GPUBindingType type;
|
||||
GPUTextureViewDimension viewDimension;
|
||||
GPUTextureComponentType textureComponentType;
|
||||
boolean multisampled = false;
|
||||
boolean hasDynamicOffset = false;
|
||||
GPUTextureFormat storageTextureFormat;
|
||||
GPUBufferBindingLayout buffer;
|
||||
GPUSamplerBindingLayout sampler;
|
||||
GPUTextureBindingLayout texture;
|
||||
GPUStorageTextureBindingLayout storageTexture;
|
||||
};
|
||||
|
||||
dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase {
|
||||
|
@ -501,6 +586,168 @@ GPUBindGroup includes GPUObjectBase;
|
|||
// PIPELINE CREATION (blend state, DS state, ..., pipelines)
|
||||
// ****************************************************************************
|
||||
|
||||
enum GPUCompilationMessageType {
|
||||
"error",
|
||||
"warning",
|
||||
"info"
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUCompilationMessage {
|
||||
readonly attribute DOMString message;
|
||||
readonly attribute GPUCompilationMessageType type;
|
||||
readonly attribute unsigned long long lineNum;
|
||||
readonly attribute unsigned long long linePos;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUCompilationInfo {
|
||||
//TODO:
|
||||
//[Cached, Frozen, Pure]
|
||||
//readonly attribute sequence<GPUCompilationMessage> messages;
|
||||
};
|
||||
|
||||
// ShaderModule
|
||||
//TODO: remove the `Uint32Array` variant, it's used for SPIR-V
|
||||
typedef (Uint32Array or USVString) GPUShaderCode;
|
||||
|
||||
dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
|
||||
required GPUShaderCode code;
|
||||
object sourceMap;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUShaderModule {
|
||||
//TODO:
|
||||
//Promise<GPUCompilationInfo> compilationInfo();
|
||||
};
|
||||
GPUShaderModule includes GPUObjectBase;
|
||||
|
||||
|
||||
// Common stuff for ComputePipeline and RenderPipeline
|
||||
dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
|
||||
GPUPipelineLayout layout;
|
||||
};
|
||||
|
||||
interface mixin GPUPipelineBase {
|
||||
GPUBindGroupLayout getBindGroupLayout(unsigned long index);
|
||||
};
|
||||
|
||||
dictionary GPUProgrammableStage {
|
||||
required GPUShaderModule module;
|
||||
required USVString entryPoint;
|
||||
};
|
||||
|
||||
// ComputePipeline
|
||||
dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
|
||||
required GPUProgrammableStage compute;
|
||||
};
|
||||
|
||||
//TODO: Serializable
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696219
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUComputePipeline {
|
||||
};
|
||||
GPUComputePipeline includes GPUObjectBase;
|
||||
GPUComputePipeline includes GPUPipelineBase;
|
||||
|
||||
// InputState
|
||||
enum GPUIndexFormat {
|
||||
"uint16",
|
||||
"uint32",
|
||||
};
|
||||
|
||||
enum GPUVertexFormat {
|
||||
"uint8x2",
|
||||
"uint8x4",
|
||||
"sint8x2",
|
||||
"sint8x4",
|
||||
"unorm8x2",
|
||||
"unorm8x4",
|
||||
"snorm8x2",
|
||||
"snorm8x4",
|
||||
"uint16x2",
|
||||
"uint16x4",
|
||||
"sint16x2",
|
||||
"sint16x4",
|
||||
"unorm16x2",
|
||||
"unorm16x4",
|
||||
"snorm16x2",
|
||||
"snorm16x4",
|
||||
"float16x2",
|
||||
"float16x4",
|
||||
"float32",
|
||||
"float32x2",
|
||||
"float32x3",
|
||||
"float32x4",
|
||||
"uint32",
|
||||
"uint32x2",
|
||||
"uint32x3",
|
||||
"uint32x4",
|
||||
"sint32",
|
||||
"sint32x2",
|
||||
"sint32x3",
|
||||
"sint32x4",
|
||||
};
|
||||
|
||||
enum GPUInputStepMode {
|
||||
"vertex",
|
||||
"instance",
|
||||
};
|
||||
|
||||
dictionary GPUVertexAttribute {
|
||||
required GPUVertexFormat format;
|
||||
required GPUSize64 offset;
|
||||
required GPUIndex32 shaderLocation;
|
||||
};
|
||||
|
||||
dictionary GPUVertexBufferLayout {
|
||||
required GPUSize64 arrayStride;
|
||||
GPUInputStepMode stepMode = "vertex";
|
||||
required sequence<GPUVertexAttribute> attributes;
|
||||
};
|
||||
|
||||
dictionary GPUVertexState: GPUProgrammableStage {
|
||||
sequence<GPUVertexBufferLayout?> buffers = [];
|
||||
};
|
||||
|
||||
// GPURenderPipeline
|
||||
enum GPUPrimitiveTopology {
|
||||
"point-list",
|
||||
"line-list",
|
||||
"line-strip",
|
||||
"triangle-list",
|
||||
"triangle-strip"
|
||||
};
|
||||
|
||||
enum GPUFrontFace {
|
||||
"ccw",
|
||||
"cw"
|
||||
};
|
||||
|
||||
enum GPUCullMode {
|
||||
"none",
|
||||
"front",
|
||||
"back"
|
||||
};
|
||||
|
||||
dictionary GPUPrimitiveState {
|
||||
GPUPrimitiveTopology topology = "triangle-list";
|
||||
GPUIndexFormat stripIndexFormat;
|
||||
GPUFrontFace frontFace = "ccw";
|
||||
GPUCullMode cullMode = "none";
|
||||
};
|
||||
|
||||
dictionary GPUMultisampleState {
|
||||
GPUSize32 count = 1;
|
||||
GPUSampleMask mask = 0xFFFFFFFF;
|
||||
boolean alphaToCoverageEnabled = false;
|
||||
};
|
||||
|
||||
// BlendState
|
||||
enum GPUBlendFactor {
|
||||
"zero",
|
||||
|
@ -526,13 +773,18 @@ enum GPUBlendOperation {
|
|||
"max"
|
||||
};
|
||||
|
||||
dictionary GPUBlendDescriptor {
|
||||
dictionary GPUBlendComponent {
|
||||
GPUBlendFactor srcFactor = "one";
|
||||
GPUBlendFactor dstFactor = "zero";
|
||||
GPUBlendOperation operation = "add";
|
||||
};
|
||||
|
||||
typedef unsigned long GPUColorWriteFlags;
|
||||
dictionary GPUBlendState {
|
||||
required GPUBlendComponent color;
|
||||
required GPUBlendComponent alpha;
|
||||
};
|
||||
|
||||
typedef [EnforceRange] unsigned long GPUColorWriteFlags;
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUColorWrite {
|
||||
|
@ -543,12 +795,14 @@ interface GPUColorWrite {
|
|||
const GPUColorWriteFlags ALL = 0xF;
|
||||
};
|
||||
|
||||
dictionary GPUColorStateDescriptor {
|
||||
dictionary GPUColorTargetState {
|
||||
required GPUTextureFormat format;
|
||||
GPUBlendState blend;
|
||||
GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL
|
||||
};
|
||||
|
||||
GPUBlendDescriptor alphaBlend = {};
|
||||
GPUBlendDescriptor colorBlend = {};
|
||||
GPUColorWriteFlags writeMask = 0xF;
|
||||
dictionary GPUFragmentState: GPUProgrammableStage {
|
||||
required sequence<GPUColorTargetState> targets;
|
||||
};
|
||||
|
||||
// DepthStencilState
|
||||
|
@ -563,170 +817,43 @@ enum GPUStencilOperation {
|
|||
"decrement-wrap"
|
||||
};
|
||||
|
||||
dictionary GPUStencilStateFaceDescriptor {
|
||||
dictionary GPUStencilFaceState {
|
||||
GPUCompareFunction compare = "always";
|
||||
GPUStencilOperation failOp = "keep";
|
||||
GPUStencilOperation depthFailOp = "keep";
|
||||
GPUStencilOperation passOp = "keep";
|
||||
};
|
||||
|
||||
dictionary GPUDepthStencilStateDescriptor {
|
||||
dictionary GPUDepthStencilState {
|
||||
required GPUTextureFormat format;
|
||||
|
||||
boolean depthWriteEnabled = false;
|
||||
GPUCompareFunction depthCompare = "always";
|
||||
|
||||
GPUStencilStateFaceDescriptor stencilFront = {};
|
||||
GPUStencilStateFaceDescriptor stencilBack = {};
|
||||
GPUStencilFaceState stencilFront = {};
|
||||
GPUStencilFaceState stencilBack = {};
|
||||
|
||||
GPUStencilValue stencilReadMask = 0xFFFFFFFF;
|
||||
GPUStencilValue stencilWriteMask = 0xFFFFFFFF;
|
||||
};
|
||||
|
||||
// InputState
|
||||
enum GPUIndexFormat {
|
||||
"uint16",
|
||||
"uint32",
|
||||
};
|
||||
|
||||
enum GPUVertexFormat {
|
||||
"uchar2",
|
||||
"uchar4",
|
||||
"char2",
|
||||
"char4",
|
||||
"uchar2norm",
|
||||
"uchar4norm",
|
||||
"char2norm",
|
||||
"char4norm",
|
||||
"ushort2",
|
||||
"ushort4",
|
||||
"short2",
|
||||
"short4",
|
||||
"ushort2norm",
|
||||
"ushort4norm",
|
||||
"short2norm",
|
||||
"short4norm",
|
||||
"half2",
|
||||
"half4",
|
||||
"float",
|
||||
"float2",
|
||||
"float3",
|
||||
"float4",
|
||||
"uint",
|
||||
"uint2",
|
||||
"uint3",
|
||||
"uint4",
|
||||
"int",
|
||||
"int2",
|
||||
"int3",
|
||||
"int4",
|
||||
};
|
||||
|
||||
enum GPUInputStepMode {
|
||||
"vertex",
|
||||
"instance",
|
||||
};
|
||||
|
||||
dictionary GPUVertexAttributeDescriptor {
|
||||
required GPUVertexFormat format;
|
||||
required GPUSize64 offset;
|
||||
required GPUIndex32 shaderLocation;
|
||||
};
|
||||
|
||||
dictionary GPUVertexBufferLayoutDescriptor {
|
||||
required GPUSize64 arrayStride;
|
||||
GPUInputStepMode stepMode = "vertex";
|
||||
required sequence<GPUVertexAttributeDescriptor> attributes;
|
||||
};
|
||||
|
||||
dictionary GPUVertexStateDescriptor {
|
||||
GPUIndexFormat indexFormat = "uint32";
|
||||
sequence<GPUVertexBufferLayoutDescriptor?> vertexBuffers = [];
|
||||
};
|
||||
|
||||
// ShaderModule
|
||||
typedef (Uint32Array or DOMString) GPUShaderCode;
|
||||
|
||||
dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
|
||||
required GPUShaderCode code;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUShaderModule {
|
||||
};
|
||||
GPUShaderModule includes GPUObjectBase;
|
||||
|
||||
// Common stuff for ComputePipeline and RenderPipeline
|
||||
dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
|
||||
GPUPipelineLayout layout;
|
||||
};
|
||||
|
||||
interface mixin GPUPipelineBase {
|
||||
GPUBindGroupLayout getBindGroupLayout(unsigned long index);
|
||||
};
|
||||
|
||||
dictionary GPUProgrammableStageDescriptor {
|
||||
required GPUShaderModule module;
|
||||
required DOMString entryPoint;
|
||||
};
|
||||
|
||||
// ComputePipeline
|
||||
dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
|
||||
required GPUProgrammableStageDescriptor computeStage;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUComputePipeline {
|
||||
};
|
||||
GPUComputePipeline includes GPUObjectBase;
|
||||
GPUComputePipeline includes GPUPipelineBase;
|
||||
|
||||
// GPURenderPipeline
|
||||
enum GPUPrimitiveTopology {
|
||||
"point-list",
|
||||
"line-list",
|
||||
"line-strip",
|
||||
"triangle-list",
|
||||
"triangle-strip"
|
||||
};
|
||||
|
||||
dictionary GPURasterizationStateDescriptor {
|
||||
GPUFrontFace frontFace = "ccw";
|
||||
GPUCullMode cullMode = "none";
|
||||
|
||||
GPUDepthBias depthBias = 0;
|
||||
float depthBiasSlopeScale = 0;
|
||||
float depthBiasClamp = 0;
|
||||
};
|
||||
|
||||
enum GPUFrontFace {
|
||||
"ccw",
|
||||
"cw"
|
||||
};
|
||||
|
||||
enum GPUCullMode {
|
||||
"none",
|
||||
"front",
|
||||
"back"
|
||||
// Enable depth clamping (requires "depth-clamping" feature)
|
||||
boolean clampDepth = false;
|
||||
};
|
||||
|
||||
dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
|
||||
required GPUProgrammableStageDescriptor vertexStage;
|
||||
GPUProgrammableStageDescriptor fragmentStage;
|
||||
|
||||
required GPUPrimitiveTopology primitiveTopology;
|
||||
GPURasterizationStateDescriptor rasterizationState = {};
|
||||
required sequence<GPUColorStateDescriptor> colorStates;
|
||||
GPUDepthStencilStateDescriptor depthStencilState;
|
||||
GPUVertexStateDescriptor vertexState = {};
|
||||
|
||||
GPUSize32 sampleCount = 1;
|
||||
GPUSampleMask sampleMask = 0xFFFFFFFF;
|
||||
boolean alphaToCoverageEnabled = false;
|
||||
required GPUVertexState vertex;
|
||||
GPUPrimitiveState primitive = {};
|
||||
GPUDepthStencilState depthStencil;
|
||||
GPUMultisampleState multisample = {};
|
||||
GPUFragmentState fragment;
|
||||
};
|
||||
|
||||
//TODO: Serializable
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696219
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPURenderPipeline {
|
||||
|
@ -768,6 +895,7 @@ dictionary GPURenderPassDepthStencilAttachmentDescriptor {
|
|||
dictionary GPURenderPassDescriptor : GPUObjectDescriptorBase {
|
||||
required sequence<GPURenderPassColorAttachmentDescriptor> colorAttachments;
|
||||
GPURenderPassDepthStencilAttachmentDescriptor depthStencilAttachment;
|
||||
GPUQuerySet occlusionQuerySet;
|
||||
};
|
||||
|
||||
dictionary GPUTextureDataLayout {
|
||||
|
@ -831,9 +959,9 @@ interface GPUCommandEncoder {
|
|||
GPUExtent3D copySize);
|
||||
*/
|
||||
|
||||
//void pushDebugGroup(DOMString groupLabel);
|
||||
//void pushDebugGroup(USVString groupLabel);
|
||||
//void popDebugGroup();
|
||||
//void insertDebugMarker(DOMString markerLabel);
|
||||
//void insertDebugMarker(USVString markerLabel);
|
||||
|
||||
[NewObject]
|
||||
GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
|
||||
|
@ -844,16 +972,16 @@ interface mixin GPUProgrammablePassEncoder {
|
|||
void setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
|
||||
optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
|
||||
|
||||
//void pushDebugGroup(DOMString groupLabel);
|
||||
//void pushDebugGroup(USVString groupLabel);
|
||||
//void popDebugGroup();
|
||||
//void insertDebugMarker(DOMString markerLabel);
|
||||
//void insertDebugMarker(USVString markerLabel);
|
||||
};
|
||||
|
||||
// Render Pass
|
||||
interface mixin GPURenderEncoderBase {
|
||||
void setPipeline(GPURenderPipeline pipeline);
|
||||
|
||||
void setIndexBuffer(GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size = 0);
|
||||
void setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size = 0);
|
||||
void setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size = 0);
|
||||
|
||||
void draw(GPUSize32 vertexCount,
|
||||
|
@ -927,46 +1055,67 @@ interface GPUCommandBuffer {
|
|||
};
|
||||
GPUCommandBuffer includes GPUObjectBase;
|
||||
|
||||
dictionary GPURenderBundleEncoderDescriptor : GPUObjectDescriptorBase {
|
||||
required sequence<GPUTextureFormat> colorFormats;
|
||||
GPUTextureFormat depthStencilFormat;
|
||||
GPUSize32 sampleCount = 1;
|
||||
};
|
||||
|
||||
// Render Bundle
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPURenderBundleEncoder {
|
||||
//GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
|
||||
};
|
||||
GPURenderBundleEncoder includes GPUObjectBase;
|
||||
//GPURenderBundleEncoder includes GPURenderEncoderBase;
|
||||
|
||||
dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase {
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPURenderBundle {
|
||||
};
|
||||
GPURenderBundle includes GPUObjectBase;
|
||||
|
||||
// ****************************************************************************
|
||||
// OTHER (Fence, Queue SwapChain, Device)
|
||||
// ****************************************************************************
|
||||
dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase {
|
||||
};
|
||||
|
||||
// Fence
|
||||
dictionary GPUFenceDescriptor : GPUObjectDescriptorBase {
|
||||
GPUFenceValue initialValue = 0;
|
||||
dictionary GPURenderBundleEncoderDescriptor : GPUObjectDescriptorBase {
|
||||
required sequence<GPUTextureFormat> colorFormats;
|
||||
GPUTextureFormat depthStencilFormat;
|
||||
GPUSize32 sampleCount = 1;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUFence {
|
||||
//GPUFenceValue getCompletedValue();
|
||||
//Promise<void> onCompletion(GPUFenceValue completionValue);
|
||||
interface GPURenderBundleEncoder {
|
||||
//GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
|
||||
};
|
||||
GPUFence includes GPUObjectBase;
|
||||
GPURenderBundleEncoder includes GPUObjectBase;
|
||||
//TODO
|
||||
//GPURenderBundleEncoder includes GPUProgrammablePassEncoder;
|
||||
//GPURenderBundleEncoder includes GPURenderEncoderBase;
|
||||
|
||||
// ****************************************************************************
|
||||
// OTHER (Query, Queue, SwapChain, Device)
|
||||
// ****************************************************************************
|
||||
|
||||
// Query set
|
||||
enum GPUQueryType {
|
||||
"occlusion",
|
||||
"pipeline-statistics",
|
||||
"timestamp"
|
||||
};
|
||||
|
||||
enum GPUPipelineStatisticName {
|
||||
"vertex-shader-invocations",
|
||||
"clipper-invocations",
|
||||
"clipper-primitives-out",
|
||||
"fragment-shader-invocations",
|
||||
"compute-shader-invocations"
|
||||
};
|
||||
|
||||
dictionary GPUQuerySetDescriptor : GPUObjectDescriptorBase {
|
||||
required GPUQueryType type;
|
||||
required GPUSize32 count;
|
||||
sequence<GPUPipelineStatisticName> pipelineStatistics = [];
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
Exposed=Window]
|
||||
interface GPUQuerySet {
|
||||
void destroy();
|
||||
};
|
||||
GPUQuerySet includes GPUObjectBase;
|
||||
|
||||
//TODO: use [AllowShared] on BufferSource
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696216
|
||||
// https://github.com/heycam/webidl/issues/961
|
||||
|
||||
// Queue
|
||||
[Pref="dom.webgpu.enabled",
|
||||
|
@ -974,21 +1123,21 @@ GPUFence includes GPUObjectBase;
|
|||
interface GPUQueue {
|
||||
void submit(sequence<GPUCommandBuffer> buffers);
|
||||
|
||||
//GPUFence createFence(optional GPUFenceDescriptor descriptor = {});
|
||||
//void signal(GPUFence fence, GPUFenceValue signalValue);
|
||||
//TODO:
|
||||
//Promise<void> onSubmittedWorkDone();
|
||||
|
||||
[Throws]
|
||||
void writeBuffer(
|
||||
GPUBuffer buffer,
|
||||
GPUSize64 bufferOffset,
|
||||
[AllowShared] ArrayBuffer data,
|
||||
BufferSource data,
|
||||
optional GPUSize64 dataOffset = 0,
|
||||
optional GPUSize64 size);
|
||||
|
||||
[Throws]
|
||||
void writeTexture(
|
||||
GPUTextureCopyView destination,
|
||||
[AllowShared] ArrayBuffer data,
|
||||
BufferSource data,
|
||||
GPUTextureDataLayout dataLayout,
|
||||
GPUExtent3D size);
|
||||
};
|
||||
|
@ -1015,5 +1164,5 @@ interface GPUCanvasContext {
|
|||
[Throws]
|
||||
GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor);
|
||||
|
||||
//Promise<GPUTextureFormat> getSwapChainPreferredFormat(GPUDevice device);
|
||||
GPUTextureFormat getSwapChainPreferredFormat(GPUAdapter adapter);
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ assignees: ''
|
|||
|
||||
---
|
||||
|
||||
<!-- Thank you for filing this! Please read the [debugging tips](https://github.com/gfx-rs/wgpu/wiki/Debbugging-wgpu-Applications).
|
||||
<!-- Thank you for filing this! Please read the [debugging tips](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications).
|
||||
That may let you investigate on your own, or provide additional information that helps us to assist.-->
|
||||
|
||||
**Description**
|
||||
|
|
|
@ -26,17 +26,21 @@ jobs:
|
|||
PKG_CONFIG_ALLOW_CROSS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install -y -qq libegl1-mesa-dev
|
||||
echo "$ANDROID_HOME/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH
|
||||
- run: echo "$ANDROID_HOME/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH
|
||||
- run: rustup component add clippy
|
||||
- run: rustup target add ${{ env.TARGET }}
|
||||
- run: cargo clippy --target ${{ env.TARGET }}
|
||||
- name: Additional core features
|
||||
run: cargo check --manifest-path wgpu-core/Cargo.toml --features trace --target ${{ env.TARGET }}
|
||||
|
||||
webgl_build:
|
||||
name: Web Assembly
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: rustup target add wasm32-unknown-unknown
|
||||
- run: cargo build --manifest-path wgpu-core/Cargo.toml --target wasm32-unknown-unknown
|
||||
|
||||
build:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
@ -61,9 +65,7 @@ jobs:
|
|||
- name: Ubuntu Stable
|
||||
os: ubuntu-18.04
|
||||
channel: stable
|
||||
prepare_command: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install -y -qq libegl1-mesa-dev
|
||||
prepare_command:
|
||||
additional_core_features: trace,replay
|
||||
additional_player_features:
|
||||
- name: Ubuntu Nightly
|
||||
|
@ -71,8 +73,6 @@ jobs:
|
|||
channel: nightly
|
||||
prepare_command: |
|
||||
sudo apt-get update -y -qq
|
||||
echo "Installing EGL"
|
||||
sudo apt-get install -y -qq libegl1-mesa-dev
|
||||
echo "Installing Vulkan"
|
||||
sudo apt-get install -y -qq mesa-vulkan-drivers
|
||||
additional_core_features: serial-pass
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
#Cargo.lock
|
||||
.fuse_hidden*
|
||||
.DS_Store
|
||||
.vscode
|
||||
.vs
|
||||
|
|
|
@ -1,5 +1,26 @@
|
|||
# Change Log
|
||||
|
||||
## v0.7 (2021-01-31)
|
||||
- Major API changes:
|
||||
- `RenderPipelineDescriptor`
|
||||
- `BindingType`
|
||||
- Features:
|
||||
- (beta) WGSL support, including the ability to bypass SPIR-V entirely
|
||||
- (beta) implicit bind group layout support
|
||||
- timestamp and pipeline statistics queries
|
||||
- ETC2 and ASTC compressed textures
|
||||
- (beta) targeting WASM with WebGL backend
|
||||
- reduced dependencies
|
||||
- Native-only:
|
||||
- clamp-to-border addressing
|
||||
- polygon fill modes
|
||||
- query a format for extra capabilities
|
||||
- `f64` support in shaders
|
||||
- Validation:
|
||||
- shader interface
|
||||
- render pipeline descriptor
|
||||
- vertex buffers
|
||||
|
||||
## v0.6 (2020-08-17)
|
||||
- Crates:
|
||||
- C API is moved to [another repository](https://github.com/gfx-rs/wgpu-native)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -5,3 +5,15 @@ members = [
|
|||
"wgpu-core",
|
||||
"wgpu-types",
|
||||
]
|
||||
|
||||
[patch."https://github.com/gfx-rs/gfx"]
|
||||
#hal = { package = "gfx-hal", path = "../gfx/src/hal" }
|
||||
#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan", features = ["naga"] }
|
||||
#gfx-backend-metal = { path = "../gfx/src/backend/metal", features = ["naga"] }
|
||||
#gfx-backend-gl = { path = "../gfx/src/backend/gl", features = ["naga"] }
|
||||
#gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" }
|
||||
#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" }
|
||||
#gfx-backend-empty = { path = "../gfx/src/backend/empty" }
|
||||
|
||||
[patch."https://github.com/gfx-rs/naga"]
|
||||
#naga = { path = "../naga" }
|
||||
|
|
|
@ -12,7 +12,7 @@ This is the core logic of an experimental [WebGPU](https://www.w3.org/community/
|
|||
The implementation consists of the following parts:
|
||||
|
||||
- [![Crates.io](https://img.shields.io/crates/v/wgpu-core.svg?label=wgpu-core)](https://crates.io/crates/wgpu-core) [![docs.rs](https://docs.rs/wgpu-core/badge.svg)](https://docs.rs/wgpu-core/) - internal Rust API for WebGPU implementations to use
|
||||
- [![Crates.io](https://img.shields.io/crates/v/wgpu-types.svg?label=wgpu-types)](https://crates.io/crates/wgpu-types) [![docs.rs](https://docs.rs/wgpu-types/badge.svg)](https://docs.rs/wgpu-types/) - Rust types shared between `wgpu-core`, `wgpu-native`, and `wgpu-rs`
|
||||
- [![Crates.io](https://img.shields.io/crates/v/wgpu-types.svg?label=wgpu-types)](https://crates.io/crates/wgpu-types) [![docs.rs](https://docs.rs/wgpu-types/badge.svg)](https://docs.rs/wgpu-types/) - Rust types shared between `wgpu-core` and `wgpu-rs`
|
||||
- `player` - standalone application for replaying the API traces, uses `winit`
|
||||
|
||||
This repository contains the core of `wgpu`, and is not usable directly by applications.
|
||||
|
@ -23,10 +23,10 @@ If you are looking for the native implementation or bindings to the API in other
|
|||
|
||||
API | Windows 7/10 | Linux & Android | macOS & iOS |
|
||||
----- | ------------------ | ------------------ | ------------------ |
|
||||
DX11 | :white_check_mark: | | |
|
||||
DX12 | :heavy_check_mark: | | |
|
||||
Vulkan | :heavy_check_mark: | :heavy_check_mark: | |
|
||||
Metal | | | :heavy_check_mark: |
|
||||
OpenGL | | :construction: | :construction: |
|
||||
DX11 | :ok: | | |
|
||||
DX12 | :white_check_mark: | | |
|
||||
Vulkan | :white_check_mark: | :white_check_mark: | |
|
||||
Metal | | | :white_check_mark: |
|
||||
GL ES3 | | :construction: | |
|
||||
|
||||
:heavy_check_mark: = Primary support — :white_check_mark: = Secondary support — :construction: = Unsupported, but support in progress
|
||||
:white_check_mark: = Primary support — :ok: = Secondary support — :construction: = Unsupported, but support in progress
|
||||
|
|
|
@ -12,5 +12,4 @@ publish = false
|
|||
[dependencies.wgc]
|
||||
path = "../wgpu-core"
|
||||
package = "wgpu-core"
|
||||
version = "0.6"
|
||||
features = ["serial-pass", "trace"]
|
||||
|
|
|
@ -13,27 +13,31 @@ license = "MPL-2.0"
|
|||
publish = false
|
||||
|
||||
[features]
|
||||
cross = ["wgc/cross"]
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.7"
|
||||
env_logger = "0.8"
|
||||
log = "0.4"
|
||||
raw-window-handle = "0.3"
|
||||
renderdoc = { version = "0.8", optional = true, default_features = false }
|
||||
renderdoc = { version = "0.10", optional = true, default_features = false }
|
||||
ron = "0.6"
|
||||
winit = { version = "0.22", optional = true }
|
||||
winit = { version = "0.24", optional = true }
|
||||
|
||||
[dependencies.wgt]
|
||||
path = "../wgpu-types"
|
||||
package = "wgpu-types"
|
||||
version = "0.6"
|
||||
features = ["replay"]
|
||||
|
||||
[dependencies.wgc]
|
||||
path = "../wgpu-core"
|
||||
package = "wgpu-core"
|
||||
version = "0.6"
|
||||
features = ["replay", "raw-window-handle"]
|
||||
|
||||
#[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies.gfx-backend-metal]
|
||||
#git = "https://github.com/gfx-rs/gfx"
|
||||
#rev = "" # insert revision here
|
||||
#features = ["auto-capture"]
|
||||
|
||||
[dependencies.wgpu-subscriber]
|
||||
git = "https://github.com/gfx-rs/subscriber.git"
|
||||
rev = "cdc9feb53f152f9c41905ed9efeff2c1ed214361"
|
||||
|
|
|
@ -51,7 +51,7 @@ fn main() {
|
|||
#[cfg(feature = "winit")]
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("wgpu player")
|
||||
.with_resizable(false)
|
||||
.with_resizable(true)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
|
@ -123,6 +123,7 @@ fn main() {
|
|||
event_loop::ControlFlow,
|
||||
};
|
||||
|
||||
let mut resize_desc = None;
|
||||
let mut frame_count = 0;
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
@ -130,21 +131,27 @@ fn main() {
|
|||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => loop {
|
||||
Event::RedrawRequested(_) if resize_desc.is_none() => loop {
|
||||
match actions.pop() {
|
||||
Some(trace::Action::CreateSwapChain(id, desc)) => {
|
||||
log::info!("Initializing the swapchain");
|
||||
assert_eq!(id.to_surface_id(), surface);
|
||||
window.set_inner_size(winit::dpi::PhysicalSize::new(
|
||||
desc.width,
|
||||
desc.height,
|
||||
));
|
||||
gfx_select!(device => global.device_create_swap_chain(device, surface, &desc));
|
||||
let current_size: (u32, u32) = window.inner_size().into();
|
||||
let size = (desc.width, desc.height);
|
||||
if current_size != size {
|
||||
window.set_inner_size(winit::dpi::PhysicalSize::new(
|
||||
desc.width,
|
||||
desc.height,
|
||||
));
|
||||
resize_desc = Some(desc);
|
||||
} else {
|
||||
gfx_select!(device => global.device_create_swap_chain(device, surface, &desc)).unwrap();
|
||||
}
|
||||
}
|
||||
Some(trace::Action::PresentSwapChain(id)) => {
|
||||
frame_count += 1;
|
||||
log::debug!("Presenting frame {}", frame_count);
|
||||
gfx_select!(device => global.swap_chain_present(id));
|
||||
gfx_select!(device => global.swap_chain_present(id)).unwrap();
|
||||
break;
|
||||
}
|
||||
Some(action) => {
|
||||
|
@ -154,6 +161,11 @@ fn main() {
|
|||
}
|
||||
},
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::Resized(_) => {
|
||||
if let Some(desc) = resize_desc.take() {
|
||||
gfx_select!(device => global.device_create_swap_chain(device, surface, &desc)).unwrap();
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
|
@ -170,7 +182,7 @@ fn main() {
|
|||
},
|
||||
Event::LoopDestroyed => {
|
||||
log::info!("Closing");
|
||||
gfx_select!(device => global.device_poll(device, true));
|
||||
gfx_select!(device => global.device_poll(device, true)).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,28 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
trace::Command::CopyTextureToTexture { src, dst, size } => self
|
||||
.command_encoder_copy_texture_to_texture::<B>(encoder, &src, &dst, &size)
|
||||
.unwrap(),
|
||||
trace::Command::WriteTimestamp {
|
||||
query_set_id,
|
||||
query_index,
|
||||
} => self
|
||||
.command_encoder_write_timestamp::<B>(encoder, query_set_id, query_index)
|
||||
.unwrap(),
|
||||
trace::Command::ResolveQuerySet {
|
||||
query_set_id,
|
||||
start_query,
|
||||
query_count,
|
||||
destination,
|
||||
destination_offset,
|
||||
} => self
|
||||
.command_encoder_resolve_query_set::<B>(
|
||||
encoder,
|
||||
query_set_id,
|
||||
start_query,
|
||||
query_count,
|
||||
destination,
|
||||
destination_offset,
|
||||
)
|
||||
.unwrap(),
|
||||
trace::Command::RunComputePass { base } => {
|
||||
self.command_encoder_run_compute_pass_impl::<B>(encoder, base.as_ref())
|
||||
.unwrap();
|
||||
|
@ -117,6 +139,7 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
) {
|
||||
use wgc::device::trace::Action as A;
|
||||
log::info!("action {:?}", action);
|
||||
//TODO: find a way to force ID perishing without excessive `maintain()` calls.
|
||||
match action {
|
||||
A::Init { .. } => panic!("Unexpected Action::Init: has to be the first action only"),
|
||||
A::CreateSwapChain { .. } | A::PresentSwapChain(_) => {
|
||||
|
@ -160,7 +183,7 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
}
|
||||
}
|
||||
A::DestroyTextureView(id) => {
|
||||
self.texture_view_drop::<B>(id).unwrap();
|
||||
self.texture_view_drop::<B>(id, true).unwrap();
|
||||
}
|
||||
A::CreateSampler(id, desc) => {
|
||||
self.device_maintain_ids::<B>(device).unwrap();
|
||||
|
@ -173,12 +196,11 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
self.sampler_drop::<B>(id);
|
||||
}
|
||||
A::GetSwapChainTexture { id, parent_id } => {
|
||||
if let Some(id) = id {
|
||||
self.swap_chain_get_current_texture_view::<B>(parent_id, id)
|
||||
.unwrap()
|
||||
.view_id
|
||||
.unwrap();
|
||||
}
|
||||
self.device_maintain_ids::<B>(device).unwrap();
|
||||
self.swap_chain_get_current_texture_view::<B>(parent_id, id)
|
||||
.unwrap()
|
||||
.view_id
|
||||
.unwrap();
|
||||
}
|
||||
A::CreateBindGroupLayout(id, desc) => {
|
||||
let (_, error) = self.device_create_bind_group_layout::<B>(device, &desc, id);
|
||||
|
@ -209,22 +231,20 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
A::DestroyBindGroup(id) => {
|
||||
self.bind_group_drop::<B>(id);
|
||||
}
|
||||
A::CreateShaderModule { id, data, label } => {
|
||||
let desc = wgc::pipeline::ShaderModuleDescriptor {
|
||||
source: if data.ends_with(".wgsl") {
|
||||
let code = fs::read_to_string(dir.join(data)).unwrap();
|
||||
wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code))
|
||||
} else {
|
||||
let byte_vec = fs::read(dir.join(data)).unwrap();
|
||||
let spv = byte_vec
|
||||
.chunks(4)
|
||||
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
|
||||
.collect::<Vec<_>>();
|
||||
wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv))
|
||||
},
|
||||
label,
|
||||
A::CreateShaderModule { id, desc, data } => {
|
||||
let source = if data.ends_with(".wgsl") {
|
||||
let code = fs::read_to_string(dir.join(data)).unwrap();
|
||||
wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code))
|
||||
} else {
|
||||
let byte_vec = fs::read(dir.join(&data))
|
||||
.unwrap_or_else(|e| panic!("Unable to open '{}': {:?}", data, e));
|
||||
let spv = byte_vec
|
||||
.chunks(4)
|
||||
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
|
||||
.collect::<Vec<_>>();
|
||||
wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv))
|
||||
};
|
||||
let (_, error) = self.device_create_shader_module::<B>(device, &desc, id);
|
||||
let (_, error) = self.device_create_shader_module::<B>(device, &desc, source, id);
|
||||
if let Some(e) = error {
|
||||
panic!("{:?}", e);
|
||||
}
|
||||
|
@ -232,10 +252,21 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
A::DestroyShaderModule(id) => {
|
||||
self.shader_module_drop::<B>(id);
|
||||
}
|
||||
A::CreateComputePipeline(id, desc) => {
|
||||
A::CreateComputePipeline {
|
||||
id,
|
||||
desc,
|
||||
implicit_context,
|
||||
} => {
|
||||
self.device_maintain_ids::<B>(device).unwrap();
|
||||
let implicit_ids =
|
||||
implicit_context
|
||||
.as_ref()
|
||||
.map(|ic| wgc::device::ImplicitPipelineIds {
|
||||
root_id: ic.root_id,
|
||||
group_ids: &ic.group_ids,
|
||||
});
|
||||
let (_, _, error) =
|
||||
self.device_create_compute_pipeline::<B>(device, &desc, id, None);
|
||||
self.device_create_compute_pipeline::<B>(device, &desc, id, implicit_ids);
|
||||
if let Some(e) = error {
|
||||
panic!("{:?}", e);
|
||||
}
|
||||
|
@ -243,10 +274,21 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
A::DestroyComputePipeline(id) => {
|
||||
self.compute_pipeline_drop::<B>(id);
|
||||
}
|
||||
A::CreateRenderPipeline(id, desc) => {
|
||||
A::CreateRenderPipeline {
|
||||
id,
|
||||
desc,
|
||||
implicit_context,
|
||||
} => {
|
||||
self.device_maintain_ids::<B>(device).unwrap();
|
||||
let implicit_ids =
|
||||
implicit_context
|
||||
.as_ref()
|
||||
.map(|ic| wgc::device::ImplicitPipelineIds {
|
||||
root_id: ic.root_id,
|
||||
group_ids: &ic.group_ids,
|
||||
});
|
||||
let (_, _, error) =
|
||||
self.device_create_render_pipeline::<B>(device, &desc, id, None);
|
||||
self.device_create_render_pipeline::<B>(device, &desc, id, implicit_ids);
|
||||
if let Some(e) = error {
|
||||
panic!("{:?}", e);
|
||||
}
|
||||
|
@ -269,6 +311,16 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
A::DestroyRenderBundle(id) => {
|
||||
self.render_bundle_drop::<B>(id);
|
||||
}
|
||||
A::CreateQuerySet { id, desc } => {
|
||||
self.device_maintain_ids::<B>(device).unwrap();
|
||||
let (_, error) = self.device_create_query_set::<B>(device, &desc, id);
|
||||
if let Some(e) = error {
|
||||
panic!("{:?}", e);
|
||||
}
|
||||
}
|
||||
A::DestroyQuerySet(id) => {
|
||||
self.query_set_drop::<B>(id);
|
||||
}
|
||||
A::WriteBuffer {
|
||||
id,
|
||||
data,
|
||||
|
@ -296,6 +348,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
|
|||
self.queue_write_texture::<B>(device, &to, &bin, &layout, &size)
|
||||
.unwrap();
|
||||
}
|
||||
A::Submit(_index, ref commands) if commands.is_empty() => {
|
||||
self.queue_submit::<B>(device, &[]).unwrap();
|
||||
}
|
||||
A::Submit(_index, commands) => {
|
||||
let (encoder, error) = self.device_create_command_encoder::<B>(
|
||||
device,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
backends: (bits: 0xF),
|
||||
tests: [
|
||||
"buffer-copy.ron",
|
||||
"buffer-zero-init.ron",
|
||||
"bind-group.ron",
|
||||
"quad.ron",
|
||||
],
|
||||
|
|
|
@ -9,17 +9,23 @@
|
|||
)),
|
||||
CreateShaderModule(
|
||||
id: Id(0, 1, Empty),
|
||||
label: None,
|
||||
data: "empty.comp.spv",
|
||||
),
|
||||
CreateComputePipeline(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
layout: Some(Id(0, 1, Empty)),
|
||||
compute_stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "main",
|
||||
desc: (
|
||||
label: None,
|
||||
flags: (bits: 3),
|
||||
),
|
||||
)),
|
||||
data: "empty.wgsl",
|
||||
),
|
||||
CreateComputePipeline(
|
||||
id: Id(0, 1, Empty),
|
||||
desc: (
|
||||
label: None,
|
||||
layout: Some(Id(0, 1, Empty)),
|
||||
stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "main",
|
||||
),
|
||||
),
|
||||
),
|
||||
CreateBuffer(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
size: 16,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[[builtin(global_invocation_id)]]
|
||||
var global_id: vec3<u32>;
|
||||
|
||||
[[block]]
|
||||
struct InOutBuffer {
|
||||
data: [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]]
|
||||
var<storage> buffer: [[access(read_write)]] InOutBuffer;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
buffer.data[global_id.x] = buffer.data[global_id.x] + global_id.x;
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
(
|
||||
features: (bits: 0x0),
|
||||
expectations: [
|
||||
// Ensuring that mapping zero-inits buffers.
|
||||
(
|
||||
name: "mapped_at_creation: false, with MAP_WRITE",
|
||||
buffer: (index: 0, epoch: 1),
|
||||
offset: 0,
|
||||
data: Raw([0x00, 0x00, 0x00, 0x00]),
|
||||
),
|
||||
(
|
||||
name: "mapped_at_creation: false, without MAP_WRITE",
|
||||
buffer: (index: 1, epoch: 1),
|
||||
offset: 0,
|
||||
data: Raw([0x00, 0x00, 0x00, 0x00]),
|
||||
),
|
||||
(
|
||||
name: "partially written buffer",
|
||||
buffer: (index: 2, epoch: 1),
|
||||
offset: 0,
|
||||
data: Raw([0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xBF,
|
||||
0x00, 0x00, 0x80, 0xBF,
|
||||
0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00]),
|
||||
),
|
||||
// Ensuring that binding zero-inits buffers
|
||||
// (by observing correct side effects of compute shader reading & writing values)
|
||||
(
|
||||
name: "buffer has correct values",
|
||||
buffer: (index: 3, epoch: 1),
|
||||
offset: 0,
|
||||
data: Raw([0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00]),
|
||||
)
|
||||
],
|
||||
actions: [
|
||||
CreateBuffer(
|
||||
Id(0, 1, Empty),
|
||||
(
|
||||
label: Some("mapped_at_creation: false, with MAP_WRITE"),
|
||||
size: 16,
|
||||
usage: (
|
||||
bits: 131, // STORAGE + MAP_READ + MAP_WRITE
|
||||
),
|
||||
mapped_at_creation: false,
|
||||
),
|
||||
),
|
||||
CreateBuffer(
|
||||
Id(1, 1, Empty),
|
||||
(
|
||||
label: Some("mapped_at_creation: false, without MAP_WRITE"),
|
||||
size: 16,
|
||||
usage: (
|
||||
bits: 129, // STORAGE + MAP_READ
|
||||
),
|
||||
mapped_at_creation: false,
|
||||
),
|
||||
),
|
||||
CreateBuffer(
|
||||
Id(2, 1, Empty),
|
||||
(
|
||||
label: Some("partially written"),
|
||||
size: 24,
|
||||
usage: (
|
||||
bits: 9, // MAP_READ + COPY_DST
|
||||
),
|
||||
mapped_at_creation: false,
|
||||
),
|
||||
),
|
||||
WriteBuffer(
|
||||
id: Id(2, 1, Empty),
|
||||
data: "data1.bin",
|
||||
range: (
|
||||
start: 4,
|
||||
end: 20,
|
||||
),
|
||||
queued: true,
|
||||
),
|
||||
CreateShaderModule(
|
||||
id: Id(0, 1, Empty),
|
||||
desc: (
|
||||
label: None,
|
||||
flags: (bits: 3),
|
||||
),
|
||||
data: "buffer-zero-init-for-binding.wgsl",
|
||||
),
|
||||
CreateBuffer(Id(3, 1, Empty), (
|
||||
label: Some("used in binding"),
|
||||
size: 16,
|
||||
usage: (
|
||||
bits: 129, // STORAGE + MAP_READ
|
||||
),
|
||||
mapped_at_creation: false,
|
||||
)),
|
||||
CreateBindGroupLayout(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
entries: [
|
||||
(
|
||||
binding: 0,
|
||||
visibility: (
|
||||
bits: 4,
|
||||
),
|
||||
ty: Buffer(
|
||||
ty: Storage(
|
||||
read_only: false,
|
||||
),
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: Some(16),
|
||||
),
|
||||
count: None,
|
||||
),
|
||||
],
|
||||
)),
|
||||
CreateBindGroup(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
layout: Id(0, 1, Empty),
|
||||
entries: [
|
||||
(
|
||||
binding: 0,
|
||||
resource: Buffer((
|
||||
buffer_id: Id(3, 1, Empty),
|
||||
offset: 0,
|
||||
size: Some(16),
|
||||
)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
CreatePipelineLayout(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
bind_group_layouts: [
|
||||
Id(0, 1, Empty),
|
||||
],
|
||||
push_constant_ranges: [],
|
||||
)),
|
||||
CreateComputePipeline(
|
||||
id: Id(0, 1, Empty),
|
||||
desc: (
|
||||
label: None,
|
||||
layout: Some(Id(0, 1, Empty)),
|
||||
stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "main",
|
||||
),
|
||||
),
|
||||
),
|
||||
Submit(1, [
|
||||
RunComputePass(
|
||||
base: (
|
||||
label: None,
|
||||
commands: [
|
||||
SetPipeline(Id(0, 1, Empty)),
|
||||
SetBindGroup(
|
||||
index: 0,
|
||||
num_dynamic_offsets: 0,
|
||||
bind_group_id: Id(0, 1, Empty),
|
||||
),
|
||||
Dispatch((4, 1, 1)),
|
||||
],
|
||||
dynamic_offsets: [],
|
||||
string_data: [],
|
||||
push_constant_data: [],
|
||||
),
|
||||
)
|
||||
]),
|
||||
]
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
#version 450
|
||||
layout(local_size_x = 1) in;
|
||||
|
||||
void main() {
|
||||
}
|
Двоичные данные
gfx/wgpu/player/tests/data/empty.comp.spv
Двоичные данные
gfx/wgpu/player/tests/data/empty.comp.spv
Двоичный файл не отображается.
|
@ -0,0 +1,3 @@
|
|||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
Двоичные данные
gfx/wgpu/player/tests/data/quad.frag.spv
Двоичные данные
gfx/wgpu/player/tests/data/quad.frag.spv
Двоичный файл не отображается.
|
@ -11,18 +11,18 @@
|
|||
actions: [
|
||||
CreateShaderModule(
|
||||
id: Id(0, 1, Empty),
|
||||
data: "quad.vert.spv",
|
||||
),
|
||||
CreateShaderModule(
|
||||
id: Id(1, 1, Empty),
|
||||
data: "quad.frag.spv",
|
||||
desc: (
|
||||
label: None,
|
||||
flags: (bits: 3),
|
||||
),
|
||||
data: "quad.wgsl",
|
||||
),
|
||||
CreateTexture(Id(0, 1, Empty), (
|
||||
label: Some("Output Texture"),
|
||||
size: (
|
||||
width: 64,
|
||||
height: 64,
|
||||
depth: 1,
|
||||
depth_or_array_layers: 1,
|
||||
),
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
|
@ -53,46 +53,31 @@
|
|||
bind_group_layouts: [],
|
||||
push_constant_ranges: [],
|
||||
)),
|
||||
CreateRenderPipeline(Id(0, 1, Empty), (
|
||||
label: None,
|
||||
layout: Some(Id(0, 1, Empty)),
|
||||
vertex_stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "main",
|
||||
),
|
||||
fragment_stage: Some((
|
||||
module: Id(1, 1, Empty),
|
||||
entry_point: "main",
|
||||
)),
|
||||
rasterization_state: None,
|
||||
primitive_topology: TriangleList,
|
||||
color_states: [
|
||||
(
|
||||
format: Rgba8Unorm,
|
||||
alpha_blend: (
|
||||
src_factor: One,
|
||||
dst_factor: Zero,
|
||||
operation: Add,
|
||||
),
|
||||
color_blend: (
|
||||
src_factor: One,
|
||||
dst_factor: Zero,
|
||||
operation: Add,
|
||||
),
|
||||
write_mask: (
|
||||
bits: 15,
|
||||
CreateRenderPipeline(
|
||||
id: Id(0, 1, Empty),
|
||||
desc: (
|
||||
label: None,
|
||||
layout: Some(Id(0, 1, Empty)),
|
||||
vertex: (
|
||||
stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "vs_main",
|
||||
),
|
||||
buffers: [],
|
||||
),
|
||||
],
|
||||
depth_stencil_state: None,
|
||||
vertex_state: (
|
||||
index_format: Uint16,
|
||||
vertex_buffers: [],
|
||||
fragment: Some((
|
||||
stage: (
|
||||
module: Id(0, 1, Empty),
|
||||
entry_point: "fs_main",
|
||||
),
|
||||
targets: [
|
||||
(
|
||||
format: Rgba8Unorm,
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
sample_count: 1,
|
||||
sample_mask: 4294967295,
|
||||
alpha_to_coverage_enabled: false,
|
||||
)),
|
||||
),
|
||||
Submit(1, [
|
||||
RunRenderPass(
|
||||
base: (
|
||||
|
@ -145,7 +130,7 @@
|
|||
size: (
|
||||
width: 64,
|
||||
height: 64,
|
||||
depth: 1,
|
||||
depth_or_array_layers: 1,
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#version 450
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main() {
|
||||
vec2 pos = vec2(gl_VertexIndex == 2 ? 3.0 : -1.0, gl_VertexIndex == 1 ? 3.0 : -1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
Двоичные данные
gfx/wgpu/player/tests/data/quad.vert.spv
Двоичные данные
gfx/wgpu/player/tests/data/quad.vert.spv
Двоичный файл не отображается.
|
@ -0,0 +1,24 @@
|
|||
[[builtin(vertex_index)]]
|
||||
var<in> in_vertex_index: u32;
|
||||
[[builtin(position)]]
|
||||
var<out> out_pos: vec4<f32>;
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main() {
|
||||
// hacky way to draw a large triangle
|
||||
var tmp1: i32 = i32(in_vertex_index) / 2;
|
||||
var tmp2: i32 = i32(in_vertex_index) & 1;
|
||||
var pos: vec2<f32> = vec2<f32>(
|
||||
f32(tmp1) * 4.0 - 1.0,
|
||||
f32(tmp2) * 4.0 - 1.0
|
||||
);
|
||||
out_pos = vec4<f32>(pos, 0.0, 1.0);
|
||||
}
|
||||
|
||||
[[location(0)]]
|
||||
var<out> out_color: vec4<f32>;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn fs_main() {
|
||||
out_color = vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
|
@ -93,7 +93,6 @@ impl Test<'_> {
|
|||
label: None,
|
||||
features: self.features | wgt::Features::MAPPABLE_PRIMARY_BUFFERS,
|
||||
limits: wgt::Limits::default(),
|
||||
shader_validation: true,
|
||||
},
|
||||
None,
|
||||
device
|
||||
|
@ -144,7 +143,12 @@ impl Test<'_> {
|
|||
}
|
||||
};
|
||||
|
||||
assert_eq!(&expected_data[..], contents);
|
||||
if &expected_data[..] != contents {
|
||||
panic!(
|
||||
"Test expectation is not met!\nBuffer content was:\n{:?}\nbut expected:\n{:?}",
|
||||
contents, expected_data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
wgc::gfx_select!(device => global.clear_backend(()));
|
||||
|
@ -213,5 +217,12 @@ impl Corpus {
|
|||
|
||||
#[test]
|
||||
fn test_api() {
|
||||
wgpu_subscriber::initialize_default_subscriber(
|
||||
std::env::var("WGPU_CHROME_TRACE")
|
||||
.as_ref()
|
||||
.map(Path::new)
|
||||
.ok(),
|
||||
);
|
||||
|
||||
Corpus::run_from(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/all.ron"))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "wgpu-core"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = ["wgpu developers"]
|
||||
edition = "2018"
|
||||
description = "WebGPU core logic on gfx-hal"
|
||||
|
@ -13,10 +13,12 @@ license = "MPL-2.0"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
# Enable SPIRV-Cross
|
||||
cross = ["gfx-backend-metal/cross"]
|
||||
# Enable API tracing
|
||||
trace = ["ron", "serde", "wgt/trace"]
|
||||
trace = ["ron", "serde", "wgt/trace", "arrayvec/serde"]
|
||||
# Enable API replaying
|
||||
replay = ["serde", "wgt/replay"]
|
||||
replay = ["serde", "wgt/replay", "arrayvec/serde"]
|
||||
# Enable serializable compute/render passes, and bundle encoders.
|
||||
serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
|
||||
|
||||
|
@ -25,8 +27,6 @@ arrayvec = "0.5"
|
|||
bitflags = "1.0"
|
||||
copyless = "0.1"
|
||||
fxhash = "0.2"
|
||||
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
parking_lot = "0.11"
|
||||
raw-window-handle = { version = "0.3", optional = true }
|
||||
ron = { version = "0.6", optional = true }
|
||||
|
@ -34,32 +34,38 @@ serde = { version = "1.0", features = ["serde_derive"], optional = true }
|
|||
smallvec = "1"
|
||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||
thiserror = "1"
|
||||
gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "d07be73f9439a37c89f5b72f2500cbf0eb4ff613" }
|
||||
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "831460c4b5120d9a74744d542f39a95b9816b5ab"}
|
||||
|
||||
gpu-alloc = { version = "0.3", features = ["tracing"] }
|
||||
gpu-descriptor = { version = "0.1", features = ["tracing"] }
|
||||
|
||||
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
|
||||
[target.'cfg(all(not(target_arch = "wasm32"), all(unix, not(target_os = "ios"), not(target_os = "macos"))))'.dependencies]
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8", features = ["naga"] }
|
||||
#gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
|
||||
[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
|
||||
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8", optional = true }
|
||||
|
||||
[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies]
|
||||
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8", features = ["naga"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
#gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "0a201d1c406b5119ec11068293a40e50ec0be4c8" }
|
||||
|
||||
[dependencies.naga]
|
||||
version = "0.2"
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "96c80738650822de35f77ab6a589f309460c8f39"
|
||||
tag = "gfx-12"
|
||||
features = ["spv-in", "spv-out", "wgsl-in"]
|
||||
|
||||
[dependencies.wgt]
|
||||
path = "../wgpu-types"
|
||||
package = "wgpu-types"
|
||||
version = "0.6"
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies]
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
#gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
|
||||
version = "0.7"
|
||||
|
||||
[dev-dependencies]
|
||||
loom = "0.3"
|
||||
|
|
|
@ -6,15 +6,15 @@ fn main() {
|
|||
// Setup cfg aliases
|
||||
cfg_aliases::cfg_aliases! {
|
||||
// Vendors/systems
|
||||
ios: { target_os = "ios" },
|
||||
macos: { target_os = "macos" },
|
||||
apple: { any(ios, macos) },
|
||||
wasm: { target_arch = "wasm32" },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
unix_wo_apple: {all(unix, not(apple))},
|
||||
|
||||
// Backends
|
||||
vulkan: { any(windows, all(unix, not(apple)), feature = "gfx-backend-vulkan") },
|
||||
metal: { apple },
|
||||
dx12: { windows },
|
||||
dx11: { windows },
|
||||
gl: { all(not(unix), not(apple), not(windows)) },
|
||||
vulkan: { all(not(wasm), any(windows, unix_wo_apple, feature = "gfx-backend-vulkan")) },
|
||||
metal: { all(not(wasm), apple) },
|
||||
dx12: { all(not(wasm), windows) },
|
||||
dx11: { all(not(wasm), windows) },
|
||||
gl: { false },
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
|||
},
|
||||
hub::Resource,
|
||||
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid},
|
||||
memory_init_tracker::MemoryInitTrackerAction,
|
||||
track::{TrackerSet, DUMMY_SELECTOR},
|
||||
validation::{MissingBufferUsageError, MissingTextureUsageError},
|
||||
FastHashMap, Label, LifeGuard, MultiRefCount, Stored, MAX_BIND_GROUPS,
|
||||
|
@ -58,11 +59,18 @@ pub enum CreateBindGroupError {
|
|||
BindingArrayLengthMismatch { actual: usize, expected: usize },
|
||||
#[error("bound buffer range {range:?} does not fit in buffer of size {size}")]
|
||||
BindingRangeTooLarge {
|
||||
buffer: BufferId,
|
||||
range: Range<wgt::BufferAddress>,
|
||||
size: u64,
|
||||
},
|
||||
#[error("buffer binding size {actual} is less than minimum {min}")]
|
||||
BindingSizeTooSmall { actual: u64, min: u64 },
|
||||
BindingSizeTooSmall {
|
||||
buffer: BufferId,
|
||||
actual: u64,
|
||||
min: u64,
|
||||
},
|
||||
#[error("buffer binding size is zero")]
|
||||
BindingZeroSize(BufferId),
|
||||
#[error("number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
|
||||
BindingsNumMismatch { actual: usize, expected: usize },
|
||||
#[error("binding {0} is used at least twice in the descriptor")]
|
||||
|
@ -81,8 +89,14 @@ pub enum CreateBindGroupError {
|
|||
SwapChainImage,
|
||||
#[error("buffer offset {0} does not respect `BIND_BUFFER_ALIGNMENT`")]
|
||||
UnalignedBufferOffset(wgt::BufferAddress),
|
||||
#[error("uniform buffer binding range exceeds `max_uniform_buffer_binding_size` limit")]
|
||||
UniformBufferRangeTooLarge,
|
||||
#[error(
|
||||
"buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
|
||||
)]
|
||||
BufferRangeTooLarge {
|
||||
binding: u32,
|
||||
given: u32,
|
||||
limit: u32,
|
||||
},
|
||||
#[error("binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
|
||||
WrongBindingType {
|
||||
// Index of the binding
|
||||
|
@ -92,10 +106,36 @@ pub enum CreateBindGroupError {
|
|||
// Human-readable description of expected types
|
||||
expected: &'static str,
|
||||
},
|
||||
#[error("texture binding {binding} expects multisampled = {layout_multisampled}, but given a view with samples = {view_samples}")]
|
||||
InvalidTextureMultisample {
|
||||
binding: u32,
|
||||
layout_multisampled: bool,
|
||||
view_samples: u32,
|
||||
},
|
||||
#[error("texture binding {binding} expects sample type = {layout_sample_type:?}, but given a view with format = {view_format:?}")]
|
||||
InvalidTextureSampleType {
|
||||
binding: u32,
|
||||
layout_sample_type: wgt::TextureSampleType,
|
||||
view_format: wgt::TextureFormat,
|
||||
},
|
||||
#[error("texture binding {binding} expects dimension = {layout_dimension:?}, but given a view with dimension = {view_dimension:?}")]
|
||||
InvalidTextureDimension {
|
||||
binding: u32,
|
||||
layout_dimension: wgt::TextureViewDimension,
|
||||
view_dimension: wgt::TextureViewDimension,
|
||||
},
|
||||
#[error("storage texture binding {binding} expects format = {layout_format:?}, but given a view with format = {view_format:?}")]
|
||||
InvalidStorageTextureFormat {
|
||||
binding: u32,
|
||||
layout_format: wgt::TextureFormat,
|
||||
view_format: wgt::TextureFormat,
|
||||
},
|
||||
#[error("the given sampler is/is not a comparison sampler, while the layout type indicates otherwise")]
|
||||
WrongSamplerComparison,
|
||||
#[error("bound texture views can not have both depth and stencil aspects enabled")]
|
||||
DepthStencilAspect,
|
||||
#[error("the adapter does not support simultaneous read + write storage texture access for the format {0:?}")]
|
||||
StorageReadWriteNotSupported(wgt::TextureFormat),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -107,10 +147,11 @@ pub enum BindingZone {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[error("too many bindings of type {kind:?} in {zone}, limit is {count}")]
|
||||
#[error("too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}")]
|
||||
pub struct BindingTypeMaxCountError {
|
||||
pub kind: BindingTypeMaxCountErrorKind,
|
||||
pub zone: BindingZone,
|
||||
pub limit: u32,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
|
@ -173,7 +214,12 @@ impl PerStageBindingTypeCounter {
|
|||
) -> Result<(), BindingTypeMaxCountError> {
|
||||
let (zone, count) = self.max();
|
||||
if limit < count {
|
||||
Err(BindingTypeMaxCountError { kind, zone, count })
|
||||
Err(BindingTypeMaxCountError {
|
||||
kind,
|
||||
zone,
|
||||
limit,
|
||||
count,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -242,6 +288,7 @@ impl BindingTypeMaxCountValidator {
|
|||
return Err(BindingTypeMaxCountError {
|
||||
kind: BindingTypeMaxCountErrorKind::DynamicUniformBuffers,
|
||||
zone: BindingZone::Pipeline,
|
||||
limit: limits.max_dynamic_uniform_buffers_per_pipeline_layout,
|
||||
count: self.dynamic_uniform_buffers,
|
||||
});
|
||||
}
|
||||
|
@ -249,6 +296,7 @@ impl BindingTypeMaxCountValidator {
|
|||
return Err(BindingTypeMaxCountError {
|
||||
kind: BindingTypeMaxCountErrorKind::DynamicStorageBuffers,
|
||||
zone: BindingZone::Pipeline,
|
||||
limit: limits.max_dynamic_storage_buffers_per_pipeline_layout,
|
||||
count: self.dynamic_storage_buffers,
|
||||
});
|
||||
}
|
||||
|
@ -423,7 +471,7 @@ pub struct PipelineLayoutDescriptor<'a> {
|
|||
/// must define the range in push constant memory that corresponds to its single `layout(push_constant)`
|
||||
/// uniform block.
|
||||
///
|
||||
/// If this array is non-empty, the [`Features::PUSH_CONSTANTS`] must be enabled.
|
||||
/// If this array is non-empty, the [`Features::PUSH_CONSTANTS`](wgt::Features::PUSH_CONSTANTS) must be enabled.
|
||||
pub push_constant_ranges: Cow<'a, [wgt::PushConstantRange]>,
|
||||
}
|
||||
|
||||
|
@ -571,6 +619,7 @@ pub struct BindGroup<B: hal::Backend> {
|
|||
pub(crate) layout_id: Valid<BindGroupLayoutId>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
pub(crate) used: TrackerSet,
|
||||
pub(crate) used_buffer_ranges: Vec<MemoryInitTrackerAction<BufferId>>,
|
||||
pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,13 @@ impl<B: hal::Backend> CommandPool<B> {
|
|||
}
|
||||
self.available.pop().unwrap()
|
||||
}
|
||||
|
||||
fn destroy(mut self, device: &B::Device) {
|
||||
unsafe {
|
||||
self.raw.free(self.available.into_iter());
|
||||
device.destroy_command_pool(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -87,13 +94,11 @@ impl<B: GfxBackend> CommandAllocator<B> {
|
|||
#[cfg(feature = "trace")] enable_tracing: bool,
|
||||
) -> Result<CommandBuffer<B>, CommandAllocatorError> {
|
||||
//debug_assert_eq!(device_id.backend(), B::VARIANT);
|
||||
let _ = label; // silence warning on release
|
||||
let thread_id = thread::current().id();
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
let pool = match inner.pools.entry(thread_id) {
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
Entry::Vacant(e) => {
|
||||
tracing::info!("Starting on thread {:?}", thread_id);
|
||||
let raw = unsafe {
|
||||
|
@ -104,27 +109,30 @@ impl<B: GfxBackend> CommandAllocator<B> {
|
|||
)
|
||||
.or(Err(DeviceError::OutOfMemory))?
|
||||
};
|
||||
let pool = CommandPool {
|
||||
e.insert(CommandPool {
|
||||
raw,
|
||||
total: 0,
|
||||
available: Vec::new(),
|
||||
pending: Vec::new(),
|
||||
};
|
||||
e.insert(pool)
|
||||
})
|
||||
}
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
};
|
||||
|
||||
let init = pool.allocate();
|
||||
//Note: we have to allocate the first buffer right here, or otherwise
|
||||
// the pool may be cleaned up by maintenance called from another thread.
|
||||
|
||||
Ok(CommandBuffer {
|
||||
raw: vec![init],
|
||||
raw: vec![pool.allocate()],
|
||||
is_recording: true,
|
||||
recorded_thread_id: thread_id,
|
||||
device_id,
|
||||
trackers: TrackerSet::new(B::VARIANT),
|
||||
used_swap_chain: None,
|
||||
used_swap_chains: Default::default(),
|
||||
buffer_memory_init_actions: Default::default(),
|
||||
limits,
|
||||
private_features,
|
||||
has_labels: label.is_some(),
|
||||
#[cfg(feature = "trace")]
|
||||
commands: if enable_tracing {
|
||||
Some(Vec::new())
|
||||
|
@ -209,15 +217,26 @@ impl<B: hal::Backend> CommandAllocator<B> {
|
|||
.push((raw, submit_index));
|
||||
}
|
||||
|
||||
pub fn after_submit(&self, cmd_buf: CommandBuffer<B>, submit_index: SubmissionIndex) {
|
||||
pub fn after_submit(
|
||||
&self,
|
||||
cmd_buf: CommandBuffer<B>,
|
||||
device: &B::Device,
|
||||
submit_index: SubmissionIndex,
|
||||
) {
|
||||
// Record this command buffer as pending
|
||||
let mut inner = self.inner.lock();
|
||||
let clear_label = cmd_buf.has_labels;
|
||||
inner
|
||||
.pools
|
||||
.get_mut(&cmd_buf.recorded_thread_id)
|
||||
.unwrap()
|
||||
.pending
|
||||
.extend(cmd_buf.raw.into_iter().map(|raw| (raw, submit_index)));
|
||||
.extend(cmd_buf.raw.into_iter().map(|mut raw| {
|
||||
if clear_label {
|
||||
unsafe { device.set_command_buffer_name(&mut raw, "") };
|
||||
}
|
||||
(raw, submit_index)
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn maintain(&self, device: &B::Device, last_done_index: SubmissionIndex) {
|
||||
|
@ -232,11 +251,8 @@ impl<B: hal::Backend> CommandAllocator<B> {
|
|||
}
|
||||
for thread_id in remove_threads {
|
||||
tracing::info!("Removing from thread {:?}", thread_id);
|
||||
let mut pool = inner.pools.remove(&thread_id).unwrap();
|
||||
unsafe {
|
||||
pool.raw.free(pool.available);
|
||||
device.destroy_command_pool(pool.raw);
|
||||
}
|
||||
let pool = inner.pools.remove(&thread_id).unwrap();
|
||||
pool.destroy(device);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,10 +269,7 @@ impl<B: hal::Backend> CommandAllocator<B> {
|
|||
pool.total
|
||||
);
|
||||
}
|
||||
unsafe {
|
||||
pool.raw.free(pool.available);
|
||||
device.destroy_command_pool(pool.raw);
|
||||
}
|
||||
pool.destroy(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,236 +11,213 @@ use crate::{
|
|||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use std::slice;
|
||||
use wgt::DynamicOffset;
|
||||
|
||||
type BindGroupMask = u8;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct BindGroupPair {
|
||||
layout_id: Valid<BindGroupLayoutId>,
|
||||
group_id: Stored<BindGroupId>,
|
||||
}
|
||||
mod compat {
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum LayoutChange<'a> {
|
||||
Unchanged,
|
||||
Match(Valid<BindGroupId>, &'a [DynamicOffset]),
|
||||
Mismatch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Provision {
|
||||
Unchanged,
|
||||
Changed { was_compatible: bool },
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct FollowUpIter<'a> {
|
||||
iter: slice::Iter<'a, BindGroupEntry>,
|
||||
}
|
||||
impl<'a> Iterator for FollowUpIter<'a> {
|
||||
type Item = (Valid<BindGroupId>, &'a [DynamicOffset]);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter
|
||||
.next()
|
||||
.and_then(|entry| Some((entry.actual_value()?, entry.dynamic_offsets.as_slice())))
|
||||
#[derive(Debug)]
|
||||
struct Entry<T> {
|
||||
assigned: Option<T>,
|
||||
expected: Option<T>,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub(super) struct BindGroupEntry {
|
||||
expected_layout_id: Option<Valid<BindGroupLayoutId>>,
|
||||
provided: Option<BindGroupPair>,
|
||||
dynamic_offsets: Vec<DynamicOffset>,
|
||||
}
|
||||
|
||||
impl BindGroupEntry {
|
||||
fn provide<B: GfxBackend>(
|
||||
&mut self,
|
||||
bind_group_id: Valid<BindGroupId>,
|
||||
bind_group: &BindGroup<B>,
|
||||
offsets: &[DynamicOffset],
|
||||
) -> Provision {
|
||||
debug_assert_eq!(B::VARIANT, bind_group_id.0.backend());
|
||||
|
||||
let was_compatible = match self.provided {
|
||||
Some(BindGroupPair {
|
||||
layout_id,
|
||||
ref group_id,
|
||||
}) => {
|
||||
if group_id.value == bind_group_id && offsets == self.dynamic_offsets.as_slice() {
|
||||
assert_eq!(layout_id, bind_group.layout_id);
|
||||
return Provision::Unchanged;
|
||||
}
|
||||
self.expected_layout_id == Some(layout_id)
|
||||
impl<T> Default for Entry<T> {
|
||||
fn default() -> Self {
|
||||
Entry {
|
||||
assigned: None,
|
||||
expected: None,
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.provided = Some(BindGroupPair {
|
||||
layout_id: bind_group.layout_id,
|
||||
group_id: Stored {
|
||||
value: bind_group_id,
|
||||
ref_count: bind_group.life_guard.add_ref(),
|
||||
},
|
||||
});
|
||||
self.dynamic_offsets.clear();
|
||||
self.dynamic_offsets.extend_from_slice(offsets);
|
||||
|
||||
Provision::Changed { was_compatible }
|
||||
}
|
||||
}
|
||||
impl<T: Copy + PartialEq> Entry<T> {
|
||||
fn is_active(&self) -> bool {
|
||||
self.assigned.is_some() && self.expected.is_some()
|
||||
}
|
||||
|
||||
pub fn expect_layout(
|
||||
&mut self,
|
||||
bind_group_layout_id: Valid<BindGroupLayoutId>,
|
||||
) -> LayoutChange {
|
||||
let some = Some(bind_group_layout_id);
|
||||
if self.expected_layout_id != some {
|
||||
self.expected_layout_id = some;
|
||||
match self.provided {
|
||||
Some(BindGroupPair {
|
||||
layout_id,
|
||||
ref group_id,
|
||||
}) if layout_id == bind_group_layout_id => {
|
||||
LayoutChange::Match(group_id.value, &self.dynamic_offsets)
|
||||
}
|
||||
Some(_) | None => LayoutChange::Mismatch,
|
||||
}
|
||||
} else {
|
||||
LayoutChange::Unchanged
|
||||
fn is_valid(&self) -> bool {
|
||||
self.expected.is_none() || self.expected == self.assigned
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Option<bool> {
|
||||
match (self.expected_layout_id, self.provided.as_ref()) {
|
||||
(None, None) => Some(true),
|
||||
(None, Some(_)) => None,
|
||||
(Some(_), None) => Some(false),
|
||||
(Some(layout), Some(pair)) => Some(layout == pair.layout_id),
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Manager<T> {
|
||||
entries: [Entry<T>; crate::MAX_BIND_GROUPS],
|
||||
}
|
||||
|
||||
fn actual_value(&self) -> Option<Valid<BindGroupId>> {
|
||||
self.expected_layout_id.and_then(|layout_id| {
|
||||
self.provided.as_ref().and_then(|pair| {
|
||||
if pair.layout_id == layout_id {
|
||||
Some(pair.group_id.value)
|
||||
impl<T: Copy + PartialEq> Manager<T> {
|
||||
pub fn new() -> Self {
|
||||
Manager {
|
||||
entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_range(&self, start_index: usize) -> Range<usize> {
|
||||
// find first incompatible entry
|
||||
let end = self
|
||||
.entries
|
||||
.iter()
|
||||
.position(|e| e.expected.is_none() || e.assigned != e.expected)
|
||||
.unwrap_or(self.entries.len());
|
||||
start_index..end.max(start_index)
|
||||
}
|
||||
|
||||
pub fn update_expectations(&mut self, expectations: &[T]) -> Range<usize> {
|
||||
let start_index = self
|
||||
.entries
|
||||
.iter()
|
||||
.zip(expectations)
|
||||
.position(|(e, &expect)| e.expected != Some(expect))
|
||||
.unwrap_or(expectations.len());
|
||||
for (e, &expect) in self.entries[start_index..]
|
||||
.iter_mut()
|
||||
.zip(expectations[start_index..].iter())
|
||||
{
|
||||
e.expected = Some(expect);
|
||||
}
|
||||
for e in self.entries[expectations.len()..].iter_mut() {
|
||||
e.expected = None;
|
||||
}
|
||||
self.make_range(start_index)
|
||||
}
|
||||
|
||||
pub fn assign(&mut self, index: usize, value: T) -> Range<usize> {
|
||||
self.entries[index].assigned = Some(value);
|
||||
self.make_range(index)
|
||||
}
|
||||
|
||||
pub fn list_active(&self) -> impl Iterator<Item = usize> + '_ {
|
||||
self.entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, e)| if e.is_active() { Some(i) } else { None })
|
||||
}
|
||||
|
||||
pub fn invalid_mask(&self) -> super::BindGroupMask {
|
||||
self.entries.iter().enumerate().fold(0, |mask, (i, entry)| {
|
||||
if entry.is_valid() {
|
||||
mask
|
||||
} else {
|
||||
None
|
||||
mask | 1u8 << i
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compatibility() {
|
||||
let mut man = Manager::<i32>::new();
|
||||
man.entries[0] = Entry {
|
||||
expected: Some(3),
|
||||
assigned: Some(2),
|
||||
};
|
||||
man.entries[1] = Entry {
|
||||
expected: Some(1),
|
||||
assigned: Some(1),
|
||||
};
|
||||
man.entries[2] = Entry {
|
||||
expected: Some(4),
|
||||
assigned: Some(5),
|
||||
};
|
||||
// check that we rebind [1] after [0] became compatible
|
||||
assert_eq!(man.assign(0, 3), 0..2);
|
||||
// check that nothing is rebound
|
||||
assert_eq!(man.update_expectations(&[3, 2]), 1..1);
|
||||
// check that [1] and [2] are rebound on expectations change
|
||||
assert_eq!(man.update_expectations(&[3, 1, 5]), 1..3);
|
||||
// reset the first two bindings
|
||||
assert_eq!(man.update_expectations(&[4, 6, 5]), 0..0);
|
||||
// check that nothing is rebound, even if there is a match,
|
||||
// since earlier binding is incompatible.
|
||||
assert_eq!(man.assign(1, 6), 1..1);
|
||||
// finally, bind everything
|
||||
assert_eq!(man.assign(0, 4), 0..3);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct EntryPayload {
|
||||
pub(super) group_id: Option<Stored<BindGroupId>>,
|
||||
pub(super) dynamic_offsets: Vec<wgt::DynamicOffset>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Binder {
|
||||
pub(super) struct Binder {
|
||||
pub(super) pipeline_layout_id: Option<Valid<PipelineLayoutId>>, //TODO: strongly `Stored`
|
||||
pub(super) entries: ArrayVec<[BindGroupEntry; MAX_BIND_GROUPS]>,
|
||||
manager: compat::Manager<Valid<BindGroupLayoutId>>,
|
||||
payloads: [EntryPayload; MAX_BIND_GROUPS],
|
||||
}
|
||||
|
||||
impl Binder {
|
||||
pub(super) fn new(max_bind_groups: u32) -> Self {
|
||||
Self {
|
||||
pub(super) fn new() -> Self {
|
||||
Binder {
|
||||
pipeline_layout_id: None,
|
||||
entries: (0..max_bind_groups)
|
||||
.map(|_| BindGroupEntry::default())
|
||||
.collect(),
|
||||
manager: compat::Manager::new(),
|
||||
payloads: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reset(&mut self) {
|
||||
self.pipeline_layout_id = None;
|
||||
self.entries.clear();
|
||||
}
|
||||
|
||||
pub(super) fn change_pipeline_layout<B: GfxBackend>(
|
||||
&mut self,
|
||||
guard: &Storage<PipelineLayout<B>, PipelineLayoutId>,
|
||||
new_id: Valid<PipelineLayoutId>,
|
||||
) {
|
||||
let old_id_opt = self.pipeline_layout_id.replace(new_id);
|
||||
let new = &guard[new_id];
|
||||
|
||||
let length = if let Some(old_id) = old_id_opt {
|
||||
let old = &guard[old_id];
|
||||
if old.push_constant_ranges == new.push_constant_ranges {
|
||||
new.bind_group_layout_ids.len()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
for entry in self.entries[length..].iter_mut() {
|
||||
entry.expected_layout_id = None;
|
||||
self.manager = compat::Manager::new();
|
||||
for payload in self.payloads.iter_mut() {
|
||||
payload.group_id = None;
|
||||
payload.dynamic_offsets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to set the value of the specified bind group index.
|
||||
/// Returns Some() when the new bind group is ready to be actually bound
|
||||
/// (i.e. compatible with current expectations). Also returns an iterator
|
||||
/// of bind group IDs to be bound with it: those are compatible bind groups
|
||||
/// that were previously blocked because the current one was incompatible.
|
||||
pub(super) fn provide_entry<'a, B: GfxBackend>(
|
||||
pub(super) fn change_pipeline_layout<'a, B: GfxBackend>(
|
||||
&'a mut self,
|
||||
guard: &Storage<PipelineLayout<B>, PipelineLayoutId>,
|
||||
new_id: Valid<PipelineLayoutId>,
|
||||
) -> (usize, &'a [EntryPayload]) {
|
||||
let old_id_opt = self.pipeline_layout_id.replace(new_id);
|
||||
let new = &guard[new_id];
|
||||
|
||||
let mut bind_range = self.manager.update_expectations(&new.bind_group_layout_ids);
|
||||
|
||||
if let Some(old_id) = old_id_opt {
|
||||
let old = &guard[old_id];
|
||||
// root constants are the base compatibility property
|
||||
if old.push_constant_ranges != new.push_constant_ranges {
|
||||
bind_range.start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
(bind_range.start, &self.payloads[bind_range])
|
||||
}
|
||||
|
||||
pub(super) fn assign_group<'a, B: GfxBackend>(
|
||||
&'a mut self,
|
||||
index: usize,
|
||||
bind_group_id: Valid<BindGroupId>,
|
||||
bind_group: &BindGroup<B>,
|
||||
offsets: &[DynamicOffset],
|
||||
) -> Option<(Valid<PipelineLayoutId>, FollowUpIter<'a>)> {
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
) -> &'a [EntryPayload] {
|
||||
tracing::trace!("\tBinding [{}] = group {:?}", index, bind_group_id);
|
||||
debug_assert_eq!(B::VARIANT, bind_group_id.0.backend());
|
||||
|
||||
match self.entries[index].provide(bind_group_id, bind_group, offsets) {
|
||||
Provision::Unchanged => None,
|
||||
Provision::Changed { was_compatible, .. } => {
|
||||
let compatible_count = self.compatible_count();
|
||||
if index < compatible_count {
|
||||
let end = compatible_count.min(if was_compatible {
|
||||
index + 1
|
||||
} else {
|
||||
self.entries.len()
|
||||
});
|
||||
tracing::trace!("\t\tbinding up to {}", end);
|
||||
Some((
|
||||
self.pipeline_layout_id?,
|
||||
FollowUpIter {
|
||||
iter: self.entries[index + 1..end].iter(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
tracing::trace!("\t\tskipping above compatible {}", compatible_count);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
let payload = &mut self.payloads[index];
|
||||
payload.group_id = Some(Stored {
|
||||
value: bind_group_id,
|
||||
ref_count: bind_group.life_guard.add_ref(),
|
||||
});
|
||||
payload.dynamic_offsets.clear();
|
||||
payload.dynamic_offsets.extend_from_slice(offsets);
|
||||
|
||||
let bind_range = self.manager.assign(index, bind_group.layout_id);
|
||||
&self.payloads[bind_range]
|
||||
}
|
||||
|
||||
pub(super) fn list_active(&self) -> impl Iterator<Item = Valid<BindGroupId>> + '_ {
|
||||
self.entries.iter().filter_map(|e| match e.provided {
|
||||
Some(ref pair) if e.expected_layout_id.is_some() => Some(pair.group_id.value),
|
||||
_ => None,
|
||||
})
|
||||
let payloads = &self.payloads;
|
||||
self.manager
|
||||
.list_active()
|
||||
.map(move |index| payloads[index].group_id.as_ref().unwrap().value)
|
||||
}
|
||||
|
||||
pub(super) fn invalid_mask(&self) -> BindGroupMask {
|
||||
self.entries.iter().enumerate().fold(0, |mask, (i, entry)| {
|
||||
if entry.is_valid().unwrap_or(true) {
|
||||
mask
|
||||
} else {
|
||||
mask | 1u8 << i
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn compatible_count(&self) -> usize {
|
||||
self.entries
|
||||
.iter()
|
||||
.position(|entry| !entry.is_valid().unwrap_or(false))
|
||||
.unwrap_or_else(|| self.entries.len())
|
||||
self.manager.invalid_mask()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ use crate::{
|
|||
},
|
||||
hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Resource, Storage, Token},
|
||||
id,
|
||||
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
|
||||
resource::BufferUse,
|
||||
span,
|
||||
track::{TrackerSet, UsageConflict},
|
||||
|
@ -56,7 +57,7 @@ use crate::{
|
|||
Label, LabelHelpers, LifeGuard, Stored, MAX_BIND_GROUPS,
|
||||
};
|
||||
use arrayvec::ArrayVec;
|
||||
use std::{borrow::Cow, iter, ops::Range};
|
||||
use std::{borrow::Cow, iter, mem, ops::Range};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Describes a [`RenderBundleEncoder`].
|
||||
|
@ -93,7 +94,7 @@ impl RenderBundleEncoder {
|
|||
) -> Result<Self, CreateRenderBundleError> {
|
||||
span!(_guard, INFO, "RenderBundleEncoder::new");
|
||||
Ok(Self {
|
||||
base: base.unwrap_or_else(BasePass::new),
|
||||
base: base.unwrap_or_else(|| BasePass::new(&desc.label)),
|
||||
parent_id,
|
||||
context: RenderPassContext {
|
||||
attachments: AttachmentData {
|
||||
|
@ -114,7 +115,7 @@ impl RenderBundleEncoder {
|
|||
|
||||
pub fn dummy(parent_id: id::DeviceId) -> Self {
|
||||
Self {
|
||||
base: BasePass::new(),
|
||||
base: BasePass::new(&None),
|
||||
parent_id,
|
||||
context: RenderPassContext {
|
||||
attachments: AttachmentData {
|
||||
|
@ -127,6 +128,11 @@ impl RenderBundleEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand> {
|
||||
BasePass::from_ref(self.base.as_ref())
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> id::DeviceId {
|
||||
self.parent_id
|
||||
}
|
||||
|
@ -159,6 +165,7 @@ impl RenderBundleEncoder {
|
|||
let mut commands = Vec::new();
|
||||
let mut base = self.base.as_ref();
|
||||
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
|
||||
let mut buffer_memory_init_actions = Vec::new();
|
||||
|
||||
for &command in base.commands {
|
||||
match command {
|
||||
|
@ -204,6 +211,8 @@ impl RenderBundleEncoder {
|
|||
.map_pass_err(scope);
|
||||
}
|
||||
|
||||
buffer_memory_init_actions.extend_from_slice(&bind_group.used_buffer_ranges);
|
||||
|
||||
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets);
|
||||
state
|
||||
.trackers
|
||||
|
@ -233,7 +242,7 @@ impl RenderBundleEncoder {
|
|||
pipeline_layout_id = Some(pipeline.layout_id.value);
|
||||
|
||||
state.set_pipeline(
|
||||
pipeline.index_format,
|
||||
pipeline.strip_index_format,
|
||||
&pipeline.vertex_strides,
|
||||
&layout.bind_group_layout_ids,
|
||||
&layout.push_constant_ranges,
|
||||
|
@ -245,6 +254,7 @@ impl RenderBundleEncoder {
|
|||
}
|
||||
RenderCommand::SetIndexBuffer {
|
||||
buffer_id,
|
||||
index_format,
|
||||
offset,
|
||||
size,
|
||||
} => {
|
||||
|
@ -261,6 +271,12 @@ impl RenderBundleEncoder {
|
|||
Some(s) => offset + s.get(),
|
||||
None => buffer.size,
|
||||
};
|
||||
buffer_memory_init_actions.push(MemoryInitTrackerAction {
|
||||
id: buffer_id,
|
||||
range: offset..end,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
});
|
||||
state.index.set_format(index_format);
|
||||
state.index.set_buffer(buffer_id, offset..end);
|
||||
}
|
||||
RenderCommand::SetVertexBuffer {
|
||||
|
@ -282,6 +298,11 @@ impl RenderBundleEncoder {
|
|||
Some(s) => offset + s.get(),
|
||||
None => buffer.size,
|
||||
};
|
||||
buffer_memory_init_actions.push(MemoryInitTrackerAction {
|
||||
id: buffer_id,
|
||||
range: offset..end,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
});
|
||||
state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
|
||||
}
|
||||
RenderCommand::SetPushConstant {
|
||||
|
@ -310,21 +331,27 @@ impl RenderBundleEncoder {
|
|||
first_vertex,
|
||||
first_instance,
|
||||
} => {
|
||||
let scope = PassErrorScope::Draw;
|
||||
let (vertex_limit, instance_limit) = state.vertex_limits();
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: false,
|
||||
indirect: false,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
let vertex_limits = state.vertex_limits();
|
||||
let last_vertex = first_vertex + vertex_count;
|
||||
if last_vertex > vertex_limit {
|
||||
if last_vertex > vertex_limits.vertex_limit {
|
||||
return Err(DrawError::VertexBeyondLimit {
|
||||
last_vertex,
|
||||
vertex_limit,
|
||||
vertex_limit: vertex_limits.vertex_limit,
|
||||
slot: vertex_limits.vertex_limit_slot,
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
let last_instance = first_instance + instance_count;
|
||||
if last_instance > instance_limit {
|
||||
if last_instance > vertex_limits.instance_limit {
|
||||
return Err(DrawError::InstanceBeyondLimit {
|
||||
last_instance,
|
||||
instance_limit,
|
||||
instance_limit: vertex_limits.instance_limit,
|
||||
slot: vertex_limits.instance_limit_slot,
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
|
@ -339,9 +366,13 @@ impl RenderBundleEncoder {
|
|||
base_vertex: _,
|
||||
first_instance,
|
||||
} => {
|
||||
let scope = PassErrorScope::DrawIndexed;
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: true,
|
||||
indirect: false,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
//TODO: validate that base_vertex + max_index() is within the provided range
|
||||
let (_, instance_limit) = state.vertex_limits();
|
||||
let vertex_limits = state.vertex_limits();
|
||||
let index_limit = state.index.limit();
|
||||
let last_index = first_index + index_count;
|
||||
if last_index > index_limit {
|
||||
|
@ -352,10 +383,11 @@ impl RenderBundleEncoder {
|
|||
.map_pass_err(scope);
|
||||
}
|
||||
let last_instance = first_instance + instance_count;
|
||||
if last_instance > instance_limit {
|
||||
if last_instance > vertex_limits.instance_limit {
|
||||
return Err(DrawError::InstanceBeyondLimit {
|
||||
last_instance,
|
||||
instance_limit,
|
||||
instance_limit: vertex_limits.instance_limit,
|
||||
slot: vertex_limits.instance_limit_slot,
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
|
@ -366,11 +398,15 @@ impl RenderBundleEncoder {
|
|||
}
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
buffer_id,
|
||||
offset: _,
|
||||
offset,
|
||||
count: None,
|
||||
indexed: false,
|
||||
} => {
|
||||
let scope = PassErrorScope::DrawIndirect;
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: false,
|
||||
indirect: true,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
let buffer = state
|
||||
.trackers
|
||||
.buffers
|
||||
|
@ -379,17 +415,34 @@ impl RenderBundleEncoder {
|
|||
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
buffer_memory_init_actions.extend(
|
||||
buffer
|
||||
.initialization_status
|
||||
.check(
|
||||
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
|
||||
)
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: buffer_id,
|
||||
range,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
}),
|
||||
);
|
||||
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds());
|
||||
commands.push(command);
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
buffer_id,
|
||||
offset: _,
|
||||
offset,
|
||||
count: None,
|
||||
indexed: true,
|
||||
} => {
|
||||
let scope = PassErrorScope::DrawIndexedIndirect;
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: true,
|
||||
indirect: true,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
let buffer = state
|
||||
.trackers
|
||||
.buffers
|
||||
|
@ -399,6 +452,19 @@ impl RenderBundleEncoder {
|
|||
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
buffer_memory_init_actions.extend(
|
||||
buffer
|
||||
.initialization_status
|
||||
.check(
|
||||
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
|
||||
)
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: buffer_id,
|
||||
range,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
}),
|
||||
);
|
||||
|
||||
commands.extend(state.index.flush());
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds());
|
||||
|
@ -409,6 +475,9 @@ impl RenderBundleEncoder {
|
|||
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::PopDebugGroup => unimplemented!(),
|
||||
RenderCommand::WriteTimestamp { .. }
|
||||
| RenderCommand::BeginPipelineStatisticsQuery { .. }
|
||||
| RenderCommand::EndPipelineStatisticsQuery => unimplemented!(),
|
||||
RenderCommand::ExecuteBundle(_)
|
||||
| RenderCommand::SetBlendColor(_)
|
||||
| RenderCommand::SetStencilReference(_)
|
||||
|
@ -417,9 +486,9 @@ impl RenderBundleEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = desc.label; //TODO: actually use
|
||||
Ok(RenderBundle {
|
||||
base: BasePass {
|
||||
label: desc.label.as_ref().map(|cow| cow.to_string()),
|
||||
commands,
|
||||
dynamic_offsets: state.flat_dynamic_offsets,
|
||||
string_data: Vec::new(),
|
||||
|
@ -430,10 +499,27 @@ impl RenderBundleEncoder {
|
|||
ref_count: device.life_guard.add_ref(),
|
||||
},
|
||||
used: state.trackers,
|
||||
buffer_memory_init_actions,
|
||||
context: self.context,
|
||||
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_index_buffer(
|
||||
&mut self,
|
||||
buffer_id: id::BufferId,
|
||||
index_format: wgt::IndexFormat,
|
||||
offset: wgt::BufferAddress,
|
||||
size: Option<wgt::BufferSize>,
|
||||
) {
|
||||
span!(_guard, DEBUG, "RenderBundle::set_index_buffer");
|
||||
self.base.commands.push(RenderCommand::SetIndexBuffer {
|
||||
buffer_id,
|
||||
index_format,
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.
|
||||
|
@ -462,6 +548,7 @@ pub struct RenderBundle {
|
|||
base: BasePass<RenderCommand>,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) used: TrackerSet,
|
||||
pub(crate) buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
|
||||
pub(crate) context: RenderPassContext,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
@ -470,11 +557,6 @@ unsafe impl Send for RenderBundle {}
|
|||
unsafe impl Sync for RenderBundle {}
|
||||
|
||||
impl RenderBundle {
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand> {
|
||||
BasePass::from_ref(self.base.as_ref())
|
||||
}
|
||||
|
||||
/// Actually encode the contents into a native command buffer.
|
||||
///
|
||||
/// This is partially duplicating the logic of `command_encoder_run_render_pass`.
|
||||
|
@ -498,8 +580,10 @@ impl RenderBundle {
|
|||
use hal::command::CommandBuffer as _;
|
||||
|
||||
let mut offsets = self.base.dynamic_offsets.as_slice();
|
||||
let mut index_type = hal::IndexType::U16;
|
||||
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
|
||||
if let Some(ref label) = self.base.label {
|
||||
cmd_buf.begin_debug_marker(label, 0);
|
||||
}
|
||||
|
||||
for command in self.base.commands.iter() {
|
||||
match *command {
|
||||
|
@ -513,21 +597,24 @@ impl RenderBundle {
|
|||
&pipeline_layout_guard[pipeline_layout_id.unwrap()].raw,
|
||||
index as usize,
|
||||
iter::once(bind_group.raw.raw()),
|
||||
&offsets[..num_dynamic_offsets as usize],
|
||||
offsets.iter().take(num_dynamic_offsets as usize).cloned(),
|
||||
);
|
||||
offsets = &offsets[num_dynamic_offsets as usize..];
|
||||
}
|
||||
RenderCommand::SetPipeline(pipeline_id) => {
|
||||
let pipeline = pipeline_guard.get(pipeline_id).unwrap();
|
||||
cmd_buf.bind_graphics_pipeline(&pipeline.raw);
|
||||
index_type = conv::map_index_format(pipeline.index_format);
|
||||
|
||||
pipeline_layout_id = Some(pipeline.layout_id.value);
|
||||
}
|
||||
RenderCommand::SetIndexBuffer {
|
||||
buffer_id,
|
||||
index_format,
|
||||
offset,
|
||||
size,
|
||||
} => {
|
||||
let index_type = conv::map_index_format(index_format);
|
||||
|
||||
let &(ref buffer, _) = buffer_guard
|
||||
.get(buffer_id)
|
||||
.unwrap()
|
||||
|
@ -651,6 +738,9 @@ impl RenderBundle {
|
|||
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::PopDebugGroup => unimplemented!(),
|
||||
RenderCommand::WriteTimestamp { .. }
|
||||
| RenderCommand::BeginPipelineStatisticsQuery { .. }
|
||||
| RenderCommand::EndPipelineStatisticsQuery => unimplemented!(),
|
||||
RenderCommand::ExecuteBundle(_)
|
||||
| RenderCommand::SetBlendColor(_)
|
||||
| RenderCommand::SetStencilReference(_)
|
||||
|
@ -659,6 +749,10 @@ impl RenderBundle {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(_) = self.base.label {
|
||||
cmd_buf.end_debug_marker();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -675,6 +769,7 @@ impl Resource for RenderBundle {
|
|||
struct IndexState {
|
||||
buffer: Option<id::BufferId>,
|
||||
format: wgt::IndexFormat,
|
||||
pipeline_format: Option<wgt::IndexFormat>,
|
||||
range: Range<wgt::BufferAddress>,
|
||||
is_dirty: bool,
|
||||
}
|
||||
|
@ -684,6 +779,7 @@ impl IndexState {
|
|||
Self {
|
||||
buffer: None,
|
||||
format: wgt::IndexFormat::default(),
|
||||
pipeline_format: None,
|
||||
range: 0..0,
|
||||
is_dirty: false,
|
||||
}
|
||||
|
@ -703,6 +799,7 @@ impl IndexState {
|
|||
self.is_dirty = false;
|
||||
Some(RenderCommand::SetIndexBuffer {
|
||||
buffer_id: self.buffer.unwrap(),
|
||||
index_format: self.format,
|
||||
offset: self.range.start,
|
||||
size: wgt::BufferSize::new(self.range.end - self.range.start),
|
||||
})
|
||||
|
@ -825,6 +922,18 @@ impl PushConstantState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VertexLimitState {
|
||||
/// Length of the shortest vertex rate vertex buffer
|
||||
vertex_limit: u32,
|
||||
/// Buffer slot which the shortest vertex rate vertex buffer is bound to
|
||||
vertex_limit_slot: u32,
|
||||
/// Length of the shortest instance rate vertex buffer
|
||||
instance_limit: u32,
|
||||
/// Buffer slot which the shortest instance rate vertex buffer is bound to
|
||||
instance_limit_slot: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
trackers: TrackerSet,
|
||||
|
@ -839,20 +948,34 @@ struct State {
|
|||
}
|
||||
|
||||
impl State {
|
||||
fn vertex_limits(&self) -> (u32, u32) {
|
||||
let mut vertex_limit = !0;
|
||||
let mut instance_limit = !0;
|
||||
for vbs in &self.vertex {
|
||||
fn vertex_limits(&self) -> VertexLimitState {
|
||||
let mut vert_state = VertexLimitState {
|
||||
vertex_limit: u32::MAX,
|
||||
vertex_limit_slot: 0,
|
||||
instance_limit: u32::MAX,
|
||||
instance_limit_slot: 0,
|
||||
};
|
||||
for (idx, vbs) in self.vertex.iter().enumerate() {
|
||||
if vbs.stride == 0 {
|
||||
continue;
|
||||
}
|
||||
let limit = ((vbs.range.end - vbs.range.start) / vbs.stride) as u32;
|
||||
match vbs.rate {
|
||||
wgt::InputStepMode::Vertex => vertex_limit = vertex_limit.min(limit),
|
||||
wgt::InputStepMode::Instance => instance_limit = instance_limit.min(limit),
|
||||
wgt::InputStepMode::Vertex => {
|
||||
if limit < vert_state.vertex_limit {
|
||||
vert_state.vertex_limit = limit;
|
||||
vert_state.vertex_limit_slot = idx as _;
|
||||
}
|
||||
}
|
||||
wgt::InputStepMode::Instance => {
|
||||
if limit < vert_state.instance_limit {
|
||||
vert_state.instance_limit = limit;
|
||||
vert_state.instance_limit_slot = idx as _;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(vertex_limit, instance_limit)
|
||||
vert_state
|
||||
}
|
||||
|
||||
fn invalidate_group_from(&mut self, slot: usize) {
|
||||
|
@ -883,12 +1006,13 @@ impl State {
|
|||
|
||||
fn set_pipeline(
|
||||
&mut self,
|
||||
index_format: wgt::IndexFormat,
|
||||
index_format: Option<wgt::IndexFormat>,
|
||||
vertex_strides: &[(wgt::BufferAddress, wgt::InputStepMode)],
|
||||
layout_ids: &[id::Valid<id::BindGroupLayoutId>],
|
||||
push_constant_layouts: &[wgt::PushConstantRange],
|
||||
) {
|
||||
self.index.set_format(index_format);
|
||||
self.index.pipeline_format = index_format;
|
||||
|
||||
for (vs, &(stride, step_mode)) in self.vertex.iter_mut().zip(vertex_strides) {
|
||||
if vs.stride != stride || vs.rate != step_mode {
|
||||
vs.stride = stride;
|
||||
|
@ -1050,10 +1174,12 @@ pub mod bundle_ffi {
|
|||
num_dynamic_offsets: offset_length.try_into().unwrap(),
|
||||
bind_group_id,
|
||||
});
|
||||
bundle
|
||||
.base
|
||||
.dynamic_offsets
|
||||
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
|
||||
if offset_length != 0 {
|
||||
bundle
|
||||
.base
|
||||
.dynamic_offsets
|
||||
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -1068,21 +1194,6 @@ pub mod bundle_ffi {
|
|||
.push(RenderCommand::SetPipeline(pipeline_id));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_render_bundle_set_index_buffer(
|
||||
bundle: &mut RenderBundleEncoder,
|
||||
buffer_id: id::BufferId,
|
||||
offset: BufferAddress,
|
||||
size: Option<BufferSize>,
|
||||
) {
|
||||
span!(_guard, DEBUG, "RenderBundle::set_index_buffer");
|
||||
bundle.base.commands.push(RenderCommand::SetIndexBuffer {
|
||||
buffer_id,
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_render_bundle_set_vertex_buffer(
|
||||
bundle: &mut RenderBundleEncoder,
|
||||
|
|
|
@ -5,25 +5,25 @@
|
|||
use crate::{
|
||||
binding_model::{BindError, BindGroup, PushConstantUploadError},
|
||||
command::{
|
||||
bind::{Binder, LayoutChange},
|
||||
BasePass, BasePassRef, CommandBuffer, CommandEncoderError, MapPassErr, PassErrorScope,
|
||||
StateChange,
|
||||
bind::Binder, end_pipeline_statistics_query, BasePass, BasePassRef, CommandBuffer,
|
||||
CommandEncoderError, MapPassErr, PassErrorScope, QueryUseError, StateChange,
|
||||
},
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
|
||||
id,
|
||||
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
|
||||
resource::{Buffer, BufferUse, Texture},
|
||||
span,
|
||||
track::{TrackerSet, UsageConflict},
|
||||
validation::{check_buffer_usage, MissingBufferUsageError},
|
||||
MAX_BIND_GROUPS,
|
||||
Label,
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use hal::command::CommandBuffer as _;
|
||||
use thiserror::Error;
|
||||
use wgt::{BufferAddress, BufferUsage, ShaderStage};
|
||||
|
||||
use std::{fmt, iter, str};
|
||||
use crate::track::UseExtendError;
|
||||
use std::{fmt, mem, str};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -61,6 +61,15 @@ pub enum ComputeCommand {
|
|||
color: u32,
|
||||
len: usize,
|
||||
},
|
||||
WriteTimestamp {
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
},
|
||||
BeginPipelineStatisticsQuery {
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
},
|
||||
EndPipelineStatisticsQuery,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
|
||||
|
@ -70,9 +79,9 @@ pub struct ComputePass {
|
|||
}
|
||||
|
||||
impl ComputePass {
|
||||
pub fn new(parent_id: id::CommandEncoderId) -> Self {
|
||||
pub fn new(parent_id: id::CommandEncoderId, desc: &ComputePassDescriptor) -> Self {
|
||||
Self {
|
||||
base: BasePass::new(),
|
||||
base: BasePass::new(&desc.label),
|
||||
parent_id,
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +108,9 @@ impl fmt::Debug for ComputePass {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ComputePassDescriptor {
|
||||
pub todo: u32,
|
||||
pub struct ComputePassDescriptor<'a> {
|
||||
pub label: Label<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error, PartialEq)]
|
||||
|
@ -128,8 +136,18 @@ pub enum ComputePassErrorInner {
|
|||
BindGroupIndexOutOfRange { index: u8, max: u32 },
|
||||
#[error("compute pipeline {0:?} is invalid")]
|
||||
InvalidPipeline(id::ComputePipelineId),
|
||||
#[error("QuerySet {0:?} is invalid")]
|
||||
InvalidQuerySet(id::QuerySetId),
|
||||
#[error("indirect buffer {0:?} is invalid or destroyed")]
|
||||
InvalidIndirectBuffer(id::BufferId),
|
||||
#[error("indirect buffer uses bytes {offset}..{end_offset} which overruns indirect buffer of size {buffer_size}")]
|
||||
IndirectBufferOverrun {
|
||||
offset: u64,
|
||||
end_offset: u64,
|
||||
buffer_size: u64,
|
||||
},
|
||||
#[error("buffer {0:?} is invalid or destroyed")]
|
||||
InvalidBuffer(id::BufferId),
|
||||
#[error(transparent)]
|
||||
ResourceUsageConflict(#[from] UsageConflict),
|
||||
#[error(transparent)]
|
||||
|
@ -142,6 +160,8 @@ pub enum ComputePassErrorInner {
|
|||
Bind(#[from] BindError),
|
||||
#[error(transparent)]
|
||||
PushConstants(#[from] PushConstantUploadError),
|
||||
#[error(transparent)]
|
||||
QueryUse(#[from] QueryUseError),
|
||||
}
|
||||
|
||||
/// Error encountered when performing a compute pass.
|
||||
|
@ -231,7 +251,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
pub fn command_encoder_run_compute_pass_impl<B: GfxBackend>(
|
||||
&self,
|
||||
encoder_id: id::CommandEncoderId,
|
||||
mut base: BasePassRef<ComputeCommand>,
|
||||
base: BasePassRef<ComputeCommand>,
|
||||
) -> Result<(), ComputePassError> {
|
||||
span!(_guard, INFO, "CommandEncoder::run_compute_pass");
|
||||
let scope = PassErrorScope::Pass(encoder_id);
|
||||
|
@ -241,7 +261,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf =
|
||||
CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id).map_pass_err(scope)?;
|
||||
CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id).map_pass_err(scope)?;
|
||||
let raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
|
@ -251,20 +271,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
});
|
||||
}
|
||||
|
||||
if let Some(ref label) = base.label {
|
||||
unsafe {
|
||||
raw.begin_debug_marker(label, 0);
|
||||
}
|
||||
}
|
||||
|
||||
let (_, mut token) = hub.render_bundles.read(&mut token);
|
||||
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
|
||||
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
|
||||
let (pipeline_guard, mut token) = hub.compute_pipelines.read(&mut token);
|
||||
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
let (texture_guard, _) = hub.textures.read(&mut token);
|
||||
|
||||
let mut state = State {
|
||||
binder: Binder::new(cmd_buf.limits.max_bind_groups),
|
||||
binder: Binder::new(),
|
||||
pipeline: StateChange::new(),
|
||||
trackers: TrackerSet::new(B::VARIANT),
|
||||
debug_scope_depth: 0,
|
||||
};
|
||||
let mut temp_offsets = Vec::new();
|
||||
let mut dynamic_offset_count = 0;
|
||||
let mut string_offset = 0;
|
||||
let mut active_query = None;
|
||||
|
||||
for command in base.commands {
|
||||
match *command {
|
||||
|
@ -285,9 +315,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
|
||||
temp_offsets.clear();
|
||||
temp_offsets
|
||||
.extend_from_slice(&base.dynamic_offsets[..num_dynamic_offsets as usize]);
|
||||
base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..];
|
||||
temp_offsets.extend_from_slice(
|
||||
&base.dynamic_offsets[dynamic_offset_count
|
||||
..dynamic_offset_count + (num_dynamic_offsets as usize)],
|
||||
);
|
||||
dynamic_offset_count += num_dynamic_offsets as usize;
|
||||
|
||||
let bind_group = cmd_buf
|
||||
.trackers
|
||||
|
@ -299,26 +331,44 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
.validate_dynamic_bindings(&temp_offsets)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
if let Some((pipeline_layout_id, follow_ups)) = state.binder.provide_entry(
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
bind_group.used_buffer_ranges.iter().filter_map(
|
||||
|action| match buffer_guard.get(action.id) {
|
||||
Ok(buffer) => buffer
|
||||
.initialization_status
|
||||
.check(action.range.clone())
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: action.id,
|
||||
range,
|
||||
kind: action.kind,
|
||||
}),
|
||||
Err(_) => None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let pipeline_layout_id = state.binder.pipeline_layout_id;
|
||||
let entries = state.binder.assign_group(
|
||||
index as usize,
|
||||
id::Valid(bind_group_id),
|
||||
bind_group,
|
||||
&temp_offsets,
|
||||
) {
|
||||
let bind_groups = iter::once(bind_group.raw.raw())
|
||||
.chain(
|
||||
follow_ups
|
||||
.clone()
|
||||
.map(|(bg_id, _)| bind_group_guard[bg_id].raw.raw()),
|
||||
)
|
||||
.collect::<ArrayVec<[_; MAX_BIND_GROUPS]>>();
|
||||
temp_offsets.extend(follow_ups.flat_map(|(_, offsets)| offsets));
|
||||
);
|
||||
if !entries.is_empty() {
|
||||
let pipeline_layout =
|
||||
&pipeline_layout_guard[pipeline_layout_id.unwrap()].raw;
|
||||
let desc_sets = entries.iter().map(|e| {
|
||||
bind_group_guard[e.group_id.as_ref().unwrap().value]
|
||||
.raw
|
||||
.raw()
|
||||
});
|
||||
let offsets = entries.iter().flat_map(|e| &e.dynamic_offsets).cloned();
|
||||
unsafe {
|
||||
raw.bind_compute_descriptor_sets(
|
||||
&pipeline_layout_guard[pipeline_layout_id].raw,
|
||||
pipeline_layout,
|
||||
index as usize,
|
||||
bind_groups,
|
||||
&temp_offsets,
|
||||
desc_sets,
|
||||
offsets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -345,36 +395,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
if state.binder.pipeline_layout_id != Some(pipeline.layout_id.value) {
|
||||
let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id.value];
|
||||
|
||||
state.binder.change_pipeline_layout(
|
||||
let (start_index, entries) = state.binder.change_pipeline_layout(
|
||||
&*pipeline_layout_guard,
|
||||
pipeline.layout_id.value,
|
||||
);
|
||||
|
||||
let mut is_compatible = true;
|
||||
|
||||
for (index, (entry, &bgl_id)) in state
|
||||
.binder
|
||||
.entries
|
||||
.iter_mut()
|
||||
.zip(&pipeline_layout.bind_group_layout_ids)
|
||||
.enumerate()
|
||||
{
|
||||
match entry.expect_layout(bgl_id) {
|
||||
LayoutChange::Match(bg_id, offsets) if is_compatible => {
|
||||
let desc_set = bind_group_guard[bg_id].raw.raw();
|
||||
unsafe {
|
||||
raw.bind_compute_descriptor_sets(
|
||||
&pipeline_layout.raw,
|
||||
index,
|
||||
iter::once(desc_set),
|
||||
offsets.iter().cloned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
LayoutChange::Match(..) | LayoutChange::Unchanged => {}
|
||||
LayoutChange::Mismatch => {
|
||||
is_compatible = false;
|
||||
}
|
||||
if !entries.is_empty() {
|
||||
let desc_sets = entries.iter().map(|e| {
|
||||
bind_group_guard[e.group_id.as_ref().unwrap().value]
|
||||
.raw
|
||||
.raw()
|
||||
});
|
||||
let offsets = entries.iter().flat_map(|e| &e.dynamic_offsets).cloned();
|
||||
unsafe {
|
||||
raw.bind_compute_descriptor_sets(
|
||||
&pipeline_layout.raw,
|
||||
start_index,
|
||||
desc_sets,
|
||||
offsets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,7 +471,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
unsafe { raw.push_compute_constants(&pipeline_layout.raw, offset, data_slice) }
|
||||
}
|
||||
ComputeCommand::Dispatch(groups) => {
|
||||
let scope = PassErrorScope::Dispatch;
|
||||
let scope = PassErrorScope::Dispatch {
|
||||
indirect: false,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
|
||||
state.is_ready().map_pass_err(scope)?;
|
||||
state
|
||||
|
@ -450,7 +491,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
}
|
||||
ComputeCommand::DispatchIndirect { buffer_id, offset } => {
|
||||
let scope = PassErrorScope::DispatchIndirect;
|
||||
let scope = PassErrorScope::Dispatch {
|
||||
indirect: true,
|
||||
pipeline: state.pipeline.last_state,
|
||||
};
|
||||
|
||||
state.is_ready().map_pass_err(scope)?;
|
||||
|
||||
|
@ -462,12 +506,36 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(indirect_buffer.usage, BufferUsage::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let end_offset = offset + mem::size_of::<wgt::DispatchIndirectArgs>() as u64;
|
||||
if end_offset > indirect_buffer.size {
|
||||
return Err(ComputePassErrorInner::IndirectBufferOverrun {
|
||||
offset,
|
||||
end_offset,
|
||||
buffer_size: indirect_buffer.size,
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
|
||||
let &(ref buf_raw, _) = indirect_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
.ok_or(ComputePassErrorInner::InvalidIndirectBuffer(buffer_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let stride = 3 * 4; // 3 integers, x/y/z group size
|
||||
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
indirect_buffer
|
||||
.initialization_status
|
||||
.check(offset..(offset + stride))
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: buffer_id,
|
||||
range,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
}),
|
||||
);
|
||||
|
||||
state
|
||||
.flush_states(
|
||||
raw,
|
||||
|
@ -483,12 +551,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
ComputeCommand::PushDebugGroup { color, len } => {
|
||||
state.debug_scope_depth += 1;
|
||||
|
||||
let label = str::from_utf8(&base.string_data[..len]).unwrap();
|
||||
let label =
|
||||
str::from_utf8(&base.string_data[string_offset..string_offset + len])
|
||||
.unwrap();
|
||||
string_offset += len;
|
||||
unsafe {
|
||||
raw.begin_debug_marker(label, color);
|
||||
}
|
||||
base.string_data = &base.string_data[len..];
|
||||
}
|
||||
ComputeCommand::PopDebugGroup => {
|
||||
let scope = PassErrorScope::PopDebugGroup;
|
||||
|
@ -503,10 +572,74 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
}
|
||||
ComputeCommand::InsertDebugMarker { color, len } => {
|
||||
let label = str::from_utf8(&base.string_data[..len]).unwrap();
|
||||
let label =
|
||||
str::from_utf8(&base.string_data[string_offset..string_offset + len])
|
||||
.unwrap();
|
||||
string_offset += len;
|
||||
unsafe { raw.insert_debug_marker(label, color) }
|
||||
base.string_data = &base.string_data[len..];
|
||||
}
|
||||
ComputeCommand::WriteTimestamp {
|
||||
query_set_id,
|
||||
query_index,
|
||||
} => {
|
||||
let scope = PassErrorScope::WriteTimestamp;
|
||||
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
ComputePassErrorInner::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
.validate_and_write_timestamp(raw, query_set_id, query_index, None)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
ComputeCommand::BeginPipelineStatisticsQuery {
|
||||
query_set_id,
|
||||
query_index,
|
||||
} => {
|
||||
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
|
||||
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
ComputePassErrorInner::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
.validate_and_begin_pipeline_statistics_query(
|
||||
raw,
|
||||
query_set_id,
|
||||
query_index,
|
||||
None,
|
||||
&mut active_query,
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
ComputeCommand::EndPipelineStatisticsQuery => {
|
||||
let scope = PassErrorScope::EndPipelineStatisticsQuery;
|
||||
|
||||
end_pipeline_statistics_query(raw, &*query_set_guard, &mut active_query)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_) = base.label {
|
||||
unsafe {
|
||||
raw.end_debug_marker();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,9 +673,11 @@ pub mod compute_ffi {
|
|||
num_dynamic_offsets: offset_length.try_into().unwrap(),
|
||||
bind_group_id,
|
||||
});
|
||||
pass.base
|
||||
.dynamic_offsets
|
||||
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
|
||||
if offset_length != 0 {
|
||||
pass.base
|
||||
.dynamic_offsets
|
||||
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -654,4 +789,49 @@ pub mod compute_ffi {
|
|||
len: bytes.len(),
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_compute_pass_write_timestamp(
|
||||
pass: &mut ComputePass,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
) {
|
||||
span!(_guard, DEBUG, "ComputePass::write_timestamp");
|
||||
|
||||
pass.base.commands.push(ComputeCommand::WriteTimestamp {
|
||||
query_set_id,
|
||||
query_index,
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_compute_pass_begin_pipeline_statistics_query(
|
||||
pass: &mut ComputePass,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
) {
|
||||
span!(
|
||||
_guard,
|
||||
DEBUG,
|
||||
"ComputePass::begin_pipeline_statistics query"
|
||||
);
|
||||
|
||||
pass.base
|
||||
.commands
|
||||
.push(ComputeCommand::BeginPipelineStatisticsQuery {
|
||||
query_set_id,
|
||||
query_index,
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_compute_pass_end_pipeline_statistics_query(
|
||||
pass: &mut ComputePass,
|
||||
) {
|
||||
span!(_guard, DEBUG, "ComputePass::end_pipeline_statistics_query");
|
||||
|
||||
pass.base
|
||||
.commands
|
||||
.push(ComputeCommand::EndPipelineStatisticsQuery);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,21 +26,37 @@ pub enum DrawError {
|
|||
MissingBlendColor,
|
||||
#[error("render pipeline must be set")]
|
||||
MissingPipeline,
|
||||
#[error("vertex buffer {index} must be set")]
|
||||
MissingVertexBuffer { index: u32 },
|
||||
#[error("index buffer must be set")]
|
||||
MissingIndexBuffer,
|
||||
#[error("current render pipeline has a layout which is incompatible with a currently set bind group, first differing at entry index {index}")]
|
||||
IncompatibleBindGroup {
|
||||
index: u32,
|
||||
//expected: BindGroupLayoutId,
|
||||
//provided: Option<(BindGroupLayoutId, BindGroupId)>,
|
||||
},
|
||||
#[error("vertex {last_vertex} extends beyond limit {vertex_limit}")]
|
||||
VertexBeyondLimit { last_vertex: u32, vertex_limit: u32 },
|
||||
#[error("instance {last_instance} extends beyond limit {instance_limit}")]
|
||||
#[error("vertex {last_vertex} extends beyond limit {vertex_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Vertex` step-rate vertex buffer?")]
|
||||
VertexBeyondLimit {
|
||||
last_vertex: u32,
|
||||
vertex_limit: u32,
|
||||
slot: u32,
|
||||
},
|
||||
#[error("instance {last_instance} extends beyond limit {instance_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Instance` step-rate vertex buffer?")]
|
||||
InstanceBeyondLimit {
|
||||
last_instance: u32,
|
||||
instance_limit: u32,
|
||||
slot: u32,
|
||||
},
|
||||
#[error("index {last_index} extends beyond limit {index_limit}")]
|
||||
#[error("index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")]
|
||||
IndexBeyondLimit { last_index: u32, index_limit: u32 },
|
||||
#[error(
|
||||
"pipeline index format ({pipeline:?}) and buffer index format ({buffer:?}) do not match"
|
||||
)]
|
||||
UnmatchedIndexFormats {
|
||||
pipeline: wgt::IndexFormat,
|
||||
buffer: wgt::IndexFormat,
|
||||
},
|
||||
}
|
||||
|
||||
/// Error encountered when encoding a render command.
|
||||
|
@ -49,6 +65,8 @@ pub enum DrawError {
|
|||
pub enum RenderCommandError {
|
||||
#[error("bind group {0:?} is invalid")]
|
||||
InvalidBindGroup(id::BindGroupId),
|
||||
#[error("render bundle {0:?} is invalid")]
|
||||
InvalidRenderBundle(id::RenderBundleId),
|
||||
#[error("bind group index {index} is greater than the device's requested `max_bind_group` limit {max}")]
|
||||
BindGroupIndexOutOfRange { index: u8, max: u32 },
|
||||
#[error("dynamic buffer offset {0} does not respect `BIND_BUFFER_ALIGNMENT`")]
|
||||
|
@ -57,6 +75,8 @@ pub enum RenderCommandError {
|
|||
InvalidDynamicOffsetCount { actual: usize, expected: usize },
|
||||
#[error("render pipeline {0:?} is invalid")]
|
||||
InvalidPipeline(id::RenderPipelineId),
|
||||
#[error("QuerySet {0:?} is invalid")]
|
||||
InvalidQuerySet(id::QuerySetId),
|
||||
#[error("Render pipeline is incompatible with render pass")]
|
||||
IncompatiblePipeline(#[from] crate::device::RenderPassCompatibilityError),
|
||||
#[error("pipeline is not compatible with the depth-stencil read-only render pass")]
|
||||
|
@ -112,6 +132,7 @@ pub enum RenderCommand {
|
|||
SetPipeline(id::RenderPipelineId),
|
||||
SetIndexBuffer {
|
||||
buffer_id: id::BufferId,
|
||||
index_format: wgt::IndexFormat,
|
||||
offset: BufferAddress,
|
||||
size: Option<BufferSize>,
|
||||
},
|
||||
|
@ -176,5 +197,14 @@ pub enum RenderCommand {
|
|||
color: u32,
|
||||
len: usize,
|
||||
},
|
||||
WriteTimestamp {
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
},
|
||||
BeginPipelineStatisticsQuery {
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
},
|
||||
EndPipelineStatisticsQuery,
|
||||
ExecuteBundle(id::RenderBundleId),
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ mod bind;
|
|||
mod bundle;
|
||||
mod compute;
|
||||
mod draw;
|
||||
mod query;
|
||||
mod render;
|
||||
mod transfer;
|
||||
|
||||
|
@ -15,6 +16,7 @@ pub use self::allocator::CommandAllocatorError;
|
|||
pub use self::bundle::*;
|
||||
pub use self::compute::*;
|
||||
pub use self::draw::*;
|
||||
pub use self::query::*;
|
||||
pub use self::render::*;
|
||||
pub use self::transfer::*;
|
||||
|
||||
|
@ -22,6 +24,7 @@ use crate::{
|
|||
device::{all_buffer_stages, all_image_stages},
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
|
||||
id,
|
||||
memory_init_tracker::MemoryInitTrackerAction,
|
||||
resource::{Buffer, Texture},
|
||||
span,
|
||||
track::TrackerSet,
|
||||
|
@ -29,6 +32,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use hal::command::CommandBuffer as _;
|
||||
use smallvec::SmallVec;
|
||||
use thiserror::Error;
|
||||
|
||||
use std::thread::ThreadId;
|
||||
|
@ -42,9 +46,11 @@ pub struct CommandBuffer<B: hal::Backend> {
|
|||
recorded_thread_id: ThreadId,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) trackers: TrackerSet,
|
||||
pub(crate) used_swap_chain: Option<(Stored<id::SwapChainId>, B::Framebuffer)>,
|
||||
pub(crate) used_swap_chains: SmallVec<[Stored<id::SwapChainId>; 1]>,
|
||||
pub(crate) buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
|
||||
limits: wgt::Limits,
|
||||
private_features: PrivateFeatures,
|
||||
has_labels: bool,
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) commands: Option<Vec<crate::device::trace::Command>>,
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -52,7 +58,7 @@ pub struct CommandBuffer<B: hal::Backend> {
|
|||
}
|
||||
|
||||
impl<B: GfxBackend> CommandBuffer<B> {
|
||||
fn get_encoder(
|
||||
fn get_encoder_mut(
|
||||
storage: &mut Storage<Self, id::CommandEncoderId>,
|
||||
id: id::CommandEncoderId,
|
||||
) -> Result<&mut Self, CommandEncoderError> {
|
||||
|
@ -120,6 +126,7 @@ impl<B: hal::Backend> crate::hub::Resource for CommandBuffer<B> {
|
|||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BasePassRef<'a, C> {
|
||||
pub label: Option<&'a str>,
|
||||
pub commands: &'a [C],
|
||||
pub dynamic_offsets: &'a [wgt::DynamicOffset],
|
||||
pub string_data: &'a [u8],
|
||||
|
@ -137,6 +144,7 @@ pub struct BasePassRef<'a, C> {
|
|||
derive(serde::Deserialize)
|
||||
)]
|
||||
pub struct BasePass<C> {
|
||||
pub label: Option<String>,
|
||||
pub commands: Vec<C>,
|
||||
pub dynamic_offsets: Vec<wgt::DynamicOffset>,
|
||||
pub string_data: Vec<u8>,
|
||||
|
@ -144,8 +152,9 @@ pub struct BasePass<C> {
|
|||
}
|
||||
|
||||
impl<C: Clone> BasePass<C> {
|
||||
fn new() -> Self {
|
||||
fn new(label: &Label) -> Self {
|
||||
Self {
|
||||
label: label.as_ref().map(|cow| cow.to_string()),
|
||||
commands: Vec::new(),
|
||||
dynamic_offsets: Vec::new(),
|
||||
string_data: Vec::new(),
|
||||
|
@ -156,6 +165,7 @@ impl<C: Clone> BasePass<C> {
|
|||
#[cfg(feature = "trace")]
|
||||
fn from_ref(base: BasePassRef<C>) -> Self {
|
||||
Self {
|
||||
label: base.label.map(str::to_string),
|
||||
commands: base.commands.to_vec(),
|
||||
dynamic_offsets: base.dynamic_offsets.to_vec(),
|
||||
string_data: base.string_data.to_vec(),
|
||||
|
@ -165,6 +175,7 @@ impl<C: Clone> BasePass<C> {
|
|||
|
||||
pub fn as_ref(&self) -> BasePassRef<C> {
|
||||
BasePassRef {
|
||||
label: self.label.as_ref().map(String::as_str),
|
||||
commands: &self.commands,
|
||||
dynamic_offsets: &self.dynamic_offsets,
|
||||
string_data: &self.string_data,
|
||||
|
@ -195,11 +206,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
//TODO: actually close the last recorded command buffer
|
||||
let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
|
||||
|
||||
let error = match CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id) {
|
||||
let error = match CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id) {
|
||||
Ok(cmd_buf) => {
|
||||
cmd_buf.is_recording = false;
|
||||
// stop tracking the swapchain image, if used
|
||||
if let Some((ref sc_id, _)) = cmd_buf.used_swap_chain {
|
||||
for sc_id in cmd_buf.used_swap_chains.iter() {
|
||||
let view_id = swap_chain_guard[sc_id.value]
|
||||
.acquired_view_id
|
||||
.as_ref()
|
||||
|
@ -226,11 +237,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
cmb_raw.begin_debug_marker(label, 0);
|
||||
cmd_buf_raw.begin_debug_marker(label, 0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -246,11 +257,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
cmb_raw.insert_debug_marker(label, 0);
|
||||
cmd_buf_raw.insert_debug_marker(label, 0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -265,11 +276,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
cmb_raw.end_debug_marker();
|
||||
cmd_buf_raw.end_debug_marker();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -343,20 +354,27 @@ pub enum PassErrorScope {
|
|||
SetViewport,
|
||||
#[error("In a set_scissor_rect command")]
|
||||
SetScissorRect,
|
||||
#[error("In a draw command")]
|
||||
Draw,
|
||||
#[error("In a draw_indexed command")]
|
||||
DrawIndexed,
|
||||
#[error("In a draw_indirect command")]
|
||||
DrawIndirect,
|
||||
#[error("In a draw_indexed_indirect command")]
|
||||
DrawIndexedIndirect,
|
||||
#[error("In a draw command, indexed:{indexed} indirect:{indirect}")]
|
||||
Draw {
|
||||
indexed: bool,
|
||||
indirect: bool,
|
||||
pipeline: Option<id::RenderPipelineId>,
|
||||
},
|
||||
#[error("While resetting queries after the renderpass was ran")]
|
||||
QueryReset,
|
||||
#[error("In a write_timestamp command")]
|
||||
WriteTimestamp,
|
||||
#[error("In a begin_pipeline_statistics_query command")]
|
||||
BeginPipelineStatisticsQuery,
|
||||
#[error("In a end_pipeline_statistics_query command")]
|
||||
EndPipelineStatisticsQuery,
|
||||
#[error("In a execute_bundle command")]
|
||||
ExecuteBundle,
|
||||
#[error("In a dispatch command")]
|
||||
Dispatch,
|
||||
#[error("In a dispatch_indirect command")]
|
||||
DispatchIndirect,
|
||||
#[error("In a dispatch command, indirect:{indirect}")]
|
||||
Dispatch {
|
||||
indirect: bool,
|
||||
pipeline: Option<id::ComputePipelineId>,
|
||||
},
|
||||
#[error("In a pop_debug_group command")]
|
||||
PopDebugGroup,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,424 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use hal::command::CommandBuffer as _;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
use crate::device::trace::Command as TraceCommand;
|
||||
use crate::{
|
||||
command::{CommandBuffer, CommandEncoderError},
|
||||
device::all_buffer_stages,
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
|
||||
id::{self, Id, TypedId},
|
||||
resource::{BufferUse, QuerySet},
|
||||
track::UseExtendError,
|
||||
Epoch, FastHashMap, Index,
|
||||
};
|
||||
use std::{iter, marker::PhantomData};
|
||||
use thiserror::Error;
|
||||
use wgt::BufferAddress;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct QueryResetMap<B: hal::Backend> {
|
||||
map: FastHashMap<Index, (Vec<bool>, Epoch)>,
|
||||
_phantom: PhantomData<B>,
|
||||
}
|
||||
impl<B: hal::Backend> QueryResetMap<B> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: FastHashMap::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_query_set(
|
||||
&mut self,
|
||||
id: id::QuerySetId,
|
||||
query_set: &QuerySet<B>,
|
||||
query: u32,
|
||||
) -> bool {
|
||||
let (index, epoch, _) = id.unzip();
|
||||
let (vec, _) = self
|
||||
.map
|
||||
.entry(index)
|
||||
.or_insert_with(|| (vec![false; query_set.desc.count as usize], epoch));
|
||||
|
||||
std::mem::replace(&mut vec[query as usize], true)
|
||||
}
|
||||
|
||||
pub fn reset_queries(
|
||||
self,
|
||||
cmd_buf_raw: &mut B::CommandBuffer,
|
||||
query_set_storage: &Storage<QuerySet<B>, id::QuerySetId>,
|
||||
backend: wgt::Backend,
|
||||
) -> Result<(), id::QuerySetId> {
|
||||
for (query_set_id, (state, epoch)) in self.map.into_iter() {
|
||||
let id = Id::zip(query_set_id, epoch, backend);
|
||||
let query_set = query_set_storage.get(id).map_err(|_| id)?;
|
||||
|
||||
debug_assert_eq!(state.len(), query_set.desc.count as usize);
|
||||
|
||||
// Need to find all "runs" of values which need resets. If the state vector is:
|
||||
// [false, true, true, false, true], we want to reset [1..3, 4..5]. This minimizes
|
||||
// the amount of resets needed.
|
||||
let mut state_iter = state.into_iter().chain(iter::once(false)).enumerate();
|
||||
let mut run_start: Option<u32> = None;
|
||||
while let Some((idx, value)) = state_iter.next() {
|
||||
match (run_start, value) {
|
||||
// We're inside of a run, do nothing
|
||||
(Some(..), true) => {}
|
||||
// We've hit the end of a run, dispatch a reset
|
||||
(Some(start), false) => {
|
||||
run_start = None;
|
||||
unsafe { cmd_buf_raw.reset_query_pool(&query_set.raw, start..idx as u32) };
|
||||
}
|
||||
// We're starting a run
|
||||
(None, true) => {
|
||||
run_start = Some(idx as u32);
|
||||
}
|
||||
// We're in a run of falses, do nothing.
|
||||
(None, false) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SimplifiedQueryType {
|
||||
Timestamp,
|
||||
PipelineStatistics,
|
||||
}
|
||||
impl From<wgt::QueryType> for SimplifiedQueryType {
|
||||
fn from(q: wgt::QueryType) -> Self {
|
||||
match q {
|
||||
wgt::QueryType::Timestamp => SimplifiedQueryType::Timestamp,
|
||||
wgt::QueryType::PipelineStatistics(..) => SimplifiedQueryType::PipelineStatistics,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error encountered when dealing with queries
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum QueryError {
|
||||
#[error(transparent)]
|
||||
Encoder(#[from] CommandEncoderError),
|
||||
#[error("Error encountered while trying to use queries")]
|
||||
Use(#[from] QueryUseError),
|
||||
#[error("Error encountered while trying to resolve a query")]
|
||||
Resolve(#[from] ResolveError),
|
||||
#[error("Buffer {0:?} is invalid or destroyed")]
|
||||
InvalidBuffer(id::BufferId),
|
||||
#[error("QuerySet {0:?} is invalid or destroyed")]
|
||||
InvalidQuerySet(id::QuerySetId),
|
||||
}
|
||||
|
||||
/// Error encountered while trying to use queries
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum QueryUseError {
|
||||
#[error("Query {query_index} is out of bounds for a query set of size {query_set_size}")]
|
||||
OutOfBounds {
|
||||
query_index: u32,
|
||||
query_set_size: u32,
|
||||
},
|
||||
#[error("Query {query_index} has already been used within the same renderpass. Queries must only be used once per renderpass")]
|
||||
UsedTwiceInsideRenderpass { query_index: u32 },
|
||||
#[error("Query {new_query_index} was started while query {active_query_index} was already active. No more than one statistic or occlusion query may be active at once")]
|
||||
AlreadyStarted {
|
||||
active_query_index: u32,
|
||||
new_query_index: u32,
|
||||
},
|
||||
#[error("Query was stopped while there was no active query")]
|
||||
AlreadyStopped,
|
||||
#[error("A query of type {query_type:?} was started using a query set of type {set_type:?}")]
|
||||
IncompatibleType {
|
||||
set_type: SimplifiedQueryType,
|
||||
query_type: SimplifiedQueryType,
|
||||
},
|
||||
}
|
||||
|
||||
/// Error encountered while trying to resolve a query.
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum ResolveError {
|
||||
#[error("Queries can only be resolved to buffers that contain the COPY_DST usage")]
|
||||
MissingBufferUsage,
|
||||
#[error("Resolving queries {start_query}..{end_query} would overrun the query set of size {query_set_size}")]
|
||||
QueryOverrun {
|
||||
start_query: u32,
|
||||
end_query: u32,
|
||||
query_set_size: u32,
|
||||
},
|
||||
#[error("Resolving queries {start_query}..{end_query} ({stride} byte queries) will end up overruning the bounds of the destination buffer of size {buffer_size} using offsets {buffer_start_offset}..{buffer_end_offset}")]
|
||||
BufferOverrun {
|
||||
start_query: u32,
|
||||
end_query: u32,
|
||||
stride: u32,
|
||||
buffer_size: BufferAddress,
|
||||
buffer_start_offset: BufferAddress,
|
||||
buffer_end_offset: BufferAddress,
|
||||
},
|
||||
}
|
||||
|
||||
impl<B: GfxBackend> QuerySet<B> {
|
||||
fn validate_query(
|
||||
&self,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_type: SimplifiedQueryType,
|
||||
query_index: u32,
|
||||
reset_state: Option<&mut QueryResetMap<B>>,
|
||||
) -> Result<hal::query::Query<'_, B>, QueryUseError> {
|
||||
// We need to defer our resets because we are in a renderpass, add the usage to the reset map.
|
||||
if let Some(reset) = reset_state {
|
||||
let used = reset.use_query_set(query_set_id, self, query_index);
|
||||
if used {
|
||||
return Err(QueryUseError::UsedTwiceInsideRenderpass { query_index }.into());
|
||||
}
|
||||
}
|
||||
|
||||
let simple_set_type = SimplifiedQueryType::from(self.desc.ty);
|
||||
if simple_set_type != query_type {
|
||||
return Err(QueryUseError::IncompatibleType {
|
||||
query_type,
|
||||
set_type: simple_set_type,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
if query_index >= self.desc.count {
|
||||
return Err(QueryUseError::OutOfBounds {
|
||||
query_index,
|
||||
query_set_size: self.desc.count,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let hal_query = hal::query::Query::<B> {
|
||||
pool: &self.raw,
|
||||
id: query_index,
|
||||
};
|
||||
|
||||
Ok(hal_query)
|
||||
}
|
||||
|
||||
pub(super) fn validate_and_write_timestamp(
|
||||
&self,
|
||||
cmd_buf_raw: &mut B::CommandBuffer,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
reset_state: Option<&mut QueryResetMap<B>>,
|
||||
) -> Result<(), QueryUseError> {
|
||||
let needs_reset = reset_state.is_none();
|
||||
let hal_query = self.validate_query(
|
||||
query_set_id,
|
||||
SimplifiedQueryType::Timestamp,
|
||||
query_index,
|
||||
reset_state,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
// If we don't have a reset state tracker which can defer resets, we must reset now.
|
||||
if needs_reset {
|
||||
cmd_buf_raw.reset_query_pool(&self.raw, query_index..(query_index + 1));
|
||||
}
|
||||
cmd_buf_raw.write_timestamp(hal::pso::PipelineStage::BOTTOM_OF_PIPE, hal_query);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn validate_and_begin_pipeline_statistics_query(
|
||||
&self,
|
||||
cmd_buf_raw: &mut B::CommandBuffer,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
reset_state: Option<&mut QueryResetMap<B>>,
|
||||
active_query: &mut Option<(id::QuerySetId, u32)>,
|
||||
) -> Result<(), QueryUseError> {
|
||||
let needs_reset = reset_state.is_none();
|
||||
let hal_query = self.validate_query(
|
||||
query_set_id,
|
||||
SimplifiedQueryType::PipelineStatistics,
|
||||
query_index,
|
||||
reset_state,
|
||||
)?;
|
||||
|
||||
if let Some((_old_id, old_idx)) = active_query.replace((query_set_id, query_index)) {
|
||||
return Err(QueryUseError::AlreadyStarted {
|
||||
active_query_index: old_idx,
|
||||
new_query_index: query_index,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// If we don't have a reset state tracker which can defer resets, we must reset now.
|
||||
if needs_reset {
|
||||
cmd_buf_raw.reset_query_pool(&self.raw, query_index..(query_index + 1));
|
||||
}
|
||||
cmd_buf_raw.begin_query(hal_query, hal::query::ControlFlags::empty());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn end_pipeline_statistics_query<B: GfxBackend>(
|
||||
cmd_buf_raw: &mut B::CommandBuffer,
|
||||
storage: &Storage<QuerySet<B>, id::QuerySetId>,
|
||||
active_query: &mut Option<(id::QuerySetId, u32)>,
|
||||
) -> Result<(), QueryUseError> {
|
||||
if let Some((query_set_id, query_index)) = active_query.take() {
|
||||
// We can unwrap here as the validity was validated when the active query was set
|
||||
let query_set = storage.get(query_set_id).unwrap();
|
||||
|
||||
let hal_query = hal::query::Query::<B> {
|
||||
pool: &query_set.raw,
|
||||
id: query_index,
|
||||
};
|
||||
|
||||
unsafe { cmd_buf_raw.end_query(hal_query) }
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(QueryUseError::AlreadyStopped)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
pub fn command_encoder_write_timestamp<B: GfxBackend>(
|
||||
&self,
|
||||
command_encoder_id: id::CommandEncoderId,
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
) -> Result<(), QueryError> {
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let (query_set_guard, _) = hub.query_sets.read(&mut token);
|
||||
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref mut list) = cmd_buf.commands {
|
||||
list.push(TraceCommand::WriteTimestamp {
|
||||
query_set_id,
|
||||
query_index,
|
||||
});
|
||||
}
|
||||
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
|
||||
query_set.validate_and_write_timestamp(cmd_buf_raw, query_set_id, query_index, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_encoder_resolve_query_set<B: GfxBackend>(
|
||||
&self,
|
||||
command_encoder_id: id::CommandEncoderId,
|
||||
query_set_id: id::QuerySetId,
|
||||
start_query: u32,
|
||||
query_count: u32,
|
||||
destination: id::BufferId,
|
||||
destination_offset: BufferAddress,
|
||||
) -> Result<(), QueryError> {
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
|
||||
let (buffer_guard, _) = hub.buffers.read(&mut token);
|
||||
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref mut list) = cmd_buf.commands {
|
||||
list.push(TraceCommand::ResolveQuerySet {
|
||||
query_set_id,
|
||||
start_query,
|
||||
query_count,
|
||||
destination,
|
||||
destination_offset,
|
||||
});
|
||||
}
|
||||
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, destination, (), BufferUse::COPY_DST)
|
||||
.map_err(QueryError::InvalidBuffer)?;
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
|
||||
if !dst_buffer.usage.contains(wgt::BufferUsage::COPY_DST) {
|
||||
return Err(ResolveError::MissingBufferUsage.into());
|
||||
}
|
||||
|
||||
let end_query = start_query + query_count;
|
||||
if end_query > query_set.desc.count {
|
||||
return Err(ResolveError::QueryOverrun {
|
||||
start_query,
|
||||
end_query,
|
||||
query_set_size: query_set.desc.count,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let stride = query_set.elements * wgt::QUERY_SIZE;
|
||||
let bytes_used = (stride * query_count) as BufferAddress;
|
||||
|
||||
let buffer_start_offset = destination_offset;
|
||||
let buffer_end_offset = buffer_start_offset + bytes_used;
|
||||
|
||||
if buffer_end_offset > dst_buffer.size {
|
||||
return Err(ResolveError::BufferOverrun {
|
||||
start_query,
|
||||
end_query,
|
||||
stride,
|
||||
buffer_size: dst_buffer.size,
|
||||
buffer_start_offset,
|
||||
buffer_end_offset,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cmd_buf_raw.pipeline_barrier(
|
||||
all_buffer_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
dst_barrier,
|
||||
);
|
||||
cmd_buf_raw.copy_query_pool_results(
|
||||
&query_set.raw,
|
||||
start_query..end_query,
|
||||
&dst_buffer.raw.as_ref().unwrap().0,
|
||||
destination_offset,
|
||||
stride,
|
||||
hal::query::ResultFlags::WAIT | hal::query::ResultFlags::BITS_64,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -10,6 +10,7 @@ use crate::{
|
|||
device::{all_buffer_stages, all_image_stages},
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
|
||||
id::{BufferId, CommandEncoderId, TextureId},
|
||||
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
|
||||
resource::{BufferUse, Texture, TextureErrorDimension, TextureUse},
|
||||
span,
|
||||
track::TextureSelector,
|
||||
|
@ -121,7 +122,7 @@ pub(crate) fn texture_copy_view_to_hal<B: hal::Backend>(
|
|||
let (layer, layer_count, z) = match texture.dimension {
|
||||
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => (
|
||||
view.origin.z as hal::image::Layer,
|
||||
size.depth as hal::image::Layer,
|
||||
size.depth_or_array_layers as hal::image::Layer,
|
||||
0,
|
||||
),
|
||||
wgt::TextureDimension::D3 => (0, 1, view.origin.z as i32),
|
||||
|
@ -149,6 +150,7 @@ pub(crate) fn texture_copy_view_to_hal<B: hal::Backend>(
|
|||
}
|
||||
|
||||
/// Function copied with minor modifications from webgpu standard https://gpuweb.github.io/gpuweb/#valid-texture-copy-range
|
||||
/// If successful, returns number of buffer bytes required for this copy.
|
||||
pub(crate) fn validate_linear_texture_data(
|
||||
layout: &wgt::TextureDataLayout,
|
||||
format: wgt::TextureFormat,
|
||||
|
@ -156,17 +158,17 @@ pub(crate) fn validate_linear_texture_data(
|
|||
buffer_side: CopySide,
|
||||
bytes_per_block: BufferAddress,
|
||||
copy_size: &Extent3d,
|
||||
) -> Result<(), TransferError> {
|
||||
) -> Result<BufferAddress, TransferError> {
|
||||
// Convert all inputs to BufferAddress (u64) to prevent overflow issues
|
||||
let copy_width = copy_size.width as BufferAddress;
|
||||
let copy_height = copy_size.height as BufferAddress;
|
||||
let copy_depth = copy_size.depth as BufferAddress;
|
||||
let copy_depth = copy_size.depth_or_array_layers as BufferAddress;
|
||||
|
||||
let offset = layout.offset;
|
||||
let rows_per_image = layout.rows_per_image as BufferAddress;
|
||||
let bytes_per_row = layout.bytes_per_row as BufferAddress;
|
||||
|
||||
let (block_width, block_height) = conv::texture_block_size(format);
|
||||
let (block_width, block_height) = format.describe().block_dimensions;
|
||||
let block_width = block_width as BufferAddress;
|
||||
let block_height = block_height as BufferAddress;
|
||||
let block_size = bytes_per_block;
|
||||
|
@ -217,10 +219,10 @@ pub(crate) fn validate_linear_texture_data(
|
|||
if copy_depth > 1 && rows_per_image == 0 {
|
||||
return Err(TransferError::InvalidRowsPerImage);
|
||||
}
|
||||
Ok(())
|
||||
Ok(required_bytes_in_copy)
|
||||
}
|
||||
|
||||
/// Function copied with minor modifications from webgpu standard https://gpuweb.github.io/gpuweb/#valid-texture-copy-range
|
||||
/// Function copied with minor modifications from webgpu standard <https://gpuweb.github.io/gpuweb/#valid-texture-copy-range>
|
||||
pub(crate) fn validate_texture_copy_range(
|
||||
texture_copy_view: &TextureCopyView,
|
||||
texture_format: wgt::TextureFormat,
|
||||
|
@ -228,12 +230,21 @@ pub(crate) fn validate_texture_copy_range(
|
|||
texture_side: CopySide,
|
||||
copy_size: &Extent3d,
|
||||
) -> Result<(), TransferError> {
|
||||
let (block_width, block_height) = conv::texture_block_size(texture_format);
|
||||
let (block_width, block_height) = texture_format.describe().block_dimensions;
|
||||
let block_width = block_width as u32;
|
||||
let block_height = block_height as u32;
|
||||
|
||||
let mut extent = texture_dimension.level_extent(texture_copy_view.mip_level as u8);
|
||||
|
||||
// Adjust extent for the physical size of mips
|
||||
if texture_copy_view.mip_level != 0 {
|
||||
extent.width = conv::align_up(extent.width, block_width);
|
||||
extent.height = conv::align_up(extent.height, block_height);
|
||||
}
|
||||
|
||||
match texture_dimension {
|
||||
hal::image::Kind::D1(..) => {
|
||||
if (copy_size.height, copy_size.depth) != (1, 1) {
|
||||
if (copy_size.height, copy_size.depth_or_array_layers) != (1, 1) {
|
||||
return Err(TransferError::InvalidCopySize);
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +274,7 @@ pub(crate) fn validate_texture_copy_range(
|
|||
side: texture_side,
|
||||
});
|
||||
}
|
||||
let z_copy_max = texture_copy_view.origin.z + copy_size.depth;
|
||||
let z_copy_max = texture_copy_view.origin.z + copy_size.depth_or_array_layers;
|
||||
if z_copy_max > extent.depth {
|
||||
return Err(TransferError::TextureOverrun {
|
||||
start_offset: texture_copy_view.origin.z,
|
||||
|
@ -308,11 +319,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let (buffer_guard, _) = hub.buffers.read(&mut token);
|
||||
// we can't hold both src_pending and dst_pending in scope because they
|
||||
// borrow the buffer tracker mutably...
|
||||
let mut barriers = Vec::new();
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref mut list) = cmd_buf.commands {
|
||||
|
@ -337,7 +345,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
if !src_buffer.usage.contains(BufferUsage::COPY_SRC) {
|
||||
Err(TransferError::MissingCopySrcUsageFlag)?
|
||||
}
|
||||
barriers.extend(src_pending.map(|pending| pending.into_hal(src_buffer)));
|
||||
// expecting only a single barrier
|
||||
let src_barrier = src_pending
|
||||
.map(|pending| pending.into_hal(src_buffer))
|
||||
.next();
|
||||
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
|
@ -354,7 +365,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
None,
|
||||
))?
|
||||
}
|
||||
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_buffer)));
|
||||
let dst_barrier = dst_pending
|
||||
.map(|pending| pending.into_hal(dst_buffer))
|
||||
.next();
|
||||
|
||||
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
Err(TransferError::UnalignedCopySize(size))?
|
||||
|
@ -390,19 +403,41 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
// Make sure source is initialized memory and mark dest as initialized.
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
dst_buffer
|
||||
.initialization_status
|
||||
.check(destination_offset..(destination_offset + size))
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: destination,
|
||||
range,
|
||||
kind: MemoryInitKind::ImplicitlyInitialized,
|
||||
}),
|
||||
);
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
src_buffer
|
||||
.initialization_status
|
||||
.check(source_offset..(source_offset + size))
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: source,
|
||||
range,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
}),
|
||||
);
|
||||
|
||||
let region = hal::command::BufferCopy {
|
||||
src: source_offset,
|
||||
dst: destination_offset,
|
||||
size,
|
||||
};
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
unsafe {
|
||||
cmb_raw.pipeline_barrier(
|
||||
cmd_buf_raw.pipeline_barrier(
|
||||
all_buffer_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
barriers,
|
||||
src_barrier.into_iter().chain(dst_barrier),
|
||||
);
|
||||
cmb_raw.copy_buffer(src_raw, dst_raw, iter::once(region));
|
||||
cmd_buf_raw.copy_buffer(src_raw, dst_raw, iter::once(region));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -419,7 +454,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
let (texture_guard, _) = hub.textures.read(&mut token);
|
||||
let (dst_layers, dst_selector, dst_offset) =
|
||||
|
@ -434,7 +469,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
});
|
||||
}
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
|
||||
tracing::trace!("Ignoring copy_buffer_to_texture of size 0");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -494,7 +529,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
CopySide::Destination,
|
||||
copy_size,
|
||||
)?;
|
||||
validate_linear_texture_data(
|
||||
let required_buffer_bytes_in_copy = validate_linear_texture_data(
|
||||
&source.layout,
|
||||
dst_texture.format,
|
||||
src_buffer.size,
|
||||
|
@ -503,30 +538,52 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
copy_size,
|
||||
)?;
|
||||
|
||||
let (block_width, _) = conv::texture_block_size(dst_texture.format);
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
src_buffer
|
||||
.initialization_status
|
||||
.check(source.layout.offset..(source.layout.offset + required_buffer_bytes_in_copy))
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: source.buffer,
|
||||
range,
|
||||
kind: MemoryInitKind::NeedsInitializedMemory,
|
||||
}),
|
||||
);
|
||||
|
||||
let (block_width, _) = dst_texture.format.describe().block_dimensions;
|
||||
if !conv::is_valid_copy_dst_texture_format(dst_texture.format) {
|
||||
Err(TransferError::CopyToForbiddenTextureFormat(
|
||||
dst_texture.format,
|
||||
))?
|
||||
}
|
||||
|
||||
let buffer_width = (source.layout.bytes_per_row / bytes_per_block) * block_width;
|
||||
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
|
||||
// the virtual size. We have passed validation, so it's safe to use the
|
||||
// image extent data directly. We want the provided copy size to be no larger than
|
||||
// the virtual size.
|
||||
let max_image_extent = dst_texture.kind.level_extent(destination.mip_level as _);
|
||||
let image_extent = Extent3d {
|
||||
width: copy_size.width.min(max_image_extent.width),
|
||||
height: copy_size.height.min(max_image_extent.height),
|
||||
depth_or_array_layers: copy_size.depth_or_array_layers,
|
||||
};
|
||||
|
||||
let buffer_width = (source.layout.bytes_per_row / bytes_per_block) * block_width as u32;
|
||||
let region = hal::command::BufferImageCopy {
|
||||
buffer_offset: source.layout.offset,
|
||||
buffer_width,
|
||||
buffer_height: source.layout.rows_per_image,
|
||||
image_layers: dst_layers,
|
||||
image_offset: dst_offset,
|
||||
image_extent: conv::map_extent(copy_size, dst_texture.dimension),
|
||||
image_extent: conv::map_extent(&image_extent, dst_texture.dimension),
|
||||
};
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
unsafe {
|
||||
cmb_raw.pipeline_barrier(
|
||||
cmd_buf_raw.pipeline_barrier(
|
||||
all_buffer_stages() | all_image_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
src_barriers.chain(dst_barriers),
|
||||
);
|
||||
cmb_raw.copy_buffer_to_image(
|
||||
cmd_buf_raw.copy_buffer_to_image(
|
||||
src_raw,
|
||||
dst_raw,
|
||||
hal::image::Layout::TransferDstOptimal,
|
||||
|
@ -548,7 +605,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
let (texture_guard, _) = hub.textures.read(&mut token);
|
||||
let (src_layers, src_selector, src_offset) =
|
||||
|
@ -563,7 +620,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
});
|
||||
}
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
|
||||
tracing::trace!("Ignoring copy_texture_to_buffer of size 0");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -623,7 +680,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
CopySide::Source,
|
||||
copy_size,
|
||||
)?;
|
||||
validate_linear_texture_data(
|
||||
let required_buffer_bytes_in_copy = validate_linear_texture_data(
|
||||
&destination.layout,
|
||||
src_texture.format,
|
||||
dst_buffer.size,
|
||||
|
@ -632,30 +689,56 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
copy_size,
|
||||
)?;
|
||||
|
||||
let (block_width, _) = conv::texture_block_size(src_texture.format);
|
||||
let (block_width, _) = src_texture.format.describe().block_dimensions;
|
||||
if !conv::is_valid_copy_src_texture_format(src_texture.format) {
|
||||
Err(TransferError::CopyFromForbiddenTextureFormat(
|
||||
src_texture.format,
|
||||
))?
|
||||
}
|
||||
|
||||
let buffer_width = (destination.layout.bytes_per_row / bytes_per_block) * block_width;
|
||||
cmd_buf.buffer_memory_init_actions.extend(
|
||||
dst_buffer
|
||||
.initialization_status
|
||||
.check(
|
||||
destination.layout.offset
|
||||
..(destination.layout.offset + required_buffer_bytes_in_copy),
|
||||
)
|
||||
.map(|range| MemoryInitTrackerAction {
|
||||
id: destination.buffer,
|
||||
range,
|
||||
kind: MemoryInitKind::ImplicitlyInitialized,
|
||||
}),
|
||||
);
|
||||
|
||||
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
|
||||
// the virtual size. We have passed validation, so it's safe to use the
|
||||
// image extent data directly. We want the provided copy size to be no larger than
|
||||
// the virtual size.
|
||||
let max_image_extent = src_texture.kind.level_extent(source.mip_level as _);
|
||||
let image_extent = Extent3d {
|
||||
width: copy_size.width.min(max_image_extent.width),
|
||||
height: copy_size.height.min(max_image_extent.height),
|
||||
depth_or_array_layers: copy_size.depth_or_array_layers,
|
||||
};
|
||||
|
||||
let buffer_width =
|
||||
(destination.layout.bytes_per_row / bytes_per_block) * block_width as u32;
|
||||
let region = hal::command::BufferImageCopy {
|
||||
buffer_offset: destination.layout.offset,
|
||||
buffer_width,
|
||||
buffer_height: destination.layout.rows_per_image,
|
||||
image_layers: src_layers,
|
||||
image_offset: src_offset,
|
||||
image_extent: conv::map_extent(copy_size, src_texture.dimension),
|
||||
image_extent: conv::map_extent(&image_extent, src_texture.dimension),
|
||||
};
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
unsafe {
|
||||
cmb_raw.pipeline_barrier(
|
||||
cmd_buf_raw.pipeline_barrier(
|
||||
all_buffer_stages() | all_image_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
src_barriers.chain(dst_barrier),
|
||||
);
|
||||
cmb_raw.copy_image_to_buffer(
|
||||
cmd_buf_raw.copy_image_to_buffer(
|
||||
src_raw,
|
||||
hal::image::Layout::TransferSrcOptimal,
|
||||
dst_raw,
|
||||
|
@ -678,12 +761,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
|
||||
let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?;
|
||||
let (_, mut token) = hub.buffers.read(&mut token); // skip token
|
||||
let (texture_guard, _) = hub.textures.read(&mut token);
|
||||
// we can't hold both src_pending and dst_pending in scope because they
|
||||
// borrow the buffer tracker mutably...
|
||||
let mut barriers = Vec::new();
|
||||
let (src_layers, src_selector, src_offset) =
|
||||
texture_copy_view_to_hal(source, copy_size, &*texture_guard)?;
|
||||
let (dst_layers, dst_selector, dst_offset) =
|
||||
|
@ -701,7 +781,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
});
|
||||
}
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
|
||||
tracing::trace!("Ignoring copy_texture_to_texture of size 0");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -723,7 +803,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
if !src_texture.usage.contains(TextureUsage::COPY_SRC) {
|
||||
Err(TransferError::MissingCopySrcUsageFlag)?
|
||||
}
|
||||
barriers.extend(src_pending.map(|pending| pending.into_hal(src_texture)));
|
||||
//TODO: try to avoid this the collection. It's needed because both
|
||||
// `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
|
||||
let mut barriers = src_pending
|
||||
.map(|pending| pending.into_hal(src_texture))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (dst_texture, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
|
@ -762,21 +846,37 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
copy_size,
|
||||
)?;
|
||||
|
||||
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
|
||||
// the virtual size. We have passed validation, so it's safe to use the
|
||||
// image extent data directly. We want the provided copy size to be no larger than
|
||||
// the virtual size.
|
||||
let max_src_image_extent = src_texture.kind.level_extent(source.mip_level as _);
|
||||
let max_dst_image_extent = dst_texture.kind.level_extent(destination.mip_level as _);
|
||||
let image_extent = Extent3d {
|
||||
width: copy_size
|
||||
.width
|
||||
.min(max_src_image_extent.width.min(max_dst_image_extent.width)),
|
||||
height: copy_size
|
||||
.height
|
||||
.min(max_src_image_extent.height.min(max_dst_image_extent.height)),
|
||||
depth_or_array_layers: copy_size.depth_or_array_layers,
|
||||
};
|
||||
|
||||
let region = hal::command::ImageCopy {
|
||||
src_subresource: src_layers,
|
||||
src_offset,
|
||||
dst_subresource: dst_layers,
|
||||
dst_offset,
|
||||
extent: conv::map_extent(copy_size, src_texture.dimension),
|
||||
extent: conv::map_extent(&image_extent, src_texture.dimension),
|
||||
};
|
||||
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
|
||||
unsafe {
|
||||
cmb_raw.pipeline_barrier(
|
||||
cmd_buf_raw.pipeline_barrier(
|
||||
all_image_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
barriers,
|
||||
barriers.into_iter(),
|
||||
);
|
||||
cmb_raw.copy_image(
|
||||
cmd_buf_raw.copy_image(
|
||||
src_raw,
|
||||
hal::image::Layout::TransferSrcOptimal,
|
||||
dst_raw,
|
||||
|
|
|
@ -9,6 +9,27 @@ use crate::{
|
|||
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub fn map_adapter_info(
|
||||
info: hal::adapter::AdapterInfo,
|
||||
backend: wgt::Backend,
|
||||
) -> wgt::AdapterInfo {
|
||||
use hal::adapter::DeviceType as Dt;
|
||||
|
||||
wgt::AdapterInfo {
|
||||
name: info.name,
|
||||
vendor: info.vendor,
|
||||
device: info.device,
|
||||
device_type: match info.device_type {
|
||||
Dt::Other => wgt::DeviceType::Other,
|
||||
Dt::IntegratedGpu => wgt::DeviceType::IntegratedGpu,
|
||||
Dt::DiscreteGpu => wgt::DeviceType::DiscreteGpu,
|
||||
Dt::VirtualGpu => wgt::DeviceType::VirtualGpu,
|
||||
Dt::Cpu => wgt::DeviceType::Cpu,
|
||||
},
|
||||
backend,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_buffer_usage(usage: wgt::BufferUsage) -> (hal::buffer::Usage, hal::memory::Properties) {
|
||||
use hal::buffer::Usage as U;
|
||||
use hal::memory::Properties as P;
|
||||
|
@ -139,7 +160,7 @@ pub fn map_extent(extent: &wgt::Extent3d, dim: wgt::TextureDimension) -> hal::im
|
|||
height: extent.height,
|
||||
depth: match dim {
|
||||
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => 1,
|
||||
wgt::TextureDimension::D3 => extent.depth,
|
||||
wgt::TextureDimension::D3 => extent.depth_or_array_layers,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -156,21 +177,15 @@ pub fn map_primitive_topology(primitive_topology: wgt::PrimitiveTopology) -> hal
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_color_state_descriptor(desc: &wgt::ColorStateDescriptor) -> hal::pso::ColorBlendDesc {
|
||||
pub fn map_color_target_state(desc: &wgt::ColorTargetState) -> hal::pso::ColorBlendDesc {
|
||||
let color_mask = desc.write_mask;
|
||||
let blend_state = if desc.color_blend != wgt::BlendDescriptor::REPLACE
|
||||
|| desc.alpha_blend != wgt::BlendDescriptor::REPLACE
|
||||
{
|
||||
Some(hal::pso::BlendState {
|
||||
color: map_blend_descriptor(&desc.color_blend),
|
||||
alpha: map_blend_descriptor(&desc.alpha_blend),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let blend = desc.blend.as_ref().map(|bs| hal::pso::BlendState {
|
||||
color: map_blend_component(&bs.color),
|
||||
alpha: map_blend_component(&bs.alpha),
|
||||
});
|
||||
hal::pso::ColorBlendDesc {
|
||||
mask: map_color_write_flags(color_mask),
|
||||
blend: blend_state,
|
||||
blend,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,21 +209,21 @@ fn map_color_write_flags(flags: wgt::ColorWrite) -> hal::pso::ColorMask {
|
|||
value
|
||||
}
|
||||
|
||||
fn map_blend_descriptor(blend_desc: &wgt::BlendDescriptor) -> hal::pso::BlendOp {
|
||||
fn map_blend_component(component: &wgt::BlendComponent) -> hal::pso::BlendOp {
|
||||
use hal::pso::BlendOp as H;
|
||||
use wgt::BlendOperation as Bo;
|
||||
match blend_desc.operation {
|
||||
match component.operation {
|
||||
Bo::Add => H::Add {
|
||||
src: map_blend_factor(blend_desc.src_factor),
|
||||
dst: map_blend_factor(blend_desc.dst_factor),
|
||||
src: map_blend_factor(component.src_factor),
|
||||
dst: map_blend_factor(component.dst_factor),
|
||||
},
|
||||
Bo::Subtract => H::Sub {
|
||||
src: map_blend_factor(blend_desc.src_factor),
|
||||
dst: map_blend_factor(blend_desc.dst_factor),
|
||||
src: map_blend_factor(component.src_factor),
|
||||
dst: map_blend_factor(component.dst_factor),
|
||||
},
|
||||
Bo::ReverseSubtract => H::RevSub {
|
||||
src: map_blend_factor(blend_desc.src_factor),
|
||||
dst: map_blend_factor(blend_desc.dst_factor),
|
||||
src: map_blend_factor(component.src_factor),
|
||||
dst: map_blend_factor(component.dst_factor),
|
||||
},
|
||||
Bo::Min => H::Min,
|
||||
Bo::Max => H::Max,
|
||||
|
@ -235,9 +250,7 @@ fn map_blend_factor(blend_factor: wgt::BlendFactor) -> hal::pso::Factor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_depth_stencil_state_descriptor(
|
||||
desc: &wgt::DepthStencilStateDescriptor,
|
||||
) -> hal::pso::DepthStencilDesc {
|
||||
pub fn map_depth_stencil_state(desc: &wgt::DepthStencilState) -> hal::pso::DepthStencilDesc {
|
||||
hal::pso::DepthStencilDesc {
|
||||
depth: if desc.is_depth_enabled() {
|
||||
Some(hal::pso::DepthTest {
|
||||
|
@ -269,9 +282,7 @@ pub fn map_depth_stencil_state_descriptor(
|
|||
}
|
||||
}
|
||||
|
||||
fn map_stencil_face(
|
||||
stencil_state_face_desc: &wgt::StencilStateFaceDescriptor,
|
||||
) -> hal::pso::StencilFace {
|
||||
fn map_stencil_face(stencil_state_face_desc: &wgt::StencilFaceState) -> hal::pso::StencilFace {
|
||||
hal::pso::StencilFace {
|
||||
fun: map_compare_function(stencil_state_face_desc.compare),
|
||||
op_fail: map_stencil_operation(stencil_state_face_desc.fail_op),
|
||||
|
@ -396,124 +407,48 @@ pub(crate) fn map_texture_format(
|
|||
Tf::Bc6hRgbUfloat => H::Bc6hUfloat,
|
||||
Tf::Bc7RgbaUnorm => H::Bc7Unorm,
|
||||
Tf::Bc7RgbaUnormSrgb => H::Bc7Srgb,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn texture_block_size(format: wgt::TextureFormat) -> (u32, u32) {
|
||||
use wgt::TextureFormat as Tf;
|
||||
match format {
|
||||
Tf::R8Unorm
|
||||
| Tf::R8Snorm
|
||||
| Tf::R8Uint
|
||||
| Tf::R8Sint
|
||||
| Tf::R16Uint
|
||||
| Tf::R16Sint
|
||||
| Tf::R16Float
|
||||
| Tf::Rg8Unorm
|
||||
| Tf::Rg8Snorm
|
||||
| Tf::Rg8Uint
|
||||
| Tf::Rg8Sint
|
||||
| Tf::R32Uint
|
||||
| Tf::R32Sint
|
||||
| Tf::R32Float
|
||||
| Tf::Rg16Uint
|
||||
| Tf::Rg16Sint
|
||||
| Tf::Rg16Float
|
||||
| Tf::Rgba8Unorm
|
||||
| Tf::Rgba8UnormSrgb
|
||||
| Tf::Rgba8Snorm
|
||||
| Tf::Rgba8Uint
|
||||
| Tf::Rgba8Sint
|
||||
| Tf::Bgra8Unorm
|
||||
| Tf::Bgra8UnormSrgb
|
||||
| Tf::Rgb10a2Unorm
|
||||
| Tf::Rg11b10Float
|
||||
| Tf::Rg32Uint
|
||||
| Tf::Rg32Sint
|
||||
| Tf::Rg32Float
|
||||
| Tf::Rgba16Uint
|
||||
| Tf::Rgba16Sint
|
||||
| Tf::Rgba16Float
|
||||
| Tf::Rgba32Uint
|
||||
| Tf::Rgba32Sint
|
||||
| Tf::Rgba32Float
|
||||
| Tf::Depth32Float
|
||||
| Tf::Depth24Plus
|
||||
| Tf::Depth24PlusStencil8 => (1, 1),
|
||||
// ETC compressed formats
|
||||
Tf::Etc2RgbUnorm => H::Etc2R8g8b8Unorm,
|
||||
Tf::Etc2RgbUnormSrgb => H::Etc2R8g8b8Srgb,
|
||||
Tf::Etc2RgbA1Unorm => H::Etc2R8g8b8a1Unorm,
|
||||
Tf::Etc2RgbA1UnormSrgb => H::Etc2R8g8b8a1Srgb,
|
||||
Tf::Etc2RgbA8Unorm => H::Etc2R8g8b8a8Unorm,
|
||||
Tf::Etc2RgbA8UnormSrgb => H::Etc2R8g8b8a8Unorm,
|
||||
Tf::EacRUnorm => H::EacR11Unorm,
|
||||
Tf::EacRSnorm => H::EacR11Snorm,
|
||||
Tf::EtcRgUnorm => H::EacR11g11Unorm,
|
||||
Tf::EtcRgSnorm => H::EacR11g11Snorm,
|
||||
|
||||
Tf::Bc1RgbaUnorm
|
||||
| Tf::Bc1RgbaUnormSrgb
|
||||
| Tf::Bc2RgbaUnorm
|
||||
| Tf::Bc2RgbaUnormSrgb
|
||||
| Tf::Bc3RgbaUnorm
|
||||
| Tf::Bc3RgbaUnormSrgb
|
||||
| Tf::Bc4RUnorm
|
||||
| Tf::Bc4RSnorm
|
||||
| Tf::Bc5RgUnorm
|
||||
| Tf::Bc5RgSnorm
|
||||
| Tf::Bc6hRgbUfloat
|
||||
| Tf::Bc6hRgbSfloat
|
||||
| Tf::Bc7RgbaUnorm
|
||||
| Tf::Bc7RgbaUnormSrgb => (4, 4),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn texture_features(format: wgt::TextureFormat) -> wgt::Features {
|
||||
use wgt::TextureFormat as Tf;
|
||||
match format {
|
||||
Tf::R8Unorm
|
||||
| Tf::R8Snorm
|
||||
| Tf::R8Uint
|
||||
| Tf::R8Sint
|
||||
| Tf::R16Uint
|
||||
| Tf::R16Sint
|
||||
| Tf::R16Float
|
||||
| Tf::Rg8Unorm
|
||||
| Tf::Rg8Snorm
|
||||
| Tf::Rg8Uint
|
||||
| Tf::Rg8Sint
|
||||
| Tf::R32Uint
|
||||
| Tf::R32Sint
|
||||
| Tf::R32Float
|
||||
| Tf::Rg16Uint
|
||||
| Tf::Rg16Sint
|
||||
| Tf::Rg16Float
|
||||
| Tf::Rgba8Unorm
|
||||
| Tf::Rgba8UnormSrgb
|
||||
| Tf::Rgba8Snorm
|
||||
| Tf::Rgba8Uint
|
||||
| Tf::Rgba8Sint
|
||||
| Tf::Bgra8Unorm
|
||||
| Tf::Bgra8UnormSrgb
|
||||
| Tf::Rgb10a2Unorm
|
||||
| Tf::Rg11b10Float
|
||||
| Tf::Rg32Uint
|
||||
| Tf::Rg32Sint
|
||||
| Tf::Rg32Float
|
||||
| Tf::Rgba16Uint
|
||||
| Tf::Rgba16Sint
|
||||
| Tf::Rgba16Float
|
||||
| Tf::Rgba32Uint
|
||||
| Tf::Rgba32Sint
|
||||
| Tf::Rgba32Float
|
||||
| Tf::Depth32Float
|
||||
| Tf::Depth24Plus
|
||||
| Tf::Depth24PlusStencil8 => wgt::Features::empty(),
|
||||
|
||||
Tf::Bc1RgbaUnorm
|
||||
| Tf::Bc1RgbaUnormSrgb
|
||||
| Tf::Bc2RgbaUnorm
|
||||
| Tf::Bc2RgbaUnormSrgb
|
||||
| Tf::Bc3RgbaUnorm
|
||||
| Tf::Bc3RgbaUnormSrgb
|
||||
| Tf::Bc4RUnorm
|
||||
| Tf::Bc4RSnorm
|
||||
| Tf::Bc5RgUnorm
|
||||
| Tf::Bc5RgSnorm
|
||||
| Tf::Bc6hRgbUfloat
|
||||
| Tf::Bc6hRgbSfloat
|
||||
| Tf::Bc7RgbaUnorm
|
||||
| Tf::Bc7RgbaUnormSrgb => wgt::Features::TEXTURE_COMPRESSION_BC,
|
||||
// ASTC compressed formats
|
||||
Tf::Astc4x4RgbaUnorm => H::Astc4x4Srgb,
|
||||
Tf::Astc4x4RgbaUnormSrgb => H::Astc4x4Srgb,
|
||||
Tf::Astc5x4RgbaUnorm => H::Astc5x4Unorm,
|
||||
Tf::Astc5x4RgbaUnormSrgb => H::Astc5x4Srgb,
|
||||
Tf::Astc5x5RgbaUnorm => H::Astc5x5Unorm,
|
||||
Tf::Astc5x5RgbaUnormSrgb => H::Astc5x5Srgb,
|
||||
Tf::Astc6x5RgbaUnorm => H::Astc6x5Unorm,
|
||||
Tf::Astc6x5RgbaUnormSrgb => H::Astc6x5Srgb,
|
||||
Tf::Astc6x6RgbaUnorm => H::Astc6x6Unorm,
|
||||
Tf::Astc6x6RgbaUnormSrgb => H::Astc6x6Srgb,
|
||||
Tf::Astc8x5RgbaUnorm => H::Astc8x5Unorm,
|
||||
Tf::Astc8x5RgbaUnormSrgb => H::Astc8x5Srgb,
|
||||
Tf::Astc8x6RgbaUnorm => H::Astc8x6Unorm,
|
||||
Tf::Astc8x6RgbaUnormSrgb => H::Astc8x6Srgb,
|
||||
Tf::Astc10x5RgbaUnorm => H::Astc10x5Unorm,
|
||||
Tf::Astc10x5RgbaUnormSrgb => H::Astc10x5Srgb,
|
||||
Tf::Astc10x6RgbaUnorm => H::Astc10x6Unorm,
|
||||
Tf::Astc10x6RgbaUnormSrgb => H::Astc10x6Srgb,
|
||||
Tf::Astc8x8RgbaUnorm => H::Astc8x8Unorm,
|
||||
Tf::Astc8x8RgbaUnormSrgb => H::Astc8x8Srgb,
|
||||
Tf::Astc10x8RgbaUnorm => H::Astc10x8Unorm,
|
||||
Tf::Astc10x8RgbaUnormSrgb => H::Astc10x8Srgb,
|
||||
Tf::Astc10x10RgbaUnorm => H::Astc10x10Unorm,
|
||||
Tf::Astc10x10RgbaUnormSrgb => H::Astc10x10Srgb,
|
||||
Tf::Astc12x10RgbaUnorm => H::Astc12x10Unorm,
|
||||
Tf::Astc12x10RgbaUnormSrgb => H::Astc12x10Srgb,
|
||||
Tf::Astc12x12RgbaUnorm => H::Astc12x12Unorm,
|
||||
Tf::Astc12x12RgbaUnormSrgb => H::Astc12x12Srgb,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -521,36 +456,40 @@ pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> hal::format::Forma
|
|||
use hal::format::Format as H;
|
||||
use wgt::VertexFormat as Vf;
|
||||
match vertex_format {
|
||||
Vf::Uchar2 => H::Rg8Uint,
|
||||
Vf::Uchar4 => H::Rgba8Uint,
|
||||
Vf::Char2 => H::Rg8Sint,
|
||||
Vf::Char4 => H::Rgba8Sint,
|
||||
Vf::Uchar2Norm => H::Rg8Unorm,
|
||||
Vf::Uchar4Norm => H::Rgba8Unorm,
|
||||
Vf::Char2Norm => H::Rg8Snorm,
|
||||
Vf::Char4Norm => H::Rgba8Snorm,
|
||||
Vf::Ushort2 => H::Rg16Uint,
|
||||
Vf::Ushort4 => H::Rgba16Uint,
|
||||
Vf::Short2 => H::Rg16Sint,
|
||||
Vf::Short4 => H::Rgba16Sint,
|
||||
Vf::Ushort2Norm => H::Rg16Unorm,
|
||||
Vf::Ushort4Norm => H::Rgba16Unorm,
|
||||
Vf::Short2Norm => H::Rg16Snorm,
|
||||
Vf::Short4Norm => H::Rgba16Snorm,
|
||||
Vf::Half2 => H::Rg16Sfloat,
|
||||
Vf::Half4 => H::Rgba16Sfloat,
|
||||
Vf::Float => H::R32Sfloat,
|
||||
Vf::Float2 => H::Rg32Sfloat,
|
||||
Vf::Float3 => H::Rgb32Sfloat,
|
||||
Vf::Float4 => H::Rgba32Sfloat,
|
||||
Vf::Uint => H::R32Uint,
|
||||
Vf::Uint2 => H::Rg32Uint,
|
||||
Vf::Uint3 => H::Rgb32Uint,
|
||||
Vf::Uint4 => H::Rgba32Uint,
|
||||
Vf::Int => H::R32Sint,
|
||||
Vf::Int2 => H::Rg32Sint,
|
||||
Vf::Int3 => H::Rgb32Sint,
|
||||
Vf::Int4 => H::Rgba32Sint,
|
||||
Vf::Uint8x2 => H::Rg8Uint,
|
||||
Vf::Uint8x4 => H::Rgba8Uint,
|
||||
Vf::Sint8x2 => H::Rg8Sint,
|
||||
Vf::Sint8x4 => H::Rgba8Sint,
|
||||
Vf::Unorm8x2 => H::Rg8Unorm,
|
||||
Vf::Unorm8x4 => H::Rgba8Unorm,
|
||||
Vf::Snorm8x2 => H::Rg8Snorm,
|
||||
Vf::Snorm8x4 => H::Rgba8Snorm,
|
||||
Vf::Uint16x2 => H::Rg16Uint,
|
||||
Vf::Uint16x4 => H::Rgba16Uint,
|
||||
Vf::Sint16x2 => H::Rg16Sint,
|
||||
Vf::Sint16x4 => H::Rgba16Sint,
|
||||
Vf::Unorm16x2 => H::Rg16Unorm,
|
||||
Vf::Unorm16x4 => H::Rgba16Unorm,
|
||||
Vf::Snorm16x2 => H::Rg16Snorm,
|
||||
Vf::Snorm16x4 => H::Rgba16Snorm,
|
||||
Vf::Float16x2 => H::Rg16Sfloat,
|
||||
Vf::Float16x4 => H::Rgba16Sfloat,
|
||||
Vf::Float32 => H::R32Sfloat,
|
||||
Vf::Float32x2 => H::Rg32Sfloat,
|
||||
Vf::Float32x3 => H::Rgb32Sfloat,
|
||||
Vf::Float32x4 => H::Rgba32Sfloat,
|
||||
Vf::Uint32 => H::R32Uint,
|
||||
Vf::Uint32x2 => H::Rg32Uint,
|
||||
Vf::Uint32x3 => H::Rgb32Uint,
|
||||
Vf::Uint32x4 => H::Rgba32Uint,
|
||||
Vf::Sint32 => H::R32Sint,
|
||||
Vf::Sint32x2 => H::Rg32Sint,
|
||||
Vf::Sint32x3 => H::Rgb32Sint,
|
||||
Vf::Sint32x4 => H::Rgba32Sint,
|
||||
Vf::Float64 => H::R64Sfloat,
|
||||
Vf::Float64x2 => H::Rg64Sfloat,
|
||||
Vf::Float64x3 => H::Rgb64Sfloat,
|
||||
Vf::Float64x4 => H::Rgba64Sfloat,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,52 +518,63 @@ pub fn map_texture_dimension_size(
|
|||
wgt::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
depth_or_array_layers,
|
||||
}: wgt::Extent3d,
|
||||
sample_size: u32,
|
||||
limits: &wgt::Limits,
|
||||
) -> Result<hal::image::Kind, resource::TextureDimensionError> {
|
||||
use hal::image::Kind as H;
|
||||
use resource::TextureDimensionError as Tde;
|
||||
use resource::{TextureDimensionError as Tde, TextureErrorDimension as Ted};
|
||||
use wgt::TextureDimension::*;
|
||||
|
||||
let zero_dim = if width == 0 {
|
||||
Some(resource::TextureErrorDimension::X)
|
||||
} else if height == 0 {
|
||||
Some(resource::TextureErrorDimension::Y)
|
||||
} else if depth == 0 {
|
||||
Some(resource::TextureErrorDimension::Z)
|
||||
} else {
|
||||
None
|
||||
let layers = depth_or_array_layers.try_into().unwrap_or(!0);
|
||||
let (kind, extent_limits, sample_limit) = match dimension {
|
||||
D1 => (
|
||||
H::D1(width, layers),
|
||||
[
|
||||
limits.max_texture_dimension_1d,
|
||||
1,
|
||||
limits.max_texture_array_layers,
|
||||
],
|
||||
1,
|
||||
),
|
||||
D2 => (
|
||||
H::D2(width, height, layers, sample_size as u8),
|
||||
[
|
||||
limits.max_texture_dimension_2d,
|
||||
limits.max_texture_dimension_2d,
|
||||
limits.max_texture_array_layers,
|
||||
],
|
||||
32,
|
||||
),
|
||||
D3 => (
|
||||
H::D3(width, height, depth_or_array_layers),
|
||||
[
|
||||
limits.max_texture_dimension_3d,
|
||||
limits.max_texture_dimension_3d,
|
||||
limits.max_texture_dimension_3d,
|
||||
],
|
||||
1,
|
||||
),
|
||||
};
|
||||
if let Some(dim) = zero_dim {
|
||||
return Err(resource::TextureDimensionError::Zero(dim));
|
||||
|
||||
for (&dim, (&given, &limit)) in [Ted::X, Ted::Y, Ted::Z].iter().zip(
|
||||
[width, height, depth_or_array_layers]
|
||||
.iter()
|
||||
.zip(extent_limits.iter()),
|
||||
) {
|
||||
if given == 0 {
|
||||
return Err(Tde::Zero(dim));
|
||||
}
|
||||
if given > limit {
|
||||
return Err(Tde::LimitExceeded { dim, given, limit });
|
||||
}
|
||||
}
|
||||
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two(sample_size) {
|
||||
return Err(Tde::InvalidSampleCount(sample_size));
|
||||
}
|
||||
|
||||
Ok(match dimension {
|
||||
D1 => {
|
||||
if height != 1 {
|
||||
return Err(Tde::InvalidHeight);
|
||||
}
|
||||
if sample_size != 1 {
|
||||
return Err(Tde::InvalidSampleCount(sample_size));
|
||||
}
|
||||
let layers = depth.try_into().unwrap_or(!0);
|
||||
H::D1(width, layers)
|
||||
}
|
||||
D2 => {
|
||||
if sample_size > 32 || !is_power_of_two(sample_size) {
|
||||
return Err(Tde::InvalidSampleCount(sample_size));
|
||||
}
|
||||
let layers = depth.try_into().unwrap_or(!0);
|
||||
H::D2(width, height, layers, sample_size as u8)
|
||||
}
|
||||
D3 => {
|
||||
if sample_size != 1 {
|
||||
return Err(Tde::InvalidSampleCount(sample_size));
|
||||
}
|
||||
H::D3(width, height, depth)
|
||||
}
|
||||
})
|
||||
Ok(kind)
|
||||
}
|
||||
|
||||
pub fn map_texture_view_dimension(dimension: wgt::TextureViewDimension) -> hal::image::ViewKind {
|
||||
|
@ -670,7 +620,7 @@ pub(crate) fn map_buffer_state(usage: resource::BufferUse) -> hal::buffer::State
|
|||
access |= A::SHADER_READ;
|
||||
}
|
||||
if usage.contains(W::STORAGE_STORE) {
|
||||
access |= A::SHADER_WRITE;
|
||||
access |= A::SHADER_READ | A::SHADER_WRITE;
|
||||
}
|
||||
if usage.contains(W::INDIRECT) {
|
||||
access |= A::INDIRECT_COMMAND_READ;
|
||||
|
@ -732,6 +682,43 @@ pub(crate) fn map_texture_state(
|
|||
(access, layout)
|
||||
}
|
||||
|
||||
pub fn map_query_type(ty: &wgt::QueryType) -> (hal::query::Type, u32) {
|
||||
match ty {
|
||||
wgt::QueryType::PipelineStatistics(pipeline_statistics) => {
|
||||
let mut ps = hal::query::PipelineStatistic::empty();
|
||||
ps.set(
|
||||
hal::query::PipelineStatistic::VERTEX_SHADER_INVOCATIONS,
|
||||
pipeline_statistics
|
||||
.contains(wgt::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS),
|
||||
);
|
||||
ps.set(
|
||||
hal::query::PipelineStatistic::CLIPPING_INVOCATIONS,
|
||||
pipeline_statistics.contains(wgt::PipelineStatisticsTypes::CLIPPER_INVOCATIONS),
|
||||
);
|
||||
ps.set(
|
||||
hal::query::PipelineStatistic::CLIPPING_PRIMITIVES,
|
||||
pipeline_statistics.contains(wgt::PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT),
|
||||
);
|
||||
ps.set(
|
||||
hal::query::PipelineStatistic::FRAGMENT_SHADER_INVOCATIONS,
|
||||
pipeline_statistics
|
||||
.contains(wgt::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS),
|
||||
);
|
||||
ps.set(
|
||||
hal::query::PipelineStatistic::COMPUTE_SHADER_INVOCATIONS,
|
||||
pipeline_statistics
|
||||
.contains(wgt::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS),
|
||||
);
|
||||
|
||||
(
|
||||
hal::query::Type::PipelineStatistics(ps),
|
||||
pipeline_statistics.bits().count_ones(),
|
||||
)
|
||||
}
|
||||
wgt::QueryType::Timestamp => (hal::query::Type::Timestamp, 1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_load_store_ops<V>(channel: &PassChannel<V>) -> hal::pass::AttachmentOps {
|
||||
hal::pass::AttachmentOps {
|
||||
load: match channel.load_op {
|
||||
|
@ -788,46 +775,84 @@ pub fn map_wrap(address: wgt::AddressMode) -> hal::image::WrapMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_rasterization_state_descriptor(
|
||||
desc: &wgt::RasterizationStateDescriptor,
|
||||
pub fn map_primitive_state_to_input_assembler(
|
||||
desc: &wgt::PrimitiveState,
|
||||
) -> hal::pso::InputAssemblerDesc {
|
||||
hal::pso::InputAssemblerDesc {
|
||||
primitive: map_primitive_topology(desc.topology),
|
||||
with_adjacency: false,
|
||||
restart_index: desc.strip_index_format.map(map_index_format),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_primitive_state_to_rasterizer(
|
||||
desc: &wgt::PrimitiveState,
|
||||
depth_stencil: Option<&wgt::DepthStencilState>,
|
||||
) -> hal::pso::Rasterizer {
|
||||
use hal::pso;
|
||||
let (depth_clamping, depth_bias) = match depth_stencil {
|
||||
Some(dsd) => {
|
||||
let bias = if dsd.bias.is_enabled() {
|
||||
Some(pso::State::Static(pso::DepthBias {
|
||||
const_factor: dsd.bias.constant as f32,
|
||||
slope_factor: dsd.bias.slope_scale,
|
||||
clamp: dsd.bias.clamp,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(dsd.clamp_depth, bias)
|
||||
}
|
||||
None => (false, None),
|
||||
};
|
||||
pso::Rasterizer {
|
||||
depth_clamping: desc.clamp_depth,
|
||||
depth_clamping,
|
||||
polygon_mode: match desc.polygon_mode {
|
||||
wgt::PolygonMode::Fill => pso::PolygonMode::Fill,
|
||||
wgt::PolygonMode::Line => pso::PolygonMode::Line,
|
||||
wgt::PolygonMode::Point => pso::PolygonMode::Point,
|
||||
},
|
||||
cull_face: match desc.cull_mode {
|
||||
wgt::CullMode::None => pso::Face::empty(),
|
||||
wgt::CullMode::Front => pso::Face::FRONT,
|
||||
wgt::CullMode::Back => pso::Face::BACK,
|
||||
None => pso::Face::empty(),
|
||||
Some(wgt::Face::Front) => pso::Face::FRONT,
|
||||
Some(wgt::Face::Back) => pso::Face::BACK,
|
||||
},
|
||||
front_face: match desc.front_face {
|
||||
wgt::FrontFace::Ccw => pso::FrontFace::CounterClockwise,
|
||||
wgt::FrontFace::Cw => pso::FrontFace::Clockwise,
|
||||
},
|
||||
depth_bias: if desc.depth_bias != 0
|
||||
|| desc.depth_bias_slope_scale != 0.0
|
||||
|| desc.depth_bias_clamp != 0.0
|
||||
{
|
||||
Some(pso::State::Static(pso::DepthBias {
|
||||
const_factor: desc.depth_bias as f32,
|
||||
slope_factor: desc.depth_bias_slope_scale,
|
||||
clamp: desc.depth_bias_clamp,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
depth_bias,
|
||||
conservative: false,
|
||||
line_width: pso::State::Static(1.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_multisample_state(desc: &wgt::MultisampleState) -> hal::pso::Multisampling {
|
||||
hal::pso::Multisampling {
|
||||
rasterization_samples: desc.count as _,
|
||||
sample_shading: None,
|
||||
sample_mask: desc.mask,
|
||||
alpha_coverage: desc.alpha_to_coverage_enabled,
|
||||
alpha_to_one: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_index_format(index_format: wgt::IndexFormat) -> hal::IndexType {
|
||||
match index_format {
|
||||
wgt::IndexFormat::Uint16 => hal::IndexType::U16,
|
||||
wgt::IndexFormat::Uint32 => hal::IndexType::U32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Take `value` and round it up to the nearest alignment `alignment`.
|
||||
///
|
||||
/// ```text
|
||||
/// (0, 3) -> 0
|
||||
/// (1, 3) -> 3
|
||||
/// (2, 3) -> 3
|
||||
/// (3, 3) -> 3
|
||||
/// (4, 3) -> 6
|
||||
/// ...
|
||||
pub fn align_up(value: u32, alignment: u32) -> u32 {
|
||||
((value + alignment - 1) / alignment) * alignment
|
||||
}
|
||||
|
|
|
@ -3,21 +3,15 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use super::DeviceError;
|
||||
use hal::device::Device;
|
||||
use std::{borrow::Cow, fmt, iter, ptr::NonNull, sync::Arc};
|
||||
use hal::device::Device as _;
|
||||
use std::{borrow::Cow, iter, ptr::NonNull};
|
||||
|
||||
pub struct MemoryAllocator<B: hal::Backend>(gpu_alloc::GpuAllocator<Arc<B::Memory>>);
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryBlock<B: hal::Backend>(gpu_alloc::MemoryBlock<Arc<B::Memory>>);
|
||||
pub struct MemoryAllocator<B: hal::Backend>(gpu_alloc::GpuAllocator<B::Memory>);
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryBlock<B: hal::Backend>(gpu_alloc::MemoryBlock<B::Memory>);
|
||||
struct MemoryDevice<'a, B: hal::Backend>(&'a B::Device);
|
||||
|
||||
//TODO: https://github.com/zakarumych/gpu-alloc/issues/9
|
||||
impl<B: hal::Backend> fmt::Debug for MemoryAllocator<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MemoryAllocator")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> MemoryAllocator<B> {
|
||||
pub fn new(mem_props: hal::adapter::MemoryProperties, limits: hal::Limits) -> Self {
|
||||
let mem_config = gpu_alloc::Config {
|
||||
|
@ -99,17 +93,19 @@ impl<B: hal::Backend> MemoryBlock<B> {
|
|||
device: &B::Device,
|
||||
buffer: &mut B::Buffer,
|
||||
) -> Result<(), DeviceError> {
|
||||
let mem = self.0.memory();
|
||||
unsafe {
|
||||
device
|
||||
.bind_buffer_memory(self.0.memory(), self.0.offset(), buffer)
|
||||
.bind_buffer_memory(mem, self.0.offset(), buffer)
|
||||
.map_err(DeviceError::from_bind)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_image(&self, device: &B::Device, image: &mut B::Image) -> Result<(), DeviceError> {
|
||||
let mem = self.0.memory();
|
||||
unsafe {
|
||||
device
|
||||
.bind_image_memory(self.0.memory(), self.0.offset(), image)
|
||||
.bind_image_memory(mem, self.0.offset(), image)
|
||||
.map_err(DeviceError::from_bind)
|
||||
}
|
||||
}
|
||||
|
@ -184,9 +180,10 @@ impl<B: hal::Backend> MemoryBlock<B> {
|
|||
size: Option<wgt::BufferAddress>,
|
||||
) -> Result<(), DeviceError> {
|
||||
let segment = self.segment(inner_offset, size);
|
||||
let mem = self.0.memory();
|
||||
unsafe {
|
||||
device
|
||||
.flush_mapped_memory_ranges(iter::once((&**self.0.memory(), segment)))
|
||||
.flush_mapped_memory_ranges(iter::once((mem, segment)))
|
||||
.or(Err(DeviceError::OutOfMemory))
|
||||
}
|
||||
}
|
||||
|
@ -198,40 +195,39 @@ impl<B: hal::Backend> MemoryBlock<B> {
|
|||
size: Option<wgt::BufferAddress>,
|
||||
) -> Result<(), DeviceError> {
|
||||
let segment = self.segment(inner_offset, size);
|
||||
let mem = self.0.memory();
|
||||
unsafe {
|
||||
device
|
||||
.invalidate_mapped_memory_ranges(iter::once((&**self.0.memory(), segment)))
|
||||
.invalidate_mapped_memory_ranges(iter::once((mem, segment)))
|
||||
.or(Err(DeviceError::OutOfMemory))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> gpu_alloc::MemoryDevice<Arc<B::Memory>> for MemoryDevice<'_, B> {
|
||||
impl<B: hal::Backend> gpu_alloc::MemoryDevice<B::Memory> for MemoryDevice<'_, B> {
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn allocate_memory(
|
||||
&self,
|
||||
size: u64,
|
||||
memory_type: u32,
|
||||
flags: gpu_alloc::AllocationFlags,
|
||||
) -> Result<Arc<B::Memory>, gpu_alloc::OutOfMemory> {
|
||||
) -> Result<B::Memory, gpu_alloc::OutOfMemory> {
|
||||
assert!(flags.is_empty());
|
||||
|
||||
self.0
|
||||
.allocate_memory(hal::MemoryTypeId(memory_type as _), size)
|
||||
.map(Arc::new)
|
||||
.map_err(|_| gpu_alloc::OutOfMemory::OutOfDeviceMemory)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn deallocate_memory(&self, memory: Arc<B::Memory>) {
|
||||
let memory = Arc::try_unwrap(memory).expect("Memory must not be used anywhere");
|
||||
unsafe fn deallocate_memory(&self, memory: B::Memory) {
|
||||
self.0.free_memory(memory);
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn map_memory(
|
||||
&self,
|
||||
memory: &Arc<B::Memory>,
|
||||
memory: &mut B::Memory,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
) -> Result<NonNull<u8>, gpu_alloc::DeviceMapError> {
|
||||
|
@ -252,22 +248,22 @@ impl<B: hal::Backend> gpu_alloc::MemoryDevice<Arc<B::Memory>> for MemoryDevice<'
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn unmap_memory(&self, memory: &Arc<B::Memory>) {
|
||||
unsafe fn unmap_memory(&self, memory: &mut B::Memory) {
|
||||
self.0.unmap_memory(memory);
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn invalidate_memory_ranges(
|
||||
&self,
|
||||
ranges: &[gpu_alloc::MappedMemoryRange<'_, Arc<B::Memory>>],
|
||||
ranges: &[gpu_alloc::MappedMemoryRange<'_, B::Memory>],
|
||||
) -> Result<(), gpu_alloc::OutOfMemory> {
|
||||
self.0
|
||||
.invalidate_mapped_memory_ranges(ranges.iter().map(|range| {
|
||||
.invalidate_mapped_memory_ranges(ranges.iter().map(|r| {
|
||||
(
|
||||
&**range.memory,
|
||||
r.memory,
|
||||
hal::memory::Segment {
|
||||
offset: range.offset,
|
||||
size: Some(range.size),
|
||||
offset: r.offset,
|
||||
size: Some(r.size),
|
||||
},
|
||||
)
|
||||
}))
|
||||
|
@ -277,15 +273,15 @@ impl<B: hal::Backend> gpu_alloc::MemoryDevice<Arc<B::Memory>> for MemoryDevice<'
|
|||
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
|
||||
unsafe fn flush_memory_ranges(
|
||||
&self,
|
||||
ranges: &[gpu_alloc::MappedMemoryRange<'_, Arc<B::Memory>>],
|
||||
ranges: &[gpu_alloc::MappedMemoryRange<'_, B::Memory>],
|
||||
) -> Result<(), gpu_alloc::OutOfMemory> {
|
||||
self.0
|
||||
.flush_mapped_memory_ranges(ranges.iter().map(|range| {
|
||||
.flush_mapped_memory_ranges(ranges.iter().map(|r| {
|
||||
(
|
||||
&**range.memory,
|
||||
r.memory,
|
||||
hal::memory::Segment {
|
||||
offset: range.offset,
|
||||
size: Some(range.size),
|
||||
offset: r.offset,
|
||||
size: Some(r.size),
|
||||
},
|
||||
)
|
||||
}))
|
||||
|
|
|
@ -17,7 +17,7 @@ struct DescriptorDevice<'a, B: hal::Backend>(&'a B::Device);
|
|||
|
||||
impl<B: hal::Backend> DescriptorAllocator<B> {
|
||||
pub fn new() -> Self {
|
||||
DescriptorAllocator(unsafe { gpu_descriptor::DescriptorAllocator::new(0) })
|
||||
DescriptorAllocator(gpu_descriptor::DescriptorAllocator::new(0))
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
|
@ -27,18 +27,19 @@ impl<B: hal::Backend> DescriptorAllocator<B> {
|
|||
layout_descriptor_count: &DescriptorTotalCount,
|
||||
count: u32,
|
||||
) -> Result<Vec<DescriptorSet<B>>, DeviceError> {
|
||||
self.0
|
||||
.allocate(
|
||||
unsafe {
|
||||
self.0.allocate(
|
||||
&DescriptorDevice::<B>(device),
|
||||
layout,
|
||||
gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(),
|
||||
layout_descriptor_count,
|
||||
count,
|
||||
)
|
||||
.map_err(|err| {
|
||||
tracing::warn!("Descriptor set allocation failed: {}", err);
|
||||
DeviceError::OutOfMemory
|
||||
})
|
||||
}
|
||||
.map_err(|err| {
|
||||
tracing::warn!("Descriptor set allocation failed: {}", err);
|
||||
DeviceError::OutOfMemory
|
||||
})
|
||||
}
|
||||
|
||||
pub fn free(&mut self, device: &B::Device, sets: impl IntoIterator<Item = DescriptorSet<B>>) {
|
||||
|
@ -46,7 +47,7 @@ impl<B: hal::Backend> DescriptorAllocator<B> {
|
|||
}
|
||||
|
||||
pub fn cleanup(&mut self, device: &B::Device) {
|
||||
self.0.cleanup(&DescriptorDevice::<B>(device))
|
||||
unsafe { self.0.cleanup(&DescriptorDevice::<B>(device)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,8 +121,8 @@ impl<B: hal::Backend>
|
|||
match hal::device::Device::create_descriptor_pool(
|
||||
self.0,
|
||||
max_sets as usize,
|
||||
ranges,
|
||||
hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits() as u32),
|
||||
ranges.into_iter(),
|
||||
hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits()),
|
||||
) {
|
||||
Ok(pool) => Ok(pool),
|
||||
Err(hal::device::OutOfMemory::Host) => {
|
||||
|
@ -140,7 +141,7 @@ impl<B: hal::Backend>
|
|||
unsafe fn alloc_descriptor_sets<'a>(
|
||||
&self,
|
||||
pool: &mut B::DescriptorPool,
|
||||
layouts: impl Iterator<Item = &'a B::DescriptorSetLayout>,
|
||||
layouts: impl ExactSizeIterator<Item = &'a B::DescriptorSetLayout>,
|
||||
sets: &mut impl Extend<B::DescriptorSet>,
|
||||
) -> Result<(), gpu_descriptor::DeviceAllocationError> {
|
||||
use gpu_descriptor::DeviceAllocationError as Dae;
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token},
|
||||
id, resource,
|
||||
track::TrackerSet,
|
||||
FastHashMap, RefCount, Stored, SubmissionIndex,
|
||||
RefCount, Stored, SubmissionIndex,
|
||||
};
|
||||
|
||||
use copyless::VecHelper as _;
|
||||
|
@ -28,7 +28,7 @@ const CLEANUP_WAIT_MS: u64 = 5000;
|
|||
|
||||
/// A struct that keeps lists of resources that are no longer needed by the user.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SuspectedResources {
|
||||
pub(super) struct SuspectedResources {
|
||||
pub(crate) buffers: Vec<id::Valid<id::BufferId>>,
|
||||
pub(crate) textures: Vec<id::Valid<id::TextureId>>,
|
||||
pub(crate) texture_views: Vec<id::Valid<id::TextureViewId>>,
|
||||
|
@ -39,10 +39,11 @@ pub struct SuspectedResources {
|
|||
pub(crate) bind_group_layouts: Vec<id::Valid<id::BindGroupLayoutId>>,
|
||||
pub(crate) pipeline_layouts: Vec<Stored<id::PipelineLayoutId>>,
|
||||
pub(crate) render_bundles: Vec<id::Valid<id::RenderBundleId>>,
|
||||
pub(crate) query_sets: Vec<id::Valid<id::QuerySetId>>,
|
||||
}
|
||||
|
||||
impl SuspectedResources {
|
||||
pub(crate) fn clear(&mut self) {
|
||||
pub(super) fn clear(&mut self) {
|
||||
self.buffers.clear();
|
||||
self.textures.clear();
|
||||
self.texture_views.clear();
|
||||
|
@ -53,9 +54,10 @@ impl SuspectedResources {
|
|||
self.bind_group_layouts.clear();
|
||||
self.pipeline_layouts.clear();
|
||||
self.render_bundles.clear();
|
||||
self.query_sets.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&mut self, other: &Self) {
|
||||
pub(super) fn extend(&mut self, other: &Self) {
|
||||
self.buffers.extend_from_slice(&other.buffers);
|
||||
self.textures.extend_from_slice(&other.textures);
|
||||
self.texture_views.extend_from_slice(&other.texture_views);
|
||||
|
@ -70,9 +72,10 @@ impl SuspectedResources {
|
|||
self.pipeline_layouts
|
||||
.extend_from_slice(&other.pipeline_layouts);
|
||||
self.render_bundles.extend_from_slice(&other.render_bundles);
|
||||
self.query_sets.extend_from_slice(&other.query_sets);
|
||||
}
|
||||
|
||||
pub(crate) fn add_trackers(&mut self, trackers: &TrackerSet) {
|
||||
pub(super) fn add_trackers(&mut self, trackers: &TrackerSet) {
|
||||
self.buffers.extend(trackers.buffers.used());
|
||||
self.textures.extend(trackers.textures.used());
|
||||
self.texture_views.extend(trackers.views.used());
|
||||
|
@ -81,6 +84,7 @@ impl SuspectedResources {
|
|||
self.compute_pipelines.extend(trackers.compute_pipes.used());
|
||||
self.render_pipelines.extend(trackers.render_pipes.used());
|
||||
self.render_bundles.extend(trackers.bundles.used());
|
||||
self.query_sets.extend(trackers.query_sets.used());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +103,7 @@ struct NonReferencedResources<B: hal::Backend> {
|
|||
graphics_pipes: Vec<B::GraphicsPipeline>,
|
||||
descriptor_set_layouts: Vec<B::DescriptorSetLayout>,
|
||||
pipeline_layouts: Vec<B::PipelineLayout>,
|
||||
query_sets: Vec<B::QueryPool>,
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> NonReferencedResources<B> {
|
||||
|
@ -114,6 +119,7 @@ impl<B: hal::Backend> NonReferencedResources<B> {
|
|||
graphics_pipes: Vec::new(),
|
||||
descriptor_set_layouts: Vec::new(),
|
||||
pipeline_layouts: Vec::new(),
|
||||
query_sets: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +132,7 @@ impl<B: hal::Backend> NonReferencedResources<B> {
|
|||
self.desc_sets.extend(other.desc_sets);
|
||||
self.compute_pipes.extend(other.compute_pipes);
|
||||
self.graphics_pipes.extend(other.graphics_pipes);
|
||||
self.query_sets.extend(other.query_sets);
|
||||
assert!(other.descriptor_set_layouts.is_empty());
|
||||
assert!(other.pipeline_layouts.is_empty());
|
||||
}
|
||||
|
@ -178,6 +185,9 @@ impl<B: hal::Backend> NonReferencedResources<B> {
|
|||
for raw in self.pipeline_layouts.drain(..) {
|
||||
device.destroy_pipeline_layout(raw);
|
||||
}
|
||||
for raw in self.query_sets.drain(..) {
|
||||
device.destroy_query_pool(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +216,7 @@ pub enum WaitIdleError {
|
|||
/// 3. When `ActiveSubmission` is retired, the mapped buffers associated with it are moved to `ready_to_map` vector.
|
||||
/// 4. Finally, `handle_mapping` issues all the callbacks.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LifetimeTracker<B: hal::Backend> {
|
||||
pub(super) struct LifetimeTracker<B: hal::Backend> {
|
||||
/// Resources that the user has requested be mapped, but are still in use.
|
||||
mapped: Vec<Stored<id::BufferId>>,
|
||||
/// Buffers can be used in a submission that is yet to be made, by the
|
||||
|
@ -316,10 +326,9 @@ impl<B: hal::Backend> LifetimeTracker<B> {
|
|||
.iter()
|
||||
.position(|a| unsafe { !device.get_fence_status(&a.fence).unwrap_or(false) })
|
||||
.unwrap_or_else(|| self.active.len());
|
||||
let last_done = if done_count != 0 {
|
||||
self.active[done_count - 1].index
|
||||
} else {
|
||||
return Ok(0);
|
||||
let last_done = match done_count.checked_sub(1) {
|
||||
Some(i) => self.active[i].index,
|
||||
None => return Ok(0),
|
||||
};
|
||||
|
||||
for a in self.active.drain(..done_count) {
|
||||
|
@ -366,7 +375,7 @@ impl<B: hal::Backend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
impl<B: GfxBackend> LifetimeTracker<B> {
|
||||
pub(crate) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
|
||||
pub(super) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
|
||||
&mut self,
|
||||
hub: &Hub<B, G>,
|
||||
trackers: &Mutex<TrackerSet>,
|
||||
|
@ -374,8 +383,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
token: &mut Token<super::Device<B>>,
|
||||
) {
|
||||
if !self.suspected_resources.render_bundles.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.render_bundles.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
while let Some(id) = self.suspected_resources.render_bundles.pop() {
|
||||
if trackers.bundles.remove_abandoned(id) {
|
||||
|
@ -390,8 +399,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.bind_groups.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.bind_groups.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
while let Some(id) = self.suspected_resources.bind_groups.pop() {
|
||||
if trackers.bind_groups.remove_abandoned(id) {
|
||||
|
@ -414,8 +423,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.texture_views.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.texture_views.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.texture_views.drain(..) {
|
||||
if trackers.views.remove_abandoned(id) {
|
||||
|
@ -444,8 +453,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.textures.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.textures.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.textures.drain(..) {
|
||||
if trackers.textures.remove_abandoned(id) {
|
||||
|
@ -466,8 +475,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.samplers.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.samplers.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.samplers.drain(..) {
|
||||
if trackers.samplers.remove_abandoned(id) {
|
||||
|
@ -488,8 +497,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.buffers.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.buffers.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.buffers.drain(..) {
|
||||
if trackers.buffers.remove_abandoned(id) {
|
||||
|
@ -499,6 +508,16 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
|
||||
if let Some(res) = hub.buffers.unregister_locked(id.0, &mut *guard) {
|
||||
let submit_index = res.life_guard.submission_index.load(Ordering::Acquire);
|
||||
if let resource::BufferMapState::Init {
|
||||
stage_buffer,
|
||||
stage_memory,
|
||||
..
|
||||
} = res.map_state
|
||||
{
|
||||
self.free_resources
|
||||
.buffers
|
||||
.push((stage_buffer, stage_memory));
|
||||
}
|
||||
self.active
|
||||
.iter_mut()
|
||||
.find(|a| a.index == submit_index)
|
||||
|
@ -511,8 +530,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.compute_pipelines.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.compute_pipelines.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.compute_pipelines.drain(..) {
|
||||
if trackers.compute_pipes.remove_abandoned(id) {
|
||||
|
@ -533,8 +552,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
|
||||
if !self.suspected_resources.render_pipelines.is_empty() {
|
||||
let mut trackers = trackers.lock();
|
||||
let (mut guard, _) = hub.render_pipelines.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.render_pipelines.drain(..) {
|
||||
if trackers.render_pipes.remove_abandoned(id) {
|
||||
|
@ -594,9 +613,30 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.suspected_resources.query_sets.is_empty() {
|
||||
let (mut guard, _) = hub.query_sets.write(token);
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.query_sets.drain(..) {
|
||||
if trackers.query_sets.remove_abandoned(id) {
|
||||
// #[cfg(feature = "trace")]
|
||||
// trace.map(|t| t.lock().add(trace::Action::DestroyComputePipeline(id.0)));
|
||||
if let Some(res) = hub.query_sets.unregister_locked(id.0, &mut *guard) {
|
||||
let submit_index = res.life_guard.submission_index.load(Ordering::Acquire);
|
||||
self.active
|
||||
.iter_mut()
|
||||
.find(|a| a.index == submit_index)
|
||||
.map_or(&mut self.free_resources, |a| &mut a.last_resources)
|
||||
.query_sets
|
||||
.push(res.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn triage_mapped<G: GlobalIdentityHandlerFactory>(
|
||||
pub(super) fn triage_mapped<G: GlobalIdentityHandlerFactory>(
|
||||
&mut self,
|
||||
hub: &Hub<B, G>,
|
||||
token: &mut Token<super::Device<B>>,
|
||||
|
@ -626,75 +666,7 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn triage_framebuffers<G: GlobalIdentityHandlerFactory>(
|
||||
&mut self,
|
||||
hub: &Hub<B, G>,
|
||||
framebuffers: &mut FastHashMap<super::FramebufferKey, B::Framebuffer>,
|
||||
token: &mut Token<super::Device<B>>,
|
||||
) {
|
||||
let (texture_view_guard, _) = hub.texture_views.read(token);
|
||||
let remove_list = framebuffers
|
||||
.keys()
|
||||
.filter_map(|key| {
|
||||
let mut last_submit = None;
|
||||
let mut needs_cleanup = false;
|
||||
|
||||
// A framebuffer needs to be scheduled for cleanup, if there's at least one
|
||||
// attachment is no longer valid.
|
||||
|
||||
for &at in key.all() {
|
||||
// If this attachment is still registered, it's still valid
|
||||
if texture_view_guard.contains(at.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This attachment is no longer registered, this framebuffer needs cleanup
|
||||
needs_cleanup = true;
|
||||
|
||||
// Check if there's any active submissions that are still referring to this
|
||||
// attachment, if there are we need to get the greatest submission index, as
|
||||
// that's the last time this attachment is still valid
|
||||
let mut attachment_last_submit = None;
|
||||
for a in &self.active {
|
||||
if a.last_resources.image_views.iter().any(|&(id, _)| id == at) {
|
||||
let max = attachment_last_submit.unwrap_or(0).max(a.index);
|
||||
attachment_last_submit = Some(max);
|
||||
}
|
||||
}
|
||||
|
||||
// Between all attachments, we need the smallest index, because that's the last
|
||||
// time this framebuffer is still valid
|
||||
if let Some(attachment_last_submit) = attachment_last_submit {
|
||||
let min = last_submit
|
||||
.unwrap_or(std::usize::MAX)
|
||||
.min(attachment_last_submit);
|
||||
last_submit = Some(min);
|
||||
}
|
||||
}
|
||||
|
||||
if needs_cleanup {
|
||||
Some((key.clone(), last_submit.unwrap_or(0)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<FastHashMap<_, _>>();
|
||||
|
||||
if !remove_list.is_empty() {
|
||||
tracing::debug!("Free framebuffers {:?}", remove_list);
|
||||
for (ref key, submit_index) in remove_list {
|
||||
let framebuffer = framebuffers.remove(key).unwrap();
|
||||
self.active
|
||||
.iter_mut()
|
||||
.find(|a| a.index == submit_index)
|
||||
.map_or(&mut self.free_resources, |a| &mut a.last_resources)
|
||||
.framebuffers
|
||||
.push(framebuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_mapping<G: GlobalIdentityHandlerFactory>(
|
||||
pub(super) fn handle_mapping<G: GlobalIdentityHandlerFactory>(
|
||||
&mut self,
|
||||
hub: &Hub<B, G>,
|
||||
raw: &B::Device,
|
||||
|
@ -726,6 +698,14 @@ impl<B: GfxBackend> LifetimeTracker<B> {
|
|||
resource::BufferMapState::Idle,
|
||||
) {
|
||||
resource::BufferMapState::Waiting(pending_mapping) => pending_mapping,
|
||||
// Mapping cancelled
|
||||
resource::BufferMapState::Idle => continue,
|
||||
// Mapping queued at least twice by map -> unmap -> map
|
||||
// and was already successfully mapped below
|
||||
active @ resource::BufferMapState::Active { .. } => {
|
||||
buffer.map_state = active;
|
||||
continue;
|
||||
}
|
||||
_ => panic!("No pending mapping."),
|
||||
};
|
||||
let status = if mapping.range.start != mapping.range.end {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -13,13 +13,14 @@ use crate::{
|
|||
device::{alloc, DeviceError, WaitIdleError},
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token},
|
||||
id,
|
||||
memory_init_tracker::MemoryInitKind,
|
||||
resource::{BufferAccessError, BufferMapState, BufferUse, TextureUse},
|
||||
span, FastHashSet,
|
||||
span, FastHashMap, FastHashSet,
|
||||
};
|
||||
|
||||
use hal::{command::CommandBuffer as _, device::Device as _, queue::CommandQueue as _};
|
||||
use hal::{command::CommandBuffer as _, device::Device as _, queue::Queue as _};
|
||||
use smallvec::SmallVec;
|
||||
use std::{iter, ptr};
|
||||
use std::{iter, ops::Range, ptr};
|
||||
use thiserror::Error;
|
||||
|
||||
struct StagingData<B: hal::Backend> {
|
||||
|
@ -147,6 +148,10 @@ impl<B: hal::Backend> super::Device<B> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[error("queue is invalid")]
|
||||
pub struct InvalidQueue;
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum QueueWriteError {
|
||||
#[error(transparent)]
|
||||
|
@ -271,6 +276,16 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
device.pending_writes.consume(stage);
|
||||
device.pending_writes.dst_buffers.insert(buffer_id);
|
||||
|
||||
// Ensure the overwritten bytes are marked as initialized so they don't need to be nulled prior to mapping or binding.
|
||||
{
|
||||
drop(buffer_guard);
|
||||
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
|
||||
|
||||
let dst = buffer_guard.get_mut(buffer_id).unwrap();
|
||||
dst.initialization_status
|
||||
.clear(buffer_offset..(buffer_offset + data_size));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -306,7 +321,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
});
|
||||
}
|
||||
|
||||
if size.width == 0 || size.height == 0 || size.depth == 0 {
|
||||
if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
|
||||
tracing::trace!("Ignoring write_texture of size 0");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -324,7 +339,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
bytes_per_block as wgt::BufferAddress,
|
||||
size,
|
||||
)?;
|
||||
let (block_width, block_height) = conv::texture_block_size(texture_format);
|
||||
|
||||
let (block_width, block_height) = texture_format.describe().block_dimensions;
|
||||
let block_width = block_width as u32;
|
||||
let block_height = block_height as u32;
|
||||
|
||||
if !conv::is_valid_copy_dst_texture_format(texture_format) {
|
||||
Err(TransferError::CopyToForbiddenTextureFormat(texture_format))?
|
||||
}
|
||||
|
@ -340,7 +359,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
);
|
||||
let stage_bytes_per_row = align_to(bytes_per_block * width_blocks, bytes_per_row_alignment);
|
||||
|
||||
let block_rows_in_copy = (size.depth - 1) * block_rows_per_image + height_blocks;
|
||||
let block_rows_in_copy =
|
||||
(size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
|
||||
let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
|
||||
let mut stage = device.prepare_stage(stage_size)?;
|
||||
|
||||
|
@ -384,7 +404,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
// Copy row by row into the optimal alignment.
|
||||
let copy_bytes_per_row =
|
||||
stage_bytes_per_row.min(data_layout.bytes_per_row) as usize;
|
||||
for layer in 0..size.depth {
|
||||
for layer in 0..size.depth_or_array_layers {
|
||||
let rows_offset = layer * block_rows_per_image;
|
||||
for row in 0..height_blocks {
|
||||
ptr::copy_nonoverlapping(
|
||||
|
@ -405,13 +425,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
stage.memory.flush_range(&device.raw, 0, None)?;
|
||||
}
|
||||
|
||||
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
|
||||
// the virtual size. We have passed validation, so it's safe to use the
|
||||
// image extent data directly. We want the provided copy size to be no larger than
|
||||
// the virtual size.
|
||||
let max_image_extent = dst.kind.level_extent(destination.mip_level as _);
|
||||
let image_extent = wgt::Extent3d {
|
||||
width: size.width.min(max_image_extent.width),
|
||||
height: size.height.min(max_image_extent.height),
|
||||
depth_or_array_layers: size.depth_or_array_layers,
|
||||
};
|
||||
|
||||
let region = hal::command::BufferImageCopy {
|
||||
buffer_offset: 0,
|
||||
buffer_width: (stage_bytes_per_row / bytes_per_block) * block_width,
|
||||
buffer_height: texel_rows_per_image,
|
||||
image_layers,
|
||||
image_offset,
|
||||
image_extent: conv::map_extent(size, dst.dimension),
|
||||
image_extent: conv::map_extent(&image_extent, dst.dimension),
|
||||
};
|
||||
unsafe {
|
||||
stage.cmdbuf.pipeline_barrier(
|
||||
|
@ -443,6 +474,135 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// Enacts all zero initializations required by the given command buffers
|
||||
// Required commands are appended to device.pending_writes
|
||||
fn initialize_used_uninitialized_memory<B: GfxBackend>(
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
command_buffer_ids: &[id::CommandBufferId],
|
||||
) -> Result<(), QueueSubmitError> {
|
||||
if command_buffer_ids.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
let mut required_buffer_inits = {
|
||||
let (command_buffer_guard, mut token) = hub.command_buffers.read(&mut token);
|
||||
|
||||
let mut required_buffer_inits: FastHashMap<
|
||||
id::BufferId,
|
||||
Vec<Range<wgt::BufferAddress>>,
|
||||
> = FastHashMap::default();
|
||||
|
||||
for &cmb_id in command_buffer_ids {
|
||||
let cmdbuf = command_buffer_guard
|
||||
.get(cmb_id)
|
||||
.map_err(|_| QueueSubmitError::InvalidCommandBuffer(cmb_id))?;
|
||||
|
||||
if cmdbuf.buffer_memory_init_actions.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
|
||||
|
||||
for buffer_use in cmdbuf.buffer_memory_init_actions.iter() {
|
||||
let buffer = buffer_guard
|
||||
.get_mut(buffer_use.id)
|
||||
.map_err(|_| QueueSubmitError::DestroyedBuffer(buffer_use.id))?;
|
||||
|
||||
let uninitialized_ranges =
|
||||
buffer.initialization_status.drain(buffer_use.range.clone());
|
||||
match buffer_use.kind {
|
||||
MemoryInitKind::ImplicitlyInitialized => {
|
||||
uninitialized_ranges.for_each(drop);
|
||||
}
|
||||
MemoryInitKind::NeedsInitializedMemory => {
|
||||
required_buffer_inits
|
||||
.entry(buffer_use.id)
|
||||
.or_default()
|
||||
.extend(uninitialized_ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
required_buffer_inits
|
||||
};
|
||||
|
||||
// Memory init is expected to be rare (means user relies on default zero!), so most of the time we early here!
|
||||
if required_buffer_inits.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (mut device_guard, mut token) = hub.devices.write(&mut token);
|
||||
let (buffer_guard, _) = hub.buffers.read(&mut token);
|
||||
let device = device_guard
|
||||
.get_mut(queue_id)
|
||||
.map_err(|_| DeviceError::Invalid)?;
|
||||
|
||||
device
|
||||
.pending_writes
|
||||
.dst_buffers
|
||||
.extend(required_buffer_inits.keys());
|
||||
device.borrow_pending_writes(); // Call ensures there is a pending_writes cmdbuffer, but using the reference returned would make the borrow checker unhappy!
|
||||
let pending_writes_cmd_buf = device.pending_writes.command_buffer.as_mut().unwrap();
|
||||
let mut trackers = device.trackers.lock();
|
||||
|
||||
for (buffer_id, mut ranges) in required_buffer_inits.drain() {
|
||||
// Collapse touching ranges. We can't do this any earlier since we only now gathered ranges from several different command buffers!
|
||||
ranges.sort_by(|a, b| a.start.cmp(&b.start));
|
||||
for i in (1..ranges.len()).rev() {
|
||||
assert!(ranges[i - 1].end <= ranges[i].start); // The memory init tracker made sure of this!
|
||||
if ranges[i].start == ranges[i - 1].end {
|
||||
ranges[i - 1].end = ranges[i].end;
|
||||
ranges.swap_remove(i); // Ordering not important at this point
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do use_replace since the buffer may already no longer have a ref_count.
|
||||
// However, we *know* that it is currently in use, so the tracker must already know about it.
|
||||
let transition = trackers.buffers.change_replace_tracked(
|
||||
id::Valid(buffer_id),
|
||||
(),
|
||||
BufferUse::COPY_DST,
|
||||
);
|
||||
let buffer = buffer_guard
|
||||
.get(buffer_id)
|
||||
.map_err(|_| QueueSubmitError::DestroyedBuffer(buffer_id))?;
|
||||
let &(ref buffer_raw, _) = buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
.ok_or(QueueSubmitError::DestroyedBuffer(buffer_id))?;
|
||||
unsafe {
|
||||
pending_writes_cmd_buf.pipeline_barrier(
|
||||
super::all_buffer_stages()..hal::pso::PipelineStage::TRANSFER,
|
||||
hal::memory::Dependencies::empty(),
|
||||
transition.map(|pending| pending.into_hal(buffer)),
|
||||
);
|
||||
}
|
||||
for range in ranges {
|
||||
let size = range.end - range.start;
|
||||
|
||||
assert!(range.start % 4 == 0, "Buffer {:?} has an uninitialized range with a start not aligned to 4 (start was {})", buffer, range.start);
|
||||
assert!(size % 4 == 0, "Buffer {:?} has an uninitialized range with a size not aligned to 4 (size was {})", buffer, size);
|
||||
|
||||
unsafe {
|
||||
pending_writes_cmd_buf.fill_buffer(
|
||||
buffer_raw,
|
||||
hal::buffer::SubRange {
|
||||
offset: range.start,
|
||||
size: Some(size),
|
||||
},
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn queue_submit<B: GfxBackend>(
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
|
@ -450,10 +610,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
) -> Result<(), QueueSubmitError> {
|
||||
span!(_guard, INFO, "Queue::submit");
|
||||
|
||||
self.initialize_used_uninitialized_memory::<B>(queue_id, command_buffer_ids)?;
|
||||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
let callbacks = {
|
||||
let mut token = Token::root();
|
||||
let (mut device_guard, mut token) = hub.devices.write(&mut token);
|
||||
let device = device_guard
|
||||
.get_mut(queue_id)
|
||||
|
@ -468,12 +630,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
|
||||
let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
|
||||
{
|
||||
if !command_buffer_ids.is_empty() {
|
||||
let (render_bundle_guard, mut token) = hub.render_bundles.read(&mut token);
|
||||
let (_, mut token) = hub.pipeline_layouts.read(&mut token);
|
||||
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
|
||||
let (compute_pipe_guard, mut token) = hub.compute_pipelines.read(&mut token);
|
||||
let (render_pipe_guard, mut token) = hub.render_pipelines.read(&mut token);
|
||||
let (mut buffer_guard, mut token) = hub.buffers.write(&mut token);
|
||||
let (texture_guard, mut token) = hub.textures.read(&mut token);
|
||||
let (texture_guard, mut token) = hub.textures.write(&mut token);
|
||||
let (texture_view_guard, mut token) = hub.texture_views.read(&mut token);
|
||||
let (sampler_guard, _) = hub.samplers.read(&mut token);
|
||||
|
||||
|
@ -497,19 +661,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
));
|
||||
}
|
||||
|
||||
if let Some((sc_id, fbo)) = cmdbuf.used_swap_chain.take() {
|
||||
for sc_id in cmdbuf.used_swap_chains.drain(..) {
|
||||
let sc = &mut swap_chain_guard[sc_id.value];
|
||||
sc.active_submission_index = submit_index;
|
||||
if sc.acquired_view_id.is_none() {
|
||||
return Err(QueueSubmitError::SwapChainOutputDropped);
|
||||
}
|
||||
// For each swapchain, we only want to have at most 1 signaled semaphore.
|
||||
if sc.acquired_framebuffers.is_empty() {
|
||||
if sc.active_submission_index != submit_index {
|
||||
sc.active_submission_index = submit_index;
|
||||
// Only add a signal if this is the first time for this swapchain
|
||||
// to be used in the submission.
|
||||
signal_swapchain_semaphores.push(sc_id.value);
|
||||
}
|
||||
sc.acquired_framebuffers.push(fbo);
|
||||
}
|
||||
|
||||
// optimize the tracked states
|
||||
|
@ -568,6 +730,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
device.temp_suspected.render_pipelines.push(id);
|
||||
}
|
||||
}
|
||||
for id in cmdbuf.trackers.bundles.used() {
|
||||
if !render_bundle_guard[id].life_guard.use_at(submit_index) {
|
||||
device.temp_suspected.render_bundles.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// execute resource transitions
|
||||
let mut transit = device.cmd_allocator.extend(cmdbuf);
|
||||
|
@ -595,24 +762,26 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
|
||||
// now prepare the GPU submission
|
||||
let fence = device
|
||||
let mut fence = device
|
||||
.raw
|
||||
.create_fence(false)
|
||||
.or(Err(DeviceError::OutOfMemory))?;
|
||||
let submission = hal::queue::Submission {
|
||||
command_buffers: pending_write_command_buffer.as_ref().into_iter().chain(
|
||||
command_buffer_ids
|
||||
.iter()
|
||||
.flat_map(|&cmb_id| &command_buffer_guard.get(cmb_id).unwrap().raw),
|
||||
),
|
||||
wait_semaphores: Vec::new(),
|
||||
signal_semaphores: signal_swapchain_semaphores
|
||||
.into_iter()
|
||||
.map(|sc_id| &swap_chain_guard[sc_id].semaphore),
|
||||
};
|
||||
let command_buffers = pending_write_command_buffer.as_ref().into_iter().chain(
|
||||
command_buffer_ids.iter().flat_map(|&cmd_buf_id| {
|
||||
command_buffer_guard.get(cmd_buf_id).unwrap().raw.iter()
|
||||
}),
|
||||
);
|
||||
let signal_semaphores = signal_swapchain_semaphores
|
||||
.into_iter()
|
||||
.map(|sc_id| &swap_chain_guard[sc_id].semaphore);
|
||||
|
||||
unsafe {
|
||||
device.queue_group.queues[0].submit(submission, Some(&fence));
|
||||
device.queue_group.queues[0].submit(
|
||||
command_buffers,
|
||||
iter::empty(),
|
||||
signal_semaphores,
|
||||
Some(&mut fence),
|
||||
);
|
||||
}
|
||||
fence
|
||||
};
|
||||
|
@ -638,17 +807,36 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
// finally, return the command buffers to the allocator
|
||||
for &cmb_id in command_buffer_ids {
|
||||
if let (Some(cmd_buf), _) = hub.command_buffers.unregister(cmb_id, &mut token) {
|
||||
device.cmd_allocator.after_submit(cmd_buf, submit_index);
|
||||
device
|
||||
.cmd_allocator
|
||||
.after_submit(cmd_buf, &device.raw, submit_index);
|
||||
}
|
||||
}
|
||||
|
||||
callbacks
|
||||
};
|
||||
|
||||
// the map callbacks should execute with nothing locked!
|
||||
drop(token);
|
||||
super::fire_map_callbacks(callbacks);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn queue_get_timestamp_period<B: GfxBackend>(
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
) -> Result<f32, InvalidQueue> {
|
||||
span!(_guard, INFO, "Queue::get_timestamp_period");
|
||||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (device_guard, _) = hub.devices.read(&mut token);
|
||||
match device_guard.get(queue_id) {
|
||||
Ok(device) => Ok(device.queue_group.queues[0].timestamp_period()),
|
||||
Err(_) => Err(InvalidQueue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_lowest_common_denom(a: u32, b: u32) -> u32 {
|
||||
|
|
|
@ -15,11 +15,11 @@ pub const FILE_NAME: &str = "trace.ron";
|
|||
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) fn new_render_bundle_encoder_descriptor<'a>(
|
||||
label: Option<&'a str>,
|
||||
label: crate::Label<'a>,
|
||||
context: &'a super::RenderPassContext,
|
||||
) -> crate::command::RenderBundleEncoderDescriptor<'a> {
|
||||
crate::command::RenderBundleEncoderDescriptor {
|
||||
label: label.map(Cow::Borrowed),
|
||||
label,
|
||||
color_formats: Cow::Borrowed(&context.attachments.colors),
|
||||
depth_stencil_format: context.attachments.depth_stencil,
|
||||
sample_count: context.sample_count as u32,
|
||||
|
@ -50,7 +50,7 @@ pub enum Action<'a> {
|
|||
DestroySampler(id::SamplerId),
|
||||
CreateSwapChain(id::SwapChainId, wgt::SwapChainDescriptor),
|
||||
GetSwapChainTexture {
|
||||
id: Option<id::TextureViewId>,
|
||||
id: id::TextureViewId,
|
||||
parent_id: id::SwapChainId,
|
||||
},
|
||||
PresentSwapChain(id::SwapChainId),
|
||||
|
@ -71,19 +71,23 @@ pub enum Action<'a> {
|
|||
DestroyBindGroup(id::BindGroupId),
|
||||
CreateShaderModule {
|
||||
id: id::ShaderModuleId,
|
||||
label: crate::Label<'a>,
|
||||
desc: crate::pipeline::ShaderModuleDescriptor<'a>,
|
||||
data: FileName,
|
||||
},
|
||||
DestroyShaderModule(id::ShaderModuleId),
|
||||
CreateComputePipeline(
|
||||
id::ComputePipelineId,
|
||||
crate::pipeline::ComputePipelineDescriptor<'a>,
|
||||
),
|
||||
CreateComputePipeline {
|
||||
id: id::ComputePipelineId,
|
||||
desc: crate::pipeline::ComputePipelineDescriptor<'a>,
|
||||
#[cfg_attr(feature = "replay", serde(default))]
|
||||
implicit_context: Option<super::ImplicitPipelineContext>,
|
||||
},
|
||||
DestroyComputePipeline(id::ComputePipelineId),
|
||||
CreateRenderPipeline(
|
||||
id::RenderPipelineId,
|
||||
crate::pipeline::RenderPipelineDescriptor<'a>,
|
||||
),
|
||||
CreateRenderPipeline {
|
||||
id: id::RenderPipelineId,
|
||||
desc: crate::pipeline::RenderPipelineDescriptor<'a>,
|
||||
#[cfg_attr(feature = "replay", serde(default))]
|
||||
implicit_context: Option<super::ImplicitPipelineContext>,
|
||||
},
|
||||
DestroyRenderPipeline(id::RenderPipelineId),
|
||||
CreateRenderBundle {
|
||||
id: id::RenderBundleId,
|
||||
|
@ -91,6 +95,11 @@ pub enum Action<'a> {
|
|||
base: crate::command::BasePass<crate::command::RenderCommand>,
|
||||
},
|
||||
DestroyRenderBundle(id::RenderBundleId),
|
||||
CreateQuerySet {
|
||||
id: id::QuerySetId,
|
||||
desc: wgt::QuerySetDescriptor,
|
||||
},
|
||||
DestroyQuerySet(id::QuerySetId),
|
||||
WriteBuffer {
|
||||
id: id::BufferId,
|
||||
data: FileName,
|
||||
|
@ -132,6 +141,17 @@ pub enum Command {
|
|||
dst: crate::command::TextureCopyView,
|
||||
size: wgt::Extent3d,
|
||||
},
|
||||
WriteTimestamp {
|
||||
query_set_id: id::QuerySetId,
|
||||
query_index: u32,
|
||||
},
|
||||
ResolveQuerySet {
|
||||
query_set_id: id::QuerySetId,
|
||||
start_query: u32,
|
||||
query_count: u32,
|
||||
destination: id::BufferId,
|
||||
destination_offset: wgt::BufferAddress,
|
||||
},
|
||||
RunComputePass {
|
||||
base: crate::command::BasePass<crate::command::ComputeCommand>,
|
||||
},
|
||||
|
|
|
@ -23,6 +23,8 @@ use crate::{
|
|||
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use wgt::Backend;
|
||||
|
||||
use crate::id::QuerySetId;
|
||||
use crate::resource::QuerySet;
|
||||
#[cfg(debug_assertions)]
|
||||
use std::cell::Cell;
|
||||
use std::{fmt::Debug, marker::PhantomData, ops, thread};
|
||||
|
@ -264,6 +266,11 @@ impl<B: hal::Backend> Access<ComputePipeline<B>> for BindGroup<B> {}
|
|||
impl<B: hal::Backend> Access<RenderPipeline<B>> for Device<B> {}
|
||||
impl<B: hal::Backend> Access<RenderPipeline<B>> for BindGroup<B> {}
|
||||
impl<B: hal::Backend> Access<RenderPipeline<B>> for ComputePipeline<B> {}
|
||||
impl<B: hal::Backend> Access<QuerySet<B>> for Root {}
|
||||
impl<B: hal::Backend> Access<QuerySet<B>> for Device<B> {}
|
||||
impl<B: hal::Backend> Access<QuerySet<B>> for CommandBuffer<B> {}
|
||||
impl<B: hal::Backend> Access<QuerySet<B>> for RenderPipeline<B> {}
|
||||
impl<B: hal::Backend> Access<QuerySet<B>> for ComputePipeline<B> {}
|
||||
impl<B: hal::Backend> Access<ShaderModule<B>> for Device<B> {}
|
||||
impl<B: hal::Backend> Access<ShaderModule<B>> for BindGroupLayout<B> {}
|
||||
impl<B: hal::Backend> Access<Buffer<B>> for Root {}
|
||||
|
@ -273,6 +280,7 @@ impl<B: hal::Backend> Access<Buffer<B>> for BindGroup<B> {}
|
|||
impl<B: hal::Backend> Access<Buffer<B>> for CommandBuffer<B> {}
|
||||
impl<B: hal::Backend> Access<Buffer<B>> for ComputePipeline<B> {}
|
||||
impl<B: hal::Backend> Access<Buffer<B>> for RenderPipeline<B> {}
|
||||
impl<B: hal::Backend> Access<Buffer<B>> for QuerySet<B> {}
|
||||
impl<B: hal::Backend> Access<Texture<B>> for Root {}
|
||||
impl<B: hal::Backend> Access<Texture<B>> for Device<B> {}
|
||||
impl<B: hal::Backend> Access<Texture<B>> for Buffer<B> {}
|
||||
|
@ -294,7 +302,7 @@ thread_local! {
|
|||
///
|
||||
/// Note: there can only be one non-borrowed `Token` alive on a thread
|
||||
/// at a time, which is enforced by `ACTIVE_TOKEN`.
|
||||
pub struct Token<'a, T: 'a> {
|
||||
pub(crate) struct Token<'a, T: 'a> {
|
||||
level: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
|
@ -374,6 +382,7 @@ pub trait GlobalIdentityHandlerFactory:
|
|||
+ IdentityHandlerFactory<RenderBundleId>
|
||||
+ IdentityHandlerFactory<RenderPipelineId>
|
||||
+ IdentityHandlerFactory<ComputePipelineId>
|
||||
+ IdentityHandlerFactory<QuerySetId>
|
||||
+ IdentityHandlerFactory<BufferId>
|
||||
+ IdentityHandlerFactory<TextureId>
|
||||
+ IdentityHandlerFactory<TextureViewId>
|
||||
|
@ -430,60 +439,58 @@ impl<T: Resource, I: TypedId, F: IdentityHandlerFactory<I>> Registry<T, I, F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Resource, I: TypedId + Copy, F: IdentityHandlerFactory<I>> Registry<T, I, F> {
|
||||
pub fn register<A: Access<T>>(&self, id: I, value: T, _token: &mut Token<A>) {
|
||||
debug_assert_eq!(id.unzip().2, self.backend);
|
||||
self.data.write().insert(id, value);
|
||||
#[must_use]
|
||||
pub(crate) struct FutureId<'a, I: TypedId, T> {
|
||||
id: I,
|
||||
data: &'a RwLock<Storage<T, I>>,
|
||||
}
|
||||
|
||||
impl<I: TypedId + Copy, T> FutureId<'_, I, T> {
|
||||
#[cfg(feature = "trace")]
|
||||
pub fn id(&self) -> I {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn read<'a, A: Access<T>>(
|
||||
pub fn into_id(self) -> I {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn assign<'a, A: Access<T>>(self, value: T, _: &'a mut Token<A>) -> Valid<I> {
|
||||
self.data.write().insert(self.id, value);
|
||||
Valid(self.id)
|
||||
}
|
||||
|
||||
pub fn assign_error<'a, A: Access<T>>(self, label: &str, _: &'a mut Token<A>) -> I {
|
||||
self.data.write().insert_error(self.id, label);
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Resource, I: TypedId + Copy, F: IdentityHandlerFactory<I>> Registry<T, I, F> {
|
||||
pub(crate) fn prepare(
|
||||
&self,
|
||||
id_in: <F::Filter as IdentityHandler<I>>::Input,
|
||||
) -> FutureId<I, T> {
|
||||
FutureId {
|
||||
id: self.identity.process(id_in, self.backend),
|
||||
data: &self.data,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read<'a, A: Access<T>>(
|
||||
&'a self,
|
||||
_token: &'a mut Token<A>,
|
||||
) -> (RwLockReadGuard<'a, Storage<T, I>>, Token<'a, T>) {
|
||||
(self.data.read(), Token::new())
|
||||
}
|
||||
|
||||
pub fn write<'a, A: Access<T>>(
|
||||
pub(crate) fn write<'a, A: Access<T>>(
|
||||
&'a self,
|
||||
_token: &'a mut Token<A>,
|
||||
) -> (RwLockWriteGuard<'a, Storage<T, I>>, Token<'a, T>) {
|
||||
(self.data.write(), Token::new())
|
||||
}
|
||||
|
||||
pub(crate) fn register_identity<A: Access<T>>(
|
||||
&self,
|
||||
id_in: <F::Filter as IdentityHandler<I>>::Input,
|
||||
value: T,
|
||||
token: &mut Token<A>,
|
||||
) -> Valid<I> {
|
||||
let id = self.identity.process(id_in, self.backend);
|
||||
self.register(id, value, token);
|
||||
Valid(id)
|
||||
}
|
||||
|
||||
pub(crate) fn register_identity_locked(
|
||||
&self,
|
||||
id_in: <F::Filter as IdentityHandler<I>>::Input,
|
||||
value: T,
|
||||
guard: &mut Storage<T, I>,
|
||||
) -> Valid<I> {
|
||||
let id = self.identity.process(id_in, self.backend);
|
||||
guard.insert(id, value);
|
||||
Valid(id)
|
||||
}
|
||||
|
||||
pub fn register_error<A: Access<T>>(
|
||||
&self,
|
||||
id_in: <F::Filter as IdentityHandler<I>>::Input,
|
||||
label: &str,
|
||||
_token: &mut Token<A>,
|
||||
) -> I {
|
||||
let id = self.identity.process(id_in, self.backend);
|
||||
debug_assert_eq!(id.unzip().2, self.backend);
|
||||
self.data.write().insert_error(id, label);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn unregister_locked(&self, id: I, guard: &mut Storage<T, I>) -> Option<T> {
|
||||
let value = guard.remove(id);
|
||||
//Note: careful about the order here!
|
||||
|
@ -492,7 +499,7 @@ impl<T: Resource, I: TypedId + Copy, F: IdentityHandlerFactory<I>> Registry<T, I
|
|||
value
|
||||
}
|
||||
|
||||
pub fn unregister<'a, A: Access<T>>(
|
||||
pub(crate) fn unregister<'a, A: Access<T>>(
|
||||
&self,
|
||||
id: I,
|
||||
_token: &'a mut Token<A>,
|
||||
|
@ -504,14 +511,6 @@ impl<T: Resource, I: TypedId + Copy, F: IdentityHandlerFactory<I>> Registry<T, I
|
|||
(value, Token::new())
|
||||
}
|
||||
|
||||
pub fn process_id(&self, id_in: <F::Filter as IdentityHandler<I>>::Input) -> I {
|
||||
self.identity.process(id_in, self.backend)
|
||||
}
|
||||
|
||||
pub fn free_id(&self, id: I) {
|
||||
self.identity.free(id)
|
||||
}
|
||||
|
||||
pub fn label_for_resource(&self, id: I) -> String {
|
||||
let guard = self.data.read();
|
||||
|
||||
|
@ -547,6 +546,7 @@ pub struct Hub<B: hal::Backend, F: GlobalIdentityHandlerFactory> {
|
|||
pub render_bundles: Registry<RenderBundle, RenderBundleId, F>,
|
||||
pub render_pipelines: Registry<RenderPipeline<B>, RenderPipelineId, F>,
|
||||
pub compute_pipelines: Registry<ComputePipeline<B>, ComputePipelineId, F>,
|
||||
pub query_sets: Registry<QuerySet<B>, QuerySetId, F>,
|
||||
pub buffers: Registry<Buffer<B>, BufferId, F>,
|
||||
pub textures: Registry<Texture<B>, TextureId, F>,
|
||||
pub texture_views: Registry<TextureView<B>, TextureViewId, F>,
|
||||
|
@ -567,6 +567,7 @@ impl<B: GfxBackend, F: GlobalIdentityHandlerFactory> Hub<B, F> {
|
|||
render_bundles: Registry::new(B::VARIANT, factory),
|
||||
render_pipelines: Registry::new(B::VARIANT, factory),
|
||||
compute_pipelines: Registry::new(B::VARIANT, factory),
|
||||
query_sets: Registry::new(B::VARIANT, factory),
|
||||
buffers: Registry::new(B::VARIANT, factory),
|
||||
textures: Registry::new(B::VARIANT, factory),
|
||||
texture_views: Registry::new(B::VARIANT, factory),
|
||||
|
@ -576,7 +577,10 @@ impl<B: GfxBackend, F: GlobalIdentityHandlerFactory> Hub<B, F> {
|
|||
}
|
||||
|
||||
impl<B: GfxBackend, F: GlobalIdentityHandlerFactory> Hub<B, F> {
|
||||
fn clear(&self, surface_guard: &mut Storage<Surface, SurfaceId>) {
|
||||
//TODO: instead of having a hacky `with_adapters` parameter,
|
||||
// we should have `clear_device(device_id)` that specifically destroys
|
||||
// everything related to a logical device.
|
||||
fn clear(&self, surface_guard: &mut Storage<Surface, SurfaceId>, with_adapters: bool) {
|
||||
use crate::resource::TextureViewInner;
|
||||
use hal::{device::Device as _, window::PresentationSurface as _};
|
||||
|
||||
|
@ -626,9 +630,10 @@ impl<B: GfxBackend, F: GlobalIdentityHandlerFactory> Hub<B, F> {
|
|||
}
|
||||
for element in self.command_buffers.data.write().map.drain(..) {
|
||||
if let Element::Occupied(command_buffer, _) = element {
|
||||
devices[command_buffer.device_id.value]
|
||||
let device = &devices[command_buffer.device_id.value];
|
||||
device
|
||||
.cmd_allocator
|
||||
.after_submit(command_buffer, 0);
|
||||
.after_submit(command_buffer, &device.raw, 0);
|
||||
}
|
||||
}
|
||||
for element in self.bind_groups.data.write().map.drain(..) {
|
||||
|
@ -697,11 +702,23 @@ impl<B: GfxBackend, F: GlobalIdentityHandlerFactory> Hub<B, F> {
|
|||
}
|
||||
}
|
||||
|
||||
for element in self.query_sets.data.write().map.drain(..) {
|
||||
if let Element::Occupied(query_set, _) = element {
|
||||
let device = &devices[query_set.device_id.value];
|
||||
unsafe {
|
||||
device.raw.destroy_query_pool(query_set.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for element in devices.map.drain(..) {
|
||||
if let Element::Occupied(device, _) = element {
|
||||
device.dispose();
|
||||
}
|
||||
}
|
||||
if with_adapters {
|
||||
self.adapters.data.write().map.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -756,7 +773,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
pub fn clear_backend<B: GfxBackend>(&self, _dummy: ()) {
|
||||
let mut surface_guard = self.surfaces.data.write();
|
||||
let hub = B::hub(self);
|
||||
hub.clear(&mut *surface_guard);
|
||||
// this is used for tests, which keep the adapter
|
||||
hub.clear(&mut *surface_guard, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -769,23 +787,23 @@ impl<G: GlobalIdentityHandlerFactory> Drop for Global<G> {
|
|||
// destroy hubs
|
||||
#[cfg(vulkan)]
|
||||
{
|
||||
self.hubs.vulkan.clear(&mut *surface_guard);
|
||||
self.hubs.vulkan.clear(&mut *surface_guard, true);
|
||||
}
|
||||
#[cfg(metal)]
|
||||
{
|
||||
self.hubs.metal.clear(&mut *surface_guard);
|
||||
self.hubs.metal.clear(&mut *surface_guard, true);
|
||||
}
|
||||
#[cfg(dx12)]
|
||||
{
|
||||
self.hubs.dx12.clear(&mut *surface_guard);
|
||||
self.hubs.dx12.clear(&mut *surface_guard, true);
|
||||
}
|
||||
#[cfg(dx11)]
|
||||
{
|
||||
self.hubs.dx11.clear(&mut *surface_guard);
|
||||
self.hubs.dx11.clear(&mut *surface_guard, true);
|
||||
}
|
||||
#[cfg(gl)]
|
||||
{
|
||||
self.hubs.gl.clear(&mut *surface_guard);
|
||||
self.hubs.gl.clear(&mut *surface_guard, true);
|
||||
}
|
||||
|
||||
// destroy surfaces
|
||||
|
|
|
@ -164,6 +164,7 @@ pub type RenderPassEncoderId = *mut crate::command::RenderPass;
|
|||
pub type ComputePassEncoderId = *mut crate::command::ComputePass;
|
||||
pub type RenderBundleEncoderId = *mut crate::command::RenderBundleEncoder;
|
||||
pub type RenderBundleId = Id<crate::command::RenderBundle>;
|
||||
pub type QuerySetId = Id<crate::resource::QuerySet<Dummy>>;
|
||||
// Swap chain
|
||||
pub type SwapChainId = Id<crate::swap_chain::SwapChain<Dummy>>;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
backend,
|
||||
backend, conv,
|
||||
device::{Device, DeviceDescriptor},
|
||||
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
|
||||
id::{AdapterId, DeviceId, SurfaceId, Valid},
|
||||
|
@ -13,10 +13,7 @@ use crate::{
|
|||
use wgt::{Backend, BackendBit, PowerPreference, BIND_BUFFER_ALIGNMENT};
|
||||
|
||||
use hal::{
|
||||
adapter::{AdapterInfo as HalAdapterInfo, DeviceType as HalDeviceType, PhysicalDevice as _},
|
||||
queue::QueueFamily as _,
|
||||
window::Surface as _,
|
||||
Instance as _,
|
||||
adapter::PhysicalDevice as _, queue::QueueFamily as _, window::Surface as _, Instance as _,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -123,6 +120,7 @@ impl crate::hub::Resource for Surface {
|
|||
pub struct Adapter<B: hal::Backend> {
|
||||
pub(crate) raw: hal::adapter::Adapter<B>,
|
||||
features: wgt::Features,
|
||||
pub(crate) private_features: PrivateFeatures,
|
||||
limits: wgt::Limits,
|
||||
life_guard: LifeGuard,
|
||||
}
|
||||
|
@ -132,10 +130,12 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
span!(_guard, INFO, "Adapter::new");
|
||||
|
||||
let adapter_features = raw.physical_device.features();
|
||||
let properties = raw.physical_device.properties();
|
||||
|
||||
let mut features = wgt::Features::default()
|
||||
| wgt::Features::MAPPABLE_PRIMARY_BUFFERS
|
||||
| wgt::Features::PUSH_CONSTANTS;
|
||||
| wgt::Features::PUSH_CONSTANTS
|
||||
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
|
||||
features.set(
|
||||
wgt::Features::DEPTH_CLAMPING,
|
||||
adapter_features.contains(hal::Features::DEPTH_CLAMP),
|
||||
|
@ -144,6 +144,14 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
wgt::Features::TEXTURE_COMPRESSION_BC,
|
||||
adapter_features.contains(hal::Features::FORMAT_BC),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::TEXTURE_COMPRESSION_ETC2,
|
||||
adapter_features.contains(hal::Features::FORMAT_ETC2),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR,
|
||||
adapter_features.contains(hal::Features::FORMAT_ASTC_LDR),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
|
||||
adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY),
|
||||
|
@ -172,62 +180,188 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
wgt::Features::NON_FILL_POLYGON_MODE,
|
||||
adapter_features.contains(hal::Features::NON_FILL_POLYGON_MODE),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::TIMESTAMP_QUERY,
|
||||
properties.limits.timestamp_compute_and_graphics,
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::PIPELINE_STATISTICS_QUERY,
|
||||
adapter_features.contains(hal::Features::PIPELINE_STATISTICS_QUERY),
|
||||
);
|
||||
features.set(
|
||||
wgt::Features::SHADER_FLOAT64,
|
||||
adapter_features.contains(hal::Features::SHADER_FLOAT64),
|
||||
);
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
//TODO: https://github.com/gfx-rs/gfx/issues/3346
|
||||
features.set(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
|
||||
|
||||
let adapter_limits = raw.physical_device.limits();
|
||||
let private_features = PrivateFeatures {
|
||||
anisotropic_filtering: adapter_features.contains(hal::Features::SAMPLER_ANISOTROPY),
|
||||
texture_d24: raw
|
||||
.physical_device
|
||||
.format_properties(Some(hal::format::Format::X8D24Unorm))
|
||||
.optimal_tiling
|
||||
.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
|
||||
texture_d24_s8: raw
|
||||
.physical_device
|
||||
.format_properties(Some(hal::format::Format::D24UnormS8Uint))
|
||||
.optimal_tiling
|
||||
.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
|
||||
};
|
||||
|
||||
let default_limits = wgt::Limits::default();
|
||||
|
||||
// All these casts to u32 are safe as the underlying vulkan types are u32s.
|
||||
// If another backend provides larger limits than u32, we need to clamp them to u32::MAX.
|
||||
// TODO: fix all gfx-hal backends to produce limits we care about, and remove .max
|
||||
let desc_limits = &properties.limits.descriptor_limits;
|
||||
let limits = wgt::Limits {
|
||||
max_bind_groups: (adapter_limits.max_bound_descriptor_sets as u32)
|
||||
max_texture_dimension_1d: properties
|
||||
.limits
|
||||
.max_image_1d_size
|
||||
.max(default_limits.max_texture_dimension_1d),
|
||||
max_texture_dimension_2d: properties
|
||||
.limits
|
||||
.max_image_2d_size
|
||||
.max(default_limits.max_texture_dimension_1d),
|
||||
max_texture_dimension_3d: properties
|
||||
.limits
|
||||
.max_image_3d_size
|
||||
.max(default_limits.max_texture_dimension_1d),
|
||||
max_texture_array_layers: (properties.limits.max_image_array_layers as u32)
|
||||
.max(default_limits.max_texture_array_layers),
|
||||
max_bind_groups: (properties.limits.max_bound_descriptor_sets as u32)
|
||||
.min(MAX_BIND_GROUPS as u32)
|
||||
.max(default_limits.max_bind_groups),
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: (adapter_limits
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: desc_limits
|
||||
.max_descriptor_set_uniform_buffers_dynamic
|
||||
as u32)
|
||||
.max(default_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: (adapter_limits
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: desc_limits
|
||||
.max_descriptor_set_storage_buffers_dynamic
|
||||
as u32)
|
||||
.max(default_limits.max_dynamic_storage_buffers_per_pipeline_layout),
|
||||
max_sampled_textures_per_shader_stage: (adapter_limits
|
||||
max_sampled_textures_per_shader_stage: desc_limits
|
||||
.max_per_stage_descriptor_sampled_images
|
||||
as u32)
|
||||
.max(default_limits.max_sampled_textures_per_shader_stage),
|
||||
max_samplers_per_shader_stage: (adapter_limits.max_per_stage_descriptor_samplers
|
||||
as u32)
|
||||
max_samplers_per_shader_stage: desc_limits
|
||||
.max_per_stage_descriptor_samplers
|
||||
.max(default_limits.max_samplers_per_shader_stage),
|
||||
max_storage_buffers_per_shader_stage: (adapter_limits
|
||||
max_storage_buffers_per_shader_stage: desc_limits
|
||||
.max_per_stage_descriptor_storage_buffers
|
||||
as u32)
|
||||
.max(default_limits.max_storage_buffers_per_shader_stage),
|
||||
max_storage_textures_per_shader_stage: (adapter_limits
|
||||
max_storage_textures_per_shader_stage: desc_limits
|
||||
.max_per_stage_descriptor_storage_images
|
||||
as u32)
|
||||
.max(default_limits.max_storage_textures_per_shader_stage),
|
||||
max_uniform_buffers_per_shader_stage: (adapter_limits
|
||||
max_uniform_buffers_per_shader_stage: desc_limits
|
||||
.max_per_stage_descriptor_uniform_buffers
|
||||
as u32)
|
||||
.max(default_limits.max_uniform_buffers_per_shader_stage),
|
||||
max_uniform_buffer_binding_size: (adapter_limits.max_uniform_buffer_range as u32)
|
||||
max_uniform_buffer_binding_size: (properties.limits.max_uniform_buffer_range as u32)
|
||||
.max(default_limits.max_uniform_buffer_binding_size),
|
||||
max_push_constant_size: (adapter_limits.max_push_constants_size as u32)
|
||||
max_storage_buffer_binding_size: (properties.limits.max_storage_buffer_range as u32)
|
||||
.max(default_limits.max_storage_buffer_binding_size),
|
||||
max_vertex_buffers: (properties.limits.max_vertex_input_bindings as u32)
|
||||
.max(default_limits.max_vertex_buffers),
|
||||
max_vertex_attributes: (properties.limits.max_vertex_input_attributes as u32)
|
||||
.max(default_limits.max_vertex_attributes),
|
||||
max_vertex_buffer_array_stride: (properties.limits.max_vertex_input_binding_stride
|
||||
as u32)
|
||||
.max(default_limits.max_vertex_buffer_array_stride),
|
||||
max_push_constant_size: (properties.limits.max_push_constants_size as u32)
|
||||
.max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum.
|
||||
};
|
||||
|
||||
Self {
|
||||
raw,
|
||||
features,
|
||||
private_features,
|
||||
limits,
|
||||
life_guard: LifeGuard::new("<Adapter>"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_swap_chain_preferred_format(
|
||||
&self,
|
||||
surface: &mut Surface,
|
||||
) -> Result<wgt::TextureFormat, GetSwapChainPreferredFormatError> {
|
||||
span!(_guard, INFO, "Adapter::get_swap_chain_preferred_format");
|
||||
|
||||
let formats = {
|
||||
let surface = B::get_surface_mut(surface);
|
||||
let queue_family = &self.raw.queue_families[0];
|
||||
if !surface.supports_queue_family(queue_family) {
|
||||
return Err(GetSwapChainPreferredFormatError::UnsupportedQueueFamily);
|
||||
}
|
||||
surface.supported_formats(&self.raw.physical_device)
|
||||
};
|
||||
if let Some(formats) = formats {
|
||||
// Check the four formats mentioned in the WebGPU spec:
|
||||
// Bgra8UnormSrgb, Rgba8UnormSrgb, Bgra8Unorm, Rgba8Unorm
|
||||
// Also, prefer sRGB over linear as it is better in
|
||||
// representing perceived colors.
|
||||
if formats.contains(&hal::format::Format::Bgra8Srgb) {
|
||||
return Ok(wgt::TextureFormat::Bgra8UnormSrgb);
|
||||
}
|
||||
if formats.contains(&hal::format::Format::Rgba8Srgb) {
|
||||
return Ok(wgt::TextureFormat::Rgba8UnormSrgb);
|
||||
}
|
||||
if formats.contains(&hal::format::Format::Bgra8Unorm) {
|
||||
return Ok(wgt::TextureFormat::Bgra8Unorm);
|
||||
}
|
||||
if formats.contains(&hal::format::Format::Rgba8Unorm) {
|
||||
return Ok(wgt::TextureFormat::Rgba8Unorm);
|
||||
}
|
||||
return Err(GetSwapChainPreferredFormatError::NotFound);
|
||||
}
|
||||
|
||||
// If no formats were returned, use Bgra8UnormSrgb
|
||||
Ok(wgt::TextureFormat::Bgra8UnormSrgb)
|
||||
}
|
||||
|
||||
pub(crate) fn get_texture_format_features(
|
||||
&self,
|
||||
format: wgt::TextureFormat,
|
||||
) -> wgt::TextureFormatFeatures {
|
||||
let texture_format_properties = self
|
||||
.raw
|
||||
.physical_device
|
||||
.format_properties(Some(conv::map_texture_format(
|
||||
format,
|
||||
self.private_features,
|
||||
)))
|
||||
.optimal_tiling;
|
||||
|
||||
let mut allowed_usages = format.describe().guaranteed_format_features.allowed_usages;
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::SAMPLED) {
|
||||
allowed_usages |= wgt::TextureUsage::SAMPLED;
|
||||
}
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::STORAGE) {
|
||||
allowed_usages |= wgt::TextureUsage::STORAGE;
|
||||
}
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::COLOR_ATTACHMENT) {
|
||||
allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT;
|
||||
}
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT) {
|
||||
allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT;
|
||||
}
|
||||
|
||||
let mut flags = wgt::TextureFormatFeatureFlags::empty();
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_ATOMIC) {
|
||||
flags |= wgt::TextureFormatFeatureFlags::STORAGE_ATOMICS;
|
||||
}
|
||||
if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_READ_WRITE) {
|
||||
flags |= wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE;
|
||||
}
|
||||
|
||||
let filterable =
|
||||
texture_format_properties.contains(hal::format::ImageFeature::SAMPLED_LINEAR);
|
||||
|
||||
wgt::TextureFormatFeatures {
|
||||
allowed_usages,
|
||||
flags,
|
||||
filterable,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_device(
|
||||
&self,
|
||||
self_id: AdapterId,
|
||||
|
@ -270,6 +404,25 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
}
|
||||
|
||||
// Features
|
||||
enabled_features.set(
|
||||
hal::Features::DEPTH_CLAMP,
|
||||
desc.features.contains(wgt::Features::DEPTH_CLAMPING),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::FORMAT_BC,
|
||||
desc.features
|
||||
.contains(wgt::Features::TEXTURE_COMPRESSION_BC),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::FORMAT_ETC2,
|
||||
desc.features
|
||||
.contains(wgt::Features::TEXTURE_COMPRESSION_ETC2),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::FORMAT_ASTC_LDR,
|
||||
desc.features
|
||||
.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::TEXTURE_DESCRIPTOR_ARRAY,
|
||||
desc.features
|
||||
|
@ -302,6 +455,15 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
hal::Features::NON_FILL_POLYGON_MODE,
|
||||
desc.features.contains(wgt::Features::NON_FILL_POLYGON_MODE),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::PIPELINE_STATISTICS_QUERY,
|
||||
desc.features
|
||||
.contains(wgt::Features::PIPELINE_STATISTICS_QUERY),
|
||||
);
|
||||
enabled_features.set(
|
||||
hal::Features::SHADER_FLOAT64,
|
||||
desc.features.contains(wgt::Features::SHADER_FLOAT64),
|
||||
);
|
||||
|
||||
let family = self
|
||||
.raw
|
||||
|
@ -309,6 +471,7 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
.iter()
|
||||
.find(|family| family.queue_type().supports_graphics())
|
||||
.ok_or(RequestDeviceError::NoGraphicsQueue)?;
|
||||
|
||||
let mut gpu =
|
||||
unsafe { phd.open(&[(family, &[1.0])], enabled_features) }.map_err(|err| {
|
||||
use hal::device::CreationError::*;
|
||||
|
@ -324,7 +487,7 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
//TODO
|
||||
}
|
||||
|
||||
let limits = phd.limits();
|
||||
let limits = phd.properties().limits;
|
||||
assert_eq!(
|
||||
0,
|
||||
BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment,
|
||||
|
@ -340,21 +503,6 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
}
|
||||
|
||||
let mem_props = phd.memory_properties();
|
||||
if !desc.shader_validation {
|
||||
tracing::warn!("Shader validation is disabled");
|
||||
}
|
||||
let private_features = PrivateFeatures {
|
||||
shader_validation: desc.shader_validation,
|
||||
anisotropic_filtering: enabled_features.contains(hal::Features::SAMPLER_ANISOTROPY),
|
||||
texture_d24: phd
|
||||
.format_properties(Some(hal::format::Format::X8D24Unorm))
|
||||
.optimal_tiling
|
||||
.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
|
||||
texture_d24_s8: phd
|
||||
.format_properties(Some(hal::format::Format::D24UnormS8Uint))
|
||||
.optimal_tiling
|
||||
.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
|
||||
};
|
||||
|
||||
Device::new(
|
||||
gpu.device,
|
||||
|
@ -365,7 +513,7 @@ impl<B: GfxBackend> Adapter<B> {
|
|||
gpu.queue_groups.swap_remove(0),
|
||||
mem_props,
|
||||
limits,
|
||||
private_features,
|
||||
self.private_features,
|
||||
desc,
|
||||
trace_path,
|
||||
)
|
||||
|
@ -381,40 +529,16 @@ impl<B: hal::Backend> crate::hub::Resource for Adapter<B> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Metadata about a backend adapter.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub struct AdapterInfo {
|
||||
/// Adapter name
|
||||
pub name: String,
|
||||
/// Vendor PCI id of the adapter
|
||||
pub vendor: usize,
|
||||
/// PCI id of the adapter
|
||||
pub device: usize,
|
||||
/// Type of device
|
||||
pub device_type: DeviceType,
|
||||
/// Backend used for device
|
||||
pub backend: Backend,
|
||||
}
|
||||
|
||||
impl AdapterInfo {
|
||||
fn from_gfx(adapter_info: HalAdapterInfo, backend: Backend) -> Self {
|
||||
let HalAdapterInfo {
|
||||
name,
|
||||
vendor,
|
||||
device,
|
||||
device_type,
|
||||
} = adapter_info;
|
||||
|
||||
Self {
|
||||
name,
|
||||
vendor,
|
||||
device,
|
||||
device_type: device_type.into(),
|
||||
backend,
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum GetSwapChainPreferredFormatError {
|
||||
#[error("no suitable format found")]
|
||||
NotFound,
|
||||
#[error("invalid adapter")]
|
||||
InvalidAdapter,
|
||||
#[error("invalid surface")]
|
||||
InvalidSurface,
|
||||
#[error("surface does not support the adapter's queue family")]
|
||||
UnsupportedQueueFamily,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -436,36 +560,6 @@ pub enum RequestDeviceError {
|
|||
UnsupportedFeature(wgt::Features),
|
||||
}
|
||||
|
||||
/// Supported physical device types.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub enum DeviceType {
|
||||
/// Other.
|
||||
Other,
|
||||
/// Integrated GPU with shared CPU/GPU memory.
|
||||
IntegratedGpu,
|
||||
/// Discrete GPU with separate CPU/GPU memory.
|
||||
DiscreteGpu,
|
||||
/// Virtual / Hosted.
|
||||
VirtualGpu,
|
||||
/// Cpu / Software Rendering.
|
||||
Cpu,
|
||||
}
|
||||
|
||||
impl From<HalDeviceType> for DeviceType {
|
||||
fn from(device_type: HalDeviceType) -> Self {
|
||||
match device_type {
|
||||
HalDeviceType::Other => Self::Other,
|
||||
HalDeviceType::IntegratedGpu => Self::IntegratedGpu,
|
||||
HalDeviceType::DiscreteGpu => Self::DiscreteGpu,
|
||||
HalDeviceType::VirtualGpu => Self::VirtualGpu,
|
||||
HalDeviceType::Cpu => Self::Cpu,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AdapterInputs<'a, I> {
|
||||
IdSet(&'a [I], fn(&I) -> Backend),
|
||||
Mask(BackendBit, fn(Backend) -> I),
|
||||
|
@ -486,8 +580,8 @@ impl<I: Clone> AdapterInputs<'_, I> {
|
|||
}
|
||||
}
|
||||
|
||||
#[error("adapter is invalid")]
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[error("adapter is invalid")]
|
||||
pub struct InvalidAdapter;
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -533,7 +627,29 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
};
|
||||
|
||||
let mut token = Token::root();
|
||||
let id = self.surfaces.register_identity(id_in, surface, &mut token);
|
||||
let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
|
||||
id.0
|
||||
}
|
||||
|
||||
#[cfg(metal)]
|
||||
pub fn instance_create_surface_metal(
|
||||
&self,
|
||||
layer: *mut std::ffi::c_void,
|
||||
id_in: Input<G, SurfaceId>,
|
||||
) -> SurfaceId {
|
||||
span!(_guard, INFO, "Instance::instance_create_surface_metal");
|
||||
|
||||
let surface =
|
||||
Surface {
|
||||
#[cfg(feature = "vulkan-portability")]
|
||||
vulkan: None, //TODO: create_surface_from_layer ?
|
||||
metal: self.instance.metal.as_ref().map(|inst| {
|
||||
inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) })
|
||||
}),
|
||||
};
|
||||
|
||||
let mut token = Token::root();
|
||||
let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
|
||||
id.0
|
||||
}
|
||||
|
||||
|
@ -559,11 +675,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
for raw in inst.enumerate_adapters() {
|
||||
let adapter = Adapter::new(raw);
|
||||
tracing::info!("Adapter {} {:?}", backend_info, adapter.raw.info);
|
||||
let id = hub.adapters.register_identity(
|
||||
id_backend.clone(),
|
||||
adapter,
|
||||
&mut token,
|
||||
);
|
||||
let id = hub.adapters
|
||||
.prepare(id_backend.clone())
|
||||
.assign(adapter, &mut token);
|
||||
adapters.push(id.0);
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +788,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
return Err(RequestAdapterError::NotFound);
|
||||
}
|
||||
|
||||
let (mut integrated, mut discrete, mut virt, mut other) = (None, None, None, None);
|
||||
let (mut integrated, mut discrete, mut virt, mut cpu, mut other) =
|
||||
(None, None, None, None, None);
|
||||
|
||||
for (i, ty) in device_types.into_iter().enumerate() {
|
||||
match ty {
|
||||
|
@ -687,15 +802,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
hal::adapter::DeviceType::VirtualGpu => {
|
||||
virt = virt.or(Some(i));
|
||||
}
|
||||
_ => {
|
||||
hal::adapter::DeviceType::Cpu => {
|
||||
cpu = cpu.or(Some(i));
|
||||
}
|
||||
hal::adapter::DeviceType::Other => {
|
||||
other = other.or(Some(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let preferred_gpu = match desc.power_preference {
|
||||
PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt),
|
||||
PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt),
|
||||
PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt).or(cpu),
|
||||
PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt).or(cpu),
|
||||
};
|
||||
|
||||
let mut selected = preferred_gpu.unwrap_or(0);
|
||||
|
@ -705,11 +823,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
if selected < adapters_backend.len() {
|
||||
let adapter = Adapter::new(adapters_backend.swap_remove(selected));
|
||||
tracing::info!("Adapter {} {:?}", info_adapter, adapter.raw.info);
|
||||
let id = backend_hub(self).adapters.register_identity(
|
||||
id_backend.take().unwrap(),
|
||||
adapter,
|
||||
&mut token,
|
||||
);
|
||||
let id = backend_hub(self).adapters
|
||||
.prepare(id_backend.take().unwrap())
|
||||
.assign(adapter, &mut token);
|
||||
return Ok(id.0);
|
||||
}
|
||||
selected -= adapters_backend.len();
|
||||
|
@ -724,7 +840,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
#[cfg(dx11)]
|
||||
map(("Dx11", &mut id_dx11, adapters_dx11, backend::Dx11::hub)),
|
||||
#[cfg(gl)]
|
||||
map(("GL", &mut id_dx11, adapters_gl, backend::Gl::hub)),
|
||||
map(("GL", &mut id_gl, adapters_gl, backend::Gl::hub)),
|
||||
}
|
||||
|
||||
let _ = (
|
||||
|
@ -742,7 +858,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
pub fn adapter_get_info<B: GfxBackend>(
|
||||
&self,
|
||||
adapter_id: AdapterId,
|
||||
) -> Result<AdapterInfo, InvalidAdapter> {
|
||||
) -> Result<wgt::AdapterInfo, InvalidAdapter> {
|
||||
span!(_guard, INFO, "Adapter::get_info");
|
||||
|
||||
let hub = B::hub(self);
|
||||
|
@ -750,7 +866,23 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let (adapter_guard, _) = hub.adapters.read(&mut token);
|
||||
adapter_guard
|
||||
.get(adapter_id)
|
||||
.map(|adapter| AdapterInfo::from_gfx(adapter.raw.info.clone(), adapter_id.backend()))
|
||||
.map(|adapter| conv::map_adapter_info(adapter.raw.info.clone(), adapter_id.backend()))
|
||||
.map_err(|_| InvalidAdapter)
|
||||
}
|
||||
|
||||
pub fn adapter_get_texture_format_features<B: GfxBackend>(
|
||||
&self,
|
||||
adapter_id: AdapterId,
|
||||
format: wgt::TextureFormat,
|
||||
) -> Result<wgt::TextureFormatFeatures, InvalidAdapter> {
|
||||
span!(_guard, INFO, "Adapter::get_texture_format_features");
|
||||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (adapter_guard, _) = hub.adapters.read(&mut token);
|
||||
adapter_guard
|
||||
.get(adapter_id)
|
||||
.map(|adapter| adapter.get_texture_format_features(format))
|
||||
.map_err(|_| InvalidAdapter)
|
||||
}
|
||||
|
||||
|
@ -791,16 +923,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let mut token = Token::root();
|
||||
let (mut adapter_guard, _) = hub.adapters.write(&mut token);
|
||||
|
||||
match adapter_guard.get_mut(adapter_id) {
|
||||
Ok(adapter) => {
|
||||
if adapter.life_guard.ref_count.take().unwrap().load() == 1 {
|
||||
hub.adapters
|
||||
.unregister_locked(adapter_id, &mut *adapter_guard);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
hub.adapters.free_id(adapter_id);
|
||||
}
|
||||
let free = match adapter_guard.get_mut(adapter_id) {
|
||||
Ok(adapter) => adapter.life_guard.ref_count.take().unwrap().load() == 1,
|
||||
Err(_) => true,
|
||||
};
|
||||
if free {
|
||||
hub.adapters
|
||||
.unregister_locked(adapter_id, &mut *adapter_guard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -817,6 +946,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let fid = hub.devices.prepare(id_in);
|
||||
|
||||
let error = loop {
|
||||
let (adapter_guard, mut token) = hub.adapters.read(&mut token);
|
||||
|
@ -828,13 +958,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
Ok(device) => device,
|
||||
Err(e) => break e,
|
||||
};
|
||||
let id = hub.devices.register_identity(id_in, device, &mut token);
|
||||
let id = fid.assign(device, &mut token);
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
let id = hub
|
||||
.devices
|
||||
.register_error(id_in, desc.label.borrow_or_default(), &mut token);
|
||||
let id = fid.assign_error(desc.label.borrow_or_default(), &mut token);
|
||||
(id, Some(error))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ pub mod device;
|
|||
pub mod hub;
|
||||
pub mod id;
|
||||
pub mod instance;
|
||||
mod memory_init_tracker;
|
||||
pub mod pipeline;
|
||||
pub mod resource;
|
||||
pub mod swap_chain;
|
||||
|
@ -219,7 +220,6 @@ struct Stored<T> {
|
|||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct PrivateFeatures {
|
||||
shader_validation: bool,
|
||||
anisotropic_filtering: bool,
|
||||
texture_d24: bool,
|
||||
texture_d24_s8: bool,
|
||||
|
@ -231,15 +231,15 @@ macro_rules! gfx_select {
|
|||
// Note: For some reason the cfg aliases defined in build.rs don't succesfully apply in this
|
||||
// macro so we must specify their equivalents manually
|
||||
match $id.backend() {
|
||||
#[cfg(any(not(any(target_os = "ios", target_os = "macos")), feature = "gfx-backend-vulkan"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), any(not(any(target_os = "ios", target_os = "macos")), feature = "gfx-backend-vulkan")))]
|
||||
wgt::Backend::Vulkan => $global.$method::<$crate::backend::Vulkan>( $($param),* ),
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
|
||||
wgt::Backend::Metal => $global.$method::<$crate::backend::Metal>( $($param),* ),
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(not(target_arch = "wasm32"), windows))]
|
||||
wgt::Backend::Dx12 => $global.$method::<$crate::backend::Dx12>( $($param),* ),
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(not(target_arch = "wasm32"), windows))]
|
||||
wgt::Backend::Dx11 => $global.$method::<$crate::backend::Dx11>( $($param),* ),
|
||||
//#[cfg(all(unix, not(any(target_os = "ios", target_os = "macos"))))]
|
||||
//#[cfg(any(target_arch = "wasm32", all(unix, not(any(target_os = "ios", target_os = "macos")))))]
|
||||
//wgt::Backend::Gl => $global.$method::<$crate::backend::Gl>( $($param),+ ),
|
||||
other => panic!("Unexpected backend {:?}", other),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum MemoryInitKind {
|
||||
// The memory range is going to be written by an already initialized source, thus doesn't need extra attention other than marking as initialized.
|
||||
ImplicitlyInitialized,
|
||||
// The memory range is going to be read, therefore needs to ensure prior initialization.
|
||||
NeedsInitializedMemory,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct MemoryInitTrackerAction<ResourceId> {
|
||||
pub(crate) id: ResourceId,
|
||||
pub(crate) range: Range<wgt::BufferAddress>,
|
||||
pub(crate) kind: MemoryInitKind,
|
||||
}
|
||||
|
||||
/// Tracks initialization status of a linear range from 0..size
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MemoryInitTracker {
|
||||
// Ordered, non overlapping list of all uninitialized ranges.
|
||||
uninitialized_ranges: Vec<Range<wgt::BufferAddress>>,
|
||||
}
|
||||
|
||||
pub(crate) struct MemoryInitTrackerDrain<'a> {
|
||||
uninitialized_ranges: &'a mut Vec<Range<wgt::BufferAddress>>,
|
||||
drain_range: Range<wgt::BufferAddress>,
|
||||
first_index: usize,
|
||||
next_index: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MemoryInitTrackerDrain<'a> {
|
||||
type Item = Range<wgt::BufferAddress>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(r) = self
|
||||
.uninitialized_ranges
|
||||
.get(self.next_index)
|
||||
.and_then(|range| {
|
||||
if range.start < self.drain_range.end {
|
||||
Some(range.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
self.next_index += 1;
|
||||
Some(r.start.max(self.drain_range.start)..r.end.min(self.drain_range.end))
|
||||
} else {
|
||||
let num_affected = self.next_index - self.first_index;
|
||||
if num_affected == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let first_range = &mut self.uninitialized_ranges[self.first_index];
|
||||
|
||||
// Split one "big" uninitialized range?
|
||||
if num_affected == 1
|
||||
&& first_range.start < self.drain_range.start
|
||||
&& first_range.end > self.drain_range.end
|
||||
{
|
||||
let old_start = first_range.start;
|
||||
first_range.start = self.drain_range.end;
|
||||
self.uninitialized_ranges
|
||||
.insert(self.first_index, old_start..self.drain_range.start);
|
||||
}
|
||||
// Adjust border ranges and delete everything in-between.
|
||||
else {
|
||||
let remove_start = if first_range.start >= self.drain_range.start {
|
||||
self.first_index
|
||||
} else {
|
||||
first_range.end = self.drain_range.start;
|
||||
self.first_index + 1
|
||||
};
|
||||
|
||||
let last_range = &mut self.uninitialized_ranges[self.next_index - 1];
|
||||
let remove_end = if last_range.end <= self.drain_range.end {
|
||||
self.next_index
|
||||
} else {
|
||||
last_range.start = self.drain_range.end;
|
||||
self.next_index - 1
|
||||
};
|
||||
|
||||
self.uninitialized_ranges.drain(remove_start..remove_end);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryInitTracker {
|
||||
pub(crate) fn new(size: wgt::BufferAddress) -> Self {
|
||||
Self {
|
||||
uninitialized_ranges: vec![0..size],
|
||||
}
|
||||
}
|
||||
|
||||
// Search smallest range.end which is bigger than bound in O(log n) (with n being number of uninitialized ranges)
|
||||
fn lower_bound(&self, bound: wgt::BufferAddress) -> usize {
|
||||
// This is equivalent to, except that it may return an out of bounds index instead of
|
||||
//self.uninitialized_ranges.iter().position(|r| r.end > bound)
|
||||
|
||||
// In future Rust versions this operation can be done with partition_point
|
||||
// See https://github.com/rust-lang/rust/pull/73577/
|
||||
let mut left = 0;
|
||||
let mut right = self.uninitialized_ranges.len();
|
||||
|
||||
while left != right {
|
||||
let mid = left + (right - left) / 2;
|
||||
let value = unsafe { self.uninitialized_ranges.get_unchecked(mid) };
|
||||
|
||||
if value.end <= bound {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
// Checks if there's any uninitialized ranges within a query.
|
||||
// If there are any, the range returned a the subrange of the query_range that contains all these uninitialized regions.
|
||||
// Returned range may be larger than necessary (tradeoff for making this function O(log n))
|
||||
pub(crate) fn check(
|
||||
&self,
|
||||
query_range: Range<wgt::BufferAddress>,
|
||||
) -> Option<Range<wgt::BufferAddress>> {
|
||||
let index = self.lower_bound(query_range.start);
|
||||
self.uninitialized_ranges
|
||||
.get(index)
|
||||
.map(|start_range| {
|
||||
if start_range.start < query_range.end {
|
||||
let start = start_range.start.max(query_range.start);
|
||||
match self.uninitialized_ranges.get(index + 1) {
|
||||
Some(next_range) => {
|
||||
if next_range.start < query_range.end {
|
||||
// Would need to keep iterating for more accurate upper bound. Don't do that here.
|
||||
Some(start..query_range.end)
|
||||
} else {
|
||||
Some(start..start_range.end.min(query_range.end))
|
||||
}
|
||||
}
|
||||
None => Some(start..start_range.end.min(query_range.end)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
// Drains uninitialized ranges in a query range.
|
||||
#[must_use]
|
||||
pub(crate) fn drain<'a>(
|
||||
&'a mut self,
|
||||
drain_range: Range<wgt::BufferAddress>,
|
||||
) -> MemoryInitTrackerDrain<'a> {
|
||||
let index = self.lower_bound(drain_range.start);
|
||||
MemoryInitTrackerDrain {
|
||||
drain_range,
|
||||
uninitialized_ranges: &mut self.uninitialized_ranges,
|
||||
first_index: index,
|
||||
next_index: index,
|
||||
}
|
||||
}
|
||||
|
||||
// Clears uninitialized ranges in a query range.
|
||||
pub(crate) fn clear(&mut self, range: Range<wgt::BufferAddress>) {
|
||||
self.drain(range).for_each(drop);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::MemoryInitTracker;
|
||||
use std::ops::Range;
|
||||
|
||||
#[test]
|
||||
fn check_for_newly_created_tracker() {
|
||||
let tracker = MemoryInitTracker::new(10);
|
||||
assert_eq!(tracker.check(0..10), Some(0..10));
|
||||
assert_eq!(tracker.check(0..3), Some(0..3));
|
||||
assert_eq!(tracker.check(3..4), Some(3..4));
|
||||
assert_eq!(tracker.check(4..10), Some(4..10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_for_cleared_tracker() {
|
||||
let mut tracker = MemoryInitTracker::new(10);
|
||||
tracker.clear(0..10);
|
||||
assert_eq!(tracker.check(0..10), None);
|
||||
assert_eq!(tracker.check(0..3), None);
|
||||
assert_eq!(tracker.check(3..4), None);
|
||||
assert_eq!(tracker.check(4..10), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_for_partially_filled_tracker() {
|
||||
let mut tracker = MemoryInitTracker::new(25);
|
||||
// Two regions of uninitialized memory
|
||||
tracker.clear(0..5);
|
||||
tracker.clear(10..15);
|
||||
tracker.clear(20..25);
|
||||
|
||||
assert_eq!(tracker.check(0..25), Some(5..25)); // entire range
|
||||
|
||||
assert_eq!(tracker.check(0..5), None); // left non-overlapping
|
||||
assert_eq!(tracker.check(3..8), Some(5..8)); // left overlapping region
|
||||
assert_eq!(tracker.check(3..17), Some(5..17)); // left overlapping region + contained region
|
||||
|
||||
assert_eq!(tracker.check(8..22), Some(8..22)); // right overlapping region + contained region (yes, doesn't fix range end!)
|
||||
assert_eq!(tracker.check(17..22), Some(17..20)); // right overlapping region
|
||||
assert_eq!(tracker.check(20..25), None); // right non-overlapping
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_already_cleared() {
|
||||
let mut tracker = MemoryInitTracker::new(30);
|
||||
tracker.clear(10..20);
|
||||
|
||||
// Overlapping with non-cleared
|
||||
tracker.clear(5..15); // Left overlap
|
||||
tracker.clear(15..25); // Right overlap
|
||||
tracker.clear(0..30); // Inner overlap
|
||||
|
||||
// Clear fully cleared
|
||||
tracker.clear(0..30);
|
||||
|
||||
assert_eq!(tracker.check(0..30), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_never_returns_ranges_twice_for_same_range() {
|
||||
let mut tracker = MemoryInitTracker::new(19);
|
||||
assert_eq!(tracker.drain(0..19).count(), 1);
|
||||
assert_eq!(tracker.drain(0..19).count(), 0);
|
||||
|
||||
let mut tracker = MemoryInitTracker::new(17);
|
||||
assert_eq!(tracker.drain(5..8).count(), 1);
|
||||
assert_eq!(tracker.drain(5..8).count(), 0);
|
||||
assert_eq!(tracker.drain(1..3).count(), 1);
|
||||
assert_eq!(tracker.drain(1..3).count(), 0);
|
||||
assert_eq!(tracker.drain(7..13).count(), 1);
|
||||
assert_eq!(tracker.drain(7..13).count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_splits_ranges_correctly() {
|
||||
let mut tracker = MemoryInitTracker::new(1337);
|
||||
assert_eq!(
|
||||
tracker
|
||||
.drain(21..42)
|
||||
.collect::<Vec<Range<wgt::BufferAddress>>>(),
|
||||
vec![21..42]
|
||||
);
|
||||
assert_eq!(
|
||||
tracker
|
||||
.drain(900..1000)
|
||||
.collect::<Vec<Range<wgt::BufferAddress>>>(),
|
||||
vec![900..1000]
|
||||
);
|
||||
|
||||
// Splitted ranges.
|
||||
assert_eq!(
|
||||
tracker
|
||||
.drain(5..1003)
|
||||
.collect::<Vec<Range<wgt::BufferAddress>>>(),
|
||||
vec![5..21, 42..900, 1000..1003]
|
||||
);
|
||||
assert_eq!(
|
||||
tracker
|
||||
.drain(0..1337)
|
||||
.collect::<Vec<Range<wgt::BufferAddress>>>(),
|
||||
vec![0..5, 1003..1337]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,37 +7,32 @@ use crate::{
|
|||
device::{DeviceError, RenderPassContext},
|
||||
hub::Resource,
|
||||
id::{DeviceId, PipelineLayoutId, ShaderModuleId},
|
||||
validation::StageError,
|
||||
Label, LifeGuard, Stored,
|
||||
validation, Label, LifeGuard, Stored,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use thiserror::Error;
|
||||
use wgt::{BufferAddress, IndexFormat, InputStepMode};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub enum ShaderModuleSource<'a> {
|
||||
SpirV(Cow<'a, [u32]>),
|
||||
Wgsl(Cow<'a, str>),
|
||||
// Unable to serialize with `naga::Module` in here:
|
||||
// requires naga serialization feature.
|
||||
//Naga(naga::Module),
|
||||
Naga(naga::Module),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub struct ShaderModuleDescriptor<'a> {
|
||||
pub label: Label<'a>,
|
||||
pub source: ShaderModuleSource<'a>,
|
||||
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
|
||||
pub flags: wgt::ShaderFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ShaderModule<B: hal::Backend> {
|
||||
pub(crate) raw: B::ShaderModule,
|
||||
pub(crate) device_id: Stored<DeviceId>,
|
||||
pub(crate) module: Option<naga::Module>,
|
||||
pub(crate) interface: Option<validation::Interface>,
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) label: String,
|
||||
}
|
||||
|
@ -59,10 +54,14 @@ impl<B: hal::Backend> Resource for ShaderModule<B> {
|
|||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum CreateShaderModuleError {
|
||||
#[error("Failed to parse WGSL")]
|
||||
Parsing,
|
||||
#[error(transparent)]
|
||||
Device(#[from] DeviceError),
|
||||
#[error(transparent)]
|
||||
Validation(#[from] naga::proc::ValidationError),
|
||||
#[error("missing required device features {0:?}")]
|
||||
MissingFeature(wgt::Features),
|
||||
}
|
||||
|
||||
/// Describes a programmable pipeline stage.
|
||||
|
@ -101,7 +100,7 @@ pub struct ComputePipelineDescriptor<'a> {
|
|||
/// The layout of bind groups for this pipeline.
|
||||
pub layout: Option<PipelineLayoutId>,
|
||||
/// The compiled compute stage and its entry point.
|
||||
pub compute_stage: ProgrammableStageDescriptor<'a>,
|
||||
pub stage: ProgrammableStageDescriptor<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -113,7 +112,7 @@ pub enum CreateComputePipelineError {
|
|||
#[error("unable to derive an implicit layout")]
|
||||
Implicit(#[from] ImplicitLayoutError),
|
||||
#[error(transparent)]
|
||||
Stage(StageError),
|
||||
Stage(validation::StageError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -136,24 +135,35 @@ impl<B: hal::Backend> Resource for ComputePipeline<B> {
|
|||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub struct VertexBufferDescriptor<'a> {
|
||||
pub struct VertexBufferLayout<'a> {
|
||||
/// The stride, in bytes, between elements of this buffer.
|
||||
pub stride: BufferAddress,
|
||||
pub array_stride: wgt::BufferAddress,
|
||||
/// How often this vertex buffer is "stepped" forward.
|
||||
pub step_mode: InputStepMode,
|
||||
pub step_mode: wgt::InputStepMode,
|
||||
/// The list of attributes which comprise a single vertex.
|
||||
pub attributes: Cow<'a, [wgt::VertexAttributeDescriptor]>,
|
||||
pub attributes: Cow<'a, [wgt::VertexAttribute]>,
|
||||
}
|
||||
|
||||
/// Describes vertex input state for a render pipeline.
|
||||
/// Describes the vertex process in a render pipeline.
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub struct VertexStateDescriptor<'a> {
|
||||
/// The format of any index buffers used with this pipeline.
|
||||
pub index_format: IndexFormat,
|
||||
pub struct VertexState<'a> {
|
||||
/// The compiled vertex stage and its entry point.
|
||||
pub stage: ProgrammableStageDescriptor<'a>,
|
||||
/// The format of any vertex buffers used with this pipeline.
|
||||
pub vertex_buffers: Cow<'a, [VertexBufferDescriptor<'a>]>,
|
||||
pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
|
||||
}
|
||||
|
||||
/// Describes fragment processing in a render pipeline.
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
|
||||
pub struct FragmentState<'a> {
|
||||
/// The compiled fragment stage and its entry point.
|
||||
pub stage: ProgrammableStageDescriptor<'a>,
|
||||
/// The effect of draw calls on the color aspect of the output target.
|
||||
pub targets: Cow<'a, [wgt::ColorTargetState]>,
|
||||
}
|
||||
|
||||
/// Describes a render (graphics) pipeline.
|
||||
|
@ -164,40 +174,26 @@ pub struct RenderPipelineDescriptor<'a> {
|
|||
pub label: Label<'a>,
|
||||
/// The layout of bind groups for this pipeline.
|
||||
pub layout: Option<PipelineLayoutId>,
|
||||
/// The compiled vertex stage and its entry point.
|
||||
pub vertex_stage: ProgrammableStageDescriptor<'a>,
|
||||
/// The compiled fragment stage and its entry point, if any.
|
||||
pub fragment_stage: Option<ProgrammableStageDescriptor<'a>>,
|
||||
/// The rasterization process for this pipeline.
|
||||
pub rasterization_state: Option<wgt::RasterizationStateDescriptor>,
|
||||
/// The primitive topology used to interpret vertices.
|
||||
pub primitive_topology: wgt::PrimitiveTopology,
|
||||
/// The effect of draw calls on the color aspect of the output target.
|
||||
pub color_states: Cow<'a, [wgt::ColorStateDescriptor]>,
|
||||
/// The vertex processing state for this pipeline.
|
||||
pub vertex: VertexState<'a>,
|
||||
/// The properties of the pipeline at the primitive assembly and rasterization level.
|
||||
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
|
||||
pub primitive: wgt::PrimitiveState,
|
||||
/// The effect of draw calls on the depth and stencil aspects of the output target, if any.
|
||||
pub depth_stencil_state: Option<wgt::DepthStencilStateDescriptor>,
|
||||
/// The vertex input state for this pipeline.
|
||||
pub vertex_state: VertexStateDescriptor<'a>,
|
||||
/// The number of samples calculated per pixel (for MSAA). For non-multisampled textures,
|
||||
/// this should be `1`
|
||||
pub sample_count: u32,
|
||||
/// Bitmask that restricts the samples of a pixel modified by this pipeline. All samples
|
||||
/// can be enabled using the value `!0`
|
||||
pub sample_mask: u32,
|
||||
/// When enabled, produces another sample mask per pixel based on the alpha output value, that
|
||||
/// is ANDed with the sample_mask and the primitive coverage to restrict the set of samples
|
||||
/// affected by a primitive.
|
||||
///
|
||||
/// The implicit mask produced for alpha of zero is guaranteed to be zero, and for alpha of one
|
||||
/// is guaranteed to be all 1-s.
|
||||
pub alpha_to_coverage_enabled: bool,
|
||||
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
|
||||
pub depth_stencil: Option<wgt::DepthStencilState>,
|
||||
/// The multi-sampling properties of the pipeline.
|
||||
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
|
||||
pub multisample: wgt::MultisampleState,
|
||||
/// The fragment processing state for this pipeline.
|
||||
pub fragment: Option<FragmentState<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum CreateRenderPipelineError {
|
||||
#[error(transparent)]
|
||||
Device(#[from] DeviceError),
|
||||
#[error("pipelie layout is invalid")]
|
||||
#[error("pipeline layout is invalid")]
|
||||
InvalidLayout,
|
||||
#[error("unable to derive an implicit layout")]
|
||||
Implicit(#[from] ImplicitLayoutError),
|
||||
|
@ -207,12 +203,26 @@ pub enum CreateRenderPipelineError {
|
|||
IncompatibleOutputFormat { index: u8 },
|
||||
#[error("invalid sample count {0}")]
|
||||
InvalidSampleCount(u32),
|
||||
#[error("the number of vertex buffers {given} exceeds the limit {limit}")]
|
||||
TooManyVertexBuffers { given: u32, limit: u32 },
|
||||
#[error("the total number of vertex attributes {given} exceeds the limit {limit}")]
|
||||
TooManyVertexAttributes { given: u32, limit: u32 },
|
||||
#[error("vertex buffer {index} stride {given} exceeds the limit {limit}")]
|
||||
VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
|
||||
#[error("vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
|
||||
UnalignedVertexStride { index: u32, stride: BufferAddress },
|
||||
UnalignedVertexStride {
|
||||
index: u32,
|
||||
stride: wgt::BufferAddress,
|
||||
},
|
||||
#[error("vertex attribute at location {location} has invalid offset {offset}")]
|
||||
InvalidVertexAttributeOffset {
|
||||
location: wgt::ShaderLocation,
|
||||
offset: BufferAddress,
|
||||
offset: wgt::BufferAddress,
|
||||
},
|
||||
#[error("strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
|
||||
StripIndexFormatForNonStripTopology {
|
||||
strip_index_format: Option<wgt::IndexFormat>,
|
||||
topology: wgt::PrimitiveTopology,
|
||||
},
|
||||
#[error("missing required device features {0:?}")]
|
||||
MissingFeature(wgt::Features),
|
||||
|
@ -220,7 +230,7 @@ pub enum CreateRenderPipelineError {
|
|||
Stage {
|
||||
flag: wgt::ShaderStage,
|
||||
#[source]
|
||||
error: StageError,
|
||||
error: validation::StageError,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -240,8 +250,8 @@ pub struct RenderPipeline<B: hal::Backend> {
|
|||
pub(crate) device_id: Stored<DeviceId>,
|
||||
pub(crate) pass_context: RenderPassContext,
|
||||
pub(crate) flags: PipelineFlags,
|
||||
pub(crate) index_format: IndexFormat,
|
||||
pub(crate) vertex_strides: Vec<(BufferAddress, InputStepMode)>,
|
||||
pub(crate) strip_index_format: Option<wgt::IndexFormat>,
|
||||
pub(crate) vertex_strides: Vec<(wgt::BufferAddress, wgt::InputStepMode)>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
|||
device::{alloc::MemoryBlock, DeviceError, HostMap},
|
||||
hub::Resource,
|
||||
id::{DeviceId, SwapChainId, TextureId},
|
||||
memory_init_tracker::MemoryInitTracker,
|
||||
track::{TextureSelector, DUMMY_SELECTOR},
|
||||
validation::MissingBufferUsageError,
|
||||
Label, LifeGuard, RefCount, Stored,
|
||||
|
@ -75,6 +76,7 @@ bitflags::bitflags! {
|
|||
pub enum BufferMapAsyncStatus {
|
||||
Success,
|
||||
Error,
|
||||
Aborted,
|
||||
Unknown,
|
||||
ContextLost,
|
||||
}
|
||||
|
@ -160,7 +162,7 @@ pub struct Buffer<B: hal::Backend> {
|
|||
pub(crate) device_id: Stored<DeviceId>,
|
||||
pub(crate) usage: wgt::BufferUsage,
|
||||
pub(crate) size: wgt::BufferAddress,
|
||||
pub(crate) full_range: (),
|
||||
pub(crate) initialization_status: MemoryInitTracker,
|
||||
pub(crate) sync_mapped_writes: Option<hal::memory::Segment>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
pub(crate) map_state: BufferMapState<B>,
|
||||
|
@ -174,6 +176,8 @@ pub enum CreateBufferError {
|
|||
AccessError(#[from] BufferAccessError),
|
||||
#[error("buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
|
||||
UnalignedSize,
|
||||
#[error("Buffers cannot have empty usage flags")]
|
||||
EmptyUsage,
|
||||
#[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
|
||||
UsageMismatch(wgt::BufferUsage),
|
||||
}
|
||||
|
@ -203,11 +207,13 @@ pub struct Texture<B: hal::Backend> {
|
|||
pub(crate) dimension: wgt::TextureDimension,
|
||||
pub(crate) kind: hal::image::Kind,
|
||||
pub(crate) format: wgt::TextureFormat,
|
||||
pub(crate) format_features: wgt::TextureFormatFeatures,
|
||||
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
|
||||
pub(crate) full_range: TextureSelector,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TextureErrorDimension {
|
||||
X,
|
||||
Y,
|
||||
|
@ -218,8 +224,12 @@ pub enum TextureErrorDimension {
|
|||
pub enum TextureDimensionError {
|
||||
#[error("Dimension {0:?} is zero")]
|
||||
Zero(TextureErrorDimension),
|
||||
#[error("1D textures must have height set to 1")]
|
||||
InvalidHeight,
|
||||
#[error("Dimension {0:?} value {given} exceeds the limit of {limit}")]
|
||||
LimitExceeded {
|
||||
dim: TextureErrorDimension,
|
||||
given: u32,
|
||||
limit: u32,
|
||||
},
|
||||
#[error("sample count {0} is invalid")]
|
||||
InvalidSampleCount(u32),
|
||||
}
|
||||
|
@ -230,10 +240,14 @@ pub enum CreateTextureError {
|
|||
Device(#[from] DeviceError),
|
||||
#[error("D24Plus textures cannot be copied")]
|
||||
CannotCopyD24Plus,
|
||||
#[error("Textures cannot have empty usage flags")]
|
||||
EmptyUsage,
|
||||
#[error(transparent)]
|
||||
InvalidDimension(#[from] TextureDimensionError),
|
||||
#[error("texture descriptor mip level count ({0}) is invalid")]
|
||||
InvalidMipLevelCount(u32),
|
||||
#[error("The texture usages {0:?} are not allowed on a texture of type {1:?}")]
|
||||
InvalidUsages(wgt::TextureUsage, wgt::TextureFormat),
|
||||
#[error("Feature {0:?} must be enabled to create a texture of type {1:?}")]
|
||||
MissingFeature(wgt::Features, wgt::TextureFormat),
|
||||
}
|
||||
|
@ -265,7 +279,7 @@ pub struct TextureViewDescriptor<'a> {
|
|||
/// The dimension of the texture view. For 1D textures, this must be `1D`. For 2D textures it must be one of
|
||||
/// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `3D`
|
||||
pub dimension: Option<wgt::TextureViewDimension>,
|
||||
/// Aspect of the texture. Color textures must be [`TextureAspect::All`].
|
||||
/// Aspect of the texture. Color textures must be [`TextureAspect::All`](wgt::TextureAspect::All).
|
||||
pub aspect: wgt::TextureAspect,
|
||||
/// Base mip level.
|
||||
pub base_mip_level: u32,
|
||||
|
@ -299,8 +313,13 @@ pub struct TextureView<B: hal::Backend> {
|
|||
//TODO: store device_id for quick access?
|
||||
pub(crate) aspects: hal::format::Aspects,
|
||||
pub(crate) format: wgt::TextureFormat,
|
||||
pub(crate) extent: hal::image::Extent,
|
||||
pub(crate) format_features: wgt::TextureFormatFeatures,
|
||||
pub(crate) dimension: wgt::TextureViewDimension,
|
||||
pub(crate) extent: wgt::Extent3d,
|
||||
pub(crate) samples: hal::image::NumSamples,
|
||||
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
|
||||
/// Internal use of this texture view when used as `BindingType::Texture`.
|
||||
pub(crate) sampled_internal_use: TextureUse,
|
||||
pub(crate) selector: TextureSelector,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
@ -381,7 +400,7 @@ pub struct SamplerDescriptor<'a> {
|
|||
pub compare: Option<wgt::CompareFunction>,
|
||||
/// Valid values: 1, 2, 4, 8, and 16.
|
||||
pub anisotropy_clamp: Option<NonZeroU8>,
|
||||
/// Border color to use when address_mode is [`AddressMode::ClampToBorder`]
|
||||
/// Border color to use when address_mode is [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)
|
||||
pub border_color: Option<wgt::SamplerBorderColor>,
|
||||
}
|
||||
|
||||
|
@ -437,6 +456,42 @@ impl<B: hal::Backend> Borrow<()> for Sampler<B> {
|
|||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum CreateQuerySetError {
|
||||
#[error(transparent)]
|
||||
Device(#[from] DeviceError),
|
||||
#[error("QuerySets cannot be made with zero queries")]
|
||||
ZeroCount,
|
||||
#[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
|
||||
TooManyQueries { count: u32, maximum: u32 },
|
||||
#[error("Feature {0:?} must be enabled")]
|
||||
MissingFeature(wgt::Features),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QuerySet<B: hal::Backend> {
|
||||
pub(crate) raw: B::QueryPool,
|
||||
pub(crate) device_id: Stored<DeviceId>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
/// Amount of queries in the query set.
|
||||
pub(crate) desc: wgt::QuerySetDescriptor,
|
||||
/// Amount of numbers in each query (i.e. a pipeline statistics query for two attributes will have this number be two)
|
||||
pub(crate) elements: u32,
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> Resource for QuerySet<B> {
|
||||
const TYPE: &'static str = "QuerySet";
|
||||
|
||||
fn life_guard(&self) -> &LifeGuard {
|
||||
&self.life_guard
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> Borrow<()> for QuerySet<B> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum DestroyError {
|
||||
|
|
|
@ -44,7 +44,7 @@ use crate::{
|
|||
LifeGuard, PrivateFeatures, Stored, SubmissionIndex,
|
||||
};
|
||||
|
||||
use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
|
||||
use hal::{queue::Queue as _, window::PresentationSurface as _};
|
||||
use thiserror::Error;
|
||||
use wgt::{SwapChainDescriptor, SwapChainStatus};
|
||||
|
||||
|
@ -59,8 +59,8 @@ pub struct SwapChain<B: hal::Backend> {
|
|||
pub(crate) num_frames: hal::window::SwapImageIndex,
|
||||
pub(crate) semaphore: B::Semaphore,
|
||||
pub(crate) acquired_view_id: Option<Stored<TextureViewId>>,
|
||||
pub(crate) acquired_framebuffers: Vec<B::Framebuffer>,
|
||||
pub(crate) active_submission_index: SubmissionIndex,
|
||||
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
|
||||
}
|
||||
|
||||
impl<B: hal::Backend> crate::hub::Resource for SwapChain<B> {
|
||||
|
@ -81,6 +81,8 @@ pub enum SwapChainError {
|
|||
Device(#[from] DeviceError),
|
||||
#[error("swap chain image is already acquired")]
|
||||
AlreadyAcquired,
|
||||
#[error("acquired frame is still referenced")]
|
||||
StillReferenced,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -91,6 +93,8 @@ pub enum CreateSwapChainError {
|
|||
InvalidSurface,
|
||||
#[error("`SwapChainOutput` must be dropped before a new `SwapChain` is made")]
|
||||
SwapChainOutputExists,
|
||||
#[error("Both `SwapChain` width and height must be non-zero. Wait to recreate the `SwapChain` until the window has non-zero area.")]
|
||||
ZeroArea,
|
||||
#[error("surface does not support the adapter's queue family")]
|
||||
UnsupportedQueueFamily,
|
||||
#[error("requested format {requested:?} is not in list of supported formats: {available:?}")]
|
||||
|
@ -139,6 +143,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
|
||||
let hub = B::hub(self);
|
||||
let mut token = Token::root();
|
||||
let fid = hub.texture_views.prepare(view_id_in);
|
||||
|
||||
let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
|
||||
let surface = surface_guard
|
||||
|
@ -149,8 +154,16 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
let sc = swap_chain_guard
|
||||
.get_mut(swap_chain_id)
|
||||
.map_err(|_| SwapChainError::Invalid)?;
|
||||
#[cfg_attr(not(feature = "trace"), allow(unused_variables))]
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let device = &device_guard[sc.device_id.value];
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref trace) = device.trace {
|
||||
trace.lock().add(Action::GetSwapChainTexture {
|
||||
id: fid.id(),
|
||||
parent_id: swap_chain_id,
|
||||
});
|
||||
}
|
||||
|
||||
let suf = B::get_surface_mut(surface);
|
||||
let (image, status) = match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } {
|
||||
|
@ -160,9 +173,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
None,
|
||||
match err {
|
||||
hal::window::AcquireError::OutOfMemory(_) => Err(DeviceError::OutOfMemory)?,
|
||||
hal::window::AcquireError::NotReady => unreachable!(), // we always set a timeout
|
||||
hal::window::AcquireError::Timeout => SwapChainStatus::Timeout,
|
||||
hal::window::AcquireError::OutOfDate => SwapChainStatus::Outdated,
|
||||
hal::window::AcquireError::NotReady { .. } => SwapChainStatus::Timeout,
|
||||
hal::window::AcquireError::OutOfDate(_) => SwapChainStatus::Outdated,
|
||||
hal::window::AcquireError::SurfaceLost(_) => SwapChainStatus::Lost,
|
||||
hal::window::AcquireError::DeviceLost(_) => Err(DeviceError::Lost)?,
|
||||
},
|
||||
|
@ -181,12 +193,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
},
|
||||
aspects: hal::format::Aspects::COLOR,
|
||||
format: sc.desc.format,
|
||||
extent: hal::image::Extent {
|
||||
format_features: wgt::TextureFormatFeatures {
|
||||
allowed_usages: wgt::TextureUsage::RENDER_ATTACHMENT,
|
||||
flags: wgt::TextureFormatFeatureFlags::empty(),
|
||||
filterable: false,
|
||||
},
|
||||
dimension: wgt::TextureViewDimension::D2,
|
||||
extent: wgt::Extent3d {
|
||||
width: sc.desc.width,
|
||||
height: sc.desc.height,
|
||||
depth: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
samples: 1,
|
||||
framebuffer_attachment: sc.framebuffer_attachment.clone(),
|
||||
sampled_internal_use: resource::TextureUse::empty(),
|
||||
selector: TextureSelector {
|
||||
layers: 0..1,
|
||||
levels: 0..1,
|
||||
|
@ -195,9 +215,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
};
|
||||
|
||||
let ref_count = view.life_guard.add_ref();
|
||||
let id = hub
|
||||
.texture_views
|
||||
.register_identity(view_id_in, view, &mut token);
|
||||
let id = fid.assign(view, &mut token);
|
||||
|
||||
if sc.acquired_view_id.is_some() {
|
||||
return Err(SwapChainError::AlreadyAcquired);
|
||||
|
@ -213,14 +231,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
None => None,
|
||||
};
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref trace) = device.trace {
|
||||
trace.lock().add(Action::GetSwapChainTexture {
|
||||
id: view_id,
|
||||
parent_id: swap_chain_id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SwapChainOutput { status, view_id })
|
||||
}
|
||||
|
||||
|
@ -249,19 +259,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
trace.lock().add(Action::PresentSwapChain(swap_chain_id));
|
||||
}
|
||||
|
||||
let view_id = sc
|
||||
.acquired_view_id
|
||||
.take()
|
||||
.ok_or(SwapChainError::AlreadyAcquired)?;
|
||||
let (view_maybe, _) = hub.texture_views.unregister(view_id.value.0, &mut token);
|
||||
let view = view_maybe.ok_or(SwapChainError::Invalid)?;
|
||||
let view = {
|
||||
let view_id = sc
|
||||
.acquired_view_id
|
||||
.take()
|
||||
.ok_or(SwapChainError::AlreadyAcquired)?;
|
||||
let (view_maybe, _) = hub.texture_views.unregister(view_id.value.0, &mut token);
|
||||
view_maybe.ok_or(SwapChainError::Invalid)?
|
||||
};
|
||||
if view.life_guard.ref_count.unwrap().load() != 1 {
|
||||
return Err(SwapChainError::StillReferenced);
|
||||
}
|
||||
let image = match view.inner {
|
||||
resource::TextureViewInner::Native { .. } => unreachable!(),
|
||||
resource::TextureViewInner::SwapChain { image, .. } => image,
|
||||
};
|
||||
|
||||
let sem = if sc.active_submission_index > device.last_completed_submission_index() {
|
||||
Some(&sc.semaphore)
|
||||
Some(&mut sc.semaphore)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -270,12 +285,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
|
||||
tracing::debug!(trace = true, "Presented. End of Frame");
|
||||
|
||||
for fbo in sc.acquired_framebuffers.drain(..) {
|
||||
unsafe {
|
||||
device.raw.destroy_framebuffer(fbo);
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(None) => Ok(SwapChainStatus::Good),
|
||||
Ok(Some(_)) => Ok(SwapChainStatus::Suboptimal),
|
||||
|
@ -283,7 +292,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
hal::window::PresentError::OutOfMemory(_) => {
|
||||
Err(SwapChainError::Device(DeviceError::OutOfMemory))
|
||||
}
|
||||
hal::window::PresentError::OutOfDate => Ok(SwapChainStatus::Outdated),
|
||||
hal::window::PresentError::OutOfDate(_) => Ok(SwapChainStatus::Outdated),
|
||||
hal::window::PresentError::SurfaceLost(_) => Ok(SwapChainStatus::Lost),
|
||||
hal::window::PresentError::DeviceLost(_) => {
|
||||
Err(SwapChainError::Device(DeviceError::Lost))
|
||||
|
|
|
@ -315,6 +315,18 @@ impl<S: ResourceState> ResourceTracker<S> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get<'a>(
|
||||
self_backend: wgt::Backend,
|
||||
map: &'a mut FastHashMap<Index, Resource<S>>,
|
||||
id: Valid<S::Id>,
|
||||
) -> &'a mut Resource<S> {
|
||||
let (index, epoch, backend) = id.0.unzip();
|
||||
debug_assert_eq!(self_backend, backend);
|
||||
let e = map.get_mut(&index).unwrap();
|
||||
assert_eq!(e.epoch, epoch);
|
||||
e
|
||||
}
|
||||
|
||||
/// Extend the usage of a specified resource.
|
||||
///
|
||||
/// Returns conflicting transition as an error.
|
||||
|
@ -345,6 +357,21 @@ impl<S: ResourceState> ResourceTracker<S> {
|
|||
self.temp.drain(..)
|
||||
}
|
||||
|
||||
/// Replace the usage of a specified already tracked resource.
|
||||
/// (panics if the resource is not yet tracked)
|
||||
pub(crate) fn change_replace_tracked(
|
||||
&mut self,
|
||||
id: Valid<S::Id>,
|
||||
selector: S::Selector,
|
||||
usage: S::Usage,
|
||||
) -> Drain<PendingTransition<S>> {
|
||||
let res = Self::get(self.backend, &mut self.map, id);
|
||||
res.state
|
||||
.change(id, selector, usage, Some(&mut self.temp))
|
||||
.ok();
|
||||
self.temp.drain(..)
|
||||
}
|
||||
|
||||
/// Turn the tracking from the "expand" mode into the "replace" one,
|
||||
/// installing the selected usage as the "first".
|
||||
/// This is a special operation only used by the render pass attachments.
|
||||
|
@ -370,7 +397,12 @@ impl<S: ResourceState> ResourceTracker<S> {
|
|||
e.insert(new.clone());
|
||||
}
|
||||
Entry::Occupied(e) => {
|
||||
assert_eq!(e.get().epoch, new.epoch);
|
||||
assert_eq!(
|
||||
e.get().epoch,
|
||||
new.epoch,
|
||||
"ID {:?} wasn't properly removed",
|
||||
S::Id::zip(index, e.get().epoch, self.backend)
|
||||
);
|
||||
let id = Valid(S::Id::zip(index, new.epoch, self.backend));
|
||||
e.into_mut().state.merge(id, &new.state, None)?;
|
||||
}
|
||||
|
@ -388,7 +420,12 @@ impl<S: ResourceState> ResourceTracker<S> {
|
|||
e.insert(new.clone());
|
||||
}
|
||||
Entry::Occupied(e) => {
|
||||
assert_eq!(e.get().epoch, new.epoch);
|
||||
assert_eq!(
|
||||
e.get().epoch,
|
||||
new.epoch,
|
||||
"ID {:?} wasn't properly removed",
|
||||
S::Id::zip(index, e.get().epoch, self.backend)
|
||||
);
|
||||
let id = Valid(S::Id::zip(index, new.epoch, self.backend));
|
||||
e.into_mut()
|
||||
.state
|
||||
|
@ -518,6 +555,7 @@ pub(crate) struct TrackerSet {
|
|||
pub compute_pipes: ResourceTracker<PhantomData<id::ComputePipelineId>>,
|
||||
pub render_pipes: ResourceTracker<PhantomData<id::RenderPipelineId>>,
|
||||
pub bundles: ResourceTracker<PhantomData<id::RenderBundleId>>,
|
||||
pub query_sets: ResourceTracker<PhantomData<id::QuerySetId>>,
|
||||
}
|
||||
|
||||
impl TrackerSet {
|
||||
|
@ -532,6 +570,7 @@ impl TrackerSet {
|
|||
compute_pipes: ResourceTracker::new(backend),
|
||||
render_pipes: ResourceTracker::new(backend),
|
||||
bundles: ResourceTracker::new(backend),
|
||||
query_sets: ResourceTracker::new(backend),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,6 +584,7 @@ impl TrackerSet {
|
|||
self.compute_pipes.clear();
|
||||
self.render_pipes.clear();
|
||||
self.bundles.clear();
|
||||
self.query_sets.clear();
|
||||
}
|
||||
|
||||
/// Try to optimize the tracking representation.
|
||||
|
@ -557,6 +597,7 @@ impl TrackerSet {
|
|||
self.compute_pipes.optimize();
|
||||
self.render_pipes.optimize();
|
||||
self.bundles.optimize();
|
||||
self.query_sets.optimize();
|
||||
}
|
||||
|
||||
/// Merge all the trackers of another instance by extending
|
||||
|
@ -584,6 +625,7 @@ impl TrackerSet {
|
|||
.unwrap();
|
||||
self.render_pipes.merge_extend(&other.render_pipes).unwrap();
|
||||
self.bundles.merge_extend(&other.bundles).unwrap();
|
||||
self.query_sets.merge_extend(&other.query_sets).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -207,26 +207,28 @@ impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> {
|
|||
(Some(&(ref ra, va)), Some(&(ref rb, vb))) => {
|
||||
let (range, usage) = if ra.start < self.base {
|
||||
// in the middle of the left stream
|
||||
if self.base == rb.start {
|
||||
let (end, end_value) = if self.base == rb.start {
|
||||
// right stream is starting
|
||||
debug_assert!(self.base < ra.end);
|
||||
(self.base..ra.end.min(rb.end), Some(*va)..Some(*vb))
|
||||
(rb.end, Some(*vb))
|
||||
} else {
|
||||
// right hasn't started yet
|
||||
debug_assert!(self.base < rb.start);
|
||||
(self.base..rb.start, Some(*va)..None)
|
||||
}
|
||||
(rb.start, None)
|
||||
};
|
||||
(self.base..ra.end.min(end), Some(*va)..end_value)
|
||||
} else if rb.start < self.base {
|
||||
// in the middle of the right stream
|
||||
if self.base == ra.start {
|
||||
let (end, start_value) = if self.base == ra.start {
|
||||
// left stream is starting
|
||||
debug_assert!(self.base < rb.end);
|
||||
(self.base..ra.end.min(rb.end), Some(*va)..Some(*vb))
|
||||
(ra.end, Some(*va))
|
||||
} else {
|
||||
// left hasn't started yet
|
||||
debug_assert!(self.base < ra.start);
|
||||
(self.base..ra.start, None..Some(*vb))
|
||||
}
|
||||
(ra.start, None)
|
||||
};
|
||||
(self.base..rb.end.min(end), start_value..Some(*vb))
|
||||
} else {
|
||||
// no active streams
|
||||
match ra.start.cmp(&rb.start) {
|
||||
|
@ -396,4 +398,40 @@ mod test {
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_complex() {
|
||||
assert_eq!(
|
||||
&easy_merge(
|
||||
&[
|
||||
(0..8, 0u8),
|
||||
(8..9, 1),
|
||||
(9..16, 2),
|
||||
(16..17, 3),
|
||||
(17..118, 4),
|
||||
(118..119, 5),
|
||||
(119..124, 6),
|
||||
(124..125, 7),
|
||||
(125..512, 8),
|
||||
],
|
||||
&[(15..16, 10u8), (51..52, 11), (126..127, 12),],
|
||||
),
|
||||
&[
|
||||
(0..8, Some(0)..None),
|
||||
(8..9, Some(1)..None),
|
||||
(9..15, Some(2)..None),
|
||||
(15..16, Some(2)..Some(10)),
|
||||
(16..17, Some(3)..None),
|
||||
(17..51, Some(4)..None),
|
||||
(51..52, Some(4)..Some(11)),
|
||||
(52..118, Some(4)..None),
|
||||
(118..119, Some(5)..None),
|
||||
(119..124, Some(6)..None),
|
||||
(124..125, Some(7)..None),
|
||||
(125..126, Some(8)..None),
|
||||
(126..127, Some(8)..Some(12)),
|
||||
(127..512, Some(8)..None),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "wgpu-types"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = ["wgpu developers"]
|
||||
edition = "2018"
|
||||
description = "WebGPU types"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,7 +4,7 @@
|
|||
|
||||
use crate::{
|
||||
cow_label, ByteBuf, CommandEncoderAction, DeviceAction, DropAction, ImplicitLayout, RawString,
|
||||
TextureAction,
|
||||
ShaderModuleSource, TextureAction,
|
||||
};
|
||||
|
||||
use wgc::{hub::IdentityManager, id};
|
||||
|
@ -17,9 +17,19 @@ use parking_lot::Mutex;
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
num::{NonZeroU32, NonZeroU8},
|
||||
ptr, slice,
|
||||
ptr,
|
||||
};
|
||||
|
||||
// we can't call `from_raw_parts` unconditionally because the caller
|
||||
// may not even have a valid pointer (e.g. NULL) if the `length` is zero.
|
||||
fn make_slice<'a, T>(pointer: *const T, length: usize) -> &'a [T] {
|
||||
if length == 0 {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { std::slice::from_raw_parts(pointer, length) }
|
||||
}
|
||||
}
|
||||
|
||||
fn make_byte_buf<T: serde::Serialize>(data: &T) -> ByteBuf {
|
||||
let vec = bincode::serialize(data).unwrap();
|
||||
ByteBuf::from_vec(vec)
|
||||
|
@ -27,6 +37,7 @@ fn make_byte_buf<T: serde::Serialize>(data: &T) -> ByteBuf {
|
|||
|
||||
#[repr(C)]
|
||||
pub struct ShaderModuleDescriptor {
|
||||
label: RawString,
|
||||
spirv_words: *const u32,
|
||||
spirv_words_length: usize,
|
||||
wgsl_chars: RawString,
|
||||
|
@ -51,39 +62,102 @@ impl ProgrammableStageDescriptor {
|
|||
pub struct ComputePipelineDescriptor {
|
||||
label: RawString,
|
||||
layout: Option<id::PipelineLayoutId>,
|
||||
compute_stage: ProgrammableStageDescriptor,
|
||||
stage: ProgrammableStageDescriptor,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct VertexBufferDescriptor {
|
||||
stride: wgt::BufferAddress,
|
||||
pub struct VertexBufferLayout {
|
||||
array_stride: wgt::BufferAddress,
|
||||
step_mode: wgt::InputStepMode,
|
||||
attributes: *const wgt::VertexAttributeDescriptor,
|
||||
attributes: *const wgt::VertexAttribute,
|
||||
attributes_length: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct VertexStateDescriptor {
|
||||
index_format: wgt::IndexFormat,
|
||||
vertex_buffers: *const VertexBufferDescriptor,
|
||||
vertex_buffers_length: usize,
|
||||
pub struct VertexState {
|
||||
stage: ProgrammableStageDescriptor,
|
||||
buffers: *const VertexBufferLayout,
|
||||
buffers_length: usize,
|
||||
}
|
||||
|
||||
impl VertexState {
|
||||
fn to_wgpu(&self) -> wgc::pipeline::VertexState {
|
||||
let buffer_layouts = make_slice(self.buffers, self.buffers_length)
|
||||
.iter()
|
||||
.map(|vb| wgc::pipeline::VertexBufferLayout {
|
||||
array_stride: vb.array_stride,
|
||||
step_mode: vb.step_mode,
|
||||
attributes: Cow::Borrowed(make_slice(vb.attributes, vb.attributes_length)),
|
||||
})
|
||||
.collect();
|
||||
wgc::pipeline::VertexState {
|
||||
stage: self.stage.to_wgpu(),
|
||||
buffers: Cow::Owned(buffer_layouts),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ColorTargetState<'a> {
|
||||
format: wgt::TextureFormat,
|
||||
blend: Option<&'a wgt::BlendState>,
|
||||
write_mask: wgt::ColorWrite,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FragmentState<'a> {
|
||||
stage: ProgrammableStageDescriptor,
|
||||
targets: *const ColorTargetState<'a>,
|
||||
targets_length: usize,
|
||||
}
|
||||
|
||||
impl FragmentState<'_> {
|
||||
fn to_wgpu(&self) -> wgc::pipeline::FragmentState {
|
||||
let color_targets = make_slice(self.targets, self.targets_length)
|
||||
.iter()
|
||||
.map(|ct| wgt::ColorTargetState {
|
||||
format: ct.format,
|
||||
blend: ct.blend.cloned(),
|
||||
write_mask: ct.write_mask,
|
||||
})
|
||||
.collect();
|
||||
wgc::pipeline::FragmentState {
|
||||
stage: self.stage.to_wgpu(),
|
||||
targets: Cow::Owned(color_targets),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PrimitiveState<'a> {
|
||||
topology: wgt::PrimitiveTopology,
|
||||
strip_index_format: Option<&'a wgt::IndexFormat>,
|
||||
front_face: wgt::FrontFace,
|
||||
cull_mode: Option<&'a wgt::Face>,
|
||||
polygon_mode: wgt::PolygonMode,
|
||||
}
|
||||
|
||||
impl PrimitiveState<'_> {
|
||||
fn to_wgpu(&self) -> wgt::PrimitiveState {
|
||||
wgt::PrimitiveState {
|
||||
topology: self.topology,
|
||||
strip_index_format: self.strip_index_format.cloned(),
|
||||
front_face: self.front_face.clone(),
|
||||
cull_mode: self.cull_mode.cloned(),
|
||||
polygon_mode: self.polygon_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RenderPipelineDescriptor<'a> {
|
||||
label: RawString,
|
||||
layout: Option<id::PipelineLayoutId>,
|
||||
vertex_stage: &'a ProgrammableStageDescriptor,
|
||||
fragment_stage: Option<&'a ProgrammableStageDescriptor>,
|
||||
primitive_topology: wgt::PrimitiveTopology,
|
||||
rasterization_state: Option<&'a wgt::RasterizationStateDescriptor>,
|
||||
color_states: *const wgt::ColorStateDescriptor,
|
||||
color_states_length: usize,
|
||||
depth_stencil_state: Option<&'a wgt::DepthStencilStateDescriptor>,
|
||||
vertex_state: VertexStateDescriptor,
|
||||
sample_count: u32,
|
||||
sample_mask: u32,
|
||||
alpha_to_coverage_enabled: bool,
|
||||
vertex: &'a VertexState,
|
||||
primitive: PrimitiveState<'a>,
|
||||
fragment: Option<&'a FragmentState<'a>>,
|
||||
depth_stencil: Option<&'a wgt::DepthStencilState>,
|
||||
multisample: wgt::MultisampleState,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -101,7 +175,6 @@ pub enum RawBindingType {
|
|||
StorageBuffer,
|
||||
ReadonlyStorageBuffer,
|
||||
Sampler,
|
||||
ComparisonSampler,
|
||||
SampledTexture,
|
||||
ReadonlyStorageTexture,
|
||||
WriteonlyStorageTexture,
|
||||
|
@ -118,6 +191,8 @@ pub struct BindGroupLayoutEntry<'a> {
|
|||
texture_sample_type: Option<&'a RawTextureSampleType>,
|
||||
multisampled: bool,
|
||||
storage_texture_format: Option<&'a wgt::TextureFormat>,
|
||||
sampler_filter: bool,
|
||||
sampler_compare: bool,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -323,7 +398,7 @@ pub unsafe extern "C" fn wgpu_client_make_adapter_ids(
|
|||
) -> usize {
|
||||
let mut identities = client.identities.lock();
|
||||
assert_ne!(id_length, 0);
|
||||
let mut ids = slice::from_raw_parts_mut(ids, id_length).iter_mut();
|
||||
let mut ids = std::slice::from_raw_parts_mut(ids, id_length).iter_mut();
|
||||
|
||||
*ids.next().unwrap() = identities.vulkan.adapters.alloc(Backend::Vulkan);
|
||||
|
||||
|
@ -339,6 +414,19 @@ pub unsafe extern "C" fn wgpu_client_make_adapter_ids(
|
|||
id_length - ids.len()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_client_fill_default_limits(limits: &mut wgt::Limits) {
|
||||
*limits = wgt::Limits::default();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_client_serialize_device_descriptor(
|
||||
desc: &wgt::DeviceDescriptor<RawString>,
|
||||
bb: &mut ByteBuf,
|
||||
) {
|
||||
*bb = make_byte_buf(&desc.map_label(cow_label));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_client_make_device_id(
|
||||
client: &Client,
|
||||
|
@ -504,12 +592,22 @@ pub extern "C" fn wgpu_client_create_command_encoder(
|
|||
id
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ComputePassDescriptor {
|
||||
pub label: RawString,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_command_encoder_begin_compute_pass(
|
||||
encoder_id: id::CommandEncoderId,
|
||||
_desc: Option<&wgc::command::ComputePassDescriptor>,
|
||||
desc: &ComputePassDescriptor,
|
||||
) -> *mut wgc::command::ComputePass {
|
||||
let pass = wgc::command::ComputePass::new(encoder_id);
|
||||
let pass = wgc::command::ComputePass::new(
|
||||
encoder_id,
|
||||
&wgc::command::ComputePassDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
},
|
||||
);
|
||||
Box::into_raw(Box::new(pass))
|
||||
}
|
||||
|
||||
|
@ -529,6 +627,7 @@ pub unsafe extern "C" fn wgpu_compute_pass_destroy(pass: *mut wgc::command::Comp
|
|||
|
||||
#[repr(C)]
|
||||
pub struct RenderPassDescriptor {
|
||||
pub label: RawString,
|
||||
pub color_attachments: *const wgc::command::ColorAttachmentDescriptor,
|
||||
pub color_attachments_length: usize,
|
||||
pub depth_stencil_attachment: *const wgc::command::DepthStencilAttachmentDescriptor,
|
||||
|
@ -541,8 +640,9 @@ pub unsafe extern "C" fn wgpu_command_encoder_begin_render_pass(
|
|||
) -> *mut wgc::command::RenderPass {
|
||||
let pass = wgc::command::RenderPass::new(
|
||||
encoder_id,
|
||||
wgc::command::RenderPassDescriptor {
|
||||
color_attachments: Cow::Borrowed(slice::from_raw_parts(
|
||||
&wgc::command::RenderPassDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
color_attachments: Cow::Borrowed(make_slice(
|
||||
desc.color_attachments,
|
||||
desc.color_attachments_length,
|
||||
)),
|
||||
|
@ -582,7 +682,7 @@ pub unsafe extern "C" fn wgpu_client_create_bind_group_layout(
|
|||
.alloc(backend);
|
||||
|
||||
let mut entries = Vec::with_capacity(desc.entries_length);
|
||||
for entry in slice::from_raw_parts(desc.entries, desc.entries_length) {
|
||||
for entry in make_slice(desc.entries, desc.entries_length) {
|
||||
entries.push(wgt::BindGroupLayoutEntry {
|
||||
binding: entry.binding,
|
||||
visibility: entry.visibility,
|
||||
|
@ -604,12 +704,8 @@ pub unsafe extern "C" fn wgpu_client_create_bind_group_layout(
|
|||
min_binding_size: entry.min_binding_size,
|
||||
},
|
||||
RawBindingType::Sampler => wgt::BindingType::Sampler {
|
||||
comparison: false,
|
||||
filtering: false,
|
||||
},
|
||||
RawBindingType::ComparisonSampler => wgt::BindingType::Sampler {
|
||||
comparison: true,
|
||||
filtering: false,
|
||||
comparison: entry.sampler_compare,
|
||||
filtering: entry.sampler_filter,
|
||||
},
|
||||
RawBindingType::SampledTexture => wgt::BindingType::Texture {
|
||||
//TODO: the spec has a bug here
|
||||
|
@ -669,7 +765,7 @@ pub unsafe extern "C" fn wgpu_client_create_pipeline_layout(
|
|||
|
||||
let wgpu_desc = wgc::binding_model::PipelineLayoutDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
bind_group_layouts: Cow::Borrowed(slice::from_raw_parts(
|
||||
bind_group_layouts: Cow::Borrowed(make_slice(
|
||||
desc.bind_group_layouts,
|
||||
desc.bind_group_layouts_length,
|
||||
)),
|
||||
|
@ -697,7 +793,7 @@ pub unsafe extern "C" fn wgpu_client_create_bind_group(
|
|||
.alloc(backend);
|
||||
|
||||
let mut entries = Vec::with_capacity(desc.entries_length);
|
||||
for entry in slice::from_raw_parts(desc.entries, desc.entries_length) {
|
||||
for entry in make_slice(desc.entries, desc.entries_length) {
|
||||
entries.push(wgc::binding_model::BindGroupEntry {
|
||||
binding: entry.binding,
|
||||
resource: if let Some(id) = entry.buffer {
|
||||
|
@ -741,16 +837,19 @@ pub unsafe extern "C" fn wgpu_client_create_shader_module(
|
|||
.shader_modules
|
||||
.alloc(backend);
|
||||
|
||||
assert!(!desc.spirv_words.is_null());
|
||||
let spv = Cow::Borrowed(if desc.spirv_words.is_null() {
|
||||
&[][..]
|
||||
} else {
|
||||
slice::from_raw_parts(desc.spirv_words, desc.spirv_words_length)
|
||||
});
|
||||
let source = match cow_label(&desc.wgsl_chars) {
|
||||
Some(code) => ShaderModuleSource::Wgsl(code),
|
||||
None => ShaderModuleSource::SpirV(Cow::Borrowed(make_slice(
|
||||
desc.spirv_words,
|
||||
desc.spirv_words_length,
|
||||
))),
|
||||
};
|
||||
let desc = wgc::pipeline::ShaderModuleDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
flags: wgt::ShaderFlags::VALIDATION, // careful here!
|
||||
};
|
||||
|
||||
let wgsl = cow_label(&desc.wgsl_chars).unwrap_or_default();
|
||||
|
||||
let action = DeviceAction::CreateShaderModule(id, spv, wgsl);
|
||||
let action = DeviceAction::CreateShaderModule(id, desc, source);
|
||||
*bb = make_byte_buf(&action);
|
||||
id
|
||||
}
|
||||
|
@ -770,7 +869,7 @@ pub unsafe extern "C" fn wgpu_client_create_compute_pipeline(
|
|||
let wgpu_desc = wgc::pipeline::ComputePipelineDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
layout: desc.layout,
|
||||
compute_stage: desc.compute_stage.to_wgpu(),
|
||||
stage: desc.stage.to_wgpu(),
|
||||
};
|
||||
|
||||
let implicit = match desc.layout {
|
||||
|
@ -804,42 +903,11 @@ pub unsafe extern "C" fn wgpu_client_create_render_pipeline(
|
|||
let wgpu_desc = wgc::pipeline::RenderPipelineDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
layout: desc.layout,
|
||||
vertex_stage: desc.vertex_stage.to_wgpu(),
|
||||
fragment_stage: desc
|
||||
.fragment_stage
|
||||
.map(ProgrammableStageDescriptor::to_wgpu),
|
||||
rasterization_state: desc.rasterization_state.cloned(),
|
||||
primitive_topology: desc.primitive_topology,
|
||||
color_states: Cow::Borrowed(slice::from_raw_parts(
|
||||
desc.color_states,
|
||||
desc.color_states_length,
|
||||
)),
|
||||
depth_stencil_state: desc.depth_stencil_state.cloned(),
|
||||
vertex_state: wgc::pipeline::VertexStateDescriptor {
|
||||
index_format: desc.vertex_state.index_format,
|
||||
vertex_buffers: {
|
||||
let vbufs = slice::from_raw_parts(
|
||||
desc.vertex_state.vertex_buffers,
|
||||
desc.vertex_state.vertex_buffers_length,
|
||||
);
|
||||
let owned = vbufs
|
||||
.iter()
|
||||
.map(|vb| wgc::pipeline::VertexBufferDescriptor {
|
||||
stride: vb.stride,
|
||||
step_mode: vb.step_mode,
|
||||
attributes: Cow::Borrowed(if vb.attributes.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(vb.attributes, vb.attributes_length)
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
Cow::Owned(owned)
|
||||
},
|
||||
},
|
||||
sample_count: desc.sample_count,
|
||||
sample_mask: desc.sample_mask,
|
||||
alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled,
|
||||
vertex: desc.vertex.to_wgpu(),
|
||||
fragment: desc.fragment.map(FragmentState::to_wgpu),
|
||||
primitive: desc.primitive.to_wgpu(),
|
||||
depth_stencil: desc.depth_stencil.cloned(),
|
||||
multisample: desc.multisample.clone(),
|
||||
};
|
||||
|
||||
let implicit = match desc.layout {
|
||||
|
@ -909,3 +977,14 @@ pub unsafe extern "C" fn wgpu_command_encoder_copy_texture_to_texture(
|
|||
let action = CommandEncoderAction::CopyTextureToTexture { src, dst, size };
|
||||
*bb = make_byte_buf(&action);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_render_pass_set_index_buffer(
|
||||
pass: &mut wgc::command::RenderPass,
|
||||
buffer: wgc::id::BufferId,
|
||||
index_format: wgt::IndexFormat,
|
||||
offset: wgt::BufferAddress,
|
||||
size: Option<wgt::BufferSize>,
|
||||
) {
|
||||
pass.set_index_buffer(buffer, index_format, offset, size);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ pub struct IdentityRecyclerFactory {
|
|||
free_render_bundle: extern "C" fn(id::RenderBundleId, FactoryParam),
|
||||
free_render_pipeline: extern "C" fn(id::RenderPipelineId, FactoryParam),
|
||||
free_compute_pipeline: extern "C" fn(id::ComputePipelineId, FactoryParam),
|
||||
free_query_set: extern "C" fn(id::QuerySetId, FactoryParam),
|
||||
free_buffer: extern "C" fn(id::BufferId, FactoryParam),
|
||||
free_texture: extern "C" fn(id::TextureId, FactoryParam),
|
||||
free_texture_view: extern "C" fn(id::TextureViewId, FactoryParam),
|
||||
|
@ -160,6 +161,16 @@ impl wgc::hub::IdentityHandlerFactory<id::ComputePipelineId> for IdentityRecycle
|
|||
}
|
||||
}
|
||||
}
|
||||
impl wgc::hub::IdentityHandlerFactory<id::QuerySetId> for IdentityRecyclerFactory {
|
||||
type Filter = IdentityRecycler<id::QuerySetId>;
|
||||
fn spawn(&self, _min_index: u32) -> Self::Filter {
|
||||
IdentityRecycler {
|
||||
fun: self.free_query_set,
|
||||
param: self.param,
|
||||
kind: "query_set",
|
||||
}
|
||||
}
|
||||
}
|
||||
impl wgc::hub::IdentityHandlerFactory<id::BufferId> for IdentityRecyclerFactory {
|
||||
type Filter = IdentityRecycler<id::BufferId>;
|
||||
fn spawn(&self, _min_index: u32) -> Self::Filter {
|
||||
|
|
|
@ -58,6 +58,12 @@ impl ByteBuf {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
enum ShaderModuleSource<'a> {
|
||||
SpirV(Cow<'a, [u32]>),
|
||||
Wgsl(Cow<'a, str>),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct ImplicitLayout<'a> {
|
||||
pipeline: id::PipelineLayoutId,
|
||||
|
@ -78,7 +84,11 @@ enum DeviceAction<'a> {
|
|||
wgc::binding_model::PipelineLayoutDescriptor<'a>,
|
||||
),
|
||||
CreateBindGroup(id::BindGroupId, wgc::binding_model::BindGroupDescriptor<'a>),
|
||||
CreateShaderModule(id::ShaderModuleId, Cow<'a, [u32]>, Cow<'a, str>),
|
||||
CreateShaderModule(
|
||||
id::ShaderModuleId,
|
||||
wgc::pipeline::ShaderModuleDescriptor<'a>,
|
||||
ShaderModuleSource<'a>,
|
||||
),
|
||||
CreateComputePipeline(
|
||||
id::ComputePipelineId,
|
||||
wgc::pipeline::ComputePipelineDescriptor<'a>,
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
use crate::{
|
||||
cow_label, identity::IdentityRecyclerFactory, ByteBuf, CommandEncoderAction, DeviceAction,
|
||||
DropAction, RawString, TextureAction,
|
||||
DropAction, RawString, ShaderModuleSource, TextureAction,
|
||||
};
|
||||
|
||||
use wgc::{gfx_select, id};
|
||||
|
||||
use std::{fmt::Display, os::raw::c_char, ptr, slice};
|
||||
use std::{error::Error, os::raw::c_char, ptr, slice};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ErrorBuffer {
|
||||
|
@ -18,9 +18,17 @@ pub struct ErrorBuffer {
|
|||
}
|
||||
|
||||
impl ErrorBuffer {
|
||||
fn init(&mut self, error: impl Display) {
|
||||
fn init(&mut self, error: impl Error) {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut string = format!("{}", error);
|
||||
let mut e = error.source();
|
||||
while let Some(source) = e {
|
||||
write!(string, ", caused by: {}", source).unwrap();
|
||||
e = source.source();
|
||||
}
|
||||
|
||||
assert_ne!(self.capacity, 0);
|
||||
let string = format!("{}", error);
|
||||
let length = if string.len() >= self.capacity {
|
||||
log::warn!(
|
||||
"Error length {} reached capacity {}",
|
||||
|
@ -105,24 +113,19 @@ pub unsafe extern "C" fn wgpu_server_instance_request_adapter(
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_server_fill_default_limits(limits: &mut wgt::Limits) {
|
||||
*limits = wgt::Limits::default();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wgpu_server_adapter_request_device(
|
||||
global: &Global,
|
||||
self_id: id::AdapterId,
|
||||
desc: &wgt::DeviceDescriptor<RawString>,
|
||||
byte_buf: &ByteBuf,
|
||||
new_id: id::DeviceId,
|
||||
mut error_buf: ErrorBuffer,
|
||||
) {
|
||||
let desc: wgc::device::DeviceDescriptor = bincode::deserialize(byte_buf.as_slice()).unwrap();
|
||||
let trace_string = std::env::var("WGPU_TRACE").ok();
|
||||
let trace_path = trace_string
|
||||
.as_ref()
|
||||
.map(|string| std::path::Path::new(string.as_str()));
|
||||
let desc = desc.map_label(cow_label);
|
||||
let (_, error) =
|
||||
gfx_select!(self_id => global.adapter_request_device(self_id, &desc, trace_path, new_id));
|
||||
if let Some(err) = error {
|
||||
|
@ -266,16 +269,14 @@ impl GlobalExt for Global {
|
|||
error_buf.init(err);
|
||||
}
|
||||
}
|
||||
DeviceAction::CreateShaderModule(id, spirv, wgsl) => {
|
||||
let desc = wgc::pipeline::ShaderModuleDescriptor {
|
||||
label: None, //TODO
|
||||
source: if spirv.is_empty() {
|
||||
wgc::pipeline::ShaderModuleSource::Wgsl(wgsl)
|
||||
} else {
|
||||
wgc::pipeline::ShaderModuleSource::SpirV(spirv)
|
||||
},
|
||||
DeviceAction::CreateShaderModule(id, desc, source) => {
|
||||
let source = match source {
|
||||
ShaderModuleSource::SpirV(data) => {
|
||||
wgc::pipeline::ShaderModuleSource::SpirV(data)
|
||||
}
|
||||
ShaderModuleSource::Wgsl(data) => wgc::pipeline::ShaderModuleSource::Wgsl(data),
|
||||
};
|
||||
let (_, error) = self.device_create_shader_module::<B>(self_id, &desc, id);
|
||||
let (_, error) = self.device_create_shader_module::<B>(self_id, &desc, source, id);
|
||||
if let Some(err) = error {
|
||||
error_buf.init(err);
|
||||
}
|
||||
|
@ -401,6 +402,34 @@ impl GlobalExt for Global {
|
|||
error_buf.init(err);
|
||||
}
|
||||
}
|
||||
CommandEncoderAction::WriteTimestamp {
|
||||
query_set_id,
|
||||
query_index,
|
||||
} => {
|
||||
if let Err(err) =
|
||||
self.command_encoder_write_timestamp::<B>(self_id, query_set_id, query_index)
|
||||
{
|
||||
error_buf.init(err);
|
||||
}
|
||||
}
|
||||
CommandEncoderAction::ResolveQuerySet {
|
||||
query_set_id,
|
||||
start_query,
|
||||
query_count,
|
||||
destination,
|
||||
destination_offset,
|
||||
} => {
|
||||
if let Err(err) = self.command_encoder_resolve_query_set::<B>(
|
||||
self_id,
|
||||
query_set_id,
|
||||
start_query,
|
||||
query_count,
|
||||
destination,
|
||||
destination_offset,
|
||||
) {
|
||||
error_buf.init(err);
|
||||
}
|
||||
}
|
||||
CommandEncoderAction::RunRenderPass {
|
||||
base,
|
||||
target_colors,
|
||||
|
@ -604,7 +633,7 @@ pub extern "C" fn wgpu_server_texture_drop(global: &Global, self_id: id::Texture
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wgpu_server_texture_view_drop(global: &Global, self_id: id::TextureViewId) {
|
||||
gfx_select!(self_id => global.texture_view_drop(self_id)).unwrap();
|
||||
gfx_select!(self_id => global.texture_view_drop(self_id, false)).unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"9dcf05b4a1c295a2775190bcb1df8cb3ffe8f39a5e91ea551d7af84793f941e7","README.md":"4708fe5036c6bb6902f1b2d191a99117ba436ffec5678a0dfc6c99a9da5f2f1c","appveyor.yml":"14da30f5712e0eaec1acbb74649f391597095d8f8aaccf7a528a358936e8f98b","bors.toml":"31d348faf24d2bac765198bce62b4c4f1d6987e567eaed3d380a6704fec5183c","src/com.rs":"874a6ecea743a1f37f10649d71850cad807bd87cce883479cdb106d57aea863b","src/command_allocator.rs":"cda791b138019bae082fe664bd735de1e5fa6b4ee979e180e1945d50b4858ef0","src/command_list.rs":"a50a8bdebd859cfbd64c02640468665c221bb107b3ae5c1a30a1de20f4e7a299","src/debug.rs":"b26d102c033933c7935cedadfa3ea69e6b4ab95d58d5165911debec729b8bdb5","src/descriptor.rs":"93b4f24565494fb1aecf5cc8f677d3fc56bbaf742717b77d9846259fa300891e","src/device.rs":"2738fce770e3392c263f2745d1bdcb40b80b60288fb08e4904419000a85bffed","src/dxgi.rs":"93547cdf0c90dd0681b5b5dfa3ebcb6f9764728537286b546d708b4e281bad06","src/heap.rs":"ee397804e083d04486fc6e48d71acdce341ee717cc19fa5c370455d7bf7c042b","src/lib.rs":"d1421cacbdc80528eb1086a6bb1d778afd70b2746ba4ab905d9c067179601e41","src/pso.rs":"073a936f7004c813b2e19fe3c5a541d0554327c598ef6aeadb774cd3c78e9743","src/query.rs":"ea36425db9a27422c361c706f3521341fa3a6fe34ef2d211ff7cfbe792c3f93b","src/queue.rs":"d0cbecfb3e538dd37e573a76a4bd2c78cde33b17c96af5b94672f992a195ede6","src/resource.rs":"cbe66c54ba11c994f644235b725402c7180113d0ed965f1878d64f824cd437df","src/sync.rs":"a6921a1f64eb0153e52e22c3c1cc12c7381c2823ed47a0f7de5834f14f3acd2b"},"package":"bc7ed48e89905e5e146bcc1951cc3facb9e44aea9adf5dc01078cda1bd24b662"}
|
||||
{"files":{"CHANGELOG.md":"d83cbbe16898f7e60c52bc464361bf2a7c29d0158f2095e23b714691cdbd959e","Cargo.toml":"ec2a1bf3f22f60f112ac6da06efd2146d2fba23c80d70dc74ac9dee50247083b","README.md":"71f2c62c9f9a892b436adf130dab47348744ea05c98af95599d2e79b54fb25a5","appveyor.yml":"69e6279a533b60e4f7ba70e645a9c6b7aba1654105a1e1362e67ed14deca5368","bors.toml":"366ea95cdc64dae238edd4fe70a3d5f698b4dd22064abeb8d91550c81c8ccc67","rustfmt.toml":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/com.rs":"1610750dcf9bd9b4ca469d37675ddbad286880e2b187952df4af0b83641bf4c7","src/command_allocator.rs":"b430fa99c7c541f5e592d6e9b8c83265b44f3f1b70205f9d44b4519e2d161cea","src/command_list.rs":"e3e6d23943827ee7d1daf82a97cdb361a6b4f1c138bbc84e18be995d66ce778c","src/debug.rs":"6d04c96fa2073ca8e3a4c29088c4f654c9bbd4944021196a209166ecf21520f9","src/descriptor.rs":"a9dd3534d8d79f8c52d0b3c3e6e2b7e218e2a4ffc915841f8270e67a8769ef4d","src/device.rs":"b4ac053d9a85d070d049eac7f243edae7afceb5c9f6d75ae1faddc2ec2875ca9","src/dxgi.rs":"36251ec2d55009796070df53591b3129e1eccadeeb0442818bc5d81065305050","src/heap.rs":"bb4c0996c63da3dc14366aaa23068e7a3d2fb43d617f8645a5aef74767a463d6","src/lib.rs":"21b72a9ef5ee0f4ad1fb1879e6e117568804de7ed01933739274e48a1c0d324d","src/pso.rs":"1dcf102f061a3cadfc0de3fd75e2414f06c1bf9ac5727be1cbdd2204883093e4","src/query.rs":"53f64ef6f2212a1367f248191e4ab93a3facb18c37709f05f850c30bdc7be8cf","src/queue.rs":"3cd807b1df00ef9dd6ba5a28dcee883033ea174d3715b6de754c6f890159302a","src/resource.rs":"9a0f53f8f23fd671ae44370413274606ce62942bb16fc7370e6f32a2410b4255","src/sync.rs":"dcce20cfd2a408ad43ad6765a91d65dfe1998615cb56a3cfb60306094f2365a8"},"package":null}
|
|
@ -0,0 +1,17 @@
|
|||
# Change Log
|
||||
|
||||
## v0.3.1 (2020-07-07)
|
||||
- create shader from IL
|
||||
- fix default doc target
|
||||
- debug impl for root descriptors
|
||||
|
||||
## v0.3.0 (2019-11-01)
|
||||
- resource transitions
|
||||
- dynamic library loading
|
||||
|
||||
## v0.2.2 (2019-10-04)
|
||||
- add `D3DHeap`
|
||||
- add root descriptor
|
||||
|
||||
## v0.1.0 (2018-12-26)
|
||||
- basic version
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче