Merge "In view test app, use a GL thread for effect instead of the main thread." into androidx-main
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
index 50291a7..d250fc8 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
@@ -17,16 +17,19 @@
package androidx.camera.integration.view
import androidx.camera.core.CameraEffect
-import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
/**
* A tone mapping effect for Preview/VideoCapture UseCase.
*/
-internal class ToneMappingSurfaceEffect : CameraEffect(
- PREVIEW or VIDEO_CAPTURE, mainThreadExecutor(), ToneMappingSurfaceProcessor(), {}
-) {
+internal class ToneMappingSurfaceEffect(
+ targets: Int = PREVIEW or VIDEO_CAPTURE,
+ private val processor: ToneMappingSurfaceProcessor = ToneMappingSurfaceProcessor()
+) :
+ CameraEffect(
+ targets, processor.getGlExecutor(), processor, {}
+ ) {
fun release() {
- (surfaceProcessor as? ToneMappingSurfaceProcessor)?.release()
+ processor.release()
}
}
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
index 9dc5199..33ac70c 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
@@ -19,16 +19,17 @@
import android.graphics.SurfaceTexture
import android.graphics.SurfaceTexture.OnFrameAvailableListener
import android.os.Handler
-import android.os.Looper
+import android.os.HandlerThread
import android.view.Surface
import androidx.annotation.VisibleForTesting
import androidx.camera.core.SurfaceOutput
import androidx.camera.core.SurfaceProcessor
import androidx.camera.core.SurfaceRequest
-import androidx.camera.core.impl.utils.Threads.checkMainThread
-import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
+import androidx.camera.core.impl.utils.executor.CameraXExecutors.newHandlerExecutor
import androidx.camera.core.processing.OpenGlRenderer
import androidx.camera.core.processing.ShaderProvider
+import androidx.core.util.Preconditions.checkState
+import java.util.concurrent.Executor
/**
* A processor that applies tone mapping on camera output.
@@ -57,28 +58,36 @@
"""
}
}
+
+ private const val GL_THREAD_NAME = "ToneMappingSurfaceProcessor"
}
- private val mainThreadHandler: Handler = Handler(Looper.getMainLooper())
+ private val glThread: HandlerThread = HandlerThread(GL_THREAD_NAME)
+ private var glHandler: Handler
+ private var glExecutor: Executor
+
+ // Members below are only accessed on GL thread.
private val glRenderer: OpenGlRenderer = OpenGlRenderer()
private val outputSurfaces: MutableMap<SurfaceOutput, Surface> = mutableMapOf()
private val textureTransform: FloatArray = FloatArray(16)
private val surfaceTransform: FloatArray = FloatArray(16)
private var isReleased = false
- // For testing.
+ // For testing only
private var surfaceRequested = false
- // For testing.
private var outputSurfaceProvided = false
init {
- mainThreadExecutor().execute {
+ glThread.start()
+ glHandler = Handler(glThread.looper)
+ glExecutor = newHandlerExecutor(glHandler)
+ glExecutor.execute {
glRenderer.init(TONE_MAPPING_SHADER_PROVIDER)
}
}
override fun onInputSurface(surfaceRequest: SurfaceRequest) {
- checkMainThread()
+ checkGlThread()
if (isReleased) {
surfaceRequest.willNotProvideSurface()
return
@@ -89,22 +98,22 @@
surfaceRequest.resolution.width, surfaceRequest.resolution.height
)
val surface = Surface(surfaceTexture)
- surfaceRequest.provideSurface(surface, mainThreadExecutor()) {
+ surfaceRequest.provideSurface(surface, glExecutor) {
surfaceTexture.setOnFrameAvailableListener(null)
surfaceTexture.release()
surface.release()
}
- surfaceTexture.setOnFrameAvailableListener(this, mainThreadHandler)
+ surfaceTexture.setOnFrameAvailableListener(this, glHandler)
}
override fun onOutputSurface(surfaceOutput: SurfaceOutput) {
- checkMainThread()
+ checkGlThread()
outputSurfaceProvided = true
if (isReleased) {
surfaceOutput.close()
return
}
- val surface = surfaceOutput.getSurface(mainThreadExecutor()) {
+ val surface = surfaceOutput.getSurface(glExecutor) {
surfaceOutput.close()
outputSurfaces.remove(surfaceOutput)?.let { removedSurface ->
glRenderer.unregisterOutputSurface(removedSurface)
@@ -120,22 +129,35 @@
}
fun release() {
- checkMainThread()
- if (isReleased) {
- return
+ glExecutor.execute {
+ releaseInternal()
}
+ }
- // Once release is called, we can stop sending frame to output surfaces.
- for (surfaceOutput in outputSurfaces.keys) {
- surfaceOutput.close()
+ private fun releaseInternal() {
+ checkGlThread()
+ if (!isReleased) {
+ // Once release is called, we can stop sending frame to output surfaces.
+ for (surfaceOutput in outputSurfaces.keys) {
+ surfaceOutput.close()
+ }
+ outputSurfaces.clear()
+ glRenderer.release()
+ glThread.quitSafely()
+ isReleased = true
}
- outputSurfaces.clear()
- glRenderer.release()
- isReleased = true
+ }
+
+ private fun checkGlThread() {
+ checkState(GL_THREAD_NAME == Thread.currentThread().name)
+ }
+
+ fun getGlExecutor(): Executor {
+ return glExecutor
}
override fun onFrameAvailable(surfaceTexture: SurfaceTexture) {
- checkMainThread()
+ checkGlThread()
if (isReleased) {
return
}