xref: /aosp_15_r20/external/skia/tools/skottie_ios_app/SkiaGLContext.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/SkColorSpace.h"
7#include "include/core/SkSurface.h"
8#include "include/gpu/ganesh/GrBackendSurface.h"
9#include "include/gpu/ganesh/GrDirectContext.h"
10#include "include/gpu/ganesh/SkSurfaceGanesh.h"
11#include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
12#include "include/gpu/ganesh/gl/GrGLDirectContext.h"
13#include "include/gpu/ganesh/gl/GrGLInterface.h"
14#include "include/gpu/ganesh/gl/GrGLTypes.h"
15#include "src/base/SkTime.h"
16
17#import <GLKit/GLKit.h>
18#import <UIKit/UIKit.h>
19#import <OpenGLES/ES3/gl.h>
20
21#include <CoreFoundation/CoreFoundation.h>
22
23static void configure_glkview_for_skia(GLKView* view) {
24    [view setDrawableColorFormat:GLKViewDrawableColorFormatRGBA8888];
25    [view setDrawableDepthFormat:GLKViewDrawableDepthFormat24];
26    [view setDrawableStencilFormat:GLKViewDrawableStencilFormat8];
27}
28
29static sk_sp<SkSurface> make_gl_surface(GrDirectContext* dContext, int width, int height) {
30    static constexpr int kStencilBits = 8;
31    static constexpr int kSampleCount = 1;
32    static const SkSurfaceProps surfaceProps;
33    if (!dContext || width <= 0 || height <= 0) {
34        return nullptr;
35    }
36    GLint fboid = 0;
37    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboid);
38    return SkSurfaces::WrapBackendRenderTarget(
39            dContext,
40            GrBackendRenderTargets::MakeGL(width,
41                                           height,
42                                           kSampleCount,
43                                           kStencilBits,
44                                           GrGLFramebufferInfo{(GrGLuint)fboid, GL_RGBA8}),
45            kBottomLeft_GrSurfaceOrigin,
46            kRGBA_8888_SkColorType,
47            nullptr,
48            &surfaceProps);
49}
50
51// A UIView that uses a GL-backed SkSurface to draw.
52@interface SkiaGLView : GLKView
53    @property (strong) SkiaViewController* controller;
54
55    // Override of the UIView interface.
56    - (void)drawRect:(CGRect)rect;
57
58    // Required initializer.
59    - (instancetype)initWithFrame:(CGRect)frame
60                    withEAGLContext:(EAGLContext*)eaglContext
61                    withDirectContext:(GrDirectContext*)dContext;
62@end
63
64@implementation SkiaGLView {
65    GrDirectContext* fDContext;
66}
67
68- (instancetype)initWithFrame:(CGRect)frame
69                withEAGLContext:(EAGLContext*)eaglContext
70                withDirectContext:(GrDirectContext*)dContext {
71    self = [super initWithFrame:frame context:eaglContext];
72    fDContext = dContext;
73    configure_glkview_for_skia(self);
74    return self;
75}
76
77- (void)drawRect:(CGRect)rect {
78    SkiaViewController* viewController = [self controller];
79    static constexpr double kFrameRate = 1.0 / 30.0;
80    double next = [viewController isPaused] ? 0 : kFrameRate + SkTime::GetNSecs() * 1e-9;
81
82    [super drawRect:rect];
83
84    int width  = (int)[self drawableWidth],
85        height = (int)[self drawableHeight];
86    if (!(fDContext)) {
87        NSLog(@"Error: GrDirectContext missing.\n");
88        return;
89    }
90    if (sk_sp<SkSurface> surface = make_gl_surface(fDContext, width, height)) {
91        [viewController draw:rect
92                        toCanvas:(surface->getCanvas())
93                        atSize:CGSize{(CGFloat)width, (CGFloat)height}];
94        fDContext->flushAndSubmit(surface.get());
95    }
96    if (next) {
97        [NSTimer scheduledTimerWithTimeInterval:std::max(0.0, next - SkTime::GetNSecs() * 1e-9)
98                 target:self
99                 selector:@selector(setNeedsDisplay)
100                 userInfo:nil
101                 repeats:NO];
102    }
103}
104@end
105
106@interface SkiaGLContext : SkiaContext
107    @property (strong) EAGLContext* eaglContext;
108    - (instancetype) init;
109    - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame;
110    - (SkiaViewController*) getViewController:(UIView*)view;
111@end
112
113@implementation SkiaGLContext {
114    sk_sp<GrDirectContext> fDContext;
115}
116- (instancetype) init {
117    self = [super init];
118    [self setEaglContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]];
119    if (![self eaglContext]) {
120        NSLog(@"Falling back to GLES2.\n");
121        [self setEaglContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]];
122    }
123    if (![self eaglContext]) {
124        NSLog(@"[[EAGLContext alloc] initWithAPI:...] failed");
125        return nil;
126    }
127    EAGLContext* oldContext = [EAGLContext currentContext];
128    [EAGLContext setCurrentContext:[self eaglContext]];
129    fDContext = GrDirectContexts::MakeGL(nullptr, GrContextOptions());
130    [EAGLContext setCurrentContext:oldContext];
131    if (!fDContext) {
132        NSLog(@"GrDirectContexts::MakeGL failed");
133        return nil;
134    }
135    return self;
136}
137
138- (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame {
139    SkiaGLView* skiaView = [[SkiaGLView alloc] initWithFrame:frame
140                                               withEAGLContext:[self eaglContext]
141                                               withDirectContext:fDContext.get()];
142    [skiaView setController:vc];
143    return skiaView;
144}
145- (SkiaViewController*) getViewController:(UIView*)view {
146    return [view isKindOfClass:[SkiaGLView class]] ? [(SkiaGLView*)view controller] : nil;
147}
148@end
149
150SkiaContext* MakeSkiaGLContext() { return [[SkiaGLContext alloc] init]; }
151