1cb93a386Sopenharmony_ci// Copyright 2020 Google LLC.
2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3cb93a386Sopenharmony_ci
4cb93a386Sopenharmony_ci#include "tools/skottie_ios_app/SkiaContext.h"
5cb93a386Sopenharmony_ci
6cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
7cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
8cb93a386Sopenharmony_ci#include "tools/skottie_ios_app/SkMetalViewBridge.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#import <Metal/Metal.h>
11cb93a386Sopenharmony_ci#import <MetalKit/MetalKit.h>
12cb93a386Sopenharmony_ci#import <UIKit/UIKit.h>
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci// A UIView that uses a Metal-backed SkSurface to draw.
15cb93a386Sopenharmony_ci@interface SkiaMtkView : MTKView
16cb93a386Sopenharmony_ci    @property (strong) SkiaViewController* controller;
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ci    // Override of the MTKView interface.  Uses Skia+Metal to draw.
19cb93a386Sopenharmony_ci    - (void)drawRect:(CGRect)rect;
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci    // Required initializer.
22cb93a386Sopenharmony_ci    - (instancetype)initWithFrame:(CGRect)frameRect
23cb93a386Sopenharmony_ci                    device:(id<MTLDevice>)device
24cb93a386Sopenharmony_ci                    queue:(id<MTLCommandQueue>)queue
25cb93a386Sopenharmony_ci                    grDevice:(GrDirectContext*)dContext;
26cb93a386Sopenharmony_ci@end
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ci@implementation SkiaMtkView {
29cb93a386Sopenharmony_ci    id<MTLCommandQueue> fQueue;
30cb93a386Sopenharmony_ci    GrDirectContext*    fDContext;
31cb93a386Sopenharmony_ci}
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci- (instancetype)initWithFrame:(CGRect)frameRect
34cb93a386Sopenharmony_ci                device:(id<MTLDevice>)mtlDevice
35cb93a386Sopenharmony_ci                queue:(id<MTLCommandQueue>)queue
36cb93a386Sopenharmony_ci                grDevice:(GrDirectContext*)dContext {
37cb93a386Sopenharmony_ci    self = [super initWithFrame:frameRect device:mtlDevice];
38cb93a386Sopenharmony_ci    fQueue = queue;
39cb93a386Sopenharmony_ci    fDContext = dContext;
40cb93a386Sopenharmony_ci    SkMtkViewConfigForSkia(self);
41cb93a386Sopenharmony_ci    return self;
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci- (void)drawRect:(CGRect)rect {
45cb93a386Sopenharmony_ci    [super drawRect:rect];
46cb93a386Sopenharmony_ci    // TODO(halcanary): Use the rect and the InvalidationController to speed up rendering.
47cb93a386Sopenharmony_ci    SkiaViewController* viewController = [self controller];
48cb93a386Sopenharmony_ci    if (!viewController || ![[self currentDrawable] texture] || !fDContext) {
49cb93a386Sopenharmony_ci        return;
50cb93a386Sopenharmony_ci    }
51cb93a386Sopenharmony_ci    CGSize size = [self drawableSize];
52cb93a386Sopenharmony_ci    sk_sp<SkSurface> surface = SkMtkViewToSurface(self, fDContext);
53cb93a386Sopenharmony_ci    if (!surface) {
54cb93a386Sopenharmony_ci        NSLog(@"error: no sksurface");
55cb93a386Sopenharmony_ci        return;
56cb93a386Sopenharmony_ci    }
57cb93a386Sopenharmony_ci    [viewController draw:rect toCanvas:surface->getCanvas() atSize:size];
58cb93a386Sopenharmony_ci    surface->flushAndSubmit();
59cb93a386Sopenharmony_ci    surface = nullptr;
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    id<MTLCommandBuffer> commandBuffer = [fQueue commandBuffer];
62cb93a386Sopenharmony_ci    [commandBuffer presentDrawable:[self currentDrawable]];
63cb93a386Sopenharmony_ci    [commandBuffer commit];
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    bool paused = [viewController isPaused];
66cb93a386Sopenharmony_ci    [self setEnableSetNeedsDisplay:paused];
67cb93a386Sopenharmony_ci    [self setPaused:paused];
68cb93a386Sopenharmony_ci}
69cb93a386Sopenharmony_ci@end
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci@interface SkiaMetalContext : SkiaContext
72cb93a386Sopenharmony_ci    @property (strong) id<MTLDevice> metalDevice;
73cb93a386Sopenharmony_ci    @property (strong) id<MTLCommandQueue> metalQueue;
74cb93a386Sopenharmony_ci    - (instancetype) init;
75cb93a386Sopenharmony_ci    - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame;
76cb93a386Sopenharmony_ci    - (SkiaViewController*) getViewController:(UIView*)view;
77cb93a386Sopenharmony_ci@end
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci@implementation SkiaMetalContext {
80cb93a386Sopenharmony_ci    sk_sp<GrDirectContext> fDContext;
81cb93a386Sopenharmony_ci}
82cb93a386Sopenharmony_ci
83cb93a386Sopenharmony_ci- (instancetype) init {
84cb93a386Sopenharmony_ci    self = [super init];
85cb93a386Sopenharmony_ci    [self setMetalDevice:MTLCreateSystemDefaultDevice()];
86cb93a386Sopenharmony_ci    if(![self metalDevice]) {
87cb93a386Sopenharmony_ci        NSLog(@"Metal is not supported on this device");
88cb93a386Sopenharmony_ci        return nil;
89cb93a386Sopenharmony_ci    }
90cb93a386Sopenharmony_ci    [self setMetalQueue:[[self metalDevice] newCommandQueue]];
91cb93a386Sopenharmony_ci    fDContext = GrDirectContext::MakeMetal((__bridge void*)[self metalDevice],
92cb93a386Sopenharmony_ci                                           (__bridge void*)[self metalQueue],
93cb93a386Sopenharmony_ci                                           GrContextOptions());
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    if (!fDContext) {
96cb93a386Sopenharmony_ci        NSLog(@"GrDirectContext::MakeMetal failed");
97cb93a386Sopenharmony_ci        return nil;
98cb93a386Sopenharmony_ci    }
99cb93a386Sopenharmony_ci    return self;
100cb93a386Sopenharmony_ci}
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci- (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame {
103cb93a386Sopenharmony_ci    SkiaMtkView* skiaView = [[SkiaMtkView alloc] initWithFrame:frame
104cb93a386Sopenharmony_ci                                                 device:[self metalDevice]
105cb93a386Sopenharmony_ci                                                 queue:[self metalQueue]
106cb93a386Sopenharmony_ci                                                 grDevice:fDContext.get()];
107cb93a386Sopenharmony_ci    [skiaView setPreferredFramesPerSecond:30];
108cb93a386Sopenharmony_ci    [skiaView setController:vc];
109cb93a386Sopenharmony_ci    return skiaView;
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci- (SkiaViewController*) getViewController:(UIView*)view {
113cb93a386Sopenharmony_ci    return [view isKindOfClass:[SkiaMtkView class]] ? [(SkiaMtkView*)view controller] : nil;
114cb93a386Sopenharmony_ci}
115cb93a386Sopenharmony_ci@end
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ciSkiaContext* MakeSkiaMetalContext() { return [[SkiaMetalContext alloc] init]; }
118