diff --git a/src/ios/iOSMiniFB.m b/src/ios/iOSMiniFB.m index 5b763d9..0401671 100644 --- a/src/ios/iOSMiniFB.m +++ b/src/ios/iOSMiniFB.m @@ -11,7 +11,7 @@ SWindowData * create_window_data(unsigned width, unsigned height) { SWindowData *window_data; - + window_data = malloc(sizeof(SWindowData)); if(window_data == 0x0) { NSLog(@"Cannot allocate window data"); @@ -48,7 +48,7 @@ create_window_data(unsigned width, unsigned height) { NSLog(@"Unable to create draw buffer"); return 0x0; } - + return window_data; } @@ -67,20 +67,11 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) kUnused(title); kUnused(flags); - + SWindowData *window_data = create_window_data(width, height); if (window_data == 0x0) { return 0x0; } - SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific; - - static Vertex s_vertices[4] = { - {-1.0, -1.0, 0, 1}, - {-1.0, 1.0, 0, 1}, - { 1.0, -1.0, 0, 1}, - { 1.0, 1.0, 0, 1}, - }; - memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices)); windows = [[UIApplication sharedApplication] windows]; numWindows = [windows count]; @@ -94,7 +85,7 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; NSLog(@"UIApplication has no window. We create one (%f, %f).", [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); } - + if([window.rootViewController isKindOfClass:[iOSViewController class]] == false) { iOSViewController *controller = [[iOSViewController alloc] initWithWindowData:window_data]; [window setRootViewController:controller]; @@ -216,7 +207,7 @@ mfb_timer_tick() { if (timebase.denom == 0) { (void) mach_timebase_info(&timebase); } - + uint64_t time = mach_absolute_time(); //return (time * s_timebase_info.numer) / s_timebase_info.denom; @@ -226,7 +217,7 @@ mfb_timer_tick() { uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom; uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom; high /= timebase.denom; - + return (high << 32) + highRem + low; } diff --git a/src/ios/iOSViewDelegate.h b/src/ios/iOSViewDelegate.h index 03c00fe..b434760 100644 --- a/src/ios/iOSViewDelegate.h +++ b/src/ios/iOSViewDelegate.h @@ -8,15 +8,12 @@ #import #include "WindowData.h" -#include "WindowData_IOS.h" // Our platform independent renderer class. // Implements the MTKViewDelegate protocol which allows it to accept per-frame // update and drawable resize callbacks. @interface iOSViewDelegate : NSObject { - @public SWindowData *window_data; - @public SWindowData_IOS *window_data_ios; } -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData; diff --git a/src/ios/iOSViewDelegate.m b/src/ios/iOSViewDelegate.m index abba8d9..6162693 100644 --- a/src/ios/iOSViewDelegate.m +++ b/src/ios/iOSViewDelegate.m @@ -21,25 +21,23 @@ //------------------------------------- enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered) -id g_metal_device = nil; -id g_library = nil; - //-- NSString *g_shader_src = kShader( "#include \n", using namespace metal; - //--------------------- + //------------- struct VertexOutput { float4 pos [[position]]; float2 texcoord; }; + //------------- struct Vertex { float4 position [[position]]; }; - //--------------------- + //------------- vertex VertexOutput vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) { VertexOutput out; @@ -52,7 +50,7 @@ NSString *g_shader_src = kShader( return out; } - //--------------------- + //------------- fragment float4 fragFunc(VertexOutput input [[stage_in]], texture2d colorTexture [[ texture(0) ]]) { constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest); @@ -67,29 +65,38 @@ NSString *g_shader_src = kShader( //------------------------------------- @implementation iOSViewDelegate { - dispatch_semaphore_t m_semaphore; - id m_command_queue; + SWindowData *window_data; + SWindowData_IOS *window_data_ios; + + id metal_device; + id metal_library; - id m_pipeline_state; - id m_texture_buffer; + dispatch_semaphore_t semaphore; + id command_queue; - uint8_t m_current_buffer; + id pipeline_state; + id texture_buffer; + + uint8_t current_buffer; } //------------------------------------- -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData { self = [super init]; if (self) { - self->window_data = windowData; - self->window_data_ios = (SWindowData_IOS *) windowData->specific; + window_data = windowData; + window_data_ios = (SWindowData_IOS *) windowData->specific; view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; view.sampleCount = 1; - g_metal_device = view.device; + metal_device = view.device; - m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight); - m_command_queue = [g_metal_device newCommandQueue]; + // Used for syncing the CPU and GPU + semaphore = dispatch_semaphore_create(MaxBuffersInFlight); + + // Setup command queue + command_queue = [metal_device newCommandQueue]; [self _createShaders]; [self _createAssets]; @@ -100,19 +107,19 @@ NSString *g_shader_src = kShader( //------------------------------------- - (bool) _createShaders { - NSError *error = nil; - - g_library = [g_metal_device newLibraryWithSource:g_shader_src - options:[[MTLCompileOptions alloc] init] - error:&error + NSError *error = 0x0; + + metal_library = [metal_device newLibraryWithSource:g_shader_src + options:[[MTLCompileOptions alloc] init] + error:&error ]; - if (error || !g_library) { + if (error || !metal_library) { NSLog(@"Unable to create shaders %@", error); return false; } - id vertex_shader_func = [g_library newFunctionWithName:@"vertFunc"]; - id fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"]; + id vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"]; + id fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"]; if (!vertex_shader_func) { NSLog(@"Unable to get vertFunc!\n"); @@ -124,14 +131,15 @@ NSString *g_shader_src = kShader( return false; } + // Create a reusable pipeline state MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipelineStateDescriptor.label = @"MiniFB_pipeline"; pipelineStateDescriptor.vertexFunction = vertex_shader_func; pipelineStateDescriptor.fragmentFunction = fragment_shader_func; pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; - m_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; - if (!m_pipeline_state) { + pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; + if (!pipeline_state) { NSLog(@"Failed to created pipeline state, error %@", error); return false; } @@ -141,26 +149,42 @@ NSString *g_shader_src = kShader( //------------------------------------- - (void) _createAssets { + static Vertex s_vertices[4] = { + {-1.0, -1.0, 0, 1}, + {-1.0, 1.0, 0, 1}, + { 1.0, -1.0, 0, 1}, + { 1.0, 1.0, 0, 1}, + }; + memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices)); + MTLTextureDescriptor *td; td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:window_data->buffer_width height:window_data->buffer_height mipmapped:false]; - m_texture_buffer = [g_metal_device newTextureWithDescriptor:td]; + // Create the texture from the device by using the descriptor + texture_buffer = [metal_device newTextureWithDescriptor:td]; } //------------------------------------- - (void) drawInMTKView:(nonnull MTKView *) view { - // Per frame updates here - dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); + // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed + // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight; + current_buffer = (current_buffer + 1) % MaxBuffersInFlight; - id commandBuffer = [m_command_queue commandBuffer]; + // Create a new command buffer for each render pass to the current drawable + id commandBuffer = [command_queue commandBuffer]; commandBuffer.label = @"minifb_command_buffer"; - __block dispatch_semaphore_t block_sema = m_semaphore; + // Add completion hander which signals semaphore when Metal and the GPU has fully + // finished processing the commands we're encoding this frame. This indicates when the + // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer + // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without + // corrupting the rendering. + __block dispatch_semaphore_t block_sema = semaphore; [commandBuffer addCompletedHandler:^(id buffer) { (void)buffer; dispatch_semaphore_signal(block_sema); @@ -168,7 +192,7 @@ NSString *g_shader_src = kShader( // Copy the bytes from our data object into the texture MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; - [m_texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; + [texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; // Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids // holding onto the drawable and blocking the display pipeline any longer than necessary @@ -181,11 +205,11 @@ NSString *g_shader_src = kShader( renderEncoder.label = @"minifb_command_encoder"; // Set render command encoder state - [renderEncoder setRenderPipelineState:m_pipeline_state]; + [renderEncoder setRenderPipelineState:pipeline_state]; [renderEncoder setVertexBytes:window_data_ios->vertices length:sizeof(window_data_ios->vertices) atIndex:0]; - //[renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0]; - [renderEncoder setFragmentTexture:m_texture_buffer atIndex:0]; + //[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0]; + [renderEncoder setFragmentTexture:texture_buffer atIndex:0]; // Draw the vertices of our quads [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; diff --git a/src/macosx/MacMiniFB.m b/src/macosx/MacMiniFB.m index b2e077c..dd9e7a4 100644 --- a/src/macosx/MacMiniFB.m +++ b/src/macosx/MacMiniFB.m @@ -132,7 +132,7 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) OSXViewController *viewController = [[OSXViewController alloc] initWithWindowData:window_data]; MTKView* view = [[MTKView alloc] initWithFrame:rectangle]; - view.device = viewController->metal_device; + view.device = viewController->metal_device; view.delegate = viewController; view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [window_data_osx->window.contentView addSubview:view]; @@ -169,13 +169,13 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void +static void destroy_window_data(SWindowData *window_data) { if(window_data == 0x0) return; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - + SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; if(window_data_osx != 0x0) { OSXWindow *window = window_data_osx->window; @@ -201,7 +201,7 @@ destroy_window_data(SWindowData *window_data) { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void +static void update_events(SWindowData *window_data) { NSEvent* event; @@ -218,7 +218,7 @@ update_events(SWindowData *window_data) { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -mfb_update_state +mfb_update_state mfb_update(struct mfb_window *window, void *buffer) { if(window == 0x0) { return STATE_INVALID_WINDOW; @@ -254,7 +254,7 @@ mfb_update(struct mfb_window *window, void *buffer) { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -mfb_update_state +mfb_update_state mfb_update_events(struct mfb_window *window) { if(window == 0x0) { return STATE_INVALID_WINDOW; @@ -310,7 +310,7 @@ mfb_wait_sync(struct mfb_window *window) { if (event) { [NSApp sendEvent:event]; } - + if(window_data->close) { destroy_window_data(window_data); return false; @@ -324,7 +324,7 @@ mfb_wait_sync(struct mfb_window *window) { else if(current >= g_time_for_frame * 0.8) { millis = 0; } - + usleep(millis * 1000); //sched_yield(); } @@ -336,7 +336,7 @@ mfb_wait_sync(struct mfb_window *window) { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool +bool mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) { if(window == 0x0) { return false; @@ -384,10 +384,10 @@ mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y extern short int g_keycodes[512]; -void +void init_keycodes() { // Clear keys - for (unsigned int i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i) + for (unsigned int i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i) g_keycodes[i] = 0; g_keycodes[0x1D] = KB_KEY_0; @@ -426,7 +426,7 @@ init_keycodes() { g_keycodes[0x07] = KB_KEY_X; g_keycodes[0x10] = KB_KEY_Y; g_keycodes[0x06] = KB_KEY_Z; - + g_keycodes[0x27] = KB_KEY_APOSTROPHE; g_keycodes[0x2A] = KB_KEY_BACKSLASH; g_keycodes[0x2B] = KB_KEY_COMMA; @@ -439,7 +439,7 @@ init_keycodes() { g_keycodes[0x29] = KB_KEY_SEMICOLON; g_keycodes[0x2C] = KB_KEY_SLASH; g_keycodes[0x0A] = KB_KEY_WORLD_1; - + g_keycodes[0x33] = KB_KEY_BACKSPACE; g_keycodes[0x39] = KB_KEY_CAPS_LOCK; g_keycodes[0x75] = KB_KEY_DELETE; @@ -486,7 +486,7 @@ init_keycodes() { g_keycodes[0x31] = KB_KEY_SPACE; g_keycodes[0x30] = KB_KEY_TAB; g_keycodes[0x7E] = KB_KEY_UP; - + g_keycodes[0x52] = KB_KEY_KP_0; g_keycodes[0x53] = KB_KEY_KP_1; g_keycodes[0x54] = KB_KEY_KP_2; @@ -511,14 +511,14 @@ init_keycodes() { extern double g_timer_frequency; extern double g_timer_resolution; -uint64_t +uint64_t mfb_timer_tick() { static mach_timebase_info_data_t timebase = { 0 }; if (timebase.denom == 0) { (void) mach_timebase_info(&timebase); } - + uint64_t time = mach_absolute_time(); //return (time * s_timebase_info.numer) / s_timebase_info.denom; @@ -528,11 +528,11 @@ mfb_timer_tick() { uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom; uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom; high /= timebase.denom; - + return (high << 32) + highRem + low; } -void +void mfb_timer_init() { g_timer_frequency = 1e+9; g_timer_resolution = 1.0 / g_timer_frequency; diff --git a/src/macosx/OSXView.m b/src/macosx/OSXView.m index a7b8991..c9a0185 100644 --- a/src/macosx/OSXView.m +++ b/src/macosx/OSXView.m @@ -1,5 +1,6 @@ #import "OSXView.h" #import "OSXWindow.h" +#import "WindowData_OSX.h" #include //------------------------------------- @@ -22,13 +23,13 @@ [self addTrackingArea:tracking_area]; } -#else +#else //------------------------------------- - (NSRect)resizeRect { const CGFloat resizeBoxSize = 16.0; const CGFloat contentViewPadding = 5.5; - + NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; NSRect resizeRect = NSMakeRect( NSMaxX(contentViewRect) + contentViewPadding, @@ -36,7 +37,7 @@ resizeBoxSize, resizeBoxSize ); - + return resizeRect; } @@ -47,16 +48,16 @@ if(window_data == 0x0) return; - SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; + SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer) return; CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4, 0x0); + CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4, 0x0); - CGImageRef img = CGImageCreate(window_data->buffer_width, window_data->buffer_height, 8, 32, window_data->buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + CGImageRef img = CGImageCreate(window_data->buffer_width, window_data->buffer_height, 8, 32, window_data->buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, provider, 0x0, false, kCGRenderingIntentDefault); const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -69,7 +70,7 @@ CGContextSetFillColorWithColor(context, black); CGContextFillRect(context, CGRectMake(0, 0, window_data->window_width, window_data->window_height)); } - + CGContextDrawImage(context, CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height), img); CGImageRelease(img); diff --git a/src/macosx/OSXViewController.h b/src/macosx/OSXViewController.h index a19734f..235ba68 100644 --- a/src/macosx/OSXViewController.h +++ b/src/macosx/OSXViewController.h @@ -1,27 +1,34 @@ #pragma once #if defined(USE_METAL_API) -#include "WindowData_OSX.h" + #import +#include "WindowData_OSX.h" // Number of textures in flight (tripple buffered) enum { MaxBuffersInFlight = 3 }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -@interface OSXViewController : NSViewController +@interface OSXViewController : NSViewController { - @public SWindowData *window_data; - @public SWindowData_OSX *window_data_osx; + @public SWindowData *window_data; + @public SWindowData_OSX *window_data_osx; - @public id metal_device; - @public id metal_library; - @public id texture_buffers[MaxBuffersInFlight]; - @public int current_buffer; - @public dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU + id metal_device; + id metal_library; + + dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU + id command_queue; + + id pipeline_state; + id texture_buffers[MaxBuffersInFlight]; + + int current_buffer; } - (id) initWithWindowData:(SWindowData *) windowData; @end + #endif diff --git a/src/macosx/OSXViewController.m b/src/macosx/OSXViewController.m index 766d921..2b98fec 100644 --- a/src/macosx/OSXViewController.m +++ b/src/macosx/OSXViewController.m @@ -1,6 +1,7 @@ #include "OSXViewController.h" #if defined(USE_METAL_API) + #import //------------------------------------- @@ -63,28 +64,28 @@ NSString *g_shader_src = kShader( return 0x0; } - [self _create_shaders]; - [self _createAssets]; - // Used for syncing the CPU and GPU semaphore = dispatch_semaphore_create(MaxBuffersInFlight); // Setup command queue - window_data_osx->metal.command_queue = [metal_device newCommandQueue]; + command_queue = [metal_device newCommandQueue]; + + [self _createShaders]; + [self _createAssets]; } return self; } //------------------------------------- -- (bool) _create_shaders { +- (bool) _createShaders { NSError *error = 0x0; - metal_library = [metal_device newLibraryWithSource:g_shader_src + metal_library = [metal_device newLibraryWithSource:g_shader_src options:[[MTLCompileOptions alloc] init] error:&error ]; if (error || !metal_library) { - NSLog(@"Unable to create shaders %@", error); + NSLog(@"Unable to create shaders %@", error); return false; } @@ -108,9 +109,10 @@ NSString *g_shader_src = kShader( pipelineStateDescriptor.fragmentFunction = fragment_shader_func; pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; - window_data_osx->metal.pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; - if (!window_data_osx->metal.pipeline_state) { + pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; + if (!pipeline_state) { NSLog(@"Failed to created pipeline state, error %@", error); + return false; } return true; @@ -126,8 +128,6 @@ NSString *g_shader_src = kShader( }; memcpy(window_data_osx->metal.vertices, s_vertices, sizeof(s_vertices)); - // Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is - // an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0) MTLTextureDescriptor *td; td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:window_data->buffer_width @@ -141,36 +141,34 @@ NSString *g_shader_src = kShader( } //------------------------------------- -- (void) drawInMTKView:(nonnull MTKView *)view { +- (void) drawInMTKView:(nonnull MTKView *) view { // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed - // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) + // by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc) dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - // Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight current_buffer = (current_buffer + 1) % MaxBuffersInFlight; - // Calculate the number of bytes per row of our image. - MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; - - // Copy the bytes from our data object into the texture - [texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; - // Create a new command buffer for each render pass to the current drawable - id commandBuffer = [window_data_osx->metal.command_queue commandBuffer]; + id commandBuffer = [command_queue commandBuffer]; commandBuffer.label = @"minifb_command_buffer"; - // Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully - // finished processing the commands we're encoding this frame. This indicates when the - // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer - // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without - // corrupting the rendering. + // Add completion hander which signals semaphore when Metal and the GPU has fully + // finished processing the commands we're encoding this frame. This indicates when the + // dynamic buffers filled with our vertices, that we're writing to this frame, will no longer + // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without + // corrupting the rendering. __block dispatch_semaphore_t block_sema = semaphore; - [commandBuffer addCompletedHandler:^(id buffer) - { - (void)buffer; + [commandBuffer addCompletedHandler:^(id buffer) { + (void)buffer; dispatch_semaphore_signal(block_sema); }]; + // Copy the bytes from our data object into the texture + MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; + [texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride]; + + // Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids + // holding onto the drawable and blocking the display pipeline any longer than necessary MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; if (renderPassDescriptor != nil) { renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); @@ -180,8 +178,7 @@ NSString *g_shader_src = kShader( renderEncoder.label = @"minifb_command_encoder"; // Set render command encoder state - [renderEncoder setRenderPipelineState:window_data_osx->metal.pipeline_state]; - + [renderEncoder setRenderPipelineState:pipeline_state]; [renderEncoder setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0]; [renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0]; @@ -209,4 +206,4 @@ NSString *g_shader_src = kShader( @end -#endif \ No newline at end of file +#endif diff --git a/src/macosx/OSXWindow.m b/src/macosx/OSXWindow.m index 26e2a5d..d8c6ffd 100644 --- a/src/macosx/OSXWindow.m +++ b/src/macosx/OSXWindow.m @@ -24,9 +24,9 @@ { [self setOpaque:YES]; [self setBackgroundColor:[NSColor clearColor]]; - + self.delegate = self; - + self->window_data = windowData; OSXView *view = (OSXView *) self->childContentView.superview; view->window_data = windowData; @@ -59,12 +59,12 @@ NSSize childBoundsSize = [childContentView bounds].size; sizeDelta.width -= childBoundsSize.width; sizeDelta.height -= childBoundsSize.height; - + OSXView *frameView = [super contentView]; NSSize newFrameSize = [frameView bounds].size; newFrameSize.width += sizeDelta.width; newFrameSize.height += sizeDelta.height; - + [super setContentSize:newFrameSize]; } @@ -164,7 +164,7 @@ if(window_data != 0x0) { NSString *characters; - NSUInteger length; + NSUInteger length; if ([string isKindOfClass:[NSAttributedString class]]) characters = [string string]; @@ -212,10 +212,10 @@ if (!frameView) { frameView = [[[OSXView alloc] initWithFrame:bounds] autorelease]; - + [super setContentView:frameView]; } - + if (childContentView) { [childContentView removeFromSuperview]; diff --git a/src/macosx/WindowData_OSX.h b/src/macosx/WindowData_OSX.h index 09f8aba..3c52e83 100644 --- a/src/macosx/WindowData_OSX.h +++ b/src/macosx/WindowData_OSX.h @@ -16,12 +16,10 @@ typedef struct Vertex { typedef struct { OSXWindow *window; struct mfb_timer *timer; - + #if defined(USE_METAL_API) struct { - id command_queue; - id pipeline_state; Vertex vertices[4]; } metal; -#endif +#endif } SWindowData_OSX;