diff --git a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp index bd64e04e2d..bdffb4ee7e 100644 --- a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp +++ b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp @@ -47,61 +47,210 @@ #endif #include +#include #include #include #include "SoFCIndexedFaceSet.h" -#include -#ifdef HAVE_QT5_OPENGL #define RENDER_GL_VAO -#endif //#define RENDER_GLARRAYS -#ifdef RENDER_GL_VAO -#include -#include -#endif - using namespace MeshGui; #if defined RENDER_GL_VAO -class MeshRenderer::Private : protected QOpenGLFunctions { + +class CoinOpenGLBuffer { public: - QOpenGLBuffer vertices; - QOpenGLBuffer indices; + CoinOpenGLBuffer(GLenum type) + : target(type) + , bufferId(0) + , context(-1) + , currentContext(-1) + , glue(0) + { + SoContextHandler::addContextDestructionCallback(context_destruction_cb, this); + } + ~CoinOpenGLBuffer() + { + SoContextHandler::removeContextDestructionCallback(context_destruction_cb, this); + + destroy(); + } + void setCurrentContext(uint32_t ctx) + { + currentContext = ctx; + glue = cc_glglue_instance(currentContext); + } + bool create() + { + if (bufferId > 0) + return true; + PFNGLGENBUFFERSPROC glGenBuffersARB = (PFNGLGENBUFFERSPROC)cc_glglue_getprocaddress(glue, "glGenBuffersARB"); + glGenBuffersARB(1, &bufferId); + context = currentContext; + return true; + } + bool isCreated() const + { + return (bufferId > 0); + } + void destroy() + { + // schedule delete for all allocated GL resources + if (bufferId > 0) { + void * ptr0 = (void*) ((uintptr_t) bufferId); + SoGLCacheContextElement::scheduleDeleteCallback(context, buffer_delete, ptr0); + bufferId = 0; + } + } + void allocate(const void *data, int count) + { + if (bufferId > 0) { + PFNGLBUFFERDATAPROC glBufferDataARB = (PFNGLBUFFERDATAPROC)cc_glglue_getprocaddress(glue, "glBufferDataARB"); + glBufferDataARB(target, count, data, GL_STATIC_DRAW); + } + } + bool bind() + { + if (bufferId) { + if (context != currentContext) { + SoDebugError::postWarning("CoinOpenGLBuffer::bind", + "buffer not created"); + return false; + } + PFNGLBINDBUFFERPROC glBindBufferARB = (PFNGLBINDBUFFERPROC)cc_glglue_getprocaddress(glue, "glBindBufferARB"); + glBindBufferARB(target, bufferId); + return true; + } + return false; + } + void release() + { + if (bufferId) { + PFNGLBINDBUFFERPROC glBindBufferARB = (PFNGLBINDBUFFERPROC)cc_glglue_getprocaddress(glue, "glBindBufferARB"); + glBindBufferARB(target, 0); + } + } + GLuint getBufferId() const + { + return bufferId; + } + uint32_t getBoundContext() const + { + return context; + } + int size() const + { + GLint value = -1; + if (bufferId > 0) { + PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)cc_glglue_getprocaddress(glue, "glGetBufferParameterivARB"); + glGetBufferParameteriv(target, GL_BUFFER_SIZE, &value); + } + return value; + } + +private: + static void context_destruction_cb(uint32_t context, void * userdata) + { + CoinOpenGLBuffer * self = static_cast(userdata); + + if (self->context == context && self->bufferId) { + const cc_glglue * glue = cc_glglue_instance((int) context); + PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)cc_glglue_getprocaddress(glue, "glDeleteBuffersARB"); + + GLuint buffer = self->bufferId; + glDeleteBuffersARB(1, &buffer); + self->context = -1; + self->bufferId = 0; + } + } + static void buffer_delete(void * closure, uint32_t contextid) + { + const cc_glglue * glue = cc_glglue_instance((int) contextid); + GLuint id = (GLuint) ((uintptr_t) closure); + cc_glglue_glDeleteBuffers(glue, 1, &id); + } + + GLenum target; + GLuint bufferId; + uint32_t context; + uint32_t currentContext; + const cc_glglue* glue; +}; + +class MeshRenderer::Private { +public: + CoinOpenGLBuffer vertices; + CoinOpenGLBuffer indices; const SbColor * pcolors; SoMaterialBindingElement::Binding matbinding; + bool initialized; Private(); - void generateGLArrays(SoMaterialBindingElement::Binding matbind, + ~Private(); + bool canRenderGLArray(SoGLRenderAction *) const; + void generateGLArrays(SoGLRenderAction* action, + SoMaterialBindingElement::Binding matbind, std::vector& vertex, std::vector& index); void renderFacesGLArray(SoGLRenderAction*); - void renderCoordsGLArray(SoGLRenderAction *action); + void renderCoordsGLArray(SoGLRenderAction *); + +private: + void renderGLArray(SoGLRenderAction *, GLenum); }; MeshRenderer::Private::Private() - : vertices(QOpenGLBuffer::VertexBuffer) - , indices(QOpenGLBuffer::IndexBuffer) + : vertices(GL_ARRAY_BUFFER) + , indices(GL_ELEMENT_ARRAY_BUFFER) , pcolors(0) , matbinding(SoMaterialBindingElement::OVERALL) + , initialized(false) { - initializeOpenGLFunctions(); - - vertices.create(); - indices.create(); - - vertices.setUsagePattern(QOpenGLBuffer::StaticDraw); - indices.setUsagePattern(QOpenGLBuffer::StaticDraw); } -void MeshRenderer::Private::generateGLArrays(SoMaterialBindingElement::Binding matbind, +MeshRenderer::Private::~Private() +{ +} + +bool MeshRenderer::Private::canRenderGLArray(SoGLRenderAction *action) const +{ + static bool init = false; + static bool vboAvailable = false; + if (!init) { + std::string ext = (const char*)(glGetString(GL_EXTENSIONS)); + vboAvailable = (ext.find("GL_ARB_vertex_buffer_object") != std::string::npos); + init = true; + } + + if (!vboAvailable) + return false; + + // if no buffer is created we must pass here in order to create it afterwards + if (!indices.isCreated()) + return true; + return indices.getBoundContext() == action->getCacheContext(); +} + +void MeshRenderer::Private::generateGLArrays(SoGLRenderAction* action, + SoMaterialBindingElement::Binding matbind, std::vector& vertex, std::vector& index) { if (vertex.empty() || index.empty()) return; + + // lazy initialization + vertices.setCurrentContext(action->getCacheContext()); + indices.setCurrentContext(action->getCacheContext()); + + if (!initialized) { + vertices.create(); + indices.create(); + initialized = true; + } + vertices.bind(); vertices.allocate(&(vertex[0]), vertex.size() * sizeof(float)); @@ -114,8 +263,17 @@ void MeshRenderer::Private::generateGLArrays(SoMaterialBindingElement::Binding m this->matbinding = matbind; } -void MeshRenderer::Private::renderFacesGLArray(SoGLRenderAction *) +void MeshRenderer::Private::renderGLArray(SoGLRenderAction *action, GLenum mode) { + if (!initialized) { + SoDebugError::postWarning("MeshRenderer", + "not initialized"); + return; + } + + vertices.setCurrentContext(action->getCacheContext()); + indices.setCurrentContext(action->getCacheContext()); + glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); @@ -126,34 +284,25 @@ void MeshRenderer::Private::renderFacesGLArray(SoGLRenderAction *) glInterleavedArrays(GL_C4F_N3F_V3F, 0, 0); else glInterleavedArrays(GL_N3F_V3F, 0, 0); - glDrawElements(GL_TRIANGLES, indices.size() / sizeof(uint32_t), + + glDrawElements(mode, indices.size() / sizeof(uint32_t), GL_UNSIGNED_INT, NULL); vertices.release(); indices.release(); + glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } -void MeshRenderer::Private::renderCoordsGLArray(SoGLRenderAction *) +void MeshRenderer::Private::renderFacesGLArray(SoGLRenderAction *action) { - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); + renderGLArray(action, GL_TRIANGLES); +} - vertices.bind(); - indices.bind(); - - if (matbinding != SoMaterialBindingElement::OVERALL) - glInterleavedArrays(GL_C4F_N3F_V3F, 0, 0); - else - glInterleavedArrays(GL_N3F_V3F, 0, 0); - glDrawElements(GL_POINTS, indices.size() / sizeof(uint32_t), - GL_UNSIGNED_INT, NULL); - - vertices.release(); - indices.release(); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); +void MeshRenderer::Private::renderCoordsGLArray(SoGLRenderAction *action) +{ + renderGLArray(action, GL_POINTS); } #elif defined RENDER_GLARRAYS class MeshRenderer::Private { @@ -169,13 +318,21 @@ public: { } - void generateGLArrays(SoMaterialBindingElement::Binding matbind, + bool canRenderGLArray(SoGLRenderAction *) const; + void generateGLArrays(SoGLRenderAction* action, + SoMaterialBindingElement::Binding matbind, std::vector& vertex, std::vector& index); void renderFacesGLArray(SoGLRenderAction *action); void renderCoordsGLArray(SoGLRenderAction *action); }; -void MeshRenderer::Private::generateGLArrays(SoMaterialBindingElement::Binding matbind, +bool MeshRenderer::Private::canRenderGLArray(SoGLRenderAction *) const +{ + return true; +} + +void MeshRenderer::Private::generateGLArrays(SoGLRenderAction*, + SoMaterialBindingElement::Binding matbind, std::vector& vertex, std::vector& index) { if (vertex.empty() || index.empty()) @@ -260,7 +417,11 @@ public: { } - void generateGLArrays(SoMaterialBindingElement::Binding, + bool canRenderGLArray(SoGLRenderAction *) const { + return false; + } + void generateGLArrays(SoGLRenderAction*, + SoMaterialBindingElement::Binding, std::vector&, std::vector&) { } @@ -283,14 +444,14 @@ MeshRenderer::~MeshRenderer() delete p; } -void MeshRenderer::generateGLArrays(SoState* state, SoMaterialBindingElement::Binding matbind, +void MeshRenderer::generateGLArrays(SoGLRenderAction* action, SoMaterialBindingElement::Binding matbind, std::vector& vertex, std::vector& index) { - SoGLLazyElement* gl = SoGLLazyElement::getInstance(state); + SoGLLazyElement* gl = SoGLLazyElement::getInstance(action->getState()); if (gl) { p->pcolors = gl->getDiffusePointer(); } - p->generateGLArrays(matbind, vertex, index); + p->generateGLArrays(action, matbind, vertex, index); } // Implementation | FPS @@ -323,6 +484,11 @@ void MeshRenderer::renderFacesGLArray(SoGLRenderAction *action) p->renderFacesGLArray(action); } +bool MeshRenderer::canRenderGLArray(SoGLRenderAction *action) const +{ + return p->canRenderGLArray(action); +} + bool MeshRenderer::matchMaterial(SoState* state) const { SoMaterialBindingElement::Binding matbind = @@ -425,16 +591,17 @@ void SoFCIndexedFaceSet::GLRender(SoGLRenderAction *action) #endif #if defined (RENDER_GLARRAYS) || defined(RENDER_GL_VAO) + SbBool matchCtx = render.canRenderGLArray(action); #if 0 SoMaterialBindingElement::Binding matbind = SoMaterialBindingElement::get(state); - if (matbind == SoMaterialBindingElement::OVERALL) { + if (matbind == SoMaterialBindingElement::OVERALL && matchCtx) { SoMaterialBundle mb(action); mb.sendFirst(); if (updateGLArray) { updateGLArray = false; - generateGLArrays(state); + generateGLArrays(action); } render.renderFacesGLArray(action); } @@ -442,12 +609,18 @@ void SoFCIndexedFaceSet::GLRender(SoGLRenderAction *action) inherited::GLRender(action); } #else - if (updateGLArray.getValue()) { + // get the VBO status of the viewer + SbBool hasVBO = true; + //Gui::SoGLVBOActivatedElement::get(state, hasVBO); + if (!matchCtx) + hasVBO = false; + + if (hasVBO && updateGLArray.getValue()) { updateGLArray = false; - generateGLArrays(state); + generateGLArrays(action); } - if (render.matchMaterial(state)) { + if (hasVBO && render.matchMaterial(state)) { SoMaterialBundle mb(action); mb.sendFirst(); render.renderFacesGLArray(action); @@ -581,7 +754,7 @@ void SoFCIndexedFaceSet::invalidate() updateGLArray = true; } -void SoFCIndexedFaceSet::generateGLArrays(SoState * state) +void SoFCIndexedFaceSet::generateGLArrays(SoGLRenderAction * action) { const SoCoordinateElement * coords; const SbVec3f * normals; @@ -596,6 +769,7 @@ void SoFCIndexedFaceSet::generateGLArrays(SoState * state) SbBool sendNormals = true; + SoState* state = action->getState(); this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); @@ -761,7 +935,7 @@ void SoFCIndexedFaceSet::generateGLArrays(SoState * state) } } - render.generateGLArrays(state, matbind, face_vertices, face_indices); + render.generateGLArrays(action, matbind, face_vertices, face_indices); } void SoFCIndexedFaceSet::doAction(SoAction * action) diff --git a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.h b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.h index 01b9420807..8a94a94fa1 100644 --- a/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.h +++ b/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.h @@ -43,10 +43,11 @@ class MeshRenderer public: MeshRenderer(); ~MeshRenderer(); - void generateGLArrays(SoState*, SoMaterialBindingElement::Binding binding, + void generateGLArrays(SoGLRenderAction*, SoMaterialBindingElement::Binding binding, std::vector& vertex, std::vector& index); void renderFacesGLArray(SoGLRenderAction *action); void renderCoordsGLArray(SoGLRenderAction *action); + bool canRenderGLArray(SoGLRenderAction *action) const; bool matchMaterial(SoState*) const; static bool shouldRenderDirectly(bool); @@ -125,7 +126,7 @@ private: void stopVisibility(SoAction * action); void renderVisibleFaces(const SbVec3f *); - void generateGLArrays(SoState * state); + void generateGLArrays(SoGLRenderAction * action); private: MeshRenderer render;