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