1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

OpenGL: Add support for a few more OpenGL profiles

- 4.1 and 4.3 contexts can now be requested
- The requested context version is no longer ignored on Linux
- Debugging contexts are now enabled in Debug builds with GL 4.3
- Fixes a bug where glEnable(GL_TEXTURE_2D) was called in core profiles
This commit is contained in:
reuk 2022-08-02 12:22:20 +01:00
parent ac6a455229
commit 02b5ab748a
8 changed files with 184 additions and 69 deletions

View file

@ -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
//==============================================================================

View file

@ -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<EAGLContext> 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);

View file

@ -28,6 +28,18 @@ namespace juce
extern XContext windowHandleXContext;
struct XFreeDeleter
{
void operator() (void* ptr) const
{
if (ptr != nullptr)
X11Symbols::getInstance()->xFree (ptr);
}
};
template <typename Data>
std::unique_ptr<Data, XFreeDeleter> makeXFreePtr (Data* raw) { return std::unique_ptr<Data, XFreeDeleter> (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<Version>
{
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<GLint> 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<int> bounds;
XVisualInfo* bestVisual = nullptr;
std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
void* contextToShareWith;
OpenGLContext* context = nullptr;

View file

@ -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;

View file

@ -206,13 +206,34 @@ private:
static HGLRC createRenderContext (OpenGLVersion version, HDC dcIn)
{
if (version >= openGL3_2 && wglCreateContextAttribsARB != nullptr)
const auto components = [&]() -> Optional<Version>
{
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
};

View file

@ -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());

View file

@ -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.

View file

@ -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&);
};