/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLState.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkOpenGLState.h" #include "vtk_glew.h" #include "vtkObjectFactory.h" #include "vtkOpenGLFramebufferObject.h" #include "vtkOpenGLRenderUtilities.h" #include "vtkOpenGLRenderWindow.h" #include "vtkOpenGLShaderCache.h" #include "vtkOpenGLVertexBufferObjectCache.h" #include "vtkRenderer.h" #include "vtkTextureUnitManager.h" // must be included after a vtkObject subclass #include "vtkOpenGLError.h" #include #include "vtksys/SystemInformation.hxx" // If you define NO_CACHE then all state->vtkgl* calls // will get passed down to OpenGL regardless of the current // state. This basically bypasses the caching mechanism // and is useful for tesing // #define NO_CACHE 1 ////////////////////////////////////////////////////////////////////////////// // // if VTK_REPORT_OPENGL_ERRORS is defined (done in CMake and vtkOpenGLError.h) // then a few things change here. // // 1) the error code of OpenGL will be checked after every call. // 2) On an error the full stack trace available will be // printed. // 3) All methods check the cache state to see if anything is out of sync // // #undef VTK_REPORT_OPENGL_ERRORS #ifdef VTK_REPORT_OPENGL_ERRORS // this method checks all the cached state to make sure // nothing is out of sync. It can be slow. void vtkOpenGLState::CheckState() { bool error = false; vtkOpenGLRenderUtilities::MarkDebugEvent("Checking OpenGL State"); GLboolean params[4]; auto& cs = this->Stack.top(); ::glGetBooleanv(GL_DEPTH_WRITEMASK, params); if (params[0] != cs.DepthMask) { vtkGenericWarningMacro("Error in cache state for GL_DEPTH_WRITEMASK"); this->ResetGLDepthMaskState(); error = true; } ::glGetBooleanv(GL_COLOR_WRITEMASK, params); if (params[0] != cs.ColorMask[0] || params[1] != cs.ColorMask[1] || params[2] != cs.ColorMask[2] || params[3] != cs.ColorMask[3]) { vtkGenericWarningMacro("Error in cache state for GL_COLOR_WRITEMASK"); this->ResetGLColorMaskState(); error = true; } ::glGetBooleanv(GL_BLEND, params); if ((params[0] != 0) != cs.Blend) { vtkGenericWarningMacro("Error in cache state for GL_BLEND"); this->ResetEnumState(GL_BLEND); error = true; } ::glGetBooleanv(GL_DEPTH_TEST, params); if ((params[0] != 0) != cs.DepthTest) { vtkGenericWarningMacro("Error in cache state for GL_DEPTH_TEST"); this->ResetEnumState(GL_DEPTH_TEST); error = true; } ::glGetBooleanv(GL_CULL_FACE, params); if ((params[0] != 0) != cs.CullFace) { vtkGenericWarningMacro("Error in cache state for GL_CULL_FACE"); this->ResetEnumState(GL_CULL_FACE); error = true; } #ifdef GL_MULTISAMPLE ::glGetBooleanv(GL_MULTISAMPLE, params); if ((params[0] != 0) != cs.MultiSample) { vtkGenericWarningMacro("Error in cache state for GL_MULTISAMPLE"); this->ResetEnumState(GL_MULTISAMPLE); error = true; } #endif ::glGetBooleanv(GL_SCISSOR_TEST, params); if ((params[0] != 0) != cs.ScissorTest) { vtkGenericWarningMacro("Error in cache state for GL_SCISSOR_TEST"); this->ResetEnumState(GL_SCISSOR_TEST); error = true; } ::glGetBooleanv(GL_STENCIL_TEST, params); if ((params[0] != 0) != cs.StencilTest) { vtkGenericWarningMacro("Error in cache state for GL_STENCIL_TEST"); this->ResetEnumState(GL_STENCIL_TEST); error = true; } #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS ::glGetBooleanv(GL_TEXTURE_CUBE_MAP_SEAMLESS, params); if ((params[0] != 0) != cs.CubeMapSeamless) { vtkGenericWarningMacro("Error in cache state for GL_TEXTURE_CUBE_MAP_SEAMLESS"); this->ResetEnumState(GL_TEXTURE_CUBE_MAP_SEAMLESS); error = true; } #endif GLint iparams[4]; #if defined(__APPLE__) // OSX systems seem to change the glViewport upon a window resize // under the hood, so our viewport cache cannot be trusted this->ResetGLViewportState(); #endif ::glGetIntegerv(GL_VIEWPORT, iparams); if (iparams[0] != cs.Viewport[0] || iparams[1] != cs.Viewport[1] || iparams[2] != cs.Viewport[2] || iparams[3] != cs.Viewport[3]) { vtkGenericWarningMacro("Error in cache state for GL_VIEWPORT"); this->ResetGLViewportState(); error = true; } ::glGetIntegerv(GL_SCISSOR_BOX, iparams); if (iparams[0] != cs.Scissor[0] || iparams[1] != cs.Scissor[1] || iparams[2] != cs.Scissor[2] || iparams[3] != cs.Scissor[3]) { vtkGenericWarningMacro("Error in cache state for GL_SCISSOR_BOX"); this->ResetGLScissorState(); error = true; } ::glGetIntegerv(GL_CULL_FACE_MODE, iparams); if (iparams[0] != static_cast(cs.CullFaceMode)) { vtkGenericWarningMacro("Error in cache state for GL_CULL_FACE_MODE"); this->ResetGLCullFaceState(); error = true; } ::glGetIntegerv(GL_ACTIVE_TEXTURE, iparams); if (iparams[0] != static_cast(cs.ActiveTexture)) { vtkGenericWarningMacro("Error in cache state for GL_ACTIVE_TEXTURE"); this->ResetGLActiveTexture(); error = true; } ::glGetIntegerv(GL_DEPTH_FUNC, iparams); if (iparams[0] != static_cast(cs.DepthFunc)) { vtkGenericWarningMacro("Error in cache state for GL_DEPTH_FUNC"); this->ResetGLDepthFuncState(); error = true; } ::glGetIntegerv(GL_BLEND_SRC_RGB, iparams); if (iparams[0] != static_cast(cs.BlendFunc[0])) { vtkGenericWarningMacro("Error in cache state for GL_BLEND_SRC_RGB"); this->ResetGLBlendFuncState(); error = true; } ::glGetIntegerv(GL_BLEND_SRC_ALPHA, iparams); if (iparams[0] != static_cast(cs.BlendFunc[2])) { vtkGenericWarningMacro("Error in cache state for GL_BLEND_SRC_ALPHA"); this->ResetGLBlendFuncState(); error = true; } ::glGetIntegerv(GL_BLEND_DST_RGB, iparams); if (iparams[0] != static_cast(cs.BlendFunc[1])) { vtkGenericWarningMacro("Error in cache state for GL_BLEND_DST_RGB"); this->ResetGLBlendFuncState(); error = true; } ::glGetIntegerv(GL_BLEND_DST_ALPHA, iparams); if (iparams[0] != static_cast(cs.BlendFunc[3])) { vtkGenericWarningMacro("Error in cache state for GL_BLEND_DST_ALPHA"); this->ResetGLBlendFuncState(); error = true; } ::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, iparams); if (iparams[0] != static_cast(cs.DrawBinding.GetBinding())) { vtkGenericWarningMacro("Error in cache state for GL_DRAW_FRAMEBUFFER_BINDING"); this->ResetFramebufferBindings(); error = true; } ::glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, iparams); if (iparams[0] != static_cast(cs.ReadBinding.GetBinding())) { vtkGenericWarningMacro("Error in cache state for GL_READ_FRAMEBUFFER_BINDING"); this->ResetFramebufferBindings(); error = true; } unsigned int sval; #ifdef GL_DRAW_BUFFER ::glGetIntegerv(GL_DRAW_BUFFER, iparams); sval = cs.DrawBinding.GetDrawBuffer(0); if (sval == GL_BACK_LEFT) { sval = GL_BACK; } if (iparams[0] == GL_BACK_LEFT) { iparams[0] = GL_BACK; } if (iparams[0] != static_cast(sval)) { vtkGenericWarningMacro("Error in cache state for GL_DRAW_BUFFER got " << iparams[0] << " expected" << cs.DrawBinding.GetDrawBuffer(0)); this->ResetFramebufferBindings(); error = true; } #endif ::glGetIntegerv(GL_READ_BUFFER, iparams); sval = cs.ReadBinding.GetReadBuffer(); // handle odd left right stuff if (sval == GL_BACK_LEFT) { sval = GL_BACK; } if (iparams[0] == GL_BACK_LEFT) { iparams[0] = GL_BACK; } if (iparams[0] != static_cast(sval)) { vtkGenericWarningMacro("Error in cache state for GL_READ_BUFFER"); this->ResetFramebufferBindings(); error = true; } GLfloat fparams[4]; // note people do set this to nan ::glGetFloatv(GL_COLOR_CLEAR_VALUE, fparams); if ((!(std::isnan(fparams[0]) && std::isnan(cs.ClearColor[0])) && fparams[0] != cs.ClearColor[0]) || (!(std::isnan(fparams[1]) && std::isnan(cs.ClearColor[1])) && fparams[1] != cs.ClearColor[1]) || (!(std::isnan(fparams[2]) && std::isnan(cs.ClearColor[2])) && fparams[2] != cs.ClearColor[2]) || (!(std::isnan(fparams[3]) && std::isnan(cs.ClearColor[3])) && fparams[3] != cs.ClearColor[3])) { vtkGenericWarningMacro("Error in cache state for GL_COLOR_CLEAR_VALUE"); this->ResetGLClearColorState(); error = true; } if (error) { std::string msg = vtksys::SystemInformation::GetProgramStack(0, 0); vtkGenericWarningMacro("at stack loc\n" << msg); } vtkOpenGLRenderUtilities::MarkDebugEvent("Finished Checking OpenGL State"); } namespace { bool reportOpenGLErrors(std::string& result) { const int maxErrors = 16; unsigned int errCode[maxErrors] = { 0 }; const char* errDesc[maxErrors] = { nullptr }; int numErrors = vtkGetOpenGLErrors(maxErrors, errCode, errDesc); if (numErrors) { std::ostringstream oss; vtkPrintOpenGLErrors(oss, maxErrors, numErrors, errCode, errDesc); oss << "\n with stack trace of\n" << vtksys::SystemInformation::GetProgramStack(0, 0); result = oss.str(); return true; } return false; } } // anon namespace #define vtkOpenGLCheckStateMacro() this->CheckState() #define vtkCheckOpenGLErrorsWithStack(message) \ { \ std::string _tmp; \ if (reportOpenGLErrors(_tmp)) \ { \ vtkGenericWarningMacro("Error " << message << _tmp); \ vtkOpenGLClearErrorMacro(); \ } \ } #else // VTK_REPORT_OPENGL_ERRORS #define vtkCheckOpenGLErrorsWithStack(message) #define vtkOpenGLCheckStateMacro() #endif // VTK_REPORT_OPENGL_ERRORS // ////////////////////////////////////////////////////////////////////////////// vtkOpenGLState::BufferBindingState::BufferBindingState() { // this->Framebuffer = nullptr; this->ReadBuffer = GL_NONE; this->DrawBuffers[0] = GL_BACK; } // bool vtkOpenGLState::BufferBindingState::operator==( // const BufferBindingState& a, const BufferBindingState& b) // { // return a.Framebuffer == b.Framebuffer && // } unsigned int vtkOpenGLState::BufferBindingState::GetBinding() { // if (this->Framebuffer) // { // return this->Framebuffer->GetFBOIndex(); // } return this->Binding; } unsigned int vtkOpenGLState::BufferBindingState::GetDrawBuffer(unsigned int val) { // if (this->Framebuffer) // { // return this->Framebuffer->GetActiveDrawBuffer(val); // } return this->DrawBuffers[val]; } unsigned int vtkOpenGLState::BufferBindingState::GetReadBuffer() { // if (this->Framebuffer) // { // return this->Framebuffer->GetActiveReadBuffer(); // } return this->ReadBuffer; } vtkOpenGLState::ScopedglDepthMask::ScopedglDepthMask(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().DepthMask; this->Method = &vtkOpenGLState::vtkglDepthMask; } vtkOpenGLState::ScopedglColorMask::ScopedglColorMask(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().ColorMask; this->Method = &vtkOpenGLState::ColorMask; } vtkOpenGLState::ScopedglDepthFunc::ScopedglDepthFunc(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().DepthFunc; this->Method = &vtkOpenGLState::vtkglDepthFunc; } vtkOpenGLState::ScopedglClearColor::ScopedglClearColor(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().ClearColor; this->Method = &vtkOpenGLState::ClearColor; } vtkOpenGLState::ScopedglScissor::ScopedglScissor(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().Scissor; this->Method = &vtkOpenGLState::Scissor; } vtkOpenGLState::ScopedglViewport::ScopedglViewport(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().Viewport; this->Method = &vtkOpenGLState::Viewport; } vtkOpenGLState::ScopedglBlendFuncSeparate::ScopedglBlendFuncSeparate(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().BlendFunc; this->Method = &vtkOpenGLState::BlendFuncSeparate; } vtkOpenGLState::ScopedglActiveTexture::ScopedglActiveTexture(vtkOpenGLState* s) { this->State = s; this->Value = this->State->Stack.top().ActiveTexture; this->Method = &vtkOpenGLState::vtkglActiveTexture; } void vtkOpenGLState::ColorMask(std::array val) { this->vtkglColorMask(val[0], val[1], val[2], val[3]); } void vtkOpenGLState::vtkglColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.ColorMask[0] != r || cs.ColorMask[1] != g || cs.ColorMask[2] != b || cs.ColorMask[3] != a) #endif { cs.ColorMask[0] = r; cs.ColorMask[1] = g; cs.ColorMask[2] = b; cs.ColorMask[3] = a; ::glColorMask(r, g, b, a); } vtkCheckOpenGLErrorsWithStack("glColorMask"); } void vtkOpenGLState::ClearColor(std::array val) { this->vtkglClearColor(val[0], val[1], val[2], val[3]); } void vtkOpenGLState::vtkglClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.ClearColor[0] != red || cs.ClearColor[1] != green || cs.ClearColor[2] != blue || cs.ClearColor[3] != alpha) #endif { cs.ClearColor[0] = red; cs.ClearColor[1] = green; cs.ClearColor[2] = blue; cs.ClearColor[3] = alpha; ::glClearColor(red, green, blue, alpha); } vtkCheckOpenGLErrorsWithStack("glClearColor"); } void vtkOpenGLState::vtkglClearDepth(double val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.ClearDepth != val) #endif { cs.ClearDepth = val; #ifdef GL_ES_VERSION_3_0 ::glClearDepthf(static_cast(val)); #else ::glClearDepth(val); #endif } vtkCheckOpenGLErrorsWithStack("glClearDepth"); } void vtkOpenGLState::vtkBindFramebuffer(unsigned int target, vtkOpenGLFramebufferObject* fo) { this->vtkglBindFramebuffer(target, fo ? fo->GetFBOIndex() : 0); } void vtkOpenGLState::vtkglBindFramebuffer(unsigned int target, unsigned int val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); if (target == GL_DRAW_FRAMEBUFFER || target == GL_FRAMEBUFFER) { #ifndef NO_CACHE if (cs.DrawBinding.Binding != val) #endif { cs.DrawBinding.Binding = val; ::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, val); #ifdef GL_DRAW_BUFFER ::glGetIntegerv(GL_DRAW_BUFFER, (int*)&cs.DrawBinding.DrawBuffers[0]); #endif } } if (target == GL_READ_FRAMEBUFFER || target == GL_FRAMEBUFFER) { #ifndef NO_CACHE if (cs.ReadBinding.Binding != val) #endif { cs.ReadBinding.Binding = val; ::glBindFramebuffer(GL_READ_FRAMEBUFFER, val); ::glGetIntegerv(GL_READ_BUFFER, (int*)&cs.ReadBinding.ReadBuffer); } } vtkCheckOpenGLErrorsWithStack("glBindFramebuffer"); } void vtkOpenGLState::vtkglDrawBuffer(unsigned int val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); if (cs.DrawBinding.Binding && val < GL_COLOR_ATTACHMENT0 && val != GL_NONE) { // todo get rid of the && and make this always an error if FO is set vtkGenericWarningMacro( "A vtkOpenGLFramebufferObject is currently bound but a hardware draw buffer was requested."); std::string msg = vtksys::SystemInformation::GetProgramStack(0, 0); vtkGenericWarningMacro("at stack loc\n" << msg); } #ifndef NO_CACHE if (cs.DrawBinding.DrawBuffers[0] != val) #endif { cs.DrawBinding.DrawBuffers[0] = val; ::glDrawBuffers(1, cs.DrawBinding.DrawBuffers); } // change all stack entries for the same framebuffer for (auto& se : this->DrawBindings) { if (se.Binding == cs.DrawBinding.Binding) { se.DrawBuffers[0] = val; } } vtkCheckOpenGLErrorsWithStack("glDrawBuffer"); } void vtkOpenGLState::vtkglDrawBuffers(unsigned int count, unsigned int* vals) { vtkOpenGLCheckStateMacro(); if (count <= 0) { return; } auto& cs = this->Stack.top(); if (cs.DrawBinding.Binding && vals[0] < GL_COLOR_ATTACHMENT0 && vals[0] != GL_NONE) { // todo get rid of the && and make this always an error if FO is set vtkGenericWarningMacro( "A vtkOpenGLFramebufferObject is currently bound but hardware draw buffers were requested."); } #ifndef NO_CACHE bool changed = false; for (int i = 0; i < static_cast(count) && i < 10; ++i) { if (vals[i] != cs.DrawBinding.DrawBuffers[i]) { changed = true; } } if (count > 10) { changed = true; } if (changed) #endif { for (unsigned int i = 0; i < count && i < 10; ++i) { cs.DrawBinding.DrawBuffers[i] = vals[i]; } ::glDrawBuffers(count, vals); } // change all stack entries for the same framebuffer for (auto& se : this->DrawBindings) { if (se.Binding == cs.DrawBinding.Binding) { for (unsigned int i = 0; i < count && i < 10; ++i) { se.DrawBuffers[i] = vals[i]; } } } vtkCheckOpenGLErrorsWithStack("glDrawBuffers"); } void vtkOpenGLState::vtkDrawBuffers( unsigned int count, unsigned int* vals, vtkOpenGLFramebufferObject* fo) { vtkOpenGLCheckStateMacro(); if (count <= 0) { return; } auto& cs = this->Stack.top(); if (fo->GetFBOIndex() != cs.DrawBinding.Binding) { vtkGenericWarningMacro( "Attempt to set draw buffers from a Framebuffer Object that is not bound."); } this->vtkglDrawBuffers(count, vals); } void vtkOpenGLState::vtkglReadBuffer(unsigned int val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); if (cs.ReadBinding.Binding && val < GL_COLOR_ATTACHMENT0 && val != GL_NONE) { vtkGenericWarningMacro( "A vtkOpenGLFramebufferObject is currently bound but a hardware read buffer was requested."); } #ifndef NO_CACHE if (cs.ReadBinding.ReadBuffer != val) #endif { cs.ReadBinding.ReadBuffer = val; ::glReadBuffer(val); } // change all stack entries for the same framebuffer for (auto& se : this->ReadBindings) { if (se.Binding == cs.ReadBinding.Binding) { se.ReadBuffer = val; } } vtkCheckOpenGLErrorsWithStack("glReadBuffer"); } void vtkOpenGLState::vtkReadBuffer(unsigned int val, vtkOpenGLFramebufferObject* fo) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); if (fo->GetFBOIndex() != cs.ReadBinding.Binding) { vtkGenericWarningMacro( "Attempt to set read buffer from a Framebuffer Object that is not bound."); } this->vtkglReadBuffer(val); } void vtkOpenGLState::vtkglDepthFunc(GLenum val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.DepthFunc != val) #endif { cs.DepthFunc = val; ::glDepthFunc(val); } vtkCheckOpenGLErrorsWithStack("glDepthFunc"); } void vtkOpenGLState::vtkglDepthMask(GLboolean val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.DepthMask != val) #endif { cs.DepthMask = val; ::glDepthMask(val); } vtkCheckOpenGLErrorsWithStack("glDepthMask"); } void vtkOpenGLState::vtkglPointSize(GLfloat val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.PointSize != val) #endif { cs.PointSize = val; #ifndef GL_ES_VERSION_3_0 ::glPointSize(val); #endif } vtkCheckOpenGLErrorsWithStack("glPointSize"); } void vtkOpenGLState::vtkglLineWidth(GLfloat val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.LineWidth != val) #endif { cs.LineWidth = val; ::glLineWidth(val); } vtkCheckOpenGLErrorsWithStack("glLineWidth"); } void vtkOpenGLState::vtkglStencilMask(GLuint mask) { this->vtkglStencilMaskSeparate(GL_FRONT_AND_BACK, mask); } void vtkOpenGLState::vtkglStencilMaskSeparate(GLuint face, GLuint mask) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); if (face == GL_FRONT || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilMaskFront != mask) #endif { cs.StencilMaskFront = mask; ::glStencilMaskSeparate(GL_FRONT, mask); } } if (face == GL_BACK || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilMaskBack != mask) #endif { cs.StencilMaskBack = mask; ::glStencilMaskSeparate(GL_BACK, mask); } } vtkCheckOpenGLErrorsWithStack("glStencilMaskSeparate"); } void vtkOpenGLState::vtkglStencilOp(GLuint sfail, GLuint dpfail, GLuint dppass) { this->vtkglStencilOpSeparate(GL_FRONT_AND_BACK, sfail, dpfail, dppass); } void vtkOpenGLState::vtkglStencilOpSeparate(GLuint face, GLuint sfail, GLuint dpfail, GLuint dppass) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); std::array vals = { sfail, dpfail, dppass }; if (face == GL_FRONT || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilOpFront != vals) #endif { cs.StencilOpFront = vals; ::glStencilOpSeparate(GL_FRONT, vals[0], vals[1], vals[2]); } } if (face == GL_BACK || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilOpBack != vals) #endif { cs.StencilOpBack = vals; ::glStencilOpSeparate(GL_BACK, vals[0], vals[1], vals[2]); } } vtkCheckOpenGLErrorsWithStack("glStencilOpSeparate"); } void vtkOpenGLState::vtkglStencilFunc(GLuint func, GLint ref, GLuint mask) { this->vtkglStencilFuncSeparate(GL_FRONT_AND_BACK, func, ref, mask); } void vtkOpenGLState::vtkglStencilFuncSeparate( unsigned int face, unsigned int func, int ref, unsigned int mask) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); std::array vals = { func, static_cast(ref), mask }; if (face == GL_FRONT || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilFuncFront != vals) #endif { cs.StencilFuncFront = vals; ::glStencilFuncSeparate(GL_FRONT, vals[0], static_cast(vals[1]), vals[2]); } } if (face == GL_BACK || face == GL_FRONT_AND_BACK) { #ifndef NO_CACHE if (cs.StencilFuncBack != vals) #endif { cs.StencilFuncBack = vals; ::glStencilFuncSeparate(GL_BACK, vals[0], static_cast(vals[1]), vals[2]); } } vtkCheckOpenGLErrorsWithStack("glStencilFuncSeparate"); } void vtkOpenGLState::BlendFuncSeparate(std::array val) { this->vtkglBlendFuncSeparate(val[0], val[1], val[2], val[3]); } void vtkOpenGLState::vtkglBlendFuncSeparate(GLenum val1, GLenum val2, GLenum val3, GLenum val4) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.BlendFunc[0] != val1 || cs.BlendFunc[1] != val2 || cs.BlendFunc[2] != val3 || cs.BlendFunc[3] != val4) #endif { cs.BlendFunc[0] = val1; cs.BlendFunc[1] = val2; cs.BlendFunc[2] = val3; cs.BlendFunc[3] = val4; ::glBlendFuncSeparate(val1, val2, val3, val4); } vtkCheckOpenGLErrorsWithStack("glBlendFuncSeparate"); } void vtkOpenGLState::vtkglBlendEquation(GLenum val) { this->vtkglBlendEquationSeparate(val, val); } void vtkOpenGLState::vtkglBlendEquationSeparate(GLenum val, GLenum val2) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.BlendEquationValue1 != val || cs.BlendEquationValue2 != val2) #endif { cs.BlendEquationValue1 = val; cs.BlendEquationValue2 = val2; ::glBlendEquationSeparate(val, val2); } vtkCheckOpenGLErrorsWithStack("glBlendEquationSeparate"); } void vtkOpenGLState::vtkglCullFace(GLenum val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.CullFaceMode != val) #endif { cs.CullFaceMode = val; ::glCullFace(val); } vtkCheckOpenGLErrorsWithStack("glCullFace"); } void vtkOpenGLState::vtkglActiveTexture(unsigned int val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.ActiveTexture != val) #endif { cs.ActiveTexture = val; ::glActiveTexture(val); } vtkCheckOpenGLErrorsWithStack("glActiveTexture"); } void vtkOpenGLState::Viewport(std::array val) { this->vtkglViewport(val[0], val[1], val[2], val[3]); } void vtkOpenGLState::vtkglViewport(GLint x, GLint y, GLsizei width, GLsizei height) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #if !defined(NO_CACHE) && !defined(__APPLE__) if (cs.Viewport[0] != x || cs.Viewport[1] != y || cs.Viewport[2] != width || cs.Viewport[3] != height) #endif { cs.Viewport[0] = x; cs.Viewport[1] = y; cs.Viewport[2] = width; cs.Viewport[3] = height; ::glViewport(x, y, width, height); } vtkCheckOpenGLErrorsWithStack("glViewport"); } void vtkOpenGLState::Scissor(std::array val) { this->vtkglScissor(val[0], val[1], val[2], val[3]); } void vtkOpenGLState::vtkglScissor(GLint x, GLint y, GLsizei width, GLsizei height) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE if (cs.Scissor[0] != x || cs.Scissor[1] != y || cs.Scissor[2] != width || cs.Scissor[3] != height) #endif { cs.Scissor[0] = x; cs.Scissor[1] = y; cs.Scissor[2] = width; cs.Scissor[3] = height; ::glScissor(x, y, width, height); } vtkCheckOpenGLErrorsWithStack("glScissor"); } void vtkOpenGLState::SetEnumState(GLenum cap, bool val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE bool changed = false; #else bool changed = true; #endif switch (cap) { case GL_BLEND: if (cs.Blend != val) { cs.Blend = val; changed = true; } break; case GL_CULL_FACE: if (cs.CullFace != val) { cs.CullFace = val; changed = true; } break; case GL_DEPTH_TEST: if (cs.DepthTest != val) { cs.DepthTest = val; changed = true; } break; #ifdef GL_LINE_SMOOTH case GL_LINE_SMOOTH: if (cs.LineSmooth != val) { cs.LineSmooth = val; changed = true; } break; #endif #ifdef GL_MULTISAMPLE case GL_MULTISAMPLE: if (cs.MultiSample != val) { cs.MultiSample = val; changed = true; } break; #endif case GL_SCISSOR_TEST: if (cs.ScissorTest != val) { cs.ScissorTest = val; changed = true; } break; case GL_STENCIL_TEST: if (cs.StencilTest != val) { cs.StencilTest = val; changed = true; } break; #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS case GL_TEXTURE_CUBE_MAP_SEAMLESS: if (cs.CubeMapSeamless != val) { cs.CubeMapSeamless = val; changed = true; } break; #endif default: changed = true; } if (!changed) { return; } if (val) { ::glEnable(cap); } else { ::glDisable(cap); } vtkCheckOpenGLErrorsWithStack("glEnable/Disable"); } void vtkOpenGLState::ResetEnumState(GLenum cap) { GLboolean params; ::glGetBooleanv(cap, ¶ms); auto& cs = this->Stack.top(); switch (cap) { case GL_BLEND: cs.Blend = params != 0; break; case GL_CULL_FACE: cs.CullFace = params != 0; break; case GL_DEPTH_TEST: cs.DepthTest = params != 0; break; #ifdef GL_LINE_SMOOTH case GL_LINE_SMOOTH: cs.LineSmooth = params != 0; break; #endif #ifdef GL_MULTISAMPLE case GL_MULTISAMPLE: cs.MultiSample = params != 0; break; #endif case GL_SCISSOR_TEST: cs.ScissorTest = params != 0; break; case GL_STENCIL_TEST: cs.StencilTest = params != 0; break; #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS case GL_TEXTURE_CUBE_MAP_SEAMLESS: cs.CubeMapSeamless = params != 0; break; #endif default: break; } } void vtkOpenGLState::vtkglEnable(GLenum cap) { this->SetEnumState(cap, true); } // return cached value if we have it // otherwise forward to opengl void vtkOpenGLState::vtkglGetBooleanv(GLenum pname, GLboolean* params) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); switch (pname) { case GL_DEPTH_WRITEMASK: *params = cs.DepthMask; break; case GL_COLOR_WRITEMASK: params[0] = cs.ColorMask[0]; params[1] = cs.ColorMask[1]; params[2] = cs.ColorMask[2]; params[3] = cs.ColorMask[3]; break; case GL_BLEND: *params = cs.Blend; break; case GL_CULL_FACE: *params = cs.CullFace; break; case GL_DEPTH_TEST: *params = cs.DepthTest; break; #ifdef GL_LINE_SMOOTH case GL_LINE_SMOOTH: *params = cs.LineSmooth; break; #endif #ifdef GL_MULTISAMPLE case GL_MULTISAMPLE: *params = cs.MultiSample; break; #endif case GL_SCISSOR_TEST: *params = cs.ScissorTest; break; case GL_STENCIL_TEST: *params = cs.StencilTest; break; #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS case GL_TEXTURE_CUBE_MAP_SEAMLESS: *params = cs.CubeMapSeamless; break; #endif default: ::glGetBooleanv(pname, params); } vtkCheckOpenGLErrorsWithStack("glGetBoolean"); } void vtkOpenGLState::vtkglGetIntegerv(GLenum pname, GLint* params) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); switch (pname) { case GL_VIEWPORT: params[0] = cs.Viewport[0]; params[1] = cs.Viewport[1]; params[2] = cs.Viewport[2]; params[3] = cs.Viewport[3]; break; case GL_SCISSOR_BOX: params[0] = cs.Scissor[0]; params[1] = cs.Scissor[1]; params[2] = cs.Scissor[2]; params[3] = cs.Scissor[3]; break; case GL_CULL_FACE_MODE: *params = cs.CullFaceMode; break; case GL_DEPTH_FUNC: *params = cs.DepthFunc; break; case GL_BLEND_SRC_RGB: *params = cs.BlendFunc[0]; break; case GL_BLEND_SRC_ALPHA: *params = cs.BlendFunc[2]; break; case GL_BLEND_DST_RGB: *params = cs.BlendFunc[1]; break; case GL_BLEND_DST_ALPHA: *params = cs.BlendFunc[3]; break; case GL_MAX_TEXTURE_SIZE: *params = this->MaxTextureSize; break; case GL_MAJOR_VERSION: *params = this->MajorVersion; break; case GL_MINOR_VERSION: *params = this->MinorVersion; break; default: ::glGetIntegerv(pname, params); } vtkCheckOpenGLErrorsWithStack("glGetInteger"); } #ifdef GL_ES_VERSION_3_0 void vtkOpenGLState::vtkglGetDoublev(GLenum pname, double*) { vtkGenericWarningMacro("glGetDouble not supported on OpenGL ES, requested: " << pname); } #else void vtkOpenGLState::vtkglGetDoublev(GLenum pname, double* params) { vtkOpenGLCheckStateMacro(); ::glGetDoublev(pname, params); vtkCheckOpenGLErrorsWithStack("glGetDouble"); } #endif void vtkOpenGLState::vtkglGetFloatv(GLenum pname, GLfloat* params) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); switch (pname) { case GL_COLOR_CLEAR_VALUE: params[0] = cs.ClearColor[0]; params[1] = cs.ClearColor[1]; params[2] = cs.ClearColor[2]; params[3] = cs.ClearColor[3]; break; default: ::glGetFloatv(pname, params); } vtkCheckOpenGLErrorsWithStack("glGetFloat"); } void vtkOpenGLState::GetBlendFuncState(int* v) { auto& cs = this->Stack.top(); v[0] = cs.BlendFunc[0]; v[1] = cs.BlendFunc[1]; v[2] = cs.BlendFunc[2]; v[3] = cs.BlendFunc[3]; } bool vtkOpenGLState::GetEnumState(GLenum cap) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); switch (cap) { case GL_BLEND: return cs.Blend; case GL_CULL_FACE: return cs.CullFace; case GL_DEPTH_TEST: return cs.DepthTest; #ifdef GL_LINE_SMOOTH case GL_LINE_SMOOTH: return cs.LineSmooth; #endif #ifdef GL_MULTISAMPLE case GL_MULTISAMPLE: return cs.MultiSample; #endif case GL_SCISSOR_TEST: return cs.ScissorTest; case GL_STENCIL_TEST: return cs.StencilTest; #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS case GL_TEXTURE_CUBE_MAP_SEAMLESS: return cs.CubeMapSeamless; #endif default: vtkGenericWarningMacro("Bad request for enum status"); } return false; } void vtkOpenGLState::vtkglDisable(GLenum cap) { this->SetEnumState(cap, false); } void vtkOpenGLState::vtkglPixelStorei(unsigned int cap, int val) { vtkOpenGLCheckStateMacro(); auto& cs = this->Stack.top(); #ifndef NO_CACHE bool changed = false; #else bool changed = true; #endif switch (cap) { case GL_PACK_ALIGNMENT: if (cs.PackAlignment != val) { cs.PackAlignment = val; changed = true; } break; case GL_UNPACK_ALIGNMENT: if (cs.UnpackAlignment != val) { cs.UnpackAlignment = val; changed = true; } break; case GL_UNPACK_ROW_LENGTH: if (cs.UnpackRowLength != val) { cs.UnpackRowLength = val; changed = true; } break; case GL_UNPACK_IMAGE_HEIGHT: if (cs.UnpackImageHeight != val) { cs.UnpackImageHeight = val; changed = true; } break; default: changed = true; } if (!changed) { return; } ::glPixelStorei(cap, val); vtkCheckOpenGLErrorsWithStack("glPixelStorei"); } // make the recorded state match OpenGL driver // Initialize makes OpenGL match the state // This makes the state match OpenGL void vtkOpenGLState::Reset() { vtkOpenGLRenderUtilities::MarkDebugEvent("Resetting OpenGL State"); this->ResetGLClearColorState(); this->ResetGLClearDepthState(); this->ResetGLDepthFuncState(); this->ResetGLDepthMaskState(); this->ResetGLColorMaskState(); this->ResetGLViewportState(); this->ResetGLScissorState(); this->ResetGLBlendFuncState(); this->ResetGLBlendEquationState(); this->ResetGLCullFaceState(); this->ResetGLActiveTexture(); this->ResetFramebufferBindings(); this->ResetEnumState(GL_BLEND); this->ResetEnumState(GL_CULL_FACE); this->ResetEnumState(GL_DEPTH_TEST); #ifdef GL_LINE_SMOOTH this->ResetEnumState(GL_LINE_SMOOTH); #endif this->ResetEnumState(GL_STENCIL_TEST); this->ResetEnumState(GL_SCISSOR_TEST); #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS this->ResetEnumState(GL_TEXTURE_CUBE_MAP_SEAMLESS); #endif #ifdef GL_MULTISAMPLE this->ResetEnumState(GL_MULTISAMPLE); #endif auto& cs = this->Stack.top(); GLint ival; #ifdef GL_POINT_SIZE ::glGetFloatv(GL_POINT_SIZE, &cs.PointSize); #endif ::glGetFloatv(GL_LINE_WIDTH, &cs.LineWidth); ::glGetIntegerv(GL_PACK_ALIGNMENT, &cs.PackAlignment); ::glGetIntegerv(GL_UNPACK_ALIGNMENT, &cs.UnpackAlignment); ::glGetIntegerv(GL_UNPACK_ROW_LENGTH, &cs.UnpackRowLength); ::glGetIntegerv(GL_UNPACK_IMAGE_HEIGHT, &cs.UnpackImageHeight); ::glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &ival); cs.StencilMaskBack = static_cast(ival); ::glGetIntegerv(GL_STENCIL_WRITEMASK, &ival); cs.StencilMaskFront = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_FAIL, &ival); cs.StencilOpBack[0] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &ival); cs.StencilOpBack[1] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &ival); cs.StencilOpBack[2] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_FAIL, &ival); cs.StencilOpFront[0] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &ival); cs.StencilOpFront[1] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &ival); cs.StencilOpFront[2] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_FUNC, &ival); cs.StencilFuncBack[0] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_REF, &ival); cs.StencilFuncBack[1] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &ival); cs.StencilFuncBack[2] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_FUNC, &ival); cs.StencilFuncFront[0] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_REF, &ival); cs.StencilFuncFront[1] = static_cast(ival); ::glGetIntegerv(GL_STENCIL_VALUE_MASK, &ival); cs.StencilFuncFront[2] = static_cast(ival); ::glGetIntegerv(GL_CURRENT_PROGRAM, &cs.BoundProgram); ::glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &cs.BoundVAO); ::glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &cs.BoundArrayBuffer); ::glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &cs.BoundElementArrayBuffer); vtkOpenGLRenderUtilities::MarkDebugEvent("Finished Resetting OpenGL State"); } void vtkOpenGLState::Push() { vtkOpenGLRenderUtilities::MarkDebugEvent("Pushing OpenGL State"); this->Stack.push(this->Stack.top()); this->PushFramebufferBindings(); vtkOpenGLRenderUtilities::MarkDebugEvent("Finished Pushing OpenGL State"); } void vtkOpenGLState::Pop() { vtkOpenGLRenderUtilities::MarkDebugEvent("Popping OpenGL State"); this->Stack.pop(); auto& cs = this->Stack.top(); this->PopFramebufferBindings(); cs.Blend ? ::glEnable(GL_BLEND) : ::glDisable(GL_BLEND); cs.DepthTest ? ::glEnable(GL_DEPTH_TEST) : ::glDisable(GL_DEPTH_TEST); #ifdef GL_LINE_SMOOTH cs.LineSmooth ? ::glEnable(GL_LINE_SMOOTH) : ::glDisable(GL_LINE_SMOOTH); #endif cs.StencilTest ? ::glEnable(GL_STENCIL_TEST) : ::glDisable(GL_STENCIL_TEST); cs.ScissorTest ? ::glEnable(GL_SCISSOR_TEST) : ::glDisable(GL_SCISSOR_TEST); cs.CullFace ? ::glEnable(GL_CULL_FACE) : ::glDisable(GL_CULL_FACE); #ifdef GL_TEXTURE_CUBE_MAP_SEAMLESS cs.CubeMapSeamless ? ::glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS) : ::glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS); #endif #ifdef GL_MULTISAMPLE cs.MultiSample = glIsEnabled(GL_MULTISAMPLE) == GL_TRUE; #endif ::glBlendFuncSeparate(cs.BlendFunc[0], cs.BlendFunc[1], cs.BlendFunc[2], cs.BlendFunc[3]); ::glClearColor(cs.ClearColor[0], cs.ClearColor[1], cs.ClearColor[2], cs.ClearColor[3]); ::glColorMask(cs.ColorMask[0], cs.ColorMask[1], cs.ColorMask[2], cs.ColorMask[3]); ::glDepthFunc(cs.DepthFunc); #ifdef GL_ES_VERSION_3_0 ::glClearDepthf(cs.ClearDepth); #else ::glClearDepth(cs.ClearDepth); #endif ::glDepthMask(cs.DepthMask); #ifndef GL_ES_VERSION_3_0 ::glPointSize(cs.PointSize); #endif ::glLineWidth(cs.LineWidth); ::glPixelStorei(GL_PACK_ALIGNMENT, cs.PackAlignment); ::glPixelStorei(GL_UNPACK_ALIGNMENT, cs.UnpackAlignment); ::glPixelStorei(GL_UNPACK_ROW_LENGTH, cs.UnpackRowLength); ::glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, cs.UnpackImageHeight); ::glStencilMaskSeparate(GL_FRONT, cs.StencilMaskFront); ::glStencilMaskSeparate(GL_BACK, cs.StencilMaskBack); ::glStencilOpSeparate(GL_FRONT, cs.StencilOpFront[0], cs.StencilOpFront[1], cs.StencilOpFront[2]); ::glStencilOpSeparate(GL_BACK, cs.StencilOpBack[0], cs.StencilOpBack[1], cs.StencilOpBack[2]); ::glStencilFuncSeparate(GL_FRONT, cs.StencilFuncFront[0], static_cast(cs.StencilFuncFront[1]), cs.StencilFuncFront[2]); ::glStencilFuncSeparate(GL_BACK, cs.StencilFuncBack[0], static_cast(cs.StencilFuncBack[1]), cs.StencilFuncBack[2]); ::glViewport(cs.Viewport[0], cs.Viewport[1], cs.Viewport[2], cs.Viewport[3]); ::glScissor(cs.Scissor[0], cs.Scissor[1], cs.Scissor[2], cs.Scissor[3]); ::glCullFace(cs.CullFaceMode); ::glBlendEquationSeparate(cs.BlendEquationValue1, cs.BlendEquationValue2); if (this->ShaderCache) { this->ShaderCache->ReleaseCurrentShader(); } ::glUseProgram(cs.BoundProgram); ::glActiveTexture(cs.ActiveTexture); ::glBindVertexArray(cs.BoundVAO); ::glBindBuffer(GL_ARRAY_BUFFER, cs.BoundArrayBuffer); ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cs.BoundElementArrayBuffer); vtkOpenGLRenderUtilities::MarkDebugEvent("Finished Popping OpenGL State"); } // make the hardware openglstate match the // state ivars void vtkOpenGLState::Initialize(vtkOpenGLRenderWindow*) { this->TextureUnitManager->Initialize(); this->InitializeTextureInternalFormats(); auto& cs = this->Stack.top(); cs.Blend ? ::glEnable(GL_BLEND) : ::glDisable(GL_BLEND); cs.CullFace ? ::glEnable(GL_CULL_FACE) : ::glDisable(GL_CULL_FACE); cs.DepthTest ? ::glEnable(GL_DEPTH_TEST) : ::glDisable(GL_DEPTH_TEST); #ifdef GL_LINE_SMOOTH cs.LineSmooth ? ::glEnable(GL_LINE_SMOOTH) : ::glDisable(GL_LINE_SMOOTH); #endif #ifdef GL_MULTISAMPLE cs.MultiSample = glIsEnabled(GL_MULTISAMPLE) == GL_TRUE; #endif cs.StencilTest ? ::glEnable(GL_STENCIL_TEST) : ::glDisable(GL_STENCIL_TEST); cs.ScissorTest ? ::glEnable(GL_SCISSOR_TEST) : ::glDisable(GL_SCISSOR_TEST); // initialize blending for transparency ::glBlendFuncSeparate(cs.BlendFunc[0], cs.BlendFunc[1], cs.BlendFunc[2], cs.BlendFunc[3]); ::glClearColor(cs.ClearColor[0], cs.ClearColor[1], cs.ClearColor[2], cs.ClearColor[3]); ::glColorMask(cs.ColorMask[0], cs.ColorMask[1], cs.ColorMask[2], cs.ColorMask[3]); ::glDepthFunc(cs.DepthFunc); #ifndef GL_ES_VERSION_3_0 ::glPointSize(cs.PointSize); #endif ::glLineWidth(cs.LineWidth); ::glPixelStorei(GL_PACK_ALIGNMENT, cs.PackAlignment); ::glPixelStorei(GL_UNPACK_ALIGNMENT, cs.UnpackAlignment); ::glPixelStorei(GL_UNPACK_ROW_LENGTH, cs.UnpackRowLength); ::glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, cs.UnpackImageHeight); ::glStencilMaskSeparate(GL_FRONT, cs.StencilMaskFront); ::glStencilMaskSeparate(GL_BACK, cs.StencilMaskBack); ::glStencilOpSeparate(GL_FRONT, cs.StencilOpFront[0], cs.StencilOpFront[1], cs.StencilOpFront[2]); ::glStencilOpSeparate(GL_BACK, cs.StencilOpBack[0], cs.StencilOpBack[1], cs.StencilOpBack[2]); ::glStencilFuncSeparate(GL_FRONT, cs.StencilFuncFront[0], static_cast(cs.StencilFuncFront[1]), cs.StencilFuncFront[2]); ::glStencilFuncSeparate(GL_BACK, cs.StencilFuncBack[0], static_cast(cs.StencilFuncBack[1]), cs.StencilFuncBack[2]); #ifdef GL_ES_VERSION_3_0 ::glClearDepthf(cs.ClearDepth); #else ::glClearDepth(cs.ClearDepth); #endif ::glDepthMask(cs.DepthMask); ::glViewport(cs.Viewport[0], cs.Viewport[1], cs.Viewport[2], cs.Viewport[3]); ::glScissor(cs.Scissor[0], cs.Scissor[1], cs.Scissor[2], cs.Scissor[3]); ::glCullFace(cs.CullFaceMode); ::glBlendEquationSeparate(cs.BlendEquationValue1, cs.BlendEquationValue2); // strictly query values below here ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &this->MaxTextureSize); ::glGetIntegerv(GL_MAJOR_VERSION, &this->MajorVersion); ::glGetIntegerv(GL_MINOR_VERSION, &this->MinorVersion); char const* tmp = reinterpret_cast(::glGetString(GL_VENDOR)); this->Vendor = tmp ? tmp : std::string(); tmp = reinterpret_cast(::glGetString(GL_RENDERER)); this->Renderer = tmp ? tmp : std::string(); tmp = reinterpret_cast(::glGetString(GL_VERSION)); this->Version = tmp ? tmp : std::string(); this->ResetFramebufferBindings(); } void vtkOpenGLState::GetCurrentDrawFramebufferState( unsigned int& drawBinding, unsigned int& drawBuffer) { auto& cs = this->Stack.top(); drawBinding = cs.DrawBinding.Binding; drawBuffer = cs.DrawBinding.DrawBuffers[0]; } void vtkOpenGLState::ResetFramebufferBindings() { auto& cs = this->Stack.top(); ::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&cs.DrawBinding.Binding); #ifdef GL_DRAW_BUFFER ::glGetIntegerv(GL_DRAW_BUFFER, (int*)&cs.DrawBinding.DrawBuffers[0]); #endif ::glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (int*)&cs.ReadBinding.Binding); ::glGetIntegerv(GL_READ_BUFFER, (int*)&cs.ReadBinding.ReadBuffer); } void vtkOpenGLState::ResetGLClearColorState() { auto& cs = this->Stack.top(); GLfloat fparams[4]; ::glGetFloatv(GL_COLOR_CLEAR_VALUE, fparams); cs.ClearColor[0] = fparams[0]; cs.ClearColor[1] = fparams[1]; cs.ClearColor[2] = fparams[2]; cs.ClearColor[3] = fparams[3]; } void vtkOpenGLState::ResetGLClearDepthState() { auto& cs = this->Stack.top(); GLfloat fparams; ::glGetFloatv(GL_DEPTH_CLEAR_VALUE, &fparams); cs.ClearDepth = fparams; } void vtkOpenGLState::ResetGLDepthFuncState() { auto& cs = this->Stack.top(); GLint iparams; ::glGetIntegerv(GL_DEPTH_FUNC, &iparams); cs.DepthFunc = static_cast(iparams); } void vtkOpenGLState::ResetGLDepthMaskState() { auto& cs = this->Stack.top(); GLboolean params; ::glGetBooleanv(GL_DEPTH_WRITEMASK, ¶ms); cs.DepthMask = params; } void vtkOpenGLState::ResetGLColorMaskState() { auto& cs = this->Stack.top(); GLboolean params[4]; ::glGetBooleanv(GL_COLOR_WRITEMASK, params); cs.ColorMask[0] = params[0]; cs.ColorMask[1] = params[1]; cs.ColorMask[2] = params[2]; cs.ColorMask[3] = params[3]; } void vtkOpenGLState::ResetGLViewportState() { auto& cs = this->Stack.top(); GLint iparams[4]; ::glGetIntegerv(GL_VIEWPORT, iparams); cs.Viewport[0] = iparams[0]; cs.Viewport[1] = iparams[1]; cs.Viewport[2] = iparams[2]; cs.Viewport[3] = iparams[3]; } void vtkOpenGLState::ResetGLScissorState() { auto& cs = this->Stack.top(); GLint iparams[4]; ::glGetIntegerv(GL_SCISSOR_BOX, iparams); cs.Scissor[0] = iparams[0]; cs.Scissor[1] = iparams[1]; cs.Scissor[2] = iparams[2]; cs.Scissor[3] = iparams[3]; } void vtkOpenGLState::ResetGLBlendFuncState() { auto& cs = this->Stack.top(); GLint iparams; ::glGetIntegerv(GL_BLEND_SRC_RGB, &iparams); cs.BlendFunc[0] = static_cast(iparams); ::glGetIntegerv(GL_BLEND_SRC_ALPHA, &iparams); cs.BlendFunc[2] = static_cast(iparams); ::glGetIntegerv(GL_BLEND_DST_RGB, &iparams); cs.BlendFunc[1] = static_cast(iparams); ::glGetIntegerv(GL_BLEND_DST_ALPHA, &iparams); cs.BlendFunc[3] = static_cast(iparams); } void vtkOpenGLState::ResetGLBlendEquationState() { auto& cs = this->Stack.top(); GLint iparams; ::glGetIntegerv(GL_BLEND_EQUATION_RGB, &iparams); cs.BlendEquationValue1 = static_cast(iparams); ::glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &iparams); cs.BlendEquationValue2 = static_cast(iparams); } void vtkOpenGLState::ResetGLCullFaceState() { auto& cs = this->Stack.top(); GLint iparams; ::glGetIntegerv(GL_CULL_FACE_MODE, &iparams); cs.CullFaceMode = static_cast(iparams); } void vtkOpenGLState::ResetGLActiveTexture() { auto& cs = this->Stack.top(); GLint iparams; ::glGetIntegerv(GL_ACTIVE_TEXTURE, &iparams); cs.ActiveTexture = static_cast(iparams); } void vtkOpenGLState::vtkglClear(GLbitfield val) { ::glClear(val); } void vtkOpenGLState::vtkglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, unsigned int mask, unsigned int filter) { // ON APPLE MACOS you must turn off scissor test for DEPTH blits to work vtkOpenGLState::ScopedglEnableDisable stsaver(this, GL_SCISSOR_TEST); this->vtkglDisable(GL_SCISSOR_TEST); ::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } //------------------------------------------------------------------------------ // Description: // Returns its texture unit manager object. vtkTextureUnitManager* vtkOpenGLState::GetTextureUnitManager() { return this->TextureUnitManager; } void vtkOpenGLState::SetTextureUnitManager(vtkTextureUnitManager* tum) { if (this->TextureUnitManager == tum) { return; } if (tum) { tum->Register(nullptr); } if (this->TextureUnitManager) { this->TextureUnitManager->Delete(); } this->TextureUnitManager = tum; } void vtkOpenGLState::ActivateTexture(vtkTextureObject* texture) { // Only add if it isn't already there typedef std::map::const_iterator TRIter; TRIter found = this->TextureResourceIds.find(texture); if (found == this->TextureResourceIds.end()) { int activeUnit = this->GetTextureUnitManager()->Allocate(); if (activeUnit < 0) { vtkGenericWarningMacro("Hardware does not support the number of textures defined."); return; } this->TextureResourceIds.insert(std::make_pair(texture, activeUnit)); this->vtkglActiveTexture(GL_TEXTURE0 + activeUnit); } else { this->vtkglActiveTexture(GL_TEXTURE0 + found->second); } } void vtkOpenGLState::DeactivateTexture(vtkTextureObject* texture) { // Only deactivate if it isn't already there typedef std::map::iterator TRIter; TRIter found = this->TextureResourceIds.find(texture); if (found != this->TextureResourceIds.end()) { this->GetTextureUnitManager()->Free(found->second); this->TextureResourceIds.erase(found); } } int vtkOpenGLState::GetTextureUnitForTexture(vtkTextureObject* texture) { // Only deactivate if it isn't already there typedef std::map::const_iterator TRIter; TRIter found = this->TextureResourceIds.find(texture); if (found != this->TextureResourceIds.end()) { return found->second; } return -1; } void vtkOpenGLState::VerifyNoActiveTextures() { if (!this->TextureResourceIds.empty()) { vtkGenericWarningMacro("There are still active textures when there should not be."); typedef std::map::const_iterator TRIter; TRIter found = this->TextureResourceIds.begin(); for (; found != this->TextureResourceIds.end(); ++found) { vtkGenericWarningMacro( "Leaked for texture object: " << const_cast(found->first)); } } } vtkStandardNewMacro(vtkOpenGLState); void vtkOpenGLState::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "MajorVersion: " << this->MajorVersion << endl; os << indent << "MinorVersion: " << this->MinorVersion << endl; os << indent << "MaxTextureSize: " << this->MaxTextureSize << endl; os << indent << "Vendor: " << this->Vendor << endl; os << indent << "Renderer: " << this->Renderer << endl; os << indent << "Version: " << this->Version << endl; } vtkCxxSetObjectMacro(vtkOpenGLState, VBOCache, vtkOpenGLVertexBufferObjectCache); // initialize all state values. This is important so that in // ::Initialize we can just set the state to the current // values (knowing that they are set). The reason we want // Initialize to set to the current values is to reduce // OpenGL churn in cases where applications call Initialize // often without really changing many of the values. For example // // viewport(0,0,100,100); // Initialize(0,0,1,1); // using hardcoded initialization // viewport(0,0,100,100); // // versus // // viewport(0,0,100,100); // Initialize(0,0,100,100); // using last value // viewport(0,0,100,100); // cache will skip this line // // Using current values avoids extra state changes when // not required. // vtkOpenGLState::vtkOpenGLState() { this->ShaderCache = vtkOpenGLShaderCache::New(); this->VBOCache = vtkOpenGLVertexBufferObjectCache::New(); this->TextureUnitManager = vtkTextureUnitManager::New(); this->Stack.push(GLState()); auto& cs = this->Stack.top(); cs.Blend = true; cs.DepthTest = true; cs.StencilTest = false; cs.ScissorTest = true; cs.CullFace = false; cs.CubeMapSeamless = false; cs.LineSmooth = false; cs.MultiSample = false; cs.LineWidth = 1.0; cs.PointSize = 1.0; cs.StencilMaskFront = 0xFF; cs.StencilMaskBack = 0xFF; cs.StencilOpFront[0] = GL_KEEP; cs.StencilOpFront[1] = GL_KEEP; cs.StencilOpFront[2] = GL_KEEP; cs.StencilOpBack[0] = GL_KEEP; cs.StencilOpBack[1] = GL_KEEP; cs.StencilOpBack[2] = GL_KEEP; cs.StencilFuncFront[0] = GL_ALWAYS; cs.StencilFuncFront[1] = 0; cs.StencilFuncFront[2] = 0xFF; cs.StencilFuncBack[0] = GL_ALWAYS; cs.StencilFuncBack[1] = 0; cs.StencilFuncBack[2] = 0xFF; cs.PackAlignment = 1; cs.UnpackAlignment = 1; cs.UnpackRowLength = 0; cs.UnpackImageHeight = 0; // initialize blending for transparency cs.BlendFunc[0] = GL_SRC_ALPHA; cs.BlendFunc[1] = GL_ONE_MINUS_SRC_ALPHA; cs.BlendFunc[2] = GL_ONE; cs.BlendFunc[3] = GL_ONE_MINUS_SRC_ALPHA; cs.ClearColor[0] = 0.0; cs.ClearColor[1] = 0.0; cs.ClearColor[2] = 0.0; cs.ClearColor[3] = 0.0; cs.ColorMask[0] = GL_TRUE; cs.ColorMask[1] = GL_TRUE; cs.ColorMask[2] = GL_TRUE; cs.ColorMask[3] = GL_TRUE; cs.DepthFunc = GL_LEQUAL; cs.ClearDepth = 1.0; cs.DepthMask = GL_TRUE; cs.Viewport[0] = 0; cs.Viewport[1] = 0; cs.Viewport[2] = 1; cs.Viewport[3] = 1; cs.Scissor[0] = 0; cs.Scissor[1] = 0; cs.Scissor[2] = 1; cs.Scissor[3] = 1; cs.CullFaceMode = GL_BACK; cs.ActiveTexture = GL_TEXTURE0; cs.BlendEquationValue1 = GL_FUNC_ADD; cs.BlendEquationValue2 = GL_FUNC_ADD; cs.DrawBinding.Binding = 0; cs.ReadBinding.Binding = 0; cs.DrawBinding.DrawBuffers[0] = GL_BACK_LEFT; for (int i = 1; i < 10; ++i) { cs.DrawBinding.DrawBuffers[i] = GL_NONE; } cs.ReadBinding.ReadBuffer = GL_BACK_LEFT; } vtkOpenGLState::~vtkOpenGLState() { this->TextureResourceIds.clear(); this->SetTextureUnitManager(nullptr); this->VBOCache->Delete(); this->ShaderCache->Delete(); } void vtkOpenGLState::PushDrawFramebufferBinding() { auto& cs = this->Stack.top(); this->DrawBindings.push_back(cs.DrawBinding); } void vtkOpenGLState::PushReadFramebufferBinding() { auto& cs = this->Stack.top(); this->ReadBindings.push_back(cs.ReadBinding); } void vtkOpenGLState::PopDrawFramebufferBinding() { if (!this->DrawBindings.empty()) { auto& cs = this->Stack.top(); BufferBindingState& bbs = this->DrawBindings.back(); ::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bbs.GetBinding()); cs.DrawBinding = bbs; this->DrawBindings.pop_back(); } else { vtkGenericWarningMacro("Attempt to pop framebuffer beyond beginning of the stack."); abort(); } } void vtkOpenGLState::PopReadFramebufferBinding() { if (!this->ReadBindings.empty()) { auto& cs = this->Stack.top(); BufferBindingState& bbs = this->ReadBindings.back(); ::glBindFramebuffer(GL_READ_FRAMEBUFFER, bbs.GetBinding()); cs.ReadBinding = bbs; this->ReadBindings.pop_back(); } else { vtkGenericWarningMacro("Attempt to pop framebuffer beyond beginning of the stack."); abort(); } } int vtkOpenGLState::GetDefaultTextureInternalFormat( int vtktype, int numComponents, bool needInt, bool needFloat, bool needSRGB) { // 0 = none // 1 = float // 2 = int int dtypeCount = sizeof(this->TextureInternalFormats) / sizeof(this->TextureInternalFormats[0]); if (vtktype >= dtypeCount) { return 0; } if (needInt) { return this->TextureInternalFormats[vtktype][2][numComponents]; } if (needFloat) { return this->TextureInternalFormats[vtktype][1][numComponents]; } int result = this->TextureInternalFormats[vtktype][0][numComponents]; if (needSRGB) { switch (result) { #ifdef GL_ES_VERSION_3_0 case GL_RGB: result = GL_SRGB8; break; case GL_RGBA: result = GL_SRGB8_ALPHA8; break; #else case GL_RGB: result = GL_SRGB; break; case GL_RGBA: result = GL_SRGB_ALPHA; break; #endif case GL_RGB8: result = GL_SRGB8; break; case GL_RGBA8: result = GL_SRGB8_ALPHA8; break; default: break; } } return result; } void vtkOpenGLState::InitializeTextureInternalFormats() { // 0 = none // 1 = float // 2 = int // initialize to zero int dtypeCount = sizeof(this->TextureInternalFormats) / sizeof(this->TextureInternalFormats[0]); for (int dtype = 0; dtype < dtypeCount; dtype++) { for (int ctype = 0; ctype < 3; ctype++) { for (int comp = 0; comp < 5; comp++) { this->TextureInternalFormats[dtype][ctype][comp] = 0; } } } this->TextureInternalFormats[VTK_VOID][0][1] = GL_DEPTH_COMPONENT; #ifdef GL_R8 this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][1] = GL_R8; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][2] = GL_RG8; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][3] = GL_RGB8; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][4] = GL_RGBA8; #else this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][1] = GL_LUMINANCE; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][2] = GL_LUMINANCE_ALPHA; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][3] = GL_RGB; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][0][4] = GL_RGBA; #endif #ifdef GL_R16 this->TextureInternalFormats[VTK_UNSIGNED_SHORT][0][1] = GL_R16; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][0][2] = GL_RG16; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][0][3] = GL_RGB16; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][0][4] = GL_RGBA16; #endif #ifdef GL_R8_SNORM this->TextureInternalFormats[VTK_SIGNED_CHAR][0][1] = GL_R8_SNORM; this->TextureInternalFormats[VTK_SIGNED_CHAR][0][2] = GL_RG8_SNORM; this->TextureInternalFormats[VTK_SIGNED_CHAR][0][3] = GL_RGB8_SNORM; this->TextureInternalFormats[VTK_SIGNED_CHAR][0][4] = GL_RGBA8_SNORM; #endif #ifdef GL_R16_SNORM this->TextureInternalFormats[VTK_SHORT][0][1] = GL_R16_SNORM; this->TextureInternalFormats[VTK_SHORT][0][2] = GL_RG16_SNORM; this->TextureInternalFormats[VTK_SHORT][0][3] = GL_RGB16_SNORM; this->TextureInternalFormats[VTK_SHORT][0][4] = GL_RGBA16_SNORM; #endif #ifdef GL_R8I this->TextureInternalFormats[VTK_SIGNED_CHAR][2][1] = GL_R8I; this->TextureInternalFormats[VTK_SIGNED_CHAR][2][2] = GL_RG8I; this->TextureInternalFormats[VTK_SIGNED_CHAR][2][3] = GL_RGB8I; this->TextureInternalFormats[VTK_SIGNED_CHAR][2][4] = GL_RGBA8I; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][2][1] = GL_R8UI; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][2][2] = GL_RG8UI; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][2][3] = GL_RGB8UI; this->TextureInternalFormats[VTK_UNSIGNED_CHAR][2][4] = GL_RGBA8UI; this->TextureInternalFormats[VTK_SHORT][2][1] = GL_R16I; this->TextureInternalFormats[VTK_SHORT][2][2] = GL_RG16I; this->TextureInternalFormats[VTK_SHORT][2][3] = GL_RGB16I; this->TextureInternalFormats[VTK_SHORT][2][4] = GL_RGBA16I; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][2][1] = GL_R16UI; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][2][2] = GL_RG16UI; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][2][3] = GL_RGB16UI; this->TextureInternalFormats[VTK_UNSIGNED_SHORT][2][4] = GL_RGBA16UI; this->TextureInternalFormats[VTK_INT][2][1] = GL_R32I; this->TextureInternalFormats[VTK_INT][2][2] = GL_RG32I; this->TextureInternalFormats[VTK_INT][2][3] = GL_RGB32I; this->TextureInternalFormats[VTK_INT][2][4] = GL_RGBA32I; this->TextureInternalFormats[VTK_UNSIGNED_INT][2][1] = GL_R32UI; this->TextureInternalFormats[VTK_UNSIGNED_INT][2][2] = GL_RG32UI; this->TextureInternalFormats[VTK_UNSIGNED_INT][2][3] = GL_RGB32UI; this->TextureInternalFormats[VTK_UNSIGNED_INT][2][4] = GL_RGBA32UI; #endif // on mesa we may not have float textures even though we think we do // this is due to Mesa being impacted by a patent issue with SGI // that is due to expire in the US in summer 2018 #ifndef GL_ES_VERSION_3_0 const char* glVersion = reinterpret_cast(glGetString(GL_VERSION)); if (glVersion && strstr(glVersion, "Mesa") != nullptr && !GLEW_ARB_texture_float) { // mesa without float support cannot even use // uchar textures with underlying float data // so pretty much anything with float data // is out of luck so return return; } #endif #ifdef GL_R32F this->TextureInternalFormats[VTK_FLOAT][1][1] = GL_R32F; this->TextureInternalFormats[VTK_FLOAT][1][2] = GL_RG32F; this->TextureInternalFormats[VTK_FLOAT][1][3] = GL_RGB32F; this->TextureInternalFormats[VTK_FLOAT][1][4] = GL_RGBA32F; this->TextureInternalFormats[VTK_SHORT][1][1] = GL_R32F; this->TextureInternalFormats[VTK_SHORT][1][2] = GL_RG32F; this->TextureInternalFormats[VTK_SHORT][1][3] = GL_RGB32F; this->TextureInternalFormats[VTK_SHORT][1][4] = GL_RGBA32F; #endif }