From the Burrow
(setf mayhem damnit)
When I was making varjo I hacked in support for setf very quickly. It gave the rough feeling of generalized references but even at the time I was thinking that this hack would come back to kick me in the arse..Turns out yesterday it took a run up first..
So I had to take a detour from my detour writing the flow analyzer to rewrite setf. Luckily that wasnt too bad and the code makes way more sense now. So tonight I’m getting back to the flow analyzer and looking at if
expressions.
These are a bit fiddly as you (usually) cant tell ahead of time which result is going to be returned. So from the point of the flow analyzer we will say they both do. So if:
(let ((a 2)
(b 3))
(if (something-something-stuff)
a
b))
Then we have to assume both a
& b
return and continue accordingly.
But wait, what if there are side effects in there?
(let ((c 2))
(if (something-something-stuff)
(setf c 10)
(setf c 20))
c)
Then we have to be aware that the setf is in a condition when we update the flow information.
It’s probably something people who know this kind of thing think is trivial but the way I’ve implemented this so far wasnt done in a way that took this into account. Still exciting though and will be cool when Ι crack it.
Back with more soon
Stumpwm invert colors of one window
One of the last things I was missing on my new stumpwm setup was being able to invert the colors of a single window. Turns out this is not hard to do.
- Make a script called
inv.sh
containing the following
#! /bin/bash
if [ "$(pidof compton)" ];
then
pkill compton
else
xdotool getactivewindow | xargs -I {} compton --backend glx --invert-color-include 'client={}'
fi
exit
-
chmod +x inv.sh
-
Add the following to your .stumpwmrc
(define-key root-map (kbd “C-i”) “exec /home/baggers/Code/shell/inv.sh”)
Boom, done. When you click <your-prefix> C-i
the currently focused windom will be inverted. Running it again disables the effect.
This is pretty hacky and I’d like to make something more integrated with stumpwm so Ι can have multiple inverted windows at a time. That will be easy enough when it comes to it, but for now this does the job.
nanowrimo & flow analysis
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
make-load-form
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
Controlling the compiler is AWESOME
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 :)
Fantastic series on Clojure persistent datastructure internals
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.
Understanding Persistent Vector - Part 1 Understanding Persistent Vector - Part 2 Understanding Persistent Vector - Part 3 Understanding Clojure Transients
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.
Top notch!
NanoWrimo-2
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.
NanoWrimo1 - This month's project
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!
More dead bugs
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.
Compiler bugs
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 #'yy
breaks
(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))
Wat?!
In real news I’m catching a good few bugs in varjo today. Can’t wait to squash this one though :p