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