The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  16. class OpenGLContext::NativeContext
  17. {
  18. public:
  19. NativeContext (Component& component,
  20. const OpenGLPixelFormat& pixFormat,
  21. void* contextToShare,
  22. bool shouldUseMultisampling,
  23. OpenGLVersion version)
  24. : owner (component)
  25. {
  26. NSOpenGLPixelFormatAttribute attribs[64] = { 0 };
  27. createAttribs (attribs, version, pixFormat, shouldUseMultisampling);
  28. NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs];
  29. static MouseForwardingNSOpenGLViewClass cls;
  30. view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  31. pixelFormat: format];
  32. if ([view respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)])
  33. [view setWantsBestResolutionOpenGLSurface: YES];
  34. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  35. [[NSNotificationCenter defaultCenter] addObserver: view
  36. selector: @selector (_surfaceNeedsUpdate:)
  37. name: NSViewGlobalFrameDidChangeNotification
  38. object: view];
  39. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  40. renderContext = [[[NSOpenGLContext alloc] initWithFormat: format
  41. shareContext: (NSOpenGLContext*) contextToShare] autorelease];
  42. [view setOpenGLContext: renderContext];
  43. [format release];
  44. viewAttachment = NSViewComponent::attachViewToComponent (component, view);
  45. }
  46. ~NativeContext()
  47. {
  48. [[NSNotificationCenter defaultCenter] removeObserver: view];
  49. [renderContext clearDrawable];
  50. [renderContext setView: nil];
  51. [view setOpenGLContext: nil];
  52. [view release];
  53. }
  54. static void createAttribs (NSOpenGLPixelFormatAttribute* attribs, OpenGLVersion version,
  55. const OpenGLPixelFormat& pixFormat, bool shouldUseMultisampling)
  56. {
  57. ignoreUnused (version);
  58. int numAttribs = 0;
  59. attribs[numAttribs++] = NSOpenGLPFAOpenGLProfile;
  60. attribs[numAttribs++] = version >= openGL3_2 ? NSOpenGLProfileVersion3_2Core
  61. : NSOpenGLProfileVersionLegacy;
  62. attribs[numAttribs++] = NSOpenGLPFADoubleBuffer;
  63. attribs[numAttribs++] = NSOpenGLPFAClosestPolicy;
  64. attribs[numAttribs++] = NSOpenGLPFANoRecovery;
  65. attribs[numAttribs++] = NSOpenGLPFAColorSize;
  66. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) (pixFormat.redBits + pixFormat.greenBits + pixFormat.blueBits);
  67. attribs[numAttribs++] = NSOpenGLPFAAlphaSize;
  68. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.alphaBits;
  69. attribs[numAttribs++] = NSOpenGLPFADepthSize;
  70. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.depthBufferBits;
  71. attribs[numAttribs++] = NSOpenGLPFAStencilSize;
  72. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.stencilBufferBits;
  73. attribs[numAttribs++] = NSOpenGLPFAAccumSize;
  74. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) (pixFormat.accumulationBufferRedBits + pixFormat.accumulationBufferGreenBits
  75. + pixFormat.accumulationBufferBlueBits + pixFormat.accumulationBufferAlphaBits);
  76. if (shouldUseMultisampling)
  77. {
  78. attribs[numAttribs++] = NSOpenGLPFAMultisample;
  79. attribs[numAttribs++] = NSOpenGLPFASampleBuffers;
  80. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) 1;
  81. attribs[numAttribs++] = NSOpenGLPFASamples;
  82. attribs[numAttribs++] = (NSOpenGLPixelFormatAttribute) pixFormat.multisamplingLevel;
  83. }
  84. }
  85. bool initialiseOnRenderThread (OpenGLContext&) { return true; }
  86. void shutdownOnRenderThread() { deactivateCurrentContext(); }
  87. bool createdOk() const noexcept { return getRawContext() != nullptr; }
  88. void* getRawContext() const noexcept { return static_cast<void*> (renderContext); }
  89. GLuint getFrameBufferID() const noexcept { return 0; }
  90. bool makeActive() const noexcept
  91. {
  92. jassert (renderContext != nil);
  93. if ([renderContext view] != view)
  94. [renderContext setView: view];
  95. if (NSOpenGLContext* context = [view openGLContext])
  96. {
  97. [context makeCurrentContext];
  98. return true;
  99. }
  100. return false;
  101. }
  102. bool isActive() const noexcept
  103. {
  104. return [NSOpenGLContext currentContext] == renderContext;
  105. }
  106. static void deactivateCurrentContext()
  107. {
  108. [NSOpenGLContext clearCurrentContext];
  109. }
  110. struct Locker
  111. {
  112. Locker (NativeContext& nc) : cglContext ((CGLContextObj) [nc.renderContext CGLContextObj])
  113. {
  114. CGLLockContext (cglContext);
  115. }
  116. ~Locker()
  117. {
  118. CGLUnlockContext (cglContext);
  119. }
  120. private:
  121. CGLContextObj cglContext;
  122. };
  123. void swapBuffers()
  124. {
  125. auto now = Time::getMillisecondCounterHiRes();
  126. [renderContext flushBuffer];
  127. if (minSwapTimeMs > 0)
  128. {
  129. // When our window is entirely occluded by other windows, flushBuffer
  130. // fails to wait for the swap interval, so the render loop spins at full
  131. // speed, burning CPU. This hack detects when things are going too fast
  132. // and sleeps if necessary.
  133. auto swapTime = Time::getMillisecondCounterHiRes() - now;
  134. auto frameTime = (int) (now - lastSwapTime);
  135. if (swapTime < 0.5 && frameTime < minSwapTimeMs - 3)
  136. {
  137. if (underrunCounter > 3)
  138. {
  139. Thread::sleep (2 * (minSwapTimeMs - frameTime));
  140. now = Time::getMillisecondCounterHiRes();
  141. }
  142. else
  143. {
  144. ++underrunCounter;
  145. }
  146. }
  147. else
  148. {
  149. if (underrunCounter > 0)
  150. --underrunCounter;
  151. }
  152. }
  153. lastSwapTime = now;
  154. }
  155. void updateWindowPosition (Rectangle<int>)
  156. {
  157. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  158. {
  159. const auto newArea = peer->getAreaCoveredBy (owner);
  160. if (convertToRectInt ([view frame]) != newArea)
  161. [view setFrame: makeNSRect (newArea)];
  162. }
  163. }
  164. bool setSwapInterval (int numFramesPerSwapIn)
  165. {
  166. numFramesPerSwap = numFramesPerSwapIn;
  167. // The macOS OpenGL programming guide says that numFramesPerSwap
  168. // can only be 0 or 1.
  169. jassert (isPositiveAndBelow (numFramesPerSwap, 2));
  170. [renderContext setValues: (const GLint*) &numFramesPerSwap
  171. forParameter: getSwapIntervalParameter()];
  172. updateMinSwapTime();
  173. return true;
  174. }
  175. int getSwapInterval() const
  176. {
  177. GLint numFrames = 0;
  178. [renderContext getValues: &numFrames
  179. forParameter: getSwapIntervalParameter()];
  180. return numFrames;
  181. }
  182. void setNominalVideoRefreshPeriodS (double periodS)
  183. {
  184. jassert (periodS > 0.0);
  185. videoRefreshPeriodS = periodS;
  186. updateMinSwapTime();
  187. }
  188. void updateMinSwapTime()
  189. {
  190. minSwapTimeMs = static_cast<int> (numFramesPerSwap * 1000 * videoRefreshPeriodS);
  191. }
  192. static NSOpenGLContextParameter getSwapIntervalParameter()
  193. {
  194. #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
  195. if (@available (macOS 10.12, *))
  196. return NSOpenGLContextParameterSwapInterval;
  197. #endif
  198. return NSOpenGLCPSwapInterval;
  199. }
  200. Component& owner;
  201. NSOpenGLContext* renderContext = nil;
  202. NSOpenGLView* view = nil;
  203. ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment;
  204. double lastSwapTime = 0;
  205. std::atomic<int> minSwapTimeMs { 0 };
  206. int underrunCounter = 0, numFramesPerSwap = 0;
  207. double videoRefreshPeriodS = 1.0 / 60.0;
  208. //==============================================================================
  209. struct MouseForwardingNSOpenGLViewClass : public ObjCClass<NSOpenGLView>
  210. {
  211. MouseForwardingNSOpenGLViewClass() : ObjCClass<NSOpenGLView> ("JUCEGLView_")
  212. {
  213. addMethod (@selector (rightMouseDown:), rightMouseDown);
  214. addMethod (@selector (rightMouseUp:), rightMouseUp);
  215. addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse);
  216. addMethod (@selector (accessibilityHitTest:), accessibilityHitTest);
  217. registerClass();
  218. }
  219. private:
  220. static void rightMouseDown (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseDown: ev]; }
  221. static void rightMouseUp (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseUp: ev]; }
  222. static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; }
  223. static id accessibilityHitTest (id self, SEL, NSPoint p) { return [[(NSOpenGLView*) self superview] accessibilityHitTest: p]; }
  224. };
  225. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  226. };
  227. //==============================================================================
  228. bool OpenGLHelpers::isContextActive()
  229. {
  230. return CGLGetCurrentContext() != CGLContextObj();
  231. }
  232. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  233. } // namespace juce