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