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