mirror of
https://github.com/CTCaer/RetroArch.git
synced 2025-03-05 09:57:06 +00:00
fix(Metal): improve shader stability
* use MTKView, which handles layer and scaling changes automatically between displays
This commit is contained in:
parent
ee8d82dcfe
commit
eacd52f009
@ -12,7 +12,6 @@
|
||||
#import "Context.h"
|
||||
#import "PixelConverter.h"
|
||||
|
||||
@class ViewDescriptor;
|
||||
@protocol View;
|
||||
|
||||
@interface Renderer : NSObject
|
||||
|
@ -113,9 +113,7 @@
|
||||
|
||||
{
|
||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||
// Cornflower Blue #58BAF9
|
||||
//rpd.colorAttachments[0].clearColor = MTLClearColorMake(0x58 / 255.0, 0xba / 255.0, 0xf9 / 255.0, 1.0);
|
||||
rpd.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
||||
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
_t_rpd = rpd;
|
||||
}
|
||||
@ -163,49 +161,60 @@
|
||||
|
||||
for (id<View> v in _views) {
|
||||
if (!v.visible) continue;
|
||||
if ([v respondsToSelector:@selector(prepareFrame:)]) {
|
||||
[v prepareFrame:_context];
|
||||
if ([v respondsToSelector:@selector(drawWithContext:)]) {
|
||||
[v drawWithContext:_context];
|
||||
}
|
||||
}
|
||||
|
||||
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
||||
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
||||
|
||||
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
||||
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
||||
|
||||
|
||||
BOOL pendingDraws = NO;
|
||||
for (id<View> v in _views) {
|
||||
if (!v.visible ||
|
||||
![v respondsToSelector:@selector(drawWithEncoder:)]) {
|
||||
continue;
|
||||
if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) {
|
||||
pendingDraws = YES;
|
||||
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;
|
||||
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
||||
dispatch_semaphore_signal(inflight);
|
||||
}];
|
||||
|
||||
[cb presentDrawable:drawable];
|
||||
[cb presentDrawable:_context.nextDrawable];
|
||||
[_context end];
|
||||
}
|
||||
|
||||
|
@ -11,33 +11,34 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// TODO(sgc): implement triple buffering
|
||||
/*! @brief maximum inflight frames */
|
||||
#define MAX_INFLIGHT 3
|
||||
#define MAX_INFLIGHT 1
|
||||
|
||||
#pragma mark - Pixel Formats
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RPixelFormat) {
|
||||
|
||||
RPixelFormatInvalid,
|
||||
|
||||
/* 16-bit formats */
|
||||
RPixelFormatBGRA4Unorm,
|
||||
RPixelFormatB5G6R5Unorm,
|
||||
|
||||
RPixelFormatBGRA8Unorm,
|
||||
RPixelFormatBGRX8Unorm,
|
||||
|
||||
RPixelFormatCount,
|
||||
|
||||
RPixelFormatInvalid,
|
||||
|
||||
/* 16-bit formats */
|
||||
RPixelFormatBGRA4Unorm,
|
||||
RPixelFormatB5G6R5Unorm,
|
||||
|
||||
RPixelFormatBGRA8Unorm,
|
||||
RPixelFormatBGRX8Unorm, // RetroArch XRGB
|
||||
|
||||
RPixelFormatCount,
|
||||
};
|
||||
|
||||
extern NSUInteger RPixelFormatToBPP(RPixelFormat format);
|
||||
extern NSString *NSStringFromRPixelFormat(RPixelFormat format);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RTextureFilter) {
|
||||
RTextureFilterNearest,
|
||||
RTextureFilterLinear,
|
||||
|
||||
RTextureFilterCount,
|
||||
RTextureFilterNearest,
|
||||
RTextureFilterLinear,
|
||||
|
||||
RTextureFilterCount,
|
||||
};
|
||||
|
||||
#endif /* RendererCommon_h */
|
||||
|
@ -13,11 +13,12 @@
|
||||
@property (readwrite) BOOL visible;
|
||||
@property (readwrite) CGRect frame;
|
||||
@property (readwrite) CGSize size;
|
||||
@property (readonly) ViewDrawState drawState;
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
- (void)drawWithContext:(Context *)ctx;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
|
||||
@end
|
||||
|
@ -35,6 +35,11 @@
|
||||
_filter = d.filter;
|
||||
_context = r.context;
|
||||
_visible = YES;
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||
_drawState = ViewDrawStateEncoder;
|
||||
} else {
|
||||
_drawState = ViewDrawStateAll;
|
||||
}
|
||||
self.size = d.size;
|
||||
self.frame = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
@ -113,7 +118,7 @@
|
||||
_pixelsDirty = NO;
|
||||
}
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx {
|
||||
- (void)drawWithContext:(Context *)ctx {
|
||||
[self _convertFormat];
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,15 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, ViewDrawState)
|
||||
{
|
||||
ViewDrawStateNone = 0x00,
|
||||
ViewDrawStateContext = 0x01,
|
||||
ViewDrawStateEncoder = 0x02,
|
||||
|
||||
ViewDrawStateAll = 0x03,
|
||||
};
|
||||
|
||||
@protocol View<NSObject>
|
||||
|
||||
@property (readonly) RPixelFormat format;
|
||||
@ -17,9 +26,10 @@
|
||||
@property (readwrite) BOOL visible;
|
||||
@property (readwrite) CGRect frame;
|
||||
@property (readwrite) CGSize size;
|
||||
@property (readonly) ViewDrawState drawState;
|
||||
|
||||
@optional
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)drawWithContext:(Context *)ctx;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
|
||||
@end
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define METAL_COMMON_H__
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#import "metal/metal_common.h"
|
||||
|
||||
#include <retro_common_api.h>
|
||||
@ -20,6 +21,9 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
extern MTLPixelFormat glslang_format_to_metal(glslang_format fmt);
|
||||
extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
|
||||
|
||||
#pragma mark - Classes
|
||||
|
||||
@interface FrameView : NSObject<View>
|
||||
@ -29,6 +33,7 @@ RETRO_BEGIN_DECLS
|
||||
@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;
|
||||
@ -53,7 +58,7 @@ RETRO_BEGIN_DECLS
|
||||
filter:(RTextureFilter)filter;
|
||||
@end
|
||||
|
||||
@interface MetalDriver : NSObject<PlatformDelegate>
|
||||
@interface MetalDriver : NSObject<MTKViewDelegate>
|
||||
|
||||
@property (readonly) video_viewport_t* viewport;
|
||||
@property (readwrite) bool keepAspect;
|
||||
@ -71,11 +76,6 @@ RETRO_BEGIN_DECLS
|
||||
/*! @brief setNeedsResize triggers a display resize */
|
||||
- (void)setNeedsResize;
|
||||
|
||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
||||
|
||||
|
||||
#pragma mark - Menu APIs
|
||||
|
||||
@end
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -32,7 +32,7 @@
|
||||
@property (readwrite) video_viewport_t *viewport;
|
||||
|
||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||
- (void)prepareFrame:(Context *)ctx;
|
||||
- (void)drawWithContext:(Context *)ctx;
|
||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||
|
||||
@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
|
||||
|
||||
- (void)setVideo:(const video_info_t *)video
|
||||
{
|
||||
_video = *video;
|
||||
_viewport->full_width = _video.width;
|
||||
_viewport->full_height = _video.height;
|
||||
|
||||
if (!_renderer) {
|
||||
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||
_device = device;
|
||||
NSView *view = (NSView *)apple_platform.renderView;
|
||||
MetalView *view = (MetalView *)apple_platform.renderView;
|
||||
view.device = device;
|
||||
CAMetalLayer *layer = (CAMetalLayer *)view.layer;
|
||||
layer.device = device;
|
||||
//layer.device = device;
|
||||
_renderer = [[Renderer alloc] initWithDevice:device layer:layer];
|
||||
_menu.renderer = _renderer;
|
||||
}
|
||||
@ -119,11 +106,6 @@
|
||||
|
||||
- (void)beginFrame
|
||||
{
|
||||
if (resize_chain) {
|
||||
[_renderer drawableSizeWillChange:CGSizeMake(_viewport->full_width, _viewport->full_height)];
|
||||
resize_chain = NO;
|
||||
}
|
||||
|
||||
video_driver_update_viewport(_viewport, NO, _keepAspect);
|
||||
|
||||
[_renderer beginFrame];
|
||||
@ -139,6 +121,21 @@
|
||||
// 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)
|
||||
{
|
||||
float near = 0;
|
||||
@ -292,12 +289,18 @@ typedef struct ALIGN(16)
|
||||
_format = d.format;
|
||||
_bpp = RPixelFormatToBPP(_format);
|
||||
_filter = d.filter;
|
||||
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||
_drawState = ViewDrawStateEncoder;
|
||||
} else {
|
||||
_drawState = ViewDrawStateAll;
|
||||
}
|
||||
_visible = YES;
|
||||
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
|
||||
[self _initSamplers];
|
||||
|
||||
self.size = d.size;
|
||||
self.frame = CGRectMake(0, 0, 1, 1);
|
||||
resize_render_targets = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -425,7 +428,6 @@ typedef struct ALIGN(16)
|
||||
if (init_history)
|
||||
[self _initHistory];
|
||||
else {
|
||||
// TODO(sgc): change to ring buffer?
|
||||
int k;
|
||||
/* todo: what about frame-duping ?
|
||||
* maybe clone d3d10_texture_t with AddRef */
|
||||
@ -546,7 +548,7 @@ static vertex_t vertex_bytes[] = {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareFrame:(Context *)ctx
|
||||
- (void)drawWithContext:(Context *)ctx
|
||||
{
|
||||
_texture = _engine.frame.texture[0].view;
|
||||
[self _convertFormat];
|
||||
@ -567,15 +569,15 @@ static vertex_t vertex_bytes[] = {
|
||||
|
||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||
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;
|
||||
|
||||
BOOL firstPass = YES;
|
||||
|
||||
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;
|
||||
}
|
||||
else {
|
||||
@ -630,7 +632,7 @@ static vertex_t vertex_bytes[] = {
|
||||
texture_sem++;
|
||||
}
|
||||
|
||||
if (lastPass) {
|
||||
if (backBuffer) {
|
||||
[rce setViewport:_engine.frame.viewport];
|
||||
}
|
||||
else {
|
||||
@ -645,7 +647,11 @@ static vertex_t vertex_bytes[] = {
|
||||
[rce endEncoding];
|
||||
_texture = _engine.pass[i].rt.view;
|
||||
}
|
||||
_texture = nil;
|
||||
if (_texture == nil) {
|
||||
_drawState = ViewDrawStateContext;
|
||||
} else {
|
||||
_drawState = ViewDrawStateAll;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_updateRenderTargets
|
||||
@ -712,15 +718,17 @@ static vertex_t vertex_bytes[] = {
|
||||
}
|
||||
|
||||
RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height);
|
||||
|
||||
if (i != (_shader->passes - 1)) {
|
||||
|
||||
MTLPixelFormat fmt = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format));
|
||||
if ((i != (_shader->passes - 1)) ||
|
||||
(width != _viewport->width) || (height != _viewport->height) ||
|
||||
fmt != MTLPixelFormatBGRA8Unorm)
|
||||
{
|
||||
_engine.pass[i].viewport.width = width;
|
||||
_engine.pass[i].viewport.height = height;
|
||||
_engine.pass[i].viewport.znear = 0.0;
|
||||
_engine.pass[i].viewport.zfar = 1.0;
|
||||
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:fmt
|
||||
width:width
|
||||
height:height
|
||||
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))
|
||||
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 {
|
||||
// vertex descriptor
|
||||
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||
vd.attributes[0].offset = offsetof(vertex_t, pos);
|
||||
vd.attributes[0].format = MTLVertexFormatFloat4;
|
||||
@ -839,8 +855,11 @@ static vertex_t vertex_bytes[] = {
|
||||
psd.label = [NSString stringWithFormat:@"pass %d", i];
|
||||
|
||||
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.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
@ -848,19 +867,18 @@ static vertex_t vertex_bytes[] = {
|
||||
|
||||
psd.sampleCount = 1;
|
||||
psd.vertexDescriptor = vd;
|
||||
NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex];
|
||||
NSLog(@"vertex function:\n%@", vs_src);
|
||||
NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment];
|
||||
NSLog(@"fragment function:\n%@", fs_src);
|
||||
|
||||
NSError *err;
|
||||
id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err];
|
||||
if (err != nil) {
|
||||
if (lib == nil) {
|
||||
save_msl = true;
|
||||
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
#if DEBUG
|
||||
RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||
#endif
|
||||
}
|
||||
|
||||
psd.vertexFunction = [lib newFunctionWithName:@"main0"];
|
||||
@ -868,16 +886,20 @@ static vertex_t vertex_bytes[] = {
|
||||
lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err];
|
||||
if (err != nil) {
|
||||
if (lib == nil) {
|
||||
save_msl = true;
|
||||
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
#if DEBUG
|
||||
RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||
#endif
|
||||
}
|
||||
psd.fragmentFunction = [lib newFunctionWithName:@"main0"];
|
||||
|
||||
STRUCT_ASSIGN(_engine.pass[i]._state,
|
||||
[_context.device newRenderPipelineStateWithDescriptor:psd error:&err]);
|
||||
if (err != nil) {
|
||||
save_msl = true;
|
||||
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String);
|
||||
return NO;
|
||||
}
|
||||
@ -892,6 +914,29 @@ static vertex_t vertex_bytes[] = {
|
||||
STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf);
|
||||
}
|
||||
} @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.fragment);
|
||||
|
||||
@ -945,3 +990,67 @@ static vertex_t vertex_bytes[] = {
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -53,13 +53,14 @@ static void *metal_init(const video_info_t *video,
|
||||
gfx_ctx_mode_t mode;
|
||||
|
||||
[apple_platform setViewType:APPLE_VIEW_TYPE_METAL];
|
||||
|
||||
MetalDriver *md = [MetalDriver new];
|
||||
if (md == nil) {
|
||||
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);
|
||||
|
||||
@ -67,8 +68,8 @@ static void *metal_init(const video_info_t *video,
|
||||
mode.height = video->height;
|
||||
mode.fullscreen = video->fullscreen;
|
||||
|
||||
[apple_platform setVideoMode:mode];
|
||||
[md setVideo:video];
|
||||
[apple_platform setVideoMode:mode];
|
||||
|
||||
*input = NULL;
|
||||
*input_data = NULL;
|
||||
|
@ -36,22 +36,16 @@ typedef enum apple_view_type {
|
||||
APPLE_VIEW_TYPE_METAL,
|
||||
} apple_view_type_t;
|
||||
|
||||
@protocol PlatformDelegate
|
||||
#ifdef HAVE_METAL
|
||||
@optional
|
||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
||||
#endif
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
@interface MetalView : MTKView
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@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 */
|
||||
@property (readonly) id renderView;
|
||||
|
||||
|
@ -19,9 +19,6 @@
|
||||
#include "cocoa_common.h"
|
||||
#ifdef HAVE_COCOA
|
||||
#include "../ui_cocoa.h"
|
||||
#ifdef HAVE_VULKAN
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <retro_assert.h>
|
||||
@ -46,6 +43,21 @@
|
||||
#include "../../../location/location_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;
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
@ -96,15 +108,13 @@ void *glkitview_init(void);
|
||||
}
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setFrame:(NSRect)frameRect
|
||||
{
|
||||
[super setFrame:frameRect];
|
||||
|
||||
if (apple_platform.delegate != nil)
|
||||
{
|
||||
[apple_platform.delegate viewDidUpdateFrame:frameRect];
|
||||
}
|
||||
|
||||
cocoagl_gfx_ctx_update();
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ id<ApplePlatform> apple_platform;
|
||||
NSWindow* _window;
|
||||
apple_view_type_t _vt;
|
||||
NSView* _renderView;
|
||||
id<PlatformDelegate> _delegate;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSWindow IBOutlet* window;
|
||||
@ -259,9 +258,9 @@ static char** waiting_argv;
|
||||
case APPLE_VIEW_TYPE_METAL:
|
||||
#if defined(HAVE_METAL) || defined(HAVE_VULKAN)
|
||||
{
|
||||
NSView *v = [CocoaView get];
|
||||
v.wantsLayer = YES;
|
||||
v.layer = CAMetalLayer.layer;
|
||||
MetalView *v = [MetalView new];
|
||||
v.paused = YES;
|
||||
v.enableSetNeedsDisplay = NO;
|
||||
_renderView = v;
|
||||
}
|
||||
#endif
|
||||
@ -280,7 +279,7 @@ static char** waiting_argv;
|
||||
|
||||
_renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||
_renderView.frame = self.window.contentView.bounds;
|
||||
|
||||
|
||||
[self.window.contentView addSubview:_renderView];
|
||||
[self.window makeFirstResponder:_renderView];
|
||||
}
|
||||
@ -293,18 +292,6 @@ static char** waiting_argv;
|
||||
return _renderView;
|
||||
}
|
||||
|
||||
- (id)delegate {
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<PlatformDelegate>)delegate {
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
- (id)viewHandle {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (bool)hasFocus {
|
||||
return [NSApp isActive];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user