Last week was pretty much dominated by being in Denmark for TRSAC (which was awesome) so I thought I'd skip everyweeks. However 2 days later I felt bad so here it is, super late everyweeks.
I have two very active users right now. One in particular is being a badass about both fixing up my terrible spelling & grammar and also being very vocal about places where CEPL is kinda painful to use.
Up until now my gpu-functions and pipelines have been kinda static, in that you define some gpu-functions
(defun-g some-vetex-function ((x :vec4)) .. shader code here ..) (defun-g some-fragment-function ((uv :vec2) &uniforms (s :texture-2d)) .. shader code here ..)
And then compose them like this:
(def-g-> a-pipeline () :vertex some-vetex-function :fragment some-fragment-function)
This ^^ uses the
some-vetex-function as the vertex shader and
some-fragment-function as the fragment shader.
This is sweet, and the fact that in lisp you can recompile anything at any time this feels pretty good. But here pipelines feel like regular functions, something that must be complete at compile time. What other cases are there?
Well in programming we like to compose functions at runtime. So say you has a function called
split that took a string and split it in two wherever there was a space. So:
split("foo bar baz") -> ["foo", "bar", "baz"]
And we also have a function called
length that gives you the length of a list.
length(["foo", "bar", "baz"]) -> 3
We can compose them at runtime to get a new function that counts the words in a string.
var f = compose(split, length); // f now holds a lambda f("foo bar baz") -> 3
Pretty cool! This is really useful for building up pipelines for your data based on things you only know at runtime, or just to avoid writing specific functions when you could just combine the ones you have.
Naturally this makes as much sense for graphics as it does other places so I want it!
The first part was very simple, make an object that will hold the code for a gpu-lambda. Code is just a bunch of lists and data in lisp so we just take those lists and shove 'em in the field of an object.
(glambda ((vert :vec4)) (* vert 2)) -> #<gpu-lambda object>
Noice. Now we need a way to take a few and compose them. I already have
def-g-> for making named pipelines from gpu-functions, so the lambda version will just be
g-> (g-> is called g-pipe and the name is stolen from haskell)
(defvar a-new-pipeline (g-> some-vetex-function some-fragment-function)) ;; a-new-pipeline now contains our pipeline (map-g a-new-pipeline some-vertex-stream :s some-sampler) ;; and now we are rendering!
So yeah the above naturally took a bit of work, but I had all TRSAC to do it and it turned out to be easy as I was mainly re-purposing things I already had.
The next natural step is to be able to have first class functions inside our shaders. This is a MUCH harder task but luckily some of the things I need are already part of my compiler so it should work. This will be a writeup for another week.
Peace all, Baggers
which reminds me that I'm supposed to post on Mondays, not Tuesdays...
on Wed Oct 26 2016 04:26:33 GMT-0400 (EDT)