So more progress today. It’s too late to explain this well but Ιm gonna try dashing it out anyway.
So rendering involves matrices everywhere, a matrix describes a transform, and we often define spaces in terms of a transform and a parent space. The odd thing however is that the spaces involved arent stored with the transform so the context has to be maintained in the programmers head.
This isnt entirely accurate as on the cpu side we may have a matrix stack or a scene graph that does maintain this. But we dont have that context on the gpu (and dont want pay the overhead of it anyway).
The question of whether this could be done in a nice way was what prompted by blogpost yesterday.
I started by assuming Ι could have spaces on the gpu but that Ι wanted to be able to upload on the transforms that were needed. This meant knowing which ‘spaces’ were used in which functions as
(transform space-x space-y vec4) needs a different matrix from
(transform space-y space-x vec4). This very quickly made me realize that I need some kind of flow analysis in varjo; I need to know where all the values can flow through the system.
Today I got the basics of this working and am going to finish it off over the next few days. I will then use this as a way to statically check the validity of transforms in shaders, which should make more of the shaders intention more transparent. Finally as these primitives will only exist at compile time, we get no performance cost at runtime (as by then its just matrices) woo!
right time for bed, Seeya
This post is more of reminder to myself than anything else. Just now Ι made a little macro that not only emitted regular code (symbols and lists) but also had an instance of a class in there. This caused an error Ι hadnt seen before:
; caught ERROR: ; don't know how to dump #<FLOW-IDENTIFIER :ids (-2905)> (default MAKE-LOAD-FORM method called).
A quick google found this this the excellent answer by Mario:
The problem you are having is due to the fact your the macro not only creates code, but that this code has actual live objects embedded into them in this case. That's the def-collection thing. That is not necessarily a problem, but in this case it is because sbcl does not know how to save such an object in a fasl. You can either figure out how to make an appropriate load form, or do something that avoids the use of the actual live object.
Intriguing, so ‘load forms’ but be a concept in lisp. Let’s have a look:
Cool, so it seems that the mechanism for definign how literals are serialized and deserialized by the compiler (sorry if those terms are inaccurate for this lisp feature) and it also seems this can apply to structures and standard objects if you implement make-load-form:
structure, standard-object: Objects of type structure-object and standard-object may appear in compiled constants if there is an appropriate make-load-form method defined for that type. The file compiler calls make-load-form on any object that is referenced as a literal object if the object is a generalized instance of standard-object, structure-object, condition, or any of a (possibly empty) implementation-dependent set of other classes. The file compiler only calls make-load-form once for any given object within a single file.
I still feel amazed at how well common lisp turned out, I get lovely suprises like this all the time as I get deeper into the language.
Anyway I need to read deeper into this, looks like fun
I really should have gotten used to this idea, with the amount of macro stuff Ι’ve been doing with lisp, but being able to mess around with compiler is just fun.
In the process of my nanowrimo project on making ‘spaces’ for cepl Ι’ve been looking into how these will be handled on the gpu. I want to be able to reason about these more explicitly than just transforming some vector with some matrix, but at runtime Ι need to keep things as simple and with as little indirection as possible.
Whilst on the cpu side I’m living in a (mostly) dynamically typed language, on the gpu I’m fully static. This opens up some possibilities for using/abusing my type system to check these things for me. I am able to add types that exist purely so the typechecker can validate what I’m doing and that will just compile down to a regular glsl vector4.
So one example I could try is:
(let ((pos (p! some-vec world-space))) (transform pos view-space))
Where p! is a function that returns a value with type ‘position-world-space’. I can then use the metadata produced by the compiler to work out what matrices (for the various spaces) need to be uploaded. I can also ensure you don’t do something potentially meaningless (like adding two vectors in different spaces) without first indication that that was what you intended.
(let ((pos-1 (p! some-vec world-space)) (pos-2 (p! some-vec clip-space))) (+ pos-1 pos-2))
This is not very interesting though as it’s not normally the mistake you make. Something more interesting would be when you are in a fragment shader (which is implicitly screen space) and you want to do some post-proc lighting based in clip-space. You could use a simple block like this:
(in-space clip-space ..your code..)
And you could be sure that any ‘position’ that was used inside would be transformed to the correct space prior to the calculations being carried out. Any time the spaces match it’s a no-op so no conversion code is emitted.
I’m thinking that a chunk to this can be implemented with the compiler the way it is, but for the bits that can’t it means I have to add and expose some new compiler features, which is cool.
So that’s why I’m excited by compilers today :D
Back to work!
p.s. Of course Ι am missing out a bunch of details especially that at least the names of the spaces would have to be statically defined. But ho hum, it will make sense soon :)
I’ve just had the joy of stumbling onto this series by Jean Niklas L’orange about clojure persistent datastructure. I had gained a basic understanding of the theory of these before now but this series of posts break it all down really well, including the optimizations that make this datastructure (more) practical.
Not only is fantastically written but Ι believe you could work through this series and write a decent persistent vector implementation by the end of it.
This is just a quick check in to log some progress.
Tonight I’ve been poking around in the current event system, mainly it was cleanup but Ι also added the ability to have weak & strong subscriptions to a event node. The weak ones were neccesary to allow GC to cleanup things that were created at the REPL and are now orphaned. However for well written code that guarentees nodes are unsubscribed, the overhead of weakrefs is undesirable.
Currently nodes can also have two functions applied to them, a filter (which can stop an event propagating) and a body (which is just run with the event as an argument). These were added in a hurry so I need to go back and turn these into something sensible. I guess I will just combine them and have the result of the body be an event or nil, events get propagated, nil doesn’t.
We shall see.
Right, in this post I want to go over what I’m going to be making this month, but first a summary of day 0.
Rewriting backscatter in cepl turned out to be a really good way to find bugs in the compiler, it was depressingly good at finding them actually. The changes have made the struct handling in varjo much better and made the function dependency code a lot better in cepl.
Whilst the compiler is now producing good glsl, the pipeline crashes hard as soon as you use it :p. Oddly enough commenting out a couple of the uniforms being passed to the pipeline stops it crashing, so I’m assuming I have either screwed up the uniform uploading, or it didn’t work before in examples like this.
Either way it is not the primary focus for this month so I am forcing myself to leave it as it is for now and concentrate on nanowrimo.
On that note let’s talk about that :)
The project for this month is to add a repl friendly, gc’able, optimizable way of managing hierarchies of transforms in cepl. If this sounds like a scene-graph then that’s no surprise, but I’m consciously trying to avoid putting game objects in the graph directly. By this Ι mean Ι don’t want to have some ‘game-object’ type that you have to subclass and that then goes in the graph.
What I would prefer is some means of defining spaces and then having objects subscribe to them. This decouples objects from the means of organizing them.
In this system it is normally wise to have some kind of caching system. This pays off when there are multiple spaces that share a parent. For example if you are calculating the position of each finger on a hand you dont need to recompute the position of the hand each time. Another property that melds well with this is that in many cases you can compute the transforms lazily. You dont need to calculate a parent transform unless the child needs it (or it is needed itself).
Slam this all together and you get a kind of event system. One that caches the latest event rather than propagating them and can be resolved by pulling.
So far so good, but there is one was case I’m interested in. It may be necessary to walk many of the spaces. To that end it would be good to have a flush event that would push all the pending events through the system.
The finale: Input events are just events which always flush. This means it may be possible to merge these event systems to produce a unified interface. One which would allow subscribing to any event. This is basically some odd rx hybrid (with less state safety). Care will have to be taken around the boundaries between different style nodes but more safe systems can be built on top of this (or it can be ignored and just used to feed some higher level system)
This may end up not working out but it’s the experiment for the month and we shall see how it goes :)
Time to go watch a bunch of talks on streams/frp/etc to steal ideas. Wooo!
Got him! turns out there was a bug in the code that merged compilation environements. One of those things telling me Ι need to have better tests in this code.
Anyhoo, that’s done. back to business.
Given this gpu struct:
(defstruct-g material () (additive-color :vec3 :accessor additive-color) (diffuse-color :vec3 :accessor diffuse-color) (specular-color :vec3 :accessor specular-color) (specular-exponent :float :accessor specular-exponent) (background-ammount :float :accessor background-ammount))
Only the call the
(varjo::defshader marg ((y material)) (labels ((xx () (additive-color y)) (yy ((x material)) (additive-color x))) (additive-color y) ;; this works (xx) ;; so does this (yy y)) ;; but this doesnt (v! 0 0 0 0))
In real news I’m catching a good few bugs in varjo today. Can’t wait to squash this one though :p
First day of November, first day of my first NanoWriMo. Traditionally this is a challenge to write a 50000 word novel in a month but I’m going to be coding along with this instead. As word/line counts are pointless in code I’m going to try do some significant work on cepl.
I’m going to aim to make a space-graph (analogous to a scenegraph) I’ll be writing this up as I go so Ι won’t burden this post with it.
Today however Ι am bug hunting by taking a rather nice shader called backscatter from shadertoy and remaking it in cepl. It was written by a good friend who tells me it’s too slow, which may be fair but it is also clearly written and pretty enough to be motivating.
writing it has already let me find a few bugs, including one in Varjo’s structs so I am off to fix that now :)
Phew, now Fuse has shipped the beta I’m not crunching so much at work so I’m back working on cepl again. (Check out Fuse btw, it’s early days but theres some kickass shit in there, I’ll be doing some posts on it in future)
I’ve not a nice branch full of changes to merge in soon in which I’ve been working on two main things:
Better error messages This took a lot more work than just tweaking the strings. I’ve gone though varjo (the lisp->glsl compiler) trying to find places where I can make suggestions of possible correct alternatives. The obvious example for this is variable names so if you type the wrong name varjo will make suggestions based on names that are significantly similar (I’m just using jaro-winkler-distance for this). In cepl it also involved going through the more hairy code, trying to seperate it into more sensible functions and also making sure I give more specific errors especially around the code that handles converting between texture-formats & lisp types.
New event system So I changed this yet again :p. The reasons for doing this have mainly been around limiting scope. I dont want unnecesarily high level abstractions in cepl core, I want ‘low enough’ abstractions that are easy to build on.
So this time the main change is that event listeners are remvoed in favour of event-nodes. Event nodes can subscribe to other event nodes to recieve their events. Any event node can also be subscribed to. Each node holds a list of weak refs to the node that are subscribed to it, and a list of strong refs to the nodes that it subscribes to. In this way we have a event graph that the garbage collector can clean up if you don’t unsubscribe explicitly.
“Why add this weak ref complexity” would be a fair question to ask, and the reason is making a balance between good practice and pragmatism. Common Lisp allows awesome levels experimentation with the repl and incremental compiling. It is inevitable that, whilst explicitly disconnecting event-nodes is the correct thing to do, managing that is a PITA when you are just trying to play with ideas. To this end we make sure we can GC away parts of the event graph that are no longer needed, letting you focus on the important stuff. This kind of thing is what I mean by ‘low level enough’, I’m not trying to bake in some glitch free reactive programming framework (though those are awesome), but I still want to make the experience good and lispy.
Less importantly, with the new changes events are now structs with read only fields.
The driving force for the above changes has been that I now have event sources other than SDL2. I have made a simple android app that sends touch events over sockets to cepl to allow more direct interaction with whatever I’m making. It’s early days but the events from the cepl remote-control are now flowing through the event system just like mouse & keyboard events so I’m pretty stoked :)
This physical hardware business makes recording youtube videos more tricky so I’m gonna go buy some extra cameras today so I can try show this stuff off.
Hopefully I’ll be back soon with a new video for you. Ciao