diff --git a/.cargo/config.in b/.cargo/config.in index d12318b6f360f..c90bb37848147 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -85,12 +85,12 @@ rev = "3484d3e3ebdc8931493aa5df4d7ee9360a90e76b" [source."https://github.com/gfx-rs/wgpu"] git = "https://github.com/gfx-rs/wgpu" replace-with = "vendored-sources" -rev = "b51fd851" +rev = "32af4f56" [source."https://github.com/gfx-rs/naga"] git = "https://github.com/gfx-rs/naga" replace-with = "vendored-sources" -rev = "1aa91549" +rev = "571302e" [source."https://github.com/gfx-rs/metal-rs"] git = "https://github.com/gfx-rs/metal-rs" diff --git a/Cargo.lock b/Cargo.lock index 47c0f80e5779b..c03b62eefa483 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,7 +3456,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664" [[package]] name = "naga" version = "0.8.0" -source = "git+https://github.com/gfx-rs/naga?rev=1aa91549#1aa9154964238af8c692cf521ff90e1f2395e147" +source = "git+https://github.com/gfx-rs/naga?rev=571302e#571302e3ff09cb856f63a3683da308159872b7cc" dependencies = [ "bit-set", "bitflags", @@ -5913,9 +5913,10 @@ dependencies = [ [[package]] name = "wgpu-core" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=b51fd851#b51fd851be51cfe40c937ef789a44244e7dc2971" +source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", + "bit-vec", "bitflags", "cfg_aliases", "codespan-reporting", @@ -5936,7 +5937,7 @@ dependencies = [ [[package]] name = "wgpu-hal" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=b51fd851#b51fd851be51cfe40c937ef789a44244e7dc2971" +source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721" dependencies = [ "arrayvec", "ash", @@ -5973,7 +5974,7 @@ dependencies = [ [[package]] name = "wgpu-types" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=b51fd851#b51fd851be51cfe40c937ef789a44244e7dc2971" +source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721" dependencies = [ "bitflags", "bitflags_serde_shim", diff --git a/dom/webgpu/ipc/WebGPUParent.cpp b/dom/webgpu/ipc/WebGPUParent.cpp index 63e9f7175661a..353027c98449f 100644 --- a/dom/webgpu/ipc/WebGPUParent.cpp +++ b/dom/webgpu/ipc/WebGPUParent.cpp @@ -383,10 +383,10 @@ ipc::IPCResult WebGPUParent::RecvBufferMap(RawId aSelfId, auto* request = new MapRequest(mContext.get(), aSelfId, aHostMap, aOffset, std::move(shmem), std::move(aResolver)); - ffi::WGPUBufferMapOperation mapOperation = { - aHostMap, &MapCallback, reinterpret_cast(request)}; - ffi::wgpu_server_buffer_map(mContext.get(), aSelfId, aOffset, aSize, - mapOperation); + ffi::WGPUBufferMapCallbackC callback = {&MapCallback, + reinterpret_cast(request)}; + ffi::wgpu_server_buffer_map(mContext.get(), aSelfId, aOffset, aSize, aHostMap, + callback); return IPC_OK(); } @@ -823,11 +823,10 @@ ipc::IPCResult WebGPUParent::RecvSwapChainPresent( data, }; - ffi::WGPUBufferMapOperation mapOperation = { - ffi::WGPUHostMap_Read, &PresentCallback, - reinterpret_cast(presentRequest)}; + ffi::WGPUBufferMapCallbackC callback = { + &PresentCallback, reinterpret_cast(presentRequest)}; ffi::wgpu_server_buffer_map(mContext.get(), bufferId, 0, bufferSize, - mapOperation); + ffi::WGPUHostMap_Read, callback); return IPC_OK(); } diff --git a/gfx/wgpu_bindings/Cargo.toml b/gfx/wgpu_bindings/Cargo.toml index 19bb05e7371c9..af82fe402113d 100644 --- a/gfx/wgpu_bindings/Cargo.toml +++ b/gfx/wgpu_bindings/Cargo.toml @@ -17,7 +17,7 @@ default = [] [dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "b51fd851" +rev = "32af4f56" #Note: "replay" shouldn't ideally be needed, # but it allows us to serialize everything across IPC. features = ["replay", "trace", "serial-pass"] @@ -25,12 +25,12 @@ features = ["replay", "trace", "serial-pass"] [dependencies.wgt] package = "wgpu-types" git = "https://github.com/gfx-rs/wgpu" -rev = "b51fd851" +rev = "32af4f56" [dependencies.wgh] package = "wgpu-hal" git = "https://github.com/gfx-rs/wgpu" -rev = "b51fd851" +rev = "32af4f56" [dependencies] bincode = "1" diff --git a/gfx/wgpu_bindings/moz.yaml b/gfx/wgpu_bindings/moz.yaml index c3fa3b260ec24..6f8d95030635e 100644 --- a/gfx/wgpu_bindings/moz.yaml +++ b/gfx/wgpu_bindings/moz.yaml @@ -20,11 +20,11 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: commit 0b61a191 + release: commit 32af4f56 # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 0b61a191 + revision: 32af4f56 license: ['MIT', 'Apache-2.0'] diff --git a/gfx/wgpu_bindings/src/server.rs b/gfx/wgpu_bindings/src/server.rs index 17bedbf89b684..c86500c65fdad 100644 --- a/gfx/wgpu_bindings/src/server.rs +++ b/gfx/wgpu_bindings/src/server.rs @@ -222,14 +222,23 @@ pub extern "C" fn wgpu_server_device_create_buffer( } } +/// # Safety +/// +/// Callers are responsible for ensuring `callback` is well-formed. #[no_mangle] -pub extern "C" fn wgpu_server_buffer_map( +pub unsafe extern "C" fn wgpu_server_buffer_map( global: &Global, buffer_id: id::BufferId, start: wgt::BufferAddress, size: wgt::BufferAddress, - operation: wgc::resource::BufferMapOperation, + map_mode: wgc::device::HostMap, + callback: wgc::resource::BufferMapCallbackC, ) { + let callback = wgc::resource::BufferMapCallback::from_c(callback); + let operation = wgc::resource::BufferMapOperation { + host: map_mode, + callback + }; gfx_select!(buffer_id => global.buffer_map_async( buffer_id, start .. start + size, diff --git a/third_party/rust/naga/.cargo-checksum.json b/third_party/rust/naga/.cargo-checksum.json index a3dc03922ef44..0640e9c892ef3 100644 --- a/third_party/rust/naga/.cargo-checksum.json +++ b/third_party/rust/naga/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".github/workflows/lazy.yml":"efffd9aafa5e1fbe8c1746035e31523c5819348116a6b982ab6ab39a8c887c78",".github/workflows/pipeline.yml":"53cfa6363710bea4a97360a9667b34b507cc9212623b8a068cb34ec60bce275d",".github/workflows/validation-linux.yml":"797389222960f54d3da8d58b017c44be1b5f45033d8f635c17172bd79a975dbd",".github/workflows/validation-macos.yml":"ace910e819b4b7f4c3bcef0f6b8109bdf9fa817806b125605bd5f860c375d77e",".github/workflows/validation-windows.yml":"3717d69c8c21b379a40a6ff5a19dff18f06c56b767b3884565ecda0ddbe54493","CHANGELOG.md":"42278a1a5df5d79308713e5a30798bb9eb32bd3f2301e3993fd7b9474b4dac5b","Cargo.toml":"cb829197f23cad2d396bf9c6fb531656e81c2f30031c46a2129ea483976c2f75","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"ca3be8518f5ef097669cea882643fc532025f29972def4fda49df885565a0480","Makefile":"f4fce2a22f5d285e2682b95c863a26e64915eee856c221a1274462d1fe0774e7","README.md":"5d2d75de1537b91114ff5fb71888c2bad104c95ec11b105dae1617f8bded4344","benches/criterion.rs":"470d09d115eabb7f4761f73365aec9cf417f3445193aba124cec3704dda45730","src/arena.rs":"eb83c22f41d85fab5d63345fb7dd5f7cf6c88d92f463d56c839264cb28c86947","src/back/dot/mod.rs":"59a44df6776e48e6695065da517a1b5bfdd50b4b031ca3c4a0a9b25860122198","src/back/glsl/features.rs":"0b0119eccba497f645a039f59364c50658d67fbd37a8bffe7242364bf37a4e28","src/back/glsl/keywords.rs":"3f23b1e63e99a7056c3b223524d5d37000ef7316ae9df25532a726a1157d1dcd","src/back/glsl/mod.rs":"bf3c8a7b0cc340a420a010e868d3f564c0094d3257fccea3b869cd312664036e","src/back/hlsl/conv.rs":"46097319c63a0ae04ed4d6e86c5d1321c45d53bdf324440fa1588ec4990b2385","src/back/hlsl/help.rs":"ce00a92bca057822b092130a2b5d9fa9ee20758bceaa7f5d7c39c8b0c1655295","src/back/hlsl/keywords.rs":"a158c52bf9a05514b5a6e29658c2872c1cf2a6f295e8094a6ac4a37f76a761ad","src/back/hlsl/mod.rs":"385078b0f10e2a502800c30a5335b8eac3c7df3cb122540ccc41ed37a8c462f5","src/back/hlsl/storage.rs":"3c72fc47db16c46ac60ca4623ac5b7fe6459ebb379840840aea1989806a51b51","src/back/hlsl/writer.rs":"2ab602ada766eaf083030f326dc6644ab84d6a82cea78dd3d457c069e11f0c6e","src/back/mod.rs":"e6f578828897c4264b553ff0842f41d4ae748429f80f6e161dffc17109e49e86","src/back/msl/keywords.rs":"e1c2bf804b1a3a56639bedb9c1b3b78fbf920724ef65f91fe2e9fe476ee3fbbf","src/back/msl/mod.rs":"be992959cc7aa58a8a30ab936ce65e432de92ab9bfba4bf2b5368bdfe63bcb27","src/back/msl/sampler.rs":"210cf28143ac17d6c56a9adf4fc0e3b85b1b16bb8b94771f9304f2a43b7ce78e","src/back/msl/writer.rs":"6b5d77384950332d0eb9f1604a959b0737b2ce2b95e4ae5b7507900c9000330e","src/back/spv/block.rs":"17dad23bd30288c627c14a0b1ab018eb8c393e6d5fff8ba68321939bada4d61e","src/back/spv/helpers.rs":"0ec3381f3ba5856a14d876559f77e21fd5d2f342cb005343a3df2ff58443e4fb","src/back/spv/image.rs":"32e6bf0a23fd0383c12bece8ffb1983a68ebc2d5421224f44b74d136981cf5a4","src/back/spv/index.rs":"7a9815bdb1f323a780d41092371582f6d4210f305bec5c74e882dbb6d2d49bc3","src/back/spv/instructions.rs":"42eb1afad17cd311d89cbf49813171848a417543195df571ba0f3de1633efc8b","src/back/spv/layout.rs":"e263de53cd2f9a03ad94b82b434ce636609bc1ed435a2d1132951663bfaa8ebd","src/back/spv/mod.rs":"0f5a5a5c10c5a301fbbcb30857156bd6b963d3a8752ea02050446cd711a5c1d6","src/back/spv/recyclable.rs":"9448eedf9766ae2b1916282b5945b218017c6067dceac2da1415580ee5796c73","src/back/spv/selection.rs":"81e404abfa0a977f7c1f76ccb37a78d13ccadbda229048dad53cc67687cc39db","src/back/spv/writer.rs":"3158aee4219bb8530705a96c48a02b8b2debc5cb681858348f330ad36a961129","src/back/wgsl/mod.rs":"2dd12bbea9ace835850192bb68c5760953da6bac6a636073d1eca19381c0c0b6","src/back/wgsl/writer.rs":"52486ff800bb86d1afb4cfd1367f3e92db1cd8028d99d77da7894b1a4ee5aba4","src/block.rs":"89c061206f608365ef3ce03c0d205d7205ef9d892035dd3dfb8d252003159f14","src/front/glsl/ast.rs":"3a1521f28f00d73087a04cb16e23bf44a33e4915137bccaab2bffc39ac2a4a2d","src/front/glsl/builtins.rs":"5c8823324a32fa3a580932ff0bc5bb8f16a9d65e78c83ada8b75b38ff46512eb","src/front/glsl/constants.rs":"2347435706da20f8ef2533eea2ed5839f40ab2b1ed1d3e9803c5f27a5d24afc5","src/front/glsl/context.rs":"5361993795c380fb2daa105e5f4bc586443f33b7231112e137247a30773451db","src/front/glsl/error.rs":"6e049db79e59cffcd79bce3ac259f781062ab72e752f95d42f94c5a72efa1710","src/front/glsl/functions.rs":"5fb2523099589df3b62904c8e1c2844f2fc01a2a1d8bdddb2ab035fadd2489b7","src/front/glsl/lex.rs":"08736ae8beb955da5b0e6e3e0f45995a824995f7096d516a2910417e9c7afa32","src/front/glsl/mod.rs":"e39041fcf9d0777a847d12796e06907e8324988b459ec0a9ceb0cfc076f6843c","src/front/glsl/offset.rs":"098f37b97112931517521c88825497a4ea6435c3ddcb5608be04a2b4f5498900","src/front/glsl/parser.rs":"b1902eff3d9ac1cc328ddfcc2d0093cdb5f8c3e8d86390f5d1b35cf326afb61f","src/front/glsl/parser/declarations.rs":"f177b239f0ca9189458c2feb69219b94f2777a837b240ff4cc8b278b0291a311","src/front/glsl/parser/expressions.rs":"9b9979e5a32bfb825871dbb5d01b3e6e64062f30096f0140e01035283353998b","src/front/glsl/parser/functions.rs":"69f2baeba529a37ba9c6ad8e3bdcb84c7be0268ff0cb3229bffc72d5168646ee","src/front/glsl/parser/types.rs":"ab09449ec40f3b5c24b16488b23dbf3b147d132beb6188ccb2068a54c0128bb8","src/front/glsl/parser_tests.rs":"4256fdce5e5de7234a173ecd0dc77eef20052932b0d298c1227b40bd6a7f6a28","src/front/glsl/token.rs":"42325adbef5bfc9e6f9e40e7ed3cbfa7ef498c05799fdb7694b123e6450410e1","src/front/glsl/types.rs":"2bd622e300596cca21f5ded67909bf88649c35d3805a659df95dd363c60c0d29","src/front/glsl/variables.rs":"35055f3618e2738224b29eef1c43f4f10a92e8d0c37263998268e0ed484c73e9","src/front/interpolator.rs":"1f19f8634c639981d83aa8aa132ca5634bdc99830212eec3eac74c856bd25274","src/front/mod.rs":"30a1d62f05d79fb5e6afc1e5ebb4aec84eed1c1a60b991fae9b0eb6e361ccca0","src/front/spv/convert.rs":"c9a77d6eed3ce56ffef14380da16cc4bc0bb4ed1f270bd08e48b6c02c9b76cab","src/front/spv/error.rs":"1d0acaf179e2b71df87b7eee9aa9cc78e60fda86866969795f9b79111748124c","src/front/spv/function.rs":"4bc19456976ae38a91b32bd3d89d098998d4f516f706a389d6ea588d1ed2a3e6","src/front/spv/image.rs":"88a041f0cb1e26a5ca90f7419ebf712149689560fd5806f313dfeda580466203","src/front/spv/mod.rs":"0bbdeda2724519325e7c4b33445493c88a870806867e10d8064dd8564827d07e","src/front/spv/null.rs":"82ecc41359a18a3bcf3c0cc44692e34c2f3a5d640c2d04b5b5a57b025090a594","src/front/wgsl/construction.rs":"73aca5fe66040393ae9b08b9e1d04d34115d58b7678d7df67fbd62b47d981a21","src/front/wgsl/conv.rs":"6ce908d5f3562b50750afd0b082627613f0336cc8cb2f8b58441ddee0e30f130","src/front/wgsl/lexer.rs":"3b7a88a6d40e7f31df8a39c595b0d5497e0a1ad6e3e2ee60418504e225eb5fe6","src/front/wgsl/mod.rs":"d47d619a7aec643e8e92a8daaaad33da952293f3a8048a0f75473f452d0b95a4","src/front/wgsl/number_literals.rs":"7d0790ae9704c679230c4a1961a3bb101a7270aab0c54ddc5b17c5c4ed77f395","src/front/wgsl/tests.rs":"67579b7374771fa79c50feb83415d62febbd64ff0b0a5797ff2f701b27efe111","src/keywords/mod.rs":"0138f3931f8af0b0a05174549d0fd2152945b027dc3febefc1bbd676581d2e45","src/keywords/wgsl.rs":"5d7df2d6dc1949d6d9b34bdc4d97f64ee8968d30afe9bf6e4a56ee3bdec3c364","src/lib.rs":"87b4b4a02fee512b7e8c455af8fb58930f311817c2bc9808d1f797466ab25ce2","src/proc/index.rs":"9f1be774ac7e47b899066116d292604faed0acae5751628869c7767b919b71aa","src/proc/layouter.rs":"bd2b64a8f1f4113faee356ff1df53f7d44743679b4046659748ede60d638265d","src/proc/mod.rs":"edf9b0fcb2352ba928167e40ac71bf0be47aef2c3d79192469eecdbebb53dd79","src/proc/namer.rs":"12d946cbc5ed44f6607f9406f1cecbf24a3bda97c3de12b8dd51d0a95e17ac6e","src/proc/terminator.rs":"40d22f4c821d24e972403a31ded5dae6078d3dc33b52a9b0993396bdf77f8d1d","src/proc/typifier.rs":"a5866b9eb74c7d9f9cddb1422c4192eea210f0169a505afe3b1a7cfa4874c828","src/span.rs":"ff1f43d8a1373db770c16ffe8f628861e47d14faaf981c85de5321be8bacdf57","src/valid/analyzer.rs":"b06a71b11abecbcc2ec83b0cf90320fded804281fe18810ffd773dbb5565d9c1","src/valid/compose.rs":"e4c029db8d07ca5e11d4e7e59d8885634856872b595a166e266a3ce0342ccdcd","src/valid/expression.rs":"ef9950f46ec05039aa620ae5e7a1eeb3ac091bcf53e4ccf6cba9163d3a266a5d","src/valid/function.rs":"dde9745837655ddc84017eddb758af17a7c9a9a196e071ca528c7fbac836c567","src/valid/interface.rs":"56a7e44b0aef36e1f1b4d93704607ebe2b6c25d4e2837fd60f89d0bc39629053","src/valid/mod.rs":"27fefd427b142106ed022c3c06e1874edf9ea96c285e7c39b688ab5f36dab605","src/valid/type.rs":"630585310135ce5ebf18ccab3ec7373120d23dbd704ca0441205abf7b3c2b8d0"},"package":null} \ No newline at end of file +{"files":{".github/workflows/lazy.yml":"efffd9aafa5e1fbe8c1746035e31523c5819348116a6b982ab6ab39a8c887c78",".github/workflows/pipeline.yml":"53cfa6363710bea4a97360a9667b34b507cc9212623b8a068cb34ec60bce275d",".github/workflows/validation-linux.yml":"797389222960f54d3da8d58b017c44be1b5f45033d8f635c17172bd79a975dbd",".github/workflows/validation-macos.yml":"ace910e819b4b7f4c3bcef0f6b8109bdf9fa817806b125605bd5f860c375d77e",".github/workflows/validation-windows.yml":"3717d69c8c21b379a40a6ff5a19dff18f06c56b767b3884565ecda0ddbe54493","CHANGELOG.md":"42278a1a5df5d79308713e5a30798bb9eb32bd3f2301e3993fd7b9474b4dac5b","Cargo.toml":"8c06729a81401a5eeebd71e363af9205c7df3a41ea02b8047e547d4e6a8efb7d","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"ca3be8518f5ef097669cea882643fc532025f29972def4fda49df885565a0480","Makefile":"f4fce2a22f5d285e2682b95c863a26e64915eee856c221a1274462d1fe0774e7","README.md":"d34eadc118913060bf258383a4ee35a284a5fbc9753e60c5e3d4d1b9e81c7304","benches/criterion.rs":"470d09d115eabb7f4761f73365aec9cf417f3445193aba124cec3704dda45730","src/arena.rs":"88e6357932288b7d26ecfc9435a6c98847c63587dde8bb0675f25da627cf8a15","src/back/dot/mod.rs":"59a44df6776e48e6695065da517a1b5bfdd50b4b031ca3c4a0a9b25860122198","src/back/glsl/features.rs":"017ae911a94d3067ee9b9ba47558bd46a2b1f2d3e8e68099bfc0d2f13f66ebe8","src/back/glsl/keywords.rs":"3f23b1e63e99a7056c3b223524d5d37000ef7316ae9df25532a726a1157d1dcd","src/back/glsl/mod.rs":"525dda15520ba47df2d3823137778aa59f0bd6b638aa1d5b627e2f6b8a181599","src/back/hlsl/conv.rs":"46097319c63a0ae04ed4d6e86c5d1321c45d53bdf324440fa1588ec4990b2385","src/back/hlsl/help.rs":"1c00fef402c8244ffee0a0839006fd5446edcdd6d1b339ca57d5a87c8aa4ac80","src/back/hlsl/keywords.rs":"a158c52bf9a05514b5a6e29658c2872c1cf2a6f295e8094a6ac4a37f76a761ad","src/back/hlsl/mod.rs":"385078b0f10e2a502800c30a5335b8eac3c7df3cb122540ccc41ed37a8c462f5","src/back/hlsl/storage.rs":"a3942cc99ccfbaa7f3c9db55daff6af805167acc993a2f8d6065dba683256d09","src/back/hlsl/writer.rs":"d420d811cbbe3e8774cdfcc042f3e6882e2c601fcc5fc6fc68f8836dcfb5cc67","src/back/mod.rs":"719cae1c096a71196fc1485943fd5ea6d8ebffbd1af47ed8663ce87bf54cd3bf","src/back/msl/keywords.rs":"e1c2bf804b1a3a56639bedb9c1b3b78fbf920724ef65f91fe2e9fe476ee3fbbf","src/back/msl/mod.rs":"be992959cc7aa58a8a30ab936ce65e432de92ab9bfba4bf2b5368bdfe63bcb27","src/back/msl/sampler.rs":"210cf28143ac17d6c56a9adf4fc0e3b85b1b16bb8b94771f9304f2a43b7ce78e","src/back/msl/writer.rs":"47103b226936063a2cd63f3017f7b3b6255e028836ff797e4a534d034e0d63c5","src/back/spv/block.rs":"33cf662b1f0152c18f0ac74d9c61826c8b13fa1b6ffb8f65a777d76497bc22b5","src/back/spv/helpers.rs":"0ec3381f3ba5856a14d876559f77e21fd5d2f342cb005343a3df2ff58443e4fb","src/back/spv/image.rs":"32e6bf0a23fd0383c12bece8ffb1983a68ebc2d5421224f44b74d136981cf5a4","src/back/spv/index.rs":"7a9815bdb1f323a780d41092371582f6d4210f305bec5c74e882dbb6d2d49bc3","src/back/spv/instructions.rs":"42eb1afad17cd311d89cbf49813171848a417543195df571ba0f3de1633efc8b","src/back/spv/layout.rs":"e263de53cd2f9a03ad94b82b434ce636609bc1ed435a2d1132951663bfaa8ebd","src/back/spv/mod.rs":"0f5a5a5c10c5a301fbbcb30857156bd6b963d3a8752ea02050446cd711a5c1d6","src/back/spv/recyclable.rs":"9448eedf9766ae2b1916282b5945b218017c6067dceac2da1415580ee5796c73","src/back/spv/selection.rs":"81e404abfa0a977f7c1f76ccb37a78d13ccadbda229048dad53cc67687cc39db","src/back/spv/writer.rs":"57b61ad31a9ececb9cec0b13e64100164ead9c40dfbe3ec4c903b22722c9d952","src/back/wgsl/mod.rs":"2dd12bbea9ace835850192bb68c5760953da6bac6a636073d1eca19381c0c0b6","src/back/wgsl/writer.rs":"de6518bedf340bf5720bdacafc80516469e96c2da258db14a8fed775177c606f","src/block.rs":"89c061206f608365ef3ce03c0d205d7205ef9d892035dd3dfb8d252003159f14","src/front/glsl/ast.rs":"839aa81cf60aeafe8c192c98864c009ecf5c6f32e60866e7a788867121c37bcf","src/front/glsl/builtins.rs":"7fbb50e468f8b1e540bf80366f90fd2af2411ddefc1c107eb3f47882cda0f09d","src/front/glsl/constants.rs":"f08fd19633c80d54a3a4234e501070a1127eaef7cf3de92d69130aa6fc9c1e51","src/front/glsl/context.rs":"d68e5db51d6445766cd7e50426c63d68e56f480a050413750d3e0347ffc08b1b","src/front/glsl/error.rs":"6e049db79e59cffcd79bce3ac259f781062ab72e752f95d42f94c5a72efa1710","src/front/glsl/functions.rs":"7b5fe425e2cfa02b8285918e1f41eb88c99817395385fb81f3156a9bde3cf3c8","src/front/glsl/lex.rs":"08736ae8beb955da5b0e6e3e0f45995a824995f7096d516a2910417e9c7afa32","src/front/glsl/mod.rs":"2aaffb60fedbdf27b1e86fa8d65c637142d7efe61e4ffcb463455dc17d5676cb","src/front/glsl/offset.rs":"098f37b97112931517521c88825497a4ea6435c3ddcb5608be04a2b4f5498900","src/front/glsl/parser.rs":"4ca425ded0e19dcf9fabc74a0e7833c9493198ab796a7cba02ead1431cff77be","src/front/glsl/parser/declarations.rs":"f177b239f0ca9189458c2feb69219b94f2777a837b240ff4cc8b278b0291a311","src/front/glsl/parser/expressions.rs":"9b9979e5a32bfb825871dbb5d01b3e6e64062f30096f0140e01035283353998b","src/front/glsl/parser/functions.rs":"3ae5110825bc867ad8ec2cd2ca35afad2874e7999142915cb4a17746851b91d4","src/front/glsl/parser/types.rs":"ab09449ec40f3b5c24b16488b23dbf3b147d132beb6188ccb2068a54c0128bb8","src/front/glsl/parser_tests.rs":"4256fdce5e5de7234a173ecd0dc77eef20052932b0d298c1227b40bd6a7f6a28","src/front/glsl/token.rs":"42325adbef5bfc9e6f9e40e7ed3cbfa7ef498c05799fdb7694b123e6450410e1","src/front/glsl/types.rs":"d043b50192ac427455177059c1a6a7472392673fc8bf9be61d6d457f1587372f","src/front/glsl/variables.rs":"35055f3618e2738224b29eef1c43f4f10a92e8d0c37263998268e0ed484c73e9","src/front/interpolator.rs":"1f19f8634c639981d83aa8aa132ca5634bdc99830212eec3eac74c856bd25274","src/front/mod.rs":"30a1d62f05d79fb5e6afc1e5ebb4aec84eed1c1a60b991fae9b0eb6e361ccca0","src/front/spv/convert.rs":"fa61b1bb53f1e39ce93be91b5ac63eb01bcd5e087a6cb46b478466ce60c833b6","src/front/spv/error.rs":"1d0acaf179e2b71df87b7eee9aa9cc78e60fda86866969795f9b79111748124c","src/front/spv/function.rs":"4bc19456976ae38a91b32bd3d89d098998d4f516f706a389d6ea588d1ed2a3e6","src/front/spv/image.rs":"88a041f0cb1e26a5ca90f7419ebf712149689560fd5806f313dfeda580466203","src/front/spv/mod.rs":"9e28202589f00073e1c7929e10e7157306639bd6bba706392c2ec28d74dc0176","src/front/spv/null.rs":"93e364cd1db48e7a6344e8c7a9aa14a18812352c9011c471f305b6657acf9b7c","src/front/wgsl/construction.rs":"fe9545c16d3171747f3592934b6289ca574de295b3356b803783ecdd34822a82","src/front/wgsl/conv.rs":"6ce908d5f3562b50750afd0b082627613f0336cc8cb2f8b58441ddee0e30f130","src/front/wgsl/lexer.rs":"4d8221bf7f624aa1dca1dab43d93901875a6bb9afa417c64b3ac59eab9b363a9","src/front/wgsl/mod.rs":"8f9b9f905eb4a09f6bf7ded6dca7924ebdef11d91af74408d103ef024cb611b5","src/front/wgsl/number_literals.rs":"7d0790ae9704c679230c4a1961a3bb101a7270aab0c54ddc5b17c5c4ed77f395","src/front/wgsl/tests.rs":"67579b7374771fa79c50feb83415d62febbd64ff0b0a5797ff2f701b27efe111","src/keywords/mod.rs":"0138f3931f8af0b0a05174549d0fd2152945b027dc3febefc1bbd676581d2e45","src/keywords/wgsl.rs":"b3c330c8134107aaac721224fb11a42c743a3c546b874314256c5c0f13e9ec1f","src/lib.rs":"92ccd5e69830d8de63d6e2494f9468279600bbc694c1ce77e5aa6e1ddaeeef44","src/proc/index.rs":"9f1be774ac7e47b899066116d292604faed0acae5751628869c7767b919b71aa","src/proc/layouter.rs":"bd2b64a8f1f4113faee356ff1df53f7d44743679b4046659748ede60d638265d","src/proc/mod.rs":"e7719689f9efcc97dbe81e4938c675aa87d1e036311237f183c30e12b56eb23b","src/proc/namer.rs":"12d946cbc5ed44f6607f9406f1cecbf24a3bda97c3de12b8dd51d0a95e17ac6e","src/proc/terminator.rs":"40d22f4c821d24e972403a31ded5dae6078d3dc33b52a9b0993396bdf77f8d1d","src/proc/typifier.rs":"3582068c0e1801d34414be7709238b8860ade0017435fb118dc25de85ae7776f","src/span.rs":"ff1f43d8a1373db770c16ffe8f628861e47d14faaf981c85de5321be8bacdf57","src/valid/analyzer.rs":"b06a71b11abecbcc2ec83b0cf90320fded804281fe18810ffd773dbb5565d9c1","src/valid/compose.rs":"e4c029db8d07ca5e11d4e7e59d8885634856872b595a166e266a3ce0342ccdcd","src/valid/expression.rs":"ba5c4e3ee582c5ccde716b49b36c0844374080c0ae569e0b8d10b0034088270b","src/valid/function.rs":"dde9745837655ddc84017eddb758af17a7c9a9a196e071ca528c7fbac836c567","src/valid/interface.rs":"af1b1e7eca72976b316912b01ddec702d5d8c25f21159962d7a35efa035e60ee","src/valid/mod.rs":"00dab13c754bac27def55d6714135dfe531c593fd7b55a416c23f04289279e1a","src/valid/type.rs":"cea1602864775c39e155eff1ab52ac59c517c289f7d76793983ea8f876486798"},"package":null} \ No newline at end of file diff --git a/third_party/rust/naga/Cargo.toml b/third_party/rust/naga/Cargo.toml index 76e30f9cc47cb..1476eb9cf8e93 100644 --- a/third_party/rust/naga/Cargo.toml +++ b/third_party/rust/naga/Cargo.toml @@ -61,10 +61,7 @@ serde = { version = "1.0.103", features = ["derive"], optional = true } petgraph = { version ="0.6", optional = true } pp-rs = { version = "0.2.1", optional = true } hexf-parse = { version = "0.2.1", optional = true } -# update unicode-xid to the next version since it has been updated to unicode v14 -# (but has no release that includes it yet) -# https://github.com/unicode-rs/unicode-xid/pull/27 -unicode-xid = { version = "0.2.2", optional = true } +unicode-xid = { version = "0.2.3", optional = true } [dev-dependencies] bincode = "1" diff --git a/third_party/rust/naga/README.md b/third_party/rust/naga/README.md index 939eb91e1c89d..fbaa063d7d85a 100644 --- a/third_party/rust/naga/README.md +++ b/third_party/rust/naga/README.md @@ -7,12 +7,10 @@ ![MSRV](https://img.shields.io/badge/rustc-1.56+-blue.svg) [![codecov.io](https://codecov.io/gh/gfx-rs/naga/branch/master/graph/badge.svg?token=9VOKYO8BM2)](https://codecov.io/gh/gfx-rs/naga) -The shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu) and [gfx-rs](https://github.com/gfx-rs/gfx) projects. +The shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu). ## Supported end-points -Everything is still work-in-progress, but some end-points are usable: - Front-end | Status | Feature | Notes | --------------- | ------------------ | ------- | ----- | SPIR-V (binary) | :white_check_mark: | spv-in | | @@ -35,12 +33,31 @@ DOT (GraphViz) | :ok: | dot-out | Not a shading language | ## Conversion tool -Naga includes a default binary target, which allows to test the conversion of different code paths. +Naga can be used as a CLI, which allows to test the conversion of different code paths. + +First, install `naga-cli` from crates.io or directly from GitHub. + +```bash +# release version +cargo install naga-cli + +# development version +cargo install naga-cli --git https://github.com/gfx-rs/naga.git +``` + +Then, you can run `naga` command. + +```bash +naga my_shader.wgsl # validate only +naga my_shader.spv my_shader.txt # dump the IR module into a file +naga my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Metal, also dump the SPIR-V flow graph to `flow-dir` +naga my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile +``` + +As naga includes a default binary target, you can also use `cargo run` without installation. This is useful when you develop naga itself, or investigate the behavior of naga at a specific commit (e.g. [wgpu](https://github.com/gfx-rs/wgpu) might pin a different version of naga than the `HEAD` of this repository). + ```bash -cargo run my_shader.wgsl # validate only -cargo run my_shader.spv my_shader.txt # dump the IR module into a file -cargo run my_shader.spv my_shader.metal --flow-dir flow-dir # convert the SPV to Metal, also dump the SPIR-V flow graph to `flow-dir` -cargo run my_shader.wgsl my_shader.vert --profile es310 # convert the WGSL to GLSL vertex stage under ES 3.20 profile +cargo run my_shader.wgsl ``` ## Development workflow diff --git a/third_party/rust/naga/src/arena.rs b/third_party/rust/naga/src/arena.rs index 77f425d1f4324..1f970524e77f5 100644 --- a/third_party/rust/naga/src/arena.rs +++ b/third_party/rust/naga/src/arena.rs @@ -77,7 +77,7 @@ impl hash::Hash for Handle { impl Handle { #[cfg(test)] pub const DUMMY: Self = Handle { - index: unsafe { NonZeroU32::new_unchecked(!0) }, + index: unsafe { NonZeroU32::new_unchecked(u32::MAX) }, marker: PhantomData, }; @@ -101,7 +101,7 @@ impl Handle { let handle_index = u32::try_from(index + 1) .ok() .and_then(Index::new) - .expect("Failed to insert into UniqueArena. Handle overflows"); + .expect("Failed to insert into arena. Handle overflows"); Handle::new(handle_index) } diff --git a/third_party/rust/naga/src/back/glsl/features.rs b/third_party/rust/naga/src/back/glsl/features.rs index 61f019682703f..d3f117045fa58 100644 --- a/third_party/rust/naga/src/back/glsl/features.rs +++ b/third_party/rust/naga/src/back/glsl/features.rs @@ -36,6 +36,8 @@ bitflags::bitflags! { const MULTI_VIEW = 1 << 17; /// Fused multiply-add. const FMA = 1 << 18; + /// Texture samples query + const TEXTURE_SAMPLES = 1 << 19; } } @@ -101,7 +103,10 @@ impl FeaturesManager { check_feature!(SAMPLE_VARIABLES, 400, 300); check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310); check_feature!(MULTI_VIEW, 140, 310); - check_feature!(FMA, 400, 310); + // Only available on glsl core, this means that opengl es can't query the number + // of samples in a image and neither do bound checks on the sample argument + // of texelFecth + check_feature!(TEXTURE_SAMPLES, 150); // Return an error if there are missing features if missing.is_empty() { @@ -205,11 +210,19 @@ impl FeaturesManager { writeln!(out, "#extension GL_EXT_multiview : require")?; } - if self.0.contains(Features::FMA) && version.is_es() { + if self.0.contains(Features::FMA) && version >= Version::Embedded(310) { // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_gpu_shader5.txt writeln!(out, "#extension GL_EXT_gpu_shader5 : require")?; } + if self.0.contains(Features::TEXTURE_SAMPLES) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_texture_image_samples.txt + writeln!( + out, + "#extension GL_ARB_shader_texture_image_samples : require" + )?; + } + Ok(()) } } @@ -363,24 +376,26 @@ impl<'a, W> Writer<'a, W> { } } - if self.options.version.supports_fma_function() { - let has_fma = self - .module - .functions - .iter() - .flat_map(|(_, f)| f.expressions.iter()) - .chain( - self.module - .entry_points - .iter() - .flat_map(|e| e.function.expressions.iter()), - ) - .any(|(_, e)| match *e { - Expression::Math { fun, .. } if fun == MathFunction::Fma => true, - _ => false, - }); - if has_fma { - self.features.request(Features::FMA); + // Loop trough all expressions in both functions and entry points + // to check for needed features + for (_, expr) in self + .module + .functions + .iter() + .flat_map(|(_, f)| f.expressions.iter()) + .chain(self.entry_point.function.expressions.iter()) + { + match *expr { + // Check for fused multiply add use + Expression::Math { fun, .. } if fun == MathFunction::Fma => { + self.features.request(Features::FMA) + } + // Check for samples query + Expression::ImageQuery { + query: crate::ImageQuery::NumSamples, + .. + } => self.features.request(Features::TEXTURE_SAMPLES), + _ => {} } } diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs index 699348f058508..5b7700ba9d521 100644 --- a/third_party/rust/naga/src/back/glsl/mod.rs +++ b/third_party/rust/naga/src/back/glsl/mod.rs @@ -1629,6 +1629,8 @@ impl<'a, W: Write> Writer<'a, W> { Some(self.namer.call(name)) } else if self.need_bake_expressions.contains(&handle) { Some(format!("{}{}", back::BAKE_PREFIX, handle.index())) + } else if info.ref_count == 0 { + Some(self.namer.call("")) } else { None }; @@ -2433,7 +2435,6 @@ impl<'a, W: Write> Writer<'a, W> { } } crate::ImageQuery::NumSamples => { - // assumes ARB_shader_texture_image_samples let fun_name = match class { ImageClass::Sampled { .. } | ImageClass::Depth { .. } => { "textureSamples" @@ -2581,6 +2582,18 @@ impl<'a, W: Write> Writer<'a, W> { write!(self.out, ")")?; } + // TODO: handle undefined behavior of BinaryOperator::Modulo + // + // sint: + // if right == 0 return 0 + // if left == min(type_of(left)) && right == -1 return 0 + // if sign(left) == -1 || sign(right) == -1 return result as defined by WGSL + // + // uint: + // if right == 0 return 0 + // + // float: + // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798 BinaryOperation::Modulo => { write!(self.out, "(")?; @@ -2806,6 +2819,30 @@ impl<'a, W: Write> Writer<'a, W> { let extract_bits = fun == Mf::ExtractBits; let insert_bits = fun == Mf::InsertBits; + // we might need to cast to unsigned integers since + // GLSL's findLSB / findMSB always return signed integers + let need_extra_paren = { + (fun == Mf::FindLsb || fun == Mf::FindMsb || fun == Mf::CountOneBits) + && match *ctx.info[arg].ty.inner_with(&self.module.types) { + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Uint, + .. + } => { + write!(self.out, "uint(")?; + true + } + crate::TypeInner::Vector { + kind: crate::ScalarKind::Uint, + size, + .. + } => { + write!(self.out, "uvec{}(", size as u8)?; + true + } + _ => false, + } + }; + write!(self.out, "{}(", fun_name)?; self.write_expr(arg, ctx)?; if let Some(arg) = arg1 { @@ -2838,7 +2875,11 @@ impl<'a, W: Write> Writer<'a, W> { self.write_expr(arg, ctx)?; } } - write!(self.out, ")")? + write!(self.out, ")")?; + + if need_extra_paren { + write!(self.out, ")")? + } } // `As` is always a call. // If `convert` is true the function name is the type @@ -2854,6 +2895,11 @@ impl<'a, W: Write> Writer<'a, W> { // this is similar to `write_type`, but with the target kind let scalar = glsl_scalar(target_kind, width)?; match *inner { + TypeInner::Matrix { columns, rows, .. } => write!( + self.out, + "{}mat{}x{}", + scalar.prefix, columns as u8, rows as u8 + )?, TypeInner::Vector { size, .. } => { write!(self.out, "{}vec{}", scalar.prefix, size as u8)? } diff --git a/third_party/rust/naga/src/back/hlsl/help.rs b/third_party/rust/naga/src/back/hlsl/help.rs index 7612457b1d824..d71675f6a1996 100644 --- a/third_party/rust/naga/src/back/hlsl/help.rs +++ b/third_party/rust/naga/src/back/hlsl/help.rs @@ -375,7 +375,19 @@ impl<'a, W: Write> super::Writer<'a, W> { const RETURN_VARIABLE_NAME: &str = "ret"; // Write function return type and name - self.write_type(module, constructor.ty)?; + if let crate::TypeInner::Array { base, size, .. } = module.types[constructor.ty].inner { + write!(self.out, "typedef ")?; + self.write_type(module, constructor.ty)?; + write!(self.out, " ret_")?; + self.write_wrapped_constructor_function_name(module, constructor)?; + self.write_array_size(module, base, size)?; + writeln!(self.out, ";")?; + + write!(self.out, "ret_")?; + self.write_wrapped_constructor_function_name(module, constructor)?; + } else { + self.write_type(module, constructor.ty)?; + } write!(self.out, " ")?; self.write_wrapped_constructor_function_name(module, constructor)?; @@ -415,10 +427,6 @@ impl<'a, W: Write> super::Writer<'a, W> { write!(self.out, ")")?; - if let crate::TypeInner::Array { base, size, .. } = module.types[constructor.ty].inner { - self.write_array_size(module, base, size)?; - } - // Write function body writeln!(self.out, " {{")?; @@ -427,8 +435,8 @@ impl<'a, W: Write> super::Writer<'a, W> { let struct_name = &self.names[&NameKey::Type(constructor.ty)]; writeln!( self.out, - "{}{} {};", - INDENT, struct_name, RETURN_VARIABLE_NAME + "{}{} {} = ({})0;", + INDENT, struct_name, RETURN_VARIABLE_NAME, struct_name )?; for (i, member) in members.iter().enumerate() { let field_name = &self.names[&NameKey::StructMember(constructor.ty, i as u32)]; @@ -880,6 +888,48 @@ impl<'a, W: Write> super::Writer<'a, W> { self.wrapped.image_queries.insert(wiq); } } + // Write `WrappedConstructor` for structs that are loaded from `AddressSpace::Storage` + // since they will later be used by the fn `write_storage_load` + crate::Expression::Load { pointer } => { + let pointer_space = func_ctx.info[pointer] + .ty + .inner_with(&module.types) + .pointer_space(); + + if let Some(crate::AddressSpace::Storage { .. }) = pointer_space { + if let Some(ty) = func_ctx.info[handle].ty.handle() { + write_wrapped_constructor(self, ty, module, func_ctx)?; + } + } + + fn write_wrapped_constructor( + writer: &mut super::Writer<'_, W>, + ty: Handle, + module: &crate::Module, + func_ctx: &FunctionCtx, + ) -> BackendResult { + match module.types[ty].inner { + crate::TypeInner::Struct { ref members, .. } => { + for member in members { + write_wrapped_constructor(writer, member.ty, module, func_ctx)?; + } + + let constructor = WrappedConstructor { ty }; + if !writer.wrapped.constructors.contains(&constructor) { + writer + .write_wrapped_constructor_function(module, constructor)?; + writer.wrapped.constructors.insert(constructor); + } + } + crate::TypeInner::Array { base, .. } => { + write_wrapped_constructor(writer, base, module, func_ctx)?; + } + _ => {} + }; + + Ok(()) + } + } crate::Expression::Compose { ty, components: _ } => { let constructor = match module.types[ty].inner { crate::TypeInner::Struct { .. } | crate::TypeInner::Array { .. } => { diff --git a/third_party/rust/naga/src/back/hlsl/storage.rs b/third_party/rust/naga/src/back/hlsl/storage.rs index cea989deec342..637b05d4bb95d 100644 --- a/third_party/rust/naga/src/back/hlsl/storage.rs +++ b/third_party/rust/naga/src/back/hlsl/storage.rs @@ -44,6 +44,9 @@ impl super::Writer<'_, W> { chain: &[SubAccess], func_ctx: &FunctionCtx, ) -> BackendResult { + if chain.is_empty() { + write!(self.out, "0")?; + } for (i, access) in chain.iter().enumerate() { if i != 0 { write!(self.out, "+")?; @@ -156,12 +159,16 @@ impl super::Writer<'_, W> { write!(self.out, "}}")?; } crate::TypeInner::Struct { ref members, .. } => { - write!(self.out, "{{")?; + let constructor = super::help::WrappedConstructor { + ty: result_ty.handle().unwrap(), + }; + self.write_wrapped_constructor_function_name(module, constructor)?; + write!(self.out, "(")?; let iter = members .iter() .map(|m| (TypeResolution::Handle(m.ty), m.offset)); self.write_storage_load_sequence(module, var_handle, iter, func_ctx)?; - write!(self.out, "}}")?; + write!(self.out, ")")?; } _ => unreachable!(), } diff --git a/third_party/rust/naga/src/back/hlsl/writer.rs b/third_party/rust/naga/src/back/hlsl/writer.rs index 97a141e42b2ad..e713c2f5dd19d 100644 --- a/third_party/rust/naga/src/back/hlsl/writer.rs +++ b/third_party/rust/naga/src/back/hlsl/writer.rs @@ -6,7 +6,7 @@ use super::{ use crate::{ back, proc::{self, NameKey}, - valid, Handle, Module, ShaderStage, TypeInner, + valid, Handle, Module, ScalarKind, ShaderStage, TypeInner, }; use std::{fmt, mem}; @@ -1167,6 +1167,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { // Otherwise, we could accidentally write variable name instead of full expression. // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. Some(self.namer.call(name)) + } else if info.ref_count == 0 { + Some(self.namer.call("")) } else { let min_ref_count = func_ctx.expressions[handle].bake_ref_count(); if min_ref_count <= info.ref_count { @@ -1795,6 +1797,38 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { self.write_expr(module, left, func_ctx)?; write!(self.out, ")")?; } + + // TODO: handle undefined behavior of BinaryOperator::Modulo + // + // sint: + // if right == 0 return 0 + // if left == min(type_of(left)) && right == -1 return 0 + // if sign(left) != sign(right) return result as defined by WGSL + // + // uint: + // if right == 0 return 0 + // + // float: + // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798 + + // While HLSL supports float operands with the % operator it is only + // defined in cases where both sides are either positive or negative. + Expression::Binary { + op: crate::BinaryOperator::Modulo, + left, + right, + } if func_ctx.info[left] + .ty + .inner_with(&module.types) + .scalar_kind() + == Some(crate::ScalarKind::Float) => + { + write!(self.out, "fmod(")?; + self.write_expr(module, left, func_ctx)?; + write!(self.out, ", ")?; + self.write_expr(module, right, func_ctx)?; + write!(self.out, ")")?; + } Expression::Binary { op, left, right } => { write!(self.out, "(")?; self.write_expr(module, left, func_ctx)?; @@ -2107,9 +2141,32 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { convert, } => { let inner = func_ctx.info[expr].ty.inner_with(&module.types); - let (size_str, src_width) = match *inner { - TypeInner::Vector { size, width, .. } => (back::vector_size_str(size), width), - TypeInner::Scalar { width, .. } => ("", width), + let get_width = |src_width| kind.to_hlsl_str(convert.unwrap_or(src_width)); + match *inner { + TypeInner::Vector { size, width, .. } => { + write!( + self.out, + "{}{}(", + get_width(width)?, + back::vector_size_str(size) + )?; + } + TypeInner::Scalar { width, .. } => { + write!(self.out, "{}(", get_width(width)?,)?; + } + TypeInner::Matrix { + columns, + rows, + width, + } => { + write!( + self.out, + "{}{}x{}(", + get_width(width)?, + back::vector_size_str(columns), + back::vector_size_str(rows) + )?; + } _ => { return Err(Error::Unimplemented(format!( "write_expr expression::as {:?}", @@ -2117,8 +2174,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { ))); } }; - let kind_str = kind.to_hlsl_str(convert.unwrap_or(src_width))?; - write!(self.out, "{}{}(", kind_str, size_str,)?; self.write_expr(module, expr, func_ctx)?; write!(self.out, ")")?; } @@ -2135,6 +2190,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Asincosh { is_sin: bool }, Atanh, Regular(&'static str), + MissingIntOverload(&'static str), } let fun = match fun { @@ -2196,8 +2252,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Mf::Transpose => Function::Regular("transpose"), Mf::Determinant => Function::Regular("determinant"), // bits - Mf::CountOneBits => Function::Regular("countbits"), - Mf::ReverseBits => Function::Regular("reversebits"), + Mf::CountOneBits => Function::MissingIntOverload("countbits"), + Mf::ReverseBits => Function::MissingIntOverload("reversebits"), Mf::FindLsb => Function::Regular("firstbitlow"), Mf::FindMsb => Function::Regular("firstbithigh"), _ => return Err(Error::Unimplemented(format!("write_expr_math {:?}", fun))), @@ -2240,6 +2296,21 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } write!(self.out, ")")? } + Function::MissingIntOverload(fun_name) => { + let scalar_kind = &func_ctx.info[arg] + .ty + .inner_with(&module.types) + .scalar_kind(); + if let Some(ScalarKind::Sint) = *scalar_kind { + write!(self.out, "asint({}(asuint(", fun_name)?; + self.write_expr(module, arg, func_ctx)?; + write!(self.out, ")))")?; + } else { + write!(self.out, "{}(", fun_name)?; + self.write_expr(module, arg, func_ctx)?; + write!(self.out, ")")?; + } + } } } Expression::Swizzle { diff --git a/third_party/rust/naga/src/back/mod.rs b/third_party/rust/naga/src/back/mod.rs index daec79a8c255e..16b503cfb700a 100644 --- a/third_party/rust/naga/src/back/mod.rs +++ b/third_party/rust/naga/src/back/mod.rs @@ -131,7 +131,7 @@ impl crate::Expression { const fn bake_ref_count(&self) -> usize { match *self { // accesses are never cached, only loads are - crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => !0, + crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => usize::MAX, // sampling may use the control flow, and image ops look better by themselves crate::Expression::ImageSample { .. } | crate::Expression::ImageLoad { .. } => 1, // derivatives use the control flow diff --git a/third_party/rust/naga/src/back/msl/writer.rs b/third_party/rust/naga/src/back/msl/writer.rs index 836985846c4ab..8294626a80c22 100644 --- a/third_party/rust/naga/src/back/msl/writer.rs +++ b/third_party/rust/naga/src/back/msl/writer.rs @@ -462,7 +462,9 @@ impl crate::AddressSpace { // rely on the actual use of a global by functions. This means we // may end up with "const" even if the binding is read-write, // and that should be OK. - Self::Storage { .. } | Self::Private | Self::WorkGroup => true, + Self::Storage { .. } => true, + // These should always be read-write. + Self::Private | Self::WorkGroup => false, // These translate to `constant` address space, no need for qualifiers. Self::Uniform | Self::PushConstant => false, // Not applicable. @@ -1482,6 +1484,20 @@ impl Writer { .resolve_type(left) .scalar_kind() .ok_or(Error::UnsupportedBinaryOp(op))?; + + // TODO: handle undefined behavior of BinaryOperator::Modulo + // + // sint: + // if right == 0 return 0 + // if left == min(type_of(left)) && right == -1 return 0 + // if sign(left) == -1 || sign(right) == -1 return result as defined by WGSL + // + // uint: + // if right == 0 return 0 + // + // float: + // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798 + if op == crate::BinaryOperator::Modulo && kind == crate::ScalarKind::Float { write!(self.out, "{}::fmod(", NAMESPACE)?; self.put_expression(left, context, true)?; @@ -1591,21 +1607,6 @@ impl Writer { crate::TypeInner::Scalar { .. } => true, _ => false, }; - let argument_size_suffix = match *context.resolve_type(arg) { - crate::TypeInner::Vector { - size: crate::VectorSize::Bi, - .. - } => "2", - crate::TypeInner::Vector { - size: crate::VectorSize::Tri, - .. - } => "3", - crate::TypeInner::Vector { - size: crate::VectorSize::Quad, - .. - } => "4", - _ => "", - }; let fun_name = match fun { // comparison @@ -1705,21 +1706,13 @@ impl Writer { self.put_expression(arg1.unwrap(), context, false)?; write!(self.out, ")")?; } else if fun == Mf::FindLsb { - write!( - self.out, - "(((1 + int{}({}::ctz(", - argument_size_suffix, NAMESPACE - )?; + write!(self.out, "((({}::ctz(", NAMESPACE)?; self.put_expression(arg, context, true)?; - write!(self.out, "))) % 33) - 1)")?; + write!(self.out, ") + 1) % 33) - 1)")?; } else if fun == Mf::FindMsb { - write!( - self.out, - "(((1 + int{}({}::clz(", - argument_size_suffix, NAMESPACE - )?; + write!(self.out, "((({}::clz(", NAMESPACE)?; self.put_expression(arg, context, true)?; - write!(self.out, "))) % 33) - 1)")?; + write!(self.out, ") + 1) % 33) - 1)")? } else if fun == Mf::Unpack2x16float { write!(self.out, "float2(as_type(")?; self.put_expression(arg, context, false)?; @@ -1748,33 +1741,45 @@ impl Writer { expr, kind, convert, - } => { - let (src_kind, src_width) = match *context.resolve_type(expr) { - crate::TypeInner::Scalar { kind, width } - | crate::TypeInner::Vector { kind, width, .. } => (kind, width), - _ => return Err(Error::Validation), - }; - let is_bool_cast = - kind == crate::ScalarKind::Bool || src_kind == crate::ScalarKind::Bool; - let op = match convert { - Some(w) if w == src_width || is_bool_cast => "static_cast", - Some(8) if kind == crate::ScalarKind::Float => { - return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64)) - } - Some(_) => return Err(Error::Validation), - None => "as_type", - }; - write!(self.out, "{}<", op)?; - match *context.resolve_type(expr) { - crate::TypeInner::Vector { size, .. } => { - put_numeric_type(&mut self.out, kind, &[size])? - } - _ => put_numeric_type(&mut self.out, kind, &[])?, - }; - write!(self.out, ">(")?; - self.put_expression(expr, context, true)?; - write!(self.out, ")")?; - } + } => match *context.resolve_type(expr) { + crate::TypeInner::Scalar { + kind: src_kind, + width: src_width, + } + | crate::TypeInner::Vector { + kind: src_kind, + width: src_width, + .. + } => { + let is_bool_cast = + kind == crate::ScalarKind::Bool || src_kind == crate::ScalarKind::Bool; + let op = match convert { + Some(w) if w == src_width || is_bool_cast => "static_cast", + Some(8) if kind == crate::ScalarKind::Float => { + return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64)) + } + Some(_) => return Err(Error::Validation), + None => "as_type", + }; + write!(self.out, "{}<", op)?; + match *context.resolve_type(expr) { + crate::TypeInner::Vector { size, .. } => { + put_numeric_type(&mut self.out, kind, &[size])? + } + _ => put_numeric_type(&mut self.out, kind, &[])?, + }; + write!(self.out, ">(")?; + self.put_expression(expr, context, true)?; + write!(self.out, ")")?; + } + crate::TypeInner::Matrix { columns, rows, .. } => { + put_numeric_type(&mut self.out, kind, &[rows, columns])?; + write!(self.out, "(")?; + self.put_expression(expr, context, true)?; + write!(self.out, ")")?; + } + _ => return Err(Error::Validation), + }, // has to be a named expression crate::Expression::CallResult(_) | crate::Expression::AtomicResult { .. } => { unreachable!() @@ -2445,6 +2450,8 @@ impl Writer { // Don't assume the names in `named_expressions` are unique, // or even valid. Use the `Namer`. Some(self.namer.call(name)) + } else if info.ref_count == 0 { + Some(self.namer.call("")) } else { // If this expression is an index that we're going to first compare // against a limit, and then actually use as an index, then we may @@ -3276,15 +3283,20 @@ impl Writer { }; let local_name = &self.names[&NameKey::FunctionLocal(fun_handle, local_handle)]; write!(self.out, "{}{} {}", back::INDENT, ty_name, local_name)?; - if let Some(value) = local.init { - let coco = ConstantContext { - handle: value, - arena: &module.constants, - names: &self.names, - first_time: false, - }; - write!(self.out, " = {}", coco)?; - } + match local.init { + Some(value) => { + let coco = ConstantContext { + handle: value, + arena: &module.constants, + names: &self.names, + first_time: false, + }; + write!(self.out, " = {}", coco)?; + } + None => { + write!(self.out, " = {{}}")?; + } + }; writeln!(self.out, ";")?; } @@ -3808,15 +3820,20 @@ impl Writer { first_time: false, }; write!(self.out, "{}{} {}", back::INDENT, ty_name, name)?; - if let Some(value) = local.init { - let coco = ConstantContext { - handle: value, - arena: &module.constants, - names: &self.names, - first_time: false, - }; - write!(self.out, " = {}", coco)?; - } + match local.init { + Some(value) => { + let coco = ConstantContext { + handle: value, + arena: &module.constants, + names: &self.names, + first_time: false, + }; + write!(self.out, " = {}", coco)?; + } + None => { + write!(self.out, " = {{}}")?; + } + }; writeln!(self.out, ";")?; } @@ -3901,7 +3918,7 @@ fn test_stack_size() { { // check expression stack - let mut addresses = !0usize..0usize; + let mut addresses = usize::MAX..0usize; for pointer in writer.put_expression_stack_pointers { addresses.start = addresses.start.min(pointer as usize); addresses.end = addresses.end.max(pointer as usize); @@ -3916,7 +3933,7 @@ fn test_stack_size() { { // check block stack - let mut addresses = !0usize..0usize; + let mut addresses = usize::MAX..0usize; for pointer in writer.put_block_stack_pointers { addresses.start = addresses.start.min(pointer as usize); addresses.end = addresses.end.max(pointer as usize); diff --git a/third_party/rust/naga/src/back/spv/block.rs b/third_party/rust/naga/src/back/spv/block.rs index 1bce7d2719780..413f4df5d327f 100644 --- a/third_party/rust/naga/src/back/spv/block.rs +++ b/third_party/rust/naga/src/back/spv/block.rs @@ -529,8 +529,15 @@ impl<'w> BlockContext<'w> { _ => unimplemented!(), }, crate::BinaryOperator::Modulo => match left_ty_inner.scalar_kind() { - Some(crate::ScalarKind::Sint) => spirv::Op::SMod, + // TODO: handle undefined behavior + // if right == 0 return 0 + // if left == min(type_of(left)) && right == -1 return 0 + Some(crate::ScalarKind::Sint) => spirv::Op::SRem, + // TODO: handle undefined behavior + // if right == 0 return 0 Some(crate::ScalarKind::Uint) => spirv::Op::UMod, + // TODO: handle undefined behavior + // if right == 0 return ? see https://github.com/gpuweb/gpuweb/issues/2798 Some(crate::ScalarKind::Float) => spirv::Op::FRem, _ => unimplemented!(), }, @@ -798,10 +805,18 @@ impl<'w> BlockContext<'w> { arg0_id, )), Mf::Determinant => MathOp::Ext(spirv::GLOp::Determinant), - Mf::ReverseBits | Mf::CountOneBits => { - log::error!("unimplemented math function {:?}", fun); - return Err(Error::FeatureNotImplemented("math function")); - } + Mf::ReverseBits => MathOp::Custom(Instruction::unary( + spirv::Op::BitReverse, + result_type_id, + id, + arg0_id, + )), + Mf::CountOneBits => MathOp::Custom(Instruction::unary( + spirv::Op::BitCount, + result_type_id, + id, + arg0_id, + )), Mf::ExtractBits => { let op = match arg_scalar_kind { Some(crate::ScalarKind::Uint) => spirv::Op::BitFieldUExtract, @@ -923,10 +938,13 @@ impl<'w> BlockContext<'w> { use crate::ScalarKind as Sk; let expr_id = self.cached[expr]; - let (src_kind, src_size, src_width) = + let (src_kind, src_size, src_width, is_matrix) = match *self.fun_info[expr].ty.inner_with(&self.ir_module.types) { - crate::TypeInner::Scalar { kind, width } => (kind, None, width), - crate::TypeInner::Vector { kind, width, size } => (kind, Some(size), width), + crate::TypeInner::Scalar { kind, width } => (kind, None, width, false), + crate::TypeInner::Vector { kind, width, size } => { + (kind, Some(size), width, false) + } + crate::TypeInner::Matrix { width, .. } => (kind, None, width, true), ref other => { log::error!("As source {:?}", other); return Err(Error::Validation("Unexpected Expression::As source")); @@ -939,102 +957,112 @@ impl<'w> BlockContext<'w> { Ternary(spirv::Op, Word, Word), } - let cast = match (src_kind, kind, convert) { - (_, _, None) | (Sk::Bool, Sk::Bool, Some(_)) => Cast::Unary(spirv::Op::Bitcast), - // casting to a bool - generate `OpXxxNotEqual` - (_, Sk::Bool, Some(_)) => { - let (op, value) = match src_kind { - Sk::Sint => (spirv::Op::INotEqual, crate::ScalarValue::Sint(0)), - Sk::Uint => (spirv::Op::INotEqual, crate::ScalarValue::Uint(0)), - Sk::Float => { - (spirv::Op::FUnordNotEqual, crate::ScalarValue::Float(0.0)) - } - Sk::Bool => unreachable!(), - }; - let zero_scalar_id = self.writer.get_constant_scalar(value, src_width); - let zero_id = match src_size { - Some(size) => { - let vector_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: Some(size), - kind: src_kind, - width: src_width, - pointer_space: None, - })); - let components = [zero_scalar_id; 4]; - - let zero_id = self.gen_id(); - block.body.push(Instruction::composite_construct( - vector_type_id, - zero_id, - &components[..size as usize], - )); - zero_id - } - None => zero_scalar_id, - }; - - Cast::Binary(op, zero_id) - } - // casting from a bool - generate `OpSelect` - (Sk::Bool, _, Some(dst_width)) => { - let (val0, val1) = match kind { - Sk::Sint => (crate::ScalarValue::Sint(0), crate::ScalarValue::Sint(1)), - Sk::Uint => (crate::ScalarValue::Uint(0), crate::ScalarValue::Uint(1)), - Sk::Float => ( - crate::ScalarValue::Float(0.0), - crate::ScalarValue::Float(1.0), - ), - Sk::Bool => unreachable!(), - }; - let scalar0_id = self.writer.get_constant_scalar(val0, dst_width); - let scalar1_id = self.writer.get_constant_scalar(val1, dst_width); - let (accept_id, reject_id) = match src_size { - Some(size) => { - let vector_type_id = - self.get_type_id(LookupType::Local(LocalType::Value { - vector_size: Some(size), - kind, - width: dst_width, - pointer_space: None, - })); - let components0 = [scalar0_id; 4]; - let components1 = [scalar1_id; 4]; + let cast = if is_matrix { + // we only support identity casts for matrices + Cast::Unary(spirv::Op::CopyObject) + } else { + match (src_kind, kind, convert) { + (Sk::Bool, Sk::Bool, _) => Cast::Unary(spirv::Op::CopyObject), + (_, _, None) => Cast::Unary(spirv::Op::Bitcast), + // casting to a bool - generate `OpXxxNotEqual` + (_, Sk::Bool, Some(_)) => { + let (op, value) = match src_kind { + Sk::Sint => (spirv::Op::INotEqual, crate::ScalarValue::Sint(0)), + Sk::Uint => (spirv::Op::INotEqual, crate::ScalarValue::Uint(0)), + Sk::Float => { + (spirv::Op::FUnordNotEqual, crate::ScalarValue::Float(0.0)) + } + Sk::Bool => unreachable!(), + }; + let zero_scalar_id = self.writer.get_constant_scalar(value, src_width); + let zero_id = match src_size { + Some(size) => { + let vector_type_id = + self.get_type_id(LookupType::Local(LocalType::Value { + vector_size: Some(size), + kind: src_kind, + width: src_width, + pointer_space: None, + })); + let components = [zero_scalar_id; 4]; + + let zero_id = self.gen_id(); + block.body.push(Instruction::composite_construct( + vector_type_id, + zero_id, + &components[..size as usize], + )); + zero_id + } + None => zero_scalar_id, + }; - let vec0_id = self.gen_id(); - block.body.push(Instruction::composite_construct( - vector_type_id, - vec0_id, - &components0[..size as usize], - )); - let vec1_id = self.gen_id(); - block.body.push(Instruction::composite_construct( - vector_type_id, - vec1_id, - &components1[..size as usize], - )); - (vec1_id, vec0_id) - } - None => (scalar1_id, scalar0_id), - }; + Cast::Binary(op, zero_id) + } + // casting from a bool - generate `OpSelect` + (Sk::Bool, _, Some(dst_width)) => { + let (val0, val1) = match kind { + Sk::Sint => { + (crate::ScalarValue::Sint(0), crate::ScalarValue::Sint(1)) + } + Sk::Uint => { + (crate::ScalarValue::Uint(0), crate::ScalarValue::Uint(1)) + } + Sk::Float => ( + crate::ScalarValue::Float(0.0), + crate::ScalarValue::Float(1.0), + ), + Sk::Bool => unreachable!(), + }; + let scalar0_id = self.writer.get_constant_scalar(val0, dst_width); + let scalar1_id = self.writer.get_constant_scalar(val1, dst_width); + let (accept_id, reject_id) = match src_size { + Some(size) => { + let vector_type_id = + self.get_type_id(LookupType::Local(LocalType::Value { + vector_size: Some(size), + kind, + width: dst_width, + pointer_space: None, + })); + let components0 = [scalar0_id; 4]; + let components1 = [scalar1_id; 4]; + + let vec0_id = self.gen_id(); + block.body.push(Instruction::composite_construct( + vector_type_id, + vec0_id, + &components0[..size as usize], + )); + let vec1_id = self.gen_id(); + block.body.push(Instruction::composite_construct( + vector_type_id, + vec1_id, + &components1[..size as usize], + )); + (vec1_id, vec0_id) + } + None => (scalar1_id, scalar0_id), + }; - Cast::Ternary(spirv::Op::Select, accept_id, reject_id) - } - (Sk::Float, Sk::Uint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToU), - (Sk::Float, Sk::Sint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToS), - (Sk::Float, Sk::Float, Some(dst_width)) if src_width != dst_width => { - Cast::Unary(spirv::Op::FConvert) - } - (Sk::Sint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertSToF), - (Sk::Sint, Sk::Sint, Some(dst_width)) if src_width != dst_width => { - Cast::Unary(spirv::Op::SConvert) - } - (Sk::Uint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertUToF), - (Sk::Uint, Sk::Uint, Some(dst_width)) if src_width != dst_width => { - Cast::Unary(spirv::Op::UConvert) + Cast::Ternary(spirv::Op::Select, accept_id, reject_id) + } + (Sk::Float, Sk::Uint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToU), + (Sk::Float, Sk::Sint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToS), + (Sk::Float, Sk::Float, Some(dst_width)) if src_width != dst_width => { + Cast::Unary(spirv::Op::FConvert) + } + (Sk::Sint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertSToF), + (Sk::Sint, Sk::Sint, Some(dst_width)) if src_width != dst_width => { + Cast::Unary(spirv::Op::SConvert) + } + (Sk::Uint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertUToF), + (Sk::Uint, Sk::Uint, Some(dst_width)) if src_width != dst_width => { + Cast::Unary(spirv::Op::UConvert) + } + // We assume it's either an identity cast, or int-uint. + _ => Cast::Unary(spirv::Op::Bitcast), } - // We assume it's either an identity cast, or int-uint. - _ => Cast::Unary(spirv::Op::Bitcast), }; let id = self.gen_id(); diff --git a/third_party/rust/naga/src/back/spv/writer.rs b/third_party/rust/naga/src/back/spv/writer.rs index a629d5beb8419..f4d9e0e603563 100644 --- a/third_party/rust/naga/src/back/spv/writer.rs +++ b/third_party/rust/naga/src/back/spv/writer.rs @@ -309,7 +309,10 @@ impl Writer { pointer_type_id, id, spirv::StorageClass::Function, - init_word, + init_word.or_else(|| { + let type_id = self.get_type_id(LookupType::Handle(variable.ty)); + Some(self.write_constant_null(type_id)) + }), ); function .variables @@ -1325,6 +1328,13 @@ impl Writer { } }; + let init_word = match global_variable.space { + crate::AddressSpace::Private => { + init_word.or_else(|| Some(self.write_constant_null(inner_type_id))) + } + _ => init_word, + }; + Instruction::variable(pointer_type_id, id, class, init_word) .to_words(&mut self.logical_layout.declarations); Ok(id) diff --git a/third_party/rust/naga/src/back/wgsl/writer.rs b/third_party/rust/naga/src/back/wgsl/writer.rs index f21d82c40aa35..487ea40d8abfc 100644 --- a/third_party/rust/naga/src/back/wgsl/writer.rs +++ b/third_party/rust/naga/src/back/wgsl/writer.rs @@ -659,6 +659,11 @@ impl Writer { // Otherwise, we could accidentally write variable name instead of full expression. // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. Some(self.namer.call(name)) + } else if info.ref_count == 0 { + write!(self.out, "{}_ = ", level)?; + self.write_expr(module, handle, func_ctx)?; + writeln!(self.out, ";")?; + continue; } else { let expr = &func_ctx.expressions[handle]; let min_ref_count = expr.bake_ref_count(); diff --git a/third_party/rust/naga/src/front/glsl/ast.rs b/third_party/rust/naga/src/front/glsl/ast.rs index dc47647dc7d47..148049a688931 100644 --- a/third_party/rust/naga/src/front/glsl/ast.rs +++ b/third_party/rust/naga/src/front/glsl/ast.rs @@ -133,9 +133,17 @@ pub enum HirExprKind { }, Variable(VariableReference), Call(FunctionCall), + /// Represents the ternary operator in glsl (`:?`) Conditional { + /// The expression that will decide which branch to take, must evaluate to a boolean condition: Handle, + /// The expression that will be evaluated if [`condition`] returns `true` + /// + /// [`condition`]: Self::Conditional::condition accept: Handle, + /// The expression that will be evaluated if [`condition`] returns `false` + /// + /// [`condition`]: Self::Conditional::condition reject: Handle, }, Assign { diff --git a/third_party/rust/naga/src/front/glsl/builtins.rs b/third_party/rust/naga/src/front/glsl/builtins.rs index 6e614813826a7..287f66d6f1b80 100644 --- a/third_party/rust/naga/src/front/glsl/builtins.rs +++ b/third_party/rust/naga/src/front/glsl/builtins.rs @@ -727,6 +727,17 @@ fn inject_standard_builtins( _ => {} } + // we need to cast the return type of findLsb / findMsb + let mc = if kind == Sk::Uint { + match mc { + MacroCall::MathFunction(MathFunction::FindLsb) => MacroCall::FindLsbUint, + MacroCall::MathFunction(MathFunction::FindMsb) => MacroCall::FindMsbUint, + mc => mc, + } + } else { + mc + }; + declaration.overloads.push(module.add_builtin(args, mc)) } } @@ -1580,6 +1591,8 @@ pub enum MacroCall { }, ImageStore, MathFunction(MathFunction), + FindLsbUint, + FindMsbUint, BitfieldExtract, BitfieldInsert, Relational(RelationalFunction), @@ -1824,7 +1837,7 @@ impl MacroCall { MacroCall::ImageStore => { let comps = parser.coordinate_components(ctx, args[0], args[1], None, meta, body)?; - ctx.emit_flush(body); + ctx.emit_restart(body); body.push( crate::Statement::ImageStore { image: args[0], @@ -1834,7 +1847,6 @@ impl MacroCall { }, meta, ); - ctx.emit_start(); return Ok(None); } MacroCall::MathFunction(fun) => ctx.add_expression( @@ -1848,6 +1860,33 @@ impl MacroCall { Span::default(), body, ), + mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => { + let fun = match mc { + MacroCall::FindLsbUint => MathFunction::FindLsb, + MacroCall::FindMsbUint => MathFunction::FindMsb, + _ => unreachable!(), + }; + let res = ctx.add_expression( + Expression::Math { + fun, + arg: args[0], + arg1: None, + arg2: None, + arg3: None, + }, + Span::default(), + body, + ); + ctx.add_expression( + Expression::As { + expr: res, + kind: Sk::Sint, + convert: Some(4), + }, + Span::default(), + body, + ) + } MacroCall::BitfieldInsert => { let conv_arg_2 = ctx.add_expression( Expression::As { @@ -1930,14 +1969,45 @@ impl MacroCall { MacroCall::Mod(size) => { ctx.implicit_splat(parser, &mut args[1], meta, size)?; - ctx.add_expression( + // x - y * floor(x / y) + + let div = ctx.add_expression( Expression::Binary { - op: BinaryOperator::Modulo, + op: BinaryOperator::Divide, left: args[0], right: args[1], }, Span::default(), body, + ); + let floor = ctx.add_expression( + Expression::Math { + fun: MathFunction::Floor, + arg: div, + arg1: None, + arg2: None, + arg3: None, + }, + Span::default(), + body, + ); + let mult = ctx.add_expression( + Expression::Binary { + op: BinaryOperator::Multiply, + left: floor, + right: args[1], + }, + Span::default(), + body, + ); + ctx.add_expression( + Expression::Binary { + op: BinaryOperator::Subtract, + left: args[0], + right: mult, + }, + Span::default(), + body, ) } MacroCall::Splatted(fun, size, i) => { @@ -1998,8 +2068,7 @@ impl MacroCall { body, ), MacroCall::Barrier => { - ctx.emit_flush(body); - ctx.emit_start(); + ctx.emit_restart(body); body.push(crate::Statement::Barrier(crate::Barrier::all()), meta); return Ok(None); } diff --git a/third_party/rust/naga/src/front/glsl/constants.rs b/third_party/rust/naga/src/front/glsl/constants.rs index 3e4bc8b5b25c7..03620ce5147e0 100644 --- a/third_party/rust/naga/src/front/glsl/constants.rs +++ b/third_party/rust/naga/src/front/glsl/constants.rs @@ -506,7 +506,7 @@ impl<'a> ConstantSolver<'a> { BinaryOperator::Subtract => a - b, BinaryOperator::Multiply => a * b, BinaryOperator::Divide => a / b, - BinaryOperator::Modulo => a % b, + BinaryOperator::Modulo => a - b * (a / b).floor(), _ => return Err(ConstantSolvingError::InvalidBinaryOpArgs), }) } diff --git a/third_party/rust/naga/src/front/glsl/context.rs b/third_party/rust/naga/src/front/glsl/context.rs index c0b4bd95f82c6..fe20df4101770 100644 --- a/third_party/rust/naga/src/front/glsl/context.rs +++ b/third_party/rust/naga/src/front/glsl/context.rs @@ -101,7 +101,7 @@ impl Context { }: GlobalLookup, body: &mut Block, ) { - self.emit_flush(body); + self.emit_end(body); let (expr, load, constant) = match kind { GlobalLookupKind::Variable(v) => { let span = parser.module.global_variables.get_span(v); @@ -170,14 +170,39 @@ impl Context { self.lookup_global_var_exps.insert(name.into(), var); } + /// Starts the expression emitter + /// + /// # Panics + /// + /// - If called twice in a row without calling [`emit_end`][Self::emit_end]. + #[inline] pub fn emit_start(&mut self) { self.emitter.start(&self.expressions) } - pub fn emit_flush(&mut self, body: &mut Block) { + /// Emits all the expressions captured by the emitter to the passed `body` + /// + /// # Panics + /// + /// - If called before calling [`emit_start`]. + /// - If called twice in a row without calling [`emit_start`]. + /// + /// [`emit_start`]: Self::emit_start + pub fn emit_end(&mut self, body: &mut Block) { body.extend(self.emitter.finish(&self.expressions)) } + /// Emits all the expressions captured by the emitter to the passed `body` + /// and starts the emitter again + /// + /// # Panics + /// + /// - If called before calling [`emit_start`][Self::emit_start]. + pub fn emit_restart(&mut self, body: &mut Block) { + self.emit_end(body); + self.emit_start() + } + pub fn add_expression( &mut self, expr: Expression, @@ -186,7 +211,7 @@ impl Context { ) -> Handle { let needs_pre_emit = expr.needs_pre_emit(); if needs_pre_emit { - self.emit_flush(body); + self.emit_end(body); } let handle = self.expressions.append(expr, meta); if needs_pre_emit { @@ -292,8 +317,7 @@ impl Context { ); let local_expr = self.add_expression(Expression::LocalVariable(handle), meta, body); - self.emit_flush(body); - self.emit_start(); + self.emit_restart(body); body.push( Statement::Store { @@ -462,8 +486,7 @@ impl Context { body, ); - self.emit_flush(body); - self.emit_start(); + self.emit_restart(body); body.push( Statement::Store { @@ -474,8 +497,7 @@ impl Context { ); } } else { - self.emit_flush(body); - self.emit_start(); + self.emit_restart(body); body.push(Statement::Store { pointer, value }, meta); } @@ -1069,35 +1091,149 @@ impl Context { )?; return Ok((maybe_expr, meta)); } + // `HirExprKind::Conditional` represents the ternary operator in glsl (`:?`) + // + // The ternary operator is defined to only evaluate one of the two possible + // expressions which means that it's behavior is that of an `if` statement, + // and it's merely syntatic sugar for it. HirExprKind::Conditional { condition, accept, reject, } if ExprPos::Lhs != pos => { + // Given an expression `a ? b : c`, we need to produce a Naga + // statement roughly like: + // + // var temp; + // if a { + // temp = convert(b); + // } else { + // temp = convert(c); + // } + // + // where `convert` stands for type conversions to bring `b` and `c` to + // the same type, and then use `temp` to represent the value of the whole + // conditional expression in subsequent code. + + // Lower the condition first to the current bodyy let condition = self .lower_expect_inner(stmt, parser, condition, ExprPos::Rhs, body)? .0; + + // Emit all expressions since we will be adding statements to + // other bodies next + self.emit_restart(body); + + // Create the bodies for the two cases + let mut accept_body = Block::new(); + let mut reject_body = Block::new(); + + // Lower the `true` branch let (mut accept, accept_meta) = - self.lower_expect_inner(stmt, parser, accept, pos, body)?; + self.lower_expect_inner(stmt, parser, accept, pos, &mut accept_body)?; + + // Flush the body of the `true` branch, to start emitting on the + // `false` branch + self.emit_restart(&mut accept_body); + + // Lower the `false` branch let (mut reject, reject_meta) = - self.lower_expect_inner(stmt, parser, reject, pos, body)?; + self.lower_expect_inner(stmt, parser, reject, pos, &mut reject_body)?; + + // Flush the body of the `false` branch + self.emit_restart(&mut reject_body); + + // We need to do some custom implicit conversions since the two target expressions + // are in different bodies + if let ( + Some((accept_power, accept_width, accept_kind)), + Some((reject_power, reject_width, reject_kind)), + ) = ( + // Get the components of both branches and calculate the type power + self.expr_scalar_components(parser, accept, accept_meta)? + .and_then(|(kind, width)| Some((type_power(kind, width)?, width, kind))), + self.expr_scalar_components(parser, reject, reject_meta)? + .and_then(|(kind, width)| Some((type_power(kind, width)?, width, kind))), + ) { + match accept_power.cmp(&reject_power) { + std::cmp::Ordering::Less => { + self.conversion(&mut accept, accept_meta, reject_kind, reject_width)?; + // The expression belongs to the `true` branch so we need to flush to + // the respective body + self.emit_end(&mut accept_body); + } + // Technically there's nothing to flush but later we will need to + // add some expressions that must not be emitted so instead + // of flushing, starting and flushing again, just make sure + // everything is flushed. + std::cmp::Ordering::Equal => self.emit_end(body), + std::cmp::Ordering::Greater => { + self.conversion(&mut reject, reject_meta, accept_kind, accept_width)?; + // The expression belongs to the `false` branch so we need to flush to + // the respective body + self.emit_end(&mut reject_body); + } + } + } - self.binary_implicit_conversion( - parser, - &mut accept, + // We need to get the type of the resulting expression to create the local, + // this must be done after implicit conversions to ensure both branches have + // the same type. + let ty = parser.resolve_type_handle(self, accept, accept_meta)?; + + // Add the local that will hold the result of our conditional + let local = self.locals.append( + LocalVariable { + name: None, + ty, + init: None, + }, + meta, + ); + + // Note: `Expression::LocalVariable` must not be emited so it's important + // that at this point the emitter is flushed but not started. + let local_expr = self + .expressions + .append(Expression::LocalVariable(local), meta); + + // Add to each body the store to the result variable + accept_body.push( + Statement::Store { + pointer: local_expr, + value: accept, + }, accept_meta, - &mut reject, + ); + reject_body.push( + Statement::Store { + pointer: local_expr, + value: reject, + }, reject_meta, - )?; + ); - self.add_expression( - Expression::Select { + // Finally add the `If` to the main body with the `condition` we lowered + // earlier and the branches we prepared. + body.push( + Statement::If { condition, - accept, - reject, + accept: accept_body, + reject: reject_body, + }, + meta, + ); + + // Restart the emitter + self.emit_start(); + + // Note: `Expression::Load` must be emited before it's used so make + // sure the emitter is active here. + self.expressions.append( + Expression::Load { + pointer: local_expr, }, meta, - body, ) } HirExprKind::Assign { tgt, value } if ExprPos::Lhs != pos => { diff --git a/third_party/rust/naga/src/front/glsl/functions.rs b/third_party/rust/naga/src/front/glsl/functions.rs index a9e8fd417ece3..325f4d8abecba 100644 --- a/third_party/rust/naga/src/front/glsl/functions.rs +++ b/third_party/rust/naga/src/front/glsl/functions.rs @@ -972,7 +972,7 @@ impl Parser { match kind { FunctionKind::Call(function) => { - ctx.emit_flush(body); + ctx.emit_end(body); let result = if !is_void { Some(ctx.add_expression(Expression::CallResult(function), meta, body)) @@ -995,8 +995,7 @@ impl Parser { for (original, pointer) in proxy_writes { let value = ctx.add_expression(Expression::Load { pointer }, meta, body); - ctx.emit_flush(body); - ctx.emit_start(); + ctx.emit_restart(body); body.push( Statement::Store { diff --git a/third_party/rust/naga/src/front/glsl/mod.rs b/third_party/rust/naga/src/front/glsl/mod.rs index 2d9645ab6dc75..f28ff7b6eca40 100644 --- a/third_party/rust/naga/src/front/glsl/mod.rs +++ b/third_party/rust/naga/src/front/glsl/mod.rs @@ -190,7 +190,7 @@ impl Parser { self.layouter.clear(); // This is necessary because if the last parsing errored out, the module - // wouldn't have been swapped + // wouldn't have been taken self.module = Module::default(); } @@ -213,13 +213,9 @@ impl Parser { } if self.errors.is_empty() { - let mut module = Module::default(); - std::mem::swap(&mut self.module, &mut module); - Ok(module) + Ok(std::mem::take(&mut self.module)) } else { - let mut errors = Vec::new(); - std::mem::swap(&mut self.errors, &mut errors); - Err(errors) + Err(std::mem::take(&mut self.errors)) } } diff --git a/third_party/rust/naga/src/front/glsl/parser.rs b/third_party/rust/naga/src/front/glsl/parser.rs index 92b8125693b25..2705fd904cd3d 100644 --- a/third_party/rust/naga/src/front/glsl/parser.rs +++ b/third_party/rust/naga/src/front/glsl/parser.rs @@ -38,7 +38,7 @@ impl<'source> ParsingContext<'source> { /// Helper method for backtracking from a consumed token /// /// This method should always be used instead of assigning to `backtracked_token` since - /// it validates that backtracking hasn't ocurred more than one time in a row + /// it validates that backtracking hasn't occurred more than one time in a row /// /// # Panics /// - If the parser already backtracked without bumping in between @@ -172,25 +172,23 @@ impl<'source> ParsingContext<'source> { self.parse_external_declaration(parser, &mut ctx, &mut body)?; } - match parser.lookup_function.get("main").and_then(|declaration| { - declaration - .overloads - .iter() - .find_map(|decl| match decl.kind { - FunctionKind::Call(handle) if decl.defined && decl.parameters.is_empty() => { - Some(handle) + // Add an `EntryPoint` to `parser.module` for `main`, if a + // suitable overload exists. Error out if we can't find one. + if let Some(declaration) = parser.lookup_function.get("main") { + for decl in declaration.overloads.iter() { + if let FunctionKind::Call(handle) = decl.kind { + if decl.defined && decl.parameters.is_empty() { + parser.add_entry_point(handle, body, ctx.expressions); + return Ok(()); } - _ => None, - }) - }) { - Some(handle) => parser.add_entry_point(handle, body, ctx.expressions), - None => parser.errors.push(Error { - kind: ErrorKind::SemanticError("Missing entry point".into()), - meta: Span::default(), - }), + } + } } - Ok(()) + Err(Error { + kind: ErrorKind::SemanticError("Missing entry point".into()), + meta: Span::default(), + }) } fn parse_uint_constant(&mut self, parser: &mut Parser) -> Result<(u32, Span)> { @@ -438,8 +436,13 @@ impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> { } } + /// Emits all the expressions captured by the emitter and starts the emitter again + /// + /// Alias to [`emit_restart`] with the declaration body + /// + /// [`emit_restart`]: Context::emit_restart + #[inline] fn flush_expressions(&mut self) { - self.ctx.emit_flush(self.body); - self.ctx.emit_start() + self.ctx.emit_restart(self.body); } } diff --git a/third_party/rust/naga/src/front/glsl/parser/functions.rs b/third_party/rust/naga/src/front/glsl/parser/functions.rs index ce6f56d8eb4ba..8a9487917beb7 100644 --- a/third_party/rust/naga/src/front/glsl/parser/functions.rs +++ b/third_party/rust/naga/src/front/glsl/parser/functions.rs @@ -103,8 +103,7 @@ impl<'source> ParsingContext<'source> { } }; - ctx.emit_flush(body); - ctx.emit_start(); + ctx.emit_restart(body); body.push(Statement::Return { value }, meta); terminator.get_or_insert(body.len()); @@ -132,8 +131,7 @@ impl<'source> ParsingContext<'source> { }; self.expect(parser, TokenValue::RightParen)?; - ctx.emit_flush(body); - ctx.emit_start(); + ctx.emit_restart(body); let mut accept = Block::new(); if let Some(more_meta) = @@ -176,8 +174,7 @@ impl<'source> ParsingContext<'source> { self.expect(parser, TokenValue::RightParen)?; - ctx.emit_flush(body); - ctx.emit_start(); + ctx.emit_restart(body); let mut cases = Vec::new(); @@ -301,8 +298,7 @@ impl<'source> ParsingContext<'source> { &mut loop_body, ); - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); + ctx.emit_restart(&mut loop_body); loop_body.push( Statement::If { @@ -359,8 +355,7 @@ impl<'source> ParsingContext<'source> { &mut loop_body, ); - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); + ctx.emit_restart(&mut loop_body); loop_body.push( Statement::If { @@ -427,8 +422,7 @@ impl<'source> ParsingContext<'source> { let pointer = parser.add_local_var(ctx, &mut block, decl)?; - ctx.emit_flush(&mut block); - ctx.emit_start(); + ctx.emit_restart(&mut block); block.push(Statement::Store { pointer, value }, meta); @@ -448,8 +442,7 @@ impl<'source> ParsingContext<'source> { &mut block, ); - ctx.emit_flush(&mut block); - ctx.emit_start(); + ctx.emit_restart(&mut block); block.push( Statement::If { diff --git a/third_party/rust/naga/src/front/glsl/types.rs b/third_party/rust/naga/src/front/glsl/types.rs index 6a197ccebdf11..05c415eba0c62 100644 --- a/third_party/rust/naga/src/front/glsl/types.rs +++ b/third_party/rust/naga/src/front/glsl/types.rs @@ -225,10 +225,23 @@ pub const fn type_power(kind: ScalarKind, width: Bytes) -> Option { } impl Parser { + /// Resolves the types of the expressions until `expr` (inclusive) + /// + /// This needs to be done before the [`typifier`] can be queried for + /// the types of the expressions in the range between the last grow and `expr`. + /// + /// # Note + /// + /// The `resolve_type*` methods (like [`resolve_type`]) automatically + /// grow the [`typifier`] so calling this method is not necessary when using + /// them. + /// + /// [`typifier`]: Context::typifier + /// [`resolve_type`]: Self::resolve_type pub(crate) fn typifier_grow( &self, ctx: &mut Context, - handle: Handle, + expr: Handle, meta: Span, ) -> Result<()> { let resolve_ctx = ResolveContext { @@ -241,28 +254,73 @@ impl Parser { }; ctx.typifier - .grow(handle, &ctx.expressions, &resolve_ctx) + .grow(expr, &ctx.expressions, &resolve_ctx) .map_err(|error| Error { kind: ErrorKind::SemanticError(format!("Can't resolve type: {:?}", error).into()), meta, }) } + /// Gets the type for the result of the `expr` expression + /// + /// Automatically grows the [`typifier`] to `expr` so calling + /// [`typifier_grow`] is not necessary + /// + /// [`typifier`]: Context::typifier + /// [`typifier_grow`]: Self::typifier_grow pub(crate) fn resolve_type<'b>( &'b self, ctx: &'b mut Context, - handle: Handle, + expr: Handle, meta: Span, ) -> Result<&'b TypeInner> { - self.typifier_grow(ctx, handle, meta)?; - Ok(ctx.typifier.get(handle, &self.module.types)) + self.typifier_grow(ctx, expr, meta)?; + Ok(ctx.typifier.get(expr, &self.module.types)) } - /// Invalidates the cached type resolution for `handle` forcing a recomputation + /// Gets the type handle for the result of the `expr` expression + /// + /// Automatically grows the [`typifier`] to `expr` so calling + /// [`typifier_grow`] is not necessary + /// + /// # Note + /// + /// Consider using [`resolve_type`] whenever possible + /// since it doesn't require adding each type to the [`types`] arena + /// and it doesn't need to mutably borrow the [`Parser`][Self] + /// + /// [`types`]: crate::Module::types + /// [`typifier`]: Context::typifier + /// [`typifier_grow`]: Self::typifier_grow + /// [`resolve_type`]: Self::resolve_type + pub(crate) fn resolve_type_handle( + &mut self, + ctx: &mut Context, + expr: Handle, + meta: Span, + ) -> Result> { + self.typifier_grow(ctx, expr, meta)?; + let resolution = &ctx.typifier[expr]; + Ok(match *resolution { + // If the resolution is already a handle return early + crate::proc::TypeResolution::Handle(ty) => ty, + // If it's a value we need to clone it + crate::proc::TypeResolution::Value(_) => match resolution.clone() { + // This is unreachable + crate::proc::TypeResolution::Handle(ty) => ty, + // Add the value to the type arena and return the handle + crate::proc::TypeResolution::Value(inner) => { + self.module.types.insert(Type { name: None, inner }, meta) + } + }, + }) + } + + /// Invalidates the cached type resolution for `expr` forcing a recomputation pub(crate) fn invalidate_expression<'b>( &'b self, ctx: &'b mut Context, - handle: Handle, + expr: Handle, meta: Span, ) -> Result<()> { let resolve_ctx = ResolveContext { @@ -275,7 +333,7 @@ impl Parser { }; ctx.typifier - .invalidate(handle, &ctx.expressions, &resolve_ctx) + .invalidate(expr, &ctx.expressions, &resolve_ctx) .map_err(|error| Error { kind: ErrorKind::SemanticError(format!("Can't resolve type: {:?}", error).into()), meta, diff --git a/third_party/rust/naga/src/front/spv/convert.rs b/third_party/rust/naga/src/front/spv/convert.rs index b555833ed9f9a..128f7ceaef3a8 100644 --- a/third_party/rust/naga/src/front/spv/convert.rs +++ b/third_party/rust/naga/src/front/spv/convert.rs @@ -12,7 +12,6 @@ pub(super) const fn map_binary_operator(word: spirv::Op) -> Result Ok(BinaryOperator::Subtract), Op::IMul | Op::FMul => Ok(BinaryOperator::Multiply), Op::UDiv | Op::SDiv | Op::FDiv => Ok(BinaryOperator::Divide), - Op::UMod | Op::SMod | Op::FMod => Ok(BinaryOperator::Modulo), Op::SRem => Ok(BinaryOperator::Modulo), // Relational and Logical Instructions Op::IEqual | Op::FOrdEqual | Op::FUnordEqual | Op::LogicalEqual => { diff --git a/third_party/rust/naga/src/front/spv/mod.rs b/third_party/rust/naga/src/front/spv/mod.rs index 141970e6f09b4..cf4f50d1d765f 100644 --- a/third_party/rust/naga/src/front/spv/mod.rs +++ b/third_party/rust/naga/src/front/spv/mod.rs @@ -1966,7 +1966,6 @@ impl> Parser { | Op::BitwiseXor | Op::BitwiseAnd | Op::SDiv - | Op::SMod | Op::SRem => { inst.expect(5)?; let operator = map_binary_operator(inst.op)?; @@ -2009,10 +2008,188 @@ impl> Parser { inst.expect(5)?; parse_expr_op!(crate::BinaryOperator::Divide, BINARY)?; } - Op::UMod | Op::FMod | Op::FRem => { + Op::UMod | Op::FRem => { inst.expect(5)?; parse_expr_op!(crate::BinaryOperator::Modulo, BINARY)?; } + Op::SMod => { + inst.expect(5)?; + + // x - y * int(floor(float(x) / float(y))) + + let start = self.data_offset; + let result_type_id = self.next()?; + let result_id = self.next()?; + let p1_id = self.next()?; + let p2_id = self.next()?; + let span = self.span_from_with_op(start); + + let p1_lexp = self.lookup_expression.lookup(p1_id)?; + let left = self.get_expr_handle( + p1_id, + p1_lexp, + ctx, + &mut emitter, + &mut block, + body_idx, + ); + let p2_lexp = self.lookup_expression.lookup(p2_id)?; + let right = self.get_expr_handle( + p2_id, + p2_lexp, + ctx, + &mut emitter, + &mut block, + body_idx, + ); + + let left_cast = ctx.expressions.append( + crate::Expression::As { + expr: left, + kind: crate::ScalarKind::Float, + convert: None, + }, + span, + ); + let right_cast = ctx.expressions.append( + crate::Expression::As { + expr: right, + kind: crate::ScalarKind::Float, + convert: None, + }, + span, + ); + let div = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Divide, + left: left_cast, + right: right_cast, + }, + span, + ); + let floor = ctx.expressions.append( + crate::Expression::Math { + fun: crate::MathFunction::Floor, + arg: div, + arg1: None, + arg2: None, + arg3: None, + }, + span, + ); + let result_ty = self.lookup_type.lookup(result_type_id)?; + let kind = ctx.type_arena[result_ty.handle] + .inner + .scalar_kind() + .unwrap(); + let cast = ctx.expressions.append( + crate::Expression::As { + expr: floor, + kind, + convert: None, + }, + span, + ); + let mult = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Multiply, + left: cast, + right, + }, + span, + ); + let sub = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Subtract, + left, + right: mult, + }, + span, + ); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: sub, + type_id: result_type_id, + block_id, + }, + ); + } + Op::FMod => { + inst.expect(5)?; + + // x - y * floor(x / y) + + let start = self.data_offset; + let span = self.span_from_with_op(start); + + let result_type_id = self.next()?; + let result_id = self.next()?; + let p1_id = self.next()?; + let p2_id = self.next()?; + + let p1_lexp = self.lookup_expression.lookup(p1_id)?; + let left = self.get_expr_handle( + p1_id, + p1_lexp, + ctx, + &mut emitter, + &mut block, + body_idx, + ); + let p2_lexp = self.lookup_expression.lookup(p2_id)?; + let right = self.get_expr_handle( + p2_id, + p2_lexp, + ctx, + &mut emitter, + &mut block, + body_idx, + ); + + let div = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Divide, + left, + right, + }, + span, + ); + let floor = ctx.expressions.append( + crate::Expression::Math { + fun: crate::MathFunction::Floor, + arg: div, + arg1: None, + arg2: None, + arg3: None, + }, + span, + ); + let mult = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Multiply, + left: floor, + right, + }, + span, + ); + let sub = ctx.expressions.append( + crate::Expression::Binary { + op: crate::BinaryOperator::Subtract, + left, + right: mult, + }, + span, + ); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: sub, + type_id: result_type_id, + block_id, + }, + ); + } Op::VectorTimesScalar | Op::VectorTimesMatrix | Op::MatrixTimesScalar @@ -2453,7 +2630,7 @@ impl> Parser { let mut max_component = 0; for _ in 5..inst.wc as usize { let mut index = self.next()?; - if index == !0 { + if index == u32::MAX { // treat Undefined as X index = 0; } diff --git a/third_party/rust/naga/src/front/spv/null.rs b/third_party/rust/naga/src/front/spv/null.rs index ca62b8454c95c..85350c563ed86 100644 --- a/third_party/rust/naga/src/front/spv/null.rs +++ b/third_party/rust/naga/src/front/spv/null.rs @@ -158,7 +158,7 @@ pub fn generate_default_built_in( width: 4, }, Some(crate::BuiltIn::SampleMask) => crate::ConstantInner::Scalar { - value: crate::ScalarValue::Uint(!0), + value: crate::ScalarValue::Uint(u64::MAX), width: 4, }, //Note: `crate::BuiltIn::ClipDistance` is intentionally left for the default path diff --git a/third_party/rust/naga/src/front/wgsl/construction.rs b/third_party/rust/naga/src/front/wgsl/construction.rs index 281116d3fd1b0..ca43894e2b865 100644 --- a/third_party/rust/naga/src/front/wgsl/construction.rs +++ b/third_party/rust/naga/src/front/wgsl/construction.rs @@ -184,12 +184,15 @@ fn parse_constructor_type<'a>( Ok(Some(ConstructorType::Vector { size, kind, width })) } (Token::Paren('<'), ConstructorType::PartialMatrix { columns, rows }) => { - let (_, width) = lexer.next_scalar_generic()?; - Ok(Some(ConstructorType::Matrix { - columns, - rows, - width, - })) + let (kind, width, span) = lexer.next_scalar_generic_with_span()?; + match kind { + ScalarKind::Float => Ok(Some(ConstructorType::Matrix { + columns, + rows, + width, + })), + _ => Err(Error::BadMatrixScalarKind(span, kind, width)), + } } (Token::Paren('<'), ConstructorType::PartialArray) => { lexer.expect_generic_paren('<')?; @@ -363,6 +366,25 @@ pub(super) fn parse_construction<'a>( convert: Some(dst_width), }, + // Vector conversion (vector -> vector) - partial + ( + Components::One { + component, + ty: + &TypeInner::Vector { + size: src_size, + kind: src_kind, + .. + }, + .. + }, + ConstructorType::PartialVector { size: dst_size }, + ) if dst_size == src_size => Expression::As { + expr: component, + kind: src_kind, + convert: None, + }, + // Matrix conversion (matrix -> matrix) ( Components::One { @@ -386,6 +408,28 @@ pub(super) fn parse_construction<'a>( convert: Some(dst_width), }, + // Matrix conversion (matrix -> matrix) - partial + ( + Components::One { + component, + ty: + &TypeInner::Matrix { + columns: src_columns, + rows: src_rows, + .. + }, + .. + }, + ConstructorType::PartialMatrix { + columns: dst_columns, + rows: dst_rows, + }, + ) if dst_columns == src_columns && dst_rows == src_rows => Expression::As { + expr: component, + kind: ScalarKind::Float, + convert: None, + }, + // Vector constructor (splat) - infer type ( Components::One { diff --git a/third_party/rust/naga/src/front/wgsl/lexer.rs b/third_party/rust/naga/src/front/wgsl/lexer.rs index 91986d552ad77..212e41f2bf678 100644 --- a/third_party/rust/naga/src/front/wgsl/lexer.rs +++ b/third_party/rust/naga/src/front/wgsl/lexer.rs @@ -544,6 +544,9 @@ impl<'a> Lexer<'a> { pub(super) fn next_ident_with_span(&mut self) -> Result<(&'a str, Span), Error<'a>> { match self.next() { + (Token::Word(word), span) if word == "_" => { + Err(Error::InvalidIdentifierUnderscore(span)) + } (Token::Word(word), span) if word.starts_with("__") => { Err(Error::ReservedIdentifierPrefix(span)) } diff --git a/third_party/rust/naga/src/front/wgsl/mod.rs b/third_party/rust/naga/src/front/wgsl/mod.rs index 610c8c5f892f4..7379b51fd6b37 100644 --- a/third_party/rust/naga/src/front/wgsl/mod.rs +++ b/third_party/rust/naga/src/front/wgsl/mod.rs @@ -130,6 +130,7 @@ pub enum Error<'a> { BadFloat(Span, BadFloatError), BadU32Constant(Span), BadScalarWidth(Span, Bytes), + BadMatrixScalarKind(Span, crate::ScalarKind, u8), BadAccessor(Span), BadTexture(Span), BadTypeCast { @@ -147,6 +148,7 @@ pub enum Error<'a> { InvalidForInitializer(Span), InvalidGatherComponent(Span, i32), InvalidConstructorComponentType(Span, i32), + InvalidIdentifierUnderscore(Span), ReservedIdentifierPrefix(Span), UnknownAddressSpace(Span), UnknownAttribute(Span), @@ -295,12 +297,20 @@ impl<'a> Error<'a> { labels: vec![(bad_span.clone(), "expected unsigned integer".into())], notes: vec![], }, - Error::BadScalarWidth(ref bad_span, width) => ParseError { message: format!("invalid width of `{}` bits for literal", width as u32 * 8,), labels: vec![(bad_span.clone(), "invalid width".into())], notes: vec!["the only valid width is 32 for now".to_string()], }, + Error::BadMatrixScalarKind( + ref span, + kind, + width, + ) => ParseError { + message: format!("matrix scalar type must be floating-point, but found `{}`", kind.to_wgsl(width)), + labels: vec![(span.clone(), "must be floating-point (e.g. `f32`)".into())], + notes: vec![], + }, Error::BadAccessor(ref accessor_span) => ParseError { message: format!( "invalid field accessor `{}`", @@ -362,6 +372,11 @@ impl<'a> Error<'a> { labels: vec![(bad_span.clone(), "invalid component type".into())], notes: vec![], }, + Error::InvalidIdentifierUnderscore(ref bad_span) => ParseError { + message: "Identifier can't be '_'".to_string(), + labels: vec![(bad_span.clone(), "invalid identifier".into())], + notes: vec!["Use phony assignment instead ('_ =' notice the absence of 'let' or 'var')".to_string()], + }, Error::ReservedIdentifierPrefix(ref bad_span) => ParseError { message: format!("Identifier starts with a reserved prefix: '{}'", &source[bad_span.clone()]), labels: vec![(bad_span.clone(), "invalid identifier".into())], @@ -2895,6 +2910,23 @@ impl Parser { Ok((members, span)) } + fn parse_matrix_scalar_type<'a>( + &mut self, + lexer: &mut Lexer<'a>, + columns: crate::VectorSize, + rows: crate::VectorSize, + ) -> Result> { + let (kind, width, span) = lexer.next_scalar_generic_with_span()?; + match kind { + crate::ScalarKind::Float => Ok(crate::TypeInner::Matrix { + columns, + rows, + width, + }), + _ => Err(Error::BadMatrixScalarKind(span, kind, width)), + } + } + fn parse_type_decl_impl<'a>( &mut self, lexer: &mut Lexer<'a>, @@ -2906,6 +2938,7 @@ impl Parser { if let Some((kind, width)) = conv::get_scalar_type(word) { return Ok(Some(crate::TypeInner::Scalar { kind, width })); } + Ok(Some(match word { "vec2" => { let (kind, width) = lexer.next_scalar_generic()?; @@ -2932,77 +2965,44 @@ impl Parser { } } "mat2x2" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Bi, - rows: crate::VectorSize::Bi, - width, - } + self.parse_matrix_scalar_type(lexer, crate::VectorSize::Bi, crate::VectorSize::Bi)? } "mat2x3" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Bi, - rows: crate::VectorSize::Tri, - width, - } - } - "mat2x4" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Bi, - rows: crate::VectorSize::Quad, - width, - } + self.parse_matrix_scalar_type(lexer, crate::VectorSize::Bi, crate::VectorSize::Tri)? } + "mat2x4" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Bi, + crate::VectorSize::Quad, + )?, "mat3x2" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Tri, - rows: crate::VectorSize::Bi, - width, - } - } - "mat3x3" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Tri, - rows: crate::VectorSize::Tri, - width, - } - } - "mat3x4" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Tri, - rows: crate::VectorSize::Quad, - width, - } - } - "mat4x2" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Quad, - rows: crate::VectorSize::Bi, - width, - } - } - "mat4x3" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Quad, - rows: crate::VectorSize::Tri, - width, - } - } - "mat4x4" => { - let (_, width) = lexer.next_scalar_generic()?; - crate::TypeInner::Matrix { - columns: crate::VectorSize::Quad, - rows: crate::VectorSize::Quad, - width, - } + self.parse_matrix_scalar_type(lexer, crate::VectorSize::Tri, crate::VectorSize::Bi)? } + "mat3x3" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Tri, + crate::VectorSize::Tri, + )?, + "mat3x4" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Tri, + crate::VectorSize::Quad, + )?, + "mat4x2" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Quad, + crate::VectorSize::Bi, + )?, + "mat4x3" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Quad, + crate::VectorSize::Tri, + )?, + "mat4x4" => self.parse_matrix_scalar_type( + lexer, + crate::VectorSize::Quad, + crate::VectorSize::Quad, + )?, "atomic" => { let (kind, width) = lexer.next_scalar_generic()?; crate::TypeInner::Atomic { kind, width } @@ -3347,25 +3347,26 @@ impl Parser { let _ = context.resolve_type(reference.handle)?; let ty = context.typifier.get(reference.handle, context.types); - let constant_inner = match ty.canonical_form(context.types) { - Some(crate::TypeInner::ValuePointer { + let (kind, width) = match *ty { + crate::TypeInner::ValuePointer { size: None, kind, width, - space: _, - }) => crate::ConstantInner::Scalar { - width, - value: match kind { - crate::ScalarKind::Sint => crate::ScalarValue::Sint(1), - crate::ScalarKind::Uint => crate::ScalarValue::Uint(1), - _ => { - return Err(Error::BadIncrDecrReferenceType(lhs_span)); - } - }, + .. + } => (kind, width), + crate::TypeInner::Pointer { base, .. } => match context.types[base].inner { + crate::TypeInner::Scalar { kind, width } => (kind, width), + _ => return Err(Error::BadIncrDecrReferenceType(lhs_span)), + }, + _ => return Err(Error::BadIncrDecrReferenceType(lhs_span)), + }; + let constant_inner = crate::ConstantInner::Scalar { + width, + value: match kind { + crate::ScalarKind::Sint => crate::ScalarValue::Sint(1), + crate::ScalarKind::Uint => crate::ScalarValue::Uint(1), + _ => return Err(Error::BadIncrDecrReferenceType(lhs_span)), }, - _ => { - return Err(Error::BadIncrDecrReferenceType(lhs_span)); - } }; let constant = context.constants.append( crate::Constant { @@ -3489,6 +3490,18 @@ impl Parser { (Token::Word(word), _) => { let mut emitter = super::Emitter::default(); let statement = match word { + "_" => { + let _ = lexer.next(); + emitter.start(context.expressions); + lexer.expect(Token::Operation('='))?; + self.parse_general_expression( + lexer, + context.as_expression(block, &mut emitter), + )?; + lexer.expect(Token::Separator(';'))?; + block.extend(emitter.finish(context.expressions)); + None + } "let" => { let _ = lexer.next(); emitter.start(context.expressions); diff --git a/third_party/rust/naga/src/keywords/wgsl.rs b/third_party/rust/naga/src/keywords/wgsl.rs index 36a8a55a193f8..3231915b565cb 100644 --- a/third_party/rust/naga/src/keywords/wgsl.rs +++ b/third_party/rust/naga/src/keywords/wgsl.rs @@ -134,7 +134,7 @@ pub const RESERVED: &[&str] = &[ "await", "become", "bf16", - "buffer", + "binding_array", "cast", "catch", "cbuffer", @@ -217,10 +217,8 @@ pub const RESERVED: &[&str] = &[ "impl", "implements", "import", - "in", "inline", "inout", - "input", "instanceof", "interface", "invariant", @@ -276,8 +274,6 @@ pub const RESERVED: &[&str] = &[ "nullptr", "of", "operator", - "out", - "output", "package", "packoffset", "partition", @@ -348,7 +344,6 @@ pub const RESERVED: &[&str] = &[ "technique10", "technique11", "template", - "texture", "texture1D", "texture1DArray", "texture2D", diff --git a/third_party/rust/naga/src/lib.rs b/third_party/rust/naga/src/lib.rs index fa632b29b7fe6..80e7e6f789217 100644 --- a/third_party/rust/naga/src/lib.rs +++ b/third_party/rust/naga/src/lib.rs @@ -698,7 +698,48 @@ pub enum TypeInner { }, /// Can be used to sample values from images. Sampler { comparison: bool }, - /// Array of bindings + + /// Array of bindings. + /// + /// A `BindingArray` represents an array where each element draws its value + /// from a separate bound resource. The array's element type `base` may be + /// [`Image`], [`Sampler`], or any type that would be permitted for a global + /// in the [`Uniform`] or [`Storage`] address spaces. Only global variables + /// may be binding arrays; on the host side, their values are provided by + /// [`TextureViewArray`], [`SamplerArray`], or [`BufferArray`] + /// bindings. + /// + /// Since each element comes from a distinct resource, a binding array of + /// images could have images of varying sizes (but not varying dimensions; + /// they must all have the same `Image` type). Or, a binding array of + /// buffers could have elements that are dynamically sized arrays, each with + /// a different length. + /// + /// Binding arrays are not [`DATA`]. This means that all binding array + /// globals must be placed in the [`Handle`] address space. Referring to + /// such a global produces a `BindingArray` value directly; there are never + /// pointers to binding arrays. The only operation permitted on + /// `BindingArray` values is indexing, which yields the element by value, + /// not a pointer to the element. (This means that buffer array contents + /// cannot be stored to; [naga#1864] covers lifting this restriction.) + /// + /// Unlike textures and samplers, binding arrays are not [`ARGUMENT`], so + /// they cannot be passed as arguments to functions. + /// + /// Naga's WGSL front end supports binding arrays with the type syntax + /// `binding_array`. + /// + /// [`Image`]: TypeInner::Image + /// [`Sampler`]: TypeInner::Sampler + /// [`Uniform`]: AddressSpace::Uniform + /// [`Storage`]: AddressSpace::Storage + /// [`TextureViewArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.TextureViewArray + /// [`SamplerArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.SamplerArray + /// [`BufferArray`]: https://docs.rs/wgpu/latest/wgpu/enum.BindingResource.html#variant.BufferArray + /// [`DATA`]: crate::valid::TypeFlags::DATA + /// [`Handle`]: AddressSpace::Handle + /// [`ARGUMENT`]: crate::valid::TypeFlags::ARGUMENT + /// [naga#1864]: https://github.com/gfx-rs/naga/issues/1864 BindingArray { base: Handle, size: ArraySize }, } @@ -1108,14 +1149,15 @@ pub enum Expression { base: Handle, index: Handle, }, - /// Array access with a known index. + /// Access the same types as [`Access`], plus [`Struct`] with a known index. + /// + /// [`Access`]: Expression::Access + /// [`Struct`]: TypeInner::Struct AccessIndex { base: Handle, index: u32, }, /// Constant value. - /// - /// Every `Constant` expression Constant(Handle), /// Splat scalar into a vector. Splat { diff --git a/third_party/rust/naga/src/proc/mod.rs b/third_party/rust/naga/src/proc/mod.rs index ebda58d1e7424..a4a63abbd0dcf 100644 --- a/third_party/rust/naga/src/proc/mod.rs +++ b/third_party/rust/naga/src/proc/mod.rs @@ -67,6 +67,15 @@ impl super::ScalarValue { } } +impl super::ScalarKind { + pub const fn is_numeric(self) -> bool { + match self { + crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float => true, + crate::ScalarKind::Bool => false, + } + } +} + pub const POINTER_SPAN: u32 = 4; impl super::TypeInner { diff --git a/third_party/rust/naga/src/proc/typifier.rs b/third_party/rust/naga/src/proc/typifier.rs index 9f3f2a49478de..a5304b738cf8e 100644 --- a/third_party/rust/naga/src/proc/typifier.rs +++ b/third_party/rust/naga/src/proc/typifier.rs @@ -821,13 +821,13 @@ impl<'a> ResolveContext<'a> { Mf::CountOneBits | Mf::ReverseBits | Mf::ExtractBits | - Mf::InsertBits => res_arg.clone(), + Mf::InsertBits | Mf::FindLsb | Mf::FindMsb => match *res_arg.inner_with(types) { - Ti::Scalar { kind: _, width } => - TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Sint, width }), - Ti::Vector { size, kind: _, width } => - TypeResolution::Value(Ti::Vector { size, kind: crate::ScalarKind::Sint, width }), + Ti::Scalar { kind: kind @ (crate::ScalarKind::Sint | crate::ScalarKind::Uint), width } => + TypeResolution::Value(Ti::Scalar { kind, width }), + Ti::Vector { size, kind: kind @ (crate::ScalarKind::Sint | crate::ScalarKind::Uint), width } => + TypeResolution::Value(Ti::Vector { size, kind, width }), ref other => return Err(ResolveError::IncompatibleOperands( format!("{:?}({:?})", fun, other) )), @@ -864,6 +864,15 @@ impl<'a> ResolveContext<'a> { size, width: convert.unwrap_or(width), }), + Ti::Matrix { + columns, + rows, + width, + } => TypeResolution::Value(Ti::Matrix { + columns, + rows, + width: convert.unwrap_or(width), + }), ref other => { return Err(ResolveError::IncompatibleOperands(format!( "{:?} as {:?}", diff --git a/third_party/rust/naga/src/valid/expression.rs b/third_party/rust/naga/src/valid/expression.rs index 9d6b852718cc1..f433b456b15c6 100644 --- a/third_party/rust/naga/src/valid/expression.rs +++ b/third_party/rust/naga/src/valid/expression.rs @@ -241,7 +241,7 @@ impl super::Validator { size: crate::ArraySize::Constant(handle), .. } => module.constants[handle].to_array_length().unwrap(), - Ti::Array { .. } | Ti::BindingArray { .. } => !0, // can't statically know, but need run-time checks + Ti::Array { .. } | Ti::BindingArray { .. } => u32::MAX, // can't statically know, but need run-time checks Ti::Pointer { base, .. } if top_level => { resolve_index_limit(module, top, &module.types[base].inner, false)? } diff --git a/third_party/rust/naga/src/valid/interface.rs b/third_party/rust/naga/src/valid/interface.rs index 50c2fe1e90e60..91c121dced905 100644 --- a/third_party/rust/naga/src/valid/interface.rs +++ b/third_party/rust/naga/src/valid/interface.rs @@ -39,6 +39,8 @@ pub enum GlobalVariableError { pub enum VaryingError { #[error("The type {0:?} does not match the varying")] InvalidType(Handle), + #[error("The type {0:?} cannot be used for user-defined entry point inputs or outputs")] + NotIOShareableType(Handle), #[error("Interpolation is not valid")] InvalidInterpolation, #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")] @@ -98,22 +100,26 @@ fn storage_usage(access: crate::StorageAccess) -> GlobalUse { } struct VaryingContext<'a> { - ty: Handle, stage: crate::ShaderStage, output: bool, types: &'a UniqueArena, + type_info: &'a Vec, location_mask: &'a mut BitSet, built_ins: &'a mut crate::FastHashSet, capabilities: Capabilities, } impl VaryingContext<'_> { - fn validate_impl(&mut self, binding: &crate::Binding) -> Result<(), VaryingError> { + fn validate_impl( + &mut self, + ty: Handle, + binding: &crate::Binding, + ) -> Result<(), VaryingError> { use crate::{ BuiltIn as Bi, ScalarKind as Sk, ShaderStage as St, TypeInner as Ti, VectorSize as Vs, }; - let ty_inner = &self.types[self.ty].inner; + let ty_inner = &self.types[ty].inner; match *binding { crate::Binding::BuiltIn(built_in) => { // Ignore the `invariant` field for the sake of duplicate checks, @@ -267,6 +273,13 @@ impl VaryingContext<'_> { interpolation, sampling, } => { + // Only IO-shareable types may be stored in locations. + if !self.type_info[ty.index()] + .flags + .contains(super::TypeFlags::IO_SHAREABLE) + { + return Err(VaryingError::NotIOShareableType(ty)); + } if !self.location_mask.insert(location as usize) { return Err(VaryingError::BindingCollision { location }); } @@ -294,7 +307,7 @@ impl VaryingContext<'_> { return Err(VaryingError::InvalidInterpolation); } } - None => return Err(VaryingError::InvalidType(self.ty)), + None => return Err(VaryingError::InvalidType(ty)), } } } @@ -302,19 +315,22 @@ impl VaryingContext<'_> { Ok(()) } - fn validate(&mut self, binding: Option<&crate::Binding>) -> Result<(), WithSpan> { - let span_context = self.types.get_span_context(self.ty); + fn validate( + &mut self, + ty: Handle, + binding: Option<&crate::Binding>, + ) -> Result<(), WithSpan> { + let span_context = self.types.get_span_context(ty); match binding { Some(binding) => self - .validate_impl(binding) + .validate_impl(ty, binding) .map_err(|e| e.with_span_context(span_context)), None => { - match self.types[self.ty].inner { + match self.types[ty].inner { //TODO: check the member types crate::TypeInner::Struct { ref members, .. } => { for (index, member) in members.iter().enumerate() { - self.ty = member.ty; - let span_context = self.types.get_span_context(self.ty); + let span_context = self.types.get_span_context(ty); match member.binding { None => { return Err(VaryingError::MemberMissingBinding(index as u32) @@ -322,7 +338,7 @@ impl VaryingContext<'_> { } // TODO: shouldn't this be validate? Some(ref binding) => self - .validate_impl(binding) + .validate_impl(member.ty, binding) .map_err(|e| e.with_span_context(span_context))?, } } @@ -364,7 +380,7 @@ impl super::Validator { )); } } - (TypeFlags::DATA | TypeFlags::HOST_SHARED, true) + (TypeFlags::DATA | TypeFlags::HOST_SHAREABLE, true) } crate::AddressSpace::Uniform => { if let Err((ty_handle, disalignment)) = type_info.uniform_layout { @@ -377,7 +393,10 @@ impl super::Validator { } } ( - TypeFlags::DATA | TypeFlags::COPY | TypeFlags::SIZED | TypeFlags::HOST_SHARED, + TypeFlags::DATA + | TypeFlags::COPY + | TypeFlags::SIZED + | TypeFlags::HOST_SHAREABLE, true, ) } @@ -402,7 +421,10 @@ impl super::Validator { )); } ( - TypeFlags::DATA | TypeFlags::COPY | TypeFlags::HOST_SHARED | TypeFlags::SIZED, + TypeFlags::DATA + | TypeFlags::COPY + | TypeFlags::HOST_SHAREABLE + | TypeFlags::SIZED, false, ) } @@ -470,15 +492,15 @@ impl super::Validator { // TODO: add span info to function arguments for (index, fa) in ep.function.arguments.iter().enumerate() { let mut ctx = VaryingContext { - ty: fa.ty, stage: ep.stage, output: false, types: &module.types, + type_info: &self.types, location_mask: &mut self.location_mask, built_ins: &mut argument_built_ins, capabilities: self.capabilities, }; - ctx.validate(fa.binding.as_ref()) + ctx.validate(fa.ty, fa.binding.as_ref()) .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?; } @@ -486,15 +508,15 @@ impl super::Validator { if let Some(ref fr) = ep.function.result { let mut result_built_ins = crate::FastHashSet::default(); let mut ctx = VaryingContext { - ty: fr.ty, stage: ep.stage, output: true, types: &module.types, + type_info: &self.types, location_mask: &mut self.location_mask, built_ins: &mut result_built_ins, capabilities: self.capabilities, }; - ctx.validate(fr.binding.as_ref()) + ctx.validate(fr.ty, fr.binding.as_ref()) .map_err_inner(|e| EntryPointError::Result(e).with_span())?; } diff --git a/third_party/rust/naga/src/valid/mod.rs b/third_party/rust/naga/src/valid/mod.rs index 77b92152b74ac..29ef658db2a54 100644 --- a/third_party/rust/naga/src/valid/mod.rs +++ b/third_party/rust/naga/src/valid/mod.rs @@ -93,7 +93,7 @@ bitflags::bitflags! { /// Support for non-uniform indexing of uniform buffers and storage texture arrays. const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 0x10; /// Support for non-uniform indexing of samplers. - const SAMPLER_NON_UNIFORM_INDEXING = 0x11; + const SAMPLER_NON_UNIFORM_INDEXING = 0x20; } } diff --git a/third_party/rust/naga/src/valid/type.rs b/third_party/rust/naga/src/valid/type.rs index 7c80adf76d4bd..71af43e99c30b 100644 --- a/third_party/rust/naga/src/valid/type.rs +++ b/third_party/rust/naga/src/valid/type.rs @@ -40,14 +40,15 @@ bitflags::bitflags! { /// The data can be copied around. const COPY = 0x4; - /// Can be be used for interfacing between pipeline stages. + /// Can be be used for user-defined IO between pipeline stages. /// - /// This includes non-bool scalars and vectors, matrices, and structs - /// and arrays containing only interface types. - const INTERFACE = 0x8; + /// This covers anything that can be in [`Location`] binding: + /// non-bool scalars and vectors, matrices, and structs and + /// arrays containing only interface types. + const IO_SHAREABLE = 0x8; /// Can be used for host-shareable structures. - const HOST_SHARED = 0x10; + const HOST_SHAREABLE = 0x10; /// This type can be passed as a function argument. const ARGUMENT = 0x40; @@ -155,6 +156,22 @@ fn check_member_layout( }; } +/// Determine whether a pointer in `space` can be passed as an argument. +/// +/// If a pointer in `space` is permitted to be passed as an argument to a +/// user-defined function, return `TypeFlags::ARGUMENT`. Otherwise, return +/// `TypeFlags::empty()`. +/// +/// Pointers passed as arguments to user-defined functions must be in the +/// `Function`, `Private`, or `Workgroup` storage space. +const fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags { + use crate::AddressSpace as As; + match space { + As::Function | As::Private | As::WorkGroup => TypeFlags::ARGUMENT, + As::Uniform | As::Storage { .. } | As::Handle | As::PushConstant => TypeFlags::empty(), + } +} + #[derive(Clone, Debug)] pub(super) struct TypeInfo { pub flags: TypeFlags, @@ -210,13 +227,17 @@ impl super::Validator { if !self.check_width(kind, width) { return Err(TypeError::InvalidWidth(kind, width)); } + let shareable = if kind.is_numeric() { + TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE + } else { + TypeFlags::empty() + }; TypeInfo::new( TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::COPY - | TypeFlags::INTERFACE - | TypeFlags::HOST_SHARED - | TypeFlags::ARGUMENT, + | TypeFlags::ARGUMENT + | shareable, width as u32, ) } @@ -224,14 +245,19 @@ impl super::Validator { if !self.check_width(kind, width) { return Err(TypeError::InvalidWidth(kind, width)); } + let shareable = if kind.is_numeric() { + TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE + } else { + TypeFlags::empty() + }; let count = if size >= crate::VectorSize::Tri { 4 } else { 2 }; TypeInfo::new( TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::COPY - | TypeFlags::INTERFACE - | TypeFlags::HOST_SHARED - | TypeFlags::ARGUMENT, + | TypeFlags::HOST_SHAREABLE + | TypeFlags::ARGUMENT + | shareable, count * (width as u32), ) } @@ -248,8 +274,7 @@ impl super::Validator { TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::COPY - | TypeFlags::INTERFACE - | TypeFlags::HOST_SHARED + | TypeFlags::HOST_SHAREABLE | TypeFlags::ARGUMENT, count * (width as u32), ) @@ -263,7 +288,7 @@ impl super::Validator { return Err(TypeError::InvalidAtomicWidth(kind, width)); } TypeInfo::new( - TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHARED, + TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE, width as u32, ) } @@ -299,20 +324,11 @@ impl super::Validator { } } - // Pointers passed as arguments to user-defined functions must - // be in the `Function`, `Private`, or `Workgroup` storage - // space. We only mark pointers in those spaces as `ARGUMENT`. - // // `Validator::validate_function` actually checks the storage // space of pointer arguments explicitly before checking the // `ARGUMENT` flag, to give better error messages. But it seems // best to set `ARGUMENT` accurately anyway. - let argument_flag = match space { - As::Function | As::Private | As::WorkGroup => TypeFlags::ARGUMENT, - As::Uniform | As::Storage { .. } | As::Handle | As::PushConstant => { - TypeFlags::empty() - } - }; + let argument_flag = ptr_space_argument_flag(space); // Pointers cannot be stored in variables, structure members, or // array elements, so we do not mark them as `DATA`. @@ -322,12 +338,28 @@ impl super::Validator { size: _, kind, width, - space: _, + space, } => { + // ValuePointer should be treated the same way as the equivalent + // Pointer / Scalar / Vector combination, so each step in those + // variants' match arms should have a counterpart here. + // + // However, some cases are trivial: All our implicit base types + // are DATA and SIZED, so we can never return + // `InvalidPointerBase` or `InvalidPointerToUnsized`. if !self.check_width(kind, width) { return Err(TypeError::InvalidWidth(kind, width)); } - TypeInfo::new(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::COPY, 0) + + // `Validator::validate_function` actually checks the storage + // space of pointer arguments explicitly before checking the + // `ARGUMENT` flag, to give better error messages. But it seems + // best to set `ARGUMENT` accurately anyway. + let argument_flag = ptr_space_argument_flag(space); + + // Pointers cannot be stored in variables, structure members, or + // array elements, so we do not mark them as `DATA`. + TypeInfo::new(argument_flag | TypeFlags::SIZED | TypeFlags::COPY, 0) } Ti::Array { base, size, stride } => { if base >= handle { @@ -445,7 +477,7 @@ impl super::Validator { } }; - let base_mask = TypeFlags::COPY | TypeFlags::HOST_SHARED | TypeFlags::INTERFACE; + let base_mask = TypeFlags::COPY | TypeFlags::HOST_SHAREABLE; TypeInfo { flags: TypeFlags::DATA | (base_info.flags & base_mask) | sized_flag, uniform_layout, @@ -461,8 +493,8 @@ impl super::Validator { TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::COPY - | TypeFlags::HOST_SHARED - | TypeFlags::INTERFACE + | TypeFlags::HOST_SHAREABLE + | TypeFlags::IO_SHAREABLE | TypeFlags::ARGUMENT, 1, ); @@ -480,7 +512,7 @@ impl super::Validator { if !base_info.flags.contains(TypeFlags::DATA) { return Err(TypeError::InvalidData(member.ty)); } - if !base_info.flags.contains(TypeFlags::HOST_SHARED) { + if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) { if ti.uniform_layout.is_ok() { ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable)); } @@ -495,7 +527,7 @@ impl super::Validator { // to not bother with offsets/alignments if they are never // used for host sharing. if member.offset == 0 { - ti.flags.set(TypeFlags::HOST_SHARED, false); + ti.flags.set(TypeFlags::HOST_SHAREABLE, false); } else { return Err(TypeError::MemberOverlap { index: i as u32, diff --git a/third_party/rust/wgpu-core/.cargo-checksum.json b/third_party/rust/wgpu-core/.cargo-checksum.json index 6d2273a973e56..0667deda91a9f 100644 --- a/third_party/rust/wgpu-core/.cargo-checksum.json +++ b/third_party/rust/wgpu-core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"f4dcb8db1e3276bfa485eac69d7906030719cd93787a44acc4cfd3598969ba8e","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"218bfaa87b291be693c27ea496111c4a66edcbc585ce2661c060d5996822334b","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"f015e176b7d7f67cd17dfef9ca67f46fd2c2edcc33286fc250929b9f00c9b18c","src/command/clear.rs":"03f624521571c46080051baa95b60230aca5698e2ddc6c83100a4c1162de4e1a","src/command/compute.rs":"05f405bba84c411f300eb635a7b77bbb557bc94fb91253b5578c49f62e1530d1","src/command/draw.rs":"c04bdbcdabfdf5e3b4524bf155089b20185e283847ecfd628acc983101b2cbba","src/command/memory_init.rs":"f7c73879b9fa728039a45d78f1f865e4eca863c860b3775a4542c973dfdb68c5","src/command/mod.rs":"6bee42ee62915d9ef6afe7c17fc3da04b22574b63ed9e38067ff2d92e51fe3bd","src/command/query.rs":"18659df22356493697ce1c5b56fe2ca0e2f3f1ef29bdf00d65f064eeecba5130","src/command/render.rs":"155aaf90898dade6ee54754824a151f3b3ddc1387767f35ca8cc991c61ac0156","src/command/transfer.rs":"6277878f4d99516bd7db5612aaf4fac280a46d48c44575a7c5f501d6fbae5c61","src/conv.rs":"15d87cd2a91cb9514273e86441940b86380861ec146796e5f214e95e4124d975","src/device/life.rs":"72ae33cc066ca1537c89ba1c7b12dab89957ea0449a4348e007a73aad471f4a5","src/device/mod.rs":"79dec24662a61af89ceeddef19c5fd101c6d97ae505f2a7f95d5699777b876e6","src/device/queue.rs":"5d070ff1f94f0796ece9359548806e41ed7c8f466828328f04fc7616644796e5","src/device/trace.rs":"de575a8213c8ae9df711e4b6afe5736d71ac65bf141375fe044d3b6c4375e039","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"e12f3ac5cbb126d37ca81c736ead67c65484b61f6f9433fc2b6ac2f904112490","src/id.rs":"420ef1442ce4663b4f8c011117bdc20682aaea4ad7bfa08c526ea3022a7b6d27","src/init_tracker/buffer.rs":"ccdddaace101f921463bf6c62ed5aca01a6520717a850b5d4442c3551e9f1875","src/init_tracker/mod.rs":"273c6ee67a927784a617899c6fe5560e47108248ab67cabdc2eebcba53133364","src/init_tracker/texture.rs":"75cf8753d850792ebc11da8ed7936836a23e12cbcdd52213a5e85ea3ff1412c1","src/instance.rs":"82d91ad4d7a98ee6fd87c05e93576e8351ccc6fa0f43fb880e1fddc5d54ca319","src/lib.rs":"f7101b4e0476e90d1ab7be5575d26855e074afb868a2f6f8dbcff04040e56689","src/pipeline.rs":"ffabdc74656717276241b1ca2ed043fabf18795662a523828193aea99d7a9ef5","src/present.rs":"50759b10537bf8bd52b1bb4cc48671fa860b325bd67d7f4d5057f8dc88794fdd","src/resource.rs":"2eb571779b9e1f3adaa67861a8d982ab58ade510023e6910936a0a1a952fd9e8","src/track/buffer.rs":"58828fa4bb6d9ca0f4c2549fd271b1ada738598675cc13714db99ef676c9332a","src/track/mod.rs":"753e8041bc4d10da311b95544e768f5f6dab235763305708632d8ad5a3f7984c","src/track/range.rs":"d24340a235103fd18e740236ebfe59b434023bc07abaeebc11c4d7b44057e267","src/track/texture.rs":"f64028e06c215d7dc9d1ac293acbcc40e88b9dbf7eb0e010214f0327d4569794","src/validation.rs":"254e9fbdd24949c3786ad300e1ca90a724ea022f1698f84f059b6fb298a2094c"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"73d22ddbc04b486026d12675ef898363c6eea04ae23a9251acdd1b000c73b126","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"79f024cdb136f44066d67ec7dc56bde7fdf3bf9e89874dc4db40e504099b2822","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"6f940e6de1e84b858790e2801ba82c83f2bc0c6afbff962576b3dd64ac315de3","src/command/clear.rs":"568aaf9d0843bada18b68980b52dd8021830c28fff36551459fad5f6baea72e1","src/command/compute.rs":"b58ae86ffbd8280af27f063d514b17c5dafee3f3ddfd5637ca050135681eb764","src/command/draw.rs":"1b9b6531b7536bc0f864ab9fdeff376993de04e33554e84c7b2db7dc65e31327","src/command/memory_init.rs":"03c3267b311f389af859615ceea8a648b402a323062cc8f0fe2690a0fb390b97","src/command/mod.rs":"c0f00529bce224972d844d2fdc9f659ffa065512086315b7bcd767501961ee1a","src/command/query.rs":"34d22d33e4713ff7ca0b345b14cdbb6177236e782b5dfb38d907215c4deb6907","src/command/render.rs":"b21201c5b9574e98c066f988bcf91b1cde0d1847fc1db683291cb059a10f3dd8","src/command/transfer.rs":"7e5e13f04fef63e036291b2838c0f0097717ec497f98f420b71296b2cc691907","src/conv.rs":"87097903e86048c9110f526f7df1749186f84cb663d75d9d40a0c467befc89ea","src/device/life.rs":"857a71da94f5f6f043f304ada7dc9ab95c6a26ed0ff63f3d64a77942e28bcafe","src/device/mod.rs":"8b886c68cd2aaec9aabdbaea0f2f256fe546ae0242fe7c9b0b8a55686f215071","src/device/queue.rs":"5fe332a0d27dafff720b19e436d991a35affd2a8031f78c2a81439a49105edd6","src/device/trace.rs":"de575a8213c8ae9df711e4b6afe5736d71ac65bf141375fe044d3b6c4375e039","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"4cc404cc79578d7a6757f74ab1fbeeb357a13a4de5f0fe87affaea8895395c8d","src/id.rs":"3ec97d09f900f34f9ad38a555ddcadb77bd9977d3d39bfad030b9b34649cf502","src/init_tracker/buffer.rs":"ccdddaace101f921463bf6c62ed5aca01a6520717a850b5d4442c3551e9f1875","src/init_tracker/mod.rs":"273c6ee67a927784a617899c6fe5560e47108248ab67cabdc2eebcba53133364","src/init_tracker/texture.rs":"d02babc4f194f91853b5e9a71bd5b20d9434842cf242f29ed9d39661bfa44980","src/instance.rs":"4a19ac634a4dd22938586e3bc554ab69f079abb2d836ef932f06cee1655d9336","src/lib.rs":"f44250478f095aa7d61fb4773692037f465d1e8df9c5626000723d4e1961166e","src/pipeline.rs":"ffabdc74656717276241b1ca2ed043fabf18795662a523828193aea99d7a9ef5","src/present.rs":"5b760e252242be41d70f09cc46b95f2bfcb8258c3482755a7bec3b5a7e4bbcb6","src/resource.rs":"50021911ff214165a32129eabc2275945c2fd22bb736fad2977634ea8ef8362d","src/track/buffer.rs":"1a7400ec55f3c16bc074c46d11b9515762b558a333d36eb236d2e7d99701bbe5","src/track/mod.rs":"3a4b07c8f1ff168609ca521b441e1e2acc00c62d7e9e4dc39cb8ab83d9813d58","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"593ec39e01e18048100ab0e1869f430851b83b96bd0497b8e524efda38782a46","src/track/texture.rs":"de154923e4825fa120360aae61aec27370b44196464edea6468bf933976ea20c","src/validation.rs":"27c76c48eaf3ca6be111855d7b1ab8ef94c2f73ed5d5e4f758d82799099f014b"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-core/Cargo.toml b/third_party/rust/wgpu-core/Cargo.toml index e93995d2a4775..aa13091282171 100644 --- a/third_party/rust/wgpu-core/Cargo.toml +++ b/third_party/rust/wgpu-core/Cargo.toml @@ -26,6 +26,7 @@ vulkan-portability = ["hal/vulkan"] [dependencies] arrayvec = "0.7" bitflags = "1.0" +bit-vec = "0.6" codespan-reporting = "0.11" copyless = "0.1" fxhash = "0.2" @@ -41,7 +42,7 @@ thiserror = "1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "1aa91549" +rev = "571302e" #version = "0.8" features = ["span", "validate", "wgsl-in"] diff --git a/third_party/rust/wgpu-core/LICENSE.APACHE b/third_party/rust/wgpu-core/LICENSE.APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/third_party/rust/wgpu-core/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/third_party/rust/wgpu-core/LICENSE.MIT b/third_party/rust/wgpu-core/LICENSE.MIT new file mode 100644 index 0000000000000..4699691b8ed37 --- /dev/null +++ b/third_party/rust/wgpu-core/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/wgpu-core/src/binding_model.rs b/third_party/rust/wgpu-core/src/binding_model.rs index 030574842a321..ef97af6d91c65 100644 --- a/third_party/rust/wgpu-core/src/binding_model.rs +++ b/third_party/rust/wgpu-core/src/binding_model.rs @@ -1,10 +1,10 @@ use crate::{ device::{DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT}, error::{ErrorFormatter, PrettyError}, - hub::Resource, - id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid}, + hub::{HalApi, Resource}, + id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureId, TextureViewId, Valid}, init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction}, - track::{TrackerSet, UsageConflict, DUMMY_SELECTOR}, + track::{BindGroupStates, UsageConflict}, validation::{MissingBufferUsageError, MissingTextureUsageError}, FastHashMap, Label, LifeGuard, MultiRefCount, Stored, }; @@ -16,10 +16,7 @@ use serde::Deserialize; #[cfg(feature = "trace")] use serde::Serialize; -use std::{ - borrow::{Borrow, Cow}, - ops::Range, -}; +use std::{borrow::Cow, ops::Range}; use thiserror::Error; @@ -63,6 +60,8 @@ pub enum CreateBindGroupError { InvalidBuffer(BufferId), #[error("texture view {0:?} is invalid")] InvalidTextureView(TextureViewId), + #[error("texture {0:?} is invalid")] + InvalidTexture(TextureId), #[error("sampler {0:?} is invalid")] InvalidSampler(SamplerId), #[error( @@ -709,13 +708,12 @@ pub(crate) fn buffer_binding_type_alignment( } } -#[derive(Debug)] -pub struct BindGroup { +pub struct BindGroup { pub(crate) raw: A::BindGroup, pub(crate) device_id: Stored, pub(crate) layout_id: Valid, pub(crate) life_guard: LifeGuard, - pub(crate) used: TrackerSet, + pub(crate) used: BindGroupStates, pub(crate) used_buffer_ranges: Vec, pub(crate) used_texture_ranges: Vec, pub(crate) dynamic_binding_info: Vec, @@ -724,7 +722,7 @@ pub struct BindGroup { pub(crate) late_buffer_binding_sizes: Vec, } -impl BindGroup { +impl BindGroup { pub(crate) fn validate_dynamic_bindings( &self, offsets: &[wgt::DynamicOffset], @@ -766,13 +764,7 @@ impl BindGroup { } } -impl Borrow<()> for BindGroup { - fn borrow(&self) -> &() { - &DUMMY_SELECTOR - } -} - -impl Resource for BindGroup { +impl Resource for BindGroup { const TYPE: &'static str = "BindGroup"; fn life_guard(&self) -> &LifeGuard { diff --git a/third_party/rust/wgpu-core/src/command/bundle.rs b/third_party/rust/wgpu-core/src/command/bundle.rs index fc51927858d1a..6121ab8f9fe05 100644 --- a/third_party/rust/wgpu-core/src/command/bundle.rs +++ b/third_party/rust/wgpu-core/src/command/bundle.rs @@ -1,40 +1,76 @@ /*! Render Bundles -## Software implementation +A render bundle is a prerecorded sequence of commands that can be replayed on a +command encoder with a single call. A single bundle can replayed any number of +times, on different encoders. Constructing a render bundle lets `wgpu` validate +and analyze its commands up front, so that replaying a bundle can be more +efficient than simply re-recording its commands each time. -The path from nothing to using a render bundle consists of 3 phases. +One important property of render bundles is that the draw calls in a render +bundle depend solely on the pipeline and state established within the render +bundle itself. A draw call in a bundle will never use a vertex buffer, say, that +was set in the `RenderPass` before executing the bundle. We call this property +'isolation', in that a render bundle is somewhat isolated from the passes that +use it. -### Initial command encoding +Render passes are also isolated from the effects of bundles. After executing a +render bundle, a render pass's pipeline, bind groups, and vertex and index +buffers are are unset, so the bundle cannot affect later draw calls in the pass. -User creates a `RenderBundleEncoder` and populates it by issuing commands -from `bundle_ffi` module, just like with `RenderPass`, except that the -set of available commands is reduced. Everything is written into a `RawPass`. +Not all commands are available in bundles; for example, a render bundle may not +contain a [`RenderCommand::SetViewport`] command. -### Bundle baking +Most of `wgpu`'s backend graphics APIs have something like bundles. For example, +Vulkan calls them "secondary command buffers", and Metal calls them "indirect +command buffers". However, `wgpu`'s implementation of render bundles does not +take advantage of those underlying platform features. At the hal level, `wgpu` +render bundles just replay the commands. -Once the commands are encoded, user calls `render_bundle_encoder_finish`. -This is perhaps the most complex part of the logic. It consumes the -commands stored in `RawPass`, while validating everything, tracking the state, -and re-recording the commands into a separate `Vec`. It -doesn't actually execute any commands. +## Render Bundle Lifecycle -What's more important, is that the produced vector of commands is "normalized", -which means it can be executed verbatim without any state tracking. More -formally, "normalized" command stream guarantees that any state required by -a draw call is set explicitly by one of the commands between the draw call -and the last changing of the pipeline. +To create a render bundle: + +1) Create a [`RenderBundleEncoder`] by calling + [`Global::device_create_render_bundle_encoder`][Gdcrbe]. + +2) Record commands in the `RenderBundleEncoder` using functions from the + [`bundle_ffi`] module. + +3) Call [`Global::render_bundle_encoder_finish`][Grbef], which analyzes and cleans up + the command stream and returns a `RenderBundleId`. + +4) Then, any number of times, call [`wgpu_render_pass_execute_bundles`][wrpeb] to + execute the bundle as part of some render pass. + +## Implementation + +The most complex part of render bundles is the "finish" step, mostly implemented +in [`RenderBundleEncoder::finish`]. This consumes the commands stored in the +encoder's [`BasePass`], while validating everything, tracking the state, +dropping redundant or unnecessary commands, and presenting the results as a new +[`RenderBundle`]. It doesn't actually execute any commands. + +This step also enforces the 'isolation' property mentioned above: every draw +call is checked to ensure that the resources it uses on were established since +the last time the pipeline was set. This means the bundle can be executed +verbatim without any state tracking. ### Execution When the bundle is used in an actual render pass, `RenderBundle::execute` is called. It goes through the commands and issues them into the native command -buffer. Thanks to the "normalized" property, it doesn't track any bind group -invalidations or index format changes. +buffer. Thanks to isolation, it doesn't track any bind group invalidations or +index format changes. + +[Gdcrbe]: crate::hub::Global::device_create_render_bundle_encoder +[Grbef]: crate::hub::Global::render_bundle_encoder_finish +[wrpeb]: crate::command::render_ffi::wgpu_render_pass_execute_bundles !*/ + #![allow(clippy::reversed_empty_ranges)] use crate::{ - binding_model::buffer_binding_type_alignment, + binding_model::{self, buffer_binding_type_alignment}, command::{ BasePass, BindGroupStateChange, DrawError, MapPassErr, PassErrorScope, RenderCommand, RenderCommandError, StateChange, @@ -48,8 +84,9 @@ use crate::{ hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token}, id, init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction}, - pipeline::PipelineFlags, - track::{TrackerSet, UsageConflict}, + pipeline::{self, PipelineFlags}, + resource, + track::RenderBundleScope, validation::check_buffer_usage, Label, LabelHelpers, LifeGuard, Stored, }; @@ -117,7 +154,7 @@ impl RenderBundleEncoder { }, sample_count: { let sc = desc.sample_count; - if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) { + if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) { return Err(CreateRenderBundleError::InvalidSampleCount(sc)); } sc @@ -167,39 +204,56 @@ impl RenderBundleEncoder { self.parent_id } - pub(crate) fn finish( + /// Convert this encoder's commands into a [`RenderBundle`]. + /// + /// We want executing a [`RenderBundle`] to be quick, so we take + /// this opportunity to clean up the [`RenderBundleEncoder`]'s + /// command stream and gather metadata about it that will help + /// keep [`ExecuteBundle`] simple and fast. We remove redundant + /// commands (along with their side data), note resource usage, + /// and accumulate buffer and texture initialization actions. + /// + /// [`ExecuteBundle`]: RenderCommand::ExecuteBundle + pub(crate) fn finish( self, desc: &RenderBundleDescriptor, device: &Device, hub: &Hub, token: &mut Token>, - ) -> Result { + ) -> Result, RenderBundleError> { let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(token); let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); - let (buffer_guard, _) = hub.buffers.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 { - trackers: TrackerSet::new(self.parent_id.backend()), + trackers: RenderBundleScope::new( + &*buffer_guard, + &*texture_guard, + &*bind_group_guard, + &*pipeline_guard, + &*query_set_guard, + ), index: IndexState::new(), vertex: (0..hal::MAX_VERTEX_BUFFERS) .map(|_| VertexState::new()) .collect(), - bind: (0..hal::MAX_BIND_GROUPS) - .map(|_| BindState::new()) - .collect(), + bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(), push_constant_ranges: PushConstantState::new(), - raw_dynamic_offsets: Vec::new(), flat_dynamic_offsets: Vec::new(), used_bind_groups: 0, pipeline: None, }; let mut commands = Vec::new(); - let mut base = self.base.as_ref(); let mut pipeline_layout_id = None::>; let mut buffer_memory_init_actions = Vec::new(); let mut texture_memory_init_actions = Vec::new(); + let base = self.base.as_ref(); + let mut next_dynamic_offset = 0; + for &command in base.commands { match command { RenderCommand::SetBindGroup { @@ -209,6 +263,15 @@ impl RenderBundleEncoder { } => { let scope = PassErrorScope::SetBindGroup(bind_group_id); + let bind_group: &binding_model::BindGroup = state + .trackers + .bind_groups + .add_single(&*bind_group_guard, bind_group_id) + .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id)) + .map_pass_err(scope)?; + self.check_valid_to_use(bind_group.device_id.value) + .map_pass_err(scope)?; + let max_bind_groups = device.limits.max_bind_groups; if (index as u32) >= max_bind_groups { return Err(RenderCommandError::BindGroupIndexOutOfRange { @@ -218,15 +281,13 @@ impl RenderBundleEncoder { .map_pass_err(scope); } - let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize]; - base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..]; + // Identify the next `num_dynamic_offsets` entries from `base.dynamic_offsets`. + let num_dynamic_offsets = num_dynamic_offsets as usize; + let offsets_range = + next_dynamic_offset..next_dynamic_offset + num_dynamic_offsets; + next_dynamic_offset = offsets_range.end; + let offsets = &base.dynamic_offsets[offsets_range.clone()]; - let bind_group = state - .trackers - .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id)) - .map_pass_err(scope)?; if bind_group.dynamic_binding_info.len() != offsets.len() { return Err(RenderCommandError::InvalidDynamicOffsetCount { actual: offsets.len(), @@ -254,11 +315,13 @@ impl RenderBundleEncoder { buffer_memory_init_actions.extend_from_slice(&bind_group.used_buffer_ranges); texture_memory_init_actions.extend_from_slice(&bind_group.used_texture_ranges); - state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets); - state - .trackers - .merge_extend_stateful(&bind_group.used) - .map_pass_err(scope)?; + state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets_range); + unsafe { + state + .trackers + .merge_bind_group(&*texture_guard, &bind_group.used) + .map_pass_err(scope)? + }; //Note: stateless trackers are not merged: the lifetime reference // is held to the bind group itself. } @@ -267,11 +330,13 @@ impl RenderBundleEncoder { state.pipeline = Some(pipeline_id); - let pipeline = state + let pipeline: &pipeline::RenderPipeline = state .trackers - .render_pipes - .use_extend(&*pipeline_guard, pipeline_id, (), ()) - .map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id)) + .render_pipelines + .add_single(&*pipeline_guard, pipeline_id) + .ok_or(RenderCommandError::InvalidPipeline(pipeline_id)) + .map_pass_err(scope)?; + self.check_valid_to_use(pipeline.device_id.value) .map_pass_err(scope)?; self.context @@ -307,11 +372,13 @@ impl RenderBundleEncoder { size, } => { let scope = PassErrorScope::SetIndexBuffer(buffer_id); - let buffer = state + let buffer: &resource::Buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX) - .unwrap(); + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX) + .map_pass_err(scope)?; + self.check_valid_to_use(buffer.device_id.value) + .map_pass_err(scope)?; check_buffer_usage(buffer.usage, wgt::BufferUsages::INDEX) .map_pass_err(scope)?; @@ -334,11 +401,13 @@ impl RenderBundleEncoder { size, } => { let scope = PassErrorScope::SetVertexBuffer(buffer_id); - let buffer = state + let buffer: &resource::Buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX) - .unwrap(); + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX) + .map_pass_err(scope)?; + self.check_valid_to_use(buffer.device_id.value) + .map_pass_err(scope)?; check_buffer_usage(buffer.usage, wgt::BufferUsages::VERTEX) .map_pass_err(scope)?; @@ -404,7 +473,7 @@ impl RenderBundleEncoder { .map_pass_err(scope); } commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); + commands.extend(state.flush_binds(base.dynamic_offsets)); commands.push(command); } RenderCommand::DrawIndexed { @@ -441,7 +510,7 @@ impl RenderBundleEncoder { } commands.extend(state.index.flush()); commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); + commands.extend(state.flush_binds(base.dynamic_offsets)); commands.push(command); } RenderCommand::MultiDrawIndirect { @@ -459,11 +528,13 @@ impl RenderBundleEncoder { .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; - let buffer = state + let buffer: &resource::Buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT) - .unwrap(); + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT) + .map_pass_err(scope)?; + self.check_valid_to_use(buffer.device_id.value) + .map_pass_err(scope)?; check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -474,7 +545,7 @@ impl RenderBundleEncoder { )); commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); + commands.extend(state.flush_binds(base.dynamic_offsets)); commands.push(command); } RenderCommand::MultiDrawIndirect { @@ -492,11 +563,12 @@ impl RenderBundleEncoder { .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; - let buffer = state + let buffer: &resource::Buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT) - .map_err(|err| RenderCommandError::Buffer(buffer_id, err)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT) + .map_pass_err(scope)?; + self.check_valid_to_use(buffer.device_id.value) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -509,7 +581,7 @@ impl RenderBundleEncoder { commands.extend(state.index.flush()); commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); + commands.extend(state.flush_binds(base.dynamic_offsets)); commands.push(command); } RenderCommand::MultiDrawIndirect { .. } @@ -549,6 +621,17 @@ impl RenderBundleEncoder { }) } + fn check_valid_to_use( + &self, + device_id: id::Valid, + ) -> Result<(), RenderBundleErrorInner> { + if device_id.0 != self.parent_id { + return Err(RenderBundleErrorInner::NotValidToUse); + } + + Ok(()) + } + pub fn set_index_buffer( &mut self, buffer_id: id::BufferId, @@ -599,24 +682,23 @@ pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor>; //Note: here, `RenderBundle` is just wrapping a raw stream of render commands. // The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle, // or Metal indirect command buffer. -#[derive(Debug)] -pub struct RenderBundle { +pub struct RenderBundle { // Normalized command stream. It can be executed verbatim, // without re-binding anything on the pipeline change. base: BasePass, pub(super) is_ds_read_only: bool, pub(crate) device_id: Stored, - pub(crate) used: TrackerSet, + pub(crate) used: RenderBundleScope, pub(super) buffer_memory_init_actions: Vec, pub(super) texture_memory_init_actions: Vec, pub(super) context: RenderPassContext, pub(crate) life_guard: LifeGuard, } -unsafe impl Send for RenderBundle {} -unsafe impl Sync for RenderBundle {} +unsafe impl Send for RenderBundle {} +unsafe impl Sync for RenderBundle {} -impl RenderBundle { +impl RenderBundle { /// Actually encode the contents into a native command buffer. /// /// This is partially duplicating the logic of `command_encoder_run_render_pass`. @@ -626,7 +708,7 @@ impl RenderBundle { /// Note that the function isn't expected to fail, generally. /// All the validation has already been done by this point. /// The only failure condition is if some of the used buffers are destroyed. - pub(super) unsafe fn execute( + pub(super) unsafe fn execute( &self, raw: &mut A::CommandEncoder, pipeline_layout_guard: &Storage< @@ -815,7 +897,7 @@ impl RenderBundle { } } -impl Resource for RenderBundle { +impl Resource for RenderBundle { const TYPE: &'static str = "RenderBundle"; fn life_guard(&self) -> &LifeGuard { @@ -823,6 +905,15 @@ impl Resource for RenderBundle { } } +/// A render bundle's current index buffer state. +/// +/// [`RenderBundleEncoder::finish`] uses this to drop redundant +/// `SetIndexBuffer` commands from the final [`RenderBundle`]. It +/// records index buffer state changes here, and then calls this +/// type's [`flush`] method before any indexed draw command to produce +/// a `SetIndexBuffer` command if one is necessary. +/// +/// [`flush`]: IndexState::flush #[derive(Debug)] struct IndexState { buffer: Option, @@ -833,6 +924,7 @@ struct IndexState { } impl IndexState { + /// Return a fresh state: no index buffer has been set yet. fn new() -> Self { Self { buffer: None, @@ -843,6 +935,9 @@ impl IndexState { } } + /// Return the number of entries in the current index buffer. + /// + /// Panic if no index buffer has been set. fn limit(&self) -> u32 { assert!(self.buffer.is_some()); let bytes_per_index = match self.format { @@ -852,6 +947,8 @@ impl IndexState { ((self.range.end - self.range.start) / bytes_per_index) as u32 } + /// Prepare for an indexed draw, producing a `SetIndexBuffer` + /// command if necessary. fn flush(&mut self) -> Option { if self.is_dirty { self.is_dirty = false; @@ -866,6 +963,7 @@ impl IndexState { } } + /// Set the current index buffer's format. fn set_format(&mut self, format: wgt::IndexFormat) { if self.format != format { self.format = format; @@ -873,6 +971,7 @@ impl IndexState { } } + /// Set the current index buffer. fn set_buffer(&mut self, id: id::BufferId, range: Range) { self.buffer = Some(id); self.range = range; @@ -880,6 +979,15 @@ impl IndexState { } } +/// The state of a single vertex buffer slot during render bundle encoding. +/// +/// [`RenderBundleEncoder::finish`] uses this to drop redundant +/// `SetVertexBuffer` commands from the final [`RenderBundle`]. It +/// records one vertex buffer slot's state changes here, and then +/// calls this type's [`flush`] method just before any draw command to +/// produce a `SetVertexBuffer` commands if one is necessary. +/// +/// [`flush`]: IndexState::flush #[derive(Debug)] struct VertexState { buffer: Option, @@ -890,6 +998,8 @@ struct VertexState { } impl VertexState { + /// Construct a fresh `VertexState`: no buffer has been set for + /// this slot. fn new() -> Self { Self { buffer: None, @@ -900,12 +1010,16 @@ impl VertexState { } } + /// Set this slot's vertex buffer. fn set_buffer(&mut self, buffer_id: id::BufferId, range: Range) { self.buffer = Some(buffer_id); self.range = range; self.is_dirty = true; } + /// Generate a `SetVertexBuffer` command for this slot, if necessary. + /// + /// `slot` is the index of the vertex buffer slot that `self` tracks. fn flush(&mut self, slot: u32) -> Option { if self.is_dirty { self.is_dirty = false; @@ -921,39 +1035,22 @@ impl VertexState { } } +/// A bind group that has been set at a particular index during render bundle encoding. #[derive(Debug)] struct BindState { - bind_group: Option<(id::BindGroupId, id::BindGroupLayoutId)>, - dynamic_offsets: Range, - is_dirty: bool, -} + /// The id of the bind group set at this index. + bind_group_id: id::BindGroupId, -impl BindState { - fn new() -> Self { - Self { - bind_group: None, - dynamic_offsets: 0..0, - is_dirty: false, - } - } + /// The layout of `group`. + layout_id: id::Valid, - fn set_group( - &mut self, - bind_group_id: id::BindGroupId, - layout_id: id::BindGroupLayoutId, - dyn_offset: usize, - dyn_count: usize, - ) -> bool { - match self.bind_group { - Some((bg_id, _)) if bg_id == bind_group_id && dyn_count == 0 => false, - _ => { - self.bind_group = Some((bind_group_id, layout_id)); - self.dynamic_offsets = dyn_offset..dyn_offset + dyn_count; - self.is_dirty = true; - true - } - } - } + /// The range of dynamic offsets for this bind group, in the original + /// command stream's `BassPass::dynamic_offsets` array. + dynamic_offsets: Range, + + /// True if this index's contents have been changed since the last time we + /// generated a `SetBindGroup` command. + is_dirty: bool, } #[derive(Debug)] @@ -992,20 +1089,42 @@ struct VertexLimitState { instance_limit_slot: u32, } -#[derive(Debug)] -struct State { - trackers: TrackerSet, +/// State for analyzing and cleaning up bundle command streams. +/// +/// To minimize state updates, [`RenderBundleEncoder::finish`] +/// actually just applies commands like [`SetBindGroup`] and +/// [`SetIndexBuffer`] to the simulated state stored here, and then +/// calls the `flush_foo` methods before draw calls to produce the +/// update commands we actually need. +struct State { + /// Resources used by this bundle. This will become [`RenderBundle::used`]. + trackers: RenderBundleScope, + + /// The current index buffer. We flush this state before indexed + /// draw commands. index: IndexState, + + /// The state of each vertex buffer slot. vertex: ArrayVec, - bind: ArrayVec, + + /// The bind group set at each index, if any. + bind: ArrayVec, { hal::MAX_BIND_GROUPS }>, + push_constant_ranges: PushConstantState, - raw_dynamic_offsets: Vec, + + /// Dynamic offset values used by the cleaned-up command sequence. + /// + /// This becomes the final [`RenderBundle`]'s [`BasePass`]'s + /// [`dynamic_offsets`] list. + /// + /// [`dynamic_offsets`]: BasePass::dynamic_offsets flat_dynamic_offsets: Vec, + used_bind_groups: usize, pipeline: Option, } -impl State { +impl State { fn vertex_limits(&self) -> VertexLimitState { let mut vert_state = VertexLimitState { vertex_limit: u32::MAX, @@ -1036,11 +1155,10 @@ impl State { vert_state } - fn invalidate_group_from(&mut self, slot: usize) { - for bind in self.bind[slot..].iter_mut() { - if bind.bind_group.is_some() { - bind.is_dirty = true; - } + /// Mark all non-empty bind group table entries from `index` onwards as dirty. + fn invalidate_group_from(&mut self, index: usize) { + for contents in self.bind[index..].iter_mut().flatten() { + contents.is_dirty = true; } } @@ -1049,17 +1167,30 @@ impl State { slot: u8, bind_group_id: id::BindGroupId, layout_id: id::Valid, - offsets: &[wgt::DynamicOffset], + dynamic_offsets: Range, ) { - if self.bind[slot as usize].set_group( - bind_group_id, - layout_id.0, - self.raw_dynamic_offsets.len(), - offsets.len(), - ) { - self.invalidate_group_from(slot as usize + 1); + // If this call wouldn't actually change this index's state, we can + // return early. (If there are dynamic offsets, the range will always + // be different.) + if dynamic_offsets.is_empty() { + if let Some(ref contents) = self.bind[slot as usize] { + if contents.bind_group_id == bind_group_id { + return; + } + } } - self.raw_dynamic_offsets.extend(offsets); + + // Record the index's new state. + self.bind[slot as usize] = Some(BindState { + bind_group_id, + layout_id, + dynamic_offsets, + is_dirty: true, + }); + + // Once we've changed the bind group at a particular index, all + // subsequent indices need to be rewritten. + self.invalidate_group_from(slot as usize + 1); } fn set_pipeline( @@ -1090,8 +1221,8 @@ impl State { self.bind .iter() .zip(layout_ids) - .position(|(bs, layout_id)| match bs.bind_group { - Some((_, bgl_id)) => bgl_id != layout_id.0, + .position(|(entry, &layout_id)| match *entry { + Some(ref contents) => contents.layout_id != layout_id, None => false, }) }; @@ -1129,29 +1260,37 @@ impl State { .flat_map(|(i, vs)| vs.flush(i as u32)) } - fn flush_binds(&mut self) -> impl Iterator + '_ { - for bs in self.bind[..self.used_bind_groups].iter() { - if bs.is_dirty { + /// Generate `SetBindGroup` commands for any bind groups that need to be updated. + fn flush_binds( + &mut self, + dynamic_offsets: &[wgt::DynamicOffset], + ) -> impl Iterator + '_ { + // Append each dirty bind group's dynamic offsets to `flat_dynamic_offsets`. + for contents in self.bind[..self.used_bind_groups].iter().flatten() { + if contents.is_dirty { self.flat_dynamic_offsets - .extend_from_slice(&self.raw_dynamic_offsets[bs.dynamic_offsets.clone()]); + .extend_from_slice(&dynamic_offsets[contents.dynamic_offsets.clone()]); } } - self.bind + + // Then, generate `SetBindGroup` commands to update the dirty bind + // groups. After this, all bind groups are clean. + self.bind[..self.used_bind_groups] .iter_mut() - .take(self.used_bind_groups) .enumerate() - .flat_map(|(i, bs)| { - if bs.is_dirty { - bs.is_dirty = false; - Some(RenderCommand::SetBindGroup { - index: i as u8, - bind_group_id: bs.bind_group.unwrap().0, - num_dynamic_offsets: (bs.dynamic_offsets.end - bs.dynamic_offsets.start) - as u8, - }) - } else { - None + .flat_map(|(i, entry)| { + if let Some(ref mut contents) = *entry { + if contents.is_dirty { + contents.is_dirty = false; + let offsets = &contents.dynamic_offsets; + return Some(RenderCommand::SetBindGroup { + index: i as u8, + bind_group_id: contents.bind_group_id, + num_dynamic_offsets: (offsets.end - offsets.start) as u8, + }); + } } + None }) } } @@ -1159,13 +1298,13 @@ impl State { /// Error encountered when finishing recording a render bundle. #[derive(Clone, Debug, Error)] pub(super) enum RenderBundleErrorInner { + #[error("resource is not valid to use with this render bundle because the resource and the bundle come from different devices")] + NotValidToUse, #[error(transparent)] Device(#[from] DeviceError), #[error(transparent)] RenderCommand(RenderCommandError), #[error(transparent)] - ResourceUsageConflict(#[from] UsageConflict), - #[error(transparent)] Draw(#[from] DrawError), #[error(transparent)] MissingDownlevelFlags(#[from] MissingDownlevelFlags), diff --git a/third_party/rust/wgpu-core/src/command/clear.rs b/third_party/rust/wgpu-core/src/command/clear.rs index 0bd909c76ed9b..3e94843798f0f 100644 --- a/third_party/rust/wgpu-core/src/command/clear.rs +++ b/third_party/rust/wgpu-core/src/command/clear.rs @@ -4,13 +4,12 @@ use std::{num::NonZeroU32, ops::Range}; use crate::device::trace::Command as TraceCommand; use crate::{ command::CommandBuffer, - device::Device, get_lowest_common_denom, - hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token}, + hub::{self, Global, GlobalIdentityHandlerFactory, HalApi, Token}, id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid}, init_tracker::{MemoryInitKind, TextureInitRange}, resource::{Texture, TextureClearMode}, - track::{ResourceTracker, TextureSelector, TextureState}, + track::{TextureSelector, TextureTracker}, }; use hal::{auxil::align_to, CommandEncoder as _}; @@ -90,8 +89,8 @@ impl Global { let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, dst, (), hal::BufferUses::COPY_DST) - .map_err(ClearError::InvalidBuffer)?; + .set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST) + .ok_or(ClearError::InvalidBuffer(dst))?; let dst_raw = dst_buffer .raw .as_ref() @@ -139,7 +138,7 @@ impl Global { let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.transition_buffers(dst_barrier); + cmd_buf_raw.transition_buffers(dst_barrier.into_iter()); cmd_buf_raw.clear_buffer(dst_raw, offset..end); } Ok(()) @@ -191,13 +190,13 @@ impl Global { // Check if subresource level range is valid let subresource_level_end = match subresource_range.mip_level_count { Some(count) => subresource_range.base_mip_level + count.get(), - None => dst_texture.full_range.levels.end, + None => dst_texture.full_range.mips.end, }; - if dst_texture.full_range.levels.start > subresource_range.base_mip_level - || dst_texture.full_range.levels.end < subresource_level_end + if dst_texture.full_range.mips.start > subresource_range.base_mip_level + || dst_texture.full_range.mips.end < subresource_level_end { return Err(ClearError::InvalidTextureLevelRange { - texture_level_range: dst_texture.full_range.levels.clone(), + texture_level_range: dst_texture.full_range.mips.clone(), subresource_base_mip_level: subresource_range.base_mip_level, subresource_mip_level_count: subresource_range.mip_level_count, }); @@ -217,48 +216,34 @@ impl Global { }); } + let device = &device_guard[cmd_buf.device_id.value]; + clear_texture( + &*texture_guard, Valid(dst), - dst_texture, TextureInitRange { mip_range: subresource_range.base_mip_level..subresource_level_end, layer_range: subresource_range.base_array_layer..subresource_layer_end, }, cmd_buf.encoder.open(), &mut cmd_buf.trackers.textures, - &device_guard[cmd_buf.device_id.value], + &device.alignments, + &device.zero_buffer, ) } } -pub(crate) fn clear_texture( - dst_texture_id: Valid, - dst_texture: &Texture, - range: TextureInitRange, - encoder: &mut A::CommandEncoder, - texture_tracker: &mut ResourceTracker, - device: &Device, -) -> Result<(), ClearError> { - clear_texture_no_device( - dst_texture_id, - dst_texture, - range, - encoder, - texture_tracker, - &device.alignments, - &device.zero_buffer, - ) -} - -pub(crate) fn clear_texture_no_device( +pub(crate) fn clear_texture( + storage: &hub::Storage, TextureId>, dst_texture_id: Valid, - dst_texture: &Texture, range: TextureInitRange, encoder: &mut A::CommandEncoder, - texture_tracker: &mut ResourceTracker, + texture_tracker: &mut TextureTracker, alignments: &hal::Alignments, zero_buffer: &A::Buffer, ) -> Result<(), ClearError> { + let dst_texture = &storage[dst_texture_id]; + let dst_raw = dst_texture .inner .as_raw() @@ -277,7 +262,7 @@ pub(crate) fn clear_texture_no_device( }; let selector = TextureSelector { - levels: range.mip_range.clone(), + mips: range.mip_range.clone(), layers: range.layer_range.clone(), }; @@ -287,14 +272,13 @@ pub(crate) fn clear_texture_no_device( // On the other hand, when coming via command_encoder_clear_texture, the life_guard is still there since in order to call it a texture object is needed. // // We could in theory distinguish these two scenarios in the internal clear_texture api in order to remove this check and call the cheaper change_replace_tracked whenever possible. - let dst_barrier = if let Some(ref_count) = dst_texture.life_guard().ref_count.as_ref() { - texture_tracker.change_replace(dst_texture_id, ref_count, selector, clear_usage) - } else { - texture_tracker.change_replace_tracked(dst_texture_id, selector, clear_usage) - } - .map(|pending| pending.into_hal(dst_texture)); + let dst_barrier = texture_tracker + .set_single(storage, dst_texture_id.0, selector, clear_usage) + .unwrap() + .1 + .map(|pending| pending.into_hal(dst_texture)); unsafe { - encoder.transition_textures(dst_barrier); + encoder.transition_textures(dst_barrier.into_iter()); } // Record actual clearing diff --git a/third_party/rust/wgpu-core/src/command/compute.rs b/third_party/rust/wgpu-core/src/command/compute.rs index 7edb147617a85..d81c726ad294e 100644 --- a/third_party/rust/wgpu-core/src/command/compute.rs +++ b/third_party/rust/wgpu-core/src/command/compute.rs @@ -14,8 +14,9 @@ use crate::{ hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, init_tracker::MemoryInitKind, - resource::{Buffer, Texture}, - track::{StatefulTrackerSubset, TrackerSet, UsageConflict, UseExtendError}, + pipeline, + resource::{self, Buffer, Texture}, + track::{Tracker, UsageConflict, UsageScope}, validation::{check_buffer_usage, MissingBufferUsageError}, Label, }; @@ -228,15 +229,14 @@ where } } -#[derive(Debug)] -struct State { +struct State { binder: Binder, pipeline: Option, - trackers: StatefulTrackerSubset, + scope: UsageScope, debug_scope_depth: u32, } -impl State { +impl State { fn is_ready(&self) -> Result<(), DispatchError> { let bind_mask = self.binder.invalid_mask(); if bind_mask != 0 { @@ -253,32 +253,36 @@ impl State { Ok(()) } - fn flush_states( + fn flush_states( &mut self, raw_encoder: &mut A::CommandEncoder, - base_trackers: &mut TrackerSet, + base_trackers: &mut Tracker, bind_group_guard: &Storage, id::BindGroupId>, buffer_guard: &Storage, id::BufferId>, texture_guard: &Storage, id::TextureId>, ) -> Result<(), UsageConflict> { for id in self.binder.list_active() { - self.trackers.merge_extend(&bind_group_guard[id].used)?; - //Note: stateless trackers are not merged: the lifetime reference + unsafe { + self.scope + .merge_bind_group(texture_guard, &bind_group_guard[id].used)? + }; + // Note: stateless trackers are not merged: the lifetime reference // is held to the bind group itself. } - log::trace!("Encoding dispatch barriers"); + for id in self.binder.list_active() { + unsafe { + base_trackers.set_and_remove_from_usage_scope_sparse( + texture_guard, + &mut self.scope, + &bind_group_guard[id].used, + ) + } + } - CommandBuffer::insert_barriers( - raw_encoder, - base_trackers, - &self.trackers.buffers, - &self.trackers.textures, - buffer_guard, - texture_guard, - ); + log::trace!("Encoding dispatch barriers"); - self.trackers.clear(); + CommandBuffer::drain_barriers(raw_encoder, base_trackers, buffer_guard, texture_guard); Ok(()) } } @@ -338,7 +342,7 @@ impl Global { let mut state = State { binder: Binder::new(), pipeline: None, - trackers: StatefulTrackerSubset::new(A::VARIANT), + scope: UsageScope::new(&*buffer_guard, &*texture_guard), debug_scope_depth: 0, }; let mut temp_offsets = Vec::new(); @@ -346,6 +350,18 @@ impl Global { let mut string_offset = 0; let mut active_query = None; + cmd_buf.trackers.set_size( + Some(&*buffer_guard), + Some(&*texture_guard), + None, + None, + Some(&*bind_group_guard), + Some(&*pipeline_guard), + None, + None, + Some(&*query_set_guard), + ); + let hal_desc = hal::ComputePassDescriptor { label: base.label }; unsafe { raw.begin_compute_pass(&hal_desc); @@ -379,11 +395,11 @@ impl Global { ); dynamic_offset_count += num_dynamic_offsets as usize; - let bind_group = cmd_buf + let bind_group: &BindGroup = cmd_buf .trackers .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .map_err(|_| ComputePassErrorInner::InvalidBindGroup(bind_group_id)) + .add_single(&*bind_group_guard, bind_group_id) + .ok_or(ComputePassErrorInner::InvalidBindGroup(bind_group_id)) .map_pass_err(scope)?; bind_group .validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits) @@ -434,11 +450,11 @@ impl Global { state.pipeline = Some(pipeline_id); - let pipeline = cmd_buf + let pipeline: &pipeline::ComputePipeline = cmd_buf .trackers - .compute_pipes - .use_extend(&*pipeline_guard, pipeline_id, (), ()) - .map_err(|_| ComputePassErrorInner::InvalidPipeline(pipeline_id)) + .compute_pipelines + .add_single(&*pipeline_guard, pipeline_id) + .ok_or(ComputePassErrorInner::InvalidPipeline(pipeline_id)) .map_pass_err(scope)?; unsafe { @@ -587,11 +603,10 @@ impl Global { .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; - let indirect_buffer = state - .trackers + let indirect_buffer: &Buffer = state + .scope .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT) - .map_err(|_| ComputePassErrorInner::InvalidIndirectBuffer(buffer_id)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, wgt::BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -670,16 +685,11 @@ impl Global { } => { let scope = PassErrorScope::WriteTimestamp; - let query_set = cmd_buf + let query_set: &resource::QuerySet = 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!(), - }) + .add_single(&*query_set_guard, query_set_id) + .ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id)) .map_pass_err(scope)?; query_set @@ -692,16 +702,11 @@ impl Global { } => { let scope = PassErrorScope::BeginPipelineStatisticsQuery; - let query_set = cmd_buf + let query_set: &resource::QuerySet = 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!(), - }) + .add_single(&*query_set_guard, query_set_id) + .ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id)) .map_pass_err(scope)?; query_set diff --git a/third_party/rust/wgpu-core/src/command/draw.rs b/third_party/rust/wgpu-core/src/command/draw.rs index e8e53e3ade8f2..2206f3f204d74 100644 --- a/third_party/rust/wgpu-core/src/command/draw.rs +++ b/third_party/rust/wgpu-core/src/command/draw.rs @@ -5,7 +5,7 @@ use crate::{ binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError}, error::ErrorFormatter, id, - track::UseExtendError, + track::UsageConflict, validation::{MissingBufferUsageError, MissingTextureUsageError}, }; use wgt::{BufferAddress, BufferSize, Color}; @@ -13,8 +13,6 @@ use wgt::{BufferAddress, BufferSize, Color}; use std::num::NonZeroU32; use thiserror::Error; -pub type BufferError = UseExtendError; - /// Error validating a draw call. #[derive(Clone, Debug, Error, PartialEq)] pub enum DrawError { @@ -79,8 +77,8 @@ pub enum RenderCommandError { IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError), #[error("pipeline writes to depth/stencil, while the pass has read-only depth/stencil")] IncompatiblePipelineRods, - #[error("buffer {0:?} is in error {1:?}")] - Buffer(id::BufferId, BufferError), + #[error(transparent)] + UsageConflict(#[from] UsageConflict), #[error("buffer {0:?} is destroyed")] DestroyedBuffer(id::BufferId), #[error(transparent)] @@ -106,7 +104,11 @@ impl crate::error::PrettyError for RenderCommandError { Self::InvalidPipeline(id) => { fmt.render_pipeline_label(&id); } - Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => { + Self::UsageConflict(UsageConflict::TextureInvalid { id }) => { + fmt.texture_label(&id); + } + Self::UsageConflict(UsageConflict::BufferInvalid { id }) + | Self::DestroyedBuffer(id) => { fmt.buffer_label(&id); } _ => {} diff --git a/third_party/rust/wgpu-core/src/command/memory_init.rs b/third_party/rust/wgpu-core/src/command/memory_init.rs index ea0b303609d55..d974575f912e9 100644 --- a/third_party/rust/wgpu-core/src/command/memory_init.rs +++ b/third_party/rust/wgpu-core/src/command/memory_init.rs @@ -4,11 +4,11 @@ use hal::CommandEncoder; use crate::{ device::Device, - hub::Storage, + hub::{HalApi, Storage}, id::{self, TextureId}, init_tracker::*, resource::{Buffer, Texture}, - track::{ResourceTracker, TextureState, TrackerSet}, + track::{TextureTracker, Tracker}, FastHashMap, }; @@ -121,36 +121,37 @@ impl CommandBufferTextureMemoryActions { // Utility function that takes discarded surfaces from (several calls to) register_init_action and initializes them on the spot. // Takes care of barriers as well! pub(crate) fn fixup_discarded_surfaces< - A: hal::Api, + A: HalApi, InitIter: Iterator, >( inits: InitIter, encoder: &mut A::CommandEncoder, texture_guard: &Storage, TextureId>, - texture_tracker: &mut ResourceTracker, + texture_tracker: &mut TextureTracker, device: &Device, ) { for init in inits { clear_texture( + texture_guard, id::Valid(init.texture), - texture_guard.get(init.texture).unwrap(), TextureInitRange { mip_range: init.mip_level..(init.mip_level + 1), layer_range: init.layer..(init.layer + 1), }, encoder, texture_tracker, - device, + &device.alignments, + &device.zero_buffer, ) .unwrap(); } } -impl BakedCommands { +impl BakedCommands { // inserts all buffer initializations that are going to be needed for executing the commands and updates resource init states accordingly pub(crate) fn initialize_buffer_memory( &mut self, - device_tracker: &mut TrackerSet, + device_tracker: &mut Tracker, buffer_guard: &mut Storage, id::BufferId>, ) -> Result<(), DestroyedBufferError> { // Gather init ranges for each buffer so we can collapse them. @@ -202,11 +203,11 @@ impl BakedCommands { // 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 = device_tracker.buffers.change_replace_tracked( - id::Valid(buffer_id), - (), - hal::BufferUses::COPY_DST, - ); + let transition = device_tracker + .buffers + .set_single(buffer_guard, buffer_id, hal::BufferUses::COPY_DST) + .unwrap() + .1; let buffer = buffer_guard .get_mut(buffer_id) @@ -214,8 +215,11 @@ impl BakedCommands { let raw_buf = buffer.raw.as_ref().ok_or(DestroyedBufferError(buffer_id))?; unsafe { - self.encoder - .transition_buffers(transition.map(|pending| pending.into_hal(buffer))); + self.encoder.transition_buffers( + transition + .map(|pending| pending.into_hal(buffer)) + .into_iter(), + ); } for range in ranges.iter() { @@ -234,7 +238,7 @@ impl BakedCommands { // any textures that are left discarded by this command buffer will be marked as uninitialized pub(crate) fn initialize_texture_memory( &mut self, - device_tracker: &mut TrackerSet, + device_tracker: &mut Tracker, texture_guard: &mut Storage, TextureId>, device: &Device, ) -> Result<(), DestroyedTextureError> { @@ -274,12 +278,13 @@ impl BakedCommands { // TODO: Could we attempt some range collapsing here? for range in ranges.drain(..) { clear_texture( + texture_guard, id::Valid(texture_use.id), - &*texture, range, &mut self.encoder, &mut device_tracker.textures, - device, + &device.alignments, + &device.zero_buffer, ) .unwrap(); } diff --git a/third_party/rust/wgpu-core/src/command/mod.rs b/third_party/rust/wgpu-core/src/command/mod.rs index a4e7967307d7a..47ee9c0eccbf5 100644 --- a/third_party/rust/wgpu-core/src/command/mod.rs +++ b/third_party/rust/wgpu-core/src/command/mod.rs @@ -10,7 +10,7 @@ mod transfer; use std::slice; -pub(crate) use self::clear::clear_texture_no_device; +pub(crate) use self::clear::clear_texture; pub use self::{ bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*, }; @@ -19,11 +19,11 @@ use self::memory_init::CommandBufferTextureMemoryActions; use crate::error::{ErrorFormatter, PrettyError}; use crate::init_tracker::BufferInitTrackerAction; +use crate::track::{Tracker, UsageScope}; use crate::{ hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, resource::{Buffer, Texture}, - track::{BufferState, ResourceTracker, TextureState, TrackerSet}, Label, Stored, }; @@ -81,10 +81,10 @@ impl CommandEncoder { } } -pub struct BakedCommands { +pub struct BakedCommands { pub(crate) encoder: A::CommandEncoder, pub(crate) list: Vec, - pub(crate) trackers: TrackerSet, + pub(crate) trackers: Tracker, buffer_memory_init_actions: Vec, texture_memory_actions: CommandBufferTextureMemoryActions, } @@ -92,11 +92,11 @@ pub struct BakedCommands { pub(crate) struct DestroyedBufferError(pub id::BufferId); pub(crate) struct DestroyedTextureError(pub id::TextureId); -pub struct CommandBuffer { +pub struct CommandBuffer { encoder: CommandEncoder, status: CommandEncoderStatus, pub(crate) device_id: Stored, - pub(crate) trackers: TrackerSet, + pub(crate) trackers: Tracker, buffer_memory_init_actions: Vec, texture_memory_actions: CommandBufferTextureMemoryActions, limits: wgt::Limits, @@ -124,7 +124,7 @@ impl CommandBuffer { }, status: CommandEncoderStatus::Recording, device_id, - trackers: TrackerSet::new(A::VARIANT), + trackers: Tracker::new(), buffer_memory_init_actions: Default::default(), texture_memory_actions: Default::default(), limits, @@ -138,23 +138,52 @@ impl CommandBuffer { } } - pub(crate) fn insert_barriers( + pub(crate) fn insert_barriers_from_tracker( raw: &mut A::CommandEncoder, - base: &mut TrackerSet, - head_buffers: &ResourceTracker, - head_textures: &ResourceTracker, + base: &mut Tracker, + head: &Tracker, buffer_guard: &Storage, id::BufferId>, texture_guard: &Storage, id::TextureId>, ) { profiling::scope!("insert_barriers"); - debug_assert_eq!(A::VARIANT, base.backend()); - let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| { - let buf = &buffer_guard[pending.id]; + base.buffers.set_from_tracker(&head.buffers); + base.textures + .set_from_tracker(&*texture_guard, &head.textures); + + Self::drain_barriers(raw, base, buffer_guard, texture_guard); + } + + pub(crate) fn insert_barriers_from_scope( + raw: &mut A::CommandEncoder, + base: &mut Tracker, + head: &UsageScope, + buffer_guard: &Storage, id::BufferId>, + texture_guard: &Storage, id::TextureId>, + ) { + profiling::scope!("insert_barriers"); + + base.buffers.set_from_usage_scope(&head.buffers); + base.textures + .set_from_usage_scope(&*texture_guard, &head.textures); + + Self::drain_barriers(raw, base, buffer_guard, texture_guard); + } + + pub(crate) fn drain_barriers( + raw: &mut A::CommandEncoder, + base: &mut Tracker, + buffer_guard: &Storage, id::BufferId>, + texture_guard: &Storage, id::TextureId>, + ) { + profiling::scope!("drain_barriers"); + + let buffer_barriers = base.buffers.drain().map(|pending| { + let buf = unsafe { &buffer_guard.get_unchecked(pending.id) }; pending.into_hal(buf) }); - let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| { - let tex = &texture_guard[pending.id]; + let texture_barriers = base.textures.drain().map(|pending| { + let tex = unsafe { texture_guard.get_unchecked(pending.id) }; pending.into_hal(tex) }); @@ -165,7 +194,7 @@ impl CommandBuffer { } } -impl CommandBuffer { +impl CommandBuffer { fn get_encoder_mut( storage: &mut Storage, id: id::CommandEncoderId, @@ -198,7 +227,7 @@ impl CommandBuffer { } } -impl crate::hub::Resource for CommandBuffer { +impl crate::hub::Resource for CommandBuffer { const TYPE: &'static str = "CommandBuffer"; fn life_guard(&self) -> &crate::LifeGuard { @@ -219,6 +248,17 @@ pub struct BasePassRef<'a, C> { pub push_constant_data: &'a [u32], } +/// A stream of commands for a render pass or compute pass. +/// +/// This also contains side tables referred to by certain commands, +/// like dynamic offsets for [`SetBindGroup`] or string data for +/// [`InsertDebugMarker`]. +/// +/// Render passes use `BasePass`, whereas compute +/// passes use `BasePass`. +/// +/// [`SetBindGroup`]: RenderCommand::SetBindGroup +/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker #[doc(hidden)] #[derive(Debug)] #[cfg_attr( @@ -231,9 +271,22 @@ pub struct BasePassRef<'a, C> { )] pub struct BasePass { pub label: Option, + + /// The stream of commands. pub commands: Vec, + + /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`. + /// + /// Each successive `SetBindGroup` consumes the next + /// [`num_dynamic_offsets`] values from this list. pub dynamic_offsets: Vec, + + /// Strings used by debug instructions. + /// + /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`] + /// instruction consumes the next `len` bytes from this vector. pub string_data: Vec, + pub push_constant_data: Vec, } @@ -297,7 +350,7 @@ impl Global { cmd_buf.status = CommandEncoderStatus::Finished; //Note: if we want to stop tracking the swapchain texture view, // this is the place to do it. - log::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers); + log::trace!("Command buffer {:?}", encoder_id); None } CommandEncoderStatus::Finished => Some(CommandEncoderError::NotRecording), diff --git a/third_party/rust/wgpu-core/src/command/query.rs b/third_party/rust/wgpu-core/src/command/query.rs index 42a0dd905b724..873a3031ac644 100644 --- a/third_party/rust/wgpu-core/src/command/query.rs +++ b/third_party/rust/wgpu-core/src/command/query.rs @@ -8,7 +8,6 @@ use crate::{ id::{self, Id, TypedId}, init_tracker::MemoryInitKind, resource::QuerySet, - track::UseExtendError, Epoch, FastHashMap, Index, }; use std::{iter, marker::PhantomData}; @@ -300,11 +299,8 @@ impl Global { 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!(), - })?; + .add_single(&*query_set_guard, query_set_id) + .ok_or(QueryError::InvalidQuerySet(query_set_id))?; query_set.validate_and_write_timestamp(raw_encoder, query_set_id, query_index, None)?; @@ -348,17 +344,14 @@ impl Global { 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!(), - })?; + .add_single(&*query_set_guard, query_set_id) + .ok_or(QueryError::InvalidQuerySet(query_set_id))?; let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST) - .map_err(QueryError::InvalidBuffer)?; + .set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST) + .ok_or(QueryError::InvalidBuffer(destination))?; let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); if !dst_buffer.usage.contains(wgt::BufferUsages::COPY_DST) { @@ -407,7 +400,7 @@ impl Global { )); unsafe { - raw_encoder.transition_buffers(dst_barrier); + raw_encoder.transition_buffers(dst_barrier.into_iter()); raw_encoder.copy_query_results( &query_set.raw, start_query..end_query, diff --git a/third_party/rust/wgpu-core/src/command/render.rs b/third_party/rust/wgpu-core/src/command/render.rs index bbbffa5a13e8f..7ef45aa0a6474 100644 --- a/third_party/rust/wgpu-core/src/command/render.rs +++ b/third_party/rust/wgpu-core/src/command/render.rs @@ -1,6 +1,7 @@ use crate::{ binding_model::BindError, command::{ + self, bind::Binder, end_pipeline_statistics_query, memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState}, @@ -17,8 +18,8 @@ use crate::{ id, init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction}, pipeline::PipelineFlags, - resource::{Texture, TextureView}, - track::{StatefulTrackerSubset, TextureSelector, UsageConflict}, + resource::{self, Buffer, Texture, TextureView}, + track::{TextureSelector, UsageConflict, UsageScope}, validation::{ check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError, }, @@ -38,7 +39,6 @@ use serde::Deserialize; #[cfg(any(feature = "serial-pass", feature = "trace"))] use serde::Serialize; -use crate::track::UseExtendError; use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str}; use super::{memory_init::TextureSurfaceDiscard, CommandBufferTextureMemoryActions}; @@ -561,9 +561,9 @@ impl TextureView { const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1; type AttachmentDataVec = ArrayVec; -struct RenderPassInfo<'a, A: hal::Api> { +struct RenderPassInfo<'a, A: HalApi> { context: RenderPassContext, - trackers: StatefulTrackerSubset, + usage_scope: UsageScope, render_attachments: AttachmentDataVec>, // All render attachments, including depth/stencil is_ds_read_only: bool, extent: wgt::Extent3d, @@ -605,7 +605,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { // but recording the discard right away be alright since the texture can't be used during the pass anyways texture_memory_actions.discard(TextureSurfaceDiscard { texture: view.parent_id.value.0, - mip_level: view.selector.levels.start, + mip_level: view.selector.mips.start, layer: view.selector.layers.start, }); } @@ -618,6 +618,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>, cmd_buf: &mut CommandBuffer, view_guard: &'a Storage, id::TextureViewId>, + buffer_guard: &'a Storage, id::BufferId>, texture_guard: &'a Storage, id::TextureId>, ) -> Result { profiling::scope!("start", "RenderPassInfo"); @@ -699,8 +700,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { let view = cmd_buf .trackers .views - .use_extend(&*view_guard, at.view, (), ()) - .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; + .add_single(&*view_guard, at.view) + .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?; check_multiview(view)?; add_view(view, "depth")?; @@ -779,7 +780,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { // Both are discarded using the regular path. discarded_surfaces.push(TextureSurfaceDiscard { texture: view.parent_id.value.0, - mip_level: view.selector.levels.start, + mip_level: view.selector.mips.start, layer: view.selector.layers.start, }); } @@ -808,8 +809,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { let color_view = cmd_buf .trackers .views - .use_extend(&*view_guard, at.view, (), ()) - .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; + .add_single(&*view_guard, at.view) + .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?; check_multiview(color_view)?; add_view(color_view, "color")?; @@ -838,8 +839,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { let resolve_view = cmd_buf .trackers .views - .use_extend(&*view_guard, resolve_target, (), ()) - .map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?; + .add_single(&*view_guard, resolve_target) + .ok_or(RenderPassErrorInner::InvalidAttachment(resolve_target))?; check_multiview(resolve_view)?; if color_view.extent != resolve_view.extent { @@ -934,7 +935,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { Ok(Self { context, - trackers: StatefulTrackerSubset::new(A::VARIANT), + usage_scope: UsageScope::new(buffer_guard, texture_guard), render_attachments, is_ds_read_only, extent, @@ -949,7 +950,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { mut self, raw: &mut A::CommandEncoder, texture_guard: &Storage, id::TextureId>, - ) -> Result<(StatefulTrackerSubset, SurfacesInDiscardState), RenderPassErrorInner> { + ) -> Result<(UsageScope, SurfacesInDiscardState), RenderPassErrorInner> { profiling::scope!("finish", "RenderPassInfo"); unsafe { raw.end_render_pass(); @@ -963,15 +964,18 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { check_texture_usage(texture.desc.usage, TextureUsages::RENDER_ATTACHMENT)?; // the tracker set of the pass is always in "extend" mode - self.trackers - .textures - .change_extend( - ra.texture_id.value, - &ra.texture_id.ref_count, - ra.selector.clone(), - ra.usage, - ) - .map_err(UsageConflict::from)?; + unsafe { + self.usage_scope + .textures + .merge_single( + &*texture_guard, + ra.texture_id.value, + Some(ra.selector.clone()), + &ra.texture_id.ref_count, + ra.usage, + ) + .map_err(UsageConflict::from)? + }; } // If either only stencil or depth was discarded, we put in a special clear pass to keep the init status of the aspects in sync. @@ -1012,7 +1016,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> { } } - Ok((self.trackers, self.pending_discard_init_fixups)) + Ok((self.usage_scope, self.pending_discard_init_fixups)) } } @@ -1047,7 +1051,7 @@ impl Global { let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); - let (trackers, query_reset_state, pending_discard_init_fixups) = { + let (scope, query_reset_state, pending_discard_init_fixups) = { let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); // Spell out the type, to placate rust-analyzer. @@ -1075,7 +1079,7 @@ impl Global { let (bundle_guard, 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.render_pipelines.read(&mut token); + let (render_pipeline_guard, mut token) = hub.render_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, mut token) = hub.textures.read(&mut token); @@ -1093,10 +1097,23 @@ impl Global { depth_stencil_attachment, cmd_buf, &*view_guard, + &*buffer_guard, &*texture_guard, ) .map_pass_err(init_scope)?; + cmd_buf.trackers.set_size( + Some(&*buffer_guard), + Some(&*texture_guard), + Some(&*view_guard), + None, + Some(&*bind_group_guard), + None, + Some(&*render_pipeline_guard), + Some(&*bundle_guard), + Some(&*query_set_guard), + ); + let raw = &mut cmd_buf.encoder.raw; let mut state = State { @@ -1142,17 +1159,19 @@ impl Global { let bind_group = cmd_buf .trackers .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id)) + .add_single(&*bind_group_guard, bind_group_id) + .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id)) .map_pass_err(scope)?; bind_group .validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits) .map_pass_err(scope)?; // merge the resource tracker in - info.trackers - .merge_extend(&bind_group.used) - .map_pass_err(scope)?; + unsafe { + info.usage_scope + .merge_bind_group(&*texture_guard, &bind_group.used) + .map_pass_err(scope)?; + } //Note: stateless trackers are not merged: the lifetime reference // is held to the bind group itself. @@ -1203,9 +1222,9 @@ impl Global { let pipeline = cmd_buf .trackers - .render_pipes - .use_extend(&*pipeline_guard, pipeline_id, (), ()) - .map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id)) + .render_pipelines + .add_single(&*render_pipeline_guard, pipeline_id) + .ok_or(RenderCommandError::InvalidPipeline(pipeline_id)) .map_pass_err(scope)?; info.context @@ -1312,11 +1331,10 @@ impl Global { size, } => { let scope = PassErrorScope::SetIndexBuffer(buffer_id); - let buffer = info - .trackers + let buffer: &Buffer = info + .usage_scope .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX) - .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, BufferUsages::INDEX) .map_pass_err(scope)?; @@ -1359,11 +1377,10 @@ impl Global { size, } => { let scope = PassErrorScope::SetVertexBuffer(buffer_id); - let buffer = info - .trackers + let buffer: &Buffer = info + .usage_scope .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX) - .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, BufferUsages::VERTEX) .map_pass_err(scope)?; @@ -1617,11 +1634,10 @@ impl Global { .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; - let indirect_buffer = info - .trackers + let indirect_buffer: &Buffer = info + .usage_scope .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT) - .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -1688,11 +1704,10 @@ impl Global { .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; - let indirect_buffer = info - .trackers + let indirect_buffer: &Buffer = info + .usage_scope .buffers - .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT) - .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) + .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -1702,16 +1717,14 @@ impl Global { .ok_or(RenderCommandError::DestroyedBuffer(buffer_id)) .map_pass_err(scope)?; - let count_buffer = info - .trackers + let count_buffer: &Buffer = info + .usage_scope .buffers - .use_extend( + .merge_single( &*buffer_guard, count_buffer_id, - (), hal::BufferUses::INDIRECT, ) - .map_err(|e| RenderCommandError::Buffer(count_buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(count_buffer.usage, BufferUsages::INDIRECT) .map_pass_err(scope)?; @@ -1814,16 +1827,11 @@ impl Global { } => { let scope = PassErrorScope::WriteTimestamp; - let query_set = cmd_buf + let query_set: &resource::QuerySet = cmd_buf .trackers .query_sets - .use_extend(&*query_set_guard, query_set_id, (), ()) - .map_err(|e| match e { - UseExtendError::InvalidResource => { - RenderCommandError::InvalidQuerySet(query_set_id) - } - _ => unreachable!(), - }) + .add_single(&*query_set_guard, query_set_id) + .ok_or(RenderCommandError::InvalidQuerySet(query_set_id)) .map_pass_err(scope)?; query_set @@ -1841,16 +1849,11 @@ impl Global { } => { let scope = PassErrorScope::BeginPipelineStatisticsQuery; - let query_set = cmd_buf + let query_set: &resource::QuerySet = cmd_buf .trackers .query_sets - .use_extend(&*query_set_guard, query_set_id, (), ()) - .map_err(|e| match e { - UseExtendError::InvalidResource => { - RenderCommandError::InvalidQuerySet(query_set_id) - } - _ => unreachable!(), - }) + .add_single(&*query_set_guard, query_set_id) + .ok_or(RenderCommandError::InvalidQuerySet(query_set_id)) .map_pass_err(scope)?; query_set @@ -1871,11 +1874,11 @@ impl Global { } RenderCommand::ExecuteBundle(bundle_id) => { let scope = PassErrorScope::ExecuteBundle; - let bundle = cmd_buf + let bundle: &command::RenderBundle = cmd_buf .trackers .bundles - .use_extend(&*bundle_guard, bundle_id, (), ()) - .map_err(|_| RenderCommandError::InvalidRenderBundle(bundle_id)) + .add_single(&*bundle_guard, bundle_id) + .ok_or(RenderCommandError::InvalidRenderBundle(bundle_id)) .map_pass_err(scope)?; info.context @@ -1913,7 +1916,7 @@ impl Global { raw, &*pipeline_layout_guard, &*bind_group_guard, - &*pipeline_guard, + &*render_pipeline_guard, &*buffer_guard, ) } @@ -1927,23 +1930,21 @@ impl Global { }) .map_pass_err(scope)?; - info.trackers - .merge_extend(&bundle.used) - .map_pass_err(scope)?; - // Start tracking the bind groups specifically, as they are the only - // compound resources, to make it easier to update submission indices - // later at submission time. - cmd_buf - .trackers - .bind_groups - .merge_extend(&bundle.used.bind_groups) - .unwrap(); + unsafe { + info.usage_scope + .merge_render_bundle(&*texture_guard, &bundle.used) + .map_pass_err(scope)?; + cmd_buf + .trackers + .add_from_render_bundle(&bundle.used) + .map_pass_err(scope)?; + }; state.reset_bundle(); } } } - log::trace!("Merging {:?} with the render pass", encoder_id); + log::trace!("Merging renderpass into cmd_buf {:?}", encoder_id); let (trackers, pending_discard_init_fixups) = info.finish(raw, &*texture_guard).map_pass_err(init_scope)?; @@ -1977,11 +1978,10 @@ impl Global { .map_err(RenderCommandError::InvalidQuerySet) .map_pass_err(PassErrorScope::QueryReset)?; - super::CommandBuffer::insert_barriers( + super::CommandBuffer::insert_barriers_from_scope( transit, &mut cmd_buf.trackers, - &trackers.buffers, - &trackers.textures, + &scope, &*buffer_guard, &*texture_guard, ); diff --git a/third_party/rust/wgpu-core/src/command/transfer.rs b/third_party/rust/wgpu-core/src/command/transfer.rs index 1033feecee0ce..2a0f9a95588a6 100644 --- a/third_party/rust/wgpu-core/src/command/transfer.rs +++ b/third_party/rust/wgpu-core/src/command/transfer.rs @@ -1,12 +1,12 @@ #[cfg(feature = "trace")] use crate::device::trace::Command as TraceCommand; use crate::{ - command::{CommandBuffer, CommandEncoderError}, + command::{clear_texture, CommandBuffer, CommandEncoderError}, conv, device::{Device, MissingDownlevelFlags}, error::{ErrorFormatter, PrettyError}, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, - id::{BufferId, CommandEncoderId, Id, TextureId, Valid}, + id::{BufferId, CommandEncoderId, TextureId, Valid}, init_tracker::{ has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange, TextureInitTrackerAction, @@ -15,14 +15,13 @@ use crate::{ track::TextureSelector, }; +use arrayvec::ArrayVec; use hal::CommandEncoder as _; use thiserror::Error; use wgt::{BufferAddress, BufferUsages, Extent3d, TextureUsages}; use std::iter; -use super::clear::clear_texture; - pub type ImageCopyBuffer = wgt::ImageCopyBuffer; pub type ImageCopyTexture = wgt::ImageCopyTexture; @@ -191,7 +190,7 @@ pub(crate) fn extract_texture_selector( aspect: copy_aspect, }; let selector = TextureSelector { - levels: copy_texture.mip_level..copy_texture.mip_level + 1, + mips: copy_texture.mip_level..copy_texture.mip_level + 1, layers, }; @@ -312,8 +311,10 @@ pub(crate) fn validate_texture_copy_range( match desc.format { wgt::TextureFormat::Depth32Float + | wgt::TextureFormat::Depth32FloatStencil8 | wgt::TextureFormat::Depth24Plus - | wgt::TextureFormat::Depth24PlusStencil8 => { + | wgt::TextureFormat::Depth24PlusStencil8 + | wgt::TextureFormat::Depth24UnormStencil8 => { if *copy_size != extent { return Err(TransferError::InvalidDepthTextureExtent); } @@ -380,14 +381,13 @@ pub(crate) fn validate_texture_copy_range( Ok((copy_extent, array_layer_count)) } -fn handle_texture_init( +fn handle_texture_init( init_kind: MemoryInitKind, cmd_buf: &mut CommandBuffer, device: &Device, copy_texture: &ImageCopyTexture, copy_size: &Extent3d, - texture_guard: &Storage, Id>>, - texture: &Texture, + texture_guard: &Storage, TextureId>, ) { let init_action = TextureInitTrackerAction { id: copy_texture.texture, @@ -409,15 +409,16 @@ fn handle_texture_init( let cmd_buf_raw = cmd_buf.encoder.open(); for init in immediate_inits { clear_texture( + texture_guard, Valid(init.texture), - texture, TextureInitRange { mip_range: init.mip_level..(init.mip_level + 1), layer_range: init.layer..(init.layer + 1), }, cmd_buf_raw, &mut cmd_buf.trackers.textures, - device, + &device.alignments, + &device.zero_buffer, ) .unwrap(); } @@ -425,14 +426,14 @@ fn handle_texture_init( } // Ensures the source texture of a transfer is in the right initialization state and records the state for after the transfer operation. -fn handle_src_texture_init( +fn handle_src_texture_init( cmd_buf: &mut CommandBuffer, device: &Device, source: &ImageCopyTexture, copy_size: &Extent3d, texture_guard: &Storage, TextureId>, ) -> Result<(), TransferError> { - let texture = texture_guard + let _ = texture_guard .get(source.texture) .map_err(|_| TransferError::InvalidTexture(source.texture))?; @@ -443,13 +444,12 @@ fn handle_src_texture_init( source, copy_size, texture_guard, - texture, ); Ok(()) } // Ensures the destination texture of a transfer is in the right initialization state and records the state for after the transfer operation. -fn handle_dst_texture_init( +fn handle_dst_texture_init( cmd_buf: &mut CommandBuffer, device: &Device, destination: &ImageCopyTexture, @@ -479,7 +479,6 @@ fn handle_dst_texture_init( destination, copy_size, texture_guard, - texture, ); Ok(()) } @@ -520,8 +519,8 @@ impl Global { let (src_buffer, src_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, source, (), hal::BufferUses::COPY_SRC) - .map_err(TransferError::InvalidBuffer)?; + .set_single(&*buffer_guard, source, hal::BufferUses::COPY_SRC) + .ok_or(TransferError::InvalidBuffer(source))?; let src_raw = src_buffer .raw .as_ref() @@ -530,15 +529,13 @@ impl Global { return Err(TransferError::MissingCopySrcUsageFlag.into()); } // expecting only a single barrier - let src_barrier = src_pending - .map(|pending| pending.into_hal(src_buffer)) - .next(); + let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer)); let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST) - .map_err(TransferError::InvalidBuffer)?; + .set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST) + .ok_or(TransferError::InvalidBuffer(destination))?; let dst_raw = dst_buffer .raw .as_ref() @@ -546,9 +543,7 @@ impl Global { if !dst_buffer.usage.contains(BufferUsages::COPY_DST) { return Err(TransferError::MissingCopyDstUsageFlag(Some(destination), None).into()); } - let dst_barrier = dst_pending - .map(|pending| pending.into_hal(dst_buffer)) - .next(); + let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); if size % wgt::COPY_BUFFER_ALIGNMENT != 0 { return Err(TransferError::UnalignedCopySize(size).into()); @@ -658,8 +653,8 @@ impl Global { let (src_buffer, src_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, source.buffer, (), hal::BufferUses::COPY_SRC) - .map_err(TransferError::InvalidBuffer)?; + .set_single(&*buffer_guard, source.buffer, hal::BufferUses::COPY_SRC) + .ok_or(TransferError::InvalidBuffer(source.buffer))?; let src_raw = src_buffer .raw .as_ref() @@ -667,18 +662,18 @@ impl Global { if !src_buffer.usage.contains(BufferUsages::COPY_SRC) { return Err(TransferError::MissingCopySrcUsageFlag.into()); } - let src_barriers = src_pending.map(|pending| pending.into_hal(src_buffer)); + let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer)); let (dst_texture, dst_pending) = cmd_buf .trackers .textures - .use_replace( + .set_single( &*texture_guard, destination.texture, dst_range, hal::TextureUses::COPY_DST, ) - .unwrap(); + .ok_or(TransferError::InvalidTexture(destination.texture))?; let dst_raw = dst_texture .inner .as_raw() @@ -688,7 +683,7 @@ impl Global { TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(), ); } - let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture)); + let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture)); let format_desc = dst_texture.desc.format.describe(); let (hal_copy_size, array_layer_count) = validate_texture_copy_range( @@ -735,8 +730,8 @@ impl Global { let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.transition_textures(dst_barriers); - cmd_buf_raw.transition_buffers(src_barriers); + cmd_buf_raw.transition_textures(dst_barrier.into_iter()); + cmd_buf_raw.transition_buffers(src_barrier.into_iter()); cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, regions); } Ok(()) @@ -785,13 +780,13 @@ impl Global { let (src_texture, src_pending) = cmd_buf .trackers .textures - .use_replace( + .set_single( &*texture_guard, source.texture, src_range, hal::TextureUses::COPY_SRC, ) - .unwrap(); + .ok_or(TransferError::InvalidTexture(source.texture))?; let src_raw = src_texture .inner .as_raw() @@ -799,18 +794,17 @@ impl Global { if !src_texture.desc.usage.contains(TextureUsages::COPY_SRC) { return Err(TransferError::MissingCopySrcUsageFlag.into()); } - let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture)); + let src_barrier = src_pending.map(|pending| pending.into_hal(src_texture)); let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace( + .set_single( &*buffer_guard, destination.buffer, - (), hal::BufferUses::COPY_DST, ) - .map_err(TransferError::InvalidBuffer)?; + .ok_or(TransferError::InvalidBuffer(destination.buffer))?; let dst_raw = dst_buffer .raw .as_ref() @@ -820,7 +814,7 @@ impl Global { TransferError::MissingCopyDstUsageFlag(Some(destination.buffer), None).into(), ); } - let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_buffer)); + let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); let format_desc = src_texture.desc.format.describe(); let (hal_copy_size, array_layer_count) = @@ -875,8 +869,8 @@ impl Global { }); let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.transition_buffers(dst_barriers); - cmd_buf_raw.transition_textures(src_barriers); + cmd_buf_raw.transition_buffers(dst_barrier.into_iter()); + cmd_buf_raw.transition_textures(src_barrier.into_iter()); cmd_buf_raw.copy_texture_to_buffer( src_raw, hal::TextureUses::COPY_SRC, @@ -936,13 +930,13 @@ impl Global { let (src_texture, src_pending) = cmd_buf .trackers .textures - .use_replace( + .set_single( &*texture_guard, source.texture, src_range, hal::TextureUses::COPY_SRC, ) - .unwrap(); + .ok_or(TransferError::InvalidTexture(source.texture))?; let src_raw = src_texture .inner .as_raw() @@ -953,20 +947,21 @@ impl Global { //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 + let mut barriers: ArrayVec<_, 2> = src_pending .map(|pending| pending.into_hal(src_texture)) - .collect::>(); + .into_iter() + .collect(); let (dst_texture, dst_pending) = cmd_buf .trackers .textures - .use_replace( + .set_single( &*texture_guard, destination.texture, dst_range, hal::TextureUses::COPY_DST, ) - .unwrap(); + .ok_or(TransferError::InvalidTexture(destination.texture))?; let dst_raw = dst_texture .inner .as_raw() diff --git a/third_party/rust/wgpu-core/src/conv.rs b/third_party/rust/wgpu-core/src/conv.rs index b6632b590395e..9ab3cbbecb232 100644 --- a/third_party/rust/wgpu-core/src/conv.rs +++ b/third_party/rust/wgpu-core/src/conv.rs @@ -1,6 +1,10 @@ use crate::resource; -pub fn is_power_of_two(val: u32) -> bool { +pub fn is_power_of_two_u16(val: u16) -> bool { + val != 0 && (val & (val - 1)) == 0 +} + +pub fn is_power_of_two_u32(val: u32) -> bool { val != 0 && (val & (val - 1)) == 0 } @@ -15,7 +19,11 @@ pub fn is_valid_copy_src_texture_format(format: wgt::TextureFormat) -> bool { pub fn is_valid_copy_dst_texture_format(format: wgt::TextureFormat) -> bool { use wgt::TextureFormat as Tf; match format { - Tf::Depth32Float | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => false, + Tf::Depth32Float + | Tf::Depth32FloatStencil8 + | Tf::Depth24Plus + | Tf::Depth24PlusStencil8 + | Tf::Depth24UnormStencil8 => false, _ => true, } } @@ -51,7 +59,7 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses { usage.contains(wgt::BufferUsages::UNIFORM), ); u.set( - hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE, + hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_READ_WRITE, usage.contains(wgt::BufferUsages::STORAGE), ); u.set( @@ -79,7 +87,7 @@ pub fn map_texture_usage( usage.contains(wgt::TextureUsages::TEXTURE_BINDING), ); u.set( - hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_WRITE, + hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_READ_WRITE, usage.contains(wgt::TextureUsages::STORAGE_BINDING), ); let is_color = aspect.contains(hal::FormatAspects::COLOR); @@ -146,7 +154,7 @@ pub fn check_texture_dimension_size( return Err(Tde::LimitExceeded { dim, given, limit }); } } - if sample_size == 0 || sample_size > sample_limit || !is_power_of_two(sample_size) { + if sample_size == 0 || sample_size > sample_limit || !is_power_of_two_u32(sample_size) { return Err(Tde::InvalidSampleCount(sample_size)); } diff --git a/third_party/rust/wgpu-core/src/device/life.rs b/third_party/rust/wgpu-core/src/device/life.rs index 9d0c8d96e36d9..5062f9189fc6f 100644 --- a/third_party/rust/wgpu-core/src/device/life.rs +++ b/third_party/rust/wgpu-core/src/device/life.rs @@ -7,7 +7,7 @@ use crate::{ }, hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token}, id, resource, - track::TrackerSet, + track::{BindGroupStates, RenderBundleScope, Tracker}, RefCount, Stored, SubmissionIndex, }; use smallvec::SmallVec; @@ -68,17 +68,21 @@ impl SuspectedResources { self.query_sets.extend_from_slice(&other.query_sets); } - pub(super) fn add_trackers(&mut self, trackers: &TrackerSet) { + pub(super) fn add_render_bundle_scope(&mut self, trackers: &RenderBundleScope) { self.buffers.extend(trackers.buffers.used()); self.textures.extend(trackers.textures.used()); - self.texture_views.extend(trackers.views.used()); - self.samplers.extend(trackers.samplers.used()); self.bind_groups.extend(trackers.bind_groups.used()); - self.compute_pipelines.extend(trackers.compute_pipes.used()); - self.render_pipelines.extend(trackers.render_pipes.used()); - self.render_bundles.extend(trackers.bundles.used()); + self.render_pipelines + .extend(trackers.render_pipelines.used()); self.query_sets.extend(trackers.query_sets.used()); } + + pub(super) fn add_bind_group_states(&mut self, trackers: &BindGroupStates) { + self.buffers.extend(trackers.buffers.used()); + self.textures.extend(trackers.textures.used()); + self.texture_views.extend(trackers.views.used()); + self.samplers.extend(trackers.samplers.used()); + } } /// Raw backend resources that should be freed shortly. @@ -273,7 +277,8 @@ pub(super) struct LifetimeTracker { /// Textures can be used in the upcoming submission by `write_texture`. pub future_suspected_textures: Vec>, - /// Resources that are suspected for destruction. + /// Resources whose user handle has died (i.e. drop/destroy has been called) + /// and will likely be ready for destruction soon. pub suspected_resources: SuspectedResources, /// Resources used by queue submissions still in flight. One entry per @@ -435,15 +440,18 @@ impl LifetimeTracker { } } - pub fn add_work_done_closure(&mut self, closure: SubmittedWorkDoneClosure) -> bool { + pub fn add_work_done_closure( + &mut self, + closure: SubmittedWorkDoneClosure, + ) -> Option { match self.active.last_mut() { Some(active) => { active.work_done_closures.push(closure); - true + None } // Note: we can't immediately invoke the closure, since it assumes // nothing is currently locked in the hubs. - None => false, + None => Some(closure), } } } @@ -491,7 +499,7 @@ impl LifetimeTracker { pub(super) fn triage_suspected( &mut self, hub: &Hub, - trackers: &Mutex, + trackers: &Mutex>, #[cfg(feature = "trace")] trace: Option<&Mutex>, token: &mut Token>, ) { @@ -510,7 +518,7 @@ impl LifetimeTracker { } if let Some(res) = hub.render_bundles.unregister_locked(id.0, &mut *guard) { - self.suspected_resources.add_trackers(&res.used); + self.suspected_resources.add_render_bundle_scope(&res.used); } } } @@ -529,7 +537,7 @@ impl LifetimeTracker { } if let Some(res) = hub.bind_groups.unregister_locked(id.0, &mut *guard) { - self.suspected_resources.add_trackers(&res.used); + self.suspected_resources.add_bind_group_states(&res.used); self.suspected_resources .bind_group_layouts @@ -670,7 +678,7 @@ impl LifetimeTracker { let mut trackers = trackers.lock(); for id in self.suspected_resources.compute_pipelines.drain(..) { - if trackers.compute_pipes.remove_abandoned(id) { + if trackers.compute_pipelines.remove_abandoned(id) { log::debug!("Compute pipeline {:?} will be destroyed", id); #[cfg(feature = "trace")] if let Some(t) = trace { @@ -695,7 +703,7 @@ impl LifetimeTracker { let mut trackers = trackers.lock(); for id in self.suspected_resources.render_pipelines.drain(..) { - if trackers.render_pipes.remove_abandoned(id) { + if trackers.render_pipelines.remove_abandoned(id) { log::debug!("Render pipeline {:?} will be destroyed", id); #[cfg(feature = "trace")] if let Some(t) = trace { @@ -829,7 +837,7 @@ impl LifetimeTracker { &mut self, hub: &Hub, raw: &A::Device, - trackers: &Mutex, + trackers: &Mutex>, token: &mut Token>, ) -> Vec { if self.ready_to_map.is_empty() { diff --git a/third_party/rust/wgpu-core/src/device/mod.rs b/third_party/rust/wgpu-core/src/device/mod.rs index c6d1583ce1eb1..0832bedd69639 100644 --- a/third_party/rust/wgpu-core/src/device/mod.rs +++ b/third_party/rust/wgpu-core/src/device/mod.rs @@ -8,7 +8,7 @@ use crate::{ TextureInitTracker, TextureInitTrackerAction, }, instance, pipeline, present, resource, - track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict}, + track::{BindGroupStates, TextureSelector, Tracker}, validation::{self, check_buffer_usage, check_texture_usage}, FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, RefCount, Stored, SubmissionIndex, DOWNLEVEL_ERROR_MESSAGE, @@ -22,7 +22,7 @@ use smallvec::SmallVec; use thiserror::Error; use wgt::{BufferAddress, TextureFormat, TextureViewDimension}; -use std::{borrow::Cow, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, ptr}; +use std::{borrow::Cow, iter, mem, num::NonZeroU32, ops::Range, ptr}; mod life; pub mod queue; @@ -141,14 +141,14 @@ impl UserClosures { self.submissions.extend(other.submissions); } - unsafe fn fire(self) { - //Note: this logic is specifically moved out of `handle_mapping()` in order to + fn fire(self) { + // Note: this logic is specifically moved out of `handle_mapping()` in order to // have nothing locked by the time we execute users callback code. for (operation, status) in self.mappings { - (operation.callback)(status, operation.user_data); + operation.callback.call(status); } for closure in self.submissions { - (closure.callback)(closure.user_data); + closure.call(); } } } @@ -256,7 +256,7 @@ impl CommandAllocator { /// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system /// 1. `self.trackers` is locked last (unenforced) /// 1. `self.trace` is locked last (unenforced) -pub struct Device { +pub struct Device { pub(crate) raw: A::Device, pub(crate) adapter_id: Stored, pub(crate) queue: A::Queue, @@ -281,7 +281,7 @@ pub struct Device { /// All live resources allocated with this [`Device`]. /// /// Has to be locked temporarily only (locked last) - pub(crate) trackers: Mutex, + pub(crate) trackers: Mutex>, // Life tracker should be locked right after the device and before anything else. life_tracker: Mutex>, /// Temporary storage for resource management functions. Cleared at the end @@ -306,7 +306,7 @@ pub enum CreateDeviceError { FailedToCreateZeroBuffer(#[from] DeviceError), } -impl Device { +impl Device { pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> { if self.features.contains(feature) { Ok(()) @@ -328,7 +328,6 @@ impl Device { } impl Device { - #[allow(clippy::too_many_arguments)] pub(crate) fn new( open: hal::OpenDevice, adapter_id: Stored, @@ -394,7 +393,7 @@ impl Device { command_allocator: Mutex::new(com_alloc), active_submission_index: 0, fence, - trackers: Mutex::new(TrackerSet::new(A::VARIANT)), + trackers: Mutex::new(Tracker::new()), life_tracker: Mutex::new(life::LifetimeTracker::new()), temp_suspected: life::SuspectedResources::default(), #[cfg(feature = "trace")] @@ -495,7 +494,7 @@ impl Device { fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( &'this mut self, hub: &Hub, - trackers: &TrackerSet, + trackers: &Tracker, token: &mut Token<'token, Self>, ) { self.temp_suspected.clear(); @@ -536,12 +535,12 @@ impl Device { self.temp_suspected.samplers.push(id); } } - for id in trackers.compute_pipes.used() { + for id in trackers.compute_pipelines.used() { if compute_pipe_guard[id].life_guard.ref_count.is_none() { self.temp_suspected.compute_pipelines.push(id); } } - for id in trackers.render_pipes.used() { + for id in trackers.render_pipelines.used() { if render_pipe_guard[id].life_guard.ref_count.is_none() { self.temp_suspected.render_pipelines.push(id); } @@ -655,7 +654,7 @@ impl Device { desc.array_layer_count(), ), full_range: TextureSelector { - levels: 0..desc.mip_level_count, + mips: 0..desc.mip_level_count, layers: 0..desc.array_layer_count(), }, life_guard: LifeGuard::new(desc.label.borrow_or_default()), @@ -876,7 +875,7 @@ impl Device { _ => texture.desc.array_layer_count(), }, }; - let level_end = texture.full_range.levels.end; + let level_end = texture.full_range.mips.end; let layer_end = texture.full_range.layers.end; if required_level_count > level_end { return Err(resource::CreateTextureViewError::TooManyMipLevels { @@ -927,7 +926,7 @@ impl Device { .array_layer_count .map_or(layer_end, |_| required_layer_count); let selector = TextureSelector { - levels: desc.range.base_mip_level..end_level, + mips: desc.range.base_mip_level..end_level, layers: desc.range.base_array_layer..end_layer, }; @@ -972,11 +971,11 @@ impl Device { wgt::TextureViewDimension::D3 => { hal::TextureUses::RESOURCE | hal::TextureUses::STORAGE_READ - | hal::TextureUses::STORAGE_WRITE + | hal::TextureUses::STORAGE_READ_WRITE } _ => hal::TextureUses::all(), }; - let mask_mip_level = if selector.levels.end - selector.levels.start != 1 { + let mask_mip_level = if selector.mips.end - selector.mips.start != 1 { hal::TextureUses::RESOURCE } else { hal::TextureUses::all() @@ -1018,16 +1017,6 @@ impl Device { format_features: texture.format_features, extent, samples: texture.desc.sample_count, - // once a storage - forever a storage - sampled_internal_use: if texture - .desc - .usage - .contains(wgt::TextureUsages::STORAGE_BINDING) - { - hal::TextureUses::RESOURCE | hal::TextureUses::STORAGE_READ - } else { - hal::TextureUses::RESOURCE - }, selector, life_guard: LifeGuard::new(desc.label.borrow_or_default()), }) @@ -1058,7 +1047,8 @@ impl Device { let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp { let clamp = clamp.get(); - let valid_clamp = clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32); + let valid_clamp = + clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two_u32(clamp as u32); if !valid_clamp { return Err(resource::CreateSamplerError::InvalidClamp(clamp)); } @@ -1486,7 +1476,6 @@ impl Device { }) } - #[allow(clippy::too_many_arguments)] fn create_buffer_binding<'a>( bb: &binding_model::BufferBinding, binding: u32, @@ -1494,7 +1483,7 @@ impl Device { used_buffer_ranges: &mut Vec, dynamic_binding_info: &mut Vec, late_buffer_binding_sizes: &mut FastHashMap, - used: &mut TrackerSet, + used: &mut BindGroupStates, storage: &'a Storage, id::BufferId>, limits: &wgt::Limits, ) -> Result, binding_model::CreateBindGroupError> { @@ -1525,7 +1514,7 @@ impl Device { if read_only { hal::BufferUses::STORAGE_READ } else { - hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE + hal::BufferUses::STORAGE_READ_WRITE }, limits.max_storage_buffer_binding_size, ), @@ -1543,8 +1532,8 @@ impl Device { let buffer = used .buffers - .use_extend(storage, bb.buffer_id, (), internal_use) - .map_err(|_| Error::InvalidBuffer(bb.buffer_id))?; + .add_single(storage, bb.buffer_id, internal_use) + .ok_or(Error::InvalidBuffer(bb.buffer_id))?; check_buffer_usage(buffer.usage, pub_usage)?; let raw_buffer = buffer .raw @@ -1613,26 +1602,26 @@ impl Device { fn create_texture_binding( view: &resource::TextureView, - texture_guard: &parking_lot::lock_api::RwLockReadGuard< - parking_lot::RawRwLock, - Storage, id::Id>>, - >, + texture_guard: &Storage, id::TextureId>, internal_use: hal::TextureUses, pub_usage: wgt::TextureUsages, - used: &mut TrackerSet, + used: &mut BindGroupStates, used_texture_ranges: &mut Vec, ) -> Result<(), binding_model::CreateBindGroupError> { // Careful here: the texture may no longer have its own ref count, // if it was deleted by the user. - let texture = &texture_guard[view.parent_id.value]; - used.textures - .change_extend( - view.parent_id.value, - &view.parent_id.ref_count, - view.selector.clone(), + let texture = used + .textures + .add_single( + texture_guard, + view.parent_id.value.0, + view.parent_id.ref_count.clone(), + Some(view.selector.clone()), internal_use, ) - .map_err(UsageConflict::from)?; + .ok_or(binding_model::CreateBindGroupError::InvalidTexture( + view.parent_id.value.0, + ))?; check_texture_usage(texture.desc.usage, pub_usage)?; used_texture_ranges.push(TextureInitTrackerAction { @@ -1674,7 +1663,7 @@ impl Device { // it needs to be in BGL iteration order, not BG entry order. let mut late_buffer_binding_sizes = FastHashMap::default(); // fill out the descriptors - let mut used = TrackerSet::new(A::VARIANT); + let mut used = BindGroupStates::new(); let (buffer_guard, mut token) = hub.buffers.read(token); let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token @@ -1738,8 +1727,8 @@ impl Device { wgt::BindingType::Sampler(ty) => { let sampler = used .samplers - .use_extend(&*sampler_guard, id, (), ()) - .map_err(|_| Error::InvalidSampler(id))?; + .add_single(&*sampler_guard, id) + .ok_or(Error::InvalidSampler(id))?; // Allowed sampler values for filtering and comparison let (allowed_filtering, allowed_comparison) = match ty { @@ -1787,8 +1776,8 @@ impl Device { for &id in bindings_array.iter() { let sampler = used .samplers - .use_extend(&*sampler_guard, id, (), ()) - .map_err(|_| Error::InvalidSampler(id))?; + .add_single(&*sampler_guard, id) + .ok_or(Error::InvalidSampler(id))?; hal_samplers.push(&sampler.raw); } @@ -1797,8 +1786,8 @@ impl Device { Br::TextureView(id) => { let view = used .views - .use_extend(&*texture_view_guard, id, (), ()) - .map_err(|_| Error::InvalidTextureView(id))?; + .add_single(&*texture_view_guard, id) + .ok_or(Error::InvalidTextureView(id))?; let (pub_usage, internal_use) = Self::texture_use_parameters( binding, decl, @@ -1828,8 +1817,8 @@ impl Device { for &id in bindings_array.iter() { let view = used .views - .use_extend(&*texture_view_guard, id, (), ()) - .map_err(|_| Error::InvalidTextureView(id))?; + .add_single(&*texture_view_guard, id) + .ok_or(Error::InvalidTextureView(id))?; let (pub_usage, internal_use) = Self::texture_use_parameters(binding, decl, view, "SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?; @@ -1858,6 +1847,8 @@ impl Device { }); } + used.optimize(); + hal_entries.sort_by_key(|entry| entry.binding); for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) { if a.binding == b.binding { @@ -1995,7 +1986,7 @@ impl Device { } Ok(( wgt::TextureUsages::TEXTURE_BINDING, - view.sampled_internal_use, + hal::TextureUses::RESOURCE, )) } wgt::BindingType::StorageTexture { @@ -2018,7 +2009,7 @@ impl Device { }); } - let mip_level_count = view.selector.levels.end - view.selector.levels.start; + let mip_level_count = view.selector.mips.end - view.selector.mips.start; if mip_level_count != 1 { return Err(Error::InvalidStorageTextureMipLevelCount { binding, @@ -2027,7 +2018,7 @@ impl Device { } let internal_use = match access { - wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_WRITE, + wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE, wgt::StorageTextureAccess::ReadOnly => { if !view .format_features @@ -2047,7 +2038,7 @@ impl Device { return Err(Error::StorageReadNotSupported(view.desc.format)); } - hal::TextureUses::STORAGE_WRITE | hal::TextureUses::STORAGE_READ + hal::TextureUses::STORAGE_READ_WRITE } }; Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use)) @@ -2551,7 +2542,7 @@ impl Device { let samples = { let sc = desc.multisample.count; - if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) { + if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) { return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc)); } sc @@ -2879,7 +2870,7 @@ impl Device { } } -impl Device { +impl Device { pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer) { if let Some(raw) = buffer.raw { unsafe { @@ -2925,7 +2916,7 @@ impl Device { } } -impl crate::hub::Resource for Device { +impl crate::hub::Resource for Device { const TYPE: &'static str = "Device"; fn life_guard(&self) -> &LifeGuard { @@ -2981,7 +2972,7 @@ pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> { } impl ImplicitPipelineIds<'_, G> { - fn prepare(self, hub: &Hub) -> ImplicitPipelineContext { + fn prepare(self, hub: &Hub) -> ImplicitPipelineContext { ImplicitPipelineContext { root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(), group_ids: self @@ -3184,8 +3175,8 @@ impl Global { .trackers .lock() .buffers - .init(id, ref_count, BufferState::with_usage(buffer_use)) - .unwrap(); + .insert_single(id, ref_count, buffer_use); + return (id.0, None); }; @@ -3193,6 +3184,43 @@ impl Global { (id, Some(error)) } + /// Assign `id_in` an error with the given `label`. + /// + /// Ensure that future attempts to use `id_in` as a buffer ID will propagate + /// the error, following the WebGPU ["contagious invalidity"] style. + /// + /// Firefox uses this function to comply strictly with the WebGPU spec, + /// which requires [`GPUBufferDescriptor`] validation to be generated on the + /// Device timeline and leave the newly created [`GPUBuffer`] invalid. + /// + /// Ideally, we would simply let [`device_create_buffer`] take care of all + /// of this, but some errors must be detected before we can even construct a + /// [`wgpu_types::BufferDescriptor`] to give it. For example, the WebGPU API + /// allows a `GPUBufferDescriptor`'s [`usage`] property to be any WebIDL + /// `unsigned long` value, but we can't construct a + /// [`wgpu_types::BufferUsages`] value from values with unassigned bits + /// set. This means we must validate `usage` before we can call + /// `device_create_buffer`. + /// + /// When that validation fails, we must arrange for the buffer id to be + /// considered invalid. This method provides the means to do so. + /// + /// ["contagious invalidity"]: https://www.w3.org/TR/webgpu/#invalidity + /// [`GPUBufferDescriptor`]: https://www.w3.org/TR/webgpu/#dictdef-gpubufferdescriptor + /// [`GPUBuffer`]: https://www.w3.org/TR/webgpu/#gpubuffer + /// [`wgpu_types::BufferDescriptor`]: wgt::BufferDescriptor + /// [`device_create_buffer`]: Global::device_create_buffer + /// [`usage`]: https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-usage + /// [`wgpu_types::BufferUsages`]: wgt::BufferUsages + pub fn create_buffer_error(&self, id_in: Input, label: Label) { + let hub = A::hub(self); + let mut token = Token::root(); + let fid = hub.buffers.prepare(id_in); + + let (_, mut token) = hub.devices.read(&mut token); + fid.assign_error(label.borrow_or_default(), &mut token); + } + #[cfg(feature = "replay")] pub fn device_wait_for_buffer( &self, @@ -3446,19 +3474,17 @@ impl Global { Ok(texture) => texture, Err(error) => break error, }; - let num_levels = texture.full_range.levels.end; - let num_layers = texture.full_range.layers.end; let ref_count = texture.life_guard.add_ref(); let id = fid.assign(texture, &mut token); log::info!("Created texture {:?} with {:?}", id, desc); - device - .trackers - .lock() - .textures - .init(id, ref_count, TextureState::new(num_levels, num_layers)) - .unwrap(); + device.trackers.lock().textures.insert_single( + id.0, + ref_count, + hal::TextureUses::UNINITIALIZED, + ); + return (id.0, None); }; @@ -3524,19 +3550,17 @@ impl Global { texture.initialization_status = TextureInitTracker::new(desc.mip_level_count, 0); - let num_levels = texture.full_range.levels.end; - let num_layers = texture.full_range.layers.end; let ref_count = texture.life_guard.add_ref(); let id = fid.assign(texture, &mut token); log::info!("Created texture {:?} with {:?}", id, desc); - device - .trackers - .lock() - .textures - .init(id, ref_count, TextureState::new(num_levels, num_layers)) - .unwrap(); + device.trackers.lock().textures.insert_single( + id.0, + ref_count, + hal::TextureUses::UNINITIALIZED, + ); + return (id.0, None); }; @@ -3694,12 +3718,7 @@ impl Global { let ref_count = view.life_guard.add_ref(); let id = fid.assign(view, &mut token); - device - .trackers - .lock() - .views - .init(id, ref_count, PhantomData) - .unwrap(); + device.trackers.lock().views.insert_single(id, ref_count); return (id.0, None); }; @@ -3792,12 +3811,8 @@ impl Global { let ref_count = sampler.life_guard.add_ref(); let id = fid.assign(sampler, &mut token); - device - .trackers - .lock() - .samplers - .init(id, ref_count, PhantomData) - .unwrap(); + device.trackers.lock().samplers.insert_single(id, ref_count); + return (id.0, None); }; @@ -4055,18 +4070,13 @@ impl Global { let ref_count = bind_group.life_guard.add_ref(); let id = fid.assign(bind_group, &mut token); - log::debug!( - "Bind group {:?} {:#?}", - id, - hub.bind_groups.read(&mut token).0[id].used - ); + log::debug!("Bind group {:?}", id,); device .trackers .lock() .bind_groups - .init(id, ref_count, PhantomData) - .unwrap(); + .insert_single(id, ref_count); return (id.0, None); }; @@ -4369,16 +4379,11 @@ impl Global { Err(e) => break e, }; - log::debug!("Render bundle {:#?}", render_bundle.used); + log::debug!("Render bundle"); let ref_count = render_bundle.life_guard.add_ref(); let id = fid.assign(render_bundle, &mut token); - device - .trackers - .lock() - .bundles - .init(id, ref_count, PhantomData) - .unwrap(); + device.trackers.lock().bundles.insert_single(id, ref_count); return (id.0, None); }; @@ -4457,8 +4462,7 @@ impl Global { .trackers .lock() .query_sets - .init(id, ref_count, PhantomData) - .unwrap(); + .insert_single(id, ref_count); return (id.0, None); }; @@ -4552,9 +4556,9 @@ impl Global { device .trackers .lock() - .render_pipes - .init(id, ref_count, PhantomData) - .unwrap(); + .render_pipelines + .insert_single(id, ref_count); + return (id.0, None); }; @@ -4693,9 +4697,8 @@ impl Global { device .trackers .lock() - .compute_pipes - .init(id, ref_count, PhantomData) - .unwrap(); + .compute_pipelines + .insert_single(id, ref_count); return (id.0, None); }; @@ -4965,9 +4968,9 @@ impl Global { .map_err(|_| DeviceError::Invalid)? .maintain(hub, force_wait, &mut token)? }; - unsafe { - closures.fire(); - } + + closures.fire(); + Ok(queue_empty) } @@ -5045,9 +5048,7 @@ impl Global { self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } - unsafe { - closures.fire(); - } + closures.fire(); Ok(all_queue_empty) } @@ -5154,7 +5155,7 @@ impl Global { return Err(resource::BufferAccessError::AlreadyMapped); } resource::BufferMapState::Waiting(_) => { - op.call_error(); + op.callback.call_error(); return Ok(()); } resource::BufferMapState::Idle => { @@ -5167,16 +5168,20 @@ impl Global { }; log::debug!("Buffer {:?} map state -> Waiting", buffer_id); - (buffer.device_id.value, buffer.life_guard.add_ref()) + let device = &device_guard[buffer.device_id.value]; + + let ret = (buffer.device_id.value, buffer.life_guard.add_ref()); + + let mut trackers = device.trackers.lock(); + trackers + .buffers + .set_single(&*buffer_guard, buffer_id, internal_use); + trackers.buffers.drain(); + + ret }; let device = &device_guard[device_id]; - device.trackers.lock().buffers.change_replace( - id::Valid(buffer_id), - &ref_count, - (), - internal_use, - ); device .lock_life(&mut token) @@ -5367,9 +5372,7 @@ impl Global { //Note: outside inner function so no locks are held when calling the callback let closure = self.buffer_unmap_inner::(buffer_id)?; if let Some((operation, status)) = closure { - unsafe { - (operation.callback)(status, operation.user_data); - } + operation.callback.call(status); } Ok(()) } diff --git a/third_party/rust/wgpu-core/src/device/queue.rs b/third_party/rust/wgpu-core/src/device/queue.rs index 572f49e5dcc1c..83de498ef656e 100644 --- a/third_party/rust/wgpu-core/src/device/queue.rs +++ b/third_party/rust/wgpu-core/src/device/queue.rs @@ -28,16 +28,56 @@ use thiserror::Error; /// without a concrete moment of when it can be cleared. const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64; -pub type OnSubmittedWorkDoneCallback = unsafe extern "C" fn(user_data: *mut u8); #[repr(C)] -#[derive(Clone, Copy, Debug)] +pub struct SubmittedWorkDoneClosureC { + callback: unsafe extern "C" fn(user_data: *mut u8), + user_data: *mut u8, +} + +unsafe impl Send for SubmittedWorkDoneClosureC {} + pub struct SubmittedWorkDoneClosure { - pub callback: OnSubmittedWorkDoneCallback, - pub user_data: *mut u8, + // We wrap this so creating the enum in the C variant can be unsafe, + // allowing our call function to be safe. + inner: SubmittedWorkDoneClosureInner, } -unsafe impl Send for SubmittedWorkDoneClosure {} -unsafe impl Sync for SubmittedWorkDoneClosure {} +enum SubmittedWorkDoneClosureInner { + Rust { + callback: Box, + }, + C { + inner: SubmittedWorkDoneClosureC, + }, +} + +impl SubmittedWorkDoneClosure { + pub fn from_rust(callback: Box) -> Self { + Self { + inner: SubmittedWorkDoneClosureInner::Rust { callback }, + } + } + + /// # Safety + /// + /// - The callback pointer must be valid to call with the provided user_data pointer. + /// - Both pointers must point to 'static data as the callback may happen at an unspecified time. + pub unsafe fn from_c(inner: SubmittedWorkDoneClosureC) -> Self { + Self { + inner: SubmittedWorkDoneClosureInner::C { inner }, + } + } + + pub(crate) fn call(self) { + match self.inner { + SubmittedWorkDoneClosureInner::Rust { callback } => callback(), + // SAFETY: the contract of the call to from_c says that this unsafe is sound. + SubmittedWorkDoneClosureInner::C { inner } => unsafe { + (inner.callback)(inner.user_data) + }, + } + } +} struct StagingData { buffer: A::Buffer, @@ -192,7 +232,7 @@ impl PendingWrites { } } -impl super::Device { +impl super::Device { fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result, DeviceError> { profiling::scope!("prepare_stage"); let stage_desc = hal::BufferDescriptor { @@ -286,8 +326,8 @@ impl Global { let mut trackers = device.trackers.lock(); let (dst, transition) = trackers .buffers - .use_replace(&*buffer_guard, buffer_id, (), hal::BufferUses::COPY_DST) - .map_err(TransferError::InvalidBuffer)?; + .set_single(&*buffer_guard, buffer_id, hal::BufferUses::COPY_DST) + .ok_or(TransferError::InvalidBuffer(buffer_id))?; let dst_raw = dst .raw .as_ref() @@ -451,9 +491,9 @@ impl Global { .drain(init_layer_range) .collect::>>() { - crate::command::clear_texture_no_device( + crate::command::clear_texture( + &*texture_guard, id::Valid(destination.texture), - &*dst, TextureInitRange { mip_range: destination.mip_level..(destination.mip_level + 1), layer_range, @@ -473,13 +513,13 @@ impl Global { let (dst, transition) = trackers .textures - .use_replace( + .set_single( &*texture_guard, destination.texture, selector, hal::TextureUses::COPY_DST, ) - .unwrap(); + .ok_or(TransferError::InvalidTexture(destination.texture))?; let (hal_copy_size, array_layer_count) = validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?; @@ -561,7 +601,8 @@ impl Global { .ok_or(TransferError::InvalidTexture(destination.texture))?; unsafe { - encoder.transition_textures(transition.map(|pending| pending.into_hal(dst))); + encoder + .transition_textures(transition.map(|pending| pending.into_hal(dst)).into_iter()); encoder.transition_buffers(iter::once(barrier)); encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions); } @@ -594,7 +635,7 @@ impl Global { device.active_submission_index += 1; let submit_index = device.active_submission_index; let mut active_executions = Vec::new(); - let mut used_surface_textures = track::ResourceTracker::new(A::VARIANT); + let mut used_surface_textures = track::TextureUsageScope::new(); { let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token); @@ -616,12 +657,15 @@ impl Global { //Note: locking the trackers has to be done after the storages let mut trackers = device.trackers.lock(); + used_surface_textures.set_size(texture_guard.len()); + //TODO: if multiple command buffers are submitted, we can re-use the last // native command buffer of the previous chain instead of always creating // a temporary one, since the chains are not finished. // finish all the command buffers first for &cmb_id in command_buffer_ids { + #[allow(unused_mut)] let mut cmdbuf = match hub .command_buffers .unregister_locked(cmb_id, &mut *command_buffer_guard) @@ -642,7 +686,7 @@ impl Global { } // optimize the tracked states - cmdbuf.trackers.optimize(); + // cmdbuf.trackers.optimize(); // update submission IDs for id in cmdbuf.trackers.buffers.used() { @@ -669,33 +713,35 @@ impl Global { } for id in cmdbuf.trackers.textures.used() { let texture = &mut texture_guard[id]; - match texture.inner { + let should_extend = match texture.inner { TextureInner::Native { raw: None } => { return Err(QueueSubmitError::DestroyedTexture(id.0)); } - TextureInner::Native { raw: Some(_) } => {} + TextureInner::Native { raw: Some(_) } => false, TextureInner::Surface { ref mut has_work, .. } => { - use track::ResourceState as _; - *has_work = true; - let ref_count = cmdbuf.trackers.textures.get_ref_count(id); - //TODO: better error handling here? - // register it in the temporary tracker. - let mut ts = track::TextureState::default(); - let _ = ts.change( - id, - texture.full_range.clone(), - hal::TextureUses::empty(), //present - None, - ); - let _ = used_surface_textures.init(id, ref_count.clone(), ts); + true } - } + }; if !texture.life_guard.use_at(submit_index) { device.temp_suspected.textures.push(id); } + if should_extend { + unsafe { + let ref_count = cmdbuf.trackers.textures.get_ref_count(id); + used_surface_textures + .merge_single( + &*texture_guard, + id, + None, + ref_count, + hal::TextureUses::PRESENT, + ) + .unwrap(); + }; + } } for id in cmdbuf.trackers.views.used() { if !texture_view_guard[id].life_guard.use_at(submit_index) { @@ -717,13 +763,13 @@ impl Global { sampler_guard[sub_id].life_guard.use_at(submit_index); } } - assert!(cmdbuf.trackers.samplers.is_empty()); - for id in cmdbuf.trackers.compute_pipes.used() { + // assert!(cmdbuf.trackers.samplers.is_empty()); + for id in cmdbuf.trackers.compute_pipelines.used() { if !compute_pipe_guard[id].life_guard.use_at(submit_index) { device.temp_suspected.compute_pipelines.push(id); } } - for id in cmdbuf.trackers.render_pipes.used() { + for id in cmdbuf.trackers.render_pipelines.used() { if !render_pipe_guard[id].life_guard.use_at(submit_index) { device.temp_suspected.render_pipelines.push(id); } @@ -741,12 +787,12 @@ impl Global { // We need to update the submission indices for the contained // state-less (!) resources as well, excluding the bind groups. // They don't get deleted too early if the bundle goes out of scope. - for sub_id in bundle.used.compute_pipes.used() { - compute_pipe_guard[sub_id].life_guard.use_at(submit_index); - } - for sub_id in bundle.used.render_pipes.used() { + for sub_id in bundle.used.render_pipelines.used() { render_pipe_guard[sub_id].life_guard.use_at(submit_index); } + for sub_id in bundle.used.query_sets.used() { + query_set_guard[sub_id].life_guard.use_at(submit_index); + } } let mut baked = cmdbuf.into_baked(); @@ -766,11 +812,10 @@ impl Global { .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?; //Note: stateless trackers are not merged: // device already knows these resources exist. - CommandBuffer::insert_barriers( + CommandBuffer::insert_barriers_from_tracker( &mut baked.encoder, &mut *trackers, - &baked.trackers.buffers, - &baked.trackers.textures, + &baked.trackers, &*buffer_guard, &*texture_guard, ); @@ -788,19 +833,19 @@ impl Global { .begin_encoding(Some("(wgpu internal) Present")) .map_err(DeviceError::from)? }; - let texture_barriers = trackers + trackers .textures - .merge_replace(&used_surface_textures) - .map(|pending| { - let tex = &texture_guard[pending.id]; - pending.into_hal(tex) - }); + .set_from_usage_scope(&*texture_guard, &used_surface_textures); + let texture_barriers = trackers.textures.drain().map(|pending| { + let tex = unsafe { texture_guard.get_unchecked(pending.id) }; + pending.into_hal(tex) + }); let present = unsafe { baked.encoder.transition_textures(texture_barriers); baked.encoder.end_encoding().unwrap() }; baked.list.push(present); - used_surface_textures.clear(); + used_surface_textures = track::TextureUsageScope::new(); } // done @@ -810,7 +855,7 @@ impl Global { }); } - log::trace!("Device after submission {}: {:#?}", submit_index, trackers); + log::trace!("Device after submission {}", submit_index); } let super::Device { @@ -830,6 +875,8 @@ impl Global { let (_, mut token) = hub.buffers.read(&mut token); // skip token let (mut texture_guard, _) = hub.textures.write(&mut token); + used_surface_textures.set_size(texture_guard.len()); + for &id in pending_writes.dst_textures.iter() { let texture = texture_guard.get_mut(id).unwrap(); match texture.inner { @@ -840,39 +887,39 @@ impl Global { TextureInner::Surface { ref mut has_work, .. } => { - use track::ResourceState as _; - *has_work = true; let ref_count = texture.life_guard.add_ref(); - //TODO: better error handling here? - // register it in the temporary tracker. - let mut ts = track::TextureState::default(); - let _ = ts.change( - id::Valid(id), - texture.full_range.clone(), - hal::TextureUses::empty(), //present - None, - ); - let _ = used_surface_textures.init(id::Valid(id), ref_count, ts); + unsafe { + used_surface_textures + .merge_single( + &*texture_guard, + id::Valid(id), + None, + &ref_count, + hal::TextureUses::PRESENT, + ) + .unwrap() + }; } } } if !used_surface_textures.is_empty() { let mut trackers = device.trackers.lock(); - let texture_barriers = trackers + + trackers .textures - .merge_replace(&used_surface_textures) - .map(|pending| { - let tex = &texture_guard[pending.id]; - pending.into_hal(tex) - }); + .set_from_usage_scope(&*texture_guard, &used_surface_textures); + let texture_barriers = trackers.textures.drain().map(|pending| { + let tex = unsafe { texture_guard.get_unchecked(pending.id) }; + pending.into_hal(tex) + }); + unsafe { pending_writes .command_encoder .transition_textures(texture_barriers); }; - used_surface_textures.clear(); } } @@ -925,9 +972,8 @@ impl Global { }; // the closures should execute with nothing locked! - unsafe { - callbacks.fire(); - } + callbacks.fire(); + Ok(()) } @@ -950,7 +996,7 @@ impl Global { closure: SubmittedWorkDoneClosure, ) -> Result<(), InvalidQueue> { //TODO: flush pending writes - let added = { + let closure_opt = { let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -959,10 +1005,8 @@ impl Global { Err(_) => return Err(InvalidQueue), } }; - if !added { - unsafe { - (closure.callback)(closure.user_data); - } + if let Some(closure) = closure_opt { + closure.call(); } Ok(()) } diff --git a/third_party/rust/wgpu-core/src/hub.rs b/third_party/rust/wgpu-core/src/hub.rs index 5df42bba18fdc..4e8ddf78f6f9d 100644 --- a/third_party/rust/wgpu-core/src/hub.rs +++ b/third_party/rust/wgpu-core/src/hub.rs @@ -194,6 +194,14 @@ impl Storage { result } + pub(crate) unsafe fn get_unchecked(&self, id: u32) -> &T { + match self.map[id as usize] { + Element::Occupied(ref v, _) => v, + Element::Vacant => panic!("{}[{}] does not exist", self.kind, id), + Element::Error(_, _) => panic!(""), + } + } + pub(crate) fn label_for_invalid_id(&self, id: I) -> &str { let (index, _, _) = id.unzip(); match self.map.get(index as usize) { @@ -266,6 +274,10 @@ impl Storage { }) } + pub(crate) fn len(&self) -> usize { + self.map.len() + } + fn generate_report(&self) -> StorageReport { let mut report = StorageReport { element_size: mem::size_of::(), @@ -298,56 +310,56 @@ pub enum Root {} impl Access for Root {} impl Access for Root {} impl Access for Instance {} -impl Access> for Root {} -impl Access> for Surface {} -impl Access> for Root {} -impl Access> for Surface {} -impl Access> for Adapter {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for RenderBundle {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for PipelineLayout {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for PipelineLayout {} -impl Access> for CommandBuffer {} -impl Access> for Root {} -impl Access> for Device {} -impl Access for Device {} -impl Access for CommandBuffer {} -impl Access> for Device {} -impl Access> for BindGroup {} -impl Access> for Device {} -impl Access> for BindGroup {} -impl Access> for ComputePipeline {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for CommandBuffer {} -impl Access> for RenderPipeline {} -impl Access> for ComputePipeline {} -impl Access> for Sampler {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for BindGroup {} -impl Access> for CommandBuffer {} -impl Access> for ComputePipeline {} -impl Access> for RenderPipeline {} -impl Access> for QuerySet {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for Buffer {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for Texture {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for TextureView {} +impl Access> for Root {} +impl Access> for Surface {} +impl Access> for Root {} +impl Access> for Surface {} +impl Access> for Adapter {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for RenderBundle {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for PipelineLayout {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for PipelineLayout {} +impl Access> for CommandBuffer {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for Device {} +impl Access> for CommandBuffer {} +impl Access> for Device {} +impl Access> for BindGroup {} +impl Access> for Device {} +impl Access> for BindGroup {} +impl Access> for ComputePipeline {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for CommandBuffer {} +impl Access> for RenderPipeline {} +impl Access> for ComputePipeline {} +impl Access> for Sampler {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for BindGroup {} +impl Access> for CommandBuffer {} +impl Access> for ComputePipeline {} +impl Access> for RenderPipeline {} +impl Access> for QuerySet {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for Buffer {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for Texture {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for TextureView {} #[cfg(debug_assertions)] thread_local! { @@ -614,7 +626,7 @@ impl HubReport { } } -pub struct Hub { +pub struct Hub { pub adapters: Registry, id::AdapterId, F>, pub devices: Registry, id::DeviceId, F>, pub pipeline_layouts: Registry, id::PipelineLayoutId, F>, @@ -622,7 +634,7 @@ pub struct Hub { pub bind_group_layouts: Registry, id::BindGroupLayoutId, F>, pub bind_groups: Registry, id::BindGroupId, F>, pub command_buffers: Registry, id::CommandBufferId, F>, - pub render_bundles: Registry, + pub render_bundles: Registry, id::RenderBundleId, F>, pub render_pipelines: Registry, id::RenderPipelineId, F>, pub compute_pipelines: Registry, id::ComputePipelineId, F>, pub query_sets: Registry, id::QuerySetId, F>, @@ -907,6 +919,17 @@ impl Global { } } + /// # Safety + /// + /// - The raw handle obtained from the hal Instance must not be manually destroyed + pub unsafe fn instance_as_hal) -> R, R>( + &self, + hal_instance_callback: F, + ) -> R { + let hal_instance = A::instance_as_hal(&self.instance); + hal_instance_callback(hal_instance) + } + pub fn clear_backend(&self, _dummy: ()) { let mut surface_guard = self.surfaces.data.write(); let hub = A::hub(self); @@ -991,11 +1014,31 @@ impl Drop for Global { pub trait HalApi: hal::Api { const VARIANT: Backend; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance; + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance>; fn hub(global: &Global) -> &Hub; fn get_surface(surface: &Surface) -> &HalSurface; fn get_surface_mut(surface: &mut Surface) -> &mut HalSurface; } +impl HalApi for hal::api::Empty { + const VARIANT: Backend = Backend::Empty; + fn create_instance_from_hal(_: &str, _: Self::Instance) -> Instance { + unimplemented!("called empty api") + } + fn instance_as_hal(_: &Instance) -> Option<&Self::Instance> { + unimplemented!("called empty api") + } + fn hub(_: &Global) -> &Hub { + unimplemented!("called empty api") + } + fn get_surface(_: &Surface) -> &HalSurface { + unimplemented!("called empty api") + } + fn get_surface_mut(_: &mut Surface) -> &mut HalSurface { + unimplemented!("called empty api") + } +} + #[cfg(vulkan)] impl HalApi for hal::api::Vulkan { const VARIANT: Backend = Backend::Vulkan; @@ -1006,6 +1049,9 @@ impl HalApi for hal::api::Vulkan { ..Default::default() } } + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance> { + instance.vulkan.as_ref() + } fn hub(global: &Global) -> &Hub { &global.hubs.vulkan } @@ -1027,6 +1073,9 @@ impl HalApi for hal::api::Metal { ..Default::default() } } + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance> { + instance.metal.as_ref() + } fn hub(global: &Global) -> &Hub { &global.hubs.metal } @@ -1048,6 +1097,9 @@ impl HalApi for hal::api::Dx12 { ..Default::default() } } + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance> { + instance.dx12.as_ref() + } fn hub(global: &Global) -> &Hub { &global.hubs.dx12 } @@ -1069,6 +1121,9 @@ impl HalApi for hal::api::Dx11 { ..Default::default() } } + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance> { + instance.dx11.as_ref() + } fn hub(global: &Global) -> &Hub { &global.hubs.dx11 } @@ -1091,6 +1146,9 @@ impl HalApi for hal::api::Gles { ..Default::default() } } + fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance> { + instance.gl.as_ref() + } fn hub(global: &Global) -> &Hub { &global.hubs.gl } diff --git a/third_party/rust/wgpu-core/src/id.rs b/third_party/rust/wgpu-core/src/id.rs index cbd7669e02454..0955857235596 100644 --- a/third_party/rust/wgpu-core/src/id.rs +++ b/third_party/rust/wgpu-core/src/id.rs @@ -64,9 +64,9 @@ impl From for Id { } impl Id { - #[cfg(test)] - pub(crate) fn dummy() -> Valid { - Valid(Id(NonZeroId::new(1).unwrap(), PhantomData)) + #[allow(dead_code)] + pub(crate) fn dummy(index: u32) -> Valid { + Valid(Id::zip(index, 1, Backend::Empty)) } pub fn backend(self) -> Backend { @@ -135,7 +135,7 @@ pub(crate) struct Valid(pub I); /// Most `wgpu-core` clients should not use this trait. Unusual clients that /// need to construct `Id` values directly, or access their components, like the /// WGPU recording player, may use this trait to do so. -pub trait TypedId { +pub trait TypedId: Copy { fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self; fn unzip(self) -> (Index, Epoch, Backend); } @@ -184,7 +184,7 @@ pub type CommandBufferId = Id>; 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; +pub type RenderBundleId = Id>; pub type QuerySetId = Id>; #[test] diff --git a/third_party/rust/wgpu-core/src/init_tracker/texture.rs b/third_party/rust/wgpu-core/src/init_tracker/texture.rs index a2913ea3d4da3..87bfcdc8878c7 100644 --- a/third_party/rust/wgpu-core/src/init_tracker/texture.rs +++ b/third_party/rust/wgpu-core/src/init_tracker/texture.rs @@ -26,7 +26,7 @@ pub(crate) fn has_copy_partial_init_tracker_coverage( impl From for TextureInitRange { fn from(selector: TextureSelector) -> Self { TextureInitRange { - mip_range: selector.levels, + mip_range: selector.mips, layer_range: selector.layers, } } diff --git a/third_party/rust/wgpu-core/src/instance.rs b/third_party/rust/wgpu-core/src/instance.rs index 315caa4544a3a..907c65d08cef1 100644 --- a/third_party/rust/wgpu-core/src/instance.rs +++ b/third_party/rust/wgpu-core/src/instance.rs @@ -494,6 +494,9 @@ impl Global { } #[cfg(dx12)] + /// # Safety + /// + /// The visual must be valid and able to be used to make a swapchain with. pub unsafe fn instance_create_surface_from_visual( &self, visual: *mut std::ffi::c_void, diff --git a/third_party/rust/wgpu-core/src/lib.rs b/third_party/rust/wgpu-core/src/lib.rs index e3036ad0f2ba6..8d28a3df26926 100644 --- a/third_party/rust/wgpu-core/src/lib.rs +++ b/third_party/rust/wgpu-core/src/lib.rs @@ -4,6 +4,8 @@ */ #![allow( + // It is much clearer to assert negative conditions with eq! false + clippy::bool_assert_comparison, // We use loops for getting early-out of scope without closures. clippy::never_loop, // We don't use syntax sugar where it's not necessary. @@ -16,6 +18,8 @@ clippy::new_without_default, // Needless updates are more scaleable, easier to play with features. clippy::needless_update, + // Need many arguments for some core functions to be able to re-use code in many situations. + clippy::too_many_arguments, // For some reason `rustc` can warn about these in const generics even // though they are required. unused_braces, diff --git a/third_party/rust/wgpu-core/src/present.rs b/third_party/rust/wgpu-core/src/present.rs index 833e65d6be9b8..fa7a34bf9d3b9 100644 --- a/third_party/rust/wgpu-core/src/present.rs +++ b/third_party/rust/wgpu-core/src/present.rs @@ -174,7 +174,7 @@ impl Global { initialization_status: TextureInitTracker::new(1, 1), full_range: track::TextureSelector { layers: 0..1, - levels: 0..1, + mips: 0..1, }, life_guard: LifeGuard::new(""), clear_mode: resource::TextureClearMode::RenderPass { @@ -187,20 +187,13 @@ impl Global { let id = fid.assign(texture, &mut token); { - use track::ResourceState as _; // register it in the device tracker as uninitialized let mut trackers = device.trackers.lock(); - let mut ts = track::TextureState::default(); - let _ = ts.change( - id, - track::TextureSelector { - layers: 0..1, - levels: 0..1, - }, + trackers.textures.insert_single( + id.0, + ref_count.clone(), hal::TextureUses::UNINITIALIZED, - None, ); - let _ = trackers.textures.init(id, ref_count.clone(), ts); } if present.acquired_texture.is_some() { @@ -273,6 +266,10 @@ impl Global { // The texture ID got added to the device tracker by `submit()`, // and now we are moving it away. + log::debug!( + "Removing swapchain texture {:?} from the device tracker", + texture_id.value + ); device.trackers.lock().textures.remove(texture_id.value); let (texture, _) = hub.textures.unregister(texture_id.value.0, &mut token); diff --git a/third_party/rust/wgpu-core/src/resource.rs b/third_party/rust/wgpu-core/src/resource.rs index 3f12aba0d5d9e..6f45e26399a45 100644 --- a/third_party/rust/wgpu-core/src/resource.rs +++ b/third_party/rust/wgpu-core/src/resource.rs @@ -1,9 +1,9 @@ use crate::{ device::{DeviceError, HostMap, MissingFeatures}, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token}, - id::{DeviceId, SurfaceId, TextureId, Valid}, + id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid}, init_tracker::{BufferInitTracker, TextureInitTracker}, - track::{TextureSelector, DUMMY_SELECTOR}, + track::TextureSelector, validation::MissingBufferUsageError, Label, LifeGuard, RefCount, Stored, }; @@ -23,7 +23,6 @@ pub enum BufferMapAsyncStatus { ContextLost, } -#[derive(Debug)] pub(crate) enum BufferMapState { /// Mapped at creation. Init { @@ -46,29 +45,67 @@ pub(crate) enum BufferMapState { unsafe impl Send for BufferMapState {} unsafe impl Sync for BufferMapState {} -pub type BufferMapCallback = unsafe extern "C" fn(status: BufferMapAsyncStatus, userdata: *mut u8); - #[repr(C)] -#[derive(Debug)] -pub struct BufferMapOperation { - pub host: HostMap, - pub callback: BufferMapCallback, - pub user_data: *mut u8, +pub struct BufferMapCallbackC { + callback: unsafe extern "C" fn(status: BufferMapAsyncStatus, user_data: *mut u8), + user_data: *mut u8, +} + +unsafe impl Send for BufferMapCallbackC {} + +pub struct BufferMapCallback { + // We wrap this so creating the enum in the C variant can be unsafe, + // allowing our call function to be safe. + inner: BufferMapCallbackInner, } -//TODO: clarify if/why this is needed here -unsafe impl Send for BufferMapOperation {} -unsafe impl Sync for BufferMapOperation {} +enum BufferMapCallbackInner { + Rust { + callback: Box, + }, + C { + inner: BufferMapCallbackC, + }, +} + +impl BufferMapCallback { + pub fn from_rust(callback: Box) -> Self { + Self { + inner: BufferMapCallbackInner::Rust { callback }, + } + } + + /// # Safety + /// + /// - The callback pointer must be valid to call with the provided user_data pointer. + /// - Both pointers must point to 'static data as the callback may happen at an unspecified time. + pub unsafe fn from_c(inner: BufferMapCallbackC) -> Self { + Self { + inner: BufferMapCallbackInner::C { inner }, + } + } + + pub(crate) fn call(self, status: BufferMapAsyncStatus) { + match self.inner { + BufferMapCallbackInner::Rust { callback } => callback(status), + // SAFETY: the contract of the call to from_c says that this unsafe is sound. + BufferMapCallbackInner::C { inner } => unsafe { + (inner.callback)(status, inner.user_data) + }, + } + } -impl BufferMapOperation { pub(crate) fn call_error(self) { log::error!("wgpu_buffer_map_async failed: buffer mapping is pending"); - unsafe { - (self.callback)(BufferMapAsyncStatus::Error, self.user_data); - } + self.call(BufferMapAsyncStatus::Error); } } +pub struct BufferMapOperation { + pub host: HostMap, + pub callback: BufferMapCallback, +} + #[derive(Clone, Debug, Error)] pub enum BufferAccessError { #[error(transparent)] @@ -105,7 +142,6 @@ pub enum BufferAccessError { }, } -#[derive(Debug)] pub(crate) struct BufferPendingMapping { pub range: Range, pub op: BufferMapOperation, @@ -115,7 +151,6 @@ pub(crate) struct BufferPendingMapping { pub type BufferDescriptor<'a> = wgt::BufferDescriptor>; -#[derive(Debug)] pub struct Buffer { pub(crate) raw: Option, pub(crate) device_id: Stored, @@ -149,12 +184,6 @@ impl Resource for Buffer { } } -impl Borrow<()> for Buffer { - fn borrow(&self) -> &() { - &DUMMY_SELECTOR - } -} - pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; #[derive(Debug)] @@ -250,6 +279,26 @@ impl Global { hal_texture_callback(hal_texture); } + /// # Safety + /// + /// - The raw adapter handle must not be manually destroyed + pub unsafe fn adapter_as_hal) -> R, R>( + &self, + id: AdapterId, + hal_adapter_callback: F, + ) -> R { + profiling::scope!("as_hal", "Adapter"); + + let hub = A::hub(self); + let mut token = Token::root(); + + let (guard, _) = hub.adapters.read(&mut token); + let adapter = guard.get(id).ok(); + let hal_adapter = adapter.map(|adapter| &adapter.raw.adapter); + + hal_adapter_callback(hal_adapter) + } + /// # Safety /// /// - The raw device handle must not be manually destroyed @@ -371,8 +420,6 @@ pub struct TextureView { pub(crate) format_features: wgt::TextureFormatFeatures, pub(crate) extent: wgt::Extent3d, pub(crate) samples: u32, - /// Internal use of this texture view when used as `BindingType::Texture`. - pub(crate) sampled_internal_use: hal::TextureUses, pub(crate) selector: TextureSelector, pub(crate) life_guard: LifeGuard, } @@ -428,12 +475,6 @@ impl Resource for TextureView { } } -impl Borrow<()> for TextureView { - fn borrow(&self) -> &() { - &DUMMY_SELECTOR - } -} - /// Describes a [`Sampler`] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] @@ -510,11 +551,6 @@ impl Resource for Sampler { } } -impl Borrow<()> for Sampler { - fn borrow(&self) -> &() { - &DUMMY_SELECTOR - } -} #[derive(Clone, Debug, Error)] pub enum CreateQuerySetError { #[error(transparent)] @@ -545,12 +581,6 @@ impl Resource for QuerySet { } } -impl Borrow<()> for QuerySet { - fn borrow(&self) -> &() { - &DUMMY_SELECTOR - } -} - #[derive(Clone, Debug, Error)] pub enum DestroyError { #[error("resource is invalid")] diff --git a/third_party/rust/wgpu-core/src/track/buffer.rs b/third_party/rust/wgpu-core/src/track/buffer.rs index 01e3a79798425..7770b423087a5 100644 --- a/third_party/rust/wgpu-core/src/track/buffer.rs +++ b/third_party/rust/wgpu-core/src/track/buffer.rs @@ -1,236 +1,778 @@ -use super::{PendingTransition, ResourceState, Unit}; -use crate::id::{BufferId, Valid}; +/*! Buffer Trackers + * + * Buffers are represented by a single state for the whole resource, + * a 16 bit bitflag of buffer usages. Because there is only ever + * one subresource, they have no selector. +!*/ + +use std::{borrow::Cow, marker::PhantomData, vec::Drain}; + +use super::PendingTransition; +use crate::{ + hub, + id::{BufferId, TypedId, Valid}, + resource::Buffer, + track::{ + invalid_resource_state, iterate_bitvec_indices, skip_barrier, ResourceMetadata, + ResourceMetadataProvider, ResourceUses, UsageConflict, + }, + LifeGuard, RefCount, +}; use hal::BufferUses; -pub(crate) type BufferState = Unit; +impl ResourceUses for BufferUses { + const EXCLUSIVE: Self = Self::EXCLUSIVE; -impl PendingTransition { - fn collapse(self) -> Result { - if self.usage.start.is_empty() - || self.usage.start == self.usage.end - || !BufferUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end) - { - Ok(self.usage.start | self.usage.end) - } else { - Err(self) - } + type Id = BufferId; + type Selector = (); + + fn bits(self) -> u16 { + Self::bits(&self) + } + + fn all_ordered(self) -> bool { + Self::ORDERED.contains(self) + } + + fn any_exclusive(self) -> bool { + self.intersects(Self::EXCLUSIVE) } } -impl Default for BufferState { - fn default() -> Self { +/// Stores all the buffers that a bind group stores. +pub(crate) struct BufferBindGroupState { + buffers: Vec<(Valid, RefCount, BufferUses)>, + + _phantom: PhantomData, +} +impl BufferBindGroupState { + pub fn new() -> Self { Self { - first: None, - last: BufferUses::empty(), + buffers: Vec::new(), + + _phantom: PhantomData, } } -} -impl BufferState { - pub fn with_usage(usage: BufferUses) -> Self { - Unit::new(usage) + /// Optimize the buffer bind group state by sorting it by ID. + /// + /// When this list of states is merged into a tracker, the memory + /// accesses will be in a constant assending order. + pub(crate) fn optimize(&mut self) { + self.buffers + .sort_unstable_by_key(|&(id, _, _)| id.0.unzip().0); + } + + /// Returns a list of all buffers tracked. May contain duplicates. + pub fn used(&self) -> impl Iterator> + '_ { + self.buffers.iter().map(|&(id, _, _)| id) + } + + /// Adds the given resource with the given state. + pub fn add_single<'a>( + &mut self, + storage: &'a hub::Storage, BufferId>, + id: BufferId, + state: BufferUses, + ) -> Option<&'a Buffer> { + let buffer = storage.get(id).ok()?; + + self.buffers + .push((Valid(id), buffer.life_guard.add_ref(), state)); + + Some(buffer) } } -impl ResourceState for BufferState { - type Id = BufferId; - type Selector = (); - type Usage = BufferUses; +/// Stores all buffer state within a single usage scope. +#[derive(Debug)] +pub(crate) struct BufferUsageScope { + state: Vec, + + metadata: ResourceMetadata, +} + +impl BufferUsageScope { + pub fn new() -> Self { + Self { + state: Vec::new(), + + metadata: ResourceMetadata::new(), + } + } + + fn debug_assert_in_bounds(&self, index: usize) { + debug_assert!(index < self.state.len()); + self.metadata.debug_assert_in_bounds(index); + } + + /// Sets the size of all the vectors inside the tracker. + /// + /// Must be called with the highest possible Buffer ID before + /// all unsafe functions are called. + pub fn set_size(&mut self, size: usize) { + self.state.resize(size, BufferUses::empty()); + self.metadata.set_size(size); + } + + /// Extend the vectors to let the given index be valid. + fn allow_index(&mut self, index: usize) { + if index >= self.state.len() { + self.set_size(index + 1); + } + } - fn query(&self, _selector: Self::Selector) -> Option { - Some(self.last) + /// Returns a list of all buffers tracked. + pub fn used(&self) -> impl Iterator> + '_ { + self.metadata.used() } - fn change( + /// Merge the list of buffer states in the given bind group into this usage scope. + /// + /// If any of the resulting states is invalid, stops the merge and returns a usage + /// conflict with the details of the invalid state. + /// + /// Because bind groups do not check if the union of all their states is valid, + /// this method is allowed to return Err on the first bind group bound. + /// + /// # Safety + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + pub unsafe fn merge_bind_group( &mut self, - id: Valid, - _selector: Self::Selector, - usage: Self::Usage, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let old = self.last; - if old != usage || !BufferUses::ORDERED.contains(usage) { - let pending = PendingTransition { - id, - selector: (), - usage: old..usage, - }; - *self = match output { - None => { - assert_eq!( - self.first, None, - "extending a state that is already a transition" - ); - Unit::new(pending.collapse()?) - } - Some(transitions) => { - transitions.push(pending); - Unit { - first: self.first.or(Some(old)), - last: usage, - } - } - }; + bind_group: &BufferBindGroupState, + ) -> Result<(), UsageConflict> { + for &(id, ref ref_count, state) in &bind_group.buffers { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + insert_or_merge( + None, + None, + &mut self.state, + &mut self.metadata, + index32, + index, + BufferStateProvider::Direct { state }, + ResourceMetadataProvider::Direct { + epoch, + ref_count: Cow::Borrowed(ref_count), + }, + )?; } + Ok(()) } - fn merge( - &mut self, - id: Valid, - other: &Self, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let old = self.last; - let new = other.port(); - if old == new && BufferUses::ORDERED.contains(new) { - if output.is_some() && self.first.is_none() { - *self = Unit { - first: Some(old), - last: other.last, - }; - } - } else { - let pending = PendingTransition { - id, - selector: (), - usage: old..new, - }; - *self = match output { - None => { - assert_eq!( - self.first, None, - "extending a state that is already a transition" - ); - Unit::new(pending.collapse()?) - } - Some(transitions) => { - transitions.push(pending); - Unit { - first: self.first.or(Some(old)), - last: other.last, - } - } + /// Merge the list of buffer states in the given usage scope into this UsageScope. + /// + /// If any of the resulting states is invalid, stops the merge and returns a usage + /// conflict with the details of the invalid state. + /// + /// If the given tracker uses IDs higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn merge_usage_scope(&mut self, scope: &Self) -> Result<(), UsageConflict> { + let incoming_size = scope.state.len(); + if incoming_size > self.state.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&scope.metadata.owned) { + self.debug_assert_in_bounds(index); + scope.debug_assert_in_bounds(index); + + unsafe { + insert_or_merge( + None, + None, + &mut self.state, + &mut self.metadata, + index as u32, + index, + BufferStateProvider::Indirect { + state: &scope.state, + }, + ResourceMetadataProvider::Indirect { + metadata: &scope.metadata, + }, + )?; }; } + Ok(()) } - fn optimize(&mut self) {} + /// Merge a single state into the UsageScope. + /// + /// If the resulting state is invalid, returns a usage + /// conflict with the details of the invalid state. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn merge_single<'a>( + &mut self, + storage: &'a hub::Storage, BufferId>, + id: BufferId, + new_state: BufferUses, + ) -> Result<&'a Buffer, UsageConflict> { + let buffer = storage + .get(id) + .map_err(|_| UsageConflict::BufferInvalid { id })?; + + let (index32, epoch, _) = id.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + insert_or_merge( + Some(&buffer.life_guard), + None, + &mut self.state, + &mut self.metadata, + index32, + index, + BufferStateProvider::Direct { state: new_state }, + ResourceMetadataProvider::Resource { epoch }, + )?; + } + + Ok(buffer) + } } -#[cfg(test)] -mod test { - use super::*; - use crate::id::Id; +/// Stores all buffer state within a command buffer or device. +pub(crate) struct BufferTracker { + start: Vec, + end: Vec, - #[test] - fn change_extend() { - let mut bs = Unit { - first: None, - last: BufferUses::INDEX, - }; - let id = Id::dummy(); - assert_eq!( - bs.change(id, (), BufferUses::STORAGE_WRITE, None), - Err(PendingTransition { - id, - selector: (), - usage: BufferUses::INDEX..BufferUses::STORAGE_WRITE, - }), - ); - bs.change(id, (), BufferUses::VERTEX, None).unwrap(); - bs.change(id, (), BufferUses::INDEX, None).unwrap(); - assert_eq!(bs, Unit::new(BufferUses::VERTEX | BufferUses::INDEX)); + metadata: ResourceMetadata, + + temp: Vec>, +} +impl BufferTracker { + pub fn new() -> Self { + Self { + start: Vec::new(), + end: Vec::new(), + + metadata: ResourceMetadata::new(), + + temp: Vec::new(), + } + } + + fn debug_assert_in_bounds(&self, index: usize) { + debug_assert!(index < self.start.len()); + debug_assert!(index < self.end.len()); + self.metadata.debug_assert_in_bounds(index); + } + + /// Sets the size of all the vectors inside the tracker. + /// + /// Must be called with the highest possible Buffer ID before + /// all unsafe functions are called. + pub fn set_size(&mut self, size: usize) { + self.start.resize(size, BufferUses::empty()); + self.end.resize(size, BufferUses::empty()); + + self.metadata.set_size(size); + } + + /// Extend the vectors to let the given index be valid. + fn allow_index(&mut self, index: usize) { + if index >= self.start.len() { + self.set_size(index + 1); + } + } + + /// Returns a list of all buffers tracked. + pub fn used(&self) -> impl Iterator> + '_ { + self.metadata.used() + } + + /// Drains all currently pending transitions. + pub fn drain(&mut self) -> Drain<'_, PendingTransition> { + self.temp.drain(..) + } + + /// Inserts a single buffer and its state into the resource tracker. + /// + /// If the resource already exists in the tracker, this will panic. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn insert_single(&mut self, id: Valid, ref_count: RefCount, state: BufferUses) { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + let currently_owned = self.metadata.owned.get(index).unwrap_unchecked(); + + if currently_owned { + panic!("Tried to insert buffer already tracked"); + } + + insert( + None, + Some(&mut self.start), + &mut self.end, + &mut self.metadata, + index, + BufferStateProvider::Direct { state }, + None, + ResourceMetadataProvider::Direct { + epoch, + ref_count: Cow::Owned(ref_count), + }, + ) + } } - #[test] - fn change_replace() { - let mut bs = Unit { - first: None, - last: BufferUses::STORAGE_WRITE, + /// Sets the state of a single buffer. + /// + /// If a transition is needed to get the buffer into the given state, that transition + /// is returned. No more than one transition is needed. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_single<'a>( + &mut self, + storage: &'a hub::Storage, BufferId>, + id: BufferId, + state: BufferUses, + ) -> Option<(&'a Buffer, Option>)> { + let value = storage.get(id).ok()?; + + let (index32, epoch, _) = id.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + insert_or_barrier_update( + Some(&value.life_guard), + Some(&mut self.start), + &mut self.end, + &mut self.metadata, + index32, + index, + BufferStateProvider::Direct { state }, + None, + ResourceMetadataProvider::Resource { epoch }, + &mut self.temp, + ) }; - let id = Id::dummy(); - let mut list = Vec::new(); - bs.change(id, (), BufferUses::VERTEX, Some(&mut list)) - .unwrap(); - assert_eq!( - &list, - &[PendingTransition { - id, - selector: (), - usage: BufferUses::STORAGE_WRITE..BufferUses::VERTEX, - }], - ); - assert_eq!( - bs, - Unit { - first: Some(BufferUses::STORAGE_WRITE), - last: BufferUses::VERTEX, + + debug_assert!(self.temp.len() <= 1); + + Some((value, self.temp.pop())) + } + + /// Sets the given state for all buffers in the given tracker. + /// + /// If a transition is needed to get the buffers into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_from_tracker(&mut self, tracker: &Self) { + let incoming_size = tracker.start.len(); + if incoming_size > self.start.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&tracker.metadata.owned) { + self.debug_assert_in_bounds(index); + tracker.debug_assert_in_bounds(index); + unsafe { + insert_or_barrier_update( + None, + Some(&mut self.start), + &mut self.end, + &mut self.metadata, + index as u32, + index, + BufferStateProvider::Indirect { + state: &tracker.start, + }, + Some(BufferStateProvider::Indirect { + state: &tracker.end, + }), + ResourceMetadataProvider::Indirect { + metadata: &tracker.metadata, + }, + &mut self.temp, + ) } - ); + } + } - list.clear(); - bs.change(id, (), BufferUses::STORAGE_WRITE, Some(&mut list)) - .unwrap(); - assert_eq!( - &list, - &[PendingTransition { - id, - selector: (), - usage: BufferUses::VERTEX..BufferUses::STORAGE_WRITE, - }], - ); - assert_eq!( - bs, - Unit { - first: Some(BufferUses::STORAGE_WRITE), - last: BufferUses::STORAGE_WRITE, + /// Sets the given state for all buffers in the given UsageScope. + /// + /// If a transition is needed to get the buffers into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_from_usage_scope(&mut self, scope: &BufferUsageScope) { + let incoming_size = scope.state.len(); + if incoming_size > self.start.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&scope.metadata.owned) { + self.debug_assert_in_bounds(index); + scope.debug_assert_in_bounds(index); + unsafe { + insert_or_barrier_update( + None, + Some(&mut self.start), + &mut self.end, + &mut self.metadata, + index as u32, + index, + BufferStateProvider::Indirect { + state: &scope.state, + }, + None, + ResourceMetadataProvider::Indirect { + metadata: &scope.metadata, + }, + &mut self.temp, + ) } - ); + } } - #[test] - fn merge_replace() { - let mut bs = Unit { - first: None, - last: BufferUses::empty(), - }; - let other_smooth = Unit { - first: Some(BufferUses::empty()), - last: BufferUses::COPY_DST, - }; - let id = Id::dummy(); - let mut list = Vec::new(); - bs.merge(id, &other_smooth, Some(&mut list)).unwrap(); - assert!(list.is_empty()); - assert_eq!( - bs, - Unit { - first: Some(BufferUses::empty()), - last: BufferUses::COPY_DST, + /// Iterates through all buffers in the given bind group and adopts + /// the state given for those buffers in the UsageScope. It also + /// removes all touched buffers from the usage scope. + /// + /// If a transition is needed to get the buffers into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// This is a really funky method used by Compute Passes to generate + /// barriers after a call to dispatch without needing to iterate + /// over all elements in the usage scope. We use each the + /// bind group as a source of which IDs to look at. The bind groups + /// must have first been added to the usage scope. + /// + /// # Safety + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + pub unsafe fn set_and_remove_from_usage_scope_sparse( + &mut self, + scope: &mut BufferUsageScope, + bind_group_state: &BufferBindGroupState, + ) { + let incoming_size = scope.state.len(); + if incoming_size > self.start.len() { + self.set_size(incoming_size); + } + + for &(id, ref ref_count, _) in bind_group_state.buffers.iter() { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + scope.debug_assert_in_bounds(index); + + if !scope.metadata.owned.get(index).unwrap_unchecked() { + continue; } - ); + insert_or_barrier_update( + None, + Some(&mut self.start), + &mut self.end, + &mut self.metadata, + index as u32, + index, + BufferStateProvider::Indirect { + state: &scope.state, + }, + None, + ResourceMetadataProvider::Direct { + epoch, + ref_count: Cow::Borrowed(ref_count), + }, + &mut self.temp, + ); - let other_rough = Unit { - first: Some(BufferUses::empty()), - last: BufferUses::UNIFORM, - }; - bs.merge(id, &other_rough, Some(&mut list)).unwrap(); - assert_eq!( - &list, - &[PendingTransition { - id, - selector: (), - usage: BufferUses::COPY_DST..BufferUses::empty(), - }], - ); - assert_eq!( - bs, - Unit { - first: Some(BufferUses::empty()), - last: BufferUses::UNIFORM, + scope.metadata.reset(index); + } + } + + /// Removes the given resource from the tracker iff we have the last reference to the + /// resource and the epoch matches. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + pub fn remove_abandoned(&mut self, id: Valid) -> bool { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + if index > self.metadata.owned.len() { + return false; + } + + self.debug_assert_in_bounds(index); + + unsafe { + if self.metadata.owned.get(index).unwrap_unchecked() { + let existing_epoch = self.metadata.epochs.get_unchecked_mut(index); + let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index); + + if *existing_epoch == epoch + && existing_ref_count.as_mut().unwrap_unchecked().load() == 1 + { + self.metadata.reset(index); + + return true; + } } + } + + false + } +} + +/// Source of Buffer State. +#[derive(Debug, Clone)] +enum BufferStateProvider<'a> { + /// Get a state that was provided directly. + Direct { state: BufferUses }, + /// Get a state from an an array of states. + Indirect { state: &'a [BufferUses] }, +} +impl BufferStateProvider<'_> { + /// Gets the state from the provider, given a resource ID index. + /// + /// # Safety + /// + /// Index must be in bounds for the indirect source iff this is in the indirect state. + #[inline(always)] + unsafe fn get_state(&self, index: usize) -> BufferUses { + match *self { + BufferStateProvider::Direct { state } => state, + BufferStateProvider::Indirect { state } => { + debug_assert!(index < state.len()); + *state.get_unchecked(index) + } + } + } +} + +/// Does an insertion operation if the index isn't tracked +/// in the current metadata, otherwise merges the given state +/// with the current state. If the merging would cause +/// a conflict, returns that usage conflict. +/// +/// # Safety +/// +/// Indexes must be valid indexes into all arrays passed in +/// to this function, either directly or via metadata or provider structs. +#[inline(always)] +unsafe fn insert_or_merge( + life_guard: Option<&LifeGuard>, + start_states: Option<&mut [BufferUses]>, + current_states: &mut [BufferUses], + resource_metadata: &mut ResourceMetadata, + index32: u32, + index: usize, + state_provider: BufferStateProvider<'_>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) -> Result<(), UsageConflict> { + let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked(); + + if !currently_owned { + insert( + life_guard, + start_states, + current_states, + resource_metadata, + index, + state_provider, + None, + metadata_provider, + ); + return Ok(()); + } + + merge( + current_states, + index32, + index, + state_provider, + metadata_provider, + ) +} + +/// If the resource isn't tracked +/// - Inserts the given resource. +/// - Uses the `start_state_provider` to populate `start_states` +/// - Uses either `end_state_provider` or `start_state_provider` +/// to populate `current_states`. +/// If the resource is tracked +/// - Inserts barriers from the state in `current_states` +/// to the state provided by `start_state_provider`. +/// - Updates the `current_states` with either the state from +/// `end_state_provider` or `start_state_provider`. +/// +/// Any barriers are added to the barrier vector. +/// +/// # Safety +/// +/// Indexes must be valid indexes into all arrays passed in +/// to this function, either directly or via metadata or provider structs. +#[inline(always)] +unsafe fn insert_or_barrier_update( + life_guard: Option<&LifeGuard>, + start_states: Option<&mut [BufferUses]>, + current_states: &mut [BufferUses], + resource_metadata: &mut ResourceMetadata, + index32: u32, + index: usize, + start_state_provider: BufferStateProvider<'_>, + end_state_provider: Option>, + metadata_provider: ResourceMetadataProvider<'_, A>, + barriers: &mut Vec>, +) { + let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked(); + + if !currently_owned { + insert( + life_guard, + start_states, + current_states, + resource_metadata, + index, + start_state_provider, + end_state_provider, + metadata_provider, ); + return; } + + let update_state_provider = end_state_provider.unwrap_or_else(|| start_state_provider.clone()); + barrier( + current_states, + index32, + index, + start_state_provider, + barriers, + ); + + update(current_states, index, update_state_provider); +} + +#[inline(always)] +unsafe fn insert( + life_guard: Option<&LifeGuard>, + start_states: Option<&mut [BufferUses]>, + current_states: &mut [BufferUses], + resource_metadata: &mut ResourceMetadata, + index: usize, + start_state_provider: BufferStateProvider<'_>, + end_state_provider: Option>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) { + let new_start_state = start_state_provider.get_state(index); + let new_end_state = end_state_provider.map_or(new_start_state, |p| p.get_state(index)); + + // This should only ever happen with a wgpu bug, but let's just double + // check that resource states don't have any conflicts. + debug_assert_eq!(invalid_resource_state(new_start_state), false); + debug_assert_eq!(invalid_resource_state(new_end_state), false); + + log::trace!("\tbuf {index}: insert {new_start_state:?}..{new_end_state:?}"); + + if let Some(&mut ref mut start_state) = start_states { + *start_state.get_unchecked_mut(index) = new_start_state; + } + *current_states.get_unchecked_mut(index) = new_end_state; + + let (epoch, ref_count) = metadata_provider.get_own(life_guard, index); + + resource_metadata.owned.set(index, true); + *resource_metadata.epochs.get_unchecked_mut(index) = epoch; + *resource_metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count); +} + +#[inline(always)] +unsafe fn merge( + current_states: &mut [BufferUses], + index32: u32, + index: usize, + state_provider: BufferStateProvider<'_>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) -> Result<(), UsageConflict> { + let current_state = current_states.get_unchecked_mut(index); + let new_state = state_provider.get_state(index); + + let merged_state = *current_state | new_state; + + if invalid_resource_state(merged_state) { + return Err(UsageConflict::from_buffer( + BufferId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT), + *current_state, + new_state, + )); + } + + log::trace!("\tbuf {index32}: merge {current_state:?} + {new_state:?}"); + + *current_state = merged_state; + + Ok(()) +} + +#[inline(always)] +unsafe fn barrier( + current_states: &mut [BufferUses], + index32: u32, + index: usize, + state_provider: BufferStateProvider<'_>, + barriers: &mut Vec>, +) { + let current_state = *current_states.get_unchecked(index); + let new_state = state_provider.get_state(index); + + if skip_barrier(current_state, new_state) { + return; + } + + barriers.push(PendingTransition { + id: index32, + selector: (), + usage: current_state..new_state, + }); + + log::trace!("\tbuf {index32}: transition {current_state:?} -> {new_state:?}"); +} + +#[inline(always)] +unsafe fn update( + current_states: &mut [BufferUses], + index: usize, + state_provider: BufferStateProvider<'_>, +) { + let current_state = current_states.get_unchecked_mut(index); + let new_state = state_provider.get_state(index); + + *current_state = new_state; } diff --git a/third_party/rust/wgpu-core/src/track/mod.rs b/third_party/rust/wgpu-core/src/track/mod.rs index bcf5eb4a4bf98..8d38ff8e725a1 100644 --- a/third_party/rust/wgpu-core/src/track/mod.rs +++ b/third_party/rust/wgpu-core/src/track/mod.rs @@ -1,126 +1,131 @@ +/*! Resource State and Lifetime Trackers + * + * These structures are responsible for keeping track of resource state, + * generating barriers where needed, and making sure resources are kept + * alive until the trackers die. + * + * ## General Architecture + * + * Tracking is some of the hottest code in the entire codebase, so the trackers + * are designed to be as cache efficient as possible. They store resource state + * in flat vectors, storing metadata SOA style, one vector per type of metadata. + * + * A lot of the tracker code is deeply unsafe, using unchecked accesses all over + * to make performance as good as possible. However, for all unsafe accesses, there + * is a corresponding debug assert the checks if that access is valid. This helps + * get bugs caught fast, while still letting users not need to pay for the bounds + * checks. + * + * In wgpu, resource IDs are allocated and re-used, so will always be as low + * as reasonably possible. This allows us to use the ID as an index into an array. + * + * ## Statefulness + * + * There are two main types of trackers, stateful and stateless. + * + * Stateful trackers are for buffers and textures. They both have + * resource state attached to them which needs to be used to generate + * automatic synchronization. Because of the different requirements of + * buffers and textures, they have two separate tracking structures. + * + * Stateless trackers only store metadata and own the given resource. + * + * ## Use Case + * + * Within each type of tracker, the trackers are further split into 3 different + * use cases, Bind Group, Usage Scope, and a full Tracker. + * + * Bind Group trackers are just a list of different resources, their refcount, + * and how they are used. Textures are used via a selector and a usage type. + * Buffers by just a usage type. Stateless resources don't have a usage type. + * + * Usage Scope trackers are only for stateful resources. These trackers represent + * a single [`UsageScope`] in the spec. When a use is added to a usage scope, + * it is merged with all other uses of that resource in that scope. If there + * is a usage conflict, merging will fail and an error will be reported. + * + * Full trackers represent a before and after state of a resource. These + * are used for tracking on the device and on command buffers. The before + * state represents the state the resource is first used as in the command buffer, + * the after state is the state the command buffer leaves the resource in. + * These double ended buffers can then be used to generate the needed transitions + * between command buffers. + * + * ## Dense Datastructure with Sparse Data + * + * This tracking system is based on having completely dense data, but trackers do + * not always contain every resource. Some resources (or even most resources) go + * unused in any given command buffer. So to help speed up the process of iterating + * through possibly thousands of resources, we use a bit vector to represent if + * a resource is in the buffer or not. This allows us extremely efficient memory + * utilization, as well as being able to bail out of whole blocks of 32-64 resources + * with a single usize comparison with zero. In practice this means that merging + * partially resident buffers is extremely quick. + * + * The main advantage of this dense datastructure is that we can do merging + * of trackers in an extremely efficient fashion that results in us doing linear + * scans down a couple of buffers. CPUs and their caches absolutely eat this up. + * + * ## Stateful Resource Operations + * + * All operations on stateful trackers boil down to one of four operations: + * - `insert(tracker, new_state)` adds a resource with a given state to the tracker + * for the first time. + * - `merge(tracker, new_state)` merges this new state with the previous state, checking + * for usage conflicts. + * - `barrier(tracker, new_state)` compares the given state to the existing state and + * generates the needed barriers. + * - `update(tracker, new_state)` takes the given new state and overrides the old state. + * + * This allows us to compose the operations to form the various kinds of tracker merges + * that need to happen in the codebase. For each resource in the given merger, the following + * operation applies: + * + * UsageScope <- Resource = insert(scope, usage) OR merge(scope, usage) + * UsageScope <- UsageScope = insert(scope, scope) OR merge(scope, scope) + * CommandBuffer <- UsageScope = insert(buffer.start, buffer.end, scope) OR barrier(buffer.end, scope) + update(buffer.end, scope) + * Deivce <- CommandBuffer = insert(device.start, device.end, buffer.start, buffer.end) OR barrier(device.end, buffer.start) + update(device.end, buffer.end) + * + * [`UsageScope`]: https://gpuweb.github.io/gpuweb/#programming-model-synchronization +!*/ + mod buffer; mod range; +mod stateless; mod texture; use crate::{ - hub, - id::{self, TypedId, Valid}, - resource, Epoch, FastHashMap, Index, RefCount, + binding_model, command, conv, hub, + id::{self, TypedId}, + pipeline, resource, Epoch, LifeGuard, RefCount, }; -use std::{ - collections::hash_map::Entry, fmt, marker::PhantomData, num::NonZeroU32, ops, vec::Drain, -}; +use bit_vec::BitVec; +use std::{borrow::Cow, fmt, marker::PhantomData, mem, num::NonZeroU32, ops}; use thiserror::Error; -pub(crate) use buffer::BufferState; -pub(crate) use texture::{TextureSelector, TextureState}; - -/// A single unit of state tracking. It keeps an initial -/// usage as well as the last/current one, similar to `Range`. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Unit { - first: Option, - last: U, -} - -impl Unit { - /// Create a new unit from a given usage. - fn new(usage: U) -> Self { - Self { - first: None, - last: usage, - } - } - - /// Return a usage to link to. - fn port(&self) -> U { - self.first.unwrap_or(self.last) - } -} - -/// The main trait that abstracts away the tracking logic of -/// a particular resource type, like a buffer or a texture. -pub(crate) trait ResourceState: Clone + Default { - /// Corresponding `HUB` identifier. - type Id: Copy + fmt::Debug + TypedId; - /// A type specifying the sub-resources. - type Selector: fmt::Debug; - /// Usage type for a `Unit` of a sub-resource. - type Usage: fmt::Debug; - - /// Check if all the selected sub-resources have the same - /// usage, and return it. - /// - /// Returns `None` if no sub-resources - /// are intersecting with the selector, or their usage - /// isn't consistent. - fn query(&self, selector: Self::Selector) -> Option; - - /// Change the last usage of the selected sub-resources. - /// - /// If `output` is specified, it's filled with the - /// `PendingTransition` objects corresponding to smaller - /// sub-resource transitions. The old usage is replaced by - /// the new one. - /// - /// If `output` is `None`, the old usage is extended with - /// the new usage. The error is returned if it's not possible, - /// specifying the conflicting transition. Extension can only - /// be done for read-only usages. - fn change( - &mut self, - id: Valid, - selector: Self::Selector, - usage: Self::Usage, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition>; - - /// Merge the state of this resource tracked by a different instance - /// with the current one. - /// - /// Same rules for `output` apply as with `change()`: last usage state - /// is either replaced (when `output` is provided) with a - /// `PendingTransition` pushed to this vector, or extended with the - /// other read-only usage, unless there is a usage conflict, and - /// the error is generated (returning the conflict). - fn merge( - &mut self, - id: Valid, - other: &Self, - output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition>; - - /// Try to optimize the internal representation. - fn optimize(&mut self); -} - -/// Structure wrapping the abstract tracking state with the relevant resource -/// data, such as the reference count and the epoch. -#[derive(Clone)] -struct Resource { - ref_count: RefCount, - state: S, - epoch: Epoch, -} +pub(crate) use buffer::{BufferBindGroupState, BufferTracker, BufferUsageScope}; +pub(crate) use stateless::{StatelessBindGroupSate, StatelessTracker}; +pub(crate) use texture::{ + TextureBindGroupState, TextureSelector, TextureTracker, TextureUsageScope, +}; /// A structure containing all the information about a particular resource /// transition. User code should be able to generate a pipeline barrier /// based on the contents. #[derive(Debug, PartialEq)] -pub(crate) struct PendingTransition { - pub id: Valid, +pub(crate) struct PendingTransition { + pub id: u32, pub selector: S::Selector, - pub usage: ops::Range, + pub usage: ops::Range, } -impl PendingTransition { +impl PendingTransition { /// Produce the hal barrier corresponding to the transition. pub fn into_hal<'a, A: hal::Api>( self, buf: &'a resource::Buffer, ) -> hal::BufferBarrier<'a, A> { - log::trace!("\tbuffer -> {:?}", self); let buffer = buf.raw.as_ref().expect("Buffer is destroyed"); hal::BufferBarrier { buffer, @@ -129,549 +134,611 @@ impl PendingTransition { } } -impl From> for UsageConflict { - fn from(e: PendingTransition) -> Self { - Self::Buffer { - id: e.id.0, - combined_use: e.usage.end, - } - } -} - -impl PendingTransition { +impl PendingTransition { /// Produce the hal barrier corresponding to the transition. pub fn into_hal<'a, A: hal::Api>( self, tex: &'a resource::Texture, ) -> hal::TextureBarrier<'a, A> { - log::trace!("\ttexture -> {:?}", self); let texture = tex.inner.as_raw().expect("Texture is destroyed"); + + // These showing up in a barrier is always a bug + debug_assert_ne!(self.usage.start, hal::TextureUses::UNKNOWN); + debug_assert_ne!(self.usage.end, hal::TextureUses::UNKNOWN); + + let mip_count = self.selector.mips.end - self.selector.mips.start; + debug_assert_ne!(mip_count, 0); + let layer_count = self.selector.layers.end - self.selector.layers.start; + debug_assert_ne!(layer_count, 0); + hal::TextureBarrier { texture, range: wgt::ImageSubresourceRange { aspect: wgt::TextureAspect::All, - base_mip_level: self.selector.levels.start, - mip_level_count: NonZeroU32::new( - self.selector.levels.end - self.selector.levels.start, - ), + base_mip_level: self.selector.mips.start, + mip_level_count: unsafe { Some(NonZeroU32::new_unchecked(mip_count)) }, base_array_layer: self.selector.layers.start, - array_layer_count: NonZeroU32::new( - self.selector.layers.end - self.selector.layers.start, - ), + array_layer_count: unsafe { Some(NonZeroU32::new_unchecked(layer_count)) }, }, usage: self.usage, } } } -impl From> for UsageConflict { - fn from(e: PendingTransition) -> Self { - Self::Texture { - id: e.id.0, - mip_levels: e.selector.levels.start..e.selector.levels.end, - array_layers: e.selector.layers.start..e.selector.layers.end, - combined_use: e.usage.end, - } - } -} +/// The uses that a resource or subresource can be in. +pub(crate) trait ResourceUses: + fmt::Debug + ops::BitAnd + ops::BitOr + PartialEq + Sized + Copy +{ + /// All flags that are exclusive. + const EXCLUSIVE: Self; -#[derive(Clone, Debug, Error)] -pub enum UseExtendError { - #[error("resource is invalid")] - InvalidResource, - #[error("total usage {0:?} is not valid")] - Conflict(U), + /// The relevant resource ID type. + type Id: Copy + fmt::Debug + TypedId; + /// The selector used by this resource. + type Selector: fmt::Debug; + + /// Turn the resource into a pile of bits. + fn bits(self) -> u16; + /// Returns true if the all the uses are ordered. + fn all_ordered(self) -> bool; + /// Returns true if any of the uses are exclusive. + fn any_exclusive(self) -> bool; } -/// A tracker for all resources of a given type. -pub(crate) struct ResourceTracker { - /// An association of known resource indices with their tracked states. - map: FastHashMap>, - /// Temporary storage for collecting transitions. - temp: Vec>, - /// The backend variant for all the tracked resources. - backend: wgt::Backend, +/// Returns true if the given states violates the usage scope rule +/// of any(inclusive) XOR one(exclusive) +fn invalid_resource_state(state: T) -> bool { + // Is power of two also means "is one bit set". We check for this as if + // we're in any exclusive state, we must only be in a single state. + state.any_exclusive() && !conv::is_power_of_two_u16(state.bits()) } -impl fmt::Debug for ResourceTracker { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - self.map - .iter() - .map(|(&index, res)| ((index, res.epoch), &res.state)) - .collect::>() - .fmt(formatter) - } +/// Returns true if the transition from one state to another does not require +/// a barrier. +fn skip_barrier(old_state: T, new_state: T) -> bool { + // If the state didn't change and all the usages are ordered, the hardware + // will guarentee the order of accesses, so we do not need to issue a barrier at all + old_state == new_state && old_state.all_ordered() } -#[allow( - // Explicit lifetimes are easier to reason about here. - clippy::needless_lifetimes, -)] -impl ResourceTracker { - /// Create a new empty tracker. - pub fn new(backend: wgt::Backend) -> Self { - Self { - map: FastHashMap::default(), - temp: Vec::new(), - backend, +/// Resizes the given bitvec to the given size. I'm not sure why this is hard to do but it is. +fn resize_bitvec(vec: &mut BitVec, size: usize) { + let owned_size_to_grow = size.checked_sub(vec.len()); + if let Some(delta) = owned_size_to_grow { + if delta != 0 { + vec.grow(delta, false); } + } else { + vec.truncate(size); } +} - /// Remove an id from the tracked map. - pub(crate) fn remove(&mut self, id: Valid) -> bool { - let (index, epoch, backend) = id.0.unzip(); - debug_assert_eq!(backend, self.backend); - match self.map.remove(&index) { - Some(resource) => { - assert_eq!(resource.epoch, epoch); - true - } - None => false, - } - } +/// Produces an iterator that yields the indexes of all bits that are set in the bitvec. +/// +/// Will skip entire usize's worth of bits if they are all false. +fn iterate_bitvec_indices(ownership: &BitVec) -> impl Iterator + '_ { + const BITS_PER_BLOCK: usize = mem::size_of::() * 8; + + let size = ownership.len(); + + ownership + .blocks() + .enumerate() + .filter(|&(_, word)| word != 0) + .flat_map(move |(word_index, mut word)| { + let bit_start = word_index * BITS_PER_BLOCK; + let bit_end = (bit_start + BITS_PER_BLOCK).min(size); + + (bit_start..bit_end).filter(move |_| { + let active = word & 0b1 != 0; + word >>= 1; + + active + }) + }) +} - /// Remove the resource `id`, only if `self` is holding the last reference to it. - /// - /// Return `true` if we did remove the resource; the underlying hal resource - /// is ready to be freed. - /// - /// This is generally only meaningful to apply to members of - /// [`Device::trackers`], which holds all resources allocated with that - /// [`Device`]. Other trackers should never be the final reference. - /// - /// [`Device`]: crate::device::Device - /// [`Device::trackers`]: crate::device::Device::trackers - pub(crate) fn remove_abandoned(&mut self, id: Valid) -> bool { - let (index, epoch, backend) = id.0.unzip(); - debug_assert_eq!(backend, self.backend); - match self.map.entry(index) { - // This code explicitly ignores requests for IDs that are no longer valid, - // i.e. corresponding to removed entries, or entries that got re-filled - // with new elements (having different epochs). - // This is needed because of the asynchronous nature of the device internals. - // As such, by the time a resource is added to the suspected list, it may - // already be fully removed from all the trackers (and be a stale ID). - // see https://github.com/gfx-rs/wgpu/issues/1996 - Entry::Occupied(e) => { - // see https://github.com/gfx-rs/wgpu/issues/1996 - if e.get().epoch == epoch && e.get().ref_count.load() == 1 { - e.remove(); - true - } else { - false - } - } - _ => false, +#[derive(Clone, Debug, Error, PartialEq)] +pub enum UsageConflict { + #[error("Attempted to use buffer {id:?} which is invalid.")] + BufferInvalid { id: id::BufferId }, + #[error("Attempted to use texture {id:?} which is invalid.")] + TextureInvalid { id: id::TextureId }, + #[error("Attempted to use buffer {id:?} with {invalid_use}.")] + Buffer { + id: id::BufferId, + invalid_use: InvalidUse, + }, + #[error("Attempted to use a texture {id:?} mips {mip_levels:?} layers {array_layers:?} with {invalid_use}.")] + Texture { + id: id::TextureId, + mip_levels: ops::Range, + array_layers: ops::Range, + invalid_use: InvalidUse, + }, +} +impl UsageConflict { + fn from_buffer( + id: id::BufferId, + current_state: hal::BufferUses, + new_state: hal::BufferUses, + ) -> Self { + Self::Buffer { + id, + invalid_use: InvalidUse { + current_state, + new_state, + }, } } - /// Try to optimize the internal representation. - pub(crate) fn optimize(&mut self) { - for resource in self.map.values_mut() { - resource.state.optimize(); + fn from_texture( + id: id::TextureId, + selector: TextureSelector, + current_state: hal::TextureUses, + new_state: hal::TextureUses, + ) -> Self { + Self::Texture { + id, + mip_levels: selector.mips, + array_layers: selector.layers, + invalid_use: InvalidUse { + current_state, + new_state, + }, } } +} - /// Return an iterator over used resources keys. - pub fn used<'a>(&'a self) -> impl 'a + Iterator> { - let backend = self.backend; - self.map - .iter() - .map(move |(&index, resource)| Valid(S::Id::zip(index, resource.epoch, backend))) - } +/// Pretty print helper that shows helpful descriptions of a conflicting usage. +#[derive(Clone, Debug, PartialEq)] +pub struct InvalidUse { + current_state: T, + new_state: T, +} - pub fn get_ref_count(&self, id: Valid) -> &RefCount { - let (index, _, _) = id.0.unzip(); - &self.map[&index].ref_count - } +impl fmt::Display for InvalidUse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let current = self.current_state; + let new = self.new_state; - /// Return true if there is nothing here. - pub fn is_empty(&self) -> bool { - self.map.is_empty() - } + let current_exclusive = current & T::EXCLUSIVE; + let new_exclusive = new & T::EXCLUSIVE; - /// Clear the tracked contents. - pub fn clear(&mut self) { - self.map.clear(); + let exclusive = current_exclusive | new_exclusive; + + // The text starts with "tried to use X resource with {self}" + write!( + f, + "conflicting usages. Current usage {current:?} and new usage {new:?}. \ + {exclusive:?} is an exclusive usage and cannot be used with any other\ + usages within the usage scope (renderpass or compute dispatch)" + ) } +} - /// Begin tracking a new resource `id` in state `state`. - /// - /// Hold `ref_count` in the tracker. - /// - /// Returns false if the resource is already registered. - pub(crate) fn init( - &mut self, - id: Valid, - ref_count: RefCount, - state: S, - ) -> Result<(), &S> { - let (index, epoch, backend) = id.0.unzip(); - debug_assert_eq!(backend, self.backend); - match self.map.entry(index) { - Entry::Vacant(e) => { - e.insert(Resource { - ref_count, - state, - epoch, - }); - Ok(()) - } - Entry::Occupied(e) => Err(&e.into_mut().state), +/// SOA container for storing metadata of a resource. +/// +/// This contins the ownership bitvec, the refcount of +/// the resource, and the epoch of the object's full ID. +#[derive(Debug)] +pub(crate) struct ResourceMetadata { + owned: BitVec, + ref_counts: Vec>, + epochs: Vec, + + _phantom: PhantomData, +} +impl ResourceMetadata { + pub fn new() -> Self { + Self { + owned: BitVec::default(), + ref_counts: Vec::new(), + epochs: Vec::new(), + + _phantom: PhantomData, } } - /// Query the usage of a resource selector. - /// - /// Returns `Some(Usage)` only if this usage is consistent - /// across the given selector. - #[allow(unused)] // TODO: figure out if this needs to be removed - pub fn query(&self, id: Valid, selector: S::Selector) -> Option { - let (index, epoch, backend) = id.0.unzip(); - debug_assert_eq!(backend, self.backend); - let res = self.map.get(&index)?; - assert_eq!(res.epoch, epoch); - res.state.query(selector) + pub fn set_size(&mut self, size: usize) { + self.ref_counts.resize(size, None); + self.epochs.resize(size, u32::MAX); + + resize_bitvec(&mut self.owned, size); } - /// Make sure that a resource is tracked, and return a mutable reference to it. + /// Ensures a given index is in bounds for all arrays and does + /// sanity checks of the presence of a refcount. /// - /// If the resource isn't tracked, start it in the default state, and take a - /// clone of `ref_count`. - /// - /// The `self_backend` and `map` arguments should be the `backend` and `map` - /// fields of a `ResourceTracker`. Ideally this function would just take - /// `&mut self` and access those from there, but that would upset the borrow - /// checker in some callers, who want to borrow `ResourceTracker::temp` - /// alongside our return value. The approach taken here has the caller - /// borrow both `map` and `temp`, so the borrow checker can see that they - /// don't alias. - fn get_or_insert<'a>( - self_backend: wgt::Backend, - map: &'a mut FastHashMap>, - id: Valid, - ref_count: &RefCount, - ) -> &'a mut Resource { - let (index, epoch, backend) = id.0.unzip(); - debug_assert_eq!(self_backend, backend); - match map.entry(index) { - Entry::Vacant(e) => e.insert(Resource { - ref_count: ref_count.clone(), - state: S::default(), - epoch, - }), - Entry::Occupied(e) => { - assert_eq!(e.get().epoch, epoch); - e.into_mut() - } - } - } + /// In release mode this function is completely empty and is removed. + fn debug_assert_in_bounds(&self, index: usize) { + debug_assert!(index < self.owned.len()); + debug_assert!(index < self.ref_counts.len()); + debug_assert!(index < self.epochs.len()); - /// Return a mutable reference to `id`'s state. - fn get<'a>( - self_backend: wgt::Backend, - map: &'a mut FastHashMap>, - id: Valid, - ) -> &'a mut Resource { - 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 + debug_assert!(if self.owned.get(index).unwrap() { + self.ref_counts[index].is_some() + } else { + true + }); } - /// Extend the usage of `id`, tracking it if necessary. + /// Returns true if the tracker owns no resources. /// - /// Returns conflicting transition as an error. - pub(crate) fn change_extend( - &mut self, - id: Valid, - ref_count: &RefCount, - selector: S::Selector, - usage: S::Usage, - ) -> Result<(), PendingTransition> { - Self::get_or_insert(self.backend, &mut self.map, id, ref_count) - .state - .change(id, selector, usage, None) + /// This is a O(n) operation. + fn is_empty(&self) -> bool { + !self.owned.any() } - /// Replace the usage of a specified resource. - pub(crate) fn change_replace( - &mut self, - id: Valid, - ref_count: &RefCount, - selector: S::Selector, - usage: S::Usage, - ) -> Drain> { - let res = Self::get_or_insert(self.backend, &mut self.map, id, ref_count); - res.state - .change(id, selector, usage, Some(&mut self.temp)) - .ok(); //TODO: unwrap? - self.temp.drain(..) + /// Returns ids for all resources we own. + fn used(&self) -> impl Iterator> + '_ { + if !self.owned.is_empty() { + self.debug_assert_in_bounds(self.owned.len() - 1) + }; + iterate_bitvec_indices(&self.owned).map(move |index| { + let epoch = unsafe { *self.epochs.get_unchecked(index) }; + id::Valid(Id::zip(index as u32, epoch, A::VARIANT)) + }) } - /// 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, - selector: S::Selector, - usage: S::Usage, - ) -> Drain> { - let res = Self::get(self.backend, &mut self.map, id); - res.state - .change(id, selector, usage, Some(&mut self.temp)) - .ok(); - self.temp.drain(..) + /// Resets the metadata for a given index to sane "invalid" values. + unsafe fn reset(&mut self, index: usize) { + *self.ref_counts.get_unchecked_mut(index) = None; + *self.epochs.get_unchecked_mut(index) = u32::MAX; + self.owned.set(index, false); } +} - /// Merge another tracker into `self` by extending the current states - /// without any transitions. - pub(crate) fn merge_extend(&mut self, other: &Self) -> Result<(), PendingTransition> { - debug_assert_eq!(self.backend, other.backend); - for (&index, new) in other.map.iter() { - match self.map.entry(index) { - Entry::Vacant(e) => { - e.insert(new.clone()); - } - Entry::Occupied(e) => { - 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)?; - } +/// A source of resource metadata. +/// +/// This is used to abstract over the various places +/// trackers can get new resource metadata from. +enum ResourceMetadataProvider<'a, A: hub::HalApi> { + /// Comes directly from explicit values. + Direct { + epoch: Epoch, + ref_count: Cow<'a, RefCount>, + }, + /// Comes from another metadata tracker. + Indirect { metadata: &'a ResourceMetadata }, + /// The epoch is given directly, but the life count comes from the resource itself. + Resource { epoch: Epoch }, +} +impl ResourceMetadataProvider<'_, A> { + /// Get the epoch and an owned refcount from this. + /// + /// # Safety + /// + /// - The index must be in bounds of the metadata tracker if this uses an indirect source. + /// - life_guard must be Some if this uses a Resource source. + #[inline(always)] + unsafe fn get_own(self, life_guard: Option<&LifeGuard>, index: usize) -> (Epoch, RefCount) { + match self { + ResourceMetadataProvider::Direct { epoch, ref_count } => { + (epoch, ref_count.into_owned()) + } + ResourceMetadataProvider::Indirect { metadata } => { + metadata.debug_assert_in_bounds(index); + ( + *metadata.epochs.get_unchecked(index), + metadata + .ref_counts + .get_unchecked(index) + .clone() + .unwrap_unchecked(), + ) + } + ResourceMetadataProvider::Resource { epoch } => { + debug_assert!(life_guard.is_some()); + (epoch, life_guard.unwrap_unchecked().add_ref()) } } - Ok(()) } - - /// Merge another tracker, adding it's transitions to `self`. - /// Transitions the current usage to the new one. - pub(crate) fn merge_replace<'a>(&'a mut self, other: &'a Self) -> Drain> { - for (&index, new) in other.map.iter() { - match self.map.entry(index) { - Entry::Vacant(e) => { - e.insert(new.clone()); - } - Entry::Occupied(e) => { - 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, Some(&mut self.temp)) - .ok(); //TODO: unwrap? - } + /// Get the epoch from this. + /// + /// # Safety + /// + /// - The index must be in bounds of the metadata tracker if this uses an indirect source. + #[inline(always)] + unsafe fn get_epoch(self, index: usize) -> Epoch { + match self { + ResourceMetadataProvider::Direct { epoch, .. } + | ResourceMetadataProvider::Resource { epoch, .. } => epoch, + ResourceMetadataProvider::Indirect { metadata } => { + metadata.debug_assert_in_bounds(index); + *metadata.epochs.get_unchecked(index) } } - self.temp.drain(..) } +} - /// Use a given resource provided by an `Id` with the specified usage. - /// Combines storage access by 'Id' with the transition that extends - /// the last read-only usage, if possible. - /// - /// Returns the old usage as an error if there is a conflict. - pub(crate) fn use_extend<'a, T: 'a + hub::Resource>( - &mut self, - storage: &'a hub::Storage, - id: S::Id, - selector: S::Selector, - usage: S::Usage, - ) -> Result<&'a T, UseExtendError> { - let item = storage - .get(id) - .map_err(|_| UseExtendError::InvalidResource)?; - self.change_extend( - Valid(id), - item.life_guard().ref_count.as_ref().unwrap(), - selector, - usage, - ) - .map(|()| item) - .map_err(|pending| UseExtendError::Conflict(pending.usage.end)) +/// All the usages that a bind group contains. The uses are not deduplicated in any way +/// and may include conflicting uses. This is fully compliant by the WebGPU spec. +/// +/// All bind group states are sorted by their ID so that when adding to a tracker, +/// they are added in the most efficient order possible (assending order). +pub(crate) struct BindGroupStates { + pub buffers: BufferBindGroupState, + pub textures: TextureBindGroupState, + pub views: StatelessBindGroupSate, id::TextureViewId>, + pub samplers: StatelessBindGroupSate, id::SamplerId>, +} + +impl BindGroupStates { + pub fn new() -> Self { + Self { + buffers: BufferBindGroupState::new(), + textures: TextureBindGroupState::new(), + views: StatelessBindGroupSate::new(), + samplers: StatelessBindGroupSate::new(), + } } - /// Use a given resource provided by an `Id` with the specified usage. - /// Combines storage access by 'Id' with the transition that replaces - /// the last usage with a new one, returning an iterator over these - /// transitions. - pub(crate) fn use_replace<'a, T: 'a + hub::Resource>( - &mut self, - storage: &'a hub::Storage, - id: S::Id, - selector: S::Selector, - usage: S::Usage, - ) -> Result<(&'a T, Drain>), S::Id> { - let item = storage.get(id).map_err(|_| id)?; - let drain = self.change_replace( - Valid(id), - item.life_guard().ref_count.as_ref().unwrap(), - selector, - usage, - ); - Ok((item, drain)) + /// Optimize the bind group states by sorting them by ID. + /// + /// When this list of states is merged into a tracker, the memory + /// accesses will be in a constant assending order. + pub fn optimize(&mut self) { + self.buffers.optimize(); + self.textures.optimize(); + self.views.optimize(); + self.samplers.optimize(); } } -impl ResourceState for PhantomData { - type Id = I; - type Selector = (); - type Usage = (); - - fn query(&self, _selector: Self::Selector) -> Option { - Some(()) - } +/// This is a render bundle specific usage scope. It includes stateless resources +/// that are not normally included in a usage scope, but are used by render bundles +/// and need to be owned by the render bundles. +pub(crate) struct RenderBundleScope { + pub buffers: BufferUsageScope, + pub textures: TextureUsageScope, + // Don't need to track views and samplers, they are never used directly, only by bind groups. + pub bind_groups: StatelessTracker, id::BindGroupId>, + pub render_pipelines: StatelessTracker, id::RenderPipelineId>, + pub query_sets: StatelessTracker, id::QuerySetId>, +} - fn change( +impl RenderBundleScope { + /// Create the render bundle scope and pull the maximum IDs from the hubs. + pub fn new( + buffers: &hub::Storage, id::BufferId>, + textures: &hub::Storage, id::TextureId>, + bind_groups: &hub::Storage, id::BindGroupId>, + render_pipelines: &hub::Storage, id::RenderPipelineId>, + query_sets: &hub::Storage, id::QuerySetId>, + ) -> Self { + let mut value = Self { + buffers: BufferUsageScope::new(), + textures: TextureUsageScope::new(), + bind_groups: StatelessTracker::new(), + render_pipelines: StatelessTracker::new(), + query_sets: StatelessTracker::new(), + }; + + value.buffers.set_size(buffers.len()); + value.textures.set_size(textures.len()); + value.bind_groups.set_size(bind_groups.len()); + value.render_pipelines.set_size(render_pipelines.len()); + value.query_sets.set_size(query_sets.len()); + + value + } + + /// Merge the inner contents of a bind group into the render bundle tracker. + /// + /// Only stateful things are merged in here, all other resources are owned + /// indirectly by the bind group. + /// + /// # Safety + /// + /// The maximum ID given by each bind group resource must be less than the + /// length of the storage given at the call to `new`. + pub unsafe fn merge_bind_group( &mut self, - _id: Valid, - _selector: Self::Selector, - _usage: Self::Usage, - _output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - Ok(()) - } + textures: &hub::Storage, id::TextureId>, + bind_group: &BindGroupStates, + ) -> Result<(), UsageConflict> { + self.buffers.merge_bind_group(&bind_group.buffers)?; + self.textures + .merge_bind_group(textures, &bind_group.textures)?; - fn merge( - &mut self, - _id: Valid, - _other: &Self, - _output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { Ok(()) } - - fn optimize(&mut self) {} } -pub const DUMMY_SELECTOR: () = (); - -#[derive(Clone, Debug, Error)] -pub enum UsageConflict { - #[error( - "Attempted to use buffer {id:?} as a combination of {combined_use:?} within a usage scope." - )] - Buffer { - id: id::BufferId, - combined_use: hal::BufferUses, - }, - #[error("Attempted to use texture {id:?} mips {mip_levels:?} layers {array_layers:?} as a combination of {combined_use:?} within a usage scope.")] - Texture { - id: id::TextureId, - mip_levels: ops::Range, - array_layers: ops::Range, - combined_use: hal::TextureUses, - }, -} - -/// A set of trackers for all relevant resources. -/// -/// `Device` uses this to track all resources allocated from that device. -/// Resources like `BindGroup`, `CommandBuffer`, and so on that may own a -/// variety of other resources also use a value of this type to keep track of -/// everything they're depending on. +/// A usage scope tracker. Only needs to store stateful resources as stateless +/// resources cannot possibly have a usage conflict. #[derive(Debug)] -pub(crate) struct TrackerSet { - pub buffers: ResourceTracker, - pub textures: ResourceTracker, - pub views: ResourceTracker>, - pub bind_groups: ResourceTracker>, - pub samplers: ResourceTracker>, - pub compute_pipes: ResourceTracker>, - pub render_pipes: ResourceTracker>, - pub bundles: ResourceTracker>, - pub query_sets: ResourceTracker>, +pub(crate) struct UsageScope { + pub buffers: BufferUsageScope, + pub textures: TextureUsageScope, } -impl TrackerSet { - /// Create an empty set. - pub fn new(backend: wgt::Backend) -> Self { - Self { - buffers: ResourceTracker::new(backend), - textures: ResourceTracker::new(backend), - views: ResourceTracker::new(backend), - bind_groups: ResourceTracker::new(backend), - samplers: ResourceTracker::new(backend), - compute_pipes: ResourceTracker::new(backend), - render_pipes: ResourceTracker::new(backend), - bundles: ResourceTracker::new(backend), - query_sets: ResourceTracker::new(backend), - } - } +impl UsageScope { + /// Create the render bundle scope and pull the maximum IDs from the hubs. + pub fn new( + buffers: &hub::Storage, id::BufferId>, + textures: &hub::Storage, id::TextureId>, + ) -> Self { + let mut value = Self { + buffers: BufferUsageScope::new(), + textures: TextureUsageScope::new(), + }; - /// Clear all the trackers. - pub fn _clear(&mut self) { - self.buffers.clear(); - self.textures.clear(); - self.views.clear(); - self.bind_groups.clear(); - self.samplers.clear(); - self.compute_pipes.clear(); - self.render_pipes.clear(); - self.bundles.clear(); - self.query_sets.clear(); - } + value.buffers.set_size(buffers.len()); + value.textures.set_size(textures.len()); - /// Try to optimize the tracking representation. - pub fn optimize(&mut self) { - self.buffers.optimize(); - self.textures.optimize(); - self.views.optimize(); - self.bind_groups.optimize(); - self.samplers.optimize(); - self.compute_pipes.optimize(); - self.render_pipes.optimize(); - self.bundles.optimize(); - self.query_sets.optimize(); + value } - /// Merge only the stateful trackers of another instance by extending - /// the usage. Returns a conflict if any. - pub fn merge_extend_stateful(&mut self, other: &Self) -> Result<(), UsageConflict> { - self.buffers.merge_extend(&other.buffers)?; - self.textures.merge_extend(&other.textures)?; + /// Merge the inner contents of a bind group into the usage scope. + /// + /// Only stateful things are merged in here, all other resources are owned + /// indirectly by the bind group. + /// + /// # Safety + /// + /// The maximum ID given by each bind group resource must be less than the + /// length of the storage given at the call to `new`. + pub unsafe fn merge_bind_group( + &mut self, + textures: &hub::Storage, id::TextureId>, + bind_group: &BindGroupStates, + ) -> Result<(), UsageConflict> { + self.buffers.merge_bind_group(&bind_group.buffers)?; + self.textures + .merge_bind_group(textures, &bind_group.textures)?; + Ok(()) } - pub fn backend(&self) -> wgt::Backend { - self.buffers.backend + /// Merge the inner contents of a bind group into the usage scope. + /// + /// Only stateful things are merged in here, all other resources are owned + /// indirectly by a bind group or are merged directly into the command buffer tracker. + /// + /// # Safety + /// + /// The maximum ID given by each bind group resource must be less than the + /// length of the storage given at the call to `new`. + pub unsafe fn merge_render_bundle( + &mut self, + textures: &hub::Storage, id::TextureId>, + render_bundle: &RenderBundleScope, + ) -> Result<(), UsageConflict> { + self.buffers.merge_usage_scope(&render_bundle.buffers)?; + self.textures + .merge_usage_scope(textures, &render_bundle.textures)?; + + Ok(()) } } -#[derive(Debug)] -pub(crate) struct StatefulTrackerSubset { - pub buffers: ResourceTracker, - pub textures: ResourceTracker, +/// A full double sided tracker used by CommandBuffers and the Device. +pub(crate) struct Tracker { + pub buffers: BufferTracker, + pub textures: TextureTracker, + pub views: StatelessTracker, id::TextureViewId>, + pub samplers: StatelessTracker, id::SamplerId>, + pub bind_groups: StatelessTracker, id::BindGroupId>, + pub compute_pipelines: StatelessTracker, id::ComputePipelineId>, + pub render_pipelines: StatelessTracker, id::RenderPipelineId>, + pub bundles: StatelessTracker, id::RenderBundleId>, + pub query_sets: StatelessTracker, id::QuerySetId>, } -impl StatefulTrackerSubset { - /// Create an empty set. - pub fn new(backend: wgt::Backend) -> Self { +impl Tracker { + pub fn new() -> Self { Self { - buffers: ResourceTracker::new(backend), - textures: ResourceTracker::new(backend), + buffers: BufferTracker::new(), + textures: TextureTracker::new(), + views: StatelessTracker::new(), + samplers: StatelessTracker::new(), + bind_groups: StatelessTracker::new(), + compute_pipelines: StatelessTracker::new(), + render_pipelines: StatelessTracker::new(), + bundles: StatelessTracker::new(), + query_sets: StatelessTracker::new(), } } - /// Clear all the trackers. - pub fn clear(&mut self) { - self.buffers.clear(); - self.textures.clear(); + /// Pull the maximum IDs from the hubs. + pub fn set_size( + &mut self, + buffers: Option<&hub::Storage, id::BufferId>>, + textures: Option<&hub::Storage, id::TextureId>>, + views: Option<&hub::Storage, id::TextureViewId>>, + samplers: Option<&hub::Storage, id::SamplerId>>, + bind_groups: Option<&hub::Storage, id::BindGroupId>>, + compute_pipelines: Option< + &hub::Storage, id::ComputePipelineId>, + >, + render_pipelines: Option<&hub::Storage, id::RenderPipelineId>>, + bundles: Option<&hub::Storage, id::RenderBundleId>>, + query_sets: Option<&hub::Storage, id::QuerySetId>>, + ) { + if let Some(buffers) = buffers { + self.buffers.set_size(buffers.len()); + }; + if let Some(textures) = textures { + self.textures.set_size(textures.len()); + }; + if let Some(views) = views { + self.views.set_size(views.len()); + }; + if let Some(samplers) = samplers { + self.samplers.set_size(samplers.len()); + }; + if let Some(bind_groups) = bind_groups { + self.bind_groups.set_size(bind_groups.len()); + }; + if let Some(compute_pipelines) = compute_pipelines { + self.compute_pipelines.set_size(compute_pipelines.len()); + } + if let Some(render_pipelines) = render_pipelines { + self.render_pipelines.set_size(render_pipelines.len()); + }; + if let Some(bundles) = bundles { + self.bundles.set_size(bundles.len()); + }; + if let Some(query_sets) = query_sets { + self.query_sets.set_size(query_sets.len()); + }; + } + + /// Iterates through all resources in the given bind group and adopts + /// the state given for those resources in the UsageScope. It also + /// removes all touched resources from the usage scope. + /// + /// If a transition is needed to get the resources into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// This is a really funky method used by Compute Passes to generate + /// barriers after a call to dispatch without needing to iterate + /// over all elements in the usage scope. We use each the + /// bind group as a source of which IDs to look at. The bind groups + /// must have first been added to the usage scope. + /// + /// Only stateful things are merged in here, all other resources are owned + /// indirectly by the bind group. + /// + /// # Safety + /// + /// The maximum ID given by each bind group resource must be less than the + /// value given to `set_size` + pub unsafe fn set_and_remove_from_usage_scope_sparse( + &mut self, + textures: &hub::Storage, id::TextureId>, + scope: &mut UsageScope, + bind_group: &BindGroupStates, + ) { + self.buffers + .set_and_remove_from_usage_scope_sparse(&mut scope.buffers, &bind_group.buffers); + self.textures.set_and_remove_from_usage_scope_sparse( + textures, + &mut scope.textures, + &bind_group.textures, + ); } - /// Merge all the trackers of another tracker the usage. - pub fn merge_extend(&mut self, other: &TrackerSet) -> Result<(), UsageConflict> { - self.buffers.merge_extend(&other.buffers)?; - self.textures.merge_extend(&other.textures)?; + /// Tracks the stateless resources from the given renderbundle. It is expected + /// that the stateful resources will get merged into a usage scope first. + /// + /// # Safety + /// + /// The maximum ID given by each bind group resource must be less than the + /// value given to `set_size` + pub unsafe fn add_from_render_bundle( + &mut self, + render_bundle: &RenderBundleScope, + ) -> Result<(), UsageConflict> { + self.bind_groups + .add_from_tracker(&render_bundle.bind_groups); + self.render_pipelines + .add_from_tracker(&render_bundle.render_pipelines); + self.query_sets.add_from_tracker(&render_bundle.query_sets); + Ok(()) } } diff --git a/third_party/rust/wgpu-core/src/track/range.rs b/third_party/rust/wgpu-core/src/track/range.rs index c2238439db5c8..12c527fb2aafb 100644 --- a/third_party/rust/wgpu-core/src/track/range.rs +++ b/third_party/rust/wgpu-core/src/track/range.rs @@ -2,25 +2,19 @@ //TODO: consider getting rid of it. use smallvec::SmallVec; -use std::{cmp::Ordering, fmt::Debug, iter, ops::Range, slice::Iter}; +use std::{fmt::Debug, iter, ops::Range}; /// Structure that keeps track of a I -> T mapping, /// optimized for a case where keys of the same values /// are often grouped together linearly. #[derive(Clone, Debug, PartialEq)] -pub struct RangedStates { +pub(crate) struct RangedStates { /// List of ranges, each associated with a singe value. /// Ranges of keys have to be non-intersecting and ordered. ranges: SmallVec<[(Range, T); 1]>, } -impl RangedStates { - pub fn empty() -> Self { - Self { - ranges: SmallVec::new(), - } - } - +impl RangedStates { pub fn from_range(range: Range, value: T) -> Self { Self { ranges: iter::once((range, value)).collect(), @@ -35,20 +29,12 @@ impl RangedStates { } } - /// Clear all the ranges. - pub fn clear(&mut self) { - self.ranges.clear(); + pub fn iter(&self) -> impl Iterator, T)> + Clone { + self.ranges.iter() } - /// Append a range. - /// - /// Assumes that the object is being constructed from a set of - /// ranges, and they are given in the ascending order of their keys. - pub fn append(&mut self, index: Range, value: T) { - if let Some(last) = self.ranges.last() { - debug_assert!(last.0.end <= index.start); - } - self.ranges.push((index, value)); + pub fn iter_mut(&mut self) -> impl Iterator, T)> { + self.ranges.iter_mut() } /// Check that all the ranges are non-intersecting and ordered. @@ -64,7 +50,6 @@ impl RangedStates { } /// Merge the neighboring ranges together, where possible. - #[allow(clippy::suspicious_operation_groupings)] pub fn coalesce(&mut self) { let mut num_removed = 0; let mut iter = self.ranges.iter_mut(); @@ -86,25 +71,18 @@ impl RangedStates { } } - /// Check if all intersecting ranges have the same value, which is returned. - /// - /// Returns `None` if no intersections are detected. - /// Returns `Some(Err)` if the intersected values are inconsistent. - pub fn query( - &self, - index: &Range, - fun: impl Fn(&T) -> U, - ) -> Option> { - let mut result = None; - for &(ref range, ref value) in self.ranges.iter() { - if range.end > index.start && range.start < index.end { - let old = result.replace(fun(value)); - if old.is_some() && old != result { - return Some(Err(())); - } - } - } - result.map(Ok) + pub fn iter_filter<'a>( + &'a self, + range: &'a Range, + ) -> impl Iterator, &T)> + 'a { + self.ranges + .iter() + .filter(move |&&(ref inner, ..)| inner.end > range.start && inner.start < range.end) + .map(move |&(ref inner, ref v)| { + let new_range = inner.start.max(range.start)..inner.end.min(range.end); + + (new_range, v) + }) } /// Split the storage ranges in such a way that there is a linear subset of @@ -176,112 +154,12 @@ impl RangedStates { clone.check_sanity(); result } - - /// Produce an iterator that merges two instances together. - /// - /// Each range in the returned iterator is a subset of a range in either - /// `self` or `other`, and the value returned as a `Range` from `self` to `other`. - pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> { - Merge { - base, - sa: self.ranges.iter().peekable(), - sb: other.ranges.iter().peekable(), - } - } -} - -/// A custom iterator that goes through two `RangedStates` and process a merge. -#[derive(Debug)] -pub struct Merge<'a, I, T> { - base: I, - sa: iter::Peekable, T)>>, - sb: iter::Peekable, T)>>, -} - -impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> { - type Item = (Range, Range>); - fn next(&mut self) -> Option { - match (self.sa.peek(), self.sb.peek()) { - // we have both streams - (Some(&&(ref ra, va)), Some(&&(ref rb, vb))) => { - let (range, usage) = if ra.start < self.base { - // in the middle of the left stream - let (end, end_value) = if self.base == rb.start { - // right stream is starting - debug_assert!(self.base < ra.end); - (rb.end, Some(vb)) - } else { - // right hasn't started yet - debug_assert!(self.base < rb.start); - (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 - let (end, start_value) = if self.base == ra.start { - // left stream is starting - debug_assert!(self.base < rb.end); - (ra.end, Some(va)) - } else { - // left hasn't started yet - debug_assert!(self.base < ra.start); - (ra.start, None) - }; - (self.base..rb.end.min(end), start_value..Some(vb)) - } else { - // no active streams - match ra.start.cmp(&rb.start) { - // both are starting - Ordering::Equal => (ra.start..ra.end.min(rb.end), Some(va)..Some(vb)), - // only left is starting - Ordering::Less => (ra.start..rb.start.min(ra.end), Some(va)..None), - // only right is starting - Ordering::Greater => (rb.start..ra.start.min(rb.end), None..Some(vb)), - } - }; - self.base = range.end; - if ra.end == range.end { - let _ = self.sa.next(); - } - if rb.end == range.end { - let _ = self.sb.next(); - } - Some((range, usage)) - } - // only right stream - (None, Some(&&(ref rb, vb))) => { - let range = self.base.max(rb.start)..rb.end; - self.base = rb.end; - let _ = self.sb.next(); - Some((range, None..Some(vb))) - } - // only left stream - (Some(&&(ref ra, va)), None) => { - let range = self.base.max(ra.start)..ra.end; - self.base = ra.end; - let _ = self.sa.next(); - Some((range, Some(va)..None)) - } - // done - (None, None) => None, - } - } } #[cfg(test)] mod test { //TODO: randomized/fuzzy testing use super::RangedStates; - use std::{fmt::Debug, ops::Range}; - - fn easy_merge( - ra: &[(Range, T)], - rb: &[(Range, T)], - ) -> Vec<(Range, Range>)> { - RangedStates::from_slice(ra) - .merge(&RangedStates::from_slice(rb), 0) - .collect() - } #[test] fn sane_good() { @@ -311,14 +189,6 @@ mod test { assert_eq!(rs.ranges.as_slice(), &[(1..5, 9), (5..7, 1), (8..9, 1),]); } - #[test] - fn query() { - let rs = RangedStates::from_slice(&[(1..4, 1u8), (5..7, 2)]); - assert_eq!(rs.query(&(0..1), |v| *v), None); - assert_eq!(rs.query(&(1..3), |v| *v), Some(Ok(1))); - assert_eq!(rs.query(&(1..6), |v| *v), Some(Err(()))); - } - #[test] fn isolate() { let rs = RangedStates::from_slice(&[(1..4, 9u8), (4..5, 9), (5..7, 1), (8..9, 1)]); @@ -333,104 +203,4 @@ mod test { &[(6..7, 1), (7..8, 0), (8..9, 1),] ); } - - #[test] - fn merge_same() { - assert_eq!( - &easy_merge(&[(1..4, 0u8),], &[(1..4, 2u8),],), - &[(1..4, Some(0)..Some(2)),] - ); - } - - #[test] - fn merge_empty() { - assert_eq!( - &easy_merge(&[(1..2, 0u8),], &[],), - &[(1..2, Some(0)..None),] - ); - assert_eq!( - &easy_merge(&[], &[(3..4, 1u8),],), - &[(3..4, None..Some(1)),] - ); - } - - #[test] - fn merge_separate() { - assert_eq!( - &easy_merge(&[(1..2, 0u8), (5..6, 1u8),], &[(2..4, 2u8),],), - &[ - (1..2, Some(0)..None), - (2..4, None..Some(2)), - (5..6, Some(1)..None), - ] - ); - } - - #[test] - fn merge_subset() { - assert_eq!( - &easy_merge(&[(1..6, 0u8),], &[(2..4, 2u8),],), - &[ - (1..2, Some(0)..None), - (2..4, Some(0)..Some(2)), - (4..6, Some(0)..None), - ] - ); - assert_eq!( - &easy_merge(&[(2..4, 0u8),], &[(1..4, 2u8),],), - &[(1..2, None..Some(2)), (2..4, Some(0)..Some(2)),] - ); - } - - #[test] - fn merge_all() { - assert_eq!( - &easy_merge(&[(1..4, 0u8), (5..8, 1u8),], &[(2..6, 2u8), (7..9, 3u8),],), - &[ - (1..2, Some(0)..None), - (2..4, Some(0)..Some(2)), - (4..5, None..Some(2)), - (5..6, Some(1)..Some(2)), - (6..7, Some(1)..None), - (7..8, Some(1)..Some(3)), - (8..9, None..Some(3)), - ] - ); - } - - #[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), - ] - ); - } } diff --git a/third_party/rust/wgpu-core/src/track/stateless.rs b/third_party/rust/wgpu-core/src/track/stateless.rs new file mode 100644 index 0000000000000..a8051bb3ceb46 --- /dev/null +++ b/third_party/rust/wgpu-core/src/track/stateless.rs @@ -0,0 +1,209 @@ +/*! Stateless Trackers + * + * Stateless trackers don't have any state, so make no + * distinction between a usage scope and a full tracker. +!*/ + +use std::marker::PhantomData; + +use crate::{ + hub, + id::{TypedId, Valid}, + track::{iterate_bitvec_indices, ResourceMetadata}, + RefCount, +}; + +/// Stores all the resources that a bind group stores. +pub(crate) struct StatelessBindGroupSate { + resources: Vec<(Valid, RefCount)>, + + _phantom: PhantomData, +} + +impl StatelessBindGroupSate { + pub fn new() -> Self { + Self { + resources: Vec::new(), + + _phantom: PhantomData, + } + } + + /// Optimize the buffer bind group state by sorting it by ID. + /// + /// When this list of states is merged into a tracker, the memory + /// accesses will be in a constant assending order. + pub(crate) fn optimize(&mut self) { + self.resources + .sort_unstable_by_key(|&(id, _)| id.0.unzip().0); + } + + /// Returns a list of all resources tracked. May contain duplicates. + pub fn used(&self) -> impl Iterator> + '_ { + self.resources.iter().map(|&(id, _)| id) + } + + /// Adds the given resource. + pub fn add_single<'a>(&mut self, storage: &'a hub::Storage, id: Id) -> Option<&'a T> { + let resource = storage.get(id).ok()?; + + self.resources + .push((Valid(id), resource.life_guard().add_ref())); + + Some(resource) + } +} + +/// Stores all resource state within a command buffer or device. +pub(crate) struct StatelessTracker { + metadata: ResourceMetadata, + + _phantom: PhantomData<(T, Id)>, +} + +impl StatelessTracker { + pub fn new() -> Self { + Self { + metadata: ResourceMetadata::new(), + + _phantom: PhantomData, + } + } + + fn debug_assert_in_bounds(&self, index: usize) { + self.metadata.debug_assert_in_bounds(index); + } + + /// Sets the size of all the vectors inside the tracker. + /// + /// Must be called with the highest possible Resource ID of this type + /// before all unsafe functions are called. + pub fn set_size(&mut self, size: usize) { + self.metadata.set_size(size); + } + + /// Extend the vectors to let the given index be valid. + fn allow_index(&mut self, index: usize) { + if index >= self.metadata.owned.len() { + self.set_size(index + 1); + } + } + + /// Returns a list of all resources tracked. + pub fn used(&self) -> impl Iterator> + '_ { + self.metadata.used() + } + + /// Inserts a single resource into the resource tracker. + /// + /// If the resource already exists in the tracker, it will be overwritten. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn insert_single(&mut self, id: Valid, ref_count: RefCount) { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + *self.metadata.epochs.get_unchecked_mut(index) = epoch; + *self.metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count); + self.metadata.owned.set(index, true); + } + } + + /// Adds the given resource to the tracker. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn add_single<'a>(&mut self, storage: &'a hub::Storage, id: Id) -> Option<&'a T> { + let item = storage.get(id).ok()?; + + let (index32, epoch, _) = id.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + *self.metadata.epochs.get_unchecked_mut(index) = epoch; + *self.metadata.ref_counts.get_unchecked_mut(index) = Some(item.life_guard().add_ref()); + self.metadata.owned.set(index, true); + } + + Some(item) + } + + /// Adds the given resources from the given tracker. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn add_from_tracker(&mut self, other: &Self) { + let incoming_size = other.metadata.owned.len(); + if incoming_size > self.metadata.owned.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&other.metadata.owned) { + self.debug_assert_in_bounds(index); + other.debug_assert_in_bounds(index); + unsafe { + let previously_owned = self.metadata.owned.get(index).unwrap_unchecked(); + + if !previously_owned { + self.metadata.owned.set(index, true); + + let other_ref_count = other + .metadata + .ref_counts + .get_unchecked(index) + .clone() + .unwrap_unchecked(); + *self.metadata.ref_counts.get_unchecked_mut(index) = Some(other_ref_count); + + let epoch = *other.metadata.epochs.get_unchecked(index); + *self.metadata.epochs.get_unchecked_mut(index) = epoch; + } + } + } + } + + /// Removes the given resource from the tracker iff we have the last reference to the + /// resource and the epoch matches. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + pub fn remove_abandoned(&mut self, id: Valid) -> bool { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + if index > self.metadata.owned.len() { + return false; + } + + self.debug_assert_in_bounds(index); + + unsafe { + if self.metadata.owned.get(index).unwrap_unchecked() { + let existing_epoch = self.metadata.epochs.get_unchecked_mut(index); + let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index); + + if *existing_epoch == epoch + && existing_ref_count.as_mut().unwrap_unchecked().load() == 1 + { + self.metadata.reset(index); + + return true; + } + } + } + + false + } +} diff --git a/third_party/rust/wgpu-core/src/track/texture.rs b/third_party/rust/wgpu-core/src/track/texture.rs index c519ee07a528a..0f157c8f15983 100644 --- a/third_party/rust/wgpu-core/src/track/texture.rs +++ b/third_party/rust/wgpu-core/src/track/texture.rs @@ -1,425 +1,1513 @@ -use super::{range::RangedStates, PendingTransition, ResourceState, Unit}; -use crate::id::{TextureId, Valid}; +/*! Texture Trackers + * + * Texture trackers are signifigantly more complicated than + * the buffer trackers because textures can be in a "complex" + * state where each individual subresource can potentially be + * in a different state from every other subtresource. These + * complex states are stored seperately from the simple states + * because they are signifignatly more difficult to track and + * most resources spend the vast majority of their lives in + * simple states. + * + * There are two special texture usages: `UNKNOWN` and `UNINITIALIZED`. + * - `UNKNOWN` is only used in complex states and is used to signify + * that the complex state does not know anything about those subresources. + * It cannot leak into transitions, it is invalid to transition into UNKNOWN + * state. + * - `UNINITIALIZED` is used in both simple and complex states to mean the texture + * is known to be in some undefined state. Any transition away from UNINITIALIZED + * will treat the contents as junk. +!*/ + +use super::{range::RangedStates, PendingTransition}; +use crate::{ + hub, + id::{TextureId, TypedId, Valid}, + resource::Texture, + track::{ + invalid_resource_state, iterate_bitvec_indices, skip_barrier, ResourceMetadata, + ResourceMetadataProvider, ResourceUses, UsageConflict, + }, + LifeGuard, RefCount, +}; use hal::TextureUses; use arrayvec::ArrayVec; +use naga::FastHashMap; -use std::{iter, ops::Range}; - -type PlaneStates = RangedStates>; +use std::{borrow::Cow, iter, marker::PhantomData, ops::Range, vec::Drain}; +/// Specifies a particular set of subresources in a texture. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TextureSelector { - //TODO: rename to `mip_levels` and `array_layers` for consistency - //pub aspects: hal::FormatAspects, - pub levels: Range, + pub mips: Range, pub layers: Range, } -#[derive(Clone, Debug, Default, PartialEq)] -pub(crate) struct TextureState { - mips: ArrayVec, - /// True if we have the information about all the subresources here - full: bool, -} +impl ResourceUses for TextureUses { + const EXCLUSIVE: Self = Self::EXCLUSIVE; -impl PendingTransition { - fn collapse(self) -> Result { - if self.usage.start.is_empty() - || self.usage.start == self.usage.end - || !TextureUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end) - { - Ok(self.usage.start | self.usage.end) - } else { - Err(self) - } + type Id = TextureId; + type Selector = TextureSelector; + + fn bits(self) -> u16 { + Self::bits(&self) + } + + fn all_ordered(self) -> bool { + Self::ORDERED.contains(self) + } + + fn any_exclusive(self) -> bool { + self.intersects(Self::EXCLUSIVE) } } -impl TextureState { - pub fn new(mip_level_count: u32, array_layer_count: u32) -> Self { +/// Represents the complex state of textures where every subresource is potentially +/// in a different state. +#[derive(Clone, Debug, Default, PartialEq)] +struct ComplexTextureState { + mips: ArrayVec, { hal::MAX_MIP_LEVELS as usize }>, +} + +impl ComplexTextureState { + /// Creates complex texture state for the given sizes. + /// + /// This state will be initialized with the UNKNOWN state, a special state + /// which means the trakcer knows nothing about the state. + fn new(mip_level_count: u32, array_layer_count: u32) -> Self { Self { mips: iter::repeat_with(|| { - PlaneStates::from_range(0..array_layer_count, Unit::new(TextureUses::UNINITIALIZED)) + RangedStates::from_range(0..array_layer_count, TextureUses::UNKNOWN) }) .take(mip_level_count as usize) .collect(), - full: true, } } -} -impl ResourceState for TextureState { - type Id = TextureId; - type Selector = TextureSelector; - type Usage = TextureUses; - - fn query(&self, selector: Self::Selector) -> Option { - let mut result = None; - // Note: we only consider the subresources tracked by `self`. - // If some are not known to `self`, it means the can assume the - // initial state to whatever we need, which we can always make - // to be the same as the query result for the known subresources. - let num_levels = self.mips.len(); - if self.full { - assert!(num_levels >= selector.levels.end as usize); - } - let mip_start = num_levels.min(selector.levels.start as usize); - let mip_end = num_levels.min(selector.levels.end as usize); - for mip in self.mips[mip_start..mip_end].iter() { - match mip.query(&selector.layers, |unit| unit.last) { - None => {} - Some(Ok(usage)) if result == Some(usage) => {} - Some(Ok(usage)) if result.is_none() => { - result = Some(usage); + /// Initialize a complex state from a selector representing the full size of the texture + /// and an iterator of a selector and a texture use, specifying a usage for a specific + /// set of subresources. + /// + /// [`Self::to_selector_state_iter`] can be used to create such an iterator. + /// + /// # Safety + /// + /// All selectors in the iterator must be inside of the full_range selector. + /// + /// The full range selector must have mips and layers start at 0. + unsafe fn from_selector_state_iter( + full_range: TextureSelector, + state_iter: impl Iterator, + ) -> Self { + debug_assert_eq!(full_range.layers.start, 0); + debug_assert_eq!(full_range.mips.start, 0); + + let mut complex = + ComplexTextureState::new(full_range.mips.len() as u32, full_range.layers.len() as u32); + for (selector, desired_state) in state_iter { + debug_assert!(selector.layers.end <= full_range.layers.end); + debug_assert!(selector.mips.end <= full_range.mips.end); + + // This should only ever happen with a wgpu bug, but let's just double + // check that resource states don't have any conflicts. + debug_assert_eq!(invalid_resource_state(desired_state), false); + + let mips = selector.mips.start as usize..selector.mips.end as usize; + for mip in complex.mips.get_unchecked_mut(mips) { + for &mut (_, ref mut state) in mip.isolate(&selector.layers, TextureUses::UNKNOWN) { + *state = desired_state; } - Some(Ok(_)) | Some(Err(())) => return None, } } - result + complex } - fn change( - &mut self, - id: Valid, - selector: Self::Selector, - usage: Self::Usage, - mut output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - if self.full { - assert!(self.mips.len() >= selector.levels.end as usize); - } else { - while self.mips.len() < selector.levels.end as usize { - self.mips.push(PlaneStates::empty()); + /// Convert a complex state into an iterator over all states stored. + /// + /// [`Self::from_selector_state_iter`] can be used to consume such an iterator. + fn to_selector_state_iter( + &self, + ) -> impl Iterator + Clone + '_ { + self.mips.iter().enumerate().flat_map(|(mip, inner)| { + let mip = mip as u32; + { + inner.iter().map(move |&(ref layers, inner)| { + ( + TextureSelector { + mips: mip..mip + 1, + layers: layers.clone(), + }, + inner, + ) + }) } + }) + } +} + +/// Stores all the textures that a bind group stores. +pub(crate) struct TextureBindGroupState { + textures: Vec<( + Valid, + Option, + RefCount, + TextureUses, + )>, + + _phantom: PhantomData, +} +impl TextureBindGroupState { + pub fn new() -> Self { + Self { + textures: Vec::new(), + + _phantom: PhantomData, + } + } + + /// Optimize the texture bind group state by sorting it by ID. + /// + /// When this list of states is merged into a tracker, the memory + /// accesses will be in a constant assending order. + pub(crate) fn optimize(&mut self) { + self.textures + .sort_unstable_by_key(|&(id, _, _, _)| id.0.unzip().0); + } + + /// Returns a list of all buffers tracked. May contain duplicates. + pub fn used(&self) -> impl Iterator> + '_ { + self.textures.iter().map(|&(id, _, _, _)| id) + } + + /// Adds the given resource with the given state. + pub fn add_single<'a>( + &mut self, + storage: &'a hub::Storage, TextureId>, + id: TextureId, + ref_count: RefCount, + selector: Option, + state: TextureUses, + ) -> Option<&'a Texture> { + let value = storage.get(id).ok()?; + + self.textures.push((Valid(id), selector, ref_count, state)); + + Some(value) + } +} + +/// Container for corresponding simple and complex texture states. +#[derive(Debug)] +pub(crate) struct TextureStateSet { + simple: Vec, + complex: FastHashMap, +} +impl TextureStateSet { + fn new() -> Self { + Self { + simple: Vec::new(), + complex: FastHashMap::default(), + } + } + + fn set_size(&mut self, size: usize) { + self.simple.resize(size, TextureUses::UNINITIALIZED); + } +} + +/// Stores all texture state within a single usage scope. +#[derive(Debug)] +pub(crate) struct TextureUsageScope { + set: TextureStateSet, + + metadata: ResourceMetadata, +} + +impl TextureUsageScope { + pub fn new() -> Self { + Self { + set: TextureStateSet::new(), + + metadata: ResourceMetadata::new(), } - for (mip_id, mip) in self.mips[selector.levels.start as usize..selector.levels.end as usize] - .iter_mut() - .enumerate() + } + + fn debug_assert_in_bounds(&self, index: usize) { + self.metadata.debug_assert_in_bounds(index); + + debug_assert!(index < self.set.simple.len()); + + debug_assert!(if self.metadata.owned.get(index).unwrap() + && self.set.simple[index] == TextureUses::COMPLEX { - let level = selector.levels.start + mip_id as u32; - let layers = mip.isolate(&selector.layers, Unit::new(usage)); - for &mut (ref range, ref mut unit) in layers { - if unit.last == usage && TextureUses::ORDERED.contains(usage) { - continue; - } - // TODO: Can't satisfy clippy here unless we modify - // `TextureSelector` to use `std::ops::RangeBounds`. - #[allow(clippy::range_plus_one)] - let pending = PendingTransition { - id, - selector: TextureSelector { - levels: level..level + 1, - layers: range.clone(), + self.set.complex.contains_key(&(index as u32)) + } else { + true + }); + } + + /// Sets the size of all the vectors inside the tracker. + /// + /// Must be called with the highest possible Texture ID before + /// all unsafe functions are called. + pub fn set_size(&mut self, size: usize) { + self.set.set_size(size); + self.metadata.set_size(size); + } + + /// Returns a list of all textures tracked. + pub fn used(&self) -> impl Iterator> + '_ { + self.metadata.used() + } + + /// Returns true if the tracker owns no resources. + /// + /// This is a O(n) operation. + pub(crate) fn is_empty(&self) -> bool { + self.metadata.is_empty() + } + + /// Merge the list of texture states in the given usage scope into this UsageScope. + /// + /// If any of the resulting states is invalid, stops the merge and returns a usage + /// conflict with the details of the invalid state. + /// + /// If the given tracker uses IDs higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn merge_usage_scope( + &mut self, + storage: &hub::Storage, TextureId>, + scope: &Self, + ) -> Result<(), UsageConflict> { + let incoming_size = scope.set.simple.len(); + if incoming_size > self.set.simple.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&scope.metadata.owned) { + let index32 = index as u32; + + self.debug_assert_in_bounds(index); + scope.debug_assert_in_bounds(index); + + unsafe { + insert_or_merge( + texture_data_from_texture(storage, index32), + &mut self.set, + &mut self.metadata, + index32, + index, + TextureStateProvider::TextureSet { set: &scope.set }, + ResourceMetadataProvider::Indirect { + metadata: &scope.metadata, }, - usage: unit.last..usage, - }; - - *unit = match output { - None => { - assert_eq!( - unit.first, None, - "extending a state that is already a transition" - ); - Unit::new(pending.collapse()?) - } - Some(ref mut out) => { - out.push(pending); - Unit { - first: unit.first.or(Some(unit.last)), - last: usage, - } - } - }; - } + )? + }; } + Ok(()) } - fn merge( + /// Merge the list of texture states in the given bind group into this usage scope. + /// + /// If any of the resulting states is invalid, stops the merge and returns a usage + /// conflict with the details of the invalid state. + /// + /// Because bind groups do not check if the union of all their states is valid, + /// this method is allowed to return Err on the first bind group bound. + /// + /// # Safety + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + pub unsafe fn merge_bind_group( &mut self, - id: Valid, - other: &Self, - mut output: Option<&mut Vec>>, - ) -> Result<(), PendingTransition> { - let mut temp = Vec::new(); - if self.full { - assert!(self.mips.len() >= other.mips.len()); - } else { - while self.mips.len() < other.mips.len() { - self.mips.push(PlaneStates::empty()); - } + storage: &hub::Storage, TextureId>, + bind_group: &TextureBindGroupState, + ) -> Result<(), UsageConflict> { + for &(id, ref selector, ref ref_count, state) in &bind_group.textures { + self.merge_single(storage, id, selector.clone(), ref_count, state)?; } - for (mip_id, (mip_self, mip_other)) in self.mips.iter_mut().zip(&other.mips).enumerate() { - let level = mip_id as u32; - temp.extend(mip_self.merge(mip_other, 0)); - mip_self.clear(); - - for (layers, states) in temp.drain(..) { - let unit = match states { - Range { - start: None, - end: None, - } => unreachable!(), - Range { - start: Some(start), - end: None, - } => start, - Range { - start: None, - end: Some(end), - } => end, - Range { - start: Some(start), - end: Some(end), - } => { - let to_usage = end.port(); - if start.last == to_usage && TextureUses::ORDERED.contains(to_usage) { - Unit { - first: match output { - None => start.first, - Some(_) => start.first.or(Some(start.last)), - }, - last: end.last, - } - } else { - // TODO: Can't satisfy clippy here unless we modify - // `TextureSelector` to use `std::ops::RangeBounds`. - #[allow(clippy::range_plus_one)] - let pending = PendingTransition { - id, - selector: TextureSelector { - levels: level..level + 1, - layers: layers.clone(), - }, - usage: start.last..to_usage, - }; - - match output { - None => { - assert_eq!( - start.first, None, - "extending a state that is already a transition" - ); - Unit::new(pending.collapse()?) - } - Some(ref mut out) => { - out.push(pending); - Unit { - // this has to leave a valid `first` state - first: start.first.or(Some(start.last)), - last: end.last, - } - } - } - } - } - }; - mip_self.append(layers, unit); - } - } + Ok(()) + } + + /// Merge a single state into the UsageScope. + /// + /// If the resulting state is invalid, returns a usage + /// conflict with the details of the invalid state. + /// + /// # Safety + /// + /// Unlike other trackers whose merge_single is safe, this method is only + /// called where there is already other unsafe tracking functions active, + /// so we can prove this unsafe "for free". + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + pub unsafe fn merge_single( + &mut self, + storage: &hub::Storage, TextureId>, + id: Valid, + selector: Option, + ref_count: &RefCount, + new_state: TextureUses, + ) -> Result<(), UsageConflict> { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + self.debug_assert_in_bounds(index); + + insert_or_merge( + texture_data_from_texture(storage, index32), + &mut self.set, + &mut self.metadata, + index32, + index, + TextureStateProvider::from_option(selector, new_state), + ResourceMetadataProvider::Direct { + epoch, + ref_count: Cow::Borrowed(ref_count), + }, + )?; Ok(()) } +} + +/// Stores all texture state within a command buffer or device. +pub(crate) struct TextureTracker { + start_set: TextureStateSet, + end_set: TextureStateSet, + + metadata: ResourceMetadata, + + temp: Vec>, + + _phantom: PhantomData, +} +impl TextureTracker { + pub fn new() -> Self { + Self { + start_set: TextureStateSet::new(), + end_set: TextureStateSet::new(), - fn optimize(&mut self) { - for mip in self.mips.iter_mut() { - mip.coalesce(); + metadata: ResourceMetadata::new(), + + temp: Vec::new(), + + _phantom: PhantomData, } } -} -#[cfg(test)] -mod test { - //TODO: change() tests - use super::*; - use crate::id::Id; - - #[test] - fn query() { - let mut ts = TextureState::default(); - ts.mips.push(PlaneStates::empty()); - ts.mips.push(PlaneStates::from_slice(&[ - (1..3, Unit::new(TextureUses::RESOURCE)), - (3..5, Unit::new(TextureUses::RESOURCE)), - (5..6, Unit::new(TextureUses::STORAGE_READ)), - ])); - - assert_eq!( - ts.query(TextureSelector { - levels: 1..2, - layers: 2..5, - }), - // level 1 matches - Some(TextureUses::RESOURCE), - ); - assert_eq!( - ts.query(TextureSelector { - levels: 0..2, - layers: 2..5, - }), - // level 0 is empty, level 1 matches - Some(TextureUses::RESOURCE), - ); - assert_eq!( - ts.query(TextureSelector { - levels: 1..2, - layers: 1..5, - }), - // level 1 matches with gaps - Some(TextureUses::RESOURCE), - ); - assert_eq!( - ts.query(TextureSelector { - levels: 1..2, - layers: 4..6, - }), - // level 1 doesn't match - None, - ); + fn debug_assert_in_bounds(&self, index: usize) { + self.metadata.debug_assert_in_bounds(index); + + debug_assert!(index < self.start_set.simple.len()); + debug_assert!(index < self.end_set.simple.len()); + + debug_assert!(if self.metadata.owned.get(index).unwrap() + && self.start_set.simple[index] == TextureUses::COMPLEX + { + self.start_set.complex.contains_key(&(index as u32)) + } else { + true + }); + debug_assert!(if self.metadata.owned.get(index).unwrap() + && self.end_set.simple[index] == TextureUses::COMPLEX + { + self.end_set.complex.contains_key(&(index as u32)) + } else { + true + }); } - #[test] - fn merge() { - let id = Id::dummy(); - let mut ts1 = TextureState::default(); - ts1.mips.push(PlaneStates::from_slice(&[( - 1..3, - Unit::new(TextureUses::RESOURCE), - )])); - let mut ts2 = TextureState::default(); - assert_eq!( - ts1.merge(id, &ts2, None), - Ok(()), - "failed to merge with an empty" - ); + /// Sets the size of all the vectors inside the tracker. + /// + /// Must be called with the highest possible Texture ID before + /// all unsafe functions are called. + pub fn set_size(&mut self, size: usize) { + self.start_set.set_size(size); + self.end_set.set_size(size); - ts2.mips.push(PlaneStates::from_slice(&[( - 1..2, - Unit::new(TextureUses::COPY_SRC), - )])); - assert_eq!( - ts1.merge(Id::dummy(), &ts2, None), - Ok(()), - "failed to extend a compatible state" - ); - assert_eq!( - ts1.mips[0].query(&(1..2), |&v| v), - Some(Ok(Unit { - first: None, - last: TextureUses::RESOURCE | TextureUses::COPY_SRC, - })), - "wrong extension result" - ); + self.metadata.set_size(size); + } - ts2.mips[0] = PlaneStates::from_slice(&[(1..2, Unit::new(TextureUses::COPY_DST))]); - assert_eq!( - ts1.clone().merge(Id::dummy(), &ts2, None), - Err(PendingTransition { - id, - selector: TextureSelector { - levels: 0..1, - layers: 1..2, + /// Extend the vectors to let the given index be valid. + fn allow_index(&mut self, index: usize) { + if index >= self.start_set.simple.len() { + self.set_size(index + 1); + } + } + + /// Returns a list of all textures tracked. + pub fn used(&self) -> impl Iterator> + '_ { + self.metadata.used() + } + + /// Drains all currently pending transitions. + pub fn drain(&mut self) -> Drain> { + self.temp.drain(..) + } + + /// Get the refcount of the given resource. + /// + /// # Safety + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + /// + /// The resource must be tracked by this tracker. + pub unsafe fn get_ref_count(&self, id: Valid) -> &RefCount { + let (index32, _, _) = id.0.unzip(); + let index = index32 as usize; + + self.debug_assert_in_bounds(index); + + self.metadata + .ref_counts + .get_unchecked(index) + .as_ref() + .unwrap_unchecked() + } + + /// Inserts a single texture and a state into the resource tracker. + /// + /// If the resource already exists in the tracker, this will panic. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn insert_single(&mut self, id: TextureId, ref_count: RefCount, usage: TextureUses) { + let (index32, epoch, _) = id.unzip(); + let index = index32 as usize; + + self.allow_index(index); + + self.debug_assert_in_bounds(index); + + unsafe { + let currently_owned = self.metadata.owned.get(index).unwrap_unchecked(); + + if currently_owned { + panic!("Tried to insert texture already tracked"); + } + + insert( + None, + Some(&mut self.start_set), + &mut self.end_set, + &mut self.metadata, + index32, + index, + TextureStateProvider::KnownSingle { state: usage }, + None, + ResourceMetadataProvider::Direct { + epoch, + ref_count: Cow::Owned(ref_count), }, - usage: TextureUses::RESOURCE | TextureUses::COPY_SRC..TextureUses::COPY_DST, - }), - "wrong error on extending with incompatible state" - ); + ) + }; + } + + /// Sets the state of a single texture. + /// + /// If a transition is needed to get the texture into the given state, that transition + /// is returned. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_single<'a>( + &mut self, + storage: &'a hub::Storage, TextureId>, + id: TextureId, + selector: TextureSelector, + new_state: TextureUses, + ) -> Option<(&'a Texture, Drain<'_, PendingTransition>)> { + let texture = storage.get(id).ok()?; + + let (index32, epoch, _) = id.unzip(); + let index = index32 as usize; + + self.allow_index(index); - let mut list = Vec::new(); - ts2.mips[0] = PlaneStates::from_slice(&[ - (1..2, Unit::new(TextureUses::COPY_DST)), - ( - 2..3, - Unit { - first: Some(TextureUses::COPY_SRC), - last: TextureUses::COLOR_TARGET, + self.debug_assert_in_bounds(index); + + unsafe { + insert_or_barrier_update( + texture_data_from_texture(storage, index32), + Some(&mut self.start_set), + &mut self.end_set, + &mut self.metadata, + index32, + index, + TextureStateProvider::Selector { + selector, + state: new_state, }, - ), - ]); - ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap(); - assert_eq!( - &list, - &[ - PendingTransition { - id, - selector: TextureSelector { - levels: 0..1, - layers: 1..2, + None, + ResourceMetadataProvider::Resource { epoch }, + &mut self.temp, + ) + } + + Some((texture, self.temp.drain(..))) + } + + /// Sets the given state for all texture in the given tracker. + /// + /// If a transition is needed to get the texture into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_from_tracker( + &mut self, + storage: &hub::Storage, TextureId>, + tracker: &Self, + ) { + let incoming_size = tracker.start_set.simple.len(); + if incoming_size > self.start_set.simple.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&tracker.metadata.owned) { + let index32 = index as u32; + + self.debug_assert_in_bounds(index); + tracker.debug_assert_in_bounds(index); + unsafe { + insert_or_barrier_update( + texture_data_from_texture(storage, index32), + Some(&mut self.start_set), + &mut self.end_set, + &mut self.metadata, + index32, + index, + TextureStateProvider::TextureSet { + set: &tracker.start_set, }, - usage: TextureUses::RESOURCE | TextureUses::COPY_SRC..TextureUses::COPY_DST, - }, - PendingTransition { - id, - selector: TextureSelector { - levels: 0..1, - layers: 2..3, + Some(TextureStateProvider::TextureSet { + set: &tracker.end_set, + }), + ResourceMetadataProvider::Indirect { + metadata: &tracker.metadata, }, - // the transition links the end of the base rage (..SAMPLED) - // with the start of the next range (COPY_SRC..) - usage: TextureUses::RESOURCE..TextureUses::COPY_SRC, - }, - ], - "replacing produced wrong transitions" - ); - assert_eq!( - ts1.mips[0].query(&(1..2), |&v| v), - Some(Ok(Unit { - first: Some(TextureUses::RESOURCE | TextureUses::COPY_SRC), - last: TextureUses::COPY_DST, - })), - "wrong final layer 1 state" - ); - assert_eq!( - ts1.mips[0].query(&(2..3), |&v| v), - Some(Ok(Unit { - first: Some(TextureUses::RESOURCE), - last: TextureUses::COLOR_TARGET, - })), - "wrong final layer 2 state" - ); + &mut self.temp, + ); + } + } + } - list.clear(); - ts2.mips[0] = PlaneStates::from_slice(&[( - 2..3, - Unit { - first: Some(TextureUses::COLOR_TARGET), - last: TextureUses::COPY_SRC, - }, - )]); - ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap(); - assert_eq!(&list, &[], "unexpected replacing transition"); - - list.clear(); - ts2.mips[0] = PlaneStates::from_slice(&[( - 2..3, - Unit { - first: Some(TextureUses::COPY_DST), - last: TextureUses::COPY_DST, - }, - )]); - ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap(); - assert_eq!( - &list, - &[PendingTransition { - id, - selector: TextureSelector { - levels: 0..1, - layers: 2..3, + /// Sets the given state for all textures in the given UsageScope. + /// + /// If a transition is needed to get the textures into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// If the ID is higher than the length of internal vectors, + /// the vectors will be extended. A call to set_size is not needed. + pub fn set_from_usage_scope( + &mut self, + storage: &hub::Storage, TextureId>, + scope: &TextureUsageScope, + ) { + let incoming_size = scope.set.simple.len(); + if incoming_size > self.start_set.simple.len() { + self.set_size(incoming_size); + } + + for index in iterate_bitvec_indices(&scope.metadata.owned) { + let index32 = index as u32; + + self.debug_assert_in_bounds(index); + scope.debug_assert_in_bounds(index); + unsafe { + insert_or_barrier_update( + texture_data_from_texture(storage, index32), + Some(&mut self.start_set), + &mut self.end_set, + &mut self.metadata, + index32, + index, + TextureStateProvider::TextureSet { set: &scope.set }, + None, + ResourceMetadataProvider::Indirect { + metadata: &scope.metadata, + }, + &mut self.temp, + ); + } + } + } + + /// Iterates through all textures in the given bind group and adopts + /// the state given for those textures in the UsageScope. It also + /// removes all touched textures from the usage scope. + /// + /// If a transition is needed to get the textures into the needed state, + /// those transitions are stored within the tracker. A subsequent + /// call to [`Self::drain`] is needed to get those transitions. + /// + /// This is a really funky method used by Compute Passes to generate + /// barriers after a call to dispatch without needing to iterate + /// over all elements in the usage scope. We use each the + /// bind group as a source of which IDs to look at. The bind groups + /// must have first been added to the usage scope. + /// + /// # Safety + /// + /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this + /// method is called. + pub unsafe fn set_and_remove_from_usage_scope_sparse( + &mut self, + storage: &hub::Storage, TextureId>, + scope: &mut TextureUsageScope, + bind_group_state: &TextureBindGroupState, + ) { + let incoming_size = scope.set.simple.len(); + if incoming_size > self.start_set.simple.len() { + self.set_size(incoming_size); + } + + for &(id, _, _, _) in bind_group_state.textures.iter() { + let (index32, _, _) = id.0.unzip(); + let index = index32 as usize; + scope.debug_assert_in_bounds(index); + + if !scope.metadata.owned.get(index).unwrap_unchecked() { + continue; + } + insert_or_barrier_update( + texture_data_from_texture(storage, index32), + Some(&mut self.start_set), + &mut self.end_set, + &mut self.metadata, + index32, + index, + TextureStateProvider::TextureSet { set: &scope.set }, + None, + ResourceMetadataProvider::Indirect { + metadata: &scope.metadata, }, - usage: TextureUses::COPY_SRC..TextureUses::COPY_DST, - },], - "invalid replacing transition" + &mut self.temp, + ); + + scope.metadata.reset(index); + } + } + + /// Unconditionally removes the given resource from the tracker. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + pub fn remove(&mut self, id: Valid) -> bool { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + if index > self.metadata.owned.len() { + return false; + } + + self.debug_assert_in_bounds(index); + + unsafe { + if self.metadata.owned.get(index).unwrap_unchecked() { + let existing_epoch = *self.metadata.epochs.get_unchecked_mut(index); + assert_eq!(existing_epoch, epoch); + + self.start_set.complex.remove(&index32); + self.end_set.complex.remove(&index32); + + self.metadata.reset(index); + + return true; + } + } + + false + } + + /// Removes the given resource from the tracker iff we have the last reference to the + /// resource and the epoch matches. + /// + /// Returns true if the resource was removed. + /// + /// If the ID is higher than the length of internal vectors, + /// false will be returned. + pub fn remove_abandoned(&mut self, id: Valid) -> bool { + let (index32, epoch, _) = id.0.unzip(); + let index = index32 as usize; + + if index > self.metadata.owned.len() { + return false; + } + + self.debug_assert_in_bounds(index); + + unsafe { + if self.metadata.owned.get(index).unwrap_unchecked() { + let existing_epoch = self.metadata.epochs.get_unchecked_mut(index); + let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index); + + if *existing_epoch == epoch + && existing_ref_count.as_mut().unwrap_unchecked().load() == 1 + { + self.start_set.complex.remove(&index32); + self.end_set.complex.remove(&index32); + + self.metadata.reset(index); + + return true; + } + } + } + + false + } +} + +/// An iterator adapter that can store two different iterator types. +#[derive(Clone)] +enum EitherIter { + Left(L), + Right(R), +} + +impl Iterator for EitherIter +where + L: Iterator, + R: Iterator, +{ + type Item = D; + + fn next(&mut self) -> Option { + match *self { + EitherIter::Left(ref mut inner) => inner.next(), + EitherIter::Right(ref mut inner) => inner.next(), + } + } +} + +/// Container that signifies storing both different things +/// if there is a single state or many different states +/// involved in the operation. +#[derive(Debug, Clone)] +enum SingleOrManyStates { + Single(S), + Many(M), +} + +/// A source of texture state. +#[derive(Clone)] +enum TextureStateProvider<'a> { + /// Comes directly from a single state. + KnownSingle { state: TextureUses }, + /// Comes from a selector and a single state. + Selector { + selector: TextureSelector, + state: TextureUses, + }, + /// Comes from another texture set. + TextureSet { set: &'a TextureStateSet }, +} +impl<'a> TextureStateProvider<'a> { + /// Convenience function turning Option into this enum. + fn from_option(selector: Option, state: TextureUses) -> Self { + match selector { + Some(selector) => Self::Selector { selector, state }, + None => Self::KnownSingle { state }, + } + } + + /// Get the state provided by this. + /// + /// # Panics + /// + /// Panics if texture_data is None and this uses a Selector source. + /// + /// # Safety + /// + /// - The index must be in bounds of the state set if this uses an TextureSet source. + #[inline(always)] + unsafe fn get_state( + self, + texture_data: Option<(&LifeGuard, &TextureSelector)>, + index32: u32, + index: usize, + ) -> SingleOrManyStates< + TextureUses, + impl Iterator + Clone + 'a, + > { + match self { + TextureStateProvider::KnownSingle { state } => SingleOrManyStates::Single(state), + TextureStateProvider::Selector { selector, state } => { + // We check if the selector given is actually for the full resource, + // and if it is we promote to a simple state. This allows upstream + // code to specify selectors willy nilly, and all that are really + // single states are promoted here. + if *texture_data.unwrap().1 == selector { + SingleOrManyStates::Single(state) + } else { + SingleOrManyStates::Many(EitherIter::Left(iter::once((selector, state)))) + } + } + TextureStateProvider::TextureSet { set } => { + let new_state = *set.simple.get_unchecked(index); + + if new_state == TextureUses::COMPLEX { + let new_complex = set.complex.get(&index32).unwrap_unchecked(); + + SingleOrManyStates::Many(EitherIter::Right( + new_complex.to_selector_state_iter(), + )) + } else { + SingleOrManyStates::Single(new_state) + } + } + } + } +} + +/// Helper function that gets what is needed from the texture storage +/// out of the texture storage. +#[inline(always)] +unsafe fn texture_data_from_texture( + storage: &hub::Storage, TextureId>, + index32: u32, +) -> (&LifeGuard, &TextureSelector) { + let texture = storage.get_unchecked(index32); + (&texture.life_guard, &texture.full_range) +} + +/// Does an insertion operation if the index isn't tracked +/// in the current metadata, otherwise merges the given state +/// with the current state. If the merging would cause +/// a conflict, returns that usage conflict. +/// +/// # Safety +/// +/// Indexes must be valid indexes into all arrays passed in +/// to this function, either directly or via metadata or provider structs. +#[inline(always)] +unsafe fn insert_or_merge( + texture_data: (&LifeGuard, &TextureSelector), + current_state_set: &mut TextureStateSet, + resource_metadata: &mut ResourceMetadata, + index32: u32, + index: usize, + state_provider: TextureStateProvider<'_>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) -> Result<(), UsageConflict> { + let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked(); + + if !currently_owned { + insert( + Some(texture_data), + None, + current_state_set, + resource_metadata, + index32, + index, + state_provider, + None, + metadata_provider, ); - assert_eq!( - ts1.mips[0].query(&(2..3), |&v| v), - Some(Ok(Unit { - // the initial state here is never expected to change - first: Some(TextureUses::RESOURCE), - last: TextureUses::COPY_DST, - })), - "wrong final layer 2 state" + return Ok(()); + } + + merge( + texture_data, + current_state_set, + index32, + index, + state_provider, + metadata_provider, + ) +} + +/// If the resource isn't tracked +/// - Inserts the given resource. +/// - Uses the `start_state_provider` to populate `start_states` +/// - Uses either `end_state_provider` or `start_state_provider` +/// to populate `current_states`. +/// If the resource is tracked +/// - Inserts barriers from the state in `current_states` +/// to the state provided by `start_state_provider`. +/// - Updates the `current_states` with either the state from +/// `end_state_provider` or `start_state_provider`. +/// +/// Any barriers are added to the barrier vector. +/// +/// # Safety +/// +/// Indexes must be valid indexes into all arrays passed in +/// to this function, either directly or via metadata or provider structs. +#[inline(always)] +unsafe fn insert_or_barrier_update( + texture_data: (&LifeGuard, &TextureSelector), + start_state: Option<&mut TextureStateSet>, + current_state_set: &mut TextureStateSet, + resource_metadata: &mut ResourceMetadata, + index32: u32, + index: usize, + start_state_provider: TextureStateProvider<'_>, + end_state_provider: Option>, + metadata_provider: ResourceMetadataProvider<'_, A>, + barriers: &mut Vec>, +) { + let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked(); + + if !currently_owned { + insert( + Some(texture_data), + start_state, + current_state_set, + resource_metadata, + index32, + index, + start_state_provider, + end_state_provider, + metadata_provider, ); + return; + } + + let update_state_provider = end_state_provider.unwrap_or_else(|| start_state_provider.clone()); + barrier( + texture_data, + current_state_set, + index32, + index, + start_state_provider, + barriers, + ); + + let start_state_set = start_state.unwrap(); + update( + texture_data, + start_state_set, + current_state_set, + index32, + index, + update_state_provider, + ); +} + +#[inline(always)] +unsafe fn insert( + texture_data: Option<(&LifeGuard, &TextureSelector)>, + start_state: Option<&mut TextureStateSet>, + end_state: &mut TextureStateSet, + resource_metadata: &mut ResourceMetadata, + index32: u32, + index: usize, + start_state_provider: TextureStateProvider<'_>, + end_state_provider: Option>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) { + let start_layers = start_state_provider.get_state(texture_data, index32, index); + match start_layers { + SingleOrManyStates::Single(state) => { + // This should only ever happen with a wgpu bug, but let's just double + // check that resource states don't have any conflicts. + debug_assert_eq!(invalid_resource_state(state), false); + + log::trace!("\ttex {index32}: insert start {state:?}"); + + if let Some(start_state) = start_state { + *start_state.simple.get_unchecked_mut(index) = state; + } + + // We only need to insert ourselves the end state if there is no end state provider. + if end_state_provider.is_none() { + *end_state.simple.get_unchecked_mut(index) = state; + } + } + SingleOrManyStates::Many(state_iter) => { + let full_range = texture_data.unwrap().1.clone(); + + let complex = ComplexTextureState::from_selector_state_iter(full_range, state_iter); + + log::trace!("\ttex {index32}: insert start {complex:?}"); + + if let Some(start_state) = start_state { + *start_state.simple.get_unchecked_mut(index) = TextureUses::COMPLEX; + start_state.complex.insert(index32, complex.clone()); + } + + // We only need to insert ourselves the end state if there is no end state provider. + if end_state_provider.is_none() { + *end_state.simple.get_unchecked_mut(index) = TextureUses::COMPLEX; + end_state.complex.insert(index32, complex); + } + } + } + + if let Some(end_state_provider) = end_state_provider { + match end_state_provider.get_state(texture_data, index32, index) { + SingleOrManyStates::Single(state) => { + // This should only ever happen with a wgpu bug, but let's just double + // check that resource states don't have any conflicts. + debug_assert_eq!(invalid_resource_state(state), false); + + log::trace!("\ttex {index32}: insert end {state:?}"); + + // We only need to insert into the end, as there is guarenteed to be + // a start state provider. + *end_state.simple.get_unchecked_mut(index) = state; + } + SingleOrManyStates::Many(state_iter) => { + let full_range = texture_data.unwrap().1.clone(); + + let complex = ComplexTextureState::from_selector_state_iter(full_range, state_iter); + + log::trace!("\ttex {index32}: insert end {complex:?}"); + + // We only need to insert into the end, as there is guarenteed to be + // a start state provider. + *end_state.simple.get_unchecked_mut(index) = TextureUses::COMPLEX; + end_state.complex.insert(index32, complex); + } + } + } + + let (epoch, ref_count) = + metadata_provider.get_own(texture_data.map(|(life_guard, _)| life_guard), index); + + resource_metadata.owned.set(index, true); + *resource_metadata.epochs.get_unchecked_mut(index) = epoch; + *resource_metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count); +} + +#[inline(always)] +unsafe fn merge( + texture_data: (&LifeGuard, &TextureSelector), + current_state_set: &mut TextureStateSet, + index32: u32, + index: usize, + state_provider: TextureStateProvider<'_>, + metadata_provider: ResourceMetadataProvider<'_, A>, +) -> Result<(), UsageConflict> { + let current_simple = current_state_set.simple.get_unchecked_mut(index); + let current_state = if *current_simple == TextureUses::COMPLEX { + SingleOrManyStates::Many( + current_state_set + .complex + .get_mut(&index32) + .unwrap_unchecked(), + ) + } else { + SingleOrManyStates::Single(current_simple) + }; + + let new_state = state_provider.get_state(Some(texture_data), index32, index); + + match (current_state, new_state) { + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => { + let merged_state = *current_simple | new_simple; + + log::trace!("\ttex {index32}: merge simple {current_simple:?} + {new_simple:?}"); + + if invalid_resource_state(merged_state) { + return Err(UsageConflict::from_texture( + TextureId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT), + texture_data.1.clone(), + *current_simple, + new_simple, + )); + } + + *current_simple = merged_state; + } + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => { + // Because we are now demoting this simple state to a complex state, we actually need to make a whole + // new complex state for us to use as there wasn't one before. + let mut new_complex = ComplexTextureState::from_selector_state_iter( + texture_data.1.clone(), + iter::once((texture_data.1.clone(), *current_simple)), + ); + + for (selector, new_state) in new_many { + let merged_state = *current_simple | new_state; + + log::trace!( + "\ttex {index32}: merge {selector:?} {current_simple:?} + {new_state:?}" + ); + + if invalid_resource_state(merged_state) { + return Err(UsageConflict::from_texture( + TextureId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT), + selector, + *current_simple, + new_state, + )); + } + + for mip in + &mut new_complex.mips[selector.mips.start as usize..selector.mips.end as usize] + { + for &mut (_, ref mut current_layer_state) in + mip.isolate(&selector.layers, TextureUses::UNKNOWN) + { + *current_layer_state = merged_state; + } + + mip.coalesce(); + } + } + + *current_simple = TextureUses::COMPLEX; + current_state_set.complex.insert(index32, new_complex); + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_simple)) => { + for (mip_id, mip) in current_complex.mips.iter_mut().enumerate() { + let mip_id = mip_id as u32; + + for &mut (ref layers, ref mut current_layer_state) in mip.iter_mut() { + let merged_state = *current_layer_state | new_simple; + + // Once we remove unknown, this will never be empty, as simple states are never unknown. + let merged_state = merged_state - TextureUses::UNKNOWN; + + log::trace!( + "\ttex {index32}: merge mip {mip_id} layers {layers:?} \ + {current_layer_state:?} + {new_simple:?}" + ); + + if invalid_resource_state(merged_state) { + return Err(UsageConflict::from_texture( + TextureId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT), + TextureSelector { + mips: mip_id..mip_id + 1, + layers: layers.clone(), + }, + *current_layer_state, + new_simple, + )); + } + + *current_layer_state = merged_state; + } + + mip.coalesce(); + } + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => { + for (selector, new_state) in new_many { + for mip_id in selector.mips { + debug_assert!((mip_id as usize) < current_complex.mips.len()); + + let mip = current_complex.mips.get_unchecked_mut(mip_id as usize); + + for &mut (ref layers, ref mut current_layer_state) in + mip.isolate(&selector.layers, TextureUses::UNKNOWN) + { + let merged_state = *current_layer_state | new_state; + let merged_state = merged_state - TextureUses::UNKNOWN; + + if merged_state.is_empty() { + // We know nothing about this state, lets just move on. + continue; + } + + log::trace!( + "\ttex {index32}: merge mip {mip_id} layers {layers:?} \ + {current_layer_state:?} + {new_state:?}" + ); + + if invalid_resource_state(merged_state) { + return Err(UsageConflict::from_texture( + TextureId::zip( + index32, + metadata_provider.get_epoch(index), + A::VARIANT, + ), + TextureSelector { + mips: mip_id..mip_id + 1, + layers: layers.clone(), + }, + *current_layer_state, + new_state, + )); + } + *current_layer_state = merged_state; + } + + mip.coalesce(); + } + } + } + } + Ok(()) +} + +#[inline(always)] +unsafe fn barrier( + texture_data: (&LifeGuard, &TextureSelector), + current_state_set: &TextureStateSet, + index32: u32, + index: usize, + state_provider: TextureStateProvider<'_>, + barriers: &mut Vec>, +) { + let current_simple = *current_state_set.simple.get_unchecked(index); + let current_state = if current_simple == TextureUses::COMPLEX { + SingleOrManyStates::Many(current_state_set.complex.get(&index32).unwrap_unchecked()) + } else { + SingleOrManyStates::Single(current_simple) + }; + + let new_state = state_provider.get_state(Some(texture_data), index32, index); + + match (current_state, new_state) { + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => { + if skip_barrier(current_simple, new_simple) { + return; + } + + log::trace!("\ttex {index32}: transition simple {current_simple:?} -> {new_simple:?}"); + + barriers.push(PendingTransition { + id: index32, + selector: texture_data.1.clone(), + usage: current_simple..new_simple, + }); + } + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => { + for (selector, new_state) in new_many { + if new_state == TextureUses::UNKNOWN { + continue; + } + + if skip_barrier(current_simple, new_state) { + continue; + } + + log::trace!( + "\ttex {index32}: transition {selector:?} {current_simple:?} -> {new_state:?}" + ); + + barriers.push(PendingTransition { + id: index32, + selector, + usage: current_simple..new_state, + }); + } + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_simple)) => { + for (mip_id, mip) in current_complex.mips.iter().enumerate() { + let mip_id = mip_id as u32; + + for &(ref layers, current_layer_state) in mip.iter() { + if current_layer_state == TextureUses::UNKNOWN { + continue; + } + + if skip_barrier(current_layer_state, new_simple) { + continue; + } + + log::trace!( + "\ttex {index32}: transition mip {mip_id} layers {layers:?} \ + {current_layer_state:?} -> {new_simple:?}" + ); + + barriers.push(PendingTransition { + id: index32, + selector: TextureSelector { + mips: mip_id..mip_id + 1, + layers: layers.clone(), + }, + usage: current_layer_state..new_simple, + }); + } + } + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => { + for (selector, new_state) in new_many { + for mip_id in selector.mips { + debug_assert!((mip_id as usize) < current_complex.mips.len()); + + let mip = current_complex.mips.get_unchecked(mip_id as usize); + + for (layers, current_layer_state) in mip.iter_filter(&selector.layers) { + if *current_layer_state == TextureUses::UNKNOWN + || new_state == TextureUses::UNKNOWN + { + continue; + } + + if skip_barrier(*current_layer_state, new_state) { + continue; + } + + log::trace!( + "\ttex {index32}: transition mip {mip_id} layers {layers:?} \ + {current_layer_state:?} -> {new_state:?}" + ); + + barriers.push(PendingTransition { + id: index32, + selector: TextureSelector { + mips: mip_id..mip_id + 1, + layers, + }, + usage: *current_layer_state..new_state, + }); + } + } + } + } + } +} + +#[allow(clippy::needless_option_as_deref)] // we use this for reborrowing Option<&mut T> +#[inline(always)] +unsafe fn update( + texture_data: (&LifeGuard, &TextureSelector), + start_state_set: &mut TextureStateSet, + current_state_set: &mut TextureStateSet, + index32: u32, + index: usize, + state_provider: TextureStateProvider<'_>, +) { + let start_simple = *start_state_set.simple.get_unchecked(index); + + // We only ever need to update the start state here if the state is complex. + // + // If the state is simple, the first insert to the tracker would cover it. + let mut start_complex = None; + if start_simple == TextureUses::COMPLEX { + start_complex = Some(start_state_set.complex.get_mut(&index32).unwrap_unchecked()); + } + + let current_simple = current_state_set.simple.get_unchecked_mut(index); + let current_state = if *current_simple == TextureUses::COMPLEX { + SingleOrManyStates::Many( + current_state_set + .complex + .get_mut(&index32) + .unwrap_unchecked(), + ) + } else { + SingleOrManyStates::Single(current_simple) + }; + + let new_state = state_provider.get_state(Some(texture_data), index32, index); + + match (current_state, new_state) { + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Single(new_simple)) => { + *current_simple = new_simple; + } + (SingleOrManyStates::Single(current_simple), SingleOrManyStates::Many(new_many)) => { + // Because we are now demoting this simple state to a complex state, we actually need to make a whole + // new complex state for us to use as there wasn't one before. + let mut new_complex = ComplexTextureState::from_selector_state_iter( + texture_data.1.clone(), + iter::once((texture_data.1.clone(), *current_simple)), + ); + + for (selector, mut new_state) in new_many { + if new_state == TextureUses::UNKNOWN { + new_state = *current_simple; + } + for mip in + &mut new_complex.mips[selector.mips.start as usize..selector.mips.end as usize] + { + for &mut (_, ref mut current_layer_state) in + mip.isolate(&selector.layers, TextureUses::UNKNOWN) + { + *current_layer_state = new_state; + } + + mip.coalesce(); + } + } + + *current_simple = TextureUses::COMPLEX; + current_state_set.complex.insert(index32, new_complex); + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Single(new_single)) => { + for (mip_id, mip) in current_complex.mips.iter().enumerate() { + for &(ref layers, current_layer_state) in mip.iter() { + // If this state is unknown, that means that the start is _also_ unknown. + if current_layer_state == TextureUses::UNKNOWN { + if let Some(&mut ref mut start_complex) = start_complex { + debug_assert!(mip_id < start_complex.mips.len()); + + let start_mip = start_complex.mips.get_unchecked_mut(mip_id); + + for &mut (_, ref mut current_start_state) in + start_mip.isolate(layers, TextureUses::UNKNOWN) + { + debug_assert_eq!(*current_start_state, TextureUses::UNKNOWN); + *current_start_state = new_single; + } + + start_mip.coalesce(); + } + } + } + } + + *current_state_set.simple.get_unchecked_mut(index) = new_single; + current_state_set + .complex + .remove(&index32) + .unwrap_unchecked(); + } + (SingleOrManyStates::Many(current_complex), SingleOrManyStates::Many(new_many)) => { + for (selector, new_state) in new_many { + if new_state == TextureUses::UNKNOWN { + // We know nothing new + continue; + } + + for mip_id in selector.mips { + let mip_id = mip_id as usize; + debug_assert!(mip_id < current_complex.mips.len()); + + let mip = current_complex.mips.get_unchecked_mut(mip_id); + + for &mut (ref layers, ref mut current_layer_state) in + mip.isolate(&selector.layers, TextureUses::UNKNOWN) + { + if *current_layer_state == TextureUses::UNKNOWN + && new_state != TextureUses::UNKNOWN + { + // We now know something about this subresource that we didn't before + // so we should go back and update the start state. + + // We know we must have starter state be complex, otherwise we would know + // about this state. + debug_assert!(start_complex.is_some()); + + let start_complex = start_complex.as_deref_mut().unwrap_unchecked(); + + debug_assert!(mip_id < start_complex.mips.len()); + + let start_mip = start_complex.mips.get_unchecked_mut(mip_id); + + for &mut (_, ref mut current_start_state) in + start_mip.isolate(layers, TextureUses::UNKNOWN) + { + debug_assert_eq!(*current_start_state, TextureUses::UNKNOWN); + *current_start_state = new_state; + } + + start_mip.coalesce(); + } + + *current_layer_state = new_state; + } + + mip.coalesce(); + } + } + } } } diff --git a/third_party/rust/wgpu-core/src/validation.rs b/third_party/rust/wgpu-core/src/validation.rs index 3b3f3f79043fb..0264edb29d0ad 100644 --- a/third_party/rust/wgpu-core/src/validation.rs +++ b/third_party/rust/wgpu-core/src/validation.rs @@ -263,6 +263,8 @@ pub enum StageError { #[source] error: InputError, }, + #[error("location[{location}] is provided by the previous stage output but is not consumed as input by this stage.")] + InputNotConsumed { location: wgt::ShaderLocation }, } fn map_storage_format_to_naga(format: wgt::TextureFormat) -> Option { @@ -702,7 +704,11 @@ impl NumericType { (NumericDimension::Vector(Vs::Quad), Sk::Sint) } Tf::Rg11b10Float => (NumericDimension::Vector(Vs::Tri), Sk::Float), - Tf::Depth32Float | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => { + Tf::Depth32Float + | Tf::Depth32FloatStencil8 + | Tf::Depth24Plus + | Tf::Depth24PlusStencil8 + | Tf::Depth24UnormStencil8 => { panic!("Unexpected depth format") } Tf::Rgb9e5Ufloat => (NumericDimension::Vector(Vs::Tri), Sk::Float), @@ -1155,6 +1161,21 @@ impl Interface { } } + // Check all vertex outputs and make sure the fragment shader consumes them. + if shader_stage == naga::ShaderStage::Fragment { + for &index in inputs.keys() { + // This is a linear scan, but the count should be low enough that this should be fine. + let found = entry_point.inputs.iter().any(|v| match *v { + Varying::Local { location, .. } => location == index, + Varying::BuiltIn(_) => false, + }); + + if !found { + return Err(StageError::InputNotConsumed { location: index }); + } + } + } + if shader_stage == naga::ShaderStage::Vertex { for output in entry_point.outputs.iter() { //TODO: count builtins towards the limit? diff --git a/third_party/rust/wgpu-hal/.cargo-checksum.json b/third_party/rust/wgpu-hal/.cargo-checksum.json index 745e6d476cb69..d06e73b399990 100644 --- a/third_party/rust/wgpu-hal/.cargo-checksum.json +++ b/third_party/rust/wgpu-hal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"e747e47c2303651b01be2badaa5c1df53a973d3dd7aa3e5de50247599392b450","README.md":"78377f5876fafd77963eff7e3c2ba3a7e3ad5cf9201b09ed5612e49c2288eb18","examples/halmark/main.rs":"c745317191eab3159465f79f4a693f671e2f75877dc3b7215a6a90d70e88ec7f","examples/halmark/shader.wgsl":"59e3628abe34c66708bf0106658e791ef24357df3cae72194d34ff07b40e8007","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"d6db84b269b934409ef85fa31914b3f4bc7e7012e40269aad3eff5454eae2a09","src/auxil/dxgi/conv.rs":"cbdb037f2be37b3886f1449b4872a8f248390e185c3d32bf61d64aef76e2008e","src/auxil/dxgi/exception.rs":"f8d69d3d475e03c4d96d22778e5a6f322afd98fcfafb1414cd4a76239fa97a37","src/auxil/dxgi/factory.rs":"82451fcfcc1f73a570ae9e708c94efa9c125d269dfb7396de97da5b32f8a4090","src/auxil/dxgi/mod.rs":"63db737b48378d4843e2f7904f104790688029ff614bc80387cd9efe444f1e52","src/auxil/dxgi/result.rs":"20c8eb03d738062dff198feca6327addb9882ed0462be842c789eadf7dca0573","src/auxil/mod.rs":"f899555124ad6d44f5326ef935f4911702539fd933ec2ab07c6009badb3ea22c","src/auxil/renderdoc.rs":"3a4da908ebd6230177ca55c541c8278639e83d78badb4595a941aea30dd7f80f","src/dx11/adapter.rs":"bf123464ef748d021f2e0c40d27b3f6bdd50222c6f91cce6d25686a912eef093","src/dx11/command.rs":"cdad8dcdb800acba56c931f1726ddada652af18db0f066465af643f82a034492","src/dx11/device.rs":"76ac52095c639482adc2058509cd3acafd49cebc0694fcd64f8d9f53abc823de","src/dx11/instance.rs":"3bbf2730956472cb8023bd8fbd2d53e49f93c5e4ce3d14664112a293a165d191","src/dx11/library.rs":"0da08a780eefa7ff50f2e0998117202f26e5dd3d3a433c58b585801cff9863d2","src/dx11/mod.rs":"e4f7c6100e1bec479b41f3e3af96e01d53e6597c1c3a8fcde6f14cc9eb8537f8","src/dx12/adapter.rs":"1bc8807a28a961df304b44314500ff2239bc53cc72c9308fab28504a6f00c54b","src/dx12/command.rs":"f9969744663c1f01fca3892b93fc516363fa8725739d270a5f633db78f7902da","src/dx12/conv.rs":"c27336e5b576a7e05a576548fa0e6588ff77be4208f08aac0bea7e649ed1301b","src/dx12/descriptor.rs":"7145d3dc6be13fae4cf6bb8bf34a1ea1749ad87e5f429b84f3cbbea7bf63c148","src/dx12/device.rs":"27f47868a77c77a74e63de7f2d152ac22da34559286f13add17568b83d9f7e1e","src/dx12/instance.rs":"ccc36443cb1df8ab8ed8366cf8599ec3d75fb5fefa5f9bb0f0f0b5e6fc1c5102","src/dx12/mod.rs":"e88f7396dca4aba859a6e28d3f9de64a57a0df85acd53cecd6ada3d96386062c","src/dx12/view.rs":"b7a5cb8933f30517a97b4bd767e10b4c09f686dbf493d53b9b265d2d0b16f1a6","src/empty.rs":"6bf65e405f63eff49b25d079af644b352b95a9c7edcf3a57be2e96a50307b66b","src/gles/adapter.rs":"1e246de20cdbbb5d1727955c618486ac967c2e9f326886feb609f8b321114d74","src/gles/command.rs":"3bef8a822b59c7e986c2c6dd084a2c537a96de77e2b48522f86210093daad3d0","src/gles/conv.rs":"c5d6ba4afd2b3245b1a36a84e4c392c640e6170a1e81df3741bf5323c57bdc51","src/gles/device.rs":"66c30c4010f410bf3b8a03ee9d8e14753832fa2b6e17b518481281f06e3d7cd9","src/gles/egl.rs":"38dc851eede42b6be2ff02f15fef3a4a116cd1b5803974fada89ac86d42a5df4","src/gles/mod.rs":"8686d9bcfb8a7be6f3b8ff7d96e7538e71c3d37b2b09762b06aa304518165dfd","src/gles/queue.rs":"c57f634c983dca6bd657d2f6150f699d9648e6b5726ead9bb4310dd975171cdd","src/gles/shaders/clear.frag":"aac702eed9ece5482db5ba6783a678b119a5e7802b1ecf93f4975dee8acab0b3","src/gles/shaders/clear.vert":"8f636168e1da2cac48091c466a543c3b09fb4a0dd8c60c1c9bf34cc890766740","src/gles/shaders/present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"083500c0b36d079a82754895d06b993ea8ed4393690b226c85f07cbec373a730","src/lib.rs":"bebe5582d5e9563aebb0d44e6f267dc1eeb556557761bb1afc5f4191634a8588","src/metal/adapter.rs":"83ef6668160178fcce7820d3036c25aa35d02932577e45577832f19b93c8a12d","src/metal/command.rs":"b06983d7e11cdde526b7c9f5f0b86f1ea8faef02b7666367cb231211a8301570","src/metal/conv.rs":"517c491a87ba57f275f4a2f130ef1fc2c17528176ebc4813772f9bcd86ffd8e5","src/metal/device.rs":"dd823c8e12ba3ed69ef7cdcb543e8d995d0056d1f838516b0901068c83d8ffe2","src/metal/mod.rs":"c4f3959732f5f506fa881aa5812205a6452d6a946d661d7f81d1c7785359a10c","src/metal/surface.rs":"82836cadc751d94fb016bd590cdfec5649cbfae2f44d14599ed074dfb0a004dc","src/vulkan/adapter.rs":"6dc8f42fce7ea939134860944495fe687f2bffd47a97dc8dfd14ff34c11cd5b5","src/vulkan/command.rs":"60d1867acd0e46c34dabecea708cd776a1f435721b6673a506b5bb8aee87ff80","src/vulkan/conv.rs":"0d7b706a854ff3fdcea0e66f0d8563d9c2aa1f82b2e84692c16f4bfcc3872ed5","src/vulkan/device.rs":"472e915c73e69be8559e460061d720a32f7e8b5d3601982bc2bc588cc2b80a5d","src/vulkan/instance.rs":"49bb57b65c886c14f258e8a7fc183138765310ac7efd2fe3b26115e7942aa1fe","src/vulkan/mod.rs":"95347a82e2a276c1953e542c838f2ae07acc18d4187025fb22c264e61684860d"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"61752b031b63d9ce967085f9a43eb3dbbad3b472bd264612a88faf3e7d6fcd57","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"78377f5876fafd77963eff7e3c2ba3a7e3ad5cf9201b09ed5612e49c2288eb18","examples/halmark/main.rs":"fefa4f8d16f1a40156e0c0ce7aee06569b222a7a6284b69a000adeebb34a915d","examples/halmark/shader.wgsl":"59e3628abe34c66708bf0106658e791ef24357df3cae72194d34ff07b40e8007","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"d6db84b269b934409ef85fa31914b3f4bc7e7012e40269aad3eff5454eae2a09","src/auxil/dxgi/conv.rs":"310a23866e652257e3dca55c85c78420118e6dea4e69ff907db4a52bda9ac1c5","src/auxil/dxgi/exception.rs":"f8d69d3d475e03c4d96d22778e5a6f322afd98fcfafb1414cd4a76239fa97a37","src/auxil/dxgi/factory.rs":"82451fcfcc1f73a570ae9e708c94efa9c125d269dfb7396de97da5b32f8a4090","src/auxil/dxgi/mod.rs":"63db737b48378d4843e2f7904f104790688029ff614bc80387cd9efe444f1e52","src/auxil/dxgi/result.rs":"20c8eb03d738062dff198feca6327addb9882ed0462be842c789eadf7dca0573","src/auxil/mod.rs":"f899555124ad6d44f5326ef935f4911702539fd933ec2ab07c6009badb3ea22c","src/auxil/renderdoc.rs":"3a4da908ebd6230177ca55c541c8278639e83d78badb4595a941aea30dd7f80f","src/dx11/adapter.rs":"bf123464ef748d021f2e0c40d27b3f6bdd50222c6f91cce6d25686a912eef093","src/dx11/command.rs":"cdad8dcdb800acba56c931f1726ddada652af18db0f066465af643f82a034492","src/dx11/device.rs":"76ac52095c639482adc2058509cd3acafd49cebc0694fcd64f8d9f53abc823de","src/dx11/instance.rs":"3bbf2730956472cb8023bd8fbd2d53e49f93c5e4ce3d14664112a293a165d191","src/dx11/library.rs":"0da08a780eefa7ff50f2e0998117202f26e5dd3d3a433c58b585801cff9863d2","src/dx11/mod.rs":"e4f7c6100e1bec479b41f3e3af96e01d53e6597c1c3a8fcde6f14cc9eb8537f8","src/dx12/adapter.rs":"3d830a70684c568a0b3f226beecc8e0dd311c3efd2b1be2caa629f688e98511e","src/dx12/command.rs":"e48636f686f4ff9efc1758f4e54522aeda284d27439c87c6a669a55352294d58","src/dx12/conv.rs":"e1bc82d9f0c019bb67aa7ee8d59e4677c047e56fee4ce3154ebc50e5388850cd","src/dx12/descriptor.rs":"7145d3dc6be13fae4cf6bb8bf34a1ea1749ad87e5f429b84f3cbbea7bf63c148","src/dx12/device.rs":"1dd830070de6e0a755164f96408d50e5c8a1bbfee539a1183a57c8f93c79e669","src/dx12/instance.rs":"ccc36443cb1df8ab8ed8366cf8599ec3d75fb5fefa5f9bb0f0f0b5e6fc1c5102","src/dx12/mod.rs":"e88f7396dca4aba859a6e28d3f9de64a57a0df85acd53cecd6ada3d96386062c","src/dx12/view.rs":"b7a5cb8933f30517a97b4bd767e10b4c09f686dbf493d53b9b265d2d0b16f1a6","src/empty.rs":"6bf65e405f63eff49b25d079af644b352b95a9c7edcf3a57be2e96a50307b66b","src/gles/adapter.rs":"47403c6cf736659b6c035873346e0aa1760b8b4b5763e64b9783e1358e599ba0","src/gles/command.rs":"31c85f3841131dc34553f7a66339396650ceb19763fa6c194c10fb4a5a3fc07e","src/gles/conv.rs":"1462ce906a4fe83139cc8375e385f8ce5a15d70588b81083ae8d5d9104f4457e","src/gles/device.rs":"66c30c4010f410bf3b8a03ee9d8e14753832fa2b6e17b518481281f06e3d7cd9","src/gles/egl.rs":"16516ef1ad62a976996a1b2123fd89ce6835a8468a2915841efd558516bb8b4f","src/gles/mod.rs":"75612e8ddd91735ba7b1bb7ecb58210b7b8469bde9671e437206c010600d16a2","src/gles/queue.rs":"b6dd8404ff53f1f9a8c9de87d4b78bd42468c146560d26fb585801d813919dab","src/gles/shaders/clear.frag":"aac702eed9ece5482db5ba6783a678b119a5e7802b1ecf93f4975dee8acab0b3","src/gles/shaders/clear.vert":"8f636168e1da2cac48091c466a543c3b09fb4a0dd8c60c1c9bf34cc890766740","src/gles/shaders/present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"083500c0b36d079a82754895d06b993ea8ed4393690b226c85f07cbec373a730","src/lib.rs":"dbd24a5fa263412c16cf821e6ff51ebda07608776accbd8b0bfb792435740619","src/metal/adapter.rs":"78f4a9eff186ab919e7c8900c08cd0b3325e15bf1656d5dcdae30a96a9d76f87","src/metal/command.rs":"b06983d7e11cdde526b7c9f5f0b86f1ea8faef02b7666367cb231211a8301570","src/metal/conv.rs":"2349ec6331a7a471c06615be249dc22b808742aca222e6d8861662d848b0c094","src/metal/device.rs":"dd823c8e12ba3ed69ef7cdcb543e8d995d0056d1f838516b0901068c83d8ffe2","src/metal/mod.rs":"c4f3959732f5f506fa881aa5812205a6452d6a946d661d7f81d1c7785359a10c","src/metal/surface.rs":"82836cadc751d94fb016bd590cdfec5649cbfae2f44d14599ed074dfb0a004dc","src/vulkan/adapter.rs":"90c4f57483589a09d9840c3f93efb8da66bc9eb5be975899877aa0192f86e4bd","src/vulkan/command.rs":"60d1867acd0e46c34dabecea708cd776a1f435721b6673a506b5bb8aee87ff80","src/vulkan/conv.rs":"b480f9d1cde0df92d6f9a07e8a42b86aaeb251f9b0692038286f4994caf45fec","src/vulkan/device.rs":"9b264c74f581345be889f1ed61ad6f7ab22e12e04183eb954dbfed1681c32d0c","src/vulkan/instance.rs":"c078d529f6955a662a3adc7739ffdb8a01b83dbef8dd1e2c3810d232b82cbb18","src/vulkan/mod.rs":"1ba41f2ea7650dc0757c1444ef62c95a8aa0f6671d98c73b4ea80eb4ea60f289"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-hal/Cargo.toml b/third_party/rust/wgpu-hal/Cargo.toml index 9cc851d609d9c..caad696d40c3b 100644 --- a/third_party/rust/wgpu-hal/Cargo.toml +++ b/third_party/rust/wgpu-hal/Cargo.toml @@ -51,7 +51,7 @@ foreign-types = { version = "0.3", optional = true } ash = { version = "0.37", optional = true } gpu-alloc = { version = "0.5", optional = true } gpu-descriptor = { version = "0.2", optional = true } -inplace_it = { version ="0.3.3", optional = true } +inplace_it = { version = "0.3.3", optional = true } # backend: Gles glow = { version = "0.11.1", optional = true } @@ -92,20 +92,20 @@ js-sys = { version = "0.3" } [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "1aa91549" +rev = "571302e" #version = "0.8" # DEV dependencies [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "1aa91549" +rev = "571302e" #version = "0.8" features = ["wgsl-in"] [dev-dependencies] env_logger = "0.9" -winit = "0.26" # for "halmark" example +winit = "0.26" # for "halmark" example [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] glutin = "0.28" # for "gles" example diff --git a/third_party/rust/wgpu-hal/LICENSE.APACHE b/third_party/rust/wgpu-hal/LICENSE.APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/third_party/rust/wgpu-hal/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/third_party/rust/wgpu-hal/LICENSE.MIT b/third_party/rust/wgpu-hal/LICENSE.MIT new file mode 100644 index 0000000000000..4699691b8ed37 --- /dev/null +++ b/third_party/rust/wgpu-hal/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/wgpu-hal/examples/halmark/main.rs b/third_party/rust/wgpu-hal/examples/halmark/main.rs index 1ed59325c15a1..ff021a889d23b 100644 --- a/third_party/rust/wgpu-hal/examples/halmark/main.rs +++ b/third_party/rust/wgpu-hal/examples/halmark/main.rs @@ -685,7 +685,7 @@ impl Example { let target_barrier1 = hal::TextureBarrier { texture: surface_tex.borrow(), range: wgt::ImageSubresourceRange::default(), - usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(), + usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::PRESENT, }; unsafe { ctx.encoder.end_render_pass(); diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs index dfcb95b0fc61b..384775c3e074d 100644 --- a/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs @@ -47,8 +47,9 @@ pub fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT, Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT, + Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT, - Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth24PlusStencil8 | Tf::Depth24UnormStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, @@ -96,9 +97,12 @@ pub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> dxgiformat::DXGI pub fn map_texture_format_nodepth(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { match format { wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_FLOAT, - wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { - dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS + wgt::TextureFormat::Depth32FloatStencil8 => { + dxgiformat::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS } + wgt::TextureFormat::Depth24Plus + | wgt::TextureFormat::Depth24PlusStencil8 + | wgt::TextureFormat::Depth24UnormStencil8 => dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS, _ => { assert_eq!( crate::FormatAspects::from(format), @@ -112,9 +116,10 @@ pub fn map_texture_format_nodepth(format: wgt::TextureFormat) -> dxgiformat::DXG pub fn map_texture_format_depth_typeless(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { match format { wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_TYPELESS, - wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { - dxgiformat::DXGI_FORMAT_R24G8_TYPELESS - } + wgt::TextureFormat::Depth32FloatStencil8 => dxgiformat::DXGI_FORMAT_R32G8X24_TYPELESS, + wgt::TextureFormat::Depth24Plus + | wgt::TextureFormat::Depth24PlusStencil8 + | wgt::TextureFormat::Depth24UnormStencil8 => dxgiformat::DXGI_FORMAT_R24G8_TYPELESS, _ => unreachable!(), } } diff --git a/third_party/rust/wgpu-hal/src/dx12/adapter.rs b/third_party/rust/wgpu-hal/src/dx12/adapter.rs index 3be86494d8084..039eb9ab11eea 100644 --- a/third_party/rust/wgpu-hal/src/dx12/adapter.rs +++ b/third_party/rust/wgpu-hal/src/dx12/adapter.rs @@ -185,6 +185,8 @@ impl super::Adapter { let mut features = wgt::Features::empty() | wgt::Features::DEPTH_CLIP_CONTROL + | wgt::Features::DEPTH24UNORM_STENCIL8 + | wgt::Features::DEPTH32FLOAT_STENCIL8 | wgt::Features::INDIRECT_FIRST_INSTANCE | wgt::Features::MAPPABLE_PRIMARY_BUFFERS | wgt::Features::MULTI_DRAW_INDIRECT diff --git a/third_party/rust/wgpu-hal/src/dx12/command.rs b/third_party/rust/wgpu-hal/src/dx12/command.rs index 95546d213abb8..dc653911c5f94 100644 --- a/third_party/rust/wgpu-hal/src/dx12/command.rs +++ b/third_party/rust/wgpu-hal/src/dx12/command.rs @@ -287,7 +287,7 @@ impl crate::CommandEncoder for super::CommandEncoder { StateAfter: s1, }; self.temp.barriers.push(raw); - } else if barrier.usage.start == crate::BufferUses::STORAGE_WRITE { + } else if barrier.usage.start == crate::BufferUses::STORAGE_READ_WRITE { let mut raw = d3d12::D3D12_RESOURCE_BARRIER { Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, @@ -382,7 +382,7 @@ impl crate::CommandEncoder for super::CommandEncoder { } } } - } else if barrier.usage.start == crate::TextureUses::STORAGE_WRITE { + } else if barrier.usage.start == crate::TextureUses::STORAGE_READ_WRITE { let mut raw = d3d12::D3D12_RESOURCE_BARRIER { Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, diff --git a/third_party/rust/wgpu-hal/src/dx12/conv.rs b/third_party/rust/wgpu-hal/src/dx12/conv.rs index 6ea0339afb626..8dfd40c92b94d 100644 --- a/third_party/rust/wgpu-hal/src/dx12/conv.rs +++ b/third_party/rust/wgpu-hal/src/dx12/conv.rs @@ -3,7 +3,7 @@ use winapi::um::{d3d12, d3dcommon}; pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS { let mut flags = 0; - if usage.contains(crate::BufferUses::STORAGE_WRITE) { + if usage.contains(crate::BufferUses::STORAGE_READ_WRITE) { flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } flags @@ -33,7 +33,7 @@ pub fn map_texture_usage_to_resource_flags( flags |= d3d12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; } } - if usage.contains(crate::TextureUses::STORAGE_WRITE) { + if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) { flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } @@ -130,7 +130,7 @@ pub fn map_buffer_usage_to_state(usage: crate::BufferUses) -> d3d12::D3D12_RESOU if usage.intersects(Bu::VERTEX | Bu::UNIFORM) { state |= d3d12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; } - if usage.intersects(Bu::STORAGE_WRITE) { + if usage.intersects(Bu::STORAGE_READ_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } else if usage.intersects(Bu::STORAGE_READ) { state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE @@ -170,7 +170,7 @@ pub fn map_texture_usage_to_state(usage: crate::TextureUses) -> d3d12::D3D12_RES if usage.intersects(Tu::DEPTH_STENCIL_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE; } - if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_WRITE) { + if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE) { state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } state diff --git a/third_party/rust/wgpu-hal/src/dx12/device.rs b/third_party/rust/wgpu-hal/src/dx12/device.rs index 2f92cab44c2e6..c25f58c76794d 100644 --- a/third_party/rust/wgpu-hal/src/dx12/device.rs +++ b/third_party/rust/wgpu-hal/src/dx12/device.rs @@ -428,7 +428,7 @@ impl crate::Device for super::Device { || !desc.usage.intersects( crate::TextureUses::RESOURCE | crate::TextureUses::STORAGE_READ - | crate::TextureUses::STORAGE_WRITE, + | crate::TextureUses::STORAGE_READ_WRITE, ) { auxil::dxgi::conv::map_texture_format(desc.format) } else { @@ -516,10 +516,9 @@ impl crate::Device for super::Device { } else { None }, - handle_uav: if desc - .usage - .intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE) - { + handle_uav: if desc.usage.intersects( + crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE, + ) { let raw_desc = view_desc.to_uav(); let handle = self.srv_uav_pool.lock().alloc_handle(); self.raw.CreateUnorderedAccessView( diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs index 7535c33cf258e..90f845813b7e5 100644 --- a/third_party/rust/wgpu-hal/src/gles/adapter.rs +++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs @@ -659,7 +659,11 @@ impl crate::Adapter for super::Adapter { Tf::Rgba32Uint => renderable | storage, Tf::Rgba32Sint => renderable | storage, Tf::Rgba32Float => unfilterable | storage, - Tf::Depth32Float | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => depth, + Tf::Depth32Float + | Tf::Depth32FloatStencil8 + | Tf::Depth24Plus + | Tf::Depth24PlusStencil8 + | Tf::Depth24UnormStencil8 => depth, Tf::Rgb9e5Ufloat | Tf::Bc1RgbaUnorm | Tf::Bc1RgbaUnormSrgb diff --git a/third_party/rust/wgpu-hal/src/gles/command.rs b/third_party/rust/wgpu-hal/src/gles/command.rs index e316844b66cb3..307a2865c3dc6 100644 --- a/third_party/rust/wgpu-hal/src/gles/command.rs +++ b/third_party/rust/wgpu-hal/src/gles/command.rs @@ -230,7 +230,11 @@ impl crate::CommandEncoder for super::CommandEncoder { } for bar in barriers { // GLES only synchronizes storage -> anything explicitly - if !bar.usage.start.contains(crate::BufferUses::STORAGE_WRITE) { + if !bar + .usage + .start + .contains(crate::BufferUses::STORAGE_READ_WRITE) + { continue; } self.cmd_buffer @@ -253,7 +257,11 @@ impl crate::CommandEncoder for super::CommandEncoder { let mut combined_usage = crate::TextureUses::empty(); for bar in barriers { // GLES only synchronizes storage -> anything explicitly - if !bar.usage.start.contains(crate::TextureUses::STORAGE_WRITE) { + if !bar + .usage + .start + .contains(crate::TextureUses::STORAGE_READ_WRITE) + { continue; } // unlike buffers, there is no need for a concrete texture @@ -520,12 +528,19 @@ impl crate::CommandEncoder for super::CommandEncoder { } } if let Some(ref dsat) = desc.depth_stencil_attachment { - if !dsat.depth_ops.contains(crate::AttachmentOps::LOAD) { + let clear_depth = !dsat.depth_ops.contains(crate::AttachmentOps::LOAD); + let clear_stencil = !dsat.stencil_ops.contains(crate::AttachmentOps::LOAD); + + if clear_depth && clear_stencil { + self.cmd_buffer.commands.push(C::ClearDepthAndStencil( + dsat.clear_value.0, + dsat.clear_value.1, + )); + } else if clear_depth { self.cmd_buffer .commands .push(C::ClearDepth(dsat.clear_value.0)); - } - if !dsat.stencil_ops.contains(crate::AttachmentOps::LOAD) { + } else if clear_stencil { self.cmd_buffer .commands .push(C::ClearStencil(dsat.clear_value.1)); diff --git a/third_party/rust/wgpu-hal/src/gles/conv.rs b/third_party/rust/wgpu-hal/src/gles/conv.rs index d7a9268c287dd..bf0f3bf0bd977 100644 --- a/third_party/rust/wgpu-hal/src/gles/conv.rs +++ b/third_party/rust/wgpu-hal/src/gles/conv.rs @@ -57,12 +57,15 @@ impl super::AdapterShared { Tf::Rgba32Sint => (glow::RGBA32I, glow::RGBA_INTEGER, glow::INT), Tf::Rgba32Float => (glow::RGBA32F, glow::RGBA, glow::FLOAT), Tf::Depth32Float => (glow::DEPTH_COMPONENT32F, glow::DEPTH_COMPONENT, glow::FLOAT), + Tf::Depth32FloatStencil8 => { + (glow::DEPTH32F_STENCIL8, glow::DEPTH_COMPONENT, glow::FLOAT) + } Tf::Depth24Plus => ( glow::DEPTH_COMPONENT24, glow::DEPTH_COMPONENT, glow::UNSIGNED_NORMALIZED, ), - Tf::Depth24PlusStencil8 => ( + Tf::Depth24PlusStencil8 | Tf::Depth24UnormStencil8 => ( glow::DEPTH24_STENCIL8, glow::DEPTH_COMPONENT, glow::UNSIGNED_INT, diff --git a/third_party/rust/wgpu-hal/src/gles/egl.rs b/third_party/rust/wgpu-hal/src/gles/egl.rs index 9539fe211e908..06167ec841ae5 100644 --- a/third_party/rust/wgpu-hal/src/gles/egl.rs +++ b/third_party/rust/wgpu-hal/src/gles/egl.rs @@ -580,6 +580,24 @@ pub struct Instance { inner: Mutex, } +impl Instance { + pub fn raw_display(&self) -> egl::Display { + self.inner + .try_lock() + .expect("Could not lock instance. This is most-likely a deadlock.") + .egl + .display + } + + /// Returns the version of the EGL display. + pub fn egl_version(&self) -> (i32, i32) { + self.inner + .try_lock() + .expect("Could not lock instance. This is most-likely a deadlock.") + .version + } +} + unsafe impl Send for Instance {} unsafe impl Sync for Instance {} diff --git a/third_party/rust/wgpu-hal/src/gles/mod.rs b/third_party/rust/wgpu-hal/src/gles/mod.rs index 321db19027e9a..338cbca774cc4 100644 --- a/third_party/rust/wgpu-hal/src/gles/mod.rs +++ b/third_party/rust/wgpu-hal/src/gles/mod.rs @@ -667,6 +667,11 @@ enum Command { ClearColorI(u32, [i32; 4]), ClearDepth(f32), ClearStencil(u32), + // Clearing both the depth and stencil buffer individually appears to + // result in the stencil buffer failing to clear, atleast in WebGL. + // It is also more efficient to emit a single command instead of two for + // this. + ClearDepthAndStencil(f32, u32), BufferBarrier(glow::Buffer, crate::BufferUses), TextureBarrier(crate::TextureUses), SetViewport { diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs index c223c2a85158b..2af9e72f21847 100644 --- a/third_party/rust/wgpu-hal/src/gles/queue.rs +++ b/third_party/rust/wgpu-hal/src/gles/queue.rs @@ -758,6 +758,9 @@ impl super::Queue { C::ClearStencil(value) => { gl.clear_buffer_i32_slice(glow::STENCIL, 0, &[value as i32]); } + C::ClearDepthAndStencil(depth, stencil_value) => { + gl.clear_buffer_depth_stencil(glow::DEPTH_STENCIL, 0, depth, stencil_value as i32); + } C::BufferBarrier(raw, usage) => { let mut flags = 0; if usage.contains(crate::BufferUses::VERTEX) { @@ -787,9 +790,9 @@ impl super::Queue { if usage.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE) { flags |= glow::BUFFER_UPDATE_BARRIER_BIT; } - if usage - .intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) - { + if usage.intersects( + crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE, + ) { flags |= glow::SHADER_STORAGE_BARRIER_BIT; } gl.memory_barrier(flags); @@ -800,7 +803,7 @@ impl super::Queue { flags |= glow::TEXTURE_FETCH_BARRIER_BIT; } if usage.intersects( - crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE, + crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE, ) { flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT; } diff --git a/third_party/rust/wgpu-hal/src/lib.rs b/third_party/rust/wgpu-hal/src/lib.rs index 2cb03e257299a..1ce7c657bae30 100644 --- a/third_party/rust/wgpu-hal/src/lib.rs +++ b/third_party/rust/wgpu-hal/src/lib.rs @@ -602,7 +602,9 @@ impl From for FormatAspects { fn from(format: wgt::TextureFormat) -> Self { match format { wgt::TextureFormat::Depth32Float | wgt::TextureFormat::Depth24Plus => Self::DEPTH, - wgt::TextureFormat::Depth24PlusStencil8 => Self::DEPTH | Self::STENCIL, + wgt::TextureFormat::Depth32FloatStencil8 + | wgt::TextureFormat::Depth24PlusStencil8 + | wgt::TextureFormat::Depth24UnormStencil8 => Self::DEPTH | Self::STENCIL, _ => Self::COLOR, } } @@ -626,53 +628,77 @@ bitflags!( bitflags::bitflags! { /// Similar to `wgt::BufferUsages` but for internal use. - pub struct BufferUses: u32 { + pub struct BufferUses: u16 { + /// The argument to a read-only mapping. const MAP_READ = 1 << 0; + /// The argument to a write-only mapping. const MAP_WRITE = 1 << 1; + /// The source of a hardware copy. const COPY_SRC = 1 << 2; + /// The destination of a hardware copy. const COPY_DST = 1 << 3; + /// The index buffer used for drawing. const INDEX = 1 << 4; + /// A vertex buffer used for drawing. const VERTEX = 1 << 5; + /// A uniform buffer bound in a bind group. const UNIFORM = 1 << 6; + /// A read-only storage buffer used in a bind group. const STORAGE_READ = 1 << 7; - const STORAGE_WRITE = 1 << 8; + /// A read-write or write-only buffer used in a bind group. + const STORAGE_READ_WRITE = 1 << 8; + /// The indirect or count buffer in a indirect draw or dispatch. const INDIRECT = 1 << 9; - /// The combination of usages that can be used together (read-only). + /// The combination of states that a buffer may be in _at the same time_. const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits | Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | Self::STORAGE_READ.bits | Self::INDIRECT.bits; - /// The combination of exclusive usages (write-only and read-write). - /// These usages may still show up with others, but can't automatically be combined. - const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits; + /// The combination of states that a buffer must exclusively be in. + const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_READ_WRITE.bits; /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. + /// If a usage is ordered, then if the buffer state doesn't change between draw calls, there + /// are no barriers needed for synchronization. const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits; } } bitflags::bitflags! { /// Similar to `wgt::TextureUsages` but for internal use. - pub struct TextureUses: u32 { - const COPY_SRC = 1 << 0; - const COPY_DST = 1 << 1; - const RESOURCE = 1 << 2; - const COLOR_TARGET = 1 << 3; - const DEPTH_STENCIL_READ = 1 << 4; - const DEPTH_STENCIL_WRITE = 1 << 5; - const STORAGE_READ = 1 << 6; - const STORAGE_WRITE = 1 << 7; - /// The combination of usages that can be used together (read-only). + pub struct TextureUses: u16 { + /// The texture is in unknown state. + const UNINITIALIZED = 1 << 0; + /// Ready to present image to the surface. + const PRESENT = 1 << 1; + /// The source of a hardware copy. + const COPY_SRC = 1 << 2; + /// The destination of a hardware copy. + const COPY_DST = 1 << 3; + /// Read-only sampled or fetched resource. + const RESOURCE = 1 << 4; + /// The color target of a renderpass. + const COLOR_TARGET = 1 << 5; + /// Read-only depth stencil usage. + const DEPTH_STENCIL_READ = 1 << 6; + /// Read-write depth stencil usage + const DEPTH_STENCIL_WRITE = 1 << 7; + /// Read-only storage buffer usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only. + const STORAGE_READ = 1 << 8; + /// Read-write or write-only storage buffer usage. + const STORAGE_READ_WRITE = 1 << 9; + /// The combination of states that a texture may be in _at the same time_. const INCLUSIVE = Self::COPY_SRC.bits | Self::RESOURCE.bits | Self::DEPTH_STENCIL_READ.bits; - /// The combination of exclusive usages (write-only and read-write). - /// These usages may still show up with others, but can't automatically be combined. - const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_WRITE.bits; + /// The combination of states that a texture must exclusively be in. + const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_READ_WRITE.bits | Self::PRESENT.bits; /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. + /// If a usage is ordered, then if the texture state doesn't change between draw calls, there + /// are no barriers needed for synchronization. const ORDERED = Self::INCLUSIVE.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits; - //TODO: remove this - const UNINITIALIZED = 0xFFFF; + + /// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource + const COMPLEX = 1 << 10; + /// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource. + /// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized. + const UNKNOWN = 1 << 11; } } diff --git a/third_party/rust/wgpu-hal/src/metal/adapter.rs b/third_party/rust/wgpu-hal/src/metal/adapter.rs index 53201c147220b..f59237100f99d 100644 --- a/third_party/rust/wgpu-hal/src/metal/adapter.rs +++ b/third_party/rust/wgpu-hal/src/metal/adapter.rs @@ -180,21 +180,31 @@ impl crate::Adapter for super::Adapter { }; flags } - Tf::Depth32Float => { - let mut flats = + Tf::Depth32Float | Tf::Depth32FloatStencil8 => { + let mut flags = Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::MULTISAMPLE | msaa_resolve_apple3x_if; if pc.format_depth32float_filter { - flats |= Tfc::SAMPLED_LINEAR + flags |= Tfc::SAMPLED_LINEAR } - flats + flags } - Tf::Depth24Plus => Tfc::empty(), - Tf::Depth24PlusStencil8 => { - if pc.msaa_desktop { - Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::SAMPLED_LINEAR | Tfc::MULTISAMPLE + Tf::Depth24Plus | Tf::Depth24PlusStencil8 => { + let mut flags = Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::MULTISAMPLE; + if pc.format_depth24_stencil8 { + flags |= Tfc::SAMPLED_LINEAR | Tfc::MULTISAMPLE_RESOLVE } else { - Tfc::empty() + flags |= msaa_resolve_apple3x_if; + if pc.format_depth32float_filter { + flags |= Tfc::SAMPLED_LINEAR + } } + flags + } + Tf::Depth24UnormStencil8 => { + Tfc::DEPTH_STENCIL_ATTACHMENT + | Tfc::SAMPLED_LINEAR + | Tfc::MULTISAMPLE + | Tfc::MULTISAMPLE_RESOLVE } Tf::Rgb9e5Ufloat => { if pc.msaa_apple3 { @@ -749,7 +759,8 @@ impl super::PrivateCapabilities { | F::POLYGON_MODE_LINE | F::CLEAR_TEXTURE | F::TEXTURE_FORMAT_16BIT_NORM - | F::SHADER_FLOAT16; + | F::SHADER_FLOAT16 + | F::DEPTH32FLOAT_STENCIL8; features.set(F::TEXTURE_COMPRESSION_ASTC_LDR, self.format_astc); features.set(F::TEXTURE_COMPRESSION_ASTC_HDR, self.format_astc_hdr); @@ -757,6 +768,7 @@ impl super::PrivateCapabilities { features.set(F::TEXTURE_COMPRESSION_ETC2, self.format_eac_etc); features.set(F::DEPTH_CLIP_CONTROL, self.supports_depth_clip_control); + features.set(F::DEPTH24UNORM_STENCIL8, self.format_depth24_stencil8); features.set( F::TEXTURE_BINDING_ARRAY @@ -889,6 +901,7 @@ impl super::PrivateCapabilities { Tf::Rgba32Sint => RGBA32Sint, Tf::Rgba32Float => RGBA32Float, Tf::Depth32Float => Depth32Float, + Tf::Depth32FloatStencil8 => Depth32Float_Stencil8, Tf::Depth24Plus => { if self.format_depth24_stencil8 { Depth24Unorm_Stencil8 @@ -903,6 +916,7 @@ impl super::PrivateCapabilities { Depth32Float_Stencil8 } } + Tf::Depth24UnormStencil8 => Depth24Unorm_Stencil8, Tf::Rgb9e5Ufloat => RGB9E5Float, Tf::Bc1RgbaUnorm => BC1_RGBA, Tf::Bc1RgbaUnormSrgb => BC1_RGBA_sRGB, diff --git a/third_party/rust/wgpu-hal/src/metal/conv.rs b/third_party/rust/wgpu-hal/src/metal/conv.rs index f275a74ec98ca..75ed58df24868 100644 --- a/third_party/rust/wgpu-hal/src/metal/conv.rs +++ b/third_party/rust/wgpu-hal/src/metal/conv.rs @@ -9,11 +9,13 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> mtl::MTLTextureUsage { ); mtl_usage.set( mtl::MTLTextureUsage::ShaderRead, - usage.intersects(Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ), + usage.intersects( + Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE, + ), ); mtl_usage.set( mtl::MTLTextureUsage::ShaderWrite, - usage.intersects(Tu::STORAGE_WRITE), + usage.intersects(Tu::STORAGE_READ_WRITE), ); mtl_usage diff --git a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs index a6ad22e5c0abf..e2b914cb6a004 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs @@ -561,6 +561,24 @@ impl PhysicalDeviceFeatures { ); } + features.set( + F::DEPTH32FLOAT_STENCIL8, + caps.supports_format( + vk::Format::D32_SFLOAT_S8_UINT, + vk::ImageTiling::OPTIMAL, + vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT, + ), + ); + + features.set( + F::DEPTH24UNORM_STENCIL8, + caps.supports_format( + vk::Format::D24_UNORM_S8_UINT, + vk::ImageTiling::OPTIMAL, + vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT, + ), + ); + (features, dl_flags) } @@ -1102,6 +1120,10 @@ impl super::Instance { } impl super::Adapter { + pub fn raw_physical_device(&self) -> ash::vk::PhysicalDevice { + self.raw + } + pub fn required_device_extensions(&self, features: wgt::Features) -> Vec<&'static CStr> { let (supported_extensions, unsupported_extensions) = self .phd_capabilities diff --git a/third_party/rust/wgpu-hal/src/vulkan/conv.rs b/third_party/rust/wgpu-hal/src/vulkan/conv.rs index 727502aed8097..df24e8e6a7d1f 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/conv.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/conv.rs @@ -49,6 +49,7 @@ impl super::PrivateCapabilities { Tf::Rgba32Sint => F::R32G32B32A32_SINT, Tf::Rgba32Float => F::R32G32B32A32_SFLOAT, Tf::Depth32Float => F::D32_SFLOAT, + Tf::Depth32FloatStencil8 => F::D32_SFLOAT_S8_UINT, Tf::Depth24Plus => { if self.texture_d24 { F::X8_D24_UNORM_PACK32 @@ -63,6 +64,7 @@ impl super::PrivateCapabilities { F::D32_SFLOAT_S8_UINT } } + Tf::Depth24UnormStencil8 => F::D24_UNORM_S8_UINT, Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32, Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK, Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK, @@ -199,7 +201,7 @@ pub fn derive_image_layout( vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL } _ => { - if usage.is_empty() { + if usage == crate::TextureUses::PRESENT { vk::ImageLayout::PRESENT_SRC_KHR } else if is_color { vk::ImageLayout::GENERAL @@ -229,7 +231,7 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> vk::ImageUsageFlags { ) { flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT; } - if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE) { + if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE) { flags |= vk::ImageUsageFlags::STORAGE; } flags @@ -275,12 +277,12 @@ pub fn map_texture_usage_to_barrier( stages |= shader_stages; access |= vk::AccessFlags::SHADER_READ; } - if usage.contains(crate::TextureUses::STORAGE_WRITE) { + if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) { stages |= shader_stages; - access |= vk::AccessFlags::SHADER_WRITE; + access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE; } - if usage == crate::TextureUses::UNINITIALIZED || usage.is_empty() { + if usage == crate::TextureUses::UNINITIALIZED || usage == crate::TextureUses::PRESENT { ( vk::PipelineStageFlags::TOP_OF_PIPE, vk::AccessFlags::empty(), @@ -308,7 +310,7 @@ pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> crate::TextureUses { bits |= crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE; } if usage.contains(vk::ImageUsageFlags::STORAGE) { - bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE; + bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE; } bits } @@ -456,7 +458,7 @@ pub fn map_buffer_usage(usage: crate::BufferUses) -> vk::BufferUsageFlags { if usage.contains(crate::BufferUses::UNIFORM) { flags |= vk::BufferUsageFlags::UNIFORM_BUFFER; } - if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) { + if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE) { flags |= vk::BufferUsageFlags::STORAGE_BUFFER; } if usage.contains(crate::BufferUses::INDEX) { @@ -504,9 +506,9 @@ pub fn map_buffer_usage_to_barrier( stages |= shader_stages; access |= vk::AccessFlags::SHADER_READ; } - if usage.intersects(crate::BufferUses::STORAGE_WRITE) { + if usage.intersects(crate::BufferUses::STORAGE_READ_WRITE) { stages |= shader_stages; - access |= vk::AccessFlags::SHADER_WRITE; + access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE; } if usage.contains(crate::BufferUses::INDEX) { stages |= vk::PipelineStageFlags::VERTEX_INPUT; diff --git a/third_party/rust/wgpu-hal/src/vulkan/device.rs b/third_party/rust/wgpu-hal/src/vulkan/device.rs index 24f992953f27b..3dd6b8b31b009 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/device.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/device.rs @@ -1413,7 +1413,7 @@ impl crate::Device for super::Device { unsafe fn destroy_shader_module(&self, module: super::ShaderModule) { match module { super::ShaderModule::Raw(raw) => { - let _ = self.shared.raw.destroy_shader_module(raw, None); + self.shared.raw.destroy_shader_module(raw, None); } super::ShaderModule::Intermediate { .. } => {} } diff --git a/third_party/rust/wgpu-hal/src/vulkan/instance.rs b/third_party/rust/wgpu-hal/src/vulkan/instance.rs index 6dff2fd485dd4..4a814fc16c1dd 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/instance.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/instance.rs @@ -133,6 +133,22 @@ impl super::Swapchain { } impl super::Instance { + pub fn entry(&self) -> &ash::Entry { + &self.shared.entry + } + + pub fn raw_instance(&self) -> &ash::Instance { + &self.shared.raw + } + + pub fn driver_api_version(&self) -> u32 { + self.shared.driver_api_version + } + + pub fn extensions(&self) -> &[&'static CStr] { + &self.extensions[..] + } + pub fn required_extensions( entry: &ash::Entry, flags: crate::InstanceFlags, @@ -266,6 +282,7 @@ impl super::Instance { get_physical_device_properties, entry, has_nv_optimus, + driver_api_version, }), extensions, }) diff --git a/third_party/rust/wgpu-hal/src/vulkan/mod.rs b/third_party/rust/wgpu-hal/src/vulkan/mod.rs index 0b7e1164371f2..b3e1a0aa7f726 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/mod.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/mod.rs @@ -87,6 +87,7 @@ struct InstanceShared { get_physical_device_properties: Option, entry: ash::Entry, has_nv_optimus: bool, + driver_api_version: u32, } pub struct Instance { diff --git a/third_party/rust/wgpu-types/.cargo-checksum.json b/third_party/rust/wgpu-types/.cargo-checksum.json index e06dbceb77c27..6c6757b36c8f3 100644 --- a/third_party/rust/wgpu-types/.cargo-checksum.json +++ b/third_party/rust/wgpu-types/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"742abe387ad0a6b054a3e525040ff1e64f16072ae7cd305db0616beaa844e389","src/lib.rs":"f1262748d81d80812fadec75241a6128842db02dced79c5e6db54586f9b35002"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"742abe387ad0a6b054a3e525040ff1e64f16072ae7cd305db0616beaa844e389","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/lib.rs":"bfac3d39cff5da6d59889dc72206ddae4d553a8aaae20c890a204d3bdc7cdf6a"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-types/LICENSE.APACHE b/third_party/rust/wgpu-types/LICENSE.APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/third_party/rust/wgpu-types/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/third_party/rust/wgpu-types/LICENSE.MIT b/third_party/rust/wgpu-types/LICENSE.MIT new file mode 100644 index 0000000000000..4699691b8ed37 --- /dev/null +++ b/third_party/rust/wgpu-types/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/wgpu-types/src/lib.rs b/third_party/rust/wgpu-types/src/lib.rs index 9277eb16ab93a..5e3a010b5a147 100644 --- a/third_party/rust/wgpu-types/src/lib.rs +++ b/third_party/rust/wgpu-types/src/lib.rs @@ -185,6 +185,24 @@ bitflags::bitflags! { /// /// This is a web and native feature. const DEPTH_CLIP_CONTROL = 1 << 0; + /// Allows for explicit creation of textures of format [`TextureFormat::Depth24UnormStencil8`] + /// + /// Supported platforms: + /// - Vulkan (some) + /// - DX12 + /// - Metal (Macs with amd GPUs) + /// + /// This is a web and native feature. + const DEPTH24UNORM_STENCIL8 = 1 << 1; + /// Allows for explicit creation of textures of format [`TextureFormat::Depth32FloatStencil8`] + /// + /// Supported platforms: + /// - Vulkan (mostly) + /// - DX12 + /// - Metal + /// + /// This is a web and native feature. + const DEPTH32FLOAT_STENCIL8 = 1 << 2; /// Enables BCn family of compressed textures. All BCn textures use 4x4 pixel blocks /// with 8 or 16 bytes per block. /// @@ -198,7 +216,37 @@ bitflags::bitflags! { /// - desktops /// /// This is a web and native feature. - const TEXTURE_COMPRESSION_BC = 1 << 1; + const TEXTURE_COMPRESSION_BC = 1 << 3; + /// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks. + /// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block. + /// + /// Compressed textures sacrifice some quality in exchange for significantly reduced + /// bandwidth usage. + /// + /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ETC2 formats. + /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages. + /// + /// Supported Platforms: + /// - Intel/Vulkan + /// - Mobile (some) + /// + /// This is a web and native feature. + const TEXTURE_COMPRESSION_ETC2 = 1 << 4; + /// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12. + /// Blocks are always 16 bytes. + /// + /// Compressed textures sacrifice some quality in exchange for significantly reduced + /// bandwidth usage. + /// + /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ASTC formats. + /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages. + /// + /// Supported Platforms: + /// - Intel/Vulkan + /// - Mobile (some) + /// + /// This is a web and native feature. + const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 5; /// Allows non-zero value for the "first instance" in indirect draw calls. /// /// Supported Platforms: @@ -207,7 +255,7 @@ bitflags::bitflags! { /// - Metal /// /// This is a web and native feature. - const INDIRECT_FIRST_INSTANCE = 1 << 2; + const INDIRECT_FIRST_INSTANCE = 1 << 6; /// Enables use of Timestamp Queries. These queries tell the current gpu timestamp when /// all work before the query is finished. Call [`CommandEncoder::write_timestamp`], /// [`RenderPassEncoder::write_timestamp`], or [`ComputePassEncoder::write_timestamp`] to @@ -225,7 +273,7 @@ bitflags::bitflags! { /// - DX12 (works) /// /// This is a web and native feature. - const TIMESTAMP_QUERY = 1 << 3; + const TIMESTAMP_QUERY = 1 << 7; /// Enables use of Pipeline Statistics Queries. These queries tell the count of various operations /// performed between the start and stop call. Call [`RenderPassEncoder::begin_pipeline_statistics_query`] to start /// a query, then call [`RenderPassEncoder::end_pipeline_statistics_query`] to stop one. @@ -240,7 +288,7 @@ bitflags::bitflags! { /// - DX12 (works) /// /// This is a web and native feature. - const PIPELINE_STATISTICS_QUERY = 1 << 4; + const PIPELINE_STATISTICS_QUERY = 1 << 8; /// Allows shaders to acquire the FP16 ability /// /// Note: this is not supported in naga yet,only through spir-v passthrough right now. @@ -250,7 +298,7 @@ bitflags::bitflags! { /// - Metal /// /// This is a web and native feature. - const SHADER_FLOAT16 = 1 << 5; + const SHADER_FLOAT16 = 1 << 9; /// Webgpu only allows the MAP_READ and MAP_WRITE buffer usage to be matched with /// COPY_DST and COPY_SRC respectively. This removes this requirement. /// @@ -437,36 +485,6 @@ bitflags::bitflags! { /// /// This is a native only feature. const POLYGON_MODE_POINT = 1 << 28; - /// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks. - /// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block. - /// - /// Compressed textures sacrifice some quality in exchange for significantly reduced - /// bandwidth usage. - /// - /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ETC2 formats. - /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages. - /// - /// Supported Platforms: - /// - Intel/Vulkan - /// - Mobile (some) - /// - /// This is a native-only feature. - const TEXTURE_COMPRESSION_ETC2 = 1 << 29; - /// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12. - /// Blocks are always 16 bytes. - /// - /// Compressed textures sacrifice some quality in exchange for significantly reduced - /// bandwidth usage. - /// - /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ASTC formats. - /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages. - /// - /// Supported Platforms: - /// - Intel/Vulkan - /// - Mobile (some) - /// - /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 30; /// Enables device specific texture format features. /// /// See `TextureFormatFeatures` for a listing of the features in question. @@ -478,7 +496,7 @@ bitflags::bitflags! { /// This extension does not enable additional formats. /// /// This is a native-only feature. - const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31; + const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 29; /// Enables 64-bit floating point types in SPIR-V shaders. /// /// Note: even when supported by GPU hardware, 64-bit floating point operations are @@ -488,7 +506,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native-only feature. - const SHADER_FLOAT64 = 1 << 32; + const SHADER_FLOAT64 = 1 << 30; /// Enables using 64-bit types for vertex attributes. /// /// Requires SHADER_FLOAT64. @@ -496,7 +514,7 @@ bitflags::bitflags! { /// Supported Platforms: N/A /// /// This is a native-only feature. - const VERTEX_ATTRIBUTE_64BIT = 1 << 33; + const VERTEX_ATTRIBUTE_64BIT = 1 << 31; /// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`] /// /// Processing of degenerate triangles/lines is hardware specific. @@ -506,7 +524,7 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const CONSERVATIVE_RASTERIZATION = 1 << 34; + const CONSERVATIVE_RASTERIZATION = 1 << 32; /// Enables bindings of writable storage buffers and textures visible to vertex shaders. /// /// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects. @@ -515,14 +533,14 @@ bitflags::bitflags! { /// - All /// /// This is a native-only feature. - const VERTEX_WRITABLE_STORAGE = 1 << 35; + const VERTEX_WRITABLE_STORAGE = 1 << 33; /// Enables clear to zero for textures. /// /// Supported platforms: /// - All /// /// This is a native only feature. - const CLEAR_TEXTURE = 1 << 36; + const CLEAR_TEXTURE = 1 << 34; /// Enables creating shader modules from SPIR-V binary data (unsafe). /// /// SPIR-V data is not parsed or interpreted in any way; you can use @@ -534,7 +552,7 @@ bitflags::bitflags! { /// Vulkan implementation. /// /// This is a native only feature. - const SPIRV_SHADER_PASSTHROUGH = 1 << 37; + const SPIRV_SHADER_PASSTHROUGH = 1 << 35; /// Enables `builtin(primitive_index)` in fragment shaders. /// /// Note: enables geometry processing for pipelines using the builtin. @@ -545,14 +563,14 @@ bitflags::bitflags! { /// - Vulkan /// /// This is a native only feature. - const SHADER_PRIMITIVE_INDEX = 1 << 38; + const SHADER_PRIMITIVE_INDEX = 1 << 36; /// Enables multiview render passes and `builtin(view_index)` in vertex shaders. /// /// Supported platforms: /// - Vulkan /// /// This is a native only feature. - const MULTIVIEW = 1 << 39; + const MULTIVIEW = 1 << 37; /// Enables normalized `16-bit` texture formats. /// /// Supported platforms: @@ -561,7 +579,7 @@ bitflags::bitflags! { /// - Metal /// /// This is a native only feature. - const TEXTURE_FORMAT_16BIT_NORM = 1 << 40; + const TEXTURE_FORMAT_16BIT_NORM = 1 << 38; /// Allows the use of [`AddressMode::ClampToBorder`] with a border color /// of [`SamplerBorderColor::Zero`]. /// @@ -573,12 +591,12 @@ bitflags::bitflags! { /// - OpenGL /// /// This is a native only feature. - const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 41; + const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 39; /// Supported Platforms: /// - Metal /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 42; + const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 40; } } @@ -1838,12 +1856,18 @@ pub enum TextureFormat { /// Special depth format with 32 bit floating point depth. #[cfg_attr(feature = "serde", serde(rename = "depth32float"))] Depth32Float, + /// Special depth/stencil format with 32 bit floating point depth and 8 bits integer stencil. + #[cfg_attr(feature = "serde", serde(rename = "depth32float-stencil8"))] + Depth32FloatStencil8, /// Special depth format with at least 24 bit integer depth. #[cfg_attr(feature = "serde", serde(rename = "depth24plus"))] Depth24Plus, /// Special depth/stencil format with at least 24 bit integer depth and 8 bits integer stencil. #[cfg_attr(feature = "serde", serde(rename = "depth24plus-stencil8"))] Depth24PlusStencil8, + /// Special depth/stencil format with 24 bit integer depth and 8 bits integer stencil. + #[cfg_attr(feature = "serde", serde(rename = "depth24unorm-stencil8"))] + Depth24UnormStencil8, // Packed uncompressed texture formats /// Packed unsigned float with 9 bits mantisa for each RGB component, then a common 5 bits exponent @@ -2046,6 +2070,8 @@ impl TextureFormat { let astc_ldr = Features::TEXTURE_COMPRESSION_ASTC_LDR; let astc_hdr = Features::TEXTURE_COMPRESSION_ASTC_HDR; let norm16bit = Features::TEXTURE_FORMAT_16BIT_NORM; + let d32_s8 = Features::DEPTH32FLOAT_STENCIL8; + let d24_s8 = Features::DEPTH24UNORM_STENCIL8; // Sample Types let uint = TextureSampleType::Uint; @@ -2135,8 +2161,10 @@ impl TextureFormat { // Depth-stencil textures Self::Depth32Float => ( native, depth, linear, msaa, (1, 1), 4, attachment, 1), + Self::Depth32FloatStencil8 =>( d32_s8, depth, linear, msaa, (1, 1), 4, attachment, 2), Self::Depth24Plus => ( native, depth, linear, msaa, (1, 1), 4, attachment, 1), Self::Depth24PlusStencil8 => ( native, depth, linear, msaa, (1, 1), 4, attachment, 2), + Self::Depth24UnormStencil8 => ( d24_s8, depth, linear, msaa, (1, 1), 4, attachment, 2), // Packed uncompressed Self::Rgb9e5Ufloat => ( native, float, linear, noaa, (1, 1), 4, basic, 3), @@ -2501,10 +2529,63 @@ impl CompareFunction { } } -/// Rate that determines when vertex data is advanced. +/// Whether a vertex buffer is indexed by vertex or by instance. +/// +/// Consider a call to [`RenderPass::draw`] like this: +/// +/// ```ignore +/// render_pass.draw(vertices, instances) +/// ``` +/// +/// where `vertices` is a `Range` of vertex indices, and +/// `instances` is a `Range` of instance indices. +/// +/// For this call, `wgpu` invokes the vertex shader entry point once +/// for every possible `(v, i)` pair, where `v` is drawn from +/// `vertices` and `i` is drawn from `instances`. These invocations +/// may happen in any order, and will usually run in parallel. +/// +/// Each vertex buffer has a step mode, established by the +/// [`step_mode`] field of its [`VertexBufferLayout`], given when the +/// pipeline was created. Buffers whose step mode is [`Vertex`] use +/// `v` as the index into their contents, whereas buffers whose step +/// mode is [`Instance`] use `i`. The indicated buffer element then +/// contributes zero or more attribute values for the `(v, i)` vertex +/// shader invocation to use, based on the [`VertexBufferLayout`]'s +/// [`attributes`] list. +/// +/// You can visualize the results from all these vertex shader +/// invocations as a matrix with a row for each `i` from `instances`, +/// and with a column for each `v` from `vertices`. In one sense, `v` +/// and `i` are symmetrical: both are used to index vertex buffers and +/// provide attribute values. But the key difference between `v` and +/// `i` is that line and triangle primitives are built from the values +/// of each row, along which `i` is constant and `v` varies, not the +/// columns. +/// +/// An indexed draw call works similarly: +/// +/// ```ignore +/// render_pass.draw_indexed(indices, base_vertex, instances) +/// ``` +/// +/// The only difference is that `v` values are drawn from the contents +/// of the index buffer—specifically, the subrange of the index +/// buffer given by `indices`—instead of simply being sequential +/// integers, as they are in a `draw` call. +/// +/// A non-instanced call, where `instances` is `0..1`, is simply a +/// matrix with only one row. /// /// Corresponds to [WebGPU `GPUVertexStepMode`]( /// https://gpuweb.github.io/gpuweb/#enumdef-gpuvertexstepmode). +/// +/// [`RenderPass::draw`]: ../wgpu/struct.RenderPass.html#method.draw +/// [`VertexBufferLayout`]: ../wgpu/struct.VertexBufferLayout.html +/// [`step_mode`]: ../wgpu/struct.VertexBufferLayout.html#structfield.step_mode +/// [`attributes`]: ../wgpu/struct.VertexBufferLayout.html#structfield.attributes +/// [`Vertex`]: VertexStepMode::Vertex +/// [`Instance`]: VertexStepMode::Instance #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "trace", derive(Serialize))]