gl-cocoa.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /******************************************************************************
  2. Copyright (C) 2013 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "gl-subsystem.h"
  15. #include <OpenGL/OpenGL.h>
  16. #import <Cocoa/Cocoa.h>
  17. #import <AppKit/AppKit.h>
  18. struct gl_windowinfo {
  19. NSView *view;
  20. NSOpenGLContext *context;
  21. gs_texture_t *texture;
  22. GLuint fbo;
  23. };
  24. struct gl_platform {
  25. NSOpenGLContext *context;
  26. };
  27. typedef struct {
  28. gs_swapchain_t *swapchain;
  29. NSOpenGLContext *platformContext;
  30. NSOpenGLContext *swapchainContext;
  31. GLuint frameBufferObjectId;
  32. enum gs_color_format textureFormat;
  33. NSSize textureSize;
  34. } OBSFrameBufferUpdateData;
  35. static NSOpenGLContext *gl_context_create(NSOpenGLContext *share)
  36. {
  37. NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
  38. NSOpenGLProfileVersion3_2Core, 0};
  39. NSOpenGLPixelFormat *pixelFormat;
  40. pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
  41. if (!pixelFormat) {
  42. blog(LOG_ERROR, "Failed to create pixel format");
  43. return nil;
  44. }
  45. NSOpenGLContext *context;
  46. context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share];
  47. [pixelFormat release];
  48. if (!context) {
  49. blog(LOG_ERROR, "Failed to create context");
  50. return nil;
  51. }
  52. [context clearDrawable];
  53. return context;
  54. }
  55. struct gl_platform *gl_platform_create(gs_device_t *device __unused, uint32_t adapter __unused)
  56. {
  57. NSOpenGLContext *context = gl_context_create(nil);
  58. if (!context) {
  59. blog(LOG_ERROR, "gl_context_create failed");
  60. return NULL;
  61. }
  62. [context makeCurrentContext];
  63. GLint swapInterval = 0;
  64. [context setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
  65. const bool isOpenGLLoaded = gladLoadGL() != 0;
  66. if (!isOpenGLLoaded) {
  67. blog(LOG_ERROR, "gladLoadGL failed");
  68. [context release];
  69. return NULL;
  70. }
  71. struct gl_platform *platform = bzalloc(sizeof(struct gl_platform));
  72. platform->context = context;
  73. return platform;
  74. }
  75. void gl_platform_destroy(struct gl_platform *platform)
  76. {
  77. if (!platform)
  78. return;
  79. [platform->context release];
  80. platform->context = nil;
  81. bfree(platform);
  82. }
  83. bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
  84. {
  85. NSOpenGLContext *platformContext = swap->device->plat->context;
  86. NSOpenGLContext *swapContext = gl_context_create(platformContext);
  87. BOOL hasFrameBuffer = NO;
  88. if (swapContext) {
  89. CGLContextObj platformContextObj = platformContext.CGLContextObj;
  90. CGLContextObj swapContextObj = nil;
  91. CGLLockContext(platformContextObj);
  92. [platformContext makeCurrentContext];
  93. struct gs_init_data *initData = &swap->info;
  94. gs_texture_t *framebufferTexture = device_texture_create(swap->device, initData->cx, initData->cy,
  95. initData->format, 1, NULL, GS_RENDER_TARGET);
  96. if (!framebufferTexture) {
  97. blog(LOG_ERROR, "gl_platform_init_swapchain: Unable to generate backing texture for frame buffer.");
  98. goto init_swapchain_cleanup;
  99. }
  100. glFlush();
  101. [NSOpenGLContext clearCurrentContext];
  102. swapContextObj = swapContext.CGLContextObj;
  103. CGLLockContext(swapContextObj);
  104. [swapContext makeCurrentContext];
  105. #pragma clang diagnostic push
  106. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  107. [swapContext setView:swap->wi->view];
  108. #pragma clang diagnostic pop
  109. GLint swapInterval = 0;
  110. [swapContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
  111. GLuint framebufferObjectId = 0;
  112. BOOL hasGeneratedFramebuffer = gl_gen_framebuffers(1, &framebufferObjectId);
  113. BOOL hasBoundFrameBuffer = gl_bind_framebuffer(GL_FRAMEBUFFER, framebufferObjectId);
  114. if (!(hasGeneratedFramebuffer && hasBoundFrameBuffer)) {
  115. goto init_swapchain_cleanup;
  116. }
  117. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTexture->texture, 0);
  118. BOOL hasBoundFramebufferTexture = gl_success("glFrameBufferTexture2D");
  119. if (!hasBoundFramebufferTexture) {
  120. goto init_swapchain_cleanup;
  121. }
  122. glFlush();
  123. gl_bind_framebuffer(GL_FRAMEBUFFER, 0);
  124. hasFrameBuffer = YES;
  125. swap->wi->context = swapContext;
  126. swap->wi->fbo = framebufferObjectId;
  127. swap->wi->texture = framebufferTexture;
  128. init_swapchain_cleanup:
  129. [NSOpenGLContext clearCurrentContext];
  130. if (swapContextObj) {
  131. CGLUnlockContext(swapContextObj);
  132. }
  133. CGLUnlockContext(platformContextObj);
  134. }
  135. return (swapContext && hasFrameBuffer);
  136. }
  137. void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
  138. {
  139. NSOpenGLContext *platformContext = swap->device->plat->context;
  140. NSOpenGLContext *swapContext = swap->wi->context;
  141. gs_texture_t *framebufferTexture = swap->wi->texture;
  142. GLuint framebufferObjectId = swap->wi->fbo;
  143. if (platformContext && swapContext) {
  144. CGLContextObj platformContextObj = platformContext.CGLContextObj;
  145. CGLLockContext(platformContextObj);
  146. CGLContextObj swapContextObj = swapContext.CGLContextObj;
  147. CGLLockContext(swapContextObj);
  148. [swapContext makeCurrentContext];
  149. gl_delete_framebuffers(1, &framebufferObjectId);
  150. glFlush();
  151. [NSOpenGLContext clearCurrentContext];
  152. CGLUnlockContext(swapContextObj);
  153. [platformContext makeCurrentContext];
  154. gs_texture_destroy(framebufferTexture);
  155. glFlush();
  156. [NSOpenGLContext clearCurrentContext];
  157. CGLUnlockContext(platformContextObj);
  158. swap->wi->fbo = 0;
  159. swap->wi->texture = NULL;
  160. swap->wi->context = nil;
  161. }
  162. }
  163. struct gl_windowinfo *gl_windowinfo_create(const struct gs_init_data *info)
  164. {
  165. if (!(info && info->window.view)) {
  166. return NULL;
  167. }
  168. NSView *projectorView = info->window.view;
  169. #pragma clang diagnostic push
  170. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  171. projectorView.wantsBestResolutionOpenGLSurface = YES;
  172. #pragma clang diagnostic pop
  173. if (projectorView.window) {
  174. projectorView.window.colorSpace = NSColorSpace.sRGBColorSpace;
  175. }
  176. struct gl_windowinfo *windowInfo = bzalloc(sizeof(struct gl_windowinfo));
  177. windowInfo->view = projectorView;
  178. return windowInfo;
  179. }
  180. void gl_windowinfo_destroy(struct gl_windowinfo *windowInfo)
  181. {
  182. if (!windowInfo)
  183. return;
  184. windowInfo->view = nil;
  185. bfree(windowInfo);
  186. }
  187. void updateSwapchainFramebuffer(gs_device_t *device, OBSFrameBufferUpdateData updateData)
  188. {
  189. if (!(device && updateData.swapchain)) {
  190. return;
  191. }
  192. NSOpenGLContext *platformContext = updateData.platformContext;
  193. NSOpenGLContext *swapContext = updateData.swapchainContext;
  194. CGLContextObj platformContextObj = platformContext.CGLContextObj;
  195. CGLLockContext(platformContextObj);
  196. CGLContextObj swapContextObj = swapContext.CGLContextObj;
  197. CGLLockContext(swapContextObj);
  198. [swapContext makeCurrentContext];
  199. [swapContext update];
  200. gs_texture_t *framebufferTexture = device_texture_create(device, updateData.textureSize.width,
  201. updateData.textureSize.height, updateData.textureFormat, 1,
  202. NULL, GS_RENDER_TARGET);
  203. if (!framebufferTexture) {
  204. blog(LOG_ERROR, "gl_update (deferred): Unable to generate backing texture for frame buffer.");
  205. goto updateSwapchainCleanup;
  206. }
  207. BOOL hasBoundFramebuffer = gl_bind_framebuffer(GL_FRAMEBUFFER, updateData.frameBufferObjectId);
  208. if (!hasBoundFramebuffer) {
  209. goto updateSwapchainCleanup;
  210. }
  211. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTexture->texture, 0);
  212. BOOL hasBoundFramebufferTexture = gl_success("glFrameBufferTexture2D");
  213. if (!hasBoundFramebufferTexture) {
  214. GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  215. if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
  216. switch (framebufferStatus) {
  217. case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
  218. blog(LOG_ERROR, "gl_update (deferred): Framebuffer object attachment is not complete.");
  219. goto updateSwapchainCleanup;
  220. case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  221. blog(LOG_ERROR, "gl_update (deferred): Framebuffer object attachment is missing.");
  222. goto updateSwapchainCleanup;
  223. default:
  224. blog(LOG_ERROR, "gl_update (deferred): Framebuffer object is incomplete.");
  225. goto updateSwapchainCleanup;
  226. }
  227. }
  228. goto updateSwapchainCleanup;
  229. }
  230. gl_bind_framebuffer(GL_FRAMEBUFFER, 0);
  231. glFlush();
  232. gs_swapchain_t *currentSwapchain = updateData.swapchain;
  233. if (currentSwapchain && currentSwapchain->wi && currentSwapchain->wi->texture) {
  234. gs_texture_t *priorFramebufferTexture = currentSwapchain->wi->texture;
  235. currentSwapchain->wi->texture = framebufferTexture;
  236. gs_texture_destroy(priorFramebufferTexture);
  237. }
  238. updateSwapchainCleanup:
  239. [NSOpenGLContext clearCurrentContext];
  240. CGLUnlockContext(swapContextObj);
  241. CGLUnlockContext(platformContextObj);
  242. }
  243. void gl_update(gs_device_t *device)
  244. {
  245. if (!(device && device->cur_swap)) {
  246. return;
  247. }
  248. gs_swapchain_t *currentSwapchain = device->cur_swap;
  249. NSOpenGLContext *platformContext = device->plat->context;
  250. NSOpenGLContext *swapContext = currentSwapchain->wi->context;
  251. OBSFrameBufferUpdateData updateData = {.swapchain = currentSwapchain,
  252. .platformContext = platformContext,
  253. .swapchainContext = swapContext,
  254. .frameBufferObjectId = currentSwapchain->wi->fbo,
  255. .textureFormat = currentSwapchain->info.format,
  256. .textureSize =
  257. NSMakeSize(currentSwapchain->info.cx, currentSwapchain->info.cy)};
  258. if (platformContext && swapContext) {
  259. [platformContext retain];
  260. [swapContext retain];
  261. dispatch_async(dispatch_get_main_queue(), ^() {
  262. updateSwapchainFramebuffer(device, updateData);
  263. [swapContext release];
  264. [platformContext release];
  265. });
  266. }
  267. }
  268. void gl_clear_context(gs_device_t *device __unused)
  269. {
  270. [NSOpenGLContext clearCurrentContext];
  271. }
  272. void device_enter_context(gs_device_t *device)
  273. {
  274. NSOpenGLContext *platformContext = device->plat->context;
  275. CGLLockContext(platformContext.CGLContextObj);
  276. [platformContext makeCurrentContext];
  277. }
  278. void device_leave_context(gs_device_t *device)
  279. {
  280. glFlush();
  281. [NSOpenGLContext clearCurrentContext];
  282. device->cur_vertex_buffer = NULL;
  283. device->cur_index_buffer = NULL;
  284. device->cur_render_target = NULL;
  285. device->cur_zstencil_buffer = NULL;
  286. device->cur_swap = NULL;
  287. device->cur_fbo = NULL;
  288. NSOpenGLContext *platformContext = device->plat->context;
  289. CGLUnlockContext(platformContext.CGLContextObj);
  290. }
  291. void *device_get_device_obj(gs_device_t *device)
  292. {
  293. return device->plat->context;
  294. }
  295. void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap)
  296. {
  297. if (device->cur_swap == swap)
  298. return;
  299. device->cur_swap = swap;
  300. if (swap) {
  301. device_set_render_target(device, swap->wi->texture, NULL);
  302. }
  303. }
  304. bool device_is_present_ready(gs_device_t *device __unused)
  305. {
  306. return true;
  307. }
  308. void device_present(gs_device_t *device)
  309. {
  310. if (!device) {
  311. blog(LOG_ERROR, "device_present: Called without valid device reference");
  312. return;
  313. }
  314. if (!(device->cur_swap && device->cur_swap->wi && device->cur_swap->wi->context)) {
  315. blog(LOG_ERROR, "device_present: Called without valid window info or NSOpenGLContext");
  316. return;
  317. }
  318. gs_swapchain_t *currentSwapchain = device->cur_swap;
  319. struct gl_windowinfo *info = currentSwapchain->wi;
  320. glFlush();
  321. NSOpenGLContext *currentContext = NSOpenGLContext.currentContext;
  322. NSOpenGLContext *swapChainContext = info->context;
  323. CGLContextObj swapChainContextObj = [swapChainContext CGLContextObj];
  324. [NSOpenGLContext clearCurrentContext];
  325. CGLLockContext(swapChainContextObj);
  326. [swapChainContext makeCurrentContext];
  327. bool hasBoundReadFramebuffer = gl_bind_framebuffer(GL_READ_FRAMEBUFFER, info->fbo);
  328. bool hasBoundDrawFramebuffer = gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, 0);
  329. if (!(hasBoundReadFramebuffer && hasBoundDrawFramebuffer)) {
  330. goto device_present_cleanup;
  331. }
  332. const UInt32 width = currentSwapchain->info.cx;
  333. const UInt32 height = currentSwapchain->info.cy;
  334. glBlitFramebuffer(0, 0, width, height, 0, height, width, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  335. bool didBlitFramebuffer = gl_success("glBlitFramebuffer");
  336. if (!didBlitFramebuffer) {
  337. goto device_present_cleanup;
  338. }
  339. [swapChainContext flushBuffer];
  340. glFlush();
  341. device_present_cleanup:
  342. CGLUnlockContext(swapChainContextObj);
  343. [NSOpenGLContext clearCurrentContext];
  344. [currentContext makeCurrentContext];
  345. }
  346. bool device_is_monitor_hdr(gs_device_t *device __unused, void *monitor __unused)
  347. {
  348. return false;
  349. }
  350. void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, uint32_t *height)
  351. {
  352. if (width)
  353. *width = swap->info.cx;
  354. if (height)
  355. *height = swap->info.cy;
  356. }
  357. gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device, void *iosurf)
  358. {
  359. IOSurfaceRef ref = (IOSurfaceRef) iosurf;
  360. struct gs_texture_2d *tex = bzalloc(sizeof(struct gs_texture_2d));
  361. OSType pixelFormat = IOSurfaceGetPixelFormat(ref);
  362. GLenum color_format = 0;
  363. GLenum internal_format = 0;
  364. GLenum texture_type = 0;
  365. switch (pixelFormat) {
  366. case kCVPixelFormatType_ARGB2101010LEPacked: {
  367. color_format = GL_BGRA;
  368. internal_format = convert_gs_internal_format(GS_R10G10B10A2);
  369. texture_type = GL_UNSIGNED_INT_2_10_10_10_REV;
  370. break;
  371. }
  372. case kCVPixelFormatType_32BGRA: {
  373. color_format = convert_gs_format(GS_BGRA);
  374. internal_format = convert_gs_internal_format(GS_BGRA);
  375. texture_type = GL_UNSIGNED_INT_8_8_8_8_REV;
  376. break;
  377. }
  378. case 0:
  379. blog(LOG_ERROR, "Invalid IOSurface Buffer");
  380. goto fail;
  381. default:
  382. blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pixelFormat, pixelFormat >> 24, pixelFormat >> 16,
  383. pixelFormat >> 8, pixelFormat);
  384. goto fail;
  385. }
  386. tex->base.device = device;
  387. tex->base.type = GS_TEXTURE_2D;
  388. tex->base.format = color_format;
  389. tex->base.levels = 1;
  390. tex->base.gl_format = color_format;
  391. tex->base.gl_internal_format = internal_format;
  392. tex->base.gl_type = texture_type;
  393. tex->base.gl_target = GL_TEXTURE_RECTANGLE_ARB;
  394. tex->base.is_dynamic = false;
  395. tex->base.is_render_target = false;
  396. tex->base.gen_mipmaps = false;
  397. tex->width = (uint32_t) IOSurfaceGetWidth(ref);
  398. tex->height = (uint32_t) IOSurfaceGetHeight(ref);
  399. if (!gl_gen_textures(1, &tex->base.texture))
  400. goto fail;
  401. if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
  402. goto fail;
  403. CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
  404. tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
  405. tex->base.gl_type, ref, 0);
  406. if (err != kCGLNoError) {
  407. blog(LOG_ERROR,
  408. "CGLTexImageIOSurface2D: %u, %s"
  409. " (device_texture_create_from_iosurface)",
  410. err, CGLErrorString(err));
  411. gl_success("CGLTexImageIOSurface2D");
  412. goto fail;
  413. }
  414. if (!gl_tex_param_i(tex->base.gl_target, GL_TEXTURE_MAX_LEVEL, 0))
  415. goto fail;
  416. if (!gl_bind_texture(tex->base.gl_target, 0))
  417. goto fail;
  418. return (gs_texture_t *) tex;
  419. fail:
  420. gs_texture_destroy((gs_texture_t *) tex);
  421. blog(LOG_ERROR, "device_texture_create_from_iosurface (GL) failed");
  422. return NULL;
  423. }
  424. gs_texture_t *device_texture_open_shared(gs_device_t *device, uint32_t handle)
  425. {
  426. gs_texture_t *texture = NULL;
  427. IOSurfaceRef ref = IOSurfaceLookupFromMachPort((mach_port_t) handle);
  428. texture = device_texture_create_from_iosurface(device, ref);
  429. CFRelease(ref);
  430. return texture;
  431. }
  432. bool device_shared_texture_available(void)
  433. {
  434. return true;
  435. }
  436. bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf)
  437. {
  438. if (!texture)
  439. return false;
  440. if (!iosurf)
  441. return false;
  442. struct gs_texture_2d *tex = (struct gs_texture_2d *) texture;
  443. IOSurfaceRef ref = (IOSurfaceRef) iosurf;
  444. OSType pixelFormat = IOSurfaceGetPixelFormat(ref);
  445. switch (pixelFormat) {
  446. case kCVPixelFormatType_ARGB2101010LEPacked:
  447. case kCVPixelFormatType_32BGRA:
  448. break;
  449. case 0:
  450. blog(LOG_ERROR, "Invalid IOSurface Buffer");
  451. return false;
  452. default:
  453. blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pixelFormat, pixelFormat >> 24, pixelFormat >> 16,
  454. pixelFormat >> 8, pixelFormat);
  455. return false;
  456. }
  457. tex->width = (uint32_t) IOSurfaceGetWidth(ref);
  458. tex->height = (uint32_t) IOSurfaceGetHeight(ref);
  459. if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
  460. return false;
  461. CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
  462. tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
  463. tex->base.gl_type, ref, 0);
  464. if (err != kCGLNoError) {
  465. blog(LOG_ERROR,
  466. "CGLTexImageIOSurface2D: %u, %s"
  467. " (gs_texture_rebind_iosurface)",
  468. err, CGLErrorString(err));
  469. gl_success("CGLTexImageIOSurface2D");
  470. return false;
  471. }
  472. if (!gl_bind_texture(tex->base.gl_target, 0))
  473. return false;
  474. return true;
  475. }