From 10fa3afbc2584b9d5b1623dca1c6f042d0f7683e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Garci=CC=81a?= Date: Mon, 11 Nov 2024 15:40:07 +0100 Subject: [PATCH 1/4] Use SurfaceTexture instead --- .../io/github/sceneview/node/ViewNode2.kt | 84 ++++++++----------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt b/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt index 7d7cd710..59d2615f 100644 --- a/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt +++ b/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt @@ -1,15 +1,12 @@ package io.github.sceneview.node import android.content.Context +import android.content.ContextWrapper import android.graphics.Canvas -import android.graphics.ImageFormat -import android.graphics.Picture import android.graphics.PixelFormat import android.graphics.PorterDuff -import android.media.ImageReader +import android.graphics.SurfaceTexture import android.os.Build -import android.os.Handler -import android.os.Looper import android.util.AttributeSet import android.view.LayoutInflater import android.view.MotionEvent @@ -17,11 +14,17 @@ import android.view.Surface import android.view.View import android.view.WindowManager.LayoutParams import android.widget.FrameLayout +import androidx.activity.ComponentActivity +import androidx.activity.setViewTreeFullyDrawnReporterOwner +import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.annotation.LayoutRes import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.google.android.filament.Engine import com.google.android.filament.MaterialInstance import com.google.android.filament.RenderableManager @@ -68,13 +71,8 @@ class ViewNode2( view: View, unlit: Boolean = false, invertFrontFaceWinding: Boolean = false, - // This seems a little high, but lower values cause occasional "client tried to acquire - // more than maxImages buffers" on a Pixel 3 - val imageReaderMaxImages: Int = 7 ) : PlaneNode(engine = engine) { - var surface: Surface? = null - // Updated when the view is added to the view manager var pxPerUnits = 250.0f set(value) { @@ -92,11 +90,11 @@ class ViewNode2( addView(view) } - private var imageReader: ImageReader? = null - private val picture = Picture() - private val directImageHandler = Handler(Looper.getMainLooper()) + private val surfaceTexture = SurfaceTexture(0).also { it.detachFromGLContext() } + private val surface = Surface(surfaceTexture) val stream: Stream = Stream.Builder() + .stream(surfaceTexture) .build(engine) val texture: Texture = Texture.Builder() @@ -295,13 +293,14 @@ class ViewNode2( // } override fun destroy() { - super.destroy() + + windowManager.removeView(layout) engine.safeDestroyMaterialInstance(materialInstance) engine.safeDestroyTexture(texture) engine.safeDestroyStream(stream) - windowManager.removeView(layout) + super.destroy() } /** @@ -332,15 +331,8 @@ class ViewNode2( super.onLayout(changed, left, top, right, bottom) // Only called when we first get View size - imageReader?.close() - imageReader = ImageReader.newInstance( - width, - height, - ImageFormat.RGB_565, - imageReaderMaxImages - ) - surface?.release() - surface = imageReader?.surface + surfaceTexture.setDefaultBufferSize(width, height) + } override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) { @@ -351,30 +343,13 @@ class ViewNode2( override fun dispatchDraw(canvas: Canvas) { if (!isAttachedToWindow) return - // Check for Stream validity - val stream = stream.takeIf { it.timestamp > 0 } ?: return // Sanity that the surface is valid. - val viewSurface = surface?.takeIf { it.isValid } ?: return - if (isDirty) { - val pictureCanvas = picture.beginRecording(width, height) - pictureCanvas.drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR) - super.dispatchDraw(pictureCanvas) - picture.endRecording() - val surfaceCanvas = viewSurface.lockCanvas(null) - picture.draw(surfaceCanvas) - viewSurface.unlockCanvasAndPost(surfaceCanvas) - - val image = imageReader!!.acquireLatestImage() - stream.setAcquiredImage( - image.hardwareBuffer!!, - directImageHandler - ) { - image.close() - } - } - // Ask for redraw to update on each frames until stream is null - invalidate() + val viewSurface = surface.takeIf { it.isValid } ?: return + val surfaceCanvas = viewSurface.lockCanvas(null) + surfaceCanvas.drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR) + super.dispatchDraw(surfaceCanvas) + viewSurface.unlockCanvasAndPost(surfaceCanvas) } } @@ -383,7 +358,17 @@ class ViewNode2( private val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager - val layout by lazy { FrameLayout(context) } + val layout by lazy { + FrameLayout(context).also { + context.findActivity()?.let { activity -> + it.setViewTreeLifecycleOwner(activity) + it.setViewTreeSavedStateRegistryOwner(activity) + it.setViewTreeViewModelStoreOwner(activity) + it.setViewTreeFullyDrawnReporterOwner(activity) + it.setViewTreeOnBackPressedDispatcherOwner(activity) + } + } + } fun addView(view: View) = layout.addView(view) fun addView(view: View, params: FrameLayout.LayoutParams) = layout.addView(view, params) @@ -442,3 +427,8 @@ class ViewNode2( } } } + + +private fun Context.findActivity(): ComponentActivity? { + return generateSequence(this) { (it as? ContextWrapper)?.baseContext }.filterIsInstance().firstOrNull() +} From c017ae883b4cf24c3c887fe6b4d17ae982376328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Garci=CC=81a?= Date: Mon, 11 Nov 2024 15:57:49 +0100 Subject: [PATCH 2/4] No minimum Android 28 required in ViewNode2 anymore --- sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt b/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt index 59d2615f..f900715d 100644 --- a/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt +++ b/sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt @@ -6,7 +6,6 @@ import android.graphics.Canvas import android.graphics.PixelFormat import android.graphics.PorterDuff import android.graphics.SurfaceTexture -import android.os.Build import android.util.AttributeSet import android.view.LayoutInflater import android.view.MotionEvent @@ -18,7 +17,6 @@ import androidx.activity.ComponentActivity import androidx.activity.setViewTreeFullyDrawnReporterOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.annotation.LayoutRes -import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy @@ -63,7 +61,6 @@ import io.github.sceneview.safeDestroyTexture * (water, mirror surfaces, front camera in AR, etc.). * True to invert front faces, false otherwise */ -@RequiresApi(Build.VERSION_CODES.P) class ViewNode2( engine: Engine, val windowManager: WindowManager, @@ -319,7 +316,6 @@ class ViewNode2( * the view will not be marked as dirty when child views are animating when hardware * accelerated. */ - @RequiresApi(Build.VERSION_CODES.P) inner class Layout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, From 47222eb3892fd278a596b44f89beea585eea5efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Garci=CC=81a?= Date: Mon, 11 Nov 2024 18:20:47 +0100 Subject: [PATCH 3/4] Actually remove RequiresApi --- sceneview/src/main/java/io/github/sceneview/Scene.kt | 3 +-- sceneview/src/main/java/io/github/sceneview/SceneView.kt | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/sceneview/src/main/java/io/github/sceneview/Scene.kt b/sceneview/src/main/java/io/github/sceneview/Scene.kt index c3486d24..0c86b127 100644 --- a/sceneview/src/main/java/io/github/sceneview/Scene.kt +++ b/sceneview/src/main/java/io/github/sceneview/Scene.kt @@ -584,7 +584,6 @@ fun rememberCameraManipulator( } } -@RequiresApi(Build.VERSION_CODES.P) @Composable fun rememberViewNodeManager( context: Context = LocalContext.current, @@ -605,4 +604,4 @@ private fun ScenePreview(modifier: Modifier) { modifier = modifier .background(Color.DarkGray) ) -} \ No newline at end of file +} diff --git a/sceneview/src/main/java/io/github/sceneview/SceneView.kt b/sceneview/src/main/java/io/github/sceneview/SceneView.kt index 0cd3497e..69f167c2 100644 --- a/sceneview/src/main/java/io/github/sceneview/SceneView.kt +++ b/sceneview/src/main/java/io/github/sceneview/SceneView.kt @@ -1055,8 +1055,6 @@ open class SceneView @JvmOverloads constructor( targetPosition = targetPosition ) - - @RequiresApi(Build.VERSION_CODES.P) fun createViewNodeManager(context: Context) = ViewNode2.WindowManager(context) fun createMainLightNode(engine: Engine): LightNode = DefaultLightNode(engine) @@ -1085,4 +1083,4 @@ open class SceneView @JvmOverloads constructor( fun createCollisionSystem(view: View) = CollisionSystem(view) } -} \ No newline at end of file +} From c3419913e0bcbc3f9e845d6e40b9254449aac521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Garci=CC=81a?= Date: Tue, 12 Nov 2024 07:47:01 +0100 Subject: [PATCH 4/4] Some more SDK guards removed --- .../src/main/java/io/github/sceneview/SceneView.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sceneview/src/main/java/io/github/sceneview/SceneView.kt b/sceneview/src/main/java/io/github/sceneview/SceneView.kt index 69f167c2..a3dce6d0 100644 --- a/sceneview/src/main/java/io/github/sceneview/SceneView.kt +++ b/sceneview/src/main/java/io/github/sceneview/SceneView.kt @@ -893,9 +893,7 @@ open class SceneView @JvmOverloads constructor( private inner class LifeCycleObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - viewNodeWindowManager?.resume(this@SceneView) - } + viewNodeWindowManager?.resume(this@SceneView) // Start the drawing when the renderer is resumed. Remove and re-add the callback // to avoid getting called twice. @@ -908,15 +906,11 @@ open class SceneView @JvmOverloads constructor( override fun onPause(owner: LifecycleOwner) { Choreographer.getInstance().removeFrameCallback(frameCallback) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - viewNodeWindowManager?.pause() - } + viewNodeWindowManager?.pause() } override fun onDestroy(owner: LifecycleOwner) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - viewNodeWindowManager?.destroy() - } + viewNodeWindowManager?.destroy() destroy() } }