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. 3 4#include "tools/skottie_ios_app/SkiaContext.h" 5 6#include "include/core/SkSurface.h" 7#include "include/gpu/GrDirectContext.h" 8#include "tools/skottie_ios_app/SkMetalViewBridge.h" 9 10#import <Metal/Metal.h> 11#import <MetalKit/MetalKit.h> 12#import <UIKit/UIKit.h> 13 14// A UIView that uses a Metal-backed SkSurface to draw. 15@interface SkiaMtkView : MTKView 16 @property (strong) SkiaViewController* controller; 17 18 // Override of the MTKView interface. Uses Skia+Metal to draw. 19 - (void)drawRect:(CGRect)rect; 20 21 // Required initializer. 22 - (instancetype)initWithFrame:(CGRect)frameRect 23 device:(id<MTLDevice>)device 24 queue:(id<MTLCommandQueue>)queue 25 grDevice:(GrDirectContext*)dContext; 26@end 27 28@implementation SkiaMtkView { 29 id<MTLCommandQueue> fQueue; 30 GrDirectContext* fDContext; 31} 32 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]; 38 fQueue = queue; 39 fDContext = dContext; 40 SkMtkViewConfigForSkia(self); 41 return self; 42} 43 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) { 49 return; 50 } 51 CGSize size = [self drawableSize]; 52 sk_sp<SkSurface> surface = SkMtkViewToSurface(self, fDContext); 53 if (!surface) { 54 NSLog(@"error: no sksurface"); 55 return; 56 } 57 [viewController draw:rect toCanvas:surface->getCanvas() atSize:size]; 58 surface->flushAndSubmit(); 59 surface = nullptr; 60 61 id<MTLCommandBuffer> commandBuffer = [fQueue commandBuffer]; 62 [commandBuffer presentDrawable:[self currentDrawable]]; 63 [commandBuffer commit]; 64 65 bool paused = [viewController isPaused]; 66 [self setEnableSetNeedsDisplay:paused]; 67 [self setPaused:paused]; 68} 69@end 70 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; 77@end 78 79@implementation SkiaMetalContext { 80 sk_sp<GrDirectContext> fDContext; 81} 82 83- (instancetype) init { 84 self = [super init]; 85 [self setMetalDevice:MTLCreateSystemDefaultDevice()]; 86 if(![self metalDevice]) { 87 NSLog(@"Metal is not supported on this device"); 88 return nil; 89 } 90 [self setMetalQueue:[[self metalDevice] newCommandQueue]]; 91 fDContext = GrDirectContext::MakeMetal((__bridge void*)[self metalDevice], 92 (__bridge void*)[self metalQueue], 93 GrContextOptions()); 94 95 if (!fDContext) { 96 NSLog(@"GrDirectContext::MakeMetal failed"); 97 return nil; 98 } 99 return self; 100} 101 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]; 109 return skiaView; 110} 111 112- (SkiaViewController*) getViewController:(UIView*)view { 113 return [view isKindOfClass:[SkiaMtkView class]] ? [(SkiaMtkView*)view controller] : nil; 114} 115@end 116 117SkiaContext* MakeSkiaMetalContext() { return [[SkiaMetalContext alloc] init]; } 118