Browse Source

macOS OpenGL: Use display refresh rate when rate limiting swapBuffers()

v6.1.6
attila 4 years ago
parent
commit
5c933d4c4e
2 changed files with 80 additions and 12 deletions
  1. +20
    -4
      modules/juce_opengl/native/juce_OpenGL_osx.h
  2. +60
    -8
      modules/juce_opengl/opengl/juce_OpenGLContext.cpp

+ 20
- 4
modules/juce_opengl/native/juce_OpenGL_osx.h View File

@@ -197,20 +197,23 @@ public:
void updateWindowPosition (Rectangle<int>) {}
bool setSwapInterval (int numFramesPerSwap)
bool setSwapInterval (int numFramesPerSwapIn)
{
numFramesPerSwap = numFramesPerSwapIn;
// The macOS OpenGL programming guide says that numFramesPerSwap
// can only be 0 or 1.
jassert (isPositiveAndBelow (numFramesPerSwap, 2));
minSwapTimeMs = (numFramesPerSwap * 1000) / 60;
[renderContext setValues: (const GLint*) &numFramesPerSwap
#if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
forParameter: NSOpenGLContextParameterSwapInterval];
#else
forParameter: NSOpenGLCPSwapInterval];
#endif
updateMinSwapTime();
return true;
}
@@ -227,11 +230,24 @@ public:
return numFrames;
}
void setNominalVideoRefreshPeriodS (double periodS)
{
jassert (periodS > 0.0);
videoRefreshPeriodS = periodS;
updateMinSwapTime();
}
void updateMinSwapTime()
{
minSwapTimeMs = static_cast<int> (numFramesPerSwap * 1000 * videoRefreshPeriodS);
}
NSOpenGLContext* renderContext = nil;
NSOpenGLView* view = nil;
ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment;
double lastSwapTime = 0;
int minSwapTimeMs = 0, underrunCounter = 0;
int minSwapTimeMs = 0, underrunCounter = 0, numFramesPerSwap = 0;
double videoRefreshPeriodS = 1.0 / 60.0;
//==============================================================================
struct MouseForwardingNSOpenGLViewClass : public ObjCClass<NSOpenGLView>


+ 60
- 8
modules/juce_opengl/opengl/juce_OpenGLContext.cpp View File

@@ -304,7 +304,21 @@ public:
if (auto* peer = component.getPeer())
{
auto localBounds = component.getLocalBounds();
auto displayScale = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds())->scale;
const auto currentDisplay = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds());
if (currentDisplay != lastDisplay)
{
#if JUCE_MAC
if (cvDisplayLinkWrapper != nullptr)
{
cvDisplayLinkWrapper->updateActiveDisplay (currentDisplay->totalArea.getTopLeft());
nativeContext->setNominalVideoRefreshPeriodS (cvDisplayLinkWrapper->getNominalVideoRefreshPeriodS());
}
#endif
lastDisplay = currentDisplay;
}
const auto displayScale = currentDisplay->scale;
auto newArea = peer->getComponent().getLocalArea (&component, localBounds).withZeroOrigin() * displayScale;
@@ -345,7 +359,10 @@ public:
auto screenBounds = component.getTopLevelComponent()->getScreenBounds();
if (lastScreenBounds != screenBounds)
{
updateViewportSize (true);
lastScreenBounds = screenBounds;
}
}
void paintComponent()
@@ -494,7 +511,7 @@ public:
break;
#if JUCE_MAC
if (cvDisplayLinkWrapper != nullptr)
if (context.continuousRepaint)
{
repaintEvent.wait (-1);
renderFrame();
@@ -561,8 +578,8 @@ public:
context.renderer->newOpenGLContextCreated();
#if JUCE_MAC
if (context.continuousRepaint)
cvDisplayLinkWrapper = std::make_unique<CVDisplayLinkWrapper> (this);
cvDisplayLinkWrapper = std::make_unique<CVDisplayLinkWrapper> (*this);
nativeContext->setNominalVideoRefreshPeriodS (cvDisplayLinkWrapper->getNominalVideoRefreshPeriodS());
#endif
return true;
@@ -680,6 +697,7 @@ public:
OpenGLFrameBuffer cachedImageFrameBuffer;
RectangleList<int> validArea;
Rectangle<int> viewportArea, lastScreenBounds;
const Displays::Display* lastDisplay = nullptr;
double scale = 1.0;
AffineTransform transform;
GLuint vertexArrayObject = 0;
@@ -700,11 +718,40 @@ public:
#if JUCE_MAC
struct CVDisplayLinkWrapper
{
CVDisplayLinkWrapper (CachedImage* im)
CVDisplayLinkWrapper (CachedImage& cachedImageIn) : cachedImage (cachedImageIn),
continuousRepaint (cachedImageIn.context.continuousRepaint.load())
{
CVDisplayLinkCreateWithActiveCGDisplays (&displayLink);
CVDisplayLinkSetOutputCallback (displayLink, &displayLinkCallback, im);
CVDisplayLinkSetOutputCallback (displayLink, &displayLinkCallback, this);
CVDisplayLinkStart (displayLink);
const auto topLeftOfCurrentScreen = Desktop::getInstance()
.getDisplays()
.getDisplayForRect (cachedImage.component.getTopLevelComponent()->getScreenBounds())
->totalArea.getTopLeft();
updateActiveDisplay (topLeftOfCurrentScreen);
}
double getNominalVideoRefreshPeriodS() const
{
const auto nominalVideoRefreshPeriod = CVDisplayLinkGetNominalOutputVideoRefreshPeriod (displayLink);
if ((nominalVideoRefreshPeriod.flags & kCVTimeIsIndefinite) == 0)
return (double) nominalVideoRefreshPeriod.timeValue / (double) nominalVideoRefreshPeriod.timeScale;
return 0.0;
}
void updateActiveDisplay (Point<int> topLeftOfDisplay)
{
CGPoint point { (CGFloat) topLeftOfDisplay.getX(), (CGFloat) topLeftOfDisplay.getY() };
CGDirectDisplayID displayID;
uint32_t numDisplays = 0;
constexpr uint32_t maxNumDisplays = 1;
CGGetDisplaysWithPoint (point, maxNumDisplays, &displayID, &numDisplays);
if (numDisplays == 1)
CVDisplayLinkSetCurrentCGDisplay (displayLink, displayID);
}
~CVDisplayLinkWrapper()
@@ -716,11 +763,16 @@ public:
static CVReturn displayLinkCallback (CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*,
CVOptionFlags, CVOptionFlags*, void* displayLinkContext)
{
auto* self = (CachedImage*) displayLinkContext;
self->repaintEvent.signal();
auto* self = reinterpret_cast<CVDisplayLinkWrapper*> (displayLinkContext);
if (self->continuousRepaint)
self->cachedImage.repaintEvent.signal();
return kCVReturnSuccess;
}
CachedImage& cachedImage;
const bool continuousRepaint;
CVDisplayLinkRef displayLink;
};


Loading…
Cancel
Save