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:
Stuart Carnie 2018-06-20 21:29:53 -07:00
parent b10f1faf7a
commit 411bcf8bdd
66 changed files with 12506 additions and 199 deletions

View File

@ -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

View File

@ -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",
};

View 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

View 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
View 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
View 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

View 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"

View 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

View 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

View 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

View 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));
}

View 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
View 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

View 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 */

View 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];
}

View 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 */

View 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);
}

View 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

View 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
View 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
View 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

View 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
View 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
View 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

View File

@ -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;

View File

@ -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
View 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,
};

View File

@ -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)
{

View 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
};

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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,

View 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
};

View File

@ -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),

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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
View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "container:RetroArch_Metal.xcodeproj">
</FileRef>
</Workspace>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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");

View File

@ -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

View File

@ -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();
}

View File

@ -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
}
}

View File

@ -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];

View File

@ -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;

View File

@ -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

View File

@ -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 = {

View File

@ -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 */