fix(Metal): improve shader stability

* use MTKView, which handles layer and scaling changes automatically
  between displays
This commit is contained in:
Stuart Carnie 2018-06-23 13:32:46 -07:00
parent ee8d82dcfe
commit eacd52f009
12 changed files with 268 additions and 142 deletions

View File

@ -12,7 +12,6 @@
#import "Context.h" #import "Context.h"
#import "PixelConverter.h" #import "PixelConverter.h"
@class ViewDescriptor;
@protocol View; @protocol View;
@interface Renderer : NSObject @interface Renderer : NSObject

View File

@ -113,9 +113,7 @@
{ {
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
// Cornflower Blue #58BAF9 rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
//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; rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
_t_rpd = rpd; _t_rpd = rpd;
} }
@ -163,49 +161,60 @@
for (id<View> v in _views) { for (id<View> v in _views) {
if (!v.visible) continue; if (!v.visible) continue;
if ([v respondsToSelector:@selector(prepareFrame:)]) { if ([v respondsToSelector:@selector(drawWithContext:)]) {
[v prepareFrame:_context]; [v drawWithContext:_context];
} }
} }
id<CAMetalDrawable> drawable = _context.nextDrawable; BOOL pendingDraws = NO;
_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) { for (id<View> v in _views) {
if (!v.visible || if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) {
![v respondsToSelector:@selector(drawWithEncoder:)]) { pendingDraws = YES;
continue; break;
} }
// 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]; if (pendingDraws) {
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:)] ||
(v.drawState & ViewDrawStateEncoder) == 0) {
continue;
}
// set view state
if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) {
[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; __block dispatch_semaphore_t inflight = _inflightSemaphore;
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) { [cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
dispatch_semaphore_signal(inflight); dispatch_semaphore_signal(inflight);
}]; }];
[cb presentDrawable:drawable]; [cb presentDrawable:_context.nextDrawable];
[_context end]; [_context end];
} }

View File

@ -11,33 +11,34 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
// TODO(sgc): implement triple buffering
/*! @brief maximum inflight frames */ /*! @brief maximum inflight frames */
#define MAX_INFLIGHT 3 #define MAX_INFLIGHT 1
#pragma mark - Pixel Formats #pragma mark - Pixel Formats
typedef NS_ENUM(NSUInteger, RPixelFormat) { typedef NS_ENUM(NSUInteger, RPixelFormat) {
RPixelFormatInvalid, RPixelFormatInvalid,
/* 16-bit formats */ /* 16-bit formats */
RPixelFormatBGRA4Unorm, RPixelFormatBGRA4Unorm,
RPixelFormatB5G6R5Unorm, RPixelFormatB5G6R5Unorm,
RPixelFormatBGRA8Unorm, RPixelFormatBGRA8Unorm,
RPixelFormatBGRX8Unorm, RPixelFormatBGRX8Unorm, // RetroArch XRGB
RPixelFormatCount, RPixelFormatCount,
}; };
extern NSUInteger RPixelFormatToBPP(RPixelFormat format); extern NSUInteger RPixelFormatToBPP(RPixelFormat format);
extern NSString *NSStringFromRPixelFormat(RPixelFormat format); extern NSString *NSStringFromRPixelFormat(RPixelFormat format);
typedef NS_ENUM(NSUInteger, RTextureFilter) { typedef NS_ENUM(NSUInteger, RTextureFilter) {
RTextureFilterNearest, RTextureFilterNearest,
RTextureFilterLinear, RTextureFilterLinear,
RTextureFilterCount, RTextureFilterCount,
}; };
#endif /* RendererCommon_h */ #endif /* RendererCommon_h */

View File

@ -13,11 +13,12 @@
@property (readwrite) BOOL visible; @property (readwrite) BOOL visible;
@property (readwrite) CGRect frame; @property (readwrite) CGRect frame;
@property (readwrite) CGSize size; @property (readwrite) CGSize size;
@property (readonly) ViewDrawState drawState;
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; - (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
- (void)prepareFrame:(Context *)ctx; - (void)drawWithContext:(Context *)ctx;
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce; - (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
@end @end

View File

@ -35,6 +35,11 @@
_filter = d.filter; _filter = d.filter;
_context = r.context; _context = r.context;
_visible = YES; _visible = YES;
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
_drawState = ViewDrawStateEncoder;
} else {
_drawState = ViewDrawStateAll;
}
self.size = d.size; self.size = d.size;
self.frame = CGRectMake(0, 0, 1, 1); self.frame = CGRectMake(0, 0, 1, 1);
} }
@ -113,7 +118,7 @@
_pixelsDirty = NO; _pixelsDirty = NO;
} }
- (void)prepareFrame:(Context *)ctx { - (void)drawWithContext:(Context *)ctx {
[self _convertFormat]; [self _convertFormat];
} }

View File

@ -10,6 +10,15 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <Metal/Metal.h> #import <Metal/Metal.h>
typedef NS_ENUM(NSInteger, ViewDrawState)
{
ViewDrawStateNone = 0x00,
ViewDrawStateContext = 0x01,
ViewDrawStateEncoder = 0x02,
ViewDrawStateAll = 0x03,
};
@protocol View<NSObject> @protocol View<NSObject>
@property (readonly) RPixelFormat format; @property (readonly) RPixelFormat format;
@ -17,9 +26,10 @@
@property (readwrite) BOOL visible; @property (readwrite) BOOL visible;
@property (readwrite) CGRect frame; @property (readwrite) CGRect frame;
@property (readwrite) CGSize size; @property (readwrite) CGSize size;
@property (readonly) ViewDrawState drawState;
@optional @optional
- (void)prepareFrame:(Context *)ctx; - (void)drawWithContext:(Context *)ctx;
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce; - (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
@end @end

View File

@ -9,6 +9,7 @@
#define METAL_COMMON_H__ #define METAL_COMMON_H__
#import <Metal/Metal.h> #import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import "metal/metal_common.h" #import "metal/metal_common.h"
#include <retro_common_api.h> #include <retro_common_api.h>
@ -20,6 +21,9 @@
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
extern MTLPixelFormat glslang_format_to_metal(glslang_format fmt);
extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
#pragma mark - Classes #pragma mark - Classes
@interface FrameView : NSObject<View> @interface FrameView : NSObject<View>
@ -29,6 +33,7 @@ RETRO_BEGIN_DECLS
@property (readwrite) BOOL visible; @property (readwrite) BOOL visible;
@property (readwrite) CGRect frame; @property (readwrite) CGRect frame;
@property (readwrite) CGSize size; @property (readwrite) CGSize size;
@property (readonly) ViewDrawState drawState;
@property (readonly) struct video_shader* shader; @property (readonly) struct video_shader* shader;
@property (readwrite) uint64_t frameCount; @property (readwrite) uint64_t frameCount;
@ -53,7 +58,7 @@ RETRO_BEGIN_DECLS
filter:(RTextureFilter)filter; filter:(RTextureFilter)filter;
@end @end
@interface MetalDriver : NSObject<PlatformDelegate> @interface MetalDriver : NSObject<MTKViewDelegate>
@property (readonly) video_viewport_t* viewport; @property (readonly) video_viewport_t* viewport;
@property (readwrite) bool keepAspect; @property (readwrite) bool keepAspect;
@ -71,11 +76,6 @@ RETRO_BEGIN_DECLS
/*! @brief setNeedsResize triggers a display resize */ /*! @brief setNeedsResize triggers a display resize */
- (void)setNeedsResize; - (void)setNeedsResize;
- (void)viewDidUpdateFrame:(NSRect)rect;
#pragma mark - Menu APIs
@end @end
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -32,7 +32,7 @@
@property (readwrite) video_viewport_t *viewport; @property (readwrite) video_viewport_t *viewport;
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; - (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
- (void)prepareFrame:(Context *)ctx; - (void)drawWithContext:(Context *)ctx;
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce; - (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
@end @end
@ -72,32 +72,19 @@
} }
} }
#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 #pragma mark - video
- (void)setVideo:(const video_info_t *)video - (void)setVideo:(const video_info_t *)video
{ {
_video = *video; _video = *video;
_viewport->full_width = _video.width;
_viewport->full_height = _video.height;
if (!_renderer) { if (!_renderer) {
id<MTLDevice> device = MTLCreateSystemDefaultDevice(); id<MTLDevice> device = MTLCreateSystemDefaultDevice();
_device = device; _device = device;
NSView *view = (NSView *)apple_platform.renderView; MetalView *view = (MetalView *)apple_platform.renderView;
view.device = device;
CAMetalLayer *layer = (CAMetalLayer *)view.layer; CAMetalLayer *layer = (CAMetalLayer *)view.layer;
layer.device = device; //layer.device = device;
_renderer = [[Renderer alloc] initWithDevice:device layer:layer]; _renderer = [[Renderer alloc] initWithDevice:device layer:layer];
_menu.renderer = _renderer; _menu.renderer = _renderer;
} }
@ -119,11 +106,6 @@
- (void)beginFrame - (void)beginFrame
{ {
if (resize_chain) {
[_renderer drawableSizeWillChange:CGSizeMake(_viewport->full_width, _viewport->full_height)];
resize_chain = NO;
}
video_driver_update_viewport(_viewport, NO, _keepAspect); video_driver_update_viewport(_viewport, NO, _keepAspect);
[_renderer beginFrame]; [_renderer beginFrame];
@ -139,6 +121,21 @@
// TODO(sgc): resize all drawables // TODO(sgc): resize all drawables
} }
#pragma mark - MTKViewDelegate
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
RARCH_LOG("[MetalDriver] drawableSizeWillChange: %s\n", NSStringFromSize(size).UTF8String);
_viewport->full_width = (unsigned int)size.width;
_viewport->full_height = (unsigned int)size.height;
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
[_renderer drawableSizeWillChange:size];
video_driver_update_viewport(_viewport, NO, _keepAspect);
}
- (void)drawInMTKView:(MTKView *)view {
}
extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom) extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom)
{ {
float near = 0; float near = 0;
@ -292,12 +289,18 @@ typedef struct ALIGN(16)
_format = d.format; _format = d.format;
_bpp = RPixelFormatToBPP(_format); _bpp = RPixelFormatToBPP(_format);
_filter = d.filter; _filter = d.filter;
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
_drawState = ViewDrawStateEncoder;
} else {
_drawState = ViewDrawStateAll;
}
_visible = YES; _visible = YES;
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1); _engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
[self _initSamplers]; [self _initSamplers];
self.size = d.size; self.size = d.size;
self.frame = CGRectMake(0, 0, 1, 1); self.frame = CGRectMake(0, 0, 1, 1);
resize_render_targets = YES;
} }
return self; return self;
} }
@ -425,7 +428,6 @@ typedef struct ALIGN(16)
if (init_history) if (init_history)
[self _initHistory]; [self _initHistory];
else { else {
// TODO(sgc): change to ring buffer?
int k; int k;
/* todo: what about frame-duping ? /* todo: what about frame-duping ?
* maybe clone d3d10_texture_t with AddRef */ * maybe clone d3d10_texture_t with AddRef */
@ -546,7 +548,7 @@ static vertex_t vertex_bytes[] = {
} }
} }
- (void)prepareFrame:(Context *)ctx - (void)drawWithContext:(Context *)ctx
{ {
_texture = _engine.frame.texture[0].view; _texture = _engine.frame.texture[0].view;
[self _convertFormat]; [self _convertFormat];
@ -567,15 +569,15 @@ static vertex_t vertex_bytes[] = {
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0); rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0);
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
rpd.colorAttachments[0].storeAction = MTLStoreActionStore; rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
BOOL firstPass = YES; BOOL firstPass = YES;
for (unsigned i = 0; i < _shader->passes; i++) { for (unsigned i = 0; i < _shader->passes; i++) {
BOOL lastPass = i == _shader->passes - 1; BOOL backBuffer = (_engine.pass[i].rt.view == nil);
if (lastPass) { if (backBuffer) {
rpd.colorAttachments[0].texture = _context.nextDrawable.texture; rpd.colorAttachments[0].texture = _context.nextDrawable.texture;
} }
else { else {
@ -630,7 +632,7 @@ static vertex_t vertex_bytes[] = {
texture_sem++; texture_sem++;
} }
if (lastPass) { if (backBuffer) {
[rce setViewport:_engine.frame.viewport]; [rce setViewport:_engine.frame.viewport];
} }
else { else {
@ -645,7 +647,11 @@ static vertex_t vertex_bytes[] = {
[rce endEncoding]; [rce endEncoding];
_texture = _engine.pass[i].rt.view; _texture = _engine.pass[i].rt.view;
} }
_texture = nil; if (_texture == nil) {
_drawState = ViewDrawStateContext;
} else {
_drawState = ViewDrawStateAll;
}
} }
- (void)_updateRenderTargets - (void)_updateRenderTargets
@ -712,15 +718,17 @@ static vertex_t vertex_bytes[] = {
} }
RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height); RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height);
MTLPixelFormat fmt = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format));
if (i != (_shader->passes - 1)) { if ((i != (_shader->passes - 1)) ||
(width != _viewport->width) || (height != _viewport->height) ||
fmt != MTLPixelFormatBGRA8Unorm)
{
_engine.pass[i].viewport.width = width; _engine.pass[i].viewport.width = width;
_engine.pass[i].viewport.height = height; _engine.pass[i].viewport.height = height;
_engine.pass[i].viewport.znear = 0.0; _engine.pass[i].viewport.znear = 0.0;
_engine.pass[i].viewport.zfar = 1.0; _engine.pass[i].viewport.zfar = 1.0;
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:fmt
width:width width:width
height:height height:height
mipmapped:false]; mipmapped:false];
@ -823,8 +831,16 @@ static vertex_t vertex_bytes[] = {
if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics)) if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics))
return NO; return NO;
#ifdef DEBUG
bool save_msl = true;
#else
bool save_msl = false;
#endif
NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex];
NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment];
// vertex descriptor
@try { @try {
// vertex descriptor
MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
vd.attributes[0].offset = offsetof(vertex_t, pos); vd.attributes[0].offset = offsetof(vertex_t, pos);
vd.attributes[0].format = MTLVertexFormatFloat4; vd.attributes[0].format = MTLVertexFormatFloat4;
@ -839,8 +855,11 @@ static vertex_t vertex_bytes[] = {
psd.label = [NSString stringWithFormat:@"pass %d", i]; psd.label = [NSString stringWithFormat:@"pass %d", i];
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
ca.pixelFormat = MTLPixelFormatBGRA8Unorm;
ca.blendingEnabled = YES; ca.pixelFormat = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format));
// TODO(sgc): confirm we never need blending for render passes
ca.blendingEnabled = NO;
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
@ -848,19 +867,18 @@ static vertex_t vertex_bytes[] = {
psd.sampleCount = 1; psd.sampleCount = 1;
psd.vertexDescriptor = vd; 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; NSError *err;
id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err]; id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err];
if (err != nil) { if (err != nil) {
if (lib == nil) { if (lib == nil) {
save_msl = true;
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String); RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
return NO; return NO;
} }
#if DEBUG
RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String); RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String);
#endif
} }
psd.vertexFunction = [lib newFunctionWithName:@"main0"]; psd.vertexFunction = [lib newFunctionWithName:@"main0"];
@ -868,16 +886,20 @@ static vertex_t vertex_bytes[] = {
lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err]; lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err];
if (err != nil) { if (err != nil) {
if (lib == nil) { if (lib == nil) {
save_msl = true;
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String); RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
return NO; return NO;
} }
#if DEBUG
RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String); RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String);
#endif
} }
psd.fragmentFunction = [lib newFunctionWithName:@"main0"]; psd.fragmentFunction = [lib newFunctionWithName:@"main0"];
STRUCT_ASSIGN(_engine.pass[i]._state, STRUCT_ASSIGN(_engine.pass[i]._state,
[_context.device newRenderPipelineStateWithDescriptor:psd error:&err]); [_context.device newRenderPipelineStateWithDescriptor:psd error:&err]);
if (err != nil) { if (err != nil) {
save_msl = true;
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String); RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String);
return NO; return NO;
} }
@ -892,6 +914,29 @@ static vertex_t vertex_bytes[] = {
STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf); STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf);
} }
} @finally { } @finally {
if (save_msl) {
RARCH_LOG("[Metal]: saving metal shader files\n");
NSError *err = nil;
NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension];
[vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"]
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:&err];
if (err != nil) {
RARCH_ERR("[Metal]: unable to save vertex shader source: %s\n", err.localizedDescription.UTF8String);
}
err = nil;
[fs_src writeToFile:[basePath stringByAppendingPathExtension:@"fs.metal"]
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:&err];
if (err != nil) {
RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", err.localizedDescription.UTF8String);
}
}
free(shader->pass[i].source.string.vertex); free(shader->pass[i].source.string.vertex);
free(shader->pass[i].source.string.fragment); free(shader->pass[i].source.string.fragment);
@ -945,3 +990,67 @@ static vertex_t vertex_bytes[] = {
} }
@end @end
MTLPixelFormat glslang_format_to_metal(glslang_format fmt)
{
#undef FMT2
#define FMT2(x,y) case SLANG_FORMAT_##x: return MTLPixelFormat##y
switch (fmt)
{
FMT2(R8_UNORM, R8Unorm);
FMT2(R8_SINT, R8Sint);
FMT2(R8_UINT, R8Uint);
FMT2(R8G8_UNORM, RG8Unorm);
FMT2(R8G8_SINT, RG8Sint);
FMT2(R8G8_UINT, RG8Uint);
FMT2(R8G8B8A8_UNORM, RGBA8Unorm);
FMT2(R8G8B8A8_SINT, RGBA8Sint);
FMT2(R8G8B8A8_UINT, RGBA8Uint);
FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB);
FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm);
FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint);
FMT2(R16_UINT, R16Uint);
FMT2(R16_SINT, R16Sint);
FMT2(R16_SFLOAT, R16Float);
FMT2(R16G16_UINT, RG16Uint);
FMT2(R16G16_SINT, RG16Sint);
FMT2(R16G16_SFLOAT, RG16Float);
FMT2(R16G16B16A16_UINT, RGBA16Uint);
FMT2(R16G16B16A16_SINT, RGBA16Sint);
FMT2(R16G16B16A16_SFLOAT, RGBA16Float);
FMT2(R32_UINT, R32Uint);
FMT2(R32_SINT, R32Sint);
FMT2(R32_SFLOAT, R32Float);
FMT2(R32G32_UINT, RG32Uint);
FMT2(R32G32_SINT, RG32Sint);
FMT2(R32G32_SFLOAT, RG32Float);
FMT2(R32G32B32A32_UINT, RGBA32Uint);
FMT2(R32G32B32A32_SINT, RGBA32Sint);
FMT2(R32G32B32A32_SFLOAT, RGBA32Float);
case SLANG_FORMAT_UNKNOWN:
default:
break;
}
#undef FMT2
return MTLPixelFormatInvalid;
}
MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt)
{
switch (fmt)
{
case MTLPixelFormatRGBA8Unorm:
return MTLPixelFormatBGRA8Unorm;
case MTLPixelFormatRGBA8Unorm_sRGB:
return MTLPixelFormatBGRA8Unorm_sRGB;
default:
return fmt;
}
}

View File

@ -53,13 +53,14 @@ static void *metal_init(const video_info_t *video,
gfx_ctx_mode_t mode; gfx_ctx_mode_t mode;
[apple_platform setViewType:APPLE_VIEW_TYPE_METAL]; [apple_platform setViewType:APPLE_VIEW_TYPE_METAL];
MetalDriver *md = [MetalDriver new]; MetalDriver *md = [MetalDriver new];
if (md == nil) { if (md == nil) {
return NULL; return NULL;
} }
MetalView *view = (MetalView *)apple_platform.renderView;
view.delegate = md;
apple_platform.delegate = md; md.keepAspect = video->force_aspect;
RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height); RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height);
@ -67,8 +68,8 @@ static void *metal_init(const video_info_t *video,
mode.height = video->height; mode.height = video->height;
mode.fullscreen = video->fullscreen; mode.fullscreen = video->fullscreen;
[apple_platform setVideoMode:mode];
[md setVideo:video]; [md setVideo:video];
[apple_platform setVideoMode:mode];
*input = NULL; *input = NULL;
*input_data = NULL; *input_data = NULL;

View File

@ -36,22 +36,16 @@ typedef enum apple_view_type {
APPLE_VIEW_TYPE_METAL, APPLE_VIEW_TYPE_METAL,
} apple_view_type_t; } apple_view_type_t;
@protocol PlatformDelegate
#ifdef HAVE_METAL #ifdef HAVE_METAL
@optional #import <MetalKit/MetalKit.h>
- (void)viewDidUpdateFrame:(NSRect)rect;
#endif @interface MetalView : MTKView
@end @end
#endif
@protocol ApplePlatform @protocol ApplePlatform
@property (readwrite,retain) 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 */ /*! @brief renderView returns the current render view based on the viewType */
@property (readonly) id renderView; @property (readonly) id renderView;

View File

@ -19,9 +19,6 @@
#include "cocoa_common.h" #include "cocoa_common.h"
#ifdef HAVE_COCOA #ifdef HAVE_COCOA
#include "../ui_cocoa.h" #include "../ui_cocoa.h"
#ifdef HAVE_VULKAN
#import <QuartzCore/CAMetalLayer.h>
#endif
#endif #endif
#include <retro_assert.h> #include <retro_assert.h>
@ -46,6 +43,21 @@
#include "../../../location/location_driver.h" #include "../../../location/location_driver.h"
#include "../../../camera/camera_driver.h" #include "../../../camera/camera_driver.h"
#ifdef HAVE_METAL
@implementation MetalView
- (void)keyDown:(NSEvent*)theEvent
{
}
/* Stop the annoying sound when pressing a key. */
- (BOOL)acceptsFirstResponder
{
return YES;
}
@end
#endif
static CocoaView* g_instance; static CocoaView* g_instance;
#if defined(HAVE_COCOA) #if defined(HAVE_COCOA)
@ -96,15 +108,13 @@ void *glkitview_init(void);
} }
#if defined(HAVE_COCOA) #if defined(HAVE_COCOA)
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window {
return YES;
}
- (void)setFrame:(NSRect)frameRect - (void)setFrame:(NSRect)frameRect
{ {
[super setFrame:frameRect]; [super setFrame:frameRect];
if (apple_platform.delegate != nil)
{
[apple_platform.delegate viewDidUpdateFrame:frameRect];
}
cocoagl_gfx_ctx_update(); cocoagl_gfx_ctx_update();
} }

View File

@ -53,7 +53,6 @@ id<ApplePlatform> apple_platform;
NSWindow* _window; NSWindow* _window;
apple_view_type_t _vt; apple_view_type_t _vt;
NSView* _renderView; NSView* _renderView;
id<PlatformDelegate> _delegate;
} }
@property (nonatomic, retain) NSWindow IBOutlet* window; @property (nonatomic, retain) NSWindow IBOutlet* window;
@ -259,9 +258,9 @@ static char** waiting_argv;
case APPLE_VIEW_TYPE_METAL: case APPLE_VIEW_TYPE_METAL:
#if defined(HAVE_METAL) || defined(HAVE_VULKAN) #if defined(HAVE_METAL) || defined(HAVE_VULKAN)
{ {
NSView *v = [CocoaView get]; MetalView *v = [MetalView new];
v.wantsLayer = YES; v.paused = YES;
v.layer = CAMetalLayer.layer; v.enableSetNeedsDisplay = NO;
_renderView = v; _renderView = v;
} }
#endif #endif
@ -280,7 +279,7 @@ static char** waiting_argv;
_renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; _renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
_renderView.frame = self.window.contentView.bounds; _renderView.frame = self.window.contentView.bounds;
[self.window.contentView addSubview:_renderView]; [self.window.contentView addSubview:_renderView];
[self.window makeFirstResponder:_renderView]; [self.window makeFirstResponder:_renderView];
} }
@ -293,18 +292,6 @@ static char** waiting_argv;
return _renderView; return _renderView;
} }
- (id)delegate {
return _delegate;
}
- (void)setDelegate:(id<PlatformDelegate>)delegate {
_delegate = delegate;
}
- (id)viewHandle {
return nil;
}
- (bool)hasFocus { - (bool)hasFocus {
return [NSApp isActive]; return [NSApp isActive];
} }