From the Burrow

Booya! Space checking lives!

2016-01-03 17:47:20 +0000

Oh progress feels so good!

Ok, now given some code like this:

(defun-g vert ((vert pos-col) &uniform (s space-g) (w space-g))
  (in s
    (in w
      (p! (v! 1 1 1 1)))
    0)
  (values (v! (pos vert) 1.0)
          (col vert)))

The in portion does nothing useful, it purly exists so that there is a position (the p!) which is created inside the scope of the space w. That position is returned into the scope of space s. To ensure this is valid the compile needs to multiple the position by some kind of w space to s space matrix.

Lets look at the compile output (it’s been cleaned up for readability)

#version 330

layout(location = 0) in vec3 fk_vert_position;
layout(location = 1) in vec4 fk_vert_color;

out vec4 OUT_34V;
uniform mat4 W-TO-S;

void main() {
  (W-TO-S * vec4(1,1,1,1));
  0;
  gl_Position = vec4(fk_vert_position,1.0f);
  OUT_34V = fk_vert_color;
}

Here we can see that there is no mention of w, s, positions or spaces. The compiler has created a uniform to hold the w space to s space transform and multiplied it with the vec4.

On the cpu side we can find this code

...
(let ((w-to-s (spaces:get-transform w s)))
  (when w-to-s
    (let ((cepl-gl::val w-to-s))
      (when (>= w-to-s-uniform-location 0)
    (cepl-gl::uniform-matrix-4ft w-to-s-uniform-location cepl-gl::val)))))
...

Here we see that the original spaces w & s are passed to the get-transform function which returns the matrix4 which is uploaded.

As you would imagine there are still plenty of little bugs to clean up, but those feel like nothing compared to how much work this has taken. I’m pretty stoked right now.

I’m looking forward to posting a proper example of how you would use this in regular shaders soon.

That’s enough for this week

Ciao!

Spaces still in progress

2015-12-09 23:48:23 +0000

Bloody hell that month went fast. Well the obvious is that the spaces feature is not done. I lost a week an a half to business, but it was in palo alto so I got to visit the computer history museum which was badass.

I’m also stoked at how much I did get done in that time. Varjo progress is marching onwards and the parts of cepl I found hairy are slowly becoming less so.

The big changes are happening in varjo purely because varjo is outgrowing the simple model that has served it up to now. Right now I’m making sure that a varjo compile pass not only gives you the result (and metadata) but also includes the ast. Varjo is currently single pass (and that was fine) but cepl wants to be able to use varjo in a multipass fashion. It needs the ast so it can create the varjo code for the second pass (where, in this case, spaces are turned into matrices).

It is likely that, in time, varjo will just become properly multipass. But I’m going to do this in steps as I don’t want to be working on features for features sake, I want the space feature to drive this.

More news as it comes

Flow analyzer complete enough

2015-11-23 10:12:42 +0000

So this week I got the flow analyzer working WOO! There are a few cases it’s missing but the hard stuff (iteration) is done and there’s enough that I can start looking at how to use it

So a refresh:

  • On the cpu side I have a tree of transforms representing different spaces (like a lightweight scenegraph)
  • I want to use these in a sane way on the gpu
  • I cant afford any runtime logic becuase then we are pissing performance up a wall.
  • Matrices describe transforms between spaces. We are interested in the spaces yet we have no explicit way of handling them.
  • If we could use spaces in our shaders and at compile time turn them into matrices (that are then uploaded as uniforms) then things would be much clearer.
  • To be able to resolve this at compile time we need a flow analyzer…which we now have, yay!

So the next question was how to use this.

position is a point (vector) with a space. Position is a compile time construct which will be compiled to a regular vector

space will be the name of the type. Spaces will be passed up as uniforms

(defun-g test ((vert :vec4) &uniform (world space))
    ...)

I was imagining a transform function that let you transform a position from one space to another:

(transform pos world-space clip-space)

This is kinda dumb though as the position knows what space it is in. So maybe

(transform pos clip-space)

Hmm, might be ok. What would be sweet though is a scope where everything inside is guaranteed to be in a certain space

(in world-space
  (some code)
  (more code))

wait though, this makes transform redundant as

(in clip-space pos)

Would transform the pos to clip-space, and it would return as it’s the last thing in the scope. Sweet!

in is also nicer to write than transform so I think we have a winner.

Note on making positions: in cepl/varjo vectors are made with the v! function. As in (v! 0 1 2) returns a vector3. Position will have a similar interface but it will expect a space, either as an argument or taken from the current in scope. So (p! 0 1 2 world-space) gives you a position3 in world space. (in world-space (p! 0 1 2)) does the same (I may kill the first version so in is the only way to do it)

So assuming this worked how does cepl use it?

My current thinking is that cepl will iterate over the function calls looking for calls involving spaces and positions. It then works out the matrices needed for each calls, de-duplicates the result and adds them as uniforms. Intermediate calculations need to be added to the source code (which I havent worked out the details for yet) and then the new lisp shader code (now without any spaces or positions) is recompiled. The result of this second pass the the shader uploaded to the gpu.

I’m off to america this week so I have no idea if I will get any of this done. We shall see :)

The flow analyzer LIVES (a bit)

2015-11-17 01:38:55 +0000

Had a couple of days off coding but now I’m back. Yet again Ι found limits in the my compiler’s architecture, this time in how I handled environments.

So before what Ι had was roughly as follows:

I have a tree of code that needs compiling. The compiler walks down the tree and compiles each form. The environment is simply an object that holds all the variables and functions that exist at that point in the program. To implement variables scope I simply copy the environment as it was at the start of the scope, add the new variable/s and then throw it away at the end. Because the stuff outside the scope only see’s the original environment it couldnt access variables from the inner scope.

This has worked great for ages but now by flow analyzer wants the answer to the question “what scope was this variable made in?” and that data has been thrown away.

So now I’ve switched to a tree of environments, and they are now all immutable. This was a damn big change so Ι took the opportunity to start using a testing framework so that I could build basic tests while getting everything working again. This went really well so now Ι can show the following.

Let’s take this code. It’s useless but you can see that we make two vectors (that’s the v! forms) each starting with x

(defshader test ()
  (let ((x 1))
    (v! x 0)
    (progn
      (let ((z 3))
        x))
    (v! x 0 0 0)))

Now, if I let the flow analyzer do it’s work and inspect the data for the v! calls, I get something like.

(V! 514 515)
(V! 514 518 519 520)

The numbers are called flow-ids, they are used to track the flow of a value through a program. If two are the same then they are the same value (even if they get bound to different variables or passed through functions).

So above we can see that 514 appears as the id of the first arg for both v! forms, that means it’s not just the same variable (x) but the same value too.

Now in the following example we set x to be a different value (it’s in a progn and a let so I could see if it would track the flow of execution properly).

(defshader test ()
  (let ((x 1))
    (v! x 0)
    (progn
      (let ((z 3))
        (setq x z)))
    (v! x 0 0 0)))

The flow analysis of the v! calls in this program look like this

(V! 506 507)
(V! 509 510 511 522)

This time the flow-ids of the first argument are different. This shows that, even though they both take x, the system knows the value has been changed.

With these fixes Ι can finally handle conditions as the trees of environments are trivial to diff, so Ι will be able to combine the flow-ids from the two execution paths.

Thanks for reading!

(setf mayhem damnit)

2015-11-10 20:54:27 +0000

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

2015-11-09 20:47:25 +0000

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.

  1. 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
  1. chmod +x inv.sh

  2. 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

2015-11-09 00:20:13 +0000

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

2015-11-09 00:06:19 +0000

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:

CLHS: make-load-form

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

2015-11-07 20:06:42 +0000

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

2015-11-07 16:19:59 +0000

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!

Mastodon