mirror of
https://github.com/OpenEmu/OpenEmuKit.git
synced 2025-11-01 11:08:14 +00:00
282 lines
9.2 KiB
Objective-C
282 lines
9.2 KiB
Objective-C
// Copyright (c) 2019, OpenEmu Team
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of the OpenEmu Team nor the
|
|
// names of its contributors may be used to endorse or promote products
|
|
// derived from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
|
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
@import CoreVideo;
|
|
@import OpenGL;
|
|
@import OpenGL.GL;
|
|
@import OpenGL.GL3;
|
|
|
|
#import "OECoreVideoTexture.h"
|
|
|
|
// source: https://developer.apple.com/documentation/metal/mixing_metal_and_opengl_rendering_in_a_view
|
|
|
|
typedef struct {
|
|
int cvPixelFormat;
|
|
MTLPixelFormat mtlFormat;
|
|
GLuint glInternalFormat;
|
|
GLuint glFormat;
|
|
GLuint glType;
|
|
} AAPLTextureFormatInfo;
|
|
|
|
// Table of equivalent formats across CoreVideo, Metal, and OpenGL
|
|
static AAPLTextureFormatInfo const AAPLInteropFormatTable[] =
|
|
{
|
|
// Core Video Pixel Format, Metal Pixel Format, GL internalformat, GL format, GL type
|
|
{ kCVPixelFormatType_32BGRA, MTLPixelFormatBGRA8Unorm, GL_RGBA, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV },
|
|
{ kCVPixelFormatType_ARGB2101010LEPacked, MTLPixelFormatBGR10A2Unorm, GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV },
|
|
{ kCVPixelFormatType_32BGRA, MTLPixelFormatBGRA8Unorm_sRGB, GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV },
|
|
{ kCVPixelFormatType_64RGBAHalf, MTLPixelFormatRGBA16Float, GL_RGBA, GL_RGBA, GL_HALF_FLOAT },
|
|
};
|
|
|
|
static const NSUInteger AAPLNumInteropFormats = sizeof(AAPLInteropFormatTable) / sizeof(AAPLTextureFormatInfo);
|
|
|
|
static AAPLTextureFormatInfo const * const textureFormatInfoFromMetalPixelFormat(MTLPixelFormat pixelFormat)
|
|
{
|
|
for(int i = 0; i < AAPLNumInteropFormats; i++) {
|
|
if(pixelFormat == AAPLInteropFormatTable[i].mtlFormat) {
|
|
return &AAPLInteropFormatTable[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
@implementation OECoreVideoTexture {
|
|
AAPLTextureFormatInfo const *_formatInfo;
|
|
|
|
CVPixelBufferRef _CVPixelBuffer;
|
|
|
|
CVMetalTextureCacheRef _CVMTLTextureCache;
|
|
CVMetalTextureRef _CVMTLTexture;
|
|
|
|
CVOpenGLTextureCacheRef _CVGLTextureCache;
|
|
CVOpenGLTextureRef _CVGLTexture;
|
|
CGLPixelFormatObj _CGLPixelFormat;
|
|
}
|
|
|
|
- (nonnull instancetype)initMetalPixelFormat:(MTLPixelFormat)mtlPixelFormat
|
|
{
|
|
self = [super init];
|
|
if (!self) {
|
|
return nil;
|
|
}
|
|
|
|
_formatInfo = textureFormatInfoFromMetalPixelFormat(mtlPixelFormat);
|
|
|
|
if(!_formatInfo)
|
|
{
|
|
assert(!"Metal Format supplied not supported in this sample");
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
#define SAFE_RELEASE(ref) \
|
|
if (ref) { \
|
|
CFRelease(ref); \
|
|
ref = nil; \
|
|
}
|
|
|
|
- (void)dealloc {
|
|
self.openGLContext = nil;
|
|
self.metalDevice = nil;
|
|
SAFE_RELEASE(_CVPixelBuffer);
|
|
}
|
|
|
|
- (void)setSize:(CGSize)size {
|
|
if (CGSizeEqualToSize(_size, size)) {
|
|
return;
|
|
}
|
|
|
|
_size = size;
|
|
SAFE_RELEASE(_CVPixelBuffer);
|
|
|
|
NSDictionary* cvBufferProperties = @{
|
|
(__bridge NSString*)kCVPixelBufferOpenGLCompatibilityKey : @YES,
|
|
(__bridge NSString*)kCVPixelBufferMetalCompatibilityKey : @YES,
|
|
};
|
|
CVReturn cvret = CVPixelBufferCreate(kCFAllocatorDefault,
|
|
size.width, size.height,
|
|
_formatInfo->cvPixelFormat,
|
|
(__bridge CFDictionaryRef)cvBufferProperties,
|
|
&_CVPixelBuffer);
|
|
|
|
if(cvret != kCVReturnSuccess)
|
|
{
|
|
assert(!"Failed to create CVPixelBufferf");
|
|
return;
|
|
}
|
|
|
|
if (_openGLContext) {
|
|
[self createGLTexture];
|
|
}
|
|
|
|
if (_metalDevice) {
|
|
[self createMetalTexture];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Metal resources
|
|
|
|
- (void)setMetalDevice:(id<MTLDevice>)metalDevice {
|
|
if (_metalDevice == metalDevice) {
|
|
return;
|
|
}
|
|
|
|
if (_metalDevice) {
|
|
[self releaseMetalTexture];
|
|
}
|
|
|
|
_metalDevice = metalDevice;
|
|
|
|
if (_metalDevice) {
|
|
[self createMetalTexture];
|
|
}
|
|
}
|
|
|
|
- (void)releaseMetalTexture {
|
|
_metalTexture = nil;
|
|
SAFE_RELEASE(_CVMTLTexture);
|
|
SAFE_RELEASE(_CVMTLTextureCache);
|
|
}
|
|
|
|
- (void)createMetalTexture {
|
|
[self releaseMetalTexture];
|
|
if (CGSizeEqualToSize(_size, CGSizeZero)) {
|
|
return;
|
|
}
|
|
|
|
CVReturn cvret;
|
|
// 1. Create a Metal Core Video texture cache from the pixel buffer.
|
|
cvret = CVMetalTextureCacheCreate(
|
|
kCFAllocatorDefault,
|
|
nil,
|
|
_metalDevice,
|
|
nil,
|
|
&_CVMTLTextureCache);
|
|
if(cvret != kCVReturnSuccess)
|
|
{
|
|
return;
|
|
}
|
|
// 2. Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
|
|
cvret = CVMetalTextureCacheCreateTextureFromImage(
|
|
kCFAllocatorDefault,
|
|
_CVMTLTextureCache,
|
|
_CVPixelBuffer, nil,
|
|
_formatInfo->mtlFormat,
|
|
_size.width, _size.height,
|
|
0,
|
|
&_CVMTLTexture);
|
|
if(cvret != kCVReturnSuccess)
|
|
{
|
|
assert(!"Failed to create Metal texture cache");
|
|
return;
|
|
}
|
|
// 3. Get a Metal texture using the CoreVideo Metal texture reference.
|
|
_metalTexture = CVMetalTextureGetTexture(_CVMTLTexture);
|
|
// Get a Metal texture object from the Core Video pixel buffer backed Metal texture image
|
|
if(!_metalTexture)
|
|
{
|
|
assert(!"Failed to get metal texture from CVMetalTextureRef");
|
|
return;
|
|
};
|
|
}
|
|
|
|
- (BOOL)metalTextureIsFlipped {
|
|
if (_CVMTLTexture) {
|
|
return CVMetalTextureIsFlipped(_CVMTLTexture);
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark - OpenGL resources
|
|
|
|
- (void)setOpenGLContext:(CGLContextObj)context {
|
|
if (_openGLContext == context) {
|
|
return;
|
|
}
|
|
|
|
if (context) {
|
|
CGLRetainContext(context);
|
|
}
|
|
|
|
if (_openGLContext != nil) {
|
|
[self releaseGLTexture];
|
|
CGLReleaseContext(_openGLContext);
|
|
}
|
|
|
|
_openGLContext = context;
|
|
|
|
if (_openGLContext) {
|
|
_CGLPixelFormat = CGLGetPixelFormat(context);
|
|
[self createGLTexture];
|
|
}
|
|
}
|
|
|
|
- (void)releaseGLTexture {
|
|
_openGLTexture = 0;
|
|
SAFE_RELEASE(_CVGLTexture);
|
|
SAFE_RELEASE(_CVGLTextureCache);
|
|
}
|
|
|
|
- (void)createGLTexture
|
|
{
|
|
[self releaseGLTexture];
|
|
|
|
if (CGSizeEqualToSize(_size, CGSizeZero)) {
|
|
return;
|
|
}
|
|
|
|
CVReturn cvret;
|
|
// 1. Create an OpenGL CoreVideo texture cache from the pixel buffer.
|
|
cvret = CVOpenGLTextureCacheCreate(kCFAllocatorDefault,
|
|
nil,
|
|
_openGLContext,
|
|
_CGLPixelFormat,
|
|
nil,
|
|
&_CVGLTextureCache);
|
|
if(cvret != kCVReturnSuccess)
|
|
{
|
|
assert(!"Failed to create OpenGL Texture Cache");
|
|
return;
|
|
}
|
|
|
|
// 2. Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
|
|
cvret = CVOpenGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
|
|
_CVGLTextureCache,
|
|
_CVPixelBuffer,
|
|
nil,
|
|
&_CVGLTexture);
|
|
if(cvret != kCVReturnSuccess)
|
|
{
|
|
assert(!"Failed to create OpenGL Texture From Image");
|
|
return;
|
|
}
|
|
|
|
// 3. Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
|
|
_openGLTexture = CVOpenGLTextureGetName(_CVGLTexture);
|
|
}
|
|
|
|
|
|
@end
|