mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-08-24 10:14:13 -05:00
624 lines
20 KiB
Objective-C
624 lines
20 KiB
Objective-C
/******************************************************************************
|
|
Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
#include "gl-subsystem.h"
|
|
#include <OpenGL/OpenGL.h>
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <AppKit/AppKit.h>
|
|
|
|
struct gl_windowinfo {
|
|
NSView *view;
|
|
NSOpenGLContext *context;
|
|
gs_texture_t *texture;
|
|
GLuint fbo;
|
|
};
|
|
|
|
struct gl_platform {
|
|
NSOpenGLContext *context;
|
|
};
|
|
|
|
typedef struct {
|
|
gs_swapchain_t *swapchain;
|
|
NSOpenGLContext *platformContext;
|
|
NSOpenGLContext *swapchainContext;
|
|
GLuint frameBufferObjectId;
|
|
enum gs_color_format textureFormat;
|
|
NSSize textureSize;
|
|
} OBSFrameBufferUpdateData;
|
|
|
|
static NSOpenGLContext *gl_context_create(NSOpenGLContext *share)
|
|
{
|
|
NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
|
|
NSOpenGLProfileVersion3_2Core, 0};
|
|
|
|
NSOpenGLPixelFormat *pixelFormat;
|
|
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
|
|
if (!pixelFormat) {
|
|
blog(LOG_ERROR, "Failed to create pixel format");
|
|
return nil;
|
|
}
|
|
|
|
NSOpenGLContext *context;
|
|
context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share];
|
|
[pixelFormat release];
|
|
|
|
if (!context) {
|
|
blog(LOG_ERROR, "Failed to create context");
|
|
return nil;
|
|
}
|
|
|
|
[context clearDrawable];
|
|
|
|
return context;
|
|
}
|
|
|
|
struct gl_platform *gl_platform_create(gs_device_t *device __unused, uint32_t adapter __unused)
|
|
{
|
|
NSOpenGLContext *context = gl_context_create(nil);
|
|
if (!context) {
|
|
blog(LOG_ERROR, "gl_context_create failed");
|
|
return NULL;
|
|
}
|
|
|
|
[context makeCurrentContext];
|
|
GLint swapInterval = 0;
|
|
[context setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
|
|
const bool isOpenGLLoaded = gladLoadGL() != 0;
|
|
|
|
if (!isOpenGLLoaded) {
|
|
blog(LOG_ERROR, "gladLoadGL failed");
|
|
[context release];
|
|
return NULL;
|
|
}
|
|
|
|
struct gl_platform *platform = bzalloc(sizeof(struct gl_platform));
|
|
platform->context = context;
|
|
return platform;
|
|
}
|
|
|
|
void gl_platform_destroy(struct gl_platform *platform)
|
|
{
|
|
if (!platform)
|
|
return;
|
|
|
|
[platform->context release];
|
|
platform->context = nil;
|
|
|
|
bfree(platform);
|
|
}
|
|
|
|
bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
|
|
{
|
|
NSOpenGLContext *platformContext = swap->device->plat->context;
|
|
NSOpenGLContext *swapContext = gl_context_create(platformContext);
|
|
|
|
BOOL hasFrameBuffer = NO;
|
|
|
|
if (swapContext) {
|
|
CGLContextObj platformContextObj = platformContext.CGLContextObj;
|
|
CGLContextObj swapContextObj = nil;
|
|
|
|
CGLLockContext(platformContextObj);
|
|
|
|
[platformContext makeCurrentContext];
|
|
|
|
struct gs_init_data *initData = &swap->info;
|
|
gs_texture_t *framebufferTexture = device_texture_create(swap->device, initData->cx, initData->cy,
|
|
initData->format, 1, NULL, GS_RENDER_TARGET);
|
|
|
|
if (!framebufferTexture) {
|
|
blog(LOG_ERROR, "gl_platform_init_swapchain: Unable to generate backing texture for frame buffer.");
|
|
goto init_swapchain_cleanup;
|
|
}
|
|
glFlush();
|
|
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
swapContextObj = swapContext.CGLContextObj;
|
|
CGLLockContext(swapContextObj);
|
|
|
|
[swapContext makeCurrentContext];
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
[swapContext setView:swap->wi->view];
|
|
#pragma clang diagnostic pop
|
|
|
|
GLint swapInterval = 0;
|
|
[swapContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
|
|
|
|
GLuint framebufferObjectId = 0;
|
|
BOOL hasGeneratedFramebuffer = gl_gen_framebuffers(1, &framebufferObjectId);
|
|
BOOL hasBoundFrameBuffer = gl_bind_framebuffer(GL_FRAMEBUFFER, framebufferObjectId);
|
|
|
|
if (!(hasGeneratedFramebuffer && hasBoundFrameBuffer)) {
|
|
goto init_swapchain_cleanup;
|
|
}
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTexture->texture, 0);
|
|
BOOL hasBoundFramebufferTexture = gl_success("glFrameBufferTexture2D");
|
|
|
|
if (!hasBoundFramebufferTexture) {
|
|
goto init_swapchain_cleanup;
|
|
}
|
|
|
|
glFlush();
|
|
gl_bind_framebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
hasFrameBuffer = YES;
|
|
swap->wi->context = swapContext;
|
|
swap->wi->fbo = framebufferObjectId;
|
|
swap->wi->texture = framebufferTexture;
|
|
|
|
init_swapchain_cleanup:
|
|
[NSOpenGLContext clearCurrentContext];
|
|
if (swapContextObj) {
|
|
CGLUnlockContext(swapContextObj);
|
|
}
|
|
CGLUnlockContext(platformContextObj);
|
|
}
|
|
|
|
return (swapContext && hasFrameBuffer);
|
|
}
|
|
|
|
void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
|
|
{
|
|
NSOpenGLContext *platformContext = swap->device->plat->context;
|
|
NSOpenGLContext *swapContext = swap->wi->context;
|
|
gs_texture_t *framebufferTexture = swap->wi->texture;
|
|
GLuint framebufferObjectId = swap->wi->fbo;
|
|
|
|
if (platformContext && swapContext) {
|
|
CGLContextObj platformContextObj = platformContext.CGLContextObj;
|
|
CGLLockContext(platformContextObj);
|
|
CGLContextObj swapContextObj = swapContext.CGLContextObj;
|
|
CGLLockContext(swapContextObj);
|
|
|
|
[swapContext makeCurrentContext];
|
|
|
|
gl_delete_framebuffers(1, &framebufferObjectId);
|
|
glFlush();
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
CGLUnlockContext(swapContextObj);
|
|
|
|
[platformContext makeCurrentContext];
|
|
gs_texture_destroy(framebufferTexture);
|
|
glFlush();
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
CGLUnlockContext(platformContextObj);
|
|
|
|
swap->wi->fbo = 0;
|
|
swap->wi->texture = NULL;
|
|
swap->wi->context = nil;
|
|
}
|
|
}
|
|
|
|
struct gl_windowinfo *gl_windowinfo_create(const struct gs_init_data *info)
|
|
{
|
|
if (!(info && info->window.view)) {
|
|
return NULL;
|
|
}
|
|
|
|
NSView *projectorView = info->window.view;
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
projectorView.wantsBestResolutionOpenGLSurface = YES;
|
|
#pragma clang diagnostic pop
|
|
|
|
if (projectorView.window) {
|
|
projectorView.window.colorSpace = NSColorSpace.sRGBColorSpace;
|
|
}
|
|
|
|
struct gl_windowinfo *windowInfo = bzalloc(sizeof(struct gl_windowinfo));
|
|
|
|
windowInfo->view = projectorView;
|
|
|
|
return windowInfo;
|
|
}
|
|
|
|
void gl_windowinfo_destroy(struct gl_windowinfo *windowInfo)
|
|
{
|
|
if (!windowInfo)
|
|
return;
|
|
|
|
windowInfo->view = nil;
|
|
bfree(windowInfo);
|
|
}
|
|
|
|
void updateSwapchainFramebuffer(gs_device_t *device, OBSFrameBufferUpdateData updateData)
|
|
{
|
|
if (!(device && updateData.swapchain)) {
|
|
return;
|
|
}
|
|
|
|
NSOpenGLContext *platformContext = updateData.platformContext;
|
|
NSOpenGLContext *swapContext = updateData.swapchainContext;
|
|
|
|
CGLContextObj platformContextObj = platformContext.CGLContextObj;
|
|
CGLLockContext(platformContextObj);
|
|
|
|
CGLContextObj swapContextObj = swapContext.CGLContextObj;
|
|
CGLLockContext(swapContextObj);
|
|
|
|
[swapContext makeCurrentContext];
|
|
[swapContext update];
|
|
|
|
gs_texture_t *framebufferTexture = device_texture_create(device, updateData.textureSize.width,
|
|
updateData.textureSize.height, updateData.textureFormat, 1,
|
|
NULL, GS_RENDER_TARGET);
|
|
|
|
if (!framebufferTexture) {
|
|
blog(LOG_ERROR, "gl_update (deferred): Unable to generate backing texture for frame buffer.");
|
|
|
|
goto updateSwapchainCleanup;
|
|
}
|
|
|
|
BOOL hasBoundFramebuffer = gl_bind_framebuffer(GL_FRAMEBUFFER, updateData.frameBufferObjectId);
|
|
|
|
if (!hasBoundFramebuffer) {
|
|
goto updateSwapchainCleanup;
|
|
}
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTexture->texture, 0);
|
|
BOOL hasBoundFramebufferTexture = gl_success("glFrameBufferTexture2D");
|
|
|
|
if (!hasBoundFramebufferTexture) {
|
|
GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
|
switch (framebufferStatus) {
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
blog(LOG_ERROR, "gl_update (deferred): Framebuffer object attachment is not complete.");
|
|
goto updateSwapchainCleanup;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
|
blog(LOG_ERROR, "gl_update (deferred): Framebuffer object attachment is missing.");
|
|
goto updateSwapchainCleanup;
|
|
default:
|
|
blog(LOG_ERROR, "gl_update (deferred): Framebuffer object is incomplete.");
|
|
goto updateSwapchainCleanup;
|
|
}
|
|
}
|
|
|
|
goto updateSwapchainCleanup;
|
|
}
|
|
|
|
gl_bind_framebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glFlush();
|
|
|
|
gs_swapchain_t *currentSwapchain = updateData.swapchain;
|
|
|
|
if (currentSwapchain && currentSwapchain->wi && currentSwapchain->wi->texture) {
|
|
gs_texture_t *priorFramebufferTexture = currentSwapchain->wi->texture;
|
|
currentSwapchain->wi->texture = framebufferTexture;
|
|
gs_texture_destroy(priorFramebufferTexture);
|
|
}
|
|
|
|
updateSwapchainCleanup:
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
CGLUnlockContext(swapContextObj);
|
|
CGLUnlockContext(platformContextObj);
|
|
}
|
|
|
|
void gl_update(gs_device_t *device)
|
|
{
|
|
if (!(device && device->cur_swap)) {
|
|
return;
|
|
}
|
|
|
|
gs_swapchain_t *currentSwapchain = device->cur_swap;
|
|
|
|
NSOpenGLContext *platformContext = device->plat->context;
|
|
NSOpenGLContext *swapContext = currentSwapchain->wi->context;
|
|
|
|
OBSFrameBufferUpdateData updateData = {.swapchain = currentSwapchain,
|
|
.platformContext = platformContext,
|
|
.swapchainContext = swapContext,
|
|
.frameBufferObjectId = currentSwapchain->wi->fbo,
|
|
.textureFormat = currentSwapchain->info.format,
|
|
.textureSize =
|
|
NSMakeSize(currentSwapchain->info.cx, currentSwapchain->info.cy)};
|
|
|
|
if (platformContext && swapContext) {
|
|
[platformContext retain];
|
|
[swapContext retain];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^() {
|
|
updateSwapchainFramebuffer(device, updateData);
|
|
|
|
[swapContext release];
|
|
[platformContext release];
|
|
});
|
|
}
|
|
}
|
|
|
|
void gl_clear_context(gs_device_t *device __unused)
|
|
{
|
|
[NSOpenGLContext clearCurrentContext];
|
|
}
|
|
|
|
void device_enter_context(gs_device_t *device)
|
|
{
|
|
NSOpenGLContext *platformContext = device->plat->context;
|
|
CGLLockContext(platformContext.CGLContextObj);
|
|
[platformContext makeCurrentContext];
|
|
}
|
|
|
|
void device_leave_context(gs_device_t *device)
|
|
{
|
|
glFlush();
|
|
[NSOpenGLContext clearCurrentContext];
|
|
device->cur_vertex_buffer = NULL;
|
|
device->cur_index_buffer = NULL;
|
|
device->cur_render_target = NULL;
|
|
device->cur_zstencil_buffer = NULL;
|
|
device->cur_swap = NULL;
|
|
device->cur_fbo = NULL;
|
|
|
|
NSOpenGLContext *platformContext = device->plat->context;
|
|
CGLUnlockContext(platformContext.CGLContextObj);
|
|
}
|
|
|
|
void *device_get_device_obj(gs_device_t *device)
|
|
{
|
|
return device->plat->context;
|
|
}
|
|
|
|
void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap)
|
|
{
|
|
if (device->cur_swap == swap)
|
|
return;
|
|
|
|
device->cur_swap = swap;
|
|
if (swap) {
|
|
device_set_render_target(device, swap->wi->texture, NULL);
|
|
}
|
|
}
|
|
|
|
bool device_is_present_ready(gs_device_t *device __unused)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void device_present(gs_device_t *device)
|
|
{
|
|
if (!device) {
|
|
blog(LOG_ERROR, "device_present: Called without valid device reference");
|
|
return;
|
|
}
|
|
|
|
if (!(device->cur_swap && device->cur_swap->wi && device->cur_swap->wi->context)) {
|
|
blog(LOG_ERROR, "device_present: Called without valid window info or NSOpenGLContext");
|
|
return;
|
|
}
|
|
|
|
gs_swapchain_t *currentSwapchain = device->cur_swap;
|
|
struct gl_windowinfo *info = currentSwapchain->wi;
|
|
|
|
glFlush();
|
|
|
|
NSOpenGLContext *currentContext = NSOpenGLContext.currentContext;
|
|
NSOpenGLContext *swapChainContext = info->context;
|
|
CGLContextObj swapChainContextObj = [swapChainContext CGLContextObj];
|
|
|
|
[NSOpenGLContext clearCurrentContext];
|
|
CGLLockContext(swapChainContextObj);
|
|
[swapChainContext makeCurrentContext];
|
|
|
|
bool hasBoundReadFramebuffer = gl_bind_framebuffer(GL_READ_FRAMEBUFFER, info->fbo);
|
|
bool hasBoundDrawFramebuffer = gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
if (!(hasBoundReadFramebuffer && hasBoundDrawFramebuffer)) {
|
|
goto device_present_cleanup;
|
|
}
|
|
|
|
const UInt32 width = currentSwapchain->info.cx;
|
|
const UInt32 height = currentSwapchain->info.cy;
|
|
|
|
glBlitFramebuffer(0, 0, width, height, 0, height, width, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
bool didBlitFramebuffer = gl_success("glBlitFramebuffer");
|
|
|
|
if (!didBlitFramebuffer) {
|
|
goto device_present_cleanup;
|
|
}
|
|
|
|
[swapChainContext flushBuffer];
|
|
glFlush();
|
|
|
|
device_present_cleanup:
|
|
CGLUnlockContext(swapChainContextObj);
|
|
[NSOpenGLContext clearCurrentContext];
|
|
[currentContext makeCurrentContext];
|
|
}
|
|
|
|
bool device_is_monitor_hdr(gs_device_t *device __unused, void *monitor __unused)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, uint32_t *height)
|
|
{
|
|
if (width)
|
|
*width = swap->info.cx;
|
|
if (height)
|
|
*height = swap->info.cy;
|
|
}
|
|
|
|
gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device, void *iosurf)
|
|
{
|
|
IOSurfaceRef ref = (IOSurfaceRef) iosurf;
|
|
struct gs_texture_2d *tex = bzalloc(sizeof(struct gs_texture_2d));
|
|
|
|
OSType pixelFormat = IOSurfaceGetPixelFormat(ref);
|
|
|
|
GLenum color_format = 0;
|
|
GLenum internal_format = 0;
|
|
GLenum texture_type = 0;
|
|
|
|
switch (pixelFormat) {
|
|
case kCVPixelFormatType_ARGB2101010LEPacked: {
|
|
color_format = GL_BGRA;
|
|
internal_format = convert_gs_internal_format(GS_R10G10B10A2);
|
|
texture_type = GL_UNSIGNED_INT_2_10_10_10_REV;
|
|
|
|
break;
|
|
}
|
|
/* Versions of Syphon before 2dc6d31 provide IOSurfaces with pixel format 0.
|
|
* A pixel format of 0 means they are invalid, but in practice the IOSurfaces provided by Syphon are always
|
|
* 32BGRA. Historically, OBS had a bug that accidentally treated any invalid surface as 32BGRA, so those
|
|
* invalid surfaces were never noticed. To keep Syphon working, we need to keep this (wrong) behavior.
|
|
* This is suboptimal as IOSurfaces that are actually invalid (possibly coming from somewhere else) also
|
|
* get treated as valid 32BGRA, but it is how it is. */
|
|
case 0:
|
|
case kCVPixelFormatType_32BGRA: {
|
|
color_format = convert_gs_format(GS_BGRA);
|
|
internal_format = convert_gs_internal_format(GS_BGRA);
|
|
texture_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
break;
|
|
}
|
|
case kCVPixelFormatType_64RGBAHalf: {
|
|
color_format = GL_RGBA;
|
|
internal_format = GL_RGBA_FLOAT16_APPLE;
|
|
texture_type = GL_HALF_APPLE;
|
|
break;
|
|
}
|
|
default:
|
|
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pixelFormat, pixelFormat >> 24, pixelFormat >> 16,
|
|
pixelFormat >> 8, pixelFormat);
|
|
goto fail;
|
|
}
|
|
|
|
tex->base.device = device;
|
|
tex->base.type = GS_TEXTURE_2D;
|
|
tex->base.format = color_format;
|
|
tex->base.levels = 1;
|
|
tex->base.gl_format = color_format;
|
|
tex->base.gl_internal_format = internal_format;
|
|
tex->base.gl_type = texture_type;
|
|
tex->base.gl_target = GL_TEXTURE_RECTANGLE_ARB;
|
|
tex->base.is_dynamic = false;
|
|
tex->base.is_render_target = false;
|
|
tex->base.gen_mipmaps = false;
|
|
tex->width = (uint32_t) IOSurfaceGetWidth(ref);
|
|
tex->height = (uint32_t) IOSurfaceGetHeight(ref);
|
|
|
|
if (!gl_gen_textures(1, &tex->base.texture))
|
|
goto fail;
|
|
|
|
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
|
|
goto fail;
|
|
|
|
CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
|
|
tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
|
|
tex->base.gl_type, ref, 0);
|
|
|
|
if (err != kCGLNoError) {
|
|
blog(LOG_ERROR,
|
|
"CGLTexImageIOSurface2D: %u, %s"
|
|
" (device_texture_create_from_iosurface)",
|
|
err, CGLErrorString(err));
|
|
|
|
gl_success("CGLTexImageIOSurface2D");
|
|
goto fail;
|
|
}
|
|
|
|
if (!gl_tex_param_i(tex->base.gl_target, GL_TEXTURE_MAX_LEVEL, 0))
|
|
goto fail;
|
|
|
|
if (!gl_bind_texture(tex->base.gl_target, 0))
|
|
goto fail;
|
|
|
|
return (gs_texture_t *) tex;
|
|
|
|
fail:
|
|
gs_texture_destroy((gs_texture_t *) tex);
|
|
blog(LOG_ERROR, "device_texture_create_from_iosurface (GL) failed");
|
|
return NULL;
|
|
}
|
|
|
|
gs_texture_t *device_texture_open_shared(gs_device_t *device, uint32_t handle)
|
|
{
|
|
gs_texture_t *texture = NULL;
|
|
IOSurfaceRef ref = IOSurfaceLookupFromMachPort((mach_port_t) handle);
|
|
texture = device_texture_create_from_iosurface(device, ref);
|
|
CFRelease(ref);
|
|
return texture;
|
|
}
|
|
|
|
bool device_shared_texture_available(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf)
|
|
{
|
|
if (!texture)
|
|
return false;
|
|
|
|
if (!iosurf)
|
|
return false;
|
|
|
|
struct gs_texture_2d *tex = (struct gs_texture_2d *) texture;
|
|
IOSurfaceRef ref = (IOSurfaceRef) iosurf;
|
|
|
|
OSType pixelFormat = IOSurfaceGetPixelFormat(ref);
|
|
|
|
switch (pixelFormat) {
|
|
case kCVPixelFormatType_ARGB2101010LEPacked:
|
|
case kCVPixelFormatType_32BGRA:
|
|
case kCVPixelFormatType_64RGBAHalf:
|
|
break;
|
|
case 0:
|
|
blog(LOG_ERROR, "Invalid IOSurface Buffer");
|
|
return false;
|
|
default:
|
|
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pixelFormat, pixelFormat >> 24, pixelFormat >> 16,
|
|
pixelFormat >> 8, pixelFormat);
|
|
return false;
|
|
}
|
|
|
|
tex->width = (uint32_t) IOSurfaceGetWidth(ref);
|
|
tex->height = (uint32_t) IOSurfaceGetHeight(ref);
|
|
|
|
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
|
|
return false;
|
|
|
|
CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
|
|
tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
|
|
tex->base.gl_type, ref, 0);
|
|
|
|
if (err != kCGLNoError) {
|
|
blog(LOG_ERROR,
|
|
"CGLTexImageIOSurface2D: %u, %s"
|
|
" (gs_texture_rebind_iosurface)",
|
|
err, CGLErrorString(err));
|
|
|
|
gl_success("CGLTexImageIOSurface2D");
|
|
return false;
|
|
}
|
|
|
|
if (!gl_bind_texture(tex->base.gl_target, 0))
|
|
return false;
|
|
|
|
return true;
|
|
}
|