diff --git a/blog/2025-04-10-shadertoys.md b/blog/2025-04-10-shadertoys.md index c2cd1a9..48711c7 100644 --- a/blog/2025-04-10-shadertoys.md +++ b/blog/2025-04-10-shadertoys.md @@ -1,7 +1,7 @@ --- title: "Shadertoys ported to Rust GPU" authors: [LegNeato] -tags: ["demo"] +tags: ["demo", "port"] --- # Porting Shadertoys to Rust with Rust GPU diff --git a/blog/2025-06-24-vulkan-shader-port.mdx b/blog/2025-06-24-vulkan-shader-port.mdx new file mode 100644 index 0000000..1c9ddf6 --- /dev/null +++ b/blog/2025-06-24-vulkan-shader-port.mdx @@ -0,0 +1,562 @@ +--- +title: "Porting GPU shaders to Rust 30x faster with AI" +authors: [LegNeato] +tags: ["demo", "port"] +--- + +import Gh from "@site/blog/src/components/UserMention"; + +# Porting GPU shaders to Rust 30x faster with AI + +I used AI to port virtually all of the shaders from Sascha Willems' [popular Vulkan +sample repo](https://github.com/SaschaWillems/Vulkan) over to Rust using [Rust +GPU](https://github.com/Rust-GPU/rust-gpu/). This demonstrates Rust GPU is ready for +production use. + +With AI handling most of the codegen and me reviewing and tweaking the results, the port +took about four days of part-time work—a **30x speedup** compared to Sascha's [manual +port to +Slang](https://www.saschawillems.de/blog/2025/06/03/shaders-for-vulkan-samples-now-also-available-in-slang/). + + + +The code is available on +[GitHub](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders). + +## What is Rust GPU? + +[Rust GPU](https://rust-gpu.github.io/) is a project that allows you to write code for +GPUs using the Rust programming language. GPUs are typically programmed using +specialized languages like [WGSL](https://www.w3.org/TR/WGSL/), +[GLSL](https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders), +[MSL](https://developer.apple.com/documentation/metal/performing_calculations_on_a_gpu), +or +[HLSL](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl). +Rust GPU changes this by letting you use Rust to write GPU programs (often called +"shaders" or "kernels"). + +These Rust GPU programs are then compiled into [SPIR-V](https://www.khronos.org/spir/), +a low-level format that [most GPUs understand](https://vulkan.gpuinfo.org/). Since +SPIR-V is the format [Vulkan](https://www.vulkan.org/) uses, Rust GPU makes it possible +to integrate Rust-based GPU programs into any Vulkan-compatible workflow. + +For more details, check out the [Rust GPU website](http://rust-gpu.github.io/) or the +[GitHub repository](https://github.com/rust-gpu/rust-gpu). + +## Porting Vulkan shaders + +I went through every sample in Sascha's Vulkan repo and ported around 90% of the +shaders. + +Each Rust shader compiles to valid SPIR-V via Rust GPU and drops into the original C++ +Vulkan pipeline without modification. This lack of host-side changes demonstrates that +Rust GPU shaders can integrate into non-Rust Vulkan workflows with only build system +changes. + +I was developing on macOS, so I used +[MoltenVK](https://github.com/KhronosGroup/MoltenVK) to run the examples. I compared the +GLSL and Rust output manually. Because MoltenVK does not support some advanced Vulkan +features, a few shaders could not be checked at runtime to confirm they worked. I plan +to eventually double check such shaders on a GPU with native Vulkan support. + +Porting these shaders proves that Rust GPU is ready for real use, but they aren't a good +representation of what "Rust-native" shaders would look like. These shaders do not use +advanced features supported in Rust GPU like traits, enums, feature flags, dependency +management, lints, and others. + +## Rust GPU Vulkan support + +I'm a maintainer of Rust GPU but not a graphics or GPU programmer by background. Going +in, I didn't know which Vulkan features and techniques would actually work with Rust +GPU. Vulkan is +[expansive](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html). Rust GPU +is a large and evolving project with [new maintainers](/blog/transition-announcement) +and little to no documentation. + +I was pleasantly surprised to find out that Rust GPU supports all the major Vulkan shader +types! In addition to simple examples covering a single shader type, most of the +examples have multiple shaders of different types working together in a full pipeline. +Rust GPU supports this in a flexible way. A single crate can output each shader to its +own file or combine them into a single [uber +shader](https://dolphin-emu.org/blog/2017/07/30/ubershaders/) with multiple entry +points. I did not use the uber shader route and instead matched what the C++ host code +expected. + +
+ All Vulkan shader types supported by Rust GPU + +- **[Vertex + Shaders](https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#vertex-stage)** + Transforms per-vertex input data (positions, normals, etc.) + (example: + [triangle](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/triangle)) + +- **[Fragment + Shaders](https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#fragment-stage)** + Runs per-fragment to compute final pixel color + (example: + [texture](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texture)) + +- **[Tessellation Control & Evaluation + Shaders](https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#tessellation)** + Dynamically subdivides geometry for smoother surfaces + (example: + [tessellation](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/tessellation)) + +- **[Geometry + Shaders](https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#geometry)** + Programmatically generates new geometry primitives + (example: + [geometryshader](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/geometryshader)) + +- **[Compute + Shaders](https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#compute-shader)** + General-purpose GPU execution outside the graphics pipeline + (example: + [computeshader](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computeshader)) + +- **[Mesh & Task + Shaders](https://www.khronos.org/blog/an-introduction-to-mesh-shaders)** + Replaces traditional vertex/geometry pipeline with flexible meshlet-based execution + (example: + [meshshader](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/meshshader)) + +- **[Ray Tracing Shaders](https://www.khronos.org/blog/ray-tracing-in-vulkan)** + Enables hardware-accelerated ray traversal and shading logic + (example: + [raytracingbasic](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/raytracingbasic)) + +
+ +Sascha Willems' Vulkan examples are a well-known reference for real-world GPU +techniques. They cover more than just API basics and include advanced techniques like +dynamic rendering, PBR, deferred shading, compute, and ray tracing. Again, I was +pleasantly surprised to see that these techniques work in Rust GPU without any major +issues! + +
+ List of shader techniques and features demonstrated + +### 🟢 Core and Basics + +- [Vulkan + basics](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/triangle) +- [Dynamic + rendering](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/dynamicrendering) +- [Pipelines](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pipelines), + [Graphics pipeline + library](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/graphicspipelinelibrary) +- [Vertex + attributes](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/vertexattributes) + +### 📦 Data Binding + +- [Descriptor + sets](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/descriptorsets) +- [Dynamic uniform + buffers](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/dynamicuniformbuffer) +- [Push + constants](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pushconstants) +- [Push + descriptors](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pushdescriptors) +- [Specialization + constants](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/specializationconstants) +- [Descriptor + buffer](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/descriptorbuffer) +- [Descriptor + indexing](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/descriptorindexing) +- [Inline uniform + blocks](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/inlineuniformblocks) + +### 🖼️ Textures + +- [2D + textures](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texture) +- [3D + textures](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texture3d) +- [Cubemaps](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texturecubemap) +- [Texture + arrays](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texturearray) +- [Cubemap + arrays](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texturecubemaparray) +- [Mipmapping](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texturemipmapgen) +- [Sparse + textures](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/texturesparseresidency) + +### 📸 Framebuffer Techniques + +- [Offscreen + rendering](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/offscreen) +- [Input + attachments](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/inputattachments) +- [Subpasses](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/subpasses) +- [Deferred + shading](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/deferred), + [Deferred with + shadows](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/deferredshadows) +- [Multisampling](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/multisampling), + [Deferred + multisampling](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/deferredmultisampling) +- [Order-independent transparency + (OIT)](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/oit) + +### 🌑 Shadows and Lighting + +- [Shadow + mapping](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/shadowmapping) +- [Cascaded + shadows](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/shadowmappingcascade) +- [Omnidirectional + shadows](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/shadowmappingomni) +- [PBR + basic](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pbrbasic) +- [PBR with + IBL](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pbribl) +- [HDR](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/hdr) +- [Bloom](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/bloom) +- [SSAO](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/ssao) + +### 🔁 Performance and Instancing + +- [Instanced + rendering](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/instancing) +- [Indirect + draw](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/indirectdraw) +- [Multithreading](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/multithreading) +- [Occlusion + queries](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/occlusionquery) +- [Pipeline + statistics](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/pipelinestatistics) +- [Conditional + rendering](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/conditionalrender) + +### 🧠 Compute and Simulation + +- [Compute + shaders](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computeshader) +- [N-body + simulation](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computenbody) +- [Cloth + simulation](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computecloth) +- [Particle + systems](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computeparticles), + [CPU-based + particles](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/particlesystem) +- [GPU culling and + LOD](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computecullandlod) +- [Headless + compute](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/computeheadless) +- [Ray + queries](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/rayquery) + +### 🔺 Geometry & Tessellation + +- [Geometry + shaders](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/geometryshader) +- [Tessellation + basics](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/tessellation) +- [Terrain + tessellation](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/terraintessellation) +- [Displacement + mapping](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/displacement) +- [Viewport + arrays](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/viewportarray) + +### 🧪 Ray Tracing + +- [Basic ray + tracing](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/raytracingbasic) + +### ⚙️ Advanced Extensions + +- [Dynamic + rendering](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/dynamicrendering) +- [Mesh + shaders](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/meshshader) +- [Shader + objects](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/shaderobjects) +- [Descriptor + buffers](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/descriptorbuffer) +- [Conservative + raster](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/conservativeraster) +- [Debug + printf](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/debugprintf) +- [Debug + utils](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/debugutils) +- [Negative + viewport](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/negativeviewportheight) +- [Multiview](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/multiview) + +### 👁️ UI and Text + +- [Distance field + fonts](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/distancefieldfonts) +- [ImGui + integration](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/imgui) +- [Text + overlay](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/textoverlay) + +### ⚡ Effects and Demos + +- [Parallax + mapping](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/parallaxmapping) +- [Radial + blur](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/radialblur) +- [Environment + mapping](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/sphericalenvmapping) +- [Gears + demo](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/gears) +- [Vulkan + scene](https://github.com/Rust-GPU/VulkanShaderExamples/tree/master/shaders/rust/vulkanscene) + +
+ +Successfully porting all these shaders demonstrates that Rust GPU can handle most major +use-cases traditionally done in GLSL. + +## How I used AI + +The project was conceived as an opportunity to use [Claude +Code](https://www.anthropic.com/claude-code) for the first time. I've been wanting to +use it for a while, but a billing issue on Anthropic's side prevented me from doing so. +Naturally, it started working when I signed up for the more expensive Pro plan! + +### The loop + +I worked interactively with Claude to port a single shader at a time. At first, I just +asked Claude to "Port X GLSL shader to Rust using rust-gpu.” The results were not good. +Claude would get confused and make obvious mistakes: + +- Name functions incorrectly +- Forget to add shader crates to the workspace +- Use incorrect crate names, paths, or imports. One thing it _loved_ to do was + hallucinate dependencies in `Cargo.toml` +- Add build scripts or hardcode things that should be auto-detected +- Get confused about where it was in the filesystem, affecting what it was reading, + writing, or running + +I would tell it what I wanted, it would do something wrong, I would tell it how to fix +it, it would get that wrong, I would tell it how to fix _that_, and so on. Eventually, I +had little fragments of directions that solved specific subproblems that kept coming up. + +I took these subproblem fixes and aggregated them into the initial prompt for the next +shader. Again, I would interactively work on the shader and gain more fragments or +tweaks to the existing ones. Over a few hours, this evolved into a detailed, +repeatable, high-quality initial prompt. Once I had this "golden prompt," things went +very smoothly and I was largely babysitting. + +
+ Golden prompt + +Here are your instructions. Refer back to them frequently to make sure you are on plan: + +Let’s rewrite the glsl / slang shaders in rust using rust-gpu. Pick an unimplemented one +that looks the easiest and tell me which you picked to work on (don’t do +computeraytracing—other raytracing is fine—or conservativeraster or bufferdeviceaddress +or variablerateshading or texturesparseresidency or oit or variablerateshading). The +glsl/slang/rust versions of the logical shader should all be semantically equivalent. We +will use the existing C++ harness to run them. Always use rust dependencies from the +workspace and make the new shader consistent with the other rust shaders. We do not need +build.rs, building is handled by an external script. + +spirv_std doesn’t have a glam feature, it is always built in at spirv_std::glam. The +shader entry points have to be named something specific to be picked up by the C++. The +C++ handles rust differently than the other languages that require “main”, it is +main\*[x] with the shader type (check the other rust shaders) + +The shader must always be built with a build script as it sets up stuff in the env. You +can run it with cd /Users/legnitto/src/vulkan_shader_examples/shaders/rust && python3 +compileshaders.py. If you are building one shader while iterating on it, you can use: +python3 compileshaders.py [shader] + +Be sure to add any new crates to the cargo workspace. Do not create any manifest.json +files, they are created when building. + +Shader crate names must be globally unique in the rust workspace. If there is a +conflict, name it [groupname]-[shadername] in the cargo.toml package section, similar to +existing ones. Only do it for the conflict. + +If a shader needs a Vulkan capability or extension, it is set in the crate’s Cargo.toml. +Example: [package.metadata.rust-gpu.build] capabilities = ["ImageQuery"] + +To test the shader, you must first compile the test / example binary. Example: + +cmake --build build --target texturecubemap + +Then you can run the example binary with the shader. Example: + +cd /Users/legnitto/src/vulkan_shader_examples && +VK_ICD_FILENAMES=~/VulkanSDK/1.4.313.1/macOS/share/vulkan/icd.d/MoltenVK_icd.json +./build/bin/computeraytracing -s rust + +Note you can swap “rust” for “glsl” to test the glsl shaders so we can compare the +output. + +Use straightforward idiomatic rust things like constants for PI, TAU, and EPSILON where +possible. Do not use bytemuck. Glam has many similar apis to glsl, use them when +possible. You also might need spirv_std::num_traits::Float to access some rust apis on +floats. + +Care should be taken to make sure padding of input and output structs is correct. + +Iterate on the shader until it compiles and is semantically equal to the glsl/slang +shader. If you get stuck, look at the other rust shaders for what they do. Look line by +line in the glsl, slang and rust shaders and make sure they have equivalent semantics +that will produce equivalent output. do not copy obvious bugs in the source shaders. Ask +if there is some glsl feature that rust-gpu does not support. If the semantics / logic / +output is not the same, fix it and check again. Compile the test binary if it does not +exist and run it. Then run the same thing with glsl so I can compare. Set the timeout to +15 seconds when running. + +After the shader is complete, compiling with the build script, and I have confirmed +both, ask if you should commit. When you commit, do not include any Claude info and look +at other commit messages to make the format match. Make the style of the commit +message match other rust shader commits. Include the lockfile. + +If you have any doubts, ask me, but you should be able to iterate your way to a +completely working shader without my help. + +
+ +## Tactics that helped AI + +**Giving AI complete control.** I kept an eye on the code to make sure it was +reasonable, but I made Claude do all edits. In the past, I have found that when both the +AI and I make changes the AI gets confused. I now choose up front if I am using AI as an +aid to _my_ coding or if I am making AI code and I am reviewing instead. + +**Not compacting.** I found that when Claude compacted the conversation, the output +quality dropped until I gave Claude more context and corrections to recover. Eventually, +my golden prompt became so effective that it was better to restart Claude with the +prompt rather than continue a compacted session. + +**Putting scaffolding in place.** In addition to the prompt, having scaffolding in place +(directory layout, workspace setup, build commands, etc) meant the AI could focus just +on the shader. This helped prevent confusion. Setting up scaffolding and working through +a clear list is apparently good advice for both human programmers and AI! + +**Tightening the feedback loop.** One thing I resisted was changing the build system to +allow specifying a single shader instead of building them all. As more shaders were +added, the build system slowed down. Claude would hit timeouts unless I told it to +increase them and velocity took a hit. I eventually bit the bullet and had Claude add a +feature to build a single shader at a time. After that, Claude could run fast +edit-compile-fix loops without my involvement. + +**Using existing examples.** As more shaders were implemented, Claude began referencing +previously ported Rust shaders and correcting itself when it went off track. Errors and +human intervention dropped significantly and velocity increased. Due to this the +development curve was definitely not linear and accelerated up until the end. There’s a +reason [OpenAI and others suggest giving AI examples in +prompts](https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api). + +## AI limitations + +Even with my golden prompt, I could never get Claude to "one-shot" a shader port. The +best I could get was a correct shader followed by manual reminders. The two main things +I had to remind Claude were: + +1. _"Look line by line in the GLSL, Slang, and Rust shaders and make sure they have + equivalent semantics that will produce equivalent output."_ +2. _"When you commit, do not include any Claude info. Look at other commit messages and + match the format. Match the style of other Rust shader commits. Include the + lockfile."_ + +
+ +I would just paste those lines back in from the golden prompt. So, Claude was +effectively "three-shotting" the task. I think it was forgetting due to a small context +window, or it was de-emphasizing those directions in the initial prompt. Interestingly, +I only needed to repaste them in once per session. For multiple ports in the same +session, Claude would remember and one-shot them correctly. Well, until compacting and +then it was time to start fresh again. + +When reviewing the generated code, I often caught Claude wallpapering over real or +imagined Rust GPU limitations. For example, it assumed push constants were unsupported +and hardcoded example values instead. Claude never _told_ me this; it only showed up in +the code. If I had only looked at the conversation I would have missed it. The code +would have run and appeared functional but it wouldn’t have been correct. + +## I ❤️ AI + +Even with all these issues, AI is _wild_. **Despite being new to the project and +inexperienced with shaders, I was able to implement everything 30x faster than the +original expert author.** Incredible! + +I've been using AI to write code for a while with mixed results. I was impressed when it +one-shotted Gscript automation or handled React and CSS cleanly. But in more complex +domains it fell apart. + +Lately I’ve been less frustrated. Partly because I use multiple AIs and have them +interact, but mostly because I've learned how to manage their quirks and my +expectations. Still, this shader porting experience really drove home just how far +things have come. The fact that an AI could handle this volume of non-trivial, +domain-specific code with minimal help is frankly insane. + +## Repos are forever + +One issue that comes up repeatedly while using AI is that when Embark transferred the +Rust GPU project to the community, they [did not transfer the code +repo](/blog/transition-announcement/#transition-challenges-repository-transfer). As a +result, AI continues to treat the Embark repo as the source of truth and heavily weights +the outdated APIs in that repository. I have to constantly teach AI the current Rust GPU +repo url, APIs, and docs. The community anticipated problems like this and asked Embark +to follow the standard repository transfer process, but they declined. + +## Rust GPU limitations + +There were only a few issues hit while porting the shaders to Rust. The first I was able +to [fix with Claude's help](https://github.com/Rust-GPU/rust-gpu/pull/281), and the +others are still outstanding. A huge benefit of using Rust for shaders is that the +compiler and standard library are written in Rust too. When you hit an issue you can +often fix it yourself, test it, and move on. No need to file bugs, wait for upstream, or +switch to a different language like C++. + +### The fixed issue + +In GLSL, querying the size of an image is done with +[`textureSize`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/textureSize.xhtml) +or [`imageSize`](https://registry.khronos.org/OpenGL-Refpages/gl4/html/imageSize.xhtml), +depending on whether a level-of-detail (LOD) is required. In Rust GPU, these map to +[`query_size_lod`](https://rust-gpu.github.io/rust-gpu/api/spirv_std/image/struct.SampledImage.html#method.query_size_lod) +for sampled images using +[`SampledImage`](https://rust-gpu.github.io/rust-gpu/api/spirv_std/image/struct.SampledImage.html), +and +[`query_size`](https://rust-gpu.github.io/rust-gpu/api/spirv_std/image/struct.SampledImage.html#method.query_size) +for multisampled or storage images. These functions return +[`glam`](https://docs.rs/glam/latest/glam/) vector types like +[`UVec2`](https://docs.rs/glam/latest/glam/struct.UVec2.html), +[`UVec3`](https://docs.rs/glam/latest/glam/struct.UVec3.html), or scalars, depending on +image dimensionality and array status. + +There was a mismatch in the expected return shape, returning a +[`UVec3`](https://docs.rs/glam/latest/glam/struct.UVec3.html) for a cubemap when SPIR-V +expects a [`UVec2`](https://docs.rs/glam/latest/glam/struct.UVec2.html). Thankfully this +showed up as a compile-time error rather than runtime error due to Rust's type system! + +### Missing APIs + +Rust GPU does not currently support +[`SPV_KHR_physical_storage_buffer`](https://registry.khronos.org/SPIR-V/extensions/KHR/SPV_KHR_physical_storage_buffer.html) +and the +[`PhysicalStorageBuffer64`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_storage_classes) +addressing model, though there is a draft PR up by community member . The shaders that use this API could not be ported. + +Rust GPU does not support +[`VK_EXT_sparse_residency`](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap35.html#features-sparseResidency), +which enables partially resident textures. Support for sparse texture operations, +including `OpImageSparse*` instructions and residency queries, is missing. + +Rust GPU also lacks support for +[`VK_KHR_fragment_shading_rate`](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap35.html#primsrast-fragment-shading-rate), +which allows different regions of the framebuffer to be shaded at varying rates. +This requires the `FragmentShadingRateKHR` built-in and associated decorations. + +## Come join us! + +Porting the Vulkan shaders to Rust with Rust GPU was successful. Rust GPU is definitely +ready to use in your Vulkan-based project. + +We're eager to add more users and contributors! We will be working on revamping the +onboarding and documentation soon. To follow along or get involved, check out the +[`rust-gpu` repo on GitHub](https://github.com/rust-gpu/rust-gpu). diff --git a/blog/tags.yml b/blog/tags.yml index 87c7ab3..ca9641e 100644 --- a/blog/tags.yml +++ b/blog/tags.yml @@ -28,6 +28,11 @@ performance: permalink: /performance description: Performance measuring and tuning +port: + label: port + permalink: /ports + description: Porting other software to Rust GPU + cuda: label: cuda permalink: /cuda