Designing a Functional Rendering Pipeline

Well, it’s been ages since I last blogged anything. I feel ashamed… It’s been quite busy on my end. I’ve had plenty of projects to work on.

There’s a bright side to all this – I now have two more languages solidly under my belt! The first is C++, which is something I’ve only had really basic knowledge on up until now. I’ve been neglecting it mostly because it sucks, but it’s become a lot easier to stand now that the new version is finalized. Glory be to lambdas on high!

The second new language of mine is Haskell. I haven’t quite got the hang of its type system (probably because I’m attempting OOP idioms where I really shouldn’t), but other than that… I am in love.

Given the post title, I may have your hopes up – “perhaps he’s going to introduce some fancy new rendering library for Haskell”! Unfortunately, the answer is no. I will be incorporating concepts that I have obtained from the functional paradigm, but I will remain with C#. Haskell may be my first true love, but C# is still my wife. Until I file a divorce, I guess I’ll have to consider Haskell my mistress. Just don’t say anything to C#…

…moving on…

Scratch that, let’s go back. Remember the four main functions of a computer? Input, Output, Processing, and Storage. In functional languages (pure ones), functions simply take in input, transform it, and then return the output, producing no side effects. Given that, we can ignore storage for now.

So how do we model a rendering pipeline in a purely functional way? How do we model input/output? The output is pretty obvious – it’s the image on your screen. How about the input? First we need a target (say, a window handle). What about what we’re actually going to draw on the target? I suppose you could throw in some sort of data structure describing your entire scene (e.g. a scene graph), but let’s go a little lower level than that. How about a list of commands? In the processing stage – which we’ll wrap and call a renderer (fancy that eh) – we take that list of commands, execute them on the desired target buffer, and then output the result. Ex:

Input à Clear(CornflowerBlue) à DrawTexture(sunTexture, [300, 425]) à Renderer à Output

But wait – what if each ‘command’ was a sort of ‘mini-renderer’ on its own? Then we could simply do:

Input à Clear(CornflowerBlue) à DrawTexture(sunTexture, [300, 425]) à Output

Now, the combined effect of both the Clear and DrawTexture commands can be considered our renderer. This is actually directly inspired by the Parser monad I happened across while working in Haskell. I should probably write a post about it someday…

…Ok, so how about some code?

public static ITarget Renderer(ITarget target);

This is straightforward. Take in a target, apply an operation, and then return the target. How about combining them?

public static Renderer Combine(Renderer first, Renderer second) 
{
	 return n => second(first(n));
}

This function applies the first renderer to the target, then the second. Even better, we could turn this into an extension method:

public static Renderer Then(this Renderer renderer, Renderer next)
{
	return n => next(renderer(n));
}

This makes it really easy to chain together commands. This chaining can lead to some other interesting functions. I took this off the top of my head:

public static Renderer Repeat(this Renderer renderer, int count)
{
	return count == 1 ? renderer : renderer.Then(renderer.Repeat(count - 1));
}

This could be useful for, say, a multi-pass blur. You could define a Blur function that returns blurCommand.Repeat(strength) or something…

I’ll stop there for now. This seems like a really neat idea, with an interesting amount of possibilities. Perhaps I can get something up and running in the near future.

Until then,
YellPika

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s