316 lines
10 KiB
C
316 lines
10 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||
|
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-6-licence
|
||
|
Privacy Policy: www.juce.com/juce-privacy-policy
|
||
|
|
||
|
Or: You may also use this code under the terms of the GPL v3 (see
|
||
|
www.gnu.org/licenses).
|
||
|
|
||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||
|
DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
@interface JuceGLView : UIView
|
||
|
{
|
||
|
}
|
||
|
+ (Class) layerClass;
|
||
|
@end
|
||
|
|
||
|
@implementation JuceGLView
|
||
|
+ (Class) layerClass
|
||
|
{
|
||
|
return [CAEAGLLayer class];
|
||
|
}
|
||
|
@end
|
||
|
|
||
|
extern "C" GLvoid glResolveMultisampleFramebufferAPPLE();
|
||
|
|
||
|
namespace juce
|
||
|
{
|
||
|
|
||
|
class OpenGLContext::NativeContext
|
||
|
{
|
||
|
public:
|
||
|
NativeContext (Component& c,
|
||
|
const OpenGLPixelFormat& pixFormat,
|
||
|
void* contextToShare,
|
||
|
bool multisampling,
|
||
|
OpenGLVersion version)
|
||
|
: component (c), openGLversion (version),
|
||
|
useDepthBuffer (pixFormat.depthBufferBits > 0),
|
||
|
useMSAA (multisampling)
|
||
|
{
|
||
|
JUCE_AUTORELEASEPOOL
|
||
|
{
|
||
|
if (auto* peer = component.getPeer())
|
||
|
{
|
||
|
auto bounds = peer->getAreaCoveredBy (component);
|
||
|
|
||
|
view = [[JuceGLView alloc] initWithFrame: convertToCGRect (bounds)];
|
||
|
view.opaque = YES;
|
||
|
view.hidden = NO;
|
||
|
view.backgroundColor = [UIColor blackColor];
|
||
|
view.userInteractionEnabled = NO;
|
||
|
|
||
|
glLayer = (CAEAGLLayer*) [view layer];
|
||
|
glLayer.opaque = true;
|
||
|
|
||
|
updateWindowPosition (bounds);
|
||
|
|
||
|
[((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);
|
||
|
}
|
||
|
|
||
|
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];
|
||
|
gl::loadFunctions();
|
||
|
createGLBuffers();
|
||
|
deactivateCurrentContext();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
jassertfalse;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
jassertfalse;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
~NativeContext()
|
||
|
{
|
||
|
releaseContext();
|
||
|
[view removeFromSuperview];
|
||
|
[view release];
|
||
|
}
|
||
|
|
||
|
bool initialiseOnRenderThread (OpenGLContext&) { return true; }
|
||
|
|
||
|
void shutdownOnRenderThread()
|
||
|
{
|
||
|
JUCE_CHECK_OPENGL_ERROR
|
||
|
freeGLBuffers();
|
||
|
deactivateCurrentContext();
|
||
|
}
|
||
|
|
||
|
bool createdOk() const noexcept { return getRawContext() != nullptr; }
|
||
|
void* getRawContext() const noexcept { return context; }
|
||
|
GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; }
|
||
|
|
||
|
bool makeActive() const noexcept
|
||
|
{
|
||
|
if (! [EAGLContext setCurrentContext: context])
|
||
|
return false;
|
||
|
|
||
|
glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle
|
||
|
: frameBufferHandle);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool isActive() const noexcept
|
||
|
{
|
||
|
return [EAGLContext currentContext] == context;
|
||
|
}
|
||
|
|
||
|
static void deactivateCurrentContext()
|
||
|
{
|
||
|
[EAGLContext setCurrentContext: nil];
|
||
|
}
|
||
|
|
||
|
void swapBuffers()
|
||
|
{
|
||
|
if (useMSAA)
|
||
|
{
|
||
|
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, frameBufferHandle);
|
||
|
glBindFramebuffer (GL_READ_FRAMEBUFFER, msaaBufferHandle);
|
||
|
|
||
|
if (openGLversion >= openGL3_2)
|
||
|
{
|
||
|
auto w = roundToInt (lastBounds.getWidth() * glLayer.contentsScale);
|
||
|
auto h = roundToInt (lastBounds.getHeight() * glLayer.contentsScale);
|
||
|
|
||
|
glBlitFramebuffer (0, 0, w, h,
|
||
|
0, 0, w, h,
|
||
|
GL_COLOR_BUFFER_BIT,
|
||
|
GL_NEAREST);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
::glResolveMultisampleFramebufferAPPLE();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
|
||
|
[context presentRenderbuffer: GL_RENDERBUFFER];
|
||
|
|
||
|
if (needToRebuildBuffers)
|
||
|
{
|
||
|
needToRebuildBuffers = false;
|
||
|
|
||
|
freeGLBuffers();
|
||
|
createGLBuffers();
|
||
|
makeActive();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void updateWindowPosition (Rectangle<int> bounds)
|
||
|
{
|
||
|
view.frame = convertToCGRect (bounds);
|
||
|
glLayer.contentsScale = (CGFloat) (Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale
|
||
|
/ component.getDesktopScaleFactor());
|
||
|
|
||
|
if (lastBounds != bounds)
|
||
|
{
|
||
|
lastBounds = bounds;
|
||
|
needToRebuildBuffers = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool setSwapInterval (int numFramesPerSwap) noexcept
|
||
|
{
|
||
|
swapFrames = numFramesPerSwap;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int getSwapInterval() const noexcept { return swapFrames; }
|
||
|
|
||
|
struct Locker { Locker (NativeContext&) {} };
|
||
|
|
||
|
private:
|
||
|
Component& component;
|
||
|
JuceGLView* view = nil;
|
||
|
CAEAGLLayer* glLayer = nil;
|
||
|
EAGLContext* context = nil;
|
||
|
const OpenGLVersion openGLversion;
|
||
|
const bool useDepthBuffer, useMSAA;
|
||
|
|
||
|
GLuint frameBufferHandle = 0, colorBufferHandle = 0, depthBufferHandle = 0,
|
||
|
msaaColorHandle = 0, msaaBufferHandle = 0;
|
||
|
|
||
|
Rectangle<int> lastBounds;
|
||
|
int swapFrames = 0;
|
||
|
bool needToRebuildBuffers = false;
|
||
|
|
||
|
bool createContext (EAGLRenderingAPI type, void* contextToShare)
|
||
|
{
|
||
|
jassert (context == nil);
|
||
|
context = [EAGLContext alloc];
|
||
|
|
||
|
context = contextToShare != nullptr
|
||
|
? [context initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]]
|
||
|
: [context initWithAPI: type];
|
||
|
|
||
|
return context != nil;
|
||
|
}
|
||
|
|
||
|
void releaseContext()
|
||
|
{
|
||
|
[context release];
|
||
|
context = nil;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void createGLBuffers()
|
||
|
{
|
||
|
glGenFramebuffers (1, &frameBufferHandle);
|
||
|
glGenRenderbuffers (1, &colorBufferHandle);
|
||
|
|
||
|
glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
|
||
|
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
|
||
|
|
||
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
|
||
|
|
||
|
bool ok = [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer];
|
||
|
jassert (ok); ignoreUnused (ok);
|
||
|
|
||
|
GLint width, height;
|
||
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
|
||
|
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
|
||
|
|
||
|
if (useMSAA)
|
||
|
{
|
||
|
glGenFramebuffers (1, &msaaBufferHandle);
|
||
|
glGenRenderbuffers (1, &msaaColorHandle);
|
||
|
|
||
|
glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
|
||
|
glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle);
|
||
|
|
||
|
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height);
|
||
|
|
||
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle);
|
||
|
}
|
||
|
|
||
|
if (useDepthBuffer)
|
||
|
{
|
||
|
glGenRenderbuffers (1, &depthBufferHandle);
|
||
|
glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle);
|
||
|
|
||
|
if (useMSAA)
|
||
|
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
|
||
|
else
|
||
|
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||
|
|
||
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle);
|
||
|
}
|
||
|
|
||
|
jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||
|
JUCE_CHECK_OPENGL_ERROR
|
||
|
}
|
||
|
|
||
|
void freeGLBuffers()
|
||
|
{
|
||
|
JUCE_CHECK_OPENGL_ERROR
|
||
|
[context renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil];
|
||
|
|
||
|
deleteFrameBuffer (frameBufferHandle);
|
||
|
deleteFrameBuffer (msaaBufferHandle);
|
||
|
deleteRenderBuffer (colorBufferHandle);
|
||
|
deleteRenderBuffer (depthBufferHandle);
|
||
|
deleteRenderBuffer (msaaColorHandle);
|
||
|
|
||
|
JUCE_CHECK_OPENGL_ERROR
|
||
|
}
|
||
|
|
||
|
static void deleteFrameBuffer (GLuint& i) { if (i != 0) glDeleteFramebuffers (1, &i); i = 0; }
|
||
|
static void deleteRenderBuffer (GLuint& i) { if (i != 0) glDeleteRenderbuffers (1, &i); i = 0; }
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
bool OpenGLHelpers::isContextActive()
|
||
|
{
|
||
|
return [EAGLContext currentContext] != nil;
|
||
|
}
|
||
|
|
||
|
} // namespace juce
|