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