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