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.

316 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-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. if (version == openGL3_2 && [[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
  59. {
  60. if (! createContext (kEAGLRenderingAPIOpenGLES3, contextToShare))
  61. {
  62. releaseContext();
  63. createContext (kEAGLRenderingAPIOpenGLES2, contextToShare);
  64. }
  65. }
  66. else
  67. {
  68. createContext (kEAGLRenderingAPIOpenGLES2, contextToShare);
  69. }
  70. if (context != nil)
  71. {
  72. // I'd prefer to put this stuff in the initialiseOnRenderThread() call, but doing
  73. // so causes mysterious timing-related failures.
  74. [EAGLContext setCurrentContext: context];
  75. gl::loadFunctions();
  76. createGLBuffers();
  77. deactivateCurrentContext();
  78. }
  79. else
  80. {
  81. jassertfalse;
  82. }
  83. }
  84. else
  85. {
  86. jassertfalse;
  87. }
  88. }
  89. }
  90. ~NativeContext()
  91. {
  92. releaseContext();
  93. [view removeFromSuperview];
  94. [view release];
  95. }
  96. bool initialiseOnRenderThread (OpenGLContext&) { return true; }
  97. void shutdownOnRenderThread()
  98. {
  99. JUCE_CHECK_OPENGL_ERROR
  100. freeGLBuffers();
  101. deactivateCurrentContext();
  102. }
  103. bool createdOk() const noexcept { return getRawContext() != nullptr; }
  104. void* getRawContext() const noexcept { return context; }
  105. GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; }
  106. bool makeActive() const noexcept
  107. {
  108. if (! [EAGLContext setCurrentContext: context])
  109. return false;
  110. glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle
  111. : frameBufferHandle);
  112. return true;
  113. }
  114. bool isActive() const noexcept
  115. {
  116. return [EAGLContext currentContext] == context;
  117. }
  118. static void deactivateCurrentContext()
  119. {
  120. [EAGLContext setCurrentContext: nil];
  121. }
  122. void swapBuffers()
  123. {
  124. if (useMSAA)
  125. {
  126. glBindFramebuffer (GL_DRAW_FRAMEBUFFER, frameBufferHandle);
  127. glBindFramebuffer (GL_READ_FRAMEBUFFER, msaaBufferHandle);
  128. if (openGLversion >= openGL3_2)
  129. {
  130. auto w = roundToInt (lastBounds.getWidth() * glLayer.contentsScale);
  131. auto h = roundToInt (lastBounds.getHeight() * glLayer.contentsScale);
  132. glBlitFramebuffer (0, 0, w, h,
  133. 0, 0, w, h,
  134. GL_COLOR_BUFFER_BIT,
  135. GL_NEAREST);
  136. }
  137. else
  138. {
  139. ::glResolveMultisampleFramebufferAPPLE();
  140. }
  141. }
  142. glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
  143. [context presentRenderbuffer: GL_RENDERBUFFER];
  144. if (needToRebuildBuffers)
  145. {
  146. needToRebuildBuffers = false;
  147. freeGLBuffers();
  148. createGLBuffers();
  149. makeActive();
  150. }
  151. }
  152. void updateWindowPosition (Rectangle<int> bounds)
  153. {
  154. view.frame = convertToCGRect (bounds);
  155. glLayer.contentsScale = (CGFloat) (Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale
  156. / component.getDesktopScaleFactor());
  157. if (lastBounds != bounds)
  158. {
  159. lastBounds = bounds;
  160. needToRebuildBuffers = true;
  161. }
  162. }
  163. bool setSwapInterval (int numFramesPerSwap) noexcept
  164. {
  165. swapFrames = numFramesPerSwap;
  166. return false;
  167. }
  168. int getSwapInterval() const noexcept { return swapFrames; }
  169. struct Locker { Locker (NativeContext&) {} };
  170. private:
  171. Component& component;
  172. JuceGLView* view = nil;
  173. CAEAGLLayer* glLayer = nil;
  174. EAGLContext* context = nil;
  175. const OpenGLVersion openGLversion;
  176. const bool useDepthBuffer, useMSAA;
  177. GLuint frameBufferHandle = 0, colorBufferHandle = 0, depthBufferHandle = 0,
  178. msaaColorHandle = 0, msaaBufferHandle = 0;
  179. Rectangle<int> lastBounds;
  180. int swapFrames = 0;
  181. bool needToRebuildBuffers = false;
  182. bool createContext (EAGLRenderingAPI type, void* contextToShare)
  183. {
  184. jassert (context == nil);
  185. context = [EAGLContext alloc];
  186. context = contextToShare != nullptr
  187. ? [context initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]]
  188. : [context initWithAPI: type];
  189. return context != nil;
  190. }
  191. void releaseContext()
  192. {
  193. [context release];
  194. context = nil;
  195. }
  196. //==============================================================================
  197. void createGLBuffers()
  198. {
  199. glGenFramebuffers (1, &frameBufferHandle);
  200. glGenRenderbuffers (1, &colorBufferHandle);
  201. glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
  202. glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
  203. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
  204. bool ok = [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer];
  205. jassert (ok); ignoreUnused (ok);
  206. GLint width, height;
  207. glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
  208. glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
  209. if (useMSAA)
  210. {
  211. glGenFramebuffers (1, &msaaBufferHandle);
  212. glGenRenderbuffers (1, &msaaColorHandle);
  213. glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
  214. glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle);
  215. glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height);
  216. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle);
  217. }
  218. if (useDepthBuffer)
  219. {
  220. glGenRenderbuffers (1, &depthBufferHandle);
  221. glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle);
  222. if (useMSAA)
  223. glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
  224. else
  225. glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
  226. glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle);
  227. }
  228. jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
  229. JUCE_CHECK_OPENGL_ERROR
  230. }
  231. void freeGLBuffers()
  232. {
  233. JUCE_CHECK_OPENGL_ERROR
  234. [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil];
  235. deleteFrameBuffer (frameBufferHandle);
  236. deleteFrameBuffer (msaaBufferHandle);
  237. deleteRenderBuffer (colorBufferHandle);
  238. deleteRenderBuffer (depthBufferHandle);
  239. deleteRenderBuffer (msaaColorHandle);
  240. JUCE_CHECK_OPENGL_ERROR
  241. }
  242. static void deleteFrameBuffer (GLuint& i) { if (i != 0) glDeleteFramebuffers (1, &i); i = 0; }
  243. static void deleteRenderBuffer (GLuint& i) { if (i != 0) glDeleteRenderbuffers (1, &i); i = 0; }
  244. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  245. };
  246. //==============================================================================
  247. bool OpenGLHelpers::isContextActive()
  248. {
  249. return [EAGLContext currentContext] != nil;
  250. }
  251. } // namespace juce