stride3d / sdsl Goto Github PK
View Code? Open in Web Editor NEWParser and compiler for the Stride shader language into SPIR-V
License: MIT License
Parser and compiler for the Stride shader language into SPIR-V
License: MIT License
Generate a control flow graph that can be usable for SPIR-V generation.
One idea is to probably replace all control flow nodes into GOTO and LABEL in the CFG.
As the library is getting closer to generating SPIR-V code, one question starts to draw itself : What file format(s) should we use for storing shaders as assets for the Stride engine or other engine ?
Ideally, the library can be a plug and run compiler for shaders used for Stride (primarily) but also usable for other engines or frameworks.
Currently, Stride stores shaders as text assets. It is loaded once and goes through the process of parsing, AST building, AST modifications and translation, and finally, those shaders are compiled as binaries and cached for future reuse.
For this new shader compiler a shader file goes through different formats, many similar to those previously used:
*.sdsl
files can contain one or many shader classesQuadruple
arrays representing each method in three address code for optimization purposes.sdspv
), it's invalid SPIR-V but has enough information to be mixed together.The most important format in the previous list is sdspv
. It's a binary format that is very close to SPIR-V but has the advantage of being mixed the same way SDSL would be done on the AST level.
Given the nature of the composition system that SDSL and SDFX offers, there's a high value in writing shaders only at design time and composing them through the SDFX either at design time or at runtime.
During the asset compilation, the shader compiler should compile those shaders into sdspv
binary files, known permutations will be compiled as SPIR-V modules directly.
sdspv
shaders can be mixed at runtime and translated using naga, tint or spirv-cross.
SDSL uses preprocessor macros for platform specific code and other uses. It offers a similar user experience for both C# and SDSL.
Since macros can be defined through C# code and used by the preprocessor, this makes SDSL compilation impredictible.
.sdsl
file that user writeThe shader compiler can compile shader snippets into sdspv and cache them for future use. This has the advantage of removing the need to parse and compile a shader snippet many times.
In an ideal scenario, if we have a shader snippet composed of many other snippets that were already compiled as sdspv, only our snippet need to be compiled, then the assembler will mix all those sdspv binaries together.
Here's a graph of how the compiler should work
flowchart TD
subgraph Spirv Compilation
A[Compile user shader A] --> B{Is shader \nalready \ncompiled ?}
B --> |yes| Spirv[Return spirv binaries]
B --> |no| C[Fetch shader code\n from asset system]
C --> ParseShader[Parse Shader]
ParseShader --> D{Does shader A \nhave compositions\nor inherited classes\nnot compiled \nto sdspv?}
D --> |no| E[Compile and mix sdspv A with \nall inherited and composed sdspv]
E --> F[Assemble shader A \nwith other snippet\n from the inherited and \ncomposed spirv shaders]
D --> |yes| G[Fetch and compile \ninherited/composed shader \nsnippets to sdspv]
G --> E
F --> Spirv
end
subgraph Parsing
Load[Load shader text \nfrom asset system] --> CommentParsing[Parse shader comments]
CommentParsing --> RemovalQ{Does shader\nhave comments ?}
RemovalQ --> |yes| Removal[build a list of ReadOnlyMemory \n of char for non-comment \ncode and use StringBuilder to \nconcatenate it into a new string]
RemovalQ --> |no| AllowedMacros{Are\nmacros\nallowed?}
AllowedMacros --> |yes| MacroRetrieve
AllowedMacros --> |no| StartCompilation
Removal --> AllowedMacros
subgraph MacroParsing
MacroRetrieve[Retrieve macro values] --> MacroObject[Replace macro objects by\ntheir defined values]
MacroObject --> MacroFunc[Replace macro function \nwith their expansions]
MacroFunc --> BuildString
BuildString[Build a string \nusing String builder] --> StartCompilation
end
end
The fact that branching through shader macros can become very complex and that a user can change these shader macro values at runtime make caching shader complicated. It also breaks the type system since a single shader can be reused with different definition in different context.
Ideally a shader type would be defined once, with a specific definition across all shaders. Everything that the preprocessor can do can be done through the inheritance and composition system. Since we would have one definition per shader type, we can cache sdspv with just the identifier of the shader type. We cut short of all re-parsing of the same type, saving some precious CPU at the cost of a different UX for users.
Both Stride and VL.Stride use macros extensively, this will need some work to rewrite things.
Since the SDSL language is being rewritten one question to ask is how do we handle type coercion and inference.
details to note about SPIR-V :
OpConvert
, which gives us the power to convert anything to anything conserving bit patterns.As user code is generally not optimized, there might be a need to implement optimization passes.
Optimization can happen on the AST generated, some passes like constant propagation, type checking and type coercion are better fit in this part of the compiler.
There is an api for creating an intermediate representation between SDSL and SPIR-V for control flow graph optimization, like finding dead code and such. This IR is created with arrays of quadruples/three address code that can be converted in a graph representation.
As this library creates an SDSL flavored SPIR-V to be able to mix and combine modules together, there might be considerations for optimizations on this level.
SPIR-V is an binary intermediate representation of shader code. It is generally consumed by drivers and is most likely optimized for binaries generated for GPUs. We can't know to which extent it is optimized and how performance could be affected since there are so many different GPUs and drivers that work differently.
SPIR-V Tools offers spirv-opt
, a tool to reduce binary size of SPIR-V modules, unfortunately not usable directly in C# unless we create bindings to the library. Fortunately, the lunarg sdk has a little pdf talking about various optimization passes possible on SPIR-V level.
Check for simple violation of code flow like break/continue out of loops
Type checking is fairly limited at the time of writing of this issue, it should include type coercion and type inference.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.