From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Fri, 19 Apr 2024 02:47:55 +0200
Subject: Adding upstream version 124.0.1.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 third_party/rust/metal/.cargo-checksum.json        |    1 +
 third_party/rust/metal/Cargo.lock                  | 1516 ++++++++++++++
 third_party/rust/metal/Cargo.toml                  |  144 ++
 third_party/rust/metal/LICENSE-APACHE              |  201 ++
 third_party/rust/metal/LICENSE-MIT                 |   25 +
 third_party/rust/metal/Makefile                    |   16 +
 third_party/rust/metal/README.md                   |   43 +
 third_party/rust/metal/assets/metal.svg            |   15 +
 third_party/rust/metal/bors.toml                   |    8 +
 .../rust/metal/examples/argument-buffer/main.rs    |   88 +
 third_party/rust/metal/examples/bind/main.rs       |   34 +
 third_party/rust/metal/examples/bindless/main.rs   |  149 ++
 third_party/rust/metal/examples/caps/main.rs       |   33 +
 third_party/rust/metal/examples/circle/README.md   |   11 +
 third_party/rust/metal/examples/circle/main.rs     |  377 ++++
 .../rust/metal/examples/circle/screenshot.png      |  Bin 0 -> 479786 bytes
 .../rust/metal/examples/circle/shaders.metal       |   39 +
 .../rust/metal/examples/circle/shaders.metallib    |  Bin 0 -> 6304 bytes
 .../examples/compute/compute-argument-buffer.metal |   14 +
 .../examples/compute/compute-argument-buffer.rs    |   95 +
 .../rust/metal/examples/compute/embedded-lib.rs    |   24 +
 third_party/rust/metal/examples/compute/main.rs    |  194 ++
 .../rust/metal/examples/compute/shaders.metal      |   10 +
 .../rust/metal/examples/compute/shaders.metallib   |  Bin 0 -> 3209 bytes
 third_party/rust/metal/examples/events/main.rs     |   50 +
 third_party/rust/metal/examples/fence/main.rs      |   30 +
 .../rust/metal/examples/headless-render/README.md  |   11 +
 .../rust/metal/examples/headless-render/main.rs    |  159 ++
 .../metal/examples/headless-render/screenshot.png  |  Bin 0 -> 88605 bytes
 third_party/rust/metal/examples/library/main.rs    |   17 +
 .../rust/metal/examples/mesh-shader/main.rs        |  118 ++
 .../rust/metal/examples/mesh-shader/shaders.metal  |   30 +
 .../metal/examples/mesh-shader/shaders.metallib    |  Bin 0 -> 6314 bytes
 third_party/rust/metal/examples/mps/main.rs        |  148 ++
 third_party/rust/metal/examples/mps/shaders.metal  |   26 +
 .../rust/metal/examples/mps/shaders.metallib       |  Bin 0 -> 14339 bytes
 .../rust/metal/examples/raytracing/README.md       |   11 +
 .../rust/metal/examples/raytracing/camera.rs       |   20 +
 .../rust/metal/examples/raytracing/geometry.rs     |  448 +++++
 third_party/rust/metal/examples/raytracing/main.rs |   87 +
 .../rust/metal/examples/raytracing/renderer.rs     |  512 +++++
 .../rust/metal/examples/raytracing/scene.rs        |  135 ++
 .../rust/metal/examples/raytracing/screenshot.png  |  Bin 0 -> 2245032 bytes
 .../rust/metal/examples/raytracing/shaders.metal   |  598 ++++++
 .../metal/examples/raytracing/shaders.metallib     |  Bin 0 -> 211489 bytes
 third_party/rust/metal/examples/reflection/main.rs |   75 +
 .../rust/metal/examples/shader-dylib/main.rs       |  177 ++
 .../metal/examples/shader-dylib/test_dylib.metal   |    8 +
 .../metal/examples/shader-dylib/test_shader.metal  |   14 +
 third_party/rust/metal/examples/window/README.md   |   11 +
 third_party/rust/metal/examples/window/main.rs     |  261 +++
 .../rust/metal/examples/window/screenshot.png      |  Bin 0 -> 55104 bytes
 .../rust/metal/examples/window/shaders.metal       |   97 +
 .../rust/metal/examples/window/shaders.metallib    |  Bin 0 -> 12332 bytes
 .../rust/metal/src/accelerator_structure.rs        |  348 ++++
 third_party/rust/metal/src/argument.rs             |  342 ++++
 third_party/rust/metal/src/blitpass.rs             |  102 +
 third_party/rust/metal/src/buffer.rs               |   71 +
 third_party/rust/metal/src/capturedescriptor.rs    |   76 +
 third_party/rust/metal/src/capturemanager.rs       |  113 ++
 third_party/rust/metal/src/commandbuffer.rs        |  185 ++
 third_party/rust/metal/src/commandqueue.rs         |   44 +
 third_party/rust/metal/src/computepass.rs          |  103 +
 third_party/rust/metal/src/constants.rs            |  152 ++
 third_party/rust/metal/src/counters.rs             |  102 +
 third_party/rust/metal/src/depthstencil.rs         |  190 ++
 third_party/rust/metal/src/device.rs               | 2119 ++++++++++++++++++++
 third_party/rust/metal/src/drawable.rs             |   26 +
 third_party/rust/metal/src/encoder.rs              | 1957 ++++++++++++++++++
 third_party/rust/metal/src/heap.rs                 |  209 ++
 third_party/rust/metal/src/indirect_encoder.rs     |  344 ++++
 third_party/rust/metal/src/lib.rs                  |  654 ++++++
 third_party/rust/metal/src/library.rs              |  901 +++++++++
 third_party/rust/metal/src/mps.rs                  |  572 ++++++
 third_party/rust/metal/src/pipeline/compute.rs     |  471 +++++
 third_party/rust/metal/src/pipeline/mod.rs         |   71 +
 third_party/rust/metal/src/pipeline/render.rs      |  719 +++++++
 third_party/rust/metal/src/renderpass.rs           |  443 ++++
 third_party/rust/metal/src/resource.rs             |  182 ++
 third_party/rust/metal/src/sampler.rs              |  161 ++
 third_party/rust/metal/src/sync.rs                 |  178 ++
 third_party/rust/metal/src/texture.rs              |  351 ++++
 third_party/rust/metal/src/types.rs                |   90 +
 third_party/rust/metal/src/vertexdescriptor.rs     |  250 +++
 84 files changed, 17606 insertions(+)
 create mode 100644 third_party/rust/metal/.cargo-checksum.json
 create mode 100644 third_party/rust/metal/Cargo.lock
 create mode 100644 third_party/rust/metal/Cargo.toml
 create mode 100644 third_party/rust/metal/LICENSE-APACHE
 create mode 100644 third_party/rust/metal/LICENSE-MIT
 create mode 100644 third_party/rust/metal/Makefile
 create mode 100644 third_party/rust/metal/README.md
 create mode 100644 third_party/rust/metal/assets/metal.svg
 create mode 100644 third_party/rust/metal/bors.toml
 create mode 100644 third_party/rust/metal/examples/argument-buffer/main.rs
 create mode 100644 third_party/rust/metal/examples/bind/main.rs
 create mode 100644 third_party/rust/metal/examples/bindless/main.rs
 create mode 100644 third_party/rust/metal/examples/caps/main.rs
 create mode 100644 third_party/rust/metal/examples/circle/README.md
 create mode 100644 third_party/rust/metal/examples/circle/main.rs
 create mode 100644 third_party/rust/metal/examples/circle/screenshot.png
 create mode 100644 third_party/rust/metal/examples/circle/shaders.metal
 create mode 100644 third_party/rust/metal/examples/circle/shaders.metallib
 create mode 100644 third_party/rust/metal/examples/compute/compute-argument-buffer.metal
 create mode 100644 third_party/rust/metal/examples/compute/compute-argument-buffer.rs
 create mode 100644 third_party/rust/metal/examples/compute/embedded-lib.rs
 create mode 100644 third_party/rust/metal/examples/compute/main.rs
 create mode 100644 third_party/rust/metal/examples/compute/shaders.metal
 create mode 100644 third_party/rust/metal/examples/compute/shaders.metallib
 create mode 100644 third_party/rust/metal/examples/events/main.rs
 create mode 100644 third_party/rust/metal/examples/fence/main.rs
 create mode 100644 third_party/rust/metal/examples/headless-render/README.md
 create mode 100644 third_party/rust/metal/examples/headless-render/main.rs
 create mode 100644 third_party/rust/metal/examples/headless-render/screenshot.png
 create mode 100644 third_party/rust/metal/examples/library/main.rs
 create mode 100644 third_party/rust/metal/examples/mesh-shader/main.rs
 create mode 100644 third_party/rust/metal/examples/mesh-shader/shaders.metal
 create mode 100644 third_party/rust/metal/examples/mesh-shader/shaders.metallib
 create mode 100644 third_party/rust/metal/examples/mps/main.rs
 create mode 100644 third_party/rust/metal/examples/mps/shaders.metal
 create mode 100644 third_party/rust/metal/examples/mps/shaders.metallib
 create mode 100644 third_party/rust/metal/examples/raytracing/README.md
 create mode 100644 third_party/rust/metal/examples/raytracing/camera.rs
 create mode 100644 third_party/rust/metal/examples/raytracing/geometry.rs
 create mode 100644 third_party/rust/metal/examples/raytracing/main.rs
 create mode 100644 third_party/rust/metal/examples/raytracing/renderer.rs
 create mode 100644 third_party/rust/metal/examples/raytracing/scene.rs
 create mode 100644 third_party/rust/metal/examples/raytracing/screenshot.png
 create mode 100644 third_party/rust/metal/examples/raytracing/shaders.metal
 create mode 100644 third_party/rust/metal/examples/raytracing/shaders.metallib
 create mode 100644 third_party/rust/metal/examples/reflection/main.rs
 create mode 100644 third_party/rust/metal/examples/shader-dylib/main.rs
 create mode 100644 third_party/rust/metal/examples/shader-dylib/test_dylib.metal
 create mode 100644 third_party/rust/metal/examples/shader-dylib/test_shader.metal
 create mode 100644 third_party/rust/metal/examples/window/README.md
 create mode 100644 third_party/rust/metal/examples/window/main.rs
 create mode 100644 third_party/rust/metal/examples/window/screenshot.png
 create mode 100644 third_party/rust/metal/examples/window/shaders.metal
 create mode 100644 third_party/rust/metal/examples/window/shaders.metallib
 create mode 100644 third_party/rust/metal/src/accelerator_structure.rs
 create mode 100644 third_party/rust/metal/src/argument.rs
 create mode 100644 third_party/rust/metal/src/blitpass.rs
 create mode 100644 third_party/rust/metal/src/buffer.rs
 create mode 100644 third_party/rust/metal/src/capturedescriptor.rs
 create mode 100644 third_party/rust/metal/src/capturemanager.rs
 create mode 100644 third_party/rust/metal/src/commandbuffer.rs
 create mode 100644 third_party/rust/metal/src/commandqueue.rs
 create mode 100644 third_party/rust/metal/src/computepass.rs
 create mode 100644 third_party/rust/metal/src/constants.rs
 create mode 100644 third_party/rust/metal/src/counters.rs
 create mode 100644 third_party/rust/metal/src/depthstencil.rs
 create mode 100644 third_party/rust/metal/src/device.rs
 create mode 100644 third_party/rust/metal/src/drawable.rs
 create mode 100644 third_party/rust/metal/src/encoder.rs
 create mode 100644 third_party/rust/metal/src/heap.rs
 create mode 100644 third_party/rust/metal/src/indirect_encoder.rs
 create mode 100644 third_party/rust/metal/src/lib.rs
 create mode 100644 third_party/rust/metal/src/library.rs
 create mode 100644 third_party/rust/metal/src/mps.rs
 create mode 100644 third_party/rust/metal/src/pipeline/compute.rs
 create mode 100644 third_party/rust/metal/src/pipeline/mod.rs
 create mode 100644 third_party/rust/metal/src/pipeline/render.rs
 create mode 100644 third_party/rust/metal/src/renderpass.rs
 create mode 100644 third_party/rust/metal/src/resource.rs
 create mode 100644 third_party/rust/metal/src/sampler.rs
 create mode 100644 third_party/rust/metal/src/sync.rs
 create mode 100644 third_party/rust/metal/src/texture.rs
 create mode 100644 third_party/rust/metal/src/types.rs
 create mode 100644 third_party/rust/metal/src/vertexdescriptor.rs

(limited to 'third_party/rust/metal')

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
new file mode 100644
index 0000000000..38f86e733d
Binary files /dev/null and b/third_party/rust/metal/examples/circle/screenshot.png differ
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
new file mode 100644
index 0000000000..cbb9bc5e5a
Binary files /dev/null and b/third_party/rust/metal/examples/circle/shaders.metallib differ
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
new file mode 100644
index 0000000000..af7cb17240
Binary files /dev/null and b/third_party/rust/metal/examples/compute/shaders.metallib differ
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
new file mode 100644
index 0000000000..2af9c5895f
Binary files /dev/null and b/third_party/rust/metal/examples/headless-render/screenshot.png differ
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
new file mode 100644
index 0000000000..4af8d60ddc
Binary files /dev/null and b/third_party/rust/metal/examples/mesh-shader/shaders.metallib differ
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
new file mode 100644
index 0000000000..2cecf7b837
Binary files /dev/null and b/third_party/rust/metal/examples/mps/shaders.metallib differ
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
new file mode 100644
index 0000000000..417a1d746d
Binary files /dev/null and b/third_party/rust/metal/examples/raytracing/screenshot.png differ
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
new file mode 100644
index 0000000000..0965a64ff5
Binary files /dev/null and b/third_party/rust/metal/examples/raytracing/shaders.metallib differ
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
new file mode 100644
index 0000000000..9f5eba8ccf
Binary files /dev/null and b/third_party/rust/metal/examples/window/screenshot.png differ
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
new file mode 100644
index 0000000000..a6388fc9bc
Binary files /dev/null and b/third_party/rust/metal/examples/window/shaders.metallib differ
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] }
+    }
+}
-- 
cgit v1.2.3