Custom Shading System & Vune Shading Language (v0.1) #808
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
WARNING: At the moment, this PR contains functionality from (#801), but it'll be replaced with (#803) once that PR is approved.
This PR is a month worth of work, so this probably will be a long read lol.
Custom Shading System
This new Shading System allows Vello to have the power of OpenGL, Vulkan, Metal, etc. when it comes to shaders, by allowing you to run custom code on the GPU for graphical purposes, similar to how Vertex & Fragment Shaders work.
Since Custom Shading overall in a Tiling Renderer is kind of unknown territory, there's a lot of questions that have to be answered.
Three of the biggest ones are:
These questions come into mind, because Vello internally is not a Traditional Raster Renderer, but a Tiling Renderer. So, even if you can code a system that replicates the traditional Vertex & Fragment Shader system, that design can end up introducing limitations, or forcing a situation where the system has to translate something that works well in raster, into tiling, causing potential performance drops.
However, that doesn't mean that you cannot make something that is "equivalent to" or "similar to". So, to make things a bit easier to design, i decided to take this approach that answers the first question:
And so on...
I'm going in this path so i don't have to follow any sort of standard/rules that aren't made by me, that could potentially put me in a situation where i have to ask myself constantly "Should i add 'x' feature or funcionality in the Fragment/Vertex Shader? Because Fragment/Vertex Shaders don't allow it."
So now, let's go to the second question.
The Shading Language.
Here's another big and tricky design decision i had to make, which is deciding what shading language i'll have to use for this system.
At first, i tried with WGSL being the main language, then i tried Slang, and later on a bunch of other tests... But there's one big problem that i had with every shading language that i tried using.
None of them know about the existence of Vello.
This was a big and constant problem, because in order to connect the shaders i was writing to Vello, the shader had to understand that it will be manipulated/executed in some way by Vello's pipeline. And to do that inside of the language of choice, you had three options.
Eventually, as Vello's system grows in power, features and capabilities. Having only a preprocessor is not going to be enough, and eventually you'll have no choice but developing option 3 sooner or later.
But even if you choose one of the options, all of them have the same problem that question number one had, which is having to follow standards/rules that aren't made by me.
And don't forget about CPU fallback... yeah, that's another problem that gets added to the mix...
So, to fix these issues & more, ladies and gentlemen, allow me to introduce:
The Vune Shading Language.
Vune is a version of Rune that aims to compile Vello-compatible shaders at runtime.
Vune uses Rune's parser to create an AST that can be used for Code Generation. At the moment, its only target is WGSL, but the idea is that it could also compile to Rune so it can run shaders on the CPU. This could also help in the future with the problem of having to write the same shader twice.
Vune, because of Rune, has a syntax identical to Rust, so, to put it short, its "Rust-GPU, but it's a Just In Time Compiler for Vello shading".
Once a Vune Shader finishes compiling, the Shading System will inject it inside of the pipeline by replacing the default shaders with custom ones.
Despite the execution being a bit primitive as you're probably going to see in the code, it allows the custom code to have zero performance penalties of any kind.
Now, what custom shaders can you make so far?
The Flatten Shader.
Here's the only shader type that is able to do so far, which is the Flatten Shader. This shader is the one that allows you to handle paths and transforms, similar to the Vertex Shader.
This shader contains two functions that act as entry points.
flat_main
, which stands for "FLAtten Transform", is the function that allows you to handle the transforms.flap_main
, which stands for "FLAtten Path", is the function that allows you to handle the individual paths (curves, lines, etc.).So far, the only one that works is
flat_main
, butflap_main
will be implemented soon in this PR.Here's a tiny example of a Vune Flatten Shader in action:
2025-01-29.20-33-46.mp4
Vune also allows you to send custom uniforms with any type of data that you want (as long as its bytemuck compatible). Here's another example where i'm sending the mouse's position & the window's resolution to the GPU:
2025-01-30.12-53-35.mp4
Flatten Shader Code:
The next video will demonstrate the amount of performance you get with this system: This is a stress test that contains 5000 shapes being scaled up and down individually the closer the mouse gets. Because the GPU takes care of the transformation instead of the CPU, this scene runs at 60+ fps on a GTX 1050 Ti (i still have to test with VSync off, that's why the "+").
2025-01-30.18-05-15.mp4
These are examples that are currently made in the Bella Engine, but i'll make a port of these to pure Vello as "with_winit" scenes.
Adding a Vune Shader.
WARNING: This API design is subject to change.
Adding a Vune Shader is pretty easy, first, you load & compile the shader you want by using the
Renderer
'sadd_vune_shader
function.If you're planning to send custom data, like a struct for example, you have to set its binding:
Once that's done, you can get its reference via
get_vune_shader
.And add it to your scene:
If you want to send the
CustomStruct
that we've made earlier to the Flatten Shader, you can useset_uniform
.Aaaaand that's it! You've added a Vune shader to your scene successfully :D
Now you can draw the scene and let the shading system take care of the rest.
If you want different shader types in different scenes, at the moment, you'll have to split the scenes into two draw calls for that to take effect, but in the future, that's probably not going to be necessary.
This is heavily WIP.
There's still A TON to do and add before this shading system can have a "full version" of it, but for this PR, i'll stop at adding the Flatten Shader and see what to do next, because the PR is getting insanely huge to review.
To-Do (before setting it up for review):
flap_main
for the Flatten Shader.EDIT: Yes, in theory, but i still have to test a bit more.
render_full
.Finally...
I want to thank @raphlinus , @DJMcNab , @waywardmonkeys and @xorgy for helping me by answering a ton of questions and giving support, i appreciate it a lot ❤️
Also, i would like to thank @udoprog for helping a ton with Rune related stuff ❤️
If there's any questions, or design choices that you want to discuss, feel free to do so! c:
Thank you for reading, and i hope y'all are having a good day/night, Cheers! :D