feat(Metal): full xmb pipeline support

Menu TODOs:

* understand why ribbon does not look the same as GL
* add clear support to `MenuDisplay` for glui
This commit is contained in:
Stuart Carnie 2018-07-03 22:28:33 -07:00
parent 4a101734a1
commit a0900ec433
18 changed files with 858 additions and 305 deletions

View File

@ -11,27 +11,35 @@
#import "RendererCommon.h"
@interface Texture : NSObject
@property (readonly) id<MTLTexture> texture;
@property (readonly) id<MTLSamplerState> sampler;
@property (nonatomic, readonly) id<MTLTexture> texture;
@property (nonatomic, readonly) id<MTLSamplerState> sampler;
@end
typedef struct
{
void *data;
NSUInteger offset;
__unsafe_unretained id<MTLBuffer> buffer;
} BufferRange;
/*! @brief Context contains the render state used by various components */
@interface Context : NSObject
@property (readonly) id<MTLDevice> device;
@property (readonly) id<MTLLibrary> library;
@property (nonatomic, readonly) id<MTLDevice> device;
@property (nonatomic, readonly) id<MTLLibrary> library;
@property (nonatomic, readwrite) MTLClearColor clearColor;
/*! @brief Returns the command buffer used for pre-render work,
* such as mip maps for applying filters
* */
@property (readonly) id<MTLCommandBuffer> blitCommandBuffer;
@property (nonatomic, readonly) id<MTLCommandBuffer> blitCommandBuffer;
/*! @brief Returns the command buffer for the current frame */
@property (readonly) id<MTLCommandBuffer> commandBuffer;
@property (readonly) id<CAMetalDrawable> nextDrawable;
@property (nonatomic, readonly) id<MTLCommandBuffer> commandBuffer;
@property (nonatomic, readonly) id<CAMetalDrawable> nextDrawable;
/*! @brief Main render encoder to back buffer */
@property (readonly) id<MTLRenderCommandEncoder> rce;
@property (nonatomic, readonly) id<MTLRenderCommandEncoder> rce;
- (instancetype)initWithDevice:(id<MTLDevice>)d
layer:(CAMetalLayer *)layer
@ -40,6 +48,8 @@
- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter;
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst;
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length;
/*! @brief begin marks the beginning of a frame */
- (void)begin;

View File

@ -10,9 +10,22 @@
#import "Filter.h"
#import <QuartzCore/QuartzCore.h>
@interface BufferNode : NSObject
@property (nonatomic, readonly) id<MTLBuffer> src;
@property (nonatomic, readwrite) NSUInteger allocated;
@property (nonatomic, readwrite) BufferNode *next;
@end
@interface BufferChain : NSObject
- (instancetype)initWithDevice:(id<MTLDevice>)device blockLen:(NSUInteger)blockLen;
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length;
- (void)commitRanges;
- (void)discard;
@end
@interface Texture()
@property (readwrite) id<MTLTexture> texture;
@property (readwrite) id<MTLSamplerState> sampler;
@property (nonatomic, readwrite) id<MTLTexture> texture;
@property (nonatomic, readwrite) id<MTLSamplerState> sampler;
@end
@interface Context()
@ -32,6 +45,10 @@
id<MTLRenderCommandEncoder> _rce;
id<MTLCommandBuffer> _blitCommandBuffer;
NSUInteger _currentChain;
BufferChain *_chain[CHAIN_LENGTH];
MTLClearColor _clearColor;
}
- (instancetype)initWithDevice:(id<MTLDevice>)d
@ -45,31 +62,39 @@
_layer = layer;
_library = l;
_commandQueue = [_device newCommandQueue];
_clearColor = MTLClearColorMake(0, 0, 0, 1);
{
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
sd.label = @"NEAREST";
_samplers[TEXTURE_FILTER_NEAREST] = [d newSamplerStateWithDescriptor:sd];
sd.label = @"NEAREST";
_samplers[TEXTURE_FILTER_NEAREST] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterNearest;
sd.label = @"MIPMAP_NEAREST";
_samplers[TEXTURE_FILTER_MIPMAP_NEAREST] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterNearest;
sd.label = @"MIPMAP_NEAREST";
_samplers[TEXTURE_FILTER_MIPMAP_NEAREST] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterNotMipmapped;
sd.minFilter = MTLSamplerMinMagFilterLinear;
sd.magFilter = MTLSamplerMinMagFilterLinear;
sd.label = @"LINEAR";
_samplers[TEXTURE_FILTER_LINEAR] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterLinear;
sd.label = @"MIPMAP_LINEAR";
_samplers[TEXTURE_FILTER_MIPMAP_LINEAR] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterNotMipmapped;
sd.minFilter = MTLSamplerMinMagFilterLinear;
sd.magFilter = MTLSamplerMinMagFilterLinear;
sd.label = @"LINEAR";
_samplers[TEXTURE_FILTER_LINEAR] = [d newSamplerStateWithDescriptor:sd];
sd.mipFilter = MTLSamplerMipFilterLinear;
sd.label = @"MIPMAP_LINEAR";
_samplers[TEXTURE_FILTER_MIPMAP_LINEAR] = [d newSamplerStateWithDescriptor:sd];
}
if (![self _initConversionFilters])
return nil;
if (![self _initMainState])
return nil;
for (int i = 0; i < CHAIN_LENGTH; i++)
{
_chain[i] = [[BufferChain alloc] initWithDevice:_device blockLen:65536];
}
}
return self;
}
@ -186,6 +211,12 @@
return _blitCommandBuffer;
}
- (void)_nextChain
{
_currentChain = (_currentChain + 1) % CHAIN_LENGTH;
[_chain[_currentChain] discard];
}
- (void)begin
{
assert(_commandBuffer == nil);
@ -199,6 +230,8 @@
if (_rce == nil)
{
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
rpd.colorAttachments[0].clearColor = _clearColor;
rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
rpd.colorAttachments[0].texture = self.nextDrawable.texture;
_rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd];
}
@ -207,7 +240,9 @@
- (void)end
{
assert(self->_commandBuffer != nil);
assert(_commandBuffer != nil);
[_chain[_currentChain] commitRanges];
if (_blitCommandBuffer)
{
@ -233,9 +268,144 @@
_commandBuffer = nil;
_drawable = nil;
[self _nextChain];
}
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length
{
return [_chain[_currentChain] allocRange:range length:length];
}
@end
@implementation Texture
@end
@implementation BufferNode
- (instancetype)initWithBuffer:(id<MTLBuffer>)src
{
if (self = [super init])
{
_src = src;
}
return self;
}
@end
@implementation BufferChain
{
id<MTLDevice> _device;
NSUInteger _blockLen;
BufferNode *_head;
NSUInteger _offset; // offset into _current
BufferNode *_current;
NSUInteger _length;
NSUInteger _allocated;
}
/* macOS requires constants in a buffer to have a 256 byte alignment. */
#ifdef TARGET_OS_MAC
static const NSUInteger kConstantAlignment = 256;
#else
static const NSUInteger kConstantAlignment = 4;
#endif
- (instancetype)initWithDevice:(id<MTLDevice>)device blockLen:(NSUInteger)blockLen
{
if (self = [super init])
{
_device = device;
_blockLen = blockLen;
}
return self;
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"length=%ld, allocated=%ld", _length, _allocated];
}
- (void)commitRanges
{
for (BufferNode *n = _head; n != nil; n = n.next)
{
if (n.allocated > 0)
{
[n.src didModifyRange:NSMakeRange(0, n.allocated)];
}
}
}
- (void)discard
{
_current = _head;
_offset = 0;
_allocated = 0;
}
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length
{
bzero(range, sizeof(*range));
if (!_head)
{
_head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:MTLResourceStorageModeManaged]];
_length += _blockLen;
_current = _head;
_offset = 0;
}
if ([self _subAllocRange:range length:length])
return YES;
while (_current.next)
{
[self _nextNode];
if ([self _subAllocRange:range length:length])
return YES;
}
NSUInteger blockLen = _blockLen;
if (length > blockLen)
{
blockLen = length;
}
_current.next = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:blockLen options:MTLResourceStorageModeManaged]];
if (!_current.next)
return NO;
_length += blockLen;
[self _nextNode];
retro_assert([self _subAllocRange:range length:length]);
return YES;
}
- (void)_nextNode
{
_current = _current.next;
_offset = 0;
}
- (BOOL)_subAllocRange:(BufferRange *)range length:(NSUInteger)length
{
NSUInteger nextOffset = _offset + length;
if (nextOffset <= _current.src.length)
{
_current.allocated = nextOffset;
_allocated += length;
range->data = _current.src.contents + _offset;
range->buffer = _current.src;
range->offset = _offset;
_offset = MTL_ALIGN_BUFFER(nextOffset);
return YES;
}
return NO;
}
@end

View File

@ -15,8 +15,8 @@
@interface Filter : NSObject
@property (readwrite) id<FilterDelegate> delegate;
@property (readonly) id<MTLSamplerState> sampler;
@property (nonatomic, readwrite) id<FilterDelegate> delegate;
@property (nonatomic, 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;

View File

@ -10,8 +10,8 @@
@interface MenuDisplay : NSObject
@property (readwrite) BOOL blend;
@property (readwrite) MTLClearColor clearColor;
@property (nonatomic, readwrite) BOOL blend;
@property (nonatomic, readwrite) MTLClearColor clearColor;
- (instancetype)initWithDriver:(MetalDriver *)driver;
- (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video;
@ -22,7 +22,6 @@
+ (const float *)defaultVertices;
+ (const float *)defaultTexCoords;
+ (const float *)defaultColor;
+ (const float *)defaultMatrix;
@end

View File

@ -45,10 +45,10 @@
+ (const float *)defaultTexCoords
{
static float dummy[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
return &dummy[0];
}
@ -64,17 +64,6 @@
return &dummy[0];
}
+ (const float *)defaultMatrix
{
static matrix_float4x4 dummy;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dummy = matrix_proj_ortho(0, 1, 0, 1);
});
return &dummy;
}
- (void)setClearColor:(MTLClearColor)clearColor
{
_clearColor = clearColor;
@ -100,133 +89,103 @@
}
}
static INLINE void write_quad4(SpriteVertex *pv,
float x, float y, float width, float height, float scale,
float tex_x, float tex_y, float tex_width, float tex_height,
const float *color)
{
unsigned i;
static const float strip[2 * 4] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
float swidth = width * scale;
float sheight = height * scale;
x += (width - swidth) / 2;
y += (height - sheight) / 2;
for (i = 0; i < 4; i++)
{
pv[i].position = simd_make_float2(x + strip[2 * i + 0] * swidth,
y + strip[2 * i + 1] * sheight);
pv[i].texCoord = simd_make_float2(tex_x + strip[2 * i + 0] * tex_width,
tex_y + strip[2 * i + 1] * tex_height);
pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]);
color += 4;
}
}
static INLINE void write_quad4a(SpriteVertex *pv,
float x, float y, float width, float height, float scale,
float tex_x, float tex_y, float tex_width, float tex_height,
const float *color)
{
unsigned i;
static const float vert[2 * 4] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
static const float tex[2 * 4] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
float swidth = width * scale;
float sheight = height * scale;
x += (width - swidth) / 2;
y += (height - sheight) / 2;
for (i = 0; i < 4; i++)
{
pv[i].position = simd_make_float2(x + vert[2 * i + 0] * swidth,
y + vert[2 * i + 1] * sheight);
pv[i].texCoord = simd_make_float2(tex_x + tex[2 * i + 0] * tex_width,
tex_y + tex[2 * i + 1] * tex_height);
pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]);
color += 4;
}
}
- (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video
{
static struct video_coords blank_coords;
draw->x = 0;
draw->y = 0;
draw->matrix_data = NULL;
_uniforms.outputSize = simd_make_float2(_driver.viewport->full_width, _driver.viewport->full_height);
draw->pipeline.backend_data = &_uniforms;
draw->pipeline.backend_data_size = sizeof(_uniforms);
switch (draw->pipeline.id)
{
// ribbon
default:
case VIDEO_SHADER_MENU:
case VIDEO_SHADER_MENU_2:
{
video_coord_array_t *ca = menu_display_get_coords_array();
draw->coords = (struct video_coords *)&ca->coords;
break;
}
case VIDEO_SHADER_MENU_3:
case VIDEO_SHADER_MENU_4:
case VIDEO_SHADER_MENU_5:
case VIDEO_SHADER_MENU_6:
{
draw->coords = &blank_coords;
blank_coords.vertices = 4;
draw->prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP;
break;
}
}
_uniforms.time += 0.01;
}
- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video
{
Texture *tex = (__bridge Texture *)(void *)draw->texture;
const float *vertex = draw->coords->vertex;
const float *tex_coord = draw->coords->tex_coord;
const float *color = draw->coords->color;
const float *vertex = draw->coords->vertex ?: MenuDisplay.defaultVertices;
const float *tex_coord = draw->coords->tex_coord ?: MenuDisplay.defaultTexCoords;
const float *color = draw->coords->color ?: MenuDisplay.defaultColor;
if (!vertex)
vertex = MenuDisplay.defaultVertices;
if (!tex_coord)
tex_coord = MenuDisplay.defaultTexCoords;
if (!draw->coords->lut_tex_coord)
draw->coords->lut_tex_coord = MenuDisplay.defaultTexCoords;
// TODO(sgc): is this necessary?
// if (!texture)
// texture = &vk->display.blank_texture;
if (!color)
color = MenuDisplay.defaultColor;
assert(draw->coords->vertices <= 4);
SpriteVertex buf[4];
SpriteVertex *pv = buf;
Uniforms *uniforms;
if (draw->coords->vertex == NULL)
NSUInteger needed = draw->coords->vertices * sizeof(SpriteVertex);
BufferRange range;
if (![_context allocRange:&range length:needed])
{
write_quad4a(pv,
draw->x,
draw->y,
draw->width,
draw->height,
draw->scale_factor,
0.0, 0.0, 1.0, 1.0, color);
RARCH_ERR("[Metal]: MenuDisplay unable to allocate buffer of %d bytes", needed);
return;
}
NSUInteger vertexCount = draw->coords->vertices;
SpriteVertex *pv = (SpriteVertex *)range.data;
for (unsigned i = 0; i < draw->coords->vertices; i++, pv++)
{
pv->position = simd_make_float2(vertex[0], 1.0 - vertex[1]);
vertex += 2;
uniforms = _driver.viewportMVP;
}
else
{
for (unsigned i = 0; i < draw->coords->vertices; i++, pv++)
{
/* Y-flip. We're using top-left coordinates */
pv->position = simd_make_float2(vertex[0], vertex[1]);
vertex += 2;
pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]);
tex_coord += 2;
pv->color = simd_make_float4(color[0], color[1], color[2], color[3]);
color += 4;
}
uniforms = &_uniforms;
pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]);
tex_coord += 2;
pv->color = simd_make_float4(color[0], color[1], color[2], color[3]);
color += 4;
}
id<MTLRenderCommandEncoder> rce = _context.rce;
MTLViewport vp = {
.originX = draw->x,
.originY = _driver.viewport->full_height - draw->y - draw->height,
.width = draw->width,
.height = draw->height,
.znear = 0,
.zfar = 1,
};
[rce setViewport:vp];
switch (draw->pipeline.id)
{
#ifdef HAVE_SHADERPIPELINE
#if HAVE_SHADERPIPELINE
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:
{
[rce setRenderPipelineState:[_driver 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];
[rce drawPrimitives:[self _toPrimitiveType:draw->prim_type] vertexStart:0 vertexCount:vertexCount];
break;
}
#endif
default:
{
@ -235,15 +194,17 @@ static INLINE void write_quad4a(SpriteVertex *pv,
// TODO(sgc): draw quad to clear
_clearNextRender = NO;
}
id<MTLRenderCommandEncoder> rce = _context.rce;
[rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]];
[rce setVertexBytes:uniforms length:sizeof(*uniforms) atIndex:BufferIndexUniforms];
[rce setVertexBytes:buf length:sizeof(buf) atIndex:BufferIndexPositions];
Uniforms uniforms = {
.projectionMatrix = draw->matrix_data ? *(matrix_float4x4 *)draw->matrix_data
: _uniforms.projectionMatrix
};
[rce setVertexBytes:&uniforms length:sizeof(uniforms) atIndex:BufferIndexUniforms];
[rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions];
[rce setFragmentTexture:tex.texture atIndex:TextureIndexColor];
[rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw];
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertexCount];
break;
}

View File

@ -14,6 +14,16 @@
// TODO(sgc): implement triple buffering
/*! @brief maximum inflight frames */
#define MAX_INFLIGHT 1
#define CHAIN_LENGTH 3
/* macOS requires constants in a buffer to have a 256 byte alignment. */
#ifdef TARGET_OS_MAC
#define kMetalBufferAlignment 256
#else
#define kMetalBufferAlignment 4
#endif
#define MTL_ALIGN_BUFFER(size) ((size + kMetalBufferAlignment - 1) & (~(kMetalBufferAlignment - 1)))
#pragma mark - Pixel Formats

View File

@ -62,10 +62,10 @@ matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bott
float ty = (top + bottom) / (bottom - top);
float tz = near / (far - near);
simd_float4 P = {sx, 0, 0, 0};
simd_float4 Q = {0, sy, 0, 0};
simd_float4 R = {0, 0, sz, 0};
simd_float4 S = {tx, ty, tz, 1};
simd_float4 P = simd_make_float4(sx, 0, 0, 0);
simd_float4 Q = simd_make_float4(0, sy, 0, 0);
simd_float4 R = simd_make_float4(0, 0, sz, 0);
simd_float4 S = simd_make_float4(tx, ty, tz, 1);
matrix_float4x4 mat = {P, Q, R, S};
return mat;

View File

@ -6,12 +6,12 @@
@interface TexturedView : NSObject
@property (readonly) RPixelFormat format;
@property (readonly) RTextureFilter filter;
@property (readwrite) BOOL visible;
@property (readwrite) CGRect frame;
@property (readwrite) CGSize size;
@property (readonly) ViewDrawState drawState;
@property (nonatomic, readonly) RPixelFormat format;
@property (nonatomic, readonly) RTextureFilter filter;
@property (nonatomic, readwrite) BOOL visible;
@property (nonatomic, readwrite) CGRect frame;
@property (nonatomic, readwrite) CGSize size;
@property (nonatomic, readonly) ViewDrawState drawState;
- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c;

View File

@ -20,9 +20,9 @@ typedef NS_ENUM(NSInteger, ViewDrawState)
};
@interface ViewDescriptor : NSObject
@property (readwrite) RPixelFormat format;
@property (readwrite) RTextureFilter filter;
@property (readwrite) CGSize size;
@property (nonatomic, readwrite) RPixelFormat format;
@property (nonatomic, readwrite) RTextureFilter filter;
@property (nonatomic, readwrite) CGSize size;
- (instancetype)init;
@end

View File

@ -0,0 +1,273 @@
//
// pipeline_ribbon.metal
// RetroArch
//
// Created by Stuart Carnie on 6/30/18.
//
#include <metal_stdlib>
#import "ShaderTypes.h"
using namespace metal;
#pragma mark - ribbon simple
namespace ribbon {
float iqhash(float n)
{
return fract(sin(n) * 43758.5453);
}
float noise(float3 x)
{
float3 p = floor(x);
float3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 57.0 + 113.0 * p.z;
return mix(mix(mix(iqhash(n), iqhash(n + 1.0), f.x),
mix(iqhash(n + 57.0), iqhash(n + 58.0), f.x), f.y),
mix(mix(iqhash(n + 113.0), iqhash(n + 114.0), f.x),
mix(iqhash(n + 170.0), iqhash(n + 171.0), f.x), f.y), f.z);
}
float xmb_noise2(float3 x, const device Uniforms &constants)
{
return cos(x.z * 4.0) * cos(x.z + constants.time / 10.0 + x.x);
}
}
#pragma mark - ribbon simple
vertex FontFragmentIn ribbon_simple_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]])
{
float4 t = (constants.projectionMatrix * float4(in.position, 0, 1));
float3 v = float3(t.x, 0.0, 1.0-t.y);
float3 v2 = v;
v2.x = v2.x + constants.time / 2.0;
v2.z = v.z * 3.0;
v.y = cos((v.x + v.z / 3.0 + constants.time) * 2.0) / 10.0 + ribbon::noise(v2.xyz) / 4.0;
v.y = -v.y;
FontFragmentIn out;
out.position = float4(v, 1.0);
return out;
}
fragment float4 ribbon_simple_fragment()
{
return float4(0.05, 0.05, 0.05, 1.0);
}
#pragma mark - ribbon
typedef struct
{
vector_float4 position [[position]];
vector_float3 vEC;
} RibbonOutIn;
vertex RibbonOutIn ribbon_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]])
{
float4 t = (constants.projectionMatrix * float4(in.position, 0, 1));
float3 v = float3(t.x, 0.0, 1.0-t.y);
float3 v2 = v;
float3 v3 = v;
v.y = ribbon::xmb_noise2(v2, constants) / 8.0;
v3.x -= constants.time / 5.0;
v3.x /= 4.0;
v3.z -= constants.time / 10.0;
v3.y -= constants.time / 100.0;
v.z -= ribbon::noise(v3 * 7.0) / 15.0;
v.y -= ribbon::noise(v3 * 7.0) / 15.0 + cos(v.x * 2.0 - constants.time / 2.0) / 5.0 - 0.3;
v.y = -v.y;
RibbonOutIn out;
out.vEC = v;
out.position = float4(v, 1.0);
return out;
}
fragment float4 ribbon_fragment(RibbonOutIn in [[ stage_in ]])
{
const float3 up = float3(0.0, 0.0, 1.0);
float3 x = dfdx(in.vEC);
float3 y = dfdy(in.vEC);
float3 normal = normalize(cross(x, y));
float c = 1.0 - dot(normal, up);
c = (1.0 - cos(c * c)) / 13.0;
return float4(c, c, c, 1.0);
}
#pragma mark - snow constants
constant float snowBaseScale [[ function_constant(0) ]]; // [1.0 .. 10.0]
constant float snowDensity [[ function_constant(1) ]]; // [0.01 .. 1.0]
constant float snowSpeed [[ function_constant(2) ]]; // [0.1 .. 1.0]
#pragma mark - snow simple
namespace snow
{
float rand(float2 co)
{
return fract(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
}
float dist_func(float2 distv)
{
float dist = sqrt((distv.x * distv.x) + (distv.y * distv.y)) * (40.0 / snowBaseScale);
dist = clamp(dist, 0.0, 1.0);
return cos(dist * (3.14159265358 * 0.5)) * 0.5;
}
float random_dots(float2 co)
{
float part = 1.0 / 20.0;
float2 cd = floor(co / part);
float p = rand(cd);
if (p > 0.005 * (snowDensity * 40.0))
return 0.0;
float2 dpos = (float2(fract(p * 2.0) , p) + float2(2.0, 2.0)) * 0.25;
float2 cellpos = fract(co / part);
float2 distv = (cellpos - dpos);
return dist_func(distv);
}
float snow(float2 pos, float time, float scale)
{
// add wobble
pos.x += cos(pos.y * 1.2 + time * 3.14159 * 2.0 + 1.0 / scale) / (8.0 / scale) * 4.0;
// add gravity
pos += time * scale * float2(-0.5, 1.0) * 4.0;
return random_dots(pos / scale) * (scale * 0.5 + 0.5);
}
}
fragment float4 snow_fragment(FontFragmentIn in [[ stage_in ]],
const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]])
{
float tim = constants.time * 0.4 * snowSpeed;
float2 pos = in.position.xy / constants.outputSize.xx;
pos.y = 1.0 - pos.y; // Flip Y
float a = 0.0;
// Each of these is a layer of snow
// Remove some for better performance
// Changing the scale (3rd value) will mess with the looping
a += snow::snow(pos, tim, 1.0);
a += snow::snow(pos, tim, 0.7);
a += snow::snow(pos, tim, 0.6);
a += snow::snow(pos, tim, 0.5);
a += snow::snow(pos, tim, 0.4);
a += snow::snow(pos, tim, 0.3);
a += snow::snow(pos, tim, 0.25);
a += snow::snow(pos, tim, 0.125);
a = a * min(pos.y * 4.0, 1.0);
return float4(1.0, 1.0, 1.0, a);
}
fragment float4 bokeh_fragment(FontFragmentIn in [[ stage_in ]],
const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]])
{
float speed = constants.time * 4.0;
float2 uv = -1.0 + 2.0 * in.position.xy / constants.outputSize;
uv.x *= constants.outputSize.x / constants.outputSize.y;
float3 color = float3(0.0);
for( int i=0; i < 8; i++ )
{
float pha = sin(float(i) * 546.13 + 1.0) * 0.5 + 0.5;
float siz = pow(sin(float(i) * 651.74 + 5.0) * 0.5 + 0.5, 4.0);
float pox = sin(float(i) * 321.55 + 4.1) * constants.outputSize.x / constants.outputSize.y;
float rad = 0.1 + 0.5 * siz + sin(pha + siz) / 4.0;
float2 pos = float2(pox + sin(speed / 15. + pha + siz), - 1.0 - rad + (2.0 + 2.0 * rad) * fract(pha + 0.3 * (speed / 7.) * (0.2 + 0.8 * siz)));
float dis = length(uv - pos);
if(dis < rad)
{
float3 col = mix(float3(0.194 * sin(speed / 6.0) + 0.3, 0.2, 0.3 * pha), float3(1.1 * sin(speed / 9.0) + 0.3, 0.2 * pha, 0.4), 0.5 + 0.5 * sin(float(i)));
color += col.zyx * (1.0 - smoothstep(rad * 0.15, rad, dis));
}
}
color *= sqrt(1.5 - 0.5 * length(uv));
return float4(color.r, color.g, color.b , 0.5);
}
namespace snowflake {
float rand_float(float x)
{
return snow::rand(float2(x, 1.0));
}
float snow(float3 pos, float2 uv, float o, float atime)
{
float2 d = (pos.xy - uv);
float a = atan(d.y / d.x) + sin(atime*1.0 + o) * 10.0;
float dist = d.x*d.x + d.y*d.y;
if(dist < pos.z/400.0)
{
float col = 0.0;
if(sin(a * 8.0) < 0.0)
{
col=1.0;
}
if(dist < pos.z/800.0)
{
col+=1.0;
}
return col * pos.z;
}
return 0.0;
}
float col(float2 c, const device Uniforms &constants)
{
float color = 0.0;
float atime = (constants.time + 1.0) / 4.0;
for (int i = 1; i < 15; i++)
{
float o = rand_float(float(i) / 3.0) * 15.0;
float z = rand_float(float(i) + 13.0);
float x = 1.8 - (3.6) * (rand_float(floor((constants.time*((z + 1.0) / 2.0) +o) / 2.0)) + sin(constants.time * o /1000.0) / 10.0);
float y = 1.0 - fmod((constants.time * ((z + 1.0)/2.0)) + o, 2.0);
color += snow(float3(x,y,z), c, o, atime);
}
return color;
}
}
fragment float4 snowflake_fragment(FontFragmentIn in [[ stage_in ]],
const device Uniforms &constants [[ buffer(BufferIndexUniforms) ]])
{
float2 uv = in.position.xy / constants.outputSize.xy;
uv = uv * 2.0 - 1.0;
float2 p = uv;
p.x *= constants.outputSize.x / constants.outputSize.y;
//p.y = -p.y;
float c = snowflake::col(p, constants);
return float4(c,c,c,c);
}

View File

@ -28,14 +28,14 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
@interface FrameView : NSObject
@property (readonly) RPixelFormat format;
@property (readonly) RTextureFilter filter;
@property (readwrite) BOOL visible;
@property (readwrite) CGRect frame;
@property (readwrite) CGSize size;
@property (readonly) ViewDrawState drawState;
@property (readonly) struct video_shader* shader;
@property (readwrite) uint64_t frameCount;
@property (nonatomic, readonly) RPixelFormat format;
@property (nonatomic, readonly) RTextureFilter filter;
@property (nonatomic, readwrite) BOOL visible;
@property (nonatomic, readwrite) CGRect frame;
@property (nonatomic, readwrite) CGSize size;
@property (nonatomic, readonly) ViewDrawState drawState;
@property (nonatomic, readonly) struct video_shader* shader;
@property (nonatomic, readwrite) uint64_t frameCount;
- (void)setFilteringIndex:(int)index smooth:(bool)smooth;
- (BOOL)setShaderFromPath:(NSString *)path;
@ -46,9 +46,9 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
@interface MetalMenu : NSObject
@property (readonly) bool hasFrame;
@property (readwrite) bool enabled;
@property (readwrite) float alpha;
@property (nonatomic, readonly) bool hasFrame;
@property (nonatomic, readwrite) bool enabled;
@property (nonatomic, readwrite) float alpha;
- (void)updateFrame:(void const *)source;
@ -60,20 +60,20 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
@interface MetalDriver : NSObject<MTKViewDelegate>
@property (readonly) video_viewport_t* viewport;
@property (readwrite) bool keepAspect;
@property (readonly) MetalMenu* menu;
@property (readonly) FrameView* frameView;
@property (readonly) MenuDisplay* display;
@property (readonly) Context* context;
@property (readonly) Uniforms* viewportMVP;
@property (nonatomic, readonly) video_viewport_t* viewport;
@property (nonatomic, readwrite) bool keepAspect;
@property (nonatomic, readonly) MetalMenu* menu;
@property (nonatomic, readonly) FrameView* frameView;
@property (nonatomic, readonly) MenuDisplay* display;
@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
inputData:(void **)inputData;
- (void)setVideo:(const video_info_t *)video;
- (void)setShaderIndex:(NSUInteger)index;
- (bool)renderFrame:(const void *)data
width:(unsigned)width
height:(unsigned)height

View File

@ -32,7 +32,7 @@
@interface FrameView()
@property (readwrite) video_viewport_t *viewport;
@property (nonatomic, readwrite) video_viewport_t *viewport;
- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)context;
- (void)drawWithContext:(Context *)ctx;
@ -41,7 +41,7 @@
@end
@interface MetalMenu()
@property (readonly) TexturedView *view;
@property (nonatomic, readonly) TexturedView *view;
- (instancetype)initWithContext:(Context *)context;
@end
@ -71,7 +71,7 @@
// other state
Uniforms _uniforms;
Uniforms _viewportMVP;
BOOL _begin, _end;
Uniforms _viewportMVPNormalized;
}
- (instancetype)initWithVideo:(const video_info_t *)video
@ -96,9 +96,6 @@
return nil;
}
_begin = NO;
_end = NO;
_video = *video;
_viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t));
@ -221,15 +218,15 @@
{
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
psd.label = @"stock_no_blend";
psd.label = @"stock";
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
ca.pixelFormat = _layer.pixelFormat;
ca.blendingEnabled = NO;
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
psd.sampleCount = 1;
psd.vertexDescriptor = vd;
@ -243,7 +240,7 @@
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];
@ -252,6 +249,114 @@
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;
}
@ -261,18 +366,44 @@
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
}
- (void)_updateViewport:(CGSize)size
{
_viewport->full_width = (unsigned int)size.width;
_viewport->full_height = (unsigned int)size.height;
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
_layer.drawableSize = size;
video_driver_update_viewport(_viewport, NO, _keepAspect);
_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);
return _states[index][blend ? 1 : 0];
}
- (void)setShaderIndex:(NSUInteger)index
{
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
@ -324,19 +455,12 @@
- (void)_beginFrame
{
video_driver_update_viewport(_viewport, NO, _keepAspect);
assert(!_begin && !_end);
_begin = YES;
[_context begin];
[self _updateUniforms];
}
- (void)_drawViews:(video_frame_info_t *)video_info
{
assert(_begin && !_end);
_begin = NO;
_end = YES;
id<MTLRenderCommandEncoder> rce = _context.rce;
// draw back buffer
@ -358,28 +482,37 @@
[_frameView drawWithEncoder:rce];
}
[rce popDebugGroup];
if (_menu.enabled && _menu.hasFrame)
{
[_menu.view drawWithContext:_context];
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
[rce setRenderPipelineState:_t_pipelineState];
if (_menu.view.filter == RTextureFilterNearest)
{
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
}
else
{
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
}
[_menu.view drawWithEncoder:rce];
}
#if defined(HAVE_MENU)
if (_menu.enabled)
{
MTLViewport viewport = {
.originX = 0.0f,
.originY = 0.0f,
.width = _viewport->full_width,
.height = _viewport->full_height,
.znear = 0.0f,
.zfar = 1.0,
};
[rce setViewport:viewport];
[rce pushDebugGroup:@"menu"];
menu_driver_frame(video_info);
if (_menu.hasFrame)
{
[_menu.view drawWithContext:_context];
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
[rce setRenderPipelineState:_t_pipelineState];
if (_menu.view.filter == RTextureFilterNearest)
{
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
}
else
{
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
}
[_menu.view drawWithEncoder:rce];
}
[rce popDebugGroup];
}
#endif
@ -387,8 +520,6 @@
- (void)_endFrame
{
assert(!_begin && _end);
_end = NO;
[_context end];
}
@ -402,18 +533,16 @@
return &_viewportMVP;
}
- (Uniforms *)viewportMVPNormalized
{
return &_viewportMVPNormalized;
}
#pragma mark - MTKViewDelegate
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
{
_viewport->full_width = (unsigned int)size.width;
_viewport->full_height = (unsigned int)size.height;
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
_layer.drawableSize = size;
video_driver_update_viewport(_viewport, NO, _keepAspect);
_viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height);
_viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0);
[self _updateViewport:size];
}
- (void)drawInMTKView:(MTKView *)view
@ -870,7 +999,6 @@ static vertex_t vertex_bytes[] = {
id<MTLCommandBuffer> cb = ctx.blitCommandBuffer;
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
// rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0);
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;

View File

@ -24,6 +24,7 @@
@interface MetalRaster : NSObject
{
__weak MetalDriver *_driver;
const font_renderer_driver_t *_font_driver;
void *_font_data;
struct font_atlas *_atlas;
@ -44,36 +45,25 @@
unsigned _vertices;
}
@property (weak, readwrite) MetalDriver *metal;
@property (readonly) struct font_atlas *atlas;
@property (readwrite) bool needsUpdate;
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size;
- (instancetype)initWithDriver:(MetalDriver *)driver fontPath:(const char *)font_path fontSize:(unsigned)font_size;
- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale;
- (int)getWidthForMessage:(const char *)msg length:(NSUInteger)length scale:(float)scale;
- (const struct font_glyph *)getGlyph:(uint32_t)code;
@end
@implementation MetalRaster
/* macOS requires constants in a buffer to have a 256 byte alignment. */
#ifdef TARGET_OS_MAC
static const NSUInteger kConstantAlignment = 256;
#else
static const NSUInteger kConstantAlignment = 4;
#endif
#define ALIGN_CONSTANTS(size) ((size + kConstantAlignment - 1) & (~(kConstantAlignment - 1)))
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size
- (instancetype)initWithDriver:(MetalDriver *)driver fontPath:(const char *)font_path fontSize:(unsigned)font_size
{
if (self = [super init])
{
if (metal == nil)
if (driver == nil)
return nil;
_metal = metal;
_context = metal.context;
_driver = driver;
_context = driver.context;
if (!font_renderer_create_default((const void **)&_font_driver,
&_font_data, font_path, font_size))
{
@ -83,7 +73,7 @@ static const NSUInteger kConstantAlignment = 4;
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
_atlas = _font_driver->get_atlas(_font_data);
_stride = ALIGN_CONSTANTS(_atlas->width);
_stride = MTL_ALIGN_BUFFER(_atlas->width);
if (_stride == _atlas->width)
{
_buffer = [_context.device newBufferWithBytes:_atlas->buffer
@ -115,7 +105,6 @@ static const NSUInteger kConstantAlignment = 4;
_capacity = 12000;
_vert = [_context.device newBufferWithLength:sizeof(SpriteVertex) *
_capacity options:MTLResourceStorageModeManaged];
_needsUpdate = true;
if (![self _initializeState])
{
return nil;
@ -188,15 +177,14 @@ static const NSUInteger kConstantAlignment = 4;
[_buffer didModifyRange:NSMakeRange(offset, len)];
_atlas->dirty = false;
_needsUpdate = true;
}
}
- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale
- (int)getWidthForMessage:(const char *)msg length:(NSUInteger)length scale:(float)scale
{
int delta_x = 0;
for (unsigned i = 0; i < length; i++)
for (NSUInteger i = 0; i < length; i++)
{
const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, (uint8_t)msg[i]);
if (!glyph) /* Do something smarter here ... */
@ -262,14 +250,14 @@ static INLINE void write_quad6(SpriteVertex *pv,
aligned:(unsigned)aligned
{
const char *msg_end = msg + length;
int x = roundf(posX * _metal.viewport->width);
int y = roundf((1.0f - posY) * _metal.viewport->height);
int x = (int)roundf(posX * _driver.viewport->full_width);
int y = (int)roundf((1.0f - posY) * _driver.viewport->full_height);
int delta_x = 0;
int delta_y = 0;
float inv_tex_size_x = 1.0f / _texture.width;
float inv_tex_size_y = 1.0f / _texture.height;
float inv_win_width = 1.0f / _metal.viewport->width;
float inv_win_height = 1.0f / _metal.viewport->height;
float inv_win_width = 1.0f / _driver.viewport->full_width;
float inv_win_height = 1.0f / _driver.viewport->full_height;
switch (aligned)
{
@ -335,8 +323,19 @@ 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];
[rce setRenderPipelineState:_state];
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
[rce setVertexBytes:&_uniforms length:sizeof(Uniforms) atIndex:BufferIndexUniforms];
[rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions];
[rce setFragmentTexture:_texture atIndex:TextureIndexColor];
[rce setFragmentSamplerState:_sampler atIndex:SamplerIndexDraw];
@ -386,7 +385,7 @@ static INLINE void write_quad6(SpriteVertex *pv,
}
else
{
unsigned msg_len = strlen(msg);
NSUInteger msg_len = strlen(msg);
[self _renderLine:msg
video:video
length:msg_len

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.viewportMVP;
return (void *)&md.viewportMVPNormalized->projectionMatrix;
}
static void menu_display_metal_blend_begin(video_frame_info_t *video_info)

View File

@ -16,7 +16,7 @@ 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_METAL -DHAVE_SLANG -DHAVE_GLSLANG -DHAVE_SPIRV_CROSS -DWANT_GLSLANG -DENABLE_HLSL -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 -DMETAL_DEBUG
SRCBASE = $(SRCROOT)/../..
DEPS_DIR = $(SRCBASE)/deps

View File

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05269A6120ABF20500C29F1E /* MetalKit.framework */; };
05770B9920E805160013DABC /* menu_pipeline.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05770B9820E805160013DABC /* menu_pipeline.metal */; };
05A8C7B420DB75A500FF7857 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C74E20DB72F100FF7857 /* Shaders.metal */; };
05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23720A63CB40084ABDA /* Metal.framework */; };
05A8E23A20A63CED0084ABDA /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23920A63CED0084ABDA /* IOSurface.framework */; };
@ -70,6 +71,7 @@
0566C78E20E49E6800BC768F /* scaler_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler_int.h; sourceTree = "<group>"; };
0566C78F20E49E6800BC768F /* filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter.h; sourceTree = "<group>"; };
0566C79020E49E6800BC768F /* gl_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gl_capabilities.h; sourceTree = "<group>"; };
05770B9820E805160013DABC /* menu_pipeline.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = menu_pipeline.metal; sourceTree = "<group>"; };
05A8C51B20DB72F000FF7857 /* menu_shader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = menu_shader.h; sourceTree = "<group>"; };
05A8C51D20DB72F000FF7857 /* menu_cbs_get_value.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_get_value.c; sourceTree = "<group>"; };
05A8C51E20DB72F000FF7857 /* menu_cbs_sublabel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_sublabel.c; sourceTree = "<group>"; };
@ -826,7 +828,6 @@
children = (
05A8C64120DB72F000FF7857 /* d3d_shaders */,
05A8C60020DB72F000FF7857 /* gl_shaders */,
05A8C7B320DB756F00FF7857 /* metal_shaders */,
05A8C62220DB72F000FF7857 /* vulkan_shaders */,
05A8C63E20DB72F000FF7857 /* d3d10.c */,
05A8C5F020DB72F000FF7857 /* gl.c */,
@ -876,33 +877,33 @@
05A8C62220DB72F000FF7857 /* vulkan_shaders */ = {
isa = PBXGroup;
children = (
05A8C62320DB72F000FF7857 /* pipeline_snow_simple.vert */,
05A8C62420DB72F000FF7857 /* opaque.vert */,
05A8C62520DB72F000FF7857 /* alpha_blend.frag */,
05A8C62620DB72F000FF7857 /* pipeline_snow.frag */,
05A8C62720DB72F000FF7857 /* pipeline_ribbon.vert */,
05A8C62820DB72F000FF7857 /* opaque.vert.inc */,
05A8C62920DB72F000FF7857 /* alpha_blend.frag.inc */,
05A8C62A20DB72F000FF7857 /* pipeline_ribbon.frag.inc */,
05A8C62B20DB72F000FF7857 /* Makefile */,
05A8C62C20DB72F000FF7857 /* pipeline_bokeh.frag.inc */,
05A8C62D20DB72F000FF7857 /* font.frag.inc */,
05A8C62E20DB72F000FF7857 /* pipeline_snow.frag.inc */,
05A8C62F20DB72F000FF7857 /* pipeline_ribbon_simple.vert */,
05A8C63020DB72F000FF7857 /* pipeline_snow_simple.frag.inc */,
05A8C63120DB72F000FF7857 /* pipeline_ribbon_simple.frag.inc */,
05A8C63220DB72F000FF7857 /* pipeline_ribbon_simple.vert.inc */,
05A8C63320DB72F000FF7857 /* pipeline_ribbon.frag */,
05A8C63420DB72F000FF7857 /* pipeline_snow_simple.vert.inc */,
05A8C62520DB72F000FF7857 /* alpha_blend.frag */,
05A8C63520DB72F000FF7857 /* font.frag */,
05A8C63620DB72F000FF7857 /* alpha_blend.vert */,
05A8C63720DB72F000FF7857 /* pipeline_snow_simple.frag */,
05A8C63820DB72F000FF7857 /* opaque.frag */,
05A8C63920DB72F000FF7857 /* pipeline_ribbon.vert.inc */,
05A8C63A20DB72F000FF7857 /* alpha_blend.vert.inc */,
05A8C63B20DB72F000FF7857 /* opaque.frag.inc */,
05A8C63C20DB72F000FF7857 /* pipeline_bokeh.frag */,
05A8C63D20DB72F000FF7857 /* pipeline_ribbon_simple.frag */,
05A8C63320DB72F000FF7857 /* pipeline_ribbon.frag */,
05A8C63720DB72F000FF7857 /* pipeline_snow_simple.frag */,
05A8C62620DB72F000FF7857 /* pipeline_snow.frag */,
05A8C62920DB72F000FF7857 /* alpha_blend.frag.inc */,
05A8C63A20DB72F000FF7857 /* alpha_blend.vert.inc */,
05A8C62D20DB72F000FF7857 /* font.frag.inc */,
05A8C63B20DB72F000FF7857 /* opaque.frag.inc */,
05A8C62820DB72F000FF7857 /* opaque.vert.inc */,
05A8C62C20DB72F000FF7857 /* pipeline_bokeh.frag.inc */,
05A8C63120DB72F000FF7857 /* pipeline_ribbon_simple.frag.inc */,
05A8C63220DB72F000FF7857 /* pipeline_ribbon_simple.vert.inc */,
05A8C62A20DB72F000FF7857 /* pipeline_ribbon.frag.inc */,
05A8C63920DB72F000FF7857 /* pipeline_ribbon.vert.inc */,
05A8C63020DB72F000FF7857 /* pipeline_snow_simple.frag.inc */,
05A8C63420DB72F000FF7857 /* pipeline_snow_simple.vert.inc */,
05A8C62E20DB72F000FF7857 /* pipeline_snow.frag.inc */,
05A8C63620DB72F000FF7857 /* alpha_blend.vert */,
05A8C62420DB72F000FF7857 /* opaque.vert */,
05A8C62F20DB72F000FF7857 /* pipeline_ribbon_simple.vert */,
05A8C62720DB72F000FF7857 /* pipeline_ribbon.vert */,
05A8C62320DB72F000FF7857 /* pipeline_snow_simple.vert */,
);
path = vulkan_shaders;
sourceTree = "<group>";
@ -960,6 +961,7 @@
05A8C74C20DB72F100FF7857 /* RendererCommon.h */,
05A8C75420DB72F100FF7857 /* RendererCommon.m */,
05A8C74E20DB72F100FF7857 /* Shaders.metal */,
05770B9820E805160013DABC /* menu_pipeline.metal */,
05A8C75120DB72F100FF7857 /* ShaderTypes.h */,
05A8C74920DB72F100FF7857 /* TexturedView.h */,
05A8C75620DB72F100FF7857 /* TexturedView.m */,
@ -1029,13 +1031,6 @@
path = drivers_font_renderer;
sourceTree = "<group>";
};
05A8C7B320DB756F00FF7857 /* metal_shaders */ = {
isa = PBXGroup;
children = (
);
path = metal_shaders;
sourceTree = "<group>";
};
05C5D53220E3DD0900654EE4 /* input */ = {
isa = PBXGroup;
children = (
@ -1318,6 +1313,7 @@
buildActionMask = 2147483647;
files = (
05D7753720A567A700646447 /* griffin_glslang.cpp in Sources */,
05770B9920E805160013DABC /* menu_pipeline.metal in Sources */,
05D7753520A567A400646447 /* griffin_cpp.cpp in Sources */,
509F0C9D1AA23AFC00619ECC /* griffin_objc.m in Sources */,
840222FC1A889EE2009AB261 /* griffin.c in Sources */,

View File

@ -55,6 +55,11 @@
{
return YES;
}
- (BOOL)isFlipped
{
return YES;
}
@end
#endif

View File

@ -297,7 +297,9 @@ static char** waiting_argv;
}
- (void)setVideoMode:(gfx_ctx_mode_t)mode {
// TODO(sgc): handle full screen
// TODO(sgc): handle full screen?
// cheap hack to ensure MTKView posts triggers a drawable resize event
[self.window setContentSize:NSMakeSize(mode.width-1, mode.height)];
[self.window setContentSize:NSMakeSize(mode.width, mode.height)];
}