[external/jetpack-camera-app] Uncomment graphics-core codes am: 378c89f8ce am: f3e10022f8
Original change: https://android-review.googlesource.com/c/platform/external/jetpack-camera-app/+/3162979
Change-Id: I480f213955fd5036f81946e64d230212d6944779
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/domain/camera/Android.bp b/domain/camera/Android.bp
index 841f3c6..0872d4f 100644
--- a/domain/camera/Android.bp
+++ b/domain/camera/Android.bp
@@ -15,7 +15,7 @@
"androidx.camera_camera-video",
"androidx.camera_camera-camera2",
"androidx.camera_camera-lifecycle",
- //"androidx.graphics_graphics-core",
+ "androidx.graphics_graphics-core",
"jetpack-camera-app_data_settings",
"jetpack-camera-app_core_common",
],
diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt
index 97dd767..a20c4a0 100644
--- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt
+++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt
@@ -661,7 +661,7 @@
addUseCase(videoCaptureUseCase!!)
}
-// effect?.let { addEffect(it) }
+ effect?.let { addEffect(it) }
captureMode = sessionSettings.captureMode
}.build()
diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/CopyingSurfaceProcessor.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/CopyingSurfaceProcessor.kt
index e5ea5ae..6f626c1 100644
--- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/CopyingSurfaceProcessor.kt
+++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/CopyingSurfaceProcessor.kt
@@ -25,9 +25,9 @@
import androidx.camera.core.SurfaceOutput
import androidx.camera.core.SurfaceProcessor
import androidx.camera.core.SurfaceRequest
-//import androidx.graphics.opengl.GLRenderer
-//import androidx.graphics.opengl.egl.EGLManager
-//import androidx.graphics.opengl.egl.EGLSpec
+import androidx.graphics.opengl.GLRenderer
+import androidx.graphics.opengl.egl.EGLManager
+import androidx.graphics.opengl.egl.EGLSpec
import com.google.jetpackcamera.core.common.RefCounted
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.CompletableDeferred
@@ -57,305 +57,305 @@
*/
class CopyingSurfaceProcessor(coroutineScope: CoroutineScope) : SurfaceProcessor {
-// private val inputSurfaceFlow = MutableStateFlow<SurfaceRequestScope?>(null)
-// private val outputSurfaceFlow = MutableStateFlow<SurfaceOutputScope?>(null)
+ private val inputSurfaceFlow = MutableStateFlow<SurfaceRequestScope?>(null)
+ private val outputSurfaceFlow = MutableStateFlow<SurfaceOutputScope?>(null)
init {
-// coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
-// inputSurfaceFlow
-// .filterNotNull()
-// .collectLatest { surfaceRequestScope ->
-// surfaceRequestScope.withSurfaceRequest { surfaceRequest ->
-//
-// val renderCallbacks = ShaderCopy(surfaceRequest.dynamicRange)
-// renderCallbacks.renderWithSurfaceRequest(surfaceRequest)
-// }
-// }
-// }
+ coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ inputSurfaceFlow
+ .filterNotNull()
+ .collectLatest { surfaceRequestScope ->
+ surfaceRequestScope.withSurfaceRequest { surfaceRequest ->
+
+ val renderCallbacks = ShaderCopy(surfaceRequest.dynamicRange)
+ renderCallbacks.renderWithSurfaceRequest(surfaceRequest)
+ }
+ }
+ }
}
-// private suspend fun RenderCallbacks.renderWithSurfaceRequest(surfaceRequest: SurfaceRequest) =
-// coroutineScope inputScope@{
-// var currentTimestamp = TIMESTAMP_UNINITIALIZED
-// val surfaceTextureRef = RefCounted<SurfaceTexture> {
-// it.release()
-// }
-// val textureTransform = FloatArray(16)
-//
-// val frameUpdateFlow = MutableStateFlow(0)
-//
-// val initializeCallback = object : GLRenderer.EGLContextCallback {
-//
-// override fun onEGLContextCreated(eglManager: EGLManager) {
-// initRenderer()
-//
-// val surfaceTex = createSurfaceTexture(
-// surfaceRequest.resolution.width,
-// surfaceRequest.resolution.height
-// )
-//
-// // Initialize the reference counted surface texture
-// surfaceTextureRef.initialize(surfaceTex)
-//
-// surfaceTex.setOnFrameAvailableListener {
-// // Increment frame counter
-// frameUpdateFlow.update { it + 1 }
-// }
-//
-// val inputSurface = Surface(surfaceTex)
-// surfaceRequest.provideSurface(inputSurface, Runnable::run) { result ->
-// inputSurface.release()
-// surfaceTextureRef.release()
-// [email protected](
-// "Input surface no longer receiving frames: $result"
-// )
-// }
-// }
-//
-// override fun onEGLContextDestroyed(eglManager: EGLManager) {
-// // no-op
-// }
-// }
-//
-// val glRenderer = GLRenderer(
-// eglSpecFactory = provideEGLSpec,
-// eglConfigFactory = initConfig
-// )
-// glRenderer.registerEGLContextCallback(initializeCallback)
-// glRenderer.start(glThreadName)
-//
-// val inputRenderTarget = glRenderer.createRenderTarget(
-// surfaceRequest.resolution.width,
-// surfaceRequest.resolution.height,
-// object : GLRenderer.RenderCallback {
-//
-// override fun onDrawFrame(eglManager: EGLManager) {
-// surfaceTextureRef.acquire()?.also {
-// try {
-// currentTimestamp =
-// if (currentTimestamp == TIMESTAMP_UNINITIALIZED) {
-// // Don't perform any updates on first draw,
-// // we're only setting up the context.
-// 0
-// } else {
-// it.updateTexImage()
-// it.getTransformMatrix(textureTransform)
-// it.timestamp
-// }
-// } finally {
-// surfaceTextureRef.release()
-// }
-// }
-// }
-// }
-// )
-//
-// // Create the context and initialize the input. This will call RenderTarget.onDrawFrame,
-// // but we won't actually update the frame since this triggers adding the frame callback.
-// // All subsequent updates will then happen through frameUpdateFlow.
-// // This should be updated when https://issuetracker.google.com/331968279 is resolved.
-// inputRenderTarget.requestRender()
-//
-// // Connect the onConnectToInput callback with the onDisconnectFromInput
-// // Should only be called on worker thread
-// var connectedToInput = false
-//
-// // Should only be called on worker thread
-// val onConnectToInput: () -> Boolean = {
-// connectedToInput = surfaceTextureRef.acquire() != null
-// connectedToInput
-// }
-//
-// // Should only be called on worker thread
-// val onDisconnectFromInput: () -> Unit = {
-// if (connectedToInput) {
-// surfaceTextureRef.release()
-// connectedToInput = false
-// }
-// }
-//
-// // Wait for output surfaces
-// outputSurfaceFlow
-// .onCompletion {
-// glRenderer.stop(cancelPending = false)
-// glRenderer.unregisterEGLContextCallback(initializeCallback)
-// }.filterNotNull()
-// .collectLatest { surfaceOutputScope ->
-// surfaceOutputScope.withSurfaceOutput { refCountedSurface,
-// size,
-// updateTransformMatrix ->
-// // If we can't acquire the surface, then the surface output is already
-// // closed, so we'll return and wait for the next output surface.
-// val outputSurface =
-// refCountedSurface.acquire() ?: return@withSurfaceOutput
-//
-// val surfaceTransform = FloatArray(16)
-// val outputRenderTarget = glRenderer.attach(
-// outputSurface,
-// size.width,
-// size.height,
-// object : GLRenderer.RenderCallback {
-//
-// override fun onSurfaceCreated(
-// spec: EGLSpec,
-// config: EGLConfig,
-// surface: Surface,
-// width: Int,
-// height: Int
-// ): EGLSurface? {
-// return if (onConnectToInput()) {
-// createOutputSurface(spec, config, surface, width, height)
-// } else {
-// null
-// }
-// }
-//
-// override fun onDrawFrame(eglManager: EGLManager) {
-// val currentDrawSurface = eglManager.currentDrawSurface
-// if (currentDrawSurface != eglManager.defaultSurface) {
-// updateTransformMatrix(
-// surfaceTransform,
-// textureTransform
-// )
-//
-// drawFrame(
-// size.width,
-// size.height,
-// surfaceTransform
-// )
-//
-// // Set timestamp
-// val display =
-// EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
-// EGLExt.eglPresentationTimeANDROID(
-// display,
-// eglManager.currentDrawSurface,
-// currentTimestamp
-// )
-// }
-// }
-// }
-// )
-//
-// frameUpdateFlow
-// .onCompletion {
-// outputRenderTarget.detach(cancelPending = false) {
-// onDisconnectFromInput()
-// refCountedSurface.release()
-// }
-// }.filterNot { it == 0 } // Don't attempt render on frame count 0
-// .collectLatest {
-// inputRenderTarget.requestRender()
-// outputRenderTarget.requestRender()
-// }
-// }
-// }
-// }
+ private suspend fun RenderCallbacks.renderWithSurfaceRequest(surfaceRequest: SurfaceRequest) =
+ coroutineScope inputScope@{
+ var currentTimestamp = TIMESTAMP_UNINITIALIZED
+ val surfaceTextureRef = RefCounted<SurfaceTexture> {
+ it.release()
+ }
+ val textureTransform = FloatArray(16)
+
+ val frameUpdateFlow = MutableStateFlow(0)
+
+ val initializeCallback = object : GLRenderer.EGLContextCallback {
+
+ override fun onEGLContextCreated(eglManager: EGLManager) {
+ initRenderer()
+
+ val surfaceTex = createSurfaceTexture(
+ surfaceRequest.resolution.width,
+ surfaceRequest.resolution.height
+ )
+
+ // Initialize the reference counted surface texture
+ surfaceTextureRef.initialize(surfaceTex)
+
+ surfaceTex.setOnFrameAvailableListener {
+ // Increment frame counter
+ frameUpdateFlow.update { it + 1 }
+ }
+
+ val inputSurface = Surface(surfaceTex)
+ surfaceRequest.provideSurface(inputSurface, Runnable::run) { result ->
+ inputSurface.release()
+ surfaceTextureRef.release()
+ [email protected](
+ "Input surface no longer receiving frames: $result"
+ )
+ }
+ }
+
+ override fun onEGLContextDestroyed(eglManager: EGLManager) {
+ // no-op
+ }
+ }
+
+ val glRenderer = GLRenderer(
+ eglSpecFactory = provideEGLSpec,
+ eglConfigFactory = initConfig
+ )
+ glRenderer.registerEGLContextCallback(initializeCallback)
+ glRenderer.start(glThreadName)
+
+ val inputRenderTarget = glRenderer.createRenderTarget(
+ surfaceRequest.resolution.width,
+ surfaceRequest.resolution.height,
+ object : GLRenderer.RenderCallback {
+
+ override fun onDrawFrame(eglManager: EGLManager) {
+ surfaceTextureRef.acquire()?.also {
+ try {
+ currentTimestamp =
+ if (currentTimestamp == TIMESTAMP_UNINITIALIZED) {
+ // Don't perform any updates on first draw,
+ // we're only setting up the context.
+ 0
+ } else {
+ it.updateTexImage()
+ it.getTransformMatrix(textureTransform)
+ it.timestamp
+ }
+ } finally {
+ surfaceTextureRef.release()
+ }
+ }
+ }
+ }
+ )
+
+ // Create the context and initialize the input. This will call RenderTarget.onDrawFrame,
+ // but we won't actually update the frame since this triggers adding the frame callback.
+ // All subsequent updates will then happen through frameUpdateFlow.
+ // This should be updated when https://issuetracker.google.com/331968279 is resolved.
+ inputRenderTarget.requestRender()
+
+ // Connect the onConnectToInput callback with the onDisconnectFromInput
+ // Should only be called on worker thread
+ var connectedToInput = false
+
+ // Should only be called on worker thread
+ val onConnectToInput: () -> Boolean = {
+ connectedToInput = surfaceTextureRef.acquire() != null
+ connectedToInput
+ }
+
+ // Should only be called on worker thread
+ val onDisconnectFromInput: () -> Unit = {
+ if (connectedToInput) {
+ surfaceTextureRef.release()
+ connectedToInput = false
+ }
+ }
+
+ // Wait for output surfaces
+ outputSurfaceFlow
+ .onCompletion {
+ glRenderer.stop(cancelPending = false)
+ glRenderer.unregisterEGLContextCallback(initializeCallback)
+ }.filterNotNull()
+ .collectLatest { surfaceOutputScope ->
+ surfaceOutputScope.withSurfaceOutput { refCountedSurface,
+ size,
+ updateTransformMatrix ->
+ // If we can't acquire the surface, then the surface output is already
+ // closed, so we'll return and wait for the next output surface.
+ val outputSurface =
+ refCountedSurface.acquire() ?: return@withSurfaceOutput
+
+ val surfaceTransform = FloatArray(16)
+ val outputRenderTarget = glRenderer.attach(
+ outputSurface,
+ size.width,
+ size.height,
+ object : GLRenderer.RenderCallback {
+
+ override fun onSurfaceCreated(
+ spec: EGLSpec,
+ config: EGLConfig,
+ surface: Surface,
+ width: Int,
+ height: Int
+ ): EGLSurface? {
+ return if (onConnectToInput()) {
+ createOutputSurface(spec, config, surface, width, height)
+ } else {
+ null
+ }
+ }
+
+ override fun onDrawFrame(eglManager: EGLManager) {
+ val currentDrawSurface = eglManager.currentDrawSurface
+ if (currentDrawSurface != eglManager.defaultSurface) {
+ updateTransformMatrix(
+ surfaceTransform,
+ textureTransform
+ )
+
+ drawFrame(
+ size.width,
+ size.height,
+ surfaceTransform
+ )
+
+ // Set timestamp
+ val display =
+ EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
+ EGLExt.eglPresentationTimeANDROID(
+ display,
+ eglManager.currentDrawSurface,
+ currentTimestamp
+ )
+ }
+ }
+ }
+ )
+
+ frameUpdateFlow
+ .onCompletion {
+ outputRenderTarget.detach(cancelPending = false) {
+ onDisconnectFromInput()
+ refCountedSurface.release()
+ }
+ }.filterNot { it == 0 } // Don't attempt render on frame count 0
+ .collectLatest {
+ inputRenderTarget.requestRender()
+ outputRenderTarget.requestRender()
+ }
+ }
+ }
+ }
override fun onInputSurface(surfaceRequest: SurfaceRequest) {
-// val newScope = SurfaceRequestScope(surfaceRequest)
-// inputSurfaceFlow.update { old ->
-// old?.cancel("New SurfaceRequest received.")
-// newScope
-// }
+ val newScope = SurfaceRequestScope(surfaceRequest)
+ inputSurfaceFlow.update { old ->
+ old?.cancel("New SurfaceRequest received.")
+ newScope
+ }
}
override fun onOutputSurface(surfaceOutput: SurfaceOutput) {
-// val newScope = SurfaceOutputScope(surfaceOutput)
-// outputSurfaceFlow.update { old ->
-// old?.cancel("New SurfaceOutput received.")
-// newScope
-// }
+ val newScope = SurfaceOutputScope(surfaceOutput)
+ outputSurfaceFlow.update { old ->
+ old?.cancel("New SurfaceOutput received.")
+ newScope
+ }
}
}
-//interface RenderCallbacks {
-// val glThreadName: String
-// val provideEGLSpec: () -> EGLSpec
-// val initConfig: EGLManager.() -> EGLConfig
-// val initRenderer: () -> Unit
-// val createSurfaceTexture: (width: Int, height: Int) -> SurfaceTexture
-// val createOutputSurface: (
-// eglSpec: EGLSpec,
-// config: EGLConfig,
-// surface: Surface,
-// width: Int,
-// height: Int
-// ) -> EGLSurface
-// val drawFrame: (outputWidth: Int, outputHeight: Int, surfaceTransform: FloatArray) -> Unit
-//}
-//
-//private class SurfaceOutputScope(val surfaceOutput: SurfaceOutput) {
-// private val surfaceLifecycleJob = SupervisorJob()
-// private val refCountedSurface = RefCounted<Surface>(onRelease = {
-// surfaceOutput.close()
-// }).apply {
-// // Ensure we don't release until after `initialize` has completed by deferring
-// // the release.
-// val deferredRelease = CompletableDeferred<Unit>()
-// initialize(
-// surfaceOutput.getSurface(Runnable::run) {
-// deferredRelease.complete(Unit)
-// }
-// )
-// CoroutineScope(Dispatchers.Unconfined).launch {
-// deferredRelease.await()
-// surfaceLifecycleJob.cancel("SurfaceOutput close requested.")
-// [email protected]()
-// }
-// }
-//
-// suspend fun <R> withSurfaceOutput(
-// block: suspend CoroutineScope.(
-// surface: RefCounted<Surface>,
-// surfaceSize: Size,
-// updateTransformMatrix: (updated: FloatArray, original: FloatArray) -> Unit
-// ) -> R
-// ): R {
-// return CoroutineScope(coroutineContext + Job(surfaceLifecycleJob)).async(
-// start = CoroutineStart.UNDISPATCHED
-// ) {
-// ensureActive()
-// block(
-// refCountedSurface,
-// surfaceOutput.size,
-// surfaceOutput::updateTransformMatrix
-// )
-// }.await()
-// }
-//
-// fun cancel(message: String? = null) {
-// message?.apply { surfaceLifecycleJob.cancel(message) } ?: surfaceLifecycleJob.cancel()
-// }
-//}
-//
-//private class SurfaceRequestScope(private val surfaceRequest: SurfaceRequest) {
-// private val requestLifecycleJob = SupervisorJob()
-//
-// init {
-// surfaceRequest.addRequestCancellationListener(Runnable::run) {
-// requestLifecycleJob.cancel("SurfaceRequest cancelled.")
-// }
-// }
-//
-// suspend fun <R> withSurfaceRequest(
-// block: suspend CoroutineScope.(
-// surfaceRequest: SurfaceRequest
-// ) -> R
-// ): R {
-// return CoroutineScope(coroutineContext + Job(requestLifecycleJob)).async(
-// start = CoroutineStart.UNDISPATCHED
-// ) {
-// ensureActive()
-// block(surfaceRequest)
-// }.await()
-// }
-//
-// fun cancel(message: String? = null) {
-// message?.apply { requestLifecycleJob.cancel(message) } ?: requestLifecycleJob.cancel()
-// // Attempt to tell frame producer we will not provide a surface. This may fail (silently)
-// // if surface was already provided or the producer has cancelled the request, in which
-// // case we don't have to do anything.
-// surfaceRequest.willNotProvideSurface()
-// }
-//}
+interface RenderCallbacks {
+ val glThreadName: String
+ val provideEGLSpec: () -> EGLSpec
+ val initConfig: EGLManager.() -> EGLConfig
+ val initRenderer: () -> Unit
+ val createSurfaceTexture: (width: Int, height: Int) -> SurfaceTexture
+ val createOutputSurface: (
+ eglSpec: EGLSpec,
+ config: EGLConfig,
+ surface: Surface,
+ width: Int,
+ height: Int
+ ) -> EGLSurface
+ val drawFrame: (outputWidth: Int, outputHeight: Int, surfaceTransform: FloatArray) -> Unit
+}
+
+private class SurfaceOutputScope(val surfaceOutput: SurfaceOutput) {
+ private val surfaceLifecycleJob = SupervisorJob()
+ private val refCountedSurface = RefCounted<Surface>(onRelease = {
+ surfaceOutput.close()
+ }).apply {
+ // Ensure we don't release until after `initialize` has completed by deferring
+ // the release.
+ val deferredRelease = CompletableDeferred<Unit>()
+ initialize(
+ surfaceOutput.getSurface(Runnable::run) {
+ deferredRelease.complete(Unit)
+ }
+ )
+ CoroutineScope(Dispatchers.Unconfined).launch {
+ deferredRelease.await()
+ surfaceLifecycleJob.cancel("SurfaceOutput close requested.")
+ [email protected]()
+ }
+ }
+
+ suspend fun <R> withSurfaceOutput(
+ block: suspend CoroutineScope.(
+ surface: RefCounted<Surface>,
+ surfaceSize: Size,
+ updateTransformMatrix: (updated: FloatArray, original: FloatArray) -> Unit
+ ) -> R
+ ): R {
+ return CoroutineScope(coroutineContext + Job(surfaceLifecycleJob)).async(
+ start = CoroutineStart.UNDISPATCHED
+ ) {
+ ensureActive()
+ block(
+ refCountedSurface,
+ surfaceOutput.size,
+ surfaceOutput::updateTransformMatrix
+ )
+ }.await()
+ }
+
+ fun cancel(message: String? = null) {
+ message?.apply { surfaceLifecycleJob.cancel(message) } ?: surfaceLifecycleJob.cancel()
+ }
+}
+
+private class SurfaceRequestScope(private val surfaceRequest: SurfaceRequest) {
+ private val requestLifecycleJob = SupervisorJob()
+
+ init {
+ surfaceRequest.addRequestCancellationListener(Runnable::run) {
+ requestLifecycleJob.cancel("SurfaceRequest cancelled.")
+ }
+ }
+
+ suspend fun <R> withSurfaceRequest(
+ block: suspend CoroutineScope.(
+ surfaceRequest: SurfaceRequest
+ ) -> R
+ ): R {
+ return CoroutineScope(coroutineContext + Job(requestLifecycleJob)).async(
+ start = CoroutineStart.UNDISPATCHED
+ ) {
+ ensureActive()
+ block(surfaceRequest)
+ }.await()
+ }
+
+ fun cancel(message: String? = null) {
+ message?.apply { requestLifecycleJob.cancel(message) } ?: requestLifecycleJob.cancel()
+ // Attempt to tell frame producer we will not provide a surface. This may fail (silently)
+ // if surface was already provided or the producer has cancelled the request, in which
+ // case we don't have to do anything.
+ surfaceRequest.willNotProvideSurface()
+ }
+}
diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/EGLSpecV14ES3.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/EGLSpecV14ES3.kt
index b895883..97918ed 100644
--- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/EGLSpecV14ES3.kt
+++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/EGLSpecV14ES3.kt
@@ -1,46 +1,46 @@
-///*
-// * Copyright (C) 2024 The Android Open Source Project
-// *
-// * Licensed under the Apache License, Version 2.0 (the "License");
-// * you may not use this file except in compliance with the License.
-// * You may obtain a copy of the License at
-// *
-// * http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//package com.google.jetpackcamera.domain.camera.effects
-//
-//import android.opengl.EGL14
-//import android.opengl.EGLConfig
-//import android.opengl.EGLContext
-//import androidx.graphics.opengl.egl.EGLSpec
-//
-//val EGLSpec.Companion.V14ES3: EGLSpec
-// get() = object : EGLSpec by V14 {
-//
-// private val contextAttributes = intArrayOf(
-// // GLES VERSION 3
-// EGL14.EGL_CONTEXT_CLIENT_VERSION,
-// 3,
-// // HWUI provides the ability to configure a context priority as well but that only
-// // seems to be configured on SystemUIApplication. This might be useful for
-// // front buffer rendering situations for performance.
-// EGL14.EGL_NONE
-// )
-//
-// override fun eglCreateContext(config: EGLConfig): EGLContext {
-// return EGL14.eglCreateContext(
-// EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
-// config,
-// // not creating from a shared context
-// EGL14.EGL_NO_CONTEXT,
-// contextAttributes,
-// 0
-// )
-// }
-// }
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.jetpackcamera.domain.camera.effects
+
+import android.opengl.EGL14
+import android.opengl.EGLConfig
+import android.opengl.EGLContext
+import androidx.graphics.opengl.egl.EGLSpec
+
+val EGLSpec.Companion.V14ES3: EGLSpec
+ get() = object : EGLSpec by V14 {
+
+ private val contextAttributes = intArrayOf(
+ // GLES VERSION 3
+ EGL14.EGL_CONTEXT_CLIENT_VERSION,
+ 3,
+ // HWUI provides the ability to configure a context priority as well but that only
+ // seems to be configured on SystemUIApplication. This might be useful for
+ // front buffer rendering situations for performance.
+ EGL14.EGL_NONE
+ )
+
+ override fun eglCreateContext(config: EGLConfig): EGLContext {
+ return EGL14.eglCreateContext(
+ EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
+ config,
+ // not creating from a shared context
+ EGL14.EGL_NO_CONTEXT,
+ contextAttributes,
+ 0
+ )
+ }
+ }
diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/ShaderCopy.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/ShaderCopy.kt
index 8373561..e3aff12 100644
--- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/ShaderCopy.kt
+++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/effects/ShaderCopy.kt
@@ -1,450 +1,450 @@
-///*
-// * Copyright (C) 2024 The Android Open Source Project
-// *
-// * Licensed under the Apache License, Version 2.0 (the "License");
-// * you may not use this file except in compliance with the License.
-// * You may obtain a copy of the License at
-// *
-// * http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//package com.google.jetpackcamera.domain.camera.effects
-//
-//import android.graphics.SurfaceTexture
-//import android.opengl.EGL14
-//import android.opengl.EGLConfig
-//import android.opengl.EGLExt
-//import android.opengl.GLES11Ext
-//import android.opengl.GLES20
-//import android.util.Log
-//import android.view.Surface
-//import androidx.annotation.WorkerThread
-//import androidx.camera.core.DynamicRange
-//import androidx.graphics.opengl.egl.EGLConfigAttributes
-//import androidx.graphics.opengl.egl.EGLManager
-//import androidx.graphics.opengl.egl.EGLSpec
-//import java.nio.ByteBuffer
-//import java.nio.ByteOrder
-//import java.nio.FloatBuffer
-//
-//class ShaderCopy(private val dynamicRange: DynamicRange) : RenderCallbacks {
-//
-// // Called on worker thread only
-// private var externalTextureId: Int = -1
-// private var programHandle = -1
-// private var texMatrixLoc = -1
-// private var positionLoc = -1
-// private var texCoordLoc = -1
-// private val use10bitPipeline: Boolean
-// get() = dynamicRange.bitDepth == DynamicRange.BIT_DEPTH_10_BIT
-//
-// override val glThreadName: String
-// get() = TAG
-//
-// override val provideEGLSpec: () -> EGLSpec
-// get() = { if (use10bitPipeline) EGLSpec.V14ES3 else EGLSpec.V14 }
-//
-// override val initConfig: EGLManager.() -> EGLConfig
-// get() = {
-// checkNotNull(
-// loadConfig(
-// EGLConfigAttributes {
-// if (use10bitPipeline) {
-// TEN_BIT_REQUIRED_EGL_EXTENSIONS.forEach {
-// check(isExtensionSupported(it)) {
-// "Required extension for 10-bit HDR is not " +
-// "supported: $it"
-// }
-// }
-// include(EGLConfigAttributes.RGBA_1010102)
-// EGL14.EGL_RENDERABLE_TYPE to
-// EGLExt.EGL_OPENGL_ES3_BIT_KHR
-// EGL14.EGL_SURFACE_TYPE to
-// (EGL14.EGL_WINDOW_BIT or EGL14.EGL_PBUFFER_BIT)
-// } else {
-// include(EGLConfigAttributes.RGBA_8888)
-// }
-// }
-// )
-// ) {
-// "Unable to select EGLConfig"
-// }
-// }
-//
-// override val initRenderer: () -> Unit
-// get() = {
-// createProgram(
-// if (use10bitPipeline) {
-// TEN_BIT_VERTEX_SHADER
-// } else {
-// DEFAULT_VERTEX_SHADER
-// },
-// if (use10bitPipeline) {
-// TEN_BIT_FRAGMENT_SHADER
-// } else {
-// DEFAULT_FRAGMENT_SHADER
-// }
-// )
-// loadLocations()
-// createTexture()
-// useAndConfigureProgram()
-// }
-//
-// override val createSurfaceTexture
-// get() = { width: Int, height: Int ->
-// SurfaceTexture(externalTextureId).apply {
-// setDefaultBufferSize(width, height)
-// }
-// }
-//
-// override val createOutputSurface
-// get() = { eglSpec: EGLSpec,
-// config: EGLConfig,
-// surface: Surface,
-// _: Int,
-// _: Int ->
-// eglSpec.eglCreateWindowSurface(
-// config,
-// surface,
-// EGLConfigAttributes {
-// if (use10bitPipeline) {
-// EGL_GL_COLORSPACE_KHR to EGL_GL_COLORSPACE_BT2020_HLG_EXT
-// }
-// }
-// )
-// }
-//
-// override val drawFrame
-// get() = { outputWidth: Int,
-// outputHeight: Int,
-// surfaceTransform: FloatArray ->
-// GLES20.glViewport(
-// 0,
-// 0,
-// outputWidth,
-// outputHeight
-// )
-// GLES20.glScissor(
-// 0,
-// 0,
-// outputWidth,
-// outputHeight
-// )
-//
-// GLES20.glUniformMatrix4fv(
-// texMatrixLoc,
-// /*count=*/
-// 1,
-// /*transpose=*/
-// false,
-// surfaceTransform,
-// /*offset=*/
-// 0
-// )
-// checkGlErrorOrThrow("glUniformMatrix4fv")
-//
-// // Draw the rect.
-// GLES20.glDrawArrays(
-// GLES20.GL_TRIANGLE_STRIP,
-// /*firstVertex=*/
-// 0,
-// /*vertexCount=*/
-// 4
-// )
-// checkGlErrorOrThrow("glDrawArrays")
-// }
-//
-// @WorkerThread
-// fun createTexture() {
-// checkGlThread()
-// val textures = IntArray(1)
-// GLES20.glGenTextures(1, textures, 0)
-// checkGlErrorOrThrow("glGenTextures")
-// val texId = textures[0]
-// GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId)
-// checkGlErrorOrThrow("glBindTexture $texId")
-// GLES20.glTexParameterf(
-// GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-// GLES20.GL_TEXTURE_MIN_FILTER,
-// GLES20.GL_NEAREST.toFloat()
-// )
-// GLES20.glTexParameterf(
-// GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-// GLES20.GL_TEXTURE_MAG_FILTER,
-// GLES20.GL_LINEAR.toFloat()
-// )
-// GLES20.glTexParameteri(
-// GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-// GLES20.GL_TEXTURE_WRAP_S,
-// GLES20.GL_CLAMP_TO_EDGE
-// )
-// GLES20.glTexParameteri(
-// GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-// GLES20.GL_TEXTURE_WRAP_T,
-// GLES20.GL_CLAMP_TO_EDGE
-// )
-// checkGlErrorOrThrow("glTexParameter")
-// externalTextureId = texId
-// }
-//
-// @WorkerThread
-// fun useAndConfigureProgram() {
-// checkGlThread()
-// // Select the program.
-// GLES20.glUseProgram(programHandle)
-// checkGlErrorOrThrow("glUseProgram")
-//
-// // Set the texture.
-// GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
-// GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, externalTextureId)
-//
-// // Enable the "aPosition" vertex attribute.
-// GLES20.glEnableVertexAttribArray(positionLoc)
-// checkGlErrorOrThrow("glEnableVertexAttribArray")
-//
-// // Connect vertexBuffer to "aPosition".
-// val coordsPerVertex = 2
-// val vertexStride = 0
-// GLES20.glVertexAttribPointer(
-// positionLoc,
-// coordsPerVertex,
-// GLES20.GL_FLOAT,
-// /*normalized=*/
-// false,
-// vertexStride,
-// VERTEX_BUF
-// )
-// checkGlErrorOrThrow("glVertexAttribPointer")
-//
-// // Enable the "aTextureCoord" vertex attribute.
-// GLES20.glEnableVertexAttribArray(texCoordLoc)
-// checkGlErrorOrThrow("glEnableVertexAttribArray")
-//
-// // Connect texBuffer to "aTextureCoord".
-// val coordsPerTex = 2
-// val texStride = 0
-// GLES20.glVertexAttribPointer(
-// texCoordLoc,
-// coordsPerTex,
-// GLES20.GL_FLOAT,
-// /*normalized=*/
-// false,
-// texStride,
-// TEX_BUF
-// )
-// checkGlErrorOrThrow("glVertexAttribPointer")
-// }
-//
-// @WorkerThread
-// private fun createProgram(vertShader: String, fragShader: String) {
-// checkGlThread()
-// var vertexShader = -1
-// var fragmentShader = -1
-// var program = -1
-// try {
-// fragmentShader = loadShader(
-// GLES20.GL_FRAGMENT_SHADER,
-// fragShader
-// )
-// vertexShader = loadShader(
-// GLES20.GL_VERTEX_SHADER,
-// vertShader
-// )
-// program = GLES20.glCreateProgram()
-// checkGlErrorOrThrow("glCreateProgram")
-// GLES20.glAttachShader(program, vertexShader)
-// checkGlErrorOrThrow("glAttachShader")
-// GLES20.glAttachShader(program, fragmentShader)
-// checkGlErrorOrThrow("glAttachShader")
-// GLES20.glLinkProgram(program)
-// val linkStatus = IntArray(1)
-// GLES20.glGetProgramiv(
-// program,
-// GLES20.GL_LINK_STATUS,
-// linkStatus,
-// /*offset=*/
-// 0
-// )
-// check(linkStatus[0] == GLES20.GL_TRUE) {
-// "Could not link program: " + GLES20.glGetProgramInfoLog(
-// program
-// )
-// }
-// programHandle = program
-// } catch (e: Exception) {
-// if (vertexShader != -1) {
-// GLES20.glDeleteShader(vertexShader)
-// }
-// if (fragmentShader != -1) {
-// GLES20.glDeleteShader(fragmentShader)
-// }
-// if (program != -1) {
-// GLES20.glDeleteProgram(program)
-// }
-// throw e
-// }
-// }
-//
-// @WorkerThread
-// private fun loadLocations() {
-// checkGlThread()
-// positionLoc = GLES20.glGetAttribLocation(programHandle, "aPosition")
-// checkLocationOrThrow(positionLoc, "aPosition")
-// texCoordLoc = GLES20.glGetAttribLocation(programHandle, "aTextureCoord")
-// checkLocationOrThrow(texCoordLoc, "aTextureCoord")
-// texMatrixLoc = GLES20.glGetUniformLocation(programHandle, "uTexMatrix")
-// checkLocationOrThrow(texMatrixLoc, "uTexMatrix")
-// }
-//
-// @WorkerThread
-// private fun loadShader(shaderType: Int, source: String): Int {
-// checkGlThread()
-// val shader = GLES20.glCreateShader(shaderType)
-// checkGlErrorOrThrow("glCreateShader type=$shaderType")
-// GLES20.glShaderSource(shader, source)
-// GLES20.glCompileShader(shader)
-// val compiled = IntArray(1)
-// GLES20.glGetShaderiv(
-// shader,
-// GLES20.GL_COMPILE_STATUS,
-// compiled,
-// /*offset=*/
-// 0
-// )
-// check(compiled[0] == GLES20.GL_TRUE) {
-// Log.w(TAG, "Could not compile shader: $source")
-// try {
-// return@check "Could not compile shader type " +
-// "$shaderType: ${GLES20.glGetShaderInfoLog(shader)}"
-// } finally {
-// GLES20.glDeleteShader(shader)
-// }
-// }
-// return shader
-// }
-//
-// @WorkerThread
-// private fun checkGlErrorOrThrow(op: String) {
-// val error = GLES20.glGetError()
-// check(error == GLES20.GL_NO_ERROR) { op + ": GL error 0x" + Integer.toHexString(error) }
-// }
-//
-// private fun checkLocationOrThrow(location: Int, label: String) {
-// check(location >= 0) { "Unable to locate '$label' in program" }
-// }
-//
-// companion object {
-// private const val SIZEOF_FLOAT = 4
-//
-// private val VERTEX_BUF = floatArrayOf(
-// // 0 bottom left
-// -1.0f,
-// -1.0f,
-// // 1 bottom right
-// 1.0f,
-// -1.0f,
-// // 2 top left
-// -1.0f,
-// 1.0f,
-// // 3 top right
-// 1.0f,
-// 1.0f
-// ).toBuffer()
-//
-// private val TEX_BUF = floatArrayOf(
-// // 0 bottom left
-// 0.0f,
-// 0.0f,
-// // 1 bottom right
-// 1.0f,
-// 0.0f,
-// // 2 top left
-// 0.0f,
-// 1.0f,
-// // 3 top right
-// 1.0f,
-// 1.0f
-// ).toBuffer()
-//
-// private const val TAG = "ShaderCopy"
-// private const val GL_THREAD_NAME = TAG
-//
-// private const val VAR_TEXTURE_COORD = "vTextureCoord"
-// private val DEFAULT_VERTEX_SHADER =
-// """
-// uniform mat4 uTexMatrix;
-// attribute vec4 aPosition;
-// attribute vec4 aTextureCoord;
-// varying vec2 $VAR_TEXTURE_COORD;
-// void main() {
-// gl_Position = aPosition;
-// $VAR_TEXTURE_COORD = (uTexMatrix * aTextureCoord).xy;
-// }
-// """.trimIndent()
-//
-// private val TEN_BIT_VERTEX_SHADER =
-// """
-// #version 300 es
-// in vec4 aPosition;
-// in vec4 aTextureCoord;
-// uniform mat4 uTexMatrix;
-// out vec2 $VAR_TEXTURE_COORD;
-// void main() {
-// gl_Position = aPosition;
-// $VAR_TEXTURE_COORD = (uTexMatrix * aTextureCoord).xy;
-// }
-// """.trimIndent()
-//
-// private const val VAR_TEXTURE = "sTexture"
-// private val DEFAULT_FRAGMENT_SHADER =
-// """
-// #extension GL_OES_EGL_image_external : require
-// precision mediump float;
-// varying vec2 $VAR_TEXTURE_COORD;
-// uniform samplerExternalOES $VAR_TEXTURE;
-// void main() {
-// gl_FragColor = texture2D($VAR_TEXTURE, $VAR_TEXTURE_COORD);
-// }
-// """.trimIndent()
-//
-// private val TEN_BIT_FRAGMENT_SHADER =
-// """
-// #version 300 es
-// #extension GL_EXT_YUV_target : require
-// precision mediump float;
-// uniform __samplerExternal2DY2YEXT $VAR_TEXTURE;
-// in vec2 $VAR_TEXTURE_COORD;
-// layout (yuv) out vec3 outColor;
-//
-// void main() {
-// outColor = texture($VAR_TEXTURE, $VAR_TEXTURE_COORD).xyz;
-// }
-// """.trimIndent()
-//
-// private const val EGL_GL_COLORSPACE_KHR = 0x309D
-// private const val EGL_GL_COLORSPACE_BT2020_HLG_EXT = 0x3540
-//
-// private val TEN_BIT_REQUIRED_EGL_EXTENSIONS = listOf(
-// "EGL_EXT_gl_colorspace_bt2020_hlg",
-// "EGL_EXT_yuv_surface"
-// )
-//
-// private fun FloatArray.toBuffer(): FloatBuffer {
-// val bb = ByteBuffer.allocateDirect(size * SIZEOF_FLOAT)
-// bb.order(ByteOrder.nativeOrder())
-// val fb = bb.asFloatBuffer()
-// fb.put(this)
-// fb.position(0)
-// return fb
-// }
-//
-// private fun checkGlThread() {
-// check(GL_THREAD_NAME == Thread.currentThread().name)
-// }
-// }
-//}
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.jetpackcamera.domain.camera.effects
+
+import android.graphics.SurfaceTexture
+import android.opengl.EGL14
+import android.opengl.EGLConfig
+import android.opengl.EGLExt
+import android.opengl.GLES11Ext
+import android.opengl.GLES20
+import android.util.Log
+import android.view.Surface
+import androidx.annotation.WorkerThread
+import androidx.camera.core.DynamicRange
+import androidx.graphics.opengl.egl.EGLConfigAttributes
+import androidx.graphics.opengl.egl.EGLManager
+import androidx.graphics.opengl.egl.EGLSpec
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.FloatBuffer
+
+class ShaderCopy(private val dynamicRange: DynamicRange) : RenderCallbacks {
+
+ // Called on worker thread only
+ private var externalTextureId: Int = -1
+ private var programHandle = -1
+ private var texMatrixLoc = -1
+ private var positionLoc = -1
+ private var texCoordLoc = -1
+ private val use10bitPipeline: Boolean
+ get() = dynamicRange.bitDepth == DynamicRange.BIT_DEPTH_10_BIT
+
+ override val glThreadName: String
+ get() = TAG
+
+ override val provideEGLSpec: () -> EGLSpec
+ get() = { if (use10bitPipeline) EGLSpec.V14ES3 else EGLSpec.V14 }
+
+ override val initConfig: EGLManager.() -> EGLConfig
+ get() = {
+ checkNotNull(
+ loadConfig(
+ EGLConfigAttributes {
+ if (use10bitPipeline) {
+ TEN_BIT_REQUIRED_EGL_EXTENSIONS.forEach {
+ check(isExtensionSupported(it)) {
+ "Required extension for 10-bit HDR is not " +
+ "supported: $it"
+ }
+ }
+ include(EGLConfigAttributes.RGBA_1010102)
+ EGL14.EGL_RENDERABLE_TYPE to
+ EGLExt.EGL_OPENGL_ES3_BIT_KHR
+ EGL14.EGL_SURFACE_TYPE to
+ (EGL14.EGL_WINDOW_BIT or EGL14.EGL_PBUFFER_BIT)
+ } else {
+ include(EGLConfigAttributes.RGBA_8888)
+ }
+ }
+ )
+ ) {
+ "Unable to select EGLConfig"
+ }
+ }
+
+ override val initRenderer: () -> Unit
+ get() = {
+ createProgram(
+ if (use10bitPipeline) {
+ TEN_BIT_VERTEX_SHADER
+ } else {
+ DEFAULT_VERTEX_SHADER
+ },
+ if (use10bitPipeline) {
+ TEN_BIT_FRAGMENT_SHADER
+ } else {
+ DEFAULT_FRAGMENT_SHADER
+ }
+ )
+ loadLocations()
+ createTexture()
+ useAndConfigureProgram()
+ }
+
+ override val createSurfaceTexture
+ get() = { width: Int, height: Int ->
+ SurfaceTexture(externalTextureId).apply {
+ setDefaultBufferSize(width, height)
+ }
+ }
+
+ override val createOutputSurface
+ get() = { eglSpec: EGLSpec,
+ config: EGLConfig,
+ surface: Surface,
+ _: Int,
+ _: Int ->
+ eglSpec.eglCreateWindowSurface(
+ config,
+ surface,
+ EGLConfigAttributes {
+ if (use10bitPipeline) {
+ EGL_GL_COLORSPACE_KHR to EGL_GL_COLORSPACE_BT2020_HLG_EXT
+ }
+ }
+ )
+ }
+
+ override val drawFrame
+ get() = { outputWidth: Int,
+ outputHeight: Int,
+ surfaceTransform: FloatArray ->
+ GLES20.glViewport(
+ 0,
+ 0,
+ outputWidth,
+ outputHeight
+ )
+ GLES20.glScissor(
+ 0,
+ 0,
+ outputWidth,
+ outputHeight
+ )
+
+ GLES20.glUniformMatrix4fv(
+ texMatrixLoc,
+ /*count=*/
+ 1,
+ /*transpose=*/
+ false,
+ surfaceTransform,
+ /*offset=*/
+ 0
+ )
+ checkGlErrorOrThrow("glUniformMatrix4fv")
+
+ // Draw the rect.
+ GLES20.glDrawArrays(
+ GLES20.GL_TRIANGLE_STRIP,
+ /*firstVertex=*/
+ 0,
+ /*vertexCount=*/
+ 4
+ )
+ checkGlErrorOrThrow("glDrawArrays")
+ }
+
+ @WorkerThread
+ fun createTexture() {
+ checkGlThread()
+ val textures = IntArray(1)
+ GLES20.glGenTextures(1, textures, 0)
+ checkGlErrorOrThrow("glGenTextures")
+ val texId = textures[0]
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId)
+ checkGlErrorOrThrow("glBindTexture $texId")
+ GLES20.glTexParameterf(
+ GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST.toFloat()
+ )
+ GLES20.glTexParameterf(
+ GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR.toFloat()
+ )
+ GLES20.glTexParameteri(
+ GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE
+ )
+ GLES20.glTexParameteri(
+ GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE
+ )
+ checkGlErrorOrThrow("glTexParameter")
+ externalTextureId = texId
+ }
+
+ @WorkerThread
+ fun useAndConfigureProgram() {
+ checkGlThread()
+ // Select the program.
+ GLES20.glUseProgram(programHandle)
+ checkGlErrorOrThrow("glUseProgram")
+
+ // Set the texture.
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, externalTextureId)
+
+ // Enable the "aPosition" vertex attribute.
+ GLES20.glEnableVertexAttribArray(positionLoc)
+ checkGlErrorOrThrow("glEnableVertexAttribArray")
+
+ // Connect vertexBuffer to "aPosition".
+ val coordsPerVertex = 2
+ val vertexStride = 0
+ GLES20.glVertexAttribPointer(
+ positionLoc,
+ coordsPerVertex,
+ GLES20.GL_FLOAT,
+ /*normalized=*/
+ false,
+ vertexStride,
+ VERTEX_BUF
+ )
+ checkGlErrorOrThrow("glVertexAttribPointer")
+
+ // Enable the "aTextureCoord" vertex attribute.
+ GLES20.glEnableVertexAttribArray(texCoordLoc)
+ checkGlErrorOrThrow("glEnableVertexAttribArray")
+
+ // Connect texBuffer to "aTextureCoord".
+ val coordsPerTex = 2
+ val texStride = 0
+ GLES20.glVertexAttribPointer(
+ texCoordLoc,
+ coordsPerTex,
+ GLES20.GL_FLOAT,
+ /*normalized=*/
+ false,
+ texStride,
+ TEX_BUF
+ )
+ checkGlErrorOrThrow("glVertexAttribPointer")
+ }
+
+ @WorkerThread
+ private fun createProgram(vertShader: String, fragShader: String) {
+ checkGlThread()
+ var vertexShader = -1
+ var fragmentShader = -1
+ var program = -1
+ try {
+ fragmentShader = loadShader(
+ GLES20.GL_FRAGMENT_SHADER,
+ fragShader
+ )
+ vertexShader = loadShader(
+ GLES20.GL_VERTEX_SHADER,
+ vertShader
+ )
+ program = GLES20.glCreateProgram()
+ checkGlErrorOrThrow("glCreateProgram")
+ GLES20.glAttachShader(program, vertexShader)
+ checkGlErrorOrThrow("glAttachShader")
+ GLES20.glAttachShader(program, fragmentShader)
+ checkGlErrorOrThrow("glAttachShader")
+ GLES20.glLinkProgram(program)
+ val linkStatus = IntArray(1)
+ GLES20.glGetProgramiv(
+ program,
+ GLES20.GL_LINK_STATUS,
+ linkStatus,
+ /*offset=*/
+ 0
+ )
+ check(linkStatus[0] == GLES20.GL_TRUE) {
+ "Could not link program: " + GLES20.glGetProgramInfoLog(
+ program
+ )
+ }
+ programHandle = program
+ } catch (e: Exception) {
+ if (vertexShader != -1) {
+ GLES20.glDeleteShader(vertexShader)
+ }
+ if (fragmentShader != -1) {
+ GLES20.glDeleteShader(fragmentShader)
+ }
+ if (program != -1) {
+ GLES20.glDeleteProgram(program)
+ }
+ throw e
+ }
+ }
+
+ @WorkerThread
+ private fun loadLocations() {
+ checkGlThread()
+ positionLoc = GLES20.glGetAttribLocation(programHandle, "aPosition")
+ checkLocationOrThrow(positionLoc, "aPosition")
+ texCoordLoc = GLES20.glGetAttribLocation(programHandle, "aTextureCoord")
+ checkLocationOrThrow(texCoordLoc, "aTextureCoord")
+ texMatrixLoc = GLES20.glGetUniformLocation(programHandle, "uTexMatrix")
+ checkLocationOrThrow(texMatrixLoc, "uTexMatrix")
+ }
+
+ @WorkerThread
+ private fun loadShader(shaderType: Int, source: String): Int {
+ checkGlThread()
+ val shader = GLES20.glCreateShader(shaderType)
+ checkGlErrorOrThrow("glCreateShader type=$shaderType")
+ GLES20.glShaderSource(shader, source)
+ GLES20.glCompileShader(shader)
+ val compiled = IntArray(1)
+ GLES20.glGetShaderiv(
+ shader,
+ GLES20.GL_COMPILE_STATUS,
+ compiled,
+ /*offset=*/
+ 0
+ )
+ check(compiled[0] == GLES20.GL_TRUE) {
+ Log.w(TAG, "Could not compile shader: $source")
+ try {
+ return@check "Could not compile shader type " +
+ "$shaderType: ${GLES20.glGetShaderInfoLog(shader)}"
+ } finally {
+ GLES20.glDeleteShader(shader)
+ }
+ }
+ return shader
+ }
+
+ @WorkerThread
+ private fun checkGlErrorOrThrow(op: String) {
+ val error = GLES20.glGetError()
+ check(error == GLES20.GL_NO_ERROR) { op + ": GL error 0x" + Integer.toHexString(error) }
+ }
+
+ private fun checkLocationOrThrow(location: Int, label: String) {
+ check(location >= 0) { "Unable to locate '$label' in program" }
+ }
+
+ companion object {
+ private const val SIZEOF_FLOAT = 4
+
+ private val VERTEX_BUF = floatArrayOf(
+ // 0 bottom left
+ -1.0f,
+ -1.0f,
+ // 1 bottom right
+ 1.0f,
+ -1.0f,
+ // 2 top left
+ -1.0f,
+ 1.0f,
+ // 3 top right
+ 1.0f,
+ 1.0f
+ ).toBuffer()
+
+ private val TEX_BUF = floatArrayOf(
+ // 0 bottom left
+ 0.0f,
+ 0.0f,
+ // 1 bottom right
+ 1.0f,
+ 0.0f,
+ // 2 top left
+ 0.0f,
+ 1.0f,
+ // 3 top right
+ 1.0f,
+ 1.0f
+ ).toBuffer()
+
+ private const val TAG = "ShaderCopy"
+ private const val GL_THREAD_NAME = TAG
+
+ private const val VAR_TEXTURE_COORD = "vTextureCoord"
+ private val DEFAULT_VERTEX_SHADER =
+ """
+ uniform mat4 uTexMatrix;
+ attribute vec4 aPosition;
+ attribute vec4 aTextureCoord;
+ varying vec2 $VAR_TEXTURE_COORD;
+ void main() {
+ gl_Position = aPosition;
+ $VAR_TEXTURE_COORD = (uTexMatrix * aTextureCoord).xy;
+ }
+ """.trimIndent()
+
+ private val TEN_BIT_VERTEX_SHADER =
+ """
+ #version 300 es
+ in vec4 aPosition;
+ in vec4 aTextureCoord;
+ uniform mat4 uTexMatrix;
+ out vec2 $VAR_TEXTURE_COORD;
+ void main() {
+ gl_Position = aPosition;
+ $VAR_TEXTURE_COORD = (uTexMatrix * aTextureCoord).xy;
+ }
+ """.trimIndent()
+
+ private const val VAR_TEXTURE = "sTexture"
+ private val DEFAULT_FRAGMENT_SHADER =
+ """
+ #extension GL_OES_EGL_image_external : require
+ precision mediump float;
+ varying vec2 $VAR_TEXTURE_COORD;
+ uniform samplerExternalOES $VAR_TEXTURE;
+ void main() {
+ gl_FragColor = texture2D($VAR_TEXTURE, $VAR_TEXTURE_COORD);
+ }
+ """.trimIndent()
+
+ private val TEN_BIT_FRAGMENT_SHADER =
+ """
+ #version 300 es
+ #extension GL_EXT_YUV_target : require
+ precision mediump float;
+ uniform __samplerExternal2DY2YEXT $VAR_TEXTURE;
+ in vec2 $VAR_TEXTURE_COORD;
+ layout (yuv) out vec3 outColor;
+
+ void main() {
+ outColor = texture($VAR_TEXTURE, $VAR_TEXTURE_COORD).xyz;
+ }
+ """.trimIndent()
+
+ private const val EGL_GL_COLORSPACE_KHR = 0x309D
+ private const val EGL_GL_COLORSPACE_BT2020_HLG_EXT = 0x3540
+
+ private val TEN_BIT_REQUIRED_EGL_EXTENSIONS = listOf(
+ "EGL_EXT_gl_colorspace_bt2020_hlg",
+ "EGL_EXT_yuv_surface"
+ )
+
+ private fun FloatArray.toBuffer(): FloatBuffer {
+ val bb = ByteBuffer.allocateDirect(size * SIZEOF_FLOAT)
+ bb.order(ByteOrder.nativeOrder())
+ val fb = bb.asFloatBuffer()
+ fb.put(this)
+ fb.position(0)
+ return fb
+ }
+
+ private fun checkGlThread() {
+ check(GL_THREAD_NAME == Thread.currentThread().name)
+ }
+ }
+}