fix(Metal): Various Metal fixes

* OSD message background
* clear glui background
* add Metal to configuration
* added optional config to compile Metal build without OpenGL
* fixed fill_pathname_join_delim; if out_path and dir are same, don't
  strlcpy, as the results are undefined.
This commit is contained in:
Stuart Carnie 2018-07-12 21:33:18 -07:00
parent dbfccb25dd
commit 167ad3b57d
18 changed files with 424 additions and 261 deletions

View File

@ -125,6 +125,7 @@ enum video_driver_enum
{
VIDEO_GL = 0,
VIDEO_VULKAN,
VIDEO_METAL,
VIDEO_DRM,
VIDEO_XVIDEO,
VIDEO_SDL,
@ -291,6 +292,8 @@ enum midi_driver_enum
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(__CELLOS_LV2__)
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_GL;
#elif defined(HAVE_METAL)
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_METAL;
#elif defined(GEKKO)
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_WII;
#elif defined(WIIU)
@ -711,6 +714,8 @@ const char *config_get_default_video(void)
return "gl";
case VIDEO_VULKAN:
return "vulkan";
case VIDEO_METAL:
return "metal";
case VIDEO_DRM:
return "drm";
case VIDEO_WII:
@ -2357,6 +2362,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx)
settings_t *settings = config_get_ptr();
if (string_is_equal(settings->arrays.video_driver, "vulkan") ||
string_is_equal(settings->arrays.video_driver, "metal") ||
string_is_equal(settings->arrays.video_driver, "d3d11") ||
string_is_equal(settings->arrays.video_driver, "d3d12") ||
string_is_equal(settings->arrays.video_driver, "gx2"))

View File

@ -28,12 +28,14 @@ typedef struct
@property (nonatomic, readonly) id<MTLDevice> device;
@property (nonatomic, readonly) id<MTLLibrary> library;
@property (nonatomic, readwrite) MTLClearColor clearColor;
@property (nonatomic, readwrite) video_viewport_t *viewport;
@property (nonatomic, readonly) Uniforms *uniforms;
/*! @brief Specifies whether rendering is synchronized with the display */
@property (nonatomic, readwrite) bool displaySyncEnabled;
/*! @brief Returns the command buffer used for pre-render work,
* such as mip maps for applying filters
* such as mip maps and shader effects
* */
@property (nonatomic, readonly) id<MTLCommandBuffer> blitCommandBuffer;
@ -51,6 +53,14 @@ typedef struct
- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter;
- (id<MTLTexture>)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped;
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst;
- (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend;
/*! @brief resets the viewport for the main render encoder to the drawable size */
- (void)resetRenderViewport;
/*! @brief draws a quad at the specified position (normalized coordinates) using the main render encoder */
- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h
r:(float)r g:(float)g b:(float)b a:(float)a;
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length;

View File

@ -38,6 +38,7 @@
id<MTLCommandQueue> _commandQueue;
CAMetalLayer *_layer;
id<CAMetalDrawable> _drawable;
video_viewport_t _viewport;
id<MTLSamplerState> _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1];
Filter *_filters[RPixelFormatCount]; // convert to bgra8888
@ -49,6 +50,10 @@
NSUInteger _currentChain;
BufferChain *_chain[CHAIN_LENGTH];
MTLClearColor _clearColor;
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
id<MTLRenderPipelineState> _clearState;
Uniforms _uniforms;
}
- (instancetype)initWithDevice:(id<MTLDevice>)d
@ -64,6 +69,7 @@
_library = l;
_commandQueue = [_device newCommandQueue];
_clearColor = MTLClearColorMake(0, 0, 0, 1);
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
{
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
@ -89,7 +95,10 @@
if (![self _initConversionFilters])
return nil;
if (![self _initMainState])
if (![self _initClearState])
return nil;
if (![self _initMenuStates])
return nil;
for (int i = 0; i < CHAIN_LENGTH; i++)
@ -100,21 +109,241 @@
return self;
}
- (video_viewport_t *)viewport
{
return &_viewport;
}
- (void)setViewport:(video_viewport_t *)viewport
{
_viewport = *viewport;
_uniforms.outputSize = simd_make_float2(_viewport.full_width, _viewport.full_height);
}
- (Uniforms *)uniforms
{
return &_uniforms;
}
- (void)setDisplaySyncEnabled:(bool)displaySyncEnabled
{
_layer.displaySyncEnabled = displaySyncEnabled;
}
#pragma mark - shaders
- (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend
{
assert(index > 0 && index < GFX_MAX_SHADERS);
switch (index)
{
case VIDEO_SHADER_STOCK_BLEND:
case VIDEO_SHADER_MENU:
case VIDEO_SHADER_MENU_2:
case VIDEO_SHADER_MENU_3:
case VIDEO_SHADER_MENU_4:
case VIDEO_SHADER_MENU_5:
case VIDEO_SHADER_MENU_6:
break;
default:
index = VIDEO_SHADER_STOCK_BLEND;
break;
}
return _states[index][blend ? 1 : 0];
}
- (bool)displaySyncEnabled
{
return _layer.displaySyncEnabled;
}
- (bool)_initMainState
- (MTLVertexDescriptor *)_spriteVertexDescriptor
{
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
vd.attributes[0].offset = 0;
vd.attributes[0].format = MTLVertexFormatFloat2;
vd.attributes[1].offset = offsetof(SpriteVertex, texCoord);
vd.attributes[1].format = MTLVertexFormatFloat2;
vd.attributes[2].offset = offsetof(SpriteVertex, color);
vd.attributes[2].format = MTLVertexFormatFloat4;
vd.layouts[0].stride = sizeof(SpriteVertex);
return vd;
}
- (bool)_initClearState
{
MTLVertexDescriptor *vd = [self _spriteVertexDescriptor];
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
psd.label = @"clear_state";
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
ca.pixelFormat = _layer.pixelFormat;
psd.vertexDescriptor = vd;
psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment_color"];
NSError *err;
_clearState = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating clear pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
return YES;
}
- (bool)_initMenuStates
{
MTLVertexDescriptor *vd = [self _spriteVertexDescriptor];
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
psd.label = @"stock";
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
ca.pixelFormat = _layer.pixelFormat;
ca.blendingEnabled = NO;
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
psd.sampleCount = 1;
psd.vertexDescriptor = vd;
psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment"];
NSError *err;
_states[VIDEO_SHADER_STOCK_BLEND][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"stock_blend";
ca.blendingEnabled = YES;
_states[VIDEO_SHADER_STOCK_BLEND][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
MTLFunctionConstantValues *vals;
psd.label = @"snow_simple";
ca.blendingEnabled = YES;
{
vals = [MTLFunctionConstantValues new];
float values[3] = {
1.25f, // baseScale
0.50f, // density
0.15f, // speed
};
[vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"];
[vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"];
[vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"];
}
psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err];
_states[VIDEO_SHADER_MENU_3][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"snow";
ca.blendingEnabled = YES;
{
vals = [MTLFunctionConstantValues new];
float values[3] = {
3.50f, // baseScale
0.70f, // density
0.25f, // speed
};
[vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"];
[vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"];
[vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"];
}
psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err];
_states[VIDEO_SHADER_MENU_4][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"bokeh";
ca.blendingEnabled = YES;
psd.fragmentFunction = [_library newFunctionWithName:@"bokeh_fragment"];
_states[VIDEO_SHADER_MENU_5][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"snowflake";
ca.blendingEnabled = YES;
psd.fragmentFunction = [_library newFunctionWithName:@"snowflake_fragment"];
_states[VIDEO_SHADER_MENU_6][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon";
ca.blendingEnabled = NO;
psd.vertexFunction = [_library newFunctionWithName:@"ribbon_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_fragment"];
_states[VIDEO_SHADER_MENU][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_blend";
ca.blendingEnabled = YES;
ca.sourceRGBBlendFactor = MTLBlendFactorOne;
ca.destinationRGBBlendFactor = MTLBlendFactorOne;
_states[VIDEO_SHADER_MENU][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_simple";
ca.blendingEnabled = NO;
psd.vertexFunction = [_library newFunctionWithName:@"ribbon_simple_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_simple_fragment"];
_states[VIDEO_SHADER_MENU_2][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_simple_blend";
ca.blendingEnabled = YES;
ca.sourceRGBBlendFactor = MTLBlendFactorOne;
ca.destinationRGBBlendFactor = MTLBlendFactorOne;
_states[VIDEO_SHADER_MENU_2][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
return YES;
}
- (bool)_initConversionFilters
{
NSError *err = nil;
@ -255,6 +484,41 @@
return _rce;
}
- (void)resetRenderViewport
{
MTLViewport vp = {
.originX = 0,
.originY = 0,
.width = _viewport.full_width,
.height = _viewport.full_height,
.znear = 0,
.zfar = 1,
};
[self.rce setViewport:vp];
}
- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h
r:(float)r g:(float)g b:(float)b a:(float)a
{
SpriteVertex v[4];
v[0].position = simd_make_float2(x, y);
v[1].position = simd_make_float2(x + w, y);
v[2].position = simd_make_float2(x, y + h);
v[3].position = simd_make_float2(x + w, y + h);
simd_float4 color = simd_make_float4(r, g, b, a);
v[0].color = color;
v[1].color = color;
v[2].color = color;
v[3].color = color;
id<MTLRenderCommandEncoder> rce = self.rce;
[rce setRenderPipelineState:_clearState];
[rce setVertexBytes:&v length:sizeof(v) atIndex:BufferIndexPositions];
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
}
- (void)end
{
assert(_commandBuffer != nil);
@ -263,7 +527,7 @@
if (_blitCommandBuffer)
{
// pending blits for mipmaps
// pending blits for mipmaps or render passes for slang shaders
[_blitCommandBuffer commit];
[_blitCommandBuffer waitUntilCompleted];
_blitCommandBuffer = nil;

View File

@ -3,17 +3,15 @@
//
#import <Foundation/Foundation.h>
#import "ShaderTypes.h"
@class Context;
@class MetalDriver;
@interface MenuDisplay : NSObject
@property (nonatomic, readwrite) BOOL blend;
@property (nonatomic, readwrite) MTLClearColor clearColor;
- (instancetype)initWithDriver:(MetalDriver *)driver;
- (instancetype)initWithContext:(Context *)context;
- (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video;
- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video;
@ -23,5 +21,4 @@
+ (const float *)defaultTexCoords;
+ (const float *)defaultColor;
@end

View File

@ -12,19 +12,17 @@
@implementation MenuDisplay
{
__weak MetalDriver *_driver;
Context *_context;
MTLClearColor _clearColor;
bool _clearNextRender;
Uniforms _uniforms;
}
- (instancetype)initWithDriver:(MetalDriver *)driver
- (instancetype)initWithContext:(Context *)context
{
if (self = [super init])
{
_driver = driver;
_context = driver.context;
_context = context;
_clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
}
@ -45,10 +43,10 @@
+ (const float *)defaultTexCoords
{
static float dummy[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
return &dummy[0];
}
@ -97,7 +95,7 @@
draw->y = 0;
draw->matrix_data = NULL;
_uniforms.outputSize = simd_make_float2(_driver.viewport->full_width, _driver.viewport->full_height);
_uniforms.outputSize = simd_make_float2(_context.viewport->full_width, _context.viewport->full_height);
draw->pipeline.backend_data = &_uniforms;
draw->pipeline.backend_data_size = sizeof(_uniforms);
@ -147,7 +145,7 @@
SpriteVertex *pv = (SpriteVertex *)range.data;
for (unsigned i = 0; i < draw->coords->vertices; i++, pv++)
{
pv->position = simd_make_float2(vertex[0], 1.0f - vertex[1]);
pv->position = simd_make_float2(vertex[0], vertex[1]);
vertex += 2;
pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]);
@ -158,9 +156,24 @@
}
id<MTLRenderCommandEncoder> rce = _context.rce;
if (_clearNextRender)
{
[_context resetRenderViewport];
[_context drawQuadX:0
y:0
w:1
h:1
r:(float)_clearColor.red
g:(float)_clearColor.green
b:(float)_clearColor.blue
a:(float)_clearColor.alpha
];
_clearNextRender = NO;
}
MTLViewport vp = {
.originX = draw->x,
.originY = _driver.viewport->full_height - draw->y - draw->height,
.originY = _context.viewport->full_height - draw->y - draw->height,
.width = draw->width,
.height = draw->height,
.znear = 0,
@ -177,7 +190,7 @@
case VIDEO_SHADER_MENU_4:
case VIDEO_SHADER_MENU_5:
case VIDEO_SHADER_MENU_6:
[rce setRenderPipelineState:[_driver getStockShader:draw->pipeline.id blend:_blend]];
[rce setRenderPipelineState:[_context getStockShader:draw->pipeline.id blend:_blend]];
[rce setVertexBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms];
[rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions];
[rce setFragmentBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms];
@ -192,13 +205,7 @@
if (tex == nil)
return;
if (_clearNextRender)
{
// TODO(sgc): draw quad to clear
_clearNextRender = NO;
}
[rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]];
[rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]];
Uniforms uniforms = {
.projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data)

View File

@ -10,6 +10,7 @@
#define RendererCommon_h
#import <Foundation/Foundation.h>
#import "ShaderTypes.h"
// TODO(sgc): implement triple buffering
/*! @brief maximum inflight frames */

View File

@ -55,11 +55,11 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format)
matrix_float4x4 make_matrix_float4x4(const float *v)
{
simd_float4 P = simd_make_float4(v[0], v[1], v[2], v[3]);
v+=4;
v += 4;
simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]);
v+=4;
v += 4;
simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]);
v+=4;
v += 4;
simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]);
matrix_float4x4 mat = {P, Q, R, S};

View File

@ -74,6 +74,11 @@ fragment float4 stock_fragment(FontFragmentIn in [[ stage_in ]],
return colorSample * in.color;
}
fragment half4 stock_fragment_color(FontFragmentIn in [[ stage_in ]])
{
return half4(in.color);
}
#pragma mark - filter kernels
kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]],

View File

@ -7,8 +7,6 @@
#import "View.h"
#import "Filter.h"
#import "ShaderTypes.h"
@implementation TexturedView
{
Context *_context;

View File

@ -79,7 +79,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
@property (nonatomic, readonly) Overlay *overlay;
@property (nonatomic, readonly) Context *context;
@property (nonatomic, readonly) Uniforms *viewportMVP;
@property (nonatomic, readonly) Uniforms *viewportMVPNormalized;
- (instancetype)initWithVideo:(const video_info_t *)video
input:(const input_driver_t **)input
@ -94,8 +93,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
msg:(const char *)msg
info:(video_frame_info_t *)video_info;
- (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend;
/*! @brief setNeedsResize triggers a display resize */
- (void)setNeedsResize;

View File

@ -71,13 +71,9 @@
id<MTLSamplerState> _samplerStateLinear;
id<MTLSamplerState> _samplerStateNearest;
//
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
// other state
Uniforms _uniforms;
Uniforms _viewportMVP;
Uniforms _viewportMVPNormalized;
}
- (instancetype)initWithVideo:(const video_info_t *)video
@ -97,13 +93,9 @@
return nil;
}
if (![self _initStates])
{
return nil;
}
_video = *video;
_viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t));
_viewportMVP.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
_keepAspect = _video.force_aspect;
@ -118,7 +110,7 @@
*inputData = NULL;
// menu display
_display = [[MenuDisplay alloc] initWithDriver:self];
_display = [[MenuDisplay alloc] initWithContext:_context];
// menu view
_menu = [[MetalMenu alloc] initWithContext:_context];
@ -214,162 +206,6 @@
return YES;
}
- (bool)_initStates
{
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
vd.attributes[0].offset = 0;
vd.attributes[0].format = MTLVertexFormatFloat2;
vd.attributes[1].offset = offsetof(SpriteVertex, texCoord);
vd.attributes[1].format = MTLVertexFormatFloat2;
vd.attributes[2].offset = offsetof(SpriteVertex, color);
vd.attributes[2].format = MTLVertexFormatFloat4;
vd.layouts[0].stride = sizeof(SpriteVertex);
{
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
psd.label = @"stock";
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
ca.pixelFormat = _layer.pixelFormat;
ca.blendingEnabled = NO;
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
psd.sampleCount = 1;
psd.vertexDescriptor = vd;
psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment"];
NSError *err;
_states[VIDEO_SHADER_STOCK_BLEND][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"stock_blend";
ca.blendingEnabled = YES;
_states[VIDEO_SHADER_STOCK_BLEND][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
MTLFunctionConstantValues *vals;
psd.label = @"snow_simple";
ca.blendingEnabled = YES;
{
vals = [MTLFunctionConstantValues new];
float values[3] = {
1.25f, // baseScale
0.50f, // density
0.15f, // speed
};
[vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"];
[vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"];
[vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"];
}
psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err];
_states[VIDEO_SHADER_MENU_3][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"snow";
ca.blendingEnabled = YES;
{
vals = [MTLFunctionConstantValues new];
float values[3] = {
3.50f, // baseScale
0.70f, // density
0.25f, // speed
};
[vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"];
[vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"];
[vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"];
}
psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err];
_states[VIDEO_SHADER_MENU_4][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"bokeh";
ca.blendingEnabled = YES;
psd.fragmentFunction = [_library newFunctionWithName:@"bokeh_fragment"];
_states[VIDEO_SHADER_MENU_5][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"snowflake";
ca.blendingEnabled = YES;
psd.fragmentFunction = [_library newFunctionWithName:@"snowflake_fragment"];
_states[VIDEO_SHADER_MENU_6][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon";
ca.blendingEnabled = NO;
psd.vertexFunction = [_library newFunctionWithName:@"ribbon_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_fragment"];
_states[VIDEO_SHADER_MENU][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_blend";
ca.blendingEnabled = YES;
ca.sourceRGBBlendFactor = MTLBlendFactorOne;
ca.destinationRGBBlendFactor = MTLBlendFactorOne;
_states[VIDEO_SHADER_MENU][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_simple";
ca.blendingEnabled = NO;
psd.vertexFunction = [_library newFunctionWithName:@"ribbon_simple_vertex"];
psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_simple_fragment"];
_states[VIDEO_SHADER_MENU_2][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
psd.label = @"ribbon_simple_blend";
ca.blendingEnabled = YES;
ca.sourceRGBBlendFactor = MTLBlendFactorOne;
ca.destinationRGBBlendFactor = MTLBlendFactorOne;
_states[VIDEO_SHADER_MENU_2][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err];
if (err != nil)
{
RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String);
return NO;
}
}
return YES;
}
- (void)_updateUniforms
{
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
@ -383,36 +219,9 @@
_layer.drawableSize = size;
video_driver_update_viewport(_viewport, NO, _keepAspect);
_context.viewport = _viewport;
_viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height);
_viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0);
_viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, 0, _viewport->full_height);
_viewportMVPNormalized.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height);
_viewportMVPNormalized.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
}
#pragma mark - shaders
- (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend
{
assert(index > 0 && index < GFX_MAX_SHADERS);
switch (index)
{
case VIDEO_SHADER_STOCK_BLEND:
case VIDEO_SHADER_MENU:
case VIDEO_SHADER_MENU_2:
case VIDEO_SHADER_MENU_3:
case VIDEO_SHADER_MENU_4:
case VIDEO_SHADER_MENU_5:
case VIDEO_SHADER_MENU_6:
break;
default:
index = VIDEO_SHADER_STOCK_BLEND;
break;
}
return _states[index][blend ? 1 : 0];
}
#pragma mark - video
@ -455,7 +264,7 @@
{
id<MTLRenderCommandEncoder> rce = _context.rce;
[rce pushDebugGroup:@"overlay"];
[rce setRenderPipelineState:[self getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]];
[rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]];
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
[_overlay drawWithEncoder:rce];
@ -464,9 +273,7 @@
#endif
if (msg && *msg)
{
font_driver_render_msg(video_info, NULL, msg, NULL);
}
[self _renderMessage:msg info:video_info];
[self _endFrame];
}
@ -474,6 +281,43 @@
return YES;
}
- (void)_renderMessage:(const char *)msg
info:(video_frame_info_t *)video_info
{
settings_t *settings = config_get_ptr();
if (settings && settings->bools.video_msg_bgcolor_enable)
{
int msg_width =
font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f);
float x = video_info->font_msg_pos_x;
float y = 1.0f - video_info->font_msg_pos_y;
float width = msg_width / (float)_viewport->full_width;
float height =
settings->floats.video_font_size / (float)_viewport->full_height;
y -= height;
float x2 = 0.005f; /* extend background around text */
float y2 = 0.005f;
x -= x2;
y -= y2;
width += x2;
height += y2;
float r = settings->uints.video_msg_bgcolor_red / 255.0f;
float g = settings->uints.video_msg_bgcolor_green / 255.0f;
float b = settings->uints.video_msg_bgcolor_blue / 255.0f;
float a = settings->floats.video_msg_bgcolor_opacity;
[_context resetRenderViewport];
[_context drawQuadX:x y:y w:width h:height r:r g:g b:b a:a];
}
font_driver_render_msg(video_info, NULL, msg, NULL);
}
- (void)_beginFrame
{
video_driver_update_viewport(_viewport, NO, _keepAspect);
@ -555,11 +399,6 @@
return &_viewportMVP;
}
- (Uniforms *)viewportMVPNormalized
{
return &_viewportMVPNormalized;
}
#pragma mark - MTKViewDelegate
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size

View File

@ -196,14 +196,6 @@ static void metal_set_video_mode(void *data,
RARCH_LOG("[Metal]: set_video_mode res=%dx%d fullscreen=%s\n",
width, height,
fullscreen ? "YES" : "NO");
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)

View File

@ -323,17 +323,8 @@ static INLINE void write_quad6(SpriteVertex *pv,
id<MTLRenderCommandEncoder> rce = _context.rce;
[rce pushDebugGroup:@"render fonts"];
MTLViewport vp = {
.originX = 0,
.originY = 0,
.width = _driver.viewport->full_width,
.height = _driver.viewport->full_height,
.znear = 0,
.zfar = 1,
};
[rce setViewport:vp];
[_context resetRenderViewport];
[rce setRenderPipelineState:_state];
[rce setVertexBytes:&_uniforms length:sizeof(Uniforms) atIndex:BufferIndexUniforms];
[rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions];

View File

@ -1235,7 +1235,7 @@ MENU
#include "../menu/drivers/rgui.c"
#endif
#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__)
#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__) || defined(HAVE_METAL)
#ifdef HAVE_XMB
#include "../menu/drivers/xmb.c"
#endif

View File

@ -921,7 +921,12 @@ void fill_pathname_join_noext(char *out_path,
void fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size)
{
size_t copied = strlcpy(out_path, dir, size);
size_t copied;
// behavior of strlcpy is undefined if dst and src overlap
if (out_path == dir)
copied = strlen(dir);
else
copied = strlcpy(out_path, dir, size);
out_path[copied] = delim;
out_path[copied+1] = '\0';

View File

@ -35,7 +35,7 @@ static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info)
if (!md)
return NULL;
return (void *)&md.viewportMVPNormalized->projectionMatrix;
return (void *)&md.viewportMVP->projectionMatrix;
}
static void menu_display_metal_blend_begin(video_frame_info_t *video_info)

View File

@ -4,6 +4,7 @@
<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="METHOD_BRACE_PLACEMENT" value="2" />
<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" />
@ -46,14 +47,15 @@
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="ObjectiveC">
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
<option name="BLANK_LINES_AFTER_IMPORTS" value="0" />
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
<option name="BRACE_STYLE" value="2" />
<option name="CLASS_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" />
@ -67,6 +69,8 @@
<option name="INDENT_SIZE" value="3" />
<option name="CONTINUATION_INDENT_SIZE" value="3" />
<option name="TAB_SIZE" value="3" />
<option name="LABEL_INDENT_ABSOLUTE" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>

View File

@ -428,6 +428,25 @@
05D7753120A55D2700646447 /* BaseConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BaseConfig.xcconfig; sourceTree = "<group>"; };
05D7753320A5678300646447 /* griffin_cpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_cpp.cpp; path = ../../griffin/griffin_cpp.cpp; sourceTree = "<group>"; };
05D7753420A5678400646447 /* griffin_glslang.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_glslang.cpp; path = ../../griffin/griffin_glslang.cpp; sourceTree = "<group>"; };
05F2872F20F2BEEA00632D47 /* task_autodetect.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_autodetect.c; sourceTree = "<group>"; };
05F2873020F2BEEA00632D47 /* task_netplay_find_content.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_netplay_find_content.c; sourceTree = "<group>"; };
05F2873120F2BEEA00632D47 /* task_netplay_nat_traversal.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_netplay_nat_traversal.c; sourceTree = "<group>"; };
05F2873220F2BEEA00632D47 /* task_screenshot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_screenshot.c; sourceTree = "<group>"; };
05F2873320F2BEEA00632D47 /* task_file_transfer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_file_transfer.c; sourceTree = "<group>"; };
05F2873420F2BEEA00632D47 /* task_database.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_database.c; sourceTree = "<group>"; };
05F2873520F2BEEA00632D47 /* task_save.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_save.c; sourceTree = "<group>"; };
05F2873620F2BEEA00632D47 /* task_powerstate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_powerstate.c; sourceTree = "<group>"; };
05F2873720F2BEEA00632D47 /* task_image.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_image.c; sourceTree = "<group>"; };
05F2873820F2BEEA00632D47 /* tasks_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tasks_internal.h; sourceTree = "<group>"; };
05F2873920F2BEEA00632D47 /* task_overlay.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_overlay.c; sourceTree = "<group>"; };
05F2873A20F2BEEA00632D47 /* task_audio_mixer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_audio_mixer.c; sourceTree = "<group>"; };
05F2873B20F2BEEA00632D47 /* task_decompress.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_decompress.c; sourceTree = "<group>"; };
05F2873C20F2BEEA00632D47 /* task_netplay_lan_scan.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_netplay_lan_scan.c; sourceTree = "<group>"; };
05F2873D20F2BEEA00632D47 /* task_wifi.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_wifi.c; sourceTree = "<group>"; };
05F2873E20F2BEEA00632D47 /* task_database_cue.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_database_cue.c; sourceTree = "<group>"; };
05F2873F20F2BEEA00632D47 /* task_content.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_content.c; sourceTree = "<group>"; };
05F2874020F2BEEA00632D47 /* task_http.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_http.c; sourceTree = "<group>"; };
05F2874120F2BEEA00632D47 /* task_patch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = task_patch.c; sourceTree = "<group>"; };
05FA11A120CC6684008C5D0A /* MetalRenderer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MetalRenderer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
089C165DFE840E0CC02AAC07 /* InfoPlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = OSX/en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1DDD58150DA1D0A300B32029 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = OSX/en.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -563,6 +582,7 @@
05A8C57120DB72F000FF7857 /* intl */,
05C5D53220E3DD0900654EE4 /* input */,
05A8C51A20DB72F000FF7857 /* menu */,
05F2872E20F2BEEA00632D47 /* tasks */,
05A8C5AD20DB72F000FF7857 /* ui */,
);
name = Sources;
@ -1194,6 +1214,33 @@
path = drivers_joypad;
sourceTree = "<group>";
};
05F2872E20F2BEEA00632D47 /* tasks */ = {
isa = PBXGroup;
children = (
05F2872F20F2BEEA00632D47 /* task_autodetect.c */,
05F2873020F2BEEA00632D47 /* task_netplay_find_content.c */,
05F2873120F2BEEA00632D47 /* task_netplay_nat_traversal.c */,
05F2873220F2BEEA00632D47 /* task_screenshot.c */,
05F2873320F2BEEA00632D47 /* task_file_transfer.c */,
05F2873420F2BEEA00632D47 /* task_database.c */,
05F2873520F2BEEA00632D47 /* task_save.c */,
05F2873620F2BEEA00632D47 /* task_powerstate.c */,
05F2873720F2BEEA00632D47 /* task_image.c */,
05F2873820F2BEEA00632D47 /* tasks_internal.h */,
05F2873920F2BEEA00632D47 /* task_overlay.c */,
05F2873A20F2BEEA00632D47 /* task_audio_mixer.c */,
05F2873B20F2BEEA00632D47 /* task_decompress.c */,
05F2873C20F2BEEA00632D47 /* task_netplay_lan_scan.c */,
05F2873D20F2BEEA00632D47 /* task_wifi.c */,
05F2873E20F2BEEA00632D47 /* task_database_cue.c */,
05F2873F20F2BEEA00632D47 /* task_content.c */,
05F2874020F2BEEA00632D47 /* task_http.c */,
05F2874120F2BEEA00632D47 /* task_patch.c */,
);
name = tasks;
path = ../../tasks;
sourceTree = "<group>";
};
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (