1 /* <lambda>null2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.graphics 18 19 import android.graphics.HardwareRenderer 20 import android.graphics.RenderNode 21 import android.graphics.SurfaceTexture 22 import android.os.Build 23 import android.os.Handler 24 import android.util.Log 25 import android.view.Surface 26 import androidx.annotation.RequiresApi 27 28 /** Class that handles drawing content of a RenderNode into a SurfaceTexture */ 29 @RequiresApi(Build.VERSION_CODES.Q) 30 internal class SurfaceTextureRenderer( 31 /** Target RenderNode of the content that is to be drawn */ 32 private val renderNode: RenderNode, 33 34 /** Width of the SurfaceTexture */ 35 width: Int, 36 37 /** Height of the SurfaceTexture */ 38 height: Int, 39 40 /** Handler used to send SurfaceTexture#OnFrameAvailableListener callbacks */ 41 private val handler: Handler, 42 43 /** Callback invoked when a new image frame is available on the underlying SurfaceTexture */ 44 private val frameAvailable: (SurfaceTexture) -> Unit 45 ) { 46 47 // Workaround: b/272751501 48 // For some reason, SurfaceTexture instances that are created in detached mode get gc'ed 49 // prematurely after the application is idle for ~10 or more seconds. This issue appears in 50 // multiple versions of Android. However, if we use a subclass of SurfaceTexture we seem to 51 // not run into this. It appears that there is some internal package name checking of 52 // android.graphics.SurfaceTexture API within jni in the platform and using a subclass seems 53 // to prevent work around this issue. 54 // 55 // An alternative solution is to create a SurfaceTexture in attached mode by providing a 56 // placeholder texture identifier then having the consuming GLThread call detachFromGLContext 57 // and attachToGLContext with a freshly created texture id. 58 // 59 // Currently we go with the original option as it may not be explicit to implementations that 60 // the initial detach is necessary here. 61 private class RenderSurfaceTexture(singleBufferMode: Boolean) : 62 SurfaceTexture(singleBufferMode) 63 64 private var mIsReleased = false 65 66 private val mSurfaceTexture = 67 RenderSurfaceTexture(false).apply { 68 setDefaultBufferSize(width, height) 69 setOnFrameAvailableListener( 70 { surfaceTexture -> frameAvailable(surfaceTexture) }, 71 handler 72 ) 73 } 74 75 private val mTextureSurface = Surface(mSurfaceTexture) 76 private val mHardwareRenderer = 77 HardwareRenderer().apply { 78 setSurface(mTextureSurface) 79 setContentRoot(renderNode) 80 start() 81 } 82 83 fun renderFrame() { 84 if (!mIsReleased) { 85 mHardwareRenderer.apply { createRenderRequest().setWaitForPresent(false).syncAndDraw() } 86 } else { 87 Log.w( 88 TAG, 89 "Attempt to renderFrame when SurfaceTextureRenderer has already " + "been released" 90 ) 91 } 92 } 93 94 /** 95 * Releases all resources of the SurfaceTextureRenderer instances. Attempts to use this object 96 * after this call has been made will be ignored. 97 */ 98 fun release() { 99 if (!mIsReleased) { 100 mHardwareRenderer.stop() 101 mHardwareRenderer.destroy() 102 mTextureSurface.release() 103 if (!mSurfaceTexture.isReleased) { 104 mSurfaceTexture.release() 105 } 106 mIsReleased = true 107 } else { 108 Log.w( 109 TAG, 110 "Attempt to release a SurfaceTextureRenderer that has " + "already been released" 111 ) 112 } 113 } 114 115 companion object { 116 private val TAG = "SurfaceTextureRenderer" 117 } 118 } 119