mirror of
https://github.com/libretro/RetroArch
synced 2025-03-28 08:37:41 +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 "Context.h"
|
||||||
#import "PixelConverter.h"
|
#import "PixelConverter.h"
|
||||||
|
|
||||||
@class ViewDescriptor;
|
|
||||||
@protocol View;
|
@protocol View;
|
||||||
|
|
||||||
@interface Renderer : NSObject
|
@interface Renderer : NSObject
|
||||||
|
@ -113,9 +113,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||||
// Cornflower Blue #58BAF9
|
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||||
//rpd.colorAttachments[0].clearColor = MTLClearColorMake(0x58 / 255.0, 0xba / 255.0, 0xf9 / 255.0, 1.0);
|
|
||||||
rpd.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
|
||||||
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||||
_t_rpd = rpd;
|
_t_rpd = rpd;
|
||||||
}
|
}
|
||||||
@ -163,49 +161,60 @@
|
|||||||
|
|
||||||
for (id<View> v in _views) {
|
for (id<View> v in _views) {
|
||||||
if (!v.visible) continue;
|
if (!v.visible) continue;
|
||||||
if ([v respondsToSelector:@selector(prepareFrame:)]) {
|
if ([v respondsToSelector:@selector(drawWithContext:)]) {
|
||||||
[v prepareFrame:_context];
|
[v drawWithContext:_context];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
BOOL pendingDraws = NO;
|
||||||
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
|
||||||
|
|
||||||
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
|
||||||
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
|
||||||
|
|
||||||
for (id<View> v in _views) {
|
for (id<View> v in _views) {
|
||||||
if (!v.visible ||
|
if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) {
|
||||||
![v respondsToSelector:@selector(drawWithEncoder:)]) {
|
pendingDraws = YES;
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set view state
|
|
||||||
if (v.format == RPixelFormatBGRX8Unorm) {
|
|
||||||
[rce setRenderPipelineState:_t_pipelineStateNoAlpha];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[rce setRenderPipelineState:_t_pipelineState];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.filter == RTextureFilterNearest) {
|
|
||||||
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
|
|
||||||
}
|
|
||||||
|
|
||||||
[v drawWithEncoder:rce];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[rce endEncoding];
|
if (pendingDraws) {
|
||||||
|
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
||||||
|
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
||||||
|
|
||||||
|
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
||||||
|
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
||||||
|
|
||||||
|
for (id<View> v in _views) {
|
||||||
|
if (!v.visible ||
|
||||||
|
![v respondsToSelector:@selector(drawWithEncoder:)] ||
|
||||||
|
(v.drawState & ViewDrawStateEncoder) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set view state
|
||||||
|
if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) {
|
||||||
|
[rce setRenderPipelineState:_t_pipelineStateNoAlpha];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[rce setRenderPipelineState:_t_pipelineState];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.filter == RTextureFilterNearest) {
|
||||||
|
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
|
||||||
|
}
|
||||||
|
|
||||||
|
[v drawWithEncoder:rce];
|
||||||
|
}
|
||||||
|
|
||||||
|
[rce endEncoding];
|
||||||
|
}
|
||||||
|
|
||||||
__block dispatch_semaphore_t inflight = _inflightSemaphore;
|
__block dispatch_semaphore_t inflight = _inflightSemaphore;
|
||||||
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
||||||
dispatch_semaphore_signal(inflight);
|
dispatch_semaphore_signal(inflight);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[cb presentDrawable:drawable];
|
[cb presentDrawable:_context.nextDrawable];
|
||||||
[_context end];
|
[_context end];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,33 +11,34 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
// TODO(sgc): implement triple buffering
|
||||||
/*! @brief maximum inflight frames */
|
/*! @brief maximum inflight frames */
|
||||||
#define MAX_INFLIGHT 3
|
#define MAX_INFLIGHT 1
|
||||||
|
|
||||||
#pragma mark - Pixel Formats
|
#pragma mark - Pixel Formats
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, RPixelFormat) {
|
typedef NS_ENUM(NSUInteger, RPixelFormat) {
|
||||||
|
|
||||||
RPixelFormatInvalid,
|
RPixelFormatInvalid,
|
||||||
|
|
||||||
/* 16-bit formats */
|
/* 16-bit formats */
|
||||||
RPixelFormatBGRA4Unorm,
|
RPixelFormatBGRA4Unorm,
|
||||||
RPixelFormatB5G6R5Unorm,
|
RPixelFormatB5G6R5Unorm,
|
||||||
|
|
||||||
RPixelFormatBGRA8Unorm,
|
RPixelFormatBGRA8Unorm,
|
||||||
RPixelFormatBGRX8Unorm,
|
RPixelFormatBGRX8Unorm, // RetroArch XRGB
|
||||||
|
|
||||||
RPixelFormatCount,
|
RPixelFormatCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NSUInteger RPixelFormatToBPP(RPixelFormat format);
|
extern NSUInteger RPixelFormatToBPP(RPixelFormat format);
|
||||||
extern NSString *NSStringFromRPixelFormat(RPixelFormat format);
|
extern NSString *NSStringFromRPixelFormat(RPixelFormat format);
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, RTextureFilter) {
|
typedef NS_ENUM(NSUInteger, RTextureFilter) {
|
||||||
RTextureFilterNearest,
|
RTextureFilterNearest,
|
||||||
RTextureFilterLinear,
|
RTextureFilterLinear,
|
||||||
|
|
||||||
RTextureFilterCount,
|
RTextureFilterCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* RendererCommon_h */
|
#endif /* RendererCommon_h */
|
||||||
|
@ -13,11 +13,12 @@
|
|||||||
@property (readwrite) BOOL visible;
|
@property (readwrite) BOOL visible;
|
||||||
@property (readwrite) CGRect frame;
|
@property (readwrite) CGRect frame;
|
||||||
@property (readwrite) CGSize size;
|
@property (readwrite) CGSize size;
|
||||||
|
@property (readonly) ViewDrawState drawState;
|
||||||
|
|
||||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||||
|
|
||||||
- (void)prepareFrame:(Context *)ctx;
|
- (void)drawWithContext:(Context *)ctx;
|
||||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
|
||||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||||
|
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -35,6 +35,11 @@
|
|||||||
_filter = d.filter;
|
_filter = d.filter;
|
||||||
_context = r.context;
|
_context = r.context;
|
||||||
_visible = YES;
|
_visible = YES;
|
||||||
|
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||||
|
_drawState = ViewDrawStateEncoder;
|
||||||
|
} else {
|
||||||
|
_drawState = ViewDrawStateAll;
|
||||||
|
}
|
||||||
self.size = d.size;
|
self.size = d.size;
|
||||||
self.frame = CGRectMake(0, 0, 1, 1);
|
self.frame = CGRectMake(0, 0, 1, 1);
|
||||||
}
|
}
|
||||||
@ -113,7 +118,7 @@
|
|||||||
_pixelsDirty = NO;
|
_pixelsDirty = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareFrame:(Context *)ctx {
|
- (void)drawWithContext:(Context *)ctx {
|
||||||
[self _convertFormat];
|
[self _convertFormat];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,15 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, ViewDrawState)
|
||||||
|
{
|
||||||
|
ViewDrawStateNone = 0x00,
|
||||||
|
ViewDrawStateContext = 0x01,
|
||||||
|
ViewDrawStateEncoder = 0x02,
|
||||||
|
|
||||||
|
ViewDrawStateAll = 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
@protocol View<NSObject>
|
@protocol View<NSObject>
|
||||||
|
|
||||||
@property (readonly) RPixelFormat format;
|
@property (readonly) RPixelFormat format;
|
||||||
@ -17,9 +26,10 @@
|
|||||||
@property (readwrite) BOOL visible;
|
@property (readwrite) BOOL visible;
|
||||||
@property (readwrite) CGRect frame;
|
@property (readwrite) CGRect frame;
|
||||||
@property (readwrite) CGSize size;
|
@property (readwrite) CGSize size;
|
||||||
|
@property (readonly) ViewDrawState drawState;
|
||||||
|
|
||||||
@optional
|
@optional
|
||||||
- (void)prepareFrame:(Context *)ctx;
|
- (void)drawWithContext:(Context *)ctx;
|
||||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define METAL_COMMON_H__
|
#define METAL_COMMON_H__
|
||||||
|
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
|
#import <MetalKit/MetalKit.h>
|
||||||
#import "metal/metal_common.h"
|
#import "metal/metal_common.h"
|
||||||
|
|
||||||
#include <retro_common_api.h>
|
#include <retro_common_api.h>
|
||||||
@ -20,6 +21,9 @@
|
|||||||
|
|
||||||
RETRO_BEGIN_DECLS
|
RETRO_BEGIN_DECLS
|
||||||
|
|
||||||
|
extern MTLPixelFormat glslang_format_to_metal(glslang_format fmt);
|
||||||
|
extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
|
||||||
|
|
||||||
#pragma mark - Classes
|
#pragma mark - Classes
|
||||||
|
|
||||||
@interface FrameView : NSObject<View>
|
@interface FrameView : NSObject<View>
|
||||||
@ -29,6 +33,7 @@ RETRO_BEGIN_DECLS
|
|||||||
@property (readwrite) BOOL visible;
|
@property (readwrite) BOOL visible;
|
||||||
@property (readwrite) CGRect frame;
|
@property (readwrite) CGRect frame;
|
||||||
@property (readwrite) CGSize size;
|
@property (readwrite) CGSize size;
|
||||||
|
@property (readonly) ViewDrawState drawState;
|
||||||
@property (readonly) struct video_shader* shader;
|
@property (readonly) struct video_shader* shader;
|
||||||
|
|
||||||
@property (readwrite) uint64_t frameCount;
|
@property (readwrite) uint64_t frameCount;
|
||||||
@ -53,7 +58,7 @@ RETRO_BEGIN_DECLS
|
|||||||
filter:(RTextureFilter)filter;
|
filter:(RTextureFilter)filter;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MetalDriver : NSObject<PlatformDelegate>
|
@interface MetalDriver : NSObject<MTKViewDelegate>
|
||||||
|
|
||||||
@property (readonly) video_viewport_t* viewport;
|
@property (readonly) video_viewport_t* viewport;
|
||||||
@property (readwrite) bool keepAspect;
|
@property (readwrite) bool keepAspect;
|
||||||
@ -71,11 +76,6 @@ RETRO_BEGIN_DECLS
|
|||||||
/*! @brief setNeedsResize triggers a display resize */
|
/*! @brief setNeedsResize triggers a display resize */
|
||||||
- (void)setNeedsResize;
|
- (void)setNeedsResize;
|
||||||
|
|
||||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Menu APIs
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
RETRO_END_DECLS
|
RETRO_END_DECLS
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
@property (readwrite) video_viewport_t *viewport;
|
@property (readwrite) video_viewport_t *viewport;
|
||||||
|
|
||||||
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer;
|
||||||
- (void)prepareFrame:(Context *)ctx;
|
- (void)drawWithContext:(Context *)ctx;
|
||||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -72,32 +72,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - swap chain
|
|
||||||
|
|
||||||
- (void)viewDidUpdateFrame:(NSRect)rect
|
|
||||||
{
|
|
||||||
RARCH_LOG("[MetalDriver] viewDidUpdateFrame %s\n", NSStringFromRect(rect).UTF8String);
|
|
||||||
_viewport->full_width = (unsigned int)rect.size.width;
|
|
||||||
_viewport->full_height = (unsigned int)rect.size.height;
|
|
||||||
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
|
|
||||||
resize_chain = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - video
|
#pragma mark - video
|
||||||
|
|
||||||
- (void)setVideo:(const video_info_t *)video
|
- (void)setVideo:(const video_info_t *)video
|
||||||
{
|
{
|
||||||
_video = *video;
|
_video = *video;
|
||||||
_viewport->full_width = _video.width;
|
|
||||||
_viewport->full_height = _video.height;
|
|
||||||
|
|
||||||
if (!_renderer) {
|
if (!_renderer) {
|
||||||
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||||
_device = device;
|
_device = device;
|
||||||
NSView *view = (NSView *)apple_platform.renderView;
|
MetalView *view = (MetalView *)apple_platform.renderView;
|
||||||
|
view.device = device;
|
||||||
CAMetalLayer *layer = (CAMetalLayer *)view.layer;
|
CAMetalLayer *layer = (CAMetalLayer *)view.layer;
|
||||||
layer.device = device;
|
//layer.device = device;
|
||||||
_renderer = [[Renderer alloc] initWithDevice:device layer:layer];
|
_renderer = [[Renderer alloc] initWithDevice:device layer:layer];
|
||||||
_menu.renderer = _renderer;
|
_menu.renderer = _renderer;
|
||||||
}
|
}
|
||||||
@ -119,11 +106,6 @@
|
|||||||
|
|
||||||
- (void)beginFrame
|
- (void)beginFrame
|
||||||
{
|
{
|
||||||
if (resize_chain) {
|
|
||||||
[_renderer drawableSizeWillChange:CGSizeMake(_viewport->full_width, _viewport->full_height)];
|
|
||||||
resize_chain = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
video_driver_update_viewport(_viewport, NO, _keepAspect);
|
video_driver_update_viewport(_viewport, NO, _keepAspect);
|
||||||
|
|
||||||
[_renderer beginFrame];
|
[_renderer beginFrame];
|
||||||
@ -139,6 +121,21 @@
|
|||||||
// TODO(sgc): resize all drawables
|
// TODO(sgc): resize all drawables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - MTKViewDelegate
|
||||||
|
|
||||||
|
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
|
||||||
|
RARCH_LOG("[MetalDriver] drawableSizeWillChange: %s\n", NSStringFromSize(size).UTF8String);
|
||||||
|
_viewport->full_width = (unsigned int)size.width;
|
||||||
|
_viewport->full_height = (unsigned int)size.height;
|
||||||
|
video_driver_set_size(&_viewport->full_width, &_viewport->full_height);
|
||||||
|
[_renderer drawableSizeWillChange:size];
|
||||||
|
video_driver_update_viewport(_viewport, NO, _keepAspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawInMTKView:(MTKView *)view {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom)
|
extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom)
|
||||||
{
|
{
|
||||||
float near = 0;
|
float near = 0;
|
||||||
@ -292,12 +289,18 @@ typedef struct ALIGN(16)
|
|||||||
_format = d.format;
|
_format = d.format;
|
||||||
_bpp = RPixelFormatToBPP(_format);
|
_bpp = RPixelFormatToBPP(_format);
|
||||||
_filter = d.filter;
|
_filter = d.filter;
|
||||||
|
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) {
|
||||||
|
_drawState = ViewDrawStateEncoder;
|
||||||
|
} else {
|
||||||
|
_drawState = ViewDrawStateAll;
|
||||||
|
}
|
||||||
_visible = YES;
|
_visible = YES;
|
||||||
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
|
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
|
||||||
[self _initSamplers];
|
[self _initSamplers];
|
||||||
|
|
||||||
self.size = d.size;
|
self.size = d.size;
|
||||||
self.frame = CGRectMake(0, 0, 1, 1);
|
self.frame = CGRectMake(0, 0, 1, 1);
|
||||||
|
resize_render_targets = YES;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -425,7 +428,6 @@ typedef struct ALIGN(16)
|
|||||||
if (init_history)
|
if (init_history)
|
||||||
[self _initHistory];
|
[self _initHistory];
|
||||||
else {
|
else {
|
||||||
// TODO(sgc): change to ring buffer?
|
|
||||||
int k;
|
int k;
|
||||||
/* todo: what about frame-duping ?
|
/* todo: what about frame-duping ?
|
||||||
* maybe clone d3d10_texture_t with AddRef */
|
* maybe clone d3d10_texture_t with AddRef */
|
||||||
@ -546,7 +548,7 @@ static vertex_t vertex_bytes[] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareFrame:(Context *)ctx
|
- (void)drawWithContext:(Context *)ctx
|
||||||
{
|
{
|
||||||
_texture = _engine.frame.texture[0].view;
|
_texture = _engine.frame.texture[0].view;
|
||||||
[self _convertFormat];
|
[self _convertFormat];
|
||||||
@ -567,15 +569,15 @@ static vertex_t vertex_bytes[] = {
|
|||||||
|
|
||||||
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new];
|
||||||
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0);
|
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0);
|
||||||
rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||||
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||||
|
|
||||||
BOOL firstPass = YES;
|
BOOL firstPass = YES;
|
||||||
|
|
||||||
for (unsigned i = 0; i < _shader->passes; i++) {
|
for (unsigned i = 0; i < _shader->passes; i++) {
|
||||||
BOOL lastPass = i == _shader->passes - 1;
|
BOOL backBuffer = (_engine.pass[i].rt.view == nil);
|
||||||
|
|
||||||
if (lastPass) {
|
if (backBuffer) {
|
||||||
rpd.colorAttachments[0].texture = _context.nextDrawable.texture;
|
rpd.colorAttachments[0].texture = _context.nextDrawable.texture;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -630,7 +632,7 @@ static vertex_t vertex_bytes[] = {
|
|||||||
texture_sem++;
|
texture_sem++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastPass) {
|
if (backBuffer) {
|
||||||
[rce setViewport:_engine.frame.viewport];
|
[rce setViewport:_engine.frame.viewport];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -645,7 +647,11 @@ static vertex_t vertex_bytes[] = {
|
|||||||
[rce endEncoding];
|
[rce endEncoding];
|
||||||
_texture = _engine.pass[i].rt.view;
|
_texture = _engine.pass[i].rt.view;
|
||||||
}
|
}
|
||||||
_texture = nil;
|
if (_texture == nil) {
|
||||||
|
_drawState = ViewDrawStateContext;
|
||||||
|
} else {
|
||||||
|
_drawState = ViewDrawStateAll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_updateRenderTargets
|
- (void)_updateRenderTargets
|
||||||
@ -712,15 +718,17 @@ static vertex_t vertex_bytes[] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height);
|
RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height);
|
||||||
|
MTLPixelFormat fmt = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format));
|
||||||
if (i != (_shader->passes - 1)) {
|
if ((i != (_shader->passes - 1)) ||
|
||||||
|
(width != _viewport->width) || (height != _viewport->height) ||
|
||||||
|
fmt != MTLPixelFormatBGRA8Unorm)
|
||||||
|
{
|
||||||
_engine.pass[i].viewport.width = width;
|
_engine.pass[i].viewport.width = width;
|
||||||
_engine.pass[i].viewport.height = height;
|
_engine.pass[i].viewport.height = height;
|
||||||
_engine.pass[i].viewport.znear = 0.0;
|
_engine.pass[i].viewport.znear = 0.0;
|
||||||
_engine.pass[i].viewport.zfar = 1.0;
|
_engine.pass[i].viewport.zfar = 1.0;
|
||||||
|
|
||||||
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:fmt
|
||||||
width:width
|
width:width
|
||||||
height:height
|
height:height
|
||||||
mipmapped:false];
|
mipmapped:false];
|
||||||
@ -823,8 +831,16 @@ static vertex_t vertex_bytes[] = {
|
|||||||
if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics))
|
if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics))
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
bool save_msl = true;
|
||||||
|
#else
|
||||||
|
bool save_msl = false;
|
||||||
|
#endif
|
||||||
|
NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex];
|
||||||
|
NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment];
|
||||||
|
|
||||||
|
// vertex descriptor
|
||||||
@try {
|
@try {
|
||||||
// vertex descriptor
|
|
||||||
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||||
vd.attributes[0].offset = offsetof(vertex_t, pos);
|
vd.attributes[0].offset = offsetof(vertex_t, pos);
|
||||||
vd.attributes[0].format = MTLVertexFormatFloat4;
|
vd.attributes[0].format = MTLVertexFormatFloat4;
|
||||||
@ -839,8 +855,11 @@ static vertex_t vertex_bytes[] = {
|
|||||||
psd.label = [NSString stringWithFormat:@"pass %d", i];
|
psd.label = [NSString stringWithFormat:@"pass %d", i];
|
||||||
|
|
||||||
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
|
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
|
||||||
ca.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
||||||
ca.blendingEnabled = YES;
|
ca.pixelFormat = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format));
|
||||||
|
|
||||||
|
// TODO(sgc): confirm we never need blending for render passes
|
||||||
|
ca.blendingEnabled = NO;
|
||||||
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||||
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||||
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||||
@ -848,19 +867,18 @@ static vertex_t vertex_bytes[] = {
|
|||||||
|
|
||||||
psd.sampleCount = 1;
|
psd.sampleCount = 1;
|
||||||
psd.vertexDescriptor = vd;
|
psd.vertexDescriptor = vd;
|
||||||
NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex];
|
|
||||||
NSLog(@"vertex function:\n%@", vs_src);
|
|
||||||
NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment];
|
|
||||||
NSLog(@"fragment function:\n%@", fs_src);
|
|
||||||
|
|
||||||
NSError *err;
|
NSError *err;
|
||||||
id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err];
|
id<MTLLibrary> lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err];
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
if (lib == nil) {
|
if (lib == nil) {
|
||||||
|
save_msl = true;
|
||||||
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
|
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String);
|
RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
psd.vertexFunction = [lib newFunctionWithName:@"main0"];
|
psd.vertexFunction = [lib newFunctionWithName:@"main0"];
|
||||||
@ -868,16 +886,20 @@ static vertex_t vertex_bytes[] = {
|
|||||||
lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err];
|
lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err];
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
if (lib == nil) {
|
if (lib == nil) {
|
||||||
|
save_msl = true;
|
||||||
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
|
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String);
|
RARCH_WARN("[Metal]: warnings compiling fragment shader: %s\n", err.localizedDescription.UTF8String);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
psd.fragmentFunction = [lib newFunctionWithName:@"main0"];
|
psd.fragmentFunction = [lib newFunctionWithName:@"main0"];
|
||||||
|
|
||||||
STRUCT_ASSIGN(_engine.pass[i]._state,
|
STRUCT_ASSIGN(_engine.pass[i]._state,
|
||||||
[_context.device newRenderPipelineStateWithDescriptor:psd error:&err]);
|
[_context.device newRenderPipelineStateWithDescriptor:psd error:&err]);
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
|
save_msl = true;
|
||||||
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String);
|
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@ -892,6 +914,29 @@ static vertex_t vertex_bytes[] = {
|
|||||||
STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf);
|
STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf);
|
||||||
}
|
}
|
||||||
} @finally {
|
} @finally {
|
||||||
|
if (save_msl) {
|
||||||
|
RARCH_LOG("[Metal]: saving metal shader files\n");
|
||||||
|
|
||||||
|
NSError *err = nil;
|
||||||
|
NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension];
|
||||||
|
[vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"]
|
||||||
|
atomically:NO
|
||||||
|
encoding:NSStringEncodingConversionAllowLossy
|
||||||
|
error:&err];
|
||||||
|
if (err != nil) {
|
||||||
|
RARCH_ERR("[Metal]: unable to save vertex shader source: %s\n", err.localizedDescription.UTF8String);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nil;
|
||||||
|
[fs_src writeToFile:[basePath stringByAppendingPathExtension:@"fs.metal"]
|
||||||
|
atomically:NO
|
||||||
|
encoding:NSStringEncodingConversionAllowLossy
|
||||||
|
error:&err];
|
||||||
|
if (err != nil) {
|
||||||
|
RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", err.localizedDescription.UTF8String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
free(shader->pass[i].source.string.vertex);
|
free(shader->pass[i].source.string.vertex);
|
||||||
free(shader->pass[i].source.string.fragment);
|
free(shader->pass[i].source.string.fragment);
|
||||||
|
|
||||||
@ -945,3 +990,67 @@ static vertex_t vertex_bytes[] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
MTLPixelFormat glslang_format_to_metal(glslang_format fmt)
|
||||||
|
{
|
||||||
|
#undef FMT2
|
||||||
|
#define FMT2(x,y) case SLANG_FORMAT_##x: return MTLPixelFormat##y
|
||||||
|
|
||||||
|
switch (fmt)
|
||||||
|
{
|
||||||
|
FMT2(R8_UNORM, R8Unorm);
|
||||||
|
FMT2(R8_SINT, R8Sint);
|
||||||
|
FMT2(R8_UINT, R8Uint);
|
||||||
|
FMT2(R8G8_UNORM, RG8Unorm);
|
||||||
|
FMT2(R8G8_SINT, RG8Sint);
|
||||||
|
FMT2(R8G8_UINT, RG8Uint);
|
||||||
|
FMT2(R8G8B8A8_UNORM, RGBA8Unorm);
|
||||||
|
FMT2(R8G8B8A8_SINT, RGBA8Sint);
|
||||||
|
FMT2(R8G8B8A8_UINT, RGBA8Uint);
|
||||||
|
FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB);
|
||||||
|
|
||||||
|
FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm);
|
||||||
|
FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint);
|
||||||
|
|
||||||
|
FMT2(R16_UINT, R16Uint);
|
||||||
|
FMT2(R16_SINT, R16Sint);
|
||||||
|
FMT2(R16_SFLOAT, R16Float);
|
||||||
|
FMT2(R16G16_UINT, RG16Uint);
|
||||||
|
FMT2(R16G16_SINT, RG16Sint);
|
||||||
|
FMT2(R16G16_SFLOAT, RG16Float);
|
||||||
|
FMT2(R16G16B16A16_UINT, RGBA16Uint);
|
||||||
|
FMT2(R16G16B16A16_SINT, RGBA16Sint);
|
||||||
|
FMT2(R16G16B16A16_SFLOAT, RGBA16Float);
|
||||||
|
|
||||||
|
FMT2(R32_UINT, R32Uint);
|
||||||
|
FMT2(R32_SINT, R32Sint);
|
||||||
|
FMT2(R32_SFLOAT, R32Float);
|
||||||
|
FMT2(R32G32_UINT, RG32Uint);
|
||||||
|
FMT2(R32G32_SINT, RG32Sint);
|
||||||
|
FMT2(R32G32_SFLOAT, RG32Float);
|
||||||
|
FMT2(R32G32B32A32_UINT, RGBA32Uint);
|
||||||
|
FMT2(R32G32B32A32_SINT, RGBA32Sint);
|
||||||
|
FMT2(R32G32B32A32_SFLOAT, RGBA32Float);
|
||||||
|
|
||||||
|
case SLANG_FORMAT_UNKNOWN:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#undef FMT2
|
||||||
|
return MTLPixelFormatInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt)
|
||||||
|
{
|
||||||
|
switch (fmt)
|
||||||
|
{
|
||||||
|
case MTLPixelFormatRGBA8Unorm:
|
||||||
|
return MTLPixelFormatBGRA8Unorm;
|
||||||
|
|
||||||
|
case MTLPixelFormatRGBA8Unorm_sRGB:
|
||||||
|
return MTLPixelFormatBGRA8Unorm_sRGB;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,13 +53,14 @@ static void *metal_init(const video_info_t *video,
|
|||||||
gfx_ctx_mode_t mode;
|
gfx_ctx_mode_t mode;
|
||||||
|
|
||||||
[apple_platform setViewType:APPLE_VIEW_TYPE_METAL];
|
[apple_platform setViewType:APPLE_VIEW_TYPE_METAL];
|
||||||
|
|
||||||
MetalDriver *md = [MetalDriver new];
|
MetalDriver *md = [MetalDriver new];
|
||||||
if (md == nil) {
|
if (md == nil) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
MetalView *view = (MetalView *)apple_platform.renderView;
|
||||||
|
view.delegate = md;
|
||||||
|
|
||||||
apple_platform.delegate = md;
|
md.keepAspect = video->force_aspect;
|
||||||
|
|
||||||
RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height);
|
RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height);
|
||||||
|
|
||||||
@ -67,8 +68,8 @@ static void *metal_init(const video_info_t *video,
|
|||||||
mode.height = video->height;
|
mode.height = video->height;
|
||||||
mode.fullscreen = video->fullscreen;
|
mode.fullscreen = video->fullscreen;
|
||||||
|
|
||||||
[apple_platform setVideoMode:mode];
|
|
||||||
[md setVideo:video];
|
[md setVideo:video];
|
||||||
|
[apple_platform setVideoMode:mode];
|
||||||
|
|
||||||
*input = NULL;
|
*input = NULL;
|
||||||
*input_data = NULL;
|
*input_data = NULL;
|
||||||
|
@ -36,22 +36,16 @@ typedef enum apple_view_type {
|
|||||||
APPLE_VIEW_TYPE_METAL,
|
APPLE_VIEW_TYPE_METAL,
|
||||||
} apple_view_type_t;
|
} apple_view_type_t;
|
||||||
|
|
||||||
@protocol PlatformDelegate
|
|
||||||
#ifdef HAVE_METAL
|
#ifdef HAVE_METAL
|
||||||
@optional
|
#import <MetalKit/MetalKit.h>
|
||||||
- (void)viewDidUpdateFrame:(NSRect)rect;
|
|
||||||
#endif
|
@interface MetalView : MTKView
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@protocol ApplePlatform
|
@protocol ApplePlatform
|
||||||
|
|
||||||
@property (readwrite,retain) id<PlatformDelegate> delegate;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief viewHandle returns an appropriate handle for the current view type
|
|
||||||
*/
|
|
||||||
@property (readonly) id viewHandle;
|
|
||||||
|
|
||||||
/*! @brief renderView returns the current render view based on the viewType */
|
/*! @brief renderView returns the current render view based on the viewType */
|
||||||
@property (readonly) id renderView;
|
@property (readonly) id renderView;
|
||||||
|
|
||||||
|
@ -19,9 +19,6 @@
|
|||||||
#include "cocoa_common.h"
|
#include "cocoa_common.h"
|
||||||
#ifdef HAVE_COCOA
|
#ifdef HAVE_COCOA
|
||||||
#include "../ui_cocoa.h"
|
#include "../ui_cocoa.h"
|
||||||
#ifdef HAVE_VULKAN
|
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <retro_assert.h>
|
#include <retro_assert.h>
|
||||||
@ -46,6 +43,21 @@
|
|||||||
#include "../../../location/location_driver.h"
|
#include "../../../location/location_driver.h"
|
||||||
#include "../../../camera/camera_driver.h"
|
#include "../../../camera/camera_driver.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_METAL
|
||||||
|
@implementation MetalView
|
||||||
|
|
||||||
|
- (void)keyDown:(NSEvent*)theEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop the annoying sound when pressing a key. */
|
||||||
|
- (BOOL)acceptsFirstResponder
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
static CocoaView* g_instance;
|
static CocoaView* g_instance;
|
||||||
|
|
||||||
#if defined(HAVE_COCOA)
|
#if defined(HAVE_COCOA)
|
||||||
@ -96,15 +108,13 @@ void *glkitview_init(void);
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_COCOA)
|
#if defined(HAVE_COCOA)
|
||||||
|
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setFrame:(NSRect)frameRect
|
- (void)setFrame:(NSRect)frameRect
|
||||||
{
|
{
|
||||||
[super setFrame:frameRect];
|
[super setFrame:frameRect];
|
||||||
|
|
||||||
if (apple_platform.delegate != nil)
|
|
||||||
{
|
|
||||||
[apple_platform.delegate viewDidUpdateFrame:frameRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
cocoagl_gfx_ctx_update();
|
cocoagl_gfx_ctx_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ id<ApplePlatform> apple_platform;
|
|||||||
NSWindow* _window;
|
NSWindow* _window;
|
||||||
apple_view_type_t _vt;
|
apple_view_type_t _vt;
|
||||||
NSView* _renderView;
|
NSView* _renderView;
|
||||||
id<PlatformDelegate> _delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (nonatomic, retain) NSWindow IBOutlet* window;
|
@property (nonatomic, retain) NSWindow IBOutlet* window;
|
||||||
@ -259,9 +258,9 @@ static char** waiting_argv;
|
|||||||
case APPLE_VIEW_TYPE_METAL:
|
case APPLE_VIEW_TYPE_METAL:
|
||||||
#if defined(HAVE_METAL) || defined(HAVE_VULKAN)
|
#if defined(HAVE_METAL) || defined(HAVE_VULKAN)
|
||||||
{
|
{
|
||||||
NSView *v = [CocoaView get];
|
MetalView *v = [MetalView new];
|
||||||
v.wantsLayer = YES;
|
v.paused = YES;
|
||||||
v.layer = CAMetalLayer.layer;
|
v.enableSetNeedsDisplay = NO;
|
||||||
_renderView = v;
|
_renderView = v;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -280,7 +279,7 @@ static char** waiting_argv;
|
|||||||
|
|
||||||
_renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
_renderView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
_renderView.frame = self.window.contentView.bounds;
|
_renderView.frame = self.window.contentView.bounds;
|
||||||
|
|
||||||
[self.window.contentView addSubview:_renderView];
|
[self.window.contentView addSubview:_renderView];
|
||||||
[self.window makeFirstResponder:_renderView];
|
[self.window makeFirstResponder:_renderView];
|
||||||
}
|
}
|
||||||
@ -293,18 +292,6 @@ static char** waiting_argv;
|
|||||||
return _renderView;
|
return _renderView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)delegate {
|
|
||||||
return _delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDelegate:(id<PlatformDelegate>)delegate {
|
|
||||||
_delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)viewHandle {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)hasFocus {
|
- (bool)hasFocus {
|
||||||
return [NSApp isActive];
|
return [NSApp isActive];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user