1/*
2 * DemoViewController.m
3 *
4 * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#import "DemoViewController.h"
20#import <QuartzCore/CAMetalLayer.h>
21
22#include <MoltenVK/mvk_vulkan.h>
23
24#include "cube.c"
25
26#pragma mark -
27#pragma mark DemoViewController
28
29@implementation DemoViewController {
30    CVDisplayLinkRef _displayLink;
31    struct demo demo;
32    NSTimer* _timer;
33}
34
35- (void)dealloc {
36    [self quit];
37    [super dealloc];
38}
39
40- (void)quit {
41    CVDisplayLinkRelease(_displayLink);
42    demo_cleanup(&demo);
43}
44
45/** Since this is a single-view app, initialize Vulkan during view loading. */
46- (void)viewDidLoad {
47    [super viewDidLoad];
48
49    self.view.wantsLayer = YES;  // Back the view with a layer created by the makeBackingLayer method.
50
51    // Convert incoming args to "C" argc/argv strings
52    NSArray *args = [[NSProcessInfo processInfo] arguments];
53    const char** argv = (const char**) alloca(sizeof(char*) * args.count);
54    for(unsigned int i = 0; i < args.count; i++) {
55        NSString *s = args[i];
56        argv[i] = s.UTF8String;
57    }
58
59    demo_main(&demo, self.view.layer, args.count, argv);
60
61    // Monitor the rendering loop for a quit condition
62    _timer = [NSTimer scheduledTimerWithTimeInterval: 0.2
63                                              target: self
64                                            selector: @selector(onTick:)
65                                            userInfo: self
66                                             repeats: YES];
67
68    // Start the rendering loop
69    CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
70    CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo);
71    CVDisplayLinkStart(_displayLink);
72
73}
74
75// Close the window if the demo is in a Quit state
76-(void)onTick:(NSTimer*)timer {
77    if (demo.quit) {
78        [[[self view] window] close];
79    }
80}
81
82#pragma mark Display loop callback function
83
84/** Rendering loop callback function for use with a CVDisplayLink. */
85static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now,
86                                    const CVTimeStamp* outputTime,
87                                    CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* target) {
88    struct demo* demo = (struct demo*)target;
89    demo_run(demo);
90    if (demo->quit) {
91        CVDisplayLinkStop(displayLink);
92    }
93    return kCVReturnSuccess;
94}
95
96@end
97
98#pragma mark -
99#pragma mark DemoView
100
101@implementation DemoView
102
103/** Indicates that the view wants to draw using the backing layer instead of using drawRect:.  */
104- (BOOL)wantsUpdateLayer {
105    return YES;
106}
107
108/** Returns a Metal-compatible layer. */
109+ (Class)layerClass {
110    return [CAMetalLayer class];
111}
112
113/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */
114- (CALayer*)makeBackingLayer {
115    CALayer* layer = [self.class.layerClass layer];
116    CGSize viewScale = [self convertSizeToBacking:CGSizeMake(1.0, 1.0)];
117    layer.contentsScale = MIN(viewScale.width, viewScale.height);
118    return layer;
119}
120
121@end
122