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.annotation.SuppressLint 20 import android.graphics.BlendMode 21 import android.graphics.Color 22 import android.graphics.HardwareBufferRenderer 23 import android.graphics.RenderNode 24 import android.hardware.HardwareBuffer 25 import android.os.Build 26 import androidx.annotation.RequiresApi 27 import androidx.core.util.Consumer 28 import androidx.graphics.surface.JniBindings 29 import androidx.hardware.BufferPool 30 import androidx.hardware.FileDescriptorMonitor 31 import androidx.hardware.SyncFenceCompat 32 import java.util.concurrent.Executor 33 import java.util.concurrent.atomic.AtomicInteger 34 import java.util.concurrent.locks.ReentrantLock 35 import kotlin.concurrent.withLock 36 37 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 38 internal class CanvasBufferedRendererV34( 39 private val mWidth: Int, 40 private val mHeight: Int, 41 private val mFormat: Int, 42 private val mUsage: Long, 43 maxBuffers: Int, 44 private val mFdMonitor: SharedFileDescriptorMonitor? = obtainSharedFdMonitor() 45 ) : CanvasBufferedRenderer.Impl { 46 47 private data class HardwareBufferProvider( 48 private val buffer: HardwareBuffer, 49 val renderer: HardwareBufferRenderer 50 ) : BufferPool.BufferProvider { 51 override val hardwareBuffer: HardwareBuffer 52 get() = buffer 53 54 override fun release() { 55 renderer.close() 56 buffer.close() 57 } 58 } 59 60 init { 61 mFdMonitor?.incrementRef() 62 } 63 64 private val mPool = BufferPool<HardwareBufferProvider>(maxBuffers) 65 66 private val mRootNode = 67 RenderNode("rootNode").apply { 68 setPosition(0, 0, mWidth, mHeight) 69 clipToBounds = false 70 } 71 72 private var mContentNode: RenderNode? = null 73 private var mLightX: Float = 0f 74 private var mLightY: Float = 0f 75 private var mLightZ: Float = 0f 76 private var mLightRadius: Float = 0f 77 private var mAmbientShadowAlpha: Float = 0f 78 private var mSpotShadowAlpha: Float = 0f 79 private var mPreserveContents = false 80 private var mCurrentBufferProvider: HardwareBufferProvider? = null 81 82 private fun obtainBufferEntry(): HardwareBufferProvider = 83 mPool.obtain { 84 val hardwareBuffer = HardwareBuffer.create(mWidth, mHeight, mFormat, 1, mUsage) 85 HardwareBufferProvider(hardwareBuffer, HardwareBufferRenderer(hardwareBuffer)) 86 } 87 88 override fun close() { 89 mPool.close() 90 mFdMonitor?.decrementRef() 91 mCurrentBufferProvider?.renderer?.close() 92 } 93 94 override fun isClosed(): Boolean = mPool.isClosed 95 96 @SuppressLint("WrongConstant") 97 override fun draw( 98 request: CanvasBufferedRenderer.RenderRequest, 99 executor: Executor, 100 callback: Consumer<CanvasBufferedRenderer.RenderResult> 101 ) { 102 val contentNode = mContentNode 103 val shouldDraw = 104 !mRootNode.hasDisplayList() || mPreserveContents != request.preserveContents 105 if (shouldDraw && contentNode != null) { 106 val canvas = mRootNode.beginRecording() 107 canvas.save() 108 if (!request.preserveContents) { 109 canvas.drawColor(Color.BLACK, BlendMode.CLEAR) 110 } 111 canvas.drawRenderNode(contentNode) 112 canvas.restore() 113 mRootNode.endRecording() 114 mPreserveContents = request.preserveContents 115 } 116 val renderNode = mRootNode 117 val lightX = mLightX 118 val lightY = mLightY 119 val lightZ = mLightZ 120 val lightRadius = mLightRadius 121 val ambientShadowAlpha = mAmbientShadowAlpha 122 val spotShadowAlpha = mSpotShadowAlpha 123 val colorSpace = request.colorSpace 124 val transform = request.transform 125 executor.execute { 126 if (!isClosed()) { 127 val bufferProvider = obtainBufferEntry() 128 mCurrentBufferProvider = bufferProvider 129 with(bufferProvider) { 130 renderer.apply { 131 setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha) 132 setLightSourceGeometry(lightX, lightY, lightZ, lightRadius) 133 setContentRoot(renderNode) 134 obtainRenderRequest() 135 .apply { 136 setColorSpace(colorSpace) 137 setBufferTransform(transform) 138 } 139 .draw(executor) { result -> 140 callback.accept( 141 CanvasBufferedRenderer.RenderResult( 142 hardwareBuffer, 143 SyncFenceCompat(result.fence), 144 result.status 145 ) 146 ) 147 } 148 } 149 } 150 } 151 } 152 } 153 154 override fun releaseBuffer(hardwareBuffer: HardwareBuffer, syncFence: SyncFenceCompat?) { 155 mPool.release(hardwareBuffer, syncFence) 156 } 157 158 override fun setContentRoot(renderNode: RenderNode) { 159 mContentNode = renderNode 160 mRootNode.discardDisplayList() 161 } 162 163 override fun setLightSourceAlpha(ambientShadowAlpha: Float, spotShadowAlpha: Float) { 164 mAmbientShadowAlpha = ambientShadowAlpha 165 mSpotShadowAlpha = spotShadowAlpha 166 } 167 168 override fun setLightSourceGeometry( 169 lightX: Float, 170 lightY: Float, 171 lightZ: Float, 172 lightRadius: Float 173 ) { 174 mLightX = lightX 175 mLightY = lightY 176 mLightZ = lightZ 177 mLightRadius = lightRadius 178 } 179 180 internal companion object { 181 private val monitorLock = ReentrantLock() 182 private var sharedFdMonitor: SharedFileDescriptorMonitor? = null 183 184 fun obtainSharedFdMonitor(): SharedFileDescriptorMonitor? { 185 val isVulkan = JniBindings.nIsHwuiUsingVulkanRenderer() 186 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE && isVulkan) { 187 // See b/295332012 188 monitorLock.withLock { 189 var monitor = sharedFdMonitor 190 if (monitor == null || !monitor.isMonitoring) { 191 monitor = 192 SharedFileDescriptorMonitor( 193 FileDescriptorMonitor().apply { startMonitoring() } 194 ) 195 sharedFdMonitor = monitor 196 } 197 return monitor 198 } 199 } else { 200 return null 201 } 202 } 203 } 204 } 205 206 internal class SharedFileDescriptorMonitor( 207 private val fileDescriptorMonitor: FileDescriptorMonitor 208 ) { 209 210 private val mRefCount = AtomicInteger(0) 211 incrementRefnull212 fun incrementRef() { 213 mRefCount.incrementAndGet() 214 } 215 216 val isMonitoring: Boolean 217 get() = fileDescriptorMonitor.isMonitoring 218 decrementRefnull219 fun decrementRef() { 220 if (mRefCount.decrementAndGet() <= 0) { 221 fileDescriptorMonitor.stopMonitoring() 222 } 223 } 224 } 225