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.opengl 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.IntRange 27 import androidx.annotation.RequiresApi 28 import androidx.annotation.WorkerThread 29 import androidx.graphics.lowlatency.BufferInfo 30 import androidx.graphics.lowlatency.BufferTransformHintResolver 31 import androidx.graphics.lowlatency.BufferTransformer 32 import androidx.graphics.opengl.egl.EGLManager 33 import androidx.graphics.opengl.egl.EGLSpec 34 import androidx.graphics.surface.SurfaceControlCompat 35 import androidx.hardware.DefaultFlags 36 import androidx.hardware.DefaultNumBuffers 37 import androidx.hardware.HardwareBufferFormat 38 import androidx.hardware.HardwareBufferUsage 39 import androidx.hardware.SyncFenceCompat 40 import java.lang.IllegalArgumentException 41 import java.lang.IllegalStateException 42 import java.util.concurrent.CountDownLatch 43 44 /** 45 * Class responsible for supporting rendering to frame buffer objects that are backed by 46 * [HardwareBuffer] instances. This provides for more flexibility in OpenGL rendering as it supports 47 * configuration of the number of buffers within the underlying swap chain, pixel format of the 48 * buffers as well as fine grained control over synchronization of buffer content. 49 */ 50 @RequiresApi(Build.VERSION_CODES.Q) 51 class GLFrameBufferRenderer 52 internal constructor( 53 private val surfaceControlProvider: SurfaceControlProvider, 54 callback: Callback, 55 private val mFormat: Int, 56 private val mUsage: Long, 57 private val mMaxBuffers: Int, 58 private val mSyncStrategy: SyncStrategy, 59 glRenderer: GLRenderer? 60 ) { 61 62 /** Builder used to create a [GLFrameBufferRenderer] with various configurations */ 63 class Builder { 64 private var mBufferFormat = HardwareBuffer.RGBA_8888 65 private var mUsageFlags = DefaultFlags 66 private var mMaxBuffers = DefaultNumBuffers 67 private var mGLRenderer: GLRenderer? = null 68 private var mSyncStrategy: SyncStrategy = SyncStrategy.ALWAYS 69 private val mSurfaceControlProvider: SurfaceControlProvider 70 private val mCallback: Callback 71 72 /** 73 * Create a new [GLFrameBufferRenderer.Builder] with the provided [SurfaceView] to be the 74 * parent of the [SurfaceControlCompat] that is presented on screen. 75 * 76 * @param surfaceView SurfaceView to be the parent of the [SurfaceControlCompat] instance 77 * used for presenting rendered content on screen 78 * @param callback Callback used to render content within the corresponding buffers as well 79 * as optionally configuring [SurfaceControlCompat.Transaction] to present contents to the 80 * display 81 */ 82 constructor(surfaceView: SurfaceView, callback: Callback) { 83 mSurfaceControlProvider = SurfaceViewProvider(surfaceView) 84 mCallback = callback 85 } 86 87 /** 88 * Creates a new [GLFrameBufferRenderer.Builder] with the provided [SurfaceControlCompat] as 89 * the parent [SurfaceControlCompat] for presenting contents to the display. 90 * 91 * It is the responsibility of the caller to release the provided [SurfaceControlCompat] 92 * instance as the [GLFrameBufferRenderer] will consume but not release it. 93 * 94 * @param parentSurfaceControl Parent [SurfaceControlCompat] instance. 95 * @param width Logical width of the content to render. This dimension matches what is 96 * provided from [SurfaceHolder.Callback.surfaceChanged]. 97 * @param height Logical height of the content to render. This dimension matches what is 98 * provided from [SurfaceHolder.Callback.surfaceChanged]. 99 * @param transformHint Hint used to specify how to pre-rotate content to optimize 100 * consumption of content by the display without having to introduce an additional GPU 101 * pass to handle rotation. 102 */ 103 internal constructor( 104 parentSurfaceControl: SurfaceControlCompat, 105 width: Int, 106 height: Int, 107 transformHint: Int, 108 callback: Callback 109 ) { 110 mSurfaceControlProvider = 111 DefaultSurfaceControlProvider(parentSurfaceControl, width, height, transformHint) 112 mCallback = callback 113 } 114 115 /** 116 * Specify the [SyncStrategy] used for determining when to create [SyncFenceCompat] objects 117 * in order to handle synchronization. The [SyncFenceCompat] instance created according to 118 * the algorithm specified in the provided [SyncStrategy] will be passed to the 119 * corresponding [SurfaceControlCompat.Transaction.setBuffer] call in order to ensure the 120 * underlying buffer is not presented by the display until the fence signals. 121 * 122 * @param syncStrategy [SyncStrategy] used to determine when to create synchronization 123 * boundaries for buffer consumption. The default is [SyncStrategy.ALWAYS], indicating a 124 * fence should always be created after a request to render has been made. 125 * @return The builder instance 126 */ 127 fun setSyncStrategy(syncStrategy: SyncStrategy): Builder { 128 mSyncStrategy = syncStrategy 129 return this 130 } 131 132 /** 133 * Specify the buffer format of the underlying buffers being rendered into by the created 134 * [GLFrameBufferRenderer]. The set of valid formats is implementation-specific and may 135 * depend on additional EGL extensions. The particular valid combinations for a given 136 * Android version and implementation should be documented by that version. 137 * 138 * [HardwareBuffer.RGBA_8888] and [HardwareBuffer.RGBX_8888] are guaranteed to be supported. 139 * However, consumers are recommended to query the desired HardwareBuffer configuration 140 * using [HardwareBuffer.isSupported]. 141 * 142 * See: khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt 143 * 144 * @param format Pixel format of the buffers to be rendered into. The default is RGBA_8888. 145 * @return The builder instance 146 */ 147 fun setBufferFormat(@HardwareBufferFormat format: Int): Builder { 148 mBufferFormat = format 149 return this 150 } 151 152 /** 153 * Specify the maximum number of buffers used within the swap chain of the 154 * [GLFrameBufferRenderer]. If 1 is specified, then the created [GLFrameBufferRenderer] is 155 * running in "single buffer mode". In this case consumption of the buffer content would 156 * need to be coordinated with the [SyncFenceCompat] instance specified by the corresponding 157 * [SyncStrategy] algorithm 158 * 159 * @param numBuffers The number of buffers within the swap chain to be consumed by the 160 * created [GLFrameBufferRenderer]. This must be greater than zero. The default number of 161 * buffers used is 3. 162 * @return The builder instance 163 * @see [setSyncStrategy]. 164 */ 165 fun setMaxBuffers(@IntRange(from = 1, to = 64) numBuffers: Int): Builder { 166 require(numBuffers > 0) { "Must have at least 1 buffer" } 167 mMaxBuffers = numBuffers 168 return this 169 } 170 171 /** 172 * Specify the usage flags to be configured on the underlying [HardwareBuffer] instances 173 * created by the [GLFrameBufferRenderer]. 174 * 175 * @param usageFlags Usage flags to be configured on the created [HardwareBuffer] instances 176 * that the [GLFrameBufferRenderer] will render into. Must be one of 177 * [HardwareBufferUsage]. Note that the provided flags here are combined with the 178 * following mandatory default flags, [HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE], 179 * [HardwareBuffer.USAGE_GPU_COLOR_OUTPUT] and [HardwareBuffer.USAGE_COMPOSER_OVERLAY] 180 * @return The builder instance 181 */ 182 fun setUsageFlags(@HardwareBufferUsage usageFlags: Long): Builder { 183 mUsageFlags = usageFlags or DefaultFlags 184 return this 185 } 186 187 /** 188 * Configure the [GLRenderer] instance to be used by the [GLFrameBufferRenderer]. By default 189 * this parameter is null indicating that the [GLFrameBufferRenderer] will create and manage 190 * its own [GLRenderer]. This is useful to share the same OpenGL resources and thread across 191 * multiple [GLFrameBufferRenderer] instances. 192 * 193 * @param glRenderer The [GLRenderer] used for leveraging OpenGL resources including the GL 194 * thread 195 * @return The builder instance 196 */ 197 fun setGLRenderer(glRenderer: GLRenderer?): Builder { 198 mGLRenderer = glRenderer 199 return this 200 } 201 202 /** 203 * Create the [GLFrameBufferRenderer] with the specified parameters on this [Builder] 204 * instance 205 * 206 * @return The newly created [GLFrameBufferRenderer] 207 */ 208 fun build(): GLFrameBufferRenderer { 209 return GLFrameBufferRenderer( 210 mSurfaceControlProvider, 211 mCallback, 212 mBufferFormat, 213 mUsageFlags, 214 mMaxBuffers, 215 mSyncStrategy, 216 mGLRenderer 217 ) 218 } 219 } 220 221 private var mSurfaceControl: SurfaceControlCompat? = null 222 private var mBufferPool: FrameBufferPool? = null 223 private var mRenderTarget: GLRenderer.RenderTarget? = null 224 225 private val mIsManagingGLRenderer: Boolean 226 private var mIsReleased = false 227 private val mContextCallbacks = 228 object : GLRenderer.EGLContextCallback { 229 override fun onEGLContextCreated(eglManager: EGLManager) { 230 // NO-OP 231 } 232 233 override fun onEGLContextDestroyed(eglManager: EGLManager) { 234 mBufferPool?.close() 235 } 236 } 237 238 private val mGLRenderer: GLRenderer 239 240 init { 241 if (mMaxBuffers < 1) { 242 throw IllegalArgumentException("FrameBufferRenderer must have at least 1 buffer") 243 } 244 val renderer = 245 if (glRenderer == null) { 246 mIsManagingGLRenderer = true 247 GLRenderer().apply { start() } 248 } else { 249 mIsManagingGLRenderer = false 250 if (!glRenderer.isRunning()) { 251 throw IllegalStateException( 252 "The provided GLRenderer must be running prior to " + 253 "creation of GLFrameBufferRenderer, " + 254 "did you forget to call GLRenderer#start()?" 255 ) 256 } 257 glRenderer 258 } 259 renderer.registerEGLContextCallback(mContextCallbacks) 260 mGLRenderer = renderer 261 surfaceControlProvider.createSurfaceControl( 262 object : SurfaceControlProvider.Callback { 263 override fun onSurfaceControlDestroyed() { 264 detachTargets(true) 265 } 266 267 override fun onSurfaceControlCreated( 268 surfaceControl: SurfaceControlCompat, 269 width: Int, 270 height: Int, 271 bufferTransformer: BufferTransformer, 272 inverseTransform: Int 273 ) { 274 val frameBufferPool = 275 FrameBufferPool( 276 bufferTransformer.bufferWidth, 277 bufferTransformer.bufferHeight, 278 this@GLFrameBufferRenderer.mFormat, 279 mUsage, 280 mMaxBuffers 281 ) 282 val renderCallback = 283 createFrameBufferRenderer( 284 surfaceControl, 285 inverseTransform, 286 bufferTransformer, 287 frameBufferPool, 288 callback 289 ) 290 mBufferPool = frameBufferPool 291 mSurfaceControl = surfaceControl 292 mRenderTarget = renderer.createRenderTarget(width, height, renderCallback) 293 } 294 295 override fun requestRender(renderComplete: Runnable?) { 296 drawAsync(renderComplete) 297 } 298 } 299 ) 300 } 301 302 /** 303 * Queue a [Runnable] to be executed on the GL rendering thread. Note it is important this 304 * [Runnable] does not block otherwise it can stall the GL thread. 305 * 306 * @param runnable to be executed 307 */ 308 fun execute(runnable: Runnable) { 309 if (isValid()) { 310 mGLRenderer.execute(runnable) 311 } else { 312 Log.w( 313 TAG, 314 "Attempt to execute runnable after " + "GLFrameBufferRenderer has been released" 315 ) 316 } 317 } 318 319 /** 320 * Returns the [HardwareBufferFormat] of the buffers that are being rendered into by this 321 * [GLFrameBufferRenderer] 322 */ 323 @HardwareBufferFormat 324 val bufferFormat: Int 325 get() = mFormat 326 327 /** 328 * Returns the current usage flag hints of the buffers that are being rendered into by this 329 * [GLFrameBufferRenderer] 330 */ 331 @HardwareBufferUsage 332 val usageFlags: Long 333 get() = mUsage 334 335 /** 336 * Returns the [GLRenderer] used for issuing requests to render into the underlying buffers with 337 * OpenGL. 338 */ 339 val glRenderer: GLRenderer 340 @JvmName("getGLRenderer") get() = mGLRenderer 341 342 /** 343 * Returns the [SyncStrategy] used for determining when to create [SyncFenceCompat] objects in 344 * order to handle synchronization. The [SyncFenceCompat] instance created according to the 345 * algorithm specified in the provided [SyncStrategy] will be passed to the corresponding 346 * [SurfaceControlCompat.Transaction.setBuffer] call in order to ensure the underlying buffer is 347 * not presented by the display until the fence signals. 348 */ 349 val syncStrategy: SyncStrategy 350 get() = mSyncStrategy 351 352 /** 353 * Returns the number of buffers within the swap chain used for rendering with this 354 * [GLFrameBufferRenderer] 355 */ 356 val maxBuffers: Int 357 get() = mMaxBuffers 358 359 private var mCurrentFrameBuffer: FrameBuffer? = null 360 361 internal fun createFrameBufferRenderer( 362 surfaceControl: SurfaceControlCompat, 363 inverseTransform: Int, 364 bufferTransformer: BufferTransformer, 365 frameBufferPool: FrameBufferPool, 366 callback: Callback 367 ): FrameBufferRenderer = 368 FrameBufferRenderer( 369 object : FrameBufferRenderer.RenderCallback { 370 371 private val width = bufferTransformer.logicalWidth 372 private val height = bufferTransformer.logicalHeight 373 374 private val bufferInfo = 375 BufferInfo().apply { 376 this.width = bufferTransformer.bufferWidth 377 this.height = bufferTransformer.bufferHeight 378 } 379 380 override fun obtainFrameBuffer(egl: EGLSpec): FrameBuffer { 381 val currentFrameBuffer = mCurrentFrameBuffer 382 // Single buffer mode if we already allocated 1 buffer just return the previous 383 // one 384 return if (mMaxBuffers == 1 && currentFrameBuffer != null) { 385 currentFrameBuffer 386 } else { 387 frameBufferPool.obtain(egl).also { 388 bufferInfo.frameBufferId = it.frameBuffer 389 mCurrentFrameBuffer = it 390 } 391 } 392 } 393 394 override fun onDraw(eglManager: EGLManager) { 395 val buffer = mCurrentFrameBuffer 396 if (buffer != null && !buffer.isClosed) { 397 callback.onDrawFrame( 398 eglManager, 399 width, 400 height, 401 bufferInfo, 402 bufferTransformer.transform 403 ) 404 } 405 } 406 407 override fun onDrawComplete( 408 frameBuffer: FrameBuffer, 409 syncFenceCompat: SyncFenceCompat? 410 ) { 411 if (surfaceControl.isValid() && !frameBuffer.isClosed) { 412 val transaction = 413 SurfaceControlCompat.Transaction() 414 .setVisibility(surfaceControl, true) 415 .setBuffer( 416 surfaceControl, 417 frameBuffer.hardwareBuffer, 418 syncFenceCompat 419 ) { releaseFence -> 420 if (mGLRenderer.isRunning()) { 421 mGLRenderer.execute { 422 callback.onBufferReleased(frameBuffer, releaseFence) 423 } 424 } 425 if (mMaxBuffers > 1 || frameBufferPool.isClosed) { 426 // Release the previous buffer only if we are not in single 427 // buffered 428 // mode 429 frameBufferPool.release(frameBuffer, releaseFence) 430 } 431 } 432 if (inverseTransform != BufferTransformHintResolver.UNKNOWN_TRANSFORM) { 433 transaction.setBufferTransform(surfaceControl, inverseTransform) 434 } 435 callback.onDrawComplete( 436 surfaceControl, 437 transaction, 438 frameBuffer, 439 syncFenceCompat 440 ) 441 transaction.commit() 442 } 443 } 444 }, 445 mSyncStrategy 446 ) 447 448 internal fun drawAsync(onComplete: Runnable? = null) { 449 val renderTarget = mRenderTarget 450 val renderer = mGLRenderer 451 if (renderTarget != null && renderer.isRunning()) { 452 // Register a callback in case the GLRenderer is torn down while we are waiting 453 // for rendering to complete. In this case invoke the drawFinished callback 454 // either if the render is complete or if the GLRenderer is torn down, whatever 455 // comes first 456 val eglContextCallback = 457 object : GLRenderer.EGLContextCallback { 458 override fun onEGLContextCreated(eglManager: EGLManager) { 459 // NO-OP 460 } 461 462 override fun onEGLContextDestroyed(eglManager: EGLManager) { 463 onComplete?.run() 464 renderer.unregisterEGLContextCallback(this) 465 } 466 } 467 renderer.registerEGLContextCallback(eglContextCallback) 468 mRenderTarget?.requestRender { 469 onComplete?.run() 470 renderer.unregisterEGLContextCallback(eglContextCallback) 471 } 472 } else { 473 onComplete?.run() 474 } 475 } 476 477 /** 478 * Release resources associated with the [GLFrameBufferRenderer]. After this method is invoked, 479 * the [GLFrameBufferRenderer] is in an invalid state and can no longer handle rendering 480 * content. 481 * 482 * @param cancelPending If true, pending render requests are cancelled immediately. If false, 483 * pending render requests are completed before releasing the renderer. 484 * @param onReleaseCallback Optional callback to be invoked on the underlying OpenGL thread when 485 * releasing resources has been completed 486 */ 487 @JvmOverloads 488 fun release(cancelPending: Boolean, onReleaseCallback: (() -> Unit)? = null) { 489 if (!mIsReleased) { 490 detachTargets(cancelPending, onReleaseCallback) 491 surfaceControlProvider.release() 492 493 mGLRenderer.unregisterEGLContextCallback(mContextCallbacks) 494 if (mIsManagingGLRenderer) { 495 mGLRenderer.stop(false) 496 } 497 498 mIsReleased = true 499 } else { 500 Log.w(TAG, "Attempt to release already released GLFrameBufferRenderer") 501 } 502 } 503 504 internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) { 505 val frameBufferPool = mBufferPool 506 val renderTarget = mRenderTarget 507 val surfaceControl = mSurfaceControl 508 renderTarget?.detach(cancelPending) 509 510 mGLRenderer.execute { 511 mCurrentFrameBuffer?.let { buffer -> frameBufferPool?.release(buffer) } 512 surfaceControl?.let { sc -> 513 SurfaceControlCompat.Transaction().reparent(sc, null).apply { 514 commit() 515 close() 516 } 517 sc.release() 518 } 519 frameBufferPool?.close() 520 onReleaseComplete?.invoke() 521 } 522 mBufferPool = null 523 mSurfaceControl = null 524 mRenderTarget = null 525 } 526 527 /** 528 * Determines whether or not the [GLFrameBufferRenderer] is in a valid state. That is the 529 * [release] method has not been called. If this returns false, then subsequent calls to 530 * [render], and [release] are ignored 531 * 532 * @return `true` if this [GLFrameBufferRenderer] has been released, `false` otherwise 533 */ 534 fun isValid(): Boolean = !mIsReleased 535 536 /** 537 * Render content to a buffer and present the result to the display. 538 * 539 * If this [GLFrameBufferRenderer] has been released, that is [isValid] returns `false`, this 540 * call is ignored. 541 */ 542 fun render() { 543 if (!mIsReleased) { 544 mRenderTarget?.requestRender() 545 } else { 546 Log.w(TAG, "renderer is released, ignoring request") 547 } 548 } 549 550 /** 551 * [GLFrameBufferRenderer] callbacks that are invoked to render OpenGL content within the 552 * underlying buffers. This includes an optional callback to be used to configure the underlying 553 * [SurfaceControlCompat.Transaction] used to present content to the display 554 */ 555 interface Callback { 556 557 /** 558 * Callback invoked on the thread backed by the [GLRenderer] to render content into a buffer 559 * with the specified parameters. 560 * 561 * @param eglManager [EGLManager] useful in configuring EGL objects to be used when issuing 562 * OpenGL commands to render into the front buffered layer 563 * @param width Logical width of the content to render. This dimension matches what is 564 * provided from [SurfaceHolder.Callback.surfaceChanged] 565 * @param height Logical height of the content to render. This dimension matches what is 566 * provided from [SurfaceHolder.Callback.surfaceChanged] 567 * @param bufferInfo [BufferInfo] about the buffer that is being rendered into. This 568 * includes the width and height of the buffer which can be different than the 569 * corresponding dimensions of the [SurfaceView] provided to the [GLFrameBufferRenderer] 570 * as pre-rotation can occasionally swap width and height parameters in order to avoid GPU 571 * composition to rotate content. This should be used as input to [GLES20.glViewport]. 572 * Additionally this also contains a frame buffer identifier that can be used to retarget 573 * rendering operations to the original destination after rendering into intermediate 574 * scratch buffers. 575 * @param transform Matrix that should be applied to the rendering in this callback. This 576 * should be consumed as input to any vertex shader implementations. Buffers are 577 * pre-rotated in advance in order to avoid unnecessary overhead of GPU composition to 578 * rotate content in the same install orientation of the display. This is a 4 x 4 matrix 579 * is represented as a flattened array of 16 floating point values. Consumers are expected 580 * to leverage [Matrix.multiplyMM] with this parameter alongside any additional 581 * transformations that are to be applied. For example: 582 * ``` 583 * val myMatrix = FloatArray(16) 584 * Matrix.orthoM( 585 * myMatrix, // matrix 586 * 0, // offset starting index into myMatrix 587 * 0f, // left 588 * bufferInfo.width.toFloat(), // right 589 * 0f, // bottom 590 * bufferInfo.height.toFloat(), // top 591 * -1f, // near 592 * 1f, // far 593 * ) 594 * val result = FloatArray(16) 595 * Matrix.multiplyMM(result, 0, myMatrix, 0, transform, 0) 596 * ``` 597 * 598 * @sample androidx.graphics.core.samples.glFrameBufferSample 599 */ 600 @WorkerThread 601 fun onDrawFrame( 602 eglManager: EGLManager, 603 width: Int, 604 height: Int, 605 bufferInfo: BufferInfo, 606 transform: FloatArray 607 ) 608 609 /** 610 * Optional callback invoked the thread backed by the [GLRenderer] when rendering to a 611 * buffer is complete but before the buffer is submitted to the hardware compositor. This 612 * provides consumers a mechanism for synchronizing the transaction with other 613 * [SurfaceControlCompat] objects that maybe rendered within the scene. 614 * 615 * @param targetSurfaceControl Handle to the [SurfaceControlCompat] where the buffer is 616 * presented. This can be used to configure various properties of the 617 * [SurfaceControlCompat] like z-ordering or visibility with the corresponding 618 * [SurfaceControlCompat.Transaction]. 619 * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered 620 * content to the front buffered layer. 621 * @param frameBuffer The buffer that has been rendered into and is ready to be displayed. 622 * The [HardwareBuffer] backing this [FrameBuffer] is already configured to be presented 623 * for the targetSurfaceControl. That is [SurfaceControlCompat.Transaction.setBuffer] is 624 * already invoked with the given [HardwareBuffer] and optional [SyncFenceCompat] instance 625 * before this method is invoked. 626 * @param syncFence Optional [SyncFenceCompat] is used to determine when rendering is done 627 * and reflected within the given frameBuffer. 628 */ 629 @WorkerThread 630 fun onDrawComplete( 631 targetSurfaceControl: SurfaceControlCompat, 632 transaction: SurfaceControlCompat.Transaction, 633 frameBuffer: FrameBuffer, 634 syncFence: SyncFenceCompat? 635 ) { 636 // NO-OP 637 } 638 639 /** 640 * Optional callback invoked the thread backed by the [GLRenderer] when the provided 641 * framebuffer is released. That is the given [FrameBuffer] instance is no longer being 642 * presented and is not visible. 643 * 644 * @param frameBuffer The buffer that is no longer being presented and has returned to the 645 * buffer allocation pool 646 * @param releaseFence Optional fence that must be waited upon before the [FrameBuffer] can 647 * be reused. The framework will invoke this callback early to improve performance and 648 * signal the fence when it is ready to be re-used. 649 */ 650 @WorkerThread 651 fun onBufferReleased(frameBuffer: FrameBuffer, releaseFence: SyncFenceCompat?) { 652 // NO-OP 653 } 654 } 655 656 /** 657 * Provider interface used to delegate creation and potential lifecycle callbacks associated 658 * with the corresponding [SurfaceControlCompat] instance 659 */ 660 internal interface SurfaceControlProvider { 661 662 /** 663 * Request a [SurfaceControlCompat] to be created and invokes the corresponding callback 664 * when the created [SurfaceControlCompat] instance is ready for consumption 665 */ 666 fun createSurfaceControl(callback: Callback) 667 668 /** Release resources associated with the [SurfaceControlProvider] */ 669 fun release() 670 671 /** 672 * Callbacks invoked by the [SurfaceControlProvider] to consumers of the created 673 * [SurfaceControlCompat] instance 674 */ 675 interface Callback { 676 677 /** 678 * Callback invoked when resources associated with the created [SurfaceControlCompat] 679 * instance should be destroyed 680 */ 681 fun onSurfaceControlDestroyed() 682 683 /** 684 * Callback invoked when the [SurfaceControlCompat] is created. This includes the 685 * logical width/height as well as a [BufferTransformer] instance that provides the 686 * metadata necessary to pre-rotate content 687 */ 688 fun onSurfaceControlCreated( 689 surfaceControl: SurfaceControlCompat, 690 width: Int, 691 height: Int, 692 bufferTransformer: BufferTransformer, 693 inverseTransform: Int 694 ) 695 696 /** 697 * Requests the consumer of the [SurfaceControlCompat] instance to render content to be 698 * presented by the [SurfaceControlCompat] instance and invoke a callback when rendering 699 * is complete. 700 */ 701 fun requestRender(renderComplete: Runnable? = null) 702 } 703 } 704 705 /** 706 * Default [SurfaceControlProvider] instance that returns the dependencies given to it to the 707 * consumer of the [SurfaceControlCompat] 708 */ 709 internal class DefaultSurfaceControlProvider( 710 private val surfaceControl: SurfaceControlCompat, 711 private val width: Int, 712 private val height: Int, 713 private val transformHint: Int, 714 ) : SurfaceControlProvider { 715 716 private val bufferTransformer = BufferTransformer() 717 718 private var mSurfaceControlCallback: SurfaceControlProvider.Callback? = null 719 720 override fun createSurfaceControl(callback: SurfaceControlProvider.Callback) { 721 val inverse = bufferTransformer.invertBufferTransform(transformHint) 722 bufferTransformer.computeTransform(width, height, inverse) 723 callback.onSurfaceControlCreated( 724 surfaceControl, 725 width, 726 height, 727 bufferTransformer, 728 inverse 729 ) 730 mSurfaceControlCallback = callback 731 } 732 733 override fun release() { 734 // NO-OP 735 } 736 } 737 738 /** 739 * [SurfaceControlProvider] instance that creates a [SurfaceControlCompat] instance with the 740 * provided SurfaceView as the parent of the created [SurfaceControlCompat]. This implementation 741 * handles all lifecycle callbacks associated with the underlying SurfaceHolder.Callback 742 * attached to the holder on the given SurfaceView. 743 */ 744 internal class SurfaceViewProvider(private var surfaceView: SurfaceView?) : 745 SurfaceControlProvider { 746 private val mTransformResolver = BufferTransformHintResolver() 747 748 private var mSurfaceControl: SurfaceControlCompat? = null 749 private var mSurfaceHolderCallback: SurfaceHolder.Callback2? = null 750 private var mSurfaceControlCallback: SurfaceControlProvider.Callback? = null 751 752 internal fun createSurfaceControl( 753 surfaceView: SurfaceView, 754 callback: SurfaceControlProvider.Callback 755 ) { 756 // Destroy previously created SurfaceControl as we are creating a new instance 757 callback.onSurfaceControlDestroyed() 758 759 val width = surfaceView.width 760 val height = surfaceView.height 761 val transformHint = mTransformResolver.getBufferTransformHint(surfaceView) 762 val bufferTransformer = BufferTransformer() 763 val inverse = bufferTransformer.invertBufferTransform(transformHint) 764 bufferTransformer.computeTransform(surfaceView.width, surfaceView.height, inverse) 765 val surfaceControl = 766 SurfaceControlCompat.Builder() 767 .setName("GLFrameBufferRendererTarget") 768 .setParent(surfaceView) 769 .build() 770 771 callback.onSurfaceControlCreated( 772 surfaceControl, 773 width, 774 height, 775 bufferTransformer, 776 inverse 777 ) 778 779 mSurfaceControl = surfaceControl 780 mSurfaceControlCallback = callback 781 } 782 783 override fun createSurfaceControl(callback: SurfaceControlProvider.Callback) { 784 surfaceView?.let { target -> 785 val surfaceHolderCallback = 786 object : SurfaceHolder.Callback2 { 787 override fun surfaceCreated(holder: SurfaceHolder) { 788 // NO-OP wait for surfaceChanged callback 789 } 790 791 override fun surfaceChanged( 792 holder: SurfaceHolder, 793 surfaceFormat: Int, 794 width: Int, 795 height: Int 796 ) { 797 if (width > 0 && height > 0) { 798 createSurfaceControl(target, callback) 799 } else { 800 Log.w( 801 TAG, 802 "Invalid dimensions provided, width and height must be > 0. " + 803 "width: $width height: $height" 804 ) 805 } 806 } 807 808 override fun surfaceDestroyed(p0: SurfaceHolder) { 809 callback.onSurfaceControlDestroyed() 810 } 811 812 override fun surfaceRedrawNeeded(p0: SurfaceHolder) { 813 val latch = CountDownLatch(1) 814 callback.requestRender { latch.countDown() } 815 latch.await() 816 } 817 818 override fun surfaceRedrawNeededAsync( 819 holder: SurfaceHolder, 820 drawingFinished: Runnable 821 ) { 822 callback.requestRender(drawingFinished) 823 } 824 } 825 val holder = target.holder 826 holder.addCallback(surfaceHolderCallback) 827 if (holder.surface != null && holder.surface.isValid) { 828 if (target.width > 0 && target.height > 0) { 829 createSurfaceControl(target, callback) 830 } 831 } 832 mSurfaceHolderCallback = surfaceHolderCallback 833 } 834 } 835 836 override fun release() { 837 surfaceView?.holder?.removeCallback(mSurfaceHolderCallback) 838 surfaceView = null 839 } 840 } 841 842 internal companion object { 843 internal val TAG = "GLFrameBufferRenderer" 844 } 845 } 846