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.

311 lines
10.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. @interface JuceGLView : UIView
  19. {
  20. }
  21. + (Class) layerClass;
  22. @end
  23. @implementation JuceGLView
  24. + (Class) layerClass
  25. {
  26. return [CAEAGLLayer class];
  27. }
  28. @end
  29. extern "C" GLvoid glResolveMultisampleFramebufferAPPLE();
  30. namespace juce
  31. {
  32. class OpenGLContext::NativeContext
  33. {
  34. public:
  35. NativeContext (Component& c,
  36. const OpenGLPixelFormat& pixFormat,
  37. void* contextToShare,
  38. bool multisampling,
  39. OpenGLVersion version)
  40. : component (c), openGLversion (version),
  41. useDepthBuffer (pixFormat.depthBufferBits > 0),
  42. useMSAA (multisampling)
  43. {
  44. JUCE_AUTORELEASEPOOL
  45. {
  46. if (auto* peer = component.getPeer())
  47. {
  48. auto bounds = peer->getAreaCoveredBy (component);
  49. view = [[JuceGLView alloc] initWithFrame: convertToCGRect (bounds)];
  50. view.opaque = YES;
  51. view.hidden = NO;
  52. view.backgroundColor = [UIColor blackColor];
  53. view.userInteractionEnabled = NO;
  54. glLayer = (CAEAGLLayer*) [view layer];
  55. glLayer.opaque = true;
  56. updateWindowPosition (bounds);
  57. [((UIView*) peer->getNativeHandle()) addSubview: view];
  58. const auto shouldUseES3 = version != defaultGLVersion
  59. && [[UIDevice currentDevice].systemVersion floatValue] >= 7.0;
  60. [[maybe_unused]] const auto gotContext = (shouldUseES3 && createContext (kEAGLRenderingAPIOpenGLES3, contextToShare))
  61. || createContext (kEAGLRenderingAPIOpenGLES2, contextToShare);
  62. jassert (gotContext);
  63. if (context != nil)
  64. {
  65. // I'd prefer to put this stuff in the initialiseOnRenderThread() call, but doing
  66. // so causes mysterious timing-related failures.
  67. [EAGLContext setCurrentContext: context.get()];
  68. gl::loadFunctions();
  69. createGLBuffers();
  70. deactivateCurrentContext();
  71. }
  72. else
  73. {
  74. jassertfalse;
  75. }
  76. }
  77. else
  78. {
  79. jassertfalse;
  80. }
  81. }
  82. }
  83. ~NativeContext()
  84. {
  85. context.reset();
  86. [view removeFromSuperview];
  87. [view release];
  88. }
  89. InitResult initialiseOnRenderThread (OpenGLContext&) { return InitResult::success; }
  90. void shutdownOnRenderThread()
  91. {
  92. JUCE_CHECK_OPENGL_ERROR
  93. freeGLBuffers();
  94. deactivateCurrentContext();
  95. }
  96. bool createdOk() const noexcept { return getRawContext() != nullptr; }
  97. void* getRawContext() const noexcept { return context.get(); }
  98. GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; }
  99. bool makeActive() const noexcept
  100. {
  101. if (! [EAGLContext setCurrentContext: context.get()])
  102. return false;
  103. glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle
  104. : frameBufferHandle);
  105. return true;
  106. }
  107. bool isActive() const noexcept
  108. {
  109. return [EAGLContext currentContext] == context.get();
  110. }
  111. static void deactivateCurrentContext()
  112. {
  113. [EAGLContext setCurrentContext: nil];
  114. }
  115. void swapBuffers()
  116. {
  117. if (useMSAA)
  118. {
  119. glBindFramebuffer (GL_DRAW_FRAMEBUFFER, frameBufferHandle);
  120. glBindFramebuffer (GL_READ_FRAMEBUFFER, msaaBufferHandle);
  121. if (openGLversion >= openGL3_2)
  122. {
  123. auto w = roundToInt (lastBounds.getWidth() * glLayer.contentsScale);
  124. auto h = roundToInt (lastBounds.getHeight() * glLayer.contentsScale);
  125. glBlitFramebuffer (0, 0, w, h,
  126. 0, 0, w, h,
  127. GL_COLOR_BUFFER_BIT,
  128. GL_NEAREST);
  129. }
  130. else
  131. {
  132. ::glResolveMultisampleFramebufferAPPLE();
  133. }
  134. }
  135. glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
  136. [context.get() presentRenderbuffer: GL_RENDERBUFFER];
  137. if (needToRebuildBuffers)
  138. {
  139. needToRebuildBuffers = false;
  140. freeGLBuffers();
  141. createGLBuffers();
  142. makeActive();
  143. }
  144. }
  145. void updateWindowPosition (Rectangle<int> bounds)
  146. {
  147. view.frame = convertToCGRect (bounds);
  148. glLayer.contentsScale = (CGFloat) (Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale
  149. / component.getDesktopScaleFactor());
  150. if (lastBounds != bounds)
  151. {
  152. lastBounds = bounds;
  153. needToRebuildBuffers = true;
  154. }
  155. }
  156. bool setSwapInterval (int numFramesPerSwap) noexcept
  157. {
  158. swapFrames = numFramesPerSwap;
  159. return false;
  160. }
  161. int getSwapInterval() const noexcept { return swapFrames; }
  162. struct Locker
  163. {
  164. explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
  165. const ScopedLock lock;
  166. };
  167. private:
  168. CriticalSection mutex;
  169. Component& component;
  170. JuceGLView* view = nil;
  171. CAEAGLLayer* glLayer = nil;
  172. NSUniquePtr<EAGLContext> context;
  173. const OpenGLVersion openGLversion;
  174. const bool useDepthBuffer, useMSAA;
  175. GLuint frameBufferHandle = 0, colorBufferHandle = 0, depthBufferHandle = 0,
  176. msaaColorHandle = 0, msaaBufferHandle = 0;
  177. Rectangle<int> lastBounds;
  178. int swapFrames = 0;
  179. bool needToRebuildBuffers = false;
  180. bool createContext (EAGLRenderingAPI type, void* contextToShare)
  181. {
  182. jassert (context == nil);
  183. context.reset ([EAGLContext alloc]);
  184. if (contextToShare != nullptr)
  185. [context.get() initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]];
  186. else
  187. [context.get() initWithAPI: type];
  188. return context != nil;
  189. }
  190. //==============================================================================
  191. void createGLBuffers()
  192. {
  193. glGenFramebuffers (1, &frameBufferHandle);
  194. glGenRenderbuffers (1, &colorBufferHandle);
  195. glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
  196. glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
  197. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
  198. [[maybe_unused]] bool ok = [context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer];
  199. jassert (ok);
  200. GLint width, height;
  201. glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
  202. glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
  203. if (useMSAA)
  204. {
  205. glGenFramebuffers (1, &msaaBufferHandle);
  206. glGenRenderbuffers (1, &msaaColorHandle);
  207. glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
  208. glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle);
  209. glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height);
  210. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle);
  211. }
  212. if (useDepthBuffer)
  213. {
  214. glGenRenderbuffers (1, &depthBufferHandle);
  215. glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle);
  216. if (useMSAA)
  217. glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
  218. else
  219. glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
  220. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle);
  221. }
  222. jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
  223. JUCE_CHECK_OPENGL_ERROR
  224. }
  225. void freeGLBuffers()
  226. {
  227. JUCE_CHECK_OPENGL_ERROR
  228. [context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil];
  229. deleteFrameBuffer (frameBufferHandle);
  230. deleteFrameBuffer (msaaBufferHandle);
  231. deleteRenderBuffer (colorBufferHandle);
  232. deleteRenderBuffer (depthBufferHandle);
  233. deleteRenderBuffer (msaaColorHandle);
  234. JUCE_CHECK_OPENGL_ERROR
  235. }
  236. static void deleteFrameBuffer (GLuint& i) { if (i != 0) glDeleteFramebuffers (1, &i); i = 0; }
  237. static void deleteRenderBuffer (GLuint& i) { if (i != 0) glDeleteRenderbuffers (1, &i); i = 0; }
  238. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  239. };
  240. //==============================================================================
  241. bool OpenGLHelpers::isContextActive()
  242. {
  243. return [EAGLContext currentContext] != nil;
  244. }
  245. } // namespace juce