Last active
October 1, 2018 13:24
-
-
Save xslattery/31f7d8e43c284c51c19866c3044f66de to your computer and use it in GitHub Desktop.
This is an example of manually driving a MacOS Metal application with a loop. Including events and rendering.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // platform_macos_metal_example.mm | |
| // macos-platform-metal-example | |
| // | |
| #import <Cocoa/Cocoa.h> | |
| #import <MetalKit/MetalKit.h> | |
| bool running = true; | |
| @interface AppDelegate : NSObject <NSApplicationDelegate> | |
| @end | |
| @implementation AppDelegate | |
| - (id)init { | |
| if(self = [super init]) { | |
| id menubar = [NSMenu new]; | |
| [NSApp setMainMenu:menubar]; | |
| id appMenuItem = [NSMenuItem new]; | |
| [menubar addItem:appMenuItem]; | |
| id appMenu = [NSMenu new]; | |
| [appMenuItem setSubmenu:appMenu]; | |
| id appName = [[NSProcessInfo processInfo] processName]; | |
| id quitTitle = [@"Quit " stringByAppendingString:appName]; | |
| id quitMenuItem = [[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"]; | |
| [appMenu addItem:quitMenuItem]; | |
| } | |
| return self; | |
| } | |
| - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { return YES; } | |
| - (void)applicationWillFinishLaunching:(NSNotification *)notification {} | |
| - (void)applicationDidFinishLaunching:(NSNotification *)notification {} | |
| - (void)applicationWillTerminate:(NSNotification *)aNotification { running = false; } | |
| @end | |
| ///////////////////////////// | |
| @interface WindowDelegate : NSObject<NSWindowDelegate> | |
| @end | |
| @implementation WindowDelegate | |
| - (void)windowWillClose:(id)sender { running = false; } | |
| - (NSSize)windowWillResize: (NSWindow*)window toSize:(NSSize)frameSize { return frameSize; } | |
| @end | |
| /////////////////////// | |
| @interface MetalView : MTKView | |
| @end | |
| @implementation MetalView | |
| - (id)initWithFrame:(NSRect)size { | |
| if(self = [super initWithFrame:size]) { | |
| // NOTE(Xavier): 2018-10-1 This is required for mouseEntered/Exited to work | |
| // Create a tracking area to keep track of the mouse movements and events: | |
| NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved); | |
| NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; | |
| [self addTrackingArea:area]; | |
| } | |
| return self; | |
| } | |
| - (BOOL)acceptsFirstResponder { return YES; } | |
| - (BOOL)acceptsFirstMouse:(NSEvent *)event { return YES; } | |
| // NOTE(Xavier): 2018-10-1 | |
| // Having these prevent a beep but are not necessary | |
| // if the main loop handles the event. | |
| - (void)flagsChanged:(NSEvent*)event {} | |
| - (void)keyDown:(NSEvent*)event {} | |
| - (void)keyUp:(NSEvent*)event {} | |
| - (void)mouseDown:(NSEvent *)event {} | |
| - (void)mouseUp:(NSEvent *)event {} | |
| - (void)mouseDragged:(NSEvent *)event {} | |
| - (void)rightMouseDown:(NSEvent *)event {} | |
| - (void)rightMouseUp:(NSEvent *)event {} | |
| - (void)rightMouseDragged:(NSEvent *)event {} | |
| - (void)otherMouseDown:(NSEvent *)event {} | |
| - (void)otherMouseUp:(NSEvent *)event {} | |
| - (void)otherMouseDragged:(NSEvent *)event {} | |
| - (void)mouseMoved:(NSEvent *)event {} | |
| - (void)mouseEntered:(NSEvent *)event {} | |
| - (void)mouseExited:(NSEvent *)event {} | |
| - (void)scrollWheel:(NSEvent *)event {} | |
| @end | |
| ///////////////////////////////////////////////// | |
| int main(int argc, const char * argv[]) { | |
| [NSApplication sharedApplication]; | |
| [NSApp setDelegate:[AppDelegate new]]; | |
| [NSApp activateIgnoringOtherApps:YES]; | |
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | |
| [NSApp finishLaunching]; | |
| NSRect contentSize = NSMakeRect(500.0, 500.0, 640.0, 480.0); | |
| NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; | |
| NSWindow *window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES]; | |
| window.backgroundColor = [NSColor whiteColor]; | |
| window.title = @"MyBareMetalApp"; | |
| [window setDelegate:[WindowDelegate new]]; | |
| [window makeKeyAndOrderFront:nil]; | |
| MetalView *metalView = [[MetalView alloc] initWithFrame:contentSize]; | |
| [window setContentView:metalView]; | |
| [window makeFirstResponder:metalView]; | |
| metalView.device = MTLCreateSystemDefaultDevice(); | |
| metalView.preferredFramesPerSecond = 30; | |
| metalView.framebufferOnly = YES; | |
| metalView.sampleCount = 1; | |
| metalView.colorPixelFormat = MTLPixelFormatBGRA8Unorm; | |
| metalView.depthStencilPixelFormat = MTLPixelFormatDepth32Float; | |
| id<MTLDevice> device = metalView.device; | |
| id<MTLCommandQueue> commandQueue = [device newCommandQueue]; | |
| running = true; | |
| while (running) { | |
| NSEvent* event; | |
| do { | |
| event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; | |
| switch ([event type]) { | |
| case NSEventTypeFlagsChanged: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeKeyDown: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeKeyUp: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeLeftMouseDown: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeLeftMouseUp: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeLeftMouseDragged: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeRightMouseDown: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeRightMouseUp: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeRightMouseDragged: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeOtherMouseDown: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeOtherMouseUp: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeOtherMouseDragged: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeMouseMoved: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeMouseEntered: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeMouseExited: { [NSApp sendEvent:event]; } break; | |
| case NSEventTypeScrollWheel: { [NSApp sendEvent:event]; } break; | |
| default: { [NSApp sendEvent:event]; } break; | |
| } | |
| } while (event != nil); | |
| id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer]; | |
| commandBuffer.label = @"MyCommand"; | |
| MTLRenderPassDescriptor *renderPassDescriptor = metalView.currentRenderPassDescriptor; | |
| renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 1.0, 0.0, 1.0); | |
| if(renderPassDescriptor != nil) { | |
| id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; | |
| renderEncoder.label = @"MyRenderEncoder"; | |
| [renderEncoder endEncoding]; | |
| [commandBuffer presentDrawable:metalView.currentDrawable]; | |
| } | |
| [commandBuffer commit]; | |
| } | |
| return 0; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment