Handling different render setups

This video shows the latest progress of my graphics engine. It demonstrates two different rendering setups- one uses light pre-pass rendering and the other uses deferred rendering. The LPP renderer has a bloom filter added to it (exaggerated for this video) and the deferred renderer has a soft focus depth of field. Without these post-processing effects, a light pre-pass and deferred setup should produce the exact same image if done correctly. Remember to watch it in 720p!

Each setup is contained in a separate class that extends from an abstract RenderProfile class. The goal of having this is so you can easily provide a set of different graphical configurations with different post-effects, or different rendering styles such as forward rendering vs. deferred rendering. I plan to really expand on this idea, as it will be one of the core features for the engine.

Loading and running each setup

The base class has a RenderFrame function that is called once every game loop. When a different rendering setup is requested, it disposes all the contents of every rendering step from the current setup (render targets, meshes, etc.) and replaces the RenderProfile with a new one. Right now, rendering setups are toggled with the press of a key.

// Toggle between deferred and light pre-pass rendering
if (currentKeyboardState.IsKeyDown(Keys.P) &&
    lastKeyboardState.IsKeyUp(Keys.P))
{
    renderMethod = 1 - renderMethod;

    if (currentRenderProfile != null)
	currentRenderProfile.DisposeRenderers();

    if (renderMethod == RenderMethod.deferred)
    {
        currentRenderProfile = new DeferredRenderer(game, this);
    }
    else
    {
        currentRenderProfile = new LightPrePassRenderer(game, this);
    }
}

This runs in the Update function of my engine’s renderer class. It’s a crude way to handle switching between profiles, but that’s because it’s only used for debugging right now.

Each RenderProfile-based object has a list of RenderTask objects, which are self-contained steps towards rendering the final image. For example, the GBuffer pass and lighting pass are different RenderTasks. And each of these contains at least one render target, created when the entire render profile loads. Once a RenderTask is run for the frame, its output render targets may be used by others.

Since we’re creating a new RenderProfile when it’s time to change, the program can waste lots of memory quickly unless everything in it is cleaned up properly. I found out this pretty quickly as I was frequently changing rendering setups. When the rendering engine is ready to “eject” a RenderProfile in order to use a new one, it calls DisposeRenderers on it. For each RenderTask, a method is attached to the Disposed event that will also Dispose any render targets:

// Class that extends RenderProfile
public void DisposeRenderers()
{
    foreach (RenderTask renderer in renderers)
    {
        renderer.Dispose();
    }
}
// Class that extends RenderTask
protected virtual void RenderTask_Disposed(Object sender, EventArgs e)
{
    foreach (RenderTargetInput input in renderInputs.Values)
    {
        input.target.Dispose();
    }
}

Basic stuff, but since each setup extends from RenderProfile, this method can be overridden to do different things when the RenderTask is disposed. You can return the data size for the contents freed, for example. The lighting pass has a vertex buffer to manage point lights, which should be disposed as well, so this can be done along with the base method.

protected override void SceneRenderer_Disposed(Object sender, EventArgs e)
{
    if (instanceVertexBuffer != null)
	instanceVertexBuffer.Dispose();

    base.SceneRenderer_Disposed(sender, e);
}

Now all associated memory is cleared and we are set to use a new RenderProfile.

A basic GUI drop down menu is already in place so I can click to view different render targets (which I will show later), and that’s where I’ll be moving the RenderProfile switcher as well.

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