Skip to content

Instantly share code, notes, and snippets.

@rotoglup
Last active May 20, 2021 08:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rotoglup/7c02080a34058fe9ef0a7a7ba410e328 to your computer and use it in GitHub Desktop.
Save rotoglup/7c02080a34058fe9ef0a7a7ba410e328 to your computer and use it in GitHub Desktop.
Minimal OSX Metal Application, single file
#include <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import <simd/simd.h>
// BUG: on OSX 10.15.6, the App menu is not clickable at the first activation, but becomes so after switching to another app
//----------------------------------------------------------------------------
@interface AppApplicationDelegate : NSObject < NSApplicationDelegate >
@end
@interface AppWindowDelegate : NSObject < NSWindowDelegate >
@end
@interface AppMetalView : MTKView
@end
//----------------------------------------------------------------------------
int main()
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
AppApplicationDelegate* appdelegate = [[AppApplicationDelegate alloc] init];
[NSApp setDelegate:appdelegate];
// Window
NSWindow* window = [[NSWindow alloc] autorelease];
[window initWithContentRect:NSMakeRect(0, 0, 800, 600)
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
backing:NSBackingStoreBuffered
defer:NO
];
[window setTitle:@"appName"];
[window setLevel:NSNormalWindowLevel];
[window center];
[window makeKeyAndOrderFront:nil];
AppWindowDelegate* windelegate = [[AppWindowDelegate alloc] init];
[window setDelegate:windelegate];
// Custom MTKView
AppMetalView* view = [[AppMetalView alloc] initWithFrame:window.frame];
window.contentView = view;
// Run
[NSApp run];
return 0;
}
//----------------------------------------------------------------------------
@implementation AppWindowDelegate
-(BOOL)windowShouldClose : (id)window
{
[NSApp terminate:nil];
return true;
}
@end
//----------------------------------------------------------------------------
@implementation AppApplicationDelegate
-(void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
// Menu
id quitMenuItemTitle = [@"Quit " stringByAppendingString:@"appName"];
id quitMenuItem = [[NSMenuItem alloc] autorelease];
[quitMenuItem
initWithTitle:quitMenuItemTitle
action:@selector(terminate:)
keyEquivalent:@"q"];
id appMenu = [[NSMenu new] autorelease];
[appMenu addItem:quitMenuItem];
id appMenuItem = [[NSMenuItem new] autorelease];
[appMenuItem setSubmenu:appMenu];
id mainMenu = [[NSMenu new] autorelease];
[mainMenu addItem:appMenuItem];
[NSApp setMainMenu:mainMenu];
// Required since OSX 10.6 to get menubar and bring to front
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Required since OSX 10.15 to get menubar working at launch
[NSApp activateIgnoringOtherApps:YES];
}
@end
//----------------------------------------------------------------------------
@implementation AppMetalView
{
id <MTLCommandQueue> _commandQueue;
long _frame;
}
- (id)initWithFrame:(CGRect)inFrame
{
// Select a GPU to run on
// Note that on Intel laptops, the Metal "system default device" is always the discrete GPU
// even if `pmset -a gpuswitch 0` has been used to force the integrated GPU.
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
self = [super initWithFrame:inFrame device:device];
if (self)
{
[self setup];
}
return self;
}
- (void)setup
{
self.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
self.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
_commandQueue = [self.device newCommandQueue];
}
- (void)drawRect:(CGRect)rect
{
// Animate clear color
_frame++;
float rad = _frame * 0.01f;
float sin = std::sin(rad), cos = std::cos(rad);
self.clearColor = MTLClearColorMake(0.5f*sin+0.5f, 0.5f, 0.25f*cos+0.25f, 1.0f);
// Clear the drawable
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
MTLRenderPassDescriptor *currentRenderPassDescriptor = self.currentRenderPassDescriptor;
id <MTLRenderCommandEncoder> encoder =
[commandBuffer renderCommandEncoderWithDescriptor:currentRenderPassDescriptor];
[encoder setViewport:(MTLViewport){0, 0, self.drawableSize.width, self.drawableSize.height, 0, 1}];
[encoder endEncoding];
// Present the drawing
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
// Draw children
[super drawRect:rect];
}
@end
@jim-ec
Copy link

jim-ec commented Mar 19, 2021

Had to compile it with:

clang++ -o minimal-metal-app minimal-metal-app.mm -framework Cocoa -framework Metal -framework MetalKit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment