New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GLSL code printer #12713
GLSL code printer #12713
Conversation
…atrix printing test
I had closed this to fix the documentation. |
I need to point out that one significant thing is missing from this printer: printing small matrices and vectors as their native GLSL types. So for example, a 3x4 matrix could be printed as a mat3x4. The reason I overlooked this is because I have been using this printer exclusively to print stuff that doesn't have native representation in GLSL, mostly Clifford algebra. I was so stuck in the Clifford algebra that it never even occurred to me until just now: people will probably want to print native GLSL matrices. Duh! 🙄 |
Today I implemented the family of This code could really use a second set of eyes, and hopefully just one set. The tests could use some basic sanity checks. I've been staring at the screen all day ⚡️ . There were some complicated decisions to be made about the fact that GLSL assumes column-major indexing for its matrix operations. I decided to provide a There's a bunch of other stuff in there too. Looking forward to some feedback 😄 |
…new features that are being tested: glsl matrix/vector type printing.
This looks really interesting @micahscopes |
@bjodah it's very similar to C, but with some different keywords and unique types. I subclassed CodePrinter, but it might have made sense to subclass the C printer. maybe that would have meant a little less copy/paste? I think for the most part, the class is now quite capable. |
Yes, that was my thought. If almost all methods are just copies of the methods in say the C99CodePrinter (or is GLSL based on C89?), then subclassing would be preferred to avoid code-duplication (which eventually becomes a burden with respect to maintenance). I am currently working on implementing a |
@bjodah There are quite a few nuances, it seems. So it's hard to say. I've been referencing this wiki, although it's a little sparse on examples. A lot of stuff seems to come more from C++ than C, but I don't write much of either of those languages, so it's not easy for me to say with certainty. As far as structs go, GLSL has structs that are more similar to C++ structs than C structs. But GLSL has built-in matrix and vector types (only for dimensions of < 4), each with their own funny keywords. There are built in functions and operations for these types, such as In GLSL you can also have arrays, I believe of most any type, although in OpenGL < 4.3, you can't make arrays of arrays. There might be some other variations on the kinds of arrays supported with different versions of OpenGL. In >= Opengl 4.3, multidimensional arrays are supported. My personal use case is doing Clifford algebra on the graphics card. So I used Sage to generate symbolic expressions for various Clifford algebraic operations (mostly the geometric product), then I used the Sympy printer to convert these expressions into GLSL expressions. I represented Clifford algebraic objects (multivectors) as float or double arrays... (really, this is enough for anything that can be represented as a finite vector space)... In early tests, that's all I needed. I just wanted a way for little expressions to be translated into arrays. I didn't even include any functionality for typing or assignment in my early printer. I did all that with a python string/templating tools. But I can see how it would be desirable to interact with the standard GLSL types. People doing standard 3D stuff use standard types a lot. I can also see how it might be useful to provide ways of rendering sympy objects into structs. To use my Clifford algebra example, maybe I'd want to have a struct MultivectorCl3
{
float c;
float x;
float y;
float z;
float xy;
float xz;
float yz;
float xyz;
}; Then a multivector might be used like: MultivectorCl3 A = MultivectorCl3(0,1,-2,4,0,0,0,0)
float length = sqrt(pow(A.x,2.0)+pow(A.y,2.0)+pow(A.z,2.0)) Code generated in this way would be much more human readable, and easier to check. Here's the product for the real Clifford algebra Cl(-1,-1), generated by my old printer. See for yourself how difficult it is to read: const int N = 4;
float[N] mul(float u[N], float v[N]) {
return float[N](u[0]*v[0] - 2.0*u[1]*v[1] - 4.0*u[2]*v[2] - 2.0*u[3]*v[3], u[0]*v[1] + u[1]*v[0] - 2.0*u[2]*v[3] + 2.0*u[3]*v[2], u[0]*v[2] + u[1]*v[3] + u[2]*v[0] - u[3]*v[1], u[0]*v[3] - 2.0*u[1]*v[2] + 2.0*u[2]*v[1] + u[3]*v[0]);
} (by the way, the only part actually generated from the printer is the stuff inside of const int N = 4;
Multivector {
float c;
float x;
float y;
float xy;
}
Multivector mul(Multivector u, Multivector v) {
return Multivector(
u.c*u.c - 2.0*u.x*v.x - 4.0*u.y*v.y - 2.0*u.xy*v.xy,
u.c*v.x + u.x*v.c - 2.0*u.y*v.xy + 2.0*u.xy*v.y,
u.c*v.y + u.x*v.xy + u.y*v.c - u.xy*v.y,
u.xy*v.xy - 2.0*u.x*v.y + 2.0*u.y*v.x + u.xy*v.c
);
} Again, the part inside of |
Here's an example of an 8-dimensional planar rotation function generated using the old version of this printer and Sage's CliffordAlgebra module: https://gist.github.com/micahscopes/e2cd2f9aa7e2daa9a433a42fffaf8122 That 8-dimensional rotation function actually takes some to bake out on a powerful machine. I think it took something like 30 seconds at least. Believe it or not, I've tested this function and it really does work! Here's an example rotating an 8 dimensional fractal: https://vimeo.com/210915603 . If you you're looking at a 3-dimensional slice of an 8 dimensional space, and only rotate in planes that are subspaces of that visible slice, it behaves just as you'd expect, it looks just like an ordinary 3-dimensional rotation. But if you're rotating in planes that are outside of the visible slice, all bets are off and stuff starts morphing in and out of thin air 😮 |
sympy/printing/glsl.py
Outdated
|
||
'order': None, | ||
'full_prec': 'auto', | ||
'precision': 32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'precision'
doesn't actually refer to the number of bits used, but rather number of significant digits. Using 9 would be the right number for float32. We are working on better representation of types in the printers.
sympy/printing/tests/test_glsl.py
Outdated
|
||
|
||
def test_glsl_code_constants_mathh(): | ||
assert glsl_code(exp(1)) == "float E = 2.7182818284590452353602874713527;\nE" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this number will have 8 decimals once precision
is changed to 9...
sympy/printing/tests/test_glsl.py
Outdated
assert glsl_code(Rational(3, 7)) == "3/7" | ||
assert glsl_code(Rational(18, 9)) == "2" | ||
assert glsl_code(Rational(3, -7)) == "-3/7" | ||
assert glsl_code(Rational(-3, -7)) == "3/7" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this integer division? Wouldn't you want 3F/7F
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, absolutely. The syntax for casting non-constant integers to float is to use constructors. So if i
and j
were functions that returned int
types, then you'd wanna do float(i())/float(j())
. But for integer constants, usually people add a decimal after the number, i.e. float x = 2.
; In this case I think it'd be more readable to do something like 3.0/7.0
|
||
|
||
def test_glsl_code_functions(): | ||
assert glsl_code(sin(x) ** cos(x)) == "pow(sin(x), cos(x))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The little GLSL code I've seen they've used the 32 bit versions of math functions i.e. powf
etc., but I don't know what is preferred. Does GLSL support fp64 too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is a powf
function (I tried searching for it here). GLSL has a double
type. I believe there's trouble if the second argument in pow
isn't a float or a double.
@micahscopes that's really fascinating stuff 👍. I made some minor comments. But since I haven't actually used GLSL myself I can't really give any in-depth feedback on GLSL specific parts. |
thanks @bjodah! I'll make some of these little changes and see if I can't subclass the c printer to get rid of some copy/paste. I'm also gonna check into using initializer lists for multidimensional arrays. I think I read somewhere that this is possible, and it'd look nicer than what's in there now. |
My feeling is that we shouldn't subclass the C printer if GLSL isn't actually a superset of the language. Otherwise, the printer will generate code for things that isn't valid GLSL. |
If there is a considerable number of methods which are now copied & pasted we could create a private super class (e.g. |
So then we should focus on the tests here, I don't know if there are any people in the SymPy community working with GLSL. I googled Would any of you be interested in this functionality @prideout @yuri-karadzhov @ataber ? Do you have any feedback on the functionality? (I hope you don't mind being pinged, please feel free to ignore the ping and sorry if it disturbs you, I am simply guessing that you might be interested). |
@bjodah I made some changes based on your review. Next thursday I'll give a talk related to this code printer at a local Python meetup. Maybe I can find some people who might feel confident in offering feedback! |
pull upstream changes
Is this ready to be merged? |
@asmeurer As it stands, this is useful and seems to be pretty solid/stable. I think it'd be safe to merge it. Maybe that would help generate some people actually using it? there is work to be done on it, but it seems to be on par with many of the other code printers. I would still like to get a few actually useful examples in (it would be helpful for users, but also for me to discover stuff I may have overlooked). but I'm not sure that would need to be done before merging. |
It's easy to make another PR later with more changes. @bjodah do you agree this can be merged? |
Yes, :) |
Hi there, I've implemented a rudimentary, generic GLSL code printer with basic tests. The code and tests were adapted from the javascript printer. I made a modification to allow the option of printing common operators as functions. In GLSL, there is no
a**b
operator, so that one always prints aspow(a,b)
, but it's also possible to rendera+b
asadd(a,b)
,a*b
asmul(a,b)
, etc.I've used a simpler version of this printer for a while to develop Fragmentarium shaders. Using a simplified printer, I've been able to generate GLSL functions from all kinds of algebras in Sage, including Clifford Algebras.
I'd love to see this in the sympy. I just finished it a moment ago, but am feeling pretty confident about it, since it passes all the tests adapted from the javascript printer. That said, I haven't used this version of the code in practice. So it could use some human testing and review, and maybe some organization, as a lot of it is copy/pasted from other printers.