diff --git a/modules/juce_opengl/juce_opengl.cpp b/modules/juce_opengl/juce_opengl.cpp index 7f2a1ae0da..103dc0e0d8 100644 --- a/modules/juce_opengl/juce_opengl.cpp +++ b/modules/juce_opengl/juce_opengl.cpp @@ -240,6 +240,22 @@ private: OpenGLTargetSaver& operator= (const OpenGLTargetSaver&); }; +static bool contextRequiresTexture2DEnableDisable() +{ + #if JUCE_OPENGL_ES + return false; + #else + clearGLError(); + GLint mask = 0; + glGetIntegerv (GL_CONTEXT_PROFILE_MASK, &mask); + + if (glGetError() == GL_INVALID_ENUM) + return true; + + return (mask & (GLint) GL_CONTEXT_CORE_PROFILE_BIT) == 0; + #endif +} + } // namespace juce //============================================================================== diff --git a/modules/juce_opengl/native/juce_OpenGL_ios.h b/modules/juce_opengl/native/juce_OpenGL_ios.h index c67a19d998..c908ddf27a 100644 --- a/modules/juce_opengl/native/juce_OpenGL_ios.h +++ b/modules/juce_opengl/native/juce_OpenGL_ios.h @@ -72,24 +72,19 @@ public: [((UIView*) peer->getNativeHandle()) addSubview: view]; - if (version == openGL3_2 && [[UIDevice currentDevice].systemVersion floatValue] >= 7.0) - { - if (! createContext (kEAGLRenderingAPIOpenGLES3, contextToShare)) - { - releaseContext(); - createContext (kEAGLRenderingAPIOpenGLES2, contextToShare); - } - } - else - { - createContext (kEAGLRenderingAPIOpenGLES2, contextToShare); - } + const auto shouldUseES3 = version != defaultGLVersion + && [[UIDevice currentDevice].systemVersion floatValue] >= 7.0; + + const auto gotContext = (shouldUseES3 && createContext (kEAGLRenderingAPIOpenGLES3, contextToShare)) + || createContext (kEAGLRenderingAPIOpenGLES2, contextToShare); + + jassertquiet (gotContext); if (context != nil) { // I'd prefer to put this stuff in the initialiseOnRenderThread() call, but doing // so causes mysterious timing-related failures. - [EAGLContext setCurrentContext: context]; + [EAGLContext setCurrentContext: context.get()]; gl::loadFunctions(); createGLBuffers(); deactivateCurrentContext(); @@ -108,7 +103,7 @@ public: ~NativeContext() { - releaseContext(); + context.reset(); [view removeFromSuperview]; [view release]; } @@ -123,12 +118,12 @@ public: } bool createdOk() const noexcept { return getRawContext() != nullptr; } - void* getRawContext() const noexcept { return context; } + void* getRawContext() const noexcept { return context.get(); } GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; } bool makeActive() const noexcept { - if (! [EAGLContext setCurrentContext: context]) + if (! [EAGLContext setCurrentContext: context.get()]) return false; glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle @@ -138,7 +133,7 @@ public: bool isActive() const noexcept { - return [EAGLContext currentContext] == context; + return [EAGLContext currentContext] == context.get(); } static void deactivateCurrentContext() @@ -170,7 +165,7 @@ public: } glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle); - [context presentRenderbuffer: GL_RENDERBUFFER]; + [context.get() presentRenderbuffer: GL_RENDERBUFFER]; if (needToRebuildBuffers) { @@ -209,7 +204,7 @@ private: Component& component; JuceGLView* view = nil; CAEAGLLayer* glLayer = nil; - EAGLContext* context = nil; + NSUniquePtr context; const OpenGLVersion openGLversion; const bool useDepthBuffer, useMSAA; @@ -223,21 +218,16 @@ private: bool createContext (EAGLRenderingAPI type, void* contextToShare) { jassert (context == nil); - context = [EAGLContext alloc]; + context.reset ([EAGLContext alloc]); - context = contextToShare != nullptr - ? [context initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]] - : [context initWithAPI: type]; + if (contextToShare != nullptr) + [context.get() initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]]; + else + [context.get() initWithAPI: type]; return context != nil; } - void releaseContext() - { - [context release]; - context = nil; - } - //============================================================================== void createGLBuffers() { @@ -249,7 +239,7 @@ private: glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle); - bool ok = [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer]; + bool ok = [context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer]; jassert (ok); ignoreUnused (ok); GLint width, height; @@ -289,7 +279,7 @@ private: void freeGLBuffers() { JUCE_CHECK_OPENGL_ERROR - [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil]; + [context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil]; deleteFrameBuffer (frameBufferHandle); deleteFrameBuffer (msaaBufferHandle); diff --git a/modules/juce_opengl/native/juce_OpenGL_linux_X11.h b/modules/juce_opengl/native/juce_OpenGL_linux_X11.h index cda1c8ab7d..8236ac1588 100644 --- a/modules/juce_opengl/native/juce_OpenGL_linux_X11.h +++ b/modules/juce_opengl/native/juce_OpenGL_linux_X11.h @@ -28,6 +28,18 @@ namespace juce extern XContext windowHandleXContext; +struct XFreeDeleter +{ + void operator() (void* ptr) const + { + if (ptr != nullptr) + X11Symbols::getInstance()->xFree (ptr); + } +}; + +template +std::unique_ptr makeXFreePtr (Data* raw) { return std::unique_ptr (raw); } + //============================================================================== // Defined juce_linux_Windowing.cpp void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy); @@ -80,15 +92,15 @@ public: jassert (peer != nullptr); auto windowH = (Window) peer->getNativeHandle(); - auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, bestVisual->visual, AllocNone); + auto visual = glXGetVisualFromFBConfig (display, *bestConfig); + auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, visual->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = colourMap; swa.border_pixel = 0; swa.event_mask = embeddedWindowEventMask; - auto glBounds = component.getTopLevelComponent() - ->getLocalArea (&component, component.getLocalBounds()); + auto glBounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds()); glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds); @@ -96,9 +108,9 @@ public: glBounds.getX(), glBounds.getY(), (unsigned int) jmax (1, glBounds.getWidth()), (unsigned int) jmax (1, glBounds.getHeight()), - 0, bestVisual->depth, + 0, visual->depth, InputOutput, - bestVisual->visual, + visual->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); @@ -135,18 +147,59 @@ public: } } } - - if (bestVisual != nullptr) - X11Symbols::getInstance()->xFree (bestVisual); } bool initialiseOnRenderThread (OpenGLContext& c) { XWindowSystemUtilities::ScopedXLock xLock; - renderContext = glXCreateContext (display, bestVisual, (GLXContext) contextToShareWith, GL_TRUE); + + const auto components = [&]() -> Optional + { + switch (c.versionRequired) + { + case OpenGLVersion::openGL3_2: return Version { 3, 2 }; + case OpenGLVersion::openGL4_1: return Version { 4, 1 }; + case OpenGLVersion::openGL4_3: return Version { 4, 3 }; + + case OpenGLVersion::defaultGLVersion: break; + } + + return {}; + }(); + + if (components.hasValue()) + { + using GLXCreateContextAttribsARB = GLXContext (*) (Display*, GLXFBConfig, GLXContext, Bool, const int*); + + if (const auto glXCreateContextAttribsARB = (GLXCreateContextAttribsARB) OpenGLHelpers::getExtensionFunction ("glXCreateContextAttribsARB")) + { + #if JUCE_DEBUG + constexpr auto contextFlags = GLX_CONTEXT_DEBUG_BIT_ARB; + #else + constexpr auto contextFlags = 0; + #endif + + const int attribs[] + { + GLX_CONTEXT_MAJOR_VERSION_ARB, components->major, + GLX_CONTEXT_MINOR_VERSION_ARB, components->minor, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, contextFlags, + None + }; + + renderContext = glXCreateContextAttribsARB (display, *bestConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs); + } + } + + if (renderContext == nullptr) + renderContext = glXCreateNewContext (display, *bestConfig, GLX_RGBA_TYPE, (GLXContext) contextToShareWith, GL_TRUE); + + if (renderContext == nullptr) + return false; + c.makeActive(); context = &c; - return true; } @@ -234,8 +287,8 @@ private: { std::vector allAttribs { - GLX_RGBA, - GLX_DOUBLEBUFFER, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, format.redBits, GLX_GREEN_SIZE, format.greenBits, GLX_BLUE_SIZE, format.blueBits, @@ -252,9 +305,10 @@ private: allAttribs.push_back (None); - bestVisual = glXChooseVisual (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data()); + int nElements = 0; + bestConfig = makeXFreePtr (glXChooseFBConfig (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data(), &nElements)); - return bestVisual != nullptr; + return nElements != 0 && bestConfig != nullptr; } static constexpr int embeddedWindowEventMask = ExposureMask | StructureNotifyMask; @@ -265,7 +319,7 @@ private: int swapFrames = 1; Rectangle bounds; - XVisualInfo* bestVisual = nullptr; + std::unique_ptr bestConfig; void* contextToShareWith; OpenGLContext* context = nullptr; diff --git a/modules/juce_opengl/native/juce_OpenGL_osx.h b/modules/juce_opengl/native/juce_OpenGL_osx.h index f3c21b929e..89afda20a9 100644 --- a/modules/juce_opengl/native/juce_OpenGL_osx.h +++ b/modules/juce_opengl/native/juce_OpenGL_osx.h @@ -82,8 +82,17 @@ public: int numAttribs = 0; attribs[numAttribs++] = NSOpenGLPFAOpenGLProfile; - attribs[numAttribs++] = version >= openGL3_2 ? NSOpenGLProfileVersion3_2Core - : NSOpenGLProfileVersionLegacy; + attribs[numAttribs++] = [version] + { + if (version == openGL3_2) + return NSOpenGLProfileVersion3_2Core; + + if (version != defaultGLVersion) + if (@available (macOS 10.10, *)) + return NSOpenGLProfileVersion4_1Core; + + return NSOpenGLProfileVersionLegacy; + }(); attribs[numAttribs++] = NSOpenGLPFADoubleBuffer; attribs[numAttribs++] = NSOpenGLPFAClosestPolicy; diff --git a/modules/juce_opengl/native/juce_OpenGL_win32.h b/modules/juce_opengl/native/juce_OpenGL_win32.h index 12dcab98a2..f5956df86b 100644 --- a/modules/juce_opengl/native/juce_OpenGL_win32.h +++ b/modules/juce_opengl/native/juce_OpenGL_win32.h @@ -206,13 +206,34 @@ private: static HGLRC createRenderContext (OpenGLVersion version, HDC dcIn) { - if (version >= openGL3_2 && wglCreateContextAttribsARB != nullptr) + const auto components = [&]() -> Optional { + switch (version) + { + case OpenGLVersion::openGL3_2: return Version { 3, 2 }; + case OpenGLVersion::openGL4_1: return Version { 4, 1 }; + case OpenGLVersion::openGL4_3: return Version { 4, 3 }; + + case OpenGLVersion::defaultGLVersion: break; + } + + return {}; + }(); + + if (components.hasValue() && wglCreateContextAttribsARB != nullptr) + { + #if JUCE_DEBUG + constexpr auto contextFlags = WGL_CONTEXT_DEBUG_BIT_ARB; + #else + constexpr auto contextFlags = 0; + #endif + const int attribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 2, + WGL_CONTEXT_MAJOR_VERSION_ARB, components->major, + WGL_CONTEXT_MINOR_VERSION_ARB, components->minor, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, contextFlags, 0 }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index 909cda6e18..545505b768 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -461,10 +461,8 @@ public: void drawComponentBuffer() { - #if ! JUCE_ANDROID - glEnable (GL_TEXTURE_2D); - clearGLError(); - #endif + if (contextRequiresTexture2DEnableDisable()) + glEnable (GL_TEXTURE_2D); #if JUCE_WINDOWS // some stupidly old drivers are missing this function, so try to at least avoid a crash here, @@ -473,7 +471,9 @@ public: jassert (context.extensions.glActiveTexture != nullptr); if (context.extensions.glActiveTexture != nullptr) #endif + { context.extensions.glActiveTexture (GL_TEXTURE0); + } glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID()); bindVertexArray(); @@ -622,6 +622,21 @@ public: bindVertexArray(); } + #if JUCE_DEBUG + if (getOpenGLVersion() >= Version { 4, 3 } && glDebugMessageCallback != nullptr) + { + glEnable (GL_DEBUG_OUTPUT); + glDebugMessageCallback ([] (GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar* message, const void*) + { + // This may reiterate issues that are also flagged by JUCE_CHECK_OPENGL_ERROR. + // The advantage of this callback is that it will catch *all* errors, even if we + // forget to check manually. + DBG ("OpenGL DBG message: " << message); + jassertfalse; + }, nullptr); + } + #endif + const auto currentViewportArea = areaAndScale.get().area; glViewport (0, 0, currentViewportArea.getWidth(), currentViewportArea.getHeight()); diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.h b/modules/juce_opengl/opengl/juce_OpenGLContext.h index b272ee2a3a..f02d70f32e 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.h @@ -136,8 +136,10 @@ public: /** OpenGL versions, used by setOpenGLVersionRequired(). */ enum OpenGLVersion { - defaultGLVersion = 0, - openGL3_2 + defaultGLVersion = 0, ///< Whatever the device decides to give us, normally a compatibility profile + openGL3_2, ///< 3.2 Core profile + openGL4_1, ///< 4.1 Core profile, the latest supported by macOS at time of writing + openGL4_3 ///< 4.3 Core profile, will enable improved debugging support when building in Debug }; /** Sets a preference for the version of GL that this context should use, if possible. diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 56380e5137..1ec0f17fc7 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -964,8 +964,10 @@ struct StateHelpers //============================================================================== struct ActiveTextures { - ActiveTextures (const OpenGLContext& c) noexcept : context (c) - {} + explicit ActiveTextures (const OpenGLContext& c) noexcept + : context (c) + { + } void clear() noexcept { @@ -979,23 +981,28 @@ struct StateHelpers { quadQueue.flush(); - for (int i = 3; --i >= 0;) + for (int i = numTextures; --i >= 0;) { if ((texturesEnabled & (1 << i)) != (textureIndexMask & (1 << i))) { setActiveTexture (i); JUCE_CHECK_OPENGL_ERROR - #if ! JUCE_ANDROID - if ((textureIndexMask & (1 << i)) != 0) - glEnable (GL_TEXTURE_2D); - else - { - glDisable (GL_TEXTURE_2D); - currentTextureID[i] = 0; - } + const auto thisTextureEnabled = (textureIndexMask & (1 << i)) != 0; - clearGLError(); + if (! thisTextureEnabled) + currentTextureID[i] = 0; + + #if ! JUCE_ANDROID + if (needsToEnableTexture) + { + if (thisTextureEnabled) + glEnable (GL_TEXTURE_2D); + else + glDisable (GL_TEXTURE_2D); + + JUCE_CHECK_OPENGL_ERROR + } #endif } } @@ -1079,6 +1086,7 @@ struct StateHelpers GLuint currentTextureID[numTextures]; int texturesEnabled = 0, currentActiveTexture = -1; const OpenGLContext& context; + const bool needsToEnableTexture = contextRequiresTexture2DEnableDisable(); ActiveTextures& operator= (const ActiveTextures&); };