1// Copyright 2019 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// This is an example of a minimal iOS application that uses Skia to draw to 5// a Metal drawable. 6 7// Much of this code is copied from the default application created by XCode. 8 9#include "tools/skottie_ios_app/SkMetalViewBridge.h" 10 11#include "include/core/SkCanvas.h" 12#include "include/core/SkColor.h" 13#include "include/core/SkColorSpace.h" 14#include "include/core/SkPaint.h" 15#include "include/core/SkSurface.h" 16#include "include/effects/SkGradientShader.h" 17#include "include/gpu/ganesh/GrBackendSurface.h" 18#include "include/gpu/ganesh/GrDirectContext.h" 19#include "include/gpu/ganesh/SkSurfaceGanesh.h" 20#include "include/gpu/ganesh/mtl/GrMtlTypes.h" 21#include "src/base/SkTime.h" 22 23#import <Metal/Metal.h> 24#import <MetalKit/MetalKit.h> 25#import <UIKit/UIKit.h> 26 27//////////////////////////////////////////////////////////////////////////////// 28 29static void config_paint(SkPaint* paint) { 30 if (!paint->getShader()) { 31 const SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite}; 32 const SkPoint points[2] = {{0, -1024}, {0, 1024}}; 33 paint->setShader(SkGradientShader::MakeLinear(points, colors, nullptr, nullptr, 2, 34 SkTileMode::kClamp, 0, nullptr)); 35 } 36} 37 38static void draw_example(SkSurface* surface, const SkPaint& paint, double rotation) { 39 SkCanvas* canvas = surface->getCanvas(); 40 canvas->translate(surface->width() * 0.5f, surface->height() * 0.5f); 41 canvas->rotate(rotation); 42 canvas->drawPaint(paint); 43} 44 45//////////////////////////////////////////////////////////////////////////////// 46 47@interface AppViewDelegate : NSObject <MTKViewDelegate> 48@property (assign, nonatomic) GrDirectContext* grContext; // non-owning pointer. 49@property (assign, nonatomic) id<MTLCommandQueue> metalQueue; 50@end 51 52@implementation AppViewDelegate { 53 SkPaint fPaint; 54} 55 56- (void)drawInMTKView:(nonnull MTKView *)view { 57 if (![self grContext] || !view) { 58 return; 59 } 60 // Do as much as possible before creating surface. 61 config_paint(&fPaint); 62 float rotation = (float)(180 * 1e-9 * SkTime::GetNSecs()); 63 64 // Create surface: 65 sk_sp<SkSurface> surface = SkMtkViewToSurface(view, [self grContext]); 66 if (!surface) { 67 NSLog(@"error: no sksurface"); 68 return; 69 } 70 71 draw_example(surface.get(), fPaint, rotation); 72 73 // Must flush *and* present for this to work! 74 skgpu::ganesh::Flush(surface); 75 surface = nullptr; 76 77 id<MTLCommandBuffer> commandBuffer = [[self metalQueue] commandBuffer]; 78 [commandBuffer presentDrawable:[view currentDrawable]]; 79 [commandBuffer commit]; 80} 81 82- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { 83 // change anything on size change? 84} 85@end 86 87//////////////////////////////////////////////////////////////////////////////// 88 89@interface AppViewController : UIViewController 90@property (strong, nonatomic) id<MTLDevice> metalDevice; 91@property (strong, nonatomic) id<MTLCommandQueue> metalQueue; 92@end 93 94@implementation AppViewController { 95 GrContextHolder fGrContext; 96} 97 98- (void)loadView { 99 [self setView:[[MTKView alloc] initWithFrame:[[UIScreen mainScreen] bounds] device:nil]]; 100} 101 102- (void)viewDidLoad { 103 [super viewDidLoad]; 104 if (!fGrContext) { 105 [self setMetalDevice:MTLCreateSystemDefaultDevice()]; 106 [self setMetalQueue:[[self metalDevice] newCommandQueue]]; 107 fGrContext = SkMetalDeviceToGrContext([self metalDevice], [self metalQueue]); 108 } 109 if (![self view] || ![self metalDevice]) { 110 NSLog(@"Metal is not supported on this device"); 111 self.view = [[UIView alloc] initWithFrame:self.view.frame]; 112 return; 113 } 114 MTKView* mtkView = (MTKView*)[self view]; 115 [mtkView setDevice:[self metalDevice]]; 116 [mtkView setBackgroundColor:[UIColor blackColor]]; 117 SkMtkViewConfigForSkia(mtkView); 118 AppViewDelegate* viewDelegate = [[AppViewDelegate alloc] init]; 119 [viewDelegate setGrContext:fGrContext.get()]; 120 [viewDelegate setMetalQueue:[self metalQueue]]; 121 [viewDelegate mtkView:mtkView drawableSizeWillChange:[mtkView bounds].size]; 122 [mtkView setDelegate:viewDelegate]; 123} 124@end 125 126//////////////////////////////////////////////////////////////////////////////// 127 128@interface AppDelegate : UIResponder <UIApplicationDelegate> 129@property (strong, nonatomic) UIWindow *window; 130@end 131 132@implementation AppDelegate 133- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary*)opts { 134 // Override point for customization after application launch. 135 [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]]; 136 [[self window] setFrame:[[UIScreen mainScreen] bounds]]; 137 [[self window] setRootViewController:[[AppViewController alloc] init]]; 138 [[self window] makeKeyAndVisible]; 139 return YES; 140} 141@end 142 143//////////////////////////////////////////////////////////////////////////////// 144 145int main(int argc, char* argv[]) { 146 @autoreleasepool { 147 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 148 } 149} 150