diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/metal | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/metal')
84 files changed, 17606 insertions, 0 deletions
diff --git a/third_party/rust/metal/.cargo-checksum.json b/third_party/rust/metal/.cargo-checksum.json new file mode 100644 index 0000000000..a14acdb20b --- /dev/null +++ b/third_party/rust/metal/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"ff54827be57e86ac1a50fb371f128cfc0fa87b26de767444e4f59905c98cacfd","Cargo.toml":"eb5025605544e8c4506fea4fa153171c5adaf9bc4a8a2741aaf79c153a1bb7df","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","Makefile":"99c4c592467981921d1257a4e9e841dce53624448738d0ed52a75431a406f7bc","README.md":"b61a350d23e7c88c34b1cd703083978e17c28a286e4231f1dc0a2383a1f85902","assets/metal.svg":"3a295bd130785bc2fea2f3a91a4f6c7b4b3c40682c3fcb7a75f9887b66b06264","bors.toml":"c2733ec512a08bf76b6a1ed77270810a0edeb888ba24c2a75679e1eb1bd563a5","examples/argument-buffer/main.rs":"a087db6648a4b092520de29616521c704d892e0d3ace935d16f3f339415c4361","examples/bind/main.rs":"a0c85aad05f08666f9b380a7146a8473a6a6fe0db5d523760373093a0af20e5f","examples/bindless/main.rs":"0aaad9e42634c1ea204342a76c1bfe80c8fcf9d3ebe8247846cf63ecb08fb0d5","examples/caps/main.rs":"b7be00c1cc9042140d34ea05051152a7035f316f0bdcd31ac5a660a97e0c4f70","examples/circle/README.md":"e1c97cf5252f0d1f2934ace78b5d839c5f45911f3007dbd2925eeceefb8f0af6","examples/circle/main.rs":"22bd52ccba85debc1ccb3f277750e4759283d3c9b135b9a0da496c747e4cf637","examples/circle/screenshot.png":"97bf07c85bf02367447b9c8a81707c124e4a3b420fa386b95ba08b21938f4f2a","examples/circle/shaders.metal":"5e4f40efca5bb386204a09e1b983cc6c634fdf1ca9dd4974227313adbf50e8b5","examples/circle/shaders.metallib":"666a9491d795ef9c0b9c122c7ada571cc2c0e8774d2d89e5b4b996f3dc47962b","examples/compute/compute-argument-buffer.metal":"6530bbd6a0101d9db2893805436f5dc877959e81ea97a27014c0fc52fc9fa77b","examples/compute/compute-argument-buffer.rs":"e3de61fd7cc2f14d9d52300e4878601dbc072bc26d9dafc66115df58f94e0470","examples/compute/embedded-lib.rs":"55f701810fa5270c27ca771e713f9f8cf09e124a997b0b03790b38435593a7ea","examples/compute/main.rs":"bf160f3aba9f7492e3f4b6cf3198f8e454a49d6a29225312a84fdb9d784ffec4","examples/compute/shaders.metal":"f2b15551bb5247b88a3029c3d8ef37c6fa04a4a6cca9f90f069894ed6822b4bf","examples/compute/shaders.metallib":"fef91643e60c0ec99ad2bd2f3916299bcc3e6a80038ea27bed59681badfea7d1","examples/events/main.rs":"9cb35381b0a3918bd7d530171de8f7cceafe3d4851c0f430b4aff1f5c2aae749","examples/fence/main.rs":"47741327e62db1d8bd344b6a9ec26ef13ffb0b56b0dd7077c5d926d43faaeff7","examples/headless-render/README.md":"b1c97b52701cfb41fc0b9e269ba7a7a454d9161746198e2f5789f2636f60842d","examples/headless-render/main.rs":"2e6eb5db66f28833d1b53df3f134b4b53907a00036eeb6cdd9e428478e164aae","examples/headless-render/screenshot.png":"01d6ea5791b63b0f01190198756446cf313fc25dc64d0138c1b4f62c9f862dd1","examples/library/main.rs":"a1420ec28a471f28a789b75b3ecf5abb699ed352b337747169914812fb98045a","examples/mesh-shader/main.rs":"49899300f80f2d1e64366176313f69b597b69f3728a52a6a6f56ff7cd54c3c23","examples/mesh-shader/shaders.metal":"6ba934c3edd3ba0b8f6c4ac37be0fd0fe35eeef004e371521b7bf5a2fae9a223","examples/mesh-shader/shaders.metallib":"0af3b7ab0cd6186a90163550b76fab5bd2ef6ba97e791354d4281ca92d4887ff","examples/mps/main.rs":"2cbb532635270bc55cdfa3ee231cc2ee20dd8b3a5e6bf76062cca89ef1e3498f","examples/mps/shaders.metal":"155922d6a4184078ae7ee29504a268e1218f07d908f921eef60e5bfa8a793bda","examples/mps/shaders.metallib":"b62451223549b1e7eb90ec3d3534c0ed4cdfdc581c7df3ffcdc4786a5fcacde4","examples/raytracing/README.md":"6f0d683efac74572099c317ce9f65c3e6ff3c5252c6870c0c38c67f08b37bb01","examples/raytracing/camera.rs":"bed7a1787e1a52060dd0d64ff630a6fb769f15098d0a9d3ea68d4b57aee78b53","examples/raytracing/geometry.rs":"d3db29b4ab2d3d39de74718e0a7133a4e576dc26dcc6b6728c047865fb78952a","examples/raytracing/main.rs":"c3571854cbaaaeea386d7eb8af1fe9ea0492eae9af62b60203f4b6937dc4999a","examples/raytracing/renderer.rs":"d4e704b8f8e61919882aafc009b3a20928902d5b7edb9122d05f3e468d32f613","examples/raytracing/scene.rs":"fc2f214e0ad90e916fdbe2a339567a5dd323ef45b916fa8432c1156b4e94b998","examples/raytracing/screenshot.png":"400bb138f5adb69e4db8626681fb17667e5e112c94864879d9282d5348d970db","examples/raytracing/shaders.metal":"696f6a0ba79d82e2fa0e03eadbff2f6cdeac87acc805c2b7df657b85c1173174","examples/raytracing/shaders.metallib":"249b71998f58ddf8b3de37d79e9cc1f4a3494fba4bd7ba3f5411fb603de9dd5a","examples/reflection/main.rs":"563b49f5c057f1f8d17f3480cbc466e073ea575bfafaf84018a331a984d90a62","examples/shader-dylib/main.rs":"f18f4eb01420e12c196848c42657d41390cf10a3e47e8870025c20a1b29ddc71","examples/shader-dylib/test_dylib.metal":"3469de785c2c0da784e84758fc0da5a81e474ca15588485d9e04398690641cc8","examples/shader-dylib/test_shader.metal":"1a04ff8ab3288b09d14cd35440b2557e92ddedbff9d07c4144a22e9062e6e1e4","examples/window/README.md":"69655cff298e07887fe70e8a13e27d8a87efcd0cc0da4e15485134e064e1aceb","examples/window/main.rs":"09c508013223de859f33fb69410bde30e8d7f04952850504d8b1f8faf7049b1b","examples/window/screenshot.png":"da7369d7cc297c7f7f6bd773c93e7d708d72442c9c5ff5a20c2f2ee6852552dc","examples/window/shaders.metal":"90dee6c752add5c617dfdca93064b2824b44ff8c01ef63986826f6a1531e95d6","examples/window/shaders.metallib":"16fa82beb70bf16c3501970cae0d5028a747a08164337161dc9c2e8965d4c366","src/accelerator_structure.rs":"3b014b17a30e96d32d6fc69b6d067c08a9d4356cb10dc9121824117711211449","src/argument.rs":"347a5a01d4804044641e09d120d37bba140fc6a364782a96470b753e1ee74b30","src/blitpass.rs":"67b561b319e0650aa8438ce056998d4abd86b765a345f382013ab3cae7a2e71f","src/buffer.rs":"78d9021ab75ef0dad09ff92d126f1ceea241cca606cd7b05553c9351458babed","src/capturedescriptor.rs":"c687c4db298fb83ef640eb34929758c2d7955174a68725e986838e367291e302","src/capturemanager.rs":"c8a42854eebcfb6a7b777d931e368e04a5e35dff2e46c38c481d266dba9b792d","src/commandbuffer.rs":"464d504969ba1c213f6f8812b414c1581e39490cd048888404e04fbb5a1bd4ce","src/commandqueue.rs":"a7d6dee5d064093521465e1da30bded851aa9d59f078942d88926d0f38de82fd","src/computepass.rs":"7c209dc60abc3cb45ed4b1648fd01faaeb561ef980d2b37c3d69a7e5fed9129d","src/constants.rs":"bbfeecf910d4f9ed149a7988d8a79df6e9ad479de81f7fc1294d3434a721b7fd","src/counters.rs":"d36d88f95015eef3655fc114eba1ef680d6a5bf74849389da2d09178244a01c4","src/depthstencil.rs":"71f221640a2031ef40449697297f09cd42d23833854c51759b3006ba55c84de9","src/device.rs":"20a30e95a7e32ee22df068bbdc0ee84790c76fa0fb3ab57f1641b8c5aedf98c6","src/drawable.rs":"2006151031fced10a8b347d9cf465ef15f62551f134d3ff1715e3f3831155f23","src/encoder.rs":"55030f58a4ababcd4c0b4c5b99884ac3bbeae6f16407fc258dce85656c71f276","src/heap.rs":"843536c73cb19643bf7d286433daebf703c28238775d5e006865247c50dabfbf","src/indirect_encoder.rs":"eee68af3cef45555864472622152fcb7a529341b27a98adec882324975fda3f1","src/lib.rs":"e5491ebe9c38e10a28ca5f3f5626bebd4c3901eb55400849c3a0921772f23afe","src/library.rs":"380ff5704b4068a1ff6415a64ff7559691340d059c3c6cd5a87270f00cba1598","src/mps.rs":"b5c5bceb8766a26c50782db80ac15b524890479bab4077685faa02af8d6265a3","src/pipeline/compute.rs":"01a0c8a9485b87a62183c22eaea85afc3345c81c30eb503d05fcf19b3a2c9baf","src/pipeline/mod.rs":"280916e71b6f3c5fffda6ffee31c616bf4aba6e5a186401ea2febe71c3264145","src/pipeline/render.rs":"611ac6d65348cd753ad897ed7a485328e5fd8c87d6f43f9c53868a58883706ed","src/renderpass.rs":"01d4bd8001b6b224f8d7ac5fe5cde45bc34712308e2c1ef05c2e1a4b50397c41","src/resource.rs":"910aa947ba979ad7ed5c5fdd3998b3a334637fe11ee45d3115c0f1363106be75","src/sampler.rs":"780b75b22ab1f6a6675d6e1bd04bd7457660c3325519bffb13a08b1abc64a79c","src/sync.rs":"d0db320dea7609992650062d341f506bf74b60efa761309730fc14bdb682539c","src/texture.rs":"334a7e6e32914ef72d40a7eba87ee90e5ed3dacc763da80b82e1076e0a77cbe2","src/types.rs":"d255f9c1b449acdb971616255e1c98d35b3b1ac54d9c388f7cdff6cfc3a8b944","src/vertexdescriptor.rs":"6a1378f270f7adf631319bcc8c8d6831c9f9be55e7b39a7ccfe151af9a9363c4"},"package":"c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"}
\ No newline at end of file diff --git a/third_party/rust/metal/Cargo.lock b/third_party/rust/metal/Cargo.lock new file mode 100644 index 0000000000..d2717b65ee --- /dev/null +++ b/third_party/rust/metal/Cargo.lock @@ -0,0 +1,1516 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "calloop" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +dependencies = [ + "bitflags 1.3.2", + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc 0.2.149", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc 0.2.149", + "objc", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc 0.2.149", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc 0.2.149", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "foreign-types 0.3.2", + "libc 0.2.149", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc 0.2.149", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossfont" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" +dependencies = [ + "cocoa", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "dwrote", + "foreign-types 0.5.0", + "freetype-rs", + "libc 0.2.149", + "log", + "objc", + "once_cell", + "pkg-config", + "servo-fontconfig", + "winapi", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.98", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc 0.2.149", + "serde", + "serde_derive", + "winapi", + "wio", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "freetype-rs" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" +dependencies = [ + "bitflags 1.3.2", + "freetype-sys", + "libc 0.2.149", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc 0.2.149", + "pkg-config", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc 0.2.149", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc 0.1.12", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc 0.2.149", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.27.0" +dependencies = [ + "bitflags 2.4.1", + "block", + "cocoa", + "core-graphics-types", + "cty", + "dispatch", + "foreign-types 0.5.0", + "glam", + "log", + "objc", + "paste", + "png", + "rand 0.8.5", + "sema", + "winit", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc 0.2.149", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" +dependencies = [ + "libc 0.2.149", + "log", + "ndk", + "ndk-context", + "ndk-macro", + "ndk-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc 0.2.149", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc 0.2.149", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc 0.2.149", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc 0.2.149", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc 0.2.149", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc 0.2.149", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +dependencies = [ + "cty", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sctk-adwaita" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" +dependencies = [ + "crossfont", + "log", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "sema" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3af15ff9b4a7d4bd2b21222c05154ee58260780a4d492c22de810f4f4187832" +dependencies = [ + "libc 0.1.12", + "rand 0.3.23", + "time", +] + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" + +[[package]] +name = "serde_derive" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc 0.2.149", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc 0.2.149", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tiny-skia" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "safe_arch", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" +dependencies = [ + "arrayref", + "bytemuck", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc 0.2.149", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winit" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc 0.2.149", + "log", + "mio", + "ndk", + "ndk-glue", + "objc", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.4.3", + "raw-window-handle 0.5.2", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-protocols", + "web-sys", + "windows-sys 0.36.1", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +dependencies = [ + "memchr", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "x11-dl" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +dependencies = [ + "lazy_static", + "libc 0.2.149", + "pkg-config", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/third_party/rust/metal/Cargo.toml b/third_party/rust/metal/Cargo.toml new file mode 100644 index 0000000000..a81b535fd0 --- /dev/null +++ b/third_party/rust/metal/Cargo.toml @@ -0,0 +1,144 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "metal" +version = "0.27.0" +authors = ["gfx-rs developers"] +exclude = [ + "guide/**/*", + "examples/texture/**/*", + "tests/**/*", + "Cargo.lock", + "target/**/*", +] +description = "Rust bindings for Metal" +homepage = "https://github.com/gfx-rs/metal-rs" +documentation = "https://docs.rs/crate/metal" +readme = "README.md" +keywords = [ + "metal", + "graphics", + "bindings", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/gfx-rs/metal-rs" + +[package.metadata.docs.rs] +default-target = "x86_64-apple-darwin" + +[[example]] +name = "window" + +[[example]] +name = "headless-render" + +[[example]] +name = "library" + +[[example]] +name = "raytracing" + +[[example]] +name = "reflection" + +[[example]] +name = "caps" + +[[example]] +name = "argument-buffer" + +[[example]] +name = "bindless" + +[[example]] +name = "circle" +path = "examples/circle/main.rs" + +[[example]] +name = "compute" +path = "examples/compute/main.rs" + +[[example]] +name = "mps" +required-features = ["mps"] + +[[example]] +name = "embedded-lib" +path = "examples/compute/embedded-lib.rs" + +[[example]] +name = "compute-argument-buffer" +path = "examples/compute/compute-argument-buffer.rs" + +[[example]] +name = "bind" + +[[example]] +name = "events" +required-features = ["dispatch"] + +[[example]] +name = "fence" + +[dependencies.bitflags] +version = "2" + +[dependencies.block] +version = "0.1.6" + +[dependencies.core-graphics-types] +version = "0.1" + +[dependencies.dispatch] +version = "0.2" +optional = true + +[dependencies.foreign-types] +version = "0.5" + +[dependencies.log] +version = "0.4" + +[dependencies.objc] +version = "0.2.4" +features = ["objc_exception"] + +[dependencies.paste] +version = "1" + +[dev-dependencies.cocoa] +version = "0.24.0" + +[dev-dependencies.cty] +version = "0.2.1" + +[dev-dependencies.glam] +version = "0.22" + +[dev-dependencies.png] +version = "0.17" + +[dev-dependencies.rand] +version = "0.8" + +[dev-dependencies.sema] +version = "0.1.4" + +[dev-dependencies.winit] +version = "0.27" + +[features] +default = ["link"] +link = [] +mps = [] +private = [] diff --git a/third_party/rust/metal/LICENSE-APACHE b/third_party/rust/metal/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/metal/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/metal/LICENSE-MIT b/third_party/rust/metal/LICENSE-MIT new file mode 100644 index 0000000000..25597d5838 --- /dev/null +++ b/third_party/rust/metal/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/metal/Makefile b/third_party/rust/metal/Makefile new file mode 100644 index 0000000000..ee7870c4e3 --- /dev/null +++ b/third_party/rust/metal/Makefile @@ -0,0 +1,16 @@ + +compute: + xcrun -sdk macosx metal -c examples/compute/shaders.metal -o examples/compute/shaders.air + xcrun -sdk macosx metallib examples/compute/shaders.air -o examples/compute/shaders.metallib + +window: + xcrun -sdk macosx metal -c examples/window/shaders.metal -o examples/window/shaders.air + xcrun -sdk macosx metallib examples/window/shaders.air -o examples/window/shaders.metallib + +circle: + xcrun -sdk macosx metal -c examples/circle/shaders.metal -o examples/circle/shaders.air + xcrun -sdk macosx metallib examples/circle/shaders.air -o examples/circle/shaders.metallib + +raytracing: + xcrun -sdk macosx metal -c -g examples/raytracing/shaders.metal -o examples/raytracing/shaders.air + xcrun -sdk macosx metallib examples/raytracing/shaders.air -o examples/raytracing/shaders.metallib diff --git a/third_party/rust/metal/README.md b/third_party/rust/metal/README.md new file mode 100644 index 0000000000..ef94a72da8 --- /dev/null +++ b/third_party/rust/metal/README.md @@ -0,0 +1,43 @@ +# metal-rs +[![Actions Status](https://github.com/gfx-rs/metal-rs/workflows/ci/badge.svg)](https://github.com/gfx-rs/metal-rs/actions) +[![Crates.io](https://img.shields.io/crates/v/metal.svg?label=metal)](https://crates.io/crates/metal) + +<p align="center"> + <img width="150" height="150" src="./assets/metal.svg"> +</p> + +<p align="center">Unsafe Rust bindings for the Metal 3D Graphics API.</p> + +## Documentation + +Note that [docs.rs](docs.rs) will fail to build the (albeit limited) documentation for this crate! +They build in a Linux container, but of course this will only compile on MacOS. + +Please build the documentation yourself with `cargo docs`. + +## Examples + +The [examples](/examples) directory highlights different ways of using the Metal graphics API for rendering +and computation. + +Examples can be run using commands such as: + +``` +# Replace `window` with the name of the example that you would like to run +cargo run --example window +``` + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/third_party/rust/metal/assets/metal.svg b/third_party/rust/metal/assets/metal.svg new file mode 100644 index 0000000000..9f0c87edfb --- /dev/null +++ b/third_party/rust/metal/assets/metal.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="720px" height="720px" viewBox="0 0 720 720" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="limeGradient"> + <stop stop-color="#0EFFDD" offset="0%"></stop> + <stop stop-color="#24FF74" offset="100%"></stop> + </linearGradient> + </defs> + <g> + <g> + <path d="M576,720 L144,720 C64.5,720 0,655.5 0,576 L0,144 C0,64.5 64.5,0 144,0 L576,0 C655.5,0 720,64.5 720,144 L720,576 C720,655.5 655.5,720 576,720 Z" id="app-backplate" fill="url(#limeGradient)"></path> + <polygon id="Path" fill="#000000" points="141 132 334 368 334 195 651 545 569 545 398 364 396 545 205 309 205 545 141 545"></polygon> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/third_party/rust/metal/bors.toml b/third_party/rust/metal/bors.toml new file mode 100644 index 0000000000..49ca8c96a8 --- /dev/null +++ b/third_party/rust/metal/bors.toml @@ -0,0 +1,8 @@ +status = [ "build (stable)" ] + +# As of May 2020 we can expect CI to take roughly 3 minutes based on +# Based on https://github.com/chinedufn/metal-rs/actions/runs/94020785 +# +# We round this up to a timeout of 5 minutes to account for any potential future +# inconsistencies in CI run times. +timeout-sec = 300 diff --git a/third_party/rust/metal/examples/argument-buffer/main.rs b/third_party/rust/metal/examples/argument-buffer/main.rs new file mode 100644 index 0000000000..23e88990d9 --- /dev/null +++ b/third_party/rust/metal/examples/argument-buffer/main.rs @@ -0,0 +1,88 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; + +fn main() { + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + + /* + + // Build encoder for the following MSL argument buffer: + struct ArgumentBuffer { + texture2d<float> texture [[id(0)]]; + sampler sampler [[id(1)]]; + array<device float *, 2> buffers [[id(2)]]; + } + + */ + + let desc1 = ArgumentDescriptor::new(); + desc1.set_index(0); + desc1.set_data_type(MTLDataType::Texture); + desc1.set_texture_type(MTLTextureType::D2); + + let desc2 = ArgumentDescriptor::new(); + desc2.set_index(1); + desc2.set_data_type(MTLDataType::Sampler); + + let desc3 = ArgumentDescriptor::new(); + desc3.set_index(2); + desc3.set_data_type(MTLDataType::Pointer); + desc3.set_array_length(2); + + let encoder = device.new_argument_encoder(Array::from_slice(&[desc1, desc2, desc3])); + println!("Encoder: {:?}", encoder); + + let argument_buffer = + device.new_buffer(encoder.encoded_length(), MTLResourceOptions::empty()); + encoder.set_argument_buffer(&argument_buffer, 0); + + let sampler = { + let descriptor = SamplerDescriptor::new(); + descriptor.set_support_argument_buffers(true); + device.new_sampler(&descriptor) + }; + println!("{:?}", sampler); + + let buffer1 = device.new_buffer(1024, MTLResourceOptions::empty()); + println!("Buffer1: {:?}", buffer1); + let buffer2 = device.new_buffer(1024, MTLResourceOptions::empty()); + println!("Buffer2: {:?}", buffer2); + + encoder.set_sampler_state(1, &sampler); + encoder.set_buffer(2, &buffer1, 0); + encoder.set_buffer(3, &buffer2, 0); + + // How to use argument buffer with render encoder. + + let queue = device.new_command_queue(); + let command_buffer = queue.new_command_buffer(); + + let render_pass_descriptor = RenderPassDescriptor::new(); + let encoder = command_buffer.new_render_command_encoder(render_pass_descriptor); + + // This method makes the array of resources resident for the selected stages of the render pass. + // Call this method before issuing any draw calls that may access the array of resources. + encoder.use_resources( + &[&buffer1, &buffer2], + MTLResourceUsage::Read, + MTLRenderStages::Vertex, + ); + // Bind argument buffer to vertex stage. + encoder.set_vertex_buffer(0, Some(&argument_buffer), 0); + + // Render pass here... + + encoder.end_encoding(); + println!("Encoder: {:?}", encoder); + + command_buffer.commit(); + }); +} diff --git a/third_party/rust/metal/examples/bind/main.rs b/third_party/rust/metal/examples/bind/main.rs new file mode 100644 index 0000000000..811b1c5a17 --- /dev/null +++ b/third_party/rust/metal/examples/bind/main.rs @@ -0,0 +1,34 @@ +// Copyright 2018 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; + +fn main() { + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + + let buffer = device.new_buffer(4, MTLResourceOptions::empty()); + let sampler = { + let descriptor = SamplerDescriptor::new(); + device.new_sampler(&descriptor) + }; + + let queue = device.new_command_queue(); + let cmd_buf = queue.new_command_buffer(); + + let encoder = cmd_buf.new_compute_command_encoder(); + + encoder.set_buffers(2, &[Some(&buffer), None], &[4, 0]); + encoder.set_sampler_states(1, &[Some(&sampler), None]); + + encoder.end_encoding(); + cmd_buf.commit(); + + println!("Everything is bound"); + }); +} diff --git a/third_party/rust/metal/examples/bindless/main.rs b/third_party/rust/metal/examples/bindless/main.rs new file mode 100644 index 0000000000..09c3a59ab9 --- /dev/null +++ b/third_party/rust/metal/examples/bindless/main.rs @@ -0,0 +1,149 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; + +const BINDLESS_TEXTURE_COUNT: NSUInteger = 100_000; // ~25Mb + +/// This example demonstrates: +/// - How to create a heap +/// - How to allocate textures from heap. +/// - How to create bindless resources via Metal's argument buffers. +/// - How to bind argument buffer to render encoder +fn main() { + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + + /* + + MSL + + struct Textures { + texture2d<float> texture; + }; + struct BindlessTextures { + device Textures *textures; + }; + + */ + + // Tier 2 argument buffers are supported by macOS devices with a discrete GPU and by the A13 GPU. + // The maximum per-app resources available at any given time are: + // - 500,000 buffers or textures + // - 2048 unique samplers + let tier = device.argument_buffers_support(); + println!("Argument buffer support: {:?}", tier); + assert_eq!(MTLArgumentBuffersTier::Tier2, tier); + + let texture_descriptor = TextureDescriptor::new(); + texture_descriptor.set_width(1); + texture_descriptor.set_height(1); + texture_descriptor.set_depth(1); + texture_descriptor.set_texture_type(MTLTextureType::D2); + texture_descriptor.set_pixel_format(MTLPixelFormat::R8Uint); + texture_descriptor.set_storage_mode(MTLStorageMode::Private); // GPU only. + println!("Texture descriptor: {:?}", texture_descriptor); + + // Determine the size required for the heap for the given descriptor + let size_and_align = device.heap_texture_size_and_align(&texture_descriptor); + + // Align the size so that more resources will fit in the heap after this texture + // See https://developer.apple.com/documentation/metal/buffers/using_argument_buffers_with_resource_heaps + let texture_size = + (size_and_align.size & (size_and_align.align - 1)) + size_and_align.align; + let heap_size = texture_size * BINDLESS_TEXTURE_COUNT; + + let heap_descriptor = HeapDescriptor::new(); + heap_descriptor.set_storage_mode(texture_descriptor.storage_mode()); // Must be compatible + heap_descriptor.set_size(heap_size); + println!("Heap descriptor: {:?}", heap_descriptor); + + let heap = device.new_heap(&heap_descriptor); + println!("Heap: {:?}", heap); + + // Allocate textures from heap + let textures = (0..BINDLESS_TEXTURE_COUNT) + .map(|i| { + heap.new_texture(&texture_descriptor) + .expect(&format!("Failed to allocate texture {}", i)) + }) + .collect::<Vec<_>>(); + + // Crate argument encoder that knows how to encode single texture + let descriptor = ArgumentDescriptor::new(); + descriptor.set_index(0); + descriptor.set_data_type(MTLDataType::Texture); + descriptor.set_texture_type(MTLTextureType::D2); + descriptor.set_access(MTLArgumentAccess::ReadOnly); + println!("Argument descriptor: {:?}", descriptor); + + let encoder = device.new_argument_encoder(Array::from_slice(&[descriptor])); + println!("Encoder: {:?}", encoder); + + // Determinate argument buffer size to allocate. + // Size needed to encode one texture * total number of bindless textures. + let argument_buffer_size = encoder.encoded_length() * BINDLESS_TEXTURE_COUNT; + let argument_buffer = device.new_buffer(argument_buffer_size, MTLResourceOptions::empty()); + + // Encode textures to the argument buffer. + textures.iter().enumerate().for_each(|(index, texture)| { + // Offset encoder to a proper texture slot + let offset = index as NSUInteger * encoder.encoded_length(); + encoder.set_argument_buffer(&argument_buffer, offset); + encoder.set_texture(0, texture); + }); + + // How to use bindless argument buffer when drawing + + let queue = device.new_command_queue(); + let command_buffer = queue.new_command_buffer(); + + let render_pass_descriptor = RenderPassDescriptor::new(); + let encoder = command_buffer.new_render_command_encoder(render_pass_descriptor); + + // Bind argument buffer. + encoder.set_fragment_buffer(0, Some(&argument_buffer), 0); + // Make sure all textures are available to the pass. + encoder.use_heap_at(&heap, MTLRenderStages::Fragment); + + // Bind material buffer at index 1 + // Draw + + /* + + // Now instead of binding individual textures each draw call, + // you can just bind material information instead: + + MSL + + struct Material { + int diffuse_texture_index; + int normal_texture_index; + // ... + } + + fragment float4 pixel( + VertexOut v [[stage_in]], + constant const BindlessTextures * textures [[buffer(0)]], + constant Material * material [[buffer(1)]] + ) { + if (material->base_color_texture_index != -1) { + textures[material->diffuse_texture_index].texture.sampler(...) + } + if (material->normal_texture_index != -1) { + ... + } + ... + } + + */ + + encoder.end_encoding(); + command_buffer.commit(); + }); +} diff --git a/third_party/rust/metal/examples/caps/main.rs b/third_party/rust/metal/examples/caps/main.rs new file mode 100644 index 0000000000..ae8fca4f0a --- /dev/null +++ b/third_party/rust/metal/examples/caps/main.rs @@ -0,0 +1,33 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; + +fn main() { + let device = Device::system_default().expect("no device found"); + + #[cfg(feature = "private")] + { + println!("Vendor: {:?}", unsafe { device.vendor() }); + println!("Family: {:?}", unsafe { device.family_name() }); + } + println!( + "Max threads per threadgroup: {:?}", + device.max_threads_per_threadgroup() + ); + #[cfg(target_os = "macos")] + { + println!("Integrated GPU: {:?}", device.is_low_power()); + println!("Headless: {:?}", device.is_headless()); + println!("D24S8: {:?}", device.d24_s8_supported()); + } + println!("maxBufferLength: {} Mb", device.max_buffer_length() >> 20); + println!( + "Indirect argument buffer: {:?}", + device.argument_buffers_support() + ); +} diff --git a/third_party/rust/metal/examples/circle/README.md b/third_party/rust/metal/examples/circle/README.md new file mode 100644 index 0000000000..f51853ac38 --- /dev/null +++ b/third_party/rust/metal/examples/circle/README.md @@ -0,0 +1,11 @@ +## circle + +Renders a circle in a window. As metal primitive types are only limited to point, line and triangle shape, this example shows how we can form complex structures out of primitive types. + +![Screenshot of the final render](./screenshot.png) + +## To Run + +``` +cargo run --example circle +``` diff --git a/third_party/rust/metal/examples/circle/main.rs b/third_party/rust/metal/examples/circle/main.rs new file mode 100644 index 0000000000..ed17a73402 --- /dev/null +++ b/third_party/rust/metal/examples/circle/main.rs @@ -0,0 +1,377 @@ +use metal::*; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + platform::macos::WindowExtMacOS, +}; + +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; + +use objc::{rc::autoreleasepool, runtime::YES}; + +use std::mem; + +// Declare the data structures needed to carry vertex layout to +// metal shading language(MSL) program. Use #[repr(C)], to make +// the data structure compatible with C++ type data structure +// for vertex defined in MSL program as MSL program is broadly +// based on C++ +#[repr(C)] +#[derive(Debug)] +pub struct position(cty::c_float, cty::c_float); +#[repr(C)] +#[derive(Debug)] +pub struct color(cty::c_float, cty::c_float, cty::c_float); +#[repr(C)] +#[derive(Debug)] +pub struct AAPLVertex { + p: position, + c: color, +} + +fn main() { + // Create a window for viewing the content + let event_loop = EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal".to_string()) + .build(&event_loop) + .unwrap(); + + // Set up the GPU device found in the system + let device = Device::system_default().expect("no device found"); + println!("Your device is: {}", device.name(),); + + // Scaffold required to sample the GPU and CPU timestamps + let mut cpu_start = 0; + let mut gpu_start = 0; + device.sample_timestamps(&mut cpu_start, &mut gpu_start); + let counter_sample_buffer = create_counter_sample_buffer(&device); + let destination_buffer = device.new_buffer( + (std::mem::size_of::<u64>() * 4 as usize) as u64, + MTLResourceOptions::StorageModeShared, + ); + let counter_sampling_point = MTLCounterSamplingPoint::AtStageBoundary; + assert!(device.supports_counter_sampling(counter_sampling_point)); + + let binary_archive_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/circle/binary_archive.metallib"); + + let binary_archive_url = + URL::new_with_string(&format!("file://{}", binary_archive_path.display())); + + let binary_archive_descriptor = BinaryArchiveDescriptor::new(); + if binary_archive_path.exists() { + binary_archive_descriptor.set_url(&binary_archive_url); + } + + // Set up a binary archive to cache compiled shaders. + let binary_archive = device + .new_binary_archive_with_descriptor(&binary_archive_descriptor) + .unwrap(); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/circle/shaders.metallib"); + + // Use the metallib file generated out of .metal shader file + let library = device.new_library_with_file(library_path).unwrap(); + + // The render pipeline generated from the vertex and fragment shaders in the .metal shader file. + let pipeline_state = prepare_pipeline_state(&device, &library, &binary_archive); + + // Serialize the binary archive to disk. + binary_archive + .serialize_to_url(&binary_archive_url) + .unwrap(); + + // Set the command queue used to pass commands to the device. + let command_queue = device.new_command_queue(); + + // Currently, MetalLayer is the only interface that provide + // layers to carry drawable texture from GPU rendaring through metal + // library to viewable windows. + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + layer.set_presents_with_transaction(false); + + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + + let draw_size = window.inner_size(); + layer.set_drawable_size(CGSize::new(draw_size.width as f64, draw_size.height as f64)); + + let vbuf = { + let vertex_data = create_vertex_points_for_circle(); + let vertex_data = vertex_data.as_slice(); + + device.new_buffer_with_data( + vertex_data.as_ptr() as *const _, + (vertex_data.len() * mem::size_of::<AAPLVertex>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, + ) + }; + + event_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + // ControlFlow::Wait pauses the event loop if no events are available to process. + // This is ideal for non-game applications that only update in response to user + // input, and uses significantly less power/CPU time than ControlFlow::Poll. + *control_flow = ControlFlow::Wait; + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + println!("The close button was pressed; stopping"); + *control_flow = ControlFlow::Exit + } + Event::MainEventsCleared => { + // Queue a RedrawRequested event. + window.request_redraw(); + } + Event::RedrawRequested(_) => { + // It's preferrable to render in this event rather than in MainEventsCleared, since + // rendering in here allows the program to gracefully handle redraws requested + // by the OS. + let drawable = match layer.next_drawable() { + Some(drawable) => drawable, + None => return, + }; + + // Create a new command buffer for each render pass to the current drawable + let command_buffer = command_queue.new_command_buffer(); + + // Obtain a renderPassDescriptor generated from the view's drawable textures. + let render_pass_descriptor = RenderPassDescriptor::new(); + handle_render_pass_color_attachment( + &render_pass_descriptor, + drawable.texture(), + ); + handle_render_pass_sample_buffer_attachment( + &render_pass_descriptor, + &counter_sample_buffer, + ); + + // Create a render command encoder. + let encoder = + command_buffer.new_render_command_encoder(&render_pass_descriptor); + encoder.set_render_pipeline_state(&pipeline_state); + // Pass in the parameter data. + encoder.set_vertex_buffer(0, Some(&vbuf), 0); + // Draw the triangles which will eventually form the circle. + encoder.draw_primitives(MTLPrimitiveType::TriangleStrip, 0, 1080); + encoder.end_encoding(); + + resolve_samples_into_buffer( + &command_buffer, + &counter_sample_buffer, + &destination_buffer, + ); + + // Schedule a present once the framebuffer is complete using the current drawable. + command_buffer.present_drawable(&drawable); + + // Finalize rendering here & push the command buffer to the GPU. + command_buffer.commit(); + command_buffer.wait_until_completed(); + + let mut cpu_end = 0; + let mut gpu_end = 0; + device.sample_timestamps(&mut cpu_end, &mut gpu_end); + handle_timestamps(&destination_buffer, cpu_start, cpu_end, gpu_start, gpu_end); + } + _ => (), + } + }); + }); +} + +// If we want to draw a circle, we need to draw it out of the three primitive +// types available with metal framework. Triangle is used in this case to form +// the circle. If we consider a circle to be total of 360 degree at center, we +// can form small triangle with one point at origin and two points at the +// perimeter of the circle for each degree. Eventually, if we can take enough +// triangle virtices for total of 360 degree, the triangles together will +// form a circle. This function captures the triangle vertices for each degree +// and push the co-ordinates of the vertices to a rust vector +fn create_vertex_points_for_circle() -> Vec<AAPLVertex> { + let mut v: Vec<AAPLVertex> = Vec::new(); + let origin_x: f32 = 0.0; + let origin_y: f32 = 0.0; + + // Size of the circle + let circle_size = 0.8f32; + + for i in 0..720 { + let y = i as f32; + // Get the X co-ordinate of each point on the perimeter of circle + let position_x: f32 = y.to_radians().cos() * 100.0; + let position_x: f32 = position_x.trunc() / 100.0; + // Set the size of the circle + let position_x: f32 = position_x * circle_size; + // Get the Y co-ordinate of each point on the perimeter of circle + let position_y: f32 = y.to_radians().sin() * 100.0; + let position_y: f32 = position_y.trunc() / 100.0; + // Set the size of the circle + let position_y: f32 = position_y * circle_size; + + v.push(AAPLVertex { + p: position(position_x, position_y), + c: color(0.7, 0.3, 0.5), + }); + + if (i + 1) % 2 == 0 { + // For each two points on perimeter, push one point of origin + v.push(AAPLVertex { + p: position(origin_x, origin_y), + c: color(0.2, 0.7, 0.4), + }); + } + } + + v +} + +fn handle_render_pass_sample_buffer_attachment( + descriptor: &RenderPassDescriptorRef, + counter_sample_buffer: &CounterSampleBufferRef, +) { + let sample_buffer_attachment_descriptor = + descriptor.sample_buffer_attachments().object_at(0).unwrap(); + sample_buffer_attachment_descriptor.set_sample_buffer(&counter_sample_buffer); + sample_buffer_attachment_descriptor.set_start_of_vertex_sample_index(0 as NSUInteger); + sample_buffer_attachment_descriptor.set_end_of_vertex_sample_index(1 as NSUInteger); + sample_buffer_attachment_descriptor.set_start_of_fragment_sample_index(2 as NSUInteger); + sample_buffer_attachment_descriptor.set_end_of_fragment_sample_index(3 as NSUInteger); +} + +fn handle_render_pass_color_attachment(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + + color_attachment.set_texture(Some(texture)); + color_attachment.set_load_action(MTLLoadAction::Clear); + // Setting a background color + color_attachment.set_clear_color(MTLClearColor::new(0.5, 0.5, 0.8, 1.0)); + color_attachment.set_store_action(MTLStoreAction::Store); +} + +fn prepare_pipeline_state( + device: &Device, + library: &Library, + binary_archive: &BinaryArchive, +) -> RenderPipelineState { + let vert = library.get_function("vs", None).unwrap(); + let frag = library.get_function("ps", None).unwrap(); + + let pipeline_state_descriptor = RenderPipelineDescriptor::new(); + pipeline_state_descriptor.set_vertex_function(Some(&vert)); + pipeline_state_descriptor.set_fragment_function(Some(&frag)); + pipeline_state_descriptor + .color_attachments() + .object_at(0) + .unwrap() + .set_pixel_format(MTLPixelFormat::BGRA8Unorm); + // Set the binary archives to search for a cached pipeline in. + pipeline_state_descriptor.set_binary_archives(&[binary_archive]); + + // Add the pipeline descriptor to the binary archive cache. + binary_archive + .add_render_pipeline_functions_with_descriptor(&pipeline_state_descriptor) + .unwrap(); + + device + .new_render_pipeline_state(&pipeline_state_descriptor) + .unwrap() +} + +fn resolve_samples_into_buffer( + command_buffer: &CommandBufferRef, + counter_sample_buffer: &CounterSampleBufferRef, + destination_buffer: &BufferRef, +) { + let blit_encoder = command_buffer.new_blit_command_encoder(); + blit_encoder.resolve_counters( + &counter_sample_buffer, + crate::NSRange::new(0_u64, 4), + &destination_buffer, + 0_u64, + ); + blit_encoder.end_encoding(); +} + +fn handle_timestamps( + resolved_sample_buffer: &BufferRef, + cpu_start: u64, + cpu_end: u64, + gpu_start: u64, + gpu_end: u64, +) { + let samples = unsafe { + std::slice::from_raw_parts(resolved_sample_buffer.contents() as *const u64, 4 as usize) + }; + let vertex_pass_start = samples[0]; + let vertex_pass_end = samples[1]; + let fragment_pass_start = samples[2]; + let fragment_pass_end = samples[3]; + + let cpu_time_span = cpu_end - cpu_start; + let gpu_time_span = gpu_end - gpu_start; + + let vertex_micros = microseconds_between_begin( + vertex_pass_start, + vertex_pass_end, + gpu_time_span, + cpu_time_span, + ); + let fragment_micros = microseconds_between_begin( + fragment_pass_start, + fragment_pass_end, + gpu_time_span, + cpu_time_span, + ); + + println!("Vertex pass duration: {:.2} µs", vertex_micros); + println!("Fragment pass duration: {:.2} µs\n", fragment_micros); +} + +fn create_counter_sample_buffer(device: &Device) -> CounterSampleBuffer { + let counter_sample_buffer_desc = metal::CounterSampleBufferDescriptor::new(); + counter_sample_buffer_desc.set_storage_mode(metal::MTLStorageMode::Shared); + counter_sample_buffer_desc.set_sample_count(4_u64); + counter_sample_buffer_desc.set_counter_set(&fetch_timestamp_counter_set(device)); + + device + .new_counter_sample_buffer_with_descriptor(&counter_sample_buffer_desc) + .unwrap() +} + +fn fetch_timestamp_counter_set(device: &Device) -> metal::CounterSet { + let counter_sets = device.counter_sets(); + let mut timestamp_counter = None; + for cs in counter_sets.iter() { + if cs.name() == "timestamp" { + timestamp_counter = Some(cs); + break; + } + } + timestamp_counter + .expect("No timestamp counter found") + .clone() +} + +/// <https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/converting_gpu_timestamps_into_cpu_time> +fn microseconds_between_begin(begin: u64, end: u64, gpu_time_span: u64, cpu_time_span: u64) -> f64 { + let time_span = (end as f64) - (begin as f64); + let nanoseconds = time_span / (gpu_time_span as f64) * (cpu_time_span as f64); + let microseconds = nanoseconds / 1000.0; + return microseconds; +} diff --git a/third_party/rust/metal/examples/circle/screenshot.png b/third_party/rust/metal/examples/circle/screenshot.png Binary files differnew file mode 100644 index 0000000000..38f86e733d --- /dev/null +++ b/third_party/rust/metal/examples/circle/screenshot.png diff --git a/third_party/rust/metal/examples/circle/shaders.metal b/third_party/rust/metal/examples/circle/shaders.metal new file mode 100644 index 0000000000..037af8a233 --- /dev/null +++ b/third_party/rust/metal/examples/circle/shaders.metal @@ -0,0 +1,39 @@ +#include <metal_stdlib> + +#include <simd/simd.h> + +using namespace metal; + +typedef struct { + float x; + float y; +}position; + +typedef struct { + float r; + float g; + float b; +}color; + +typedef struct { + position p; + color c; +}AAPLVertex; + +struct ColorInOut { + float4 position[[position]]; + float4 color; +}; + +vertex ColorInOut vs(constant AAPLVertex * vertex_array[[buffer(0)]], unsigned int vid[[vertex_id]]) { + ColorInOut out; + + out.position = float4(float2(vertex_array[vid].p.x, vertex_array[vid].p.y), 0.0, 1.0); + out.color = float4(float3(vertex_array[vid].c.r, vertex_array[vid].c.g, vertex_array[vid].c.b), 1.0); + + return out; +} + +fragment float4 ps(ColorInOut in [[stage_in]]) { + return in.color; +} diff --git a/third_party/rust/metal/examples/circle/shaders.metallib b/third_party/rust/metal/examples/circle/shaders.metallib Binary files differnew file mode 100644 index 0000000000..cbb9bc5e5a --- /dev/null +++ b/third_party/rust/metal/examples/circle/shaders.metallib diff --git a/third_party/rust/metal/examples/compute/compute-argument-buffer.metal b/third_party/rust/metal/examples/compute/compute-argument-buffer.metal new file mode 100644 index 0000000000..1dcc79daf5 --- /dev/null +++ b/third_party/rust/metal/examples/compute/compute-argument-buffer.metal @@ -0,0 +1,14 @@ +#include <metal_stdlib> + +using namespace metal; + +struct SumInput { + device uint *data; + volatile device atomic_uint *sum; +}; + +kernel void sum(device SumInput& input [[ buffer(0) ]], + uint gid [[ thread_position_in_grid ]]) +{ + atomic_fetch_add_explicit(input.sum, input.data[gid], memory_order_relaxed); +} diff --git a/third_party/rust/metal/examples/compute/compute-argument-buffer.rs b/third_party/rust/metal/examples/compute/compute-argument-buffer.rs new file mode 100644 index 0000000000..97527091a3 --- /dev/null +++ b/third_party/rust/metal/examples/compute/compute-argument-buffer.rs @@ -0,0 +1,95 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; +use std::mem; + +static LIBRARY_SRC: &str = include_str!("compute-argument-buffer.metal"); + +fn main() { + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + let command_queue = device.new_command_queue(); + + let data = [ + 1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, + ]; + + let buffer = device.new_buffer_with_data( + unsafe { mem::transmute(data.as_ptr()) }, + (data.len() * mem::size_of::<u32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache, + ); + + let sum = { + let data = [0u32]; + device.new_buffer_with_data( + unsafe { mem::transmute(data.as_ptr()) }, + (data.len() * mem::size_of::<u32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache, + ) + }; + + let command_buffer = command_queue.new_command_buffer(); + let encoder = command_buffer.new_compute_command_encoder(); + + let library = device + .new_library_with_source(LIBRARY_SRC, &CompileOptions::new()) + .unwrap(); + let kernel = library.get_function("sum", None).unwrap(); + + let argument_encoder = kernel.new_argument_encoder(0); + let arg_buffer = device.new_buffer( + argument_encoder.encoded_length(), + MTLResourceOptions::empty(), + ); + argument_encoder.set_argument_buffer(&arg_buffer, 0); + argument_encoder.set_buffer(0, &buffer, 0); + argument_encoder.set_buffer(1, &sum, 0); + + let pipeline_state_descriptor = ComputePipelineDescriptor::new(); + pipeline_state_descriptor.set_compute_function(Some(&kernel)); + + let pipeline_state = device + .new_compute_pipeline_state_with_function( + pipeline_state_descriptor.compute_function().unwrap(), + ) + .unwrap(); + + encoder.set_compute_pipeline_state(&pipeline_state); + encoder.set_buffer(0, Some(&arg_buffer), 0); + + encoder.use_resource(&buffer, MTLResourceUsage::Read); + encoder.use_resource(&sum, MTLResourceUsage::Write); + + let width = 16; + + let thread_group_count = MTLSize { + width, + height: 1, + depth: 1, + }; + + let thread_group_size = MTLSize { + width: (data.len() as u64 + width) / width, + height: 1, + depth: 1, + }; + + encoder.dispatch_thread_groups(thread_group_count, thread_group_size); + encoder.end_encoding(); + command_buffer.commit(); + command_buffer.wait_until_completed(); + + let ptr = sum.contents() as *mut u32; + unsafe { + assert_eq!(465, *ptr); + } + }); +} diff --git a/third_party/rust/metal/examples/compute/embedded-lib.rs b/third_party/rust/metal/examples/compute/embedded-lib.rs new file mode 100644 index 0000000000..0fd193abe3 --- /dev/null +++ b/third_party/rust/metal/examples/compute/embedded-lib.rs @@ -0,0 +1,24 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; + +fn main() { + let library_data = include_bytes!("shaders.metallib"); + + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + + let library = device.new_library_with_data(&library_data[..]).unwrap(); + let kernel = library.get_function("sum", None).unwrap(); + + println!("Function name: {}", kernel.name()); + println!("Function type: {:?}", kernel.function_type()); + println!("OK"); + }); +} diff --git a/third_party/rust/metal/examples/compute/main.rs b/third_party/rust/metal/examples/compute/main.rs new file mode 100644 index 0000000000..b654a666f1 --- /dev/null +++ b/third_party/rust/metal/examples/compute/main.rs @@ -0,0 +1,194 @@ +use metal::*; +use objc::rc::autoreleasepool; +use std::path::PathBuf; + +const NUM_SAMPLES: u64 = 2; + +fn main() { + let num_elements = std::env::args() + .nth(1) + .map(|s| s.parse::<u32>().unwrap()) + .unwrap_or(64 * 64); + + autoreleasepool(|| { + let device = Device::system_default().expect("No device found"); + let mut cpu_start = 0; + let mut gpu_start = 0; + device.sample_timestamps(&mut cpu_start, &mut gpu_start); + + let counter_sample_buffer = create_counter_sample_buffer(&device); + let destination_buffer = device.new_buffer( + (std::mem::size_of::<u64>() * NUM_SAMPLES as usize) as u64, + MTLResourceOptions::StorageModeShared, + ); + + let counter_sampling_point = MTLCounterSamplingPoint::AtStageBoundary; + assert!(device.supports_counter_sampling(counter_sampling_point)); + + let command_queue = device.new_command_queue(); + let command_buffer = command_queue.new_command_buffer(); + + let compute_pass_descriptor = ComputePassDescriptor::new(); + handle_compute_pass_sample_buffer_attachment( + compute_pass_descriptor, + &counter_sample_buffer, + ); + let encoder = + command_buffer.compute_command_encoder_with_descriptor(compute_pass_descriptor); + + let pipeline_state = create_pipeline_state(&device); + encoder.set_compute_pipeline_state(&pipeline_state); + + let (buffer, sum) = create_input_and_output_buffers(&device, num_elements); + encoder.set_buffer(0, Some(&buffer), 0); + encoder.set_buffer(1, Some(&sum), 0); + + let num_threads = pipeline_state.thread_execution_width(); + + let thread_group_count = MTLSize { + width: ((num_elements as NSUInteger + num_threads) / num_threads), + height: 1, + depth: 1, + }; + + let thread_group_size = MTLSize { + width: num_threads, + height: 1, + depth: 1, + }; + + encoder.dispatch_thread_groups(thread_group_count, thread_group_size); + encoder.end_encoding(); + + resolve_samples_into_buffer(command_buffer, &counter_sample_buffer, &destination_buffer); + + command_buffer.commit(); + command_buffer.wait_until_completed(); + let mut cpu_end = 0; + let mut gpu_end = 0; + device.sample_timestamps(&mut cpu_end, &mut gpu_end); + + let ptr = sum.contents() as *mut u32; + println!("Compute shader sum: {}", unsafe { *ptr }); + + unsafe { + assert_eq!(num_elements, *ptr); + } + + handle_timestamps(&destination_buffer, cpu_start, cpu_end, gpu_start, gpu_end); + }); +} + +fn create_pipeline_state(device: &Device) -> ComputePipelineState { + let library_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/compute/shaders.metallib"); + let library = device.new_library_with_file(library_path).unwrap(); + let kernel = library.get_function("sum", None).unwrap(); + + let pipeline_state_descriptor = ComputePipelineDescriptor::new(); + pipeline_state_descriptor.set_compute_function(Some(&kernel)); + + device + .new_compute_pipeline_state_with_function( + pipeline_state_descriptor.compute_function().unwrap(), + ) + .unwrap() +} + +fn handle_compute_pass_sample_buffer_attachment( + compute_pass_descriptor: &ComputePassDescriptorRef, + counter_sample_buffer: &CounterSampleBufferRef, +) { + let sample_buffer_attachment_descriptor = compute_pass_descriptor + .sample_buffer_attachments() + .object_at(0) + .unwrap(); + + sample_buffer_attachment_descriptor.set_sample_buffer(counter_sample_buffer); + sample_buffer_attachment_descriptor.set_start_of_encoder_sample_index(0); + sample_buffer_attachment_descriptor.set_end_of_encoder_sample_index(1); +} + +fn resolve_samples_into_buffer( + command_buffer: &CommandBufferRef, + counter_sample_buffer: &CounterSampleBufferRef, + destination_buffer: &BufferRef, +) { + let blit_encoder = command_buffer.new_blit_command_encoder(); + blit_encoder.resolve_counters( + counter_sample_buffer, + crate::NSRange::new(0_u64, NUM_SAMPLES), + destination_buffer, + 0_u64, + ); + blit_encoder.end_encoding(); +} + +fn handle_timestamps( + resolved_sample_buffer: &BufferRef, + cpu_start: u64, + cpu_end: u64, + gpu_start: u64, + gpu_end: u64, +) { + let samples = unsafe { + std::slice::from_raw_parts( + resolved_sample_buffer.contents() as *const u64, + NUM_SAMPLES as usize, + ) + }; + let pass_start = samples[0]; + let pass_end = samples[1]; + + let cpu_time_span = cpu_end - cpu_start; + let gpu_time_span = gpu_end - gpu_start; + + let micros = microseconds_between_begin(pass_start, pass_end, gpu_time_span, cpu_time_span); + println!("Compute pass duration: {} µs", micros); +} + +fn create_counter_sample_buffer(device: &Device) -> CounterSampleBuffer { + let counter_sample_buffer_desc = metal::CounterSampleBufferDescriptor::new(); + counter_sample_buffer_desc.set_storage_mode(metal::MTLStorageMode::Shared); + counter_sample_buffer_desc.set_sample_count(NUM_SAMPLES); + let counter_sets = device.counter_sets(); + + let timestamp_counter = counter_sets.iter().find(|cs| cs.name() == "timestamp"); + + counter_sample_buffer_desc + .set_counter_set(timestamp_counter.expect("No timestamp counter found")); + + device + .new_counter_sample_buffer_with_descriptor(&counter_sample_buffer_desc) + .unwrap() +} + +fn create_input_and_output_buffers( + device: &Device, + num_elements: u32, +) -> (metal::Buffer, metal::Buffer) { + let data = vec![1u32; num_elements as usize]; + + let buffer = device.new_buffer_with_data( + unsafe { std::mem::transmute(data.as_ptr()) }, + (data.len() * std::mem::size_of::<u32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache, + ); + + let sum = { + let data = [0u32]; + device.new_buffer_with_data( + unsafe { std::mem::transmute(data.as_ptr()) }, + (data.len() * std::mem::size_of::<u32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache, + ) + }; + (buffer, sum) +} + +/// <https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/converting_gpu_timestamps_into_cpu_time> +fn microseconds_between_begin(begin: u64, end: u64, gpu_time_span: u64, cpu_time_span: u64) -> f64 { + let time_span = (end as f64) - (begin as f64); + let nanoseconds = time_span / (gpu_time_span as f64) * (cpu_time_span as f64); + nanoseconds / 1000.0 +} diff --git a/third_party/rust/metal/examples/compute/shaders.metal b/third_party/rust/metal/examples/compute/shaders.metal new file mode 100644 index 0000000000..51363a1d36 --- /dev/null +++ b/third_party/rust/metal/examples/compute/shaders.metal @@ -0,0 +1,10 @@ +#include <metal_stdlib> + +using namespace metal; + +kernel void sum(device uint *data [[ buffer(0) ]], + volatile device atomic_uint *sum [[ buffer(1) ]], + uint gid [[ thread_position_in_grid ]]) +{ + atomic_fetch_add_explicit(sum, data[gid], memory_order_relaxed); +} diff --git a/third_party/rust/metal/examples/compute/shaders.metallib b/third_party/rust/metal/examples/compute/shaders.metallib Binary files differnew file mode 100644 index 0000000000..af7cb17240 --- /dev/null +++ b/third_party/rust/metal/examples/compute/shaders.metallib diff --git a/third_party/rust/metal/examples/events/main.rs b/third_party/rust/metal/examples/events/main.rs new file mode 100644 index 0000000000..9e4fe0e820 --- /dev/null +++ b/third_party/rust/metal/examples/events/main.rs @@ -0,0 +1,50 @@ +// Copyright 2020 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use dispatch::{Queue, QueueAttribute}; +use metal::*; + +/// This example replicates `Synchronizing Events Between a GPU and the CPU` article. +/// See https://developer.apple.com/documentation/metal/synchronization/synchronizing_events_between_a_gpu_and_the_cpu +fn main() { + let device = Device::system_default().expect("No device found"); + + let command_queue = device.new_command_queue(); + let command_buffer = command_queue.new_command_buffer(); + + // Shareable event + let shared_event = device.new_shared_event(); + + // Shareable event listener + let my_queue = Queue::create( + "com.example.apple-samplecode.MyQueue", + QueueAttribute::Serial, + ); + + // Enable `dispatch` feature to use dispatch queues, + // otherwise unsafe `from_queue_handle` is available for use with native APIs. + let shared_event_listener = SharedEventListener::from_queue(&my_queue); + + // Register CPU work + let notify_block = block::ConcreteBlock::new(move |evt: &SharedEventRef, val: u64| { + println!("Got notification from GPU: {}", val); + evt.set_signaled_value(3); + }); + + shared_event.notify(&shared_event_listener, 2, notify_block.copy()); + + // Encode GPU work + command_buffer.encode_signal_event(&shared_event, 1); + command_buffer.encode_signal_event(&shared_event, 2); + command_buffer.encode_wait_for_event(&shared_event, 3); + + command_buffer.commit(); + + command_buffer.wait_until_completed(); + + println!("Done"); +} diff --git a/third_party/rust/metal/examples/fence/main.rs b/third_party/rust/metal/examples/fence/main.rs new file mode 100644 index 0000000000..53515d39f5 --- /dev/null +++ b/third_party/rust/metal/examples/fence/main.rs @@ -0,0 +1,30 @@ +// Copyright 2020 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; + +fn main() { + let device = Device::system_default().expect("No device found"); + + let command_queue = device.new_command_queue(); + let command_buffer = command_queue.new_command_buffer(); + + let fence = device.new_fence(); + + let blit_encoder = command_buffer.new_blit_command_encoder(); + blit_encoder.update_fence(&fence); + blit_encoder.end_encoding(); + + let compute_encoder = command_buffer.new_compute_command_encoder(); + compute_encoder.wait_for_fence(&fence); + compute_encoder.end_encoding(); + + command_buffer.commit(); + command_buffer.wait_until_completed(); + + println!("Done"); +} diff --git a/third_party/rust/metal/examples/headless-render/README.md b/third_party/rust/metal/examples/headless-render/README.md new file mode 100644 index 0000000000..6bc434b44a --- /dev/null +++ b/third_party/rust/metal/examples/headless-render/README.md @@ -0,0 +1,11 @@ +## headless-render + +Renders the triangle from the [window example](../window) headlessly and then writes it to a PNG file. + +![Screenshot of the final render](./screenshot.png) + +## To Run + +``` +cargo run --example headless-render +``` diff --git a/third_party/rust/metal/examples/headless-render/main.rs b/third_party/rust/metal/examples/headless-render/main.rs new file mode 100644 index 0000000000..449170251f --- /dev/null +++ b/third_party/rust/metal/examples/headless-render/main.rs @@ -0,0 +1,159 @@ +use std::mem; +use std::path::PathBuf; + +use std::fs::File; +use std::io::BufWriter; + +use metal::{ + Buffer, Device, DeviceRef, LibraryRef, MTLClearColor, MTLLoadAction, MTLOrigin, MTLPixelFormat, + MTLPrimitiveType, MTLRegion, MTLResourceOptions, MTLSize, MTLStoreAction, RenderPassDescriptor, + RenderPassDescriptorRef, RenderPipelineDescriptor, RenderPipelineState, Texture, + TextureDescriptor, TextureRef, +}; +use png::ColorType; + +const VIEW_WIDTH: u64 = 512; +const VIEW_HEIGHT: u64 = 512; +const TOTAL_BYTES: usize = (VIEW_WIDTH * VIEW_HEIGHT * 4) as usize; + +const VERTEX_SHADER: &'static str = "triangle_vertex"; +const FRAGMENT_SHADER: &'static str = "triangle_fragment"; + +// [2 bytes position, 3 bytes color] * 3 +#[rustfmt::skip] +const VERTEX_ATTRIBS: [f32; 15] = [ + 0.0, 0.5, 1.0, 0.0, 0.0, + -0.5, -0.5, 0.0, 1.0, 0.0, + 0.5, -0.5, 0.0, 0.0, 1.0, +]; + +/// This example shows how to render headlessly by: +/// +/// 1. Rendering a triangle to an MtlDrawable +/// +/// 2. Waiting for the render to complete and the color texture to be synchronized with the CPU +/// by using a blit command encoder +/// +/// 3. Reading the texture bytes from the MtlTexture +/// +/// 4. Saving the texture to a PNG file +fn main() { + let device = Device::system_default().expect("No device found"); + + let texture = create_texture(&device); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/window/shaders.metallib"); + + let library = device.new_library_with_file(library_path).unwrap(); + + let pipeline_state = prepare_pipeline_state(&device, &library); + + let command_queue = device.new_command_queue(); + + let vertex_buffer = create_vertex_buffer(&device); + + let render_pass_descriptor = RenderPassDescriptor::new(); + initialize_color_attachment(&render_pass_descriptor, &texture); + + let command_buffer = command_queue.new_command_buffer(); + let rc_encoder = command_buffer.new_render_command_encoder(&render_pass_descriptor); + rc_encoder.set_render_pipeline_state(&pipeline_state); + rc_encoder.set_vertex_buffer(0, Some(&vertex_buffer), 0); + rc_encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 3); + rc_encoder.end_encoding(); + + render_pass_descriptor + .color_attachments() + .object_at(0) + .unwrap() + .set_load_action(MTLLoadAction::DontCare); + + let blit_encoder = command_buffer.new_blit_command_encoder(); + blit_encoder.synchronize_resource(&texture); + blit_encoder.end_encoding(); + + command_buffer.commit(); + + command_buffer.wait_until_completed(); + + save_image(&texture); +} + +fn save_image(texture: &TextureRef) { + let mut image = vec![0; TOTAL_BYTES]; + + texture.get_bytes( + image.as_mut_ptr() as *mut std::ffi::c_void, + VIEW_WIDTH * 4, + MTLRegion { + origin: MTLOrigin { x: 0, y: 0, z: 0 }, + size: MTLSize { + width: VIEW_WIDTH, + height: VIEW_HEIGHT, + depth: 1, + }, + }, + 0, + ); + + let out_file = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/headless-render/out.png"); + let file = File::create(&out_file).unwrap(); + let ref mut w = BufWriter::new(file); + + let mut encoder = png::Encoder::new(w, VIEW_WIDTH as u32, VIEW_HEIGHT as u32); + encoder.set_color(ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder.write_header().unwrap(); + + writer.write_image_data(&image).unwrap(); + + println!("Image saved to {:?}", out_file); +} + +fn create_texture(device: &Device) -> Texture { + let texture = TextureDescriptor::new(); + texture.set_width(VIEW_WIDTH); + texture.set_height(VIEW_HEIGHT); + texture.set_pixel_format(MTLPixelFormat::RGBA8Unorm); + + device.new_texture(&texture) +} + +fn prepare_pipeline_state(device: &DeviceRef, library: &LibraryRef) -> RenderPipelineState { + let vert = library.get_function(VERTEX_SHADER, None).unwrap(); + let frag = library.get_function(FRAGMENT_SHADER, None).unwrap(); + + let pipeline_state_descriptor = RenderPipelineDescriptor::new(); + + pipeline_state_descriptor.set_vertex_function(Some(&vert)); + pipeline_state_descriptor.set_fragment_function(Some(&frag)); + + pipeline_state_descriptor + .color_attachments() + .object_at(0) + .unwrap() + .set_pixel_format(MTLPixelFormat::RGBA8Unorm); + + device + .new_render_pipeline_state(&pipeline_state_descriptor) + .unwrap() +} + +fn create_vertex_buffer(device: &DeviceRef) -> Buffer { + device.new_buffer_with_data( + VERTEX_ATTRIBS.as_ptr() as *const _, + (VERTEX_ATTRIBS.len() * mem::size_of::<f32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, + ) +} + +fn initialize_color_attachment(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + + color_attachment.set_texture(Some(texture)); + color_attachment.set_load_action(MTLLoadAction::Clear); + color_attachment.set_clear_color(MTLClearColor::new(0.5, 0.2, 0.2, 1.0)); + color_attachment.set_store_action(MTLStoreAction::Store); +} diff --git a/third_party/rust/metal/examples/headless-render/screenshot.png b/third_party/rust/metal/examples/headless-render/screenshot.png Binary files differnew file mode 100644 index 0000000000..2af9c5895f --- /dev/null +++ b/third_party/rust/metal/examples/headless-render/screenshot.png diff --git a/third_party/rust/metal/examples/library/main.rs b/third_party/rust/metal/examples/library/main.rs new file mode 100644 index 0000000000..7223db89c1 --- /dev/null +++ b/third_party/rust/metal/examples/library/main.rs @@ -0,0 +1,17 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; + +const PROGRAM: &'static str = ""; + +fn main() { + let device = Device::system_default().expect("no device found"); + + let options = CompileOptions::new(); + let _library = device.new_library_with_source(PROGRAM, &options); +} diff --git a/third_party/rust/metal/examples/mesh-shader/main.rs b/third_party/rust/metal/examples/mesh-shader/main.rs new file mode 100644 index 0000000000..8edb30ce1f --- /dev/null +++ b/third_party/rust/metal/examples/mesh-shader/main.rs @@ -0,0 +1,118 @@ +extern crate objc; + +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; + +use metal::*; +use objc::{rc::autoreleasepool, runtime::YES}; +use std::mem; +use winit::platform::macos::WindowExtMacOS; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, +}; + +fn prepare_render_pass_descriptor(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + + color_attachment.set_texture(Some(texture)); + color_attachment.set_load_action(MTLLoadAction::Clear); + color_attachment.set_clear_color(MTLClearColor::new(0.2, 0.2, 0.25, 1.0)); + color_attachment.set_store_action(MTLStoreAction::Store); +} + +fn main() { + let events_loop = winit::event_loop::EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal Mesh Shader Example".to_string()) + .build(&events_loop) + .unwrap(); + + let device = Device::system_default().expect("no device found"); + + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + layer.set_presents_with_transaction(false); + + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + + let draw_size = window.inner_size(); + layer.set_drawable_size(CGSize::new(draw_size.width as f64, draw_size.height as f64)); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/mesh-shader/shaders.metallib"); + let library = device.new_library_with_file(library_path).unwrap(); + + let mesh = library.get_function("mesh_function", None).unwrap(); + let frag = library.get_function("fragment_function", None).unwrap(); + + let pipeline_state_desc = MeshRenderPipelineDescriptor::new(); + pipeline_state_desc + .color_attachments() + .object_at(0) + .unwrap() + .set_pixel_format(MTLPixelFormat::BGRA8Unorm); + pipeline_state_desc.set_mesh_function(Some(&mesh)); + pipeline_state_desc.set_fragment_function(Some(&frag)); + + let pipeline_state = device + .new_mesh_render_pipeline_state(&pipeline_state_desc) + .unwrap(); + + let command_queue = device.new_command_queue(); + + events_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + *control_flow = ControlFlow::Poll; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64)); + } + _ => (), + }, + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + let drawable = match layer.next_drawable() { + Some(drawable) => drawable, + None => return, + }; + + let render_pass_descriptor = RenderPassDescriptor::new(); + + prepare_render_pass_descriptor(&render_pass_descriptor, drawable.texture()); + + let command_buffer = command_queue.new_command_buffer(); + let encoder = + command_buffer.new_render_command_encoder(&render_pass_descriptor); + + encoder.set_render_pipeline_state(&pipeline_state); + encoder.draw_mesh_threads( + MTLSize::new(1, 1, 1), + MTLSize::new(1, 1, 1), + MTLSize::new(1, 1, 1), + ); + + encoder.end_encoding(); + + command_buffer.present_drawable(&drawable); + command_buffer.commit(); + } + _ => {} + } + }); + }); +} diff --git a/third_party/rust/metal/examples/mesh-shader/shaders.metal b/third_party/rust/metal/examples/mesh-shader/shaders.metal new file mode 100644 index 0000000000..1a82530742 --- /dev/null +++ b/third_party/rust/metal/examples/mesh-shader/shaders.metal @@ -0,0 +1,30 @@ +#include <metal_stdlib> + +using namespace metal; + +struct VertexOut { + float4 position [[position]]; +}; + +using mesh_t = mesh<VertexOut, void, 3, 1, topology::triangle>; + +[[mesh]] void mesh_function(mesh_t m) { + VertexOut v; + v.position = float4(-1.0, -1.0, 0.0, 1.0); + + m.set_primitive_count(1); + + m.set_vertex(0, v); + v.position = float4(0.0, 1.0, 0.0, 1.0); + m.set_vertex(1, v); + v.position = float4(1.0, -1.0, 0.0, 1.0); + m.set_vertex(2, v); + + m.set_index(0, 0); + m.set_index(1, 1); + m.set_index(2, 2); +} + +fragment half4 fragment_function() { + return half4(0.1, 1.0, 0.1, 1.0); +}
\ No newline at end of file diff --git a/third_party/rust/metal/examples/mesh-shader/shaders.metallib b/third_party/rust/metal/examples/mesh-shader/shaders.metallib Binary files differnew file mode 100644 index 0000000000..4af8d60ddc --- /dev/null +++ b/third_party/rust/metal/examples/mesh-shader/shaders.metallib diff --git a/third_party/rust/metal/examples/mps/main.rs b/third_party/rust/metal/examples/mps/main.rs new file mode 100644 index 0000000000..cc01b7a40d --- /dev/null +++ b/third_party/rust/metal/examples/mps/main.rs @@ -0,0 +1,148 @@ +use metal::*; +use std::ffi::c_void; +use std::mem; + +#[repr(C)] +struct Vertex { + xyz: [f32; 3], +} + +type Ray = mps::MPSRayOriginMinDistanceDirectionMaxDistance; +type Intersection = mps::MPSIntersectionDistancePrimitiveIndexCoordinates; + +// Original example taken from https://sergeyreznik.github.io/metal-ray-tracer/part-1/index.html +fn main() { + let device = Device::system_default().expect("No device found"); + + let library_path = + std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/mps/shaders.metallib"); + let library = device + .new_library_with_file(library_path) + .expect("Failed to load shader library"); + + let generate_rays_pipeline = create_pipeline("generateRays", &library, &device); + + let queue = device.new_command_queue(); + let command_buffer = queue.new_command_buffer(); + + // Simple vertex/index buffer data + + let vertices: [Vertex; 3] = [ + Vertex { + xyz: [0.25, 0.25, 0.0], + }, + Vertex { + xyz: [0.75, 0.25, 0.0], + }, + Vertex { + xyz: [0.50, 0.75, 0.0], + }, + ]; + + let vertex_stride = mem::size_of::<Vertex>(); + + let indices: [u32; 3] = [0, 1, 2]; + + // Vertex data should be stored in private or managed buffers on discrete GPU systems (AMD, NVIDIA). + // Private buffers are stored entirely in GPU memory and cannot be accessed by the CPU. Managed + // buffers maintain a copy in CPU memory and a copy in GPU memory. + let buffer_opts = MTLResourceOptions::StorageModeManaged; + + let vertex_buffer = device.new_buffer_with_data( + vertices.as_ptr() as *const c_void, + (vertex_stride * vertices.len()) as u64, + buffer_opts, + ); + + let index_buffer = device.new_buffer_with_data( + indices.as_ptr() as *const c_void, + (mem::size_of::<u32>() * indices.len()) as u64, + buffer_opts, + ); + + // Build an acceleration structure using our vertex and index buffers containing the single triangle. + let acceleration_structure = mps::TriangleAccelerationStructure::from_device(&device) + .expect("Failed to create acceleration structure"); + + acceleration_structure.set_vertex_buffer(Some(&vertex_buffer)); + acceleration_structure.set_vertex_stride(vertex_stride as u64); + acceleration_structure.set_index_buffer(Some(&index_buffer)); + acceleration_structure.set_index_type(mps::MPSDataType::UInt32); + acceleration_structure.set_triangle_count(1); + acceleration_structure.set_usage(mps::MPSAccelerationStructureUsage::None); + acceleration_structure.rebuild(); + + let ray_intersector = + mps::RayIntersector::from_device(&device).expect("Failed to create ray intersector"); + + ray_intersector.set_ray_stride(mem::size_of::<Ray>() as u64); + ray_intersector.set_ray_data_type(mps::MPSRayDataType::OriginMinDistanceDirectionMaxDistance); + ray_intersector.set_intersection_stride(mem::size_of::<Intersection>() as u64); + ray_intersector.set_intersection_data_type( + mps::MPSIntersectionDataType::DistancePrimitiveIndexCoordinates, + ); + + // Create a buffer to hold generated rays and intersection results + let ray_count = 1024; + let ray_buffer = device.new_buffer( + (mem::size_of::<Ray>() * ray_count) as u64, + MTLResourceOptions::StorageModePrivate, + ); + + let intersection_buffer = device.new_buffer( + (mem::size_of::<Intersection>() * ray_count) as u64, + MTLResourceOptions::StorageModePrivate, + ); + + // Run the compute shader to generate rays + let encoder = command_buffer.new_compute_command_encoder(); + encoder.set_buffer(0, Some(&ray_buffer), 0); + encoder.set_compute_pipeline_state(&generate_rays_pipeline); + encoder.dispatch_thread_groups( + MTLSize { + width: 4, + height: 4, + depth: 1, + }, + MTLSize { + width: 8, + height: 8, + depth: 1, + }, + ); + encoder.end_encoding(); + + // Intersect rays with triangles inside acceleration structure + ray_intersector.encode_intersection_to_command_buffer( + &command_buffer, + mps::MPSIntersectionType::Nearest, + &ray_buffer, + 0, + &intersection_buffer, + 0, + ray_count as u64, + &acceleration_structure, + ); + + command_buffer.commit(); + command_buffer.wait_until_completed(); + + println!("Done"); +} + +fn create_pipeline(func: &str, library: &LibraryRef, device: &DeviceRef) -> ComputePipelineState { + // Create compute pipelines will will execute code on the GPU + let compute_descriptor = ComputePipelineDescriptor::new(); + + // Set to YES to allow compiler to make certain optimizations + compute_descriptor.set_thread_group_size_is_multiple_of_thread_execution_width(true); + + let function = library.get_function(func, None).unwrap(); + compute_descriptor.set_compute_function(Some(&function)); + + let pipeline = device + .new_compute_pipeline_state(&compute_descriptor) + .unwrap(); + + pipeline +} diff --git a/third_party/rust/metal/examples/mps/shaders.metal b/third_party/rust/metal/examples/mps/shaders.metal new file mode 100644 index 0000000000..d824d70d1b --- /dev/null +++ b/third_party/rust/metal/examples/mps/shaders.metal @@ -0,0 +1,26 @@ +// +// Created by Sergey Reznik on 9/15/18. +// Copyright © 2018 Serhii Rieznik. All rights reserved. +// + +// Taken from https://github.com/sergeyreznik/metal-ray-tracer/tree/part-1/source/Shaders +// MIT License https://github.com/sergeyreznik/metal-ray-tracer/blob/part-1/LICENSE + +#include <MetalPerformanceShaders/MetalPerformanceShaders.h> + +using Ray = MPSRayOriginMinDistanceDirectionMaxDistance; +using Intersection = MPSIntersectionDistancePrimitiveIndexCoordinates; + +kernel void generateRays( + device Ray* rays [[buffer(0)]], + uint2 coordinates [[thread_position_in_grid]], + uint2 size [[threads_per_grid]]) +{ + float2 uv = float2(coordinates) / float2(size - 1); + + uint rayIndex = coordinates.x + coordinates.y * size.x; + rays[rayIndex].origin = MPSPackedFloat3(uv.x, uv.y, -1.0); + rays[rayIndex].direction = MPSPackedFloat3(0.0, 0.0, 1.0); + rays[rayIndex].minDistance = 0.0f; + rays[rayIndex].maxDistance = 2.0f; +} diff --git a/third_party/rust/metal/examples/mps/shaders.metallib b/third_party/rust/metal/examples/mps/shaders.metallib Binary files differnew file mode 100644 index 0000000000..2cecf7b837 --- /dev/null +++ b/third_party/rust/metal/examples/mps/shaders.metallib diff --git a/third_party/rust/metal/examples/raytracing/README.md b/third_party/rust/metal/examples/raytracing/README.md new file mode 100644 index 0000000000..0071e59747 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/README.md @@ -0,0 +1,11 @@ +## Raytracing + +A good showcase of Metal 3 raytracing features. + +![Screenshot of the final render](./screenshot.png) + +## To Run + +``` +cargo run --example raytracing +``` diff --git a/third_party/rust/metal/examples/raytracing/camera.rs b/third_party/rust/metal/examples/raytracing/camera.rs new file mode 100644 index 0000000000..5548445c06 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/camera.rs @@ -0,0 +1,20 @@ +use glam::f32::Vec4; + +#[repr(C)] +pub struct Camera { + pub position: Vec4, + pub right: Vec4, + pub up: Vec4, + pub forward: Vec4, +} + +impl Camera { + pub fn new() -> Self { + Self { + position: Vec4::new(0.0, 3.0, 10.0, 0.0), + right: Vec4::new(1.0, 0.0, 0.0, 0.0), + up: Vec4::new(0.0, 1.0, 0.0, 0.0), + forward: Vec4::new(0.0, 0.0, -1.0, 0.0), + } + } +} diff --git a/third_party/rust/metal/examples/raytracing/geometry.rs b/third_party/rust/metal/examples/raytracing/geometry.rs new file mode 100644 index 0000000000..93fdc196d1 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/geometry.rs @@ -0,0 +1,448 @@ +use std::{ + mem::{size_of, transmute}, + sync::Arc, +}; + +use glam::{ + f32::{Mat4, Vec3, Vec4}, + Vec4Swizzles, +}; + +use metal::*; + +pub const GEOMETRY_MASK_TRIANGLE: u32 = 1; +pub const GEOMETRY_MASK_SPHERE: u32 = 2; +pub const GEOMETRY_MASK_LIGHT: u32 = 4; + +pub const FACE_MASK_NONE: u16 = 0; +pub const FACE_MASK_NEGATIVE_X: u16 = 1 << 0; +pub const FACE_MASK_POSITIVE_X: u16 = 1 << 1; +pub const FACE_MASK_NEGATIVE_Y: u16 = 1 << 2; +pub const FACE_MASK_POSITIVE_Y: u16 = 1 << 3; +pub const FACE_MASK_NEGATIVE_Z: u16 = 1 << 4; +pub const FACE_MASK_POSITIVE_Z: u16 = 1 << 5; +pub const FACE_MASK_ALL: u16 = (1 << 6) - 1; + +pub trait Geometry { + fn upload_to_buffers(&mut self) { + todo!() + } + fn clear(&mut self) { + todo!() + } + fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { + todo!() + } + fn get_resources(&self) -> Vec<Resource> { + todo!() + } + fn get_intersection_function_name(&self) -> Option<&str> { + None + } +} + +pub fn compute_triangle_normal(v0: &Vec3, v1: &Vec3, v2: &Vec3) -> Vec3 { + let e1 = Vec3::normalize(*v1 - *v0); + let e2 = Vec3::normalize(*v2 - *v0); + return Vec3::cross(e1, e2); +} + +#[derive(Default)] +#[repr(C)] +pub struct Triangle { + pub normals: [Vec4; 3], + pub colours: [Vec4; 3], +} + +pub fn get_managed_buffer_storage_mode() -> MTLResourceOptions { + return MTLResourceOptions::StorageModeManaged; +} + +pub struct TriangleGeometry { + pub device: Device, + pub name: String, + pub index_buffer: Option<Buffer>, + pub vertex_position_buffer: Option<Buffer>, + pub vertex_normal_buffer: Option<Buffer>, + pub vertex_colour_buffer: Option<Buffer>, + pub per_primitive_data_buffer: Option<Buffer>, + pub indices: Vec<u16>, + pub vertices: Vec<Vec4>, + pub normals: Vec<Vec4>, + pub colours: Vec<Vec4>, + pub triangles: Vec<Triangle>, +} + +impl TriangleGeometry { + pub fn new(device: Device, name: String) -> Self { + Self { + device, + name, + index_buffer: None, + vertex_position_buffer: None, + vertex_normal_buffer: None, + vertex_colour_buffer: None, + per_primitive_data_buffer: None, + indices: Vec::new(), + vertices: Vec::new(), + normals: Vec::new(), + colours: Vec::new(), + triangles: Vec::new(), + } + } + + pub fn add_cube_face_with_cube_vertices( + &mut self, + cube_vertices: &[Vec3], + colour: Vec3, + i0: u16, + i1: u16, + i2: u16, + i3: u16, + inward_normals: bool, + ) { + let v0 = cube_vertices[i0 as usize]; + let v1 = cube_vertices[i1 as usize]; + let v2 = cube_vertices[i2 as usize]; + let v3 = cube_vertices[i3 as usize]; + + let n0 = compute_triangle_normal(&v0, &v1, &v2) * if inward_normals { -1f32 } else { 1f32 }; + let n1 = compute_triangle_normal(&v0, &v2, &v3) * if inward_normals { -1f32 } else { 1f32 }; + + let first_index = self.indices.len(); + let base_index = self.vertices.len() as u16; + + self.indices.push(base_index + 0); + self.indices.push(base_index + 1); + self.indices.push(base_index + 2); + self.indices.push(base_index + 0); + self.indices.push(base_index + 2); + self.indices.push(base_index + 3); + + self.vertices.push(From::from((v0, 0.0))); + self.vertices.push(From::from((v1, 0.0))); + self.vertices.push(From::from((v2, 0.0))); + self.vertices.push(From::from((v3, 0.0))); + + self.normals + .push(From::from((Vec3::normalize(n0 + n1), 0.0))); + self.normals.push(From::from((n0, 0.0))); + self.normals + .push(From::from((Vec3::normalize(n0 + n1), 0.0))); + self.normals.push(From::from((n1, 0.0))); + + for _ in 0..4 { + self.colours.push(From::from((colour, 0.0))); + } + + for triangle_index in 0..2 { + let mut triangle = Triangle::default(); + for i in 0..3 { + let index = self.indices[first_index + triangle_index * 3 + i]; + triangle.normals[i] = self.normals[index as usize]; + triangle.colours[i] = self.colours[index as usize]; + } + self.triangles.push(triangle); + } + } + + pub fn add_cube_with_faces( + &mut self, + face_mask: u16, + colour: Vec3, + transform: Mat4, + inward_normals: bool, + ) { + let mut cube_vertices = [ + Vec3::new(-0.5, -0.5, -0.5), + Vec3::new(0.5, -0.5, -0.5), + Vec3::new(-0.5, 0.5, -0.5), + Vec3::new(0.5, 0.5, -0.5), + Vec3::new(-0.5, -0.5, 0.5), + Vec3::new(0.5, -0.5, 0.5), + Vec3::new(-0.5, 0.5, 0.5), + Vec3::new(0.5, 0.5, 0.5), + ]; + + for i in 0..8 { + let transformed_vertex = Vec4::from((cube_vertices[i], 1.0)); + let transformed_vertex = transform * transformed_vertex; + cube_vertices[i] = transformed_vertex.xyz(); + } + + const CUBE_INDICES: [[u16; 4]; 6] = [ + [0, 4, 6, 2], + [1, 3, 7, 5], + [0, 1, 5, 4], + [2, 6, 7, 3], + [0, 2, 3, 1], + [4, 5, 7, 6], + ]; + + for face in 0..6 { + if face_mask & (1 << face) != 0 { + self.add_cube_face_with_cube_vertices( + &cube_vertices, + colour, + CUBE_INDICES[face][0], + CUBE_INDICES[face][1], + CUBE_INDICES[face][2], + CUBE_INDICES[face][3], + inward_normals, + ); + } + } + } +} + +impl Geometry for TriangleGeometry { + fn upload_to_buffers(&mut self) { + self.index_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.indices.as_ptr()), + (self.indices.len() * size_of::<u16>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.vertex_position_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.vertices.as_ptr()), + (self.vertices.len() * size_of::<Vec4>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.vertex_normal_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.normals.as_ptr()), + (self.normals.len() * size_of::<Vec4>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.vertex_colour_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.colours.as_ptr()), + (self.colours.len() * size_of::<Vec4>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.per_primitive_data_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.triangles.as_ptr()), + (self.triangles.len() * size_of::<Triangle>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.index_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.index_buffer.as_ref().unwrap().length(), + )); + self.vertex_position_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.vertex_position_buffer.as_ref().unwrap().length(), + )); + self.vertex_normal_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.vertex_normal_buffer.as_ref().unwrap().length(), + )); + self.vertex_colour_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.vertex_colour_buffer.as_ref().unwrap().length(), + )); + self.per_primitive_data_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.per_primitive_data_buffer.as_ref().unwrap().length(), + )); + + self.index_buffer + .as_ref() + .unwrap() + .set_label(&format!("index buffer of {}", self.name)); + self.vertex_position_buffer + .as_ref() + .unwrap() + .set_label(&format!("vertex position buffer of {}", self.name)); + self.vertex_normal_buffer + .as_ref() + .unwrap() + .set_label(&format!("vertex normal buffer of {}", self.name)); + self.vertex_colour_buffer + .as_ref() + .unwrap() + .set_label(&format!("vertex colour buffer of {}", self.name)); + self.per_primitive_data_buffer + .as_ref() + .unwrap() + .set_label(&format!("per primitive data buffer of {}", self.name)); + } + + fn clear(&mut self) { + self.indices.clear(); + self.vertices.clear(); + self.normals.clear(); + self.colours.clear(); + self.triangles.clear(); + } + + fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { + let descriptor = AccelerationStructureTriangleGeometryDescriptor::descriptor(); + + descriptor.set_index_buffer(Some(self.index_buffer.as_ref().unwrap())); + descriptor.set_index_type(MTLIndexType::UInt16); + descriptor.set_vertex_buffer(Some(self.vertex_position_buffer.as_ref().unwrap())); + descriptor.set_vertex_stride(size_of::<Vec4>() as NSUInteger); + descriptor.set_triangle_count((self.indices.len() / 3) as NSUInteger); + descriptor + .set_primitive_data_buffer(Some(self.per_primitive_data_buffer.as_ref().unwrap())); + descriptor.set_primitive_data_stride(size_of::<Triangle>() as NSUInteger); + descriptor.set_primitive_data_element_size(size_of::<Triangle>() as NSUInteger); + From::from(descriptor) + } + + fn get_resources(&self) -> Vec<Resource> { + vec![ + From::from(self.index_buffer.as_ref().unwrap().clone()), + From::from(self.vertex_normal_buffer.as_ref().unwrap().clone()), + From::from(self.vertex_colour_buffer.as_ref().unwrap().clone()), + ] + } +} + +#[repr(C)] +pub struct BoundingBox { + pub min: Vec3, + pub max: Vec3, +} + +#[repr(C)] +pub struct Sphere { + pub origin_radius_squared: Vec4, + pub colour_radius: Vec4, +} + +pub struct SphereGeometry { + pub device: Device, + pub sphere_buffer: Option<Buffer>, + pub bounding_box_buffer: Option<Buffer>, + pub per_primitive_data_buffer: Option<Buffer>, + pub spheres: Vec<Sphere>, +} + +impl SphereGeometry { + pub fn new(device: Device) -> Self { + Self { + device, + sphere_buffer: None, + bounding_box_buffer: None, + per_primitive_data_buffer: None, + spheres: Vec::new(), + } + } + + pub fn add_sphere_with_origin(&mut self, origin: Vec3, radius: f32, colour: Vec3) { + self.spheres.push(Sphere { + origin_radius_squared: Vec4::from((origin, radius * radius)), + colour_radius: Vec4::from((colour, radius)), + }); + } +} + +impl Geometry for SphereGeometry { + fn upload_to_buffers(&mut self) { + self.sphere_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(self.spheres.as_ptr()), + (self.spheres.len() * size_of::<Sphere>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.sphere_buffer + .as_ref() + .unwrap() + .set_label("sphere buffer"); + let mut bounding_boxes = Vec::new(); + for sphere in &self.spheres { + bounding_boxes.push(BoundingBox { + min: sphere.origin_radius_squared.xyz() - sphere.colour_radius.w, + max: sphere.origin_radius_squared.xyz() + sphere.colour_radius.w, + }); + } + self.bounding_box_buffer = Some(unsafe { + self.device.new_buffer_with_data( + transmute(bounding_boxes.as_ptr()), + (bounding_boxes.len() * size_of::<BoundingBox>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ) + }); + self.bounding_box_buffer + .as_ref() + .unwrap() + .set_label("bounding box buffer"); + self.sphere_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.sphere_buffer.as_ref().unwrap().length(), + )); + self.bounding_box_buffer + .as_ref() + .unwrap() + .did_modify_range(NSRange::new( + 0, + self.bounding_box_buffer.as_ref().unwrap().length(), + )); + } + + fn clear(&mut self) { + self.spheres.clear(); + } + + fn get_geometry_descriptor(&self) -> AccelerationStructureGeometryDescriptor { + let descriptor = AccelerationStructureBoundingBoxGeometryDescriptor::descriptor(); + descriptor.set_bounding_box_buffer(Some(self.bounding_box_buffer.as_ref().unwrap())); + descriptor.set_bounding_box_count(self.spheres.len() as NSUInteger); + descriptor.set_primitive_data_buffer(Some(&self.sphere_buffer.as_ref().unwrap())); + descriptor.set_primitive_data_stride(size_of::<Sphere>() as NSUInteger); + descriptor.set_primitive_data_element_size(size_of::<Sphere>() as NSUInteger); + From::from(descriptor) + } + + fn get_resources(&self) -> Vec<Resource> { + return vec![From::from(self.sphere_buffer.as_ref().unwrap().clone())]; + } + + fn get_intersection_function_name(&self) -> Option<&str> { + Some("sphereIntersectionFunction") + } +} + +pub struct GeometryInstance { + pub geometry: Arc<dyn Geometry>, + pub transform: Mat4, + pub mask: u32, + pub index_in_scene: NSUInteger, +} + +#[repr(C)] +pub struct AreaLight { + pub position: Vec4, + pub forward: Vec4, + pub right: Vec4, + pub up: Vec4, + pub colour: Vec4, +} diff --git a/third_party/rust/metal/examples/raytracing/main.rs b/third_party/rust/metal/examples/raytracing/main.rs new file mode 100644 index 0000000000..68eaf3df59 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/main.rs @@ -0,0 +1,87 @@ +extern crate objc; + +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; +use metal::*; +use objc::{rc::autoreleasepool, runtime::YES}; +use std::mem; +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, + platform::macos::WindowExtMacOS, +}; + +pub mod camera; +pub mod geometry; +pub mod renderer; +pub mod scene; + +fn find_raytracing_supporting_device() -> Device { + for device in Device::all() { + if !device.supports_raytracing() { + continue; + } + if device.is_low_power() { + continue; + } + return device; + } + + panic!("No device in this machine supports raytracing!") +} + +fn main() { + let events_loop = winit::event_loop::EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal Raytracing Example".to_string()) + .build(&events_loop) + .unwrap(); + + let device = find_raytracing_supporting_device(); + + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::RGBA16Float); + layer.set_presents_with_transaction(false); + + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + + let draw_size = window.inner_size(); + let cg_size = CGSize::new(draw_size.width as f64, draw_size.height as f64); + layer.set_drawable_size(cg_size); + + let mut renderer = renderer::Renderer::new(device); + renderer.window_resized(cg_size); + + events_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + *control_flow = ControlFlow::Poll; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + let size = CGSize::new(size.width as f64, size.height as f64); + layer.set_drawable_size(size); + renderer.window_resized(size); + } + _ => (), + }, + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + renderer.draw(&layer); + } + _ => {} + } + }); + }); +} diff --git a/third_party/rust/metal/examples/raytracing/renderer.rs b/third_party/rust/metal/examples/raytracing/renderer.rs new file mode 100644 index 0000000000..f483d3e0a8 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/renderer.rs @@ -0,0 +1,512 @@ +use core_graphics_types::{base::CGFloat, geometry::CGSize}; +use std::{ + collections::BTreeMap, + ffi::c_void, + mem::{size_of, transmute}, + ops::Index, + sync::{Arc, Condvar, Mutex}, +}; + +use glam::{Vec3, Vec4, Vec4Swizzles}; +use rand::{thread_rng, RngCore}; + +use metal::{foreign_types::ForeignType, *}; + +use crate::{camera::Camera, geometry::get_managed_buffer_storage_mode, scene::Scene}; + +#[repr(C)] +struct Uniforms { + pub width: u32, + pub height: u32, + pub frame_index: u32, + pub light_count: u32, + pub camera: Camera, +} + +pub const MAX_FRAMES_IN_FLIGHT: NSUInteger = 3; +pub const ALIGNED_UNIFORMS_SIZE: NSUInteger = (size_of::<Uniforms>() as NSUInteger + 255) & !255; +pub const UNIFORM_BUFFER_SIZE: NSUInteger = MAX_FRAMES_IN_FLIGHT * ALIGNED_UNIFORMS_SIZE; + +#[derive(Clone)] +struct Semaphore { + data: Arc<(Mutex<usize>, Condvar)>, +} + +impl Semaphore { + fn new(capacity: usize) -> Self { + Self { + data: Arc::new((Mutex::new(capacity), Condvar::new())), + } + } + + fn acquire(&self) { + let mut value = self.data.0.lock().unwrap(); + while *value == 0 { + value = self.data.1.wait(value).unwrap(); + } + *value -= 1; + } + + fn release(&self) { + let mut value = self.data.0.lock().unwrap(); + *value += 1; + self.data.1.notify_one(); + } +} + +pub struct Renderer { + pub device: Device, + pub scene: Scene, + pub uniform_buffer: Buffer, + pub resource_buffer: Buffer, + pub instance_acceleration_structure: AccelerationStructure, + pub accumulation_targets: [Texture; 2], + pub random_texture: Texture, + pub frame_index: NSUInteger, + pub uniform_buffer_index: NSUInteger, + pub uniform_buffer_offset: NSUInteger, + pub size: CGSize, + semaphore: Semaphore, + pub queue: CommandQueue, + instance_buffer: Buffer, + intersection_function_table: IntersectionFunctionTable, + primitive_acceleration_structures: Vec<AccelerationStructure>, + raytracing_pipeline: ComputePipelineState, + copy_pipeline: RenderPipelineState, +} + +impl Renderer { + pub fn new(device: Device) -> Self { + let scene = Scene::new(device.clone()); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/raytracing/shaders.metallib"); + let library = device.new_library_with_file(library_path).unwrap(); + let queue = device.new_command_queue(); + + let buffer_data = [0u8; UNIFORM_BUFFER_SIZE as usize]; + let uniform_buffer = device.new_buffer_with_data( + buffer_data.as_ptr() as *const c_void, + UNIFORM_BUFFER_SIZE, + get_managed_buffer_storage_mode(), + ); + uniform_buffer.set_label("uniform buffer"); + let resources_stride = { + let mut max = 0; + for geometry in &scene.geometries { + let s = geometry.get_resources().len(); + if s > max { + max = s; + } + } + max + }; + let mut resource_buffer_data = vec![0u64; resources_stride * scene.geometries.len()]; + for geometry_index in 0..scene.geometries.len() { + let geometry = scene.geometries[geometry_index].as_ref(); + let resource_buffer_begin_index = resources_stride * geometry_index; + let resources = geometry.get_resources(); + + for argument_index in 0..resources.len() { + let resource_buffer_index = resource_buffer_begin_index + argument_index; + let resource = resources[argument_index].clone(); + resource_buffer_data[resource_buffer_index] = + if resource.conforms_to_protocol::<MTLBuffer>().unwrap() { + let buffer = unsafe { Buffer::from_ptr(transmute(resource.into_ptr())) }; + buffer.gpu_address() + } else if resource.conforms_to_protocol::<MTLTexture>().unwrap() { + let texture = unsafe { Texture::from_ptr(transmute(resource.into_ptr())) }; + texture.gpu_resource_id()._impl + } else { + panic!("Unexpected resource!") + } + } + } + let resource_buffer = device.new_buffer_with_data( + resource_buffer_data.as_ptr() as *const c_void, + (resource_buffer_data.len() * size_of::<u64>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ); + resource_buffer.set_label("resource buffer"); + resource_buffer.did_modify_range(NSRange::new(0, resource_buffer.length())); + + let mut primitive_acceleration_structures = Vec::new(); + for i in 0..scene.geometries.len() { + let mesh = scene.geometries[i].as_ref(); + let geometry_descriptor = mesh.get_geometry_descriptor(); + geometry_descriptor.set_intersection_function_table_offset(i as NSUInteger); + let geometry_descriptors = Array::from_owned_slice(&[geometry_descriptor]); + let accel_descriptor = PrimitiveAccelerationStructureDescriptor::descriptor(); + accel_descriptor.set_geometry_descriptors(&geometry_descriptors); + let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor); + primitive_acceleration_structures.push( + Self::new_acceleration_structure_with_descriptor( + &device, + &queue, + &accel_descriptor, + ), + ); + } + + let mut instance_descriptors = vec![ + MTLAccelerationStructureInstanceDescriptor::default(); + scene.geometry_instances.len() + ]; + for instance_index in 0..scene.geometry_instances.len() { + let instance = scene.geometry_instances[instance_index].as_ref(); + let geometry_index = instance.index_in_scene; + instance_descriptors[instance_index].acceleration_structure_index = + geometry_index as u32; + instance_descriptors[instance_index].options = + if instance.geometry.get_intersection_function_name().is_none() { + MTLAccelerationStructureInstanceOptions::Opaque + } else { + MTLAccelerationStructureInstanceOptions::None + }; + instance_descriptors[instance_index].intersection_function_table_offset = 0; + instance_descriptors[instance_index].mask = instance.mask as u32; + for column in 0..4 { + for row in 0..3 { + instance_descriptors[instance_index].transformation_matrix[column][row] = + *instance.transform.col(column).index(row); + } + } + } + let instance_buffer = device.new_buffer_with_data( + instance_descriptors.as_ptr() as *const c_void, + (size_of::<MTLAccelerationStructureInstanceDescriptor>() + * scene.geometry_instances.len()) as NSUInteger, + get_managed_buffer_storage_mode(), + ); + instance_buffer.set_label("instance buffer"); + instance_buffer.did_modify_range(NSRange::new(0, instance_buffer.length())); + + let accel_descriptor = InstanceAccelerationStructureDescriptor::descriptor(); + accel_descriptor.set_instanced_acceleration_structures(&Array::from_owned_slice( + &primitive_acceleration_structures, + )); + accel_descriptor.set_instance_count(scene.geometry_instances.len() as NSUInteger); + accel_descriptor.set_instance_descriptor_buffer(&instance_buffer); + let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor); + let instance_acceleration_structure = + Self::new_acceleration_structure_with_descriptor(&device, &queue, &accel_descriptor); + + let mut intersection_functions = BTreeMap::<String, Function>::new(); + for geometry in &scene.geometries { + if let Some(name) = geometry.get_intersection_function_name() { + if !intersection_functions.contains_key(name) { + let intersection_function = Self::new_specialised_function_with_name( + &library, + resources_stride as u32, + name, + ); + intersection_functions.insert(name.to_string(), intersection_function); + } + } + } + let raytracing_function = Self::new_specialised_function_with_name( + &library, + resources_stride as u32, + "raytracingKernel", + ); + let intersection_function_array: Vec<&FunctionRef> = intersection_functions + .values() + .map(|f| -> &FunctionRef { f }) + .collect(); + let raytracing_pipeline = Self::new_compute_pipeline_state_with_function( + &device, + &raytracing_function, + &intersection_function_array, + ); + let intersection_function_table_descriptor = IntersectionFunctionTableDescriptor::new(); + intersection_function_table_descriptor + .set_function_count(scene.geometries.len() as NSUInteger); + let intersection_function_table = raytracing_pipeline + .new_intersection_function_table_with_descriptor( + &intersection_function_table_descriptor, + ); + for geometry_index in 0..scene.geometries.len() { + let geometry = scene.geometries[geometry_index].as_ref(); + if let Some(intersection_function_name) = geometry.get_intersection_function_name() { + let intersection_function = &intersection_functions[intersection_function_name]; + let handle = raytracing_pipeline + .function_handle_with_function(intersection_function) + .unwrap(); + intersection_function_table.set_function(handle, geometry_index as NSUInteger); + } + } + let render_descriptor = RenderPipelineDescriptor::new(); + render_descriptor + .set_vertex_function(Some(&library.get_function("copyVertex", None).unwrap())); + render_descriptor + .set_fragment_function(Some(&library.get_function("copyFragment", None).unwrap())); + render_descriptor + .color_attachments() + .object_at(0) + .unwrap() + .set_pixel_format(MTLPixelFormat::RGBA16Float); + let copy_pipeline = device + .new_render_pipeline_state(&render_descriptor) + .unwrap(); + + let texture_descriptor = Self::create_target_descriptor(1024, 1024); + let accumulation_targets = [ + device.new_texture(&texture_descriptor), + device.new_texture(&texture_descriptor), + ]; + let random_texture = device.new_texture(&texture_descriptor); + + Self { + device, + scene, + uniform_buffer, + resource_buffer, + instance_acceleration_structure, + accumulation_targets, + random_texture, + frame_index: 0, + uniform_buffer_index: 0, + uniform_buffer_offset: 0, + size: CGSize::new(1024 as CGFloat, 1024 as CGFloat), + semaphore: Semaphore::new((MAX_FRAMES_IN_FLIGHT - 2) as usize), + instance_buffer, + queue, + intersection_function_table, + primitive_acceleration_structures, + raytracing_pipeline, + copy_pipeline, + } + } + + fn create_target_descriptor(width: NSUInteger, height: NSUInteger) -> TextureDescriptor { + let texture_descriptor = TextureDescriptor::new(); + texture_descriptor.set_pixel_format(MTLPixelFormat::RGBA32Float); + texture_descriptor.set_texture_type(MTLTextureType::D2); + texture_descriptor.set_width(width); + texture_descriptor.set_height(height); + texture_descriptor.set_storage_mode(MTLStorageMode::Private); + texture_descriptor.set_usage(MTLTextureUsage::ShaderRead | MTLTextureUsage::ShaderWrite); + texture_descriptor + } + + pub fn window_resized(&mut self, size: CGSize) { + self.size = size; + let texture_descriptor = + Self::create_target_descriptor(size.width as NSUInteger, size.height as NSUInteger); + self.accumulation_targets[0] = self.device.new_texture(&texture_descriptor); + self.accumulation_targets[1] = self.device.new_texture(&texture_descriptor); + texture_descriptor.set_pixel_format(MTLPixelFormat::R32Uint); + texture_descriptor.set_usage(MTLTextureUsage::ShaderRead); + texture_descriptor.set_storage_mode(MTLStorageMode::Managed); + self.random_texture = self.device.new_texture(&texture_descriptor); + let mut rng = thread_rng(); + let mut random_values = vec![0u32; (size.width * size.height) as usize]; + for v in &mut random_values { + *v = rng.next_u32(); + } + self.random_texture.replace_region( + MTLRegion::new_2d(0, 0, size.width as NSUInteger, size.height as NSUInteger), + 0, + random_values.as_ptr() as *const c_void, + size_of::<u32>() as NSUInteger * size.width as NSUInteger, + ); + self.frame_index = 0; + } + + fn update_uniforms(&mut self) { + self.uniform_buffer_offset = ALIGNED_UNIFORMS_SIZE * self.uniform_buffer_index; + + let uniforms = unsafe { + &mut *((self.uniform_buffer.contents() as *mut u8) + .add(self.uniform_buffer_offset as usize) as *mut Uniforms) + }; + + let position = self.scene.camera.position; + let target = self.scene.camera.forward; + let up = self.scene.camera.up; + + let forward = Vec3::normalize(target.xyz() - position.xyz()); + let right = Vec3::normalize(Vec3::cross(forward, up.xyz())); + let up = Vec3::normalize(Vec3::cross(right, forward)); + + uniforms.camera.position = position; + uniforms.camera.forward = Vec4::from((forward, 0.0)); + uniforms.camera.right = Vec4::from((right, 0.0)); + uniforms.camera.up = Vec4::from((up, 0.0)); + + let field_of_view = 45.0 * (std::f32::consts::PI / 180.0); + let aspect_ratio = self.size.width as f32 / self.size.height as f32; + let image_plane_height = f32::tan(field_of_view / 2.0); + let image_plane_width = aspect_ratio * image_plane_height; + + uniforms.camera.right *= image_plane_width; + uniforms.camera.up *= image_plane_height; + + uniforms.width = self.size.width as u32; + uniforms.height = self.size.height as u32; + + uniforms.frame_index = self.frame_index as u32; + self.frame_index += 1; + + uniforms.light_count = self.scene.lights.len() as u32; + + self.uniform_buffer.did_modify_range(NSRange { + location: self.uniform_buffer_offset, + length: ALIGNED_UNIFORMS_SIZE, + }); + + self.uniform_buffer_index = (self.uniform_buffer_index + 1) % MAX_FRAMES_IN_FLIGHT; + } + + pub fn draw(&mut self, layer: &MetalLayer) { + self.semaphore.acquire(); + self.update_uniforms(); + let command_buffer = self.queue.new_command_buffer(); + let sem = self.semaphore.clone(); + let block = block::ConcreteBlock::new(move |_| { + sem.release(); + }) + .copy(); + command_buffer.add_completed_handler(&block); + let width = self.size.width as NSUInteger; + let height = self.size.height as NSUInteger; + let threads_per_thread_group = MTLSize::new(8, 8, 1); + let thread_groups = MTLSize::new( + (width + threads_per_thread_group.width - 1) / threads_per_thread_group.width, + (height + threads_per_thread_group.height - 1) / threads_per_thread_group.height, + 1, + ); + let compute_encoder = command_buffer.new_compute_command_encoder(); + compute_encoder.set_buffer(0, Some(&self.uniform_buffer), self.uniform_buffer_offset); + compute_encoder.set_buffer(2, Some(&self.instance_buffer), 0); + compute_encoder.set_buffer(3, Some(&self.scene.lights_buffer), 0); + compute_encoder.set_acceleration_structure(4, Some(&self.instance_acceleration_structure)); + compute_encoder.set_intersection_function_table(5, Some(&self.intersection_function_table)); + compute_encoder.set_texture(0, Some(&self.random_texture)); + compute_encoder.set_texture(1, Some(&self.accumulation_targets[0])); + compute_encoder.set_texture(2, Some(&self.accumulation_targets[1])); + for geometry in &self.scene.geometries { + for resource in geometry.get_resources() { + compute_encoder.use_resource(&resource, MTLResourceUsage::Read); + } + } + for primitive_acceleration_structure in &self.primitive_acceleration_structures { + let resource: Resource = From::from(primitive_acceleration_structure.clone()); + compute_encoder.use_resource(&resource, MTLResourceUsage::Read); + } + compute_encoder.set_compute_pipeline_state(&self.raytracing_pipeline); + compute_encoder.dispatch_thread_groups(thread_groups, threads_per_thread_group); + compute_encoder.end_encoding(); + (self.accumulation_targets[0], self.accumulation_targets[1]) = ( + self.accumulation_targets[1].clone(), + self.accumulation_targets[0].clone(), + ); + if let Some(drawable) = layer.next_drawable() { + let render_pass_descriptor = RenderPassDescriptor::new(); + let colour_attachment = render_pass_descriptor + .color_attachments() + .object_at(0) + .unwrap(); + colour_attachment.set_texture(Some(drawable.texture())); + colour_attachment.set_load_action(MTLLoadAction::Clear); + colour_attachment.set_clear_color(MTLClearColor::new(0.0, 0.0, 0.0, 1.0)); + let render_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor); + render_encoder.set_render_pipeline_state(&self.copy_pipeline); + render_encoder.set_fragment_texture(0, Some(&self.accumulation_targets[0])); + render_encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 6); + render_encoder.end_encoding(); + command_buffer.present_drawable(&drawable); + } + command_buffer.commit(); + } + + fn new_acceleration_structure_with_descriptor( + device: &Device, + queue: &CommandQueue, + descriptor: &AccelerationStructureDescriptorRef, + ) -> AccelerationStructure { + let accel_sizes = device.acceleration_structure_sizes_with_descriptor(descriptor); + let acceleration_structure = + device.new_acceleration_structure_with_size(accel_sizes.acceleration_structure_size); + let scratch_buffer = device.new_buffer( + accel_sizes.build_scratch_buffer_size, + MTLResourceOptions::StorageModePrivate, + ); + let command_buffer = queue.new_command_buffer(); + let command_encoder = command_buffer.new_acceleration_structure_command_encoder(); + let compacted_size_buffer = device.new_buffer( + size_of::<u32>() as NSUInteger, + MTLResourceOptions::StorageModeShared, + ); + command_encoder.build_acceleration_structure( + &acceleration_structure, + &descriptor, + &scratch_buffer, + 0, + ); + command_encoder.write_compacted_acceleration_structure_size( + &acceleration_structure, + &compacted_size_buffer, + 0, + ); + command_encoder.end_encoding(); + command_buffer.commit(); + command_buffer.wait_until_completed(); + let compacted_size: *const u32 = unsafe { transmute(compacted_size_buffer.contents()) }; + let compacted_size = unsafe { *compacted_size } as NSUInteger; + let compacted_acceleration_structure = + device.new_acceleration_structure_with_size(compacted_size); + let command_buffer = queue.new_command_buffer(); + let command_encoder = command_buffer.new_acceleration_structure_command_encoder(); + command_encoder.copy_and_compact_acceleration_structure( + &acceleration_structure, + &compacted_acceleration_structure, + ); + command_encoder.end_encoding(); + command_buffer.commit(); + compacted_acceleration_structure + } + + fn new_specialised_function_with_name( + library: &Library, + resources_stride: u32, + name: &str, + ) -> Function { + let constants = FunctionConstantValues::new(); + let resources_stride = resources_stride * size_of::<u64>() as u32; + constants.set_constant_value_at_index( + &resources_stride as *const u32 as *const c_void, + MTLDataType::UInt, + 0, + ); + let v = true; + constants.set_constant_value_at_index( + &v as *const bool as *const c_void, + MTLDataType::Bool, + 1, + ); + constants.set_constant_value_at_index( + &v as *const bool as *const c_void, + MTLDataType::Bool, + 2, + ); + library.get_function(name, Some(constants)).unwrap() + } + + fn new_compute_pipeline_state_with_function( + device: &Device, + function: &Function, + linked_functions: &[&FunctionRef], + ) -> ComputePipelineState { + let linked_functions = { + let lf = LinkedFunctions::new(); + lf.set_functions(linked_functions); + lf + }; + let descriptor = ComputePipelineDescriptor::new(); + descriptor.set_compute_function(Some(function)); + descriptor.set_linked_functions(linked_functions.as_ref()); + descriptor.set_thread_group_size_is_multiple_of_thread_execution_width(true); + device.new_compute_pipeline_state(&descriptor).unwrap() + } +} diff --git a/third_party/rust/metal/examples/raytracing/scene.rs b/third_party/rust/metal/examples/raytracing/scene.rs new file mode 100644 index 0000000000..8ecf8568de --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/scene.rs @@ -0,0 +1,135 @@ +use std::{ffi::c_void, mem::size_of, sync::Arc}; + +use glam::{Mat4, Vec3, Vec4}; +use rand::{thread_rng, Rng}; + +use metal::{Buffer, Device, NSRange, NSUInteger}; + +use super::{camera::Camera, geometry::*}; + +pub struct Scene { + pub device: Device, + pub camera: Camera, + pub geometries: Vec<Arc<dyn Geometry>>, + pub geometry_instances: Vec<Arc<GeometryInstance>>, + pub lights: Vec<AreaLight>, + pub lights_buffer: Buffer, +} + +impl Scene { + pub fn new(device: Device) -> Self { + let mut geometries = Vec::<Arc<dyn Geometry>>::new(); + let mut light_mesh = TriangleGeometry::new(device.clone(), "light".to_string()); + let transform = Mat4::from_translation(Vec3::new(0.0, 1.0, 0.0)) + * Mat4::from_scale(Vec3::new(0.5, 1.98, 0.5)); + light_mesh.add_cube_with_faces( + FACE_MASK_POSITIVE_Y, + Vec3::new(1.0, 1.0, 1.0), + transform, + true, + ); + light_mesh.upload_to_buffers(); + let light_mesh = Arc::new(light_mesh); + geometries.push(light_mesh.clone()); + + let mut geometry_mesh = TriangleGeometry::new(device.clone(), "geometry".to_string()); + let transform = Mat4::from_translation(Vec3::new(0.0, 1.0, 0.0)) + * Mat4::from_scale(Vec3::new(2.0, 2.0, 2.0)); + geometry_mesh.add_cube_with_faces( + FACE_MASK_NEGATIVE_Y | FACE_MASK_POSITIVE_Y | FACE_MASK_NEGATIVE_Z, + Vec3::new(0.725, 0.71, 0.68), + transform, + true, + ); + geometry_mesh.add_cube_with_faces( + FACE_MASK_NEGATIVE_X, + Vec3::new(0.63, 0.065, 0.05), + transform, + true, + ); + geometry_mesh.add_cube_with_faces( + FACE_MASK_POSITIVE_X, + Vec3::new(0.14, 0.45, 0.091), + transform, + true, + ); + let transform = Mat4::from_translation(Vec3::new(-0.335, 0.6, -0.29)) + * Mat4::from_rotation_y(0.3) + * Mat4::from_scale(Vec3::new(0.6, 1.2, 0.6)); + geometry_mesh.add_cube_with_faces( + FACE_MASK_ALL, + Vec3::new(0.725, 0.71, 0.68), + transform, + false, + ); + geometry_mesh.upload_to_buffers(); + let geometry_mesh = Arc::new(geometry_mesh); + geometries.push(geometry_mesh.clone()); + + let mut sphere_geometry = SphereGeometry::new(device.clone()); + sphere_geometry.add_sphere_with_origin( + Vec3::new(0.3275, 0.3, 0.3725), + 0.3, + Vec3::new(0.725, 0.71, 0.68), + ); + sphere_geometry.upload_to_buffers(); + let sphere_geometry = Arc::new(sphere_geometry); + geometries.push(sphere_geometry.clone()); + + let mut rng = thread_rng(); + let mut geometry_instances = Vec::new(); + let mut lights = Vec::new(); + for y in -1..2 { + for x in -1..2 { + let transform = + Mat4::from_translation(Vec3::new(x as f32 * 2.5, y as f32 * 2.5, 0.0)); + geometry_instances.push(Arc::new(GeometryInstance { + geometry: light_mesh.clone(), + transform, + mask: GEOMETRY_MASK_LIGHT, + index_in_scene: 0, + })); + geometry_instances.push(Arc::new(GeometryInstance { + geometry: geometry_mesh.clone(), + transform, + mask: GEOMETRY_MASK_TRIANGLE, + index_in_scene: 1, + })); + geometry_instances.push(Arc::new(GeometryInstance { + geometry: sphere_geometry.clone(), + transform, + mask: GEOMETRY_MASK_SPHERE, + index_in_scene: 2, + })); + lights.push(AreaLight { + position: Vec4::new(x as f32 * 2.5, y as f32 * 2.5 + 1.98, 0.0, 0.0), + forward: Vec4::new(0.0, -1.0, 0.0, 0.0), + right: Vec4::new(0.25, 0.0, 0.0, 0.0), + up: Vec4::new(0.0, 0.0, 0.25, 0.0), + colour: Vec4::new( + rng.gen_range(0f32..=1.0), + rng.gen_range(0f32..=1.0), + rng.gen_range(0f32..=1.0), + 0.0, + ), + }); + } + } + let lights_buffer = device.new_buffer_with_data( + lights.as_ptr() as *const c_void, + (lights.len() * size_of::<AreaLight>()) as NSUInteger, + get_managed_buffer_storage_mode(), + ); + lights_buffer.did_modify_range(NSRange::new(0, lights_buffer.length())); + lights_buffer.set_label("lights buffer"); + + Self { + device, + camera: Camera::new(), + geometries, + geometry_instances, + lights, + lights_buffer, + } + } +} diff --git a/third_party/rust/metal/examples/raytracing/screenshot.png b/third_party/rust/metal/examples/raytracing/screenshot.png Binary files differnew file mode 100644 index 0000000000..417a1d746d --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/screenshot.png diff --git a/third_party/rust/metal/examples/raytracing/shaders.metal b/third_party/rust/metal/examples/raytracing/shaders.metal new file mode 100644 index 0000000000..54aa2a4f47 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/shaders.metal @@ -0,0 +1,598 @@ +/* +See LICENSE folder for this sample’s licensing information. + +Abstract: +The Metal shaders used for this sample. +*/ + +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +using namespace raytracing; + + +#define GEOMETRY_MASK_TRIANGLE 1 +#define GEOMETRY_MASK_SPHERE 2 +#define GEOMETRY_MASK_LIGHT 4 + +#define GEOMETRY_MASK_GEOMETRY (GEOMETRY_MASK_TRIANGLE | GEOMETRY_MASK_SPHERE) + +#define RAY_MASK_PRIMARY (GEOMETRY_MASK_GEOMETRY | GEOMETRY_MASK_LIGHT) +#define RAY_MASK_SHADOW GEOMETRY_MASK_GEOMETRY +#define RAY_MASK_SECONDARY GEOMETRY_MASK_GEOMETRY + +struct Camera { + vector_float3 position; + vector_float3 right; + vector_float3 up; + vector_float3 forward; +}; + +struct AreaLight { + vector_float3 position; + vector_float3 forward; + vector_float3 right; + vector_float3 up; + vector_float3 color; +}; + +struct Uniforms { + unsigned int width; + unsigned int height; + unsigned int frameIndex; + unsigned int lightCount; + Camera camera; +}; + +struct Sphere { + packed_float3 origin; + float radiusSquared; + packed_float3 color; + float radius; +}; + +struct Triangle { + vector_float3 normals[3]; + vector_float3 colors[3]; +}; + +constant unsigned int resourcesStride [[function_constant(0)]]; +constant bool useIntersectionFunctions [[function_constant(1)]]; +constant bool usePerPrimitiveData [[function_constant(2)]]; +constant bool useResourcesBuffer = !usePerPrimitiveData; + +constant unsigned int primes[] = { + 2, 3, 5, 7, + 11, 13, 17, 19, + 23, 29, 31, 37, + 41, 43, 47, 53, + 59, 61, 67, 71, + 73, 79, 83, 89 +}; + +// Returns the i'th element of the Halton sequence using the d'th prime number as a +// base. The Halton sequence is a low discrepency sequence: the values appear +// random, but are more evenly distributed than a purely random sequence. Each random +// value used to render the image uses a different independent dimension, `d`, +// and each sample (frame) uses a different index `i`. To decorrelate each pixel, +// you can apply a random offset to `i`. +float halton(unsigned int i, unsigned int d) { + unsigned int b = primes[d]; + + float f = 1.0f; + float invB = 1.0f / b; + + float r = 0; + + while (i > 0) { + f = f * invB; + r = r + f * (i % b); + i = i / b; + } + + return r; +} + +// Interpolates the vertex attribute of an arbitrary type across the surface of a triangle +// given the barycentric coordinates and triangle index in an intersection structure. +template<typename T, typename IndexType> +inline T interpolateVertexAttribute(device T *attributes, + IndexType i0, + IndexType i1, + IndexType i2, + float2 uv) { + // Look up value for each vertex. + const T T0 = attributes[i0]; + const T T1 = attributes[i1]; + const T T2 = attributes[i2]; + + // Compute the sum of the vertex attributes weighted by the barycentric coordinates. + // The barycentric coordinates sum to one. + return (1.0f - uv.x - uv.y) * T0 + uv.x * T1 + uv.y * T2; +} + +template<typename T> +inline T interpolateVertexAttribute(thread T *attributes, float2 uv) { + // Look up the value for each vertex. + const T T0 = attributes[0]; + const T T1 = attributes[1]; + const T T2 = attributes[2]; + + // Compute the sum of the vertex attributes weighted by the barycentric coordinates. + // The barycentric coordinates sum to one. + return (1.0f - uv.x - uv.y) * T0 + uv.x * T1 + uv.y * T2; +} + +// Uses the inversion method to map two uniformly random numbers to a 3D +// unit hemisphere, where the probability of a given sample is proportional to the cosine +// of the angle between the sample direction and the "up" direction (0, 1, 0). +inline float3 sampleCosineWeightedHemisphere(float2 u) { + float phi = 2.0f * M_PI_F * u.x; + + float cos_phi; + float sin_phi = sincos(phi, cos_phi); + + float cos_theta = sqrt(u.y); + float sin_theta = sqrt(1.0f - cos_theta * cos_theta); + + return float3(sin_theta * cos_phi, cos_theta, sin_theta * sin_phi); +} + +// Maps two uniformly random numbers to the surface of a 2D area light +// source and returns the direction to this point, the amount of light that travels +// between the intersection point and the sample point on the light source, as well +// as the distance between these two points. + +inline void sampleAreaLight(constant AreaLight & light, + float2 u, + float3 position, + thread float3 & lightDirection, + thread float3 & lightColor, + thread float & lightDistance) +{ + // Map to -1..1 + u = u * 2.0f - 1.0f; + + // Transform into the light's coordinate system. + float3 samplePosition = light.position + + light.right * u.x + + light.up * u.y; + + // Compute the vector from sample point on the light source to intersection point. + lightDirection = samplePosition - position; + + lightDistance = length(lightDirection); + + float inverseLightDistance = 1.0f / max(lightDistance, 1e-3f); + + // Normalize the light direction. + lightDirection *= inverseLightDistance; + + // Start with the light's color. + lightColor = light.color; + + // Light falls off with the inverse square of the distance to the intersection point. + lightColor *= (inverseLightDistance * inverseLightDistance); + + // Light also falls off with the cosine of the angle between the intersection point + // and the light source. + lightColor *= saturate(dot(-lightDirection, light.forward)); +} + +// Aligns a direction on the unit hemisphere such that the hemisphere's "up" direction +// (0, 1, 0) maps to the given surface normal direction. +inline float3 alignHemisphereWithNormal(float3 sample, float3 normal) { + // Set the "up" vector to the normal + float3 up = normal; + + // Find an arbitrary direction perpendicular to the normal, which becomes the + // "right" vector. + float3 right = normalize(cross(normal, float3(0.0072f, 1.0f, 0.0034f))); + + // Find a third vector perpendicular to the previous two, which becomes the + // "forward" vector. + float3 forward = cross(right, up); + + // Map the direction on the unit hemisphere to the coordinate system aligned + // with the normal. + return sample.x * right + sample.y * up + sample.z * forward; +} + +// Return the type for a bounding box intersection function. +struct BoundingBoxIntersection { + bool accept [[accept_intersection]]; // Whether to accept or reject the intersection. + float distance [[distance]]; // Distance from the ray origin to the intersection point. +}; + +// Resources for a piece of triangle geometry. +struct TriangleResources { + device uint16_t *indices; + device float3 *vertexNormals; + device float3 *vertexColors; +}; + +// Resources for a piece of sphere geometry. +struct SphereResources { + device Sphere *spheres; +}; + +/* + Custom sphere intersection function. The [[intersection]] keyword marks this as an intersection + function. The [[bounding_box]] keyword means that this intersection function handles intersecting rays + with bounding box primitives. To create sphere primitives, the sample creates bounding boxes that + enclose the sphere primitives. + + The [[triangle_data]] and [[instancing]] keywords indicate that the intersector that calls this + intersection function returns barycentric coordinates for triangle intersections and traverses + an instance acceleration structure. These keywords must match between the intersection functions, + intersection function table, intersector, and intersection result to ensure that Metal propagates + data correctly between stages. Using fewer tags when possible may result in better performance, + as Metal may need to store less data and pass less data between stages. For example, if you do not + need barycentric coordinates, omitting [[triangle_data]] means Metal can avoid computing and storing + them. + + The arguments to the intersection function contain information about the ray, primitive to be + tested, and so on. The ray intersector provides this datas when it calls the intersection function. + Metal provides other built-in arguments, but this sample doesn't use them. + */ +[[intersection(bounding_box, triangle_data, instancing)]] +BoundingBoxIntersection sphereIntersectionFunction(// Ray parameters passed to the ray intersector below + float3 origin [[origin]], + float3 direction [[direction]], + float minDistance [[min_distance]], + float maxDistance [[max_distance]], + // Information about the primitive. + unsigned int primitiveIndex [[primitive_id]], + unsigned int geometryIndex [[geometry_intersection_function_table_offset]], + // Custom resources bound to the intersection function table. + device void *resources [[buffer(0), function_constant(useResourcesBuffer)]] + ,const device void* perPrimitiveData [[primitive_data]]) +{ + Sphere sphere; + // Look up the resources for this piece of sphere geometry. + if (usePerPrimitiveData) { + // Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureBoundingBoxGeometryDescriptor. + sphere = *(const device Sphere*)perPrimitiveData; + } else + { + device SphereResources& sphereResources = *(device SphereResources *)((device char *)resources + resourcesStride * geometryIndex); + // Get the actual sphere enclosed in this bounding box. + sphere = sphereResources.spheres[primitiveIndex]; + } + + // Check for intersection between the ray and sphere mathematically. + float3 oc = origin - sphere.origin; + + float a = dot(direction, direction); + float b = 2 * dot(oc, direction); + float c = dot(oc, oc) - sphere.radiusSquared; + + float disc = b * b - 4 * a * c; + + BoundingBoxIntersection ret; + + if (disc <= 0.0f) { + // If the ray missed the sphere, return false. + ret.accept = false; + } + else { + // Otherwise, compute the intersection distance. + ret.distance = (-b - sqrt(disc)) / (2 * a); + + // The intersection function must also check whether the intersection distance is + // within the acceptable range. Intersection functions do not run in any particular order, + // so the maximum distance may be different from the one passed into the ray intersector. + ret.accept = ret.distance >= minDistance && ret.distance <= maxDistance; + } + + return ret; +} + +__attribute__((always_inline)) +float3 transformPoint(float3 p, float4x4 transform) { + return (transform * float4(p.x, p.y, p.z, 1.0f)).xyz; +} + +__attribute__((always_inline)) +float3 transformDirection(float3 p, float4x4 transform) { + return (transform * float4(p.x, p.y, p.z, 0.0f)).xyz; +} + +// Main ray tracing kernel. +kernel void raytracingKernel( + uint2 tid [[thread_position_in_grid]], + constant Uniforms & uniforms [[buffer(0)]], + texture2d<unsigned int> randomTex [[texture(0)]], + texture2d<float> prevTex [[texture(1)]], + texture2d<float, access::write> dstTex [[texture(2)]], + device void *resources [[buffer(1), function_constant(useResourcesBuffer)]], + constant MTLAccelerationStructureInstanceDescriptor *instances [[buffer(2)]], + constant AreaLight *areaLights [[buffer(3)]], + instance_acceleration_structure accelerationStructure [[buffer(4)]], + intersection_function_table<triangle_data, instancing> intersectionFunctionTable [[buffer(5)]] +) +{ + // The sample aligns the thread count to the threadgroup size, which means the thread count + // may be different than the bounds of the texture. Test to make sure this thread + // is referencing a pixel within the bounds of the texture. + if (tid.x >= uniforms.width || tid.y >= uniforms.height) return; + + // The ray to cast. + ray ray; + + // Pixel coordinates for this thread. + float2 pixel = (float2)tid; + + // Apply a random offset to the random number index to decorrelate pixels. + unsigned int offset = randomTex.read(tid).x; + + // Add a random offset to the pixel coordinates for antialiasing. + float2 r = float2(halton(offset + uniforms.frameIndex, 0), + halton(offset + uniforms.frameIndex, 1)); + + pixel += r; + + // Map pixel coordinates to -1..1. + float2 uv = (float2)pixel / float2(uniforms.width, uniforms.height); + uv = uv * 2.0f - 1.0f; + + constant Camera & camera = uniforms.camera; + + // Rays start at the camera position. + ray.origin = camera.position; + + // Map normalized pixel coordinates into camera's coordinate system. + ray.direction = normalize(uv.x * camera.right + + uv.y * camera.up + + camera.forward); + + // Don't limit intersection distance. + ray.max_distance = INFINITY; + + // Start with a fully white color. The kernel scales the light each time the + // ray bounces off of a surface, based on how much of each light component + // the surface absorbs. + float3 color = float3(1.0f, 1.0f, 1.0f); + + float3 accumulatedColor = float3(0.0f, 0.0f, 0.0f); + + // Create an intersector to test for intersection between the ray and the geometry in the scene. + intersector<triangle_data, instancing> i; + + // If the sample isn't using intersection functions, provide some hints to Metal for + // better performance. + if (!useIntersectionFunctions) { + i.assume_geometry_type(geometry_type::triangle); + i.force_opacity(forced_opacity::opaque); + } + + typename intersector<triangle_data, instancing>::result_type intersection; + + // Simulate up to three ray bounces. Each bounce propagates light backward along the + // ray's path toward the camera. + for (int bounce = 0; bounce < 3; bounce++) { + // Get the closest intersection, not the first intersection. This is the default, but + // the sample adjusts this property below when it casts shadow rays. + i.accept_any_intersection(false); + + // Check for intersection between the ray and the acceleration structure. If the sample + // isn't using intersection functions, it doesn't need to include one. + if (useIntersectionFunctions) + intersection = i.intersect(ray, accelerationStructure, bounce == 0 ? RAY_MASK_PRIMARY : RAY_MASK_SECONDARY, intersectionFunctionTable); + else + intersection = i.intersect(ray, accelerationStructure, bounce == 0 ? RAY_MASK_PRIMARY : RAY_MASK_SECONDARY); + + // Stop if the ray didn't hit anything and has bounced out of the scene. + if (intersection.type == intersection_type::none) + break; + + unsigned int instanceIndex = intersection.instance_id; + + // Look up the mask for this instance, which indicates what type of geometry the ray hit. + unsigned int mask = instances[instanceIndex].mask; + + // If the ray hit a light source, set the color to white, and stop immediately. + if (mask == GEOMETRY_MASK_LIGHT) { + accumulatedColor = float3(1.0f, 1.0f, 1.0f); + break; + } + + // The ray hit something. Look up the transformation matrix for this instance. + float4x4 objectToWorldSpaceTransform(1.0f); + + for (int column = 0; column < 4; column++) + for (int row = 0; row < 3; row++) + objectToWorldSpaceTransform[column][row] = instances[instanceIndex].transformationMatrix[column][row]; + + // Compute the intersection point in world space. + float3 worldSpaceIntersectionPoint = ray.origin + ray.direction * intersection.distance; + + unsigned primitiveIndex = intersection.primitive_id; + unsigned int geometryIndex = instances[instanceIndex].accelerationStructureIndex; + float2 barycentric_coords = intersection.triangle_barycentric_coord; + + float3 worldSpaceSurfaceNormal = 0.0f; + float3 surfaceColor = 0.0f; + + if (mask & GEOMETRY_MASK_TRIANGLE) { + Triangle triangle; + + float3 objectSpaceSurfaceNormal; + if (usePerPrimitiveData) { + // Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureTriangleGeometryDescriptor. + triangle = *(const device Triangle*)intersection.primitive_data; + } else + { + // The ray hit a triangle. Look up the corresponding geometry's normal and UV buffers. + device TriangleResources & triangleResources = *(device TriangleResources *)((device char *)resources + resourcesStride * geometryIndex); + + triangle.normals[0] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 0]]; + triangle.normals[1] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 1]]; + triangle.normals[2] = triangleResources.vertexNormals[triangleResources.indices[primitiveIndex * 3 + 2]]; + + triangle.colors[0] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 0]]; + triangle.colors[1] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 1]]; + triangle.colors[2] = triangleResources.vertexColors[triangleResources.indices[primitiveIndex * 3 + 2]]; + } + + // Interpolate the vertex normal at the intersection point. + objectSpaceSurfaceNormal = interpolateVertexAttribute(triangle.normals, barycentric_coords); + + // Interpolate the vertex color at the intersection point. + surfaceColor = interpolateVertexAttribute(triangle.colors, barycentric_coords); + + // Transform the normal from object to world space. + worldSpaceSurfaceNormal = normalize(transformDirection(objectSpaceSurfaceNormal, objectToWorldSpaceTransform)); + } + else if (mask & GEOMETRY_MASK_SPHERE) { + Sphere sphere; + if (usePerPrimitiveData) { + // Per-primitive data points to data from the specified buffer as was configured in the MTLAccelerationStructureBoundingBoxGeometryDescriptor. + sphere = *(const device Sphere*)intersection.primitive_data; + } else + { + // The ray hit a sphere. Look up the corresponding sphere buffer. + device SphereResources & sphereResources = *(device SphereResources *)((device char *)resources + resourcesStride * geometryIndex); + sphere = sphereResources.spheres[primitiveIndex]; + } + + // Transform the sphere's origin from object space to world space. + float3 worldSpaceOrigin = transformPoint(sphere.origin, objectToWorldSpaceTransform); + + // Compute the surface normal directly in world space. + worldSpaceSurfaceNormal = normalize(worldSpaceIntersectionPoint - worldSpaceOrigin); + + // The sphere is a uniform color, so you don't need to interpolate the color across the surface. + surfaceColor = sphere.color; + } + + dstTex.write(float4(accumulatedColor, 1.0f), tid); + + // Choose a random light source to sample. + float lightSample = halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 0); + unsigned int lightIndex = min((unsigned int)(lightSample * uniforms.lightCount), uniforms.lightCount - 1); + + // Choose a random point to sample on the light source. + float2 r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 1), + halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 2)); + + float3 worldSpaceLightDirection; + float3 lightColor; + float lightDistance; + + // Sample the lighting between the intersection point and the point on the area light. + sampleAreaLight(areaLights[lightIndex], r, worldSpaceIntersectionPoint, worldSpaceLightDirection, + lightColor, lightDistance); + + // Scale the light color by the cosine of the angle between the light direction and + // surface normal. + lightColor *= saturate(dot(worldSpaceSurfaceNormal, worldSpaceLightDirection)); + + // Scale the light color by the number of lights to compensate for the fact that + // the sample samples only one light source at random. + lightColor *= uniforms.lightCount; + + // Scale the ray color by the color of the surface to simulate the surface absorbing light. + color *= surfaceColor; + + // Compute the shadow ray. The shadow ray checks whether the sample position on the + // light source is visible from the current intersection point. + // If it is, the kernel adds lighting to the output image. + struct ray shadowRay; + + // Add a small offset to the intersection point to avoid intersecting the same + // triangle again. + shadowRay.origin = worldSpaceIntersectionPoint + worldSpaceSurfaceNormal * 1e-3f; + + // Travel toward the light source. + shadowRay.direction = worldSpaceLightDirection; + + // Don't overshoot the light source. + shadowRay.max_distance = lightDistance - 1e-3f; + + // Shadow rays check only whether there is an object between the intersection point + // and the light source. Tell Metal to return after finding any intersection. + i.accept_any_intersection(true); + + if (useIntersectionFunctions) + intersection = i.intersect(shadowRay, accelerationStructure, RAY_MASK_SHADOW, intersectionFunctionTable); + else + intersection = i.intersect(shadowRay, accelerationStructure, RAY_MASK_SHADOW); + + // If there was no intersection, then the light source is visible from the original + // intersection point. Add the light's contribution to the image. + if (intersection.type == intersection_type::none) + accumulatedColor += lightColor * color; + + // Choose a random direction to continue the path of the ray. This causes light to + // bounce between surfaces. An app might evaluate a more complicated equation to + // calculate the amount of light that reflects between intersection points. However, + // all the math in this kernel cancels out because this app assumes a simple diffuse + // BRDF and samples the rays with a cosine distribution over the hemisphere (importance + // sampling). This requires that the kernel only multiply the colors together. This + // sampling strategy also reduces the amount of noise in the output image. + r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 3), + halton(offset + uniforms.frameIndex, 2 + bounce * 5 + 4)); + + float3 worldSpaceSampleDirection = sampleCosineWeightedHemisphere(r); + worldSpaceSampleDirection = alignHemisphereWithNormal(worldSpaceSampleDirection, worldSpaceSurfaceNormal); + + ray.origin = worldSpaceIntersectionPoint + worldSpaceSurfaceNormal * 1e-3f; + ray.direction = worldSpaceSampleDirection; + } + + // Average this frame's sample with all of the previous frames. + if (uniforms.frameIndex > 0) { + float3 prevColor = prevTex.read(tid).xyz; + prevColor *= uniforms.frameIndex; + + accumulatedColor += prevColor; + accumulatedColor /= (uniforms.frameIndex + 1); + } + + dstTex.write(float4(accumulatedColor, 1.0f), tid); +} + +// Screen filling quad in normalized device coordinates. +constant float2 quadVertices[] = { + float2(-1, -1), + float2(-1, 1), + float2( 1, 1), + float2(-1, -1), + float2( 1, 1), + float2( 1, -1) +}; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +// Simple vertex shader that passes through NDC quad positions. +vertex CopyVertexOut copyVertex(unsigned short vid [[vertex_id]]) { + float2 position = quadVertices[vid]; + + CopyVertexOut out; + + out.position = float4(position, 0, 1); + out.uv = position * 0.5f + 0.5f; + + return out; +} + +// Simple fragment shader that copies a texture and applies a simple tonemapping function. +fragment float4 copyFragment(CopyVertexOut in [[stage_in]], + texture2d<float> tex) +{ + constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); + + float3 color = tex.sample(sam, in.uv).xyz; + + // Apply a simple tonemapping function to reduce the dynamic range of the + // input image into a range which the screen can display. + color = color / (1.0f + color); + + return float4(color, 1.0f); +} diff --git a/third_party/rust/metal/examples/raytracing/shaders.metallib b/third_party/rust/metal/examples/raytracing/shaders.metallib Binary files differnew file mode 100644 index 0000000000..0965a64ff5 --- /dev/null +++ b/third_party/rust/metal/examples/raytracing/shaders.metallib diff --git a/third_party/rust/metal/examples/reflection/main.rs b/third_party/rust/metal/examples/reflection/main.rs new file mode 100644 index 0000000000..058199cc35 --- /dev/null +++ b/third_party/rust/metal/examples/reflection/main.rs @@ -0,0 +1,75 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use metal::*; +use objc::rc::autoreleasepool; + +const PROGRAM: &'static str = r" + #include <metal_stdlib> + + using namespace metal; + + typedef struct { + float2 position; + float3 color; + } vertex_t; + + struct ColorInOut { + float4 position [[position]]; + float4 color; + }; + + vertex ColorInOut vs(device vertex_t* vertex_array [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]]) + { + ColorInOut out; + + out.position = float4(float2(vertex_array[vid].position), 0.0, 1.0); + out.color = float4(float3(vertex_array[vid].color), 1.0); + + return out; + } + + fragment float4 ps(ColorInOut in [[stage_in]]) + { + return in.color; + }; +"; + +fn main() { + autoreleasepool(|| { + let device = Device::system_default().expect("no device found"); + + let options = CompileOptions::new(); + let library = device.new_library_with_source(PROGRAM, &options).unwrap(); + let (vs, ps) = ( + library.get_function("vs", None).unwrap(), + library.get_function("ps", None).unwrap(), + ); + + let vertex_desc = VertexDescriptor::new(); + + let desc = RenderPipelineDescriptor::new(); + desc.set_vertex_function(Some(&vs)); + desc.set_fragment_function(Some(&ps)); + desc.set_vertex_descriptor(Some(vertex_desc)); + + println!("{:?}", desc); + + let reflect_options = MTLPipelineOption::ArgumentInfo | MTLPipelineOption::BufferTypeInfo; + let (_, reflection) = device + .new_render_pipeline_state_with_reflection(&desc, reflect_options) + .unwrap(); + + println!("Vertex arguments: "); + let vertex_arguments = reflection.vertex_arguments(); + for index in 0..vertex_arguments.count() { + let argument = vertex_arguments.object_at(index).unwrap(); + println!("{:?}", argument); + } + }); +} diff --git a/third_party/rust/metal/examples/shader-dylib/main.rs b/third_party/rust/metal/examples/shader-dylib/main.rs new file mode 100644 index 0000000000..60b6aa0d0c --- /dev/null +++ b/third_party/rust/metal/examples/shader-dylib/main.rs @@ -0,0 +1,177 @@ +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; + +use metal::*; +use objc::{rc::autoreleasepool, runtime::YES}; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, + platform::macos::WindowExtMacOS, +}; + +use std::mem; + +struct App { + pub _device: Device, + pub command_queue: CommandQueue, + pub layer: MetalLayer, + pub image_fill_cps: ComputePipelineState, + pub width: u32, + pub height: u32, +} + +fn select_device() -> Option<Device> { + let devices = Device::all(); + for device in devices { + if device.supports_dynamic_libraries() { + return Some(device); + } + } + + None +} + +impl App { + fn new(window: &winit::window::Window) -> Self { + let device = select_device().expect("no device found that supports dynamic libraries"); + let command_queue = device.new_command_queue(); + + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + layer.set_presents_with_transaction(false); + layer.set_framebuffer_only(false); + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + let draw_size = window.inner_size(); + layer.set_drawable_size(CGSize::new(draw_size.width as f64, draw_size.height as f64)); + + // compile dynamic lib shader + let dylib_src_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/shader-dylib/test_dylib.metal"); + let install_path = + std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/test_dylib.metallib"); + + let dylib_src = std::fs::read_to_string(dylib_src_path).expect("bad shit"); + let opts = metal::CompileOptions::new(); + opts.set_library_type(MTLLibraryType::Dynamic); + opts.set_install_name(install_path.to_str().unwrap()); + + let lib = device + .new_library_with_source(dylib_src.as_str(), &opts) + .unwrap(); + + // create dylib + let dylib = device.new_dynamic_library(&lib).unwrap(); + dylib.set_label("test_dylib"); + + // optional: serialize binary blob that can be loaded later + let blob_url = String::from("file://") + install_path.to_str().unwrap(); + let url = URL::new_with_string(&blob_url); + dylib.serialize_to_url(&url).unwrap(); + + // create shader that links with dylib + let shader_src_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/shader-dylib/test_shader.metal"); + + let shader_src = std::fs::read_to_string(shader_src_path).expect("bad shit"); + let opts = metal::CompileOptions::new(); + // add dynamic library to link with + let libraries = [dylib.as_ref()]; + opts.set_libraries(&libraries); + + // compile + let shader_lib = device + .new_library_with_source(shader_src.as_str(), &opts) + .unwrap(); + + let func = shader_lib.get_function("test_kernel", None).unwrap(); + + // create pipeline state + // linking occurs here + let image_fill_cps = device + .new_compute_pipeline_state_with_function(&func) + .unwrap(); + + Self { + _device: device, + command_queue, + layer, + image_fill_cps, + width: draw_size.width, + height: draw_size.height, + } + } + + fn resize(&mut self, width: u32, height: u32) { + self.layer + .set_drawable_size(CGSize::new(width as f64, height as f64)); + self.width = width; + self.height = height; + } + + fn draw(&self) { + let drawable = match self.layer.next_drawable() { + Some(drawable) => drawable, + None => return, + }; + + let w = self.image_fill_cps.thread_execution_width(); + let h = self.image_fill_cps.max_total_threads_per_threadgroup() / w; + let threads_per_threadgroup = MTLSize::new(w, h, 1); + let threads_per_grid = MTLSize::new(self.width as _, self.height as _, 1); + + let command_buffer = self.command_queue.new_command_buffer(); + + { + let encoder = command_buffer.new_compute_command_encoder(); + encoder.set_compute_pipeline_state(&self.image_fill_cps); + encoder.set_texture(0, Some(&drawable.texture())); + encoder.dispatch_threads(threads_per_grid, threads_per_threadgroup); + encoder.end_encoding(); + } + + command_buffer.present_drawable(&drawable); + command_buffer.commit(); + } +} + +fn main() { + let events_loop = winit::event_loop::EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal Shader Dylib Example".to_string()) + .build(&events_loop) + .unwrap(); + + let mut app = App::new(&window); + + events_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + *control_flow = ControlFlow::Poll; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + app.resize(size.width, size.height); + } + _ => (), + }, + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + app.draw(); + } + _ => {} + } + }); + }); +} diff --git a/third_party/rust/metal/examples/shader-dylib/test_dylib.metal b/third_party/rust/metal/examples/shader-dylib/test_dylib.metal new file mode 100644 index 0000000000..5faa4a803a --- /dev/null +++ b/third_party/rust/metal/examples/shader-dylib/test_dylib.metal @@ -0,0 +1,8 @@ +#include <metal_stdlib> + +using namespace metal; + +float4 get_color_test(float4 inColor) +{ + return float4(inColor.r, inColor.g, inColor.b, 0); +} diff --git a/third_party/rust/metal/examples/shader-dylib/test_shader.metal b/third_party/rust/metal/examples/shader-dylib/test_shader.metal new file mode 100644 index 0000000000..38203a64a5 --- /dev/null +++ b/third_party/rust/metal/examples/shader-dylib/test_shader.metal @@ -0,0 +1,14 @@ +#include <metal_stdlib> + +using namespace metal; + +extern float4 get_color_test(float4 inColor); + +kernel void test_kernel( + texture2d<float, access::write> image [[texture(0)]], + uint2 coordinates [[thread_position_in_grid]], + uint2 size [[threads_per_grid]]) +{ + float2 uv = float2(coordinates) / float2(size - 1); + image.write(get_color_test(float4(uv, 0.0, 1.0)), coordinates); +} diff --git a/third_party/rust/metal/examples/window/README.md b/third_party/rust/metal/examples/window/README.md new file mode 100644 index 0000000000..62233be356 --- /dev/null +++ b/third_party/rust/metal/examples/window/README.md @@ -0,0 +1,11 @@ +## window + +Renders a spinning triangle to a [winit](https://github.com/rust-windowing/winit) window. + +![Screenshot of the final render](./screenshot.png) + +## To Run + +``` +cargo run --example window +``` diff --git a/third_party/rust/metal/examples/window/main.rs b/third_party/rust/metal/examples/window/main.rs new file mode 100644 index 0000000000..08936e82fc --- /dev/null +++ b/third_party/rust/metal/examples/window/main.rs @@ -0,0 +1,261 @@ +// Copyright 2016 metal-rs developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +extern crate objc; + +use cocoa::{appkit::NSView, base::id as cocoa_id}; +use core_graphics_types::geometry::CGSize; + +use metal::*; +use objc::{rc::autoreleasepool, runtime::YES}; +use std::mem; +use winit::platform::macos::WindowExtMacOS; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::ControlFlow, +}; + +#[repr(C)] +struct Rect { + pub x: f32, + pub y: f32, + pub w: f32, + pub h: f32, +} + +#[repr(C)] +struct Color { + pub r: f32, + pub g: f32, + pub b: f32, + pub a: f32, +} + +#[repr(C)] +struct ClearRect { + pub rect: Rect, + pub color: Color, +} + +fn prepare_pipeline_state<'a>( + device: &DeviceRef, + library: &LibraryRef, + vertex_shader: &str, + fragment_shader: &str, +) -> RenderPipelineState { + let vert = library.get_function(vertex_shader, None).unwrap(); + let frag = library.get_function(fragment_shader, None).unwrap(); + + let pipeline_state_descriptor = RenderPipelineDescriptor::new(); + pipeline_state_descriptor.set_vertex_function(Some(&vert)); + pipeline_state_descriptor.set_fragment_function(Some(&frag)); + let attachment = pipeline_state_descriptor + .color_attachments() + .object_at(0) + .unwrap(); + attachment.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + + attachment.set_blending_enabled(true); + attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add); + attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add); + attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha); + attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::SourceAlpha); + attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); + attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); + + device + .new_render_pipeline_state(&pipeline_state_descriptor) + .unwrap() +} + +fn prepare_render_pass_descriptor(descriptor: &RenderPassDescriptorRef, texture: &TextureRef) { + //descriptor.color_attachments().set_object_at(0, MTLRenderPassColorAttachmentDescriptor::alloc()); + //let color_attachment: MTLRenderPassColorAttachmentDescriptor = unsafe { msg_send![descriptor.color_attachments().0, _descriptorAtIndex:0] };//descriptor.color_attachments().object_at(0); + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + + color_attachment.set_texture(Some(texture)); + color_attachment.set_load_action(MTLLoadAction::Clear); + color_attachment.set_clear_color(MTLClearColor::new(0.2, 0.2, 0.25, 1.0)); + color_attachment.set_store_action(MTLStoreAction::Store); +} + +fn main() { + let events_loop = winit::event_loop::EventLoop::new(); + let size = winit::dpi::LogicalSize::new(800, 600); + + let window = winit::window::WindowBuilder::new() + .with_inner_size(size) + .with_title("Metal Window Example".to_string()) + .build(&events_loop) + .unwrap(); + + let device = Device::system_default().expect("no device found"); + + let layer = MetalLayer::new(); + layer.set_device(&device); + layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); + layer.set_presents_with_transaction(false); + + unsafe { + let view = window.ns_view() as cocoa_id; + view.setWantsLayer(YES); + view.setLayer(mem::transmute(layer.as_ref())); + } + + let draw_size = window.inner_size(); + layer.set_drawable_size(CGSize::new(draw_size.width as f64, draw_size.height as f64)); + + let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples/window/shaders.metallib"); + + let library = device.new_library_with_file(library_path).unwrap(); + let triangle_pipeline_state = + prepare_pipeline_state(&device, &library, "triangle_vertex", "triangle_fragment"); + let clear_rect_pipeline_state = prepare_pipeline_state( + &device, + &library, + "clear_rect_vertex", + "clear_rect_fragment", + ); + + let command_queue = device.new_command_queue(); + //let nc: () = msg_send![command_queue.0, setExecutionEnabled:true]; + + let vbuf = { + let vertex_data = [ + 0.0f32, 0.5, 1.0, 0.0, 0.0, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 0.0, 1.0, + ]; + + device.new_buffer_with_data( + vertex_data.as_ptr() as *const _, + (vertex_data.len() * mem::size_of::<f32>()) as u64, + MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, + ) + }; + + let mut r = 0.0f32; + + let clear_rect = vec![ClearRect { + rect: Rect { + x: -1.0, + y: -1.0, + w: 2.0, + h: 2.0, + }, + color: Color { + r: 0.5, + g: 0.8, + b: 0.5, + a: 1.0, + }, + }]; + + let clear_rect_buffer = device.new_buffer_with_data( + clear_rect.as_ptr() as *const _, + mem::size_of::<ClearRect>() as u64, + MTLResourceOptions::CPUCacheModeDefaultCache | MTLResourceOptions::StorageModeManaged, + ); + + events_loop.run(move |event, _, control_flow| { + autoreleasepool(|| { + *control_flow = ControlFlow::Poll; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64)); + } + _ => (), + }, + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + let p = vbuf.contents(); + let vertex_data = [ + 0.0f32, + 0.5, + 1.0, + 0.0, + 0.0, + -0.5 + (r.cos() / 2. + 0.5), + -0.5, + 0.0, + 1.0, + 0.0, + 0.5 - (r.cos() / 2. + 0.5), + -0.5, + 0.0, + 0.0, + 1.0, + ]; + + unsafe { + std::ptr::copy( + vertex_data.as_ptr(), + p as *mut f32, + (vertex_data.len() * mem::size_of::<f32>()) as usize, + ); + } + + vbuf.did_modify_range(crate::NSRange::new( + 0 as u64, + (vertex_data.len() * mem::size_of::<f32>()) as u64, + )); + + let drawable = match layer.next_drawable() { + Some(drawable) => drawable, + None => return, + }; + + let render_pass_descriptor = RenderPassDescriptor::new(); + + prepare_render_pass_descriptor(&render_pass_descriptor, drawable.texture()); + + let command_buffer = command_queue.new_command_buffer(); + let encoder = + command_buffer.new_render_command_encoder(&render_pass_descriptor); + + encoder.set_scissor_rect(MTLScissorRect { + x: 20, + y: 20, + width: 100, + height: 100, + }); + encoder.set_render_pipeline_state(&clear_rect_pipeline_state); + encoder.set_vertex_buffer(0, Some(&clear_rect_buffer), 0); + encoder.draw_primitives_instanced( + metal::MTLPrimitiveType::TriangleStrip, + 0, + 4, + 1, + ); + let physical_size = window.inner_size(); + encoder.set_scissor_rect(MTLScissorRect { + x: 0, + y: 0, + width: physical_size.width as _, + height: physical_size.height as _, + }); + + encoder.set_render_pipeline_state(&triangle_pipeline_state); + encoder.set_vertex_buffer(0, Some(&vbuf), 0); + encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 3); + encoder.end_encoding(); + + command_buffer.present_drawable(&drawable); + command_buffer.commit(); + + r += 0.01f32; + } + _ => {} + } + }); + }); +} diff --git a/third_party/rust/metal/examples/window/screenshot.png b/third_party/rust/metal/examples/window/screenshot.png Binary files differnew file mode 100644 index 0000000000..9f5eba8ccf --- /dev/null +++ b/third_party/rust/metal/examples/window/screenshot.png diff --git a/third_party/rust/metal/examples/window/shaders.metal b/third_party/rust/metal/examples/window/shaders.metal new file mode 100644 index 0000000000..cc05f5d57e --- /dev/null +++ b/third_party/rust/metal/examples/window/shaders.metal @@ -0,0 +1,97 @@ +#include <metal_stdlib> + +using namespace metal; + +typedef struct { + packed_float2 position; + packed_float3 color; +} vertex_t; + +struct ColorInOut { + float4 position [[position]]; + float4 color; +}; +// vertex shader function +vertex ColorInOut triangle_vertex(const device vertex_t* vertex_array [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]]) +{ + ColorInOut out; + + auto device const &v = vertex_array[vid]; + out.position = float4(v.position.x, v.position.y, 0.0, 1.0); + out.color = float4(v.color.x, v.color.y, v.color.z, 0.2); + + return out; +} + +// fragment shader function +fragment float4 triangle_fragment(ColorInOut in [[stage_in]]) +{ + return in.color; +}; + + +struct Rect { + float x; + float y; + float w; + float h; +}; + +struct Color { + float r; + float g; + float b; + float a; +}; + +struct ClearRect { + Rect rect; + Color color; +}; + +float2 rect_vert( + Rect rect, + uint vid +) { + float2 pos; + + float left = rect.x; + float right = rect.x + rect.w; + float bottom = rect.y; + float top = rect.y + rect.h; + + switch (vid) { + case 0: + pos = float2(right, top); + break; + case 1: + pos = float2(left, top); + break; + case 2: + pos = float2(right, bottom); + break; + case 3: + pos = float2(left, bottom); + break; + } + return pos; +} + +vertex ColorInOut clear_rect_vertex( + const device ClearRect *clear_rect [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]] +) { + ColorInOut out; + float4 pos = float4(rect_vert(clear_rect->rect, vid), 0, 1); + auto col = clear_rect->color; + + out.position = pos; + out.color = float4(col.r, col.g, col.b, col.a); + return out; +} + +fragment float4 clear_rect_fragment(ColorInOut in [[stage_in]]) +{ + return in.color; +}; diff --git a/third_party/rust/metal/examples/window/shaders.metallib b/third_party/rust/metal/examples/window/shaders.metallib Binary files differnew file mode 100644 index 0000000000..a6388fc9bc --- /dev/null +++ b/third_party/rust/metal/examples/window/shaders.metallib diff --git a/third_party/rust/metal/src/accelerator_structure.rs b/third_party/rust/metal/src/accelerator_structure.rs new file mode 100644 index 0000000000..5c8ac4d5d2 --- /dev/null +++ b/third_party/rust/metal/src/accelerator_structure.rs @@ -0,0 +1,348 @@ +// Copyright 2023 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +bitflags! { + #[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLAccelerationStructureInstanceOptions: u32 { + const None = 0; + const DisableTriangleCulling = (1 << 0); + const TriangleFrontFacingWindingCounterClockwise = (1 << 1); + const Opaque = (1 << 2); + const NonOpaque = (1 << 3); + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlaccelerationstructureinstancedescriptortype> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum MTLAccelerationStructureInstanceDescriptorType { + Default = 0, + UserID = 1, + Motion = 2, +} + +#[derive(Clone, Copy, PartialEq, Debug, Default)] +#[repr(C)] +pub struct MTLAccelerationStructureInstanceDescriptor { + pub transformation_matrix: [[f32; 3]; 4], + pub options: MTLAccelerationStructureInstanceOptions, + pub mask: u32, + pub intersection_function_table_offset: u32, + pub acceleration_structure_index: u32, +} + +#[derive(Clone, Copy, PartialEq, Debug, Default)] +#[repr(C)] +pub struct MTLAccelerationStructureUserIDInstanceDescriptor { + pub transformation_matrix: [[f32; 3]; 4], + pub options: MTLAccelerationStructureInstanceOptions, + pub mask: u32, + pub intersection_function_table_offset: u32, + pub acceleration_structure_index: u32, + pub user_id: u32, +} + +pub enum MTLAccelerationStructureDescriptor {} + +foreign_obj_type! { + type CType = MTLAccelerationStructureDescriptor; + pub struct AccelerationStructureDescriptor; + type ParentType = NsObject; +} + +pub enum MTLPrimitiveAccelerationStructureDescriptor {} + +foreign_obj_type! { + type CType = MTLPrimitiveAccelerationStructureDescriptor; + pub struct PrimitiveAccelerationStructureDescriptor; + type ParentType = AccelerationStructureDescriptor; +} + +impl PrimitiveAccelerationStructureDescriptor { + pub fn descriptor() -> Self { + unsafe { + let class = class!(MTLPrimitiveAccelerationStructureDescriptor); + msg_send![class, descriptor] + } + } +} + +impl PrimitiveAccelerationStructureDescriptorRef { + pub fn set_geometry_descriptors( + &self, + descriptors: &ArrayRef<AccelerationStructureGeometryDescriptor>, + ) { + unsafe { msg_send![self, setGeometryDescriptors: descriptors] } + } +} + +pub enum MTLAccelerationStructure {} + +foreign_obj_type! { + type CType = MTLAccelerationStructure; + pub struct AccelerationStructure; + type ParentType = Resource; +} + +pub enum MTLAccelerationStructureGeometryDescriptor {} + +foreign_obj_type! { + type CType = MTLAccelerationStructureGeometryDescriptor; + pub struct AccelerationStructureGeometryDescriptor; + type ParentType = NsObject; +} + +impl AccelerationStructureGeometryDescriptorRef { + pub fn set_opaque(&self, opaque: bool) { + unsafe { msg_send![self, setOpaque: opaque] } + } + pub fn set_primitive_data_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setPrimitiveDataBuffer: buffer] } + } + + pub fn set_primitive_data_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setPrimitiveDataStride: stride] } + } + + pub fn set_primitive_data_element_size(&self, size: NSUInteger) { + unsafe { msg_send![self, setPrimitiveDataElementSize: size] } + } + + pub fn set_intersection_function_table_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setIntersectionFunctionTableOffset: offset] } + } +} + +pub enum MTLAccelerationStructureTriangleGeometryDescriptor {} + +foreign_obj_type! { + type CType = MTLAccelerationStructureTriangleGeometryDescriptor; + pub struct AccelerationStructureTriangleGeometryDescriptor; + type ParentType = AccelerationStructureGeometryDescriptor; +} + +impl AccelerationStructureTriangleGeometryDescriptor { + pub fn descriptor() -> Self { + unsafe { + let class = class!(MTLAccelerationStructureTriangleGeometryDescriptor); + msg_send![class, descriptor] + } + } +} + +impl AccelerationStructureTriangleGeometryDescriptorRef { + pub fn set_index_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setIndexBuffer: buffer] } + } + + pub fn set_index_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setIndexBufferOffset: offset] } + } + + pub fn set_index_type(&self, t: MTLIndexType) { + unsafe { msg_send![self, setIndexType: t] } + } + + pub fn set_vertex_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setVertexBuffer: buffer] } + } + + pub fn set_vertex_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setVertexBufferOffset: offset] } + } + + pub fn set_vertex_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setVertexStride: stride] } + } + + pub fn set_triangle_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setTriangleCount: count] } + } + + pub fn set_vertex_format(&self, format: MTLAttributeFormat) { + unsafe { msg_send![self, setVertexFormat: format] } + } + + pub fn set_transformation_matrix_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setTransformationMatrixBuffer: buffer] } + } + + pub fn set_transformation_matrix_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setTransformationMatrixBufferOffset: offset] } + } +} + +pub enum MTLAccelerationStructureBoundingBoxGeometryDescriptor {} + +foreign_obj_type! { + type CType = MTLAccelerationStructureBoundingBoxGeometryDescriptor; + pub struct AccelerationStructureBoundingBoxGeometryDescriptor; + type ParentType = AccelerationStructureGeometryDescriptor; +} + +impl AccelerationStructureBoundingBoxGeometryDescriptor { + pub fn descriptor() -> Self { + unsafe { + let class = class!(MTLAccelerationStructureBoundingBoxGeometryDescriptor); + msg_send![class, descriptor] + } + } +} + +impl AccelerationStructureBoundingBoxGeometryDescriptorRef { + pub fn set_bounding_box_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setBoundingBoxBuffer: buffer] } + } + + pub fn set_bounding_box_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setBoundingBoxCount: count] } + } +} + +pub enum MTLInstanceAccelerationStructureDescriptor {} + +foreign_obj_type! { + type CType = MTLInstanceAccelerationStructureDescriptor; + pub struct InstanceAccelerationStructureDescriptor; + type ParentType = AccelerationStructureDescriptor; +} + +impl InstanceAccelerationStructureDescriptor { + pub fn descriptor() -> Self { + unsafe { + let class = class!(MTLInstanceAccelerationStructureDescriptor); + msg_send![class, descriptor] + } + } +} + +impl InstanceAccelerationStructureDescriptorRef { + pub fn set_instance_descriptor_type(&self, ty: MTLAccelerationStructureInstanceDescriptorType) { + unsafe { msg_send![self, setInstanceDescriptorType: ty] } + } + + pub fn set_instanced_acceleration_structures( + &self, + instances: &ArrayRef<AccelerationStructure>, + ) { + unsafe { msg_send![self, setInstancedAccelerationStructures: instances] } + } + + pub fn set_instance_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setInstanceCount: count] } + } + + pub fn set_instance_descriptor_buffer(&self, buffer: &BufferRef) { + unsafe { msg_send![self, setInstanceDescriptorBuffer: buffer] } + } + + pub fn set_instance_descriptor_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setInstanceDescriptorBufferOffset: offset] } + } + + pub fn set_instance_descriptor_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setInstanceDescriptorStride: stride] } + } +} + +pub enum MTLAccelerationStructureCommandEncoder {} + +foreign_obj_type! { + type CType = MTLAccelerationStructureCommandEncoder; + pub struct AccelerationStructureCommandEncoder; + type ParentType = CommandEncoder; +} + +impl AccelerationStructureCommandEncoderRef { + pub fn build_acceleration_structure( + &self, + acceleration_structure: &self::AccelerationStructureRef, + descriptor: &self::AccelerationStructureDescriptorRef, + scratch_buffer: &BufferRef, + scratch_buffer_offset: NSUInteger, + ) { + unsafe { + msg_send![ + self, + buildAccelerationStructure: acceleration_structure + descriptor: descriptor + scratchBuffer: scratch_buffer + scratchBufferOffset: scratch_buffer_offset] + } + } + + pub fn write_compacted_acceleration_structure_size( + &self, + acceleration_structure: &AccelerationStructureRef, + to_buffer: &BufferRef, + offset: NSUInteger, + ) { + unsafe { + msg_send![ + self, + writeCompactedAccelerationStructureSize: acceleration_structure + toBuffer: to_buffer + offset: offset + ] + } + } + + pub fn copy_and_compact_acceleration_structure( + &self, + source: &AccelerationStructureRef, + destination: &AccelerationStructureRef, + ) { + unsafe { + msg_send![ + self, + copyAndCompactAccelerationStructure: source + toAccelerationStructure: destination + ] + } + } +} + +pub enum MTLIntersectionFunctionTableDescriptor {} + +foreign_obj_type! { + type CType = MTLIntersectionFunctionTableDescriptor; + pub struct IntersectionFunctionTableDescriptor; + type ParentType = NsObject; +} + +impl IntersectionFunctionTableDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLIntersectionFunctionTableDescriptor); + let this: *mut <Self as ForeignType>::CType = msg_send![class, alloc]; + msg_send![this, init] + } + } +} + +impl IntersectionFunctionTableDescriptorRef { + pub fn set_function_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setFunctionCount: count] } + } +} + +pub enum MTLIntersectionFunctionTable {} + +foreign_obj_type! { + type CType = MTLIntersectionFunctionTable; + pub struct IntersectionFunctionTable; + type ParentType = Resource; +} + +impl IntersectionFunctionTableRef { + pub fn set_function(&self, function: &FunctionHandleRef, index: NSUInteger) { + unsafe { msg_send![self, setFunction: function atIndex: index] } + } +} diff --git a/third_party/rust/metal/src/argument.rs b/third_party/rust/metal/src/argument.rs new file mode 100644 index 0000000000..a85a737858 --- /dev/null +++ b/third_party/rust/metal/src/argument.rs @@ -0,0 +1,342 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::{MTLTextureType, NSUInteger}; +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtldatatype> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLDataType { + None = 0, + + Struct = 1, + Array = 2, + + Float = 3, + Float2 = 4, + Float3 = 5, + Float4 = 6, + + Float2x2 = 7, + Float2x3 = 8, + Float2x4 = 9, + + Float3x2 = 10, + Float3x3 = 11, + Float3x4 = 12, + + Float4x2 = 13, + Float4x3 = 14, + Float4x4 = 15, + + Half = 16, + Half2 = 17, + Half3 = 18, + Half4 = 19, + + Half2x2 = 20, + Half2x3 = 21, + Half2x4 = 22, + + Half3x2 = 23, + Half3x3 = 24, + Half3x4 = 25, + + Half4x2 = 26, + Half4x3 = 27, + Half4x4 = 28, + + Int = 29, + Int2 = 30, + Int3 = 31, + Int4 = 32, + + UInt = 33, + UInt2 = 34, + UInt3 = 35, + UInt4 = 36, + + Short = 37, + Short2 = 38, + Short3 = 39, + Short4 = 40, + + UShort = 41, + UShort2 = 42, + UShort3 = 43, + UShort4 = 44, + + Char = 45, + Char2 = 46, + Char3 = 47, + Char4 = 48, + + UChar = 49, + UChar2 = 50, + UChar3 = 51, + UChar4 = 52, + + Bool = 53, + Bool2 = 54, + Bool3 = 55, + Bool4 = 56, + + Texture = 58, + Sampler = 59, + Pointer = 60, + R8Unorm = 62, + R8Snorm = 63, + R16Unorm = 64, + R16Snorm = 65, + RG8Unorm = 66, + RG8Snorm = 67, + RG16Unorm = 68, + RG16Snorm = 69, + RGBA8Unorm = 70, + RGBA8Unorm_sRGB = 71, + RGBA8Snorm = 72, + RGBA16Unorm = 73, + RGBA16Snorm = 74, + RGB10A2Unorm = 75, + RG11B10Float = 76, + RGB9E5Float = 77, +} + +/// See <https://developer.apple.com/documentation/metal/mtlargumenttype> +#[repr(u64)] +#[deprecated( + note = "Since: iOS 8.0–16.0, iPadOS 8.0–16.0, macOS 10.11–13.0, Mac Catalyst 13.1–16.0, tvOS 9.0–16.0" +)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLArgumentType { + Buffer = 0, + ThreadgroupMemory = 1, + Texture = 2, + Sampler = 3, + ImageblockData = 16, + Imageblock = 17, +} + +/// See <https://developer.apple.com/documentation/metal/mtlargumentaccess> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLArgumentAccess { + ReadOnly = 0, + ReadWrite = 1, + WriteOnly = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstructmember> +pub enum MTLStructMember {} + +foreign_obj_type! { + type CType = MTLStructMember; + pub struct StructMember; +} + +impl StructMemberRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn offset(&self) -> NSUInteger { + unsafe { msg_send![self, offset] } + } + + pub fn data_type(&self) -> MTLDataType { + unsafe { msg_send![self, dataType] } + } + + pub fn struct_type(&self) -> MTLStructType { + unsafe { msg_send![self, structType] } + } + + pub fn array_type(&self) -> MTLArrayType { + unsafe { msg_send![self, arrayType] } + } +} + +pub enum MTLStructMemberArray {} + +foreign_obj_type! { + type CType = MTLStructMemberArray; + pub struct StructMemberArray; +} + +impl StructMemberArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&StructMemberRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn count(&self) -> NSUInteger { + unsafe { msg_send![self, count] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlstructtype> +pub enum MTLStructType {} + +foreign_obj_type! { + type CType = MTLStructType; + pub struct StructType; +} + +impl StructTypeRef { + pub fn members(&self) -> &StructMemberArrayRef { + unsafe { msg_send![self, members] } + } + + pub fn member_from_name(&self, name: &str) -> Option<&StructMemberRef> { + let nsname = crate::nsstring_from_str(name); + + unsafe { msg_send![self, memberByName: nsname] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlarraytype> +pub enum MTLArrayType {} + +foreign_obj_type! { + type CType = MTLArrayType; + pub struct ArrayType; +} + +impl ArrayTypeRef { + pub fn array_length(&self) -> NSUInteger { + unsafe { msg_send![self, arrayLength] } + } + + pub fn stride(&self) -> NSUInteger { + unsafe { msg_send![self, stride] } + } + + pub fn element_type(&self) -> MTLDataType { + unsafe { msg_send![self, elementType] } + } + + pub fn element_struct_type(&self) -> MTLStructType { + unsafe { msg_send![self, elementStructType] } + } + + pub fn element_array_type(&self) -> MTLArrayType { + unsafe { msg_send![self, elementArrayType] } + } +} + +/// <https://developer.apple.com/documentation/metal/mtlargument> +#[deprecated( + note = "Since iOS 8.0–16.0, iPadOS 8.0–16.0, macOS 10.11–13.0, Mac Catalyst 13.1–16.0, tvOS 9.0–16.0" +)] +pub enum MTLArgument {} + +foreign_obj_type! { + type CType = MTLArgument; + pub struct Argument; +} + +impl ArgumentRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn type_(&self) -> MTLArgumentType { + unsafe { msg_send![self, type] } + } + + pub fn access(&self) -> MTLArgumentAccess { + unsafe { msg_send![self, access] } + } + + pub fn index(&self) -> NSUInteger { + unsafe { msg_send![self, index] } + } + + pub fn is_active(&self) -> bool { + unsafe { msg_send_bool![self, isActive] } + } + + pub fn buffer_alignment(&self) -> NSUInteger { + unsafe { msg_send![self, bufferAlignment] } + } + + pub fn buffer_data_size(&self) -> NSUInteger { + unsafe { msg_send![self, bufferDataSize] } + } + + pub fn buffer_data_type(&self) -> MTLDataType { + unsafe { msg_send![self, bufferDataType] } + } + + pub fn buffer_struct_type(&self) -> &StructTypeRef { + unsafe { msg_send![self, bufferStructType] } + } + + pub fn threadgroup_memory_alignment(&self) -> NSUInteger { + unsafe { msg_send![self, threadgroupMemoryAlignment] } + } + + pub fn threadgroup_memory_data_size(&self) -> NSUInteger { + unsafe { msg_send![self, threadgroupMemoryDataSize] } + } + + pub fn texture_type(&self) -> MTLTextureType { + unsafe { msg_send![self, textureType] } + } + + pub fn texture_data_type(&self) -> MTLDataType { + unsafe { msg_send![self, textureDataType] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlargumentdescriptor> +pub enum MTLArgumentDescriptor {} + +foreign_obj_type! { + type CType = MTLArgumentDescriptor; + pub struct ArgumentDescriptor; +} + +impl ArgumentDescriptor { + pub fn new<'a>() -> &'a ArgumentDescriptorRef { + unsafe { + let class = class!(MTLArgumentDescriptor); + msg_send![class, argumentDescriptor] + } + } +} + +impl ArgumentDescriptorRef { + pub fn set_data_type(&self, ty: MTLDataType) { + unsafe { msg_send![self, setDataType: ty] } + } + + pub fn set_index(&self, index: NSUInteger) { + unsafe { msg_send![self, setIndex: index] } + } + + pub fn set_access(&self, access: MTLArgumentAccess) { + unsafe { msg_send![self, setAccess: access] } + } + + pub fn set_array_length(&self, length: NSUInteger) { + unsafe { msg_send![self, setArrayLength: length] } + } + + pub fn set_texture_type(&self, ty: MTLTextureType) { + unsafe { msg_send![self, setTextureType: ty] } + } +} diff --git a/third_party/rust/metal/src/blitpass.rs b/third_party/rust/metal/src/blitpass.rs new file mode 100644 index 0000000000..290cadfc87 --- /dev/null +++ b/third_party/rust/metal/src/blitpass.rs @@ -0,0 +1,102 @@ +use super::*; + +/// See <https://developer.apple.com/documentation/metal/mtlblitpassdescriptor> +pub enum MTLBlitPassDescriptor {} + +foreign_obj_type! { + type CType = MTLBlitPassDescriptor; + pub struct BlitPassDescriptor; +} + +impl BlitPassDescriptor { + /// Creates a default blit command pass descriptor with no attachments. + pub fn new<'a>() -> &'a BlitPassDescriptorRef { + unsafe { msg_send![class!(MTLBlitPassDescriptor), blitPassDescriptor] } + } +} + +impl BlitPassDescriptorRef { + // See <https://developer.apple.com/documentation/metal/mtlblitpassdescriptor> + pub fn sample_buffer_attachments(&self) -> &BlitPassSampleBufferAttachmentDescriptorArrayRef { + unsafe { msg_send![self, sampleBufferAttachments] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlblitpasssamplebufferattachmentdescriptorarray> +pub enum MTLBlitPassSampleBufferAttachmentDescriptorArray {} + +foreign_obj_type! { + type CType = MTLBlitPassSampleBufferAttachmentDescriptorArray; + pub struct BlitPassSampleBufferAttachmentDescriptorArray; +} + +impl BlitPassSampleBufferAttachmentDescriptorArrayRef { + pub fn object_at( + &self, + index: NSUInteger, + ) -> Option<&BlitPassSampleBufferAttachmentDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attachment: Option<&BlitPassSampleBufferAttachmentDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attachment + atIndexedSubscript:index] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlblitpasssamplebufferattachmentdescriptor> +pub enum MTLBlitPassSampleBufferAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLBlitPassSampleBufferAttachmentDescriptor; + pub struct BlitPassSampleBufferAttachmentDescriptor; +} + +impl BlitPassSampleBufferAttachmentDescriptor { + pub fn new() -> Self { + let class = class!(MTLBlitPassSampleBufferAttachmentDescriptor); + unsafe { msg_send![class, new] } + } +} + +impl BlitPassSampleBufferAttachmentDescriptorRef { + pub fn sample_buffer(&self) -> &CounterSampleBufferRef { + unsafe { msg_send![self, sampleBuffer] } + } + + pub fn set_sample_buffer(&self, sample_buffer: &CounterSampleBufferRef) { + unsafe { msg_send![self, setSampleBuffer: sample_buffer] } + } + + pub fn start_of_encoder_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, startOfEncoderSampleIndex] } + } + + pub fn set_start_of_encoder_sample_index(&self, start_of_encoder_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setStartOfEncoderSampleIndex: start_of_encoder_sample_index + ] + } + } + + pub fn end_of_encoder_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, endOfEncoderSampleIndex] } + } + + pub fn set_end_of_encoder_sample_index(&self, end_of_encoder_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setEndOfEncoderSampleIndex: end_of_encoder_sample_index + ] + } + } +} diff --git a/third_party/rust/metal/src/buffer.rs b/third_party/rust/metal/src/buffer.rs new file mode 100644 index 0000000000..8f3108af96 --- /dev/null +++ b/third_party/rust/metal/src/buffer.rs @@ -0,0 +1,71 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +/// See <https://developer.apple.com/documentation/metal/mtlbuffer> +pub enum MTLBuffer {} + +foreign_obj_type! { + type CType = MTLBuffer; + pub struct Buffer; + type ParentType = Resource; +} + +impl BufferRef { + pub fn length(&self) -> u64 { + unsafe { msg_send![self, length] } + } + + pub fn contents(&self) -> *mut std::ffi::c_void { + unsafe { msg_send![self, contents] } + } + + pub fn did_modify_range(&self, range: crate::NSRange) { + unsafe { msg_send![self, didModifyRange: range] } + } + + pub fn new_texture_with_descriptor( + &self, + descriptor: &TextureDescriptorRef, + offset: u64, + bytes_per_row: u64, + ) -> Texture { + unsafe { + msg_send![self, + newTextureWithDescriptor:descriptor + offset:offset + bytesPerRow:bytes_per_row + ] + } + } + + /// Only available on macos(10.15), NOT available on (ios) + pub fn remote_storage_buffer(&self) -> &BufferRef { + unsafe { msg_send![self, remoteStorageBuffer] } + } + + /// Only available on (macos(10.15), NOT available on (ios) + pub fn new_remote_buffer_view_for_device(&self, device: &DeviceRef) -> Buffer { + unsafe { msg_send![self, newRemoteBufferViewForDevice: device] } + } + + pub fn add_debug_marker(&self, name: &str, range: crate::NSRange) { + unsafe { + let name = crate::nsstring_from_str(name); + msg_send![self, addDebugMarker:name range:range] + } + } + + pub fn remove_all_debug_markers(&self) { + unsafe { msg_send![self, removeAllDebugMarkers] } + } + + pub fn gpu_address(&self) -> u64 { + unsafe { msg_send![self, gpuAddress] } + } +} diff --git a/third_party/rust/metal/src/capturedescriptor.rs b/third_party/rust/metal/src/capturedescriptor.rs new file mode 100644 index 0000000000..1e620cc9a0 --- /dev/null +++ b/third_party/rust/metal/src/capturedescriptor.rs @@ -0,0 +1,76 @@ +// Copyright 2020 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use std::path::Path; + +/// See <https://developer.apple.com/documentation/metal/mtlcapturedestination?language=objc> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCaptureDestination { + DeveloperTools = 1, + GpuTraceDocument = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor> +pub enum MTLCaptureDescriptor {} + +foreign_obj_type! { + type CType = MTLCaptureDescriptor; + pub struct CaptureDescriptor; +} + +impl CaptureDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLCaptureDescriptor); + msg_send![class, new] + } + } +} + +impl CaptureDescriptorRef { + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor/3237248-captureobject> + pub fn set_capture_device(&self, device: &DeviceRef) { + unsafe { msg_send![self, setCaptureObject: device] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor/3237248-captureobject> + pub fn set_capture_scope(&self, scope: &CaptureScopeRef) { + unsafe { msg_send![self, setCaptureObject: scope] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor/3237248-captureobject> + pub fn set_capture_command_queue(&self, command_queue: &CommandQueueRef) { + unsafe { msg_send![self, setCaptureObject: command_queue] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor/3237250-outputurl> + pub fn output_url(&self) -> &Path { + let url: &URLRef = unsafe { msg_send![self, outputURL] }; + Path::new(url.path()) + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor/3237250-outputurl> + pub fn set_output_url<P: AsRef<Path>>(&self, output_url: P) { + let output_url_string = String::from("file://") + output_url.as_ref().to_str().unwrap(); + let output_url = URL::new_with_string(&output_url_string); + unsafe { msg_send![self, setOutputURL: output_url] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor?language=objc> + pub fn destination(&self) -> MTLCaptureDestination { + unsafe { msg_send![self, destination] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturedescriptor?language=objc> + pub fn set_destination(&self, destination: MTLCaptureDestination) { + unsafe { msg_send![self, setDestination: destination] } + } +} diff --git a/third_party/rust/metal/src/capturemanager.rs b/third_party/rust/metal/src/capturemanager.rs new file mode 100644 index 0000000000..7c9c407cb1 --- /dev/null +++ b/third_party/rust/metal/src/capturemanager.rs @@ -0,0 +1,113 @@ +// Copyright 2018 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; +use std::ffi::CStr; + +/// See <https://developer.apple.com/documentation/metal/mtlcapturescope> +pub enum MTLCaptureScope {} + +foreign_obj_type! { + type CType = MTLCaptureScope; + pub struct CaptureScope; +} + +impl CaptureScopeRef { + pub fn begin_scope(&self) { + unsafe { msg_send![self, beginScope] } + } + + pub fn end_scope(&self) { + unsafe { msg_send![self, endScope] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcapturemanager> +pub enum MTLCaptureManager {} + +foreign_obj_type! { + type CType = MTLCaptureManager; + pub struct CaptureManager; +} + +impl CaptureManager { + pub fn shared<'a>() -> &'a CaptureManagerRef { + unsafe { + let class = class!(MTLCaptureManager); + msg_send![class, sharedCaptureManager] + } + } +} + +impl CaptureManagerRef { + pub fn new_capture_scope_with_device(&self, device: &DeviceRef) -> CaptureScope { + unsafe { msg_send![self, newCaptureScopeWithDevice: device] } + } + + pub fn new_capture_scope_with_command_queue( + &self, + command_queue: &CommandQueueRef, + ) -> CaptureScope { + unsafe { msg_send![self, newCaptureScopeWithCommandQueue: command_queue] } + } + + pub fn default_capture_scope(&self) -> Option<&CaptureScopeRef> { + unsafe { msg_send![self, defaultCaptureScope] } + } + + pub fn set_default_capture_scope(&self, scope: &CaptureScopeRef) { + unsafe { msg_send![self, setDefaultCaptureScope: scope] } + } + + /// Starts capturing with the capture session defined by a descriptor object. + /// + /// This function will panic if Metal capture is not enabled. Capture can be enabled by + /// either: + /// 1. Running from Xcode + /// 2. Setting the environment variable `METAL_CAPTURE_ENABLED=1` + /// 3. Adding an info.plist file containing the `MetalCaptureEnabled` key set to `YES` + pub fn start_capture(&self, descriptor: &CaptureDescriptorRef) -> Result<(), String> { + unsafe { + Ok(try_objc! { err => + msg_send![self, startCaptureWithDescriptor: descriptor + error: &mut err] + }) + } + } + + pub fn start_capture_with_device(&self, device: &DeviceRef) { + unsafe { msg_send![self, startCaptureWithDevice: device] } + } + + pub fn start_capture_with_command_queue(&self, command_queue: &CommandQueueRef) { + unsafe { msg_send![self, startCaptureWithCommandQueue: command_queue] } + } + + pub fn start_capture_with_scope(&self, scope: &CaptureScopeRef) { + unsafe { msg_send![self, startCaptureWithScope: scope] } + } + + pub fn stop_capture(&self) { + unsafe { msg_send![self, stopCapture] } + } + + pub fn is_capturing(&self) -> bool { + unsafe { msg_send![self, isCapturing] } + } + + /// See <https://developer.apple.com/documentation/metal/mtlcapturemanager/3237260-supportsdestination?language=objc> + pub fn supports_destination(&self, destination: MTLCaptureDestination) -> bool { + unsafe { msg_send![self, supportsDestination: destination] } + } +} diff --git a/third_party/rust/metal/src/commandbuffer.rs b/third_party/rust/metal/src/commandbuffer.rs new file mode 100644 index 0000000000..26fea2e559 --- /dev/null +++ b/third_party/rust/metal/src/commandbuffer.rs @@ -0,0 +1,185 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use block::Block; + +/// See <https://developer.apple.com/documentation/metal/mtlcommandbufferstatus> +#[repr(u32)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCommandBufferStatus { + NotEnqueued = 0, + Enqueued = 1, + Committed = 2, + Scheduled = 3, + Completed = 4, + Error = 5, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcommandbuffererror> +#[repr(u32)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCommandBufferError { + None = 0, + Internal = 1, + Timeout = 2, + PageFault = 3, + Blacklisted = 4, + NotPermitted = 7, + OutOfMemory = 8, + InvalidResource = 9, + Memoryless = 10, + DeviceRemoved = 11, +} + +/// See <https://developer.apple.com/documentation/metal/mtldispatchtype> +#[repr(u32)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLDispatchType { + Serial = 0, + Concurrent = 1, +} + +type CommandBufferHandler<'a> = Block<(&'a CommandBufferRef,), ()>; + +/// See <https://developer.apple.com/documentation/metal/mtlcommandbuffer>. +pub enum MTLCommandBuffer {} + +foreign_obj_type! { + type CType = MTLCommandBuffer; + pub struct CommandBuffer; +} + +impl CommandBufferRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn enqueue(&self) { + unsafe { msg_send![self, enqueue] } + } + + pub fn commit(&self) { + unsafe { msg_send![self, commit] } + } + + pub fn status(&self) -> MTLCommandBufferStatus { + unsafe { msg_send![self, status] } + } + + pub fn present_drawable(&self, drawable: &DrawableRef) { + unsafe { msg_send![self, presentDrawable: drawable] } + } + + pub fn wait_until_completed(&self) { + unsafe { msg_send![self, waitUntilCompleted] } + } + + pub fn wait_until_scheduled(&self) { + unsafe { msg_send![self, waitUntilScheduled] } + } + + pub fn add_completed_handler(&self, block: &CommandBufferHandler) { + unsafe { msg_send![self, addCompletedHandler: block] } + } + + pub fn add_scheduled_handler(&self, block: &CommandBufferHandler) { + unsafe { msg_send![self, addScheduledHandler: block] } + } + + pub fn new_blit_command_encoder(&self) -> &BlitCommandEncoderRef { + unsafe { msg_send![self, blitCommandEncoder] } + } + + pub fn blit_command_encoder_with_descriptor( + &self, + descriptor: &BlitPassDescriptorRef, + ) -> &BlitCommandEncoderRef { + unsafe { msg_send![self, blitCommandEncoderWithDescriptor: descriptor] } + } + + pub fn new_compute_command_encoder(&self) -> &ComputeCommandEncoderRef { + unsafe { msg_send![self, computeCommandEncoder] } + } + + pub fn compute_command_encoder_with_dispatch_type( + &self, + ty: MTLDispatchType, + ) -> &ComputeCommandEncoderRef { + unsafe { msg_send![self, computeCommandEncoderWithDispatchType: ty] } + } + + pub fn compute_command_encoder_with_descriptor( + &self, + descriptor: &ComputePassDescriptorRef, + ) -> &ComputeCommandEncoderRef { + unsafe { msg_send![self, computeCommandEncoderWithDescriptor: descriptor] } + } + + pub fn new_render_command_encoder( + &self, + descriptor: &RenderPassDescriptorRef, + ) -> &RenderCommandEncoderRef { + unsafe { msg_send![self, renderCommandEncoderWithDescriptor: descriptor] } + } + + pub fn new_parallel_render_command_encoder( + &self, + descriptor: &RenderPassDescriptorRef, + ) -> &ParallelRenderCommandEncoderRef { + unsafe { msg_send![self, parallelRenderCommandEncoderWithDescriptor: descriptor] } + } + + pub fn new_acceleration_structure_command_encoder( + &self, + ) -> &AccelerationStructureCommandEncoderRef { + unsafe { msg_send![self, accelerationStructureCommandEncoder] } + } + + pub fn encode_signal_event(&self, event: &EventRef, new_value: u64) { + unsafe { + msg_send![self, + encodeSignalEvent: event + value: new_value + ] + } + } + + pub fn encode_wait_for_event(&self, event: &EventRef, value: u64) { + unsafe { + msg_send![self, + encodeWaitForEvent: event + value: value + ] + } + } + + pub fn push_debug_group(&self, name: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(name); + msg_send![self, pushDebugGroup: nslabel] + } + } + + pub fn pop_debug_group(&self) { + unsafe { msg_send![self, popDebugGroup] } + } +} diff --git a/third_party/rust/metal/src/commandqueue.rs b/third_party/rust/metal/src/commandqueue.rs new file mode 100644 index 0000000000..7c2dda29c9 --- /dev/null +++ b/third_party/rust/metal/src/commandqueue.rs @@ -0,0 +1,44 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +/// See <https://developer.apple.com/documentation/metal/mtlcommandqueue>. +pub enum MTLCommandQueue {} + +foreign_obj_type! { + type CType = MTLCommandQueue; + pub struct CommandQueue; +} + +impl CommandQueueRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn new_command_buffer(&self) -> &CommandBufferRef { + unsafe { msg_send![self, commandBuffer] } + } + + pub fn new_command_buffer_with_unretained_references(&self) -> &CommandBufferRef { + unsafe { msg_send![self, commandBufferWithUnretainedReferences] } + } + + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } +} diff --git a/third_party/rust/metal/src/computepass.rs b/third_party/rust/metal/src/computepass.rs new file mode 100644 index 0000000000..50774d6871 --- /dev/null +++ b/third_party/rust/metal/src/computepass.rs @@ -0,0 +1,103 @@ +use super::*; + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepassdescriptor> +pub enum MTLComputePassDescriptor {} + +foreign_obj_type! { + type CType = MTLComputePassDescriptor; + pub struct ComputePassDescriptor; +} + +impl ComputePassDescriptor { + /// Creates a default compute pass descriptor with no attachments. + pub fn new<'a>() -> &'a ComputePassDescriptorRef { + unsafe { msg_send![class!(MTLComputePassDescriptor), computePassDescriptor] } + } +} + +impl ComputePassDescriptorRef { + pub fn sample_buffer_attachments( + &self, + ) -> &ComputePassSampleBufferAttachmentDescriptorArrayRef { + unsafe { msg_send![self, sampleBufferAttachments] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepasssamplebufferattachmentdescriptorarray> +pub enum MTLComputePassSampleBufferAttachmentDescriptorArray {} + +foreign_obj_type! { + type CType = MTLComputePassSampleBufferAttachmentDescriptorArray; + pub struct ComputePassSampleBufferAttachmentDescriptorArray; +} + +impl ComputePassSampleBufferAttachmentDescriptorArrayRef { + pub fn object_at( + &self, + index: NSUInteger, + ) -> Option<&ComputePassSampleBufferAttachmentDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attachment: Option<&ComputePassSampleBufferAttachmentDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attachment + atIndexedSubscript:index] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepasssamplebufferattachmentdescriptor> +pub enum MTLComputePassSampleBufferAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLComputePassSampleBufferAttachmentDescriptor; + pub struct ComputePassSampleBufferAttachmentDescriptor; +} + +impl ComputePassSampleBufferAttachmentDescriptor { + pub fn new() -> Self { + let class = class!(MTLComputePassSampleBufferAttachmentDescriptor); + unsafe { msg_send![class, new] } + } +} + +impl ComputePassSampleBufferAttachmentDescriptorRef { + pub fn sample_buffer(&self) -> &CounterSampleBufferRef { + unsafe { msg_send![self, sampleBuffer] } + } + + pub fn set_sample_buffer(&self, sample_buffer: &CounterSampleBufferRef) { + unsafe { msg_send![self, setSampleBuffer: sample_buffer] } + } + + pub fn start_of_encoder_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, startOfEncoderSampleIndex] } + } + + pub fn set_start_of_encoder_sample_index(&self, start_of_encoder_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setStartOfEncoderSampleIndex: start_of_encoder_sample_index + ] + } + } + + pub fn end_of_encoder_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, endOfEncoderSampleIndex] } + } + + pub fn set_end_of_encoder_sample_index(&self, end_of_encoder_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setEndOfEncoderSampleIndex: end_of_encoder_sample_index + ] + } + } +} diff --git a/third_party/rust/metal/src/constants.rs b/third_party/rust/metal/src/constants.rs new file mode 100644 index 0000000000..4e0e7a9595 --- /dev/null +++ b/third_party/rust/metal/src/constants.rs @@ -0,0 +1,152 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +/// See <https://developer.apple.com/documentation/metal/mtlpixelformat> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum MTLPixelFormat { + Invalid = 0, + A8Unorm = 1, + R8Unorm = 10, + R8Unorm_sRGB = 11, + R8Snorm = 12, + R8Uint = 13, + R8Sint = 14, + R16Unorm = 20, + R16Snorm = 22, + R16Uint = 23, + R16Sint = 24, + R16Float = 25, + RG8Unorm = 30, + RG8Unorm_sRGB = 31, + RG8Snorm = 32, + RG8Uint = 33, + RG8Sint = 34, + B5G6R5Unorm = 40, + A1BGR5Unorm = 41, + ABGR4Unorm = 42, + BGR5A1Unorm = 43, + R32Uint = 53, + R32Sint = 54, + R32Float = 55, + RG16Unorm = 60, + RG16Snorm = 62, + RG16Uint = 63, + RG16Sint = 64, + RG16Float = 65, + RGBA8Unorm = 70, + RGBA8Unorm_sRGB = 71, + RGBA8Snorm = 72, + RGBA8Uint = 73, + RGBA8Sint = 74, + BGRA8Unorm = 80, + BGRA8Unorm_sRGB = 81, + RGB10A2Unorm = 90, + RGB10A2Uint = 91, + RG11B10Float = 92, + RGB9E5Float = 93, + BGR10A2Unorm = 94, + RG32Uint = 103, + RG32Sint = 104, + RG32Float = 105, + RGBA16Unorm = 110, + RGBA16Snorm = 112, + RGBA16Uint = 113, + RGBA16Sint = 114, + RGBA16Float = 115, + RGBA32Uint = 123, + RGBA32Sint = 124, + RGBA32Float = 125, + BC1_RGBA = 130, + BC1_RGBA_sRGB = 131, + BC2_RGBA = 132, + BC2_RGBA_sRGB = 133, + BC3_RGBA = 134, + BC3_RGBA_sRGB = 135, + BC4_RUnorm = 140, + BC4_RSnorm = 141, + BC5_RGUnorm = 142, + BC5_RGSnorm = 143, + BC6H_RGBFloat = 150, + BC6H_RGBUfloat = 151, + BC7_RGBAUnorm = 152, + BC7_RGBAUnorm_sRGB = 153, + PVRTC_RGB_2BPP = 160, + PVRTC_RGB_2BPP_sRGB = 161, + PVRTC_RGB_4BPP = 162, + PVRTC_RGB_4BPP_sRGB = 163, + PVRTC_RGBA_2BPP = 164, + PVRTC_RGBA_2BPP_sRGB = 165, + PVRTC_RGBA_4BPP = 166, + PVRTC_RGBA_4BPP_sRGB = 167, + EAC_R11Unorm = 170, + EAC_R11Snorm = 172, + EAC_RG11Unorm = 174, + EAC_RG11Snorm = 176, + EAC_RGBA8 = 178, + EAC_RGBA8_sRGB = 179, + ETC2_RGB8 = 180, + ETC2_RGB8_sRGB = 181, + ETC2_RGB8A1 = 182, + ETC2_RGB8A1_sRGB = 183, + ASTC_4x4_sRGB = 186, + ASTC_5x4_sRGB = 187, + ASTC_5x5_sRGB = 188, + ASTC_6x5_sRGB = 189, + ASTC_6x6_sRGB = 190, + ASTC_8x5_sRGB = 192, + ASTC_8x6_sRGB = 193, + ASTC_8x8_sRGB = 194, + ASTC_10x5_sRGB = 195, + ASTC_10x6_sRGB = 196, + ASTC_10x8_sRGB = 197, + ASTC_10x10_sRGB = 198, + ASTC_12x10_sRGB = 199, + ASTC_12x12_sRGB = 200, + ASTC_4x4_LDR = 204, + ASTC_5x4_LDR = 205, + ASTC_5x5_LDR = 206, + ASTC_6x5_LDR = 207, + ASTC_6x6_LDR = 208, + ASTC_8x5_LDR = 210, + ASTC_8x6_LDR = 211, + ASTC_8x8_LDR = 212, + ASTC_10x5_LDR = 213, + ASTC_10x6_LDR = 214, + ASTC_10x8_LDR = 215, + ASTC_10x10_LDR = 216, + ASTC_12x10_LDR = 217, + ASTC_12x12_LDR = 218, + ASTC_4x4_HDR = 222, + ASTC_5x4_HDR = 223, + ASTC_5x5_HDR = 224, + ASTC_6x5_HDR = 225, + ASTC_6x6_HDR = 226, + ASTC_8x5_HDR = 228, + ASTC_8x6_HDR = 229, + ASTC_8x8_HDR = 230, + ASTC_10x5_HDR = 231, + ASTC_10x6_HDR = 232, + ASTC_10x8_HDR = 233, + ASTC_10x10_HDR = 234, + ASTC_12x10_HDR = 235, + ASTC_12x12_HDR = 236, + GBGR422 = 240, + BGRG422 = 241, + Depth16Unorm = 250, + Depth32Float = 252, + Stencil8 = 253, + Depth24Unorm_Stencil8 = 255, + Depth32Float_Stencil8 = 260, + X32_Stencil8 = 261, + X24_Stencil8 = 262, + BGRA10_XR = 552, + BGRA10_XR_SRGB = 553, + BGR10_XR = 554, + BGR10_XR_SRGB = 555, +} diff --git a/third_party/rust/metal/src/counters.rs b/third_party/rust/metal/src/counters.rs new file mode 100644 index 0000000000..0a39752d2d --- /dev/null +++ b/third_party/rust/metal/src/counters.rs @@ -0,0 +1,102 @@ +use crate::MTLStorageMode; + +/// See <https://developer.apple.com/documentation/metal/mtlcountersamplebufferdescriptor> +pub enum MTLCounterSampleBufferDescriptor {} + +foreign_obj_type! { + type CType = MTLCounterSampleBufferDescriptor; + pub struct CounterSampleBufferDescriptor; +} + +impl CounterSampleBufferDescriptor { + pub fn new() -> Self { + let class = class!(MTLCounterSampleBufferDescriptor); + unsafe { msg_send![class, new] } + } +} + +impl CounterSampleBufferDescriptorRef { + pub fn counter_set(&self) -> &CounterSetRef { + unsafe { msg_send![self, counterSet] } + } + + pub fn set_counter_set(&self, counter_set: &CounterSetRef) { + unsafe { msg_send![self, setCounterSet: counter_set] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn sample_count(&self) -> u64 { + unsafe { msg_send![self, sampleCount] } + } + + pub fn set_sample_count(&self, sample_count: u64) { + unsafe { msg_send![self, setSampleCount: sample_count] } + } + + pub fn storage_mode(&self) -> MTLStorageMode { + unsafe { msg_send![self, storageMode] } + } + + pub fn set_storage_mode(&self, storage_mode: MTLStorageMode) { + unsafe { msg_send![self, setStorageMode: storage_mode] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcountersamplebuffer> +pub enum MTLCounterSampleBuffer {} + +foreign_obj_type! { + type CType = MTLCounterSampleBuffer; + pub struct CounterSampleBuffer; +} + +/// See <https://developer.apple.com/documentation/metal/mtlcounter> +pub enum MTLCounter {} + +foreign_obj_type! { + type CType = MTLCounter; + pub struct Counter; +} + +impl CounterRef {} + +/// See <https://developer.apple.com/documentation/metal/mtlcounterset> +pub enum MTLCounterSet {} + +foreign_obj_type! { + type CType = MTLCounterSet; + pub struct CounterSet; +} + +impl CounterSetRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcommoncounterset> +pub enum MTLCommonCounterSet {} + +/// See <https://developer.apple.com/documentation/metal/mtlcommoncounter> +pub enum MTLCommonCounter {} + +foreign_obj_type! { + type CType = MTLCommonCounter; + pub struct CommonCounter; +} diff --git a/third_party/rust/metal/src/depthstencil.rs b/third_party/rust/metal/src/depthstencil.rs new file mode 100644 index 0000000000..fe0611bb6b --- /dev/null +++ b/third_party/rust/metal/src/depthstencil.rs @@ -0,0 +1,190 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use crate::DeviceRef; +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtlcomparefunction> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCompareFunction { + Never = 0, + Less = 1, + Equal = 2, + LessEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterEqual = 6, + Always = 7, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstenciloperation> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLStencilOperation { + Keep = 0, + Zero = 1, + Replace = 2, + IncrementClamp = 3, + DecrementClamp = 4, + Invert = 5, + IncrementWrap = 6, + DecrementWrap = 7, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstencildescriptor> +pub enum MTLStencilDescriptor {} + +foreign_obj_type! { + type CType = MTLStencilDescriptor; + pub struct StencilDescriptor; +} + +impl StencilDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLStencilDescriptor); + msg_send![class, new] + } + } +} + +impl StencilDescriptorRef { + pub fn stencil_compare_function(&self) -> MTLCompareFunction { + unsafe { msg_send![self, stencilCompareFunction] } + } + + pub fn set_stencil_compare_function(&self, func: MTLCompareFunction) { + unsafe { msg_send![self, setStencilCompareFunction: func] } + } + + pub fn stencil_failure_operation(&self) -> MTLStencilOperation { + unsafe { msg_send![self, stencilFailureOperation] } + } + + pub fn set_stencil_failure_operation(&self, operation: MTLStencilOperation) { + unsafe { msg_send![self, setStencilFailureOperation: operation] } + } + + pub fn depth_failure_operation(&self) -> MTLStencilOperation { + unsafe { msg_send![self, depthFailureOperation] } + } + + pub fn set_depth_failure_operation(&self, operation: MTLStencilOperation) { + unsafe { msg_send![self, setDepthFailureOperation: operation] } + } + + pub fn depth_stencil_pass_operation(&self) -> MTLStencilOperation { + unsafe { msg_send![self, depthStencilPassOperation] } + } + + pub fn set_depth_stencil_pass_operation(&self, operation: MTLStencilOperation) { + unsafe { msg_send![self, setDepthStencilPassOperation: operation] } + } + + pub fn read_mask(&self) -> u32 { + unsafe { msg_send![self, readMask] } + } + + pub fn set_read_mask(&self, mask: u32) { + unsafe { msg_send![self, setReadMask: mask] } + } + + pub fn write_mask(&self) -> u32 { + unsafe { msg_send![self, writeMask] } + } + + pub fn set_write_mask(&self, mask: u32) { + unsafe { msg_send![self, setWriteMask: mask] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtldepthstencildescriptor> +pub enum MTLDepthStencilDescriptor {} + +foreign_obj_type! { + type CType = MTLDepthStencilDescriptor; + pub struct DepthStencilDescriptor; +} + +impl DepthStencilDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLDepthStencilDescriptor); + msg_send![class, new] + } + } +} + +impl DepthStencilDescriptorRef { + pub fn depth_compare_function(&self) -> MTLCompareFunction { + unsafe { msg_send![self, depthCompareFunction] } + } + + pub fn set_depth_compare_function(&self, func: MTLCompareFunction) { + unsafe { msg_send![self, setDepthCompareFunction: func] } + } + + pub fn depth_write_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isDepthWriteEnabled] } + } + + pub fn set_depth_write_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setDepthWriteEnabled: enabled] } + } + + pub fn front_face_stencil(&self) -> Option<&StencilDescriptorRef> { + unsafe { msg_send![self, frontFaceStencil] } + } + + pub fn set_front_face_stencil(&self, descriptor: Option<&StencilDescriptorRef>) { + unsafe { msg_send![self, setFrontFaceStencil: descriptor] } + } + + pub fn back_face_stencil(&self) -> Option<&StencilDescriptorRef> { + unsafe { msg_send![self, backFaceStencil] } + } + + pub fn set_back_face_stencil(&self, descriptor: Option<&StencilDescriptorRef>) { + unsafe { msg_send![self, setBackFaceStencil: descriptor] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtldepthstencilstate> +pub enum MTLDepthStencilState {} + +foreign_obj_type! { + type CType = MTLDepthStencilState; + pub struct DepthStencilState; +} + +impl DepthStencilStateRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } +} diff --git a/third_party/rust/metal/src/device.rs b/third_party/rust/metal/src/device.rs new file mode 100644 index 0000000000..11b5575577 --- /dev/null +++ b/third_party/rust/metal/src/device.rs @@ -0,0 +1,2119 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use block::{Block, ConcreteBlock}; +use foreign_types::ForeignType; +use objc::runtime::{Object, NO, YES}; + +use std::{ffi::CStr, os::raw::c_char, path::Path, ptr}; + +/// Available on macOS 10.11+, iOS 8.0+, tvOS 9.0+ +/// +/// See <https://developer.apple.com/documentation/metal/mtlfeatureset> +#[allow(non_camel_case_types)] +#[deprecated( + note = "Since iOS 8.0–16.0 iPadOS 8.0–16.0 macOS 10.11–13.0 Mac Catalyst 13.1–16.0 tvOS 9.0–16.0" +)] +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLFeatureSet { + iOS_GPUFamily1_v1 = 0, + iOS_GPUFamily2_v1 = 1, + iOS_GPUFamily1_v2 = 2, + iOS_GPUFamily2_v2 = 3, + iOS_GPUFamily3_v1 = 4, + iOS_GPUFamily1_v3 = 5, + iOS_GPUFamily2_v3 = 6, + iOS_GPUFamily3_v2 = 7, + iOS_GPUFamily1_v4 = 8, + iOS_GPUFamily2_v4 = 9, + iOS_GPUFamily3_v3 = 10, + iOS_GPUFamily4_v1 = 11, + iOS_GPUFamily1_v5 = 12, + iOS_GPUFamily2_v5 = 13, + iOS_GPUFamily3_v4 = 14, + iOS_GPUFamily4_v2 = 15, + iOS_GPUFamily5_v1 = 16, + + tvOS_GPUFamily1_v1 = 30000, + tvOS_GPUFamily1_v2 = 30001, + tvOS_GPUFamily1_v3 = 30002, + tvOS_GPUFamily2_v1 = 30003, + tvOS_GPUFamily1_v4 = 30004, + tvOS_GPUFamily2_v2 = 30005, + + macOS_GPUFamily1_v1 = 10000, + macOS_GPUFamily1_v2 = 10001, + // Available on macOS 10.12+ + macOS_ReadWriteTextureTier2 = 10002, + macOS_GPUFamily1_v3 = 10003, + macOS_GPUFamily1_v4 = 10004, + macOS_GPUFamily2_v1 = 10005, +} + +/// Available on macOS 10.15+, iOS 13.0+ +/// +/// See <https://developer.apple.com/documentation/metal/mtlgpufamily> +#[repr(i64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[non_exhaustive] +pub enum MTLGPUFamily { + Common1 = 3001, + Common2 = 3002, + Common3 = 3003, + Apple1 = 1001, + Apple2 = 1002, + Apple3 = 1003, + Apple4 = 1004, + Apple5 = 1005, + Apple6 = 1006, + Apple7 = 1007, + Apple8 = 1008, + Apple9 = 1009, + Mac1 = 2001, + Mac2 = 2002, + MacCatalyst1 = 4001, + MacCatalyst2 = 4002, + Metal3 = 5001, +} + +/// See <https://developer.apple.com/documentation/metal/mtldevicelocation> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLDeviceLocation { + BuiltIn = 0, + Slot = 1, + External = 2, + Unspecified = u64::MAX, +} + +bitflags! { + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct PixelFormatCapabilities: u32 { + const Filter = 1 << 0; + const Write = 1 << 1; + const Color = 1 << 2; + const Blend = 1 << 3; + const Msaa = 1 << 4; + const Resolve = 1 << 5; + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +enum OS { + iOS, + tvOS, + macOS, +} + +const KB: u32 = 1024; +const MB: u32 = 1024 * KB; +const GB: u32 = 1024 * MB; + +impl MTLFeatureSet { + fn os(&self) -> OS { + let value = *self as u64; + if value < 10_000 { + OS::iOS + } else if value < 20_000 { + OS::macOS + } else if value >= 30_000 || value < 40_000 { + OS::tvOS + } else { + unreachable!() + } + } + + // returns the minor version on macos + fn os_version(&self) -> u32 { + use MTLFeatureSet::*; + match self { + iOS_GPUFamily1_v1 | iOS_GPUFamily2_v1 => 8, + iOS_GPUFamily1_v2 | iOS_GPUFamily2_v2 | iOS_GPUFamily3_v1 => 9, + iOS_GPUFamily1_v3 | iOS_GPUFamily2_v3 | iOS_GPUFamily3_v2 => 10, + iOS_GPUFamily1_v4 | iOS_GPUFamily2_v4 | iOS_GPUFamily3_v3 | iOS_GPUFamily4_v1 => 11, + iOS_GPUFamily1_v5 | iOS_GPUFamily2_v5 | iOS_GPUFamily3_v4 | iOS_GPUFamily4_v2 + | iOS_GPUFamily5_v1 => 12, + tvOS_GPUFamily1_v1 => 9, + tvOS_GPUFamily1_v2 => 10, + tvOS_GPUFamily1_v3 | tvOS_GPUFamily2_v1 => 11, + tvOS_GPUFamily1_v4 | tvOS_GPUFamily2_v2 => 12, + macOS_GPUFamily1_v1 => 11, + macOS_GPUFamily1_v2 | macOS_ReadWriteTextureTier2 => 12, + macOS_GPUFamily1_v3 => 13, + macOS_GPUFamily1_v4 | macOS_GPUFamily2_v1 => 14, + } + } + + fn gpu_family(&self) -> u32 { + use MTLFeatureSet::*; + match self { + iOS_GPUFamily1_v1 + | iOS_GPUFamily1_v2 + | iOS_GPUFamily1_v3 + | iOS_GPUFamily1_v4 + | iOS_GPUFamily1_v5 + | tvOS_GPUFamily1_v1 + | tvOS_GPUFamily1_v2 + | tvOS_GPUFamily1_v3 + | tvOS_GPUFamily1_v4 + | macOS_GPUFamily1_v1 + | macOS_GPUFamily1_v2 + | macOS_ReadWriteTextureTier2 + | macOS_GPUFamily1_v3 + | macOS_GPUFamily1_v4 => 1, + iOS_GPUFamily2_v1 | iOS_GPUFamily2_v2 | iOS_GPUFamily2_v3 | iOS_GPUFamily2_v4 + | iOS_GPUFamily2_v5 | tvOS_GPUFamily2_v1 | tvOS_GPUFamily2_v2 | macOS_GPUFamily2_v1 => { + 2 + } + iOS_GPUFamily3_v1 | iOS_GPUFamily3_v2 | iOS_GPUFamily3_v3 | iOS_GPUFamily3_v4 => 3, + iOS_GPUFamily4_v1 | iOS_GPUFamily4_v2 => 4, + iOS_GPUFamily5_v1 => 5, + } + } + + fn version(&self) -> u32 { + use MTLFeatureSet::*; + match self { + iOS_GPUFamily1_v1 + | iOS_GPUFamily2_v1 + | iOS_GPUFamily3_v1 + | iOS_GPUFamily4_v1 + | iOS_GPUFamily5_v1 + | macOS_GPUFamily1_v1 + | macOS_GPUFamily2_v1 + | macOS_ReadWriteTextureTier2 + | tvOS_GPUFamily1_v1 + | tvOS_GPUFamily2_v1 => 1, + iOS_GPUFamily1_v2 | iOS_GPUFamily2_v2 | iOS_GPUFamily3_v2 | iOS_GPUFamily4_v2 + | macOS_GPUFamily1_v2 | tvOS_GPUFamily1_v2 | tvOS_GPUFamily2_v2 => 2, + iOS_GPUFamily1_v3 | iOS_GPUFamily2_v3 | iOS_GPUFamily3_v3 | macOS_GPUFamily1_v3 + | tvOS_GPUFamily1_v3 => 3, + iOS_GPUFamily1_v4 | iOS_GPUFamily2_v4 | iOS_GPUFamily3_v4 | tvOS_GPUFamily1_v4 + | macOS_GPUFamily1_v4 => 4, + iOS_GPUFamily1_v5 | iOS_GPUFamily2_v5 => 5, + } + } + + pub fn supports_metal_kit(&self) -> bool { + true + } + + pub fn supports_metal_performance_shaders(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 2, + OS::tvOS => true, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_programmable_blending(&self) -> bool { + self.os() != OS::macOS + } + + pub fn supports_pvrtc_pixel_formats(&self) -> bool { + self.os() != OS::macOS + } + + pub fn supports_eac_etc_pixel_formats(&self) -> bool { + self.os() != OS::macOS + } + + pub fn supports_astc_pixel_formats(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 2, + OS::tvOS => true, + OS::macOS => false, + } + } + + pub fn supports_linear_textures(&self) -> bool { + self.os() != OS::macOS || self.os_version() >= 13 + } + + pub fn supports_bc_pixel_formats(&self) -> bool { + self.os() == OS::macOS + } + + pub fn supports_msaa_depth_resolve(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => false, + } + } + + pub fn supports_counting_occlusion_query(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + } + } + + pub fn supports_base_vertex_instance_drawing(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + } + } + + pub fn supports_indirect_buffers(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + } + } + + pub fn supports_cube_map_texture_arrays(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 4, + OS::tvOS => false, + OS::macOS => true, + } + } + + pub fn supports_texture_barriers(&self) -> bool { + self.os() == OS::macOS + } + + pub fn supports_layered_rendering(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 5, + OS::tvOS => false, + OS::macOS => true, + } + } + + pub fn supports_tessellation(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 10, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_resource_heaps(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_memoryless_render_targets(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => false, + } + } + + pub fn supports_function_specialization(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_function_buffer_read_writes(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 10, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_function_texture_read_writes(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 4, + OS::tvOS => false, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_array_of_textures(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 10, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_array_of_samplers(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 11, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_stencil_texture_views(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_depth_16_pixel_format(&self) -> bool { + self.os() == OS::macOS && self.os_version() >= 12 + } + + pub fn supports_extended_range_pixel_formats(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 10, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => false, + } + } + + pub fn supports_wide_color_pixel_format(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 11, + OS::tvOS => self.os_version() >= 11, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_combined_msaa_store_and_resolve_action(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 && self.os_version() >= 10, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_deferred_store_action(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_msaa_blits(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => true, + } + } + + pub fn supports_srgb_writes(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3 || (self.gpu_family() >= 2 && self.version() >= 3), + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.gpu_family() >= 2, + } + } + + pub fn supports_16_bit_unsigned_integer_coordinates(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_extract_insert_and_reverse_bits(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_simd_barrier(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_sampler_max_anisotropy(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_sampler_lod_clamp(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 10, + OS::tvOS => self.os_version() >= 10, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_border_color(&self) -> bool { + self.os() == OS::macOS && self.os_version() >= 12 + } + + pub fn supports_dual_source_blending(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 11, + OS::tvOS => self.os_version() >= 11, + OS::macOS => self.os_version() >= 12, + } + } + + pub fn supports_argument_buffers(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 11, + OS::tvOS => self.os_version() >= 11, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_programmable_sample_positions(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 11, + OS::tvOS => self.os_version() >= 11, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_uniform_type(&self) -> bool { + match self.os() { + OS::iOS => self.os_version() >= 11, + OS::tvOS => self.os_version() >= 11, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_imageblocks(&self) -> bool { + self.os() == OS::iOS && self.gpu_family() >= 4 + } + + pub fn supports_tile_shaders(&self) -> bool { + self.os() == OS::iOS && self.gpu_family() >= 4 + } + + pub fn supports_imageblock_sample_coverage_control(&self) -> bool { + self.os() == OS::iOS && self.gpu_family() >= 4 + } + + pub fn supports_threadgroup_sharing(&self) -> bool { + self.os() == OS::iOS && self.gpu_family() >= 4 + } + + pub fn supports_post_depth_coverage(&self) -> bool { + self.os() == OS::iOS && self.gpu_family() >= 4 + } + + pub fn supports_quad_scoped_permute_operations(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 4, + OS::tvOS => false, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_raster_order_groups(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 4, + OS::tvOS => false, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_non_uniform_threadgroup_size(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 4, + OS::tvOS => false, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_multiple_viewports(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 5, + OS::tvOS => false, + OS::macOS => self.os_version() >= 13, + } + } + + pub fn supports_device_notifications(&self) -> bool { + self.os() == OS::macOS && self.os_version() >= 13 + } + + pub fn supports_stencil_feedback(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 5, + OS::tvOS => false, + OS::macOS => self.gpu_family() >= 2, + } + } + + pub fn supports_stencil_resolve(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 5, + OS::tvOS => false, + OS::macOS => self.gpu_family() >= 2, + } + } + + pub fn supports_binary_archive(&self) -> bool { + match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 3, + OS::macOS => self.gpu_family() >= 1, + } + } + + pub fn max_vertex_attributes(&self) -> u32 { + 31 + } + + pub fn max_buffer_argument_entries(&self) -> u32 { + 31 + } + + pub fn max_texture_argument_entries(&self) -> u32 { + if self.os() == OS::macOS { + 128 + } else { + 31 + } + } + + pub fn max_sampler_state_argument_entries(&self) -> u32 { + 16 + } + + pub fn max_threadgroup_memory_argument_entries(&self) -> u32 { + 31 + } + + pub fn max_inlined_constant_data_buffers(&self) -> u32 { + if self.os() == OS::macOS { + 14 + } else { + 31 + } + } + + pub fn max_inline_constant_buffer_length(&self) -> u32 { + 4 * KB + } + + pub fn max_threads_per_threadgroup(&self) -> u32 { + if self.os() == OS::macOS || self.gpu_family() >= 4 { + 1024 + } else { + 512 + } + } + + pub fn max_total_threadgroup_memory_allocation(&self) -> u32 { + match (self.os(), self.gpu_family()) { + (OS::iOS, 5) => 64 * KB, + (OS::iOS, 4) => { + if self.os_version() >= 12 { + 64 * KB + } else { + 32 * KB + } + } + (OS::iOS, 3) => 16 * KB, + (OS::iOS, _) => 16 * KB - 32, + (OS::tvOS, 1) => 16 * KB - 32, + (OS::tvOS, _) => 16 * KB, + (OS::macOS, _) => 32 * KB, + } + } + + pub fn max_total_tile_memory_allocation(&self) -> u32 { + if self.os() == OS::iOS && self.gpu_family() == 4 { + 32 * KB + } else { + 0 + } + } + + pub fn threadgroup_memory_length_alignment(&self) -> u32 { + 16 + } + + pub fn max_constant_buffer_function_memory_allocation(&self) -> Option<u32> { + if self.os() == OS::macOS { + Some(64 * KB) + } else { + None + } + } + + pub fn max_fragment_inputs(&self) -> u32 { + if self.os() == OS::macOS { + 32 + } else { + 60 + } + } + + pub fn max_fragment_input_components(&self) -> u32 { + if self.os() == OS::macOS { + 128 + } else { + 60 + } + } + + pub fn max_function_constants(&self) -> u32 { + match self.os() { + OS::iOS if self.os_version() >= 11 => 65536, + OS::tvOS if self.os_version() >= 10 => 65536, + OS::macOS if self.os_version() >= 12 => 65536, + _ => 0, + } + } + + pub fn max_tessellation_factor(&self) -> u32 { + if self.supports_tessellation() { + match self.os() { + OS::iOS if self.gpu_family() >= 5 => 64, + OS::iOS => 16, + OS::tvOS => 16, + OS::macOS => 64, + } + } else { + 0 + } + } + + pub fn max_viewports_and_scissor_rectangles(&self) -> u32 { + if self.supports_multiple_viewports() { + 16 + } else { + 1 + } + } + + pub fn max_raster_order_groups(&self) -> u32 { + if self.supports_raster_order_groups() { + 8 + } else { + 0 + } + } + + pub fn max_buffer_length(&self) -> u32 { + if self.os() == OS::macOS && self.os_version() >= 12 { + 1 * GB + } else { + 256 * MB + } + } + + pub fn min_buffer_offset_alignment(&self) -> u32 { + if self.os() == OS::macOS { + 256 + } else { + 4 + } + } + + pub fn max_1d_texture_size(&self) -> u32 { + match (self.os(), self.gpu_family()) { + (OS::iOS, 1) | (OS::iOS, 2) => { + if self.version() <= 2 { + 4096 + } else { + 8192 + } + } + (OS::tvOS, 1) => 8192, + _ => 16384, + } + } + + pub fn max_2d_texture_size(&self) -> u32 { + match (self.os(), self.gpu_family()) { + (OS::iOS, 1) | (OS::iOS, 2) => { + if self.version() <= 2 { + 4096 + } else { + 8192 + } + } + (OS::tvOS, 1) => 8192, + _ => 16384, + } + } + + pub fn max_cube_map_texture_size(&self) -> u32 { + match (self.os(), self.gpu_family()) { + (OS::iOS, 1) | (OS::iOS, 2) => { + if self.version() <= 2 { + 4096 + } else { + 8192 + } + } + (OS::tvOS, 1) => 8192, + _ => 16384, + } + } + + pub fn max_3d_texture_size(&self) -> u32 { + 2048 + } + + pub fn max_array_layers(&self) -> u32 { + 2048 + } + + pub fn copy_texture_buffer_alignment(&self) -> u32 { + match (self.os(), self.gpu_family()) { + (OS::iOS, 1) | (OS::iOS, 2) | (OS::tvOS, 1) => 64, + (OS::iOS, _) | (OS::tvOS, _) => 16, + (OS::macOS, _) => 256, + } + } + + /// If this function returns `None` but linear textures are supported, + /// the buffer alignment can be discovered via API query + pub fn new_texture_buffer_alignment(&self) -> Option<u32> { + match self.os() { + OS::iOS => { + if self.os_version() >= 11 { + None + } else if self.gpu_family() == 3 { + Some(16) + } else { + Some(64) + } + } + OS::tvOS => { + if self.os_version() >= 11 { + None + } else { + Some(64) + } + } + OS::macOS => None, + } + } + + pub fn max_color_render_targets(&self) -> u32 { + if self.os() == OS::iOS && self.gpu_family() == 1 { + 4 + } else { + 8 + } + } + + pub fn max_point_primitive_size(&self) -> u32 { + 511 + } + + pub fn max_total_color_render_target_size(&self) -> Option<u32> { + match (self.os(), self.gpu_family()) { + (OS::iOS, 1) => Some(128), + (OS::iOS, 2) | (OS::iOS, 3) => Some(256), + (OS::iOS, _) => Some(512), + (OS::tvOS, _) => Some(256), + (OS::macOS, _) => None, + } + } + + pub fn max_visibility_query_offset(&self) -> u32 { + 64 * KB - 8 + } + + pub fn a8_unorm_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Filter + } + + pub fn r8_unorm_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn r8_unorm_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else if self.supports_srgb_writes() { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn r8_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.gpu_family() == 1 { + !PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::all() + } + } + + pub fn r8_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn r8_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn r16_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() != OS::macOS { + !PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::all() + } + } + + pub fn r16_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() != OS::macOS { + !PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::all() + } + } + + pub fn r16_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn r16_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn r16_float_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn rg8_unorm_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn rg8_unorm_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else if self.supports_srgb_writes() { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rg8_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.gpu_family() == 1 { + !PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::all() + } + } + + pub fn rg8_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rg8_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn b5_g6_r5_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn a1_bgr5_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn abgr4_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn bgr5_a1_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::empty() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn r32_uint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn r32_sint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn r32_float_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Blend + | PixelFormatCapabilities::Msaa + } else if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Blend + | PixelFormatCapabilities::Msaa + } + } + + pub fn rg16_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Resolve + } + } + + pub fn rg16_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Resolve + } + } + + pub fn rg16_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rg16_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rg16_float_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn rgba8_unorm_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn rgba8_unorm_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_srgb_writes() { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgba8_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.gpu_family() == 1 { + !PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::all() + } + } + + pub fn rgba8_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rgba8_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn bgra8_unorm_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn bgra8_unorm_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_srgb_writes() { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgb10_a2_unorm_capabilities(&self) -> PixelFormatCapabilities { + let supports_writes = match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + }; + if supports_writes { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgb10_a2_uint_capabilities(&self) -> PixelFormatCapabilities { + let supports_writes = match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + }; + if supports_writes { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Msaa + } + } + + pub fn rg11_b10_float_capabilities(&self) -> PixelFormatCapabilities { + let supports_writes = match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => true, + }; + if supports_writes { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgb9_e5_float_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::Filter + } else { + let supports_writes = match self.os() { + OS::iOS => self.gpu_family() >= 3, + OS::tvOS => self.gpu_family() >= 2, + OS::macOS => false, + }; + if supports_writes { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + } + + pub fn rg32_uint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn rg32_sint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn rg32_float_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Blend + } else { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Blend + } + } + + pub fn rgba16_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgba16_snorm_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else { + !PixelFormatCapabilities::Write + } + } + + pub fn rgba16_uint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rgba16_sint_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Write + | PixelFormatCapabilities::Color + | PixelFormatCapabilities::Msaa + } + + pub fn rgba16_float_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::all() + } + + pub fn rgba32_uint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn rgba32_sint_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::iOS && self.os_version() == 8 { + PixelFormatCapabilities::Color + } else if self.os() == OS::macOS { + PixelFormatCapabilities::Color + | PixelFormatCapabilities::Write + | PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::Color | PixelFormatCapabilities::Write + } + } + + pub fn rgba32_float_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::all() + } else if self.os() == OS::iOS && self.version() == 8 { + PixelFormatCapabilities::Color + } else { + PixelFormatCapabilities::Write | PixelFormatCapabilities::Color + } + } + + pub fn pvrtc_pixel_formats_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_pvrtc_pixel_formats() { + PixelFormatCapabilities::Filter + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn eac_etc_pixel_formats_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_eac_etc_pixel_formats() { + PixelFormatCapabilities::Filter + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn astc_pixel_formats_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_astc_pixel_formats() { + PixelFormatCapabilities::Filter + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn bc_pixel_formats_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_bc_pixel_formats() { + PixelFormatCapabilities::Filter + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn gbgr422_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Filter + } + + pub fn bgrg422_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Filter + } + + pub fn depth16_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_depth_16_pixel_format() { + PixelFormatCapabilities::Filter + | PixelFormatCapabilities::Msaa + | PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn depth32_float_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::Filter + | PixelFormatCapabilities::Msaa + | PixelFormatCapabilities::Resolve + } else if self.supports_msaa_depth_resolve() { + PixelFormatCapabilities::Msaa | PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::Msaa + } + } + + pub fn stencil8_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Msaa + } + + pub fn depth24_unorm_stencil8_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::Filter + | PixelFormatCapabilities::Msaa + | PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn depth32_float_stencil8_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::Filter + | PixelFormatCapabilities::Msaa + | PixelFormatCapabilities::Resolve + } else if self.supports_msaa_depth_resolve() { + PixelFormatCapabilities::Msaa | PixelFormatCapabilities::Resolve + } else { + PixelFormatCapabilities::Msaa + } + } + + pub fn x24_stencil8_capabilities(&self) -> PixelFormatCapabilities { + if self.os() == OS::macOS { + PixelFormatCapabilities::Msaa + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn x32_stencil8_capabilities(&self) -> PixelFormatCapabilities { + PixelFormatCapabilities::Msaa + } + + pub fn bgra10_xr_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_extended_range_pixel_formats() { + PixelFormatCapabilities::all() + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn bgra10_xr_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_extended_range_pixel_formats() { + PixelFormatCapabilities::all() + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn bgr10_xr_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_extended_range_pixel_formats() { + PixelFormatCapabilities::all() + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn bgr10_xr_srgb_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_extended_range_pixel_formats() { + PixelFormatCapabilities::all() + } else { + PixelFormatCapabilities::empty() + } + } + + pub fn bgr10_a2_unorm_capabilities(&self) -> PixelFormatCapabilities { + if self.supports_wide_color_pixel_format() { + if self.os() == OS::macOS { + !PixelFormatCapabilities::Write + } else { + PixelFormatCapabilities::all() + } + } else { + PixelFormatCapabilities::empty() + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlargumentbufferstier> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLArgumentBuffersTier { + Tier1 = 0, + Tier2 = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlreadwritetexturetier> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLReadWriteTextureTier { + TierNone = 0, + Tier1 = 1, + Tier2 = 2, +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlcountersamplingpoint> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCounterSamplingPoint { + AtStageBoundary = 0, + AtDrawBoundary = 1, + AtDispatchBoundary = 2, + AtTileDispatchBoundary = 3, + AtBlitBoundary = 4, +} + +/// Only available on (macos(11.0), macCatalyst(14.0), ios(13.0)) +/// Kinda a long name! +/// +/// See <https://developer.apple.com/documentation/metal/mtlsparsetextureregionalignmentmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLSparseTextureRegionAlignmentMode { + Outward = 0, + Inward = 1, +} + +bitflags! { + /// Options that determine how Metal prepares the pipeline. + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLPipelineOption: NSUInteger { + /// Do not provide any reflection information. + const None = 0; + /// An option that requests argument information for buffers, textures, and threadgroup memory. + const ArgumentInfo = 1 << 0; + /// An option that requests detailed buffer type information for buffer arguments. + const BufferTypeInfo = 1 << 1; + /// An option that specifies that Metal should create the pipeline state object only if the + /// compiled shader is present inside the binary archive. + /// + /// Only available on (macos(11.0), ios(14.0)) + const FailOnBinaryArchiveMiss = 1 << 2; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlaccelerationstructuresizes> +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[repr(C)] +pub struct MTLAccelerationStructureSizes { + pub acceleration_structure_size: NSUInteger, + pub build_scratch_buffer_size: NSUInteger, + pub refit_scratch_buffer_size: NSUInteger, +} + +#[cfg_attr(feature = "link", link(name = "Metal", kind = "framework"))] +extern "C" { + fn MTLCreateSystemDefaultDevice() -> *mut MTLDevice; + #[cfg(not(target_os = "ios"))] + fn MTLCopyAllDevices() -> *mut Object; //TODO: Array +} + +#[allow(non_camel_case_types)] +type dispatch_data_t = *mut Object; +#[allow(non_camel_case_types)] +pub type dispatch_queue_t = *mut Object; +#[allow(non_camel_case_types)] +type dispatch_block_t = *const Block<(), ()>; + +#[cfg_attr( + all(feature = "link", any(target_os = "macos", target_os = "ios")), + link(name = "System", kind = "dylib") +)] +#[cfg_attr( + all(feature = "link", not(any(target_os = "macos", target_os = "ios"))), + link(name = "dispatch", kind = "dylib") +)] +#[allow(improper_ctypes)] +extern "C" { + static _dispatch_main_q: dispatch_queue_t; + + fn dispatch_data_create( + buffer: *const std::ffi::c_void, + size: crate::c_size_t, + queue: dispatch_queue_t, + destructor: dispatch_block_t, + ) -> dispatch_data_t; + fn dispatch_release(object: dispatch_data_t); // actually dispatch_object_t +} + +/*type MTLNewLibraryCompletionHandler = extern fn(library: id, error: id); +type MTLNewRenderPipelineStateCompletionHandler = extern fn(renderPipelineState: id, error: id); +type MTLNewRenderPipelineStateWithReflectionCompletionHandler = extern fn(renderPipelineState: id, reflection: id, error: id); +type MTLNewComputePipelineStateCompletionHandler = extern fn(computePipelineState: id, error: id); +type MTLNewComputePipelineStateWithReflectionCompletionHandler = extern fn(computePipelineState: id, reflection: id, error: id);*/ + +/// See <https://developer.apple.com/documentation/metal/mtldevice> +pub enum MTLDevice {} + +foreign_obj_type! { + type CType = MTLDevice; + pub struct Device; +} + +impl Device { + pub fn system_default() -> Option<Self> { + // `MTLCreateSystemDefaultDevice` may return null if Metal is not supported + unsafe { + MTLCreateSystemDefaultDevice() + .as_mut() + .map(|x| Self(x.into())) + } + } + + pub fn all() -> Vec<Self> { + #[cfg(target_os = "ios")] + { + Self::system_default().into_iter().collect() + } + #[cfg(not(target_os = "ios"))] + unsafe { + let array = MTLCopyAllDevices(); + let count: NSUInteger = msg_send![array, count]; + let ret = (0..count) + .map(|i| msg_send![array, objectAtIndex: i]) + // The elements of this array are references---we convert them to owned references + // (which just means that we increment the reference count here, and it is + // decremented in the `Drop` impl for `Device`) + .map(|device: *mut Object| msg_send![device, retain]) + .collect(); + let () = msg_send![array, release]; + ret + } + } +} + +impl DeviceRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + #[cfg(feature = "private")] + pub unsafe fn vendor(&self) -> &str { + let name = msg_send![self, vendorName]; + crate::nsstring_as_str(name) + } + + #[cfg(feature = "private")] + pub unsafe fn family_name(&self) -> &str { + let name = msg_send![self, familyName]; + crate::nsstring_as_str(name) + } + + pub fn registry_id(&self) -> u64 { + unsafe { msg_send![self, registryID] } + } + + pub fn location(&self) -> MTLDeviceLocation { + unsafe { msg_send![self, location] } + } + + pub fn location_number(&self) -> NSUInteger { + unsafe { msg_send![self, locationNumber] } + } + + pub fn max_threadgroup_memory_length(&self) -> NSUInteger { + unsafe { msg_send![self, maxThreadgroupMemoryLength] } + } + + pub fn max_threads_per_threadgroup(&self) -> MTLSize { + unsafe { msg_send![self, maxThreadsPerThreadgroup] } + } + + pub fn is_low_power(&self) -> bool { + unsafe { msg_send_bool![self, isLowPower] } + } + + pub fn is_headless(&self) -> bool { + unsafe { msg_send_bool![self, isHeadless] } + } + + pub fn is_removable(&self) -> bool { + unsafe { msg_send_bool![self, isRemovable] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_raytracing(&self) -> bool { + unsafe { msg_send_bool![self, supportsRaytracing] } + } + + pub fn has_unified_memory(&self) -> bool { + unsafe { msg_send![self, hasUnifiedMemory] } + } + + pub fn recommended_max_working_set_size(&self) -> u64 { + unsafe { msg_send![self, recommendedMaxWorkingSetSize] } + } + + pub fn max_transfer_rate(&self) -> u64 { + unsafe { msg_send![self, maxTransferRate] } + } + + pub fn supports_feature_set(&self, feature: MTLFeatureSet) -> bool { + unsafe { msg_send_bool![self, supportsFeatureSet: feature] } + } + + pub fn supports_family(&self, family: MTLGPUFamily) -> bool { + unsafe { msg_send_bool![self, supportsFamily: family] } + } + + pub fn supports_vertex_amplification_count(&self, count: NSUInteger) -> bool { + unsafe { msg_send_bool![self, supportsVertexAmplificationCount: count] } + } + + pub fn supports_texture_sample_count(&self, count: NSUInteger) -> bool { + unsafe { msg_send_bool![self, supportsTextureSampleCount: count] } + } + + pub fn supports_shader_barycentric_coordinates(&self) -> bool { + unsafe { msg_send_bool![self, supportsShaderBarycentricCoordinates] } + } + + pub fn supports_function_pointers(&self) -> bool { + unsafe { msg_send_bool![self, supportsFunctionPointers] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_dynamic_libraries(&self) -> bool { + unsafe { msg_send_bool![self, supportsDynamicLibraries] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_counter_sampling(&self, sampling_point: MTLCounterSamplingPoint) -> bool { + unsafe { msg_send_bool![self, supportsCounterSampling: sampling_point] } + } + + pub fn d24_s8_supported(&self) -> bool { + unsafe { msg_send_bool![self, isDepth24Stencil8PixelFormatSupported] } + } + + pub fn new_fence(&self) -> Fence { + unsafe { msg_send![self, newFence] } + } + + pub fn new_command_queue(&self) -> CommandQueue { + unsafe { msg_send![self, newCommandQueue] } + } + + pub fn new_command_queue_with_max_command_buffer_count( + &self, + count: NSUInteger, + ) -> CommandQueue { + unsafe { msg_send![self, newCommandQueueWithMaxCommandBufferCount: count] } + } + + pub fn new_default_library(&self) -> Library { + unsafe { msg_send![self, newDefaultLibrary] } + } + + pub fn new_library_with_source( + &self, + src: &str, + options: &CompileOptionsRef, + ) -> Result<Library, String> { + let source = nsstring_from_str(src); + unsafe { + let mut err: *mut Object = ptr::null_mut(); + let library: *mut MTLLibrary = msg_send![self, newLibraryWithSource:source + options:options + error:&mut err]; + if !err.is_null() { + let desc: *mut Object = msg_send![err, localizedDescription]; + let compile_error: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned(); + if library.is_null() { + return Err(message); + } else { + warn!("Shader warnings: {}", message); + } + } + + assert!(!library.is_null()); + Ok(Library::from_ptr(library)) + } + } + + pub fn new_library_with_file<P: AsRef<Path>>(&self, file: P) -> Result<Library, String> { + let filename = nsstring_from_str(file.as_ref().to_string_lossy().as_ref()); + unsafe { + let library: *mut MTLLibrary = try_objc! { err => + msg_send![self, newLibraryWithFile:filename.as_ref() + error:&mut err] + }; + Ok(Library::from_ptr(library)) + } + } + + pub fn new_library_with_data(&self, library_data: &[u8]) -> Result<Library, String> { + unsafe { + let destructor_block = ConcreteBlock::new(|| {}).copy(); + let data = dispatch_data_create( + library_data.as_ptr() as *const std::ffi::c_void, + library_data.len() as crate::c_size_t, + &_dispatch_main_q as *const _ as dispatch_queue_t, + &*destructor_block.deref(), + ); + + let library: *mut MTLLibrary = try_objc! { err => + msg_send![self, newLibraryWithData:data + error:&mut err] + }; + dispatch_release(data); + Ok(Library::from_ptr(library)) + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_dynamic_library(&self, library: &LibraryRef) -> Result<DynamicLibrary, String> { + unsafe { + let mut err: *mut Object = ptr::null_mut(); + let dynamic_library: *mut MTLDynamicLibrary = msg_send![self, newDynamicLibrary:library + error:&mut err]; + if !err.is_null() { + // FIXME: copy pasta + let desc: *mut Object = msg_send![err, localizedDescription]; + let compile_error: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned(); + Err(message) + } else { + Ok(DynamicLibrary::from_ptr(dynamic_library)) + } + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_dynamic_library_with_url(&self, url: &URLRef) -> Result<DynamicLibrary, String> { + unsafe { + let mut err: *mut Object = ptr::null_mut(); + let dynamic_library: *mut MTLDynamicLibrary = msg_send![self, newDynamicLibraryWithURL:url + error:&mut err]; + if !err.is_null() { + // FIXME: copy pasta + let desc: *mut Object = msg_send![err, localizedDescription]; + let compile_error: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned(); + Err(message) + } else { + Ok(DynamicLibrary::from_ptr(dynamic_library)) + } + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_binary_archive_with_descriptor( + &self, + descriptor: &BinaryArchiveDescriptorRef, + ) -> Result<BinaryArchive, String> { + unsafe { + let mut err: *mut Object = ptr::null_mut(); + let binary_archive: *mut MTLBinaryArchive = msg_send![self, newBinaryArchiveWithDescriptor:descriptor + error:&mut err]; + if !err.is_null() { + // TODO: copy pasta + let desc: *mut Object = msg_send![err, localizedDescription]; + let c_msg: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(c_msg).to_string_lossy().into_owned(); + Err(message) + } else { + Ok(BinaryArchive::from_ptr(binary_archive)) + } + } + } + + /// Synchronously creates a render pipeline state object and associated reflection information. + pub fn new_render_pipeline_state_with_reflection( + &self, + descriptor: &RenderPipelineDescriptorRef, + reflection_options: MTLPipelineOption, + ) -> Result<(RenderPipelineState, RenderPipelineReflection), String> { + unsafe { + let mut reflection: *mut Object = ptr::null_mut(); + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithDescriptor:descriptor + options:reflection_options + reflection:&mut reflection + error:&mut err] + }; + + let state = RenderPipelineState::from_ptr(pipeline_state); + + let () = msg_send![reflection, retain]; + let reflection = RenderPipelineReflection::from_ptr(reflection as _); + + Ok((state, reflection)) + } + } + + pub fn new_render_pipeline_state( + &self, + descriptor: &RenderPipelineDescriptorRef, + ) -> Result<RenderPipelineState, String> { + unsafe { + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithDescriptor:descriptor + error:&mut err] + }; + + Ok(RenderPipelineState::from_ptr(pipeline_state)) + } + } + + /// Only available on (macos(13.0), ios(16.0)) + pub fn new_mesh_render_pipeline_state_with_reflection( + &self, + descriptor: &MeshRenderPipelineDescriptorRef, + reflection_options: MTLPipelineOption, + ) -> Result<(RenderPipelineState, RenderPipelineReflection), String> { + unsafe { + let mut reflection: *mut Object = ptr::null_mut(); + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithMeshDescriptor:descriptor + options:reflection_options + reflection:&mut reflection + error:&mut err] + }; + + let state = RenderPipelineState::from_ptr(pipeline_state); + + let () = msg_send![reflection, retain]; + let reflection = RenderPipelineReflection::from_ptr(reflection as _); + + Ok((state, reflection)) + } + } + + /// Only available on (macos(13.0), ios(16.0)) + pub fn new_mesh_render_pipeline_state( + &self, + descriptor: &MeshRenderPipelineDescriptorRef, + ) -> Result<RenderPipelineState, String> { + unsafe { + let pipeline_state: *mut MTLRenderPipelineState = try_objc! { err => + msg_send![self, newRenderPipelineStateWithMeshDescriptor:descriptor + error:&mut err] + }; + + Ok(RenderPipelineState::from_ptr(pipeline_state)) + } + } + + pub fn new_compute_pipeline_state_with_function( + &self, + function: &FunctionRef, + ) -> Result<ComputePipelineState, String> { + unsafe { + let pipeline_state: *mut MTLComputePipelineState = try_objc! { err => + msg_send![self, newComputePipelineStateWithFunction:function + error:&mut err] + }; + + Ok(ComputePipelineState::from_ptr(pipeline_state)) + } + } + + pub fn new_compute_pipeline_state( + &self, + descriptor: &ComputePipelineDescriptorRef, + ) -> Result<ComputePipelineState, String> { + unsafe { + let pipeline_state: *mut MTLComputePipelineState = try_objc! { err => + msg_send![self, newComputePipelineStateWithDescriptor:descriptor + error:&mut err] + }; + + Ok(ComputePipelineState::from_ptr(pipeline_state)) + } + } + + /// Synchronously creates a compute pipeline state object and associated reflection information, + /// using a compute pipeline descriptor. + pub fn new_compute_pipeline_state_with_reflection( + &self, + descriptor: &ComputePipelineDescriptorRef, + reflection_options: MTLPipelineOption, + ) -> Result<(ComputePipelineState, ComputePipelineReflection), String> { + unsafe { + let mut reflection: *mut Object = ptr::null_mut(); + let pipeline_state: *mut MTLComputePipelineState = try_objc! { err => + msg_send![self, newComputePipelineStateWithDescriptor:descriptor + options:reflection_options + reflection:&mut reflection + error:&mut err] + }; + + let state = ComputePipelineState::from_ptr(pipeline_state); + + let () = msg_send![reflection, retain]; + let reflection = ComputePipelineReflection::from_ptr(reflection as _); + + Ok((state, reflection)) + } + } + + pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> Buffer { + unsafe { + msg_send![self, newBufferWithLength:length + options:options] + } + } + + pub fn new_buffer_with_bytes_no_copy( + &self, + bytes: *const std::ffi::c_void, + length: NSUInteger, + options: MTLResourceOptions, + deallocator: Option<&Block<(*const std::ffi::c_void, NSUInteger), ()>>, + ) -> Buffer { + unsafe { + msg_send![self, newBufferWithBytesNoCopy:bytes + length:length + options:options + deallocator:deallocator] + } + } + + pub fn new_buffer_with_data( + &self, + bytes: *const std::ffi::c_void, + length: NSUInteger, + options: MTLResourceOptions, + ) -> Buffer { + unsafe { + msg_send![self, newBufferWithBytes:bytes + length:length + options:options] + } + } + + pub fn new_counter_sample_buffer_with_descriptor( + &self, + descriptor: &CounterSampleBufferDescriptorRef, + ) -> Result<CounterSampleBuffer, String> { + unsafe { + let counter_sample_buffer: *mut MTLCounterSampleBuffer = try_objc! { err => + msg_send![self, newCounterSampleBufferWithDescriptor: descriptor error:&mut err] + }; + + assert!(!counter_sample_buffer.is_null()); + Ok(CounterSampleBuffer::from_ptr(counter_sample_buffer)) + } + } + + pub fn new_indirect_command_buffer_with_descriptor( + &self, + descriptor: &IndirectCommandBufferDescriptorRef, + max_command_count: NSUInteger, + options: MTLResourceOptions, + ) -> IndirectCommandBuffer { + unsafe { + msg_send![self, newIndirectCommandBufferWithDescriptor:descriptor + maxCommandCount:max_command_count + options:options] + } + } + + pub fn new_texture(&self, descriptor: &TextureDescriptorRef) -> Texture { + unsafe { msg_send![self, newTextureWithDescriptor: descriptor] } + } + + pub fn new_sampler(&self, descriptor: &SamplerDescriptorRef) -> SamplerState { + unsafe { msg_send![self, newSamplerStateWithDescriptor: descriptor] } + } + + pub fn new_depth_stencil_state( + &self, + descriptor: &DepthStencilDescriptorRef, + ) -> DepthStencilState { + unsafe { msg_send![self, newDepthStencilStateWithDescriptor: descriptor] } + } + + pub fn argument_buffers_support(&self) -> MTLArgumentBuffersTier { + unsafe { msg_send![self, argumentBuffersSupport] } + } + + pub fn read_write_texture_support(&self) -> MTLReadWriteTextureTier { + unsafe { msg_send![self, readWriteTextureSupport] } + } + + pub fn raster_order_groups_supported(&self) -> bool { + unsafe { msg_send_bool![self, rasterOrderGroupsSupported] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_32bit_float_filtering(&self) -> bool { + unsafe { msg_send_bool![self, supports32BitFloatFiltering] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_32bit_MSAA(&self) -> bool { + unsafe { msg_send_bool![self, supports32BitMSAA] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_query_texture_LOD(&self) -> bool { + unsafe { msg_send_bool![self, supportsQueryTextureLOD] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_BC_texture_compression(&self) -> bool { + unsafe { msg_send_bool![self, supportsBCTextureCompression] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn supports_pull_model_interpolation(&self) -> bool { + unsafe { msg_send_bool![self, supportsPullModelInterpolation] } + } + + pub fn new_argument_encoder( + &self, + arguments: &ArrayRef<ArgumentDescriptor>, + ) -> ArgumentEncoder { + unsafe { msg_send![self, newArgumentEncoderWithArguments: arguments] } + } + + pub fn new_heap(&self, descriptor: &HeapDescriptorRef) -> Heap { + unsafe { msg_send![self, newHeapWithDescriptor: descriptor] } + } + + pub fn new_event(&self) -> Event { + unsafe { msg_send![self, newEvent] } + } + + pub fn new_shared_event(&self) -> SharedEvent { + unsafe { msg_send![self, newSharedEvent] } + } + + pub fn heap_buffer_size_and_align( + &self, + length: NSUInteger, + options: MTLResourceOptions, + ) -> MTLSizeAndAlign { + unsafe { msg_send![self, heapBufferSizeAndAlignWithLength: length options: options] } + } + + pub fn heap_texture_size_and_align( + &self, + descriptor: &TextureDescriptorRef, + ) -> MTLSizeAndAlign { + unsafe { msg_send![self, heapTextureSizeAndAlignWithDescriptor: descriptor] } + } + + pub fn minimum_linear_texture_alignment_for_pixel_format( + &self, + format: MTLPixelFormat, + ) -> NSUInteger { + unsafe { msg_send![self, minimumLinearTextureAlignmentForPixelFormat: format] } + } + + pub fn minimum_texture_buffer_alignment_for_pixel_format( + &self, + format: MTLPixelFormat, + ) -> NSUInteger { + unsafe { msg_send![self, minimumTextureBufferAlignmentForPixelFormat: format] } + } + + pub fn max_argument_buffer_sampler_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxArgumentBufferSamplerCount] } + } + + pub fn current_allocated_size(&self) -> NSUInteger { + unsafe { msg_send![self, currentAllocatedSize] } + } + + /// Only available on (macos(10.14), ios(12.0), tvos(12.0)) + pub fn max_buffer_length(&self) -> NSUInteger { + unsafe { msg_send![self, maxBufferLength] } + } + + pub fn acceleration_structure_sizes_with_descriptor( + &self, + desc: &AccelerationStructureDescriptorRef, + ) -> MTLAccelerationStructureSizes { + unsafe { msg_send![self, accelerationStructureSizesWithDescriptor: desc] } + } + + pub fn new_acceleration_structure_with_size( + &self, + size: NSUInteger, + ) -> accelerator_structure::AccelerationStructure { + unsafe { msg_send![self, newAccelerationStructureWithSize: size] } + } + + pub fn sample_timestamps(&self, cpu_timestamp: &mut u64, gpu_timestamp: &mut u64) { + unsafe { msg_send![self, sampleTimestamps: cpu_timestamp gpuTimestamp: gpu_timestamp] } + } + + pub fn counter_sets(&self) -> Vec<CounterSet> { + unsafe { + let counter_sets: *mut Object = msg_send![self, counterSets]; + let count: NSUInteger = msg_send![counter_sets, count]; + let ret = (0..count) + .map(|i| { + let csp: *mut MTLCounterSet = msg_send![counter_sets, objectAtIndex: i]; + let () = msg_send![csp, retain]; + CounterSet::from_ptr(csp) + }) + .collect(); + ret + } + } +} diff --git a/third_party/rust/metal/src/drawable.rs b/third_party/rust/metal/src/drawable.rs new file mode 100644 index 0000000000..5c03584985 --- /dev/null +++ b/third_party/rust/metal/src/drawable.rs @@ -0,0 +1,26 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::NSUInteger; + +/// See <https://developer.apple.com/documentation/metal/mtldrawable> +pub enum MTLDrawable {} + +foreign_obj_type! { + type CType = MTLDrawable; + pub struct Drawable; +} + +impl DrawableRef { + pub fn present(&self) { + unsafe { msg_send![self, present] } + } + + pub fn drawable_id(&self) -> NSUInteger { + unsafe { msg_send![self, drawableID] } + } +} diff --git a/third_party/rust/metal/src/encoder.rs b/third_party/rust/metal/src/encoder.rs new file mode 100644 index 0000000000..7ecc56dade --- /dev/null +++ b/third_party/rust/metal/src/encoder.rs @@ -0,0 +1,1957 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use std::ops::Range; + +/// See <https://developer.apple.com/documentation/metal/mtlcounterdontsample> +pub const COUNTER_DONT_SAMPLE: NSUInteger = NSUInteger::MAX; // #define MTLCounterDontSample ((NSUInteger)-1) + +/// See <https://developer.apple.com/documentation/metal/mtlprimitivetype> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLPrimitiveType { + Point = 0, + Line = 1, + LineStrip = 2, + Triangle = 3, + TriangleStrip = 4, +} + +/// See <https://developer.apple.com/documentation/metal/mtlindextype> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLIndexType { + UInt16 = 0, + UInt32 = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlvisibilityresultmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLVisibilityResultMode { + Disabled = 0, + Boolean = 1, + Counting = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcullmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLCullMode { + None = 0, + Front = 1, + Back = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlwinding> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLWinding { + Clockwise = 0, + CounterClockwise = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtldepthclipmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLDepthClipMode { + Clip = 0, + Clamp = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtltrianglefillmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLTriangleFillMode { + Fill = 0, + Lines = 1, +} + +bitflags! { + /// https://developer.apple.com/documentation/metal/mtlblitoption + #[allow(non_upper_case_globals)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLBlitOption: NSUInteger { + /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionnone + const None = 0; + /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptiondepthfromdepthstencil + const DepthFromDepthStencil = 1 << 0; + /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionstencilfromdepthstencil + const StencilFromDepthStencil = 1 << 1; + /// https://developer.apple.com/documentation/metal/mtlblitoption/mtlblitoptionrowlinearpvrtc + const RowLinearPVRTC = 1 << 2; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlscissorrect> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MTLScissorRect { + pub x: NSUInteger, + pub y: NSUInteger, + pub width: NSUInteger, + pub height: NSUInteger, +} + +/// See <https://developer.apple.com/documentation/metal/mtlviewport> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MTLViewport { + pub originX: f64, + pub originY: f64, + pub width: f64, + pub height: f64, + pub znear: f64, + pub zfar: f64, +} + +/// See <https://developer.apple.com/documentation/metal/mtldrawprimitivesindirectarguments> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MTLDrawPrimitivesIndirectArguments { + pub vertexCount: u32, + pub instanceCount: u32, + pub vertexStart: u32, + pub baseInstance: u32, +} + +/// See <https://developer.apple.com/documentation/metal/mtldrawindexedprimitivesindirectarguments> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MTLDrawIndexedPrimitivesIndirectArguments { + pub indexCount: u32, + pub instanceCount: u32, + pub indexStart: u32, + pub baseVertex: i32, + pub baseInstance: u32, +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexamplificationviewmapping> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct VertexAmplificationViewMapping { + pub renderTargetArrayIndexOffset: u32, + pub viewportArrayIndexOffset: u32, +} + +#[allow(dead_code)] +type MTLVertexAmplicationViewMapping = VertexAmplificationViewMapping; + +/// See <https://developer.apple.com/documentation/metal/mtlcommandencoder> +pub enum MTLCommandEncoder {} + +foreign_obj_type! { + type CType = MTLCommandEncoder; + pub struct CommandEncoder; +} + +impl CommandEncoderRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + msg_send![self, setLabel: nslabel] + } + } + + pub fn end_encoding(&self) { + unsafe { msg_send![self, endEncoding] } + } + + pub fn insert_debug_signpost(&self, name: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(name); + msg_send![self, insertDebugSignpost: nslabel] + } + } + + pub fn push_debug_group(&self, name: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(name); + msg_send![self, pushDebugGroup: nslabel] + } + } + + pub fn pop_debug_group(&self) { + unsafe { msg_send![self, popDebugGroup] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlparallelrendercommandencoder> +pub enum MTLParallelRenderCommandEncoder {} + +foreign_obj_type! { + type CType = MTLParallelRenderCommandEncoder; + pub struct ParallelRenderCommandEncoder; + type ParentType = CommandEncoder; +} + +impl ParallelRenderCommandEncoderRef { + pub fn render_command_encoder(&self) -> &RenderCommandEncoderRef { + unsafe { msg_send![self, renderCommandEncoder] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrendercommandencoder/> +pub enum MTLRenderCommandEncoder {} + +foreign_obj_type! { + type CType = MTLRenderCommandEncoder; + pub struct RenderCommandEncoder; + type ParentType = CommandEncoder; +} + +impl RenderCommandEncoderRef { + pub fn set_render_pipeline_state(&self, pipeline_state: &RenderPipelineStateRef) { + unsafe { msg_send![self, setRenderPipelineState: pipeline_state] } + } + + pub fn set_viewport(&self, viewport: MTLViewport) { + unsafe { msg_send![self, setViewport: viewport] } + } + + pub fn set_front_facing_winding(&self, winding: MTLWinding) { + unsafe { msg_send![self, setFrontFacingWinding: winding] } + } + + pub fn set_cull_mode(&self, mode: MTLCullMode) { + unsafe { msg_send![self, setCullMode: mode] } + } + + pub fn set_depth_clip_mode(&self, mode: MTLDepthClipMode) { + unsafe { msg_send![self, setDepthClipMode: mode] } + } + + pub fn set_depth_bias(&self, bias: f32, scale: f32, clamp: f32) { + unsafe { + msg_send![self, setDepthBias:bias + slopeScale:scale + clamp:clamp] + } + } + + pub fn set_scissor_rect(&self, rect: MTLScissorRect) { + unsafe { msg_send![self, setScissorRect: rect] } + } + + pub fn set_triangle_fill_mode(&self, mode: MTLTriangleFillMode) { + unsafe { msg_send![self, setTriangleFillMode: mode] } + } + + pub fn set_blend_color(&self, red: f32, green: f32, blue: f32, alpha: f32) { + unsafe { + msg_send![self, setBlendColorRed:red + green:green + blue:blue + alpha:alpha] + } + } + + pub fn set_depth_stencil_state(&self, depth_stencil_state: &DepthStencilStateRef) { + unsafe { msg_send![self, setDepthStencilState: depth_stencil_state] } + } + + pub fn set_stencil_reference_value(&self, value: u32) { + unsafe { msg_send![self, setStencilReferenceValue: value] } + } + + pub fn set_stencil_front_back_reference_value(&self, front: u32, back: u32) { + unsafe { + msg_send![self, setStencilFrontReferenceValue:front + backReferenceValue:back] + } + } + + pub fn set_visibility_result_mode(&self, mode: MTLVisibilityResultMode, offset: NSUInteger) { + unsafe { + msg_send![self, setVisibilityResultMode:mode + offset:offset] + } + } + + pub fn set_vertex_amplification_count( + &self, + count: NSUInteger, + view_mappings: Option<&[VertexAmplificationViewMapping]>, + ) { + unsafe { + msg_send! [self, setVertexAmplificationCount: count viewMappings: view_mappings.map_or(std::ptr::null(), |vm| vm.as_ptr())] + } + } + + // Specifying Resources for a Vertex Shader Function + + pub fn set_vertex_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setVertexBytes:bytes + length:length + atIndex:index + ] + } + } + + pub fn set_vertex_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setVertexBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + pub fn set_vertex_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setVertexBufferOffset:offset + atIndex:index + ] + } + } + + pub fn set_vertex_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setVertexBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_vertex_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setVertexTexture:texture + atIndex:index + ] + } + } + + pub fn set_vertex_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setVertexTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_vertex_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setVertexSamplerState:sampler + atIndex:index + ] + } + } + + pub fn set_vertex_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setVertexSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_vertex_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range<f32>, + ) { + unsafe { + msg_send![self, + setVertexSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(12.0), ios(15.0)) + pub fn set_vertex_acceleration_structure( + &self, + index: NSUInteger, + accel: Option<&accelerator_structure::AccelerationStructureRef>, + ) { + unsafe { + msg_send![ + self, + setVertexAccelerationStructure: accel + atBufferIndex: index + ] + } + } + + /// Only available in (macos(12.0), ios(15.0)) + pub fn set_vertex_intersection_function_table( + &self, + index: NSUInteger, + table: Option<&IntersectionFunctionTableRef>, + ) { + unsafe { + msg_send![ + self, + setVertexIntersectionFunctionTable: table + atBufferIndex: index + ] + } + } + + // Specifying Resources for a Object Shader Function + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setObjectBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setObjectBufferOffset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setObjectBytes:bytes + length:length + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setObjectSamplerState:sampler + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range<f32>, + ) { + unsafe { + msg_send![self, + setObjectSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setObjectTexture:texture + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_threadgroup_memory_length(&self, index: NSUInteger, length: NSUInteger) { + unsafe { + msg_send![self, + setObjectThreadgroupMemoryLength: length + atIndex: index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setObjectBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setObjectSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_object_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setObjectTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + // Specifying Resources for a Mesh Shader + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setMeshBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setMeshBufferOffset:offset + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setMeshBytes:bytes + length:length + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setMeshSamplerState:sampler + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range<f32>, + ) { + unsafe { + msg_send![self, + setMeshSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setMeshTexture:texture + atIndex:index + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setMeshBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setMeshSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn set_mesh_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setMeshTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + // Specifying Resources for a Fragment Shader Function + + pub fn set_fragment_bytes( + &self, + index: NSUInteger, + length: NSUInteger, + bytes: *const std::ffi::c_void, + ) { + unsafe { + msg_send![self, + setFragmentBytes:bytes + length:length + atIndex:index + ] + } + } + + pub fn set_fragment_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setFragmentBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + pub fn set_fragment_buffer_offset(&self, index: NSUInteger, offset: NSUInteger) { + unsafe { + msg_send![self, + setFragmentBufferOffset:offset + atIndex:index + ] + } + } + + pub fn set_fragment_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setFragmentBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_fragment_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setFragmentTexture:texture + atIndex:index + ] + } + } + + pub fn set_fragment_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setFragmentTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_fragment_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, setFragmentSamplerState:sampler + atIndex:index] + } + } + + pub fn set_fragment_sampler_states( + &self, + start_index: NSUInteger, + data: &[Option<&SamplerStateRef>], + ) { + unsafe { + msg_send![self, + setFragmentSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_fragment_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range<f32>, + ) { + unsafe { + msg_send![self, + setFragmentSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + /// Only available in (macos(12.0), ios(15.0)) + pub fn set_fragment_acceleration_structure( + &self, + index: NSUInteger, + accel: Option<&accelerator_structure::AccelerationStructureRef>, + ) { + unsafe { + msg_send![ + self, + setFragmentAccelerationStructure: accel + atBufferIndex: index + ] + } + } + + /// Only available in (macos(12.0), ios(15.0)) + pub fn set_fragment_intersection_function_table( + &self, + index: NSUInteger, + table: Option<&IntersectionFunctionTableRef>, + ) { + unsafe { + msg_send![ + self, + setFragmentIntersectionFunctionTable: table + atBufferIndex: index + ] + } + } + + // Drawing Geometric Primitives + + pub fn draw_primitives( + &self, + primitive_type: MTLPrimitiveType, + vertex_start: NSUInteger, + vertex_count: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPrimitives: primitive_type + vertexStart: vertex_start + vertexCount: vertex_count + ] + } + } + + pub fn draw_primitives_instanced( + &self, + primitive_type: MTLPrimitiveType, + vertex_start: NSUInteger, + vertex_count: NSUInteger, + instance_count: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPrimitives: primitive_type + vertexStart: vertex_start + vertexCount: vertex_count + instanceCount: instance_count + ] + } + } + + pub fn draw_primitives_instanced_base_instance( + &self, + primitive_type: MTLPrimitiveType, + vertex_start: NSUInteger, + vertex_count: NSUInteger, + instance_count: NSUInteger, + base_instance: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPrimitives: primitive_type + vertexStart: vertex_start + vertexCount: vertex_count + instanceCount: instance_count + baseInstance: base_instance + ] + } + } + + pub fn draw_primitives_indirect( + &self, + primitive_type: MTLPrimitiveType, + indirect_buffer: &BufferRef, + indirect_buffer_offset: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPrimitives: primitive_type + indirectBuffer: indirect_buffer + indirectBufferOffset: indirect_buffer_offset + ] + } + } + + pub fn draw_indexed_primitives( + &self, + primitive_type: MTLPrimitiveType, + index_count: NSUInteger, + index_type: MTLIndexType, + index_buffer: &BufferRef, + index_buffer_offset: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPrimitives: primitive_type + indexCount: index_count + indexType: index_type + indexBuffer: index_buffer + indexBufferOffset: index_buffer_offset + ] + } + } + + pub fn draw_indexed_primitives_instanced( + &self, + primitive_type: MTLPrimitiveType, + index_count: NSUInteger, + index_type: MTLIndexType, + index_buffer: &BufferRef, + index_buffer_offset: NSUInteger, + instance_count: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPrimitives: primitive_type + indexCount: index_count + indexType: index_type + indexBuffer: index_buffer + indexBufferOffset: index_buffer_offset + instanceCount: instance_count + ] + } + } + + pub fn draw_indexed_primitives_instanced_base_instance( + &self, + primitive_type: MTLPrimitiveType, + index_count: NSUInteger, + index_type: MTLIndexType, + index_buffer: &BufferRef, + index_buffer_offset: NSUInteger, + instance_count: NSUInteger, + base_vertex: NSInteger, + base_instance: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPrimitives: primitive_type + indexCount: index_count + indexType: index_type + indexBuffer: index_buffer + indexBufferOffset: index_buffer_offset + instanceCount: instance_count + baseVertex: base_vertex + baseInstance: base_instance + ] + } + } + + pub fn draw_indexed_primitives_indirect( + &self, + primitive_type: MTLPrimitiveType, + index_type: MTLIndexType, + index_buffer: &BufferRef, + index_buffer_offset: NSUInteger, + indirect_buffer: &BufferRef, + indirect_buffer_offset: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPrimitives: primitive_type + indexType: index_type + indexBuffer: index_buffer + indexBufferOffset: index_buffer_offset + indirectBuffer: indirect_buffer + indirectBufferOffset: indirect_buffer_offset + ] + } + } + + // TODO: more draws + // fn setVertexBufferOffset_atIndex(self, offset: NSUInteger, index: NSUInteger); + // fn setVertexBuffers_offsets_withRange(self, buffers: *const id, offsets: *const NSUInteger, range: NSRange); + // fn setVertexSamplerStates_lodMinClamps_lodMaxClamps_withRange(self, samplers: *const id, lodMinClamps: *const f32, lodMaxClamps: *const f32, range: NSRange); + + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threadgroups( + &self, + threadgroups_per_grid: MTLSize, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreadgroups: threadgroups_per_grid + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threadgroups_with_indirect_buffer( + &self, + indirect_buffer: &BufferRef, + indirect_buffer_offset: NSUInteger, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreadgroupsWithIndirectBuffer: indirect_buffer + indirectBufferOffset: indirect_buffer_offset + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + + /// Only available in (macos(13.0), ios(16.0)) + pub fn draw_mesh_threads( + &self, + threads_per_grid: MTLSize, + threads_per_object_threadgroup: MTLSize, + threads_per_mesh_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + drawMeshThreads: threads_per_grid + threadsPerObjectThreadgroup: threads_per_object_threadgroup + threadsPerMeshThreadgroup: threads_per_mesh_threadgroup + ] + } + } + + /// Adds an untracked resource to the render pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// # Arguments + /// * `resource`: A resource within an argument buffer. + /// * `usage`: Options for describing how a graphics function uses the resource. + /// + /// See <https://developer.apple.com/documentation/metal/mtlrendercommandencoder/2866168-useresource?language=objc> + #[deprecated(note = "Use use_resource_at instead")] + pub fn use_resource(&self, resource: &ResourceRef, usage: MTLResourceUsage) { + unsafe { + msg_send![self, + useResource:resource + usage:usage + ] + } + } + + /// Adds an untracked resource to the render pass, specifying which render stages need it. + /// + /// Availability: iOS 13.0+, macOS 10.15+ + /// + /// # Arguments + /// * `resource`: A resource within an argument buffer. + /// * `usage`: Options for describing how a graphics function uses the resource. + /// * `stages`: The render stages where the resource must be resident. + /// + /// See <https://developer.apple.com/documentation/metal/mtlrendercommandencoder/3043404-useresource> + pub fn use_resource_at( + &self, + resource: &ResourceRef, + usage: MTLResourceUsage, + stages: MTLRenderStages, + ) { + unsafe { + msg_send![self, + useResource: resource + usage: usage + stages: stages + ] + } + } + + /// Adds an array of untracked resources to the render pass, specifying which stages need them. + /// + /// When working with color render targets, call this method as late as possible to improve performance. + /// + /// Availability: iOS 13.0+, macOS 10.15+ + /// + /// # Arguments + /// * `resources`: A slice of resources within an argument buffer. + /// * `usage`: Options for describing how a graphics function uses the resources. + /// * `stages`: The render stages where the resources must be resident. + pub fn use_resources( + &self, + resources: &[&ResourceRef], + usage: MTLResourceUsage, + stages: MTLRenderStages, + ) { + unsafe { + msg_send![self, + useResources: resources.as_ptr() + count: resources.len() as NSUInteger + usage: usage + stages: stages + ] + } + } + + /// Adds the resources in a heap to the render pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// # Arguments: + /// * `heap`: A heap that contains resources within an argument buffer. + /// + /// See <https://developer.apple.com/documentation/metal/mtlrendercommandencoder/2866163-useheap?language=objc> + #[deprecated(note = "Use use_heap_at instead")] + pub fn use_heap(&self, heap: &HeapRef) { + unsafe { msg_send![self, useHeap: heap] } + } + + /// Adds the resources in a heap to the render pass, specifying which render stages need them. + /// + /// Availability: iOS 13.0+, macOS 10.15+ + /// + /// # Arguments + /// * `heap`: A heap that contains resources within an argument buffer. + /// * `stages`: The render stages where the resources must be resident. + /// + pub fn use_heap_at(&self, heap: &HeapRef, stages: MTLRenderStages) { + unsafe { + msg_send![self, + useHeap: heap + stages: stages + ] + } + } + + /// Adds the resources in an array of heaps to the render pass, specifying which render stages need them. + /// + /// Availability: iOS 13.0+, macOS 10.15+ + /// + /// # Arguments + /// + /// * `heaps`: A slice of heaps that contains resources within an argument buffer. + /// * `stages`: The render stages where the resources must be resident. + pub fn use_heaps(&self, heaps: &[&HeapRef], stages: MTLRenderStages) { + unsafe { + msg_send![self, + useHeaps: heaps.as_ptr() + count: heaps.len() as NSUInteger + stages: stages + ] + } + } + + pub fn execute_commands_in_buffer( + &self, + buffer: &IndirectCommandBufferRef, + with_range: NSRange, + ) { + unsafe { msg_send![self, executeCommandsInBuffer:buffer withRange:with_range] } + } + + pub fn update_fence(&self, fence: &FenceRef, after_stages: MTLRenderStages) { + unsafe { + msg_send![self, + updateFence: fence + afterStages: after_stages + ] + } + } + + pub fn wait_for_fence(&self, fence: &FenceRef, before_stages: MTLRenderStages) { + unsafe { + msg_send![self, + waitForFence: fence + beforeStages: before_stages + ] + } + } + + /// See: <https://developer.apple.com/documentation/metal/mtlrendercommandencoder/3194379-samplecountersinbuffer> + pub fn sample_counters_in_buffer( + &self, + sample_buffer: &CounterSampleBufferRef, + sample_index: NSUInteger, + with_barrier: bool, + ) { + unsafe { + msg_send![self, + sampleCountersInBuffer: sample_buffer + atSampleIndex: sample_index + withBarrier: with_barrier + ] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlblitcommandencoder/> +pub enum MTLBlitCommandEncoder {} + +foreign_obj_type! { + type CType = MTLBlitCommandEncoder; + pub struct BlitCommandEncoder; + type ParentType = CommandEncoder; +} + +impl BlitCommandEncoderRef { + pub fn synchronize_resource(&self, resource: &ResourceRef) { + unsafe { msg_send![self, synchronizeResource: resource] } + } + + pub fn fill_buffer(&self, destination_buffer: &BufferRef, range: crate::NSRange, value: u8) { + unsafe { + msg_send![self, + fillBuffer: destination_buffer + range: range + value: value + ] + } + } + + pub fn generate_mipmaps(&self, texture: &TextureRef) { + unsafe { msg_send![self, generateMipmapsForTexture: texture] } + } + + pub fn copy_from_buffer( + &self, + source_buffer: &BufferRef, + source_offset: NSUInteger, + destination_buffer: &BufferRef, + destination_offset: NSUInteger, + size: NSUInteger, + ) { + unsafe { + msg_send![self, + copyFromBuffer: source_buffer + sourceOffset: source_offset + toBuffer: destination_buffer + destinationOffset: destination_offset + size: size + ] + } + } + + pub fn copy_from_texture( + &self, + source_texture: &TextureRef, + source_slice: NSUInteger, + source_level: NSUInteger, + source_origin: MTLOrigin, + source_size: MTLSize, + destination_texture: &TextureRef, + destination_slice: NSUInteger, + destination_level: NSUInteger, + destination_origin: MTLOrigin, + ) { + unsafe { + msg_send![self, + copyFromTexture: source_texture + sourceSlice: source_slice + sourceLevel: source_level + sourceOrigin: source_origin + sourceSize: source_size + toTexture: destination_texture + destinationSlice: destination_slice + destinationLevel: destination_level + destinationOrigin: destination_origin + ] + } + } + + pub fn copy_from_buffer_to_texture( + &self, + source_buffer: &BufferRef, + source_offset: NSUInteger, + source_bytes_per_row: NSUInteger, + source_bytes_per_image: NSUInteger, + source_size: MTLSize, + destination_texture: &TextureRef, + destination_slice: NSUInteger, + destination_level: NSUInteger, + destination_origin: MTLOrigin, + options: MTLBlitOption, + ) { + unsafe { + msg_send![self, + copyFromBuffer: source_buffer + sourceOffset: source_offset + sourceBytesPerRow: source_bytes_per_row + sourceBytesPerImage: source_bytes_per_image + sourceSize: source_size + toTexture: destination_texture + destinationSlice: destination_slice + destinationLevel: destination_level + destinationOrigin: destination_origin + options: options + ] + } + } + + /// See <https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copy> + pub fn copy_from_texture_to_buffer( + &self, + source_texture: &TextureRef, + source_slice: NSUInteger, + source_level: NSUInteger, + source_origin: MTLOrigin, + source_size: MTLSize, + destination_buffer: &BufferRef, + destination_offset: NSUInteger, + destination_bytes_per_row: NSUInteger, + destination_bytes_per_image: NSUInteger, + options: MTLBlitOption, + ) { + unsafe { + msg_send![self, + copyFromTexture: source_texture + sourceSlice: source_slice + sourceLevel: source_level + sourceOrigin: source_origin + sourceSize: source_size + toBuffer: destination_buffer + destinationOffset: destination_offset + destinationBytesPerRow: destination_bytes_per_row + destinationBytesPerImage: destination_bytes_per_image + options: options + ] + } + } + + pub fn optimize_contents_for_gpu_access(&self, texture: &TextureRef) { + unsafe { msg_send![self, optimizeContentsForGPUAccess: texture] } + } + + pub fn optimize_contents_for_gpu_access_slice_level( + &self, + texture: &TextureRef, + slice: NSUInteger, + level: NSUInteger, + ) { + unsafe { + msg_send![self, + optimizeContentsForGPUAccess: texture + slice: slice + level: level + ] + } + } + + pub fn optimize_contents_for_cpu_access(&self, texture: &TextureRef) { + unsafe { msg_send![self, optimizeContentsForCPUAccess: texture] } + } + + pub fn optimize_contents_for_cpu_access_slice_level( + &self, + texture: &TextureRef, + slice: NSUInteger, + level: NSUInteger, + ) { + unsafe { + msg_send![self, + optimizeContentsForCPUAccess: texture + slice: slice + level: level + ] + } + } + + pub fn update_fence(&self, fence: &FenceRef) { + unsafe { msg_send![self, updateFence: fence] } + } + + pub fn wait_for_fence(&self, fence: &FenceRef) { + unsafe { msg_send![self, waitForFence: fence] } + } + + pub fn copy_indirect_command_buffer( + &self, + source: &IndirectCommandBufferRef, + source_range: NSRange, + destination: &IndirectCommandBufferRef, + destination_index: NSUInteger, + ) { + unsafe { + msg_send![self, + copyIndirectCommandBuffer: source + sourceRange: source_range + destination: destination + destinationIndex: destination_index + ] + } + } + + pub fn reset_commands_in_buffer(&self, buffer: &IndirectCommandBufferRef, range: NSRange) { + unsafe { + msg_send![self, + resetCommandsInBuffer: buffer + withRange: range + ] + } + } + + pub fn optimize_indirect_command_buffer( + &self, + buffer: &IndirectCommandBufferRef, + range: NSRange, + ) { + unsafe { + msg_send![self, + optimizeIndirectCommandBuffer: buffer + withRange: range + ] + } + } + + /// See: <https://developer.apple.com/documentation/metal/mtlblitcommandencoder/3194348-samplecountersinbuffer> + pub fn sample_counters_in_buffer( + &self, + sample_buffer: &CounterSampleBufferRef, + sample_index: NSUInteger, + with_barrier: bool, + ) { + unsafe { + msg_send![self, + sampleCountersInBuffer: sample_buffer + atSampleIndex: sample_index + withBarrier: with_barrier + ] + } + } + + /// See: <https://developer.apple.com/documentation/metal/mtlblitcommandencoder/3194347-resolvecounters> + pub fn resolve_counters( + &self, + sample_buffer: &CounterSampleBufferRef, + range: crate::NSRange, + destination_buffer: &BufferRef, + destination_offset: NSUInteger, + ) { + unsafe { + msg_send![self, + resolveCounters: sample_buffer + inRange: range + destinationBuffer: destination_buffer + destinationOffset: destination_offset + ] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/> +pub enum MTLComputeCommandEncoder {} + +foreign_obj_type! { + type CType = MTLComputeCommandEncoder; + pub struct ComputeCommandEncoder; + type ParentType = CommandEncoder; +} + +impl ComputeCommandEncoderRef { + pub fn set_compute_pipeline_state(&self, state: &ComputePipelineStateRef) { + unsafe { msg_send![self, setComputePipelineState: state] } + } + + pub fn set_buffer(&self, index: NSUInteger, buffer: Option<&BufferRef>, offset: NSUInteger) { + unsafe { msg_send![self, setBuffer:buffer offset:offset atIndex:index] } + } + + pub fn set_buffers( + &self, + start_index: NSUInteger, + data: &[Option<&BufferRef>], + offsets: &[NSUInteger], + ) { + debug_assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_texture(&self, index: NSUInteger, texture: Option<&TextureRef>) { + unsafe { + msg_send![self, + setTexture:texture + atIndex:index + ] + } + } + + pub fn set_textures(&self, start_index: NSUInteger, data: &[Option<&TextureRef>]) { + unsafe { + msg_send![self, + setTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_sampler_state(&self, index: NSUInteger, sampler: Option<&SamplerStateRef>) { + unsafe { + msg_send![self, + setSamplerState:sampler + atIndex:index + ] + } + } + + pub fn set_sampler_states(&self, start_index: NSUInteger, data: &[Option<&SamplerStateRef>]) { + unsafe { + msg_send![self, + setSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_sampler_state_with_lod( + &self, + index: NSUInteger, + sampler: Option<&SamplerStateRef>, + lod_clamp: Range<f32>, + ) { + unsafe { + msg_send![self, + setSamplerState:sampler + lodMinClamp:lod_clamp.start + lodMaxClamp:lod_clamp.end + atIndex:index + ] + } + } + + pub fn set_bytes(&self, index: NSUInteger, length: NSUInteger, bytes: *const std::ffi::c_void) { + unsafe { + msg_send![self, + setBytes: bytes + length: length + atIndex: index + ] + } + } + + pub fn dispatch_thread_groups( + &self, + thread_groups_count: MTLSize, + threads_per_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + dispatchThreadgroups:thread_groups_count + threadsPerThreadgroup:threads_per_threadgroup + ] + } + } + + pub fn dispatch_threads(&self, threads_per_grid: MTLSize, threads_per_thread_group: MTLSize) { + unsafe { + msg_send![self, + dispatchThreads:threads_per_grid + threadsPerThreadgroup:threads_per_thread_group + ] + } + } + + pub fn dispatch_thread_groups_indirect( + &self, + buffer: &BufferRef, + offset: NSUInteger, + threads_per_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + dispatchThreadgroupsWithIndirectBuffer:buffer + indirectBufferOffset:offset + threadsPerThreadgroup:threads_per_threadgroup + ] + } + } + + pub fn set_threadgroup_memory_length(&self, at_index: NSUInteger, size: NSUInteger) { + unsafe { + msg_send![self, + setThreadgroupMemoryLength:size + atIndex: at_index + ] + } + } + + /// Encodes a barrier so that changes to a set of resources made by commands encoded before the + /// barrier are completed before further commands are executed. + /// + /// Availability: iOS 12.0+, macOS 10.14+ + /// + /// # Arguments + /// * `resources`: A slice of resources. + pub fn memory_barrier_with_resources(&self, resources: &[&ResourceRef]) { + unsafe { + msg_send![self, + memoryBarrierWithResources: resources.as_ptr() + count: resources.len() as NSUInteger + ] + } + } + + /// Specifies that a resource in an argument buffer can be safely used by a compute pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// # Arguments + /// * `resource`: A specific resource within an argument buffer. + /// * `usage`: The options that describe how the resource will be used by a compute function. + pub fn use_resource(&self, resource: &ResourceRef, usage: MTLResourceUsage) { + unsafe { + msg_send![self, + useResource: resource + usage: usage + ] + } + } + + /// Specifies that an array of resources in an argument buffer can be safely used by a compute pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// See <https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/2866561-useresources> + /// + /// # Arguments + /// * `resources`: A slice of resources within an argument buffer. + /// * `usage`: The options that describe how the array of resources will be used by a compute function. + pub fn use_resources(&self, resources: &[&ResourceRef], usage: MTLResourceUsage) { + unsafe { + msg_send![self, + useResources: resources.as_ptr() + count: resources.len() as NSUInteger + usage: usage + ] + } + } + + /// Specifies that a heap containing resources in an argument buffer can be safely used by a compute pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// See <https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/2866530-useheap> + /// + /// # Arguments + /// * `heap`: A heap that contains resources within an argument buffer. + pub fn use_heap(&self, heap: &HeapRef) { + unsafe { msg_send![self, useHeap: heap] } + } + + /// Specifies that an array of heaps containing resources in an argument buffer can be safely + /// used by a compute pass. + /// + /// Availability: iOS 11.0+, macOS 10.13+ + /// + /// # Arguments + /// * `heaps`: A slice of heaps that contains resources within an argument buffer. + pub fn use_heaps(&self, heaps: &[&HeapRef]) { + unsafe { + msg_send![self, + useHeaps: heaps.as_ptr() + count: heaps.len() as NSUInteger + ] + } + } + + pub fn update_fence(&self, fence: &FenceRef) { + unsafe { msg_send![self, updateFence: fence] } + } + + pub fn wait_for_fence(&self, fence: &FenceRef) { + unsafe { msg_send![self, waitForFence: fence] } + } + + /// Only available in (macos(11.0), ios(14.0)) + pub fn set_acceleration_structure( + &self, + index: NSUInteger, + accel: Option<&accelerator_structure::AccelerationStructureRef>, + ) { + unsafe { + msg_send![ + self, + setAccelerationStructure: accel + atBufferIndex: index + ] + } + } + + /// Only available in (macos(11.0), ios(14.0)) + pub fn set_intersection_function_table( + &self, + index: NSUInteger, + table: Option<&IntersectionFunctionTableRef>, + ) { + unsafe { + msg_send![ + self, + setIntersectionFunctionTable: table + atBufferIndex: index + ] + } + } + + /// See: <https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/3194349-samplecountersinbuffer> + pub fn sample_counters_in_buffer( + &self, + sample_buffer: &CounterSampleBufferRef, + sample_index: NSUInteger, + with_barrier: bool, + ) { + unsafe { + msg_send![self, + sampleCountersInBuffer: sample_buffer + atSampleIndex: sample_index + withBarrier: with_barrier + ] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlargumentencoder/> +pub enum MTLArgumentEncoder {} + +foreign_obj_type! { + type CType = MTLArgumentEncoder; + pub struct ArgumentEncoder; +} + +impl ArgumentEncoderRef { + pub fn encoded_length(&self) -> NSUInteger { + unsafe { msg_send![self, encodedLength] } + } + + pub fn alignment(&self) -> NSUInteger { + unsafe { msg_send![self, alignment] } + } + + pub fn set_argument_buffer(&self, buffer: &BufferRef, offset: NSUInteger) { + unsafe { + msg_send![self, + setArgumentBuffer: buffer + offset: offset + ] + } + } + + pub fn set_argument_buffer_to_element( + &self, + array_element: NSUInteger, + buffer: &BufferRef, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setArgumentBuffer: buffer + startOffset: offset + arrayElement: array_element + ] + } + } + + pub fn set_buffer(&self, at_index: NSUInteger, buffer: &BufferRef, offset: NSUInteger) { + unsafe { + msg_send![self, + setBuffer: buffer + offset: offset + atIndex: at_index + ] + } + } + + pub fn set_buffers( + &self, + start_index: NSUInteger, + data: &[&BufferRef], + offsets: &[NSUInteger], + ) { + assert_eq!(offsets.len(), data.len()); + unsafe { + msg_send![self, + setBuffers: data.as_ptr() + offsets: offsets.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_texture(&self, at_index: NSUInteger, texture: &TextureRef) { + unsafe { + msg_send![self, + setTexture: texture + atIndex: at_index + ] + } + } + + pub fn set_textures(&self, start_index: NSUInteger, data: &[&TextureRef]) { + unsafe { + msg_send![self, + setTextures: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_sampler_state(&self, at_index: NSUInteger, sampler_state: &SamplerStateRef) { + unsafe { + msg_send![self, + setSamplerState: sampler_state + atIndex: at_index + ] + } + } + + pub fn set_sampler_states(&self, start_index: NSUInteger, data: &[&SamplerStateRef]) { + unsafe { + msg_send![self, + setSamplerStates: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn set_render_pipeline_state( + &self, + at_index: NSUInteger, + pipeline: &RenderPipelineStateRef, + ) { + unsafe { + msg_send![self, + setRenderPipelineState: pipeline + atIndex: at_index + ] + } + } + + pub fn set_render_pipeline_states( + &self, + start_index: NSUInteger, + pipelines: &[&RenderPipelineStateRef], + ) { + unsafe { + msg_send![self, + setRenderPipelineStates: pipelines.as_ptr() + withRange: NSRange { + location: start_index, + length: pipelines.len() as _, + } + ] + } + } + + pub fn constant_data(&self, at_index: NSUInteger) -> *mut std::ffi::c_void { + unsafe { msg_send![self, constantDataAtIndex: at_index] } + } + + pub fn set_indirect_command_buffer( + &self, + at_index: NSUInteger, + buffer: &IndirectCommandBufferRef, + ) { + unsafe { + msg_send![self, + setIndirectCommandBuffer: buffer + atIndex: at_index + ] + } + } + + pub fn set_indirect_command_buffers( + &self, + start_index: NSUInteger, + data: &[&IndirectCommandBufferRef], + ) { + unsafe { + msg_send![self, + setIndirectCommandBuffers: data.as_ptr() + withRange: NSRange { + location: start_index, + length: data.len() as _, + } + ] + } + } + + pub fn new_argument_encoder_for_buffer(&self, index: NSUInteger) -> ArgumentEncoder { + unsafe { + let ptr = msg_send![self, newArgumentEncoderForBufferAtIndex: index]; + ArgumentEncoder::from_ptr(ptr) + } + } +} diff --git a/third_party/rust/metal/src/heap.rs b/third_party/rust/metal/src/heap.rs new file mode 100644 index 0000000000..d2c11bbcf6 --- /dev/null +++ b/third_party/rust/metal/src/heap.rs @@ -0,0 +1,209 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +/// Only available on macos(10.15), ios(13.0) +/// +/// See <https://developer.apple.com/documentation/metal/mtlheaptype/> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLHeapType { + Automatic = 0, + Placement = 1, + /// Only available on macos(11.0), macCatalyst(14.0) + Sparse = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlheap/> +pub enum MTLHeap {} + +foreign_obj_type! { + type CType = MTLHeap; + pub struct Heap; +} + +impl HeapRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn cpu_cache_mode(&self) -> MTLCPUCacheMode { + unsafe { msg_send![self, cpuCacheMode] } + } + + pub fn storage_mode(&self) -> MTLStorageMode { + unsafe { msg_send![self, storageMode] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn hazard_tracking_mode(&self) -> MTLHazardTrackingMode { + unsafe { msg_send![self, hazardTrackingMode] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn resource_options(&self) -> MTLResourceOptions { + unsafe { msg_send![self, resourceOptions] } + } + + pub fn set_purgeable_state(&self, state: MTLPurgeableState) -> MTLPurgeableState { + unsafe { msg_send![self, setPurgeableState: state] } + } + + pub fn size(&self) -> NSUInteger { + unsafe { msg_send![self, size] } + } + + pub fn used_size(&self) -> NSUInteger { + unsafe { msg_send![self, usedSize] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn heap_type(&self) -> MTLHeapType { + unsafe { msg_send![self, type] } + } + + /// Only available on macos(10.13), ios(11.0) + pub fn current_allocated_size(&self) -> NSUInteger { + unsafe { msg_send![self, currentAllocatedSize] } + } + + pub fn max_available_size_with_alignment(&self, alignment: NSUInteger) -> NSUInteger { + unsafe { msg_send![self, maxAvailableSizeWithAlignment: alignment] } + } + + pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> Option<Buffer> { + unsafe { + let ptr: *mut MTLBuffer = msg_send![self, newBufferWithLength:length + options:options]; + if !ptr.is_null() { + Some(Buffer::from_ptr(ptr)) + } else { + None + } + } + } + + pub fn new_texture(&self, descriptor: &TextureDescriptorRef) -> Option<Texture> { + unsafe { + let ptr: *mut MTLTexture = msg_send![self, newTextureWithDescriptor: descriptor]; + if !ptr.is_null() { + Some(Texture::from_ptr(ptr)) + } else { + None + } + } + } + + /// Only available on macOS 10.15+ & iOS 13.0+ + pub fn new_buffer_with_offset( + &self, + length: u64, + options: MTLResourceOptions, + offset: u64, + ) -> Option<Buffer> { + unsafe { + let ptr: *mut MTLBuffer = msg_send![self, newBufferWithLength:length + options:options + offset:offset]; + if !ptr.is_null() { + Some(Buffer::from_ptr(ptr)) + } else { + None + } + } + } + + /// Only available on macOS 10.15+ & iOS 13.0+ + pub fn new_texture_with_offset( + &self, + descriptor: &TextureDescriptorRef, + offset: u64, + ) -> Option<Texture> { + unsafe { + let ptr: *mut MTLTexture = msg_send![self, newTextureWithDescriptor:descriptor + offset:offset]; + if !ptr.is_null() { + Some(Texture::from_ptr(ptr)) + } else { + None + } + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlheapdescriptor/> +pub enum MTLHeapDescriptor {} + +foreign_obj_type! { + type CType = MTLHeapDescriptor; + pub struct HeapDescriptor; +} + +impl HeapDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLHeapDescriptor); + msg_send![class, new] + } + } +} + +impl HeapDescriptorRef { + pub fn cpu_cache_mode(&self) -> MTLCPUCacheMode { + unsafe { msg_send![self, cpuCacheMode] } + } + + pub fn set_cpu_cache_mode(&self, mode: MTLCPUCacheMode) { + unsafe { msg_send![self, setCpuCacheMode: mode] } + } + + pub fn storage_mode(&self) -> MTLStorageMode { + unsafe { msg_send![self, storageMode] } + } + + pub fn set_storage_mode(&self, mode: MTLStorageMode) { + unsafe { msg_send![self, setStorageMode: mode] } + } + + pub fn size(&self) -> NSUInteger { + unsafe { msg_send![self, size] } + } + + pub fn set_size(&self, size: NSUInteger) { + unsafe { msg_send![self, setSize: size] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn hazard_tracking_mode(&self) -> MTLHazardTrackingMode { + unsafe { msg_send![self, hazardTrackingMode] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn resource_options(&self) -> MTLResourceOptions { + unsafe { msg_send![self, resourceOptions] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn heap_type(&self) -> MTLHeapType { + unsafe { msg_send![self, type] } + } +} diff --git a/third_party/rust/metal/src/indirect_encoder.rs b/third_party/rust/metal/src/indirect_encoder.rs new file mode 100644 index 0000000000..e434d348f2 --- /dev/null +++ b/third_party/rust/metal/src/indirect_encoder.rs @@ -0,0 +1,344 @@ +use super::*; + +bitflags! { + /// See <https://developer.apple.com/documentation/metal/mtlindirectcommandtype/> + #[allow(non_upper_case_globals)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLIndirectCommandType: NSUInteger { + const Draw = 1 << 0; + const DrawIndexed = 1 << 1; + const DrawPatches = 1 << 2; + const DrawIndexedPatches = 1 << 3; + const ConcurrentDispatch = 1 << 4; + const ConcurrentDispatchThreads = 1 << 5; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlindirectcommandbufferdescriptor/> +pub enum MTLIndirectCommandBufferDescriptor {} + +foreign_obj_type! { + type CType = MTLIndirectCommandBufferDescriptor; + pub struct IndirectCommandBufferDescriptor; +} + +impl IndirectCommandBufferDescriptor { + pub fn new() -> Self { + let class = class!(MTLIndirectCommandBufferDescriptor); + unsafe { msg_send![class, new] } + } +} + +impl IndirectCommandBufferDescriptorRef { + pub fn command_types(&self) -> MTLIndirectCommandType { + unsafe { msg_send![self, commandTypes] } + } + + pub fn set_command_types(&self, types: MTLIndirectCommandType) { + unsafe { msg_send![self, setCommandTypes: types] } + } + + pub fn inherit_buffers(&self) -> bool { + unsafe { msg_send_bool![self, inheritBuffers] } + } + + pub fn set_inherit_buffers(&self, inherit: bool) { + unsafe { msg_send![self, setInheritBuffers: inherit] } + } + + pub fn inherit_pipeline_state(&self) -> bool { + unsafe { msg_send_bool![self, inheritPipelineState] } + } + + pub fn set_inherit_pipeline_state(&self, inherit: bool) { + unsafe { msg_send![self, setInheritPipelineState: inherit] } + } + + pub fn max_vertex_buffer_bind_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxVertexBufferBindCount] } + } + + pub fn set_max_vertex_buffer_bind_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMaxVertexBufferBindCount: count] } + } + + pub fn max_fragment_buffer_bind_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxFragmentBufferBindCount] } + } + + pub fn set_max_fragment_buffer_bind_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMaxFragmentBufferBindCount: count] } + } + + pub fn max_kernel_buffer_bind_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxKernelBufferBindCount] } + } + + pub fn set_max_kernel_buffer_bind_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMaxKernelBufferBindCount: count] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlindirectcommandbuffer/> +pub enum MTLIndirectCommandBuffer {} + +foreign_obj_type! { + type CType = MTLIndirectCommandBuffer; + pub struct IndirectCommandBuffer; + type ParentType = Resource; +} + +impl IndirectCommandBufferRef { + pub fn size(&self) -> NSUInteger { + unsafe { msg_send![self, size] } + } + + pub fn indirect_render_command_at_index(&self, index: NSUInteger) -> &IndirectRenderCommandRef { + unsafe { msg_send![self, indirectRenderCommandAtIndex: index] } + } + + pub fn indirect_compute_command_at_index( + &self, + index: NSUInteger, + ) -> &IndirectComputeCommandRef { + unsafe { msg_send![self, indirectComputeCommandAtIndex: index] } + } + + pub fn reset_with_range(&self, range: crate::NSRange) { + unsafe { msg_send![self, resetWithRange: range] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlindirectrendercommand/> +pub enum MTLIndirectRenderCommand {} + +foreign_obj_type! { + type CType = MTLIndirectRenderCommand; + pub struct IndirectRenderCommand; +} + +impl IndirectRenderCommandRef { + pub fn set_render_pipeline_state(&self, pipeline_state: &RenderPipelineStateRef) { + unsafe { msg_send![self, setRenderPipelineState: pipeline_state] } + } + + pub fn set_vertex_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setVertexBuffer: buffer + offset: offset + atIndex: index + ] + } + } + + pub fn set_fragment_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setFragmentBuffer:buffer + offset:offset + atIndex:index + ] + } + } + + pub fn draw_primitives( + &self, + primitive_type: MTLPrimitiveType, + vertex_start: NSUInteger, + vertex_count: NSUInteger, + instance_count: NSUInteger, + base_instance: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPrimitives: primitive_type + vertexStart: vertex_start + vertexCount: vertex_count + instanceCount: instance_count + baseInstance: base_instance + ] + } + } + + pub fn draw_indexed_primitives( + &self, + primitive_type: MTLPrimitiveType, + index_count: NSUInteger, + index_type: MTLIndexType, + index_buffer: &BufferRef, + index_buffer_offset: NSUInteger, + instance_count: NSUInteger, + base_vertex: NSUInteger, + base_instance: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPrimitives: primitive_type + indexCount: index_count + indexType: index_type + indexBuffer: index_buffer + indexBufferOffset: index_buffer_offset + instanceCount: instance_count + baseVertex: base_vertex + baseInstance: base_instance + ] + } + } + + pub fn draw_patches( + &self, + number_of_patch_control_points: NSUInteger, + patch_start: NSUInteger, + patch_count: NSUInteger, + patch_index_buffer: &BufferRef, + patch_index_buffer_offset: NSUInteger, + instance_count: NSUInteger, + base_instance: NSUInteger, + tesselation_factor_buffer: &BufferRef, + tesselation_factor_buffer_offset: NSUInteger, + tesselation_factor_buffer_instance_stride: NSUInteger, + ) { + unsafe { + msg_send![self, + drawPatches: number_of_patch_control_points + patchStart: patch_start + patchCount: patch_count + patchIndexBuffer: patch_index_buffer + patchIndexBufferOffset: patch_index_buffer_offset + instanceCount: instance_count + baseInstance: base_instance + tessellationFactorBuffer: tesselation_factor_buffer + tessellationFactorBufferOffset: tesselation_factor_buffer_offset + tessellationFactorBufferInstanceStride: tesselation_factor_buffer_instance_stride + ] + } + } + + pub fn draw_indexed_patches( + &self, + number_of_patch_control_points: NSUInteger, + patch_start: NSUInteger, + patch_count: NSUInteger, + patch_index_buffer: &BufferRef, + patch_index_buffer_offset: NSUInteger, + control_point_index_buffer: &BufferRef, + control_point_index_buffer_offset: NSUInteger, + instance_count: NSUInteger, + base_instance: NSUInteger, + tesselation_factor_buffer: &BufferRef, + tesselation_factor_buffer_offset: NSUInteger, + tesselation_factor_buffer_instance_stride: NSUInteger, + ) { + unsafe { + msg_send![self, + drawIndexedPatches: number_of_patch_control_points + patchStart: patch_start + patchCount: patch_count + patchIndexBuffer: patch_index_buffer + patchIndexBufferOffset: patch_index_buffer_offset + controlPointIndexBuffer: control_point_index_buffer + controlPointIndexBufferOffset: control_point_index_buffer_offset + instanceCount: instance_count + baseInstance: base_instance + tessellationFactorBuffer: tesselation_factor_buffer + tessellationFactorBufferOffset: tesselation_factor_buffer_offset + tessellationFactorBufferInstanceStride: tesselation_factor_buffer_instance_stride + ] + } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlindirectcomputecommand/> +pub enum MTLIndirectComputeCommand {} + +foreign_obj_type! { + type CType = MTLIndirectComputeCommand; + pub struct IndirectComputeCommand; +} + +impl IndirectComputeCommandRef { + pub fn set_compute_pipeline_state(&self, state: &ComputePipelineStateRef) { + unsafe { msg_send![self, setComputePipelineState: state] } + } + + pub fn set_kernel_buffer( + &self, + index: NSUInteger, + buffer: Option<&BufferRef>, + offset: NSUInteger, + ) { + unsafe { + msg_send![self, + setKernelBuffer: buffer + offset: offset + atIndex: index + ] + } + } + + pub fn set_threadgroup_memory_length(&self, index: NSUInteger, length: NSUInteger) { + unsafe { + msg_send![self, + setThreadgroupMemoryLength: length + atIndex: index + ] + } + } + + pub fn set_stage_in_region(&self, region: MTLRegion) { + unsafe { msg_send![self, setStageInRegion: region] } + } + + pub fn set_barrier(&self) { + unsafe { msg_send![self, setBarrier] } + } + + pub fn clear_barrier(&self) { + unsafe { msg_send![self, clearBarrier] } + } + + pub fn concurrent_dispatch_threadgroups( + &self, + thread_groups_per_grid: MTLSize, + threads_per_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + concurrentDispatchThreadgroups: thread_groups_per_grid + threadsPerThreadgroup: threads_per_threadgroup + ] + } + } + + pub fn concurrent_dispatch_threads( + &self, + thread_groups_per_grid: MTLSize, + threads_per_threadgroup: MTLSize, + ) { + unsafe { + msg_send![self, + concurrentDispatchThreads: thread_groups_per_grid + threadsPerThreadgroup: threads_per_threadgroup + ] + } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} diff --git a/third_party/rust/metal/src/lib.rs b/third_party/rust/metal/src/lib.rs new file mode 100644 index 0000000000..b79acf6e84 --- /dev/null +++ b/third_party/rust/metal/src/lib.rs @@ -0,0 +1,654 @@ +// Copyright 2023 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +#![allow(deprecated)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] + +#[macro_use] +pub extern crate bitflags; +#[macro_use] +pub extern crate log; +#[macro_use] +pub extern crate objc; +#[macro_use] +pub extern crate foreign_types; +#[macro_use] +pub extern crate paste; + +use std::{ + borrow::{Borrow, ToOwned}, + marker::PhantomData, + mem, + ops::Deref, + os::raw::c_void, +}; + +use core_graphics_types::{base::CGFloat, geometry::CGSize}; +use foreign_types::ForeignType; +use objc::runtime::{Object, NO, YES}; + +/// See <https://developer.apple.com/documentation/objectivec/nsinteger> +#[cfg(target_pointer_width = "64")] +pub type NSInteger = i64; + +/// See <https://developer.apple.com/documentation/objectivec/nsinteger> +#[cfg(not(target_pointer_width = "64"))] +pub type NSInteger = i32; + +/// See <https://developer.apple.com/documentation/objectivec/nsuinteger> +#[cfg(target_pointer_width = "64")] +pub type NSUInteger = u64; + +/// See <https://developer.apple.com/documentation/objectivec/nsuinteger> +#[cfg(target_pointer_width = "32")] +pub type NSUInteger = u32; + +/// See <https://developer.apple.com/documentation/foundation/nsrange> +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NSRange { + pub location: NSUInteger, + pub length: NSUInteger, +} + +impl NSRange { + #[inline] + pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange { + NSRange { location, length } + } +} + +fn nsstring_as_str(nsstr: &objc::runtime::Object) -> &str { + let bytes = unsafe { + let bytes: *const std::os::raw::c_char = msg_send![nsstr, UTF8String]; + bytes as *const u8 + }; + let len: NSUInteger = unsafe { msg_send![nsstr, length] }; + unsafe { + let bytes = std::slice::from_raw_parts(bytes, len as usize); + std::str::from_utf8(bytes).unwrap() + } +} + +fn nsstring_from_str(string: &str) -> *mut objc::runtime::Object { + const UTF8_ENCODING: usize = 4; + + let cls = class!(NSString); + let bytes = string.as_ptr() as *const c_void; + unsafe { + let obj: *mut objc::runtime::Object = msg_send![cls, alloc]; + let obj: *mut objc::runtime::Object = msg_send![ + obj, + initWithBytes:bytes + length:string.len() + encoding:UTF8_ENCODING + ]; + let _: *mut c_void = msg_send![obj, autorelease]; + obj + } +} + +/// Define a Rust wrapper for an Objective-C opaque type. +/// +/// This macro adapts the `foreign-types` crate's [`foreign_type!`] +/// macro to Objective-C, defining Rust types that represent owned and +/// borrowed forms of some underlying Objective-C type, using +/// Objective-C's reference counting to manage its lifetime. +/// +/// Given a use of the form: +/// +/// ```ignore +/// foreign_obj_type! { +/// type CType = MTLBuffer; // underlying Objective-C type +/// pub struct Buffer; // owned Rust type +/// pub struct BufferRef; // borrowed Rust type +/// type ParentType = ResourceRef; // borrowed parent class +/// } +/// ``` +/// +/// This defines the types `Buffer` and `BufferRef` as owning and +/// non-owning types, analogous to `String` and `str`, that manage +/// some underlying `*mut MTLBuffer`: +/// +/// - Both `Buffer` and `BufferRef` implement [`obj::Message`], indicating +/// that they can be sent Objective-C messages. +/// +/// - Dropping a `Buffer` sends the underlying `MTLBuffer` a `release` +/// message, and cloning a `BufferRef` sends a `retain` message and +/// returns a new `Buffer`. +/// +/// - `Buffer` dereferences to `BufferRef`. +/// +/// - `BufferRef` dereferences to its parent type `ResourceRef`. The +/// `ParentType` component is optional; if omitted, the `Ref` type +/// doesn't implement `Deref` or `DerefMut`. +/// +/// - Both `Buffer` and `BufferRef` implement `std::fmt::Debug`, +/// sending an Objective-C `debugDescription` message to the +/// underlying `MTLBuffer`. +/// +/// Following the `foreign_types` crate's nomenclature, the `Ref` +/// suffix indicates that `BufferRef` and `ResourceRef` are non-owning +/// types, used *by reference*, like `&BufferRef` or `&ResourceRef`. +/// These types are not, themselves, references. +macro_rules! foreign_obj_type { + { + type CType = $raw_ident:ident; + pub struct $owned_ident:ident; + type ParentType = $parent_ident:ident; + } => { + foreign_obj_type! { + type CType = $raw_ident; + pub struct $owned_ident; + } + + impl ::std::ops::Deref for paste!{[<$owned_ident Ref>]} { + type Target = paste!{[<$parent_ident Ref>]}; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } + } + + impl ::std::convert::From<$owned_ident> for $parent_ident { + fn from(item: $owned_ident) -> Self { + unsafe { Self::from_ptr(::std::mem::transmute(item.into_ptr())) } + } + } + }; + { + type CType = $raw_ident:ident; + pub struct $owned_ident:ident; + } => { + foreign_type! { + pub unsafe type $owned_ident: Sync + Send { + type CType = $raw_ident; + fn drop = crate::obj_drop; + fn clone = crate::obj_clone; + } + } + + unsafe impl ::objc::Message for $raw_ident { + } + unsafe impl ::objc::Message for paste!{[<$owned_ident Ref>]} { + } + + impl ::std::fmt::Debug for paste!{[<$owned_ident Ref>]} { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + unsafe { + let string: *mut ::objc::runtime::Object = msg_send![self, debugDescription]; + write!(f, "{}", crate::nsstring_as_str(&*string)) + } + } + } + + impl ::std::fmt::Debug for $owned_ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::ops::Deref::deref(self).fmt(f) + } + } + }; +} + +macro_rules! try_objc { + { + $err_name: ident => $body:expr + } => { + { + let mut $err_name: *mut Object = ::std::ptr::null_mut(); + let value = $body; + if !$err_name.is_null() { + let desc: *mut Object = msg_send![$err_name, localizedDescription]; + let compile_error: *const std::os::raw::c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned(); + return Err(message); + } + value + } + }; +} + +macro_rules! msg_send_bool { + ($obj:expr, $name:ident) => {{ + match msg_send![$obj, $name] { + YES => true, + NO => false, + #[cfg(not(target_arch = "aarch64"))] + _ => unreachable!(), + } + }}; + ($obj:expr, $name:ident : $arg:expr) => {{ + match msg_send![$obj, $name: $arg] { + YES => true, + NO => false, + #[cfg(not(target_arch = "aarch64"))] + _ => unreachable!(), + } + }}; +} + +macro_rules! msg_send_bool_error_check { + ($obj:expr, $name:ident: $arg:expr) => {{ + let mut err: *mut Object = ptr::null_mut(); + let result: BOOL = msg_send![$obj, $name:$arg + error:&mut err]; + if !err.is_null() { + let desc: *mut Object = msg_send![err, localizedDescription]; + let c_msg: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(c_msg).to_string_lossy().into_owned(); + Err(message) + } else { + match result { + YES => Ok(true), + NO => Ok(false), + #[cfg(not(target_arch = "aarch64"))] + _ => unreachable!(), + } + } + }}; +} + +/// See <https://developer.apple.com/documentation/foundation/nsarray> +pub struct NSArray<T> { + _phantom: PhantomData<T>, +} + +pub struct Array<T>(*mut NSArray<T>) +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static; + +pub struct ArrayRef<T>(foreign_types::Opaque, PhantomData<T>) +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static; + +impl<T> Drop for Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + fn drop(&mut self) { + unsafe { + let () = msg_send![self.0, release]; + } + } +} + +impl<T> Clone for Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + fn clone(&self) -> Self { + unsafe { Array(msg_send![self.0, retain]) } + } +} + +unsafe impl<T> objc::Message for NSArray<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ +} + +unsafe impl<T> objc::Message for ArrayRef<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ +} + +impl<T> Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + pub fn from_slice<'a>(s: &[&T::Ref]) -> &'a ArrayRef<T> { + unsafe { + let class = class!(NSArray); + msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()] + } + } + + pub fn from_owned_slice<'a>(s: &[T]) -> &'a ArrayRef<T> { + unsafe { + let class = class!(NSArray); + msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()] + } + } +} + +unsafe impl<T> foreign_types::ForeignType for Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + type CType = NSArray<T>; + type Ref = ArrayRef<T>; + + unsafe fn from_ptr(p: *mut NSArray<T>) -> Self { + Array(p) + } + + fn as_ptr(&self) -> *mut NSArray<T> { + self.0 + } +} + +unsafe impl<T> foreign_types::ForeignTypeRef for ArrayRef<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + type CType = NSArray<T>; +} + +impl<T> Deref for Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + type Target = ArrayRef<T>; + + #[inline] + fn deref(&self) -> &ArrayRef<T> { + unsafe { mem::transmute(self.as_ptr()) } + } +} + +impl<T> Borrow<ArrayRef<T>> for Array<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + fn borrow(&self) -> &ArrayRef<T> { + unsafe { mem::transmute(self.as_ptr()) } + } +} + +impl<T> ToOwned for ArrayRef<T> +where + T: ForeignType + 'static, + T::Ref: objc::Message + 'static, +{ + type Owned = Array<T>; + + fn to_owned(&self) -> Array<T> { + unsafe { Array::from_ptr(msg_send![self, retain]) } + } +} + +/// See <https://developer.apple.com/documentation/quartzcore/cametaldrawable> +pub enum CAMetalDrawable {} + +foreign_obj_type! { + type CType = CAMetalDrawable; + pub struct MetalDrawable; + type ParentType = Drawable; +} + +impl MetalDrawableRef { + pub fn texture(&self) -> &TextureRef { + unsafe { msg_send![self, texture] } + } +} + +pub enum NSObject {} + +foreign_obj_type! { + type CType = NSObject; + pub struct NsObject; +} + +impl NsObjectRef { + pub fn conforms_to_protocol<T>(&self) -> Result<bool, String> { + let name = ::std::any::type_name::<T>(); + if let Some(name) = name.split("::").last() { + if let Some(protocol) = objc::runtime::Protocol::get(name) { + Ok(unsafe { msg_send![self, conformsToProtocol: protocol] }) + } else { + Err(format!("Can not find the protocol for type: {}.", name)) + } + } else { + Err(format!("Unexpected type name: {}.", name)) + } + } +} + +// See <https://developer.apple.com/documentation/quartzcore/cametallayer> +pub enum CAMetalLayer {} + +foreign_obj_type! { + type CType = CAMetalLayer; + pub struct MetalLayer; +} + +impl MetalLayer { + pub fn new() -> Self { + unsafe { + let class = class!(CAMetalLayer); + msg_send![class, new] + } + } +} + +impl MetalLayerRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn set_device(&self, device: &DeviceRef) { + unsafe { msg_send![self, setDevice: device] } + } + + pub fn pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, pixelFormat] } + } + + pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setPixelFormat: pixel_format] } + } + + pub fn drawable_size(&self) -> CGSize { + unsafe { msg_send![self, drawableSize] } + } + + pub fn set_drawable_size(&self, size: CGSize) { + unsafe { msg_send![self, setDrawableSize: size] } + } + + pub fn presents_with_transaction(&self) -> bool { + unsafe { msg_send_bool![self, presentsWithTransaction] } + } + + pub fn set_presents_with_transaction(&self, transaction: bool) { + unsafe { msg_send![self, setPresentsWithTransaction: transaction] } + } + + pub fn display_sync_enabled(&self) -> bool { + unsafe { msg_send_bool![self, displaySyncEnabled] } + } + + pub fn set_display_sync_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setDisplaySyncEnabled: enabled] } + } + + pub fn maximum_drawable_count(&self) -> NSUInteger { + unsafe { msg_send![self, maximumDrawableCount] } + } + + pub fn set_maximum_drawable_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMaximumDrawableCount: count] } + } + + pub fn set_edge_antialiasing_mask(&self, mask: u64) { + unsafe { msg_send![self, setEdgeAntialiasingMask: mask] } + } + + pub fn set_masks_to_bounds(&self, masks: bool) { + unsafe { msg_send![self, setMasksToBounds: masks] } + } + + pub fn remove_all_animations(&self) { + unsafe { msg_send![self, removeAllAnimations] } + } + + pub fn next_drawable(&self) -> Option<&MetalDrawableRef> { + unsafe { msg_send![self, nextDrawable] } + } + + pub fn contents_scale(&self) -> CGFloat { + unsafe { msg_send![self, contentsScale] } + } + + pub fn set_contents_scale(&self, scale: CGFloat) { + unsafe { msg_send![self, setContentsScale: scale] } + } + + /// [framebufferOnly Apple Docs](https://developer.apple.com/documentation/metal/mtltexture/1515749-framebufferonly?language=objc) + pub fn framebuffer_only(&self) -> bool { + unsafe { msg_send_bool!(self, framebufferOnly) } + } + + pub fn set_framebuffer_only(&self, framebuffer_only: bool) { + unsafe { msg_send![self, setFramebufferOnly: framebuffer_only] } + } + + pub fn is_opaque(&self) -> bool { + unsafe { msg_send_bool!(self, isOpaque) } + } + + pub fn set_opaque(&self, opaque: bool) { + unsafe { msg_send![self, setOpaque: opaque] } + } + + pub fn wants_extended_dynamic_range_content(&self) -> bool { + unsafe { msg_send_bool![self, wantsExtendedDynamicRangeContent] } + } + + pub fn set_wants_extended_dynamic_range_content( + &self, + wants_extended_dynamic_range_content: bool, + ) { + unsafe { + msg_send![ + self, + setWantsExtendedDynamicRangeContent: wants_extended_dynamic_range_content + ] + } + } +} + +mod accelerator_structure; +mod argument; +mod blitpass; +mod buffer; +mod capturedescriptor; +mod capturemanager; +mod commandbuffer; +mod commandqueue; +mod computepass; +mod constants; +mod counters; +mod depthstencil; +mod device; +mod drawable; +mod encoder; +mod heap; +mod indirect_encoder; +mod library; +#[cfg(feature = "mps")] +pub mod mps; +mod pipeline; +mod renderpass; +mod resource; +mod sampler; +mod sync; +mod texture; +mod types; +mod vertexdescriptor; + +#[rustfmt::skip] +pub use { + accelerator_structure::*, + argument::*, + blitpass::*, + buffer::*, + counters::*, + computepass::*, + capturedescriptor::*, + capturemanager::*, + commandbuffer::*, + commandqueue::*, + constants::*, + depthstencil::*, + device::*, + drawable::*, + encoder::*, + heap::*, + indirect_encoder::*, + library::*, + pipeline::*, + renderpass::*, + resource::*, + sampler::*, + texture::*, + types::*, + vertexdescriptor::*, + sync::*, +}; + +#[inline] +unsafe fn obj_drop<T>(p: *mut T) { + msg_send![(p as *mut Object), release] +} + +#[inline] +unsafe fn obj_clone<T: 'static>(p: *mut T) -> *mut T { + msg_send![(p as *mut Object), retain] +} + +#[allow(non_camel_case_types)] +type c_size_t = usize; + +// TODO: expand supported interface +/// See <https://developer.apple.com/documentation/foundation/nsurl> +pub enum NSURL {} + +foreign_obj_type! { + type CType = NSURL; + pub struct URL; +} + +impl URL { + pub fn new_with_string(string: &str) -> Self { + unsafe { + let ns_str = crate::nsstring_from_str(string); + let class = class!(NSURL); + msg_send![class, URLWithString: ns_str] + } + } +} + +impl URLRef { + pub fn absolute_string(&self) -> &str { + unsafe { + let absolute_string = msg_send![self, absoluteString]; + crate::nsstring_as_str(absolute_string) + } + } + + pub fn path(&self) -> &str { + unsafe { + let path = msg_send![self, path]; + crate::nsstring_as_str(path) + } + } +} diff --git a/third_party/rust/metal/src/library.rs b/third_party/rust/metal/src/library.rs new file mode 100644 index 0000000000..2c7d0c92ce --- /dev/null +++ b/third_party/rust/metal/src/library.rs @@ -0,0 +1,901 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use foreign_types::ForeignType; +use objc::runtime::{Object, BOOL, NO, YES}; + +use std::ffi::CStr; +use std::os::raw::{c_char, c_void}; +use std::ptr; + +/// Only available on (macos(10.12), ios(10.0) +/// +/// See <https://developer.apple.com/documentation/metal/mtlpatchtype/> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLPatchType { + None = 0, + Triangle = 1, + Quad = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexattribute/> +pub enum MTLVertexAttribute {} + +foreign_obj_type! { + type CType = MTLVertexAttribute; + pub struct VertexAttribute; +} + +impl VertexAttributeRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn attribute_index(&self) -> u64 { + unsafe { msg_send![self, attributeIndex] } + } + + pub fn attribute_type(&self) -> MTLDataType { + unsafe { msg_send![self, attributeType] } + } + + pub fn is_active(&self) -> bool { + unsafe { msg_send_bool![self, isActive] } + } + + /// Only available on (macos(10.12), ios(10.0) + pub fn is_patch_data(&self) -> bool { + unsafe { msg_send_bool![self, isPatchData] } + } + + /// Only available on (macos(10.12), ios(10.0) + pub fn is_patch_control_point_data(&self) -> bool { + unsafe { msg_send_bool![self, isPatchControlPointData] } + } +} + +/// Only available on (macos(10.12), ios(10.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlattribute/> +pub enum MTLAttribute {} + +foreign_obj_type! { + type CType = MTLAttribute; + pub struct Attribute; +} + +impl AttributeRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn attribute_index(&self) -> u64 { + unsafe { msg_send![self, attributeIndex] } + } + + pub fn attribute_type(&self) -> MTLDataType { + unsafe { msg_send![self, attributeType] } + } + + pub fn is_active(&self) -> bool { + unsafe { msg_send_bool![self, isActive] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn is_patch_data(&self) -> bool { + unsafe { msg_send_bool![self, isPatchData] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn is_patch_control_point_data(&self) -> bool { + unsafe { msg_send_bool![self, isPatchControlPointData] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlfunctiontype/> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLFunctionType { + Vertex = 1, + Fragment = 2, + Kernel = 3, + /// Only available on (macos(11.0), ios(14.0)) + Visible = 5, + /// Only available on (macos(11.0), ios(14.0)) + Intersection = 6, +} + +/// Only available on (macos(10.12), ios(10.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlfunctionconstant/> +pub enum MTLFunctionConstant {} + +foreign_obj_type! { + type CType = MTLFunctionConstant; + pub struct FunctionConstant; +} + +impl FunctionConstantRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn data_type(&self) -> MTLDataType { + unsafe { msg_send![self, type] } + } + + pub fn index(&self) -> NSUInteger { + unsafe { msg_send![self, index] } + } + + pub fn required(&self) -> bool { + unsafe { msg_send_bool![self, required] } + } +} + +bitflags! { + /// Only available on (macos(11.0), ios(14.0)) + /// + /// See <https://developer.apple.com/documentation/metal/mtlfunctionoptions/> + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLFunctionOptions: NSUInteger { + const None = 0; + const CompileToBinary = 1 << 0; + } +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlfunctiondescriptor/> +pub enum MTLFunctionDescriptor {} + +foreign_obj_type! { + type CType = MTLFunctionDescriptor; + pub struct FunctionDescriptor; +} + +impl FunctionDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLFunctionDescriptor); + msg_send![class, new] + } + } +} + +impl FunctionDescriptorRef { + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn set_name(&self, name: &str) { + unsafe { + let ns_name = crate::nsstring_from_str(name); + let () = msg_send![self, setName: ns_name]; + } + } + + pub fn specialized_name(&self) -> &str { + unsafe { + let name = msg_send![self, specializedName]; + crate::nsstring_as_str(name) + } + } + + pub fn set_specialized_name(&self, name: &str) { + unsafe { + let ns_name = crate::nsstring_from_str(name); + let () = msg_send![self, setSpecializedName: ns_name]; + } + } + + pub fn constant_values(&self) -> &FunctionConstantValuesRef { + unsafe { msg_send![self, constantValues] } + } + + pub fn set_constant_values(&self, values: &FunctionConstantValuesRef) { + unsafe { msg_send![self, setConstantValues: values] } + } + + pub fn options(&self) -> MTLFunctionOptions { + unsafe { msg_send![self, options] } + } + + pub fn set_options(&self, options: MTLFunctionOptions) { + unsafe { msg_send![self, setOptions: options] } + } +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlintersectionfunctiondescriptor/> +pub enum MTLIntersectionFunctionDescriptor {} + +foreign_obj_type! { + type CType = MTLIntersectionFunctionDescriptor; + pub struct IntersectionFunctionDescriptor; + type ParentType = FunctionDescriptor; +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtlfunctionhandle/> +pub enum MTLFunctionHandle {} + +foreign_obj_type! { + type CType = MTLFunctionHandle; + pub struct FunctionHandle; +} + +impl FunctionHandleRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn name(&self) -> &str { + unsafe { + let ns_name = msg_send![self, name]; + crate::nsstring_as_str(ns_name) + } + } + + pub fn function_type(&self) -> MTLFunctionType { + unsafe { msg_send![self, functionType] } + } +} + +// TODO: +// MTLVisibleFunctionTableDescriptor +// MTLVisibleFunctionTable +// MTLIntersectionFunctionSignature +// MTLIntersectionFunctionTableDescriptor +// MTLIntersectionFunctionTable + +/// See <https://developer.apple.com/documentation/metal/mtlfunction/> +pub enum MTLFunction {} + +foreign_obj_type! { + type CType = MTLFunction; + pub struct Function; +} + +impl FunctionRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn label(&self) -> &str { + unsafe { + let ns_label = msg_send![self, label]; + crate::nsstring_as_str(ns_label) + } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn set_label(&self, label: &str) { + unsafe { + let ns_label = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: ns_label]; + } + } + + pub fn name(&self) -> &str { + unsafe { + let name = msg_send![self, name]; + crate::nsstring_as_str(name) + } + } + + pub fn function_type(&self) -> MTLFunctionType { + unsafe { msg_send![self, functionType] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn patch_type(&self) -> MTLPatchType { + unsafe { msg_send![self, patchType] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn patch_control_point_count(&self) -> NSUInteger { + unsafe { msg_send![self, patchControlPointCount] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn vertex_attributes(&self) -> &Array<VertexAttribute> { + unsafe { msg_send![self, vertexAttributes] } + } + + /// Only available on (macos(10.12), ios(10.0)) + pub fn stage_input_attributes(&self) -> &Array<Attribute> { + unsafe { msg_send![self, stageInputAttributes] } + } + + pub fn new_argument_encoder(&self, buffer_index: NSUInteger) -> ArgumentEncoder { + unsafe { + let ptr = msg_send![self, newArgumentEncoderWithBufferIndex: buffer_index]; + ArgumentEncoder::from_ptr(ptr) + } + } + + pub fn function_constants_dictionary(&self) -> *mut Object { + unsafe { msg_send![self, functionConstantsDictionary] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn options(&self) -> MTLFunctionOptions { + unsafe { msg_send![self, options] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtllanguageversion/> +#[repr(u64)] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub enum MTLLanguageVersion { + V1_0 = 0x10000, + V1_1 = 0x10001, + V1_2 = 0x10002, + V2_0 = 0x20000, + V2_1 = 0x20001, + V2_2 = 0x20002, + /// available on macOS 11.0+, iOS 14.0+ + V2_3 = 0x20003, + /// available on macOS 12.0+, iOS 15.0+ + V2_4 = 0x20004, +} + +/// See <https://developer.apple.com/documentation/metal/mtlfunctionconstantvalues/> +pub enum MTLFunctionConstantValues {} + +foreign_obj_type! { + type CType = MTLFunctionConstantValues; + pub struct FunctionConstantValues; +} + +impl FunctionConstantValues { + pub fn new() -> Self { + unsafe { + let class = class!(MTLFunctionConstantValues); + msg_send![class, new] + } + } +} + +impl FunctionConstantValuesRef { + pub fn set_constant_value_at_index( + &self, + value: *const c_void, + ty: MTLDataType, + index: NSUInteger, + ) { + unsafe { msg_send![self, setConstantValue:value type:ty atIndex:index] } + } + + pub fn set_constant_values_with_range( + &self, + values: *const c_void, + ty: MTLDataType, + range: NSRange, + ) { + unsafe { msg_send![self, setConstantValues:values type:ty withRange:range] } + } + + pub fn set_constant_value_with_name(&self, value: *const c_void, ty: MTLDataType, name: &str) { + unsafe { + let ns_name = crate::nsstring_from_str(name); + msg_send![self, setConstantValue:value type:ty withName:ns_name] + } + } +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtllibrarytype/> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLLibraryType { + Executable = 0, + Dynamic = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcompileoptions/> +pub enum MTLCompileOptions {} + +foreign_obj_type! { + type CType = MTLCompileOptions; + pub struct CompileOptions; +} + +impl CompileOptions { + pub fn new() -> Self { + unsafe { + let class = class!(MTLCompileOptions); + msg_send![class, new] + } + } +} + +impl CompileOptionsRef { + pub unsafe fn preprocessor_macros(&self) -> *mut Object { + msg_send![self, preprocessorMacros] + } + + pub unsafe fn set_preprocessor_macros(&self, defines: *mut Object) { + msg_send![self, setPreprocessorMacros: defines] + } + + pub fn is_fast_math_enabled(&self) -> bool { + unsafe { msg_send_bool![self, fastMathEnabled] } + } + + pub fn set_fast_math_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setFastMathEnabled: enabled] } + } + + /// Only available on (macos(10.11), ios(9.0)) + pub fn language_version(&self) -> MTLLanguageVersion { + unsafe { msg_send![self, languageVersion] } + } + + /// Only available on (macos(10.11), ios(9.0)) + pub fn set_language_version(&self, version: MTLLanguageVersion) { + unsafe { msg_send![self, setLanguageVersion: version] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn library_type(&self) -> MTLLibraryType { + unsafe { msg_send![self, libraryType] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn set_library_type(&self, lib_type: MTLLibraryType) { + unsafe { msg_send![self, setLibraryType: lib_type] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn install_name(&self) -> &str { + unsafe { + let name = msg_send![self, installName]; + crate::nsstring_as_str(name) + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn set_install_name(&self, name: &str) { + unsafe { + let install_name = crate::nsstring_from_str(name); + let () = msg_send![self, setInstallName: install_name]; + } + } + + /// Only available on (macos(11.0), ios(14.0)) + /// + /// Marshal to Rust Vec + pub fn libraries(&self) -> Vec<DynamicLibrary> { + unsafe { + let libraries: *mut Object = msg_send![self, libraries]; + let count: NSUInteger = msg_send![libraries, count]; + let ret = (0..count) + .map(|i| { + let lib = msg_send![libraries, objectAtIndex: i]; + DynamicLibrary::from_ptr(lib) + }) + .collect(); + ret + } + } + + /// Only available on (macos(11.0), ios(14.0)) + /// + /// As raw NSArray + pub fn libraries_as_nsarray(&self) -> &ArrayRef<DynamicLibrary> { + unsafe { msg_send![self, libraries] } + } + + /// Only available on (macos(11.0), ios(14.0)) + /// + /// Marshal from Rust slice + pub fn set_libraries(&self, libraries: &[&DynamicLibraryRef]) { + let ns_array = Array::<DynamicLibrary>::from_slice(libraries); + unsafe { msg_send![self, setLibraries: ns_array] } + } + + /// Only available on (macos(11.0), ios(14.0)) + /// + /// From raw NSArray + pub fn set_libraries_nsarray(&self, libraries: &ArrayRef<DynamicLibrary>) { + unsafe { msg_send![self, setLibraries: libraries] } + } + + /// Only available on (macos(11.0), macCatalyst(14.0), ios(13.0)) + pub fn preserve_invariance(&self) -> bool { + unsafe { msg_send_bool![self, preserveInvariance] } + } + + /// Only available on (macos(11.0), macCatalyst(14.0), ios(13.0)) + pub fn set_preserve_invariance(&self, preserve: bool) { + unsafe { msg_send![self, setPreserveInvariance: preserve] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtllibraryerror/> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLLibraryError { + Unsupported = 1, + Internal = 2, + CompileFailure = 3, + CompileWarning = 4, + /// Only available on (macos(10.12), ios(10.0)) + FunctionNotFound = 5, + /// Only available on (macos(10.12), ios(10.0)) + FileNotFound = 6, +} + +/// See <https://developer.apple.com/documentation/metal/mtllibrary/> +pub enum MTLLibrary {} + +foreign_obj_type! { + type CType = MTLLibrary; + pub struct Library; +} + +impl LibraryRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + // FIXME: should rename to new_function + pub fn get_function( + &self, + name: &str, + constants: Option<FunctionConstantValues>, + ) -> Result<Function, String> { + unsafe { + let nsname = crate::nsstring_from_str(name); + + let function: *mut MTLFunction = match constants { + Some(c) => try_objc! { err => msg_send![self, + newFunctionWithName: nsname.as_ref() + constantValues: c.as_ref() + error: &mut err + ]}, + None => msg_send![self, newFunctionWithName: nsname.as_ref()], + }; + + if !function.is_null() { + Ok(Function::from_ptr(function)) + } else { + Err(format!("Function '{}' does not exist", name)) + } + } + } + + // TODO: get_function_async with completion handler + + pub fn function_names(&self) -> Vec<String> { + unsafe { + let names: *mut Object = msg_send![self, functionNames]; + let count: NSUInteger = msg_send![names, count]; + let ret = (0..count) + .map(|i| { + let name = msg_send![names, objectAtIndex: i]; + nsstring_as_str(name).to_string() + }) + .collect(); + ret + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn library_type(&self) -> MTLLibraryType { + unsafe { msg_send![self, type] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn install_name(&self) -> Option<&str> { + unsafe { + let maybe_name: *mut Object = msg_send![self, installName]; + maybe_name.as_ref().map(crate::nsstring_as_str) + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_function_with_descriptor( + &self, + descriptor: &FunctionDescriptorRef, + ) -> Result<Function, String> { + unsafe { + let function: *mut MTLFunction = try_objc! { + err => msg_send![self, + newFunctionWithDescriptor: descriptor + error: &mut err + ] + }; + + if !function.is_null() { + Ok(Function::from_ptr(function)) + } else { + Err(String::from("new_function_with_descriptor() failed")) + } + } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_intersection_function_with_descriptor( + &self, + descriptor: &IntersectionFunctionDescriptorRef, + ) -> Result<Function, String> { + unsafe { + let function: *mut MTLFunction = try_objc! { + err => msg_send![self, + newIntersectionFunctionWithDescriptor: descriptor + error: &mut err + ] + }; + + if !function.is_null() { + Ok(Function::from_ptr(function)) + } else { + Err(String::from( + "new_intersection_function_with_descriptor() failed", + )) + } + } + } +} + +/// Only available on (macos(11.0), ios(14.0)) +/// +/// See <https://developer.apple.com/documentation/metal/mtldynamiclibraryerror/> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLDynamicLibraryError { + None = 0, + InvalidFile = 1, + CompilationFailure = 2, + UnresolvedInstallName = 3, + DependencyLoadFailure = 4, + Unsupported = 5, +} + +/// See <https://developer.apple.com/documentation/metal/mtldynamiclibrary/> +pub enum MTLDynamicLibrary {} + +foreign_obj_type! { + type CType = MTLDynamicLibrary; + pub struct DynamicLibrary; +} + +impl DynamicLibraryRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn install_name(&self) -> &str { + unsafe { + let name = msg_send![self, installName]; + crate::nsstring_as_str(name) + } + } + + pub fn serialize_to_url(&self, url: &URLRef) -> Result<bool, String> { + unsafe { msg_send_bool_error_check![self, serializeToURL: url] } + } +} + +/// macOS 11.0+ iOS 14.0+ +/// +/// See <https://developer.apple.com/documentation/metal/mtlbinaryarchivedescriptor/> +pub enum MTLBinaryArchiveDescriptor {} + +foreign_obj_type! { + type CType = MTLBinaryArchiveDescriptor; + pub struct BinaryArchiveDescriptor; +} + +impl BinaryArchiveDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLBinaryArchiveDescriptor); + msg_send![class, new] + } + } +} + +impl BinaryArchiveDescriptorRef { + pub fn url(&self) -> &URLRef { + unsafe { msg_send![self, url] } + } + pub fn set_url(&self, url: &URLRef) { + unsafe { msg_send![self, setUrl: url] } + } +} + +/// macOS 11.0+ iOS 14.0+ +/// +/// See <https://developer.apple.com/documentation/metal/mtlbinaryarchive/> +pub enum MTLBinaryArchive {} + +foreign_obj_type! { + type CType = MTLBinaryArchive; + pub struct BinaryArchive; +} + +impl BinaryArchiveRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn add_compute_pipeline_functions_with_descriptor( + &self, + descriptor: &ComputePipelineDescriptorRef, + ) -> Result<bool, String> { + unsafe { + msg_send_bool_error_check![self, addComputePipelineFunctionsWithDescriptor: descriptor] + } + } + + pub fn add_render_pipeline_functions_with_descriptor( + &self, + descriptor: &RenderPipelineDescriptorRef, + ) -> Result<bool, String> { + unsafe { + msg_send_bool_error_check![self, addRenderPipelineFunctionsWithDescriptor: descriptor] + } + } + + // TODO: addTileRenderPipelineFunctionsWithDescriptor + // - (BOOL)addTileRenderPipelineFunctionsWithDescriptor:(MTLTileRenderPipelineDescriptor *)descriptor + // error:(NSError * _Nullable *)error; + + pub fn serialize_to_url(&self, url: &URLRef) -> Result<bool, String> { + unsafe { + let mut err: *mut Object = ptr::null_mut(); + let result: BOOL = msg_send![self, serializeToURL:url + error:&mut err]; + if !err.is_null() { + // FIXME: copy pasta + let desc: *mut Object = msg_send![err, localizedDescription]; + let c_msg: *const c_char = msg_send![desc, UTF8String]; + let message = CStr::from_ptr(c_msg).to_string_lossy().into_owned(); + Err(message) + } else { + match result { + YES => Ok(true), + NO => Ok(false), + #[cfg(not(target_arch = "aarch64"))] + _ => unreachable!(), + } + } + } + } +} + +/// macOS 11.0+ iOS 14.0+ +/// +/// See <https://developer.apple.com/documentation/metal/mtllinkedfunctions/> +pub enum MTLLinkedFunctions {} + +foreign_obj_type! { + type CType = MTLLinkedFunctions; + pub struct LinkedFunctions; +} + +impl LinkedFunctions { + pub fn new() -> Self { + unsafe { + let class = class!(MTLLinkedFunctions); + msg_send![class, new] + } + } +} + +impl LinkedFunctionsRef { + /// Marshal to Rust Vec + pub fn functions(&self) -> Vec<Function> { + unsafe { + let functions: *mut Object = msg_send![self, functions]; + let count: NSUInteger = msg_send![functions, count]; + let ret = (0..count) + .map(|i| { + let f = msg_send![functions, objectAtIndex: i]; + Function::from_ptr(f) + }) + .collect(); + ret + } + } + + /// Marshal from Rust slice + pub fn set_functions(&self, functions: &[&FunctionRef]) { + let ns_array = Array::<Function>::from_slice(functions); + unsafe { msg_send![self, setFunctions: ns_array] } + } + + /// Marshal to Rust Vec + pub fn binary_functions(&self) -> Vec<Function> { + unsafe { + let functions: *mut Object = msg_send![self, binaryFunctions]; + let count: NSUInteger = msg_send![functions, count]; + let ret = (0..count) + .map(|i| { + let f = msg_send![functions, objectAtIndex: i]; + Function::from_ptr(f) + }) + .collect(); + ret + } + } + + /// Marshal from Rust slice + pub fn set_binary_functions(&self, functions: &[&FunctionRef]) { + let ns_array = Array::<Function>::from_slice(functions); + unsafe { msg_send![self, setBinaryFunctions: ns_array] } + } + + // TODO: figure out NSDictionary wrapper + // TODO: groups + // @property (readwrite, nonatomic, copy, nullable) NSDictionary<NSString*, NSArray<id<MTLFunction>>*> *groups; +} diff --git a/third_party/rust/metal/src/mps.rs b/third_party/rust/metal/src/mps.rs new file mode 100644 index 0000000000..edd4936e8e --- /dev/null +++ b/third_party/rust/metal/src/mps.rs @@ -0,0 +1,572 @@ +// Copyright 2020 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use objc::runtime::{BOOL, YES}; + +#[cfg_attr(feature = "link", link(name = "MetalPerformanceShaders", kind = "framework"))] +extern "C" { + fn MPSSupportsMTLDevice(device: *const std::ffi::c_void) -> BOOL; +} + +pub fn mps_supports_device(device: &DeviceRef) -> bool { + let b: BOOL = unsafe { + let ptr: *const DeviceRef = device; + MPSSupportsMTLDevice(ptr as _) + }; + b == YES +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpskernel> +pub enum MPSKernel {} + +foreign_obj_type! { + type CType = MPSKernel; + pub struct Kernel; +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsraydatatype> +pub enum MPSRayDataType { + OriginDirection = 0, + OriginMinDistanceDirectionMaxDistance = 1, + OriginMaskDirectionMaxDistance = 2, +} + +bitflags! { + /// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsraymaskoptions> + #[allow(non_upper_case_globals)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MPSRayMaskOptions: NSUInteger { + /// Enable primitive masks + const Primitive = 1; + /// Enable instance masks + const Instance = 2; + } +} + +/// Options that determine the data contained in an intersection result. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiondatatype> +pub enum MPSIntersectionDataType { + Distance = 0, + DistancePrimitiveIndex = 1, + DistancePrimitiveIndexCoordinates = 2, + DistancePrimitiveIndexInstanceIndex = 3, + DistancePrimitiveIndexInstanceIndexCoordinates = 4, +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiontype> +pub enum MPSIntersectionType { + /// Find the closest intersection to the ray's origin along the ray direction. + /// This is potentially slower than `Any` but is well suited to primary visibility rays. + Nearest = 0, + /// Find any intersection along the ray direction. This is potentially faster than `Nearest` and + /// is well suited to shadow and occlusion rays. + Any = 1, +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsraymaskoperator> +pub enum MPSRayMaskOperator { + /// Accept the intersection if `(primitive mask & ray mask) != 0`. + And = 0, + /// Accept the intersection if `~(primitive mask & ray mask) != 0`. + NotAnd = 1, + /// Accept the intersection if `(primitive mask | ray mask) != 0`. + Or = 2, + /// Accept the intersection if `~(primitive mask | ray mask) != 0`. + NotOr = 3, + /// Accept the intersection if `(primitive mask ^ ray mask) != 0`. + /// Note that this is equivalent to the "!=" operator. + Xor = 4, + /// Accept the intersection if `~(primitive mask ^ ray mask) != 0`. + /// Note that this is equivalent to the "==" operator. + NotXor = 5, + /// Accept the intersection if `(primitive mask < ray mask) != 0`. + LessThan = 6, + /// Accept the intersection if `(primitive mask <= ray mask) != 0`. + LessThanOrEqualTo = 7, + /// Accept the intersection if `(primitive mask > ray mask) != 0`. + GreaterThan = 8, + /// Accept the intersection if `(primitive mask >= ray mask) != 0`. + GreaterThanOrEqualTo = 9, +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpstriangleintersectiontesttype> +pub enum MPSTriangleIntersectionTestType { + /// Use the default ray/triangle intersection test + Default = 0, + /// Use a watertight ray/triangle intersection test which avoids gaps along shared triangle edges. + /// Shared vertices may still have gaps. + /// This intersection test may be slower than `Default`. + Watertight = 1, +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsaccelerationstructurestatus> +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MPSAccelerationStructureStatus { + Unbuilt = 0, + Built = 1, +} + +bitflags! { + /// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsaccelerationstructureusage> + #[allow(non_upper_case_globals)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MPSAccelerationStructureUsage: NSUInteger { + /// No usage options specified + const None = 0; + /// Option that enables support for refitting the acceleration structure after it has been built. + const Refit = 1; + /// Option indicating that the acceleration structure will be rebuilt frequently. + const FrequentRebuild = 2; + const PreferGPUBuild = 4; + const PreferCPUBuild = 8; + } +} + +/// A common bit for all floating point data types. +const MPSDataTypeFloatBit: isize = 0x10000000; +const MPSDataTypeSignedBit: isize = 0x20000000; +const MPSDataTypeNormalizedBit: isize = 0x40000000; + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsdatatype> +pub enum MPSDataType { + Invalid = 0, + + Float32 = MPSDataTypeFloatBit | 32, + Float16 = MPSDataTypeFloatBit | 16, + + // Signed integers. + Int8 = MPSDataTypeSignedBit | 8, + Int16 = MPSDataTypeSignedBit | 16, + Int32 = MPSDataTypeSignedBit | 32, + + // Unsigned integers. Range: [0, UTYPE_MAX] + UInt8 = 8, + UInt16 = 16, + UInt32 = 32, + + // Unsigned normalized. Range: [0, 1.0] + Unorm1 = MPSDataTypeNormalizedBit | 1, + Unorm8 = MPSDataTypeNormalizedBit | 8, +} + +/// A kernel that performs intersection tests between rays and geometry. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsrayintersector> +pub enum MPSRayIntersector {} + +foreign_obj_type! { + type CType = MPSRayIntersector; + pub struct RayIntersector; + type ParentType = Kernel; +} + +impl RayIntersector { + pub fn from_device(device: &DeviceRef) -> Option<Self> { + unsafe { + let intersector: RayIntersector = msg_send![class!(MPSRayIntersector), alloc]; + let ptr: *mut Object = msg_send![intersector.as_ref(), initWithDevice: device]; + if ptr.is_null() { + None + } else { + Some(intersector) + } + } + } +} + +impl RayIntersectorRef { + pub fn set_cull_mode(&self, mode: MTLCullMode) { + unsafe { msg_send![self, setCullMode: mode] } + } + + pub fn set_front_facing_winding(&self, winding: MTLWinding) { + unsafe { msg_send![self, setFrontFacingWinding: winding] } + } + + pub fn set_intersection_data_type(&self, options: MPSIntersectionDataType) { + unsafe { msg_send![self, setIntersectionDataType: options] } + } + + pub fn set_intersection_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setIntersectionStride: stride] } + } + + pub fn set_ray_data_type(&self, ty: MPSRayDataType) { + unsafe { msg_send![self, setRayDataType: ty] } + } + + pub fn set_ray_index_data_type(&self, ty: MPSDataType) { + unsafe { msg_send![self, setRayIndexDataType: ty] } + } + + pub fn set_ray_mask(&self, ray_mask: u32) { + unsafe { msg_send![self, setRayMask: ray_mask] } + } + + pub fn set_ray_mask_operator(&self, operator: MPSRayMaskOperator) { + unsafe { msg_send![self, setRayMaskOperator: operator] } + } + + pub fn set_ray_mask_options(&self, options: MPSRayMaskOptions) { + unsafe { msg_send![self, setRayMaskOptions: options] } + } + + pub fn set_ray_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setRayStride: stride] } + } + + pub fn set_triangle_intersection_test_type(&self, test_type: MPSTriangleIntersectionTestType) { + unsafe { msg_send![self, setTriangleIntersectionTestType: test_type] } + } + + pub fn encode_intersection_to_command_buffer( + &self, + command_buffer: &CommandBufferRef, + intersection_type: MPSIntersectionType, + ray_buffer: &BufferRef, + ray_buffer_offset: NSUInteger, + intersection_buffer: &BufferRef, + intersection_buffer_offset: NSUInteger, + ray_count: NSUInteger, + acceleration_structure: &AccelerationStructureRef, + ) { + unsafe { + msg_send![ + self, + encodeIntersectionToCommandBuffer: command_buffer + intersectionType: intersection_type + rayBuffer: ray_buffer + rayBufferOffset: ray_buffer_offset + intersectionBuffer: intersection_buffer + intersectionBufferOffset: intersection_buffer_offset + rayCount: ray_count + accelerationStructure: acceleration_structure + ] + } + } + + pub fn recommended_minimum_ray_batch_size_for_ray_count( + &self, + ray_count: NSUInteger, + ) -> NSUInteger { + unsafe { msg_send![self, recommendedMinimumRayBatchSizeForRayCount: ray_count] } + } +} + +/// A group of acceleration structures which may be used together in an instance acceleration structure. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsaccelerationstructuregroup> +pub enum MPSAccelerationStructureGroup {} + +foreign_obj_type! { + type CType = MPSAccelerationStructureGroup; + pub struct AccelerationStructureGroup; +} + +impl AccelerationStructureGroup { + pub fn new_with_device(device: &DeviceRef) -> Option<Self> { + unsafe { + let group: AccelerationStructureGroup = + msg_send![class!(MPSAccelerationStructureGroup), alloc]; + let ptr: *mut Object = msg_send![group.as_ref(), initWithDevice: device]; + if ptr.is_null() { + None + } else { + Some(group) + } + } + } +} + +impl AccelerationStructureGroupRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } +} + +/// The base class for data structures that are built over geometry and used to accelerate ray tracing. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsaccelerationstructure> +pub enum MPSAccelerationStructure {} + +foreign_obj_type! { + type CType = MPSAccelerationStructure; + pub struct AccelerationStructure; +} + +impl AccelerationStructureRef { + pub fn status(&self) -> MPSAccelerationStructureStatus { + unsafe { msg_send![self, status] } + } + + pub fn usage(&self) -> MPSAccelerationStructureUsage { + unsafe { msg_send![self, usage] } + } + + pub fn set_usage(&self, usage: MPSAccelerationStructureUsage) { + unsafe { msg_send![self, setUsage: usage] } + } + + pub fn group(&self) -> &AccelerationStructureGroupRef { + unsafe { msg_send![self, group] } + } + + pub fn encode_refit_to_command_buffer(&self, buffer: &CommandBufferRef) { + unsafe { msg_send![self, encodeRefitToCommandBuffer: buffer] } + } + + pub fn rebuild(&self) { + unsafe { msg_send![self, rebuild] } + } +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpspolygonaccelerationstructure> +pub enum MPSPolygonAccelerationStructure {} + +foreign_obj_type! { + type CType = MPSPolygonAccelerationStructure; + pub struct PolygonAccelerationStructure; + type ParentType = AccelerationStructure; +} + +impl PolygonAccelerationStructureRef { + pub fn set_index_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setIndexBuffer: buffer] } + } + + pub fn set_index_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setIndexBufferOffset: offset] } + } + + pub fn set_index_type(&self, data_type: MPSDataType) { + unsafe { msg_send![self, setIndexType: data_type] } + } + + pub fn set_mask_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setMaskBuffer: buffer] } + } + + pub fn set_mask_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setMaskBufferOffset: offset] } + } + + pub fn set_vertex_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setVertexBuffer: buffer] } + } + + pub fn set_vertex_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setVertexBufferOffset: offset] } + } + + pub fn set_vertex_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setVertexStride: stride] } + } +} + +/// An acceleration structure built over triangles. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpstriangleaccelerationstructure> +pub enum MPSTriangleAccelerationStructure {} + +foreign_obj_type! { + type CType = MPSTriangleAccelerationStructure; + pub struct TriangleAccelerationStructure; + type ParentType = PolygonAccelerationStructure; +} + +impl TriangleAccelerationStructure { + pub fn from_device(device: &DeviceRef) -> Option<Self> { + unsafe { + let structure: TriangleAccelerationStructure = + msg_send![class!(MPSTriangleAccelerationStructure), alloc]; + let ptr: *mut Object = msg_send![structure.as_ref(), initWithDevice: device]; + if ptr.is_null() { + None + } else { + Some(structure) + } + } + } +} + +impl TriangleAccelerationStructureRef { + pub fn triangle_count(&self) -> NSUInteger { + unsafe { msg_send![self, triangleCount] } + } + + pub fn set_triangle_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setTriangleCount: count] } + } +} + +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpstransformtype> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MPSTransformType { + Float4x4 = 0, + Identity = 1, +} + +/// An acceleration structure built over instances of other acceleration structures +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsinstanceaccelerationstructure> +pub enum MPSInstanceAccelerationStructure {} + +foreign_obj_type! { + type CType = MPSInstanceAccelerationStructure; + pub struct InstanceAccelerationStructure; + type ParentType = AccelerationStructure; +} + +impl InstanceAccelerationStructure { + pub fn init_with_group(group: &AccelerationStructureGroupRef) -> Option<Self> { + unsafe { + let structure: InstanceAccelerationStructure = + msg_send![class!(MPSInstanceAccelerationStructure), alloc]; + let ptr: *mut Object = msg_send![structure.as_ref(), initWithGroup: group]; + if ptr.is_null() { + None + } else { + Some(structure) + } + } + } +} + +impl InstanceAccelerationStructureRef { + /// Marshal to Rust Vec + pub fn acceleration_structures(&self) -> Vec<PolygonAccelerationStructure> { + unsafe { + let acs: *mut Object = msg_send![self, accelerationStructures]; + let count: NSUInteger = msg_send![acs, count]; + let ret = (0..count) + .map(|i| { + let ac = msg_send![acs, objectAtIndex: i]; + PolygonAccelerationStructure::from_ptr(ac) + }) + .collect(); + ret + } + } + + /// Marshal from Rust slice + pub fn set_acceleration_structures(&self, acs: &[&PolygonAccelerationStructureRef]) { + let ns_array = Array::<PolygonAccelerationStructure>::from_slice(acs); + unsafe { msg_send![self, setAccelerationStructures: ns_array] } + } + + pub fn instance_buffer(&self) -> &BufferRef { + unsafe { msg_send![self, instanceBuffer] } + } + + pub fn set_instance_buffer(&self, buffer: &BufferRef) { + unsafe { msg_send![self, setInstanceBuffer: buffer] } + } + + pub fn instance_buffer_offset(&self) -> NSUInteger { + unsafe { msg_send![self, instanceBufferOffset] } + } + + pub fn set_instance_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setInstanceBufferOffset: offset] } + } + + pub fn transform_buffer(&self) -> &BufferRef { + unsafe { msg_send![self, transformBuffer] } + } + + pub fn set_transform_buffer(&self, buffer: &BufferRef) { + unsafe { msg_send![self, setTransformBuffer: buffer] } + } + + pub fn transform_buffer_offset(&self) -> NSUInteger { + unsafe { msg_send![self, transformBufferOffset] } + } + + pub fn set_transform_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setTransformBufferOffset: offset] } + } + + pub fn transform_type(&self) -> MPSTransformType { + unsafe { msg_send![self, transformType] } + } + + pub fn set_transform_type(&self, transform_type: MPSTransformType) { + unsafe { msg_send![self, setTransformType: transform_type] } + } + + pub fn mask_buffer(&self) -> &BufferRef { + unsafe { msg_send![self, maskBuffer] } + } + + pub fn set_mask_buffer(&self, buffer: &BufferRef) { + unsafe { msg_send![self, setMaskBuffer: buffer] } + } + + pub fn mask_buffer_offset(&self) -> NSUInteger { + unsafe { msg_send![self, maskBufferOffset] } + } + + pub fn set_mask_buffer_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setMaskBufferOffset: offset] } + } + + pub fn instance_count(&self) -> NSUInteger { + unsafe { msg_send![self, instanceCount] } + } + + pub fn set_instance_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setInstanceCount: count] } + } +} + +#[repr(C)] +pub struct MPSPackedFloat3 { + pub elements: [f32; 3], +} + +/// Represents a 3D ray with an origin, a direction, and an intersection distance range from the origin. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsrayoriginmindistancedirectionmaxdistance> +#[repr(C)] +pub struct MPSRayOriginMinDistanceDirectionMaxDistance { + /// Ray origin. The intersection test will be skipped if the origin contains NaNs or infinities. + pub origin: MPSPackedFloat3, + /// Minimum intersection distance from the origin along the ray direction. + /// The intersection test will be skipped if the minimum distance is equal to positive infinity or NaN. + pub min_distance: f32, + /// Ray direction. Does not need to be normalized. The intersection test will be skipped if + /// the direction has length zero or contains NaNs or infinities. + pub direction: MPSPackedFloat3, + /// Maximum intersection distance from the origin along the ray direction. May be infinite. + /// The intersection test will be skipped if the maximum distance is less than zero, NaN, or + /// less than the minimum intersection distance. + pub max_distance: f32, +} + +/// Intersection result which contains the distance from the ray origin to the intersection point, +/// the index of the intersected primitive, and the first two barycentric coordinates of the intersection point. +/// +/// See <https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiondistanceprimitiveindexcoordinates> +#[repr(C)] +pub struct MPSIntersectionDistancePrimitiveIndexCoordinates { + /// Distance from the ray origin to the intersection point along the ray direction vector such + /// that `intersection = ray.origin + ray.direction * distance`. + /// Is negative if there is no intersection. If the intersection type is `MPSIntersectionTypeAny`, + /// is a positive value for a hit or a negative value for a miss. + pub distance: f32, + /// Index of the intersected primitive. Undefined if the ray does not intersect a primitive or + /// if the intersection type is `MPSIntersectionTypeAny`. + pub primitive_index: u32, + /// The first two barycentric coordinates `U` and `V` of the intersection point. + /// The third coordinate `W = 1 - U - V`. Undefined if the ray does not intersect a primitive or + /// if the intersection type is `MPSIntersectionTypeAny`. + pub coordinates: [f32; 2], +} diff --git a/third_party/rust/metal/src/pipeline/compute.rs b/third_party/rust/metal/src/pipeline/compute.rs new file mode 100644 index 0000000000..bcadc3d720 --- /dev/null +++ b/third_party/rust/metal/src/pipeline/compute.rs @@ -0,0 +1,471 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtlattributeformat> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLAttributeFormat { + Invalid = 0, + UChar2 = 1, + UChar3 = 2, + UChar4 = 3, + Char2 = 4, + Char3 = 5, + Char4 = 6, + UChar2Normalized = 7, + UChar3Normalized = 8, + UChar4Normalized = 9, + Char2Normalized = 10, + Char3Normalized = 11, + Char4Normalized = 12, + UShort2 = 13, + UShort3 = 14, + UShort4 = 15, + Short2 = 16, + Short3 = 17, + Short4 = 18, + UShort2Normalized = 19, + UShort3Normalized = 20, + UShort4Normalized = 21, + Short2Normalized = 22, + Short3Normalized = 23, + Short4Normalized = 24, + Half2 = 25, + Half3 = 26, + Half4 = 27, + Float = 28, + Float2 = 29, + Float3 = 30, + Float4 = 31, + Int = 32, + Int2 = 33, + Int3 = 34, + Int4 = 35, + UInt = 36, + UInt2 = 37, + UInt3 = 38, + UInt4 = 39, + Int1010102Normalized = 40, + UInt1010102Normalized = 41, + UChar4Normalized_BGRA = 42, + UChar = 45, + Char = 46, + UCharNormalized = 47, + CharNormalized = 48, + UShort = 49, + Short = 50, + UShortNormalized = 51, + ShortNormalized = 52, + Half = 53, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstepfunction> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLStepFunction { + Constant = 0, + PerInstance = 1, + PerPatch = 2, + PerPatchControlPoint = 3, + PerVertex = 4, + ThreadPositionInGridX = 5, + ThreadPositionInGridXIndexed = 6, + ThreadPositionInGridY = 7, + ThreadPositionInGridYIndexed = 8, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepipelinedescriptor> +pub enum MTLComputePipelineDescriptor {} + +foreign_obj_type! { + type CType = MTLComputePipelineDescriptor; + pub struct ComputePipelineDescriptor; +} + +impl ComputePipelineDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLComputePipelineDescriptor); + msg_send![class, new] + } + } +} + +impl ComputePipelineDescriptorRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn compute_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, computeFunction] } + } + + pub fn set_compute_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setComputeFunction: function] } + } + + pub fn thread_group_size_is_multiple_of_thread_execution_width(&self) -> bool { + unsafe { msg_send_bool![self, threadGroupSizeIsMultipleOfThreadExecutionWidth] } + } + + pub fn set_thread_group_size_is_multiple_of_thread_execution_width( + &self, + size_is_multiple_of_width: bool, + ) { + unsafe { + msg_send![ + self, + setThreadGroupSizeIsMultipleOfThreadExecutionWidth: size_is_multiple_of_width + ] + } + } + + /// API_AVAILABLE(macos(10.14), ios(12.0)); + pub fn max_total_threads_per_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerThreadgroup] } + } + + /// API_AVAILABLE(macos(10.14), ios(12.0)); + pub fn set_max_total_threads_per_threadgroup(&self, max_total_threads: NSUInteger) { + unsafe { msg_send![self, setMaxTotalThreadsPerThreadgroup: max_total_threads] } + } + + /// API_AVAILABLE(ios(13.0),macos(11.0)); + pub fn support_indirect_command_buffers(&self) -> bool { + unsafe { msg_send_bool![self, supportIndirectCommandBuffers] } + } + + /// API_AVAILABLE(ios(13.0),macos(11.0)); + pub fn set_support_indirect_command_buffers(&self, support: bool) { + unsafe { msg_send![self, setSupportIndirectCommandBuffers: support] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn support_adding_binary_functions(&self) -> bool { + unsafe { msg_send_bool![self, supportAddingBinaryFunctions] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn set_support_adding_binary_functions(&self, support: bool) { + unsafe { msg_send![self, setSupportAddingBinaryFunctions: support] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn max_call_stack_depth(&self) -> NSUInteger { + unsafe { msg_send![self, maxCallStackDepth] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn set_max_call_stack_depth(&self, depth: NSUInteger) { + unsafe { msg_send![self, setMaxCallStackDepth: depth] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + /// Marshal to Rust Vec + pub fn insert_libraries(&self) -> Vec<DynamicLibrary> { + unsafe { + let libraries: *mut Object = msg_send![self, insertLibraries]; + let count: NSUInteger = msg_send![libraries, count]; + let ret = (0..count) + .map(|i| { + let lib = msg_send![libraries, objectAtIndex: i]; + DynamicLibrary::from_ptr(lib) + }) + .collect(); + ret + } + } + + /// Marshal from Rust slice + pub fn set_insert_libraries(&self, libraries: &[&DynamicLibraryRef]) { + let ns_array = Array::<DynamicLibrary>::from_slice(libraries); + unsafe { msg_send![self, setInsertLibraries: ns_array] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + /// Marshal to Rust Vec + pub fn binary_archives(&self) -> Vec<BinaryArchive> { + unsafe { + let archives: *mut Object = msg_send![self, binaryArchives]; + let count: NSUInteger = msg_send![archives, count]; + let ret = (0..count) + .map(|i| { + let a = msg_send![archives, objectAtIndex: i]; + BinaryArchive::from_ptr(a) + }) + .collect(); + ret + } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + /// Marshal from Rust slice + pub fn set_binary_archives(&self, archives: &[&BinaryArchiveRef]) { + let ns_array = Array::<BinaryArchive>::from_slice(archives); + unsafe { msg_send![self, setBinaryArchives: ns_array] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn linked_functions(&self) -> &LinkedFunctionsRef { + unsafe { msg_send![self, linkedFunctions] } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + pub fn set_linked_functions(&self, functions: &LinkedFunctionsRef) { + unsafe { msg_send![self, setLinkedFunctions: functions] } + } + + pub fn stage_input_descriptor(&self) -> Option<&StageInputOutputDescriptorRef> { + unsafe { msg_send![self, stageInputDescriptor] } + } + + pub fn set_stage_input_descriptor(&self, descriptor: Option<&StageInputOutputDescriptorRef>) { + unsafe { msg_send![self, setStageInputDescriptor: descriptor] } + } + + pub fn buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, buffers] } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepipelinestate> +pub enum MTLComputePipelineState {} + +foreign_obj_type! { + type CType = MTLComputePipelineState; + pub struct ComputePipelineState; +} + +impl ComputePipelineStateRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn max_total_threads_per_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerThreadgroup] } + } + + pub fn thread_execution_width(&self) -> NSUInteger { + unsafe { msg_send![self, threadExecutionWidth] } + } + + pub fn static_threadgroup_memory_length(&self) -> NSUInteger { + unsafe { msg_send![self, staticThreadgroupMemoryLength] } + } + + /// Only available on (ios(11.0), macos(11.0), macCatalyst(14.0)) NOT available on (tvos) + pub fn imageblock_memory_length_for_dimensions(&self, dimensions: MTLSize) -> NSUInteger { + unsafe { msg_send![self, imageblockMemoryLengthForDimensions: dimensions] } + } + + /// Only available on (ios(13.0), macos(11.0)) + pub fn support_indirect_command_buffers(&self) -> bool { + unsafe { msg_send_bool![self, supportIndirectCommandBuffers] } + } + + /// Only available on (macos(11.0), ios(14.0)) + pub fn function_handle_with_function( + &self, + function: &FunctionRef, + ) -> Option<&FunctionHandleRef> { + unsafe { msg_send![self, functionHandleWithFunction: function] } + } + + // API_AVAILABLE(macos(11.0), ios(14.0)); + // TODO: newComputePipelineStateWithAdditionalBinaryFunctions + // - (nullable id <MTLComputePipelineState>)newComputePipelineStateWithAdditionalBinaryFunctions:(nonnull NSArray<id<MTLFunction>> *)functions error:(__autoreleasing NSError **)error + + // API_AVAILABLE(macos(11.0), ios(14.0)); + // TODO: newVisibleFunctionTableWithDescriptor + // - (nullable id<MTLVisibleFunctionTable>)newVisibleFunctionTableWithDescriptor:(MTLVisibleFunctionTableDescriptor * __nonnull)descriptor + + /// Only available on (macos(11.0), ios(14.0)) + pub fn new_intersection_function_table_with_descriptor( + &self, + descriptor: &IntersectionFunctionTableDescriptorRef, + ) -> IntersectionFunctionTable { + unsafe { msg_send![self, newIntersectionFunctionTableWithDescriptor: descriptor] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlstageinputoutputdescriptor> +pub enum MTLStageInputOutputDescriptor {} + +foreign_obj_type! { + type CType = MTLStageInputOutputDescriptor; + pub struct StageInputOutputDescriptor; +} + +impl StageInputOutputDescriptor { + pub fn new<'a>() -> &'a StageInputOutputDescriptorRef { + unsafe { + let class = class!(MTLStageInputOutputDescriptor); + msg_send![class, stageInputOutputDescriptor] + } + } +} + +impl StageInputOutputDescriptorRef { + pub fn attributes(&self) -> Option<&AttributeDescriptorArrayRef> { + unsafe { msg_send![self, attributes] } + } + + pub fn index_buffer_index(&self) -> NSUInteger { + unsafe { msg_send![self, indexBufferIndex] } + } + + pub fn set_index_buffer_index(&self, idx_buffer_idx: NSUInteger) { + unsafe { msg_send![self, setIndexBufferIndex: idx_buffer_idx] } + } + + pub fn index_type(&self) -> MTLIndexType { + unsafe { msg_send![self, indexType] } + } + + pub fn set_index_type(&self, index_ty: MTLIndexType) { + unsafe { msg_send![self, setIndexType: index_ty] } + } + + pub fn layouts(&self) -> Option<&BufferLayoutDescriptorArrayRef> { + unsafe { msg_send![self, layouts] } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlattributedescriptorarray> +pub enum MTLAttributeDescriptorArray {} + +foreign_obj_type! { + type CType = MTLAttributeDescriptorArray; + pub struct AttributeDescriptorArray; +} + +impl AttributeDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&AttributeDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at(&self, index: NSUInteger, buffer_desc: Option<&AttributeDescriptorRef>) { + unsafe { msg_send![self, setObject:buffer_desc atIndexedSubscript:index] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlattributedescriptor> +pub enum MTLAttributeDescriptor {} + +foreign_obj_type! { + type CType = MTLAttributeDescriptor; + pub struct AttributeDescriptor; +} + +impl AttributeDescriptorRef { + pub fn buffer_index(&self) -> NSUInteger { + unsafe { msg_send![self, bufferIndex] } + } + + pub fn set_buffer_index(&self, buffer_index: NSUInteger) { + unsafe { msg_send![self, setBufferIndex: buffer_index] } + } + + pub fn format(&self) -> MTLAttributeFormat { + unsafe { msg_send![self, format] } + } + + pub fn set_format(&self, format: MTLAttributeFormat) { + unsafe { msg_send![self, setFormat: format] } + } + + pub fn offset(&self) -> NSUInteger { + unsafe { msg_send![self, offset] } + } + + pub fn set_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setOffset: offset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlbufferlayoutdescriptorarray> +pub enum MTLBufferLayoutDescriptorArray {} + +foreign_obj_type! { + type CType = MTLBufferLayoutDescriptorArray; + pub struct BufferLayoutDescriptorArray; +} + +impl BufferLayoutDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&BufferLayoutDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + buffer_desc: Option<&BufferLayoutDescriptorRef>, + ) { + unsafe { msg_send![self, setObject:buffer_desc atIndexedSubscript:index] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlbufferlayoutdescriptor> +pub enum MTLBufferLayoutDescriptor {} + +foreign_obj_type! { + type CType = MTLBufferLayoutDescriptor; + pub struct BufferLayoutDescriptor; +} + +impl BufferLayoutDescriptorRef { + pub fn step_function(&self) -> MTLStepFunction { + unsafe { msg_send![self, stepFunction] } + } + + pub fn set_step_function(&self, step_function: MTLStepFunction) { + unsafe { msg_send![self, setStepFunction: step_function] } + } + + pub fn step_rate(&self) -> NSUInteger { + unsafe { msg_send![self, stepRate] } + } + + pub fn set_step_rate(&self, step_rate: NSUInteger) { + unsafe { msg_send![self, setStepRate: step_rate] } + } + + pub fn stride(&self) -> NSUInteger { + unsafe { msg_send![self, stride] } + } + + pub fn set_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setStride: stride] } + } +} diff --git a/third_party/rust/metal/src/pipeline/mod.rs b/third_party/rust/metal/src/pipeline/mod.rs new file mode 100644 index 0000000000..fe46bcad43 --- /dev/null +++ b/third_party/rust/metal/src/pipeline/mod.rs @@ -0,0 +1,71 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +mod compute; +mod render; + +pub use self::compute::*; +pub use self::render::*; + +/// See <https://developer.apple.com/documentation/metal/mtlmutability> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLMutability { + Default = 0, + Mutable = 1, + Immutable = 2, +} + +impl Default for MTLMutability { + #[inline] + fn default() -> Self { + MTLMutability::Default + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlpipelinebufferdescriptorarray> +pub enum MTLPipelineBufferDescriptorArray {} + +foreign_obj_type! { + type CType = MTLPipelineBufferDescriptorArray; + pub struct PipelineBufferDescriptorArray; +} + +impl PipelineBufferDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&PipelineBufferDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + buffer_desc: Option<&PipelineBufferDescriptorRef>, + ) { + unsafe { msg_send![self, setObject:buffer_desc atIndexedSubscript:index] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlpipelinebufferdescriptor> +pub enum MTLPipelineBufferDescriptor {} + +foreign_obj_type! { + type CType = MTLPipelineBufferDescriptor; + pub struct PipelineBufferDescriptor; +} + +impl PipelineBufferDescriptorRef { + pub fn mutability(&self) -> MTLMutability { + unsafe { msg_send![self, mutability] } + } + + pub fn set_mutability(&self, new_mutability: MTLMutability) { + unsafe { msg_send![self, setMutability: new_mutability] } + } +} diff --git a/third_party/rust/metal/src/pipeline/render.rs b/third_party/rust/metal/src/pipeline/render.rs new file mode 100644 index 0000000000..5c06546d52 --- /dev/null +++ b/third_party/rust/metal/src/pipeline/render.rs @@ -0,0 +1,719 @@ +// Copyright 2017 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtlblendfactor> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLBlendFactor { + Zero = 0, + One = 1, + SourceColor = 2, + OneMinusSourceColor = 3, + SourceAlpha = 4, + OneMinusSourceAlpha = 5, + DestinationColor = 6, + OneMinusDestinationColor = 7, + DestinationAlpha = 8, + OneMinusDestinationAlpha = 9, + SourceAlphaSaturated = 10, + BlendColor = 11, + OneMinusBlendColor = 12, + BlendAlpha = 13, + OneMinusBlendAlpha = 14, + Source1Color = 15, + OneMinusSource1Color = 16, + Source1Alpha = 17, + OneMinusSource1Alpha = 18, +} + +/// See <https://developer.apple.com/documentation/metal/mtlblendoperation> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLBlendOperation { + Add = 0, + Subtract = 1, + ReverseSubtract = 2, + Min = 3, + Max = 4, +} + +bitflags! { + /// See <https://developer.apple.com/documentation/metal/mtlcolorwritemask> + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLColorWriteMask: NSUInteger { + const None = 0; + const Red = 0x1 << 3; + const Green = 0x1 << 2; + const Blue = 0x1 << 1; + const Alpha = 0x1 << 0; + const All = 0xf; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlprimitivetopologyclass> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLPrimitiveTopologyClass { + Unspecified = 0, + Point = 1, + Line = 2, + Triangle = 3, +} + +// TODO: MTLTessellationPartitionMode +// TODO: MTLTessellationFactorStepFunction +// TODO: MTLTessellationFactorFormat +// TODO: MTLTessellationControlPointIndexType + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor> +pub enum MTLRenderPipelineColorAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPipelineColorAttachmentDescriptor; + pub struct RenderPipelineColorAttachmentDescriptor; +} + +impl RenderPipelineColorAttachmentDescriptorRef { + pub fn pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, pixelFormat] } + } + + pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setPixelFormat: pixel_format] } + } + + pub fn is_blending_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isBlendingEnabled] } + } + + pub fn set_blending_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setBlendingEnabled: enabled] } + } + + pub fn source_rgb_blend_factor(&self) -> MTLBlendFactor { + unsafe { msg_send![self, sourceRGBBlendFactor] } + } + + pub fn set_source_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) { + unsafe { msg_send![self, setSourceRGBBlendFactor: blend_factor] } + } + + pub fn destination_rgb_blend_factor(&self) -> MTLBlendFactor { + unsafe { msg_send![self, destinationRGBBlendFactor] } + } + + pub fn set_destination_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) { + unsafe { msg_send![self, setDestinationRGBBlendFactor: blend_factor] } + } + + pub fn rgb_blend_operation(&self) -> MTLBlendOperation { + unsafe { msg_send![self, rgbBlendOperation] } + } + + pub fn set_rgb_blend_operation(&self, blend_operation: MTLBlendOperation) { + unsafe { msg_send![self, setRgbBlendOperation: blend_operation] } + } + + pub fn source_alpha_blend_factor(&self) -> MTLBlendFactor { + unsafe { msg_send![self, sourceAlphaBlendFactor] } + } + + pub fn set_source_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) { + unsafe { msg_send![self, setSourceAlphaBlendFactor: blend_factor] } + } + + pub fn destination_alpha_blend_factor(&self) -> MTLBlendFactor { + unsafe { msg_send![self, destinationAlphaBlendFactor] } + } + + pub fn set_destination_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) { + unsafe { msg_send![self, setDestinationAlphaBlendFactor: blend_factor] } + } + + pub fn alpha_blend_operation(&self) -> MTLBlendOperation { + unsafe { msg_send![self, alphaBlendOperation] } + } + + pub fn set_alpha_blend_operation(&self, blend_operation: MTLBlendOperation) { + unsafe { msg_send![self, setAlphaBlendOperation: blend_operation] } + } + + pub fn write_mask(&self) -> MTLColorWriteMask { + unsafe { msg_send![self, writeMask] } + } + + pub fn set_write_mask(&self, mask: MTLColorWriteMask) { + unsafe { msg_send![self, setWriteMask: mask] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpipelinereflection> +pub enum MTLRenderPipelineReflection {} + +foreign_obj_type! { + type CType = MTLRenderPipelineReflection; + pub struct RenderPipelineReflection; +} + +impl RenderPipelineReflection { + #[cfg(feature = "private")] + pub unsafe fn new( + vertex_data: *mut std::ffi::c_void, + fragment_data: *mut std::ffi::c_void, + vertex_desc: *mut std::ffi::c_void, + device: &DeviceRef, + options: u64, + flags: u64, + ) -> Self { + let class = class!(MTLRenderPipelineReflection); + let this: RenderPipelineReflection = msg_send![class, alloc]; + let this_alias: *mut Object = msg_send![this.as_ref(), initWithVertexData:vertex_data + fragmentData:fragment_data + serializedVertexDescriptor:vertex_desc + device:device + options:options + flags:flags]; + if this_alias.is_null() { + panic!("[MTLRenderPipelineReflection init] failed"); + } + this + } +} + +impl RenderPipelineReflectionRef { + /// An array of objects that describe the arguments of a fragment function. + pub fn fragment_arguments(&self) -> &ArgumentArrayRef { + unsafe { msg_send![self, fragmentArguments] } + } + + /// An array of objects that describe the arguments of a vertex function. + pub fn vertex_arguments(&self) -> &ArgumentArrayRef { + unsafe { msg_send![self, vertexArguments] } + } + + /// An array of objects that describe the arguments of a tile shading function. + pub fn tile_arguments(&self) -> &ArgumentArrayRef { + unsafe { msg_send![self, tileArguments] } + } +} + +/// TODO: Find documentation link. +pub enum MTLArgumentArray {} + +foreign_obj_type! { + type CType = MTLArgumentArray; + pub struct ArgumentArray; +} + +impl ArgumentArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&ArgumentRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn count(&self) -> NSUInteger { + unsafe { msg_send![self, count] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlcomputepipelinereflection> +pub enum MTLComputePipelineReflection {} + +foreign_obj_type! { + type CType = MTLComputePipelineReflection; + pub struct ComputePipelineReflection; +} + +impl ComputePipelineReflectionRef { + /// An array of objects that describe the arguments of a compute function. + pub fn arguments(&self) -> &ArgumentArrayRef { + unsafe { msg_send![self, arguments] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlmeshrenderpipelinedescriptor> +/// Only available in (macos(13.0), ios(16.0)) +pub enum MTLMeshRenderPipelineDescriptor {} + +foreign_obj_type! { + type CType = MTLMeshRenderPipelineDescriptor; + pub struct MeshRenderPipelineDescriptor; +} + +impl MeshRenderPipelineDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLMeshRenderPipelineDescriptor); + msg_send![class, new] + } + } +} + +impl MeshRenderPipelineDescriptorRef { + pub fn color_attachments(&self) -> &RenderPipelineColorAttachmentDescriptorArrayRef { + unsafe { msg_send![self, colorAttachments] } + } + + pub fn depth_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, depthAttachmentPixelFormat] } + } + + pub fn set_depth_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setDepthAttachmentPixelFormat: pixel_format] } + } + + pub fn fragment_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, fragmentBuffers] } + } + + pub fn fragment_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, fragmentFunction] } + } + + pub fn set_fragment_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setFragmentFunction: function] } + } + + pub fn is_alpha_to_coverage_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isAlphaToCoverageEnabled] } + } + + pub fn set_alpha_to_coverage_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToCoverageEnabled: enabled] } + } + + pub fn is_alpha_to_one_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isAlphaToOneEnabled] } + } + + pub fn set_alpha_to_one_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToOneEnabled: enabled] } + } + + pub fn is_rasterization_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isRasterizationEnabled] } + } + + pub fn set_rasterization_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setRasterizationEnabled: enabled] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn max_total_threadgroups_per_mesh_grid(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadgroupsPerMeshGrid] } + } + + pub fn set_max_total_threadgroups_per_mesh_grid( + &self, + max_total_threadgroups_per_mesh_grid: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadgroupsPerMeshGrid: max_total_threadgroups_per_mesh_grid + ] + } + } + + pub fn max_total_threads_per_mesh_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerMeshThreadgroup] } + } + + pub fn set_max_total_threads_per_mesh_threadgroup( + &self, + max_total_threads_per_mesh_threadgroup: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadsPerMeshThreadgroup: max_total_threads_per_mesh_threadgroup + ] + } + } + + pub fn max_total_threads_per_object_threadgroup(&self) -> NSUInteger { + unsafe { msg_send![self, maxTotalThreadsPerObjectThreadgroup] } + } + + pub fn set_max_total_threads_per_object_threadgroup( + &self, + max_total_threads_per_object_threadgroup: NSUInteger, + ) { + unsafe { + msg_send![ + self, + setMaxTotalThreadsPerObjectThreadgroup: max_total_threads_per_object_threadgroup + ] + } + } + + pub fn max_vertex_amplification_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxVertexAmplificationCount] } + } + + pub fn set_max_vertex_amplification_count(&self, max_vertex_amplification_count: NSUInteger) { + unsafe { + msg_send![ + self, + setMaxVertexAmplificationCount: max_vertex_amplification_count + ] + } + } + + pub fn mesh_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, meshBuffers] } + } + + pub fn mesh_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, meshFunction] } + } + + pub fn set_mesh_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setMeshFunction: function] } + } + + pub fn mesh_threadgroup_size_is_multiple_of_thread_execution_width(&self) -> bool { + unsafe { msg_send_bool![self, isMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth] } + } + + pub fn set_mesh_threadgroup_size_is_multiple_of_thread_execution_width( + &self, + mesh_threadgroup_size_is_multiple_of_thread_execution_width: bool, + ) { + unsafe { + msg_send![ + self, + setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth: + mesh_threadgroup_size_is_multiple_of_thread_execution_width + ] + } + } + + pub fn object_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, objectBuffers] } + } + + pub fn object_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, objectFunction] } + } + + pub fn set_object_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setObjectFunction: function] } + } + + pub fn object_threadgroup_size_is_multiple_of_thread_execution_width(&self) -> bool { + unsafe { + msg_send_bool![ + self, + isObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth + ] + } + } + + pub fn set_object_threadgroup_size_is_multiple_of_thread_execution_width( + &self, + object_threadgroup_size_is_multiple_of_thread_execution_width: bool, + ) { + unsafe { + msg_send![ + self, + setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth: + object_threadgroup_size_is_multiple_of_thread_execution_width + ] + } + } + + pub fn payload_memory_length(&self) -> NSUInteger { + unsafe { msg_send![self, payloadMemoryLength] } + } + + pub fn set_payload_memory_length(&self, payload_memory_length: NSUInteger) { + unsafe { msg_send![self, setPayloadMemoryLength: payload_memory_length] } + } + + pub fn raster_sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, rasterSampleCount] } + } + + pub fn set_raster_sample_count(&self, raster_sample_count: NSUInteger) { + unsafe { msg_send![self, setRasterSampleCount: raster_sample_count] } + } + + pub fn stencil_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, stencilAttachmentPixelFormat] } + } + + pub fn set_stencil_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setStencilAttachmentPixelFormat: pixel_format] } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor> +pub enum MTLRenderPipelineDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPipelineDescriptor; + pub struct RenderPipelineDescriptor; +} + +impl RenderPipelineDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLRenderPipelineDescriptor); + msg_send![class, new] + } + } +} + +impl RenderPipelineDescriptorRef { + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn vertex_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, vertexFunction] } + } + + pub fn set_vertex_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setVertexFunction: function] } + } + + pub fn fragment_function(&self) -> Option<&FunctionRef> { + unsafe { msg_send![self, fragmentFunction] } + } + + pub fn set_fragment_function(&self, function: Option<&FunctionRef>) { + unsafe { msg_send![self, setFragmentFunction: function] } + } + + pub fn vertex_descriptor(&self) -> Option<&VertexDescriptorRef> { + unsafe { msg_send![self, vertexDescriptor] } + } + + pub fn set_vertex_descriptor(&self, descriptor: Option<&VertexDescriptorRef>) { + unsafe { msg_send![self, setVertexDescriptor: descriptor] } + } + + /// DEPRECATED - aliases rasterSampleCount property + pub fn sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, sampleCount] } + } + + /// DEPRECATED - aliases rasterSampleCount property + pub fn set_sample_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setSampleCount: count] } + } + + pub fn raster_sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, rasterSampleCount] } + } + + pub fn set_raster_sample_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setRasterSampleCount: count] } + } + + pub fn max_vertex_amplification_count(&self) -> NSUInteger { + unsafe { msg_send![self, maxVertexAmplificationCount] } + } + + pub fn set_max_vertex_amplification_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMaxVertexAmplificationCount: count] } + } + + pub fn is_alpha_to_coverage_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isAlphaToCoverageEnabled] } + } + + pub fn set_alpha_to_coverage_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToCoverageEnabled: enabled] } + } + + pub fn is_alpha_to_one_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isAlphaToOneEnabled] } + } + + pub fn set_alpha_to_one_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setAlphaToOneEnabled: enabled] } + } + + pub fn is_rasterization_enabled(&self) -> bool { + unsafe { msg_send_bool![self, isRasterizationEnabled] } + } + + pub fn set_rasterization_enabled(&self, enabled: bool) { + unsafe { msg_send![self, setRasterizationEnabled: enabled] } + } + + pub fn color_attachments(&self) -> &RenderPipelineColorAttachmentDescriptorArrayRef { + unsafe { msg_send![self, colorAttachments] } + } + + pub fn depth_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, depthAttachmentPixelFormat] } + } + + pub fn set_depth_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setDepthAttachmentPixelFormat: pixel_format] } + } + + pub fn stencil_attachment_pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, stencilAttachmentPixelFormat] } + } + + pub fn set_stencil_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setStencilAttachmentPixelFormat: pixel_format] } + } + + pub fn input_primitive_topology(&self) -> MTLPrimitiveTopologyClass { + unsafe { msg_send![self, inputPrimitiveTopology] } + } + + pub fn set_input_primitive_topology(&self, topology: MTLPrimitiveTopologyClass) { + unsafe { msg_send![self, setInputPrimitiveTopology: topology] } + } + + #[cfg(feature = "private")] + pub unsafe fn serialize_vertex_data(&self) -> *mut std::ffi::c_void { + use std::ptr; + let flags = 0; + let err: *mut Object = ptr::null_mut(); + msg_send![self, newSerializedVertexDataWithFlags:flags + error:err] + } + + #[cfg(feature = "private")] + pub unsafe fn serialize_fragment_data(&self) -> *mut std::ffi::c_void { + msg_send![self, serializeFragmentData] + } + + pub fn support_indirect_command_buffers(&self) -> bool { + unsafe { msg_send_bool![self, supportIndirectCommandBuffers] } + } + + pub fn set_support_indirect_command_buffers(&self, support: bool) { + unsafe { msg_send![self, setSupportIndirectCommandBuffers: support] } + } + + pub fn vertex_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, vertexBuffers] } + } + + pub fn fragment_buffers(&self) -> Option<&PipelineBufferDescriptorArrayRef> { + unsafe { msg_send![self, fragmentBuffers] } + } + + // TODO: tesselation stuff + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + /// Marshal to Rust Vec + pub fn binary_archives(&self) -> Vec<BinaryArchive> { + unsafe { + let archives: *mut Object = msg_send![self, binaryArchives]; + let count: NSUInteger = msg_send![archives, count]; + let ret = (0..count) + .map(|i| { + let a = msg_send![archives, objectAtIndex: i]; + BinaryArchive::from_ptr(a) + }) + .collect(); + ret + } + } + + /// API_AVAILABLE(macos(11.0), ios(14.0)); + /// Marshal from Rust slice + pub fn set_binary_archives(&self, archives: &[&BinaryArchiveRef]) { + let ns_array = Array::<BinaryArchive>::from_slice(archives); + unsafe { msg_send![self, setBinaryArchives: ns_array] } + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpipelinestate> +pub enum MTLRenderPipelineState {} + +foreign_obj_type! { + type CType = MTLRenderPipelineState; + pub struct RenderPipelineState; +} + +impl RenderPipelineStateRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptorarray> +pub enum MTLRenderPipelineColorAttachmentDescriptorArray {} + +foreign_obj_type! { + type CType = MTLRenderPipelineColorAttachmentDescriptorArray; + pub struct RenderPipelineColorAttachmentDescriptorArray; +} + +impl RenderPipelineColorAttachmentDescriptorArrayRef { + pub fn object_at( + &self, + index: NSUInteger, + ) -> Option<&RenderPipelineColorAttachmentDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attachment: Option<&RenderPipelineColorAttachmentDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attachment + atIndexedSubscript:index] + } + } +} diff --git a/third_party/rust/metal/src/renderpass.rs b/third_party/rust/metal/src/renderpass.rs new file mode 100644 index 0000000000..f9aa50b5aa --- /dev/null +++ b/third_party/rust/metal/src/renderpass.rs @@ -0,0 +1,443 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +/// See <https://developer.apple.com/documentation/metal/mtlloadaction> +#[repr(u64)] +#[derive(Copy, Clone, Debug)] +pub enum MTLLoadAction { + DontCare = 0, + Load = 1, + Clear = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstoreaction> +#[repr(u64)] +#[derive(Copy, Clone, Debug)] +pub enum MTLStoreAction { + DontCare = 0, + Store = 1, + MultisampleResolve = 2, + StoreAndMultisampleResolve = 3, + Unknown = 4, + CustomSampleDepthStore = 5, +} + +/// See <https://developer.apple.com/documentation/metal/mtlclearcolor> +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MTLClearColor { + pub red: f64, + pub green: f64, + pub blue: f64, + pub alpha: f64, +} + +impl MTLClearColor { + #[inline] + pub fn new(red: f64, green: f64, blue: f64, alpha: f64) -> Self { + Self { + red, + green, + blue, + alpha, + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlmultisamplestencilresolvefilter> +#[repr(u32)] +#[allow(non_camel_case_types)] +pub enum MTLMultisampleStencilResolveFilter { + Sample0 = 0, + DepthResolvedSample = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor> +pub enum MTLRenderPassAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassAttachmentDescriptor; + pub struct RenderPassAttachmentDescriptor; +} + +impl RenderPassAttachmentDescriptorRef { + pub fn texture(&self) -> Option<&TextureRef> { + unsafe { msg_send![self, texture] } + } + + pub fn set_texture(&self, texture: Option<&TextureRef>) { + unsafe { msg_send![self, setTexture: texture] } + } + + pub fn level(&self) -> NSUInteger { + unsafe { msg_send![self, level] } + } + + pub fn set_level(&self, level: NSUInteger) { + unsafe { msg_send![self, setLevel: level] } + } + + pub fn slice(&self) -> NSUInteger { + unsafe { msg_send![self, slice] } + } + + pub fn set_slice(&self, slice: NSUInteger) { + unsafe { msg_send![self, setSlice: slice] } + } + + pub fn depth_plane(&self) -> NSUInteger { + unsafe { msg_send![self, depthPlane] } + } + + pub fn set_depth_plane(&self, depth_plane: NSUInteger) { + unsafe { msg_send![self, setDepthPlane: depth_plane] } + } + + pub fn resolve_texture(&self) -> Option<&TextureRef> { + unsafe { msg_send![self, resolveTexture] } + } + + pub fn set_resolve_texture(&self, resolve_texture: Option<&TextureRef>) { + unsafe { msg_send![self, setResolveTexture: resolve_texture] } + } + + pub fn resolve_level(&self) -> NSUInteger { + unsafe { msg_send![self, resolveLevel] } + } + + pub fn set_resolve_level(&self, resolve_level: NSUInteger) { + unsafe { msg_send![self, setResolveLevel: resolve_level] } + } + + pub fn resolve_slice(&self) -> NSUInteger { + unsafe { msg_send![self, resolveSlice] } + } + + pub fn set_resolve_slice(&self, resolve_slice: NSUInteger) { + unsafe { msg_send![self, setResolveSlice: resolve_slice] } + } + + pub fn resolve_depth_plane(&self) -> NSUInteger { + unsafe { msg_send![self, resolveDepthPlane] } + } + + pub fn set_resolve_depth_plane(&self, resolve_depth_plane: NSUInteger) { + unsafe { msg_send![self, setResolveDepthPlane: resolve_depth_plane] } + } + + pub fn load_action(&self) -> MTLLoadAction { + unsafe { msg_send![self, loadAction] } + } + + pub fn set_load_action(&self, load_action: MTLLoadAction) { + unsafe { msg_send![self, setLoadAction: load_action] } + } + + pub fn store_action(&self) -> MTLStoreAction { + unsafe { msg_send![self, storeAction] } + } + + pub fn set_store_action(&self, store_action: MTLStoreAction) { + unsafe { msg_send![self, setStoreAction: store_action] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor> +pub enum MTLRenderPassColorAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassColorAttachmentDescriptor; + pub struct RenderPassColorAttachmentDescriptor; + type ParentType = RenderPassAttachmentDescriptor; +} + +impl RenderPassColorAttachmentDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLRenderPassColorAttachmentDescriptor); + msg_send![class, new] + } + } +} + +impl RenderPassColorAttachmentDescriptorRef { + pub fn clear_color(&self) -> MTLClearColor { + unsafe { msg_send![self, clearColor] } + } + + pub fn set_clear_color(&self, clear_color: MTLClearColor) { + unsafe { msg_send![self, setClearColor: clear_color] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpassdepthattachmentdescriptor> +pub enum MTLRenderPassDepthAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassDepthAttachmentDescriptor; + pub struct RenderPassDepthAttachmentDescriptor; + type ParentType = RenderPassAttachmentDescriptor; +} + +impl RenderPassDepthAttachmentDescriptorRef { + pub fn clear_depth(&self) -> f64 { + unsafe { msg_send![self, clearDepth] } + } + + pub fn set_clear_depth(&self, clear_depth: f64) { + unsafe { msg_send![self, setClearDepth: clear_depth] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor> +pub enum MTLRenderPassStencilAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassStencilAttachmentDescriptor; + pub struct RenderPassStencilAttachmentDescriptor; + type ParentType = RenderPassAttachmentDescriptor; +} + +impl RenderPassStencilAttachmentDescriptorRef { + pub fn clear_stencil(&self) -> u32 { + unsafe { msg_send![self, clearStencil] } + } + + pub fn set_clear_stencil(&self, clear_stencil: u32) { + unsafe { msg_send![self, setClearStencil: clear_stencil] } + } + + pub fn stencil_resolve_filter(&self) -> MTLMultisampleStencilResolveFilter { + unsafe { msg_send![self, stencilResolveFilter] } + } + + pub fn set_stencil_resolve_filter( + &self, + stencil_resolve_filter: MTLMultisampleStencilResolveFilter, + ) { + unsafe { msg_send![self, setStencilResolveFilter: stencil_resolve_filter] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptorarray> +pub enum MTLRenderPassColorAttachmentDescriptorArray {} + +foreign_obj_type! { + type CType = MTLRenderPassColorAttachmentDescriptorArray; + pub struct RenderPassColorAttachmentDescriptorArray; +} + +impl RenderPassColorAttachmentDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&RenderPassColorAttachmentDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attachment: Option<&RenderPassColorAttachmentDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attachment + atIndexedSubscript:index] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpasssamplebufferattachmentdescriptor> +pub enum MTLRenderPassSampleBufferAttachmentDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassSampleBufferAttachmentDescriptor; + pub struct RenderPassSampleBufferAttachmentDescriptor; +} + +impl RenderPassSampleBufferAttachmentDescriptor { + pub fn new() -> Self { + let class = class!(MTLRenderPassSampleBufferAttachmentDescriptor); + unsafe { msg_send![class, new] } + } +} + +impl RenderPassSampleBufferAttachmentDescriptorRef { + pub fn sample_buffer(&self) -> &CounterSampleBufferRef { + unsafe { msg_send![self, sampleBuffer] } + } + + pub fn set_sample_buffer(&self, sample_buffer: &CounterSampleBufferRef) { + unsafe { msg_send![self, setSampleBuffer: sample_buffer] } + } + + pub fn start_of_vertex_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, startOfVertexSampleIndex] } + } + + pub fn set_start_of_vertex_sample_index(&self, start_of_vertex_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setStartOfVertexSampleIndex: start_of_vertex_sample_index + ] + } + } + + pub fn end_of_vertex_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, endOfVertexSampleIndex] } + } + + pub fn set_end_of_vertex_sample_index(&self, end_of_vertex_sample_index: NSUInteger) { + unsafe { msg_send![self, setEndOfVertexSampleIndex: end_of_vertex_sample_index] } + } + + pub fn start_of_fragment_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, startOfFragmentSampleIndex] } + } + + pub fn set_start_of_fragment_sample_index(&self, start_of_fragment_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setStartOfFragmentSampleIndex: start_of_fragment_sample_index + ] + } + } + + pub fn end_of_fragment_sample_index(&self) -> NSUInteger { + unsafe { msg_send![self, endOfFragmentSampleIndex] } + } + + pub fn set_end_of_fragment_sample_index(&self, end_of_fragment_sample_index: NSUInteger) { + unsafe { + msg_send![ + self, + setEndOfFragmentSampleIndex: end_of_fragment_sample_index + ] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlrenderpasssamplebufferattachmentdescriptorarray> +pub enum MTLRenderPassSampleBufferAttachmentDescriptorArray {} + +foreign_obj_type! { + type CType = MTLRenderPassSampleBufferAttachmentDescriptorArray; + pub struct RenderPassSampleBufferAttachmentDescriptorArray; +} + +impl RenderPassSampleBufferAttachmentDescriptorArrayRef { + pub fn object_at( + &self, + index: NSUInteger, + ) -> Option<&RenderPassSampleBufferAttachmentDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attachment: Option<&RenderPassSampleBufferAttachmentDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attachment + atIndexedSubscript:index] + } + } +} + +/// ## Important! +/// When configuring a [`MTLTextureDescriptor`] object for use with an attachment, set its usage +/// value to renderTarget if you already know that you intend to use the resulting MTLTexture object in +/// an attachment. This may significantly improve your app’s performance with certain hardware. +/// +/// See <https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor> +pub enum MTLRenderPassDescriptor {} + +foreign_obj_type! { + type CType = MTLRenderPassDescriptor; + pub struct RenderPassDescriptor; +} + +impl RenderPassDescriptor { + /// Creates a default render pass descriptor with no attachments. + pub fn new<'a>() -> &'a RenderPassDescriptorRef { + unsafe { msg_send![class!(MTLRenderPassDescriptor), renderPassDescriptor] } + } +} + +impl RenderPassDescriptorRef { + pub fn color_attachments(&self) -> &RenderPassColorAttachmentDescriptorArrayRef { + unsafe { msg_send![self, colorAttachments] } + } + + pub fn depth_attachment(&self) -> Option<&RenderPassDepthAttachmentDescriptorRef> { + unsafe { msg_send![self, depthAttachment] } + } + + pub fn set_depth_attachment( + &self, + depth_attachment: Option<&RenderPassDepthAttachmentDescriptorRef>, + ) { + unsafe { msg_send![self, setDepthAttachment: depth_attachment] } + } + + pub fn stencil_attachment(&self) -> Option<&RenderPassStencilAttachmentDescriptorRef> { + unsafe { msg_send![self, stencilAttachment] } + } + + pub fn set_stencil_attachment( + &self, + stencil_attachment: Option<&RenderPassStencilAttachmentDescriptorRef>, + ) { + unsafe { msg_send![self, setStencilAttachment: stencil_attachment] } + } + + pub fn visibility_result_buffer(&self) -> Option<&BufferRef> { + unsafe { msg_send![self, visibilityResultBuffer] } + } + + pub fn set_visibility_result_buffer(&self, buffer: Option<&BufferRef>) { + unsafe { msg_send![self, setVisibilityResultBuffer: buffer] } + } + + pub fn render_target_array_length(&self) -> NSUInteger { + unsafe { msg_send![self, renderTargetArrayLength] } + } + + pub fn set_render_target_array_length(&self, length: NSUInteger) { + unsafe { msg_send![self, setRenderTargetArrayLength: length] } + } + + pub fn render_target_width(&self) -> NSUInteger { + unsafe { msg_send![self, renderTargetWidth] } + } + + pub fn set_render_target_width(&self, size: NSUInteger) { + unsafe { msg_send![self, setRenderTargetWidth: size] } + } + + pub fn render_target_height(&self) -> NSUInteger { + unsafe { msg_send![self, renderTargetHeight] } + } + + pub fn set_render_target_height(&self, size: NSUInteger) { + unsafe { msg_send![self, setRenderTargetHeight: size] } + } + + pub fn default_raster_sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, defaultRasterSampleCount] } + } + + pub fn set_default_raster_sample_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setDefaultRasterSampleCount: count] } + } + + pub fn sample_buffer_attachments(&self) -> &RenderPassSampleBufferAttachmentDescriptorArrayRef { + unsafe { msg_send![self, sampleBufferAttachments] } + } +} diff --git a/third_party/rust/metal/src/resource.rs b/third_party/rust/metal/src/resource.rs new file mode 100644 index 0000000000..19cee900a7 --- /dev/null +++ b/third_party/rust/metal/src/resource.rs @@ -0,0 +1,182 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtlpurgeablestate> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLPurgeableState { + KeepCurrent = 1, + NonVolatile = 2, + Volatile = 3, + Empty = 4, +} + +/// See <https://developer.apple.com/documentation/metal/mtlcpucachemode> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLCPUCacheMode { + DefaultCache = 0, + WriteCombined = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlstoragemode> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLStorageMode { + Shared = 0, + Managed = 1, + Private = 2, + /// Only available on macos(11.0), macCatalyst(14.0), ios(10.0) + Memoryless = 3, +} + +/// Only available on macos(10.15), ios(13.0) +/// +/// See <https://developer.apple.com/documentation/metal/mtlhazardtrackingmode> +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum MTLHazardTrackingMode { + Default = 0, + Untracked = 1, + Tracked = 2, +} + +pub const MTLResourceCPUCacheModeShift: NSUInteger = 0; +pub const MTLResourceCPUCacheModeMask: NSUInteger = 0xf << MTLResourceCPUCacheModeShift; +pub const MTLResourceStorageModeShift: NSUInteger = 4; +pub const MTLResourceStorageModeMask: NSUInteger = 0xf << MTLResourceStorageModeShift; +pub const MTLResourceHazardTrackingModeShift: NSUInteger = 8; +pub const MTLResourceHazardTrackingModeMask: NSUInteger = 0x3 << MTLResourceHazardTrackingModeShift; + +bitflags! { + /// See <https://developer.apple.com/documentation/metal/mtlresourceoptions> + #[allow(non_upper_case_globals)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLResourceOptions: NSUInteger { + const CPUCacheModeDefaultCache = (MTLCPUCacheMode::DefaultCache as NSUInteger) << MTLResourceCPUCacheModeShift; + const CPUCacheModeWriteCombined = (MTLCPUCacheMode::WriteCombined as NSUInteger) << MTLResourceCPUCacheModeShift; + + const StorageModeShared = (MTLStorageMode::Shared as NSUInteger) << MTLResourceStorageModeShift; + const StorageModeManaged = (MTLStorageMode::Managed as NSUInteger) << MTLResourceStorageModeShift; + const StorageModePrivate = (MTLStorageMode::Private as NSUInteger) << MTLResourceStorageModeShift; + const StorageModeMemoryless = (MTLStorageMode::Memoryless as NSUInteger) << MTLResourceStorageModeShift; + + /// Only available on macos(10.13), ios(10.0) + const HazardTrackingModeDefault = (MTLHazardTrackingMode::Default as NSUInteger) << MTLResourceHazardTrackingModeShift; + /// Only available on macos(10.13), ios(10.0) + const HazardTrackingModeUntracked = (MTLHazardTrackingMode::Untracked as NSUInteger) << MTLResourceHazardTrackingModeShift; + /// Only available on macos(10.15), ios(13.0) + const HazardTrackingModeTracked = (MTLHazardTrackingMode::Tracked as NSUInteger) << MTLResourceHazardTrackingModeShift; + } +} + +bitflags! { + /// Options that describe how a graphics or compute function uses an argument buffer’s resource. + /// + /// Enabling certain options for certain resources determines whether the Metal driver should + /// convert the resource to another format (for example, whether to decompress a color render target). + /// + /// See <https://developer.apple.com/documentation/metal/mtlresourceusage> + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLResourceUsage: NSUInteger { + /// An option that enables reading from the resource. + const Read = 1 << 0; + /// An option that enables writing to the resource. + const Write = 1 << 1; + /// An option that enables sampling from the resource. + /// + /// Specify this option only if the resource is a texture. + const Sample = 1 << 2; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlsizeandalign> +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[repr(C)] +pub struct MTLSizeAndAlign { + pub size: NSUInteger, + pub align: NSUInteger, +} + +/// See <https://developer.apple.com/documentation/metal/mtlresource> +pub enum MTLResource {} + +foreign_obj_type! { + type CType = MTLResource; + pub struct Resource; + type ParentType = NsObject; +} + +impl ResourceRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } + + pub fn cpu_cache_mode(&self) -> MTLCPUCacheMode { + unsafe { msg_send![self, cpuCacheMode] } + } + + pub fn storage_mode(&self) -> MTLStorageMode { + unsafe { msg_send![self, storageMode] } + } + + pub fn set_purgeable_state(&self, state: MTLPurgeableState) -> MTLPurgeableState { + unsafe { msg_send![self, setPurgeableState: state] } + } + + /// Only available on macOS 10.13+ & iOS 10.11+ + pub fn allocated_size(&self) -> NSUInteger { + unsafe { msg_send![self, allocatedSize] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn hazard_tracking_mode(&self) -> MTLHazardTrackingMode { + unsafe { msg_send![self, hazardTrackingMode] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn resource_options(&self) -> MTLResourceOptions { + unsafe { msg_send![self, resourceOptions] } + } + + /// Only available on macos(10.13), ios(10.0) + pub fn heap(&self) -> &HeapRef { + unsafe { msg_send![self, heap] } + } + + /// Only available on macos(10.15), ios(13.0) + pub fn heap_offset(&self) -> NSUInteger { + unsafe { msg_send![self, heapOffset] } + } + + /// Only available on macos(10.13), ios(10.0) + pub fn make_aliasable(&self) { + unsafe { msg_send![self, makeAliasable] } + } + + /// Only available on macos(10.13), ios(10.0) + pub fn is_aliasable(&self) -> bool { + unsafe { msg_send_bool![self, isAliasable] } + } +} diff --git a/third_party/rust/metal/src/sampler.rs b/third_party/rust/metal/src/sampler.rs new file mode 100644 index 0000000000..e9f6416e2a --- /dev/null +++ b/third_party/rust/metal/src/sampler.rs @@ -0,0 +1,161 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::{depthstencil::MTLCompareFunction, DeviceRef, NSUInteger}; + +/// See <https://developer.apple.com/documentation/metal/mtlsamplerminmagfilter> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLSamplerMinMagFilter { + Nearest = 0, + Linear = 1, +} + +/// See <https://developer.apple.com/documentation/metal/mtlsamplermipfilter> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLSamplerMipFilter { + NotMipmapped = 0, + Nearest = 1, + Linear = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlsampleraddressmode> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLSamplerAddressMode { + ClampToEdge = 0, + MirrorClampToEdge = 1, + Repeat = 2, + MirrorRepeat = 3, + ClampToZero = 4, + ClampToBorderColor = 5, +} + +/// See <https://developer.apple.com/documentation/metal/mtlsamplerbordercolor> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLSamplerBorderColor { + TransparentBlack = 0, + OpaqueBlack = 1, + OpaqueWhite = 2, +} + +/// See <https://developer.apple.com/documentation/metal/mtlsamplerdescriptor> +pub enum MTLSamplerDescriptor {} + +foreign_obj_type! { + type CType = MTLSamplerDescriptor; + pub struct SamplerDescriptor; +} + +impl SamplerDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLSamplerDescriptor); + msg_send![class, new] + } + } +} + +impl SamplerDescriptorRef { + pub fn set_min_filter(&self, filter: MTLSamplerMinMagFilter) { + unsafe { msg_send![self, setMinFilter: filter] } + } + + pub fn set_mag_filter(&self, filter: MTLSamplerMinMagFilter) { + unsafe { msg_send![self, setMagFilter: filter] } + } + + pub fn set_mip_filter(&self, filter: MTLSamplerMipFilter) { + unsafe { msg_send![self, setMipFilter: filter] } + } + + pub fn set_address_mode_s(&self, mode: MTLSamplerAddressMode) { + unsafe { msg_send![self, setSAddressMode: mode] } + } + + pub fn set_address_mode_t(&self, mode: MTLSamplerAddressMode) { + unsafe { msg_send![self, setTAddressMode: mode] } + } + + pub fn set_address_mode_r(&self, mode: MTLSamplerAddressMode) { + unsafe { msg_send![self, setRAddressMode: mode] } + } + + pub fn set_max_anisotropy(&self, anisotropy: NSUInteger) { + unsafe { msg_send![self, setMaxAnisotropy: anisotropy] } + } + + pub fn set_compare_function(&self, func: MTLCompareFunction) { + unsafe { msg_send![self, setCompareFunction: func] } + } + + #[cfg(feature = "private")] + pub unsafe fn set_lod_bias(&self, bias: f32) { + msg_send![self, setLodBias: bias] + } + + pub fn set_lod_min_clamp(&self, clamp: f32) { + unsafe { msg_send![self, setLodMinClamp: clamp] } + } + + pub fn set_lod_max_clamp(&self, clamp: f32) { + unsafe { msg_send![self, setLodMaxClamp: clamp] } + } + + pub fn set_lod_average(&self, enable: bool) { + unsafe { msg_send![self, setLodAverage: enable] } + } + + pub fn set_normalized_coordinates(&self, enable: bool) { + unsafe { msg_send![self, setNormalizedCoordinates: enable] } + } + + pub fn set_support_argument_buffers(&self, enable: bool) { + unsafe { msg_send![self, setSupportArgumentBuffers: enable] } + } + + pub fn set_border_color(&self, color: MTLSamplerBorderColor) { + unsafe { msg_send![self, setBorderColor: color] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlsamplerstate> +pub enum MTLSamplerState {} + +foreign_obj_type! { + type CType = MTLSamplerState; + pub struct SamplerState; +} + +impl SamplerStateRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } +} diff --git a/third_party/rust/metal/src/sync.rs b/third_party/rust/metal/src/sync.rs new file mode 100644 index 0000000000..550e06be12 --- /dev/null +++ b/third_party/rust/metal/src/sync.rs @@ -0,0 +1,178 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; +use block::{Block, RcBlock}; +use std::mem; + +#[cfg(feature = "dispatch_queue")] +use dispatch; + +/// See <https://developer.apple.com/documentation/metal/mtlsharedeventnotificationblock> +type MTLSharedEventNotificationBlock<'a> = RcBlock<(&'a SharedEventRef, u64), ()>; + +/// See <https://developer.apple.com/documentation/metal/mtlevent> +pub enum MTLEvent {} + +foreign_obj_type! { + type CType = MTLEvent; + pub struct Event; +} + +impl EventRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlsharedevent> +pub enum MTLSharedEvent {} + +foreign_obj_type! { + type CType = MTLSharedEvent; + pub struct SharedEvent; + type ParentType = Event; +} + +impl SharedEventRef { + pub fn signaled_value(&self) -> u64 { + unsafe { msg_send![self, signaledValue] } + } + + pub fn set_signaled_value(&self, new_value: u64) { + unsafe { msg_send![self, setSignaledValue: new_value] } + } + + /// Schedules a notification handler to be called after the shareable event’s signal value + /// equals or exceeds a given value. + pub fn notify( + &self, + listener: &SharedEventListenerRef, + value: u64, + block: MTLSharedEventNotificationBlock, + ) { + unsafe { + // If the block doesn't have a signature, this segfaults. + // Taken from https://github.com/servo/pathfinder/blob/e858c8dc1d8ff02a5b603e21e09a64d6b3e11327/metal/src/lib.rs#L2327 + let block = mem::transmute::< + MTLSharedEventNotificationBlock, + *mut BlockBase<(&SharedEventRef, u64), ()>, + >(block); + (*block).flags |= BLOCK_HAS_SIGNATURE | BLOCK_HAS_COPY_DISPOSE; + (*block).extra = &BLOCK_EXTRA; + let () = msg_send![self, notifyListener:listener atValue:value block:block]; + } + + extern "C" fn dtor(_: *mut BlockBase<(&SharedEventRef, u64), ()>) {} + + const SIGNATURE: &[u8] = b"v16@?0Q8\0"; + const SIGNATURE_PTR: *const i8 = &SIGNATURE[0] as *const u8 as *const i8; + static mut BLOCK_EXTRA: BlockExtra<(&SharedEventRef, u64), ()> = BlockExtra { + unknown0: 0 as *mut i32, + unknown1: 0 as *mut i32, + unknown2: 0 as *mut i32, + dtor, + signature: &SIGNATURE_PTR, + }; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlsharedeventlistener> +pub enum MTLSharedEventListener {} + +foreign_obj_type! { + type CType = MTLSharedEventListener; + pub struct SharedEventListener; +} + +impl SharedEventListener { + pub unsafe fn from_queue_handle(queue: dispatch_queue_t) -> Self { + let listener: SharedEventListener = msg_send![class!(MTLSharedEventListener), alloc]; + let ptr: *mut Object = msg_send![listener.as_ref(), initWithDispatchQueue: queue]; + if ptr.is_null() { + panic!("[MTLSharedEventListener alloc] initWithDispatchQueue failed"); + } + listener + } + + #[cfg(feature = "dispatch")] + pub fn from_queue(queue: &dispatch::Queue) -> Self { + unsafe { + let raw_queue = std::mem::transmute::<&dispatch::Queue, *const dispatch_queue_t>(queue); + Self::from_queue_handle(*raw_queue) + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlfence> +pub enum MTLFence {} + +foreign_obj_type! { + type CType = MTLFence; + pub struct Fence; +} + +impl FenceRef { + pub fn device(&self) -> &DeviceRef { + unsafe { msg_send![self, device] } + } + + pub fn label(&self) -> &str { + unsafe { + let label = msg_send![self, label]; + crate::nsstring_as_str(label) + } + } + + pub fn set_label(&self, label: &str) { + unsafe { + let nslabel = crate::nsstring_from_str(label); + let () = msg_send![self, setLabel: nslabel]; + } + } +} + +bitflags! { + /// The render stages at which a synchronization command is triggered. + /// + /// Render stages provide finer control for specifying when synchronization must occur, + /// allowing for vertex and fragment processing to overlap in execution. + /// + /// See <https://developer.apple.com/documentation/metal/mtlrenderstages> + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLRenderStages: NSUInteger { + /// The vertex rendering stage. + const Vertex = 1 << 0; + /// The fragment rendering stage. + const Fragment = 1 << 1; + /// The tile rendering stage. + const Tile = 1 << 2; + } +} + +const BLOCK_HAS_COPY_DISPOSE: i32 = 0x02000000; +const BLOCK_HAS_SIGNATURE: i32 = 0x40000000; + +#[repr(C)] +struct BlockBase<A, R> { + isa: *const std::ffi::c_void, // 0x00 + flags: i32, // 0x08 + _reserved: i32, // 0x0c + invoke: unsafe extern "C" fn(*mut Block<A, R>, ...) -> R, // 0x10 + extra: *const BlockExtra<A, R>, // 0x18 +} + +type BlockExtraDtor<A, R> = extern "C" fn(*mut BlockBase<A, R>); + +#[repr(C)] +struct BlockExtra<A, R> { + unknown0: *mut i32, // 0x00 + unknown1: *mut i32, // 0x08 + unknown2: *mut i32, // 0x10 + dtor: BlockExtraDtor<A, R>, // 0x18 + signature: *const *const i8, // 0x20 +} diff --git a/third_party/rust/metal/src/texture.rs b/third_party/rust/metal/src/texture.rs new file mode 100644 index 0000000000..01655d6ab6 --- /dev/null +++ b/third_party/rust/metal/src/texture.rs @@ -0,0 +1,351 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::*; + +use objc::runtime::{NO, YES}; + +/// See <https://developer.apple.com/documentation/metal/mtltexturetype> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum MTLTextureType { + D1 = 0, + D1Array = 1, + D2 = 2, + D2Array = 3, + D2Multisample = 4, + Cube = 5, + CubeArray = 6, + D3 = 7, + D2MultisampleArray = 8, +} + +/// See <https://developer.apple.com/documentation/metal/mtltexturecompressiontype> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum MTLTextureCompressionType { + Lossless = 0, + Lossy = 1, +} + +bitflags! { + /// See <https://developer.apple.com/documentation/metal/mtltextureusage> + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] + pub struct MTLTextureUsage: NSUInteger { + const Unknown = 0x0000; + const ShaderRead = 0x0001; + const ShaderWrite = 0x0002; + const RenderTarget = 0x0004; + const PixelFormatView = 0x0010; + } +} + +/// See <https://developer.apple.com/documentation/metal/mtltexturedescriptor> +pub enum MTLTextureDescriptor {} + +foreign_obj_type! { + type CType = MTLTextureDescriptor; + pub struct TextureDescriptor; +} + +impl TextureDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLTextureDescriptor); + msg_send![class, new] + } + } +} + +impl TextureDescriptorRef { + pub fn texture_type(&self) -> MTLTextureType { + unsafe { msg_send![self, textureType] } + } + + pub fn set_texture_type(&self, texture_type: MTLTextureType) { + unsafe { msg_send![self, setTextureType: texture_type] } + } + + pub fn pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, pixelFormat] } + } + + pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) { + unsafe { msg_send![self, setPixelFormat: pixel_format] } + } + + pub fn width(&self) -> NSUInteger { + unsafe { msg_send![self, width] } + } + + pub fn set_width(&self, width: NSUInteger) { + unsafe { msg_send![self, setWidth: width] } + } + + pub fn height(&self) -> NSUInteger { + unsafe { msg_send![self, height] } + } + + pub fn set_height(&self, height: NSUInteger) { + unsafe { msg_send![self, setHeight: height] } + } + + pub fn depth(&self) -> NSUInteger { + unsafe { msg_send![self, depth] } + } + + pub fn set_depth(&self, depth: NSUInteger) { + unsafe { msg_send![self, setDepth: depth] } + } + + pub fn mipmap_level_count(&self) -> NSUInteger { + unsafe { msg_send![self, mipmapLevelCount] } + } + + pub fn set_mipmap_level_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setMipmapLevelCount: count] } + } + + pub fn set_mipmap_level_count_for_size(&self, size: MTLSize) { + let MTLSize { + width, + height, + depth, + } = size; + let count = (width.max(height).max(depth) as f64).log2().ceil() as u64; + self.set_mipmap_level_count(count); + } + + pub fn sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, sampleCount] } + } + + pub fn set_sample_count(&self, count: NSUInteger) { + unsafe { msg_send![self, setSampleCount: count] } + } + + pub fn array_length(&self) -> NSUInteger { + unsafe { msg_send![self, arrayLength] } + } + + pub fn set_array_length(&self, length: NSUInteger) { + unsafe { msg_send![self, setArrayLength: length] } + } + + pub fn resource_options(&self) -> MTLResourceOptions { + unsafe { msg_send![self, resourceOptions] } + } + + pub fn set_resource_options(&self, options: MTLResourceOptions) { + unsafe { msg_send![self, setResourceOptions: options] } + } + + pub fn cpu_cache_mode(&self) -> MTLCPUCacheMode { + unsafe { msg_send![self, cpuCacheMode] } + } + + pub fn set_cpu_cache_mode(&self, mode: MTLCPUCacheMode) { + unsafe { msg_send![self, setCpuCacheMode: mode] } + } + + pub fn storage_mode(&self) -> MTLStorageMode { + unsafe { msg_send![self, storageMode] } + } + + pub fn set_storage_mode(&self, mode: MTLStorageMode) { + unsafe { msg_send![self, setStorageMode: mode] } + } + + pub fn usage(&self) -> MTLTextureUsage { + unsafe { msg_send![self, usage] } + } + + pub fn set_usage(&self, usage: MTLTextureUsage) { + unsafe { msg_send![self, setUsage: usage] } + } + + pub fn compression_type(&self) -> MTLTextureCompressionType { + unsafe { msg_send![self, compressionType] } + } + + pub fn set_compression_type(&self, compression_type: MTLTextureCompressionType) { + unsafe { msg_send![self, setCompressionType: compression_type] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtltexture> +pub enum MTLTexture {} + +foreign_obj_type! { + type CType = MTLTexture; + pub struct Texture; + type ParentType = Resource; +} + +impl TextureRef { + #[deprecated(since = "0.13.0")] + pub fn root_resource(&self) -> Option<&ResourceRef> { + unsafe { msg_send![self, rootResource] } + } + + pub fn parent_texture(&self) -> Option<&TextureRef> { + unsafe { msg_send![self, parentTexture] } + } + + pub fn parent_relative_level(&self) -> NSUInteger { + unsafe { msg_send![self, parentRelativeLevel] } + } + + pub fn parent_relative_slice(&self) -> NSUInteger { + unsafe { msg_send![self, parentRelativeSlice] } + } + + pub fn buffer(&self) -> Option<&BufferRef> { + unsafe { msg_send![self, buffer] } + } + + pub fn buffer_offset(&self) -> NSUInteger { + unsafe { msg_send![self, bufferOffset] } + } + + pub fn buffer_stride(&self) -> NSUInteger { + unsafe { msg_send![self, bufferBytesPerRow] } + } + + pub fn texture_type(&self) -> MTLTextureType { + unsafe { msg_send![self, textureType] } + } + + pub fn pixel_format(&self) -> MTLPixelFormat { + unsafe { msg_send![self, pixelFormat] } + } + + pub fn width(&self) -> NSUInteger { + unsafe { msg_send![self, width] } + } + + pub fn height(&self) -> NSUInteger { + unsafe { msg_send![self, height] } + } + + pub fn depth(&self) -> NSUInteger { + unsafe { msg_send![self, depth] } + } + + pub fn mipmap_level_count(&self) -> NSUInteger { + unsafe { msg_send![self, mipmapLevelCount] } + } + + pub fn sample_count(&self) -> NSUInteger { + unsafe { msg_send![self, sampleCount] } + } + + pub fn array_length(&self) -> NSUInteger { + unsafe { msg_send![self, arrayLength] } + } + + pub fn usage(&self) -> MTLTextureUsage { + unsafe { msg_send![self, usage] } + } + + /// [framebufferOnly Apple Docs](https://developer.apple.com/documentation/metal/mtltexture/1515749-framebufferonly?language=objc) + pub fn framebuffer_only(&self) -> bool { + unsafe { msg_send_bool![self, isFramebufferOnly] } + } + + pub fn get_bytes( + &self, + bytes: *mut std::ffi::c_void, + stride: NSUInteger, + region: MTLRegion, + mipmap_level: NSUInteger, + ) { + unsafe { + msg_send![self, getBytes:bytes + bytesPerRow:stride + fromRegion:region + mipmapLevel:mipmap_level] + } + } + + pub fn get_bytes_in_slice( + &self, + bytes: *mut std::ffi::c_void, + stride: NSUInteger, + image_stride: NSUInteger, + region: MTLRegion, + mipmap_level: NSUInteger, + slice: NSUInteger, + ) { + unsafe { + msg_send![self, getBytes:bytes + bytesPerRow:stride + bytesPerImage:image_stride + fromRegion:region + mipmapLevel:mipmap_level + slice:slice] + } + } + + pub fn replace_region( + &self, + region: MTLRegion, + mipmap_level: NSUInteger, + bytes: *const std::ffi::c_void, + stride: NSUInteger, + ) { + unsafe { + msg_send![self, replaceRegion:region + mipmapLevel:mipmap_level + withBytes:bytes + bytesPerRow:stride] + } + } + + pub fn replace_region_in_slice( + &self, + region: MTLRegion, + mipmap_level: NSUInteger, + slice: NSUInteger, + bytes: *const std::ffi::c_void, + stride: NSUInteger, + image_stride: NSUInteger, + ) { + unsafe { + msg_send![self, replaceRegion:region + mipmapLevel:mipmap_level + slice:slice + withBytes:bytes + bytesPerRow:stride + bytesPerImage:image_stride] + } + } + + pub fn new_texture_view(&self, pixel_format: MTLPixelFormat) -> Texture { + unsafe { msg_send![self, newTextureViewWithPixelFormat: pixel_format] } + } + + pub fn new_texture_view_from_slice( + &self, + pixel_format: MTLPixelFormat, + texture_type: MTLTextureType, + mipmap_levels: crate::NSRange, + slices: crate::NSRange, + ) -> Texture { + unsafe { + msg_send![self, newTextureViewWithPixelFormat:pixel_format + textureType:texture_type + levels:mipmap_levels + slices:slices] + } + } + + pub fn gpu_resource_id(&self) -> MTLResourceID { + unsafe { msg_send![self, gpuResourceID] } + } +} diff --git a/third_party/rust/metal/src/types.rs b/third_party/rust/metal/src/types.rs new file mode 100644 index 0000000000..92f9daf305 --- /dev/null +++ b/third_party/rust/metal/src/types.rs @@ -0,0 +1,90 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::NSUInteger; +use std::default::Default; + +/// See <https://developer.apple.com/documentation/metal/mtlorigin> +#[repr(C)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub struct MTLOrigin { + pub x: NSUInteger, + pub y: NSUInteger, + pub z: NSUInteger, +} + +/// See <https://developer.apple.com/documentation/metal/mtlsize> +#[repr(C)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub struct MTLSize { + pub width: NSUInteger, + pub height: NSUInteger, + pub depth: NSUInteger, +} + +impl MTLSize { + pub fn new(width: NSUInteger, height: NSUInteger, depth: NSUInteger) -> Self { + Self { + width, + height, + depth, + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlregion> +#[repr(C)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub struct MTLRegion { + pub origin: MTLOrigin, + pub size: MTLSize, +} + +impl MTLRegion { + #[inline] + pub fn new_1d(x: NSUInteger, width: NSUInteger) -> Self { + Self::new_2d(x, 0, width, 1) + } + + #[inline] + pub fn new_2d(x: NSUInteger, y: NSUInteger, width: NSUInteger, height: NSUInteger) -> Self { + Self::new_3d(x, y, 0, width, height, 1) + } + + #[inline] + pub fn new_3d( + x: NSUInteger, + y: NSUInteger, + z: NSUInteger, + width: NSUInteger, + height: NSUInteger, + depth: NSUInteger, + ) -> Self { + Self { + origin: MTLOrigin { x, y, z }, + size: MTLSize { + width, + height, + depth, + }, + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlsampleposition> +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq, Default)] +pub struct MTLSamplePosition { + pub x: f32, + pub y: f32, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub struct MTLResourceID { + pub _impl: u64, +} diff --git a/third_party/rust/metal/src/vertexdescriptor.rs b/third_party/rust/metal/src/vertexdescriptor.rs new file mode 100644 index 0000000000..5e01be4359 --- /dev/null +++ b/third_party/rust/metal/src/vertexdescriptor.rs @@ -0,0 +1,250 @@ +// Copyright 2016 GFX developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use super::NSUInteger; + +/// See <https://developer.apple.com/documentation/metal/mtlvertexformat> +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLVertexFormat { + Invalid = 0, + UChar2 = 1, + UChar3 = 2, + UChar4 = 3, + Char2 = 4, + Char3 = 5, + Char4 = 6, + UChar2Normalized = 7, + UChar3Normalized = 8, + UChar4Normalized = 9, + Char2Normalized = 10, + Char3Normalized = 11, + Char4Normalized = 12, + UShort2 = 13, + UShort3 = 14, + UShort4 = 15, + Short2 = 16, + Short3 = 17, + Short4 = 18, + UShort2Normalized = 19, + UShort3Normalized = 20, + UShort4Normalized = 21, + Short2Normalized = 22, + Short3Normalized = 23, + Short4Normalized = 24, + Half2 = 25, + Half3 = 26, + Half4 = 27, + Float = 28, + Float2 = 29, + Float3 = 30, + Float4 = 31, + Int = 32, + Int2 = 33, + Int3 = 34, + Int4 = 35, + UInt = 36, + UInt2 = 37, + UInt3 = 38, + UInt4 = 39, + Int1010102Normalized = 40, + UInt1010102Normalized = 41, + UChar4Normalized_BGRA = 42, + UChar = 45, + Char = 46, + UCharNormalized = 47, + CharNormalized = 48, + UShort = 49, + Short = 50, + UShortNormalized = 51, + ShortNormalized = 52, + Half = 53, +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexstepfunction> +#[repr(u64)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MTLVertexStepFunction { + Constant = 0, + PerVertex = 1, + PerInstance = 2, + PerPatch = 3, + PerPatchControlPoint = 4, +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexbufferlayoutdescriptor> +pub enum MTLVertexBufferLayoutDescriptor {} + +foreign_obj_type! { + type CType = MTLVertexBufferLayoutDescriptor; + pub struct VertexBufferLayoutDescriptor; +} + +impl VertexBufferLayoutDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLVertexBufferLayoutDescriptor); + msg_send![class, new] + } + } +} + +impl VertexBufferLayoutDescriptorRef { + pub fn stride(&self) -> NSUInteger { + unsafe { msg_send![self, stride] } + } + + pub fn set_stride(&self, stride: NSUInteger) { + unsafe { msg_send![self, setStride: stride] } + } + + pub fn step_function(&self) -> MTLVertexStepFunction { + unsafe { msg_send![self, stepFunction] } + } + + pub fn set_step_function(&self, func: MTLVertexStepFunction) { + unsafe { msg_send![self, setStepFunction: func] } + } + + pub fn step_rate(&self) -> NSUInteger { + unsafe { msg_send![self, stepRate] } + } + + pub fn set_step_rate(&self, step_rate: NSUInteger) { + unsafe { msg_send![self, setStepRate: step_rate] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexbufferlayoutdescriptorarray> +pub enum MTLVertexBufferLayoutDescriptorArray {} + +foreign_obj_type! { + type CType = MTLVertexBufferLayoutDescriptorArray; + pub struct VertexBufferLayoutDescriptorArray; +} + +impl VertexBufferLayoutDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&VertexBufferLayoutDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + layout: Option<&VertexBufferLayoutDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:layout + atIndexedSubscript:index] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor> +pub enum MTLVertexAttributeDescriptor {} + +foreign_obj_type! { + type CType = MTLVertexAttributeDescriptor; + pub struct VertexAttributeDescriptor; +} + +impl VertexAttributeDescriptor { + pub fn new() -> Self { + unsafe { + let class = class!(MTLVertexAttributeDescriptor); + msg_send![class, new] + } + } +} + +impl VertexAttributeDescriptorRef { + pub fn format(&self) -> MTLVertexFormat { + unsafe { msg_send![self, format] } + } + + pub fn set_format(&self, format: MTLVertexFormat) { + unsafe { msg_send![self, setFormat: format] } + } + + pub fn offset(&self) -> NSUInteger { + unsafe { msg_send![self, offset] } + } + + pub fn set_offset(&self, offset: NSUInteger) { + unsafe { msg_send![self, setOffset: offset] } + } + + pub fn buffer_index(&self) -> NSUInteger { + unsafe { msg_send![self, bufferIndex] } + } + + pub fn set_buffer_index(&self, index: NSUInteger) { + unsafe { msg_send![self, setBufferIndex: index] } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexattributedescriptorarray> +pub enum MTLVertexAttributeDescriptorArray {} + +foreign_obj_type! { + type CType = MTLVertexAttributeDescriptorArray; + pub struct VertexAttributeDescriptorArray; +} + +impl VertexAttributeDescriptorArrayRef { + pub fn object_at(&self, index: NSUInteger) -> Option<&VertexAttributeDescriptorRef> { + unsafe { msg_send![self, objectAtIndexedSubscript: index] } + } + + pub fn set_object_at( + &self, + index: NSUInteger, + attribute: Option<&VertexAttributeDescriptorRef>, + ) { + unsafe { + msg_send![self, setObject:attribute + atIndexedSubscript:index] + } + } +} + +/// See <https://developer.apple.com/documentation/metal/mtlvertexdescriptor> +pub enum MTLVertexDescriptor {} + +foreign_obj_type! { + type CType = MTLVertexDescriptor; + pub struct VertexDescriptor; +} + +impl VertexDescriptor { + pub fn new<'a>() -> &'a VertexDescriptorRef { + unsafe { + let class = class!(MTLVertexDescriptor); + msg_send![class, vertexDescriptor] + } + } +} + +impl VertexDescriptorRef { + pub fn layouts(&self) -> &VertexBufferLayoutDescriptorArrayRef { + unsafe { msg_send![self, layouts] } + } + + pub fn attributes(&self) -> &VertexAttributeDescriptorArrayRef { + unsafe { msg_send![self, attributes] } + } + + #[cfg(feature = "private")] + pub unsafe fn serialize_descriptor(&self) -> *mut std::ffi::c_void { + msg_send![self, newSerializedDescriptor] + } + + pub fn reset(&self) { + unsafe { msg_send![self, reset] } + } +} |