diff --git a/.cargo/config.in b/.cargo/config.in --- a/.cargo/config.in +++ b/.cargo/config.in @@ -83,16 +83,16 @@ rev = "3484d3e3ebdc8931493aa5df4d7ee9360a90e76b" [source."https://github.com/gfx-rs/wgpu"] git = "https://github.com/gfx-rs/wgpu" replace-with = "vendored-sources" -rev = "0b61a191" +rev = "ebca3298f01ea72f80aeac4ab1be889469f9c699" [source."https://github.com/gfx-rs/naga"] git = "https://github.com/gfx-rs/naga" replace-with = "vendored-sources" -rev = "85056524" +rev = "1aa91549" [source."https://github.com/gfx-rs/metal-rs"] git = "https://github.com/gfx-rs/metal-rs" replace-with = "vendored-sources" rev = "1aaa903" diff --git a/Cargo.lock b/Cargo.lock --- a/Cargo.lock +++ b/Cargo.lock @@ -3427,11 +3427,11 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664" [[package]] name = "naga" version = "0.8.0" -source = "git+https://github.com/gfx-rs/naga?rev=85056524#850565243d1d0d03215f13246c94d63e1d4c51cd" +source = "git+https://github.com/gfx-rs/naga?rev=1aa91549#1aa9154964238af8c692cf521ff90e1f2395e147" dependencies = [ "bit-set", "bitflags", "codespan-reporting", "hexf-parse", @@ -5894,11 +5894,11 @@ ] [[package]] name = "wgpu-core" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=0b61a191#0b61a191244da0f0d987d53614a6698097a7622f" +source = "git+https://github.com/gfx-rs/wgpu?rev=ebca3298f01ea72f80aeac4ab1be889469f9c699#ebca3298f01ea72f80aeac4ab1be889469f9c699" dependencies = [ "arrayvec 0.7.2", "bitflags", "cfg_aliases", "codespan-reporting", @@ -5917,11 +5917,11 @@ ] [[package]] name = "wgpu-hal" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=0b61a191#0b61a191244da0f0d987d53614a6698097a7622f" +source = "git+https://github.com/gfx-rs/wgpu?rev=ebca3298f01ea72f80aeac4ab1be889469f9c699#ebca3298f01ea72f80aeac4ab1be889469f9c699" dependencies = [ "arrayvec 0.7.2", "ash", "bit-set", "bitflags", @@ -5954,11 +5954,11 @@ ] [[package]] name = "wgpu-types" version = "0.12.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=0b61a191#0b61a191244da0f0d987d53614a6698097a7622f" +source = "git+https://github.com/gfx-rs/wgpu?rev=ebca3298f01ea72f80aeac4ab1be889469f9c699#ebca3298f01ea72f80aeac4ab1be889469f9c699" dependencies = [ "bitflags", "bitflags_serde_shim", "serde", ] diff --git a/gfx/wgpu_bindings/Cargo.toml b/gfx/wgpu_bindings/Cargo.toml --- a/gfx/wgpu_bindings/Cargo.toml +++ b/gfx/wgpu_bindings/Cargo.toml @@ -15,24 +15,24 @@ default = [] [dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "0b61a191" +rev = "ebca3298f01ea72f80aeac4ab1be889469f9c699" #Note: "replay" shouldn't ideally be needed, # but it allows us to serialize everything across IPC. features = ["replay", "trace", "serial-pass"] [dependencies.wgt] package = "wgpu-types" git = "https://github.com/gfx-rs/wgpu" -rev = "0b61a191" +rev = "ebca3298f01ea72f80aeac4ab1be889469f9c699" [dependencies.wgh] package = "wgpu-hal" git = "https://github.com/gfx-rs/wgpu" -rev = "0b61a191" +rev = "ebca3298f01ea72f80aeac4ab1be889469f9c699" [dependencies] bincode = "1" log = "0.4" parking_lot = "0.11" diff --git a/gfx/wgpu_bindings/moz.yaml b/gfx/wgpu_bindings/moz.yaml --- a/gfx/wgpu_bindings/moz.yaml +++ b/gfx/wgpu_bindings/moz.yaml @@ -18,15 +18,15 @@ # Usually different from repository url url: https://github.com/gfx-rs/wgpu # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: commit 0b61a191 + release: ebca3298f01ea72f80aeac4ab1be889469f9c699 (2022-04-25T21:38:57Z). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 0b61a191 + revision: ebca3298f01ea72f80aeac4ab1be889469f9c699 license: ['MIT', 'Apache-2.0'] updatebot: maintainer-phab: jimb diff --git a/third_party/rust/naga/.cargo-checksum.json b/third_party/rust/naga/.cargo-checksum.json --- 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":"5c0481a31adf71b7f88e5087cad00e1a9b83ed23de543c7395421b10e0a05566","src/back/hlsl/conv.rs":"46097319c63a0ae04ed4d6e86c5d1321c45d53bdf324440fa1588ec4990b2385","src/back/hlsl/help.rs":"ce00a92bca057822b092130a2b5d9fa9ee20758bceaa7f5d7c39c8b0c1655295","src/back/hlsl/keywords.rs":"a158c52bf9a05514b5a6e29658c2872c1cf2a6f295e8094a6ac4a37f76a761ad","src/back/hlsl/mod.rs":"e9b01b47e658145d825fce664c3b65660adc5c7332f9e337568a42c35fba4b34","src/back/hlsl/storage.rs":"3c72fc47db16c46ac60ca4623ac5b7fe6459ebb379840840aea1989806a51b51","src/back/hlsl/writer.rs":"b5949e08d7cbdca27d6abad9bb79b753a402cb2850453e8b7e7b194dc8842f36","src/back/mod.rs":"e6f578828897c4264b553ff0842f41d4ae748429f80f6e161dffc17109e49e86","src/back/msl/keywords.rs":"e1c2bf804b1a3a56639bedb9c1b3b78fbf920724ef65f91fe2e9fe476ee3fbbf","src/back/msl/mod.rs":"16842c813a1a54e26ba90dd119b99d12f886e99822bed21ed4e64aebca86063b","src/back/msl/sampler.rs":"210cf28143ac17d6c56a9adf4fc0e3b85b1b16bb8b94771f9304f2a43b7ce78e","src/back/msl/writer.rs":"6d3c03f401baa3378e6e8cf877951c03b1fb5dbe0d70671185a90b8f37e553e9","src/back/spv/block.rs":"c3c634170a831c37e74660dd7824289dbd60a8e245e44a4e4b1bb3b4cf40f295","src/back/spv/helpers.rs":"0ec3381f3ba5856a14d876559f77e21fd5d2f342cb005343a3df2ff58443e4fb","src/back/spv/image.rs":"d5d48f1294e096265d7077131f8486c936d817472e6f131b3834b6acc1bc93d2","src/back/spv/index.rs":"7a9815bdb1f323a780d41092371582f6d4210f305bec5c74e882dbb6d2d49bc3","src/back/spv/instructions.rs":"42eb1afad17cd311d89cbf49813171848a417543195df571ba0f3de1633efc8b","src/back/spv/layout.rs":"e263de53cd2f9a03ad94b82b434ce636609bc1ed435a2d1132951663bfaa8ebd","src/back/spv/mod.rs":"ac16b73172eff02ef292a3b8a3a13820c8c45b2b7cc7ef0211b5d1cc1895e400","src/back/spv/recyclable.rs":"1deb340e077f820dd90826456410a7b9d5c66cd0c708ff7844a449a4bfca1450","src/back/spv/selection.rs":"81e404abfa0a977f7c1f76ccb37a78d13ccadbda229048dad53cc67687cc39db","src/back/spv/writer.rs":"198808b5cacc33b3340b3c1852c1d60e630eed84a5d95e1d28b636fc37eeb13b","src/back/wgsl/mod.rs":"2dd12bbea9ace835850192bb68c5760953da6bac6a636073d1eca19381c0c0b6","src/back/wgsl/writer.rs":"adbe090454ed12db89e16b0034ff234b68098cba11c4b4eecbbaf7cce593df9e","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":"964fd8d5803ab8969ce5fd8e7907dc8ae5235dc1d4b462a21fe1ed6ebc71e63e","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":"10c3d53660fd61b015e88ae199bbe20a1c92ef500db65e7af111a3a527496295","src/proc/index.rs":"b9fb9b49b8a29cf063f51e6e71bb88ccf8b0bfe203ebb537c84dff9050d2ffbf","src/proc/layouter.rs":"2cb7629f99f776cdf4e0f1127516e83cccabb5884c5fd95542fdf83bb5ad469b","src/proc/mod.rs":"f56faa7927ba9db5e26f04594ebb7cf44bfc38dc84d5fdfa4c9af26d753488a2","src/proc/namer.rs":"2cab7428296a243a23234a516ceb569104f86ad4d68ffdf022a060a9fc0c4e83","src/proc/terminator.rs":"40d22f4c821d24e972403a31ded5dae6078d3dc33b52a9b0993396bdf77f8d1d","src/proc/typifier.rs":"5d228918c36659610997aef0e87c8fef41e6c4a22094ac6657818417d964f889","src/span.rs":"ff1f43d8a1373db770c16ffe8f628861e47d14faaf981c85de5321be8bacdf57","src/valid/analyzer.rs":"94a32a56e0a8b7dd3b619d8af080375ede272051162a0661f0993f5834a13a3d","src/valid/compose.rs":"e4c029db8d07ca5e11d4e7e59d8885634856872b595a166e266a3ce0342ccdcd","src/valid/expression.rs":"1d010a881f9b4ce6d6d14d258a20f57257ce53b3afb243425565d701c094bbf4","src/valid/function.rs":"478b41bc208b7a4d98c494aa3dbc9da8f5140ab0226898ff5dbf31a58744cf0c","src/valid/interface.rs":"83d017f09d5cc4aab2f46fe69e4c6273f04204ffbb3e189c96bab0253e89b718","src/valid/mod.rs":"30b8f0498d9d352a472892b39f5fc89f8988184036bfa50e2e393811e1944f12","src/valid/type.rs":"016e396437cea625fad21e5f1938c9dd185edffac776f3292c802fa65464a9bf"},"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":"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 diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs --- a/third_party/rust/naga/src/back/glsl/mod.rs +++ b/third_party/rust/naga/src/back/glsl/mod.rs @@ -783,11 +783,12 @@ // Write all variants instead of `_` so that if new variants are added a // no exhaustiveness error is thrown TypeInner::Pointer { .. } | TypeInner::Struct { .. } | TypeInner::Image { .. } - | TypeInner::Sampler { .. } => { + | TypeInner::Sampler { .. } + | TypeInner::BindingArray { .. } => { return Err(Error::Custom(format!("Unable to write type {:?}", inner))) } } Ok(()) diff --git a/third_party/rust/naga/src/back/hlsl/mod.rs b/third_party/rust/naga/src/back/hlsl/mod.rs --- a/third_party/rust/naga/src/back/hlsl/mod.rs +++ b/third_party/rust/naga/src/back/hlsl/mod.rs @@ -117,10 +117,12 @@ #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub struct BindTarget { pub space: u8, pub register: u32, + /// If the binding is an unsized binding array, this overrides the size. + pub binding_array_size: Option, } // Using `BTreeMap` instead of `HashMap` so that we can hash itself. pub type BindingMap = std::collections::BTreeMap; @@ -212,10 +214,11 @@ match self.binding_map.get(res_binding) { Some(target) => Ok(target.clone()), None if self.fake_missing_bindings => Ok(BindTarget { space: res_binding.group as u8, register: res_binding.binding, + binding_array_size: None, }), None => Err(EntryPointError::MissingBinding(res_binding.clone())), } } } diff --git a/third_party/rust/naga/src/back/hlsl/writer.rs b/third_party/rust/naga/src/back/hlsl/writer.rs --- a/third_party/rust/naga/src/back/hlsl/writer.rs +++ b/third_party/rust/naga/src/back/hlsl/writer.rs @@ -601,11 +601,16 @@ }; write!(self.out, "{}ByteAddressBuffer", prefix)?; register } crate::AddressSpace::Handle => { - let register = match *inner { + let handle_ty = match *inner { + TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner, + _ => inner, + }; + + let register = match *handle_ty { TypeInner::Sampler { .. } => "s", // all storage textures are UAV, unconditionally TypeInner::Image { class: crate::ImageClass::Storage { .. }, .. @@ -622,10 +627,20 @@ write!(self.out, " {}", name)?; if let Some(ref binding) = global.binding { // this was already resolved earlier when we started evaluating an entry point. let bt = self.options.resolve_resource_binding(binding).unwrap(); + + // need to write the binding array size if the type was emitted with `write_type` + if let TypeInner::BindingArray { base, size, .. } = module.types[global.ty].inner { + if let Some(overridden_size) = bt.binding_array_size { + write!(self.out, "[{}]", overridden_size)?; + } else { + self.write_array_size(module, base, size)?; + } + } + write!(self.out, " : register({}{}", register_ty, bt.register)?; if bt.space != 0 { write!(self.out, ", space{}", bt.space)?; } write!(self.out, ")")?; @@ -730,11 +745,11 @@ match size { crate::ArraySize::Constant(const_handle) => { let size = module.constants[const_handle].to_array_length().unwrap(); write!(self.out, "{}", size)?; } - crate::ArraySize::Dynamic => unreachable!(), + crate::ArraySize::Dynamic => {} } write!(self.out, "]")?; if let TypeInner::Array { @@ -872,11 +887,13 @@ pub(super) fn write_type(&mut self, module: &Module, ty: Handle) -> BackendResult { let inner = &module.types[ty].inner; match *inner { TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?, // hlsl array has the size separated from the base type - TypeInner::Array { base, .. } => self.write_type(module, base)?, + TypeInner::Array { base, .. } | TypeInner::BindingArray { base, .. } => { + self.write_type(module, base)? + } ref other => self.write_value_type(module, other)?, } Ok(()) } @@ -931,11 +948,11 @@ write!(self.out, "{}", sampler)?; } // HLSL arrays are written as `type name[size]` // Current code is written arrays only as `[size]` // Base `type` and `name` should be written outside - TypeInner::Array { base, size, .. } => { + TypeInner::Array { base, size, .. } | TypeInner::BindingArray { base, size } => { self.write_array_size(module, base, size)?; } _ => { return Err(Error::Unimplemented(format!( "write_value_type {:?}", @@ -1791,13 +1808,31 @@ .inner_with(&module.types) .pointer_space() { // do nothing, the chain is written on `Load`/`Store` } else { + let base_ty_res = &func_ctx.info[base].ty; + let resolved = base_ty_res.inner_with(&module.types); + + let non_uniform_qualifier = match *resolved { + TypeInner::BindingArray { .. } => { + let uniformity = &func_ctx.info[index].uniformity; + + uniformity.non_uniform_result.is_some() + } + _ => false, + }; + self.write_expr(module, base, func_ctx)?; write!(self.out, "[")?; + if non_uniform_qualifier { + write!(self.out, "NonUniformResourceIndex(")?; + } self.write_expr(module, index, func_ctx)?; + if non_uniform_qualifier { + write!(self.out, ")")?; + } write!(self.out, "]")?; } } Expression::AccessIndex { base, index } => { if let Some(crate::AddressSpace::Storage { .. }) = func_ctx.info[expr] @@ -1850,10 +1885,11 @@ // Write vector access as a swizzle write!(self.out, ".{}", back::COMPONENTS[index as usize])? } TypeInner::Matrix { .. } | TypeInner::Array { .. } + | TypeInner::BindingArray { .. } | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, TypeInner::Struct { .. } => { // This will never panic in case the type is a `Struct`, this is not true // for other types so we can only check while inside this match arm let ty = base_ty_handle.unwrap(); diff --git a/third_party/rust/naga/src/back/msl/mod.rs b/third_party/rust/naga/src/back/msl/mod.rs --- a/third_party/rust/naga/src/back/msl/mod.rs +++ b/third_party/rust/naga/src/back/msl/mod.rs @@ -55,10 +55,12 @@ #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))] pub struct BindTarget { pub buffer: Option, pub texture: Option, pub sampler: Option, + /// If the binding is an unsized binding array, this overrides the size. + pub binding_array_size: Option, pub mutable: bool, } // Using `BTreeMap` instead of `HashMap` so that we can hash itself. pub type BindingMap = std::collections::BTreeMap; @@ -317,10 +319,11 @@ match slot { Some(slot) => Ok(ResolvedBinding::Resource(BindTarget { buffer: Some(slot), texture: None, sampler: None, + binding_array_size: None, mutable: false, })), None if self.fake_missing_bindings => Ok(ResolvedBinding::User { prefix: "fake", index: 0, @@ -338,10 +341,11 @@ match slot { Some(slot) => Ok(ResolvedBinding::Resource(BindTarget { buffer: Some(slot), texture: None, sampler: None, + binding_array_size: None, mutable: false, })), None if self.fake_missing_bindings => Ok(ResolvedBinding::User { prefix: "fake", index: 0, @@ -361,10 +365,17 @@ }) => Some(&options.inline_samplers[index as usize]), _ => None, } } + const fn as_bind_target(&self) -> Option<&BindTarget> { + match *self { + Self::Resource(ref target) => Some(target), + _ => None, + } + } + fn try_fmt(&self, out: &mut W) -> Result<(), Error> { write!(out, " [[")?; match *self { Self::BuiltIn(built_in) => { use crate::BuiltIn as Bi; diff --git a/third_party/rust/naga/src/back/msl/writer.rs b/third_party/rust/naga/src/back/msl/writer.rs --- a/third_party/rust/naga/src/back/msl/writer.rs +++ b/third_party/rust/naga/src/back/msl/writer.rs @@ -68,19 +68,20 @@ /// Prefix for cached clamped level-of-detail values for `ImageLoad` expressions. const CLAMPED_LOD_LOAD_PREFIX: &str = "clamped_lod_e"; struct TypeContext<'a> { handle: Handle, - arena: &'a crate::UniqueArena, + module: &'a crate::Module, names: &'a FastHashMap, access: crate::StorageAccess, + binding: Option<&'a super::ResolvedBinding>, first_time: bool, } impl<'a> Display for TypeContext<'a> { fn fmt(&self, out: &mut Formatter<'_>) -> Result<(), FmtError> { - let ty = &self.arena[self.handle]; + let ty = &self.module.types[self.handle]; if ty.needs_alias() && !self.first_time { let name = &self.names[&NameKey::Type(self.handle)]; return write!(out, "{}", name); } @@ -199,19 +200,53 @@ ) } crate::TypeInner::Sampler { comparison: _ } => { write!(out, "{}::sampler", NAMESPACE) } + crate::TypeInner::BindingArray { base, size } => { + let base_tyname = Self { + handle: base, + first_time: false, + ..*self + }; + + if let Some(&super::ResolvedBinding::Resource(super::BindTarget { + binding_array_size: Some(override_size), + .. + })) = self.binding + { + write!( + out, + "{}::array<{}, {}>", + NAMESPACE, base_tyname, override_size + ) + } else if let crate::ArraySize::Constant(size) = size { + let constant_ctx = ConstantContext { + handle: size, + arena: &self.module.constants, + names: self.names, + first_time: false, + }; + write!( + out, + "{}::array<{}, {}>", + NAMESPACE, base_tyname, constant_ctx + ) + } else { + unreachable!("metal requires all arrays be constant sized"); + } + } } } } struct TypedGlobalVariable<'a> { module: &'a crate::Module, names: &'a FastHashMap, handle: Handle, usage: valid::GlobalUse, + binding: Option<&'a super::ResolvedBinding>, reference: bool, } impl<'a> TypedGlobalVariable<'a> { fn try_fmt(&self, out: &mut W) -> BackendResult { @@ -223,18 +258,28 @@ _ => match self.module.types[var.ty].inner { crate::TypeInner::Image { class: crate::ImageClass::Storage { access, .. }, .. } => access, + crate::TypeInner::BindingArray { base, .. } => { + match self.module.types[base].inner { + crate::TypeInner::Image { + class: crate::ImageClass::Storage { access, .. }, + .. + } => access, + _ => crate::StorageAccess::default(), + } + } _ => crate::StorageAccess::default(), }, }; let ty_name = TypeContext { handle: var.ty, - arena: &self.module.types, + module: self.module, names: self.names, access: storage_access, + binding: self.binding, first_time: false, }; let (space, access, reference) = match var.space.to_msl_name() { Some(space) if self.reference => { @@ -450,11 +495,11 @@ | Ti::Pointer { .. } | Ti::ValuePointer { .. } => self.name.is_some(), // composite types are better to be aliased, regardless of the name Ti::Struct { .. } | Ti::Array { .. } => true, // handle types may be different, depending on the global var access, so we always inline them - Ti::Image { .. } | Ti::Sampler { .. } => false, + Ti::Image { .. } | Ti::Sampler { .. } | Ti::BindingArray { .. } => false, } } } impl crate::Constant { @@ -1223,17 +1268,18 @@ } let expression = &context.function.expressions[expr_handle]; log::trace!("expression {:?} = {:?}", expr_handle, expression); match *expression { - crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => { + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { // This is an acceptable place to generate a `ReadZeroSkipWrite` check. // Since `put_bounds_checks` and `put_access_chain` handle an entire // access chain at a time, recursing back through `put_expression` only // for index expressions and the base object, we will never see intermediate // `Access` or `AccessIndex` expressions here. - let policy = context.choose_bounds_check_policy(expr_handle); + let policy = context.choose_bounds_check_policy(base); if policy == index::BoundsCheckPolicy::ReadZeroSkipWrite && self.put_bounds_checks( expr_handle, context, back::Level(0), @@ -2267,13 +2313,14 @@ ) -> BackendResult { match context.info[handle].ty { TypeResolution::Handle(ty_handle) => { let ty_name = TypeContext { handle: ty_handle, - arena: &context.module.types, + module: context.module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; write!(self.out, "{}", ty_name)?; } TypeResolution::Value(crate::TypeInner::Scalar { kind, .. }) => { @@ -2861,13 +2908,14 @@ size, stride: _, } => { let base_name = TypeContext { handle: base, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; match size { crate::ArraySize::Constant(const_handle) => { @@ -2924,13 +2972,14 @@ )?; } None => { let base_name = TypeContext { handle: member.ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; writeln!( self.out, "{}{} {};", @@ -2954,13 +3003,14 @@ writeln!(self.out, "}};")?; } _ => { let ty_name = TypeContext { handle, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: true, }; writeln!(self.out, "typedef {} {};", ty_name, name)?; } } @@ -3013,13 +3063,14 @@ crate::ConstantInner::Composite { ty, ref components } => { debug_assert!(constant.needs_alias()); let name = &self.names[&NameKey::Constant(handle)]; let ty_name = TypeContext { handle: ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; write!(self.out, "constant {} {} = {{", ty_name, name,)?; for (i, &sub_handle) in components.iter().enumerate() { let separator = if i != 0 { ", " } else { "" }; @@ -3148,13 +3199,14 @@ let fun_name = &self.names[&NameKey::Function(fun_handle)]; match fun.result { Some(ref result) => { let ty_name = TypeContext { handle: result.ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; write!(self.out, "{}", ty_name)?; } None => { @@ -3165,13 +3217,14 @@ for (index, arg) in fun.arguments.iter().enumerate() { let name = &self.names[&NameKey::FunctionArgument(fun_handle, index as u32)]; let param_type_name = TypeContext { handle: arg.ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; let separator = separate( !pass_through_globals.is_empty() || index + 1 != fun.arguments.len() @@ -3190,10 +3243,11 @@ let tyvar = TypedGlobalVariable { module, names: &self.names, handle, usage: fun_info[handle], + binding: None, reference: true, }; let separator = separate(index + 1 != pass_through_globals.len() || supports_array_length); write!(self.out, "{}", back::INDENT)?; @@ -3212,13 +3266,14 @@ writeln!(self.out, ") {{")?; for (local_handle, local) in fun.local_variables.iter() { let ty_name = TypeContext { handle: local.ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; 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 { @@ -3283,15 +3338,23 @@ if fun_info[var_handle].is_empty() { continue; } if let Some(ref br) = var.binding { let good = match options.per_stage_map[ep.stage].resources.get(br) { - Some(target) => match module.types[var.ty].inner { - crate::TypeInner::Image { .. } => target.texture.is_some(), - crate::TypeInner::Sampler { .. } => target.sampler.is_some(), - _ => target.buffer.is_some(), - }, + Some(target) => { + let binding_ty = match module.types[var.ty].inner { + crate::TypeInner::BindingArray { base, .. } => { + &module.types[base].inner + } + ref ty => ty, + }; + match *binding_ty { + crate::TypeInner::Image { .. } => target.texture.is_some(), + crate::TypeInner::Sampler { .. } => target.sampler.is_some(), + _ => target.buffer.is_some(), + } + } None => false, }; if !good { ep_error = Some(super::EntryPointError::MissingBinding(br.clone())); break; @@ -3375,13 +3438,14 @@ }; has_varyings = true; let name = &self.names[name_key]; let ty_name = TypeContext { handle: ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; let resolved = options.resolve_local_binding(binding, in_mode)?; write!(self.out, "{}{} {}", back::INDENT, ty_name, name)?; resolved.try_fmt(&mut self.out)?; @@ -3418,13 +3482,14 @@ writeln!(self.out, "struct {} {{", stage_out_name)?; let mut has_point_size = false; for (name, ty, binding) in result_members { let ty_name = TypeContext { handle: ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: true, }; let binding = binding.ok_or(Error::Validation)?; match *binding { @@ -3519,13 +3584,14 @@ } else { &self.names[name_key] }; let ty_name = TypeContext { handle: ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; let resolved = options.resolve_local_binding(binding, in_mode)?; let separator = if is_first_argument { is_first_argument = false; @@ -3570,10 +3636,11 @@ let tyvar = TypedGlobalVariable { module, names: &self.names, handle, usage, + binding: resolved.as_ref(), reference: true, }; let separator = if is_first_argument { is_first_argument = false; ' ' @@ -3630,10 +3697,11 @@ let tyvar = TypedGlobalVariable { module, names: &self.names, handle, usage, + binding: None, reference: false, }; write!(self.out, "{}", back::INDENT)?; tyvar.try_fmt(&mut self.out)?; match var.init { @@ -3731,13 +3799,14 @@ //TODO: we can postpone this till the relevant expressions are emitted for (local_handle, local) in fun.local_variables.iter() { let name = &self.names[&NameKey::EntryPointLocal(ep_index as _, local_handle)]; let ty_name = TypeContext { handle: local.ty, - arena: &module.types, + module, names: &self.names, access: crate::StorageAccess::empty(), + binding: None, first_time: false, }; write!(self.out, "{}{} {}", back::INDENT, ty_name, name)?; if let Some(value) = local.init { let coco = ConstantContext { @@ -3853,10 +3922,10 @@ addresses.end = addresses.end.max(pointer as usize); } let stack_size = addresses.end - addresses.start; // check the size (in debug only) // last observed macOS value: 19152 (CI) - if !(11000..=20000).contains(&stack_size) { + if !(9500..=20000).contains(&stack_size) { panic!("`put_block` stack size {} has changed!", stack_size); } } } diff --git a/third_party/rust/naga/src/back/spv/block.rs b/third_party/rust/naga/src/back/spv/block.rs --- a/third_party/rust/naga/src/back/spv/block.rs +++ b/third_party/rust/naga/src/back/spv/block.rs @@ -171,11 +171,18 @@ /// once we encounter a `Load` or `Store` expression that actually needs the /// chain's value, we call `write_expression_pointer` to handle the whole /// thing in one fell swoop. fn is_intermediate(&self, expr_handle: Handle) -> bool { match self.ir_function.expressions[expr_handle] { - crate::Expression::GlobalVariable(_) | crate::Expression::LocalVariable(_) => true, + crate::Expression::GlobalVariable(handle) => { + let ty = self.ir_module.global_variables[handle].ty; + match self.ir_module.types[ty].inner { + crate::TypeInner::BindingArray { .. } => false, + _ => true, + } + } + crate::Expression::LocalVariable(_) => true, crate::Expression::FunctionArgument(index) => { let arg = &self.ir_function.arguments[index as usize]; self.ir_module.types[arg.ty].inner.pointer_space().is_some() } @@ -198,26 +205,68 @@ // See `is_intermediate`; we'll handle this later in // `write_expression_pointer`. 0 } crate::Expression::Access { base, index } => { - let base_ty = self.fun_info[base].ty.inner_with(&self.ir_module.types); - match *base_ty { - crate::TypeInner::Vector { .. } => (), + let base_ty_inner = self.fun_info[base].ty.inner_with(&self.ir_module.types); + match *base_ty_inner { + crate::TypeInner::Vector { .. } => { + self.write_vector_access(expr_handle, base, index, block)? + } + crate::TypeInner::BindingArray { + base: binding_type, .. + } => { + let binding_array_false_pointer = LookupType::Local(LocalType::Pointer { + base: binding_type, + class: spirv::StorageClass::UniformConstant, + }); + + let result_id = match self.write_expression_pointer( + expr_handle, + block, + Some(binding_array_false_pointer), + )? { + ExpressionPointer::Ready { pointer_id } => pointer_id, + ExpressionPointer::Conditional { .. } => { + return Err(Error::FeatureNotImplemented( + "Texture array out-of-bounds handling", + )); + } + }; + + let binding_type_id = self.get_type_id(LookupType::Handle(binding_type)); + + let load_id = self.gen_id(); + block.body.push(Instruction::load( + binding_type_id, + load_id, + result_id, + None, + )); + + if self.fun_info[index].uniformity.non_uniform_result.is_some() { + self.writer.require_any( + "NonUniformEXT", + &[spirv::Capability::ShaderNonUniform], + )?; + self.writer.use_extension("SPV_EXT_descriptor_indexing"); + self.writer + .decorate(load_id, spirv::Decoration::NonUniform, &[]); + } + load_id + } ref other => { log::error!( "Unable to access base {:?} of type {:?}", self.ir_function.expressions[base], other ); return Err(Error::Validation( "only vectors may be dynamically indexed by value", )); } - }; - - self.write_vector_access(expr_handle, base, index, block)? + } } crate::Expression::AccessIndex { base, index: _ } if self.is_intermediate(base) => { // See `is_intermediate`; we'll handle this later in // `write_expression_pointer`. 0 @@ -240,10 +289,43 @@ base_id, &[index], )); id } + crate::TypeInner::BindingArray { + base: binding_type, .. + } => { + let binding_array_false_pointer = LookupType::Local(LocalType::Pointer { + base: binding_type, + class: spirv::StorageClass::UniformConstant, + }); + + let result_id = match self.write_expression_pointer( + expr_handle, + block, + Some(binding_array_false_pointer), + )? { + ExpressionPointer::Ready { pointer_id } => pointer_id, + ExpressionPointer::Conditional { .. } => { + return Err(Error::FeatureNotImplemented( + "Texture array out-of-bounds handling", + )); + } + }; + + let binding_type_id = self.get_type_id(LookupType::Handle(binding_type)); + + let load_id = self.gen_id(); + block.body.push(Instruction::load( + binding_type_id, + load_id, + result_id, + None, + )); + + load_id + } ref other => { log::error!("Unable to access index of {:?}", other); return Err(Error::FeatureNotImplemented("access index for type")); } } @@ -774,11 +856,11 @@ }); id } crate::Expression::LocalVariable(variable) => self.function.variables[&variable].id, crate::Expression::Load { pointer } => { - match self.write_expression_pointer(pointer, block)? { + match self.write_expression_pointer(pointer, block, None)? { ExpressionPointer::Ready { pointer_id } => { let id = self.gen_id(); let atomic_space = match *self.fun_info[pointer].ty.inner_with(&self.ir_module.types) { crate::TypeInner::Pointer { base, space } => { @@ -1098,19 +1180,30 @@ /// Build an `OpAccessChain` instruction. /// /// Emit any needed bounds-checking expressions to `block`. /// + /// Some cases we need to generate a different return type than what the IR gives us. + /// This is because pointers to binding arrays don't exist in the IR, but we need to + /// create them to create an access chain in SPIRV. + /// /// On success, the return value is an [`ExpressionPointer`] value; see the /// documentation for that type. fn write_expression_pointer( &mut self, mut expr_handle: Handle, block: &mut Block, + return_type_override: Option, ) -> Result { let result_lookup_ty = match self.fun_info[expr_handle].ty { - TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle), + TypeResolution::Handle(ty_handle) => match return_type_override { + // We use the return type override as a special case for binding arrays as the OpAccessChain + // needs to return a pointer, but indexing into a binding array just gives you the type of + // the binding in the IR. + Some(ty) => ty, + None => LookupType::Handle(ty_handle), + }, TypeResolution::Value(ref inner) => LookupType::Local(make_local(inner).unwrap()), }; let result_type_id = self.get_type_id(result_lookup_ty); // The id of the boolean `and` of all dynamic bounds checks up to this point. If @@ -1610,11 +1703,11 @@ semantics_id, )); } crate::Statement::Store { pointer, value } => { let value_id = self.cached[value]; - match self.write_expression_pointer(pointer, &mut block)? { + match self.write_expression_pointer(pointer, &mut block, None)? { ExpressionPointer::Ready { pointer_id } => { let atomic_space = match *self.fun_info[pointer] .ty .inner_with(&self.ir_module.types) { @@ -1700,18 +1793,19 @@ let id = self.gen_id(); let result_type_id = self.get_expression_type_id(&self.fun_info[result].ty); self.cached[result] = id; - let pointer_id = match self.write_expression_pointer(pointer, &mut block)? { - ExpressionPointer::Ready { pointer_id } => pointer_id, - ExpressionPointer::Conditional { .. } => { - return Err(Error::FeatureNotImplemented( - "Atomics out-of-bounds handling", - )); - } - }; + let pointer_id = + match self.write_expression_pointer(pointer, &mut block, None)? { + ExpressionPointer::Ready { pointer_id } => pointer_id, + ExpressionPointer::Conditional { .. } => { + return Err(Error::FeatureNotImplemented( + "Atomics out-of-bounds handling", + )); + } + }; let space = self.fun_info[pointer] .ty .inner_with(&self.ir_module.types) .pointer_space() diff --git a/third_party/rust/naga/src/back/spv/image.rs b/third_party/rust/naga/src/back/spv/image.rs --- a/third_party/rust/naga/src/back/spv/image.rs +++ b/third_party/rust/naga/src/back/spv/image.rs @@ -363,10 +363,13 @@ self.writer.global_variables[handle.index()].handle_id } crate::Expression::FunctionArgument(i) => { self.function.parameters[i as usize].handle_id } + crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => { + self.cached[expr_handle] + } ref other => unreachable!("Unexpected image expression {:?}", other), }; if id == 0 { unreachable!( diff --git a/third_party/rust/naga/src/back/spv/mod.rs b/third_party/rust/naga/src/back/spv/mod.rs --- a/third_party/rust/naga/src/back/spv/mod.rs +++ b/third_party/rust/naga/src/back/spv/mod.rs @@ -285,10 +285,18 @@ Image(LocalImageType), SampledImage { image_type_id: Word, }, Sampler, + PointerToBindingArray { + base: Handle, + size: u64, + }, + BindingArray { + base: Handle, + size: u64, + }, } /// A type encountered during SPIR-V generation. /// /// In the process of writing SPIR-V, we need to synthesize various types for @@ -565,10 +573,13 @@ /// /// If `capabilities_available` is `Some`, then this is always a subset of /// that. capabilities_used: crate::FastHashSet, + /// The set of spirv extensions used. + extensions_used: crate::FastHashSet<&'static str>, + debugs: Vec, annotations: Vec, flags: WriterFlags, bounds_check_policies: BoundsCheckPolicies, void_type: Word, @@ -577,10 +588,11 @@ lookup_function: crate::FastHashMap, Word>, lookup_function_type: crate::FastHashMap, constant_ids: Vec, cached_constants: crate::FastHashMap<(crate::ScalarValue, crate::Bytes), Word>, global_variables: Vec, + binding_map: BindingMap, // Cached expressions are only meaningful within a BlockContext, but we // retain the table here between functions to save heap allocations. saved_cached: CachedExpressions, @@ -605,18 +617,32 @@ /// Clamp `BuiltIn::FragDepth` output between 0 and 1. const CLAMP_FRAG_DEPTH = 0x10; } } +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +pub struct BindingInfo { + /// If the binding is an unsized binding array, this overrides the size. + pub binding_array_size: Option, +} + +// Using `BTreeMap` instead of `HashMap` so that we can hash itself. +pub type BindingMap = std::collections::BTreeMap; + #[derive(Debug, Clone)] pub struct Options { /// (Major, Minor) target version of the SPIR-V. pub lang_version: (u8, u8), /// Configuration flags for the writer. pub flags: WriterFlags, + /// Map of resources to information about the binding. + pub binding_map: BindingMap, + /// If given, the set of capabilities modules are allowed to use. Code that /// requires capabilities beyond these is rejected with an error. /// /// If this is `None`, all capabilities are permitted. pub capabilities: Option>, @@ -635,10 +661,11 @@ flags |= WriterFlags::DEBUG; } Options { lang_version: (1, 0), flags, + binding_map: BindingMap::default(), capabilities: None, bounds_check_policies: crate::proc::BoundsCheckPolicies::default(), } } } diff --git a/third_party/rust/naga/src/back/spv/recyclable.rs b/third_party/rust/naga/src/back/spv/recyclable.rs --- a/third_party/rust/naga/src/back/spv/recyclable.rs +++ b/third_party/rust/naga/src/back/spv/recyclable.rs @@ -49,5 +49,12 @@ fn recycle(mut self) -> Self { self.clear(); self } } + +impl Recyclable for std::collections::BTreeMap { + fn recycle(mut self) -> Self { + self.clear(); + self + } +} diff --git a/third_party/rust/naga/src/back/spv/writer.rs b/third_party/rust/naga/src/back/spv/writer.rs --- a/third_party/rust/naga/src/back/spv/writer.rs +++ b/third_party/rust/naga/src/back/spv/writer.rs @@ -5,10 +5,11 @@ LogicalLayout, LookupFunctionType, LookupType, LoopContext, Options, PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags, BITS_PER_BYTE, }; use crate::{ arena::{Handle, UniqueArena}, + back::spv::BindingInfo, proc::TypeResolution, valid::{FunctionInfo, ModuleInfo}, }; use spirv::Word; use std::collections::hash_map::Entry; @@ -57,10 +58,11 @@ physical_layout: PhysicalLayout::new(raw_version), logical_layout: LogicalLayout::default(), id_gen, capabilities_available: options.capabilities.clone(), capabilities_used, + extensions_used: crate::FastHashSet::default(), debugs: vec![], annotations: vec![], flags: options.flags, bounds_check_policies: options.bounds_check_policies, void_type, @@ -68,10 +70,11 @@ lookup_function: crate::FastHashMap::default(), lookup_function_type: crate::FastHashMap::default(), constant_ids: Vec::new(), cached_constants: crate::FastHashMap::default(), global_variables: Vec::new(), + binding_map: options.binding_map.clone(), saved_cached: CachedExpressions::default(), gl450_ext_inst_id, temp_list: Vec::new(), }) } @@ -98,18 +101,20 @@ let fresh = Writer { // Copied from the old Writer: flags: self.flags, bounds_check_policies: self.bounds_check_policies, capabilities_available: take(&mut self.capabilities_available), + binding_map: take(&mut self.binding_map), // Initialized afresh: id_gen, void_type, gl450_ext_inst_id, // Recycled: capabilities_used: take(&mut self.capabilities_used).recycle(), + extensions_used: take(&mut self.extensions_used).recycle(), physical_layout: self.physical_layout.clone().recycle(), logical_layout: take(&mut self.logical_layout).recycle(), debugs: take(&mut self.debugs).recycle(), annotations: take(&mut self.annotations).recycle(), lookup_type: take(&mut self.lookup_type).recycle(), @@ -166,10 +171,15 @@ Ok(()) } } } + /// Indicate that the code uses the given extension. + pub(super) fn use_extension(&mut self, extension: &'static str) { + self.extensions_used.insert(extension); + } + pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word { match self.lookup_type.entry(lookup_ty) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { let local = match lookup_ty { @@ -265,11 +275,11 @@ pointer_space: None, }; self.get_type_id(local_type.into()) } - fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) { + pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) { self.annotations .push(Instruction::decorate(id, decoration, operands)); } fn write_function( @@ -495,11 +505,18 @@ } let mut gv = self.global_variables[handle.index()].clone(); // Handle globals are pre-emitted and should be loaded automatically. - if var.space == crate::AddressSpace::Handle { + // + // Any that are binding arrays we skip as we cannot load the array, we must load the result after indexing. + let is_binding_array = match ir_module.types[var.ty].inner { + crate::TypeInner::BindingArray { .. } => true, + _ => false, + }; + + if var.space == crate::AddressSpace::Handle && !is_binding_array { let var_type_id = self.get_type_id(LookupType::Handle(var.ty)); let id = self.id_gen.next(); prelude .body .push(Instruction::load(var_type_id, id, gv.var_id, None)); @@ -786,10 +803,20 @@ } LocalType::Sampler => Instruction::type_sampler(id), LocalType::SampledImage { image_type_id } => { Instruction::type_sampled_image(id, image_type_id) } + LocalType::BindingArray { base, size } => { + let inner_ty = self.get_type_id(LookupType::Handle(base)); + let scalar_id = self.get_constant_scalar(crate::ScalarValue::Uint(size), 4); + Instruction::type_array(id, inner_ty, scalar_id) + } + LocalType::PointerToBindingArray { base, size } => { + let inner_ty = + self.get_type_id(LookupType::Local(LocalType::BindingArray { base, size })); + Instruction::type_pointer(id, spirv::StorageClass::UniformConstant, inner_ty) + } }; instruction.to_words(&mut self.logical_layout.declarations); } @@ -836,10 +863,20 @@ Instruction::type_array(id, type_id, length_id) } crate::ArraySize::Dynamic => Instruction::type_runtime_array(id, type_id), } } + crate::TypeInner::BindingArray { base, size } => { + let type_id = self.get_type_id(LookupType::Handle(base)); + match size { + crate::ArraySize::Constant(const_handle) => { + let length_id = self.constant_ids[const_handle.index()]; + Instruction::type_array(id, type_id, length_id) + } + crate::ArraySize::Dynamic => Instruction::type_runtime_array(id, type_id), + } + } crate::TypeInner::Struct { ref members, span: _, } => { let mut member_ids = Vec::with_capacity(members.len()); @@ -1221,19 +1258,38 @@ if !storage_access.contains(crate::StorageAccess::STORE) { self.decorate(id, Decoration::NonWritable, &[]); } } + let mut substitute_inner_type_lookup = None; if let Some(ref res_binding) = global_variable.binding { self.decorate(id, Decoration::DescriptorSet, &[res_binding.group]); self.decorate(id, Decoration::Binding, &[res_binding.binding]); - } + + if let Some(&BindingInfo { + binding_array_size: Some(remapped_binding_array_size), + }) = self.binding_map.get(res_binding) + { + if let crate::TypeInner::BindingArray { base, .. } = + ir_module.types[global_variable.ty].inner + { + substitute_inner_type_lookup = + Some(LookupType::Local(LocalType::PointerToBindingArray { + base, + size: remapped_binding_array_size as u64, + })) + } + } else { + } + }; let init_word = global_variable .init .map(|constant| self.constant_ids[constant.index()]); - let inner_type_id = self.get_type_id(LookupType::Handle(global_variable.ty)); + let inner_type_id = self.get_type_id( + substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)), + ); // generate the wrapping structure if needed let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) { let wrapper_type_id = self.id_gen.next(); @@ -1260,11 +1316,15 @@ // a runtime-sized array. In this case, we need to decorate it with // Block. if let crate::AddressSpace::Storage { .. } = global_variable.space { self.decorate(inner_type_id, Decoration::Block, &[]); } - self.get_pointer_id(&ir_module.types, global_variable.ty, class)? + if substitute_inner_type_lookup.is_some() { + inner_type_id + } else { + self.get_pointer_id(&ir_module.types, global_variable.ty, class)? + } }; Instruction::variable(pointer_type_id, id, class, init_word) .to_words(&mut self.logical_layout.declarations); Ok(id) @@ -1491,10 +1551,13 @@ } for capability in self.capabilities_used.iter() { Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities); } + for extension in self.extensions_used.iter() { + Instruction::extension(extension).to_words(&mut self.logical_layout.extensions); + } if ir_module.entry_points.is_empty() { // SPIR-V doesn't like modules without entry points Instruction::capability(spirv::Capability::Linkage) .to_words(&mut self.logical_layout.capabilities); } diff --git a/third_party/rust/naga/src/back/wgsl/writer.rs b/third_party/rust/naga/src/back/wgsl/writer.rs --- a/third_party/rust/naga/src/back/wgsl/writer.rs +++ b/third_party/rust/naga/src/back/wgsl/writer.rs @@ -533,10 +533,25 @@ self.write_type(module, base)?; } } write!(self.out, ">")?; } + TypeInner::BindingArray { base, size } => { + // More info https://github.com/gpuweb/gpuweb/issues/2105 + write!(self.out, "binding_array<")?; + match size { + crate::ArraySize::Constant(handle) => { + self.write_type(module, base)?; + write!(self.out, ",")?; + self.write_constant(module, handle)?; + } + crate::ArraySize::Dynamic => { + self.write_type(module, base)?; + } + } + write!(self.out, ">")?; + } TypeInner::Matrix { columns, rows, width: _, } => { @@ -1179,10 +1194,11 @@ // Write vector access as a swizzle write!(self.out, ".{}", back::COMPONENTS[index as usize])? } TypeInner::Matrix { .. } | TypeInner::Array { .. } + | TypeInner::BindingArray { .. } | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, TypeInner::Struct { .. } => { // This will never panic in case the type is a `Struct`, this is not true // for other types so we can only check while inside this match arm let ty = base_ty_handle.unwrap(); diff --git a/third_party/rust/naga/src/front/wgsl/mod.rs b/third_party/rust/naga/src/front/wgsl/mod.rs --- a/third_party/rust/naga/src/front/wgsl/mod.rs +++ b/third_party/rust/naga/src/front/wgsl/mod.rs @@ -578,13 +578,13 @@ let member_type = &types[base]; let base = member_type.name.as_deref().unwrap_or("unknown"); match size { crate::ArraySize::Constant(size) => { let size = constants[size].name.as_deref().unwrap_or("unknown"); - format!("{}[{}]", base, size) + format!("array<{}, {}>", base, size) } - crate::ArraySize::Dynamic => format!("{}[]", base), + crate::ArraySize::Dynamic => format!("array<{}>", base), } } Ti::Struct { .. } => { // TODO: Actually output the struct? "struct".to_string() @@ -632,10 +632,21 @@ "texture{}{}{}{}", class_suffix, dim_suffix, array_suffix, type_in_brackets ) } Ti::Sampler { .. } => "sampler".to_string(), + Ti::BindingArray { base, size, .. } => { + let member_type = &types[base]; + let base = member_type.name.as_deref().unwrap_or("unknown"); + match size { + crate::ArraySize::Constant(size) => { + let size = constants[size].name.as_deref().unwrap_or("unknown"); + format!("binding_array<{}, {}>", base, size) + } + crate::ArraySize::Dynamic => format!("binding_array<{}>", base), + } + } } } } mod type_inner_tests { @@ -679,11 +690,11 @@ let array = crate::TypeInner::Array { base: mytype1, stride: 4, size: crate::ArraySize::Constant(c), }; - assert_eq!(array.to_wgsl(&types, &constants), "MyType1[C]"); + assert_eq!(array.to_wgsl(&types, &constants), "array"); let mat = crate::TypeInner::Matrix { rows: crate::VectorSize::Quad, columns: crate::VectorSize::Bi, width: 8, @@ -725,10 +736,19 @@ }; assert_eq!( img3.to_wgsl(&types, &constants), "texture_depth_multisampled_2d" ); + + let array = crate::TypeInner::BindingArray { + base: mytype1, + size: crate::ArraySize::Constant(c), + }; + assert_eq!( + array.to_wgsl(&types, &constants), + "binding_array" + ); } } impl crate::ScalarKind { /// Format a scalar kind+width as a type is written in wgsl. @@ -863,14 +883,13 @@ } } fn prepare_sampling( &mut self, - image_name: &'a str, + image: Handle, span: Span, ) -> Result> { - let image = self.lookup_ident.lookup(image_name, span.clone())?.handle; Ok(SamplingContext { image, arrayed: match *self.resolve_type(image)? { crate::TypeInner::Image { arrayed, .. } => arrayed, _ => return Err(Error::BadTexture(span)), @@ -1760,16 +1779,17 @@ } // texture sampling "textureSample" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1780,11 +1800,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Auto, @@ -1792,16 +1812,17 @@ } } "textureSampleLevel" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1814,11 +1835,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Exact(level), @@ -1826,16 +1847,17 @@ } } "textureSampleBias" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1848,11 +1870,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Bias(bias), @@ -1860,16 +1882,17 @@ } } "textureSampleGrad" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1884,11 +1907,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Gradient { x, y }, @@ -1896,16 +1919,17 @@ } } "textureSampleCompare" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1918,11 +1942,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Auto, @@ -1930,16 +1954,17 @@ } } "textureSampleCompareLevel" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -1952,11 +1977,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: None, coordinate, array_index, offset, level: crate::SampleLevel::Zero, @@ -1981,16 +2006,17 @@ .get(index as usize) .ok_or(Error::InvalidGatherComponent(span, index))? } else { crate::SwizzleComponent::X }; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -2001,11 +2027,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: Some(component), coordinate, array_index, offset, level: crate::SampleLevel::Zero, @@ -2013,16 +2039,17 @@ } } "textureGatherCompare" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; - let (sampler_name, sampler_span) = lexer.next_ident_with_span()?; + let sampler_expr = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; - let sc = ctx.prepare_sampling(image_name, image_span)?; + let sc = ctx.prepare_sampling(image, image_span)?; let array_index = if sc.arrayed { lexer.expect(Token::Separator(','))?; Some(self.parse_general_expression(lexer, ctx.reborrow())?) } else { None @@ -2035,11 +2062,11 @@ None }; lexer.close_arguments()?; crate::Expression::ImageSample { image: sc.image, - sampler: ctx.lookup_ident.lookup(sampler_name, sampler_span)?.handle, + sampler: sampler_expr, gather: Some(crate::SwizzleComponent::X), coordinate, array_index, offset, level: crate::SampleLevel::Zero, @@ -2047,15 +2074,12 @@ } } "textureLoad" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx - .lookup_ident - .lookup(image_name, image_span.clone())? - .handle; + let (image, image_span) = + self.parse_general_expression_with_span(lexer, ctx.reborrow())?; lexer.expect(Token::Separator(','))?; let coordinate = self.parse_general_expression(lexer, ctx.reborrow())?; let (class, arrayed) = match *ctx.resolve_type(image)? { crate::TypeInner::Image { class, arrayed, .. } => (class, arrayed), _ => return Err(Error::BadTexture(image_span)), @@ -2088,12 +2112,11 @@ } } "textureDimensions" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?.handle; + let image = self.parse_general_expression(lexer, ctx.reborrow())?; let level = if lexer.skip(Token::Separator(',')) { let expr = self.parse_general_expression(lexer, ctx.reborrow())?; Some(expr) } else { None @@ -2105,34 +2128,31 @@ } } "textureNumLevels" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?.handle; + let image = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, query: crate::ImageQuery::NumLevels, } } "textureNumLayers" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?.handle; + let image = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, query: crate::ImageQuery::NumLayers, } } "textureNumSamples" => { let _ = lexer.next(); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = ctx.lookup_ident.lookup(image_name, image_span)?.handle; + let image = self.parse_general_expression(lexer, ctx.reborrow())?; lexer.close_arguments()?; crate::Expression::ImageQuery { image, query: crate::ImageQuery::NumSamples, } @@ -2269,11 +2289,12 @@ // Will be popped inside match, possibly inside parse_function_call_inner or parse_construction self.push_scope(Scope::PrimaryExpr, lexer); let expr = match lexer.peek() { (Token::Paren('('), _) => { let _ = lexer.next(); - let expr = self.parse_general_expression_for_reference(lexer, ctx.reborrow())?; + let (expr, _span) = + self.parse_general_expression_for_reference(lexer, ctx.reborrow())?; lexer.expect(Token::Paren(')'))?; self.pop_scope(lexer); expr } token @ (Token::Word("true" | "false") | Token::Number { .. }, _) => { @@ -2656,24 +2677,33 @@ ) }, ) } + fn parse_general_expression_with_span<'a>( + &mut self, + lexer: &mut Lexer<'a>, + mut ctx: ExpressionContext<'a, '_, '_>, + ) -> Result<(Handle, Span), Error<'a>> { + let (expr, span) = self.parse_general_expression_for_reference(lexer, ctx.reborrow())?; + Ok((ctx.apply_load_rule(expr), span)) + } + fn parse_general_expression<'a>( &mut self, lexer: &mut Lexer<'a>, mut ctx: ExpressionContext<'a, '_, '_>, ) -> Result, Error<'a>> { - let expr = self.parse_general_expression_for_reference(lexer, ctx.reborrow())?; + let (expr, _span) = self.parse_general_expression_for_reference(lexer, ctx.reborrow())?; Ok(ctx.apply_load_rule(expr)) } fn parse_general_expression_for_reference<'a>( &mut self, lexer: &mut Lexer<'a>, mut context: ExpressionContext<'a, '_, '_>, - ) -> Result> { + ) -> Result<(TypedExpression, Span), Error<'a>> { self.push_scope(Scope::GeneralExpr, lexer); // logical_or_expression let handle = context.parse_binary_op( lexer, |token| match token { @@ -2726,12 +2756,11 @@ ) }, ) }, )?; - self.pop_scope(lexer); - Ok(handle) + Ok((handle, self.pop_scope(lexer))) } fn parse_variable_ident_decl<'a>( &mut self, lexer: &mut Lexer<'a>, @@ -3010,10 +3039,24 @@ self.layouter.update(type_arena, const_arena).unwrap(); self.layouter[base].to_stride() }; crate::TypeInner::Array { base, size, stride } } + "binding_array" => { + lexer.expect_generic_paren('<')?; + let (base, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?; + let size = if lexer.skip(Token::Separator(',')) { + let const_handle = + self.parse_const_expression(lexer, type_arena, const_arena)?; + crate::ArraySize::Constant(const_handle) + } else { + crate::ArraySize::Dynamic + }; + lexer.expect_generic_paren('>')?; + + crate::TypeInner::BindingArray { base, size } + } "sampler" => crate::TypeInner::Sampler { comparison: false }, "sampler_comparison" => crate::TypeInner::Sampler { comparison: true }, "texture_1d" => { let (kind, width, span) = lexer.next_scalar_generic_with_span()?; Self::check_texture_sample_type(kind, width, span)?; @@ -3934,17 +3977,14 @@ } "textureStore" => { let _ = lexer.next(); emitter.start(context.expressions); lexer.open_arguments()?; - let (image_name, image_span) = lexer.next_ident_with_span()?; - let image = context - .lookup_ident - .lookup(image_name, image_span.clone())? - .handle; + let mut expr_context = context.as_expression(block, &mut emitter); + let (image, image_span) = self + .parse_general_expression_with_span(lexer, expr_context.reborrow())?; lexer.expect(Token::Separator(','))?; - let mut expr_context = context.as_expression(block, &mut emitter); let arrayed = match *expr_context.resolve_type(image)? { crate::TypeInner::Image { arrayed, .. } => arrayed, _ => return Err(Error::BadTexture(image_span)), }; let coordinate = self.parse_general_expression(lexer, expr_context)?; diff --git a/third_party/rust/naga/src/lib.rs b/third_party/rust/naga/src/lib.rs --- a/third_party/rust/naga/src/lib.rs +++ b/third_party/rust/naga/src/lib.rs @@ -696,10 +696,12 @@ //TODO: consider moving `multisampled: bool` out class: ImageClass, }, /// Can be used to sample values from images. Sampler { comparison: bool }, + /// Array of bindings + BindingArray { base: Handle, size: ArraySize }, } /// Constant value. #[derive(Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] diff --git a/third_party/rust/naga/src/proc/index.rs b/third_party/rust/naga/src/proc/index.rs --- a/third_party/rust/naga/src/proc/index.rs +++ b/third_party/rust/naga/src/proc/index.rs @@ -115,34 +115,45 @@ /// /// [`ImageLoad`]: crate::Expression::ImageLoad /// [`ImageStore`]: crate::Statement::ImageStore #[cfg_attr(feature = "deserialize", serde(default))] pub image: BoundsCheckPolicy, + + /// How should the generated code handle binding array indexes that are out of bounds. + #[cfg_attr(feature = "deserialize", serde(default))] + pub binding_array: BoundsCheckPolicy, } /// The default `BoundsCheckPolicy` is `Unchecked`. impl Default for BoundsCheckPolicy { fn default() -> Self { BoundsCheckPolicy::Unchecked } } impl BoundsCheckPolicies { - /// Determine which policy applies to `access`. + /// Determine which policy applies to `base`. /// - /// `access` is a subtree of `Access` and `AccessIndex` expressions, - /// operating either on a pointer to a value, or on a value directly. + /// `base` is the "base" expression (the expression being indexed) of a `Access` + /// and `AccessIndex` expression. This is either a pointer, a value, being directly + /// indexed, or a binding array. /// /// See the documentation for [`BoundsCheckPolicy`] for details about /// when each policy applies. pub fn choose_policy( &self, - access: Handle, + base: Handle, types: &UniqueArena, info: &valid::FunctionInfo, ) -> BoundsCheckPolicy { - match info[access].ty.inner_with(types).pointer_space() { + let ty = info[base].ty.inner_with(types); + + if let crate::TypeInner::BindingArray { .. } = *ty { + return self.binding_array; + } + + match ty.pointer_space() { Some(crate::AddressSpace::Storage { access: _ } | crate::AddressSpace::Uniform) => { self.buffer } // This covers other address spaces, but also accessing vectors and // matrices by value, where no pointer is involved. @@ -363,11 +374,11 @@ ) -> Result { use crate::TypeInner as Ti; let known_length = match *self { Ti::Vector { size, .. } => size as _, Ti::Matrix { columns, .. } => columns as _, - Ti::Array { size, .. } => { + Ti::Array { size, .. } | Ti::BindingArray { size, .. } => { return size.to_indexable_length(module); } Ti::ValuePointer { size: Some(size), .. } => size as _, diff --git a/third_party/rust/naga/src/proc/layouter.rs b/third_party/rust/naga/src/proc/layouter.rs --- a/third_party/rust/naga/src/proc/layouter.rs +++ b/third_party/rust/naga/src/proc/layouter.rs @@ -200,11 +200,11 @@ TypeLayout { size: span, alignment, } } - Ti::Image { .. } | Ti::Sampler { .. } => TypeLayout { + Ti::Image { .. } | Ti::Sampler { .. } | Ti::BindingArray { .. } => TypeLayout { size, alignment: Alignment::new(1).unwrap(), }, }; debug_assert!(size <= layout.size); diff --git a/third_party/rust/naga/src/proc/mod.rs b/third_party/rust/naga/src/proc/mod.rs --- a/third_party/rust/naga/src/proc/mod.rs +++ b/third_party/rust/naga/src/proc/mod.rs @@ -123,11 +123,11 @@ super::ArraySize::Dynamic => 1, }; count * stride } Self::Struct { span, .. } => span, - Self::Image { .. } | Self::Sampler { .. } => 0, + Self::Image { .. } | Self::Sampler { .. } | Self::BindingArray { .. } => 0, }) } /// Get the size of this type. Panics if the `constants` doesn't contain /// a referenced handle. This may not happen in a properly validated IR module. diff --git a/third_party/rust/naga/src/proc/namer.rs b/third_party/rust/naga/src/proc/namer.rs --- a/third_party/rust/naga/src/proc/namer.rs +++ b/third_party/rust/naga/src/proc/namer.rs @@ -95,11 +95,11 @@ match self.unique.get_mut(base.as_ref()) { Some(count) => { *count += 1; // Add the suffix. This may fit in base's existing allocation. let mut suffixed = base.into_owned(); - write!(&mut suffixed, "{}{}", SEPARATOR, *count).unwrap(); + write!(suffixed, "{}{}", SEPARATOR, *count).unwrap(); suffixed } None => { let mut suffixed = base.to_string(); if base.ends_with(char::is_numeric) || self.keywords.contains(base.as_ref()) { diff --git a/third_party/rust/naga/src/proc/typifier.rs b/third_party/rust/naga/src/proc/typifier.rs --- a/third_party/rust/naga/src/proc/typifier.rs +++ b/third_party/rust/naga/src/proc/typifier.rs @@ -289,10 +289,11 @@ indexed: false, }); } }) } + Ti::BindingArray { base, .. } => TypeResolution::Handle(base), ref other => { log::error!("Access type {:?}", other); return Err(ResolveError::InvalidAccess { expr: base, indexed: false, @@ -390,10 +391,11 @@ ty: ty_base, indexed: true, }); } }), + Ti::BindingArray { base, .. } => TypeResolution::Handle(base), ref other => { log::error!("Access index type {:?}", other); return Err(ResolveError::InvalidAccess { expr: base, indexed: true, diff --git a/third_party/rust/naga/src/valid/analyzer.rs b/third_party/rust/naga/src/valid/analyzer.rs --- a/third_party/rust/naga/src/valid/analyzer.rs +++ b/third_party/rust/naga/src/valid/analyzer.rs @@ -166,15 +166,23 @@ enum GlobalOrArgument { Global(Handle), Argument(u32), } -impl crate::Expression { - const fn to_global_or_argument(&self) -> Result { - Ok(match *self { +impl GlobalOrArgument { + fn from_expression( + expression_arena: &Arena, + expression: Handle, + ) -> Result { + Ok(match expression_arena[expression] { crate::Expression::GlobalVariable(var) => GlobalOrArgument::Global(var), crate::Expression::FunctionArgument(i) => GlobalOrArgument::Argument(i), + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => match expression_arena[base] { + crate::Expression::GlobalVariable(var) => GlobalOrArgument::Global(var), + _ => return Err(ExpressionError::ExpectedGlobalOrArgument), + }, _ => return Err(ExpressionError::ExpectedGlobalOrArgument), }) } } @@ -354,29 +362,29 @@ // we may now be able to determine which globals those referred to. let image_storage = match sampling.image { GlobalOrArgument::Global(var) => GlobalOrArgument::Global(var), GlobalOrArgument::Argument(i) => { let handle = arguments[i as usize]; - expression_arena[handle] - .to_global_or_argument() - .map_err(|error| { + GlobalOrArgument::from_expression(expression_arena, handle).map_err( + |error| { FunctionError::Expression { handle, error } .with_span_handle(handle, expression_arena) - })? + }, + )? } }; let sampler_storage = match sampling.sampler { GlobalOrArgument::Global(var) => GlobalOrArgument::Global(var), GlobalOrArgument::Argument(i) => { let handle = arguments[i as usize]; - expression_arena[handle] - .to_global_or_argument() - .map_err(|error| { + GlobalOrArgument::from_expression(expression_arena, handle).map_err( + |error| { FunctionError::Expression { handle, error } .with_span_handle(handle, expression_arena) - })? + }, + )? } }; // If we've managed to pin both the image and sampler down to // specific globals, record that in our `sampling_set`. Otherwise, @@ -415,21 +423,77 @@ handle: Handle, expression: &crate::Expression, expression_arena: &Arena, other_functions: &[FunctionInfo], resolve_context: &ResolveContext, + capabilities: super::Capabilities, ) -> Result<(), ExpressionError> { use crate::{Expression as E, SampleLevel as Sl}; let mut assignable_global = None; let uniformity = match *expression { - E::Access { base, index } => Uniformity { - non_uniform_result: self - .add_assignable_ref(base, &mut assignable_global) - .or(self.add_ref(index)), - requirements: UniformityRequirements::empty(), - }, + E::Access { base, index } => { + let base_ty = self[base].ty.inner_with(resolve_context.types); + + // build up the caps needed if this is indexed non-uniformly + let mut needed_caps = super::Capabilities::empty(); + let is_binding_array = match *base_ty { + crate::TypeInner::BindingArray { + base: array_element_ty_handle, + .. + } => { + // these are nasty aliases, but these idents are too long and break rustfmt + let ub_st = super::Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING; + let st_sb = super::Capabilities::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING; + let sampler = super::Capabilities::SAMPLER_NON_UNIFORM_INDEXING; + + // We're a binding array, so lets use the type of _what_ we are array of to determine if we can non-uniformly index it. + let array_element_ty = + &resolve_context.types[array_element_ty_handle].inner; + + needed_caps |= match *array_element_ty { + // If we're an image, use the appropriate limit. + crate::TypeInner::Image { class, .. } => match class { + crate::ImageClass::Storage { .. } => ub_st, + _ => st_sb, + }, + crate::TypeInner::Sampler { .. } => sampler, + // If we're anything but an image, assume we're a buffer and use the address space. + _ => { + if let E::GlobalVariable(global_handle) = expression_arena[base] { + let global = &resolve_context.global_vars[global_handle]; + match global.space { + crate::AddressSpace::Uniform => ub_st, + crate::AddressSpace::Storage { .. } => st_sb, + _ => unreachable!(), + } + } else { + unreachable!() + } + } + }; + + true + } + _ => false, + }; + + if self[index].uniformity.non_uniform_result.is_some() + && !capabilities.contains(needed_caps) + && is_binding_array + { + return Err(ExpressionError::MissingCapabilities(needed_caps)); + } + + let _ = (); + Uniformity { + non_uniform_result: self + .add_assignable_ref(base, &mut assignable_global) + .or(self.add_ref(index)), + requirements: UniformityRequirements::empty(), + } + } E::AccessIndex { base, .. } => Uniformity { non_uniform_result: self.add_assignable_ref(base, &mut assignable_global), requirements: UniformityRequirements::empty(), }, // always uniform @@ -513,12 +577,12 @@ array_index, offset: _, level, depth_ref, } => { - let image_storage = expression_arena[image].to_global_or_argument()?; - let sampler_storage = expression_arena[sampler].to_global_or_argument()?; + let image_storage = GlobalOrArgument::from_expression(expression_arena, image)?; + let sampler_storage = GlobalOrArgument::from_expression(expression_arena, sampler)?; match (image_storage, sampler_storage) { (GlobalOrArgument::Global(image), GlobalOrArgument::Global(sampler)) => { self.sampling_set.insert(SamplingKey { image, sampler }); } @@ -868,10 +932,11 @@ pub(super) fn process_function( &self, fun: &crate::Function, module: &crate::Module, flags: ValidationFlags, + capabilities: super::Capabilities, ) -> Result> { let mut info = FunctionInfo { flags, available_stages: ShaderStages::all(), uniformity: Uniformity::new(), @@ -895,10 +960,11 @@ handle, expr, &fun.expressions, &self.functions, &resolve_context, + capabilities, ) { return Err(FunctionError::Expression { handle, error } .with_span_handle(handle, &fun.expressions)); } } @@ -1013,12 +1079,19 @@ local_vars: &Arena::new(), functions: &Arena::new(), arguments: &[], }; for (handle, expression) in expressions.iter() { - info.process_expression(handle, expression, &expressions, &[], &resolve_context) - .unwrap(); + info.process_expression( + handle, + expression, + &expressions, + &[], + &resolve_context, + super::Capabilities::empty(), + ) + .unwrap(); } assert_eq!(info[non_uniform_global_expr].ref_count, 1); assert_eq!(info[uniform_global_expr].ref_count, 1); assert_eq!(info[query_expr].ref_count, 0); assert_eq!(info[access_expr].ref_count, 0); diff --git a/third_party/rust/naga/src/valid/expression.rs b/third_party/rust/naga/src/valid/expression.rs --- a/third_party/rust/naga/src/valid/expression.rs +++ b/third_party/rust/naga/src/valid/expression.rs @@ -63,10 +63,12 @@ ExpectedGlobalVariable, #[error("Not a global variable or a function argument")] ExpectedGlobalOrArgument, #[error("Calling an undeclared function {0:?}")] CallToUndeclaredFunction(Handle), + #[error("Needs to be an binding array instead of {0:?}")] + ExpectedBindingArrayType(Handle), #[error("Needs to be an image instead of {0:?}")] ExpectedImageType(Handle), #[error("Needs to be an image instead of {0:?}")] ExpectedSamplerType(Handle), #[error("Unable to operate on image class {0:?}")] @@ -113,10 +115,12 @@ WrongArgumentCount(crate::MathFunction), #[error("Argument [{1}] to {0:?} as expression {2:?} has an invalid type.")] InvalidArgumentType(crate::MathFunction, u32, Handle), #[error("Atomic result type can't be {0:?} of {1} bytes")] InvalidAtomicResultType(crate::ScalarKind, crate::Bytes), + #[error("Shader requires capability {0:?}")] + MissingCapabilities(super::Capabilities), } #[cfg(feature = "validate")] struct ExpressionTypeResolver<'a> { root: Handle, @@ -162,11 +166,13 @@ let base_type = resolver.resolve(base)?; // See the documentation for `Expression::Access`. let dynamic_indexing_restricted = match *base_type { Ti::Vector { .. } => false, Ti::Matrix { .. } | Ti::Array { .. } => true, - Ti::Pointer { .. } | Ti::ValuePointer { size: Some(_), .. } => false, + Ti::Pointer { .. } + | Ti::ValuePointer { size: Some(_), .. } + | Ti::BindingArray { .. } => false, ref other => { log::error!("Indexing of {:?}", other); return Err(ExpressionError::InvalidBaseType(base)); } }; @@ -233,11 +239,11 @@ Ti::Matrix { columns, .. } => columns as u32, Ti::Array { size: crate::ArraySize::Constant(handle), .. } => module.constants[handle].to_array_length().unwrap(), - Ti::Array { .. } => !0, // can't statically know, but need run-time checks + Ti::Array { .. } | Ti::BindingArray { .. } => !0, // 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)? } Ti::Struct { ref members, .. } => members.len() as u32, ref other => { @@ -343,17 +349,54 @@ let image_ty = match function.expressions[image] { crate::Expression::GlobalVariable(var_handle) => { module.global_variables[var_handle].ty } crate::Expression::FunctionArgument(i) => function.arguments[i as usize].ty, + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { + match function.expressions[base] { + crate::Expression::GlobalVariable(var_handle) => { + let array_ty = module.global_variables[var_handle].ty; + + match module.types[array_ty].inner { + Ti::BindingArray { base, .. } => base, + _ => { + return Err(ExpressionError::ExpectedBindingArrayType( + array_ty, + )) + } + } + } + _ => return Err(ExpressionError::ExpectedGlobalVariable), + } + } _ => return Err(ExpressionError::ExpectedGlobalVariable), }; + let sampler_ty = match function.expressions[sampler] { crate::Expression::GlobalVariable(var_handle) => { module.global_variables[var_handle].ty } crate::Expression::FunctionArgument(i) => function.arguments[i as usize].ty, + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { + match function.expressions[base] { + crate::Expression::GlobalVariable(var_handle) => { + let array_ty = module.global_variables[var_handle].ty; + + match module.types[array_ty].inner { + Ti::BindingArray { base, .. } => base, + _ => { + return Err(ExpressionError::ExpectedBindingArrayType( + array_ty, + )) + } + } + } + _ => return Err(ExpressionError::ExpectedGlobalVariable), + } + } _ => return Err(ExpressionError::ExpectedGlobalVariable), }; let comparison = match module.types[sampler_ty].inner { Ti::Sampler { comparison } => comparison, _ => return Err(ExpressionError::ExpectedSamplerType(sampler_ty)), @@ -539,10 +582,28 @@ let ty = match function.expressions[image] { crate::Expression::GlobalVariable(var_handle) => { module.global_variables[var_handle].ty } crate::Expression::FunctionArgument(i) => function.arguments[i as usize].ty, + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { + match function.expressions[base] { + crate::Expression::GlobalVariable(var_handle) => { + let array_ty = module.global_variables[var_handle].ty; + + match module.types[array_ty].inner { + Ti::BindingArray { base, .. } => base, + _ => { + return Err(ExpressionError::ExpectedBindingArrayType( + array_ty, + )) + } + } + } + _ => return Err(ExpressionError::ExpectedGlobalVariable), + } + } _ => return Err(ExpressionError::ExpectedGlobalVariable), }; match module.types[ty].inner { Ti::Image { class, @@ -604,10 +665,28 @@ let ty = match function.expressions[image] { crate::Expression::GlobalVariable(var_handle) => { module.global_variables[var_handle].ty } crate::Expression::FunctionArgument(i) => function.arguments[i as usize].ty, + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { + match function.expressions[base] { + crate::Expression::GlobalVariable(var_handle) => { + let array_ty = module.global_variables[var_handle].ty; + + match module.types[array_ty].inner { + Ti::BindingArray { base, .. } => base, + _ => { + return Err(ExpressionError::ExpectedBindingArrayType( + array_ty, + )) + } + } + } + _ => return Err(ExpressionError::ExpectedGlobalVariable), + } + } _ => return Err(ExpressionError::ExpectedGlobalVariable), }; match module.types[ty].inner { Ti::Image { class, arrayed, .. } => { let good = match query { diff --git a/third_party/rust/naga/src/valid/function.rs b/third_party/rust/naga/src/valid/function.rs --- a/third_party/rust/naga/src/valid/function.rs +++ b/third_party/rust/naga/src/valid/function.rs @@ -655,19 +655,40 @@ // and could probably be refactored. let var = match *context.get_expression(image).map_err(|e| e.with_span())? { crate::Expression::GlobalVariable(var_handle) => { &context.global_vars[var_handle] } + // We're looking at a binding index situation, so punch through the index and look at the global behind it. + crate::Expression::Access { base, .. } + | crate::Expression::AccessIndex { base, .. } => { + match *context.get_expression(base).map_err(|e| e.with_span())? { + crate::Expression::GlobalVariable(var_handle) => { + &context.global_vars[var_handle] + } + _ => { + return Err(FunctionError::InvalidImageStore( + ExpressionError::ExpectedGlobalVariable, + ) + .with_span_handle(image, context.expressions)) + } + } + } _ => { return Err(FunctionError::InvalidImageStore( ExpressionError::ExpectedGlobalVariable, ) .with_span_handle(image, context.expressions)) } }; - let value_ty = match context.types[var.ty].inner { + // Punch through a binding array to get the underlying type + let global_ty = match context.types[var.ty].inner { + Ti::BindingArray { base, .. } => &context.types[base].inner, + ref inner => inner, + }; + + let value_ty = match *global_ty { Ti::Image { class, arrayed, dim, } => { @@ -821,11 +842,11 @@ fun: &crate::Function, module: &crate::Module, mod_info: &ModuleInfo, ) -> Result> { #[cfg_attr(not(feature = "validate"), allow(unused_mut))] - let mut info = mod_info.process_function(fun, module, self.flags)?; + let mut info = mod_info.process_function(fun, module, self.flags, self.capabilities)?; #[cfg(feature = "validate")] for (var_handle, var) in fun.local_variables.iter() { self.validate_local_var(var, &module.types, &module.constants) .map_err(|error| { diff --git a/third_party/rust/naga/src/valid/interface.rs b/third_party/rust/naga/src/valid/interface.rs --- a/third_party/rust/naga/src/valid/interface.rs +++ b/third_party/rust/naga/src/valid/interface.rs @@ -381,11 +381,13 @@ true, ) } crate::AddressSpace::Handle => { match types[var.ty].inner { - crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } => {} + crate::TypeInner::Image { .. } + | crate::TypeInner::Sampler { .. } + | crate::TypeInner::BindingArray { .. } => {} _ => { return Err(GlobalVariableError::InvalidType(var.space)); } }; (TypeFlags::empty(), true) @@ -510,10 +512,17 @@ let allowed_usage = match var.space { crate::AddressSpace::Function => unreachable!(), crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY, crate::AddressSpace::Storage { access } => storage_usage(access), crate::AddressSpace::Handle => match module.types[var.ty].inner { + crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner { + crate::TypeInner::Image { + class: crate::ImageClass::Storage { access, .. }, + .. + } => storage_usage(access), + _ => GlobalUse::READ | GlobalUse::QUERY, + }, crate::TypeInner::Image { class: crate::ImageClass::Storage { access, .. }, .. } => storage_usage(access), _ => GlobalUse::READ | GlobalUse::QUERY, diff --git a/third_party/rust/naga/src/valid/mod.rs b/third_party/rust/naga/src/valid/mod.rs --- a/third_party/rust/naga/src/valid/mod.rs +++ b/third_party/rust/naga/src/valid/mod.rs @@ -86,10 +86,16 @@ const PUSH_CONSTANT = 0x1; /// Float values with width = 8. const FLOAT64 = 0x2; /// Support for `Builtin:PrimitiveIndex`. const PRIMITIVE_INDEX = 0x4; + /// Support for non-uniform indexing of sampled textures and storage buffer arrays. + const SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 0x8; + /// 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; } } bitflags::bitflags! { /// Validation flags. @@ -201,11 +207,14 @@ } | Self::Atomic { .. } | Self::Pointer { .. } | Self::ValuePointer { .. } | Self::Struct { .. } => true, - Self::Array { .. } | Self::Image { .. } | Self::Sampler { .. } => false, + Self::Array { .. } + | Self::Image { .. } + | Self::Sampler { .. } + | Self::BindingArray { .. } => false, } } /// Return the `ImageDimension` for which `self` is an appropriate coordinate. #[cfg(feature = "validate")] diff --git a/third_party/rust/naga/src/valid/type.rs b/third_party/rust/naga/src/valid/type.rs --- a/third_party/rust/naga/src/valid/type.rs +++ b/third_party/rust/naga/src/valid/type.rs @@ -578,8 +578,9 @@ } ti } Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::new(TypeFlags::ARGUMENT, 0), + Ti::BindingArray { .. } => TypeInfo::new(TypeFlags::empty(), 0), }) } } diff --git a/third_party/rust/wgpu-core/.cargo-checksum.json b/third_party/rust/wgpu-core/.cargo-checksum.json --- a/third_party/rust/wgpu-core/.cargo-checksum.json +++ b/third_party/rust/wgpu-core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"8843330929d66b09e5eb1ee5ebc5aa0991d777187d69a9fd58e719d20bf9c02a","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"218bfaa87b291be693c27ea496111c4a66edcbc585ce2661c060d5996822334b","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"4519a28c5acc4a70de274ad7e577073beb85c5b273d7e6081b7e78e87154ae93","src/command/clear.rs":"03f624521571c46080051baa95b60230aca5698e2ddc6c83100a4c1162de4e1a","src/command/compute.rs":"775d08b85c27cadfe7123b6312386099a165a72e1ff8fbdb25e9e84e23ca5623","src/command/draw.rs":"c04bdbcdabfdf5e3b4524bf155089b20185e283847ecfd628acc983101b2cbba","src/command/memory_init.rs":"f7c73879b9fa728039a45d78f1f865e4eca863c860b3775a4542c973dfdb68c5","src/command/mod.rs":"993b8a9806fa55f6d24eb6d49db353ab1ae1fc16cac5f4fb2557f6a3286df758","src/command/query.rs":"18659df22356493697ce1c5b56fe2ca0e2f3f1ef29bdf00d65f064eeecba5130","src/command/render.rs":"db903e1e659eb01ed521ffa7e07afd626026e8086e206eb699783ff15fc03cf8","src/command/transfer.rs":"6277878f4d99516bd7db5612aaf4fac280a46d48c44575a7c5f501d6fbae5c61","src/conv.rs":"15d87cd2a91cb9514273e86441940b86380861ec146796e5f214e95e4124d975","src/device/life.rs":"72ae33cc066ca1537c89ba1c7b12dab89957ea0449a4348e007a73aad471f4a5","src/device/mod.rs":"74c8f6bb462a65fe89115e77e5908df295e4582d5db50095b2817d0168c5bd7b","src/device/queue.rs":"5d070ff1f94f0796ece9359548806e41ed7c8f466828328f04fc7616644796e5","src/device/trace.rs":"de575a8213c8ae9df711e4b6afe5736d71ac65bf141375fe044d3b6c4375e039","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"5b6094b56b02c1a799e64ca6121f87dbb446e9e5bf1f7f3f2792a34b68c5d109","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":"30890e69af605c18c0402134cd7475ee504694e69d2ae1a50432053d3e90448c","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":"068c8a3560abac8e7f811cceb2386441ad88dc380c4e6ca7007f09b4e2a39321"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"815e6341367472fde59a2939c7fb20cd78e05bc6db20eb0b75838e57b4d2266b","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"218bfaa87b291be693c27ea496111c4a66edcbc585ce2661c060d5996822334b","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"680091508e4abf22c4a4edb3d889945f1dd023cd2ae2bbfb0344c79ed0a3752c","src/command/clear.rs":"03f624521571c46080051baa95b60230aca5698e2ddc6c83100a4c1162de4e1a","src/command/compute.rs":"001359977e2d5d87cfc9f0b6a4b0ff44c6c2a13dc21d3771056ee028d9105f45","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":"132d1e3762a6ffe525b54e0848b35ce51e3104285b075232cb3281e5bfe2ad27","src/command/transfer.rs":"6277878f4d99516bd7db5612aaf4fac280a46d48c44575a7c5f501d6fbae5c61","src/conv.rs":"15d87cd2a91cb9514273e86441940b86380861ec146796e5f214e95e4124d975","src/device/life.rs":"72ae33cc066ca1537c89ba1c7b12dab89957ea0449a4348e007a73aad471f4a5","src/device/mod.rs":"060212413f3380f524446113bc2d40b783a7f4f048c5e30f091a9aacb83aab96","src/device/queue.rs":"5d070ff1f94f0796ece9359548806e41ed7c8f466828328f04fc7616644796e5","src/device/trace.rs":"de575a8213c8ae9df711e4b6afe5736d71ac65bf141375fe044d3b6c4375e039","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"5b6094b56b02c1a799e64ca6121f87dbb446e9e5bf1f7f3f2792a34b68c5d109","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":"30890e69af605c18c0402134cd7475ee504694e69d2ae1a50432053d3e90448c","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 diff --git a/third_party/rust/wgpu-core/Cargo.toml b/third_party/rust/wgpu-core/Cargo.toml --- a/third_party/rust/wgpu-core/Cargo.toml +++ b/third_party/rust/wgpu-core/Cargo.toml @@ -38,11 +38,11 @@ smallvec = "1" thiserror = "1" [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["span", "validate", "wgsl-in"] [dependencies.wgt] path = "../wgpu-types" diff --git a/third_party/rust/wgpu-core/src/command/bundle.rs b/third_party/rust/wgpu-core/src/command/bundle.rs --- a/third_party/rust/wgpu-core/src/command/bundle.rs +++ b/third_party/rust/wgpu-core/src/command/bundle.rs @@ -34,12 +34,12 @@ #![allow(clippy::reversed_empty_ranges)] use crate::{ binding_model::buffer_binding_type_alignment, command::{ - BasePass, DrawError, MapPassErr, PassErrorScope, RenderCommand, RenderCommandError, - StateChange, + BasePass, BindGroupStateChange, DrawError, MapPassErr, PassErrorScope, RenderCommand, + RenderCommandError, StateChange, }, conv, device::{ AttachmentData, Device, DeviceError, MissingDownlevelFlags, RenderPassContext, SHADER_STAGE_COUNT, @@ -84,10 +84,16 @@ pub struct RenderBundleEncoder { base: BasePass, parent_id: id::DeviceId, pub(crate) context: RenderPassContext, pub(crate) is_ds_read_only: bool, + + // Resource binding dedupe state. + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_bind_groups: BindGroupStateChange, + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_pipeline: StateChange, } impl RenderBundleEncoder { pub fn new( desc: &RenderBundleEncoderDescriptor, @@ -124,10 +130,13 @@ (!aspects.contains(hal::FormatAspects::DEPTH) || ds.depth_read_only) && (!aspects.contains(hal::FormatAspects::STENCIL) || ds.stencil_read_only) } None => false, }, + + current_bind_groups: BindGroupStateChange::new(), + current_pipeline: StateChange::new(), }) } pub fn dummy(parent_id: id::DeviceId) -> Self { Self { @@ -141,10 +150,13 @@ }, sample_count: 0, multiview: None, }, is_ds_read_only: false, + + current_bind_groups: BindGroupStateChange::new(), + current_pipeline: StateChange::new(), } } #[cfg(feature = "trace")] pub(crate) fn to_base_pass(&self) -> BasePass { @@ -178,11 +190,11 @@ .collect(), push_constant_ranges: PushConstantState::new(), raw_dynamic_offsets: Vec::new(), flat_dynamic_offsets: Vec::new(), used_bind_groups: 0, - pipeline: StateChange::new(), + 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(); @@ -250,13 +262,12 @@ //Note: stateless trackers are not merged: the lifetime reference // is held to the bind group itself. } RenderCommand::SetPipeline(pipeline_id) => { let scope = PassErrorScope::SetPipelineRender(pipeline_id); - if state.pipeline.set_and_check_redundant(pipeline_id) { - continue; - } + + state.pipeline = Some(pipeline_id); let pipeline = state .trackers .render_pipes .use_extend(&*pipeline_guard, pipeline_id, (), ()) @@ -368,11 +379,11 @@ first_instance, } => { let scope = PassErrorScope::Draw { indexed: false, indirect: false, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; let vertex_limits = state.vertex_limits(); let last_vertex = first_vertex + vertex_count; if last_vertex > vertex_limits.vertex_limit { return Err(DrawError::VertexBeyondLimit { @@ -403,11 +414,11 @@ first_instance, } => { let scope = PassErrorScope::Draw { indexed: true, indirect: false, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; //TODO: validate that base_vertex + max_index() is within the provided range let vertex_limits = state.vertex_limits(); let index_limit = state.index.limit(); let last_index = first_index + index_count; @@ -439,11 +450,11 @@ indexed: false, } => { let scope = PassErrorScope::Draw { indexed: false, indirect: true, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; device .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; @@ -472,11 +483,11 @@ indexed: true, } => { let scope = PassErrorScope::Draw { indexed: true, indirect: true, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; device .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION) .map_pass_err(scope)?; @@ -988,11 +999,11 @@ bind: ArrayVec, push_constant_ranges: PushConstantState, raw_dynamic_offsets: Vec, flat_dynamic_offsets: Vec, used_bind_groups: usize, - pipeline: StateChange, + pipeline: Option, } impl State { fn vertex_limits(&self) -> VertexLimitState { let mut vert_state = VertexLimitState { @@ -1220,28 +1231,38 @@ index: u32, bind_group_id: id::BindGroupId, offsets: *const DynamicOffset, offset_length: usize, ) { + let redundant = bundle.current_bind_groups.set_and_check_redundant( + bind_group_id, + index, + &mut bundle.base.dynamic_offsets, + offsets, + offset_length, + ); + + if redundant { + return; + } + bundle.base.commands.push(RenderCommand::SetBindGroup { index: index.try_into().unwrap(), num_dynamic_offsets: offset_length.try_into().unwrap(), bind_group_id, }); - if offset_length != 0 { - bundle - .base - .dynamic_offsets - .extend_from_slice(slice::from_raw_parts(offsets, offset_length)); - } } #[no_mangle] pub extern "C" fn wgpu_render_bundle_set_pipeline( bundle: &mut RenderBundleEncoder, pipeline_id: id::RenderPipelineId, ) { + if bundle.current_pipeline.set_and_check_redundant(pipeline_id) { + return; + } + bundle .base .commands .push(RenderCommand::SetPipeline(pipeline_id)); } diff --git a/third_party/rust/wgpu-core/src/command/compute.rs b/third_party/rust/wgpu-core/src/command/compute.rs --- a/third_party/rust/wgpu-core/src/command/compute.rs +++ b/third_party/rust/wgpu-core/src/command/compute.rs @@ -4,12 +4,12 @@ }, command::{ bind::Binder, end_pipeline_statistics_query, memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState}, - BasePass, BasePassRef, CommandBuffer, CommandEncoderError, CommandEncoderStatus, - MapPassErr, PassErrorScope, QueryUseError, StateChange, + BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError, + CommandEncoderStatus, MapPassErr, PassErrorScope, QueryUseError, StateChange, }, device::MissingDownlevelFlags, error::{ErrorFormatter, PrettyError}, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, @@ -74,17 +74,26 @@ #[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))] pub struct ComputePass { base: BasePass, parent_id: id::CommandEncoderId, + + // Resource binding dedupe state. + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_bind_groups: BindGroupStateChange, + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_pipeline: StateChange, } impl ComputePass { pub fn new(parent_id: id::CommandEncoderId, desc: &ComputePassDescriptor) -> Self { Self { base: BasePass::new(&desc.label), parent_id, + + current_bind_groups: BindGroupStateChange::new(), + current_pipeline: StateChange::new(), } } pub fn parent_id(&self) -> id::CommandEncoderId { self.parent_id @@ -220,11 +229,11 @@ } #[derive(Debug)] struct State { binder: Binder, - pipeline: StateChange, + pipeline: Option, trackers: StatefulTrackerSubset, debug_scope_depth: u32, } impl State { @@ -234,11 +243,11 @@ //let (expected, provided) = self.binder.entries[index as usize].info(); return Err(DispatchError::IncompatibleBindGroup { index: bind_mask.trailing_zeros(), }); } - if self.pipeline.is_unset() { + if self.pipeline.is_none() { return Err(DispatchError::MissingPipeline); } self.binder.check_late_buffer_bindings()?; Ok(()) @@ -323,11 +332,11 @@ let (buffer_guard, mut token) = hub.buffers.read(&mut token); let (texture_guard, _) = hub.textures.read(&mut token); let mut state = State { binder: Binder::new(), - pipeline: StateChange::new(), + pipeline: None, trackers: StatefulTrackerSubset::new(A::VARIANT), debug_scope_depth: 0, }; let mut temp_offsets = Vec::new(); let mut dynamic_offset_count = 0; @@ -418,13 +427,11 @@ } } ComputeCommand::SetPipeline(pipeline_id) => { let scope = PassErrorScope::SetPipelineCompute(pipeline_id); - if state.pipeline.set_and_check_redundant(pipeline_id) { - continue; - } + state.pipeline = Some(pipeline_id); let pipeline = cmd_buf .trackers .compute_pipes .use_extend(&*pipeline_guard, pipeline_id, (), ()) @@ -522,11 +529,11 @@ } } ComputeCommand::Dispatch(groups) => { let scope = PassErrorScope::Dispatch { indirect: false, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; fixup_discarded_surfaces( pending_discard_init_fixups.drain(..), raw, @@ -566,11 +573,11 @@ } } ComputeCommand::DispatchIndirect { buffer_id, offset } => { let scope = PassErrorScope::Dispatch { indirect: true, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; state.is_ready().map_pass_err(scope)?; device @@ -748,27 +755,38 @@ index: u32, bind_group_id: id::BindGroupId, offsets: *const DynamicOffset, offset_length: usize, ) { + let redundant = pass.current_bind_groups.set_and_check_redundant( + bind_group_id, + index, + &mut pass.base.dynamic_offsets, + offsets, + offset_length, + ); + + if redundant { + return; + } + pass.base.commands.push(ComputeCommand::SetBindGroup { index: index.try_into().unwrap(), num_dynamic_offsets: offset_length.try_into().unwrap(), bind_group_id, }); - if offset_length != 0 { - pass.base - .dynamic_offsets - .extend_from_slice(slice::from_raw_parts(offsets, offset_length)); - } } #[no_mangle] pub extern "C" fn wgpu_compute_pass_set_pipeline( pass: &mut ComputePass, pipeline_id: id::ComputePipelineId, ) { + if pass.current_pipeline.set_and_check_redundant(pipeline_id) { + return; + } + pass.base .commands .push(ComputeCommand::SetPipeline(pipeline_id)); } @@ -810,11 +828,11 @@ values_offset: value_offset, }); } #[no_mangle] - pub extern "C" fn wgpu_compute_pass_dispatch( + pub extern "C" fn wgpu_compute_pass_dispatch_workgroups( pass: &mut ComputePass, groups_x: u32, groups_y: u32, groups_z: u32, ) { @@ -822,11 +840,11 @@ .commands .push(ComputeCommand::Dispatch([groups_x, groups_y, groups_z])); } #[no_mangle] - pub extern "C" fn wgpu_compute_pass_dispatch_indirect( + pub extern "C" fn wgpu_compute_pass_dispatch_workgroups_indirect( pass: &mut ComputePass, buffer_id: id::BufferId, offset: BufferAddress, ) { pass.base diff --git a/third_party/rust/wgpu-core/src/command/mod.rs b/third_party/rust/wgpu-core/src/command/mod.rs --- a/third_party/rust/wgpu-core/src/command/mod.rs +++ b/third_party/rust/wgpu-core/src/command/mod.rs @@ -6,10 +6,12 @@ mod memory_init; mod query; mod render; mod transfer; +use std::slice; + pub(crate) use self::clear::clear_texture_no_device; pub use self::{ bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*, }; @@ -403,11 +405,11 @@ count_words += size_to_write_words; } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] struct StateChange { last_state: Option, } impl StateChange { @@ -417,18 +419,69 @@ fn set_and_check_redundant(&mut self, new_state: T) -> bool { let already_set = self.last_state == Some(new_state); self.last_state = Some(new_state); already_set } - fn is_unset(&self) -> bool { - self.last_state.is_none() - } fn reset(&mut self) { self.last_state = None; } } +impl Default for StateChange { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug)] +struct BindGroupStateChange { + last_states: [StateChange; hal::MAX_BIND_GROUPS], +} + +impl BindGroupStateChange { + fn new() -> Self { + Self { + last_states: [StateChange::new(); hal::MAX_BIND_GROUPS], + } + } + + unsafe fn set_and_check_redundant( + &mut self, + bind_group_id: id::BindGroupId, + index: u32, + dynamic_offsets: &mut Vec, + offsets: *const wgt::DynamicOffset, + offset_length: usize, + ) -> bool { + // For now never deduplicate bind groups with dynamic offsets. + if offset_length == 0 { + // If this get returns None, that means we're well over the limit, so let the call through to get a proper error + if let Some(current_bind_group) = self.last_states.get_mut(index as usize) { + // Bail out if we're binding the same bind group. + if current_bind_group.set_and_check_redundant(bind_group_id) { + return true; + } + } + } else { + // We intentionally remove the memory of this bind group if we have dynamic offsets, + // such that if you try to bind this bind group later with _no_ dynamic offsets it + // tries to bind it again and gives a proper validation error. + if let Some(current_bind_group) = self.last_states.get_mut(index as usize) { + current_bind_group.reset(); + } + dynamic_offsets.extend_from_slice(slice::from_raw_parts(offsets, offset_length)); + } + false + } +} + +impl Default for BindGroupStateChange { + fn default() -> Self { + Self::new() + } +} + trait MapPassErr { fn map_pass_err(self, scope: PassErrorScope) -> Result; } #[derive(Clone, Copy, Debug, Error)] diff --git a/third_party/rust/wgpu-core/src/command/render.rs b/third_party/rust/wgpu-core/src/command/render.rs --- a/third_party/rust/wgpu-core/src/command/render.rs +++ b/third_party/rust/wgpu-core/src/command/render.rs @@ -2,13 +2,13 @@ binding_model::BindError, command::{ bind::Binder, end_pipeline_statistics_query, memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState}, - BasePass, BasePassRef, CommandBuffer, CommandEncoderError, CommandEncoderStatus, DrawError, - ExecutionError, MapPassErr, PassErrorScope, QueryResetMap, QueryUseError, RenderCommand, - RenderCommandError, StateChange, + BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError, + CommandEncoderStatus, DrawError, ExecutionError, MapPassErr, PassErrorScope, QueryResetMap, + QueryUseError, RenderCommand, RenderCommandError, StateChange, }, device::{ AttachmentData, Device, MissingDownlevelFlags, MissingFeatures, RenderPassCompatibilityError, RenderPassContext, }, @@ -162,19 +162,28 @@ pub struct RenderPass { base: BasePass, parent_id: id::CommandEncoderId, color_targets: ArrayVec, depth_stencil_target: Option, + + // Resource binding dedupe state. + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_bind_groups: BindGroupStateChange, + #[cfg_attr(feature = "serial-pass", serde(skip))] + current_pipeline: StateChange, } impl RenderPass { pub fn new(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self { Self { base: BasePass::new(&desc.label), parent_id, color_targets: desc.color_attachments.iter().cloned().collect(), depth_stencil_target: desc.depth_stencil_attachment.cloned(), + + current_bind_groups: BindGroupStateChange::new(), + current_pipeline: StateChange::new(), } } pub fn parent_id(&self) -> id::CommandEncoderId { self.parent_id @@ -335,11 +344,11 @@ struct State { pipeline_flags: PipelineFlags, binder: Binder, blend_constant: OptionalState, stencil_reference: u32, - pipeline: StateChange, + pipeline: Option, index: IndexState, vertex: VertexState, debug_scope_depth: u32, } @@ -359,11 +368,11 @@ //let (expected, provided) = self.binder.entries[index as usize].info(); return Err(DrawError::IncompatibleBindGroup { index: bind_mask.trailing_zeros(), }); } - if self.pipeline.is_unset() { + if self.pipeline.is_none() { return Err(DrawError::MissingPipeline); } if self.blend_constant == OptionalState::Required { return Err(DrawError::MissingBlendConstant); } @@ -390,11 +399,11 @@ } /// Reset the `RenderBundle`-related states. fn reset_bundle(&mut self) { self.binder.reset(); - self.pipeline.reset(); + self.pipeline = None; self.index.reset(); self.vertex.reset(); } } @@ -1090,11 +1099,11 @@ let mut state = State { pipeline_flags: PipelineFlags::empty(), binder: Binder::new(), blend_constant: OptionalState::Unused, stencil_reference: 0, - pipeline: StateChange::new(), + pipeline: None, index: IndexState::default(), vertex: VertexState::default(), debug_scope_depth: 0, }; let mut temp_offsets = Vec::new(); @@ -1185,13 +1194,11 @@ } } } RenderCommand::SetPipeline(pipeline_id) => { let scope = PassErrorScope::SetPipelineRender(pipeline_id); - if state.pipeline.set_and_check_redundant(pipeline_id) { - continue; - } + state.pipeline = Some(pipeline_id); let pipeline = cmd_buf .trackers .render_pipes .use_extend(&*pipeline_guard, pipeline_id, (), ()) @@ -1503,11 +1510,11 @@ } => { let indexed = false; let scope = PassErrorScope::Draw { indexed, indirect: false, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; state.is_ready(indexed).map_pass_err(scope)?; let last_vertex = first_vertex + vertex_count; let vertex_limit = state.vertex.vertex_limit; @@ -1543,11 +1550,11 @@ } => { let indexed = true; let scope = PassErrorScope::Draw { indexed, indirect: false, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; state.is_ready(indexed).map_pass_err(scope)?; //TODO: validate that base_vertex + max_index() is within the provided range let last_index = first_index + index_count; @@ -1587,11 +1594,11 @@ indexed, } => { let scope = PassErrorScope::Draw { indexed, indirect: true, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; state.is_ready(indexed).map_pass_err(scope)?; let stride = match indexed { false => mem::size_of::(), @@ -1660,11 +1667,11 @@ indexed, } => { let scope = PassErrorScope::Draw { indexed, indirect: true, - pipeline: state.pipeline.last_state, + pipeline: state.pipeline, }; state.is_ready(indexed).map_pass_err(scope)?; let stride = match indexed { false => mem::size_of::(), @@ -2010,27 +2017,38 @@ index: u32, bind_group_id: id::BindGroupId, offsets: *const DynamicOffset, offset_length: usize, ) { + let redundant = pass.current_bind_groups.set_and_check_redundant( + bind_group_id, + index, + &mut pass.base.dynamic_offsets, + offsets, + offset_length, + ); + + if redundant { + return; + } + pass.base.commands.push(RenderCommand::SetBindGroup { index: index.try_into().unwrap(), num_dynamic_offsets: offset_length.try_into().unwrap(), bind_group_id, }); - if offset_length != 0 { - pass.base - .dynamic_offsets - .extend_from_slice(slice::from_raw_parts(offsets, offset_length)); - } } #[no_mangle] pub extern "C" fn wgpu_render_pass_set_pipeline( pass: &mut RenderPass, pipeline_id: id::RenderPipelineId, ) { + if pass.current_pipeline.set_and_check_redundant(pipeline_id) { + return; + } + pass.base .commands .push(RenderCommand::SetPipeline(pipeline_id)); } diff --git a/third_party/rust/wgpu-core/src/device/mod.rs b/third_party/rust/wgpu-core/src/device/mod.rs --- a/third_party/rust/wgpu-core/src/device/mod.rs +++ b/third_party/rust/wgpu-core/src/device/mod.rs @@ -854,14 +854,11 @@ } dim } None => match texture.desc.dimension { wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1, - wgt::TextureDimension::D2 - if texture.desc.size.depth_or_array_layers > 1 - && desc.range.array_layer_count.is_none() => - { + wgt::TextureDimension::D2 if texture.desc.size.depth_or_array_layers > 1 => { wgt::TextureViewDimension::D2Array } wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2, wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3, }, @@ -869,11 +866,17 @@ let required_level_count = desc.range.base_mip_level + desc.range.mip_level_count.map_or(1, |count| count.get()); let required_layer_count = match desc.range.array_layer_count { Some(count) => desc.range.base_array_layer + count.get(), - None => texture.desc.array_layer_count(), + None => match view_dim { + wgt::TextureViewDimension::D1 + | wgt::TextureViewDimension::D2 + | wgt::TextureViewDimension::D3 => 1, + wgt::TextureViewDimension::Cube => 6, + _ => texture.desc.array_layer_count(), + }, }; let level_end = texture.full_range.levels.end; let layer_end = texture.full_range.layers.end; if required_level_count > level_end { return Err(resource::CreateTextureViewError::TooManyMipLevels { @@ -1140,10 +1143,29 @@ caps.set( Caps::PRIMITIVE_INDEX, self.features .contains(wgt::Features::SHADER_PRIMITIVE_INDEX), ); + caps.set( + Caps::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ), + ); + caps.set( + Caps::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ), + ); + // TODO: This needs a proper wgpu feature + caps.set( + Caps::SAMPLER_NON_UNIFORM_INDEXING, + self.features.contains( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ), + ); let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) .validate(&module) .map_err(|inner| { pipeline::CreateShaderModuleError::Validation(pipeline::ShaderError { source, diff --git a/third_party/rust/wgpu-core/src/validation.rs b/third_party/rust/wgpu-core/src/validation.rs --- a/third_party/rust/wgpu-core/src/validation.rs +++ b/third_party/rust/wgpu-core/src/validation.rs @@ -865,11 +865,18 @@ for (var_handle, var) in module.global_variables.iter() { let bind = match var.binding { Some(ref br) => br.clone(), _ => continue, }; - let ty = match module.types[var.ty].inner { + let naga_ty = &module.types[var.ty].inner; + + let inner_ty = match *naga_ty { + naga::TypeInner::BindingArray { base, .. } => &module.types[base].inner, + ref ty => ty, + }; + + let ty = match *inner_ty { naga::TypeInner::Image { dim, arrayed, class, } => ResourceType::Texture { @@ -897,11 +904,11 @@ resource_mapping.insert(var_handle, handle); } let mut entry_points = FastHashMap::default(); entry_points.reserve(module.entry_points.len()); - for (index, entry_point) in (&module.entry_points).iter().enumerate() { + for (index, entry_point) in module.entry_points.iter().enumerate() { let info = info.get_entry_point(index); let mut ep = EntryPoint::default(); for arg in entry_point.function.arguments.iter() { Self::populate(&mut ep.inputs, arg.binding.as_ref(), arg.ty, &module.types); } diff --git a/third_party/rust/wgpu-hal/.cargo-checksum.json b/third_party/rust/wgpu-hal/.cargo-checksum.json --- a/third_party/rust/wgpu-hal/.cargo-checksum.json +++ b/third_party/rust/wgpu-hal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"e7dc9c948392b41eaaa81f730d2371e4cc94c9e49acb30edc9318bb0e0031a13","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":"dfd24df4d699772a02f8f1a9bb63e276d41ecb8ee899325d2818e01202b0845b","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":"5b6f4110b558a3f5a35eca803b8253bae0f00156e9f99c486a0f20fb66a494da","src/dx12/command.rs":"f9969744663c1f01fca3892b93fc516363fa8725739d270a5f633db78f7902da","src/dx12/conv.rs":"c27336e5b576a7e05a576548fa0e6588ff77be4208f08aac0bea7e649ed1301b","src/dx12/descriptor.rs":"7145d3dc6be13fae4cf6bb8bf34a1ea1749ad87e5f429b84f3cbbea7bf63c148","src/dx12/device.rs":"d7509b4ab3537b1a4925d3f1abcca16b3ce77465a0a5ec3dcfa7c436a0f46576","src/dx12/instance.rs":"ccc36443cb1df8ab8ed8366cf8599ec3d75fb5fefa5f9bb0f0f0b5e6fc1c5102","src/dx12/mod.rs":"2ff1c8f52acbd86fa31838119025c20abfcdd13a4224fa396074b7857759a9a5","src/dx12/view.rs":"b7a5cb8933f30517a97b4bd767e10b4c09f686dbf493d53b9b265d2d0b16f1a6","src/empty.rs":"6bf65e405f63eff49b25d079af644b352b95a9c7edcf3a57be2e96a50307b66b","src/gles/adapter.rs":"c5b405d405c820cf7c2540125f1b3fed72983c9e4cabda1ef949b949a92c5915","src/gles/command.rs":"3bef8a822b59c7e986c2c6dd084a2c537a96de77e2b48522f86210093daad3d0","src/gles/conv.rs":"c5d6ba4afd2b3245b1a36a84e4c392c640e6170a1e81df3741bf5323c57bdc51","src/gles/device.rs":"95a9ab1d029ff25711e009c8d5fb4fe1454575b862e1c1eb699a67a660cfcccc","src/gles/egl.rs":"38dc851eede42b6be2ff02f15fef3a4a116cd1b5803974fada89ac86d42a5df4","src/gles/mod.rs":"8686d9bcfb8a7be6f3b8ff7d96e7538e71c3d37b2b09762b06aa304518165dfd","src/gles/queue.rs":"21543baba720fbd68aa679334c82f05ed4e138304c485cb4a9748c588cda67a6","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":"aff587532db234498bdf8e7282bb3100e378daf547feff4ffa88ea6e0d72da0d","src/metal/adapter.rs":"eb2c4969e20841115453dae06957741d317428c196e795f2fb1f64706bf09cd0","src/metal/command.rs":"de41fa5eaa2208abd9ff3d7daf929b17664c445cb26d43331aa84bcc7cfb67cd","src/metal/conv.rs":"517c491a87ba57f275f4a2f130ef1fc2c17528176ebc4813772f9bcd86ffd8e5","src/metal/device.rs":"839f39636c680098c3b9bec20770d2240775f0afeb0dc0111566feab92e9b93b","src/metal/mod.rs":"cb4ebc93b08fee670dc9b28115eb14b8e351a4e1b00bc8cc5384b6967c6ad614","src/metal/surface.rs":"82836cadc751d94fb016bd590cdfec5649cbfae2f44d14599ed074dfb0a004dc","src/vulkan/adapter.rs":"30943968799a96810e25ed79f304b331cafc85598180a6a407675ebdb595c0d0","src/vulkan/command.rs":"60d1867acd0e46c34dabecea708cd776a1f435721b6673a506b5bb8aee87ff80","src/vulkan/conv.rs":"0d7b706a854ff3fdcea0e66f0d8563d9c2aa1f82b2e84692c16f4bfcc3872ed5","src/vulkan/device.rs":"ed980a4cbad2a0aa9d2b6b2b3f28b71121f6773521664baf0bb66e225bbae376","src/vulkan/instance.rs":"49bb57b65c886c14f258e8a7fc183138765310ac7efd2fe3b26115e7942aa1fe","src/vulkan/mod.rs":"85e6a91864f36ec95517094a695b59a0dc0deb000b8772bb3c8656c2b2a4e9b4"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"4e2ab8fc5f312374719f32baf4a24c9a69106b8a8a0be6739aae02af286c1b9f","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":"41012a4d69c9a88574b7246c556ccf71d6c8c28bb855239ea140c1e25728f7e0","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":"aff587532db234498bdf8e7282bb3100e378daf547feff4ffa88ea6e0d72da0d","src/metal/adapter.rs":"eb2c4969e20841115453dae06957741d317428c196e795f2fb1f64706bf09cd0","src/metal/command.rs":"b06983d7e11cdde526b7c9f5f0b86f1ea8faef02b7666367cb231211a8301570","src/metal/conv.rs":"517c491a87ba57f275f4a2f130ef1fc2c17528176ebc4813772f9bcd86ffd8e5","src/metal/device.rs":"dd823c8e12ba3ed69ef7cdcb543e8d995d0056d1f838516b0901068c83d8ffe2","src/metal/mod.rs":"cb4ebc93b08fee670dc9b28115eb14b8e351a4e1b00bc8cc5384b6967c6ad614","src/metal/surface.rs":"82836cadc751d94fb016bd590cdfec5649cbfae2f44d14599ed074dfb0a004dc","src/vulkan/adapter.rs":"f9b720e41b04b84d3f4beb8adae9e06a699d964722179638f45abfb936bf5b7e","src/vulkan/command.rs":"60d1867acd0e46c34dabecea708cd776a1f435721b6673a506b5bb8aee87ff80","src/vulkan/conv.rs":"0d7b706a854ff3fdcea0e66f0d8563d9c2aa1f82b2e84692c16f4bfcc3872ed5","src/vulkan/device.rs":"5e6e9dcc38161ded97f08b359861663042014286bf8dcccd33c4774a683548ba","src/vulkan/instance.rs":"49bb57b65c886c14f258e8a7fc183138765310ac7efd2fe3b26115e7942aa1fe","src/vulkan/mod.rs":"95347a82e2a276c1953e542c838f2ae07acc18d4187025fb22c264e61684860d"},"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 --- a/third_party/rust/wgpu-hal/Cargo.toml +++ b/third_party/rust/wgpu-hal/Cargo.toml @@ -89,18 +89,18 @@ web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] } js-sys = { version = "0.3" } [dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" # DEV dependencies [dev-dependencies.naga] git = "https://github.com/gfx-rs/naga" -rev = "85056524" +rev = "1aa91549" #version = "0.8" features = ["wgsl-in"] [dev-dependencies] env_logger = "0.9" diff --git a/third_party/rust/wgpu-hal/src/dx11/adapter.rs b/third_party/rust/wgpu-hal/src/dx11/adapter.rs --- a/third_party/rust/wgpu-hal/src/dx11/adapter.rs +++ b/third_party/rust/wgpu-hal/src/dx11/adapter.rs @@ -191,12 +191,12 @@ let max_compute_workgroups_per_dimension = d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; let limits = wgt::Limits { max_texture_dimension_1d: max_texture_dimension_2d, - max_texture_dimension_2d: max_texture_dimension_2d, - max_texture_dimension_3d: max_texture_dimension_3d, + max_texture_dimension_2d, + max_texture_dimension_3d, max_texture_array_layers: max_texture_dimension_3d, max_bind_groups: u32::MAX, max_dynamic_uniform_buffers_per_pipeline_layout: max_constant_buffers, max_dynamic_storage_buffers_per_pipeline_layout: 0, max_sampled_textures_per_shader_stage: max_sampled_textures, @@ -204,11 +204,11 @@ max_storage_buffers_per_shader_stage: max_uavs, max_storage_textures_per_shader_stage: max_uavs, max_uniform_buffers_per_shader_stage: max_constant_buffers, max_uniform_buffer_binding_size: 1 << 16, max_storage_buffer_binding_size: u32::MAX, - max_vertex_buffers: max_vertex_buffers, + max_vertex_buffers, max_vertex_attributes: max_vertex_buffers, max_vertex_buffer_array_stride: u32::MAX, max_push_constant_size: 1 << 16, min_uniform_buffer_offset_alignment: 256, min_storage_buffer_offset_alignment: 1, diff --git a/third_party/rust/wgpu-hal/src/dx12/adapter.rs b/third_party/rust/wgpu-hal/src/dx12/adapter.rs --- a/third_party/rust/wgpu-hal/src/dx12/adapter.rs +++ b/third_party/rust/wgpu-hal/src/dx12/adapter.rs @@ -89,10 +89,22 @@ &mut features_architecture as *mut _ as *mut _, mem::size_of::() as _, ) }); + let mut shader_model_support: d3d12::D3D12_FEATURE_DATA_SHADER_MODEL = + d3d12::D3D12_FEATURE_DATA_SHADER_MODEL { + HighestShaderModel: d3d12::D3D_SHADER_MODEL_6_0, + }; + assert_eq!(0, unsafe { + device.CheckFeatureSupport( + d3d12::D3D12_FEATURE_SHADER_MODEL, + &mut shader_model_support as *mut _ as *mut _, + mem::size_of::() as _, + ) + }); + let mut workarounds = super::Workarounds::default(); let info = wgt::AdapterInfo { backend: wgt::Backend::Dx12, name: device_name, @@ -173,15 +185,10 @@ let mut features = wgt::Features::empty() | wgt::Features::DEPTH_CLIP_CONTROL | wgt::Features::INDIRECT_FIRST_INSTANCE | wgt::Features::MAPPABLE_PRIMARY_BUFFERS - //TODO: Naga part - //| wgt::Features::TEXTURE_BINDING_ARRAY - //| wgt::Features::BUFFER_BINDING_ARRAY - //| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY - //| wgt::Features::UNSIZED_BINDING_ARRAY | wgt::Features::MULTI_DRAW_INDIRECT | wgt::Features::MULTI_DRAW_INDIRECT_COUNT | wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO | wgt::Features::POLYGON_MODE_LINE @@ -202,10 +209,17 @@ wgt::Features::CONSERVATIVE_RASTERIZATION, options.ConservativeRasterizationTier != d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED, ); + features.set( + wgt::Features::TEXTURE_BINDING_ARRAY + | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING + | wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + shader_model_support.HighestShaderModel >= d3d12::D3D_SHADER_MODEL_5_1, + ); + let base = wgt::Limits::default(); Some(crate::ExposedAdapter { adapter: super::Adapter { raw: adapter, @@ -280,11 +294,11 @@ } impl crate::Adapter for super::Adapter { unsafe fn open( &self, - features: wgt::Features, + _features: wgt::Features, _limits: &wgt::Limits, ) -> Result, crate::DeviceError> { let queue = { profiling::scope!("ID3D12Device::CreateCommandQueue"); self.device @@ -295,17 +309,11 @@ 0, ) .into_device_result("Queue creation")? }; - let device = super::Device::new( - self.device, - queue, - features, - self.private_caps, - &self.library, - )?; + let device = super::Device::new(self.device, queue, self.private_caps, &self.library)?; Ok(crate::OpenDevice { device, queue: super::Queue { raw: queue, temp_lists: Vec::new(), diff --git a/third_party/rust/wgpu-hal/src/dx12/device.rs b/third_party/rust/wgpu-hal/src/dx12/device.rs --- a/third_party/rust/wgpu-hal/src/dx12/device.rs +++ b/third_party/rust/wgpu-hal/src/dx12/device.rs @@ -19,11 +19,10 @@ impl super::Device { pub(super) fn new( raw: native::Device, present_queue: native::CommandQueue, - features: wgt::Features, private_caps: super::PrivateCapabilities, library: &Arc, ) -> Result { let mut idle_fence = native::Fence::null(); let hr = unsafe { @@ -85,11 +84,10 @@ // maximum number of CBV/SRV/UAV descriptors in heap for Tier 1 let capacity_views = 1_000_000; let capacity_samplers = 2_048; let shared = super::DeviceShared { - features, zero_buffer, cmd_signatures: super::CommandSignatures { draw: raw .create_command_signature( native::RootSignature::null(), @@ -220,17 +218,10 @@ .contains(crate::InstanceFlags::DEBUG) { compile_flags |= d3dcompiler::D3DCOMPILE_DEBUG | d3dcompiler::D3DCOMPILE_SKIP_OPTIMIZATION; } - if self - .shared - .features - .contains(wgt::Features::UNSIZED_BINDING_ARRAY) - { - compile_flags |= d3dcompiler::D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES; - } let source_name = match stage.module.raw_name { Some(ref cstr) => cstr.as_c_str().as_ptr(), None => ptr::null(), }; @@ -689,20 +680,21 @@ &self, desc: &crate::BindGroupLayoutDescriptor, ) -> Result { let (mut num_buffer_views, mut num_samplers, mut num_texture_views) = (0, 0, 0); for entry in desc.entries.iter() { + let count = entry.count.map_or(1, NonZeroU32::get); match entry.ty { wgt::BindingType::Buffer { has_dynamic_offset: true, .. } => {} - wgt::BindingType::Buffer { .. } => num_buffer_views += 1, + wgt::BindingType::Buffer { .. } => num_buffer_views += count, wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { - num_texture_views += 1 + num_texture_views += count } - wgt::BindingType::Sampler { .. } => num_samplers += 1, + wgt::BindingType::Sampler { .. } => num_samplers += count, } } let num_views = num_buffer_views + num_texture_views; Ok(super::BindGroupLayout { @@ -856,19 +848,22 @@ binding_map.insert( naga::ResourceBinding { group: index as u32, binding: entry.binding, }, - bt.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bt.clone() + }, ); ranges.push(native::DescriptorRange::new( range_ty, entry.count.map_or(1, |count| count.get()), native_binding(bt), d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, )); - bt.register += 1; + bt.register += entry.count.map(NonZeroU32::get).unwrap_or(1); } if ranges.len() > range_base { log::debug!( "\tParam[{}] = views (vis = {:?}, count = {})", parameters.len(), @@ -892,19 +887,22 @@ binding_map.insert( naga::ResourceBinding { group: index as u32, binding: entry.binding, }, - bind_sampler.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bind_sampler.clone() + }, ); ranges.push(native::DescriptorRange::new( range_ty, entry.count.map_or(1, |count| count.get()), native_binding(&bind_sampler), d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, )); - bind_sampler.register += 1; + bind_sampler.register += entry.count.map(NonZeroU32::get).unwrap_or(1); } if ranges.len() > range_base { log::debug!( "\tParam[{}] = samplers (vis = {:?}, count = {})", parameters.len(), @@ -951,11 +949,14 @@ binding_map.insert( naga::ResourceBinding { group: index as u32, binding: entry.binding, }, - bt.clone(), + hlsl::BindTarget { + binding_array_size: entry.count.map(NonZeroU32::get), + ..bt.clone() + }, ); info.dynamic_buffers.push(kind); log::debug!( "\tParam[{}] = dynamic {:?} (vis = {:?})", @@ -967,11 +968,11 @@ parameter_ty, dynamic_buffers_visibility, native_binding(bt), )); - bt.register += 1; + bt.register += entry.count.map_or(1, NonZeroU32::get); } bind_group_infos.push(info); } @@ -1080,86 +1081,101 @@ match layout.ty { wgt::BindingType::Buffer { has_dynamic_offset: true, .. } => { - let data = &desc.buffers[entry.resource_index as usize]; - dynamic_buffers.push(data.resolve_address()); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.buffers[start..end] { + dynamic_buffers.push(data.resolve_address()); + } } wgt::BindingType::Buffer { ty, .. } => { - let data = &desc.buffers[entry.resource_index as usize]; - let gpu_address = data.resolve_address(); - let size = data.resolve_size() as u32; - let inner = cpu_views.as_mut().unwrap(); - let cpu_index = inner.stage.len() as u32; - let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index); - match ty { - wgt::BufferBindingType::Uniform => { - let size_mask = - d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; - let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { - BufferLocation: gpu_address, - SizeInBytes: ((size - 1) | size_mask) + 1, - }; - self.raw.CreateConstantBufferView(&raw_desc, handle); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.buffers[start..end] { + let gpu_address = data.resolve_address(); + let size = data.resolve_size() as u32; + let inner = cpu_views.as_mut().unwrap(); + let cpu_index = inner.stage.len() as u32; + let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index); + match ty { + wgt::BufferBindingType::Uniform => { + let size_mask = + d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1; + let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC { + BufferLocation: gpu_address, + SizeInBytes: ((size - 1) | size_mask) + 1, + }; + self.raw.CreateConstantBufferView(&raw_desc, handle); + } + wgt::BufferBindingType::Storage { read_only: true } => { + let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + Shader4ComponentMapping: + view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { + FirstElement: data.offset / 4, + NumElements: size / 4, + StructureByteStride: 0, + Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, + }; + self.raw.CreateShaderResourceView( + data.buffer.resource.as_mut_ptr(), + &raw_desc, + handle, + ); + } + wgt::BufferBindingType::Storage { read_only: false } => { + let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { + Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, + u: mem::zeroed(), + }; + *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { + FirstElement: data.offset / 4, + NumElements: size / 4, + StructureByteStride: 0, + CounterOffsetInBytes: 0, + Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, + }; + self.raw.CreateUnorderedAccessView( + data.buffer.resource.as_mut_ptr(), + ptr::null_mut(), + &raw_desc, + handle, + ); + } } - wgt::BufferBindingType::Storage { read_only: true } => { - let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - Shader4ComponentMapping: - view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, - ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV { - FirstElement: data.offset / 4, - NumElements: size / 4, - StructureByteStride: 0, - Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW, - }; - self.raw.CreateShaderResourceView( - data.buffer.resource.as_mut_ptr(), - &raw_desc, - handle, - ); - } - wgt::BufferBindingType::Storage { read_only: false } => { - let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { - Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, - ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, - u: mem::zeroed(), - }; - *raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { - FirstElement: data.offset / 4, - NumElements: size / 4, - StructureByteStride: 0, - CounterOffsetInBytes: 0, - Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, - }; - self.raw.CreateUnorderedAccessView( - data.buffer.resource.as_mut_ptr(), - ptr::null_mut(), - &raw_desc, - handle, - ); - } + inner.stage.push(handle); } - inner.stage.push(handle); } wgt::BindingType::Texture { .. } => { - let data = &desc.textures[entry.resource_index as usize]; - let handle = data.view.handle_srv.unwrap(); - cpu_views.as_mut().unwrap().stage.push(handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.textures[start..end] { + let handle = data.view.handle_srv.unwrap(); + cpu_views.as_mut().unwrap().stage.push(handle.raw); + } } wgt::BindingType::StorageTexture { .. } => { - let data = &desc.textures[entry.resource_index as usize]; - let handle = data.view.handle_uav.unwrap(); - cpu_views.as_mut().unwrap().stage.push(handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.textures[start..end] { + let handle = data.view.handle_uav.unwrap(); + cpu_views.as_mut().unwrap().stage.push(handle.raw); + } } wgt::BindingType::Sampler { .. } => { - let data = &desc.samplers[entry.resource_index as usize]; - cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw); + let start = entry.resource_index as usize; + let end = start + entry.count as usize; + for data in &desc.samplers[start..end] { + cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw); + } } } } let handle_views = match cpu_views { diff --git a/third_party/rust/wgpu-hal/src/dx12/mod.rs b/third_party/rust/wgpu-hal/src/dx12/mod.rs --- a/third_party/rust/wgpu-hal/src/dx12/mod.rs +++ b/third_party/rust/wgpu-hal/src/dx12/mod.rs @@ -197,11 +197,10 @@ self.dispatch.destroy(); } } struct DeviceShared { - features: wgt::Features, zero_buffer: native::Resource, cmd_signatures: CommandSignatures, heap_views: descriptor::GeneralHeap, heap_samplers: descriptor::GeneralHeap, } diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs --- a/third_party/rust/wgpu-hal/src/gles/adapter.rs +++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs @@ -282,10 +282,14 @@ max_storage_block_size != 0 && max_storage_buffers_per_shader_stage != 0 && (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero), ); downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage); + downlevel_flags.set( + wgt::DownlevelFlags::ANISOTROPIC_FILTERING, + extensions.contains("EXT_texture_filter_anisotropic"), + ); let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgt::Features::CLEAR_TEXTURE | wgt::Features::PUSH_CONSTANTS; diff --git a/third_party/rust/wgpu-hal/src/gles/device.rs b/third_party/rust/wgpu-hal/src/gles/device.rs --- a/third_party/rust/wgpu-hal/src/gles/device.rs +++ b/third_party/rust/wgpu-hal/src/gles/device.rs @@ -738,12 +738,13 @@ if let Some(ref range) = desc.lod_clamp { gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, range.start); gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, range.end); } - //TODO: `desc.anisotropy_clamp` depends on the downlevel flag - // gl.sampler_parameter_f32(rawow::TEXTURE_MAX_ANISOTROPY, aniso as f32); + if let Some(anisotropy) = desc.anisotropy_clamp { + gl.sampler_parameter_i32(raw, glow::TEXTURE_MAX_ANISOTROPY, anisotropy.get() as i32); + } //set_param_float(glow::TEXTURE_LOD_BIAS, info.lod_bias.0); if let Some(compare) = desc.compare { gl.sampler_parameter_i32( diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs --- a/third_party/rust/wgpu-hal/src/gles/queue.rs +++ b/third_party/rust/wgpu-hal/src/gles/queue.rs @@ -491,25 +491,11 @@ let src_data = &buffer_data.as_slice() [(offset as usize)..(offset + bytes_in_upload) as usize]; glow::CompressedPixelUnpackData::Slice(src_data) } }; - log::error!( - "bytes_per_row: {}, \ - minimum_rows_per_image: {}, \ - rows_per_image: {}, \ - bytes_per_image: {}, \ - minimum_bytes_per_image: {}, \ - bytes_in_upload: {}\ - ", - bytes_per_row, - minimum_rows_per_image, - rows_per_image, - bytes_per_image, - minimum_bytes_per_image, - bytes_in_upload - ); + match dst_target { glow::TEXTURE_3D | glow::TEXTURE_CUBE_MAP_ARRAY | glow::TEXTURE_2D_ARRAY => { gl.compressed_tex_sub_image_3d( diff --git a/third_party/rust/wgpu-hal/src/metal/command.rs b/third_party/rust/wgpu-hal/src/metal/command.rs --- a/third_party/rust/wgpu-hal/src/metal/command.rs +++ b/third_party/rust/wgpu-hal/src/metal/command.rs @@ -72,17 +72,25 @@ stage: naga::ShaderStage, result_sizes: &'a mut Vec, ) -> Option<(u32, &'a [u32])> { let stage_info = &self.stage_infos[stage]; let slot = stage_info.sizes_slot?; + result_sizes.clear(); - for br in stage_info.sized_bindings.iter() { - // If it's None, this isn't the right time to update the sizes - let size = self.storage_buffer_length_map.get(br)?; - result_sizes.push(size.get().min(!0u32 as u64) as u32); + result_sizes.extend( + stage_info + .sized_bindings + .iter() + .filter_map(|br| self.storage_buffer_length_map.get(br)) + .map(|size| size.get().min(!0u32 as u64) as u32), + ); + + if !result_sizes.is_empty() { + Some((slot as _, result_sizes)) + } else { + None } - Some((slot as _, result_sizes)) } } impl crate::CommandEncoder for super::CommandEncoder { unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { diff --git a/third_party/rust/wgpu-hal/src/metal/device.rs b/third_party/rust/wgpu-hal/src/metal/device.rs --- a/third_party/rust/wgpu-hal/src/metal/device.rs +++ b/third_party/rust/wgpu-hal/src/metal/device.rs @@ -1,7 +1,8 @@ use parking_lot::Mutex; use std::{ + num::NonZeroU32, ptr, sync::{atomic, Arc}, thread, time, }; @@ -71,10 +72,17 @@ &layout.naga_options, &pipeline_options, ) .map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("MSL: {:?}", e)))?; + log::debug!( + "Naga generated shader for entry point '{}' and stage {:?}\n{}", + stage.entry_point, + naga_stage, + &source + ); + let options = mtl::CompileOptions::new(); options.set_language_version(self.shared.private_caps.msl_version); if self.shared.private_caps.supports_preserve_invariance { options.set_preserve_invariance(true); @@ -556,31 +564,33 @@ if !entry.visibility.contains(map_naga_stage(info.stage)) { continue; } let mut target = naga::back::msl::BindTarget::default(); + let count = entry.count.map_or(1, NonZeroU32::get); + target.binding_array_size = entry.count.map(NonZeroU32::get); match entry.ty { wgt::BindingType::Buffer { ty, .. } => { target.buffer = Some(info.counters.buffers as _); - info.counters.buffers += 1; + info.counters.buffers += count; if let wgt::BufferBindingType::Storage { read_only } = ty { target.mutable = !read_only; } } wgt::BindingType::Sampler { .. } => { target.sampler = Some(naga::back::msl::BindSamplerTarget::Resource( info.counters.samplers as _, )); - info.counters.samplers += 1; + info.counters.samplers += count; } wgt::BindingType::Texture { .. } => { target.texture = Some(info.counters.textures as _); - info.counters.textures += 1; + info.counters.textures += count; } wgt::BindingType::StorageTexture { access, .. } => { target.texture = Some(info.counters.textures as _); - info.counters.textures += 1; + info.counters.textures += count; target.mutable = match access { wgt::StorageTextureAccess::ReadOnly => false, wgt::StorageTextureAccess::WriteOnly => true, wgt::StorageTextureAccess::ReadWrite => true, }; @@ -665,10 +675,12 @@ }, bounds_check_policies: naga::proc::BoundsCheckPolicies { index: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, buffer: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, image: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite, + // TODO: support bounds checks on binding arrays + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }, }, total_push_constants, }) } @@ -687,54 +699,59 @@ if let wgt::BindingType::Buffer { has_dynamic_offset: true, .. } = layout.ty { - dynamic_offsets_count += 1; + dynamic_offsets_count += size; } if !layout.visibility.contains(stage_bit) { continue; } match layout.ty { wgt::BindingType::Buffer { ty, has_dynamic_offset, .. } => { - debug_assert_eq!(size, 1); - let source = &desc.buffers[entry.resource_index as usize]; - let remaining_size = - wgt::BufferSize::new(source.buffer.size - source.offset); - let binding_size = match ty { - wgt::BufferBindingType::Storage { .. } => { - source.size.or(remaining_size) - } - _ => None, - }; - bg.buffers.push(super::BufferResource { - ptr: source.buffer.as_raw(), - offset: source.offset, - dynamic_index: if has_dynamic_offset { - Some(dynamic_offsets_count - 1) - } else { - None - }, - binding_size, - binding_location: layout.binding, - }); + let start = entry.resource_index as usize; + let end = start + size as usize; + bg.buffers + .extend(desc.buffers[start..end].iter().map(|source| { + let remaining_size = + wgt::BufferSize::new(source.buffer.size - source.offset); + let binding_size = match ty { + wgt::BufferBindingType::Storage { .. } => { + source.size.or(remaining_size) + } + _ => None, + }; + super::BufferResource { + ptr: source.buffer.as_raw(), + offset: source.offset, + dynamic_index: if has_dynamic_offset { + Some(dynamic_offsets_count - 1) + } else { + None + }, + binding_size, + binding_location: layout.binding, + } + })); counter.buffers += 1; } wgt::BindingType::Sampler { .. } => { - let res = desc.samplers[entry.resource_index as usize].as_raw(); - bg.samplers.push(res); - counter.samplers += 1; + let start = entry.resource_index as usize; + let end = start + size as usize; + bg.samplers + .extend(desc.samplers[start..end].iter().map(|samp| samp.as_raw())); + counter.samplers += size; } wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { - let start = entry.resource_index; - let end = start + size; + let start = entry.resource_index as usize; + let end = start + size as usize; bg.textures.extend( - desc.textures[start as usize..end as usize] + desc.textures[start..end] .iter() .map(|tex| tex.view.as_raw()), ); counter.textures += size; } diff --git a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs --- a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs @@ -1,17 +1,16 @@ use super::conv; use ash::{extensions::khr, vk}; use parking_lot::Mutex; -use std::{ffi::CStr, sync::Arc}; +use std::{collections::BTreeMap, ffi::CStr, sync::Arc}; //TODO: const fn? fn indexing_features() -> wgt::Features { wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING - | wgt::Features::UNSIZED_BINDING_ARRAY } /// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`. #[derive(Debug, Default)] pub struct PhysicalDeviceFeatures { @@ -210,13 +209,10 @@ ) .descriptor_binding_storage_buffer_update_after_bind( uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER), ) .descriptor_binding_partially_bound(needs_partially_bound) - .runtime_descriptor_array( - requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), - ) //.sampler_filter_minmax(requested_features.contains(wgt::Features::SAMPLER_REDUCTION)) .imageless_framebuffer(private_caps.imageless_framebuffers) .timeline_semaphore(private_caps.timeline_semaphores) .build(), ) @@ -251,13 +247,10 @@ ) .descriptor_binding_storage_buffer_update_after_bind( uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER), ) .descriptor_binding_partially_bound(needs_partially_bound) - .runtime_descriptor_array( - requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), - ) .build(), ) } else { None }, @@ -469,13 +462,10 @@ ), ], ) { features.insert(F::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING); } - if vulkan_1_2.runtime_descriptor_array != 0 { - features |= F::UNSIZED_BINDING_ARRAY; - } if vulkan_1_2.descriptor_binding_partially_bound != 0 && !intel_windows { features |= F::PARTIALLY_BOUND_BINDING_ARRAY; } //if vulkan_1_2.sampler_mirror_clamp_to_edge != 0 { //if vulkan_1_2.sampler_filter_minmax != 0 { @@ -517,13 +507,10 @@ features.insert(F::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING); } if descriptor_indexing.descriptor_binding_partially_bound != 0 && !intel_windows { features |= F::PARTIALLY_BOUND_BINDING_ARRAY; } - if descriptor_indexing.runtime_descriptor_array != 0 { - features |= F::UNSIZED_BINDING_ARRAY; - } } if let Some(ref feature) = self.depth_clip_enable { features.set(F::DEPTH_CLIP_CONTROL, feature.depth_clip_enable != 0); } @@ -1181,10 +1168,17 @@ if features.contains(wgt::Features::MULTIVIEW) { capabilities.push(spv::Capability::MultiView); } + if features.intersects( + wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING + | wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ) { + capabilities.push(spv::Capability::ShaderNonUniform); + } + let mut flags = spv::WriterFlags::empty(); flags.set( spv::WriterFlags::DEBUG, self.instance.flags.contains(crate::InstanceFlags::DEBUG), ); @@ -1213,11 +1207,15 @@ image: if self.private_caps.robust_image_access { naga::proc::BoundsCheckPolicy::Unchecked } else { naga::proc::BoundsCheckPolicy::Restrict }, + // TODO: support bounds checks on binding arrays + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }, + // We need to build this separately for each invocation, so just default it out here + binding_map: BTreeMap::default(), } }; log::info!("Private capabilities: {:?}", self.private_caps); let raw_queue = { diff --git a/third_party/rust/wgpu-hal/src/vulkan/device.rs b/third_party/rust/wgpu-hal/src/vulkan/device.rs --- a/third_party/rust/wgpu-hal/src/vulkan/device.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/device.rs @@ -4,11 +4,16 @@ use ash::{extensions::khr, vk}; use inplace_it::inplace_or_alloc_from_iter; use parking_lot::Mutex; use std::{ - borrow::Cow, collections::hash_map::Entry, ffi::CString, num::NonZeroU32, ptr, sync::Arc, + borrow::Cow, + collections::{hash_map::Entry, BTreeMap}, + ffi::CString, + num::NonZeroU32, + ptr, + sync::Arc, }; impl super::DeviceShared { pub(super) unsafe fn set_object_name( &self, @@ -626,10 +631,11 @@ fn compile_stage( &self, stage: &crate::ProgrammableStage, naga_stage: naga::ShaderStage, + binding_map: &naga::back::spv::BindingMap, ) -> Result { let stage_flags = crate::auxil::map_naga_stage(naga_stage); let vk_module = match *stage.module { super::ShaderModule::Raw(raw) => raw, super::ShaderModule::Intermediate { @@ -638,20 +644,25 @@ } => { let pipeline_options = naga::back::spv::PipelineOptions { entry_point: stage.entry_point.to_string(), shader_stage: naga_stage, }; - let temp_options; - let options = if !runtime_checks { - temp_options = naga::back::spv::Options { - bounds_check_policies: naga::proc::BoundsCheckPolicies { + let needs_temp_options = !runtime_checks || !binding_map.is_empty(); + let mut temp_options; + let options = if needs_temp_options { + temp_options = self.naga_options.clone(); + if !runtime_checks { + temp_options.bounds_check_policies = naga::proc::BoundsCheckPolicies { index: naga::proc::BoundsCheckPolicy::Unchecked, buffer: naga::proc::BoundsCheckPolicy::Unchecked, image: naga::proc::BoundsCheckPolicy::Unchecked, - }, - ..self.naga_options.clone() - }; + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, + }; + } + if !binding_map.is_empty() { + temp_options.binding_map = binding_map.clone(); + } &temp_options } else { &self.naga_options }; let spv = { @@ -1098,10 +1109,17 @@ }) .collect::>(); let vk_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&vk_bindings); + let binding_arrays = desc + .entries + .iter() + .enumerate() + .filter_map(|(idx, entry)| entry.count.map(|count| (idx as u32, count))) + .collect(); + let mut binding_flag_info; let binding_flag_vec; let mut requires_update_after_bind = false; let partially_bound = desc @@ -1174,10 +1192,11 @@ Ok(super::BindGroupLayout { raw, desc_count, types: types.into_boxed_slice(), + binding_arrays, requires_update_after_bind, }) } unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) { self.shared @@ -1218,11 +1237,29 @@ if let Some(label) = desc.label { self.shared .set_object_name(vk::ObjectType::PIPELINE_LAYOUT, raw, label); } - Ok(super::PipelineLayout { raw }) + let mut binding_arrays = BTreeMap::new(); + for (group, &layout) in desc.bind_group_layouts.iter().enumerate() { + for &(binding, binding_array_size) in &layout.binding_arrays { + binding_arrays.insert( + naga::ResourceBinding { + group: group as u32, + binding, + }, + naga::back::spv::BindingInfo { + binding_array_size: Some(binding_array_size.get()), + }, + ); + } + } + + Ok(super::PipelineLayout { + raw, + binding_arrays, + }) } unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { self.shared .raw .destroy_pipeline_layout(pipeline_layout.raw, None); @@ -1264,15 +1301,19 @@ .dst_binding(entry.binding) .descriptor_type(ty); write = match ty { vk::DescriptorType::SAMPLER => { let index = sampler_infos.len(); - let binding = desc.samplers[entry.resource_index as usize]; - let vk_info = vk::DescriptorImageInfo::builder() - .sampler(binding.raw) - .build(); - sampler_infos.push(vk_info); + let start = entry.resource_index; + let end = start + entry.count; + sampler_infos.extend(desc.samplers[start as usize..end as usize].iter().map( + |binding| { + vk::DescriptorImageInfo::builder() + .sampler(binding.raw) + .build() + }, + )); write.image_info(&sampler_infos[index..]) } vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => { let index = image_infos.len(); let start = entry.resource_index; @@ -1342,10 +1383,11 @@ if !desc.runtime_checks { naga_options.bounds_check_policies = naga::proc::BoundsCheckPolicies { index: naga::proc::BoundsCheckPolicy::Unchecked, buffer: naga::proc::BoundsCheckPolicy::Unchecked, image: naga::proc::BoundsCheckPolicy::Unchecked, + binding_array: naga::proc::BoundsCheckPolicy::Unchecked, }; } Cow::Owned( naga::back::spv::write_vec( &naga_shader.module, @@ -1423,15 +1465,23 @@ let vk_input_assembly = vk::PipelineInputAssemblyStateCreateInfo::builder() .topology(conv::map_topology(desc.primitive.topology)) .primitive_restart_enable(desc.primitive.strip_index_format.is_some()) .build(); - let compiled_vs = self.compile_stage(&desc.vertex_stage, naga::ShaderStage::Vertex)?; + let compiled_vs = self.compile_stage( + &desc.vertex_stage, + naga::ShaderStage::Vertex, + &desc.layout.binding_arrays, + )?; stages.push(compiled_vs.create_info); let compiled_fs = match desc.fragment_stage { Some(ref stage) => { - let compiled = self.compile_stage(stage, naga::ShaderStage::Fragment)?; + let compiled = self.compile_stage( + stage, + naga::ShaderStage::Fragment, + &desc.layout.binding_arrays, + )?; stages.push(compiled.create_info); Some(compiled) } None => None, }; @@ -1602,11 +1652,15 @@ unsafe fn create_compute_pipeline( &self, desc: &crate::ComputePipelineDescriptor, ) -> Result { - let compiled = self.compile_stage(&desc.stage, naga::ShaderStage::Compute)?; + let compiled = self.compile_stage( + &desc.stage, + naga::ShaderStage::Compute, + &desc.layout.binding_arrays, + )?; let vk_infos = [{ vk::ComputePipelineCreateInfo::builder() .layout(desc.layout.raw) .stage(compiled.create_info) diff --git a/third_party/rust/wgpu-hal/src/vulkan/mod.rs b/third_party/rust/wgpu-hal/src/vulkan/mod.rs --- a/third_party/rust/wgpu-hal/src/vulkan/mod.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/mod.rs @@ -396,16 +396,19 @@ #[derive(Debug)] pub struct BindGroupLayout { raw: vk::DescriptorSetLayout, desc_count: gpu_descriptor::DescriptorTotalCount, types: Box<[(vk::DescriptorType, u32)]>, + /// Map of binding index to size, + binding_arrays: Vec<(u32, NonZeroU32)>, requires_update_after_bind: bool, } #[derive(Debug)] pub struct PipelineLayout { raw: vk::PipelineLayout, + binding_arrays: naga::back::spv::BindingMap, } #[derive(Debug)] pub struct BindGroup { set: gpu_descriptor::DescriptorSet, diff --git a/third_party/rust/wgpu-types/.cargo-checksum.json b/third_party/rust/wgpu-types/.cargo-checksum.json --- 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":"291e4f8cf6b42c2d00484fa4b71454e543695eae03a999e28a96c5e4a49ff828"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"742abe387ad0a6b054a3e525040ff1e64f16072ae7cd305db0616beaa844e389","src/lib.rs":"638307eb4f80c9c181abf496dbf11f368d026675a691fac40c5ae0ada16462de"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-types/src/lib.rs b/third_party/rust/wgpu-types/src/lib.rs --- a/third_party/rust/wgpu-types/src/lib.rs +++ b/third_party/rust/wgpu-types/src/lib.rs @@ -346,40 +346,30 @@ const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 1 << 21; /// Allows the user to create bind groups continaing arrays with less bindings than the BindGroupLayout. /// /// This is a native only feature. const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 22; - /// Allows the user to create unsized uniform arrays of bindings: - /// - /// eg. `uniform texture2D textures[]`. - /// - /// Supported platforms: - /// - DX12 - /// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s runtimeDescriptorArray feature - /// - /// This is a native only feature. - const UNSIZED_BINDING_ARRAY = 1 << 23; /// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`]. /// /// Allows multiple indirect calls to be dispatched from a single buffer. /// /// Supported platforms: /// - DX12 /// - Vulkan /// /// This is a native only feature. - const MULTI_DRAW_INDIRECT = 1 << 24; + const MULTI_DRAW_INDIRECT = 1 << 23; /// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`]. /// /// This allows the use of a buffer containing the actual number of draw calls. /// /// Supported platforms: /// - DX12 /// - Vulkan 1.2+ (or VK_KHR_draw_indirect_count) /// /// This is a native only feature. - const MULTI_DRAW_INDIRECT_COUNT = 1 << 25; + const MULTI_DRAW_INDIRECT_COUNT = 1 << 24; /// Allows the use of push constants: small, fast bits of memory that can be updated /// inside a [`RenderPass`]. /// /// Allows the user to call [`RenderPass::set_push_constants`], provide a non-empty array /// to [`PipelineLayoutDescriptor`], and provide a non-zero limit to [`Limits::max_push_constant_size`]. @@ -392,11 +382,11 @@ /// - Metal /// - DX11 (emulated with uniforms) /// - OpenGL (emulated with uniforms) /// /// This is a native only feature. - const PUSH_CONSTANTS = 1 << 26; + const PUSH_CONSTANTS = 1 << 25; /// Allows the use of [`AddressMode::ClampToBorder`] with a border color /// other than [`SamplerBorderColor::Zero`]. /// /// Supported platforms: /// - DX12 @@ -404,32 +394,32 @@ /// - Metal (macOS 10.12+ only) /// - DX11 /// - OpenGL /// /// This is a web and native feature. - const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 27; + const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 26; /// Allows the user to set [`PolygonMode::Line`] in [`PrimitiveState::polygon_mode`] /// /// This allows drawing polygons/triangles as lines (wireframe) instead of filled /// /// Supported platforms: /// - DX12 /// - Vulkan /// - Metal /// /// This is a native only feature. - const POLYGON_MODE_LINE = 1 << 28; + const POLYGON_MODE_LINE = 1 << 27; /// Allows the user to set [`PolygonMode::Point`] in [`PrimitiveState::polygon_mode`] /// /// This allows only drawing the vertices of polygons/triangles instead of filled /// /// Supported platforms: /// - DX12 /// - Vulkan /// /// This is a native only feature. - const POLYGON_MODE_POINT = 1 << 29; + 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. @@ -440,11 +430,11 @@ /// Supported Platforms: /// - Intel/Vulkan /// - Mobile (some) /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ETC2 = 1 << 30; + 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. @@ -455,11 +445,11 @@ /// Supported Platforms: /// - Intel/Vulkan /// - Mobile (some) /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 31; + const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 30; /// Enables device specific texture format features. /// /// See `TextureFormatFeatures` for a listing of the features in question. /// /// By default only texture format properties as defined by the WebGPU specification are allowed. @@ -467,55 +457,55 @@ /// Note that without this flag, read/write storage access is not allowed at all. /// /// This extension does not enable additional formats. /// /// This is a native-only feature. - const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 32; + const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31; /// Enables 64-bit floating point types in SPIR-V shaders. /// /// Note: even when supported by GPU hardware, 64-bit floating point operations are /// frequently between 16 and 64 _times_ slower than equivalent operations on 32-bit floats. /// /// Supported Platforms: /// - Vulkan /// /// This is a native-only feature. - const SHADER_FLOAT64 = 1 << 33; + const SHADER_FLOAT64 = 1 << 32; /// Enables using 64-bit types for vertex attributes. /// /// Requires SHADER_FLOAT64. /// /// Supported Platforms: N/A /// /// This is a native-only feature. - const VERTEX_ATTRIBUTE_64BIT = 1 << 34; + const VERTEX_ATTRIBUTE_64BIT = 1 << 33; /// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`] /// /// Processing of degenerate triangles/lines is hardware specific. /// Only triangles are supported. /// /// Supported platforms: /// - Vulkan /// /// This is a native only feature. - const CONSERVATIVE_RASTERIZATION = 1 << 35; + const CONSERVATIVE_RASTERIZATION = 1 << 34; /// 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. /// /// Supported Platforms: /// - All /// /// This is a native-only feature. - const VERTEX_WRITABLE_STORAGE = 1 << 36; + const VERTEX_WRITABLE_STORAGE = 1 << 35; /// Enables clear to zero for textures. /// /// Supported platforms: /// - All /// /// This is a native only feature. - const CLEAR_TEXTURE = 1 << 37; + const CLEAR_TEXTURE = 1 << 36; /// Enables creating shader modules from SPIR-V binary data (unsafe). /// /// SPIR-V data is not parsed or interpreted in any way; you can use /// [`wgpu::make_spirv_raw!`] to check for alignment and magic number when converting from /// raw bytes. @@ -523,38 +513,38 @@ /// Supported platforms: /// - Vulkan, in case shader's requested capabilities and extensions agree with /// Vulkan implementation. /// /// This is a native only feature. - const SPIRV_SHADER_PASSTHROUGH = 1 << 38; + const SPIRV_SHADER_PASSTHROUGH = 1 << 37; /// Enables `builtin(primitive_index)` in fragment shaders. /// /// Note: enables geometry processing for pipelines using the builtin. /// This may come with a significant performance impact on some hardware. /// Other pipelines are not affected. /// /// Supported platforms: /// - Vulkan /// /// This is a native only feature. - const SHADER_PRIMITIVE_INDEX = 1 << 39; + const SHADER_PRIMITIVE_INDEX = 1 << 38; /// Enables multiview render passes and `builtin(view_index)` in vertex shaders. /// /// Supported platforms: /// - Vulkan /// /// This is a native only feature. - const MULTIVIEW = 1 << 40; + const MULTIVIEW = 1 << 39; /// Enables normalized `16-bit` texture formats. /// /// Supported platforms: /// - Vulkan /// - DX12 /// - Metal /// /// This is a native only feature. - const TEXTURE_FORMAT_16BIT_NORM = 1 << 41; + const TEXTURE_FORMAT_16BIT_NORM = 1 << 40; /// Allows the use of [`AddressMode::ClampToBorder`] with a border color /// of [`SamplerBorderColor::Zero`]. /// /// Supported platforms: /// - DX12 @@ -562,16 +552,16 @@ /// - Metal /// - DX11 /// - OpenGL /// /// This is a native only feature. - const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 42; + const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 41; /// Supported Platforms: /// - Metal /// /// This is a native-only feature. - const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 43; + const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 42; } } #[cfg(feature = "bitflags_serde_shim")] bitflags_serde_shim::impl_serde_for_bitflags!(Features); @@ -3116,12 +3106,12 @@ } /// Returns the number of array layers. pub fn array_layer_count(&self) -> u32 { match self.dimension { - TextureDimension::D1 | TextureDimension::D2 => self.size.depth_or_array_layers, - TextureDimension::D3 => 1, + TextureDimension::D1 | TextureDimension::D3 => 1, + TextureDimension::D2 => self.size.depth_or_array_layers, } } } /// Kind of data the texture holds.