mirror of
https://github.com/CTCaer/RetroArch.git
synced 2025-01-26 13:25:28 +00:00
feat(apple): Metal support for macOS
* includes rgui * shader support This is a work-in-progress and there are some bugs and visual artifacts still to be fixed.
This commit is contained in:
parent
b10f1faf7a
commit
411bcf8bdd
@ -80,6 +80,12 @@ static const bool _vulkan_supp = true;
|
||||
static const bool _vulkan_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_METAL
|
||||
static const bool _metal_supp = true;
|
||||
#else
|
||||
static const bool _metal_supp = false;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_OPENGLES) || defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3) || defined(HAVE_OPENGLES_3_1) || defined(HAVE_OPENGLES_3_2)
|
||||
static const bool _opengles_supp = true;
|
||||
#else
|
||||
|
@ -292,23 +292,29 @@ static void frontend_darwin_get_os(char *s, size_t len, int *major, int *minor)
|
||||
get_ios_version(major, minor);
|
||||
strlcpy(s, "iOS", len);
|
||||
#elif defined(OSX)
|
||||
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)])
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
NSInteger majorVersion;
|
||||
NSInteger minorVersion;
|
||||
NSInteger patchVersion;
|
||||
} NSMyOSVersion;
|
||||
NSMyOSVersion version = ((NSMyOSVersion(*)(id, SEL))objc_msgSend_stret)([NSProcessInfo processInfo], @selector(operatingSystemVersion));
|
||||
*major = version.majorVersion;
|
||||
*minor = version.minorVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
Gestalt(gestaltSystemVersionMinor, (SInt32*)minor);
|
||||
Gestalt(gestaltSystemVersionMajor, (SInt32*)major);
|
||||
}
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
|
||||
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
*major = (int)version.majorVersion;
|
||||
*minor = (int)version.minorVersion;
|
||||
#else
|
||||
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)])
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
NSInteger majorVersion;
|
||||
NSInteger minorVersion;
|
||||
NSInteger patchVersion;
|
||||
} NSMyOSVersion;
|
||||
NSMyOSVersion version = ((NSMyOSVersion(*)(id, SEL))objc_msgSend_stret)([NSProcessInfo processInfo], @selector(operatingSystemVersion));
|
||||
*major = version.majorVersion;
|
||||
*minor = version.minorVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
Gestalt(gestaltSystemVersionMinor, (SInt32*)minor);
|
||||
Gestalt(gestaltSystemVersionMajor, (SInt32*)major);
|
||||
}
|
||||
#endif
|
||||
strlcpy(s, "OSX", len);
|
||||
#endif
|
||||
}
|
||||
@ -743,5 +749,6 @@ frontend_ctx_driver_t frontend_ctx_darwin = {
|
||||
NULL, /* detach_console */
|
||||
NULL, /* watch_path_for_changes */
|
||||
NULL, /* check_for_path_changes */
|
||||
NULL, /* set_sustained_performance_mode */
|
||||
"darwin",
|
||||
};
|
||||
|
37
gfx/common/metal/Context.h
Normal file
37
gfx/common/metal/Context.h
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Context.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface Context : NSObject
|
||||
|
||||
@property (readonly) id<MTLDevice> device;
|
||||
@property (readonly) id<MTLLibrary> library;
|
||||
@property (readonly) id<MTLCommandQueue> commandQueue;
|
||||
/*! @brief Returns the command buffer for the current frame */
|
||||
@property (readonly) id<MTLCommandBuffer> commandBuffer;
|
||||
@property (readonly) id<CAMetalDrawable> nextDrawable;
|
||||
@property (readonly) id<MTLTexture> renderTexture;
|
||||
|
||||
+ (instancetype)newContextWithDevice:(id<MTLDevice>)d
|
||||
layer:(CAMetalLayer *)layer
|
||||
library:(id<MTLLibrary>)l
|
||||
commandQueue:(id<MTLCommandQueue>)q;
|
||||
|
||||
/*! @brief begin marks the beginning of a frame */
|
||||
- (void)begin;
|
||||
|
||||
/*! @brief end commits the command buffer */
|
||||
- (void)end;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
61
gfx/common/metal/Context.m
Normal file
61
gfx/common/metal/Context.m
Normal file
@ -0,0 +1,61 @@
|
||||
//
|
||||
// Context.m
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Context.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@interface Context()
|
||||
{
|
||||
CAMetalLayer *_layer;
|
||||
id<CAMetalDrawable> _drawable;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Context
|
||||
|
||||
+ (instancetype)newContextWithDevice:(id<MTLDevice>)d
|
||||
layer:(CAMetalLayer *)layer
|
||||
library:(id<MTLLibrary>)l
|
||||
commandQueue:(id<MTLCommandQueue>)q
|
||||
{
|
||||
Context *c = [Context new];
|
||||
c->_device = d;
|
||||
c->_layer = layer;
|
||||
c->_library = l;
|
||||
c->_commandQueue = q;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
- (id<CAMetalDrawable>)nextDrawable {
|
||||
if (_drawable == nil) {
|
||||
_drawable = _layer.nextDrawable;
|
||||
}
|
||||
return _drawable;
|
||||
}
|
||||
|
||||
- (id<MTLTexture>)renderTexture {
|
||||
return self.nextDrawable.texture;
|
||||
}
|
||||
|
||||
- (void)begin
|
||||
{
|
||||
assert(_commandBuffer == nil);
|
||||
_commandBuffer = [_commandQueue commandBuffer];
|
||||
}
|
||||
|
||||
- (void)end
|
||||
{
|
||||
assert(self->_commandBuffer != nil);
|
||||
[_commandBuffer commit];
|
||||
_commandBuffer = nil;
|
||||
_drawable = nil;
|
||||
}
|
||||
|
||||
@end
|
26
gfx/common/metal/Filter.h
Normal file
26
gfx/common/metal/Filter.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Filter.h
|
||||
// MetalByExampleObjC
|
||||
//
|
||||
// Created by Stuart Carnie on 5/15/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
@protocol FilterDelegate
|
||||
-(void)configure:(id<MTLCommandEncoder>)encoder;
|
||||
@end
|
||||
|
||||
@interface Filter : NSObject
|
||||
|
||||
@property (readwrite) id<FilterDelegate> delegate;
|
||||
@property (readonly) id<MTLSamplerState> sampler;
|
||||
|
||||
-(void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout;
|
||||
-(void)apply:(id<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout;
|
||||
|
||||
+(instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)library error:(NSError **)error;
|
||||
|
||||
@end
|
88
gfx/common/metal/Filter.m
Normal file
88
gfx/common/metal/Filter.m
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// Filter.m
|
||||
// MetalByExampleObjC
|
||||
//
|
||||
// Created by Stuart Carnie on 5/15/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Filter.h"
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
@interface Filter()
|
||||
-( instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler;
|
||||
@end
|
||||
|
||||
@implementation Filter {
|
||||
id<MTLComputePipelineState> _kernel;
|
||||
}
|
||||
|
||||
+(instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)library error:(NSError **)error {
|
||||
id<MTLFunction> function = [library newFunctionWithName:name];
|
||||
id<MTLComputePipelineState> kernel = [device newComputePipelineStateWithFunction:function error:error];
|
||||
if (*error != nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
MTLSamplerDescriptor * sd = [MTLSamplerDescriptor new];
|
||||
sd.minFilter = MTLSamplerMinMagFilterNearest;
|
||||
sd.magFilter = MTLSamplerMinMagFilterNearest;
|
||||
sd.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||
sd.tAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||
sd.mipFilter = MTLSamplerMipFilterNotMipmapped;
|
||||
id<MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:sd];
|
||||
|
||||
return [[Filter alloc] initWithKernel:kernel sampler:sampler];
|
||||
}
|
||||
|
||||
-( instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler {
|
||||
if (self = [super init]) {
|
||||
_kernel = kernel;
|
||||
_sampler = sampler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout {
|
||||
id<MTLComputeCommandEncoder> ce = [cb computeCommandEncoder];
|
||||
ce.label = @"filter kernel";
|
||||
[ce pushDebugGroup:@"filter kernel"];
|
||||
|
||||
[ce setComputePipelineState:_kernel];
|
||||
|
||||
[ce setTexture:tin atIndex:0];
|
||||
[ce setTexture:tout atIndex:1];
|
||||
|
||||
[self.delegate configure:ce];
|
||||
|
||||
MTLSize size = MTLSizeMake(16, 16, 1);
|
||||
MTLSize count = MTLSizeMake((tin.width + size.width + 1) / size.width, (tin.height + size.height + 1) / size.height, 1);
|
||||
|
||||
[ce dispatchThreadgroups:count threadsPerThreadgroup:size];
|
||||
|
||||
[ce popDebugGroup];
|
||||
[ce endEncoding];
|
||||
}
|
||||
|
||||
-(void)apply:(id<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout {
|
||||
id<MTLComputeCommandEncoder> ce = [cb computeCommandEncoder];
|
||||
ce.label = @"filter kernel";
|
||||
[ce pushDebugGroup:@"filter kernel"];
|
||||
|
||||
[ce setComputePipelineState:_kernel];
|
||||
|
||||
[ce setBuffer:tin offset:0 atIndex:0];
|
||||
[ce setTexture:tout atIndex:0];
|
||||
|
||||
[self.delegate configure:ce];
|
||||
|
||||
MTLSize size = MTLSizeMake(32, 1, 1);
|
||||
MTLSize count = MTLSizeMake((tin.length + 00) / 32, 1, 1);
|
||||
|
||||
[ce dispatchThreadgroups:count threadsPerThreadgroup:size];
|
||||
|
||||
[ce popDebugGroup];
|
||||
[ce endEncoding];
|
||||
}
|
||||
|
||||
@end
|
10
gfx/common/metal/MetalRenderer.h
Normal file
10
gfx/common/metal/MetalRenderer.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// MetalRenderer.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/7/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Renderer.h"
|
||||
#import "RView.h"
|
13
gfx/common/metal/PixelConverter+private.h
Normal file
13
gfx/common/metal/PixelConverter+private.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// PixelConverter+private.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
#import "PixelConverter.h"
|
||||
#import "Context.h"
|
||||
|
||||
@interface PixelConverter()
|
||||
- (instancetype)initWithContext:(Context *)c;
|
||||
@end
|
20
gfx/common/metal/PixelConverter.h
Normal file
20
gfx/common/metal/PixelConverter.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// PixelConverter.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
#import "RendererCommon.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PixelConverter : NSObject
|
||||
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
49
gfx/common/metal/PixelConverter.m
Normal file
49
gfx/common/metal/PixelConverter.m
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// PixelConverter.m
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PixelConverter+private.h"
|
||||
#import "Filter.h"
|
||||
#import "Context.h"
|
||||
|
||||
@implementation PixelConverter {
|
||||
Context *_context;
|
||||
Filter *_filters[RPixelFormatCount]; // convert to bgra8888
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(Context *)c
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_context = c;
|
||||
NSError *err = nil;
|
||||
_filters[RPixelFormatBGRA4Unorm] = [Filter newFilterWithFunctionName:@"convert_abgr4444_to_bgra8888"
|
||||
device:c.device library:c.library
|
||||
error:&err];
|
||||
_filters[RPixelFormatB5G6R5Unorm] = [Filter newFilterWithFunctionName:@"convert_bgr565_to_bgra8888"
|
||||
device:c.device
|
||||
library:c.library
|
||||
error:&err];
|
||||
if (err)
|
||||
{
|
||||
NSLog(@"unable to create pixel conversion filter: %@", err.localizedDescription);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst
|
||||
{
|
||||
assert(dst.width*dst.height == src.length/RPixelFormatToBPP(fmt));
|
||||
assert(fmt >= 0 && fmt < RPixelFormatCount);
|
||||
Filter *conv = _filters[fmt];
|
||||
assert(conv != nil);
|
||||
[conv apply:_context.commandBuffer inBuf:src outTex:dst];
|
||||
}
|
||||
|
||||
@end
|
65
gfx/common/metal/PixelConverter.metal
Normal file
65
gfx/common/metal/PixelConverter.metal
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// PixelConverter.metal
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
|
||||
#pragma mark - filter kernels
|
||||
|
||||
kernel void convert_abgr4444_to_bgra8888_tex(texture2d<ushort, access::read> in [[ texture(0) ]],
|
||||
texture2d<half, access::write> out [[ texture(1) ]],
|
||||
uint2 gid [[ thread_position_in_grid ]])
|
||||
{
|
||||
ushort pix = in.read(gid).r;
|
||||
|
||||
uchar4 pix2 = uchar4(
|
||||
extract_bits(pix, 4, 4),
|
||||
extract_bits(pix, 8, 4),
|
||||
extract_bits(pix, 12, 4),
|
||||
extract_bits(pix, 0, 4)
|
||||
);
|
||||
|
||||
out.write(half4(pix2) / 15.0, gid);
|
||||
}
|
||||
|
||||
kernel void convert_abgr4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]],
|
||||
texture2d<half, access::write> out [[ texture(0) ]],
|
||||
uint id [[ thread_position_in_grid ]])
|
||||
{
|
||||
uint16_t pix = in[id];
|
||||
uchar4 pix2 = uchar4(
|
||||
extract_bits(pix, 4, 4),
|
||||
extract_bits(pix, 8, 4),
|
||||
extract_bits(pix, 12, 4),
|
||||
extract_bits(pix, 0, 4)
|
||||
);
|
||||
|
||||
uint ypos = id / out.get_width();
|
||||
uint xpos = id % out.get_width();
|
||||
|
||||
out.write(half4(pix2) / 15.0, uint2(xpos, ypos));
|
||||
}
|
||||
|
||||
kernel void convert_bgr565_to_bgra8888(device uint16_t * in [[ buffer(0) ]],
|
||||
texture2d<half, access::write> out [[ texture(0) ]],
|
||||
uint id [[ thread_position_in_grid ]])
|
||||
{
|
||||
uint16_t pix = in[id];
|
||||
uchar4 pix2 = uchar4(
|
||||
extract_bits(pix, 11, 5),
|
||||
extract_bits(pix, 5, 6),
|
||||
extract_bits(pix, 0, 5),
|
||||
0xf
|
||||
);
|
||||
|
||||
uint ypos = id / out.get_width();
|
||||
uint xpos = id % out.get_width();
|
||||
|
||||
out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos));
|
||||
}
|
37
gfx/common/metal/Renderer.h
Normal file
37
gfx/common/metal/Renderer.h
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Renderer.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "Context.h"
|
||||
#import "PixelConverter.h"
|
||||
|
||||
@class ViewDescriptor;
|
||||
@protocol View;
|
||||
|
||||
@interface Renderer : NSObject
|
||||
|
||||
@property (readonly) Context* context;
|
||||
@property (readonly) PixelConverter* conv;
|
||||
|
||||
- (instancetype)initWithDevice:(id<MTLDevice>)device layer:(CAMetalLayer *)layer;
|
||||
- (void)drawableSizeWillChange:(CGSize)size;
|
||||
|
||||
- (void)beginFrame;
|
||||
- (void)drawFrame;
|
||||
|
||||
#pragma mark - view management
|
||||
|
||||
- (void)addView:(id<View>)view;
|
||||
- (void)removeView:(id<View>)view;
|
||||
- (void)bringViewToFront:(id<View>)view;
|
||||
- (void)sendViewToBack:(id<View>)view;
|
||||
|
||||
@end
|
||||
|
273
gfx/common/metal/Renderer.m
Normal file
273
gfx/common/metal/Renderer.m
Normal file
@ -0,0 +1,273 @@
|
||||
//
|
||||
// Renderer.m
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <simd/simd.h>
|
||||
|
||||
#import "RendererCommon.h"
|
||||
#import "Renderer.h"
|
||||
#import "View.h"
|
||||
#import "PixelConverter+private.h"
|
||||
|
||||
// Include header shared between C code here, which executes Metal API commands, and .metal files
|
||||
#import "ShaderTypes.h"
|
||||
|
||||
@implementation Renderer
|
||||
{
|
||||
dispatch_semaphore_t _inflightSemaphore;
|
||||
id<MTLDevice> _device;
|
||||
id<MTLLibrary> _library;
|
||||
id<MTLCommandQueue> _commandQueue;
|
||||
Context *_context;
|
||||
|
||||
PixelConverter *_conv;
|
||||
|
||||
CAMetalLayer *_layer;
|
||||
|
||||
// render target layer state
|
||||
id<MTLRenderPipelineState> _t_pipelineState;
|
||||
id<MTLRenderPipelineState> _t_pipelineStateNoAlpha;
|
||||
MTLRenderPassDescriptor *_t_rpd;
|
||||
|
||||
id<MTLSamplerState> _samplerStateLinear;
|
||||
id<MTLSamplerState> _samplerStateNearest;
|
||||
|
||||
// views
|
||||
|
||||
NSMutableArray<id<View>> *_views;
|
||||
|
||||
// other state
|
||||
Uniforms _uniforms;
|
||||
BOOL _begin;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDevice:(id<MTLDevice>)device layer:(CAMetalLayer *)layer
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT);
|
||||
_device = device;
|
||||
_layer = layer;
|
||||
_views = [NSMutableArray new];
|
||||
[self _initMetal];
|
||||
|
||||
_conv = [[PixelConverter alloc] initWithContext:_context];
|
||||
_begin = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_initMetal
|
||||
{
|
||||
_commandQueue = [_device newCommandQueue];
|
||||
_library = [_device newDefaultLibrary];
|
||||
_context = [Context newContextWithDevice:_device
|
||||
layer:_layer
|
||||
library:_library
|
||||
commandQueue:_commandQueue];
|
||||
|
||||
{
|
||||
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||
vd.attributes[0].offset = 0;
|
||||
vd.attributes[0].format = MTLVertexFormatFloat3;
|
||||
vd.attributes[1].offset = offsetof(Vertex, texCoord);
|
||||
vd.attributes[1].format = MTLVertexFormatFloat2;
|
||||
vd.layouts[0].stride = sizeof(Vertex);
|
||||
vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
|
||||
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
|
||||
psd.label = @"Pipeline+Alpha";
|
||||
|
||||
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
|
||||
ca.pixelFormat = _layer.pixelFormat;
|
||||
ca.blendingEnabled = YES;
|
||||
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
|
||||
psd.sampleCount = 1;
|
||||
psd.vertexDescriptor = vd;
|
||||
psd.vertexFunction = [_library newFunctionWithName:@"basic_vertex_proj_tex"];
|
||||
psd.fragmentFunction = [_library newFunctionWithName:@"basic_fragment_proj_tex"];
|
||||
|
||||
NSError *err;
|
||||
_t_pipelineState = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
|
||||
if (err != nil) {
|
||||
NSLog(@"error creating pipeline state: %@", err.localizedDescription);
|
||||
abort();
|
||||
}
|
||||
|
||||
ca.blendingEnabled = NO;
|
||||
_t_pipelineStateNoAlpha = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
|
||||
if (err != nil) {
|
||||
NSLog(@"error creating pipeline state: %@", err.localizedDescription);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||
// Cornflower Blue #58BAF9
|
||||
//rpd.colorAttachments[0].clearColor = MTLClearColorMake(0x58 / 255.0, 0xba / 255.0, 0xf9 / 255.0, 1.0);
|
||||
rpd.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
||||
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
_t_rpd = rpd;
|
||||
}
|
||||
|
||||
{
|
||||
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
|
||||
_samplerStateNearest = [_device newSamplerStateWithDescriptor:sd];
|
||||
|
||||
sd.minFilter = MTLSamplerMinMagFilterLinear;
|
||||
sd.magFilter = MTLSamplerMinMagFilterLinear;
|
||||
_samplerStateLinear = [_device newSamplerStateWithDescriptor:sd];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_updateUniforms
|
||||
{
|
||||
//CGSize s = _layer.drawableSize;
|
||||
//_uniforms.projectionMatrix = matrix_proj_ortho(0, s.width, 0, s.height);
|
||||
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
|
||||
}
|
||||
|
||||
- (void)beginFrame
|
||||
{
|
||||
assert(!_begin);
|
||||
_begin = YES;
|
||||
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
|
||||
[_context begin];
|
||||
[self _updateUniforms];
|
||||
}
|
||||
|
||||
- (void)drawFrame
|
||||
{
|
||||
@autoreleasepool {
|
||||
[self _render];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_render
|
||||
{
|
||||
assert(_begin);
|
||||
_begin = NO;
|
||||
|
||||
id<MTLCommandBuffer> cb = _context.commandBuffer;
|
||||
cb.label = @"renderer cb";
|
||||
|
||||
for (id<View> v in _views) {
|
||||
if (!v.visible) continue;
|
||||
if ([v respondsToSelector:@selector(prepareFrame:)]) {
|
||||
[v prepareFrame:_context];
|
||||
}
|
||||
}
|
||||
|
||||
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
||||
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
||||
|
||||
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
||||
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
||||
|
||||
for (id<View> v in _views) {
|
||||
if (!v.visible ||
|
||||
![v respondsToSelector:@selector(drawWithEncoder:)]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// set view state
|
||||
if (v.format == RPixelFormatBGRX8Unorm) {
|
||||
[rce setRenderPipelineState:_t_pipelineStateNoAlpha];
|
||||
}
|
||||
else {
|
||||
[rce setRenderPipelineState:_t_pipelineState];
|
||||
}
|
||||
|
||||
if (v.filter == RTextureFilterNearest) {
|
||||
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
|
||||
}
|
||||
else {
|
||||
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
|
||||
}
|
||||
|
||||
[v drawWithEncoder:rce];
|
||||
}
|
||||
|
||||
[rce endEncoding];
|
||||
|
||||
__block dispatch_semaphore_t inflight = _inflightSemaphore;
|
||||
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
||||
dispatch_semaphore_signal(inflight);
|
||||
}];
|
||||
|
||||
[cb presentDrawable:drawable];
|
||||
[_context end];
|
||||
}
|
||||
|
||||
#pragma mark - view APIs
|
||||
|
||||
- (void)bringViewToFront:(id<View>)view
|
||||
{
|
||||
NSUInteger pos = [_views indexOfObject:view];
|
||||
if (pos == NSNotFound || pos == _views.count - 1)
|
||||
return;
|
||||
[_views removeObjectAtIndex:pos];
|
||||
[_views addObject:view];
|
||||
}
|
||||
|
||||
- (void)sendViewToBack:(id<View>)view
|
||||
{
|
||||
NSUInteger pos = [_views indexOfObject:view];
|
||||
if (pos == NSNotFound || pos == 0)
|
||||
return;
|
||||
[_views removeObjectAtIndex:pos];
|
||||
[_views insertObject:view atIndex:0];
|
||||
}
|
||||
|
||||
- (void)addView:(id<View>)view
|
||||
{
|
||||
[_views addObject:view];
|
||||
}
|
||||
|
||||
- (void)removeView:(id<View>)view
|
||||
{
|
||||
NSUInteger pos = [_views indexOfObject:view];
|
||||
if (pos == NSNotFound)
|
||||
return;
|
||||
[_views removeObjectAtIndex:pos];
|
||||
}
|
||||
|
||||
- (void)drawableSizeWillChange:(CGSize)size
|
||||
{
|
||||
_layer.drawableSize = size;
|
||||
}
|
||||
|
||||
#pragma mark Matrix Math Utilities
|
||||
|
||||
extern inline matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom)
|
||||
{
|
||||
float near = 0;
|
||||
float far = 1;
|
||||
|
||||
float sx = 2 / (right - left);
|
||||
float sy = 2 / (top - bottom);
|
||||
float sz = 1 / (far - near);
|
||||
float tx = (right + left) / (left - right);
|
||||
float ty = (top + bottom) / (bottom - top);
|
||||
float tz = near / (far - near);
|
||||
|
||||
vector_float4 P = {sx, 0, 0, 0};
|
||||
vector_float4 Q = {0, sy, 0, 0};
|
||||
vector_float4 R = {0, 0, sz, 0};
|
||||
vector_float4 S = {tx, ty, tz, 1};
|
||||
|
||||
matrix_float4x4 mat = {P, Q, R, S};
|
||||
return mat;
|
||||
}
|
||||
|
||||
@end
|
43
gfx/common/metal/RendererCommon.h
Normal file
43
gfx/common/metal/RendererCommon.h
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// RendererCommon.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/3/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef RendererCommon_h
|
||||
#define RendererCommon_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*! @brief maximum inflight frames */
|
||||
#define MAX_INFLIGHT 3
|
||||
|
||||
#pragma mark - Pixel Formats
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RPixelFormat) {
|
||||
|
||||
RPixelFormatInvalid,
|
||||
|
||||
/* 16-bit formats */
|
||||
RPixelFormatBGRA4Unorm,
|
||||
RPixelFormatB5G6R5Unorm,
|
||||
|
||||
RPixelFormatBGRA8Unorm,
|
||||
RPixelFormatBGRX8Unorm,
|
||||
|
||||
RPixelFormatCount,
|
||||
};
|
||||
|
||||
extern NSUInteger RPixelFormatToBPP(RPixelFormat format);
|
||||
extern NSString *NSStringFromRPixelFormat(RPixelFormat format);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RTextureFilter) {
|
||||
RTextureFilterNearest,
|
||||
RTextureFilterLinear,
|
||||
|
||||
RTextureFilterCount,
|
||||
};
|
||||
|
||||
#endif /* RendererCommon_h */
|
55
gfx/common/metal/RendererCommon.m
Normal file
55
gfx/common/metal/RendererCommon.m
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// RendererCommon.m
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/3/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RendererCommon.h"
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
NSUInteger RPixelFormatToBPP(RPixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case RPixelFormatBGRA8Unorm:
|
||||
case RPixelFormatBGRX8Unorm:
|
||||
return 4;
|
||||
|
||||
case RPixelFormatB5G6R5Unorm:
|
||||
case RPixelFormatBGRA4Unorm:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
NSLog(@"Unknown format %ld", format);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static NSString * RPixelStrings[RPixelFormatCount];
|
||||
|
||||
NSString *NSStringFromRPixelFormat(RPixelFormat format)
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
#define STRING(literal) RPixelStrings[literal] = @#literal
|
||||
STRING(RPixelFormatInvalid);
|
||||
STRING(RPixelFormatB5G6R5Unorm);
|
||||
STRING(RPixelFormatBGRA4Unorm);
|
||||
STRING(RPixelFormatBGRA8Unorm);
|
||||
STRING(RPixelFormatBGRX8Unorm);
|
||||
#undef STRING
|
||||
|
||||
});
|
||||
|
||||
if (format >= RPixelFormatCount)
|
||||
{
|
||||
format = 0;
|
||||
}
|
||||
|
||||
return RPixelStrings[format];
|
||||
}
|
||||
|
||||
|
74
gfx/common/metal/ShaderTypes.h
Normal file
74
gfx/common/metal/ShaderTypes.h
Normal file
@ -0,0 +1,74 @@
|
||||
//
|
||||
// ShaderTypes.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
//
|
||||
// Header containing types and enum constants shared between Metal shaders and Swift/ObjC source
|
||||
//
|
||||
#ifndef ShaderTypes_h
|
||||
#define ShaderTypes_h
|
||||
|
||||
#ifdef __METAL_VERSION__
|
||||
|
||||
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
|
||||
#define NSInteger metal::int32_t
|
||||
|
||||
#define METAL_ATTRIBUTE(x) [[attribute(x)]]
|
||||
#define METAL_POSITION [[position]]
|
||||
|
||||
#else
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define METAL_ATTRIBUTE(x)
|
||||
#define METAL_POSITION
|
||||
|
||||
#endif
|
||||
|
||||
#include <simd/simd.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, BufferIndex)
|
||||
{
|
||||
BufferIndexPositions = 0,
|
||||
BufferIndexUniforms = 1
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, VertexAttribute)
|
||||
{
|
||||
VertexAttributePosition = 0,
|
||||
VertexAttributeTexcoord = 1,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, TextureIndex)
|
||||
{
|
||||
TextureIndexColor = 0,
|
||||
};
|
||||
|
||||
|
||||
typedef NS_ENUM(NSInteger, SamplerIndex)
|
||||
{
|
||||
SamplerIndexDraw = 0,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
vector_float3 position METAL_ATTRIBUTE(VertexAttributePosition);
|
||||
vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord);
|
||||
} Vertex;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vector_float4 position METAL_POSITION;
|
||||
vector_float2 texCoord;
|
||||
} ColorInOut;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
matrix_float4x4 projectionMatrix;
|
||||
} Uniforms;
|
||||
|
||||
#endif /* ShaderTypes_h */
|
||||
|
55
gfx/common/metal/Shaders.metal
Normal file
55
gfx/common/metal/Shaders.metal
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// Shaders.metal
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
// File for Metal kernel and shader functions
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
// Including header shared between this Metal shader code and Swift/C code executing Metal API commands
|
||||
#import "ShaderTypes.h"
|
||||
|
||||
using namespace metal;
|
||||
|
||||
#pragma mark - functions using projected coordinates
|
||||
|
||||
vertex ColorInOut basic_vertex_proj_tex(const Vertex in [[ stage_in ]],
|
||||
const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]])
|
||||
{
|
||||
ColorInOut out;
|
||||
out.position = uniforms.projectionMatrix * float4(in.position, 1.0);
|
||||
out.texCoord = in.texCoord;
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment float4 basic_fragment_proj_tex(ColorInOut in [[stage_in]],
|
||||
constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]],
|
||||
texture2d<half> tex [[ texture(TextureIndexColor) ]],
|
||||
sampler samp [[ sampler(SamplerIndexDraw) ]])
|
||||
{
|
||||
half4 colorSample = tex.sample(samp, in.texCoord.xy);
|
||||
return float4(colorSample);
|
||||
}
|
||||
|
||||
#pragma mark - functions using normalized device coordinates
|
||||
|
||||
vertex ColorInOut basic_vertex_ndc_tex(const Vertex in [[ stage_in ]])
|
||||
{
|
||||
ColorInOut out;
|
||||
out.position = float4(in.position, 1.0);
|
||||
out.texCoord = in.texCoord;
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment float4 basic_fragment_ndc_tex(ColorInOut in [[stage_in]],
|
||||
texture2d<half> tex [[ texture(TextureIndexColor) ]],
|
||||
sampler samp [[ sampler(SamplerIndexDraw) ]])
|
||||
{
|
||||
half4 colorSample = tex.sample(samp, in.texCoord.xy);
|
||||
return float4(colorSample);
|
||||
}
|
23
gfx/common/metal/TexturedView.h
Normal file
23
gfx/common/metal/TexturedView.h
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by Stuart Carnie on 6/16/18.
|
||||
//
|
||||
|
||||
#import "View.h"
|
||||
|
||||
@class Renderer;
|
||||
|
||||
@interface TexturedView : NSObject<View>
|
||||
|
||||
@property (readonly) RPixelFormat format;
|
||||
@property (readonly) RTextureFilter filter;
|
||||
@property (readwrite) BOOL visible;
|
||||
@property (readwrite) CGRect frame;
|
||||
@property (readwrite) CGSize size;
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
|
||||
@end
|
155
gfx/common/metal/TexturedView.m
Normal file
155
gfx/common/metal/TexturedView.m
Normal file
@ -0,0 +1,155 @@
|
||||
//
|
||||
// Created by Stuart Carnie on 6/16/18.
|
||||
//
|
||||
|
||||
#import "TexturedView.h"
|
||||
#import "RendererCommon.h"
|
||||
#import "Renderer.h"
|
||||
#import "View.h"
|
||||
#import "Filter.h"
|
||||
|
||||
#import "ShaderTypes.h"
|
||||
|
||||
|
||||
@implementation TexturedView
|
||||
{
|
||||
__weak Renderer *_renderer;
|
||||
Context *_context;
|
||||
id<MTLTexture> _texture; // optimal render texture
|
||||
Vertex _v[4];
|
||||
CGSize _size; // size of view in pixels
|
||||
CGRect _frame;
|
||||
NSUInteger _bpp;
|
||||
|
||||
id<MTLBuffer> _pixels; // frame buffer in _srcFmt
|
||||
bool _pixelsDirty;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_renderer = r;
|
||||
_format = d.format;
|
||||
_bpp = RPixelFormatToBPP(_format);
|
||||
_filter = d.filter;
|
||||
_context = r.context;
|
||||
_visible = YES;
|
||||
self.size = d.size;
|
||||
self.frame = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)size
|
||||
{
|
||||
if (CGSizeEqualToSize(_size, size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
|
||||
// create new texture
|
||||
{
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:(NSUInteger)size.width
|
||||
height:(NSUInteger)size.height
|
||||
mipmapped:NO];
|
||||
td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
_texture = [_context.device newTextureWithDescriptor:td];
|
||||
}
|
||||
|
||||
if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) {
|
||||
_pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2)
|
||||
options:MTLResourceStorageModeManaged];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)size
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
if (CGRectEqualToRect(_frame, frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_frame = frame;
|
||||
|
||||
// update vertices
|
||||
CGPoint o = frame.origin;
|
||||
CGSize s = frame.size;
|
||||
|
||||
float l = o.x;
|
||||
float t = o.y;
|
||||
float r = o.x + s.width;
|
||||
float b = o.y + s.height;
|
||||
|
||||
Vertex v[4] = {
|
||||
{{l, b, 0}, {0, 1}},
|
||||
{{r, b, 0}, {1, 1}},
|
||||
{{l, t, 0}, {0, 0}},
|
||||
{{r, t, 0}, {1, 0}},
|
||||
};
|
||||
memcpy(_v, v, sizeof(_v));
|
||||
}
|
||||
|
||||
- (CGRect)frame
|
||||
{
|
||||
return _frame;
|
||||
}
|
||||
|
||||
- (void)_convertFormat
|
||||
{
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm)
|
||||
return;
|
||||
|
||||
if (!_pixelsDirty)
|
||||
return;
|
||||
|
||||
[_renderer.conv convertFormat:_format from:_pixels to:_texture];
|
||||
_pixelsDirty = NO;
|
||||
}
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx {
|
||||
[self _convertFormat];
|
||||
}
|
||||
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
|
||||
{
|
||||
[rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions];
|
||||
[rce setFragmentTexture:_texture atIndex:TextureIndexColor];
|
||||
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
}
|
||||
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch
|
||||
{
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||
[_texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height)
|
||||
mipmapLevel:0 withBytes:src
|
||||
bytesPerRow:(NSUInteger)(4 * _size.width)];
|
||||
}
|
||||
else {
|
||||
void *dst = _pixels.contents;
|
||||
size_t len = (size_t)(_bpp * _size.width);
|
||||
assert(len <= pitch); // the length can't be larger?
|
||||
|
||||
if (len < pitch) {
|
||||
for (int i = 0; i < _size.height; i++) {
|
||||
memcpy(dst, src, len);
|
||||
dst += len;
|
||||
src += pitch;
|
||||
}
|
||||
}
|
||||
else {
|
||||
memcpy(dst, src, _pixels.length);
|
||||
}
|
||||
|
||||
[_pixels didModifyRange:NSMakeRange(0, _pixels.length)];
|
||||
_pixelsDirty = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
33
gfx/common/metal/View.h
Normal file
33
gfx/common/metal/View.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// RView.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RendererCommon.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
@protocol View<NSObject>
|
||||
|
||||
@property (readonly) RPixelFormat format;
|
||||
@property (readonly) RTextureFilter filter;
|
||||
@property (readwrite) BOOL visible;
|
||||
@property (readwrite) CGRect frame;
|
||||
@property (readwrite) CGSize size;
|
||||
|
||||
@optional
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
|
||||
@end
|
||||
|
||||
@interface ViewDescriptor : NSObject
|
||||
@property (readwrite) RPixelFormat format;
|
||||
@property (readwrite) RTextureFilter filter;
|
||||
@property (readwrite) CGSize size;
|
||||
|
||||
- (instancetype)init;
|
||||
@end
|
29
gfx/common/metal/View.m
Normal file
29
gfx/common/metal/View.m
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// RView.m
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 5/31/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RendererCommon.h"
|
||||
|
||||
@implementation ViewDescriptor
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_format = RPixelFormatBGRA8Unorm;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription
|
||||
{
|
||||
return [NSString stringWithFormat:@"( format = %@, frame = %@ )",
|
||||
NSStringFromRPixelFormat(_format),
|
||||
NSStringFromSize(_size)];
|
||||
}
|
||||
|
||||
@end
|
11
gfx/common/metal/metal_common.h
Normal file
11
gfx/common/metal/metal_common.h
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// metal_common.h
|
||||
// RetroArch_Metal
|
||||
//
|
||||
// Created by Stuart Carnie on 6/15/18.
|
||||
//
|
||||
|
||||
#import "RendererCommon.h"
|
||||
#import "Renderer.h"
|
||||
#import "View.h"
|
||||
#import "TexturedView.h"
|
83
gfx/common/metal_common.h
Normal file
83
gfx/common/metal_common.h
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// metal_common.h
|
||||
// RetroArch_Metal
|
||||
//
|
||||
// Created by Stuart Carnie on 5/14/18.
|
||||
//
|
||||
|
||||
#ifndef METAL_COMMON_H__
|
||||
#define METAL_COMMON_H__
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import "metal/metal_common.h"
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include "../drivers_shader/slang_process.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#pragma mark - Classes
|
||||
|
||||
@interface FrameView : NSObject<View>
|
||||
|
||||
@property (readonly) RPixelFormat format;
|
||||
@property (readonly) RTextureFilter filter;
|
||||
@property (readwrite) BOOL visible;
|
||||
@property (readwrite) CGRect frame;
|
||||
@property (readwrite) CGSize size;
|
||||
@property (readonly) struct video_shader* shader;
|
||||
|
||||
@property (readwrite) uint64_t frameCount;
|
||||
|
||||
- (void)setFilteringIndex:(int)index smooth:(bool)smooth;
|
||||
- (BOOL)setShaderFromPath:(NSString *)path;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface MetalMenu : NSObject
|
||||
|
||||
@property (nonatomic, readwrite) BOOL enabled;
|
||||
@property (readwrite) float alpha;
|
||||
|
||||
- (void)updateFrame:(void const *)source;
|
||||
|
||||
- (void)updateWidth:(int)width
|
||||
height:(int)height
|
||||
format:(RPixelFormat)format
|
||||
filter:(RTextureFilter)filter;
|
||||
@end
|
||||
|
||||
@interface MetalDriver : NSObject<PlatformDelegate>
|
||||
|
||||
@property (readonly) video_viewport_t* viewport;
|
||||
@property (readwrite) bool keepAspect;
|
||||
@property (readonly) MetalMenu* menu;
|
||||
@property (readwrite) uint64_t frameCount;
|
||||
@property (readonly) FrameView* frameView;
|
||||
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)setVideo:(const video_info_t *)video;
|
||||
|
||||
- (void)beginFrame;
|
||||
- (void)endFrame;
|
||||
|
||||
/*! @brief setNeedsResize triggers a display resize */
|
||||
- (void)setNeedsResize;
|
||||
|
||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
||||
|
||||
|
||||
#pragma mark - Menu APIs
|
||||
|
||||
@end
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
947
gfx/common/metal_common.m
Normal file
947
gfx/common/metal_common.m
Normal file
@ -0,0 +1,947 @@
|
||||
//
|
||||
// metal_common.m
|
||||
// RetroArch_Metal
|
||||
//
|
||||
// Created by Stuart Carnie on 5/14/18.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "metal_common.h"
|
||||
#import "cocoa_common.h"
|
||||
#import <memory.h>
|
||||
#import <gfx/video_frame.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <stddef.h>
|
||||
#include <simd/simd.h>
|
||||
|
||||
#define STRUCT_ASSIGN(x, y) \
|
||||
{ \
|
||||
NSObject * __y = y; \
|
||||
if (x != nil) { \
|
||||
NSObject * __foo = (__bridge_transfer NSObject *)(__bridge void *)(x); \
|
||||
__foo = nil; \
|
||||
x = (__bridge __typeof__(x))nil; \
|
||||
} \
|
||||
if (__y != nil) \
|
||||
x = (__bridge __typeof__(x))(__bridge_retained void *)((NSObject *)__y); \
|
||||
}
|
||||
|
||||
@interface FrameView()
|
||||
|
||||
@property (readwrite) video_viewport_t *viewport;
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - private categories
|
||||
|
||||
@interface MetalMenu()
|
||||
@property (readwrite) Renderer *renderer;
|
||||
@end
|
||||
|
||||
@implementation MetalDriver
|
||||
{
|
||||
id<MTLDevice> _device;
|
||||
|
||||
Renderer *_renderer;
|
||||
FrameView *_frameView;
|
||||
|
||||
video_info_t _video;
|
||||
bool resize_chain;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_frameCount = 0;
|
||||
_viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t));
|
||||
_menu = [MetalMenu new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_viewport) {
|
||||
free(_viewport);
|
||||
_viewport = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - swap chain
|
||||
|
||||
- (void)viewDidUpdateFrame:(NSRect)rect
|
||||
{
|
||||
RARCH_LOG("[MetalDriver] viewDidUpdateFrame %s\n", NSStringFromRect(rect).UTF8String);
|
||||
_viewport->full_width = (unsigned int)rect.size.width;
|
||||
_viewport->full_height = (unsigned int)rect.size.height;
|
||||
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
|
||||
resize_chain = YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - video
|
||||
|
||||
- (void)setVideo:(const video_info_t *)video
|
||||
{
|
||||
_video = *video;
|
||||
_viewport->full_width = _video.width;
|
||||
_viewport->full_height = _video.height;
|
||||
|
||||
if (!_renderer) {
|
||||
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||
_device = device;
|
||||
NSView *view = (NSView *)apple_platform.renderView;
|
||||
CAMetalLayer *layer = (CAMetalLayer *)view.layer;
|
||||
layer.device = device;
|
||||
_renderer = [[Renderer alloc] initWithDevice:device layer:layer];
|
||||
_menu.renderer = _renderer;
|
||||
}
|
||||
|
||||
if (!_frameView) {
|
||||
ViewDescriptor *vd = [ViewDescriptor new];
|
||||
vd.format = _video.rgb32 ? RPixelFormatBGRX8Unorm : RPixelFormatB5G6R5Unorm;
|
||||
vd.size = CGSizeMake(video->width, video->height);
|
||||
vd.filter = _video.smooth ? RTextureFilterLinear : RTextureFilterNearest;
|
||||
_frameView = [[FrameView alloc] initWithDescriptor:vd renderer:_renderer];
|
||||
_frameView.viewport = _viewport;
|
||||
[_renderer addView:_frameView];
|
||||
[_renderer sendViewToBack:_frameView];
|
||||
[_frameView setFilteringIndex:0 smooth:video->smooth];
|
||||
}
|
||||
|
||||
resize_chain = YES;
|
||||
}
|
||||
|
||||
- (void)beginFrame
|
||||
{
|
||||
if (resize_chain) {
|
||||
[_renderer drawableSizeWillChange:CGSizeMake(_viewport->full_width, _viewport->full_height)];
|
||||
resize_chain = NO;
|
||||
}
|
||||
|
||||
video_driver_update_viewport(_viewport, NO, _keepAspect);
|
||||
|
||||
[_renderer beginFrame];
|
||||
}
|
||||
|
||||
- (void)endFrame
|
||||
{
|
||||
[_renderer drawFrame];
|
||||
}
|
||||
|
||||
- (void)setNeedsResize
|
||||
{
|
||||
// TODO(sgc): resize all drawables
|
||||
}
|
||||
|
||||
extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom)
|
||||
{
|
||||
float near = 0;
|
||||
float far = 1;
|
||||
|
||||
float sx = 2 / (right - left);
|
||||
float sy = 2 / (top - bottom);
|
||||
float sz = 1 / (far - near);
|
||||
float tx = (right + left) / (left - right);
|
||||
float ty = (top + bottom) / (bottom - top);
|
||||
float tz = near / (far - near);
|
||||
|
||||
vector_float4 P = {sx, 0, 0, 0};
|
||||
vector_float4 Q = {0, sy, 0, 0};
|
||||
vector_float4 R = {0, 0, sz, 0};
|
||||
vector_float4 S = {tx, ty, tz, 1};
|
||||
|
||||
matrix_float4x4 mat = {P, Q, R, S};
|
||||
return mat;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MetalMenu
|
||||
{
|
||||
Renderer *_renderer;
|
||||
TexturedView *_view;
|
||||
BOOL _enabled;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)enabled
|
||||
{
|
||||
if (_enabled == enabled) return;
|
||||
_enabled = enabled;
|
||||
_view.visible = enabled;
|
||||
}
|
||||
|
||||
- (BOOL)enabled
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
- (void)updateWidth:(int)width
|
||||
height:(int)height
|
||||
format:(RPixelFormat)format
|
||||
filter:(RTextureFilter)filter
|
||||
{
|
||||
CGSize size = CGSizeMake(width, height);
|
||||
|
||||
if (_view) {
|
||||
if (!(CGSizeEqualToSize(_view.size, size) &&
|
||||
_view.format == format &&
|
||||
_view.filter == filter)) {
|
||||
[_renderer removeView:_view];
|
||||
_view = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_view) {
|
||||
ViewDescriptor *vd = [ViewDescriptor new];
|
||||
vd.format = format;
|
||||
vd.filter = filter;
|
||||
vd.size = size;
|
||||
_view = [[TexturedView alloc] initWithDescriptor:vd renderer:_renderer];
|
||||
[_renderer addView:_view];
|
||||
_view.visible = _enabled;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateFrame:(void const *)source
|
||||
{
|
||||
[_view updateFrame:source pitch:RPixelFormatToBPP(_view.format) * (NSUInteger)_view.size.width];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - FrameView
|
||||
|
||||
#define ALIGN(x) __attribute__((aligned(x)))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
} float4_t;
|
||||
|
||||
typedef struct texture
|
||||
{
|
||||
__unsafe_unretained id<MTLTexture> view;
|
||||
float4_t size_data;
|
||||
} texture_t;
|
||||
|
||||
typedef struct ALIGN(16)
|
||||
{
|
||||
matrix_float4x4 mvp;
|
||||
|
||||
struct
|
||||
{
|
||||
texture_t texture[GFX_MAX_FRAME_HISTORY + 1];
|
||||
MTLViewport viewport;
|
||||
float4_t output_size;
|
||||
} frame;
|
||||
|
||||
struct
|
||||
{
|
||||
__unsafe_unretained id<MTLBuffer> buffers[SLANG_CBUFFER_MAX];
|
||||
texture_t rt;
|
||||
texture_t feedback;
|
||||
uint32_t frame_count;
|
||||
pass_semantics_t semantics;
|
||||
MTLViewport viewport;
|
||||
__unsafe_unretained id<MTLRenderPipelineState> _state;
|
||||
} pass[GFX_MAX_SHADERS];
|
||||
|
||||
texture_t luts[GFX_MAX_TEXTURES];
|
||||
|
||||
} engine_t;
|
||||
|
||||
@implementation FrameView
|
||||
{
|
||||
__weak Renderer *_renderer;
|
||||
Context *_context;
|
||||
id<MTLTexture> _texture; // final render texture
|
||||
Vertex _v[4];
|
||||
CGSize _size; // size of view in pixels
|
||||
CGRect _frame;
|
||||
NSUInteger _bpp;
|
||||
|
||||
id<MTLBuffer> _pixels; // frame buffer in _srcFmt
|
||||
bool _pixelsDirty;
|
||||
|
||||
id<MTLSamplerState> _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX];
|
||||
struct video_shader *_shader;
|
||||
id<MTLFence> _fence;
|
||||
|
||||
engine_t _engine;
|
||||
|
||||
bool resize_render_targets;
|
||||
bool init_history;
|
||||
video_viewport_t *_viewport;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_renderer = r;
|
||||
_context = r.context;
|
||||
_format = d.format;
|
||||
_bpp = RPixelFormatToBPP(_format);
|
||||
_filter = d.filter;
|
||||
_visible = YES;
|
||||
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
|
||||
[self _initSamplers];
|
||||
|
||||
self.size = d.size;
|
||||
self.frame = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_initSamplers
|
||||
{
|
||||
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
|
||||
|
||||
/* Initialize samplers */
|
||||
for (unsigned i = 0; i < RARCH_WRAP_MAX; i++) {
|
||||
switch (i) {
|
||||
case RARCH_WRAP_BORDER:
|
||||
sd.sAddressMode = MTLSamplerAddressModeClampToBorderColor;
|
||||
break;
|
||||
|
||||
case RARCH_WRAP_EDGE:
|
||||
sd.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||
break;
|
||||
|
||||
case RARCH_WRAP_REPEAT:
|
||||
sd.sAddressMode = MTLSamplerAddressModeRepeat;
|
||||
break;
|
||||
|
||||
case RARCH_WRAP_MIRRORED_REPEAT:
|
||||
sd.sAddressMode = MTLSamplerAddressModeMirrorRepeat;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
sd.tAddressMode = sd.sAddressMode;
|
||||
sd.rAddressMode = sd.sAddressMode;
|
||||
sd.minFilter = MTLSamplerMinMagFilterLinear;
|
||||
sd.magFilter = MTLSamplerMinMagFilterLinear;
|
||||
|
||||
id<MTLSamplerState> ss = [_context.device newSamplerStateWithDescriptor:sd];
|
||||
_samplers[RARCH_FILTER_LINEAR][i] = ss;
|
||||
|
||||
sd.minFilter = MTLSamplerMinMagFilterNearest;
|
||||
sd.magFilter = MTLSamplerMinMagFilterNearest;
|
||||
|
||||
ss = [_context.device newSamplerStateWithDescriptor:sd];
|
||||
_samplers[RARCH_FILTER_NEAREST][i] = ss;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFilteringIndex:(int)index smooth:(bool)smooth
|
||||
{
|
||||
for (int i = 0; i < RARCH_WRAP_MAX; i++) {
|
||||
if (smooth)
|
||||
_samplers[RARCH_FILTER_UNSPEC][i] = _samplers[RARCH_FILTER_LINEAR][i];
|
||||
else
|
||||
_samplers[RARCH_FILTER_UNSPEC][i] = _samplers[RARCH_FILTER_NEAREST][i];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)size
|
||||
{
|
||||
if (CGSizeEqualToSize(_size, size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_size = size;
|
||||
|
||||
resize_render_targets = YES;
|
||||
|
||||
if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) {
|
||||
_pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2)
|
||||
options:MTLResourceStorageModeManaged];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)size
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
if (CGRectEqualToRect(_frame, frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_frame = frame;
|
||||
|
||||
// update vertices
|
||||
CGPoint o = frame.origin;
|
||||
CGSize s = frame.size;
|
||||
|
||||
CGFloat l = o.x;
|
||||
CGFloat t = o.y;
|
||||
CGFloat r = o.x + s.width;
|
||||
CGFloat b = o.y + s.height;
|
||||
|
||||
Vertex v[4] = {
|
||||
{{l, b, 0}, {0, 1}},
|
||||
{{r, b, 0}, {1, 1}},
|
||||
{{l, t, 0}, {0, 0}},
|
||||
{{r, t, 0}, {1, 0}},
|
||||
};
|
||||
memcpy(_v, v, sizeof(_v));
|
||||
}
|
||||
|
||||
- (CGRect)frame
|
||||
{
|
||||
return _frame;
|
||||
}
|
||||
|
||||
- (void)_convertFormat
|
||||
{
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm)
|
||||
return;
|
||||
|
||||
if (!_pixelsDirty)
|
||||
return;
|
||||
|
||||
[_renderer.conv convertFormat:_format from:_pixels to:_texture];
|
||||
_pixelsDirty = NO;
|
||||
}
|
||||
|
||||
- (void)_updateHistory
|
||||
{
|
||||
if (_shader) {
|
||||
if (_shader->history_size) {
|
||||
if (init_history)
|
||||
[self _initHistory];
|
||||
else {
|
||||
// TODO(sgc): change to ring buffer?
|
||||
int k;
|
||||
/* todo: what about frame-duping ?
|
||||
* maybe clone d3d10_texture_t with AddRef */
|
||||
texture_t tmp = _engine.frame.texture[_shader->history_size];
|
||||
for (k = _shader->history_size; k > 0; k--)
|
||||
_engine.frame.texture[k] = _engine.frame.texture[k - 1];
|
||||
_engine.frame.texture[0] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* either no history, or we moved a texture of a different size in the front slot */
|
||||
if (_engine.frame.texture[0].size_data.x != _size.width ||
|
||||
_engine.frame.texture[0].size_data.y != _size.height) {
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:(NSUInteger)_size.width
|
||||
height:(NSUInteger)_size.height
|
||||
mipmapped:false];
|
||||
td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
|
||||
[self _initTexture:&_engine.frame.texture[0] withDescriptor:td];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch
|
||||
{
|
||||
if (_shader && (_engine.frame.output_size.x != _viewport->width ||
|
||||
_engine.frame.output_size.y != _viewport->height)) {
|
||||
resize_render_targets = YES;
|
||||
}
|
||||
|
||||
_engine.frame.viewport.originX = _viewport->x;
|
||||
_engine.frame.viewport.originY = _viewport->y;
|
||||
_engine.frame.viewport.width = _viewport->width;
|
||||
_engine.frame.viewport.height = _viewport->height;
|
||||
_engine.frame.viewport.znear = 0.0f;
|
||||
_engine.frame.viewport.zfar = 1.0f;
|
||||
_engine.frame.output_size.x = _viewport->width;
|
||||
_engine.frame.output_size.y = _viewport->height;
|
||||
_engine.frame.output_size.z = 1.0f / _viewport->width;
|
||||
_engine.frame.output_size.w = 1.0f / _viewport->height;
|
||||
|
||||
if (resize_render_targets) {
|
||||
[self _updateRenderTargets];
|
||||
}
|
||||
|
||||
[self _updateHistory];
|
||||
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||
id<MTLTexture> tex = _engine.frame.texture[0].view;
|
||||
[tex replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height)
|
||||
mipmapLevel:0 withBytes:src
|
||||
bytesPerRow:(NSUInteger)(4 * _size.width)];
|
||||
}
|
||||
else {
|
||||
void *dst = _pixels.contents;
|
||||
size_t len = (size_t)(_bpp * _size.width);
|
||||
assert(len <= pitch); // the length can't be larger?
|
||||
|
||||
if (len < pitch) {
|
||||
for (int i = 0; i < _size.height; i++) {
|
||||
memcpy(dst, src, len);
|
||||
dst += len;
|
||||
src += pitch;
|
||||
}
|
||||
}
|
||||
else {
|
||||
memcpy(dst, src, _pixels.length);
|
||||
}
|
||||
|
||||
[_pixels didModifyRange:NSMakeRange(0, _pixels.length)];
|
||||
_pixelsDirty = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_initTexture:(texture_t *)t withDescriptor:(MTLTextureDescriptor *)td
|
||||
{
|
||||
STRUCT_ASSIGN(t->view, [_context.device newTextureWithDescriptor:td]);
|
||||
t->size_data.x = td.width;
|
||||
t->size_data.y = td.height;
|
||||
t->size_data.z = 1.0f / td.width;
|
||||
t->size_data.w = 1.0f / td.height;
|
||||
}
|
||||
|
||||
- (void)_initHistory
|
||||
{
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:(NSUInteger)_size.width
|
||||
height:(NSUInteger)_size.height
|
||||
mipmapped:false];
|
||||
td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget;
|
||||
|
||||
for (int i = 0; i < _shader->history_size + 1; i++) {
|
||||
[self _initTexture:&_engine.frame.texture[i] withDescriptor:td];
|
||||
}
|
||||
init_history = NO;
|
||||
}
|
||||
|
||||
typedef struct vertex
|
||||
{
|
||||
simd_float4 pos;
|
||||
simd_float2 tex;
|
||||
} vertex_t;
|
||||
|
||||
static vertex_t vertex_bytes[] = {
|
||||
{{0, 1, 0, 1}, {0, 1}},
|
||||
{{1, 1, 0, 1}, {1, 1}},
|
||||
{{0, 0, 0, 1}, {0, 0}},
|
||||
{{1, 0, 0, 1}, {1, 0}},
|
||||
};
|
||||
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
|
||||
{
|
||||
if (_texture) {
|
||||
[rce setViewport:_engine.frame.viewport];
|
||||
[rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions];
|
||||
[rce setFragmentTexture:_texture atIndex:TextureIndexColor];
|
||||
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx
|
||||
{
|
||||
_texture = _engine.frame.texture[0].view;
|
||||
[self _convertFormat];
|
||||
|
||||
if (!_shader || _shader->passes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < _shader->passes; i++) {
|
||||
if (_shader->pass[i].feedback) {
|
||||
texture_t tmp = _engine.pass[i].feedback;
|
||||
_engine.pass[i].feedback = _engine.pass[i].rt;
|
||||
_engine.pass[i].rt = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> cb = ctx.commandBuffer;
|
||||
|
||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0);
|
||||
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
|
||||
BOOL firstPass = YES;
|
||||
|
||||
for (unsigned i = 0; i < _shader->passes; i++) {
|
||||
BOOL lastPass = i == _shader->passes - 1;
|
||||
|
||||
if (lastPass) {
|
||||
rpd.colorAttachments[0].texture = _context.nextDrawable.texture;
|
||||
}
|
||||
else {
|
||||
rpd.colorAttachments[0].texture = _engine.pass[i].rt.view;
|
||||
}
|
||||
|
||||
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:rpd];
|
||||
if (firstPass) {
|
||||
firstPass = NO;
|
||||
} else {
|
||||
[rce waitForFence:_fence beforeStages:MTLRenderStageVertex];
|
||||
}
|
||||
|
||||
[rce setRenderPipelineState:_engine.pass[i]._state];
|
||||
|
||||
_engine.pass[i].frame_count = (uint32_t)_frameCount;
|
||||
if (_shader->pass[i].frame_count_mod)
|
||||
_engine.pass[i].frame_count %= _shader->pass[i].frame_count_mod;
|
||||
|
||||
for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) {
|
||||
id<MTLBuffer> buffer = _engine.pass[i].buffers[j];
|
||||
cbuffer_sem_t *buffer_sem = &_engine.pass[i].semantics.cbuffers[j];
|
||||
|
||||
if (buffer_sem->stage_mask && buffer_sem->uniforms) {
|
||||
void *data = buffer.contents;
|
||||
uniform_sem_t *uniform = buffer_sem->uniforms;
|
||||
|
||||
while (uniform->size) {
|
||||
if (uniform->data)
|
||||
memcpy((uint8_t *)data + uniform->offset, uniform->data, uniform->size);
|
||||
uniform++;
|
||||
}
|
||||
|
||||
if (buffer_sem->stage_mask & SLANG_STAGE_VERTEX_MASK)
|
||||
[rce setVertexBuffer:buffer offset:0 atIndex:buffer_sem->binding];
|
||||
|
||||
if (buffer_sem->stage_mask & SLANG_STAGE_FRAGMENT_MASK)
|
||||
[rce setFragmentBuffer:buffer offset:0 atIndex:buffer_sem->binding];
|
||||
[buffer didModifyRange:NSMakeRange(0, buffer.length)];
|
||||
}
|
||||
}
|
||||
|
||||
__unsafe_unretained id<MTLTexture> textures[SLANG_NUM_BINDINGS] = {NULL};
|
||||
id<MTLSamplerState> samplers[SLANG_NUM_BINDINGS] = {NULL};
|
||||
|
||||
texture_sem_t *texture_sem = _engine.pass[i].semantics.textures;
|
||||
while (texture_sem->stage_mask) {
|
||||
int binding = texture_sem->binding;
|
||||
id<MTLTexture> tex = (__bridge id<MTLTexture>)*(void **)texture_sem->texture_data;
|
||||
textures[binding] = tex;
|
||||
samplers[binding] = _samplers[texture_sem->filter][texture_sem->wrap];
|
||||
texture_sem++;
|
||||
}
|
||||
|
||||
if (lastPass) {
|
||||
[rce setViewport:_engine.frame.viewport];
|
||||
}
|
||||
else {
|
||||
[rce setViewport:_engine.pass[i].viewport];
|
||||
}
|
||||
|
||||
[rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
||||
[rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
||||
[rce setVertexBytes:vertex_bytes length:sizeof(vertex_bytes) atIndex:4];
|
||||
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
[rce updateFence:_fence afterStages:MTLRenderStageFragment];
|
||||
[rce endEncoding];
|
||||
_texture = _engine.pass[i].rt.view;
|
||||
}
|
||||
_texture = nil;
|
||||
}
|
||||
|
||||
- (void)_updateRenderTargets
|
||||
{
|
||||
if (!_shader || !resize_render_targets) return;
|
||||
|
||||
// release existing targets
|
||||
for (int i = 0; i < _shader->passes; i++) {
|
||||
STRUCT_ASSIGN(_engine.pass[i].rt.view, nil);
|
||||
STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil);
|
||||
memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt));
|
||||
memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback));
|
||||
}
|
||||
|
||||
NSUInteger width = (NSUInteger)_size.width, height = (NSUInteger)_size.height;
|
||||
|
||||
for (unsigned i = 0; i < _shader->passes; i++) {
|
||||
struct video_shader_pass *shader_pass = &_shader->pass[i];
|
||||
|
||||
if (shader_pass->fbo.valid) {
|
||||
switch (shader_pass->fbo.type_x) {
|
||||
case RARCH_SCALE_INPUT:
|
||||
width *= shader_pass->fbo.scale_x;
|
||||
break;
|
||||
|
||||
case RARCH_SCALE_VIEWPORT:
|
||||
width = (NSUInteger)(_viewport->width * shader_pass->fbo.scale_x);
|
||||
break;
|
||||
|
||||
case RARCH_SCALE_ABSOLUTE:
|
||||
width = shader_pass->fbo.abs_x;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!width)
|
||||
width = _viewport->width;
|
||||
|
||||
switch (shader_pass->fbo.type_y) {
|
||||
case RARCH_SCALE_INPUT:
|
||||
height *= shader_pass->fbo.scale_y;
|
||||
break;
|
||||
|
||||
case RARCH_SCALE_VIEWPORT:
|
||||
height = (NSUInteger)(_viewport->height * shader_pass->fbo.scale_y);
|
||||
break;
|
||||
|
||||
case RARCH_SCALE_ABSOLUTE:
|
||||
height = shader_pass->fbo.abs_y;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!height)
|
||||
height = _viewport->height;
|
||||
}
|
||||
else if (i == (_shader->passes - 1)) {
|
||||
width = _viewport->width;
|
||||
height = _viewport->height;
|
||||
}
|
||||
|
||||
RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height);
|
||||
|
||||
if (i != (_shader->passes - 1)) {
|
||||
|
||||
_engine.pass[i].viewport.width = width;
|
||||
_engine.pass[i].viewport.height = height;
|
||||
_engine.pass[i].viewport.znear = 0.0;
|
||||
_engine.pass[i].viewport.zfar = 1.0;
|
||||
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:width
|
||||
height:height
|
||||
mipmapped:false];
|
||||
td.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
|
||||
[self _initTexture:&_engine.pass[i].rt withDescriptor:td];
|
||||
|
||||
if (shader_pass->feedback) {
|
||||
[self _initTexture:&_engine.pass[i].feedback withDescriptor:td];
|
||||
}
|
||||
}
|
||||
else {
|
||||
_engine.pass[i].rt.size_data.x = width;
|
||||
_engine.pass[i].rt.size_data.y = height;
|
||||
_engine.pass[i].rt.size_data.z = 1.0f / width;
|
||||
_engine.pass[i].rt.size_data.w = 1.0f / height;
|
||||
}
|
||||
}
|
||||
|
||||
resize_render_targets = NO;
|
||||
}
|
||||
|
||||
- (void)_freeVideoShader:(struct video_shader *)shader
|
||||
{
|
||||
if (!shader)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < GFX_MAX_SHADERS; i++) {
|
||||
STRUCT_ASSIGN(_engine.pass[i].rt.view, nil);
|
||||
STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil);
|
||||
memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt));
|
||||
memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback));
|
||||
|
||||
STRUCT_ASSIGN(_engine.pass[i]._state, nil);
|
||||
|
||||
for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) {
|
||||
STRUCT_ASSIGN(_engine.pass[i].buffers[j], nil);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < GFX_MAX_TEXTURES; i++) {
|
||||
STRUCT_ASSIGN(_engine.luts[i].view, nil);
|
||||
}
|
||||
|
||||
free(shader);
|
||||
_fence = nil;
|
||||
}
|
||||
|
||||
- (BOOL)setShaderFromPath:(NSString *)path
|
||||
{
|
||||
[self _freeVideoShader:_shader];
|
||||
_shader = nil;
|
||||
|
||||
config_file_t *conf = config_file_new(path.UTF8String);
|
||||
struct video_shader *shader = (struct video_shader *)calloc(1, sizeof(*shader));
|
||||
|
||||
@try {
|
||||
if (!video_shader_read_conf_cgp(conf, shader))
|
||||
return NO;
|
||||
|
||||
video_shader_resolve_relative(shader, path.UTF8String);
|
||||
|
||||
texture_t *source = &_engine.frame.texture[0];
|
||||
for (unsigned i = 0; i < shader->passes; source = &_engine.pass[i++].rt) {
|
||||
/* clang-format off */
|
||||
semantics_map_t semantics_map = {
|
||||
{
|
||||
/* Original */
|
||||
{&_engine.frame.texture[0].view, 0,
|
||||
&_engine.frame.texture[0].size_data, 0},
|
||||
|
||||
/* Source */
|
||||
{&source->view, 0,
|
||||
&source->size_data, 0},
|
||||
|
||||
/* OriginalHistory */
|
||||
{&_engine.frame.texture[0].view, sizeof(*_engine.frame.texture),
|
||||
&_engine.frame.texture[0].size_data, sizeof(*_engine.frame.texture)},
|
||||
|
||||
/* PassOutput */
|
||||
{&_engine.pass[0].rt.view, sizeof(*_engine.pass),
|
||||
&_engine.pass[0].rt.size_data, sizeof(*_engine.pass)},
|
||||
|
||||
/* PassFeedback */
|
||||
{&_engine.pass[0].feedback.view, sizeof(*_engine.pass),
|
||||
&_engine.pass[0].feedback.size_data, sizeof(*_engine.pass)},
|
||||
|
||||
/* User */
|
||||
{&_engine.luts[0].view, sizeof(*_engine.luts),
|
||||
&_engine.luts[0].size_data, sizeof(*_engine.luts)},
|
||||
},
|
||||
{
|
||||
&_engine.mvp, /* MVP */
|
||||
&_engine.pass[i].rt.size_data, /* OutputSize */
|
||||
&_engine.frame.output_size, /* FinalViewportSize */
|
||||
&_engine.pass[i].frame_count, /* FrameCount */
|
||||
}
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics))
|
||||
return NO;
|
||||
|
||||
@try {
|
||||
// vertex descriptor
|
||||
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||
vd.attributes[0].offset = offsetof(vertex_t, pos);
|
||||
vd.attributes[0].format = MTLVertexFormatFloat4;
|
||||
vd.attributes[0].bufferIndex = 4;
|
||||
vd.attributes[1].offset = offsetof(vertex_t, tex);
|
||||
vd.attributes[1].format = MTLVertexFormatFloat2;
|
||||
vd.attributes[1].bufferIndex = 4;
|
||||
vd.layouts[4].stride = sizeof(vertex_t);
|
||||
vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
|
||||
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
|
||||
psd.label = [NSString stringWithFormat:@"pass %d", i];
|
||||
|
||||
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
|
||||
ca.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
ca.blendingEnabled = YES;
|
||||
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
|
||||
psd.sampleCount = 1;
|
||||
psd.vertexDescriptor = vd;
|
||||
NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex];
|
||||
NSLog(@"vertex function:\n%@", vs_src);
|
||||
NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment];
|
||||
NSLog(@"fragment function:\n%@", fs_src);
|
||||
|
||||
NSError *err;
|
||||
id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err];
|
||||
if (err != nil) {
|
||||
if (lib == nil) {
|
||||
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||
}
|
||||
|
||||
psd.vertexFunction = [lib newFunctionWithName:@"main0"];
|
||||
|
||||
lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err];
|
||||
if (err != nil) {
|
||||
if (lib == nil) {
|
||||
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||
}
|
||||
psd.fragmentFunction = [lib newFunctionWithName:@"main0"];
|
||||
|
||||
STRUCT_ASSIGN(_engine.pass[i]._state,
|
||||
[_context.device newRenderPipelineStateWithDescriptor:psd error:&err]);
|
||||
if (err != nil) {
|
||||
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) {
|
||||
unsigned int size = _engine.pass[i].semantics.cbuffers[j].size;
|
||||
if (size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
id<MTLBuffer> buf = [_context.device newBufferWithLength:size options:MTLResourceStorageModeManaged];
|
||||
STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf);
|
||||
}
|
||||
} @finally {
|
||||
free(shader->pass[i].source.string.vertex);
|
||||
free(shader->pass[i].source.string.fragment);
|
||||
|
||||
shader->pass[i].source.string.vertex = NULL;
|
||||
shader->pass[i].source.string.fragment = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < shader->luts; i++) {
|
||||
struct texture_image image = {0};
|
||||
image.supports_rgba = true;
|
||||
|
||||
if (!image_texture_load(&image, shader->lut[i].path))
|
||||
return NO;
|
||||
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
|
||||
width:image.width
|
||||
height:image.height
|
||||
mipmapped:shader->lut[i].mipmap];
|
||||
td.usage = MTLTextureUsageShaderRead;
|
||||
[self _initTexture:&_engine.luts[i] withDescriptor:td];
|
||||
|
||||
[_engine.luts[i].view replaceRegion:MTLRegionMake2D(0, 0, image.width, image.height)
|
||||
mipmapLevel:0 withBytes:image.pixels
|
||||
bytesPerRow:4 * image.width];
|
||||
|
||||
// TODO(sgc): generate mip maps
|
||||
image_texture_free(&image);
|
||||
}
|
||||
|
||||
video_shader_resolve_current_parameters(conf, shader);
|
||||
_shader = shader;
|
||||
shader = nil;
|
||||
_fence = [_context.device newFence];
|
||||
}
|
||||
@finally {
|
||||
if (shader) {
|
||||
[self _freeVideoShader:shader];
|
||||
}
|
||||
|
||||
if (conf) {
|
||||
config_file_free(conf);
|
||||
conf = nil;
|
||||
}
|
||||
}
|
||||
|
||||
resize_render_targets = YES;
|
||||
init_history = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
@ -30,6 +30,13 @@
|
||||
#include "vulkan_common.h"
|
||||
#include "../../libretro-common/include/retro_timers.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../include/vulkan/vulkan.h"
|
||||
#include "../../libretro-common/include/retro_assert.h"
|
||||
#include "vksym.h"
|
||||
#include "../../libretro-common/include/dynamic/dylib.h"
|
||||
#include "../../libretro-common/include/libretro_vulkan.h"
|
||||
#include "../../libretro-common/include/retro_math.h"
|
||||
#include "../../libretro-common/include/string/stdstring.h"
|
||||
|
||||
static dylib_t vulkan_library;
|
||||
static VkInstance cached_instance_vk;
|
||||
|
@ -48,6 +48,10 @@
|
||||
#include "../font_driver.h"
|
||||
#include "../video_driver.h"
|
||||
#include "../drivers_shader/shader_vulkan.h"
|
||||
#include "../../libretro-common/include/gfx/math/matrix_4x4.h"
|
||||
#include "../include/vulkan/vulkan.h"
|
||||
#include "../../libretro-common/include/gfx/scaler/scaler.h"
|
||||
#include "../../libretro-common/include/libretro_vulkan.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
|
384
gfx/drivers/metal.m
Normal file
384
gfx/drivers/metal.m
Normal file
@ -0,0 +1,384 @@
|
||||
//
|
||||
// metal.m
|
||||
// RetroArch_Metal
|
||||
//
|
||||
// Created by Stuart Carnie on 5/14/18.
|
||||
//
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <gfx/scaler/scaler.h>
|
||||
#include <gfx/video_frame.h>
|
||||
#include <formats/image.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_math.h>
|
||||
#include <retro_assert.h>
|
||||
#include <libretro.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#import "../../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#import "../../menu/menu_driver.h"
|
||||
|
||||
#endif
|
||||
|
||||
#import "../font_driver.h"
|
||||
|
||||
#import "../common/metal_common.h"
|
||||
|
||||
#import "../../driver.h"
|
||||
#import "../../configuration.h"
|
||||
#import "../../record/record_driver.h"
|
||||
|
||||
#import "../../retroarch.h"
|
||||
#import "../../verbosity.h"
|
||||
|
||||
#import "../video_coord_array.h"
|
||||
|
||||
static void *metal_init(const video_info_t *video,
|
||||
const input_driver_t **input,
|
||||
void **input_data)
|
||||
{
|
||||
gfx_ctx_mode_t mode;
|
||||
|
||||
[apple_platform setViewType:APPLE_VIEW_TYPE_METAL];
|
||||
|
||||
MetalDriver *md = [MetalDriver new];
|
||||
if (md == nil) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
apple_platform.delegate = md;
|
||||
|
||||
RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height);
|
||||
|
||||
mode.width = video->width;
|
||||
mode.height = video->height;
|
||||
mode.fullscreen = video->fullscreen;
|
||||
|
||||
[apple_platform setVideoMode:mode];
|
||||
[md setVideo:video];
|
||||
|
||||
*input = NULL;
|
||||
*input_data = NULL;
|
||||
|
||||
return (__bridge_retained void *)md;
|
||||
}
|
||||
|
||||
static bool metal_frame(void *data, const void *frame,
|
||||
unsigned frame_width, unsigned frame_height,
|
||||
uint64_t frame_count,
|
||||
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
[md beginFrame];
|
||||
|
||||
FrameView *v = md.frameView;
|
||||
v.frameCount = frame_count;
|
||||
v.size = CGSizeMake(frame_width, frame_height);
|
||||
[v updateFrame:frame pitch:pitch];
|
||||
|
||||
#if defined(HAVE_MENU)
|
||||
if (md.menu.enabled) {
|
||||
menu_driver_frame(video_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
[md endFrame];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static void metal_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
}
|
||||
|
||||
static bool metal_alive(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool metal_focus(void *data)
|
||||
{
|
||||
return apple_platform.hasFocus;
|
||||
}
|
||||
|
||||
static bool metal_suppress_screensaver(void *data, bool enable)
|
||||
{
|
||||
bool enabled = enable;
|
||||
(void)data;
|
||||
|
||||
return video_context_driver_suppress_screensaver(&enabled);
|
||||
}
|
||||
|
||||
static bool metal_set_shader(void *data,
|
||||
enum rarch_shader_type type, const char *path)
|
||||
{
|
||||
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
if (!md)
|
||||
return false;
|
||||
if (!path)
|
||||
return true;
|
||||
|
||||
if (type != RARCH_SHADER_SLANG) {
|
||||
RARCH_WARN("[Metal] Only .slang or .slangp shaders are supported. Falling back to stock.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return [md.frameView setShaderFromPath:[NSString stringWithUTF8String:path]];
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void metal_free(void *data)
|
||||
{
|
||||
MetalDriver *md = (__bridge_transfer MetalDriver *)data;
|
||||
md = nil;
|
||||
}
|
||||
|
||||
static void metal_set_viewport(void *data, unsigned viewport_width,
|
||||
unsigned viewport_height, bool force_full, bool allow_rotate)
|
||||
{
|
||||
RARCH_LOG("[Metal]: set_viewport\n");
|
||||
}
|
||||
|
||||
static void metal_set_rotation(void *data, unsigned rotation)
|
||||
{
|
||||
}
|
||||
|
||||
static void metal_viewport_info(void *data, struct video_viewport *vp)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
*vp = *md.viewport;
|
||||
}
|
||||
|
||||
static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
|
||||
static const video_overlay_interface_t metal_overlay_interface = {
|
||||
// metal_overlay_enable,
|
||||
// metal_overlay_load,
|
||||
// metal_overlay_tex_geom,
|
||||
// metal_overlay_vertex_geom,
|
||||
// metal_overlay_full_screen,
|
||||
// metal_overlay_set_alpha,
|
||||
};
|
||||
|
||||
static void metal_get_overlay_interface(void *data,
|
||||
const video_overlay_interface_t **iface)
|
||||
{
|
||||
(void)data;
|
||||
*iface = &metal_overlay_interface;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static uintptr_t metal_load_texture(void *video_data, void *data,
|
||||
bool threaded, enum texture_filter_type filter_type)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)video_data;
|
||||
struct texture_image *image = (struct texture_image *)data;
|
||||
if (!image)
|
||||
return 0;
|
||||
|
||||
if (!image->pixels && !image->width && !image->height) {
|
||||
/* Create a dummy texture instead. */
|
||||
#define T0 0xff000000u
|
||||
#define T1 0xffffffffu
|
||||
static const uint32_t checkerboard[] = {
|
||||
T0, T1, T0, T1, T0, T1, T0, T1,
|
||||
T1, T0, T1, T0, T1, T0, T1, T0,
|
||||
T0, T1, T0, T1, T0, T1, T0, T1,
|
||||
T1, T0, T1, T0, T1, T0, T1, T0,
|
||||
T0, T1, T0, T1, T0, T1, T0, T1,
|
||||
T1, T0, T1, T0, T1, T0, T1, T0,
|
||||
T0, T1, T0, T1, T0, T1, T0, T1,
|
||||
T1, T0, T1, T0, T1, T0, T1, T0,
|
||||
};
|
||||
#undef T0
|
||||
#undef T1
|
||||
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void metal_unload_texture(void *data, uintptr_t handle)
|
||||
{
|
||||
}
|
||||
|
||||
static void metal_set_video_mode(void *data,
|
||||
unsigned width, unsigned height,
|
||||
bool fullscreen)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
gfx_ctx_mode_t mode = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.fullscreen = fullscreen,
|
||||
};
|
||||
|
||||
//[md setVideoMode:mode];
|
||||
}
|
||||
|
||||
static float metal_get_refresh_rate(void *data)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
(void)md;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void metal_set_filtering(void *data, unsigned index, bool smooth)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
[md.frameView setFilteringIndex:index smooth:smooth];
|
||||
}
|
||||
|
||||
static void metal_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
|
||||
switch (aspect_ratio_idx) {
|
||||
case ASPECT_RATIO_SQUARE:
|
||||
video_driver_set_viewport_square_pixel();
|
||||
break;
|
||||
|
||||
case ASPECT_RATIO_CORE:
|
||||
video_driver_set_viewport_core();
|
||||
break;
|
||||
|
||||
case ASPECT_RATIO_CONFIG:
|
||||
video_driver_set_viewport_config();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
video_driver_set_aspect_ratio_value(
|
||||
aspectratio_lut[aspect_ratio_idx].value);
|
||||
|
||||
md.keepAspect = YES;
|
||||
[md setNeedsResize];
|
||||
}
|
||||
|
||||
static void metal_apply_state_changes(void *data)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
[md setNeedsResize];
|
||||
}
|
||||
|
||||
static void metal_set_texture_frame(void *data, const void *frame,
|
||||
bool rgb32, unsigned width, unsigned height,
|
||||
float alpha)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
[md.menu updateWidth:width
|
||||
height:height
|
||||
format:rgb32 ? RPixelFormatBGRA8Unorm : RPixelFormatBGRA4Unorm
|
||||
filter:settings->bools.menu_linear_filter ? RTextureFilterLinear : RTextureFilterNearest];
|
||||
[md.menu updateFrame:frame];
|
||||
md.menu.alpha = alpha;
|
||||
}
|
||||
|
||||
static void metal_set_texture_enable(void *data, bool state, bool full_screen)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
if (!md)
|
||||
return;
|
||||
|
||||
md.menu.enabled = state;
|
||||
//md.menu.fullScreen = full_screen;
|
||||
}
|
||||
|
||||
|
||||
static void metal_show_mouse(void *data, bool state)
|
||||
{
|
||||
[apple_platform setCursorVisible:state];
|
||||
}
|
||||
|
||||
static struct video_shader* metal_get_current_shader(void* data)
|
||||
{
|
||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||
if (!md)
|
||||
return NULL;
|
||||
|
||||
return md.frameView.shader;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t metal_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static const video_poke_interface_t metal_poke_interface = {
|
||||
.get_flags = metal_get_flags,
|
||||
.load_texture = metal_load_texture,
|
||||
.unload_texture = metal_unload_texture,
|
||||
.set_video_mode = metal_set_video_mode,
|
||||
.get_refresh_rate = metal_get_refresh_rate,
|
||||
.set_filtering = metal_set_filtering,
|
||||
.set_aspect_ratio = metal_set_aspect_ratio,
|
||||
.apply_state_changes = metal_apply_state_changes,
|
||||
.set_texture_frame = metal_set_texture_frame,
|
||||
.set_texture_enable = metal_set_texture_enable,
|
||||
.show_mouse = metal_show_mouse,
|
||||
.get_current_shader = metal_get_current_shader,
|
||||
};
|
||||
|
||||
static void metal_get_poke_interface(void *data,
|
||||
const video_poke_interface_t **iface)
|
||||
{
|
||||
(void)data;
|
||||
*iface = &metal_poke_interface;
|
||||
}
|
||||
|
||||
|
||||
video_driver_t video_metal = {
|
||||
.init = metal_init,
|
||||
.frame = metal_frame,
|
||||
.set_nonblock_state = metal_set_nonblock_state,
|
||||
.alive = metal_alive,
|
||||
.focus = metal_focus,
|
||||
.suppress_screensaver = metal_suppress_screensaver,
|
||||
.set_shader = metal_set_shader,
|
||||
.free = metal_free,
|
||||
.ident = "metal",
|
||||
.set_viewport = metal_set_viewport,
|
||||
.set_rotation = metal_set_rotation,
|
||||
.viewport_info = metal_viewport_info,
|
||||
.read_viewport = metal_read_viewport,
|
||||
#ifdef HAVE_OVERLAY
|
||||
.overlay_interface = metal_get_overlay_interface,
|
||||
#endif
|
||||
.poke_interface = metal_get_poke_interface,
|
||||
};
|
@ -36,11 +36,6 @@
|
||||
#ifdef HAVE_VULKAN
|
||||
#include "../common/vulkan_common.h"
|
||||
#endif
|
||||
#if __has_feature(objc_arc)
|
||||
#define BRIDGE __bridge
|
||||
#else
|
||||
#define BRIDGE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
#define GLContextClass EAGLContext
|
||||
@ -110,7 +105,7 @@ static NSOpenGLPixelFormat* g_format;
|
||||
|
||||
void *glcontext_get_ptr(void)
|
||||
{
|
||||
return g_context;
|
||||
return (BRIDGE void *)g_context;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -167,10 +162,7 @@ static float get_from_selector(Class obj_class, id obj_id, SEL selector, CGFloat
|
||||
[invocation setTarget:obj_id];
|
||||
[invocation invoke];
|
||||
[invocation getReturnValue:ret];
|
||||
#if __has_feature(objc_arc)
|
||||
#else
|
||||
[invocation release];
|
||||
#endif
|
||||
RELEASE(invocation);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
@ -212,7 +204,8 @@ float get_backing_scale_factor(void)
|
||||
if ([screen respondsToSelector:selector])
|
||||
{
|
||||
CGFloat ret;
|
||||
CocoaView *g_view = (CocoaView*)nsview_get_ptr();
|
||||
NSView *g_view = apple_platform.renderView;
|
||||
//CocoaView *g_view = (CocoaView*)nsview_get_ptr();
|
||||
backing_scale_def = (float)get_from_selector
|
||||
([[g_view window] class], [g_view window], selector, &ret);
|
||||
}
|
||||
@ -224,19 +217,22 @@ float get_backing_scale_factor(void)
|
||||
|
||||
void cocoagl_gfx_ctx_update(void)
|
||||
{
|
||||
if (cocoagl_api == GFX_CTX_VULKAN_API) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cocoagl_api)
|
||||
{
|
||||
case GFX_CTX_OPENGL_API:
|
||||
#if defined(HAVE_COCOA)
|
||||
#if MAC_OS_X_VERSION_10_7
|
||||
CGLUpdateContext(g_hw_ctx.CGLContextObj);
|
||||
CGLUpdateContext(g_context.CGLContextObj);
|
||||
CGLUpdateContext(g_hw_ctx.CGLContextObj);
|
||||
CGLUpdateContext(g_context.CGLContextObj);
|
||||
#else
|
||||
[g_hw_ctx update];
|
||||
[g_context update];
|
||||
[g_hw_ctx update];
|
||||
[g_context update];
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cocoagl_gfx_ctx_destroy(void *data)
|
||||
@ -254,18 +250,13 @@ static void cocoagl_gfx_ctx_destroy(void *data)
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
[g_context clearDrawable];
|
||||
if (g_context)
|
||||
[g_context release];
|
||||
g_context = nil;
|
||||
if (g_format)
|
||||
[g_format release];
|
||||
g_format = nil;
|
||||
RELEASE(g_context);
|
||||
RELEASE(g_format);
|
||||
if (g_hw_ctx)
|
||||
{
|
||||
[g_hw_ctx clearDrawable];
|
||||
[g_hw_ctx release];
|
||||
}
|
||||
g_hw_ctx = nil;
|
||||
RELEASE(g_hw_ctx);
|
||||
#endif
|
||||
[GLContextClass clearCurrentContext];
|
||||
g_context = nil;
|
||||
@ -298,8 +289,18 @@ static void *cocoagl_gfx_ctx_init(video_frame_info_t *video_info, void *video_dr
|
||||
|
||||
switch (cocoagl_api)
|
||||
{
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
case GFX_CTX_OPENGL_ES_API:
|
||||
[apple_platform setViewType:APPLE_VIEW_TYPE_OPENGL_ES];
|
||||
break;
|
||||
#elif defined(HAVE_COCOA)
|
||||
case GFX_CTX_OPENGL_API:
|
||||
[apple_platform setViewType:APPLE_VIEW_TYPE_OPENGL];
|
||||
break;
|
||||
#endif
|
||||
case GFX_CTX_VULKAN_API:
|
||||
#ifdef HAVE_VULKAN
|
||||
[apple_platform setViewType:APPLE_VIEW_TYPE_VULKAN];
|
||||
if (!vulkan_context_init(&cocoa_ctx->vk, VULKAN_WSI_MVK_MACOS))
|
||||
{
|
||||
goto error;
|
||||
@ -314,7 +315,7 @@ static void *cocoagl_gfx_ctx_init(video_frame_info_t *video_info, void *video_dr
|
||||
return cocoa_ctx;
|
||||
|
||||
error:
|
||||
cocoagl_gfx_ctx_destroy(&cocoa_ctx);
|
||||
free(cocoa_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -412,7 +413,8 @@ static bool cocoagl_gfx_ctx_set_video_mode(void *data,
|
||||
cocoa_ctx->height = height;
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
CocoaView *g_view = (CocoaView*)nsview_get_ptr();
|
||||
//CocoaView *g_view = (BRIDGE CocoaView *)nsview_get_ptr();
|
||||
NSView *g_view = apple_platform.renderView;
|
||||
#endif
|
||||
|
||||
switch (cocoagl_api)
|
||||
@ -482,7 +484,7 @@ static bool cocoagl_gfx_ctx_set_video_mode(void *data,
|
||||
#ifdef HAVE_VULKAN
|
||||
RARCH_LOG("[macOS]: Native window size: %u x %u.\n", cocoa_ctx->width, cocoa_ctx->height);
|
||||
if (!vulkan_surface_create(&cocoa_ctx->vk, VULKAN_WSI_MVK_MACOS, NULL,
|
||||
g_view, cocoa_ctx->width, cocoa_ctx->height,
|
||||
(BRIDGE void *)g_view, cocoa_ctx->width, cocoa_ctx->height,
|
||||
cocoa_ctx->swap_interval))
|
||||
{
|
||||
RARCH_ERR("[macOS]: Failed to create surface.\n");
|
||||
@ -503,7 +505,7 @@ static bool cocoagl_gfx_ctx_set_video_mode(void *data,
|
||||
{
|
||||
if (!has_went_fullscreen)
|
||||
{
|
||||
[g_view enterFullScreenMode:get_chosen_screen() withOptions:nil];
|
||||
[g_view enterFullScreenMode:(BRIDGE NSScreen *)get_chosen_screen() withOptions:nil];
|
||||
cocoagl_gfx_ctx_show_mouse(data, false);
|
||||
}
|
||||
}
|
||||
@ -554,7 +556,8 @@ static void cocoagl_gfx_ctx_get_video_size(void *data, unsigned* width, unsigned
|
||||
#if defined(HAVE_COCOA)
|
||||
CGRect size;
|
||||
GLsizei backingPixelWidth, backingPixelHeight;
|
||||
CocoaView *g_view = (CocoaView*)nsview_get_ptr();
|
||||
NSView *g_view = apple_platform.renderView;
|
||||
//CocoaView *g_view = (CocoaView*)nsview_get_ptr();
|
||||
CGRect cgrect = NSRectToCGRect([g_view frame]);
|
||||
#if MAC_OS_X_VERSION_10_7
|
||||
SEL selector = NSSelectorFromString(BOXSTRING("convertRectToBacking:"));
|
||||
@ -577,7 +580,8 @@ static void cocoagl_gfx_ctx_update_title(void *data, void *data2)
|
||||
ui_window_cocoa_t view;
|
||||
const ui_window_t *window = ui_companion_driver_get_window_ptr();
|
||||
|
||||
view.data = (CocoaView*)nsview_get_ptr();
|
||||
//view.data = (CocoaView*)nsview_get_ptr();
|
||||
view.data = (BRIDGE void *)apple_platform.renderView;
|
||||
|
||||
if (window)
|
||||
{
|
||||
|
290
gfx/drivers_font/metal_raster_font.m
Normal file
290
gfx/drivers_font/metal_raster_font.m
Normal file
@ -0,0 +1,290 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2016-2017 - Hans-Kristian Arntzen
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <encodings/utf.h>
|
||||
#include <compat/strl.h>
|
||||
|
||||
#include "../common/metal_common.h"
|
||||
|
||||
#include "../font_driver.h"
|
||||
|
||||
typedef struct {
|
||||
int stride;
|
||||
void * mapped;
|
||||
} metal_texture_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const font_renderer_driver_t *font_driver;
|
||||
void *font_data;
|
||||
metal_texture_t texture;
|
||||
struct font_atlas *atlas;
|
||||
} font_ctx_t;
|
||||
|
||||
@interface MetalRaster: NSObject {
|
||||
font_ctx_t *_font;
|
||||
}
|
||||
|
||||
@property (readwrite) MetalDriver *metal;
|
||||
@property (readwrite) font_ctx_t *font;
|
||||
@property (readwrite) bool needsUpdate;
|
||||
|
||||
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MetalRaster
|
||||
|
||||
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size {
|
||||
if (self = [super init])
|
||||
{
|
||||
if (metal == nil)
|
||||
return nil;
|
||||
|
||||
_metal = metal;
|
||||
_font = (font_ctx_t *)calloc(1, sizeof(font_ctx_t));
|
||||
if (!font_renderer_create_default((const void**)&_font->font_driver,
|
||||
&_font->font_data, font_path, font_size))
|
||||
{
|
||||
RARCH_WARN("Couldn't initialize font renderer.\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
_font->atlas = _font->font_driver->get_atlas(_font->font_data);
|
||||
|
||||
// font->texture = vulkan_create_texture(font->vk, NULL,
|
||||
// font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, font->atlas->buffer,
|
||||
// NULL /*&swizzle*/, VULKAN_TEXTURE_STAGING);
|
||||
//
|
||||
// vulkan_map_persistent_texture(
|
||||
// font->vk->context->device, &font->texture);
|
||||
//
|
||||
// font->texture_optimal = vulkan_create_texture(font->vk, NULL,
|
||||
// font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, NULL,
|
||||
// NULL /*&swizzle*/, VULKAN_TEXTURE_DYNAMIC);
|
||||
//
|
||||
_needsUpdate = true;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_font) {
|
||||
if (_font->font_driver && _font->font_data) {
|
||||
_font->font_driver->free(_font->font_data);
|
||||
_font->font_data = NULL;
|
||||
_font->font_driver = NULL;
|
||||
}
|
||||
|
||||
free(_font);
|
||||
_font = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
static void metal_raster_font_free_font(void *data, bool is_threaded);
|
||||
|
||||
static void *metal_raster_font_init_font(void *data,
|
||||
const char *font_path, float font_size,
|
||||
bool is_threaded)
|
||||
{
|
||||
MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge MetalDriver *)data fontPath:font_path fontSize:font_size];
|
||||
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
return (__bridge_retained void *)r;
|
||||
}
|
||||
|
||||
static void metal_raster_font_free_font(void *data, bool is_threaded)
|
||||
{
|
||||
MetalRaster * r = (__bridge_transfer MetalRaster *)data;
|
||||
r = nil;
|
||||
}
|
||||
|
||||
static INLINE void metal_raster_font_update_glyph(MetalRaster *r, const struct font_glyph *glyph)
|
||||
{
|
||||
font_ctx_t * font = r.font;
|
||||
|
||||
if(font->atlas->dirty)
|
||||
{
|
||||
unsigned row;
|
||||
for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++)
|
||||
{
|
||||
uint8_t *src = font->atlas->buffer + row * font->atlas->width + glyph->atlas_offset_x;
|
||||
uint8_t *dst = (uint8_t*)font->texture.mapped + row * font->texture.stride + glyph->atlas_offset_x;
|
||||
memcpy(dst, src, glyph->width);
|
||||
}
|
||||
|
||||
font->atlas->dirty = false;
|
||||
r.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int metal_get_message_width(void *data, const char *msg,
|
||||
unsigned msg_len, float scale)
|
||||
{
|
||||
MetalRaster * r = (__bridge MetalRaster *)data;
|
||||
font_ctx_t *font = r.font;
|
||||
|
||||
unsigned i;
|
||||
int delta_x = 0;
|
||||
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < msg_len; i++)
|
||||
{
|
||||
const struct font_glyph *glyph =
|
||||
font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]);
|
||||
if (!glyph) /* Do something smarter here ... */
|
||||
glyph = font->font_driver->get_glyph(font->font_data, '?');
|
||||
|
||||
|
||||
if (glyph)
|
||||
{
|
||||
metal_raster_font_update_glyph(r, glyph);
|
||||
delta_x += glyph->advance_x;
|
||||
}
|
||||
}
|
||||
|
||||
return delta_x * scale;
|
||||
}
|
||||
|
||||
static void metal_raster_font_render_line(
|
||||
MetalRaster *r, const char *msg, unsigned msg_len,
|
||||
float scale, const float color[4], float pos_x,
|
||||
float pos_y, unsigned text_align)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void metal_raster_font_render_message(
|
||||
MetalRaster *r, const char *msg, float scale,
|
||||
const float color[4], float pos_x, float pos_y,
|
||||
unsigned text_align)
|
||||
{
|
||||
font_ctx_t *font = r.font;
|
||||
|
||||
int lines = 0;
|
||||
float line_height;
|
||||
|
||||
if (!msg || !*msg || !r.metal)
|
||||
return;
|
||||
|
||||
/* If the font height is not supported just draw as usual */
|
||||
if (!font->font_driver->get_line_height)
|
||||
{
|
||||
if (r.metal)
|
||||
metal_raster_font_render_line(r, msg, strlen(msg),
|
||||
scale, color, pos_x, pos_y, text_align);
|
||||
return;
|
||||
}
|
||||
|
||||
line_height = (float) font->font_driver->get_line_height(font->font_data) *
|
||||
scale / r.metal.viewport->height;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const char *delim = strchr(msg, '\n');
|
||||
|
||||
/* Draw the line */
|
||||
if (delim)
|
||||
{
|
||||
unsigned msg_len = delim - msg;
|
||||
if (r.metal)
|
||||
metal_raster_font_render_line(r, msg, msg_len,
|
||||
scale, color, pos_x, pos_y - (float)lines * line_height,
|
||||
text_align);
|
||||
msg += msg_len + 1;
|
||||
lines++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned msg_len = strlen(msg);
|
||||
if (r.metal)
|
||||
metal_raster_font_render_line(r, msg, msg_len,
|
||||
scale, color, pos_x, pos_y - (float)lines * line_height,
|
||||
text_align);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void metal_raster_font_flush(MetalRaster *font)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void metal_raster_font_render_msg(
|
||||
video_frame_info_t *video_info,
|
||||
void *data, const char *msg,
|
||||
const struct font_params *params)
|
||||
{
|
||||
MetalRaster *r = (__bridge MetalRaster *)data;
|
||||
|
||||
if (!r || !msg || !*msg)
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static const struct font_glyph *metal_raster_font_get_glyph(
|
||||
void *data, uint32_t code)
|
||||
{
|
||||
const struct font_glyph* glyph;
|
||||
MetalRaster * r = (__bridge MetalRaster *)data;
|
||||
font_ctx_t *font = r.font;
|
||||
|
||||
if (!font || !font->font_driver)
|
||||
return NULL;
|
||||
|
||||
if (!font->font_driver->ident)
|
||||
return NULL;
|
||||
|
||||
glyph = font->font_driver->get_glyph((void*)font->font_driver, code);
|
||||
|
||||
if(glyph)
|
||||
metal_raster_font_update_glyph(r, glyph);
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
static void metal_raster_font_flush_block(unsigned width, unsigned height,
|
||||
void *data, video_frame_info_t *video_info)
|
||||
{
|
||||
(void)data;
|
||||
}
|
||||
|
||||
static void metal_raster_font_bind_block(void *data, void *userdata)
|
||||
{
|
||||
(void)data;
|
||||
}
|
||||
|
||||
font_renderer_t metal_raster_font = {
|
||||
.init = metal_raster_font_init_font,
|
||||
.free = metal_raster_font_free_font,
|
||||
.render_msg = metal_raster_font_render_msg,
|
||||
.ident = "Metal raster",
|
||||
.get_glyph = metal_raster_font_get_glyph,
|
||||
.bind_block = metal_raster_font_bind_block,
|
||||
.flush = metal_raster_font_flush_block,
|
||||
.get_message_width = metal_get_message_width
|
||||
};
|
@ -382,15 +382,23 @@ bool slang_process(
|
||||
string vs_code;
|
||||
string ps_code;
|
||||
|
||||
if (dst_type == RARCH_SHADER_HLSL || dst_type == RARCH_SHADER_CG)
|
||||
switch (dst_type)
|
||||
{
|
||||
vs_compiler = new CompilerHLSL(output.vertex);
|
||||
ps_compiler = new CompilerHLSL(output.fragment);
|
||||
}
|
||||
else
|
||||
{
|
||||
vs_compiler = new CompilerGLSL(output.vertex);
|
||||
ps_compiler = new CompilerGLSL(output.fragment);
|
||||
case RARCH_SHADER_HLSL:
|
||||
case RARCH_SHADER_CG:
|
||||
vs_compiler = new CompilerHLSL(output.vertex);
|
||||
ps_compiler = new CompilerHLSL(output.fragment);
|
||||
break;
|
||||
|
||||
case RARCH_SHADER_METAL:
|
||||
vs_compiler = new CompilerMSL(output.vertex);
|
||||
ps_compiler = new CompilerMSL(output.fragment);
|
||||
break;
|
||||
|
||||
default:
|
||||
vs_compiler = new CompilerGLSL(output.vertex);
|
||||
ps_compiler = new CompilerGLSL(output.fragment);
|
||||
break;
|
||||
}
|
||||
|
||||
vs_resources = vs_compiler->get_shader_resources();
|
||||
@ -448,6 +456,26 @@ bool slang_process(
|
||||
vs_code = vs->compile();
|
||||
ps_code = ps->compile(ps_attrib_remap);
|
||||
}
|
||||
else if (dst_type == RARCH_SHADER_METAL)
|
||||
{
|
||||
CompilerMSL::Options options;
|
||||
CompilerMSL* vs = (CompilerMSL*)vs_compiler;
|
||||
CompilerMSL* ps = (CompilerMSL*)ps_compiler;
|
||||
options.msl_version = version;
|
||||
vs->set_msl_options(options);
|
||||
ps->set_msl_options(options);
|
||||
|
||||
std::vector<MSLVertexAttr> vs_attrib_remap;
|
||||
std::vector<MSLResourceBinding> vs_res;
|
||||
|
||||
for (Resource& resource : vs_resources.stage_inputs)
|
||||
{
|
||||
std::string name = vs->get_name(resource.id);
|
||||
}
|
||||
|
||||
vs_code = vs->compile();
|
||||
ps_code = ps->compile();
|
||||
}
|
||||
else if (shader_info->type == RARCH_SHADER_GLSL)
|
||||
{
|
||||
CompilerGLSL::Options options;
|
||||
|
@ -310,6 +310,37 @@ static bool vulkan_font_init_first(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_METAL
|
||||
static const font_renderer_t *metal_font_backends[] = {
|
||||
&metal_raster_font,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static bool metal_font_init_first(
|
||||
const void **font_driver, void **font_handle,
|
||||
void *video_data, const char *font_path,
|
||||
float font_size, bool is_threaded)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; metal_font_backends[i]; i++)
|
||||
{
|
||||
void *data = metal_font_backends[i]->init(video_data,
|
||||
font_path, font_size,
|
||||
is_threaded);
|
||||
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
*font_driver = metal_font_backends[i];
|
||||
*font_handle = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_D3D10
|
||||
static const font_renderer_t *d3d10_font_backends[] = {
|
||||
&d3d10_font,
|
||||
@ -514,6 +545,11 @@ static bool font_init_first(
|
||||
return vulkan_font_init_first(font_driver, font_handle,
|
||||
video_data, font_path, font_size, is_threaded);
|
||||
#endif
|
||||
#ifdef HAVE_METAL
|
||||
case FONT_DRIVER_RENDER_METAL_API:
|
||||
return metal_font_init_first(font_driver, font_handle,
|
||||
video_data, font_path, font_size, is_threaded);
|
||||
#endif
|
||||
#ifdef HAVE_D3D8
|
||||
case FONT_DRIVER_RENDER_D3D8_API:
|
||||
return d3d8_font_init_first(font_driver, font_handle,
|
||||
|
@ -163,6 +163,7 @@ extern font_renderer_t vita2d_vita_font;
|
||||
extern font_renderer_t ctr_font;
|
||||
extern font_renderer_t wiiu_font;
|
||||
extern font_renderer_t vulkan_raster_font;
|
||||
extern font_renderer_t metal_raster_font;
|
||||
extern font_renderer_t d3d10_font;
|
||||
extern font_renderer_t d3d11_font;
|
||||
extern font_renderer_t d3d12_font;
|
||||
|
@ -91,6 +91,7 @@ enum font_driver_render_api
|
||||
FONT_DRIVER_RENDER_CTR,
|
||||
FONT_DRIVER_RENDER_WIIU,
|
||||
FONT_DRIVER_RENDER_VULKAN_API,
|
||||
FONT_DRIVER_RENDER_METAL_API,
|
||||
FONT_DRIVER_RENDER_CACA,
|
||||
FONT_DRIVER_RENDER_GDI,
|
||||
FONT_DRIVER_RENDER_VGA
|
||||
|
@ -267,6 +267,9 @@ static const video_driver_t *video_drivers[] = {
|
||||
#ifdef HAVE_OPENGL
|
||||
&video_gl,
|
||||
#endif
|
||||
#ifdef HAVE_METAL
|
||||
&video_metal,
|
||||
#endif
|
||||
#ifdef XENON
|
||||
&video_xenon360,
|
||||
#endif
|
||||
@ -3364,6 +3367,8 @@ enum gfx_ctx_api video_context_driver_get_api(void)
|
||||
return GFX_CTX_OPENGL_API;
|
||||
else if (string_is_equal(video_driver, "vulkan"))
|
||||
return GFX_CTX_VULKAN_API;
|
||||
else if (string_is_equal(video_driver, "metal"))
|
||||
return GFX_CTX_METAL_API;
|
||||
|
||||
return GFX_CTX_NONE;
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ enum gfx_ctx_api
|
||||
GFX_CTX_DIRECT3D12_API,
|
||||
GFX_CTX_OPENVG_API,
|
||||
GFX_CTX_VULKAN_API,
|
||||
GFX_CTX_METAL_API,
|
||||
GFX_CTX_GDI_API,
|
||||
GFX_CTX_GX_API,
|
||||
GFX_CTX_GX2_API
|
||||
@ -1246,6 +1247,7 @@ bool video_driver_started_fullscreen(void);
|
||||
|
||||
extern video_driver_t video_gl;
|
||||
extern video_driver_t video_vulkan;
|
||||
extern video_driver_t video_metal;
|
||||
extern video_driver_t video_psp1;
|
||||
extern video_driver_t video_vita2d;
|
||||
extern video_driver_t video_ctr;
|
||||
|
@ -1220,6 +1220,7 @@ enum rarch_shader_type video_shader_get_type_from_ext(
|
||||
case GFX_CTX_DIRECT3D12_API:
|
||||
case GFX_CTX_GX2_API:
|
||||
case GFX_CTX_VULKAN_API:
|
||||
case GFX_CTX_METAL_API:
|
||||
return RARCH_SHADER_SLANG;
|
||||
default:
|
||||
break;
|
||||
@ -1238,6 +1239,7 @@ enum rarch_shader_type video_shader_get_type_from_ext(
|
||||
case GFX_CTX_DIRECT3D12_API:
|
||||
case GFX_CTX_GX2_API:
|
||||
case GFX_CTX_VULKAN_API:
|
||||
case GFX_CTX_METAL_API:
|
||||
return RARCH_SHADER_SLANG;
|
||||
default:
|
||||
break;
|
||||
|
@ -53,7 +53,8 @@ enum rarch_shader_type
|
||||
RARCH_SHADER_CG,
|
||||
RARCH_SHADER_HLSL,
|
||||
RARCH_SHADER_GLSL,
|
||||
RARCH_SHADER_SLANG
|
||||
RARCH_SHADER_SLANG,
|
||||
RARCH_SHADER_METAL
|
||||
};
|
||||
|
||||
enum gfx_scale_type
|
||||
|
@ -50,6 +50,10 @@
|
||||
#include "../deps/glslang/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp"
|
||||
#include "../deps/glslang/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "../deps/glslang/glslang/glslang/OSDependent/Unix/ossource.cpp"
|
||||
#endif
|
||||
|
||||
#include "../deps/glslang/glslang/hlsl/hlslAttributes.cpp"
|
||||
#include "../deps/glslang/glslang/hlsl/hlslGrammar.cpp"
|
||||
#include "../deps/glslang/glslang/hlsl/hlslOpMap.cpp"
|
||||
|
@ -56,3 +56,17 @@
|
||||
#if defined(HAVE_DISCORD)
|
||||
#include "../deps/discord-rpc/src/discord_register_osx.m"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_METAL
|
||||
#import "../gfx/common/metal/Context.m"
|
||||
#import "../gfx/common/metal/Filter.m"
|
||||
#import "../gfx/common/metal/PixelConverter.m"
|
||||
#import "../gfx/common/metal/Renderer.m"
|
||||
#import "../gfx/common/metal/RendererCommon.m"
|
||||
#import "../gfx/common/metal/View.m"
|
||||
#import "../gfx/common/metal/TexturedView.m"
|
||||
#import "../gfx/common/metal_common.m"
|
||||
#import "../gfx/drivers/metal.m"
|
||||
#import "../menu/drivers_display/menu_display_metal.m"
|
||||
#import "../gfx/drivers_font/metal_raster_font.m"
|
||||
#endif
|
||||
|
@ -1639,6 +1639,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VIDEO_CONTEXT_DRIVER,
|
||||
"Video context driver")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VULKAN_SUPPORT,
|
||||
"Vulkan support")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_METAL_SUPPORT,
|
||||
"Metal support")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WAYLAND_SUPPORT,
|
||||
"Wayland support")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_X11_SUPPORT,
|
||||
|
104
menu/drivers_display/menu_display_metal.m
Normal file
104
menu/drivers_display/menu_display_metal.m
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// menu_display_metal.m
|
||||
// RetroArch_Metal
|
||||
//
|
||||
// Created by Stuart Carnie on 5/25/18.
|
||||
//
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include "../menu_driver.h"
|
||||
|
||||
#include "../../gfx/font_driver.h"
|
||||
#include "../../gfx/video_driver.h"
|
||||
#import "../../gfx/common/metal_common.h"
|
||||
|
||||
static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void menu_display_metal_blend_begin(video_frame_info_t *video_info)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_blend_end(video_frame_info_t *video_info)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_draw(menu_display_ctx_draw_t *draw,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_draw_pipeline(
|
||||
menu_display_ctx_draw_t *draw, video_frame_info_t *video_info)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_viewport(menu_display_ctx_draw_t *draw,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_restore_clear_color(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void menu_display_metal_clear_color(
|
||||
menu_display_ctx_clearcolor_t *clearcolor,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
(void)clearcolor;
|
||||
}
|
||||
|
||||
static bool menu_display_metal_font_init_first(
|
||||
void **font_handle, void *video_data,
|
||||
const char *font_path, float font_size,
|
||||
bool is_threaded)
|
||||
{
|
||||
font_data_t **handle = (font_data_t**)font_handle;
|
||||
*handle = font_driver_init_first(video_data,
|
||||
font_path, font_size, true,
|
||||
is_threaded,
|
||||
FONT_DRIVER_RENDER_METAL_API);
|
||||
|
||||
if (*handle)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const float *menu_display_metal_get_default_vertices(void)
|
||||
{
|
||||
static float dummy[16] = {0.0f};
|
||||
return &dummy[0];
|
||||
}
|
||||
|
||||
static const float *menu_display_metal_get_default_tex_coords(void)
|
||||
{
|
||||
static float dummy[16] = {0.0f};
|
||||
return &dummy[0];
|
||||
}
|
||||
|
||||
menu_display_ctx_driver_t menu_display_ctx_metal = {
|
||||
menu_display_metal_draw,
|
||||
menu_display_metal_draw_pipeline,
|
||||
menu_display_metal_viewport,
|
||||
menu_display_metal_blend_begin,
|
||||
menu_display_metal_blend_end,
|
||||
menu_display_metal_restore_clear_color,
|
||||
menu_display_metal_clear_color,
|
||||
menu_display_metal_get_default_mvp,
|
||||
menu_display_metal_get_default_vertices,
|
||||
menu_display_metal_get_default_tex_coords,
|
||||
menu_display_metal_font_init_first,
|
||||
MENU_VIDEO_DRIVER_GENERIC,
|
||||
"menu_display_metal",
|
||||
false
|
||||
};
|
||||
|
@ -903,6 +903,15 @@ static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info)
|
||||
menu_entries_append_enum(info->list, feat_str, "",
|
||||
MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0);
|
||||
|
||||
snprintf(feat_str, sizeof(feat_str),
|
||||
"%s: %s",
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_METAL_SUPPORT),
|
||||
_metal_supp ?
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_YES) :
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO));
|
||||
menu_entries_append_enum(info->list, feat_str, "",
|
||||
MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0);
|
||||
|
||||
snprintf(feat_str, sizeof(feat_str),
|
||||
"%s: %s",
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENGL_SUPPORT),
|
||||
|
@ -118,6 +118,9 @@ static menu_display_ctx_driver_t *menu_display_ctx_drivers[] = {
|
||||
#ifdef HAVE_VULKAN
|
||||
&menu_display_ctx_vulkan,
|
||||
#endif
|
||||
#ifdef HAVE_METAL
|
||||
&menu_display_ctx_metal,
|
||||
#endif
|
||||
#ifdef HAVE_VITA2D
|
||||
&menu_display_ctx_vita2d,
|
||||
#endif
|
||||
@ -260,6 +263,10 @@ static bool menu_display_check_compatibility(
|
||||
if (string_is_equal(video_driver, "vulkan"))
|
||||
return true;
|
||||
break;
|
||||
case MENU_VIDEO_DRIVER_METAL:
|
||||
if (string_is_equal(video_driver, "metal"))
|
||||
return true;
|
||||
break;
|
||||
case MENU_VIDEO_DRIVER_DIRECT3D8:
|
||||
if (string_is_equal(video_driver, "d3d8"))
|
||||
return true;
|
||||
|
@ -305,6 +305,7 @@ enum menu_display_driver_type
|
||||
MENU_VIDEO_DRIVER_GENERIC = 0,
|
||||
MENU_VIDEO_DRIVER_OPENGL,
|
||||
MENU_VIDEO_DRIVER_VULKAN,
|
||||
MENU_VIDEO_DRIVER_METAL,
|
||||
MENU_VIDEO_DRIVER_DIRECT3D8,
|
||||
MENU_VIDEO_DRIVER_DIRECT3D9,
|
||||
MENU_VIDEO_DRIVER_DIRECT3D10,
|
||||
@ -801,6 +802,7 @@ extern uintptr_t menu_display_white_texture;
|
||||
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_gl;
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_vulkan;
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_metal;
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_d3d8;
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_d3d9;
|
||||
extern menu_display_ctx_driver_t menu_display_ctx_d3d10;
|
||||
|
@ -1641,6 +1641,7 @@ enum msg_hash_enums
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_SDL_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_SDL2_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_VULKAN_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_METAL_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENGL_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENGLES_SUPPORT,
|
||||
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_THREADING_SUPPORT,
|
||||
|
@ -16,10 +16,12 @@ LIBRARY_SEARCH_PATHS[sdk=macosx*] = $(inherited) $(VULKAN_FRAMEWORK_PATH)
|
||||
|
||||
// OTHER_LDFLAGS = $(inherited) -lMoltenVK -framework MoltenVK
|
||||
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_RUNAHEAD -DHAVE_GRIFFIN -DHAVE_FLAC -DHAVE_DR_FLAC -DHAVE_DR_MP3 -DHAVE_LROUND -DFLAC__HAS_OGG=0 -DHAVE_CHD -DHAVE_STB_VORBIS -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DHAVE_UPDATE_ASSETS -DHAVE_LANGEXTRA -DHAVE_CHEEVOS -DHAVE_IMAGEVIEWER -DHAVE_IOHIDMANAGER -DHAVE_CORETEXT -DHAVE_RGUI -DHAVE_MENU -DOSX -DHAVE_OPENGL -DHAVE_CC_RESAMPLER -DHAVE_GLSL -DINLINE=inline -D__LIBRETRO__ -DHAVE_COREAUDIO -DHAVE_DYNAMIC -DHAVE_OVERLAY -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_COCOA -DHAVE_MAIN -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_DYLIB -DHAVE_7ZIP -DHAVE_MATERIALUI -DHAVE_HID -DHAVE_XMB -DHAVE_SEGA -DHAVE_SHADERPIPELINE -DHAVE_MMAP -DHAVE_LIBRETRODB -DHAVE_GETOPT_LONG -DHAVE_VULKAN -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_SPIRV_CROSS -DWANT_GLSLANG -DGLSLANG_OSINCLUDE_UNIX
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_RUNAHEAD -DHAVE_GRIFFIN -DHAVE_FLAC -DHAVE_DR_FLAC -DHAVE_DR_MP3 -DHAVE_LROUND -DFLAC__HAS_OGG=0 -DHAVE_CHD -DHAVE_STB_VORBIS -DHAVE_MINIUPNPC -DHAVE_BUILTINMINIUPNPC -DHAVE_UPDATE_ASSETS -DHAVE_LANGEXTRA -DHAVE_CHEEVOS -DHAVE_IMAGEVIEWER -DHAVE_IOHIDMANAGER -DHAVE_CORETEXT -DHAVE_RGUI -DHAVE_MENU -DOSX -DHAVE_OPENGL -DHAVE_CC_RESAMPLER -DHAVE_GLSL -DINLINE=inline -D__LIBRETRO__ -DHAVE_COREAUDIO -DHAVE_DYNAMIC -DHAVE_OVERLAY -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_COCOA -DHAVE_MAIN -DHAVE_NETWORKGAMEPAD -DHAVE_NETWORKING -DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_DYLIB -DHAVE_7ZIP -DHAVE_MATERIALUI -DHAVE_HID -DHAVE_XMB -DHAVE_SEGA -DHAVE_SHADERPIPELINE -DHAVE_MMAP -DHAVE_LIBRETRODB -DHAVE_GETOPT_LONG -DHAVE_METAL -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_SPIRV_CROSS -DWANT_GLSLANG -DENABLE_HLSL -DGLSLANG_OSINCLUDE_UNIX
|
||||
|
||||
SRCBASE = $(SRCROOT)/../..
|
||||
DEPS_DIR = $(SRCBASE)/deps
|
||||
HEADER_SEARCH_PATHS = $(inherited) $(SRCBASE) $(SRCBASE)/gfx/include $(SRCBASE)/libretro-common/include $(DEPS_DIR)/libFLAC/include $(DEPS_DIR)/7zip $(DEPS_DIR)/stb $(DEPS_DIR) $(DEPS_DIR)/SPIRV-Cross $(DEPS_DIR)/glslang $(DEPS_DIR)/glslang/glslang/glslang/Public $(DEPS_DIR)/glslang/glslang/glslang/MachineIndependent $(DEPS_DIR)/glslang/glslang/SPIRV $(DEPS_DIR)/glslang/glslang/glslang/OSDependent/Unix
|
||||
CLANG_CXX_LANGUAGE_STANDARD=c++11
|
||||
LD_RUNPATH_SEARCH_PATHS = @executable_path
|
||||
CLANG_ENABLE_OBJC_ARC=YES
|
||||
//SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
|
||||
|
72
pkg/apple/IntelliJ.xml
Normal file
72
pkg/apple/IntelliJ.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<code_scheme name="Project" version="173">
|
||||
<Objective-C>
|
||||
<option name="INDENT_NAMESPACE_MEMBERS" value="3" />
|
||||
<option name="INDENT_C_STRUCT_MEMBERS" value="3" />
|
||||
<option name="INDENT_CLASS_MEMBERS" value="3" />
|
||||
<option name="INDENT_INSIDE_CODE_BLOCK" value="3" />
|
||||
<option name="FUNCTION_BRACE_PLACEMENT" value="2" />
|
||||
<option name="FUNCTION_NON_TOP_AFTER_RETURN_TYPE_WRAP" value="1" />
|
||||
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="1" />
|
||||
<option name="FUNCTION_PARAMETERS_WRAP" value="1" />
|
||||
<option name="FUNCTION_PARAMETERS_ALIGN_MULTILINE" value="true" />
|
||||
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="1" />
|
||||
<option name="FUNCTION_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
|
||||
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="true" />
|
||||
<option name="SPACE_BEFORE_PROPERTY_ATTRIBUTES_PARENTHESES" value="true" />
|
||||
<option name="SPACE_BEFORE_CATEGORY_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_PROTOCOLS_BRACKETS" value="false" />
|
||||
<option name="SPACE_BETWEEN_ADJACENT_BRACKETS" value="false" />
|
||||
</Objective-C>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="ClassPredef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Class" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="mm" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ASSIGNMENT" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
|
||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="false" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="5" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="3" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="3" />
|
||||
<option name="TAB_SIZE" value="3" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
@ -22,7 +22,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>retroarch</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>libretro.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
7
pkg/apple/RetroArch.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
pkg/apple/RetroArch.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "container:RetroArch_Metal.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>6707FA08-D738-4E96-838A-767375E3B767</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>RetroArch</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>66A80EF91DB414BA5E941825F216CCE8F40CCCF2</key>
|
||||
<string>https://github.com/libretro/common-shaders.git</string>
|
||||
<key>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</key>
|
||||
<string>https://github.com/libretro/retroarch-joypad-autoconfig.git</string>
|
||||
<key>76200F0D6584D865E96F58DE862E738E88B23A3C</key>
|
||||
<string>https://github.com/libretro/libretro-super.git</string>
|
||||
<key>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</key>
|
||||
<string>https://github.com/libretro/retroarch-assets.git</string>
|
||||
<key>C3AEE01BDA902108663DB5DB9CD7916436919463</key>
|
||||
<string>https://github.com/libretro/libretro-database.git</string>
|
||||
<key>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</key>
|
||||
<string>https://github.com/libretro/RetroArch.git</string>
|
||||
<key>EF363D58F01B3FB341FA6C851870E60E4F080E97</key>
|
||||
<string>https://github.com/libretro/common-overlays.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>apple/RetroArch.xcodeproj</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>66A80EF91DB414BA5E941825F216CCE8F40CCCF2</key>
|
||||
<string>../../../media/shaders_cg</string>
|
||||
<key>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</key>
|
||||
<string>../../../media/autoconfig</string>
|
||||
<key>76200F0D6584D865E96F58DE862E738E88B23A3C</key>
|
||||
<string>../../../..</string>
|
||||
<key>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</key>
|
||||
<string>../../../media/assets</string>
|
||||
<key>C3AEE01BDA902108663DB5DB9CD7916436919463</key>
|
||||
<string>../../../media/libretrodb</string>
|
||||
<key>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</key>
|
||||
<string>../../..</string>
|
||||
<key>EF363D58F01B3FB341FA6C851870E60E4F080E97</key>
|
||||
<string>../../../media/overlays</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/libretro/RetroArch.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>76200F0D6584D865E96F58DE862E738E88B23A3C</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>assets</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>autoconfig</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>C3AEE01BDA902108663DB5DB9CD7916436919463</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>libretrodb</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>EF363D58F01B3FB341FA6C851870E60E4F080E97</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>overlays</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>retroarch</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>66A80EF91DB414BA5E941825F216CCE8F40CCCF2</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>shaders_cg</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
@ -389,6 +389,7 @@ static void retroarch_print_features(void)
|
||||
_PSUPP(thread, "Threads", "Threading support");
|
||||
|
||||
_PSUPP(vulkan, "Vulkan", "Vulkan video driver");
|
||||
_PSUPP(metal, "Metal", "Metal video driver");
|
||||
_PSUPP(opengl, "OpenGL", "OpenGL video driver support");
|
||||
_PSUPP(opengles, "OpenGL ES", "OpenGLES video driver support");
|
||||
_PSUPP(xvideo, "XVideo", "Video driver");
|
||||
|
@ -28,6 +28,46 @@
|
||||
#include <CoreLocation/CoreLocation.h>
|
||||
#endif
|
||||
|
||||
typedef enum apple_view_type {
|
||||
APPLE_VIEW_TYPE_NONE,
|
||||
APPLE_VIEW_TYPE_OPENGL_ES,
|
||||
APPLE_VIEW_TYPE_OPENGL,
|
||||
APPLE_VIEW_TYPE_VULKAN,
|
||||
APPLE_VIEW_TYPE_METAL,
|
||||
} apple_view_type_t;
|
||||
|
||||
@protocol PlatformDelegate
|
||||
@optional
|
||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
||||
@end
|
||||
|
||||
@protocol ApplePlatform
|
||||
|
||||
@property (readwrite) id<PlatformDelegate> delegate;
|
||||
|
||||
/*!
|
||||
@brief viewHandle returns an appropriate handle for the current view type
|
||||
*/
|
||||
@property (readonly) id viewHandle;
|
||||
|
||||
/*! @brief renderView returns the current render view based on the viewType */
|
||||
@property (readonly) id renderView;
|
||||
|
||||
/*! @brief isActive returns true if the application has focus */
|
||||
@property (readonly) bool hasFocus;
|
||||
|
||||
@property (readwrite) apple_view_type_t viewType;
|
||||
|
||||
/*! @brief setVideoMode adjusts the video display to the specified mode */
|
||||
- (void)setVideoMode:(gfx_ctx_mode_t)mode;
|
||||
|
||||
/*! @brief setCursorVisible specifies whether the cursor is visible */
|
||||
- (void)setCursorVisible:(bool)v;
|
||||
|
||||
@end
|
||||
|
||||
extern id<ApplePlatform> apple_platform;
|
||||
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
@ -66,7 +106,7 @@ AVCaptureAudioDataOutputSampleBufferDelegate>
|
||||
@end
|
||||
|
||||
@interface RetroArch_iOS : UINavigationController<UIApplicationDelegate,
|
||||
UINavigationControllerDelegate>
|
||||
UINavigationControllerDelegate, ApplePlatform>
|
||||
|
||||
@property (nonatomic) UIWindow* window;
|
||||
@property (nonatomic) NSString* documentsDirectory;
|
||||
@ -101,8 +141,6 @@ void get_ios_version(int *major, int *minor);
|
||||
|
||||
@end
|
||||
|
||||
CocoaView* recreate_cocoa_view();
|
||||
|
||||
#endif
|
||||
|
||||
#define BOXSTRING(x) [NSString stringWithUTF8String:x]
|
||||
@ -110,4 +148,15 @@ CocoaView* recreate_cocoa_view();
|
||||
#define BOXUINT(x) [NSNumber numberWithUnsignedInt:x]
|
||||
#define BOXFLOAT(x) [NSNumber numberWithDouble:x]
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#define RELEASE(x) x = nil
|
||||
#define BRIDGE __bridge
|
||||
#define UNSAFE_UNRETAINED __unsafe_unretained
|
||||
#else
|
||||
#define RELEASE(x) [x release]; \
|
||||
x = nil
|
||||
#define BRIDGE
|
||||
#define UNSAFE_UNRETAINED
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -51,19 +51,7 @@ static CocoaView* g_instance;
|
||||
#if defined(HAVE_COCOA)
|
||||
void *nsview_get_ptr(void)
|
||||
{
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
CocoaView* recreate_cocoa_view()
|
||||
{
|
||||
NSWindow* window = g_instance.window;
|
||||
[g_instance removeFromSuperview];
|
||||
g_instance = nil;
|
||||
[[CocoaView get] setFrame: [[window contentView] bounds]];
|
||||
[[window contentView] setAutoresizesSubviews:YES];
|
||||
[[window contentView] addSubview:[CocoaView get]];
|
||||
[window makeFirstResponder:[CocoaView get]];
|
||||
return [CocoaView get];
|
||||
return (BRIDGE void *)g_instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -81,28 +69,6 @@ void *glkitview_init(void);
|
||||
(void)apple;
|
||||
}
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
/** Indicates that the view wants to draw using the backing layer instead of using drawRect:. */
|
||||
-(BOOL) wantsUpdateLayer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/** Returns a Metal-compatible layer. */
|
||||
+(Class) layerClass
|
||||
{
|
||||
return [CAMetalLayer class];
|
||||
}
|
||||
|
||||
/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */
|
||||
-(CALayer*) makeBackingLayer
|
||||
{
|
||||
CALayer* layer = [self.class.layerClass layer];
|
||||
CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)];
|
||||
layer.contentsScale = MIN(viewScale.width, viewScale.height);
|
||||
return layer;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+ (CocoaView*)get
|
||||
@ -119,11 +85,6 @@ void *glkitview_init(void);
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
[self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
ui_window_cocoa_t cocoa_view;
|
||||
cocoa_view.data = (CocoaView*)self;
|
||||
//self.wantsLayer = YES;
|
||||
|
||||
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSColorPboardType, NSFilenamesPboardType, nil]];
|
||||
#elif defined(HAVE_COCOATOUCH)
|
||||
self.view = (__bridge GLKView*)glkitview_init();
|
||||
@ -139,6 +100,11 @@ void *glkitview_init(void);
|
||||
{
|
||||
[super setFrame:frameRect];
|
||||
|
||||
if (apple_platform.delegate != nil)
|
||||
{
|
||||
[apple_platform.delegate viewDidUpdateFrame:frameRect];
|
||||
}
|
||||
|
||||
cocoagl_gfx_ctx_update();
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,14 @@ static void ui_application_cocoa_process_events(void)
|
||||
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
if (!event)
|
||||
break;
|
||||
#if __has_feature(objc_arc)
|
||||
[NSApp sendEvent: event];
|
||||
|
||||
#else
|
||||
[event retain];
|
||||
[NSApp sendEvent: event];
|
||||
[event release];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,14 @@
|
||||
|
||||
#include "../../ui_companion_driver.h"
|
||||
|
||||
extern id apple_platform;
|
||||
|
||||
static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_state *state, enum ui_msg_window_type type)
|
||||
{
|
||||
NSInteger response;
|
||||
#if __has_feature(objc_arc)
|
||||
NSAlert* alert = [NSAlert new];
|
||||
#else
|
||||
NSAlert* alert = [[NSAlert new] autorelease];
|
||||
#endif
|
||||
|
||||
if (!string_is_empty(state->title))
|
||||
[alert setMessageText:BOXSTRING(state->title)];
|
||||
@ -72,7 +74,7 @@ static enum ui_msg_window_response ui_msg_window_cocoa_dialog(ui_msg_window_stat
|
||||
break;
|
||||
}
|
||||
|
||||
[alert beginSheetModalForWindow:ui_companion_driver_get_main_window()
|
||||
[alert beginSheetModalForWindow:(BRIDGE NSWindow *)ui_companion_driver_get_main_window()
|
||||
modalDelegate:apple_platform
|
||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
|
||||
contextInfo:nil];
|
||||
|
@ -32,15 +32,18 @@ static void* ui_window_cocoa_init(void)
|
||||
|
||||
static void ui_window_cocoa_destroy(void *data)
|
||||
{
|
||||
#if !__has_feature(objc_arc)
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
// TODO(sgc): incorrect behavior
|
||||
[[cocoa_view window] release];
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ui_window_cocoa_set_focused(void *data)
|
||||
{
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
CocoaView *cocoa_view = (BRIDGE CocoaView*)cocoa->data;
|
||||
[[cocoa_view window] makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
@ -48,7 +51,7 @@ static void ui_window_cocoa_set_visible(void *data,
|
||||
bool set_visible)
|
||||
{
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
CocoaView *cocoa_view = (BRIDGE CocoaView*)cocoa->data;
|
||||
if (set_visible)
|
||||
[[cocoa_view window] makeKeyAndOrderFront:nil];
|
||||
else
|
||||
@ -58,7 +61,7 @@ static void ui_window_cocoa_set_visible(void *data,
|
||||
static void ui_window_cocoa_set_title(void *data, char *buf)
|
||||
{
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
CocoaView *cocoa_view = (BRIDGE CocoaView*)cocoa->data;
|
||||
const char* const text = buf; /* < Can't access buffer directly in the block */
|
||||
[[cocoa_view window] setTitle:[NSString stringWithCString:text encoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
@ -66,7 +69,7 @@ static void ui_window_cocoa_set_title(void *data, char *buf)
|
||||
static void ui_window_cocoa_set_droppable(void *data, bool droppable)
|
||||
{
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
CocoaView *cocoa_view = (BRIDGE CocoaView*)cocoa->data;
|
||||
|
||||
if (droppable)
|
||||
{
|
||||
@ -81,7 +84,7 @@ static void ui_window_cocoa_set_droppable(void *data, bool droppable)
|
||||
static bool ui_window_cocoa_focused(void *data)
|
||||
{
|
||||
ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data;
|
||||
CocoaView *cocoa_view = (CocoaView*)cocoa->data;
|
||||
CocoaView *cocoa_view = (BRIDGE CocoaView*)cocoa->data;
|
||||
if ([[cocoa_view window] isMainWindow] == YES)
|
||||
return true;
|
||||
return false;
|
||||
|
@ -36,7 +36,7 @@ typedef struct ui_application_cocoa
|
||||
|
||||
typedef struct ui_window_cocoa
|
||||
{
|
||||
CocoaView *data;
|
||||
void *data;
|
||||
} ui_window_cocoa_t;
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -37,15 +37,23 @@
|
||||
#include "../../retroarch.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
|
||||
id apple_platform;
|
||||
#if HAVE_METAL
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#endif
|
||||
|
||||
id<ApplePlatform> apple_platform;
|
||||
|
||||
#if (defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))
|
||||
@interface RetroArch_OSX : NSObject
|
||||
@interface RetroArch_OSX : NSObject <ApplePlatform>
|
||||
#else
|
||||
@interface RetroArch_OSX : NSObject <NSApplicationDelegate>
|
||||
@interface RetroArch_OSX : NSObject <ApplePlatform, NSApplicationDelegate>
|
||||
#endif
|
||||
{
|
||||
NSWindow* _window;
|
||||
NSWindow* _window;
|
||||
apple_view_type_t _vt;
|
||||
NSView* _renderView;
|
||||
id<PlatformDelegate> _delegate;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSWindow IBOutlet* window;
|
||||
@ -134,11 +142,11 @@ static void app_terminate(void)
|
||||
apple->mouse_rel_y = event.deltaY;
|
||||
|
||||
/* Absolute */
|
||||
pos = [[CocoaView get] convertPoint:[event locationInWindow] fromView:nil];
|
||||
pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil];
|
||||
apple->touches[0].screen_x = pos.x;
|
||||
apple->touches[0].screen_y = pos.y;
|
||||
|
||||
mouse_pos = [[CocoaView get] convertPoint:[event locationInWindow] fromView:nil];
|
||||
mouse_pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil];
|
||||
apple->window_pos_x = (int16_t)mouse_pos.x;
|
||||
apple->window_pos_y = (int16_t)mouse_pos.y;
|
||||
}
|
||||
@ -150,7 +158,7 @@ static void app_terminate(void)
|
||||
case NSRightMouseDown:
|
||||
case NSOtherMouseDown:
|
||||
{
|
||||
NSPoint pos = [[CocoaView get] convertPoint:[event locationInWindow] fromView:nil];
|
||||
NSPoint pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil];
|
||||
apple = (cocoa_input_data_t*)input_driver_get_data();
|
||||
if (!apple || pos.y < 0)
|
||||
return;
|
||||
@ -163,7 +171,7 @@ static void app_terminate(void)
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseUp:
|
||||
{
|
||||
NSPoint pos = [[CocoaView get] convertPoint:[event locationInWindow] fromView:nil];
|
||||
NSPoint pos = [apple_platform.renderView convertPoint:[event locationInWindow] fromView:nil];
|
||||
apple = (cocoa_input_data_t*)input_driver_get_data();
|
||||
if (!apple || pos.y < 0)
|
||||
return;
|
||||
@ -183,33 +191,35 @@ static char** waiting_argv;
|
||||
|
||||
@synthesize window = _window;
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
- (void)dealloc
|
||||
{
|
||||
[_window release];
|
||||
[super dealloc];
|
||||
}
|
||||
#endif
|
||||
|
||||
#define NS_WINDOW_COLLECTION_BEHAVIOR_FULLSCREEN_PRIMARY (1 << 17)
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
unsigned i;
|
||||
apple_platform = self;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
|
||||
self.window.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary;
|
||||
#else
|
||||
SEL selector = NSSelectorFromString(BOXSTRING("setCollectionBehavior:"));
|
||||
SEL fsselector = NSSelectorFromString(BOXSTRING("toggleFullScreen:"));
|
||||
apple_platform = self;
|
||||
|
||||
if ([self.window respondsToSelector:selector])
|
||||
{
|
||||
if ([self.window respondsToSelector:fsselector])
|
||||
[self.window setCollectionBehavior:NS_WINDOW_COLLECTION_BEHAVIOR_FULLSCREEN_PRIMARY];
|
||||
}
|
||||
|
||||
#endif
|
||||
[self.window setAcceptsMouseMovedEvents: YES];
|
||||
|
||||
[[CocoaView get] setFrame: [[self.window contentView] bounds]];
|
||||
[[self.window contentView] setAutoresizesSubviews:YES];
|
||||
[[self.window contentView] addSubview:[CocoaView get]];
|
||||
[self.window makeFirstResponder:[CocoaView get]];
|
||||
|
||||
for (i = 0; i < waiting_argc; i++)
|
||||
{
|
||||
@ -228,6 +238,87 @@ static char** waiting_argv;
|
||||
[self performSelectorOnMainThread:@selector(rarch_main) withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)setViewType:(apple_view_type_t)vt {
|
||||
if (vt == _vt) {
|
||||
return;
|
||||
}
|
||||
|
||||
RARCH_LOG("[Cocoa] change view type: %d → %d\n", _vt, vt);
|
||||
|
||||
_vt = vt;
|
||||
if (_renderView != nil)
|
||||
{
|
||||
_renderView.wantsLayer = NO;
|
||||
_renderView.layer = nil;
|
||||
[_renderView removeFromSuperview];
|
||||
_renderView = nil;
|
||||
}
|
||||
|
||||
switch (vt) {
|
||||
case APPLE_VIEW_TYPE_VULKAN:
|
||||
case APPLE_VIEW_TYPE_METAL:
|
||||
{
|
||||
NSView *v = [CocoaView get];
|
||||
v.wantsLayer = YES;
|
||||
v.layer = CAMetalLayer.layer;
|
||||
_renderView = v;
|
||||
break;
|
||||
}
|
||||
|
||||
case APPLE_VIEW_TYPE_OPENGL:
|
||||
{
|
||||
_renderView = [CocoaView get];
|
||||
break;
|
||||
}
|
||||
|
||||
case APPLE_VIEW_TYPE_NONE:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
_renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||
_renderView.frame = self.window.contentView.bounds;
|
||||
|
||||
[self.window.contentView addSubview:_renderView];
|
||||
[self.window makeFirstResponder:_renderView];
|
||||
}
|
||||
|
||||
- (apple_view_type_t)viewType {
|
||||
return _vt;
|
||||
}
|
||||
|
||||
- (id)renderView {
|
||||
return _renderView;
|
||||
}
|
||||
|
||||
- (id)delegate {
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<PlatformDelegate>)delegate {
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
- (id)viewHandle {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (bool)hasFocus {
|
||||
return [NSApp isActive];
|
||||
}
|
||||
|
||||
- (void)setVideoMode:(gfx_ctx_mode_t)mode {
|
||||
// TODO(sgc): handle full screen
|
||||
[self.window setContentSize:NSMakeSize(mode.width, mode.height)];
|
||||
}
|
||||
|
||||
- (void)setCursorVisible:(bool)v {
|
||||
if (v)
|
||||
[NSCursor unhide];
|
||||
else
|
||||
[NSCursor hide];
|
||||
}
|
||||
|
||||
- (void) rarch_main
|
||||
{
|
||||
do
|
||||
@ -569,7 +660,7 @@ static void ui_companion_cocoa_notify_list_pushed(void *data,
|
||||
|
||||
static void *ui_companion_cocoa_get_main_window(void *data)
|
||||
{
|
||||
return ((RetroArch_OSX*)[[NSApplication sharedApplication] delegate]).window;
|
||||
return (BRIDGE void *)((RetroArch_OSX*)[[NSApplication sharedApplication] delegate]).window;
|
||||
}
|
||||
|
||||
ui_companion_driver_t ui_companion_cocoa = {
|
||||
|
@ -41,7 +41,7 @@
|
||||
#endif
|
||||
|
||||
static char msg_old[PATH_MAX_LENGTH];
|
||||
static id apple_platform;
|
||||
static id<ApplePlatform> apple_platform;
|
||||
static CFRunLoopObserverRef iterate_observer;
|
||||
|
||||
/* forward declaration */
|
||||
|
Loading…
x
Reference in New Issue
Block a user