mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-17 17:16:40 +08:00
Per the Apple documentation, when 0 is returned from
IOSurfaceGetPixelFormat, the IOSurface is invalid. Prior to [1] however,
any invalid surfaces were (likely accidentally) treated as 32BGRA.
And as it turns out, any IOSurface received from Syphon does not have
the pixel format set, thus returning 0. Because the pixel format happens
to always be 32BGRA, this was never caught. [1] then slightly refactored
the code, fixing the bug not realizing it ever existed. This lead to
Syphon no longer working.
Treating 0 as 32BGRA appears to be the only way to fix this situation
for now and restore prior behavior. Ideally in the future Syphon
provides valid IOSurfaces. An upstream fix to Syphon-Framework is
already submitted, however even if merged, servers would need to update
to that fixed version before things work again. Therefore, this
workaround is needed on the OBS side.
However, the plan for now is for the Metal renderer to not get this
relaxation. It never had it (so Syphon never worked there, making this
not a regression) and it's marked as experimental. Hopefully, by the
time it's stable and/or default, Syphon will be fixed and servers have
been updated.
[1]: 53ad05db9f
617 lines
20 KiB
Objective-C
617 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;
|
|
}
|
|
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:
|
|
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;
|
|
}
|