// Copyright 2020 Google LLC. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #include "tools/skottie_ios_app/SkiaContext.h" #include "include/core/SkSurface.h" #include "include/gpu/GrDirectContext.h" #include "tools/skottie_ios_app/SkMetalViewBridge.h" #import #import #import // A UIView that uses a Metal-backed SkSurface to draw. @interface SkiaMtkView : MTKView @property (strong) SkiaViewController* controller; // Override of the MTKView interface. Uses Skia+Metal to draw. - (void)drawRect:(CGRect)rect; // Required initializer. - (instancetype)initWithFrame:(CGRect)frameRect device:(id)device queue:(id)queue grDevice:(GrDirectContext*)dContext; @end @implementation SkiaMtkView { id fQueue; GrDirectContext* fDContext; } - (instancetype)initWithFrame:(CGRect)frameRect device:(id)mtlDevice queue:(id)queue grDevice:(GrDirectContext*)dContext { self = [super initWithFrame:frameRect device:mtlDevice]; fQueue = queue; fDContext = dContext; SkMtkViewConfigForSkia(self); return self; } - (void)drawRect:(CGRect)rect { [super drawRect:rect]; // TODO(halcanary): Use the rect and the InvalidationController to speed up rendering. SkiaViewController* viewController = [self controller]; if (!viewController || ![[self currentDrawable] texture] || !fDContext) { return; } CGSize size = [self drawableSize]; sk_sp surface = SkMtkViewToSurface(self, fDContext); if (!surface) { NSLog(@"error: no sksurface"); return; } [viewController draw:rect toCanvas:surface->getCanvas() atSize:size]; surface->flushAndSubmit(); surface = nullptr; id commandBuffer = [fQueue commandBuffer]; [commandBuffer presentDrawable:[self currentDrawable]]; [commandBuffer commit]; bool paused = [viewController isPaused]; [self setEnableSetNeedsDisplay:paused]; [self setPaused:paused]; } @end @interface SkiaMetalContext : SkiaContext @property (strong) id metalDevice; @property (strong) id metalQueue; - (instancetype) init; - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame; - (SkiaViewController*) getViewController:(UIView*)view; @end @implementation SkiaMetalContext { sk_sp fDContext; } - (instancetype) init { self = [super init]; [self setMetalDevice:MTLCreateSystemDefaultDevice()]; if(![self metalDevice]) { NSLog(@"Metal is not supported on this device"); return nil; } [self setMetalQueue:[[self metalDevice] newCommandQueue]]; fDContext = GrDirectContext::MakeMetal((__bridge void*)[self metalDevice], (__bridge void*)[self metalQueue], GrContextOptions()); if (!fDContext) { NSLog(@"GrDirectContext::MakeMetal failed"); return nil; } return self; } - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame { SkiaMtkView* skiaView = [[SkiaMtkView alloc] initWithFrame:frame device:[self metalDevice] queue:[self metalQueue] grDevice:fDContext.get()]; [skiaView setPreferredFramesPerSecond:30]; [skiaView setController:vc]; return skiaView; } - (SkiaViewController*) getViewController:(UIView*)view { return [view isKindOfClass:[SkiaMtkView class]] ? [(SkiaMtkView*)view controller] : nil; } @end SkiaContext* MakeSkiaMetalContext() { return [[SkiaMetalContext alloc] init]; }