From 5b39cacc8a92274ab139342c9f5b9e63512a4212 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 15 Jun 2018 12:58:33 +0300 Subject: [PATCH] Basic Metal support. No OpenGL fallback, no shaders, no blending. --- Cocoa/GBView.m | 8 +-- Cocoa/GBViewMetal.h | 7 ++ Cocoa/GBViewMetal.m | 131 +++++++++++++++++++++++++++++++++++++ Makefile | 8 +-- Shaders/MasterShader.metal | 45 +++++++++++++ 5 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 Cocoa/GBViewMetal.h create mode 100644 Cocoa/GBViewMetal.m create mode 100644 Shaders/MasterShader.metal diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m index 92d0d5c..1504cdd 100644 --- a/Cocoa/GBView.m +++ b/Cocoa/GBView.m @@ -1,6 +1,7 @@ #import #import "GBView.h" #import "GBViewGL.h" +#import "GBViewMetal.h" #import "GBButtons.h" #import "NSString+StringForKey.h" @@ -19,16 +20,13 @@ + (instancetype)alloc { - if (self == [GBView class]) { - return [GBViewGL alloc]; - } - return [super alloc]; + return [self allocWithZone:NULL]; } + (instancetype)allocWithZone:(struct _NSZone *)zone { if (self == [GBView class]) { - return [GBViewGL allocWithZone: zone]; + return [GBViewMetal allocWithZone: zone]; } return [super allocWithZone:zone]; } diff --git a/Cocoa/GBViewMetal.h b/Cocoa/GBViewMetal.h new file mode 100644 index 0000000..a28a343 --- /dev/null +++ b/Cocoa/GBViewMetal.h @@ -0,0 +1,7 @@ +#import +#import +#import "GBView.h" + +@interface GBViewMetal : GBView + +@end diff --git a/Cocoa/GBViewMetal.m b/Cocoa/GBViewMetal.m new file mode 100644 index 0000000..157ddf3 --- /dev/null +++ b/Cocoa/GBViewMetal.m @@ -0,0 +1,131 @@ +#import "GBViewMetal.h" + +#define WIDTH 160 +#define HEIGHT 144 +#define PITCH (160 * 4) + +static const MTLRegion region = { + {0, 0, 0}, // MTLOrigin + {WIDTH, HEIGHT, 1} // MTLSize +}; + +static const vector_float2 rect[] = +{ + {-1, -1}, + { 1, -1}, + {-1, 1}, + { 1, 1}, +}; + +@implementation GBViewMetal +{ + id device; + id texture, previous_texture; + id vertices; + id pipeline_state; + id command_queue; +} + +- (void)createInternalView +{ + MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())]; + view.delegate = self; + self.internalView = view; + + MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init]; + + texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; + + texture_descriptor.width = WIDTH; + texture_descriptor.height = HEIGHT; + + texture = [device newTextureWithDescriptor:texture_descriptor]; + previous_texture = [device newTextureWithDescriptor:texture_descriptor]; + + vertices = [device newBufferWithBytes:rect + length:sizeof(rect) + options:MTLResourceStorageModeShared]; + + [self loadShader]; +} + +- (void) loadShader +{ + NSError *error = nil; + NSString *shader_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MasterShader" + ofType:@"metal" + inDirectory:@"Shaders"] + encoding:NSUTF8StringEncoding + error:nil]; + id library = [device newLibraryWithSource:shader_source + options:nil + error:&error]; + if (error) { + NSLog(@"Error: %@", error); + } + + id vertex_function = [library newFunctionWithName:@"vertex_shader"]; + id fragment_function = [library newFunctionWithName:@"fragment_shader"]; + + // Set up a descriptor for creating a pipeline state object + MTLRenderPipelineDescriptor *pipeline_state_descriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipeline_state_descriptor.vertexFunction = vertex_function; + pipeline_state_descriptor.fragmentFunction = fragment_function; + pipeline_state_descriptor.colorAttachments[0].pixelFormat = ((MTKView *)self.internalView).colorPixelFormat; + + error = nil; + pipeline_state = [device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor + error:&error]; + if (error) { + NSLog(@"Failed to created pipeline state, error %@", error); + } + + command_queue = [device newCommandQueue]; +} + +- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size +{ +} + +- (void)drawInMTKView:(nonnull MTKView *)view +{ + [texture replaceRegion:region + mipmapLevel:0 + withBytes:[self currentBuffer] + bytesPerRow:PITCH]; + + MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor; + id command_buffer = [command_queue commandBuffer]; + + if(render_pass_descriptor != nil) + { + id render_encoder = + [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + + [render_encoder setViewport:(MTLViewport){0.0, 0.0, + view.bounds.size.width * view.window.backingScaleFactor, + view.bounds.size.height * view.window.backingScaleFactor, + -1.0, 1.0}]; + + [render_encoder setRenderPipelineState:pipeline_state]; + + [render_encoder setVertexBuffer:vertices + offset:0 + atIndex:0]; + + [render_encoder setFragmentTexture:texture + atIndex:0]; + + [render_encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip + vertexStart:0 + vertexCount:4]; + + [render_encoder endEncoding]; + + [command_buffer presentDrawable:view.currentDrawable]; + } + + + [command_buffer commit]; +} +@end diff --git a/Makefile b/Makefile index f32e931..afd56b6 100755 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ ifeq ($(PLATFORM),Darwin) SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> /dev/null) CFLAGS += -F/Library/Frameworks OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -mmacosx-version-min=10.9 -LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore +LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -framework Metal -framework MetalKit SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2 -framework OpenGL endif CFLAGS += -Wno-deprecated-declarations @@ -160,8 +160,6 @@ $(OBJ)/%.m.o: %.m # Cocoa Port -Shaders:$(shell ls Shaders/*.fsh) - $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \ $(shell ls Cocoa/*.icns) \ Cocoa/License.html \ @@ -178,7 +176,7 @@ $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \ sed s/@VERSION/$(VERSION)/ < Cocoa/Info.plist > $(BIN)/SameBoy.app/Contents/Info.plist cp Cocoa/License.html $(BIN)/SameBoy.app/Contents/Resources/Credits.html $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources/Shaders - cp Shaders/*.fsh $(BIN)/SameBoy.app/Contents/Resources/Shaders + cp Shaders/*.fsh Shaders/*.metal $(BIN)/SameBoy.app/Contents/Resources/Shaders $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Library/QuickLook/ cp -rf $(BIN)/SameBoy.qlgenerator $(BIN)/SameBoy.app/Contents/Library/QuickLook/ @@ -289,7 +287,7 @@ $(BIN)/SDL/background.bmp: SDL/background.bmp $(BIN)/SDL/Shaders: Shaders -@$(MKDIR) -p $(dir $@) - cp -rf $^ $@ + cp -rf Shaders/*.fsh $@ # Boot ROMs diff --git a/Shaders/MasterShader.metal b/Shaders/MasterShader.metal new file mode 100644 index 0000000..a515764 --- /dev/null +++ b/Shaders/MasterShader.metal @@ -0,0 +1,45 @@ +#include +#include +#include + +using namespace metal; + +/* For GLSL compatibility */ +typedef float2 vec2; +typedef float3 vec3; +typedef float4 vec4; + +typedef struct { + float4 position [[position]]; + float2 texcoords; +} rasterizer_data; + +// Vertex Function +vertex rasterizer_data vertex_shader(uint index [[ vertex_id ]], + constant vector_float2 *vertices [[ buffer(0) ]]) + +{ + rasterizer_data out; + + out.position.xy = vertices[index].xy; + out.position.z = 0.0; + out.position.w = 1.0; + out.texcoords = (vertices[index].xy + float2(1, 1)) / 2.0; + + return out; +} + + +static inline float4 texture(texture2d texture, float2 pos) +{ + constexpr sampler texture_sampler; + return float4(texture.sample(texture_sampler, pos)); +} + +fragment float4 fragment_shader(rasterizer_data in [[stage_in]], + texture2d image [[ texture(0) ]]) +{ + in.texcoords.y = 1 - in.texcoords.y; + return texture(image, in.texcoords); +} +