1 /* <lambda>null2 * Copyright 2022 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.lowlatency 18 19 import android.hardware.HardwareBuffer 20 import android.opengl.GLES20 21 import android.opengl.Matrix 22 import android.os.Build 23 import android.util.Log 24 import android.view.SurfaceHolder 25 import android.view.SurfaceView 26 import androidx.annotation.RequiresApi 27 import androidx.annotation.WorkerThread 28 import androidx.graphics.lowlatency.FrontBufferUtils.Companion.obtainHardwareBufferUsageFlags 29 import androidx.graphics.opengl.FrameBuffer 30 import androidx.graphics.opengl.FrameBufferPool 31 import androidx.graphics.opengl.FrameBufferRenderer 32 import androidx.graphics.opengl.GLFrameBufferRenderer 33 import androidx.graphics.opengl.GLRenderer 34 import androidx.graphics.opengl.egl.EGLManager 35 import androidx.graphics.surface.SurfaceControlCompat 36 import androidx.hardware.HardwareBufferFormat 37 import androidx.hardware.SyncFenceCompat 38 import androidx.opengl.EGLExt.Companion.EGL_ANDROID_NATIVE_FENCE_SYNC 39 import androidx.opengl.EGLExt.Companion.EGL_KHR_FENCE_SYNC 40 import java.util.concurrent.ConcurrentLinkedQueue 41 import java.util.concurrent.CountDownLatch 42 import java.util.concurrent.atomic.AtomicBoolean 43 import java.util.concurrent.atomic.AtomicInteger 44 import kotlin.math.max 45 46 /** 47 * Class responsible for supporting a "front buffered" rendering system. This allows for lower 48 * latency graphics by leveraging a combination of front buffered and multi buffered content layers. 49 * Active content is rendered first into the front buffered layer which is simultaneously being 50 * presented to the display. Periodically content is rendered into the multi buffered layer which 51 * will have more traditional latency guarantees, however, minimize the impact of visual artifacts 52 * due to graphical tearing. 53 * 54 * @param surfaceView Target SurfaceView to act as the parent rendering layer for multi buffered 55 * content 56 * @param callback Callbacks used to render into front and multi buffered layers as well as 57 * configuring [SurfaceControlCompat.Transaction]s for controlling these layers in addition to 58 * other [SurfaceControlCompat] instances that must be updated atomically within the user 59 * interface. These callbacks are invoked on the backing GL Thread. 60 * @param glRenderer Optional [GLRenderer] instance that this [GLFrontBufferedRenderer] should use 61 * for coordinating requests to render content. If this parameter is specified, the caller of this 62 * API is responsible for releasing resources on [GLRenderer] by calling [GLRenderer.stop]. 63 * Otherwise [GLFrontBufferedRenderer] will create and manage its own [GLRenderer] internally and 64 * will automatically release its resources within [GLFrontBufferedRenderer.release] 65 * @param bufferFormat format of the underlying buffers being rendered into by 66 * [GLFrontBufferedRenderer]. The set of valid formats is implementation-specific and may depend 67 * on additional EGL extensions. The particular valid combinations for a given Android version and 68 * implementation should be documented by that version. [HardwareBuffer.RGBA_8888] and 69 * [HardwareBuffer.RGBX_8888] are guaranteed to be supported. However, consumers are recommended 70 * to query the desired HardwareBuffer configuration using [HardwareBuffer.isSupported]. The 71 * default is [HardwareBuffer.RGBA_8888]. 72 * 73 * See: khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt and 74 * https://developer.android.com/reference/android/hardware/HardwareBuffer 75 */ 76 @RequiresApi(Build.VERSION_CODES.Q) 77 class GLFrontBufferedRenderer<T> 78 @JvmOverloads 79 constructor( 80 surfaceView: SurfaceView, 81 callback: Callback<T>, 82 @Suppress("ListenerLast") glRenderer: GLRenderer? = null, 83 @HardwareBufferFormat val bufferFormat: Int = HardwareBuffer.RGBA_8888 84 ) { 85 86 private val mFrontBufferedCallbacks = 87 object : GLFrameBufferRenderer.Callback { 88 override fun onDrawFrame( 89 eglManager: EGLManager, 90 width: Int, 91 height: Int, 92 bufferInfo: BufferInfo, 93 transform: FloatArray 94 ) { 95 if (mPendingClear.compareAndSet(true, false)) { 96 waitForFrontBufferFence() 97 clearContents(width, height) 98 } 99 val result = mPendingRenderCount.updateAndGet { value -> max(value - 1, 0) } 100 mActiveSegment.next { param -> 101 callback.onDrawFrontBufferedLayer( 102 eglManager, 103 width, 104 height, 105 bufferInfo, 106 transform, 107 param 108 ) 109 } 110 if (result > 0) { 111 mFrontBufferedRenderer?.render() 112 } 113 } 114 115 override fun onDrawComplete( 116 targetSurfaceControl: SurfaceControlCompat, 117 transaction: SurfaceControlCompat.Transaction, 118 frameBuffer: FrameBuffer, 119 syncFence: SyncFenceCompat? 120 ) { 121 mFrontBufferSyncStrategy.isVisible = true 122 mFrontLayerBuffer = frameBuffer 123 transaction.setLayer(targetSurfaceControl, Integer.MAX_VALUE) 124 transaction.setBuffer( 125 targetSurfaceControl, 126 frameBuffer.hardwareBuffer, 127 syncFence 128 ) { releaseFence -> 129 mFrontBufferReleaseFence?.close() 130 mFrontBufferReleaseFence = releaseFence 131 } 132 callback.onFrontBufferedLayerRenderComplete(targetSurfaceControl, transaction) 133 } 134 } 135 136 /** 137 * AtomicInteger to determine if there is a pending request to commit content to the multi 138 * buffered layer 139 */ 140 private val mCommitCount = AtomicInteger(0) 141 142 @Volatile private var mFrontBufferReleaseFence: SyncFenceCompat? = null 143 144 private fun waitForFrontBufferFence() { 145 mFrontBufferReleaseFence?.let { fence -> 146 fence.awaitForever() 147 mFrontBufferReleaseFence = null 148 } 149 } 150 151 private fun clearContents(viewportWidth: Int, viewportHeight: Int) { 152 GLES20.glViewport(0, 0, viewportWidth, viewportHeight) 153 GLES20.glClearColor(0f, 0f, 0f, 0f) 154 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) 155 GLES20.glFlush() 156 } 157 158 // GLThread 159 private val mClearFrontBufferRunnable = Runnable { 160 mFrontLayerBuffer?.let { frameBuffer -> 161 if (mPendingClear.compareAndSet(true, false)) { 162 if (!frameBuffer.isClosed) { 163 frameBuffer.makeCurrent() 164 waitForFrontBufferFence() 165 with(frameBuffer) { clearContents(hardwareBuffer.width, hardwareBuffer.height) } 166 } 167 } 168 } 169 } 170 171 /** 172 * [GLRenderer.EGLContextCallback]s used to release the corresponding [FrameBufferPool] if the 173 * [GLRenderer] is torn down. This is especially helpful if a [GLRenderer] is being provided and 174 * shared across other [GLRenderer.RenderTarget] instances in which case it can be released 175 * externally from [GLFrontBufferedRenderer] 176 */ 177 private val mContextCallbacks = 178 object : GLRenderer.EGLContextCallback { 179 override fun onEGLContextCreated(eglManager: EGLManager) { 180 with(eglManager) { 181 val supportsEglFences = isExtensionSupported(EGL_KHR_FENCE_SYNC) 182 val supportsAndroidFences = isExtensionSupported(EGL_ANDROID_NATIVE_FENCE_SYNC) 183 Log.d(TAG, "Supports KHR_FENCE_SYNC: $supportsEglFences") 184 Log.d(TAG, "Supports ANDROID_NATIVE_FENCE_SYNC: $supportsAndroidFences") 185 } 186 } 187 188 override fun onEGLContextDestroyed(eglManager: EGLManager) { 189 // NO-OP 190 } 191 } 192 193 private val mMultiBufferedRenderCallbacks = 194 object : GLFrameBufferRenderer.Callback { 195 override fun onDrawFrame( 196 eglManager: EGLManager, 197 width: Int, 198 height: Int, 199 bufferInfo: BufferInfo, 200 transform: FloatArray 201 ) { 202 GLES20.glViewport(0, 0, bufferInfo.width, bufferInfo.height) 203 GLES20.glClearColor(0f, 0f, 0f, 0f) 204 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) 205 callback.onDrawMultiBufferedLayer( 206 eglManager, 207 width, 208 height, 209 bufferInfo, 210 transform, 211 mSegments.poll() ?: emptyList() 212 ) 213 } 214 215 override fun onDrawComplete( 216 targetSurfaceControl: SurfaceControlCompat, 217 transaction: SurfaceControlCompat.Transaction, 218 frameBuffer: FrameBuffer, 219 syncFence: SyncFenceCompat? 220 ) { 221 mFrontBufferSyncStrategy.isVisible = false 222 transaction.apply { 223 mFrontBufferedLayerSurfaceControl?.let { frontSurfaceControl -> 224 setVisibility(frontSurfaceControl, false) 225 setBuffer(frontSurfaceControl, null) 226 callback.onMultiBufferedLayerRenderComplete( 227 frontSurfaceControl, 228 targetSurfaceControl, 229 transaction 230 ) 231 } 232 } 233 } 234 235 override fun onBufferReleased( 236 frameBuffer: FrameBuffer, 237 releaseFence: SyncFenceCompat? 238 ) { 239 if (isValid()) { 240 mPendingClear.set(true) 241 val result = mCommitCount.updateAndGet { value -> max(value - 1, 0) } 242 if (result != 0) { 243 commitInternal() 244 } else if (mPendingRenderCount.get() > 0) { 245 mFrontBufferedRenderer?.render() 246 } 247 if (!mFrontBufferSyncStrategy.isVisible) { 248 mClearFrontBufferRunnable.run() 249 } 250 } 251 } 252 } 253 254 private val mSurfaceCallbacks = 255 object : SurfaceHolder.Callback2 { 256 override fun surfaceCreated(holder: SurfaceHolder) { 257 // NO-OP 258 } 259 260 override fun surfaceChanged( 261 holder: SurfaceHolder, 262 format: Int, 263 width: Int, 264 height: Int 265 ) { 266 mSurfaceView?.let { surfaceView -> update(surfaceView, width, height) } 267 } 268 269 override fun surfaceDestroyed(p0: SurfaceHolder) { 270 detachTargets(true) 271 } 272 273 override fun surfaceRedrawNeeded(p0: SurfaceHolder) { 274 val countDownLatch = CountDownLatch(1) 275 requestDraw { countDownLatch.countDown() } 276 countDownLatch.await() 277 } 278 279 override fun surfaceRedrawNeededAsync( 280 holder: SurfaceHolder, 281 drawingFinished: Runnable 282 ) { 283 requestDraw(drawingFinished) 284 } 285 286 fun requestDraw(onComplete: Runnable) { 287 val multiBufferedRenderer = mMultiBufferedRenderer 288 if (multiBufferedRenderer != null) { 289 val eglCallback = 290 object : GLRenderer.EGLContextCallback { 291 override fun onEGLContextCreated(eglManager: EGLManager) { 292 // NO-OP 293 } 294 295 override fun onEGLContextDestroyed(eglManager: EGLManager) { 296 onComplete.run() 297 mGLRenderer.unregisterEGLContextCallback(this) 298 } 299 } 300 mGLRenderer.registerEGLContextCallback(eglCallback) 301 multiBufferedRenderer.render() 302 mGLRenderer.execute { 303 onComplete.run() 304 mGLRenderer.unregisterEGLContextCallback(eglCallback) 305 } 306 } else { 307 onComplete.run() 308 } 309 } 310 } 311 312 /** 313 * Flag to determine if a request to clear the front buffer content is pending. This should only 314 * be accessed on the GLThread 315 */ 316 private val mPendingClear = AtomicBoolean(false) 317 318 /** 319 * Count of pending requests to render to the front buffer while content is being committed to 320 * the multi buffered layer 321 */ 322 private val mPendingRenderCount = AtomicInteger(0) 323 324 /** SurfaceView that hosts both the multi buffered and front buffered SurfaceControls */ 325 private var mSurfaceView: SurfaceView? = surfaceView 326 327 /** 328 * Runnable executed on the GLThread to update [FrontBufferSyncStrategy.isVisible] as well as 329 * hide the SurfaceControl associated with the front buffered layer 330 */ 331 private val mCancelRunnable = Runnable { 332 mPendingClear.set(true) 333 mFrontBufferSyncStrategy.isVisible = false 334 mFrontBufferedLayerSurfaceControl?.let { frontBufferSurfaceControl -> 335 SurfaceControlCompat.Transaction() 336 .setVisibility(frontBufferSurfaceControl, false) 337 .commit() 338 } 339 } 340 341 /** 342 * Queue of parameters to be consumed in [Callback.onDrawFrontBufferedLayer] with the parameter 343 * provided in [renderFrontBufferedLayer] 344 */ 345 private val mActiveSegment = ParamQueue<T>() 346 347 /** 348 * Collection of parameters to be consumed in [Callback.onMultiBufferedLayerRenderComplete] with 349 * the parameters defined in consecutive calls to [renderFrontBufferedLayer]. Once the 350 * corresponding [Callback.onMultiBufferedLayerRenderComplete] callback is invoked, this 351 * collection is cleared and new parameters are added to it with consecutive calls to 352 * [renderFrontBufferedLayer]. 353 */ 354 private val mSegments = ConcurrentLinkedQueue<Collection<T>>() 355 356 /** 357 * [FrameBuffer] used for rendering into the front buffered layer. This buffer is persisted 358 * across frames as part of front buffered rendering and is not expected to be released again 359 * after the corresponding [SurfaceControlCompat.Transaction] that submits this buffer is 360 * applied as per the implementation of "scan line racing" that is done for front buffered 361 * rendering 362 */ 363 private var mFrontLayerBuffer: FrameBuffer? = null 364 365 /** 366 * [SurfaceControlCompat] used to configure buffers and visibility of the multi buffered layer 367 */ 368 private var mMultiBufferedLayerSurfaceControl: SurfaceControlCompat? = null 369 370 /** 371 * [SurfaceControlCompat] used to configure buffers and visibility of the front buffered layer 372 */ 373 private var mFrontBufferedLayerSurfaceControl: SurfaceControlCompat? = null 374 375 /** 376 * [FrontBufferSyncStrategy] used for [FrameBufferRenderer] to conditionally decide when to 377 * create a [SyncFenceCompat] for transaction calls. 378 */ 379 private val mFrontBufferSyncStrategy: FrontBufferSyncStrategy 380 381 /** 382 * Width of the layers to render. Only if the size changes to we re-initialize the internal 383 * state of the [GLFrontBufferedRenderer] 384 */ 385 private var mWidth = -1 386 387 /** 388 * Height of the layers to render. Only if the size changes do we re-initialize the internal 389 * state of the [GLFrontBufferedRenderer] 390 */ 391 private var mHeight = -1 392 393 /** Current transform to apply to pre-rotate content */ 394 private var mTransform = BufferTransformHintResolver.UNKNOWN_TRANSFORM 395 396 /** [GLRenderer] used to issue requests to render into front/multi buffered layers */ 397 private val mGLRenderer: GLRenderer 398 399 /** 400 * Flag indicating if the [GLRenderer] being used was created internally within 401 * [GLFrontBufferedRenderer] as opposed to being provided by the consumer. If the former, then 402 * the [GLFrontBufferedRenderer] is responsible for stopping/releasing this [GLRenderer] in the 403 * [release] method. If this is being provided, then we should not be releasing this 404 * [GLRenderer] as it maybe used by other consumers. In this case, only the front/multi buffered 405 * [GLRenderer.RenderTarget]s are detached. 406 */ 407 private val mIsManagingGLRenderer: Boolean 408 409 /** [GLFrameBufferRenderer] used to issue requests to render into the front buffered layer */ 410 private var mFrontBufferedRenderer: GLFrameBufferRenderer? = null 411 412 /** [GLFrameBufferRenderer] used to issue requests to render into the multi buffered layer */ 413 private var mMultiBufferedRenderer: GLFrameBufferRenderer? = null 414 415 /** 416 * Flag to determine if the [GLFrontBufferedRenderer] has previously been released. If this flag 417 * is true, then subsequent requests to [renderFrontBufferedLayer], [commit], and [release] are 418 * ignored. 419 */ 420 private var mIsReleased = false 421 422 init { 423 val renderer = 424 if (glRenderer == null) { 425 // If we have not been provided a [GLRenderer] then we should create/start one 426 // ourselves 427 mIsManagingGLRenderer = true 428 GLRenderer().apply { start() } 429 } else { 430 // ... otherwise use the [GLRenderer] that is being provided for us 431 mIsManagingGLRenderer = false 432 if (!glRenderer.isRunning()) { 433 throw IllegalStateException( 434 "The provided GLRenderer must be running prior to " + 435 "creation of GLFrontBufferedRenderer, " + 436 "did you forget to call GLRenderer#start()?" 437 ) 438 } 439 glRenderer 440 } 441 renderer.registerEGLContextCallback(mContextCallbacks) 442 443 mGLRenderer = renderer 444 445 mFrontBufferSyncStrategy = FrontBufferSyncStrategy(obtainHardwareBufferUsageFlags()) 446 447 mSurfaceView = surfaceView 448 surfaceView.holder?.let { holder -> 449 holder.addCallback(mSurfaceCallbacks) 450 if (holder.surface != null && holder.surface.isValid) { 451 update(surfaceView, surfaceView.width, surfaceView.height) 452 } 453 } 454 } 455 456 internal fun update(surfaceView: SurfaceView, width: Int, height: Int) { 457 if (width <= 0 || height <= 0) { 458 Log.w( 459 TAG, 460 "Invalid dimensions provided, width and height must be > 0. " + 461 "width: $width height: $height" 462 ) 463 return 464 } 465 val transformHint = BufferTransformHintResolver().getBufferTransformHint(surfaceView) 466 if ((mTransform != transformHint || mWidth != width || mHeight != height) && isValid()) { 467 detachTargets(true) 468 469 val parentSurfaceControl = 470 SurfaceControlCompat.Builder() 471 .setParent(surfaceView) 472 .setName("MultiBufferedSurfaceControl") 473 .build() 474 475 val frontSurfaceControl = 476 SurfaceControlCompat.Builder() 477 .setParent(parentSurfaceControl) 478 .setName("FrontBufferedSurfaceControl") 479 .build() 480 481 FrontBufferUtils.configureFrontBufferLayerFrameRate(frontSurfaceControl)?.commit() 482 483 val multiBufferedRenderer = 484 GLFrameBufferRenderer.Builder( 485 parentSurfaceControl, 486 width, 487 height, 488 transformHint, 489 mMultiBufferedRenderCallbacks 490 ) 491 .setGLRenderer(mGLRenderer) 492 .setUsageFlags(FrontBufferUtils.BaseFlags) 493 .setBufferFormat(bufferFormat) 494 .build() 495 496 val frontBufferedRenderer = 497 GLFrameBufferRenderer.Builder( 498 frontSurfaceControl, 499 width, 500 height, 501 transformHint, 502 mFrontBufferedCallbacks 503 ) 504 .setGLRenderer(mGLRenderer) 505 .setMaxBuffers(1) 506 .setUsageFlags(obtainHardwareBufferUsageFlags()) 507 .setBufferFormat(bufferFormat) 508 .setSyncStrategy(mFrontBufferSyncStrategy) 509 .build() 510 511 mFrontBufferedRenderer = frontBufferedRenderer 512 mFrontBufferedLayerSurfaceControl = frontSurfaceControl 513 mMultiBufferedLayerSurfaceControl = parentSurfaceControl 514 mMultiBufferedRenderer = multiBufferedRenderer 515 mWidth = width 516 mHeight = height 517 mTransform = transformHint 518 } 519 } 520 521 /** 522 * Determines whether or not the [GLFrontBufferedRenderer] is in a valid state. That is the 523 * [release] method has not been called. If this returns false, then subsequent calls to 524 * [renderFrontBufferedLayer], [commit], and [release] are ignored 525 * 526 * @return `true` if this [GLFrontBufferedRenderer] has been released, `false` otherwise 527 */ 528 fun isValid(): Boolean = !mIsReleased 529 530 /** 531 * Render content to the front buffered layer providing optional parameters to be consumed in 532 * [Callback.onDrawFrontBufferedLayer]. Additionally the parameter provided here will also be 533 * consumed in [Callback.onDrawMultiBufferedLayer] when the corresponding [commit] method is 534 * invoked, which will include all [param]s in each call made to this method up to the 535 * corresponding [commit] call. 536 * 537 * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`, this 538 * call is ignored. 539 * 540 * @param param Optional parameter to be consumed when rendering content into the commit layer 541 */ 542 fun renderFrontBufferedLayer(param: T) { 543 if (isValid()) { 544 mActiveSegment.add(param) 545 if (mCommitCount.get() == 0) { 546 mFrontBufferedRenderer?.render() 547 } else { 548 mPendingRenderCount.incrementAndGet() 549 } 550 } else { 551 Log.w( 552 TAG, 553 "Attempt to render to front buffered layer when " + 554 "GLFrontBufferedRenderer has been released" 555 ) 556 } 557 } 558 559 /** 560 * Requests to render to the multi buffered layer. This schedules a call to 561 * [Callback.onDrawMultiBufferedLayer] with the parameters provided. If the front buffered layer 562 * is visible, this will hide this layer after rendering to the multi buffered layer is 563 * complete. This is equivalent to calling [GLFrontBufferedRenderer.renderFrontBufferedLayer] 564 * for each parameter provided in the collection followed by a single call to 565 * [GLFrontBufferedRenderer.commit]. This is useful for re-rendering the multi buffered scene 566 * when the corresponding Activity is being resumed from the background in which the contents 567 * should be re-drawn. Additionally this allows for applications to decide to dynamically render 568 * to either front or multi buffered layers. 569 * 570 * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns 'false', this 571 * call is ignored. 572 * 573 * @param params Parameters that to be consumed when rendering to the multi buffered layer. 574 * These parameters will be provided in the corresponding call to 575 * [Callback.onDrawMultiBufferedLayer] 576 */ 577 fun renderMultiBufferedLayer(params: Collection<T>) { 578 if (isValid()) { 579 mSegments.add(params) 580 mMultiBufferedRenderer?.render() 581 } else { 582 Log.w( 583 TAG, 584 "Attempt to render to the multi buffered layer when " + 585 "GLFrontBufferedRenderer has been released" 586 ) 587 } 588 } 589 590 /** 591 * Clears the contents of both the front and multi buffered layers. This triggers a call to 592 * [Callback.onMultiBufferedLayerRenderComplete] and hides the front buffered layer. 593 */ 594 fun clear() { 595 clearParamQueues() 596 mPendingClear.set(true) 597 mMultiBufferedRenderer?.render() 598 } 599 600 /** 601 * Requests to render the entire scene to the multi buffered layer and schedules a call to 602 * [Callback.onDrawMultiBufferedLayer]. The parameters provided to 603 * [Callback.onDrawMultiBufferedLayer] will include each argument provided to every 604 * [renderFrontBufferedLayer] call since the last call to [commit] has been made. 605 * 606 * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`, this 607 * call is ignored. 608 */ 609 fun commit() { 610 if (mCommitCount.getAndIncrement() == 0) { 611 commitInternal() 612 } 613 } 614 615 private fun commitInternal() { 616 if (isValid()) { 617 mPendingRenderCount.set(0) 618 mSegments.add(if (mActiveSegment.isEmpty()) emptyList() else mActiveSegment.release()) 619 mMultiBufferedRenderer?.render() 620 } else { 621 Log.w( 622 TAG, 623 "Attempt to render to the multi buffered layer when " + 624 "GLFrontBufferedRenderer has been released" 625 ) 626 } 627 } 628 629 /** 630 * Requests to cancel rendering and hides the front buffered layer. Unlike [commit], this does 631 * not schedule a call to render into the multi buffered layer. 632 * 633 * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`, this 634 * call is ignored. 635 */ 636 fun cancel() { 637 if (isValid()) { 638 mActiveSegment.clear() 639 mPendingRenderCount.set(0) 640 mGLRenderer.execute(mCancelRunnable) 641 } else { 642 Log.w( 643 TAG, 644 "Attempt to cancel rendering to front buffer after " + 645 "GLFrontBufferedRenderer has been released" 646 ) 647 } 648 } 649 650 /** 651 * Queue a [Runnable] to be executed on the GL rendering thread. Note it is important this 652 * [Runnable] does not block otherwise it can stall the GL thread. 653 * 654 * @param runnable to be executed 655 */ 656 fun execute(runnable: Runnable) { 657 if (isValid()) { 658 mGLRenderer.execute(runnable) 659 } else { 660 Log.w( 661 TAG, 662 "Attempt to execute runnable after GLFrontBufferedRenderer has " + "been released" 663 ) 664 } 665 } 666 667 /** 668 * Helper method used to detach the front and multi buffered render targets as well as release 669 * SurfaceControl instances 670 */ 671 internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) { 672 // If we're not cancelling the pending renders, dispatch requested but deferred renders 673 // immediately so they end up run before the thread is actually canceled. 674 if (!cancelPending) { 675 // This is the count of pending commits including an already-in-flight one, so update 676 // first to count and commit the remaining ones. 677 while (mCommitCount.updateAndGet { value -> max(value - 1, 0) } > 0) { 678 commitInternal() 679 } 680 // This is the count of deferred front-layer renders, so we update after to get the 681 // current count. Note that if there was a deferred commit, any deferred front-layer 682 // renders are preempted; the call to commitInternal sets mPendingRenderCount to 0. 683 while (mPendingRenderCount.getAndUpdate { value -> max(value - 1, 0) } > 0) { 684 mFrontBufferedRenderer?.render() 685 } 686 } 687 mMultiBufferedRenderer?.release(cancelPending) 688 mFrontBufferedRenderer?.release(cancelPending) 689 val frontSc = mFrontBufferedLayerSurfaceControl 690 val parentSc = mMultiBufferedLayerSurfaceControl 691 mGLRenderer.execute { 692 // This is deferred until all the GL callbacks not canceled by release are complete. 693 clearParamQueues() 694 if (frontSc != null && frontSc.isValid() && parentSc != null && parentSc.isValid()) { 695 SurfaceControlCompat.Transaction() 696 .reparent(frontSc, null) 697 .reparent(parentSc, null) 698 .commit() 699 frontSc.release() 700 parentSc.release() 701 } 702 mFrontBufferReleaseFence?.let { fence -> 703 fence.awaitForever() 704 fence.close() 705 mFrontBufferReleaseFence = null 706 } 707 mFrontLayerBuffer?.close() 708 onReleaseComplete?.invoke() 709 } 710 mMultiBufferedRenderer = null 711 mFrontBufferedRenderer = null 712 mFrontBufferedLayerSurfaceControl = null 713 mMultiBufferedLayerSurfaceControl = null 714 mWidth = -1 715 mHeight = -1 716 } 717 718 /** 719 * Releases the [GLFrontBufferedRenderer] and provides an optional callback that is invoked when 720 * the [GLFrontBufferedRenderer] is fully torn down. If the [cancelPending] flag is false, all 721 * pending requests to render into the front or multi buffered layers will be processed before 722 * the [GLFrontBufferedRenderer] is torn down. Otherwise, all in progress requests are ignored. 723 * If the [GLFrontBufferedRenderer] is already released, that is [isValid] returns `false`, this 724 * method does nothing. 725 * 726 * @param cancelPending If true, pending render requests are cancelled immediately. If false, 727 * pending render requests are completed before releasing the renderer. 728 * @param onReleaseComplete Optional callback invoked when the [GLFrontBufferedRenderer] has 729 * been released. This callback is invoked on the backing GLThread 730 */ 731 @JvmOverloads 732 fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) { 733 if (!isValid()) { 734 Log.w(TAG, "Attempt to release GLFrontBufferedRenderer that is already released") 735 return 736 } 737 738 detachTargets(cancelPending, onReleaseComplete) 739 740 mGLRenderer.unregisterEGLContextCallback(mContextCallbacks) 741 if (mIsManagingGLRenderer) { 742 // If we are managing the GLRenderer that we created ourselves 743 // do not cancel pending operations as we will miss callbacks that we are 744 // expecting above to properly teardown resources 745 // Instead rely on the cancel pending flags for detaching the front/multi buffered 746 // render targets instead 747 mGLRenderer.stop(false) 748 } 749 750 mSurfaceView?.holder?.removeCallback(mSurfaceCallbacks) 751 mSurfaceView = null 752 753 mIsReleased = true 754 } 755 756 private fun clearParamQueues() { 757 mActiveSegment.clear() 758 mCommitCount.set(0) 759 mSegments.clear() 760 mPendingRenderCount.set(0) 761 } 762 763 internal companion object { 764 765 internal const val TAG = "GLFrontBufferedRenderer" 766 } 767 768 @JvmDefaultWithCompatibility 769 /** 770 * Provides callbacks for consumers to draw into the front and multi buffered layers as well as 771 * provide opportunities to synchronize [SurfaceControlCompat.Transaction]s to submit the layers 772 * to the hardware compositor. 773 */ 774 interface Callback<T> { 775 776 /** 777 * Callback invoked to render content into the front buffered layer with the specified 778 * parameters. 779 * 780 * @param eglManager [EGLManager] useful in configuring EGL objects to be used when issuing 781 * OpenGL commands to render into the front buffered layer 782 * @param width Logical width of the content to render. This dimension matches what is 783 * provided from [SurfaceHolder.Callback.surfaceChanged] 784 * @param height Logical height of the content to render. This dimension matches what is 785 * provided from [SurfaceHolder.Callback.surfaceChanged] 786 * @param bufferInfo [BufferInfo] about the buffer that is being rendered into. This 787 * includes the width and height of the buffer which can be different than the 788 * corresponding dimensions of the [SurfaceView] provided to the [GLFrontBufferedRenderer] 789 * as pre-rotation can occasionally swap width and height parameters in order to avoid GPU 790 * composition to rotate content. This should be used as input to [GLES20.glViewport]. 791 * Additionally this also contains a frame buffer identifier that can be used to retarget 792 * rendering operations to the original destination after rendering into intermediate 793 * scratch buffers. 794 * @param transform Matrix that should be applied to the rendering in this callback. This 795 * should be consumed as input to any vertex shader implementations. Buffers are 796 * pre-rotated in advance in order to avoid unnecessary overhead of GPU composition to 797 * rotate content in the same install orientation of the display. This is a 4 x 4 matrix 798 * is represented as a flattened array of 16 floating point values. Consumers are expected 799 * to leverage [Matrix.multiplyMM] with this parameter alongside any additional 800 * transformations that are to be applied. For example: 801 * ``` 802 * val myMatrix = FloatArray(16) 803 * Matrix.orthoM( 804 * myMatrix, // matrix 805 * 0, // offset starting index into myMatrix 806 * 0f, // left 807 * bufferInfo.bufferWidth.toFloat(), // right 808 * 0f, // bottom 809 * bufferInfo.bufferHeight.toFloat(), // top 810 * -1f, // near 811 * 1f, // far 812 * ) 813 * val result = FloatArray(16) 814 * Matrix.multiplyMM(result, 0, myMatrix, 0, transform, 0) 815 * ``` 816 * 817 * @param param optional parameter provided the corresponding 818 * [GLFrontBufferedRenderer.renderFrontBufferedLayer] method that triggered this request 819 * to render into the front buffered layer 820 */ 821 @WorkerThread 822 fun onDrawFrontBufferedLayer( 823 eglManager: EGLManager, 824 width: Int, 825 height: Int, 826 bufferInfo: BufferInfo, 827 transform: FloatArray, 828 param: T 829 ) 830 831 /** 832 * Callback invoked to render content into the multi buffered layer with the specified 833 * parameters. 834 * 835 * @param eglManager [EGLManager] useful in configuring EGL objects to be used when issuing 836 * OpenGL commands to render into the multi buffered layer 837 * @param width Logical width of the content to render. This dimension matches what is 838 * provided from [SurfaceHolder.Callback.surfaceChanged] 839 * @param height Logical height of the content to render. This dimension matches what is 840 * provided from [SurfaceHolder.Callback.surfaceChanged] 841 * @param bufferInfo [BufferInfo] about the buffer that is being rendered into. This 842 * includes the width and height of the buffer which can be different than the 843 * corresponding dimensions of the [SurfaceView] provided to the [GLFrontBufferedRenderer] 844 * as pre-rotation can occasionally swap width and height parameters in order to avoid GPU 845 * composition to rotate content. This should be used as input to [GLES20.glViewport]. 846 * Additionally this also contains a frame buffer identifier that can be used to retarget 847 * rendering operations to the original destination after rendering into intermediate 848 * scratch buffers. 849 * @param transform Matrix that should be applied to the rendering in this callback. This 850 * should be consumed as input to any vertex shader implementations. Buffers are 851 * pre-rotated in advance in order to avoid unnecessary overhead of GPU composition to 852 * rotate content in the same install orientation of the display. This is a 4 x 4 matrix 853 * is represented as a flattened array of 16 floating point values. Consumers are expected 854 * to leverage [Matrix.multiplyMM] with this parameter alongside any additional 855 * transformations that are to be applied. For example: 856 * ``` 857 * val myMatrix = FloatArray(16) 858 * Matrix.orthoM( 859 * myMatrix, // matrix 860 * 0, // offset starting index into myMatrix 861 * 0f, // left 862 * bufferInfo.bufferWidth.toFloat(), // right 863 * 0f, // bottom 864 * bufferInfo.bufferHeight.toFloat(), // top 865 * -1f, // near 866 * 1f, // far 867 * ) 868 * val result = FloatArray(16) 869 * Matrix.multiplyMM(result, 0, myMatrix, 0, transform, 0) 870 * ``` 871 * 872 * @param params optional parameter provided to render the entire scene into the multi 873 * buffered layer. This is a collection of all parameters provided in consecutive 874 * invocations to [GLFrontBufferedRenderer.renderFrontBufferedLayer] since the last call 875 * to [GLFrontBufferedRenderer.commit] has been made. After 876 * [GLFrontBufferedRenderer.commit] is invoked, this collection is cleared and new 877 * parameters are added on each subsequent call to 878 * [GLFrontBufferedRenderer.renderFrontBufferedLayer]. 879 * 880 * Consider the following example: 881 * 882 * myFrontBufferedRenderer.renderFrontBufferedLayer(1) 883 * myFrontBufferedRenderer.renderFrontBufferedLayer(2) 884 * myFrontBufferedRenderer.renderFrontBufferedLayer(3) myFrontBufferedRenderer.commit() 885 * 886 * This will generate a callback to this method with the params collection containing values 887 * [1, 2, 3] 888 * 889 * myFrontBufferedRenderer.renderFrontBufferedLayer(4) 890 * myFrontBufferedRenderer.renderFrontBufferedLayer(5) myFrontBufferedRenderer.commit() 891 * 892 * This will generate a callback to this method with the params collection containing values 893 * [4, 5] 894 * 895 * By default [GLES20.glViewport] is invoked with the correct dimensions of the buffer that 896 * is being rendered into taking into account pre-rotation transformations 897 */ 898 @WorkerThread 899 fun onDrawMultiBufferedLayer( 900 eglManager: EGLManager, 901 width: Int, 902 height: Int, 903 bufferInfo: BufferInfo, 904 transform: FloatArray, 905 params: Collection<T> 906 ) 907 908 /** 909 * Optional callback invoked when rendering to the front buffered layer is complete but 910 * before the buffers are submitted to the hardware compositor. This provides consumers a 911 * mechanism for synchronizing the transaction with other [SurfaceControlCompat] objects 912 * that maybe rendered within the scene. 913 * 914 * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 915 * front buffered layer content is drawn. This can be used to configure various properties 916 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 917 * [SurfaceControlCompat.Transaction]. 918 * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered 919 * content to the front buffered layer. 920 */ 921 @WorkerThread 922 fun onFrontBufferedLayerRenderComplete( 923 frontBufferedLayerSurfaceControl: SurfaceControlCompat, 924 transaction: SurfaceControlCompat.Transaction 925 ) { 926 // Default implementation is a no-op 927 } 928 929 /** 930 * Optional callback invoked when rendering to the multi buffered layer is complete but 931 * before the buffers are submitted to the hardware compositor. This provides consumers a 932 * mechanism for synchronizing the transaction with other [SurfaceControlCompat] objects 933 * that maybe rendered within the scene. 934 * 935 * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 936 * front buffered layer content is drawn. This can be used to configure various properties 937 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 938 * [SurfaceControlCompat.Transaction]. 939 * @param multiBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 940 * front buffered layer content is drawn. This can be used to configure various properties 941 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 942 * [SurfaceControlCompat.Transaction]. 943 * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered 944 * content to the multi buffered layer. 945 */ 946 @WorkerThread 947 fun onMultiBufferedLayerRenderComplete( 948 frontBufferedLayerSurfaceControl: SurfaceControlCompat, 949 multiBufferedLayerSurfaceControl: SurfaceControlCompat, 950 transaction: SurfaceControlCompat.Transaction 951 ) { 952 // Default implementation is a no-op 953 } 954 } 955 } 956