xref: /aosp_15_r20/external/skia/experimental/minimal_ios_mtl_skia_app/main.mm (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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