Using functional decomposition, rendering in my engine is performed in a dedicated rendering thread. Starting with a robust Renderer class my goal was to offload rendering to a separate thread with minimum modification to the engine. I came up with the idea of creating a simple abstract IRenderable interface. The interface provides three abstract functions to create, render and destroy rendering data that any pre-existing class would simply need to implement.
The Renderer thread, which owns the OpenGL context, then simply processes all opaque IRenderable objects eliminating the complexity of OpenGL context switches with minimal modifications to existing classes.
The provided source code below has been simplified and stripped down to better illustrate the aforementioned.
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* file IRendererable.h date 05/01/2013 author Warsam Osman brief Renderable Interface */ #pragma once #ifndef _IRENDERABLE_ #define _IRENDERABLE_ #include "Mutex.h" namespace core { class IRenderable : public Mutex { public: IRenderable() {} virtual ~IRenderable() {} virtual void IRenderCreate() = 0; virtual void Render() = 0; virtual void IRenderDestroy() = 0; }; } #endif //_IRENDERABLE_ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
/* file Renderer.h date 01/18/2013 author Warsam Osman brief Renderer class */ #pragma once #ifndef _RENDERER_ #define _RENDERER_ #include "../Core/Thread.h" namespace renderer { class IRenderable; //Stripped down Renderer for clarity. class Renderer : public Thread { public: Renderer(); virtual ~Renderer(); void Init( HWND hwnd ); void ShutDown(); void RegisterRenderable( IRenderable* renderableObject ); void CreateRenderables(); void DestroyRenderables(); void RequestDraw(); protected: HGLRC RenderingContext; HDC HDCWindow; HWND HWnd; LastError err; private: void InitializeOpenGL(); void ShutdownOpenGL(); //Virtual Rendering Thread Function void VThreadProc(); CONDITION_VARIABLE RenderingThreadDone; std::vector< IRenderable* > Renderables; Mutex RendererLock; bool bWindowResizing; bool bRendering; bool bShutDownRendererThread; PREVENT_COPYING( Renderer ) }; extern Renderer* g_pRenderer; } #endif //_RENDERER_ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#include "stdafx.h" #include "Renderer.h" namespace renderer { Renderer* g_pRenderer = nullptr; Renderer::Renderer() :m_hWnd( nullptr ) ,bRendering( false ) ,bShutDownRendererThread( false ) ,bWindowResizing( false ) { assert( !g_pRenderer ); g_pRenderer = this; } Renderer::~Renderer() { Thread::join(nullptr); } void Renderer::Init( HWND hwnd ) { HWnd = hwnd; assert( HWnd ); InitializeConditionVariable(&RenderingThreadDone); //Start thread Thread::VInit( Thread::normal ); Thread::start(); //Wait for Thread to finish init OpenGL if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR(L"Renderer::Init SleepConditionVar error!") ); } void Renderer::ShutDown() { RendererLock.Lock(); if( bShutDownRendererThread ) return; while( bRendering || bWindowResizing ) { MutexUnlock unlock(RendererLock); if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR(L"Renderer::OnWindowResized SleepConditionVar error!") ); } std::cout << "Requesting thread shut down \n"; bShutDownRendererThread = true; RendererLock.Unlock(); //Wait for Thread to finish shutting down OpenGL if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR(L"Renderer::ShutDown SleepConditionVar error!") ); } void Renderer::OnWindowResized() { RendererLock.Lock(); while( bRendering || bWindowResizing ) { //MutexUnlock unlock(RendererLock); RendererLock.Unlock(); if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR(L"Renderer::OnWindowResized SleepConditionVar error!") ); } std::cout << "Requesting window resize \n"; bWindowResizing = true; RendererLock.Unlock(); //Wait for Thread to finish resize window if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR(L"Renderer::OnWindowResized SleepConditionVar error!") ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
#include "stdafx.h" #include "Renderer.h" #include "..\Core\IRenderable.h" namespace renderer { void Renderer::VThreadProc( void ) { std::cout << "Initializing OpenGL... \n"; InitializeOpenGL(); //Set up initial render state ResizeWindow(); //Initialize RenderableObjects CreateRenderables(); //Finished init - wake main thread WakeConditionVariable (&RenderingThreadDone); while( true ) { RendererLock.Lock(); //Shut down? if( bShutDownRendererThread ) { std::cout << "Shutting down rendering thread... \n"; DestroyRenderables(); ShutdownOpenGL(); RendererLock.Unlock(); WakeConditionVariable (&RenderingThreadDone); break; } //Resize window? else if( bWindowResizing ) { std::cout << "Resizing window in thread... \n"; bWindowResizing = false; RendererLock.Unlock(); ResizeWindow(); } //Render? else if( bRendering ) { bRendering = false; RendererLock.Unlock(); //Render PreRender(); Render(); PostRender(); } else RendererLock.Unlock(); WakeConditionVariable (&RenderingThreadDone); } return; } void Renderer::PreRender() { ClearBufferToColor( RENDERER_CLEAR_COLOR ); PushMatrix( GetModelView3() ); } void Renderer::Render() const { for( auto iter = m_renderableObjectList.cbegin(); iter != m_renderableObjectList.cend(); ++iter ) (*iter)->Render(); } void Renderer::PostRender() { DrawFrame(); PopMatrix(); } void Renderer::RegisterRenderable( IRenderable* renderableObject ) { MutexLock scopedLock( RendererLock ); m_renderableObjectList.push_back( renderableObject ); } void Renderer::CreateRenderables() { for( auto iter = m_renderableObjectList.begin(); iter != m_renderableObjectList.end(); ++iter ) (*iter)->IRenderCreate(); } void Renderer::DestroyRenderables() { for( auto iter = m_renderableObjectList.begin(); iter != m_renderableObjectList.end(); ++iter ) (*iter)->IRenderDestroy(); } void Renderer::RequestDraw() { RendererLock.Lock(); //Sync threads // |Simulate... wait |Simulate... wait | // |Render...........|Render...........| while( bRendering || bWindowResizing ) { RendererLock.Unlock(); if( SleepConditionVariableCS (&RenderingThreadDone, &RendererLock, INFINITE) == 0 ) err.ShowLastError( LPTSTR("Renderer::RequestDraw SleepConditionVar error!") ); } bRendering = true; RendererLock.Unlock(); } } |