{"action":"created","comment":{"url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/comments/183172932","pull_request_review_id":114123068,"id":183172932,"diff_hunk":"@@ -66,955 +66,963 @@ namespace {\n } // namespace\n \n namespace openspace {\n-void saveTextureToPPMFile(const GLenum color_buffer_attachment,\n-    const std::string & fileName,\n-    const int width, const int height);\n-\n-void saveTextureToMemory(const GLenum color_buffer_attachment,\n-    const int width, const int height, std::vector<double> & memory);\n-\n-\n-FramebufferRenderer::FramebufferRenderer()\n-    : _camera(nullptr)\n-    , _scene(nullptr)\n-    , _resolution(glm::vec2(0))\n-    , _hdrExposure(0.4f)\n-    , _hdrBackground(2.8f)\n-    , _gamma(2.2f)\n-{}\n-\n-FramebufferRenderer::~FramebufferRenderer() {}\n-\n-void FramebufferRenderer::initialize() {\n-    LDEBUG(\"Initializing FramebufferRenderer\");\n-\n-    const GLfloat size = 1.0f;\n-    const GLfloat vertex_data[] = {\n-        //      x      y     s     t\n-        -size, -size, 0.0f, 1.0f,\n-        size,    size, 0.0f, 1.0f,\n-        -size,  size, 0.0f, 1.0f,\n-        -size, -size, 0.0f, 1.0f,\n-        size, -size, 0.0f, 1.0f,\n-        size,    size, 0.0f, 1.0f\n-    };\n-\n-    glGenVertexArrays(1, &_screenQuad);\n-    glBindVertexArray(_screenQuad);\n-\n-    glGenBuffers(1, &_vertexPositionBuffer);\n-    glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);\n-\n-    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);\n-    glVertexAttribPointer(\n-        0,\n-        4,\n-        GL_FLOAT,\n-        GL_FALSE,\n-        sizeof(GLfloat) * 4,\n-        nullptr\n-    );\n-    glEnableVertexAttribArray(0);\n-\n-    GLint defaultFbo;\n-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n-\n-    // Main framebuffer\n-    glGenTextures(1, &_mainColorTexture);\n-    glGenTextures(1, &_mainDepthTexture);\n-    glGenFramebuffers(1, &_mainFramebuffer);\n-\n-    // Exit framebuffer\n-    glGenTextures(1, &_exitColorTexture);\n-    glGenTextures(1, &_exitDepthTexture);\n-    glGenFramebuffers(1, &_exitFramebuffer);\n-\n-    // Deferred framebuffer\n-    glGenTextures(1, &_deferredColorTexture);\n-    glGenTextures(1, &_mainPositionTexture);\n-    glGenTextures(1, &_mainNormalTexture);\n-    glGenFramebuffers(1, &_deferredFramebuffer);\n-\n-    updateResolution();\n-    updateRendererData();\n-    updateRaycastData();\n-\n-    glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer);\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT0,\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _mainColorTexture,\n-        0\n-    );\n-    // G-buffer\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT1,\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _mainPositionTexture,\n-        0\n-    );\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT2,\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _mainNormalTexture,\n-        0\n-    );\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_DEPTH_ATTACHMENT,\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _mainDepthTexture,\n-        0\n-    );\n-\n-    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n-    if (status != GL_FRAMEBUFFER_COMPLETE) {\n-        LERROR(\"Main framebuffer is not complete\");\n-    }\n+    void saveTextureToPPMFile(const GLenum color_buffer_attachment,\n+        const std::string & fileName,\n+        const int width, const int height);\n+\n+    void saveTextureToMemory(const GLenum color_buffer_attachment,\n+        const int width, const int height, std::vector<double> & memory);\n+\n+\n+    FramebufferRenderer::FramebufferRenderer()\n+        : _camera(nullptr)\n+        , _scene(nullptr)\n+        , _resolution(glm::vec2(0))\n+        , _hdrExposure(0.4f)\n+        , _hdrBackground(2.8f)\n+        , _gamma(2.2f)\n+    {}\n+\n+    FramebufferRenderer::~FramebufferRenderer() {}\n+\n+    void FramebufferRenderer::initialize() {\n+        LDEBUG(\"Initializing FramebufferRenderer\");\n+\n+        const GLfloat size = 1.0f;\n+        const GLfloat vertex_data[] = {\n+            //      x      y     s     t\n+            -size, -size, 0.0f, 1.0f,\n+            size,    size, 0.0f, 1.0f,\n+            -size,  size, 0.0f, 1.0f,\n+            -size, -size, 0.0f, 1.0f,\n+            size, -size, 0.0f, 1.0f,\n+            size,    size, 0.0f, 1.0f\n+        };\n+\n+        glGenVertexArrays(1, &_screenQuad);\n+        glBindVertexArray(_screenQuad);\n \n-    glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer);\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT0,\n-        GL_TEXTURE_2D,\n-        _exitColorTexture,\n-        0\n-    );\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_DEPTH_ATTACHMENT,\n-        GL_TEXTURE_2D,\n-        _exitDepthTexture,\n-        0\n-    );\n-\n-    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n-    if (status != GL_FRAMEBUFFER_COMPLETE) {\n-        LERROR(\"Exit framebuffer is not complete\");\n-    }\n+        glGenBuffers(1, &_vertexPositionBuffer);\n+        glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);\n+\n+        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);\n+        glVertexAttribPointer(\n+            0,\n+            4,\n+            GL_FLOAT,\n+            GL_FALSE,\n+            sizeof(GLfloat) * 4,\n+            nullptr\n+        );\n+        glEnableVertexAttribArray(0);\n \n-    glBindFramebuffer(GL_FRAMEBUFFER, _deferredFramebuffer);\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT0,\n-        GL_TEXTURE_2D,\n-        _deferredColorTexture,\n-        0\n-    );\n-\n-    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n-    if (status != GL_FRAMEBUFFER_COMPLETE) {\n-        LERROR(\"Deferred framebuffer is not complete\");\n-    }\n+        GLint defaultFbo;\n+        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n \n-    // JCC: Moved to here to avoid NVidia: \"Program/shader state performance warning\"\n-    updateHDRData();\n-    updateDeferredcastData();\n-    updateMSAASamplingPattern();\n+        // Main framebuffer\n+        glGenTextures(1, &_mainColorTexture);\n+        glGenTextures(1, &_mainDepthTexture);\n+        glGenFramebuffers(1, &_mainFramebuffer);\n \n-    glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);\n+        // Exit framebuffer\n+        glGenTextures(1, &_exitColorTexture);\n+        glGenTextures(1, &_exitDepthTexture);\n+        glGenFramebuffers(1, &_exitFramebuffer);\n \n-    _resolveProgram = ghoul::opengl::ProgramObject::Build(\n-        \"Framebuffer Resolve\",\n-        absPath(\"${SHADERS}/framebuffer/resolveframebuffer.vert\"),\n-        absPath(\"${SHADERS}/framebuffer/resolveframebuffer.frag\")\n-    );\n+        // Deferred framebuffer\n+        glGenTextures(1, &_deferredColorTexture);\n+        glGenTextures(1, &_mainPositionTexture);\n+        glGenTextures(1, &_mainNormalTexture);\n+        glGenFramebuffers(1, &_deferredFramebuffer);\n \n-    _uniformCache.mainColorTexture = _resolveProgram->uniformLocation(\"mainColorTexture\");\n-    _uniformCache.blackoutFactor = _resolveProgram->uniformLocation(\"blackoutFactor\");\n-    _uniformCache.nAaSamples = _resolveProgram->uniformLocation(\"nAaSamples\");\n+        updateResolution();\n+        updateRendererData();\n+        updateRaycastData();\n \n-    OsEng.renderEngine().raycasterManager().addListener(*this);\n-    OsEng.renderEngine().deferredcasterManager().addListener(*this);\n-}\n+        glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer);\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT0,\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _mainColorTexture,\n+            0\n+        );\n+        // G-buffer\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT1,\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _mainPositionTexture,\n+            0\n+        );\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT2,\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _mainNormalTexture,\n+            0\n+        );\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_DEPTH_ATTACHMENT,\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _mainDepthTexture,\n+            0\n+        );\n \n-void FramebufferRenderer::deinitialize() {\n-    LINFO(\"Deinitializing FramebufferRenderer\");\n+        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n+        if (status != GL_FRAMEBUFFER_COMPLETE) {\n+            LERROR(\"Main framebuffer is not complete\");\n+        }\n \n-    glDeleteFramebuffers(1, &_mainFramebuffer);\n-    glDeleteFramebuffers(1, &_exitFramebuffer);\n-    glDeleteFramebuffers(1, &_deferredFramebuffer);\n+        glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer);\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT0,\n+            GL_TEXTURE_2D,\n+            _exitColorTexture,\n+            0\n+        );\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_DEPTH_ATTACHMENT,\n+            GL_TEXTURE_2D,\n+            _exitDepthTexture,\n+            0\n+        );\n \n-    glDeleteTextures(1, &_mainColorTexture);\n-    glDeleteTextures(1, &_mainDepthTexture);\n+        status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n+        if (status != GL_FRAMEBUFFER_COMPLETE) {\n+            LERROR(\"Exit framebuffer is not complete\");\n+        }\n \n-    // DEBUG: deferred g-buffer\n-    glDeleteTextures(1, &_deferredColorTexture);\n-    glDeleteTextures(1, &_mainPositionTexture);\n-    glDeleteTextures(1, &_mainNormalTexture);\n+        glBindFramebuffer(GL_FRAMEBUFFER, _deferredFramebuffer);\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT0,\n+            GL_TEXTURE_2D,\n+            _deferredColorTexture,\n+            0\n+        );\n \n-    glDeleteTextures(1, &_exitColorTexture);\n-    glDeleteTextures(1, &_exitDepthTexture);\n+        status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n+        if (status != GL_FRAMEBUFFER_COMPLETE) {\n+            LERROR(\"Deferred framebuffer is not complete\");\n+        }\n \n-    glDeleteBuffers(1, &_vertexPositionBuffer);\n-    glDeleteVertexArrays(1, &_screenQuad);\n+        // JCC: Moved to here to avoid NVidia: \"Program/shader state performance warning\"\n+        updateHDRData();\n+        updateDeferredcastData();\n+        _dirtyMsaaSamplingPattern = true;\n \n-    OsEng.renderEngine().raycasterManager().removeListener(*this);\n-    OsEng.renderEngine().deferredcasterManager().removeListener(*this);\n-}\n+        glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);\n \n-void FramebufferRenderer::raycastersChanged(VolumeRaycaster&, bool) {\n-    _dirtyRaycastData = true;\n-}\n+        _resolveProgram = ghoul::opengl::ProgramObject::Build(\n+            \"Framebuffer Resolve\",\n+            absPath(\"${SHADERS}/framebuffer/resolveframebuffer.vert\"),\n+            absPath(\"${SHADERS}/framebuffer/resolveframebuffer.frag\")\n+        );\n \n-void FramebufferRenderer::deferredcastersChanged(Deferredcaster& deferredcaster,\n-                                                 isAttached isAttached)\n-{\n-    (void) deferredcaster;\n-    (void) isAttached;\n-    _dirtyDeferredcastData = true;\n-}\n+        _uniformCache.mainColorTexture = _resolveProgram->uniformLocation(\"mainColorTexture\");\n+        _uniformCache.blackoutFactor = _resolveProgram->uniformLocation(\"blackoutFactor\");\n+        _uniformCache.nAaSamples = _resolveProgram->uniformLocation(\"nAaSamples\");\n \n-void FramebufferRenderer::update() {\n-    if (_dirtyResolution) {\n-        updateResolution();\n-        updateMSAASamplingPattern();\n+        OsEng.renderEngine().raycasterManager().addListener(*this);\n+        OsEng.renderEngine().deferredcasterManager().addListener(*this);\n     }\n \n-    if (_dirtyRaycastData) {\n-        updateRaycastData();\n+    void FramebufferRenderer::deinitialize() {\n+        LINFO(\"Deinitializing FramebufferRenderer\");\n+\n+        glDeleteFramebuffers(1, &_mainFramebuffer);\n+        glDeleteFramebuffers(1, &_exitFramebuffer);\n+        glDeleteFramebuffers(1, &_deferredFramebuffer);\n+\n+        glDeleteTextures(1, &_mainColorTexture);\n+        glDeleteTextures(1, &_mainDepthTexture);\n+\n+        // DEBUG: deferred g-buffer\n+        glDeleteTextures(1, &_deferredColorTexture);\n+        glDeleteTextures(1, &_mainPositionTexture);\n+        glDeleteTextures(1, &_mainNormalTexture);\n+\n+        glDeleteTextures(1, &_exitColorTexture);\n+        glDeleteTextures(1, &_exitDepthTexture);\n+\n+        glDeleteBuffers(1, &_vertexPositionBuffer);\n+        glDeleteVertexArrays(1, &_screenQuad);\n+\n+        OsEng.renderEngine().raycasterManager().removeListener(*this);\n+        OsEng.renderEngine().deferredcasterManager().removeListener(*this);\n     }\n \n-    if (_dirtyDeferredcastData) {\n-        updateDeferredcastData();\n+    void FramebufferRenderer::raycastersChanged(VolumeRaycaster&, bool) {\n+        _dirtyRaycastData = true;\n     }\n \n-    // If the resolve dictionary changed (or a file changed on disk)\n-    // then rebuild the resolve program.\n-    if (_hdrBackGroundProgram && _hdrBackGroundProgram->isDirty()) {\n-        _hdrBackGroundProgram->rebuildFromFile();\n+    void FramebufferRenderer::deferredcastersChanged(Deferredcaster& deferredcaster,\n+        isAttached isAttached)\n+    {\n+        (void)deferredcaster;\n+        (void)isAttached;\n+        _dirtyDeferredcastData = true;\n     }\n \n-    if (_resolveProgram->isDirty()) {\n-        _resolveProgram->rebuildFromFile();\n+    void FramebufferRenderer::update() {\n+        // If the resolve dictionary changed (or a file changed on disk)\n+        // then rebuild the resolve program.\n+        if (_hdrBackGroundProgram && _hdrBackGroundProgram->isDirty()) {\n+            _hdrBackGroundProgram->rebuildFromFile();\n+        }\n \n-        _uniformCache.mainColorTexture = _resolveProgram->uniformLocation(\n-            \"mainColorTexture\"\n-        );\n-        _uniformCache.blackoutFactor = _resolveProgram->uniformLocation(\"blackoutFactor\");\n-        _uniformCache.nAaSamples = _resolveProgram->uniformLocation(\"nAaSamples\");\n-    }\n+        if (_resolveProgram->isDirty()) {\n+            _resolveProgram->rebuildFromFile();\n \n-    for (auto& program : _exitPrograms) {\n-        if (program.second->isDirty()) {\n-            try {\n-                program.second->rebuildFromFile();\n-            } catch (const ghoul::RuntimeError& e) {\n-                LERRORC(e.component, e.message);\n+            _uniformCache.mainColorTexture = _resolveProgram->uniformLocation(\n+                \"mainColorTexture\"\n+            );\n+            _uniformCache.blackoutFactor = _resolveProgram->uniformLocation(\"blackoutFactor\");\n+            _uniformCache.nAaSamples = _resolveProgram->uniformLocation(\"nAaSamples\");\n+        }\n+\n+        for (auto& program : _exitPrograms) {\n+            if (program.second->isDirty()) {\n+                try {\n+                    program.second->rebuildFromFile();\n+                } \n+                catch (const ghoul::RuntimeError& e) {\n+                    LERRORC(e.component, e.message);\n+                }\n             }\n         }\n-    }\n \n-    for (auto& program : _raycastPrograms) {\n-        if (program.second->isDirty()) {\n-            try {\n-                program.second->rebuildFromFile();\n-            } catch (const ghoul::RuntimeError& e) {\n-                LERRORC(e.component, e.message);\n+        for (auto& program : _raycastPrograms) {\n+            if (program.second->isDirty()) {\n+                try {\n+                    program.second->rebuildFromFile();\n+                } \n+                catch (const ghoul::RuntimeError& e) {\n+                    LERRORC(e.component, e.message);\n+                }\n+            }\n+        }\n+\n+        for (auto& program : _insideRaycastPrograms) {\n+            if (program.second->isDirty()) {\n+                try {\n+                    program.second->rebuildFromFile();\n+                } \n+                catch (const ghoul::RuntimeError& e) {\n+                    LERRORC(e.component, e.message);\n+                }\n             }\n         }\n+\n+        for (auto &program : _deferredcastPrograms) {\n+            if (program.second && program.second->isDirty()) {\n+                try {\n+                    program.second->rebuildFromFile();\n+                } \n+                catch (const ghoul::RuntimeError& e) {\n+                    LERRORC(e.component, e.message);\n+                }\n+            }\n+        }\n+    }\n+\n+    void FramebufferRenderer::updateResolution() {\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture);\n+\n+        glTexImage2DMultisample(\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _nAaSamples,\n+            GL_RGBA,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            true\n+        );\n+\n+        // G-buffer\n+        glBindTexture(GL_TEXTURE_2D, _deferredColorTexture);\n+\n+        glTexImage2D(\n+            GL_TEXTURE_2D,\n+            0,\n+            GL_RGBA32F,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            0,\n+            GL_RGBA,\n+            GL_FLOAT,\n+            nullptr);\n+\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainPositionTexture);\n+\n+        glTexImage2DMultisample(\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _nAaSamples,\n+            GL_RGBA32F,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            true);\n+\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainNormalTexture);\n+\n+        glTexImage2DMultisample(\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _nAaSamples,\n+            GL_RGBA32F,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            true);\n+\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture);\n+        glTexImage2DMultisample(\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _nAaSamples,\n+            GL_DEPTH_COMPONENT32F,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            true\n+        );\n+\n+        glBindTexture(GL_TEXTURE_2D, _exitColorTexture);\n+        glTexImage2D(\n+            GL_TEXTURE_2D,\n+            0,\n+            GL_RGBA16,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            0,\n+            GL_RGBA,\n+            GL_UNSIGNED_SHORT,\n+            nullptr\n+        );\n+\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\n+        glBindTexture(GL_TEXTURE_2D, _exitDepthTexture);\n+\n+        glTexImage2D(\n+            GL_TEXTURE_2D,\n+            0,\n+            GL_DEPTH_COMPONENT32F,\n+            GLsizei(_resolution.x),\n+            GLsizei(_resolution.y),\n+            0,\n+            GL_DEPTH_COMPONENT,\n+            GL_FLOAT,\n+            nullptr\n+        );\n+\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\n+        _dirtyResolution = false;\n     }\n \n-    for (auto& program : _insideRaycastPrograms) {\n-        if (program.second->isDirty()) {\n+    void FramebufferRenderer::updateRaycastData() {\n+        _raycastData.clear();\n+        _exitPrograms.clear();\n+        _raycastPrograms.clear();\n+        _insideRaycastPrograms.clear();\n+\n+        const std::vector<VolumeRaycaster*>& raycasters =\n+            OsEng.renderEngine().raycasterManager().raycasters();\n+        int nextId = 0;\n+        for (auto& raycaster : raycasters) {\n+            RaycastData data;\n+            data.id = nextId++;\n+            data.namespaceName = \"HELPER\";\n+\n+            std::string vsPath = raycaster->getBoundsVsPath();\n+            std::string fsPath = raycaster->getBoundsFsPath();\n+\n+            ghoul::Dictionary dict;\n+            dict.setValue(\"rendererData\", _rendererData);\n+            dict.setValue(\"fragmentPath\", fsPath);\n+            dict.setValue(\"id\", data.id);\n+            std::string helperPath = raycaster->getHelperPath();\n+            ghoul::Dictionary helpersDict;\n+            if (!helperPath.empty()) {\n+                helpersDict.setValue(\"0\", helperPath);\n+            }\n+            dict.setValue(\"helperPaths\", helpersDict);\n+            dict.setValue(\"raycastPath\", raycaster->getRaycastPath());\n+\n+            _raycastData[raycaster] = data;\n+\n             try {\n-                program.second->rebuildFromFile();\n+                _exitPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n+                    \"Volume \" + std::to_string(data.id) + \" exit\",\n+                    vsPath,\n+                    ExitFragmentShaderPath,\n+                    dict\n+                );\n+            } \n+            catch (ghoul::RuntimeError e) {\n+                LERROR(e.message);\n+            }\n+            try {\n+                ghoul::Dictionary outsideDict = dict;\n+                outsideDict.setValue(\"getEntryPath\", GetEntryOutsidePath);\n+                _raycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n+                    \"Volume \" + std::to_string(data.id) + \" raycast\",\n+                    absPath(vsPath),\n+                    absPath(RaycastFragmentShaderPath),\n+                    outsideDict\n+                );\n+            } \n+            catch (ghoul::RuntimeError e) {\n+                LERROR(e.message);\n+            }\n+            try {\n+                ghoul::Dictionary insideDict = dict;\n+                insideDict.setValue(\"getEntryPath\", GetEntryInsidePath);\n+                _insideRaycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n+                    \"Volume \" + std::to_string(data.id) + \" inside raycast\",\n+                    absPath(\"${SHADERS}/framebuffer/resolveframebuffer.vert\"),\n+                    absPath(RaycastFragmentShaderPath),\n+                    insideDict\n+                );\n             }\n             catch (const ghoul::RuntimeError& e) {\n                 LERRORC(e.component, e.message);\n             }\n         }\n+        _dirtyRaycastData = false;\n     }\n \n-    for (auto &program : _deferredcastPrograms) {\n-        if (program.second && program.second->isDirty()) {\n-            program.second->rebuildFromFile();\n-        }\n-    }\n-}\n-\n-void FramebufferRenderer::updateResolution() {\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture);\n-\n-    glTexImage2DMultisample(\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _nAaSamples,\n-        GL_RGBA,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        true\n-    );\n-\n-    // G-buffer\n-    glBindTexture(GL_TEXTURE_2D, _deferredColorTexture);\n-\n-    glTexImage2D(\n-        GL_TEXTURE_2D,\n-        0,\n-        GL_RGBA32F,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        0,\n-        GL_RGBA,\n-        GL_FLOAT,\n-        nullptr);\n-\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n-\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainPositionTexture);\n-\n-    glTexImage2DMultisample(\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _nAaSamples,\n-        GL_RGBA32F,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        true);\n-\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainNormalTexture);\n-\n-    glTexImage2DMultisample(\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _nAaSamples,\n-        GL_RGBA32F,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        true);\n-\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture);\n-    glTexImage2DMultisample(\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _nAaSamples,\n-        GL_DEPTH_COMPONENT32F,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        true\n-    );\n-\n-    glBindTexture(GL_TEXTURE_2D, _exitColorTexture);\n-    glTexImage2D(\n-        GL_TEXTURE_2D,\n-        0,\n-        GL_RGBA16,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        0,\n-        GL_RGBA,\n-        GL_UNSIGNED_SHORT,\n-        nullptr\n-    );\n-\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n-\n-    glBindTexture(GL_TEXTURE_2D, _exitDepthTexture);\n-\n-    glTexImage2D(\n-        GL_TEXTURE_2D,\n-        0,\n-        GL_DEPTH_COMPONENT32F,\n-        GLsizei(_resolution.x),\n-        GLsizei(_resolution.y),\n-        0,\n-        GL_DEPTH_COMPONENT,\n-        GL_FLOAT,\n-        nullptr\n-    );\n-\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n-\n-    _dirtyResolution = false;\n-}\n-\n-void FramebufferRenderer::updateRaycastData() {\n-    _raycastData.clear();\n-    _exitPrograms.clear();\n-    _raycastPrograms.clear();\n-    _insideRaycastPrograms.clear();\n-\n-    const std::vector<VolumeRaycaster*>& raycasters =\n-        OsEng.renderEngine().raycasterManager().raycasters();\n-    int nextId = 0;\n-    for (auto& raycaster : raycasters) {\n-        RaycastData data;\n-        data.id = nextId++;\n-        data.namespaceName = \"HELPER\";\n-\n-        std::string vsPath = raycaster->getBoundsVsPath();\n-        std::string fsPath = raycaster->getBoundsFsPath();\n+    void FramebufferRenderer::updateDeferredcastData() {\n+        _deferredcastData.clear();\n+        _deferredcastPrograms.clear();\n+\n+        const std::vector<Deferredcaster*>& deferredcasters =\n+            OsEng.renderEngine().deferredcasterManager().deferredcasters();\n+        int nextId = 0;\n+        for (auto& caster : deferredcasters) {\n+            DeferredcastData data;\n+            data.id = nextId++;\n+            data.namespaceName = \"HELPER\";\n+\n+            std::string vsPath = caster->deferredcastVSPath();\n+            std::string fsPath = caster->deferredcastFSPath();\n+            std::string deferredShaderPath = caster->deferredcastPath();\n+\n+            ghoul::Dictionary dict;\n+            dict.setValue(\"rendererData\", _rendererData);\n+            //dict.setValue(\"fragmentPath\", fsPath);\n+            dict.setValue(\"id\", data.id);\n+            std::string helperPath = caster->helperPath();\n+            ghoul::Dictionary helpersDict;\n+            if (helperPath != \"\") {\n+                helpersDict.setValue(\"0\", helperPath);\n+            }\n+            dict.setValue(\"helperPaths\", helpersDict);\n+            //dict.setValue(\"deferredcastPath\", caster->getDeferredcastPath());\n \n-        ghoul::Dictionary dict;\n-        dict.setValue(\"rendererData\", _rendererData);\n-        dict.setValue(\"fragmentPath\", fsPath);\n-        dict.setValue(\"id\", data.id);\n-        std::string helperPath = raycaster->getHelperPath();\n-        ghoul::Dictionary helpersDict;\n-        if (!helperPath.empty()) {\n-            helpersDict.setValue(\"0\", helperPath);\n-        }\n-        dict.setValue(\"helperPaths\", helpersDict);\n-        dict.setValue(\"raycastPath\", raycaster->getRaycastPath());\n+            _deferredcastData[caster] = data;\n \n-        _raycastData[raycaster] = data;\n+            try {\n+                ghoul::Dictionary deferredDict = dict;\n+                //deferredDict.setValue(\"getEntryPath\", GetEntryOutsidePath);\n+                _deferredcastPrograms[caster] = ghoul::opengl::ProgramObject::Build(\n+                    \"Deferred \" + std::to_string(data.id) + \" raycast\",\n+                    absPath(vsPath),\n+                    absPath(deferredShaderPath),\n+                    deferredDict);\n+                using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;\n+                _deferredcastPrograms[caster]->setIgnoreSubroutineUniformLocationError(\n+                    IgnoreError::Yes\n+                );\n+                _deferredcastPrograms[caster]->setIgnoreUniformLocationError(\n+                    IgnoreError::Yes\n+                );\n \n-        try {\n-            _exitPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n-                \"Volume \" + std::to_string(data.id) + \" exit\",\n-                vsPath,\n-                absPath(ExitFragmentShaderPath),\n-                dict\n-            );\n-        } catch (ghoul::RuntimeError e) {\n-            LERROR(e.message);\n-        }\n-        try {\n-            ghoul::Dictionary outsideDict = dict;\n-            outsideDict.setValue(\"getEntryPath\", absPath(GetEntryOutsidePath));\n-            _raycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n-                \"Volume \" + std::to_string(data.id) + \" raycast\",\n-                absPath(vsPath),\n-                absPath(RaycastFragmentShaderPath),\n-                outsideDict\n-            );\n-        } catch (ghoul::RuntimeError e) {\n-            LERROR(e.message);\n+                caster->initializeCachedVariables(*_deferredcastPrograms[caster]);\n+            }\n+            catch (ghoul::RuntimeError& e) {\n+                LERRORC(e.component, e.message);\n+            }\n         }\n+        _dirtyDeferredcastData = false;\n+    }\n+\n+    void FramebufferRenderer::updateHDRData() {\n         try {\n-            ghoul::Dictionary insideDict = dict;\n-            insideDict.setValue(\"getEntryPath\", absPath(GetEntryInsidePath));\n-            _insideRaycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build(\n-                \"Volume \" + std::to_string(data.id) + \" inside raycast\",\n-                absPath(\"${SHADERS}/framebuffer/resolveframebuffer.vert\"),\n-                absPath(RaycastFragmentShaderPath),\n-                insideDict\n+            _hdrBackGroundProgram = ghoul::opengl::ProgramObject::Build(\n+                \"HDR Background Control\",\n+                absPath(\"${SHADERS}/framebuffer/hdrBackground.vert\"),\n+                absPath(\"${SHADERS}/framebuffer/hdrBackground.frag\")\n             );\n+            using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;\n+            _hdrBackGroundProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);\n+            _hdrBackGroundProgram->setIgnoreUniformLocationError(IgnoreError::Yes);\n         }\n         catch (const ghoul::RuntimeError& e) {\n             LERRORC(e.component, e.message);\n         }\n     }\n-    _dirtyRaycastData = false;\n-}\n \n-void FramebufferRenderer::updateDeferredcastData() {\n-    _deferredcastData.clear();\n-    _deferredcastPrograms.clear();\n+    void FramebufferRenderer::updateMSAASamplingPattern() {\n+        LDEBUG(\"Updating MSAA Sampling Pattern\");\n+\n+        const int GRIDSIZE = 32;\n+        GLfloat step = 2.0f / static_cast<GLfloat>(GRIDSIZE);\n+        GLfloat sizeX = -1.0f,\n+            sizeY = 1.0f;\n+\n+        const int NVERTEX = 4 * 6;\n+        // openPixelSizeVertexData\n+        GLfloat vertexData[GRIDSIZE * GRIDSIZE * NVERTEX];\n+\n+        for (int y = 0; y < GRIDSIZE; ++y) {\n+            for (int x = 0; x < GRIDSIZE; ++x) {\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX] = sizeX;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 1] = sizeY - step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 2] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 3] = 1.0f;\n+\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 4] = sizeX + step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 5] = sizeY;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 6] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 7] = 1.0f;\n+\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 8] = sizeX;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 9] = sizeY;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 10] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 11] = 1.0f;\n+\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 12] = sizeX;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 13] = sizeY - step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 14] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 15] = 1.0f;\n+\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 16] = sizeX + step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 17] = sizeY - step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 18] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 19] = 1.0f;\n+\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 20] = sizeX + step;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 21] = sizeY;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 22] = 0.0f;\n+                vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 23] = 1.0f;\n+\n+                sizeX += step;\n+            }\n+            sizeX = -1.0f;\n+            sizeY -= step;\n+        }\n+\n+        GLuint pixelSizeQuadVAO = 0;\n+        GLuint pixelSizeQuadVBO = 0;\n \n-    const std::vector<Deferredcaster*>& deferredcasters =\n-        OsEng.renderEngine().deferredcasterManager().deferredcasters();\n-    int nextId = 0;\n-    for (auto& caster : deferredcasters) {\n-        DeferredcastData data;\n-        data.id = nextId++;\n-        data.namespaceName = \"HELPER\";\n+        glGenVertexArrays(1, &pixelSizeQuadVAO);\n+        glBindVertexArray(pixelSizeQuadVAO);\n \n-        std::string vsPath = caster->deferredcastVSPath();\n-        std::string fsPath = caster->deferredcastFSPath();\n-        std::string deferredShaderPath = caster->deferredcastPath();\n+        glGenBuffers(1, &pixelSizeQuadVBO);\n+        glBindBuffer(GL_ARRAY_BUFFER, pixelSizeQuadVBO);\n \n-        ghoul::Dictionary dict;\n-        dict.setValue(\"rendererData\", _rendererData);\n-        //dict.setValue(\"fragmentPath\", fsPath);\n-        dict.setValue(\"id\", data.id);\n-        std::string helperPath = caster->helperPath();\n-        ghoul::Dictionary helpersDict;\n-        if (helperPath != \"\") {\n-            helpersDict.setValue(\"0\", helperPath);\n-        }\n-        dict.setValue(\"helperPaths\", helpersDict);\n-        //dict.setValue(\"deferredcastPath\", caster->getDeferredcastPath());\n+        glBufferData(\n+            GL_ARRAY_BUFFER,\n+            sizeof(GLfloat) * GRIDSIZE * GRIDSIZE * NVERTEX,\n+            vertexData,\n+            GL_STATIC_DRAW\n+        );\n+\n+        // Position\n+        glVertexAttribPointer(\n+            0,\n+            4,\n+            GL_FLOAT,\n+            GL_FALSE,\n+            0,\n+            nullptr\n+        );\n+        glEnableVertexAttribArray(0);\n+\n+        // Saves current state\n+        GLint defaultFbo;\n+        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n+\n+        glm::ivec4 viewport = OsEng.windowWrapper().viewportPixelCoordinates();\n+\n+        // Main framebuffer\n+        GLuint pixelSizeTexture = 0;\n+        GLuint pixelSizeFramebuffer = 0;\n+\n+        glGenTextures(1, &pixelSizeTexture);\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture);\n+\n+        const GLsizei ONEPIXEL = 1;\n+        glTexImage2DMultisample(\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            _nAaSamples,\n+            GL_RGBA32F,\n+            ONEPIXEL,\n+            ONEPIXEL,\n+            true\n+        );\n \n-        _deferredcastData[caster] = data;\n+        glViewport(0, 0, ONEPIXEL, ONEPIXEL);\n \n+        glGenFramebuffers(1, &pixelSizeFramebuffer);\n+        glBindFramebuffer(GL_FRAMEBUFFER, pixelSizeFramebuffer);\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT0,\n+            GL_TEXTURE_2D_MULTISAMPLE,\n+            pixelSizeTexture,\n+            0\n+        );\n+\n+        GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 };\n+        glDrawBuffers(1, textureBuffers);\n+\n+        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);\n+        glClear(GL_COLOR_BUFFER_BIT);\n+\n+        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n+        if (status != GL_FRAMEBUFFER_COMPLETE) {\n+            LERROR(\"MSAA Sampling pattern framebuffer is not complete\");\n+            return;\n+        }\n+\n+        std::unique_ptr<ghoul::opengl::ProgramObject> pixelSizeProgram = nullptr;\n         try {\n-            ghoul::Dictionary deferredDict = dict;\n-            //deferredDict.setValue(\"getEntryPath\", absPath(GetEntryOutsidePath));\n-            _deferredcastPrograms[caster] = ghoul::opengl::ProgramObject::Build(\n-                \"Deferred \" + std::to_string(data.id) + \" raycast\",\n-                absPath(vsPath),\n-                absPath(deferredShaderPath),\n-                deferredDict);\n+            pixelSizeProgram = ghoul::opengl::ProgramObject::Build(\n+                \"OnePixel MSAA\",\n+                absPath(\"${SHADERS}/framebuffer/pixelSizeMSAA.vert\"),\n+                absPath(\"${SHADERS}/framebuffer/pixelSizeMSAA.frag\")\n \n-            using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;\n-            _deferredcastPrograms[caster]->setIgnoreSubroutineUniformLocationError(\n-                IgnoreError::Yes\n-            );\n-            _deferredcastPrograms[caster]->setIgnoreUniformLocationError(\n-                IgnoreError::Yes\n             );\n-\n-            caster->initializeCachedVariables(*_deferredcastPrograms[caster]);\n         }\n-        catch (ghoul::RuntimeError& e) {\n+        catch (const ghoul::RuntimeError& e) {\n             LERRORC(e.component, e.message);\n         }\n \n-    }\n-    _dirtyDeferredcastData = false;\n-}\n-\n-void FramebufferRenderer::updateHDRData() {\n-    try {\n-        _hdrBackGroundProgram = ghoul::opengl::ProgramObject::Build(\n-            \"HDR Background Control\",\n-            absPath(\"${SHADERS}/framebuffer/hdrBackground.vert\"),\n-            absPath(\"${SHADERS}/framebuffer/hdrBackground.frag\")\n-        );\n-        using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;\n-        _hdrBackGroundProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);\n-        _hdrBackGroundProgram->setIgnoreUniformLocationError(IgnoreError::Yes);\n-    }\n-    catch (const ghoul::RuntimeError& e) {\n-        LERRORC(e.component, e.message);\n-    }\n-}\n-\n-void FramebufferRenderer::updateMSAASamplingPattern() {\n-    LDEBUG(\"Updating MSAA Sampling Pattern\");\n-\n-    const int GRIDSIZE = 32;\n-    GLfloat step = 2.0f / static_cast<GLfloat>(GRIDSIZE);\n-    GLfloat sizeX = -1.0f,\n-        sizeY = 1.0f;\n-\n-    const int NVERTEX = 4 * 6;\n-    // openPixelSizeVertexData\n-    GLfloat vertexData[GRIDSIZE * GRIDSIZE * NVERTEX];\n-\n-    for (int y = 0; y < GRIDSIZE; ++y) {\n-        for (int x = 0; x < GRIDSIZE; ++x) {\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX] = sizeX;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 1] = sizeY - step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 2] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 3] = 1.0f;\n-\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 4] = sizeX + step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 5] = sizeY;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 6] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 7] = 1.0f;\n-\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 8] = sizeX;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 9] = sizeY;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 10] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 11] = 1.0f;\n-\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 12] = sizeX;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 13] = sizeY - step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 14] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 15] = 1.0f;\n-\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 16] = sizeX + step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 17] = sizeY - step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 18] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 19] = 1.0f;\n-\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 20] = sizeX + step;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 21] = sizeY;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 22] = 0.0f;\n-            vertexData[y * GRIDSIZE * NVERTEX + x * NVERTEX + 23] = 1.0f;\n+        pixelSizeProgram->activate();\n+\n+        // Draw sub-pixel grid\n+        glEnable(GL_SAMPLE_SHADING);\n+        glBindVertexArray(pixelSizeQuadVAO);\n+        glDisable(GL_DEPTH_TEST);\n+        glDepthMask(false);\n+        glDrawArrays(GL_TRIANGLES, 0, GRIDSIZE * GRIDSIZE * 6);\n+        glBindVertexArray(0);\n+        glDepthMask(true);\n+        glEnable(GL_DEPTH_TEST);\n+        glDisable(GL_SAMPLE_SHADING);\n+\n+        pixelSizeProgram->deactivate();\n+\n+        // Now we render the Nx1 quad strip\n+        GLuint nOneStripFramebuffer = 0;\n+        GLuint nOneStripVAO = 0;\n+        GLuint nOneStripVBO = 0;\n+        GLuint nOneStripTexture = 0;\n+\n+        sizeX = -1.0f;\n+        step = 2.0f / static_cast<GLfloat>(_nAaSamples);\n+\n+        GLfloat * nOneStripVertexData = new GLfloat[_nAaSamples * (NVERTEX + 12)];\n+\n+        for (int x = 0; x < _nAaSamples; ++x) {\n+            nOneStripVertexData[x * (NVERTEX + 12)] = sizeX;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 1] = -1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 2] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 3] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 4] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 5] = 0.0f;\n+\n+            nOneStripVertexData[x * (NVERTEX + 12) + 6] = sizeX + step;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 7] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 8] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 9] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 10] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 11] = 1.0f;\n+\n+            nOneStripVertexData[x * (NVERTEX + 12) + 12] = sizeX;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 13] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 14] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 15] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 16] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 17] = 0.0f;\n+\n+            nOneStripVertexData[x * (NVERTEX + 12) + 18] = sizeX;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 19] = -1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 20] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 21] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 22] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 23] = 0.0f;\n+\n+            nOneStripVertexData[x * (NVERTEX + 12) + 24] = sizeX + step;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 25] = -1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 26] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 27] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 28] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 29] = 1.0f;\n+\n+            nOneStripVertexData[x * (NVERTEX + 12) + 30] = sizeX + step;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 31] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 32] = 0.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 33] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 34] = 1.0f;\n+            nOneStripVertexData[x * (NVERTEX + 12) + 35] = 1.0f;\n \n             sizeX += step;\n         }\n-        sizeX = -1.0f;\n-        sizeY -= step;\n-    }\n \n-    GLuint pixelSizeQuadVAO = 0;\n-    GLuint pixelSizeQuadVBO = 0;\n-\n-    glGenVertexArrays(1, &pixelSizeQuadVAO);\n-    glBindVertexArray(pixelSizeQuadVAO);\n-\n-    glGenBuffers(1, &pixelSizeQuadVBO);\n-    glBindBuffer(GL_ARRAY_BUFFER, pixelSizeQuadVBO);\n-\n-    glBufferData(\n-        GL_ARRAY_BUFFER,\n-        sizeof(GLfloat) * GRIDSIZE * GRIDSIZE * NVERTEX,\n-        vertexData,\n-        GL_STATIC_DRAW\n-    );\n-\n-    // Position\n-    glVertexAttribPointer(\n-        0,\n-        4,\n-        GL_FLOAT,\n-        GL_FALSE,\n-        0,\n-        nullptr\n-    );\n-    glEnableVertexAttribArray(0);\n-\n-    // Saves current state\n-    GLint defaultFbo;\n-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n-    GLint viewport[4];\n-    glGetIntegerv(GL_VIEWPORT, viewport);\n-\n-    // Main framebuffer\n-    GLuint pixelSizeTexture = 0;\n-    GLuint pixelSizeFramebuffer = 0;\n-\n-    glGenTextures(1, &pixelSizeTexture);\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture);\n-\n-    const GLsizei ONEPIXEL = 1;\n-    glTexImage2DMultisample(\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        _nAaSamples,\n-        GL_RGBA32F,\n-        ONEPIXEL,\n-        ONEPIXEL,\n-        true\n-    );\n-\n-    glViewport(0, 0, ONEPIXEL, ONEPIXEL);\n-\n-    glGenFramebuffers(1, &pixelSizeFramebuffer);\n-    glBindFramebuffer(GL_FRAMEBUFFER, pixelSizeFramebuffer);\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT0,\n-        GL_TEXTURE_2D_MULTISAMPLE,\n-        pixelSizeTexture,\n-        0\n-    );\n-\n-    GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 };\n-    glDrawBuffers(1, textureBuffers);\n-\n-    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);\n-    glClear(GL_COLOR_BUFFER_BIT);\n-\n-    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n-    if (status != GL_FRAMEBUFFER_COMPLETE) {\n-        LERROR(\"MSAA Sampling pattern framebuffer is not complete\");\n-        return;\n-    }\n+        glGenVertexArrays(1, &nOneStripVAO);\n+        glBindVertexArray(nOneStripVAO);\n+        glGenBuffers(1, &nOneStripVBO);\n+        glBindBuffer(GL_ARRAY_BUFFER, nOneStripVBO);\n+        glBufferData(\n+            GL_ARRAY_BUFFER,\n+            sizeof(GLfloat) * _nAaSamples * (NVERTEX + 12),\n+            nOneStripVertexData,\n+            GL_STATIC_DRAW\n+        );\n \n-    std::unique_ptr<ghoul::opengl::ProgramObject> pixelSizeProgram = nullptr;\n-    try {\n-        pixelSizeProgram = ghoul::opengl::ProgramObject::Build(\n-            \"OnePixel MSAA\",\n-            absPath(\"${SHADERS}/framebuffer/pixelSizeMSAA.vert\"),\n-            absPath(\"${SHADERS}/framebuffer/pixelSizeMSAA.frag\")\n+        // position\n+        glVertexAttribPointer(\n+            0,\n+            4,\n+            GL_FLOAT,\n+            GL_FALSE,\n+            sizeof(GLfloat) * 6,\n+            nullptr\n+        );\n+        glEnableVertexAttribArray(0);\n+\n+        // texture coords\n+        glVertexAttribPointer(\n+            1,\n+            2,\n+            GL_FLOAT,\n+            GL_FALSE,\n+            sizeof(GLfloat) * 6,\n+            reinterpret_cast<GLvoid*>(sizeof(GLfloat) * 4)\n+        );\n+        glEnableVertexAttribArray(1);\n+        delete[] nOneStripVertexData;\n+\n+        // fbo texture buffer\n+        glGenTextures(1, &nOneStripTexture);\n+        glBindTexture(GL_TEXTURE_2D, nOneStripTexture);\n+        glTexImage2D(\n+            GL_TEXTURE_2D,\n+            0,\n+            GL_RGBA32F,\n+            _nAaSamples,\n+            ONEPIXEL,\n+            0,\n+            GL_RGBA,\n+            GL_FLOAT,\n+            nullptr\n         );\n-    }\n-    catch (const ghoul::RuntimeError& e) {\n-        LERRORC(e.component, e.message);\n-    }\n \n-    pixelSizeProgram->activate();\n-\n-    // Draw sub-pixel grid\n-    glEnable(GL_SAMPLE_SHADING);\n-    glBindVertexArray(pixelSizeQuadVAO);\n-    glDisable(GL_DEPTH_TEST);\n-    glDepthMask(false);\n-    glDrawArrays(GL_TRIANGLES, 0, GRIDSIZE * GRIDSIZE * 6);\n-    glBindVertexArray(0);\n-    glDepthMask(true);\n-    glEnable(GL_DEPTH_TEST);\n-    glDisable(GL_SAMPLE_SHADING);\n-\n-    pixelSizeProgram->deactivate();\n-\n-    // Now we render the Nx1 quad strip\n-    GLuint nOneStripFramebuffer = 0;\n-    GLuint nOneStripVAO = 0;\n-    GLuint nOneStripVBO = 0;\n-    GLuint nOneStripTexture = 0;\n-\n-    sizeX = -1.0f;\n-    step = 2.0f / static_cast<GLfloat>(_nAaSamples);\n-\n-    GLfloat * nOneStripVertexData = new GLfloat[_nAaSamples * (NVERTEX + 12)];\n-\n-    for (int x = 0; x < _nAaSamples; ++x) {\n-        nOneStripVertexData[x * (NVERTEX + 12)] = sizeX;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 1] = -1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 2] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 3] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 4] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 5] = 0.0f;\n-\n-        nOneStripVertexData[x * (NVERTEX + 12) + 6] = sizeX + step;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 7] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 8] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 9] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 10] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 11] = 1.0f;\n-\n-        nOneStripVertexData[x * (NVERTEX + 12) + 12] = sizeX;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 13] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 14] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 15] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 16] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 17] = 0.0f;\n-\n-        nOneStripVertexData[x * (NVERTEX + 12) + 18] = sizeX;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 19] = -1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 20] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 21] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 22] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 23] = 0.0f;\n-\n-        nOneStripVertexData[x * (NVERTEX + 12) + 24] = sizeX + step;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 25] = -1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 26] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 27] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 28] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 29] = 1.0f;\n-\n-        nOneStripVertexData[x * (NVERTEX + 12) + 30] = sizeX + step;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 31] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 32] = 0.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 33] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 34] = 1.0f;\n-        nOneStripVertexData[x * (NVERTEX + 12) + 35] = 1.0f;\n-\n-        sizeX += step;\n-    }\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\n+        glGenFramebuffers(1, &nOneStripFramebuffer);\n+        glBindFramebuffer(GL_FRAMEBUFFER, nOneStripFramebuffer);\n+        glFramebufferTexture2D(\n+            GL_FRAMEBUFFER,\n+            GL_COLOR_ATTACHMENT0,\n+            GL_TEXTURE_2D,\n+            nOneStripTexture,\n+            0\n+        );\n \n-    glGenVertexArrays(1, &nOneStripVAO);\n-    glBindVertexArray(nOneStripVAO);\n-    glGenBuffers(1, &nOneStripVBO);\n-    glBindBuffer(GL_ARRAY_BUFFER, nOneStripVBO);\n-    glBufferData(\n-        GL_ARRAY_BUFFER,\n-        sizeof(GLfloat) * _nAaSamples * (NVERTEX + 12),\n-        nOneStripVertexData,\n-        GL_STATIC_DRAW\n-    );\n-\n-    // position\n-    glVertexAttribPointer(\n-        0,\n-        4,\n-        GL_FLOAT,\n-        GL_FALSE,\n-        sizeof(GLfloat) * 6,\n-        nullptr\n-    );\n-    glEnableVertexAttribArray(0);\n-\n-    // texture coords\n-    glVertexAttribPointer(\n-        1,\n-        2,\n-        GL_FLOAT,\n-        GL_FALSE,\n-        sizeof(GLfloat) * 6,\n-        reinterpret_cast<GLvoid*>(sizeof(GLfloat) * 4)\n-    );\n-    glEnableVertexAttribArray(1);\n-    delete[] nOneStripVertexData;\n-\n-    // fbo texture buffer\n-    glGenTextures(1, &nOneStripTexture);\n-    glBindTexture(GL_TEXTURE_2D, nOneStripTexture);\n-    glTexImage2D(\n-        GL_TEXTURE_2D,\n-        0,\n-        GL_RGBA32F,\n-        _nAaSamples,\n-        ONEPIXEL,\n-        0,\n-        GL_RGBA,\n-        GL_FLOAT,\n-        nullptr\n-    );\n-\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n-\n-    glGenFramebuffers(1, &nOneStripFramebuffer);\n-    glBindFramebuffer(GL_FRAMEBUFFER, nOneStripFramebuffer);\n-    glFramebufferTexture2D(\n-        GL_FRAMEBUFFER,\n-        GL_COLOR_ATTACHMENT0,\n-        GL_TEXTURE_2D,\n-        nOneStripTexture,\n-        0\n-    );\n-\n-    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n-    if (status != GL_FRAMEBUFFER_COMPLETE) {\n-        LERROR(\"nOneStrip framebuffer is not complete\");\n-    }\n+        status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n+        if (status != GL_FRAMEBUFFER_COMPLETE) {\n+            LERROR(\"nOneStrip framebuffer is not complete\");\n+        }\n \n-    glViewport(0, 0, _nAaSamples, ONEPIXEL);\n+        glViewport(0, 0, _nAaSamples, ONEPIXEL);\n \n-    std::unique_ptr<ghoul::opengl::ProgramObject> nOneStripProgram = nullptr;\n-    try {\n-        nOneStripProgram = ghoul::opengl::ProgramObject::Build(\n-            \"OneStrip MSAA\",\n-            absPath(\"${SHADERS}/framebuffer/nOneStripMSAA.vert\"),\n-            absPath(\"${SHADERS}/framebuffer/nOneStripMSAA.frag\")\n-        );\n-    }\n-    catch (const ghoul::RuntimeError& e) {\n-        LERRORC(e.component, e.message);\n-    }\n+        std::unique_ptr<ghoul::opengl::ProgramObject> nOneStripProgram = nullptr;\n+        try {\n+            nOneStripProgram = ghoul::opengl::ProgramObject::Build(\n+                \"OneStrip MSAA\",\n+                absPath(\"${SHADERS}/framebuffer/nOneStripMSAA.vert\"),\n+                absPath(\"${SHADERS}/framebuffer/nOneStripMSAA.frag\")\n+            );\n+        }\n+        catch (const ghoul::RuntimeError& e) {\n+            LERRORC(e.component, e.message);\n+        }\n \n-    nOneStripProgram->activate();\n+        nOneStripProgram->activate();\n \n-    ghoul::opengl::TextureUnit pixelSizeTextureUnit;\n-    pixelSizeTextureUnit.activate();\n-    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture);\n-    nOneStripProgram->setUniform(\"pixelSizeTexture\", pixelSizeTextureUnit);\n+        ghoul::opengl::TextureUnit pixelSizeTextureUnit;\n+        pixelSizeTextureUnit.activate();\n+        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture);\n+        nOneStripProgram->setUniform(\"pixelSizeTexture\", pixelSizeTextureUnit);\n \n-    // render strip\n-    glDrawBuffers(1, textureBuffers);\n+        // render strip\n+        glDrawBuffers(1, textureBuffers);\n \n-    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);\n-    glClear(GL_COLOR_BUFFER_BIT);\n-    glBindVertexArray(nOneStripVAO);\n-    glDisable(GL_DEPTH_TEST);\n-    glDepthMask(false);\n+        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);\n+        glClear(GL_COLOR_BUFFER_BIT);\n+        glBindVertexArray(nOneStripVAO);\n+        glDisable(GL_DEPTH_TEST);\n+        glDepthMask(false);\n \n-    for (int sample = 0; sample < _nAaSamples; ++sample) {\n-        nOneStripProgram->setUniform(\"currentSample\", sample);\n-        glDrawArrays(GL_TRIANGLES, sample * 6, 6);\n-    }\n-    glDepthMask(true);\n-    glEnable(GL_DEPTH_TEST);\n-    glBindVertexArray(0);\n-\n-    saveTextureToMemory(GL_COLOR_ATTACHMENT0, _nAaSamples, 1, _mSAAPattern);\n-    // Convert back to [-1, 1] range and then scale for the current viewport size:\n-    for (int d = 0; d < _nAaSamples; ++d) {\n-        _mSAAPattern[d * 3]       = (2.0 * _mSAAPattern[d * 3] - 1.0) /\n-                                    static_cast<double>(viewport[2]);\n-        _mSAAPattern[(d * 3) + 1] = (2.0 * _mSAAPattern[(d * 3) + 1] - 1.0) /\n-                                    static_cast<double>(viewport[3]);\n-        _mSAAPattern[(d * 3) + 2] = 0.0;\n-    }\n+        for (int sample = 0; sample < _nAaSamples; ++sample) {\n+            nOneStripProgram->setUniform(\"currentSample\", sample);\n+            glDrawArrays(GL_TRIANGLES, sample * 6, 6);\n+        }\n+        glDepthMask(true);\n+        glEnable(GL_DEPTH_TEST);\n+        glBindVertexArray(0);\n \n-    nOneStripProgram->deactivate();\n-\n-    // Restores default state\n-    glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);\n-    glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);\n-\n-    // Deletes unused buffers\n-    glDeleteFramebuffers(1, &pixelSizeFramebuffer);\n-    glDeleteTextures(1, &pixelSizeTexture);\n-    glDeleteBuffers(1, &pixelSizeQuadVBO);\n-    glDeleteVertexArrays(1, &pixelSizeQuadVAO);\n-\n-    glDeleteFramebuffers(1, &nOneStripFramebuffer);\n-    glDeleteTextures(1, &nOneStripTexture);\n-    glDeleteBuffers(1, &nOneStripVBO);\n-    glDeleteVertexArrays(1, &nOneStripVAO);\n-}\n-\n-void FramebufferRenderer::render(float blackoutFactor, bool doPerformanceMeasurements) {\n-    std::unique_ptr<performance::PerformanceMeasurement> perf;\n-    if (doPerformanceMeasurements) {\n-        perf = std::make_unique<performance::PerformanceMeasurement>(\n-            \"FramebufferRenderer::render\",\n-            OsEng.renderEngine().performanceManager()\n-        );\n-    }\n+        saveTextureToMemory(GL_COLOR_ATTACHMENT0, _nAaSamples, 1, _mSAAPattern);\n+        // Convert back to [-1, 1] range and then scale for the current viewport size:\n+        for (int d = 0; d < _nAaSamples; ++d) {\n+            _mSAAPattern[d * 3] = (2.0 * _mSAAPattern[d * 3] - 1.0) /\n+                static_cast<double>(viewport[1]);\n+            _mSAAPattern[(d * 3) + 1] = (2.0 * _mSAAPattern[(d * 3) + 1] - 1.0) /\n+                static_cast<double>(viewport[3]);\n+            _mSAAPattern[(d * 3) + 2] = 0.0;\n+        }\n \n-    if (!_scene || !_camera) {\n-        return;\n-    }\n+        nOneStripProgram->deactivate();\n \n-    glEnable(GL_DEPTH_TEST);\n+        // Restores default state\n+        glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);\n+        glViewport(viewport[0], viewport[2], viewport[1], viewport[3]);\n \n+        // Deletes unused buffers\n+        glDeleteFramebuffers(1, &pixelSizeFramebuffer);\n+        glDeleteTextures(1, &pixelSizeTexture);\n+        glDeleteBuffers(1, &pixelSizeQuadVBO);\n+        glDeleteVertexArrays(1, &pixelSizeQuadVAO);\n \n-    Time time = OsEng.timeManager().time();\n+        glDeleteFramebuffers(1, &nOneStripFramebuffer);\n+        glDeleteTextures(1, &nOneStripTexture);\n+        glDeleteBuffers(1, &nOneStripVBO);\n+        glDeleteVertexArrays(1, &nOneStripVAO);\n \n-    RenderData data = { *_camera, psc(), time, doPerformanceMeasurements, 0, {} };\n-    RendererTasks tasks;\n+        _dirtyMsaaSamplingPattern = false;\n+    }\n \n-    // Capture standard fbo\n-    GLint defaultFbo;\n-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n+    void FramebufferRenderer::render(float blackoutFactor, bool doPerformanceMeasurements) {\n+        if (_dirtyResolution) {\n+            updateResolution();\n+        }\n \n-    glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer);\n-    // deferred g-buffer\n-    GLenum textureBuffers[3] = {\n-        GL_COLOR_ATTACHMENT0,\n-        GL_COLOR_ATTACHMENT1,\n-        GL_COLOR_ATTACHMENT2,\n-    };\n-    glDrawBuffers(3, textureBuffers);\n-    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n+        if (_dirtyMsaaSamplingPattern) {\n+            updateMSAASamplingPattern();\n+        }\n \n-    glEnablei(GL_BLEND, 0);\n-    glDisablei(GL_BLEND, 1);\n-    glDisablei(GL_BLEND, 2);\n-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n+        if (_dirtyRaycastData) {\n+            updateRaycastData();\n+        }\n \n-    {\n-        std::unique_ptr<performance::PerformanceMeasurement> perfInternal;\n+        if (_dirtyDeferredcastData) {\n+            updateDeferredcastData();\n+        }\n+\n+        std::unique_ptr<performance::PerformanceMeasurement> perf;\n         if (doPerformanceMeasurements) {\n-            perfInternal = std::make_unique<performance::PerformanceMeasurement>(\n-                \"FramebufferRenderer::render::background\",\n+            perf = std::make_unique<performance::PerformanceMeasurement>(\n+                \"FramebufferRenderer::render\",\n                 OsEng.renderEngine().performanceManager()\n                 );\n         }\n \n-        data.renderBinMask = static_cast<int>(Renderable::RenderBin::Background);\n-        _scene->render(data, tasks);\n-    }\n-    {\n-        std::unique_ptr<performance::PerformanceMeasurement> perfInternal;\n-        if (doPerformanceMeasurements) {\n-            perfInternal = std::make_unique<performance::PerformanceMeasurement>(\n-                \"FramebufferRenderer::render::opaque\",\n-                OsEng.renderEngine().performanceManager()\n-            );\n+        if (!_scene || !_camera) {\n+            return;\n         }\n \n+        // Capture standard fbo\n+        GLint defaultFbo;\n+        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo);\n+\n+        glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer);\n+        glEnable(GL_DEPTH_TEST);\n+\n+        // deferred g-buffer\n+        GLenum textureBuffers[3] = {\n+            GL_COLOR_ATTACHMENT0,\n+            GL_COLOR_ATTACHMENT1,\n+            GL_COLOR_ATTACHMENT2,\n+        };\n+        glDrawBuffers(3, textureBuffers);\n+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n+\n+        glEnablei(GL_BLEND, 0);\n+        glDisablei(GL_BLEND, 1);\n+        glDisablei(GL_BLEND, 2);\n+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n+\n+        Time time = OsEng.timeManager().time();\n+\n+        RenderData data = { *_camera, psc(), time, doPerformanceMeasurements, 0,{} };\n+        RendererTasks tasks;\n+\n+        data.renderBinMask = static_cast<int>(Renderable::RenderBin::Background);","path":"src/rendering/framebufferrenderer.cpp","position":1766,"original_position":1766,"commit_id":"cec7d45fed68a47c9595a5908f813d26af769f9e","original_commit_id":"cec7d45fed68a47c9595a5908f813d26af769f9e","user":{"login":"alexanderbock","id":4921724,"avatar_url":"https://avatars2.githubusercontent.com/u/4921724?v=4","gravatar_id":"","url":"https://api.github.com/users/alexanderbock","html_url":"https://github.com/alexanderbock","followers_url":"https://api.github.com/users/alexanderbock/followers","following_url":"https://api.github.com/users/alexanderbock/following{/other_user}","gists_url":"https://api.github.com/users/alexanderbock/gists{/gist_id}","starred_url":"https://api.github.com/users/alexanderbock/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexanderbock/subscriptions","organizations_url":"https://api.github.com/users/alexanderbock/orgs","repos_url":"https://api.github.com/users/alexanderbock/repos","events_url":"https://api.github.com/users/alexanderbock/events{/privacy}","received_events_url":"https://api.github.com/users/alexanderbock/received_events","type":"User","site_admin":false},"body":"Why isn't the `renderBinMask` of type `Renderable::RenderBin`?","created_at":"2018-04-20T21:24:08Z","updated_at":"2018-04-20T21:33:43Z","html_url":"https://github.com/OpenSpace/OpenSpace/pull/603#discussion_r183172932","pull_request_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/comments/183172932"},"html":{"href":"https://github.com/OpenSpace/OpenSpace/pull/603#discussion_r183172932"},"pull_request":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603"}}},"pull_request":{"url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603","id":183079315,"html_url":"https://github.com/OpenSpace/OpenSpace/pull/603","diff_url":"https://github.com/OpenSpace/OpenSpace/pull/603.diff","patch_url":"https://github.com/OpenSpace/OpenSpace/pull/603.patch","issue_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/603","number":603,"state":"open","locked":false,"title":"Stereo and Performance ATM","user":{"login":"jccosta","id":874760,"avatar_url":"https://avatars3.githubusercontent.com/u/874760?v=4","gravatar_id":"","url":"https://api.github.com/users/jccosta","html_url":"https://github.com/jccosta","followers_url":"https://api.github.com/users/jccosta/followers","following_url":"https://api.github.com/users/jccosta/following{/other_user}","gists_url":"https://api.github.com/users/jccosta/gists{/gist_id}","starred_url":"https://api.github.com/users/jccosta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jccosta/subscriptions","organizations_url":"https://api.github.com/users/jccosta/orgs","repos_url":"https://api.github.com/users/jccosta/repos","events_url":"https://api.github.com/users/jccosta/events{/privacy}","received_events_url":"https://api.github.com/users/jccosta/received_events","type":"User","site_admin":false},"body":"The stereo compatible stars are implemented in feature/NewStarsRendering and will be merged with master too.","created_at":"2018-04-20T14:28:04Z","updated_at":"2018-04-20T21:33:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e7c11d9483bab48beb14f6e00a3bac2ee93f1807","assignee":null,"assignees":[],"requested_reviewers":[{"login":"emiax","id":1081118,"avatar_url":"https://avatars1.githubusercontent.com/u/1081118?v=4","gravatar_id":"","url":"https://api.github.com/users/emiax","html_url":"https://github.com/emiax","followers_url":"https://api.github.com/users/emiax/followers","following_url":"https://api.github.com/users/emiax/following{/other_user}","gists_url":"https://api.github.com/users/emiax/gists{/gist_id}","starred_url":"https://api.github.com/users/emiax/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emiax/subscriptions","organizations_url":"https://api.github.com/users/emiax/orgs","repos_url":"https://api.github.com/users/emiax/repos","events_url":"https://api.github.com/users/emiax/events{/privacy}","received_events_url":"https://api.github.com/users/emiax/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603/commits","review_comments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603/comments","review_comment_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/comments{/number}","comments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/603/comments","statuses_url":"https://api.github.com/repos/OpenSpace/OpenSpace/statuses/cec7d45fed68a47c9595a5908f813d26af769f9e","head":{"label":"OpenSpace:feature/StereoATM","ref":"feature/StereoATM","sha":"cec7d45fed68a47c9595a5908f813d26af769f9e","user":{"login":"OpenSpace","id":9393286,"avatar_url":"https://avatars1.githubusercontent.com/u/9393286?v=4","gravatar_id":"","url":"https://api.github.com/users/OpenSpace","html_url":"https://github.com/OpenSpace","followers_url":"https://api.github.com/users/OpenSpace/followers","following_url":"https://api.github.com/users/OpenSpace/following{/other_user}","gists_url":"https://api.github.com/users/OpenSpace/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenSpace/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenSpace/subscriptions","organizations_url":"https://api.github.com/users/OpenSpace/orgs","repos_url":"https://api.github.com/users/OpenSpace/repos","events_url":"https://api.github.com/users/OpenSpace/events{/privacy}","received_events_url":"https://api.github.com/users/OpenSpace/received_events","type":"Organization","site_admin":false},"repo":{"id":38380543,"name":"OpenSpace","full_name":"OpenSpace/OpenSpace","owner":{"login":"OpenSpace","id":9393286,"avatar_url":"https://avatars1.githubusercontent.com/u/9393286?v=4","gravatar_id":"","url":"https://api.github.com/users/OpenSpace","html_url":"https://github.com/OpenSpace","followers_url":"https://api.github.com/users/OpenSpace/followers","following_url":"https://api.github.com/users/OpenSpace/following{/other_user}","gists_url":"https://api.github.com/users/OpenSpace/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenSpace/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenSpace/subscriptions","organizations_url":"https://api.github.com/users/OpenSpace/orgs","repos_url":"https://api.github.com/users/OpenSpace/repos","events_url":"https://api.github.com/users/OpenSpace/events{/privacy}","received_events_url":"https://api.github.com/users/OpenSpace/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OpenSpace/OpenSpace","description":"This is the official GitHub repository for OpenSpace: an open source astrovisualization project. For instructions on how to build and run OpenSpace, see the Getting Started Guides on the wiki page.","fork":false,"url":"https://api.github.com/repos/OpenSpace/OpenSpace","forks_url":"https://api.github.com/repos/OpenSpace/OpenSpace/forks","keys_url":"https://api.github.com/repos/OpenSpace/OpenSpace/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OpenSpace/OpenSpace/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OpenSpace/OpenSpace/teams","hooks_url":"https://api.github.com/repos/OpenSpace/OpenSpace/hooks","issue_events_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/events{/number}","events_url":"https://api.github.com/repos/OpenSpace/OpenSpace/events","assignees_url":"https://api.github.com/repos/OpenSpace/OpenSpace/assignees{/user}","branches_url":"https://api.github.com/repos/OpenSpace/OpenSpace/branches{/branch}","tags_url":"https://api.github.com/repos/OpenSpace/OpenSpace/tags","blobs_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/refs{/sha}","trees_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OpenSpace/OpenSpace/statuses/{sha}","languages_url":"https://api.github.com/repos/OpenSpace/OpenSpace/languages","stargazers_url":"https://api.github.com/repos/OpenSpace/OpenSpace/stargazers","contributors_url":"https://api.github.com/repos/OpenSpace/OpenSpace/contributors","subscribers_url":"https://api.github.com/repos/OpenSpace/OpenSpace/subscribers","subscription_url":"https://api.github.com/repos/OpenSpace/OpenSpace/subscription","commits_url":"https://api.github.com/repos/OpenSpace/OpenSpace/commits{/sha}","git_commits_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/commits{/sha}","comments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/comments{/number}","issue_comment_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/comments{/number}","contents_url":"https://api.github.com/repos/OpenSpace/OpenSpace/contents/{+path}","compare_url":"https://api.github.com/repos/OpenSpace/OpenSpace/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OpenSpace/OpenSpace/merges","archive_url":"https://api.github.com/repos/OpenSpace/OpenSpace/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OpenSpace/OpenSpace/downloads","issues_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues{/number}","pulls_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls{/number}","milestones_url":"https://api.github.com/repos/OpenSpace/OpenSpace/milestones{/number}","notifications_url":"https://api.github.com/repos/OpenSpace/OpenSpace/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OpenSpace/OpenSpace/labels{/name}","releases_url":"https://api.github.com/repos/OpenSpace/OpenSpace/releases{/id}","deployments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/deployments","created_at":"2015-07-01T15:59:54Z","updated_at":"2018-04-20T20:12:21Z","pushed_at":"2018-04-20T20:14:25Z","git_url":"git://github.com/OpenSpace/OpenSpace.git","ssh_url":"git@github.com:OpenSpace/OpenSpace.git","clone_url":"https://github.com/OpenSpace/OpenSpace.git","svn_url":"https://github.com/OpenSpace/OpenSpace","homepage":"http://openspaceproject.com","size":281214,"stargazers_count":101,"watchers_count":101,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"archived":false,"open_issues_count":179,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":20,"open_issues":179,"watchers":101,"default_branch":"master"}},"base":{"label":"OpenSpace:master","ref":"master","sha":"a1e1c3841a152d40e6710a6d28df2b3a022a6b05","user":{"login":"OpenSpace","id":9393286,"avatar_url":"https://avatars1.githubusercontent.com/u/9393286?v=4","gravatar_id":"","url":"https://api.github.com/users/OpenSpace","html_url":"https://github.com/OpenSpace","followers_url":"https://api.github.com/users/OpenSpace/followers","following_url":"https://api.github.com/users/OpenSpace/following{/other_user}","gists_url":"https://api.github.com/users/OpenSpace/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenSpace/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenSpace/subscriptions","organizations_url":"https://api.github.com/users/OpenSpace/orgs","repos_url":"https://api.github.com/users/OpenSpace/repos","events_url":"https://api.github.com/users/OpenSpace/events{/privacy}","received_events_url":"https://api.github.com/users/OpenSpace/received_events","type":"Organization","site_admin":false},"repo":{"id":38380543,"name":"OpenSpace","full_name":"OpenSpace/OpenSpace","owner":{"login":"OpenSpace","id":9393286,"avatar_url":"https://avatars1.githubusercontent.com/u/9393286?v=4","gravatar_id":"","url":"https://api.github.com/users/OpenSpace","html_url":"https://github.com/OpenSpace","followers_url":"https://api.github.com/users/OpenSpace/followers","following_url":"https://api.github.com/users/OpenSpace/following{/other_user}","gists_url":"https://api.github.com/users/OpenSpace/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenSpace/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenSpace/subscriptions","organizations_url":"https://api.github.com/users/OpenSpace/orgs","repos_url":"https://api.github.com/users/OpenSpace/repos","events_url":"https://api.github.com/users/OpenSpace/events{/privacy}","received_events_url":"https://api.github.com/users/OpenSpace/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OpenSpace/OpenSpace","description":"This is the official GitHub repository for OpenSpace: an open source astrovisualization project. For instructions on how to build and run OpenSpace, see the Getting Started Guides on the wiki page.","fork":false,"url":"https://api.github.com/repos/OpenSpace/OpenSpace","forks_url":"https://api.github.com/repos/OpenSpace/OpenSpace/forks","keys_url":"https://api.github.com/repos/OpenSpace/OpenSpace/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OpenSpace/OpenSpace/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OpenSpace/OpenSpace/teams","hooks_url":"https://api.github.com/repos/OpenSpace/OpenSpace/hooks","issue_events_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/events{/number}","events_url":"https://api.github.com/repos/OpenSpace/OpenSpace/events","assignees_url":"https://api.github.com/repos/OpenSpace/OpenSpace/assignees{/user}","branches_url":"https://api.github.com/repos/OpenSpace/OpenSpace/branches{/branch}","tags_url":"https://api.github.com/repos/OpenSpace/OpenSpace/tags","blobs_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/refs{/sha}","trees_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OpenSpace/OpenSpace/statuses/{sha}","languages_url":"https://api.github.com/repos/OpenSpace/OpenSpace/languages","stargazers_url":"https://api.github.com/repos/OpenSpace/OpenSpace/stargazers","contributors_url":"https://api.github.com/repos/OpenSpace/OpenSpace/contributors","subscribers_url":"https://api.github.com/repos/OpenSpace/OpenSpace/subscribers","subscription_url":"https://api.github.com/repos/OpenSpace/OpenSpace/subscription","commits_url":"https://api.github.com/repos/OpenSpace/OpenSpace/commits{/sha}","git_commits_url":"https://api.github.com/repos/OpenSpace/OpenSpace/git/commits{/sha}","comments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/comments{/number}","issue_comment_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/comments{/number}","contents_url":"https://api.github.com/repos/OpenSpace/OpenSpace/contents/{+path}","compare_url":"https://api.github.com/repos/OpenSpace/OpenSpace/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OpenSpace/OpenSpace/merges","archive_url":"https://api.github.com/repos/OpenSpace/OpenSpace/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OpenSpace/OpenSpace/downloads","issues_url":"https://api.github.com/repos/OpenSpace/OpenSpace/issues{/number}","pulls_url":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls{/number}","milestones_url":"https://api.github.com/repos/OpenSpace/OpenSpace/milestones{/number}","notifications_url":"https://api.github.com/repos/OpenSpace/OpenSpace/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OpenSpace/OpenSpace/labels{/name}","releases_url":"https://api.github.com/repos/OpenSpace/OpenSpace/releases{/id}","deployments_url":"https://api.github.com/repos/OpenSpace/OpenSpace/deployments","created_at":"2015-07-01T15:59:54Z","updated_at":"2018-04-20T20:12:21Z","pushed_at":"2018-04-20T20:14:25Z","git_url":"git://github.com/OpenSpace/OpenSpace.git","ssh_url":"git@github.com:OpenSpace/OpenSpace.git","clone_url":"https://github.com/OpenSpace/OpenSpace.git","svn_url":"https://github.com/OpenSpace/OpenSpace","homepage":"http://openspaceproject.com","size":281214,"stargazers_count":101,"watchers_count":101,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"archived":false,"open_issues_count":179,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":20,"open_issues":179,"watchers":101,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603"},"html":{"href":"https://github.com/OpenSpace/OpenSpace/pull/603"},"issue":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/603"},"comments":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/issues/603/comments"},"review_comments":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603/comments"},"review_comment":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/pulls/603/commits"},"statuses":{"href":"https://api.github.com/repos/OpenSpace/OpenSpace/statuses/cec7d45fed68a47c9595a5908f813d26af769f9e"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"alexanderbock"}}