{"id":438,"date":"2013-09-11T16:15:55","date_gmt":"2013-09-11T21:15:55","guid":{"rendered":"http:\/\/www.warsam.com\/?page_id=438"},"modified":"2013-09-17T14:40:27","modified_gmt":"2013-09-17T19:40:27","slug":"concurrent-renderer","status":"publish","type":"page","link":"https:\/\/www.warsam.com\/?page_id=438","title":{"rendered":"Concurrent Renderer"},"content":{"rendered":"<p>Using\u00a0<span class=\"bh\">functional decomposition<\/span>,\u00a0rendering in my engine is performed in a <span class=\"bh\">dedicated rendering thread<\/span>. 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 <span class=\"bh\">IRenderable interface<\/span>. The interface provides three abstract functions to create, render and destroy rendering data that any pre-existing class would simply need to implement.<\/p>\n<p>The Renderer thread, which owns the OpenGL context, then simply processes all opaque IRenderable objects <span class=\"bh\">eliminating the complexity of OpenGL context switches<\/span>\u00a0with <span class=\"bh\">minimal modifications to existing classes<\/span>.<\/p>\n<p>The provided source code below has been simplified and stripped down to better illustrate the aforementioned.<\/p>\n<h2>Source Code<\/h2>\n<div id=\"squelch-taas-tab-group-0\" class=\"squelch-taas-tab-group squelch-taas-override\" data-title=\"\" data-disabled=\"false\" data-collapsible=\"true\" data-active=\"0\" data-event=\"click\"><ul><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-0\">IRendererable.h<\/a><\/li><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-1\">Renderer.h<\/a><\/li><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-2\">Renderer.cpp<\/a><\/li><\/ul><div id=\"squelch-taas-tab-content-0-0\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n\/*<br \/>\nfile    IRendererable.h<br \/>\ndate    05\/01\/2013<br \/>\nauthor  Warsam Osman<br \/>\nbrief   Renderable Interface<br \/>\n*\/<br \/>\n#pragma once<\/p>\n<p>#ifndef _IRENDERABLE_<br \/>\n#define _IRENDERABLE_<\/p>\n<p>#include &#8220;Mutex.h&#8221;<\/p>\n<p>namespace core<br \/>\n{<br \/>\n    class IRenderable : public Mutex<br \/>\n    {<br \/>\n    public:<br \/>\n        IRenderable() {}<br \/>\n        virtual ~IRenderable() {}<\/p>\n<p>        virtual void IRenderCreate() = 0;<br \/>\n        virtual void Render() = 0;<br \/>\n        virtual void IRenderDestroy() = 0;<br \/>\n    };<br \/>\n}<\/p>\n<p>#endif  \/\/_IRENDERABLE_<\/p>\n<p>[\/crayon]<\/div><div id=\"squelch-taas-tab-content-0-1\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n\/*<br \/>\nfile    Renderer.h<br \/>\ndate    01\/18\/2013<br \/>\nauthor  Warsam Osman<br \/>\nbrief   Renderer class<br \/>\n*\/<br \/>\n#pragma  once<\/p>\n<p>#ifndef _RENDERER_<br \/>\n#define _RENDERER_<\/p>\n<p>#include &#8220;..\/Core\/Thread.h&#8221;<\/p>\n<p>namespace renderer<br \/>\n{<br \/>\n    class IRenderable;<\/p>\n<p>    \/\/Stripped down Renderer for clarity.<\/p>\n<p>    class Renderer : public Thread<br \/>\n    {<br \/>\n    public:<br \/>\n        Renderer();<br \/>\n        virtual ~Renderer();<\/p>\n<p>        void Init( HWND hwnd );<br \/>\n        void ShutDown();<br \/>\n        void RegisterRenderable( IRenderable* renderableObject );<br \/>\n        void CreateRenderables();<br \/>\n        void DestroyRenderables();<br \/>\n        void RequestDraw();<\/p>\n<p>    protected:<br \/>\n        HGLRC       RenderingContext;<br \/>\n        HDC         HDCWindow;<br \/>\n        HWND        HWnd;<br \/>\n        LastError   err;<\/p>\n<p>    private:<br \/>\n        void InitializeOpenGL();<br \/>\n        void ShutdownOpenGL();<\/p>\n<p>        \/\/Virtual Rendering Thread Function<br \/>\n        void VThreadProc();<\/p>\n<p>        CONDITION_VARIABLE              RenderingThreadDone;<br \/>\n        std::vector< IRenderable* >     Renderables;<br \/>\n        Mutex                           RendererLock;<br \/>\n        bool                            bWindowResizing;<br \/>\n        bool                            bRendering;<br \/>\n        bool                            bShutDownRendererThread;<\/p>\n<p>        PREVENT_COPYING( Renderer )<br \/>\n    };<\/p>\n<p>    extern Renderer* g_pRenderer;<br \/>\n}<br \/>\n#endif \/\/_RENDERER_<\/p>\n<p>[\/crayon]<\/div><div id=\"squelch-taas-tab-content-0-2\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n#include &#8220;stdafx.h&#8221;<br \/>\n#include &#8220;Renderer.h&#8221;<\/p>\n<p>namespace renderer<br \/>\n{<br \/>\n    Renderer* g_pRenderer = nullptr;<\/p>\n<p>    Renderer::Renderer()<br \/>\n        :m_hWnd( nullptr )<br \/>\n        ,bRendering( false )<br \/>\n        ,bShutDownRendererThread( false )<br \/>\n        ,bWindowResizing( false )<br \/>\n    {<br \/>\n        assert( !g_pRenderer );<br \/>\n        g_pRenderer = this;<br \/>\n    }<\/p>\n<p>    Renderer::~Renderer()<br \/>\n    {<br \/>\n        Thread::join(nullptr);<br \/>\n    }<\/p>\n<p>    void Renderer::Init( HWND hwnd )<br \/>\n    {<br \/>\n        HWnd = hwnd;<br \/>\n        assert( HWnd );<\/p>\n<p>        InitializeConditionVariable(&#038;RenderingThreadDone);<\/p>\n<p>        \/\/Start thread<br \/>\n        Thread::VInit( Thread::normal );<br \/>\n        Thread::start();<\/p>\n<p>        \/\/Wait for Thread to finish init OpenGL<br \/>\n        if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )<br \/>\n            err.ShowLastError( LPTSTR(L&#8221;Renderer::Init SleepConditionVar error!&#8221;) );<\/p>\n<p>    }<\/p>\n<p>    void Renderer::ShutDown()<br \/>\n    {<br \/>\n        RendererLock.Lock();<br \/>\n        if( bShutDownRendererThread ) return;<br \/>\n        while( bRendering || bWindowResizing )<br \/>\n        {<br \/>\n            MutexUnlock unlock(RendererLock);<br \/>\n            if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )<br \/>\n                err.ShowLastError( LPTSTR(L&#8221;Renderer::OnWindowResized SleepConditionVar error!&#8221;) );<br \/>\n        }<br \/>\n        std::cout <<  \"Requesting thread shut down \\n\";\n        bShutDownRendererThread = true;\n        RendererLock.Unlock();\n\n        \/\/Wait for Thread to finish shutting down OpenGL\n        if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )\n            err.ShowLastError( LPTSTR(L\"Renderer::ShutDown SleepConditionVar error!\") );\n    }\n    \n    void Renderer::OnWindowResized()\n    {       \n        RendererLock.Lock();\n\n        while( bRendering || bWindowResizing )\n        {\n            \/\/MutexUnlock unlock(RendererLock);\n            RendererLock.Unlock();\n            if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )\n                err.ShowLastError( LPTSTR(L\"Renderer::OnWindowResized SleepConditionVar error!\") );\n        }\n        \n        std::cout <<  \"Requesting window resize \\n\";\n        bWindowResizing = true;     \n        RendererLock.Unlock();\n        \n        \/\/Wait for Thread to finish resize window\n        if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )\n            err.ShowLastError( LPTSTR(L\"Renderer::OnWindowResized SleepConditionVar error!\") );\n        \n    }\n}\n\n&#091;\/crayon&#093;\n&#091;\/tab&#093;\n&#091;tab title=\"RenderThread.cpp\"&#093;\n&#091;crayon lang=\"cpp\"&#093;\n#include \"stdafx.h\"\n#include \"Renderer.h\"\n#include \"..\\Core\\IRenderable.h\"    \n\nnamespace renderer\n{\n    void Renderer::VThreadProc( void )\n    {\n        std::cout <<  \"Initializing OpenGL... \\n\";\n        InitializeOpenGL();\n\n        \/\/Set up initial render state\n        ResizeWindow();\n\n        \/\/Initialize RenderableObjects\n        CreateRenderables();\n\n        \/\/Finished init - wake main thread\n        WakeConditionVariable (&#038;RenderingThreadDone);\n\n        while( true )\n        {\n            RendererLock.Lock();\n\n            \/\/Shut down?\n            if( bShutDownRendererThread ) \n            {\n                std::cout <<  \"Shutting down rendering thread... \\n\";\n                DestroyRenderables();\n\n                ShutdownOpenGL();\n                \n                RendererLock.Unlock();\n\n                WakeConditionVariable (&#038;RenderingThreadDone);   \n    \n                break;\n            }\n\n            \/\/Resize window?\n            else if( bWindowResizing )\n            {\n                std::cout <<  \"Resizing window in thread... \\n\";\n                bWindowResizing = false;\n                RendererLock.Unlock();\n\n                ResizeWindow();\n            }\n\n            \/\/Render?\n            else if( bRendering )\n            {\n                bRendering = false;\n                RendererLock.Unlock();\n\n                \/\/Render\n                PreRender();\n                Render(); \n                PostRender();\n            }   \n            else\n                RendererLock.Unlock();\n            \n            WakeConditionVariable (&#038;RenderingThreadDone);\n        }\n        return;\n    }\n\n    void Renderer::PreRender()\n    {\n        ClearBufferToColor( RENDERER_CLEAR_COLOR );\n        PushMatrix( GetModelView3() );\n    }\n\n    void Renderer::Render() const\n    {\n        for( auto iter = m_renderableObjectList.cbegin(); iter != m_renderableObjectList.cend(); ++iter )\n            (*iter)->Render();<br \/>\n    }<\/p>\n<p>    void Renderer::PostRender()<br \/>\n    {<br \/>\n        DrawFrame();<br \/>\n        PopMatrix();<br \/>\n    }<\/p>\n<p>    void Renderer::RegisterRenderable( IRenderable* renderableObject )<br \/>\n    {<br \/>\n        MutexLock scopedLock( RendererLock );<br \/>\n        m_renderableObjectList.push_back( renderableObject );<br \/>\n    }<\/p>\n<p>    void Renderer::CreateRenderables()<br \/>\n    {<br \/>\n        for( auto iter = m_renderableObjectList.begin(); iter != m_renderableObjectList.end(); ++iter )<br \/>\n            (*iter)->IRenderCreate();<br \/>\n    }<\/p>\n<p>    void Renderer::DestroyRenderables()<br \/>\n    {<br \/>\n        for( auto iter = m_renderableObjectList.begin(); iter != m_renderableObjectList.end(); ++iter )<br \/>\n            (*iter)->IRenderDestroy();<br \/>\n    }<\/p>\n<p>    void Renderer::RequestDraw()<br \/>\n    {<br \/>\n        RendererLock.Lock();<\/p>\n<p>        \/\/Sync threads<br \/>\n        \/\/ |Simulate&#8230; wait |Simulate&#8230; wait |<br \/>\n        \/\/ |Render&#8230;&#8230;&#8230;..|Render&#8230;&#8230;&#8230;..|<br \/>\n        while( bRendering || bWindowResizing )<br \/>\n        {<br \/>\n            RendererLock.Unlock();<br \/>\n            if( SleepConditionVariableCS (&#038;RenderingThreadDone, &#038;RendererLock, INFINITE) == 0 )<br \/>\n                err.ShowLastError( LPTSTR(&#8220;Renderer::RequestDraw SleepConditionVar error!&#8221;) );<br \/>\n        }<\/p>\n<p>        bRendering = true;<\/p>\n<p>        RendererLock.Unlock();<br \/>\n    }<br \/>\n}[\/crayon]<\/div><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Using\u00a0functional decomposition,\u00a0rendering 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<span class=\"ellipsis\">&hellip;<\/span><\/p>\n<div class=\"read-more\"><a href=\"https:\/\/www.warsam.com\/?page_id=438\">Read more &#8250;<\/a><\/div>\n<p><!-- end of .read-more --><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"full-width-page.php","meta":[],"_links":{"self":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/438"}],"collection":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.warsam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=438"}],"version-history":[{"count":29,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/438\/revisions"}],"predecessor-version":[{"id":630,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/438\/revisions\/630"}],"wp:attachment":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}