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.annotation.SuppressLint 20 import android.graphics.BlendMode 21 import android.graphics.Canvas 22 import android.graphics.Color 23 import android.graphics.ColorSpace 24 import android.graphics.RenderNode 25 import android.hardware.HardwareBuffer 26 import android.os.Build 27 import android.util.Log 28 import android.view.SurfaceHolder 29 import android.view.SurfaceView 30 import androidx.annotation.RequiresApi 31 import androidx.annotation.WorkerThread 32 import androidx.graphics.CanvasBufferedRenderer 33 import androidx.graphics.surface.SurfaceControlCompat 34 import androidx.graphics.utils.HandlerThreadExecutor 35 import androidx.hardware.HardwareBufferFormat 36 import androidx.hardware.SyncFenceCompat 37 import java.util.Collections 38 import java.util.concurrent.CountDownLatch 39 import java.util.concurrent.atomic.AtomicBoolean 40 import java.util.concurrent.atomic.AtomicInteger 41 import kotlin.math.max 42 43 /** 44 * Class responsible for supporting a "front buffered" rendering system. This allows for lower 45 * latency graphics by leveraging a combination of front buffered and multi buffered content layers. 46 * Active content is rendered first into the front buffered layer which is simultaneously being 47 * presented to the display. Periodically content is rendered into the multi buffered layer which 48 * will have more traditional latency guarantees, however, minimizes the impact of visual artifacts 49 * due to graphical tearing. 50 * 51 * @param surfaceView Target SurfaceView to act as the parent rendering layer for multi buffered 52 * content 53 * @param callback Callbacks used to render into front and multi buffered layers as well as 54 * configuring [SurfaceControlCompat.Transaction]s for controlling these layers in addition to 55 * other [SurfaceControlCompat] instances that must be updated atomically within the user 56 * interface. These callbacks are invoked on an internal rendering thread. The templated type here 57 * is consumer defined to represent the data structures to be consumed for rendering within 58 * [Callback.onDrawFrontBufferedLayer] and [Callback.onDrawMultiBufferedLayer] and are provided by 59 * the [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] and 60 * [CanvasFrontBufferedRenderer.renderMultiBufferedLayer] methods. 61 * @param bufferFormat format of the underlying buffers being rendered into by 62 * [CanvasFrontBufferedRenderer]. The particular valid combinations for a given Android version 63 * and implementation should be documented by that version. [HardwareBuffer.RGBA_8888] and 64 * [HardwareBuffer.RGBX_8888] are guaranteed to be supported. However, consumers are recommended 65 * to query the desired HardwareBuffer configuration using [HardwareBuffer.isSupported]. The 66 * default is [HardwareBuffer.RGBA_8888]. 67 */ 68 @RequiresApi(Build.VERSION_CODES.Q) 69 class CanvasFrontBufferedRenderer<T> 70 @JvmOverloads 71 constructor( 72 surfaceView: SurfaceView, 73 callback: Callback<T>, 74 @HardwareBufferFormat val bufferFormat: Int = HardwareBuffer.RGBA_8888 75 ) { 76 77 /** Target SurfaceView for rendering */ 78 private var mSurfaceView: SurfaceView? = null 79 80 private var mCallback: Callback<T>? = null 81 82 /** 83 * Executor used to deliver callbacks for rendering as well as issuing surface control 84 * transactions 85 */ 86 private val mHandlerThread = HandlerThreadExecutor("CanvasRenderThread") 87 88 /** RenderNode used to render multi buffered content */ 89 private var mMultiBufferedRenderNode: RenderNode? = null 90 91 /** 92 * Renderer used to draw [RenderNode] into a [HardwareBuffer] that is used to configure the 93 * parent SurfaceControl that represents the multi-buffered scene 94 */ 95 private var mMultiBufferedCanvasRenderer: CanvasBufferedRenderer? = null 96 97 /** 98 * Renderer used to draw the front buffer content into a HardwareBuffer instance that is 99 * preserved across frames 100 */ 101 private var mPersistedCanvasRenderer: SingleBufferedCanvasRenderer<T>? = null 102 103 /** 104 * [SurfaceControlCompat] used to configure buffers and visibility of the front buffered layer 105 */ 106 private var mFrontBufferSurfaceControl: SurfaceControlCompat? = null 107 108 /** 109 * [SurfaceControlCompat] used to configure buffers and visibility of the multi-buffered layer 110 */ 111 private var mParentSurfaceControl: SurfaceControlCompat? = null 112 113 /** 114 * Queue of parameters to be consumed in [Callback.onDrawFrontBufferedLayer] with the parameter 115 * provided in [renderFrontBufferedLayer]. When [commit] is invoked the collection is used to 116 * render the multi-buffered scene and is subsequently cleared 117 */ 118 private var mParams = ParamQueue<T>() 119 120 /** 121 * Flag to determine if the [CanvasFrontBufferedRenderer] has previously been released. If this 122 * flag is true, then subsequent requests to [renderFrontBufferedLayer], 123 * [renderMultiBufferedLayer], [commit], and [release] are ignored. 124 */ 125 private var mIsReleased = false 126 127 /** 128 * Flag to determine if a request to clear the front buffer content is pending. This should only 129 * be accessed on the background thread 130 */ 131 private val mPendingClear = AtomicBoolean(true) 132 133 /** 134 * Runnable executed on the GLThread to update [FrontBufferSyncStrategy.isVisible] as well as 135 * hide the SurfaceControl associated with the front buffered layer 136 */ 137 private val mCancelRunnable = Runnable { 138 mPersistedCanvasRenderer?.isVisible = false 139 mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl -> 140 SurfaceControlCompat.Transaction() 141 .setVisibility(frontBufferSurfaceControl, false) 142 .commit() 143 } 144 } 145 146 /** Current HardwareBuffer that is being presented by the multi buffered layer */ 147 private var mCurrentMultiBuffer: HardwareBuffer? = null 148 149 @Volatile private var mFrontBufferReleaseFence: SyncFenceCompat? = null 150 private val mCommitCount = AtomicInteger(0) 151 private var mColorSpace: ColorSpace = CanvasBufferedRenderer.DefaultColorSpace 152 private var mInverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM 153 private var mWidth = -1 154 private var mHeight = -1 155 private var mTransform = BufferTransformHintResolver.UNKNOWN_TRANSFORM 156 private val mTransformResolver = BufferTransformHintResolver() 157 private val mHolderCallback = 158 object : SurfaceHolder.Callback2 { 159 160 override fun surfaceCreated(p0: SurfaceHolder) { 161 // NO-OP 162 } 163 164 override fun surfaceChanged( 165 holder: SurfaceHolder, 166 format: Int, 167 width: Int, 168 height: Int 169 ) { 170 mSurfaceView?.let { update(it, width, height) } 171 } 172 173 override fun surfaceDestroyed(p0: SurfaceHolder) { 174 releaseInternal(true) 175 } 176 177 override fun surfaceRedrawNeeded(holder: SurfaceHolder) { 178 val latch = CountDownLatch(1) 179 renderMultiBufferedLayerInternal { latch.countDown() } 180 latch.await() 181 } 182 183 override fun surfaceRedrawNeededAsync( 184 holder: SurfaceHolder, 185 drawingFinished: Runnable 186 ) { 187 renderMultiBufferedLayerInternal(callback = drawingFinished) 188 } 189 } 190 191 init { 192 mSurfaceView = surfaceView 193 mCallback = callback 194 surfaceView.holder.addCallback(mHolderCallback) 195 with(surfaceView.holder) { 196 if (surface != null && surface.isValid) { 197 update(surfaceView, surfaceView.width, surfaceView.height) 198 renderMultiBufferedLayerInternal() 199 } 200 } 201 } 202 203 internal fun update(surfaceView: SurfaceView, width: Int, height: Int) { 204 if (width <= 0 || height <= 0) { 205 Log.w( 206 TAG, 207 "Invalid dimensions provided, width and height must be > 0. " + 208 "width: $width height: $height" 209 ) 210 return 211 } 212 val transformHint = mTransformResolver.getBufferTransformHint(surfaceView) 213 if ((mTransform != transformHint || mWidth != width || mHeight != height) && isValid()) { 214 releaseInternal(true) 215 216 val bufferTransform = BufferTransformer() 217 val inverse = bufferTransform.invertBufferTransform(transformHint) 218 bufferTransform.computeTransform(width, height, inverse) 219 val bufferWidth = bufferTransform.bufferWidth 220 val bufferHeight = bufferTransform.bufferHeight 221 222 val parentSurfaceControl = 223 SurfaceControlCompat.Builder() 224 .setParent(surfaceView) 225 .setName("MultiBufferedLayer") 226 .build() 227 .apply { 228 // SurfaceControl is not visible by default so make it visible right 229 // after creation 230 SurfaceControlCompat.Transaction().setVisibility(this, true).commit() 231 } 232 233 val frontBufferSurfaceControl = 234 SurfaceControlCompat.Builder() 235 .setParent(parentSurfaceControl) 236 .setName("FrontBufferedLayer") 237 .build() 238 239 FrontBufferUtils.configureFrontBufferLayerFrameRate(frontBufferSurfaceControl)?.commit() 240 241 var singleBufferedCanvasRenderer: SingleBufferedCanvasRenderer<T>? = null 242 singleBufferedCanvasRenderer = 243 SingleBufferedCanvasRenderer( 244 width, 245 height, 246 bufferWidth, 247 bufferHeight, 248 bufferFormat, 249 inverse, 250 mHandlerThread, 251 object : SingleBufferedCanvasRenderer.RenderCallbacks<T> { 252 253 override fun render(canvas: Canvas, width: Int, height: Int, param: T) { 254 if (mPendingClear.compareAndSet(true, false)) { 255 mFrontBufferReleaseFence?.let { fence -> 256 fence.awaitForever() 257 fence.close() 258 mFrontBufferReleaseFence = null 259 } 260 canvas.drawColor(Color.BLACK, BlendMode.CLEAR) 261 } 262 mCallback?.onDrawFrontBufferedLayer(canvas, width, height, param) 263 } 264 265 @SuppressLint("WrongConstant") 266 override fun onBufferReady( 267 hardwareBuffer: HardwareBuffer, 268 syncFenceCompat: SyncFenceCompat? 269 ) { 270 if (frontBufferSurfaceControl.isValid()) { 271 val transaction = 272 SurfaceControlCompat.Transaction() 273 .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE) 274 .setBuffer( 275 frontBufferSurfaceControl, 276 hardwareBuffer, 277 if ( 278 singleBufferedCanvasRenderer?.isVisible == true 279 ) { 280 null 281 } else { 282 syncFenceCompat 283 } 284 ) { releaseFence -> 285 mFrontBufferReleaseFence?.close() 286 mFrontBufferReleaseFence = releaseFence 287 } 288 .setVisibility(frontBufferSurfaceControl, true) 289 .reparent( 290 frontBufferSurfaceControl, 291 parentSurfaceControl 292 ) 293 if ( 294 transformHint != 295 BufferTransformHintResolver.UNKNOWN_TRANSFORM 296 ) { 297 transaction.setBufferTransform( 298 frontBufferSurfaceControl, 299 transformHint 300 ) 301 } 302 mCallback?.onFrontBufferedLayerRenderComplete( 303 frontBufferSurfaceControl, 304 transaction 305 ) 306 transaction.commit() 307 singleBufferedCanvasRenderer?.isVisible = true 308 } 309 syncFenceCompat?.close() 310 } 311 } 312 ) 313 .apply { colorSpace = mColorSpace } 314 315 val renderNode = RenderNode("node").apply { setPosition(0, 0, width, height) } 316 317 mMultiBufferedCanvasRenderer = 318 CanvasBufferedRenderer.Builder(bufferWidth, bufferHeight) 319 .setUsageFlags(FrontBufferUtils.BaseFlags) 320 .setBufferFormat(bufferFormat) 321 .build() 322 .apply { setContentRoot(renderNode) } 323 324 mMultiBufferedRenderNode = renderNode 325 mFrontBufferSurfaceControl = frontBufferSurfaceControl 326 mPersistedCanvasRenderer = singleBufferedCanvasRenderer 327 mParentSurfaceControl = parentSurfaceControl 328 mTransform = transformHint 329 mWidth = width 330 mHeight = height 331 mInverse = inverse 332 } 333 } 334 335 /** 336 * Configures the [ColorSpace] that the content should be rendered with for the front and multi 337 * buffered layers. This parameter is only consumed on Android U and above. For older API levels 338 * this is ignored. 339 */ 340 var colorSpace: ColorSpace 341 get() = mColorSpace 342 set(value) { 343 mColorSpace = value 344 mPersistedCanvasRenderer?.colorSpace = value 345 } 346 347 /** 348 * Render content to the front buffered layer providing optional parameters to be consumed in 349 * [Callback.onDrawFrontBufferedLayer]. Additionally the parameter provided here will also be 350 * consumed in [Callback.onDrawMultiBufferedLayer] when the corresponding [commit] method is 351 * invoked, which will include all [param]s in each call made to this method up to the 352 * corresponding [commit] call. 353 * 354 * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns `false`, 355 * this call is ignored. 356 * 357 * @param param Optional parameter to be consumed when rendering content into the commit layer 358 */ 359 fun renderFrontBufferedLayer(param: T) { 360 if (isValid()) { 361 mParams.add(param) 362 if (!isCommitting()) { 363 flushPendingFrontBufferRenders() 364 } 365 } else { 366 Log.w( 367 TAG, 368 "Attempt to render to front buffered layer when " + 369 "CanvasFrontBufferedRenderer has been released" 370 ) 371 } 372 } 373 374 private fun isCommitting() = mCommitCount.get() != 0 375 376 private fun flushPendingFrontBufferRenders() { 377 mParams.flush { p -> mPersistedCanvasRenderer?.render(p) } 378 } 379 380 /** 381 * Requests to render to the multi buffered layer. This schedules a call to 382 * [Callback.onDrawMultiBufferedLayer] with the parameters provided. If the front buffered layer 383 * is visible, this will hide this layer after rendering to the multi buffered layer is 384 * complete. This is equivalent to calling 385 * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] for each parameter provided in the 386 * collection followed by a single call to [CanvasFrontBufferedRenderer.commit]. This is useful 387 * for re-rendering the multi buffered scene when the corresponding Activity is being resumed 388 * from the background in which the contents should be re-drawn. Additionally this allows for 389 * applications to decide to dynamically render to either front or multi buffered layers. 390 * 391 * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns 'false', 392 * this call is ignored. 393 * 394 * @param params Parameters that to be consumed when rendering to the multi buffered layer. 395 * These parameters will be provided in the corresponding call to 396 * [Callback.onDrawMultiBufferedLayer] 397 */ 398 fun renderMultiBufferedLayer(params: Collection<T>) { 399 renderMultiBufferedLayerInternal(params) 400 } 401 402 /** 403 * Helper method to commit contents to the multi buffered layer invoking an optional callback 404 * when rendering is complete 405 */ 406 internal fun renderMultiBufferedLayerInternal( 407 params: Collection<T> = Collections.emptyList(), 408 callback: Runnable? = null 409 ) { 410 if (isValid()) { 411 mParams.addAll(params) 412 commitInternal(callback) 413 } else { 414 Log.w( 415 TAG, 416 "Attempt to render to the multi buffered layer when " + 417 "CanvasFrontBufferedRenderer has been released" 418 ) 419 } 420 } 421 422 /** 423 * Determines whether or not the [CanvasFrontBufferedRenderer] is in a valid state. That is the 424 * [release] method has not been called. If this returns false, then subsequent calls to 425 * [renderFrontBufferedLayer], [renderMultiBufferedLayer], [commit], and [release] are ignored 426 * 427 * @return `true` if this [CanvasFrontBufferedRenderer] has been released, `false` otherwise 428 */ 429 fun isValid() = !mIsReleased 430 431 @SuppressLint("WrongConstant") 432 internal fun setParentSurfaceControlBuffer( 433 frontBufferSurfaceControl: SurfaceControlCompat?, 434 parentSurfaceControl: SurfaceControlCompat?, 435 persistedCanvasRenderer: SingleBufferedCanvasRenderer<T>?, 436 multiBufferedCanvasRenderer: CanvasBufferedRenderer, 437 transform: Int, 438 buffer: HardwareBuffer, 439 fence: SyncFenceCompat? 440 ) { 441 if ( 442 frontBufferSurfaceControl != null && 443 frontBufferSurfaceControl.isValid() && 444 parentSurfaceControl != null && 445 parentSurfaceControl.isValid() 446 ) { 447 mCurrentMultiBuffer = buffer 448 persistedCanvasRenderer?.isVisible = false 449 val transaction = 450 SurfaceControlCompat.Transaction() 451 .setVisibility(frontBufferSurfaceControl, false) 452 // Set a null buffer here so that the original front buffer's release callback 453 // gets invoked and we can clear the content of the front buffer 454 .setBuffer(frontBufferSurfaceControl, null) 455 .setVisibility(parentSurfaceControl, true) 456 .setBuffer(parentSurfaceControl, buffer, fence) { releaseFence -> 457 mPendingClear.set(true) 458 val result = mCommitCount.updateAndGet { value -> max(value - 1, 0) } 459 if (result != 0) { 460 mSurfaceView?.post { commitInternal() } 461 } else { 462 flushPendingFrontBufferRenders() 463 } 464 multiBufferedCanvasRenderer.releaseBuffer(buffer, releaseFence) 465 } 466 467 if (transform != BufferTransformHintResolver.UNKNOWN_TRANSFORM) { 468 transaction.setBufferTransform(parentSurfaceControl, transform) 469 } 470 mCallback?.onMultiBufferedLayerRenderComplete( 471 frontBufferSurfaceControl, 472 parentSurfaceControl, 473 transaction 474 ) 475 transaction.commit() 476 } 477 } 478 479 /** 480 * Clears the contents of both the front and multi buffered layers. This triggers a call to 481 * [Callback.onMultiBufferedLayerRenderComplete] and hides the front buffered layer. 482 */ 483 @SuppressWarnings("WrongConstant") 484 fun clear() { 485 if (isValid()) { 486 mParams.clear() 487 val persistedCanvasRenderer = 488 mPersistedCanvasRenderer?.apply { 489 cancelPending() 490 clear() 491 } 492 val transform = mTransform 493 val inverse = mInverse 494 val frontBufferSurfaceControl = mFrontBufferSurfaceControl 495 val parentSurfaceControl = mParentSurfaceControl 496 val multiBufferedCanvasRenderer = mMultiBufferedCanvasRenderer 497 val targetColorSpace = mColorSpace 498 mHandlerThread.execute { 499 multiBufferedCanvasRenderer?.let { multiBufferRenderer -> 500 with(multiBufferRenderer) { 501 mMultiBufferedRenderNode?.let { renderNode -> 502 val canvas = renderNode.beginRecording() 503 canvas.drawColor(Color.BLACK, BlendMode.CLEAR) 504 renderNode.endRecording() 505 } 506 507 obtainRenderRequest() 508 .apply { 509 if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) { 510 setBufferTransform(inverse) 511 } 512 } 513 .setColorSpace(targetColorSpace) 514 .drawAsync(mHandlerThread) { result -> 515 setParentSurfaceControlBuffer( 516 frontBufferSurfaceControl, 517 parentSurfaceControl, 518 persistedCanvasRenderer, 519 multiBufferRenderer, 520 transform, 521 result.hardwareBuffer, 522 result.fence 523 ) 524 } 525 } 526 } 527 } 528 } else { 529 Log.w( 530 TAG, 531 "Attempt to clear front buffer after CanvasFrontBufferRenderer " + 532 "has been released" 533 ) 534 } 535 } 536 537 /** 538 * Requests to render the entire scene to the multi buffered layer and schedules a call to 539 * [Callback.onDrawMultiBufferedLayer]. The parameters provided to 540 * [Callback.onDrawMultiBufferedLayer] will include each argument provided to every 541 * [renderFrontBufferedLayer] call since the last call to [commit] has been made. When rendering 542 * to the multi-buffered layer is complete, this synchronously hides the front buffer and 543 * updates the multi buffered layer. 544 * 545 * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns `false`, 546 * this call is ignored. 547 */ 548 fun commit() { 549 if (mCommitCount.getAndIncrement() == 0) { 550 commitInternal() 551 } 552 } 553 554 /** 555 * Helper method to commit contents to the multi buffered layer, invoking an optional callback 556 * on completion 557 */ 558 @SuppressWarnings("WrongConstant") 559 private fun commitInternal(onComplete: Runnable? = null) { 560 if (isValid()) { 561 val persistedCanvasRenderer = mPersistedCanvasRenderer?.apply { cancelPending() } 562 val params = mParams.release() 563 val width = mWidth 564 val height = mHeight 565 val frontBufferSurfaceControl = mFrontBufferSurfaceControl 566 val parentSurfaceControl = mParentSurfaceControl 567 val multiBufferedCanvasRenderer = mMultiBufferedCanvasRenderer 568 val inverse = mInverse 569 val transform = mTransform 570 val targetColorSpace = mColorSpace 571 mHandlerThread.execute { 572 multiBufferedCanvasRenderer?.let { multiBufferedRenderer -> 573 with(multiBufferedRenderer) { 574 mMultiBufferedRenderNode?.let { renderNode -> 575 val canvas = renderNode.beginRecording() 576 mCallback?.onDrawMultiBufferedLayer(canvas, width, height, params) 577 renderNode.endRecording() 578 } 579 580 params.clear() 581 obtainRenderRequest() 582 .apply { 583 if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) { 584 setBufferTransform(inverse) 585 } 586 } 587 .setColorSpace(targetColorSpace) 588 .drawAsync(mHandlerThread) { result -> 589 setParentSurfaceControlBuffer( 590 frontBufferSurfaceControl, 591 parentSurfaceControl, 592 persistedCanvasRenderer, 593 multiBufferedCanvasRenderer, 594 transform, 595 result.hardwareBuffer, 596 result.fence 597 ) 598 onComplete?.run() 599 } 600 } 601 } 602 } 603 } else { 604 Log.w( 605 TAG, 606 "Attempt to render to the multi buffered layer when " + 607 "CanvasFrontBufferedRenderer has been released" 608 ) 609 } 610 } 611 612 /** 613 * Requests to cancel rendering and hides the front buffered layer. Unlike [commit], this does 614 * not schedule a call to render into the multi buffered layer. This is useful in palm rejection 615 * use cases, where some initial touch events might be processed before a corresponding cancel 616 * event is received indicating the touch gesture is coming from a palm rather than intentional 617 * user input. In the case where MotionEvent#getAction returns ACTION_CANCEL, this is to be 618 * invoked. 619 * 620 * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`, this 621 * call is ignored. 622 */ 623 fun cancel() { 624 if (isValid()) { 625 mParams.clear() 626 mPersistedCanvasRenderer?.cancelPending() 627 mHandlerThread.execute(mCancelRunnable) 628 mPersistedCanvasRenderer?.clear() 629 } else { 630 Log.w( 631 TAG, 632 "Attempt to cancel rendering to front buffer after " + 633 "CanvasFrontBufferRenderer has been released" 634 ) 635 } 636 } 637 638 internal fun releaseInternal(cancelPending: Boolean, releaseCallback: (() -> Unit)? = null) { 639 val renderer = mPersistedCanvasRenderer 640 if (renderer != null) { 641 // Store a local copy of the corresponding SurfaceControls and renderers to make sure 642 // the release callback is not invoked on potentially newly created dependencies 643 // if we are in the middle of a render request and we get a surface changed event 644 val frontBufferSurfaceControl = mFrontBufferSurfaceControl 645 val parentSurfaceControl = mParentSurfaceControl 646 val multiBufferRenderer = mMultiBufferedCanvasRenderer 647 val renderNode = mMultiBufferedRenderNode 648 649 mFrontBufferSurfaceControl = null 650 mParentSurfaceControl = null 651 mPersistedCanvasRenderer = null 652 mMultiBufferedCanvasRenderer = null 653 mMultiBufferedRenderNode = null 654 mWidth = -1 655 mHeight = -1 656 mTransform = BufferTransformHintResolver.UNKNOWN_TRANSFORM 657 658 renderer.release(cancelPending) { 659 mCurrentMultiBuffer?.close() 660 mCurrentMultiBuffer = null 661 val reparentTransaction = SurfaceControlCompat.Transaction() 662 if (frontBufferSurfaceControl?.isValid() == true) { 663 reparentTransaction.reparent(frontBufferSurfaceControl, null) 664 } 665 if (parentSurfaceControl?.isValid() == true) { 666 reparentTransaction.reparent(parentSurfaceControl, null) 667 } 668 reparentTransaction.commit() 669 reparentTransaction.close() 670 frontBufferSurfaceControl?.release() 671 parentSurfaceControl?.release() 672 multiBufferRenderer?.close() 673 renderNode?.discardDisplayList() 674 releaseCallback?.invoke() 675 } 676 } else if (releaseCallback != null) { 677 mHandlerThread.execute(releaseCallback) 678 } 679 } 680 681 /** 682 * Releases the [CanvasFrontBufferedRenderer]. In process requests are ignored. If the 683 * [CanvasFrontBufferedRenderer] is already released, that is [isValid] returns `false`, this 684 * method does nothing. 685 */ 686 @JvmOverloads 687 fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) { 688 if (!mIsReleased) { 689 mSurfaceView?.holder?.removeCallback(mHolderCallback) 690 mSurfaceView = null 691 releaseInternal(cancelPending) { 692 mCallback = null 693 onReleaseComplete?.invoke() 694 mHandlerThread.quit() 695 } 696 mIsReleased = true 697 } 698 } 699 700 /** 701 * Provides callbacks for consumers to draw into the front and multi buffered layers as well as 702 * provide opportunities to synchronize [SurfaceControlCompat.Transaction]s to submit the layers 703 * to the hardware compositor. 704 */ 705 @JvmDefaultWithCompatibility 706 interface Callback<T> { 707 708 /** 709 * Callback invoked to render content into the front buffered layer with the specified 710 * parameters. 711 * 712 * @param canvas [Canvas] used to issue drawing instructions into the front buffered layer 713 * @param bufferWidth Width of the buffer that is being rendered into. 714 * @param bufferHeight Height of the buffer that is being rendered into. 715 * @param param optional parameter provided the corresponding 716 * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] method that triggered this 717 * request to render into the front buffered layer 718 */ 719 @WorkerThread 720 fun onDrawFrontBufferedLayer(canvas: Canvas, bufferWidth: Int, bufferHeight: Int, param: T) 721 722 /** 723 * Callback invoked to render content into the front buffered layer with the specified 724 * parameters. 725 * 726 * @param canvas [Canvas] used to issue drawing instructions into the front buffered layer 727 * @param bufferWidth Width of the buffer that is being rendered into. 728 * @param bufferHeight Height of the buffer that is being rendered into. 729 * @param params optional parameter provided to render the entire scene into the multi 730 * buffered layer. This is a collection of all parameters provided in consecutive 731 * invocations to [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] since the last 732 * call to [CanvasFrontBufferedRenderer.commit] has been made. After 733 * [CanvasFrontBufferedRenderer.commit] is invoked, this collection is cleared and new 734 * parameters are added on each subsequent call to 735 * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] 736 */ 737 @WorkerThread 738 fun onDrawMultiBufferedLayer( 739 canvas: Canvas, 740 bufferWidth: Int, 741 bufferHeight: Int, 742 params: Collection<T> 743 ) 744 745 /** 746 * Optional callback invoked when rendering to the front buffered layer is complete but 747 * before the buffers are submitted to the hardware compositor. This provides consumers a 748 * mechanism for synchronizing the transaction with other [SurfaceControlCompat] objects 749 * that maybe rendered within the scene. 750 * 751 * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 752 * front buffered layer content is drawn. This can be used to configure various properties 753 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 754 * [SurfaceControlCompat.Transaction]. 755 * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered 756 * content to the front buffered layer. 757 */ 758 @WorkerThread 759 fun onFrontBufferedLayerRenderComplete( 760 frontBufferedLayerSurfaceControl: SurfaceControlCompat, 761 transaction: SurfaceControlCompat.Transaction 762 ) { 763 // Default implementation is a no-op 764 } 765 766 /** 767 * Optional callback invoked when rendering to the multi buffered layer is complete but 768 * before the buffers are submitted to the hardware compositor. This provides consumers a 769 * mechanism for synchronizing the transaction with other [SurfaceControlCompat] objects 770 * that maybe rendered within the scene. 771 * 772 * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 773 * front buffered layer content is drawn. This can be used to configure various properties 774 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 775 * [SurfaceControlCompat.Transaction]. 776 * @param multiBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the 777 * multi-buffered layer content is drawn. This can be used to configure various properties 778 * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding 779 * [SurfaceControlCompat.Transaction]. 780 * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered 781 * content to the multi buffered layer. 782 */ 783 @WorkerThread 784 fun onMultiBufferedLayerRenderComplete( 785 frontBufferedLayerSurfaceControl: SurfaceControlCompat, 786 multiBufferedLayerSurfaceControl: SurfaceControlCompat, 787 transaction: SurfaceControlCompat.Transaction 788 ) { 789 // Default implementation is a no-op 790 } 791 } 792 793 internal companion object { 794 795 internal const val TAG = "LowLatencyCanvas" 796 } 797 } 798