1 /* 2 * Copyright 2021 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 package org.skia.androidkit.util; 9 10 import android.util.Log; 11 import android.view.SurfaceHolder; 12 import org.skia.androidkit.Canvas; 13 import org.skia.androidkit.Surface; 14 15 /** 16 * Utility base class facilitating the implementation of Surface-bound animations. 17 * 18 * Provides a dedicated render thread and user content callbacks. 19 */ 20 public abstract class SurfaceRenderer implements SurfaceHolder.Callback, Runnable { 21 private android.view.Surface mAndroidSurface; 22 private Thread mRenderThread; 23 private boolean mThreadRunning; 24 private long mTimeBase; 25 26 /** 27 * Initialization callback. 28 * 29 * This can be invoked multiple times if the underlying surface changes. 30 */ onSurfaceInitialized(Surface surface)31 protected abstract void onSurfaceInitialized(Surface surface); 32 33 /** 34 * Callback for frame content. 35 * 36 * Invoked once per (vsync'ed) frame. 37 */ onRenderFrame(Canvas canvas, long ms)38 protected abstract void onRenderFrame(Canvas canvas, long ms); 39 40 @Override run()41 public void run() { 42 Log.d("SurfaceRenderer", "Render thread started."); 43 44 // TODO: Vulkan support? 45 Surface surface = Surface.CreateGL(mAndroidSurface); 46 onSurfaceInitialized(surface); 47 48 mTimeBase = java.lang.System.currentTimeMillis(); 49 50 while (mThreadRunning) { 51 long timestamp = java.lang.System.currentTimeMillis() - mTimeBase; 52 onRenderFrame(surface.getCanvas(), timestamp); 53 surface.flushAndSubmit(); 54 } 55 56 // Ensure that the backing surface is released on the same thread. 57 surface.release(); 58 59 Log.d("SurfaceRenderer", "Render thread finished."); 60 } 61 62 @Override surfaceCreated(SurfaceHolder holder)63 public void surfaceCreated(SurfaceHolder holder) { 64 // Initialization handled in surfaceChanged(). 65 } 66 67 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)68 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 69 mAndroidSurface = holder.getSurface(); 70 71 stopRenderThread(); 72 startRenderThread(); 73 } 74 75 @Override surfaceDestroyed(SurfaceHolder holder)76 public void surfaceDestroyed(SurfaceHolder holder) { 77 stopRenderThread(); 78 } 79 startRenderThread()80 private void startRenderThread() { 81 if (!mThreadRunning) { 82 mThreadRunning = true; 83 mRenderThread = new Thread(this); 84 mRenderThread.start(); 85 } 86 } 87 stopRenderThread()88 private void stopRenderThread() { 89 if (mThreadRunning) { 90 mThreadRunning = false; 91 try { 92 mRenderThread.join(); 93 } catch (InterruptedException e) {} 94 } 95 } 96 setBaseTime(long millis)97 public void setBaseTime(long millis) { 98 mTimeBase = millis; 99 } 100 release()101 public void release() { 102 stopRenderThread(); 103 } 104 } 105