• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 Google LLC.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4// This is an example of a minimal iOS application that uses Skia to draw to
5// a Metal drawable.
6
7// Much of this code is copied from the default application created by XCode.
8
9#include "tools/skottie_ios_app/SkMetalViewBridge.h"
10
11#include "include/core/SkCanvas.h"
12#include "include/core/SkPaint.h"
13#include "include/core/SkSurface.h"
14#include "include/core/SkTime.h"
15#include "include/effects/SkGradientShader.h"
16#include "include/gpu/GrBackendSurface.h"
17#include "include/gpu/GrDirectContext.h"
18#include "include/gpu/mtl/GrMtlTypes.h"
19
20#import <Metal/Metal.h>
21#import <MetalKit/MetalKit.h>
22#import <UIKit/UIKit.h>
23
24////////////////////////////////////////////////////////////////////////////////
25
26static void config_paint(SkPaint* paint) {
27    if (!paint->getShader()) {
28        const SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite};
29        const SkPoint points[2] = {{0, -1024}, {0, 1024}};
30        paint->setShader(SkGradientShader::MakeLinear(points, colors, nullptr, nullptr, 2,
31                                                      SkTileMode::kClamp, 0, nullptr));
32    }
33}
34
35static void draw_example(SkSurface* surface, const SkPaint& paint, double rotation) {
36    SkCanvas* canvas = surface->getCanvas();
37    canvas->translate(surface->width() * 0.5f, surface->height() * 0.5f);
38    canvas->rotate(rotation);
39    canvas->drawPaint(paint);
40}
41
42////////////////////////////////////////////////////////////////////////////////
43
44@interface AppViewDelegate : NSObject <MTKViewDelegate>
45@property (assign, nonatomic) GrDirectContext* grContext;  // non-owning pointer.
46@property (assign, nonatomic) id<MTLCommandQueue> metalQueue;
47@end
48
49@implementation AppViewDelegate {
50    SkPaint fPaint;
51}
52
53- (void)drawInMTKView:(nonnull MTKView *)view {
54    if (![self grContext] || !view) {
55        return;
56    }
57    // Do as much as possible before creating surface.
58    config_paint(&fPaint);
59    float rotation = (float)(180 * 1e-9 * SkTime::GetNSecs());
60
61    // Create surface:
62    sk_sp<SkSurface> surface = SkMtkViewToSurface(view, [self grContext]);
63    if (!surface) {
64        NSLog(@"error: no sksurface");
65        return;
66    }
67
68    draw_example(surface.get(), fPaint, rotation);
69
70    // Must flush *and* present for this to work!
71    surface->flushAndSubmit();
72    surface = nullptr;
73
74    id<MTLCommandBuffer> commandBuffer = [[self metalQueue] commandBuffer];
75    [commandBuffer presentDrawable:[view currentDrawable]];
76    [commandBuffer commit];
77}
78
79- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
80    // change anything on size change?
81}
82@end
83
84////////////////////////////////////////////////////////////////////////////////
85
86@interface AppViewController : UIViewController
87@property (strong, nonatomic) id<MTLDevice> metalDevice;
88@property (strong, nonatomic) id<MTLCommandQueue> metalQueue;
89@end
90
91@implementation AppViewController {
92    GrContextHolder fGrContext;
93}
94
95- (void)loadView {
96    [self setView:[[MTKView alloc] initWithFrame:[[UIScreen mainScreen] bounds] device:nil]];
97}
98
99- (void)viewDidLoad {
100    [super viewDidLoad];
101    if (!fGrContext) {
102        [self setMetalDevice:MTLCreateSystemDefaultDevice()];
103        [self setMetalQueue:[[self metalDevice] newCommandQueue]];
104        fGrContext = SkMetalDeviceToGrContext([self metalDevice], [self metalQueue]);
105    }
106    if (![self view] || ![self metalDevice]) {
107        NSLog(@"Metal is not supported on this device");
108        self.view = [[UIView alloc] initWithFrame:self.view.frame];
109        return;
110    }
111    MTKView* mtkView = (MTKView*)[self view];
112    [mtkView setDevice:[self metalDevice]];
113    [mtkView setBackgroundColor:[UIColor blackColor]];
114    SkMtkViewConfigForSkia(mtkView);
115    AppViewDelegate* viewDelegate = [[AppViewDelegate alloc] init];
116    [viewDelegate setGrContext:fGrContext.get()];
117    [viewDelegate setMetalQueue:[self metalQueue]];
118    [viewDelegate mtkView:mtkView drawableSizeWillChange:[mtkView bounds].size];
119    [mtkView setDelegate:viewDelegate];
120}
121@end
122
123////////////////////////////////////////////////////////////////////////////////
124
125@interface AppDelegate : UIResponder <UIApplicationDelegate>
126@property (strong, nonatomic) UIWindow *window;
127@end
128
129@implementation AppDelegate
130- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary*)opts {
131    // Override point for customization after application launch.
132    [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]];
133    [[self window] setFrame:[[UIScreen mainScreen] bounds]];
134    [[self window] setRootViewController:[[AppViewController alloc] init]];
135    [[self window] makeKeyAndVisible];
136    return YES;
137}
138@end
139
140////////////////////////////////////////////////////////////////////////////////
141
142int main(int argc, char* argv[]) {
143    @autoreleasepool {
144        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
145    }
146}
147