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