git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

commit 4c5d89663016a4e8c49a1ee2f268d8026b11dace
parent 093998389c547e50f41fb3131136d0ecbc14bd39
Author: Lior Halphon <LIJI32@gmail.com>
Date:   Fri, 13 Jan 2023 21:57:57 +0200

Porting GBViewMetal

Diffstat:
AAppleCommon/GBViewBase.h35+++++++++++++++++++++++++++++++++++
AAppleCommon/GBViewBase.m83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AAppleCommon/GBViewMetal.h11+++++++++++
AAppleCommon/GBViewMetal.m244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MCocoa/GBView.h24++++--------------------
MCocoa/GBView.m61+++----------------------------------------------------------
DCocoa/GBViewMetal.h7-------
DCocoa/GBViewMetal.m232-------------------------------------------------------------------------------
AiOS/GBView.h5+++++
AiOS/GBView.m12++++++++++++
MiOS/GBViewController.m36++++++++++++++++++++++++++++--------
11 files changed, 425 insertions(+), 325 deletions(-)

diff --git a/AppleCommon/GBViewBase.h b/AppleCommon/GBViewBase.h @@ -0,0 +1,35 @@ +#include <TargetConditionals.h> +#include <Core/gb.h> + +#if TARGET_OS_IPHONE +#define NSView UIView +#import <UIKit/UIKit.h> +#else +#import <Cocoa/Cocoa.h> +#endif + +typedef enum { + GB_FRAME_BLENDING_MODE_DISABLED, + GB_FRAME_BLENDING_MODE_SIMPLE, + GB_FRAME_BLENDING_MODE_ACCURATE, + GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE, + GB_FRAME_BLENDING_MODE_ACCURATE_ODD, +} GB_frame_blending_mode_t; + +@interface GBViewBase : NSView +{ + @public + GB_gameboy_t *_gb; +} + +@property (nonatomic) GB_gameboy_t *gb; +@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode; +@property (nonatomic, strong) NSView *internalView; +- (void) flip; +- (uint32_t *) pixels; +- (void)screenSizeChanged; +- (void) createInternalView; +- (uint32_t *)currentBuffer; +- (uint32_t *)previousBuffer; + +@end diff --git a/AppleCommon/GBViewBase.m b/AppleCommon/GBViewBase.m @@ -0,0 +1,83 @@ +#import "GBViewBase.h" + +@implementation GBViewBase +{ + uint32_t *_imageBuffers[3]; + unsigned _currentBuffer; + GB_frame_blending_mode_t _frameBlendingMode; +} + +- (void)screenSizeChanged +{ + if (_imageBuffers[0]) free(_imageBuffers[0]); + if (_imageBuffers[1]) free(_imageBuffers[1]); + if (_imageBuffers[2]) free(_imageBuffers[2]); + + size_t buffer_size = sizeof(_imageBuffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb); + + _imageBuffers[0] = calloc(1, buffer_size); + _imageBuffers[1] = calloc(1, buffer_size); + _imageBuffers[2] = calloc(1, buffer_size); +} + +- (void)flip +{ + _currentBuffer = (_currentBuffer + 1) % self.numberOfBuffers; +} + +- (unsigned) numberOfBuffers +{ + return _frameBlendingMode? 3 : 2; +} + +- (void) createInternalView +{ + assert(false && "createInternalView must not be inherited"); +} + +- (uint32_t *)currentBuffer +{ + return _imageBuffers[_currentBuffer]; +} + +- (uint32_t *)previousBuffer +{ + return _imageBuffers[(_currentBuffer + 2) % self.numberOfBuffers]; +} + +- (uint32_t *) pixels +{ + return _imageBuffers[(_currentBuffer + 1) % self.numberOfBuffers]; +} + +- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode +{ + _frameBlendingMode = frameBlendingMode; + [self setNeedsDisplay]; +} + +- (GB_frame_blending_mode_t)frameBlendingMode +{ + if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) { + if (!_gb || GB_is_sgb(_gb)) { + return GB_FRAME_BLENDING_MODE_SIMPLE; + } + return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN; + } + return _frameBlendingMode; +} + +- (void)dealloc +{ + free(_imageBuffers[0]); + free(_imageBuffers[1]); + free(_imageBuffers[2]); +} + +#if !TARGET_OS_IPHONE +- (void)setNeedsDisplay +{ + [self setNeedsDisplay:true]; +} +#endif +@end diff --git a/AppleCommon/GBViewMetal.h b/AppleCommon/GBViewMetal.h @@ -0,0 +1,11 @@ +#include <TargetConditionals.h> +#import <MetalKit/MetalKit.h> +#if TARGET_OS_IPHONE +#import "../iOS/GBView.h" +#else +#import "../Cocoa/GBView.h" +#endif + +@interface GBViewMetal : GBView<MTKViewDelegate> ++ (bool) isSupported; +@end diff --git a/AppleCommon/GBViewMetal.m b/AppleCommon/GBViewMetal.m @@ -0,0 +1,244 @@ +#import <CoreImage/CoreImage.h> +#import "GBViewMetal.h" +#pragma clang diagnostic ignored "-Wpartial-availability" + + +static const vector_float2 rect[] = +{ + {-1, -1}, + { 1, -1}, + {-1, 1}, + { 1, 1}, +}; + +@implementation GBViewMetal +{ + id<MTLDevice> device; + id<MTLTexture> texture, previous_texture; + id<MTLBuffer> vertices; + id<MTLRenderPipelineState> pipeline_state; + id<MTLCommandQueue> command_queue; + id<MTLBuffer> frame_blending_mode_buffer; + id<MTLBuffer> output_resolution_buffer; + vector_float2 output_resolution; +} + ++ (bool)isSupported +{ +#if TARGET_OS_IPHONE + return true; +#else + if (MTLCopyAllDevices) { + return [MTLCopyAllDevices() count]; + } + return false; +#endif +} + +- (void) allocateTextures +{ + if (!device) return; + + MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init]; + + texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; + + texture_descriptor.width = GB_get_screen_width(self.gb); + texture_descriptor.height = GB_get_screen_height(self.gb); + + texture = [device newTextureWithDescriptor:texture_descriptor]; + previous_texture = [device newTextureWithDescriptor:texture_descriptor]; + +} + +- (void)createInternalView +{ + MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())]; + view.delegate = self; + self.internalView = view; + view.paused = true; + view.enableSetNeedsDisplay = true; + view.framebufferOnly = false; + + vertices = [device newBufferWithBytes:rect + length:sizeof(rect) + options:MTLResourceStorageModeShared]; + + static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED; + frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode + length:sizeof(default_blending_mode) + options:MTLResourceStorageModeShared]; + + output_resolution_buffer = [device newBufferWithBytes:&output_resolution + length:sizeof(output_resolution) + options:MTLResourceStorageModeShared]; + + output_resolution = (simd_float2){view.drawableSize.width, view.drawableSize.height}; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadShader) name:@"GBFilterChanged" object:nil]; + [self loadShader]; +} + +- (void) loadShader +{ + NSError *error = nil; + NSString *shader_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MasterShader" + ofType:@"metal" + inDirectory:@"Shaders"] + encoding:NSUTF8StringEncoding + error:nil]; + + NSString *shader_name = [[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]; + NSString *scaler_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:shader_name + ofType:@"fsh" + inDirectory:@"Shaders"] + encoding:NSUTF8StringEncoding + error:nil]; + + shader_source = [shader_source stringByReplacingOccurrencesOfString:@"{filter}" + withString:scaler_source]; + + MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; + options.fastMathEnabled = true; + id<MTLLibrary> library = [device newLibraryWithSource:shader_source + options:options + error:&error]; + if (error) { + NSLog(@"Error: %@", error); + if (!library) { + return; + } + } + + id<MTLFunction> vertex_function = [library newFunctionWithName:@"vertex_shader"]; + id<MTLFunction> 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); + return; + } + + command_queue = [device newCommandQueue]; +} + +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size +{ + output_resolution = (vector_float2){size.width, size.height}; + dispatch_async(dispatch_get_main_queue(), ^{ + [(MTKView *)self.internalView draw]; + }); +} + +- (void)drawInMTKView:(MTKView *)view +{ +#if !TARGET_OS_IPHONE + if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return; +#endif + if (!self.gb) return; + if (texture.width != GB_get_screen_width(self.gb) || + texture.height != GB_get_screen_height(self.gb)) { + [self allocateTextures]; + } + + MTLRegion region = { + {0, 0, 0}, // MTLOrigin + {texture.width, texture.height, 1} // MTLSize + }; + + [texture replaceRegion:region + mipmapLevel:0 + withBytes:[self currentBuffer] + bytesPerRow:texture.width * 4]; + if ([self frameBlendingMode]) { + [previous_texture replaceRegion:region + mipmapLevel:0 + withBytes:[self previousBuffer] + bytesPerRow:texture.width * 4]; + } + + MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor; + id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer]; + + if (render_pass_descriptor) { + *(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode]; + *(vector_float2 *)[output_resolution_buffer contents] = output_resolution; + + id<MTLRenderCommandEncoder> render_encoder = + [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + + [render_encoder setViewport:(MTLViewport){0.0, 0.0, + output_resolution.x, + output_resolution.y, + -1.0, 1.0}]; + + [render_encoder setRenderPipelineState:pipeline_state]; + + [render_encoder setVertexBuffer:vertices + offset:0 + atIndex:0]; + + [render_encoder setFragmentBuffer:frame_blending_mode_buffer + offset:0 + atIndex:0]; + + [render_encoder setFragmentBuffer:output_resolution_buffer + offset:0 + atIndex:1]; + + [render_encoder setFragmentTexture:texture + atIndex:0]; + + [render_encoder setFragmentTexture:previous_texture + atIndex:1]; + + [render_encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip + vertexStart:0 + vertexCount:4]; + + [render_encoder endEncoding]; + + [command_buffer presentDrawable:view.currentDrawable]; + } + + + [command_buffer commit]; +} + +- (void)flip +{ + [super flip]; + dispatch_async(dispatch_get_main_queue(), ^{ +#if TARGET_OS_IPHONE + [(MTKView *)self.internalView setNeedsDisplay]; +#else + [(MTKView *)self.internalView setNeedsDisplay:true]; +#endif + }); +} + +#if !TARGET_OS_IPHONE +- (NSImage *)renderToImage +{ + CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture] + options:@{ + kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB() + }]; + ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), + 0, ciImage.extent.size.height)]; + CIContext *context = [CIContext context]; + CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent]; + NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size]; + CGImageRelease(cgImage); + return ret; +} +#endif + +@end diff --git a/Cocoa/GBView.h b/Cocoa/GBView.h @@ -1,32 +1,16 @@ #import <Cocoa/Cocoa.h> -#include <Core/gb.h> #import <JoyKit/JoyKit.h> #import "GBOSDView.h" +#import "GBViewBase.h" -@class Document; -typedef enum { - GB_FRAME_BLENDING_MODE_DISABLED, - GB_FRAME_BLENDING_MODE_SIMPLE, - GB_FRAME_BLENDING_MODE_ACCURATE, - GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE, - GB_FRAME_BLENDING_MODE_ACCURATE_ODD, -} GB_frame_blending_mode_t; +@class Document; -@interface GBView : NSView<JOYListener> -- (void) flip; -- (uint32_t *) pixels; +@interface GBView : GBViewBase<JOYListener> @property (nonatomic, weak) IBOutlet Document *document; -@property (nonatomic) GB_gameboy_t *gb; -@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode; @property (nonatomic, getter=isMouseHidingEnabled) bool mouseHidingEnabled; @property (nonatomic) bool isRewinding; -@property (nonatomic, strong) NSView *internalView; @property (weak) GBOSDView *osdView; -- (void) createInternalView; -- (uint32_t *)currentBuffer; -- (uint32_t *)previousBuffer; -- (void)screenSizeChanged; -- (void)setRumble: (double)amp; - (NSImage *)renderToImage; +- (void)setRumble: (double)amp; @end diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m @@ -104,8 +104,6 @@ static const uint8_t workboy_vk_to_key[] = { @implementation GBView { - uint32_t *image_buffers[3]; - unsigned char current_buffer; bool mouse_hidden; NSTrackingArea *tracking_area; bool _mouseHidingEnabled; @@ -116,7 +114,6 @@ static const uint8_t workboy_vk_to_key[] = { bool analogClockMultiplierValid; NSEventModifierFlags previousModifiers; JOYController *lastController; - GB_frame_blending_mode_t _frameBlendingMode; bool _turbo; bool _mouseControlEnabled; } @@ -137,11 +134,6 @@ static const uint8_t workboy_vk_to_key[] = { return [super allocWithZone:zone]; } -- (void) createInternalView -{ - assert(false && "createInternalView must not be inherited"); -} - - (void) _init { [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]]; @@ -162,15 +154,7 @@ static const uint8_t workboy_vk_to_key[] = { - (void)screenSizeChanged { - if (image_buffers[0]) free(image_buffers[0]); - if (image_buffers[1]) free(image_buffers[1]); - if (image_buffers[2]) free(image_buffers[2]); - - size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb); - - image_buffers[0] = calloc(1, buffer_size); - image_buffers[1] = calloc(1, buffer_size); - image_buffers[2] = calloc(1, buffer_size); + [super screenSizeChanged]; dispatch_async(dispatch_get_main_queue(), ^{ [self setFrame:self.superview.frame]; @@ -182,33 +166,8 @@ static const uint8_t workboy_vk_to_key[] = { [self setFrame:self.superview.frame]; } -- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode -{ - _frameBlendingMode = frameBlendingMode; - [self setNeedsDisplay:true]; -} - - -- (GB_frame_blending_mode_t)frameBlendingMode -{ - if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) { - if (!_gb || GB_is_sgb(_gb)) { - return GB_FRAME_BLENDING_MODE_SIMPLE; - } - return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN; - } - return _frameBlendingMode; -} -- (unsigned char) numberOfBuffers -{ - return _frameBlendingMode? 3 : 2; -} - - (void)dealloc { - free(image_buffers[0]); - free(image_buffers[1]); - free(image_buffers[2]); if (mouse_hidden) { mouse_hidden = false; [NSCursor unhide]; @@ -217,6 +176,7 @@ static const uint8_t workboy_vk_to_key[] = { [self setRumble:0]; [JOYController unregisterListener:self]; } + - (instancetype)initWithCoder:(NSCoder *)coder { if (!(self = [super initWithCoder:coder])) { @@ -301,12 +261,7 @@ static const uint8_t workboy_vk_to_key[] = { (analogClockMultiplierValid && analogClockMultiplier < 1)) { [self.osdView displayText:@"Slow motion..."]; } - current_buffer = (current_buffer + 1) % self.numberOfBuffers; -} - -- (uint32_t *) pixels -{ - return image_buffers[(current_buffer + 1) % self.numberOfBuffers]; + [super flip]; } -(void)keyDown:(NSEvent *)theEvent @@ -760,16 +715,6 @@ static const uint8_t workboy_vk_to_key[] = { previousModifiers = event.modifierFlags; } -- (uint32_t *)currentBuffer -{ - return image_buffers[current_buffer]; -} - -- (uint32_t *)previousBuffer -{ - return image_buffers[(current_buffer + 2) % self.numberOfBuffers]; -} - -(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { NSPasteboard *pboard = [sender draggingPasteboard]; diff --git a/Cocoa/GBViewMetal.h b/Cocoa/GBViewMetal.h @@ -1,7 +0,0 @@ -#import <Cocoa/Cocoa.h> -#import <MetalKit/MetalKit.h> -#import "GBView.h" - -@interface GBViewMetal : GBView<MTKViewDelegate> -+ (bool) isSupported; -@end diff --git a/Cocoa/GBViewMetal.m b/Cocoa/GBViewMetal.m @@ -1,232 +0,0 @@ -#import <CoreImage/CoreImage.h> -#import "GBViewMetal.h" -#pragma clang diagnostic ignored "-Wpartial-availability" - - -static const vector_float2 rect[] = -{ - {-1, -1}, - { 1, -1}, - {-1, 1}, - { 1, 1}, -}; - -@implementation GBViewMetal -{ - id<MTLDevice> device; - id<MTLTexture> texture, previous_texture; - id<MTLBuffer> vertices; - id<MTLRenderPipelineState> pipeline_state; - id<MTLCommandQueue> command_queue; - id<MTLBuffer> frame_blending_mode_buffer; - id<MTLBuffer> output_resolution_buffer; - vector_float2 output_resolution; -} - -+ (bool)isSupported -{ - if (MTLCopyAllDevices) { - return [MTLCopyAllDevices() count]; - } - return false; -} - -- (void) allocateTextures -{ - if (!device) return; - - MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init]; - - texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; - - texture_descriptor.width = GB_get_screen_width(self.gb); - texture_descriptor.height = GB_get_screen_height(self.gb); - - texture = [device newTextureWithDescriptor:texture_descriptor]; - previous_texture = [device newTextureWithDescriptor:texture_descriptor]; - -} - -- (void)createInternalView -{ - MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())]; - view.delegate = self; - self.internalView = view; - view.paused = true; - view.enableSetNeedsDisplay = true; - view.framebufferOnly = false; - - vertices = [device newBufferWithBytes:rect - length:sizeof(rect) - options:MTLResourceStorageModeShared]; - - static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED; - frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode - length:sizeof(default_blending_mode) - options:MTLResourceStorageModeShared]; - - output_resolution_buffer = [device newBufferWithBytes:&output_resolution - length:sizeof(output_resolution) - options:MTLResourceStorageModeShared]; - - output_resolution = (simd_float2){view.drawableSize.width, view.drawableSize.height}; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadShader) name:@"GBFilterChanged" object:nil]; - [self loadShader]; -} - -- (void) loadShader -{ - NSError *error = nil; - NSString *shader_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MasterShader" - ofType:@"metal" - inDirectory:@"Shaders"] - encoding:NSUTF8StringEncoding - error:nil]; - - NSString *shader_name = [[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]; - NSString *scaler_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:shader_name - ofType:@"fsh" - inDirectory:@"Shaders"] - encoding:NSUTF8StringEncoding - error:nil]; - - shader_source = [shader_source stringByReplacingOccurrencesOfString:@"{filter}" - withString:scaler_source]; - - MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; - options.fastMathEnabled = true; - id<MTLLibrary> library = [device newLibraryWithSource:shader_source - options:options - error:&error]; - if (error) { - NSLog(@"Error: %@", error); - if (!library) { - return; - } - } - - id<MTLFunction> vertex_function = [library newFunctionWithName:@"vertex_shader"]; - id<MTLFunction> 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); - return; - } - - command_queue = [device newCommandQueue]; -} - -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size -{ - output_resolution = (vector_float2){size.width, size.height}; - dispatch_async(dispatch_get_main_queue(), ^{ - [(MTKView *)self.internalView draw]; - }); -} - -- (void)drawInMTKView:(MTKView *)view -{ - if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return; - if (!self.gb) return; - if (texture.width != GB_get_screen_width(self.gb) || - texture.height != GB_get_screen_height(self.gb)) { - [self allocateTextures]; - } - - MTLRegion region = { - {0, 0, 0}, // MTLOrigin - {texture.width, texture.height, 1} // MTLSize - }; - - [texture replaceRegion:region - mipmapLevel:0 - withBytes:[self currentBuffer] - bytesPerRow:texture.width * 4]; - if ([self frameBlendingMode]) { - [previous_texture replaceRegion:region - mipmapLevel:0 - withBytes:[self previousBuffer] - bytesPerRow:texture.width * 4]; - } - - MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor; - id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer]; - - if (render_pass_descriptor != nil) { - *(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode]; - *(vector_float2 *)[output_resolution_buffer contents] = output_resolution; - - id<MTLRenderCommandEncoder> render_encoder = - [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; - - [render_encoder setViewport:(MTLViewport){0.0, 0.0, - output_resolution.x, - output_resolution.y, - -1.0, 1.0}]; - - [render_encoder setRenderPipelineState:pipeline_state]; - - [render_encoder setVertexBuffer:vertices - offset:0 - atIndex:0]; - - [render_encoder setFragmentBuffer:frame_blending_mode_buffer - offset:0 - atIndex:0]; - - [render_encoder setFragmentBuffer:output_resolution_buffer - offset:0 - atIndex:1]; - - [render_encoder setFragmentTexture:texture - atIndex:0]; - - [render_encoder setFragmentTexture:previous_texture - atIndex:1]; - - [render_encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip - vertexStart:0 - vertexCount:4]; - - [render_encoder endEncoding]; - - [command_buffer presentDrawable:view.currentDrawable]; - } - - - [command_buffer commit]; -} - -- (void)flip -{ - [super flip]; - dispatch_async(dispatch_get_main_queue(), ^{ - [(MTKView *)self.internalView setNeedsDisplay:true]; - }); -} - -- (NSImage *)renderToImage -{ - CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture] - options:@{ - kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB() - }]; - ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), - 0, ciImage.extent.size.height)]; - CIContext *context = [CIContext context]; - CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent]; - NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size]; - CGImageRelease(cgImage); - return ret; -} - -@end diff --git a/iOS/GBView.h b/iOS/GBView.h @@ -0,0 +1,5 @@ +#import "GBViewBase.h" + +@interface GBView : GBViewBase + +@end diff --git a/iOS/GBView.m b/iOS/GBView.m @@ -0,0 +1,12 @@ +#import "GBView.h" + +@implementation GBView +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + [self createInternalView]; + [self addSubview:self.internalView]; + self.internalView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + return self; +} +@end diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m @@ -1,6 +1,8 @@ #import "GBViewController.h" #import "GBHorizontalLayout.h" #import "GBVerticalLayout.h" +#import "GBViewMetal.h" +#include <Core/gb.h> static void positionView(UIImageView *view, CGPoint position) { @@ -26,6 +28,8 @@ static void positionView(UIImageView *view, CGPoint position) UIImageView *_bButtonView; UIImageView *_startButtonView; UIImageView *_selectButtonView; + GBView *_gbView; + GB_gameboy_t _gb; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions @@ -33,36 +37,42 @@ static void positionView(UIImageView *view, CGPoint position) _window = [[UIWindow alloc] init]; _window.rootViewController = self; [_window makeKeyAndVisible]; + + _window.backgroundColor = [UIColor colorWithRed:174 / 255.0 green:176 / 255.0 blue:180 / 255.0 alpha:1.0]; + _horizontalLayout = [[GBHorizontalLayout alloc] init]; _verticalLayout = [[GBVerticalLayout alloc] init]; _backgroundView = [[UIImageView alloc] initWithImage:nil]; [_window addSubview:_backgroundView]; + self.view = _backgroundView; _dpadView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dpad"]]; _aButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; _bButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; _startButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; _selectButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; + _gbView = [[GBViewMetal alloc] initWithFrame:CGRectZero]; + + GB_init(&_gb, GB_MODEL_CGB_E); + _gbView.gb = &_gb; + [_gbView screenSizeChanged]; + + [self willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation + duration:0]; [_backgroundView addSubview:_dpadView]; [_backgroundView addSubview:_aButtonView]; [_backgroundView addSubview:_bButtonView]; [_backgroundView addSubview:_startButtonView]; [_backgroundView addSubview:_selectButtonView]; + [_backgroundView addSubview:_gbView]; - [self orientationChange]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(orientationChange) - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; return true; } -- (void)orientationChange +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration { - UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) { _currentLayout = _verticalLayout; } @@ -78,6 +88,16 @@ static void positionView(UIImageView *view, CGPoint position) positionView(_bButtonView, _currentLayout.bLocation); positionView(_startButtonView, _currentLayout.startLocation); positionView(_selectButtonView, _currentLayout.selectLocation); + + CGRect screenFrame = _currentLayout.screenRect; + screenFrame.origin.x /= [UIScreen mainScreen].scale; + screenFrame.origin.y /= [UIScreen mainScreen].scale; + screenFrame.size.width /= [UIScreen mainScreen].scale; + screenFrame.size.height /= [UIScreen mainScreen].scale; + + _gbView.frame = screenFrame; + memset(_gbView.pixels, rand(), 160 * 144 * 4); + [_gbView flip]; } - (BOOL)prefersHomeIndicatorAutoHidden

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.