This means that `layout(triangles)` etc now work, though there are
issues with frag shaders missing functions (since getting function calls
working) and compute shaders silently failing.
But only for non-extension. I'm not sure it's quite right (as I don't
know if all void intrinsics should not have res type/id (all extension
instructions *do*)).
This is a nice common place (all languages and targets use it) for the
warning and any conversions, though those are still spread in various
places (and need to be cleaned up), so it doesn't do all that much yet.
The spir-v back-end needs to emit literal values in some places and
supporting that in explicit intrinsics required marking the expression
as special. Unary = seems a little odd at first, but at the same time
seems to make sense, especially for marking expressions as "use literal
value" (eg =ImageOperands.Lod will result in a literal 2 instead of an
op id that points to the 2).
The dereferencing was done for implicit intrinsic args, but I had
forgotten about it for explicit intrinsics. Now the scalar `a` version
of `mix` works.
While there's a bit of code duplication, it very much cleans things up
for the various targets, especially v6 progs. However, spirv is not
implemented yet, so that's broken again.
Parentheses confused the term counting because they weren't taken into
account for the "neither expression can be split" check, resulting in a
later segfault due to walking off the end of the array.
That is, vector ops with 3-component general vectors and quaternion ops
with 3 and 4 component general vectors "promote" the general vectors to
vector or quaternion as appropriate. Needed for operations with
vector and quaternion literals.
The v6 and v6p targets don't have horizontal operations, instead they
have direct vector/quaternion equality to float/int scalar result. Fixes
an ice when building game-source/quake.
The operand kinds form namespaces for their enumerants (only BitEnum and
ValueEnum operand kinds are supported for this). Now `Lod` and `Bias`
use `ImageOperands.Lod` and `ImageOperands.Bias`, which is probably a
big improvement in the long run.
Finally, all of QF's shaders *compile*, though the spir-v is generally
incorrect (capabilities etc), and the code gen may still be full of
bugs.
This took sorting out a few issues with type property evaluation, but it
seems to work nicely now. Just one known error to sort out and then it's
time to get the spir-v correct.
Explicit intrinsics are very similar to inline functions, so the
function data for generic explicit intrinsics functions also need a full
scope. Fixes the undefined symbols for generic type names.
There are problems with symbol lookup (eg, generic type names, image
operand names) but the system seems to be working: texelFetch ->
OpImageFetch (which uses explicit arguments even though it doesn't need
to) seems to set the arguments to OpImageFetch correctly.
Now all my glsl shaders build, though most likely none of them
correctly. However, I'm finally out of that tunnel... only to find
myself in a moonlit forest fill with the sounds of wolves (at least, I
hope they're wolves).
All was fine... until the texture handling. Ugh, what a mess: 16
variants of "texture", times all the sampler variants. And I haven't
done even half of them.
I won't say it belongs in glsl-builtins, though the glsl-specific stuff
probably does (glsl texture handling is a mess). Also adds sampler
attribute handling (which falls back to image when necessary).
If `@intrinsic()` is followed by `[expr_list]` then those expresses will
be used to create the intrinsic rather than the function's parameters,
allowing for reordering, adding extra parameters or even complex
expressions.
However, only the parsing is implemented.
Unfortunately, this require using different syntax for the two cases:
type.attr works in cases where types are expected, but not in
expressions (lots of shift/reduce and reduce/reduce conflicts). However,
treating type like an Objective-C class works nicely, though
`[type attrib(params)]` looks a little odd. However, this allows using
generic types to provide function calls (eg, converting texture
coordinates).
I had forgotten that subpassInput can't be used but the normal imageLoad
functions since they don't (and mustn't, by the spec) have subpassInput
as a possible type. While creating a specialized version works, I think
I need to come up with a better way of specifying intrinsics.
It seems the base atomic functions should use const for the data
parameters and just a reference for the mem parameter (not entirely sure
yet as I need to check with spirv-val, but there are still build
issues). Using __imageTexel correctly took a little thinking since it
returns a pointer but the atomic functions take a reference.
I guess I hadn't thought of it because GLSL doesn't have `auto`, but
since the builtin functions are implemented in Ruamoko, `auto` is
available and I use it for the atomic image functions.
Used the wrong generic type name for gvec4*, and missed @construct. Now
gridplane.frag tries to generate spir-v again (fails due to the
instruction names).
The goal is to make it easy to get size/coord/base types from image
types without creating a zillion type functions.
It was necessary to make it possible for any type to have an attribute
function (returns an expression so it can be more useful: types are
returned via type expressions). Algebra types were the first victim
(which was nice for testing).
And disable it for GLSL. I might need to make it target-dependent
instead as I don't know if I'll be able to implement it in SPIR-V, and
when I get to the C back-end, it will be superfluous.
GLSL's texture functions are a bit of a nightmare. The return types are
wrong, but I need to decide how I want to pull the sample type from a
texture or sampler.
Spir-v is pretty slack with the exact types of the operants to the
right-shift instructions so no need to care about the type for shift so
long as it has the correct number of components.
I had already implemented the code generation side (though using type
ids instead of encodings is a nice change), but I hadn't implemented the
actual evaluation or even called it. Now return types can be computed
from generic parameters (eg, ivecN from vecN).
It's still not great (mostly on the language side, I think), but
different glsl shader types get the correct model and fragment shaders
even get the correct mode.
Access qualifiers aren't supported yet, and some of the image handling
is a bit messy (currently tied to glsl when it should be general), but
the basics are there. However, now I've got to sort out the execution
model (need Fragment or GLCompute for ImplicitLod).
I really don't like the way they're included (I'm really looking forward
to #embed, but gotta wait for gcc 15), and I'm a tad grumpy that the
documentation for them
(https://registry.khronos.org/SPIR-V/specs/unified1/MachineReadableGrammar.html)
is wrong (missing fields), but I think I like the result.
The grammars (core and glsl.std.450) are parsed into structs that should
be fairly easy to interpret: the instructions, kinds, and enumerant
values are sorted by name for search with bsearch. Having the data
parsed in means source code can refer to the items by name rather than
magic numbers, which will be very nice for intrinsics and image types
(and probably a few other things).
The idea is to allow certain contexts to interpret something like `2D`
as an identifier instead of 2.0 (double), or (not sure I'll go there,
5e12 as a bivector instead of a double or float.
In the end, it does simplify things a lot, though it helped get function
pointers working at least a bit (they're not quite the same as C yet,
but I think that's mostly that functions can be struct fields).
They should now work in generic contexts, but the pressing need to work
on arrays was due to constant expressions for element counts breaking.
As a side effect, function pointers are now a thing (and seem to work
like they do in C)
GA expressions need to be optimized so things that should cancel out do,
and this requires everything to be dagged. Doing so in expr_process gets
most of the expressions, and then a few stragglers in proc_field. There
might still be some more, but my test scene compiles again.
I suspect they were once for dealing with class and typedef names in
selectors, but they're confusing things now that I'm trying to rework
type declarations, and I doubt they were ever actually needed.
I suspect this is actually a bug in parameter setup for inline function
calls, but it would certainly be a bug to get an unprocessed swizzle
expression as they're generated from field expressions.
It's just a jump to a label at the end of the block, but it's enough for
now as it takes care of the assumption that the return is the last
expression in the inlined function.
The code is pretty lousy in that it assumes there's only one `return`
and it's the end of the function, and the generated code is even worse
(too many load/store ops in the spir-v), but it looks like it at least
works. It does pass validation.
This gets the incoming parameters initialized, though currently rather
suboptimally (but that's due to the setup of the call). Next, just need
to handle `return` in inline functions.
Sometimes, premature optimization can help, but whatever. Finally,
alias.vert compiles, but the output is fairly broken (incorrect handling
of `return` and the parameters aren't initialized).
They need a valid name for the stricter checks. The name is taken from
the interface name, and since that needs to be unique, prepending a `.`
should be enough.
However, once past a couple of errors, they're not getting initialized
in the spir-v, so I've probably missed something there. For now I've
kept things simple and made them regular variables regardless of
qualifiers (and no support for out/inout yet).
If the block's result is just a variable reference, it won't match any
expression in the block's list so it needs to be processed independently
in such cases. The `mix(genFType x, genFType y, float a)` inline now
gets as far as spir-v code gen although there are still many issues to
fix (parameter symbols, `return` handling, etc).
I decided to not mess with actual casts. In retrospect, this is probably
for the best as it avoids any bit-cast conflicts, but should allow for
block initializers with a few tweaks. `@construct (type, args...)` in
Ruamoko follows the same path as `type(args...)` in glsl.
They're buggy in that the defspaces for parameters and locals are
incorrect (they need to point to the calling scope's space). Also,
parameters are not yet hooked up correctly. However, errors (because I
need to allow casts from scalars to vectors) do get handled.
Because the symbol tables for generic functions are ephemeral (as such),
they need to be easily removed from the scope chain, it's easiest if
definitions are never added to them (instead, they get added to the
parent symbol table). This keeps handling of function declarations or
definitions and their parameter scopes simple as the function gets put
in the global scope still, and the parameter scope simply gets
reconnected to the global scope (really, the generic scope's parent)
when the parameter scope is popped within a generic scope.
I guess either I goofed and thought void functions had no return type at
all, or at one stage that was the case, but either way, I had never
tested procedures.
The back-end support for renamed builtins (fte's = #0:name) was needed
for pascal functions because the internal name is now prefixed with an @
to allow the lvalue/rvalue selection of behavior for function symbols
This catches qp up with qc and glsl, which means I can modify all three
languages to support inline functions at the same time. There is the
minor(?) problem of attempting to pass parameters to a
function/procedure that takes none producing the wrong error, but that's
documented in the parser.
xvalue symbols refer to two expressions: an lvalue and an rvalue. They
are meant to be used with xvalue expressions.
xvalue expressions are useful when a distinction must be made between
the behavior of something (eg, a pascal function symbol) must change
depending on whether it's an lvalue (assignment of the function's return
value) or an rvalue (a call to the function, especially when the
function takes no parameters).
Because the glsl front-end uses Ruamoko to compile its builtins, it
needs to switch languages, and the cleanest way to do so is to use a
context object that gets passed around. This removes not only the
current_language global, but also (as a bonus) any real references to
flex's scanner object (there's still a pointer in rua_ctx_t, but it's no
longer a parameter (which caused some pain in the change)).
While it is a pessimism, I really want to get rid of fold_constants (in
favor of something smarter), and it's currently in the way of more
important things.
The main goal was to make it possible to give generic functions
definitions (since the code would be very dependent on the actual
parameter types), but will also allow for inline functions. It also
helped move a lot of the back-end dependent code out of semantics
processing and almost completely (if not completely) out of the parser.
Possibly more importantly, it gets the dags flushing out of the parser,
which means such is now shared by all front-ends.
There's probably a lot of dead code in expr.c now, but that can be taken
care of another time.
When getting an offset alias of an already aliased def, the offset needs
to be updated so it points to the correct part of the def. This is
actually a rather old bug but it got uncovered somehow with my rework of
the ruamoko semantic processing (probably an altered cast).
I guess I'd wanted to avoid supporting automatic types in type
functions, but that broke long and unsigned int: either no type, or just
int, depending on the decl style.
Unfortunately, it turns out that generic functions are over-grouped so
all functions with the same name get the last definition, so `mix` with
a float (which should get GLSLstd450FMix) gets the bool version instead
(SpvOpSelect).
When the number of supplied vectors matches the number of columns in the
matrix and all vectors have the same width as the number of rows in the
matrix, there's no need to expand the vectors into components only to be
gathered again.
SPIR-V requires that matrices are constructed from vectors rather than
individual components. While not optimal, iqm.vert's output now passes
spirv-val. Also probably still lots wrong with fine details.
I think I need to come up with a better way of defining glsl builtins
and the strings are rather ugly (and I don't want to use qfcc system
header files).
iqm.vert now compiles, but doesn't pass validation yet (matrix bugs).