xref: /aosp_15_r20/external/skia/tools/skottie_ios_app/SkiaMetalContext.mm (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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