Skip to content

Instantly share code, notes, and snippets.

@warrenm
Created May 25, 2022 21:52
Show Gist options
  • Save warrenm/50bb40e80d6bb57f9fbd09048bd12891 to your computer and use it in GitHub Desktop.
Save warrenm/50bb40e80d6bb57f9fbd09048bd12891 to your computer and use it in GitHub Desktop.
A one-file example of rendering into a UIView with Metal and CADisplayLink
#import <UIKit/UIKit.h>
#import <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
@class MetalView;
@protocol MetalViewDelegate
- (void)drawInView:(MetalView *)view;
@end
@interface MetalView : UIView
@property (nonatomic, strong) id<MTLDevice> device;
@property (nonatomic, weak) id<MetalViewDelegate> delegate;
@property (nonatomic, nullable, strong) CADisplayLink *displayLink;
@end
@implementation MetalView
+ (Class)layerClass {
return [CAMetalLayer class];
}
- (id<MTLDevice>)device {
return ((CAMetalLayer *)self.layer).device;
}
- (void)setDevice:(id<MTLDevice>)device {
((CAMetalLayer *)self.layer).device = device;
}
- (id<CAMetalDrawable>)currentDrawable {
return [((CAMetalLayer *)self.layer) nextDrawable];
}
- (void)didMoveToWindow {
if (self.window == nil) {
[self deactivateDisplayLink];
} else {
[self activateDisplayLink];
}
}
- (void)activateDisplayLink {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkDidFire:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)deactivateDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)displayLinkDidFire:(CADisplayLink *)displayLink {
[self drawRect:self.bounds];
}
- (void)drawRect:(CGRect)rect {
[self.delegate drawInView:self];
}
@end
@interface ViewController : UIViewController <MetalViewDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) id<MTLDevice> device;
@property (nonatomic, strong) id<MTLCommandQueue> commandQueue;
@end
@implementation ViewController
- (instancetype)init {
if (self = [super init]) {
_device = MTLCreateSystemDefaultDevice();
_commandQueue = [_device newCommandQueue];
}
return self;
}
- (void)loadView {
CGRect bounds = UIScreen.mainScreen.bounds;
MetalView *view = [[MetalView alloc] initWithFrame:bounds];
view.delegate = self;
self.view = view;
}
- (void)drawInView:(MetalView *)view {
id<CAMetalDrawable> drawable = [view currentDrawable];
if (drawable == nil) {
return;
}
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor new];
passDescriptor.colorAttachments[0].texture = drawable.texture;
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0.5, 1, 1);
passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
passDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
id<MTLRenderCommandEncoder> renderCommandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:passDescriptor];
[renderCommandEncoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ViewController *vc = [[ViewController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:vc.view.bounds];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
return YES;
}
@end
int main(int argc, char * argv[]) {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment