Open icculus opened 2 years ago
I also strongly dislike the idea of having two syntaxes for variable declarations. I don't really care about how it looks like in the end, but please stick with one. The C-like style probably makes more sense given how C-like the rest of the language is, and out of practical concerns such as GLSL/HLSL compatibility for people who enjoy pain.
I'm not a fan of the var
either; personally I think it should be removed if it doesn't complicate the parsing, but I can live with it.
I can remove one of them, but why is this such a terrible thing that there are two ways to do this? Are people that are capable of developing shaders going to see an unexpected "var mydata : int;" and not be able to piece together its cryptic meaning?
I think part of the struggle is not just creating a better language, but creating something that is still close enough to C that people won't rebel--which drives me nuts, but feels crucial. Keeping the C-like version of variable declaration as an option is part of that.
But if I had to drop one, the C-like version would be the one I'd like to drop.
The good news is that, since this is currently just syntactic sugar, it's easy to leave it in place now, and yank either out at the last moment, so decisions don't need to be made now.
I can remove one of them, but why is this such a terrible thing that there are two ways to do this? Are people that are capable of developing shaders going to see an unexpected "var mydata : int;" and not be able to piece together its cryptic meaning?
No that's not the problem. As I said above, the actual syntax doesn't matter all that much. But when (not if, when) I eventually see both of those syntaxes used in the same project I'm working on, or god forbid in the same file, I'm probably punching my monitor in OCD fueled rage :)
Are there actually any examples of other languages with two different, functionally identical syntaxes for variable declaration?
I think part of the struggle is not just creating a better language, but creating something that is still close enough to C that people won't rebel--which drives me nuts, but feels crucial. Keeping the C-like version of variable declaration as an option is part of that. But if I had to drop one, the C-like version would be the one I'd like to drop.
I'm personally not very invested in keeping the language very C-like. I'm just saying that if that's the direction you're going with, the "rusty" declaration style makes little sense, in my opinion. I just don't see it adding anything other than needless inconsistency and mild confusion. For one, you can't use it in structs you share with your C/C++ code. And at that point, why would you go out of your way to use it anywhere else? Is there any reason for the alternative syntax to exist other than that it looks nicer (which is debatable in itself)?
On that note, I think the compiler needs some sort of a "reflection" API to probe at the layout of types for non-C-like languages and people who don't want or can't share headers between C/C++ and the shaders.
By the way, I have to say one departure from C that I really love is requiring braces for if/else/for/while/etc.
clauses. Sometimes I wish actual C compilers had warning options to enforce this style.
One question though: how does else if
work with this syntax? In C it emerges naturally, but I guess it's a special case syntactic sugar here?
By the way, I have to say one departure from C that I really love is requiring braces for
if/else/for/while/etc.
clauses. Sometimes I wish actual C compilers had warning options to enforce this style.One question though: how does
else if
work with this syntax? In C it emerges naturally, but I guess it's a special case syntactic sugar here?
I think I probably forgot to update the grammar to deal with this, but yeah, I'd like "else if" to be legal. But it also makes me nervous that so many popular programming languages went with a separate elseif
or elsif
keyword here, like they discovered that this was a giant pain to parse. I guess I'll report back when I try this and find out its a disaster.
Does anyone have strong feelings about using array dereferencing with vectors?
So you have a float4 named x
, and it's legal to do y = x[2];
and this is exactly identical to y = x.z;
... referencing x[4]
would be a compile error, and possibly we disallow variables here, too, so it's literally syntactic sugar that needs an integer constant.
Or maybe we just forbid array syntax at all with vectors?
Opinions?
I can remove one of them, but why is this such a terrible thing that there are two ways to do this? Are people that are capable of developing shaders going to see an unexpected "var mydata : int;" and not be able to piece together its cryptic meaning?
No that's not the problem. As I said above, the actual syntax doesn't matter all that much. But when (not if, when) I eventually see both of those syntaxes used in the same project I'm working on, or god forbid in the same file, I'm probably punching my monitor in OCD fueled rage :)
I'd prefer a single syntax for the language too, no matter which one is chosen in the end.
Does anyone have strong feelings about using array dereferencing with vectors?
There has been various times where I would've wanted to do that.
However bear in mind such cases were usually of the sort:
vec3 myVec = ...;
for( int i = 0; i < 3; ++i )
myVec[i] = ...;
Rather than using literals. Which adds a few issues on the compiler side:
i
being dynamic (i.e. unknown at compile time). For the rest, older HW, however:i
is known at compile time, it should be possible to support everywhere
for( int i = 0; i < fully_dynamic_variable; ++i )
{
if( someBuffer[i] < 3 )
myVector[someBuffer[i]]; // Technically legal, it's possible to support with an if-ladder. Do you want to support this?
}
FXC HLSL supports this (D3D11), but it can result in weird compiler errors once you nest a few loops or use ThreadGroup barriers, or divergence is involved; and it can lead to long compile times as FXC goes the extra mile to unroll the loop and try to make it work.
I don't know if all Vulkan/D3D12 HW would support dynamic addressing just fine though.
So, for a large number of GPU's there is a world of difference between dynamic and static array access. The usual story is that all variables (except UBO's, SSBO's and samplers/textures) in a shader are realized as a register. An array access determined at compile time is easy, but an array access of run time is a proper pain. For Intel GPU's the shader driver then stores these arrays in a scratch buffer (with enough buffer to cover all threads of all EU's). Reading and writing to that array then becomes a memory read and write with the gamble that the read and write are heavily cached. However, this is much, much slower than straight up register access.
The upshot is that allowing for the index of myVec4[]
to be dynamic means a proper nightmare when it finally runs on a GPU. With that in mind, I suggest to avoid the issue completely and to not have array syntax to access elements of a [iu]vec2
, [iu]vec3
, [iu]vec4
unless the indices are static constants. Ditto for matrix element access too.
How the parser will be implemented?
(a) Let's say that is to write it from scratch, but the catch here is to see the future and prevent common mistakes and pitfalls (or workarounds) that other parsers had to do historically. Say for example a project reference like this can give some proper insight on how things can possible turn out. https://github.com/Thekla/hlslparser/blob/master/src/HLSLParser.cpp
(b) Another case is that the parser will be generated with some generator (ie: Antlr or something), so in this case there is not so much to mention about since the process is automated and algorithmic. More or less it means that we could possible get exactly identical to GLSL syntax but also if needed throw an even more additions onto the mix (like other specialized syntax or more API additions). https://github.com/AcademySoftwareFoundation/OpenShadingLanguage/blob/main/src/liboslcomp/osllex.l https://github.com/google/graphicsfuzz/blob/master/ast/src/main/antlr4/com/graphicsfuzz/parser/GLSL.g4
Another question is what is the fundamental syntax used as a reference? From what I understand is a GLSL approach but with a few alterations (eg: var/function and attributes). One thing to note is that if automated parser generator is used then the var/function might seem obsolete since the generated parser will figure out the details internally. Another point is that if the parser is auto-generated, then there is nothing to loose by conforming to a standard syntax. Say for example you keep the best parts of GLSL (which is important in terms of compability and reusability of existing shaders), and making it streamlined and neat as OSL.
Saying that since GLSL and HLSL have been into a constant evolution for many decades they had their ups-and-downs in terms of evolution and improvements. Now there are lots of messy things, lots of workarounds, huge specs etc...
Starting from scratch, is a best bet to go with the OSL route instead, since by focus scope OSL was very dedicated and precise on what it tried to do and still does, it never tried to become too robust or advanced, but provide just exactly what is important. Thus having great API and very stable and formal procedures, without any monkeypatches an workarounds. https://github.com/sambler/osl-shaders/blob/master/patterns/ChHalftone/ChHalftone.osl
These were only two of my comments, on the parser implementation, and then on the possible final goal of the SYNTAX. What must be done must be pragmatic, most possible is that by project scope it will implement the most common and typical shaders of this generation, like 2DGUI, Vertex Skinning, HDPR, etc... So if it covers these use cases then there would be nothing other to think about in terms of upfront design.
We have a parser, it's built with Lemon. Here it is:
https://github.com/libsdl-org/SDL_shader_tools/blob/main/SDL_shader_parser.lemon
(This is still work in progress, so things are changing.)
An overview of the language syntax is here: https://github.com/libsdl-org/SDL_shader_tools/blob/main/docs/README-shader-language-quickstart.md
Nice to see shader work aimed at the SDL community! Can you elaborate a little on the design goals, please? I've been looking around a bit for GLSL alternatives, and there seem to be a bunch of other shader languages out there (slang, rust-gpu, shady, ...), and I am a bit lost as to what the different advantages of the different languages are-- why would I use SDLSL instead of GLSL or one of the other shader languages?
You list "fast and cheap" and "reasonable to ship at runtime or embed in another project" as goals for the compiler, but it's not clear to me that this requires a new language (as opposed to a new compiler for an existing language).
I do agree that language design informs the necessary complexity of the compiler, but, if I may play devil's advocate for a second, making a compiler "fast and cheap" through a new language design is probably easier if you drop complex syntax and go for LISP-like S-expressions; parsing is still a nontrivial part of compile-time overhead. Also, removing the preprocessor would avoid costly token/string manipulation; for meta-programming / modularisation, a scheme/rust-like macro mechanism that operates directly on the AST (or perhaps just better options for linking) would likely be faster.
Thus, my guess is that SDLSL has additional design goals, on top of the ones that are explicitly listed?
The upshot is that allowing for the index of
myVec4[]
to be dynamic means a proper nightmare when it finally runs on a GPU. With that in mind, I suggest to avoid the issue completely and to not have array syntax to access elements of a[iu]vec2
,[iu]vec3
,[iu]vec4
unless the indices are static constants. Ditto for matrix element access too.
Right now (in my working copy), my_vector[2]
will dither down to the same code as my_vector.z
...if one were to specify my_vector[i]
then it becomes a VECTORDEREF instruction in the bytecode instead, which I fully expect might turn into something at runtime as atrocious as this psuedo code:
float val;
if (i == 0) {
val = my_vector.x;
} else if (i == 1) {
val = my_vector.y;
} else if (i == 2) {
val = my_vector.z;
} else if (i == 3) {
val = my_vector.w;
} else {
val = 0.0f;
}
I don't intend to bog down into loop unrolling to attempt to turn that back into a simple swizzle operation, because there will certainly be times where one can't obviously unroll the loop, and I don't want to have people trying to solve compiler error messages by making their code arbitrarily less complex.
I haven't decided if the luxury of this working slowly is worth it...if people are bothered that there's two ways to declare variables, surely they'll also be bothered by both vec[0]
and vec.x
working, and the unexpectedly bad fallback to that psuedocode when you don't have a constant index is probably not worth it.
why would I use SDLSL instead of GLSL or one of the other shader languages?
My personal hopes are that it's enjoyable to use, can be jumped into with an absolute minimum of hassle, and it Just Works everywhere you want your games to be. If other languages offer that, too, that's fantastic.
if people are bothered that there's two ways to declare variables, surely they'll also be bothered by both vec[0] and vec.x working, and the unexpectedly bad fallback to that psuedocode when you don't have a constant index is probably not worth it.
IMO this is not as bad as the two ways to declare a variable, because vec[i]
and vec.x
suggest different purposes: the former allows dynamic access and the later allows swizzling (and is arguably easier to read and write).
Dynamic access being silently transformed into horrible if-ladders is a nasty surprise, however. If the vec[i]
syntax stays in the language, I would like to have a compiler option to make any non-constant access an error and/or a warning (regardless of whether the backend supports it or not). That'll be useful for people who wish to target hardware that does not support actual dynamic vector access.
I'd prefer a language completely different from GLSL over a language that is GLSL-like with a handful of differences. It's easier for my brain to compartmentalize it.
For instance, jumping between C11 and Python I can do very easily on the fly. Jumping between C# and Swift, also very easy. They inhabit separate parts on my brain.
However, jumping between GLSL and HLSL? Bit more cumbersome. I keep wanting to write something like GLSL in HLSL and vice versa. Jumping between JavaScript and Typescript, also cumbersome. My brain doesn't fully switch modes.
Makes me think of back when unity had javascript-like "UnityScript" and python-like "Boo" which were .NET CLR langs they made to look like javascript and python but worked different under the hood in many ways. Absolutely hated it. Programming in a language that looks like a certain language you have much muscle memory in, but behaves differently, is very annoying. I don't think I was alone in this, so few people used UnityScript and Boo they eventually removed both. Community in mass glommed onto C# even though it was technically more complex than either one and had "fluff" syntax for unity that you didn't really need. Knowing a more complex language with some things you don't need depending on context is less mental overhead than knowing two different languages that look the same but behave differently.
So, I'd expect if it is 95% like GLSL with a few differences, those few differences are going to be really obnoxious to me. Especially under-the-hood behavior changes where I have to go read docs to figure out what's happening. That is the type of thing that I can imagine for years, over and over, I will just keep forgetting on the periodic times I might jump out of GLSL and into this other lang. I'm inclined to say, if you're going to take GLSL style, take it all, even what you might not like. If it looks like GLSL it should behave like GLSL.
Or make it behave like C. Since SDL is so C-friendly making it so C can compile to shaders would probably fit quite well.
Changing the way buffers and images are declared is fine, probably preferable. Or changing/adding some attributes, that's probably fine. But the syntax and behavior of the general ops that do the logic? Something "GLSL-Like with little differences" will be far more annoying to me than just GLSL with things I have to omit or some methods I have to change. I'd rather copy over a chunk of GLSL and get a list of methods it doesn't support when trying to compile rather than rewriting it with a bunch of little nuances here and there.
What about adopting ReShade FX? One advantage there is that there's a wealth of examples and shaders that you can potentially just drop into your project.
Also want to point out this to be thorough: https://github.com/heroseh/hcc Although I don't seriously consider anyone would get on board with making C11 the shader lang at this point :P But it's sitting right there, working, and is being actively worked on by its author. It would fit in with the overtly C-Style aesthetic of everything else in SDL well. Also the author heroseh might find SDL3 adopting this pretty cool and be into helping make changes.
something like https://github.com/heroseh/hcc seems perfect for me doing my indy stuff, as my engine and games are in C anyway, using C also for the shader lang would be the simplest thing. But I'll use what is there as long as it fairly simple to get going, and can be made to work on all relevant platforms (pc, android etc).
I NEED FEEDBACK. IF YOU LOVE OR HATE THIS, PLEASE SPEAK LOUDLY.
IF THIS WILL ABSOLUTELY CAUSE PAIN AND MISERY, SPEAK TWICE AS LOUDLY.
THANK YOU.
Here's a first example of where I'm heading towards. The syntax and the heavy commenting explaining the syntax are what's important; the code itself is not and just meant to be an example.