One of the first things that truly broadened my horizons on how amazing software development could be, was when I saw my first “Demo”. A demo is simply a non-interactive real-time display of a visualization effect. Some of the most classic (and simple) examples are the Starfield, Plasma, Fire and Water. The real appeal of these first demos was that developers, on basically identical hardware, could really display their algorithm optimization talents – some going as far as exploiting hardware defects to achieve faster rendering routines.
History of the Demo
Traditionally, demos were generally written in pure Assembly language due to the lack of overhead generated by higher level languages such as C, Pascal or Basic. However, as computer hardware bandwidth increased coupled with the realization that in a demo essentially all the computation is inside the main render routine – a lot of demo developers started using higher level languages to create a basic “framework” for common tasks, and then just hand optimized the render routine with inline Assembly to maintain maximum computation efficiency.
As computer programming has evolved over the years, most developers generally file away Assembly, or even C, in the “things you should be aware of, but don’t really need to know” category – which is a shame.
However, I can’t argue the extraordinary benefits that high-level object-oriented languages provide for modern software development. Modularity, abstraction, encapsulation and inheritance are extremely useful concepts that just make life as a software developer easier.
Applying the aforementioned concepts to demo development, I decided to write a very simple real-time rendering framework in C#. The framework is composed of the essential parts for real-time rendering in a Windows environment: A container (form), a display surface (client area) and a timing device (frame-rate controller).
These three separate parts are all encapsulated by an abstract base class called “RenderingCore”. This class implements two interfaces, each used to apply an essential design pattern to our rendering engine.
- Disposable – This pattern is essential in real-time rendering and is implemented through the IDisposable interface. When dealing with graphics development, we’re naturally going to have unmanaged resources allocated (at a minimum a back-buffer for page flipping) and we don’t want to instantiate and release these resources each frame due to the enormous performance penalty – only when a “device changed” event is triggered (ResetDevice in RenderingCore). Since this class is abstract, we need to implement the full disposing pattern here to ensure that inheriting classes will get their unmanaged resources deallocated correctly.
- Observer – This pattern is used to notify the engine when the windows form is able to render a new frame – this allows the engine to invoke the main render routine and dispatch the frame controller to manage and calculate the frame rate. The rendering form exposes a method “BeginProcessing” which takes a single argument, IRenderCallback. The form attaches itself to the “Application.Idle” static event, which is invoked when the application thread is not processing. Inside the “OnApplicationIdle” routine we check if there are any messages in the processing queue (using the native method, PeekMessage). If there are no messages waiting to be processed, we call the IRenderCallback.Render routine to invoke our engine processing. Otherwise, our rendering routine would cause thread starvation and the form wouldn’t be able to respond to normal windows events, i.e. cause the window to become unresponsive.
The Container is a standard C# Windows Form that, as previously stated, essentially runs in a continuous loop achieved by hooking into the “Application.OnIdle” event dispatching callbacks to then engine when the form is able to render a frame.
The container form also has some default constructor arguments for setting the width, height and form icon. The only required argument to instantiate a container form is the application name – which is also used as the window title.
The form also hooks into keyboard and mouse events in order to implement a couple of “nice-to-have” features. If you click on the client area of the form and hold the mouse button down you can drag the form across the screen – this is achieved by handling the native window message “WM_NCHITTEST” and essentially tricks windows into thinking the title bar was clicked when you click the client area, thus allowing form dragging.
The timing device used in RenderingCore is called the “FrameController” – although this class is used to both limited and calculate the frames rendered per second.
The static constructor for this class invokes the native method “QueryPerformanceFrequency” which returns the amount the counter will increment over a 1 second interval, we store this value in a static “long” (signed 64-bit integer) variable to use as our base-line.
There are two routines that are invoked in this class from the core rendering routine – “PreRender” and “PostRender”. These two routines simply call “QueryPerformanceCounter” to get the current value of the counter, calculating the delta of these two values (pre and post render) will yield the amount of ticks spent rendering the current frame.
Once we have the ticks spend rendering the current frame we can compare it to the “desired” time (CPU Frequency / FrameLimit) we want to spend rendering a frame – if the current frame was rendered too quickly we go into an “infinite” loop that invokes “Thread.Sleep” with an argument value of 1 or 0 depending on the sleep threshold.
We do this because if we know we’re going to be waiting more than say, 10ms before the next frame should be rendered and we’re in a loop calling “Thread.Sleep(0)” repeatedly, it’s simply going to give up our threads timeslice to another waiting thread – so our CPU usage will still be 100% wasting CPU bandwidth (and battery life). Doing this allows us to control our frame rate both accurately and efficiently. The loop will “break-out” of itself once it has waited the desired amount of time.
RenderingCore attaches itself to the form’s KeyPress event allowing you to control the frame controller with the keyboard – ‘+’ and ‘-‘ will increment / decrement the allowed frame rate and ‘Enter’ will enable / disable the controller.
The Render Routine
The final points of interest in RenderingCore are the three protected abstract virtual methods – “Render”, “CreateDevice” and “ResetDevice”.These methods are abstract so any deriving classes are required to implement them.
However, you’ll notice that these functions takes no arguments nor returns any value – this is because they’re essentially used as callbacks to your concrete implementation in which you’re expected to prepare your “display surface” and any other required resources internally, then expose a public render delegate that is invoked every frame to draw your scene.
Sample Application and Examples
This article was originally written to be an independent post, however it has since become a sort of documentation for how my rendering framework project works. You’re more than welcome to go there and download the full project which includes the framework and some sample effects.