Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ViewNode2 rendering #579

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions sceneview/src/main/java/io/github/sceneview/Scene.kt
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,6 @@ fun rememberCameraManipulator(
}
}

@RequiresApi(Build.VERSION_CODES.P)
@Composable
fun rememberViewNodeManager(
context: Context = LocalContext.current,
Expand All @@ -605,4 +604,4 @@ private fun ScenePreview(modifier: Modifier) {
modifier = modifier
.background(Color.DarkGray)
)
}
}
16 changes: 4 additions & 12 deletions sceneview/src/main/java/io/github/sceneview/SceneView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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()
}
}
Expand Down Expand Up @@ -1055,8 +1049,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)
Expand Down Expand Up @@ -1085,4 +1077,4 @@ open class SceneView @JvmOverloads constructor(

fun createCollisionSystem(view: View) = CollisionSystem(view)
}
}
}
88 changes: 37 additions & 51 deletions sceneview/src/main/java/io/github/sceneview/node/ViewNode2.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
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.os.Build
import android.os.Handler
import android.os.Looper
import android.graphics.SurfaceTexture
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
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
Expand Down Expand Up @@ -60,21 +61,15 @@ 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,
materialLoader: MaterialLoader,
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) {
Expand All @@ -92,11 +87,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()
Expand Down Expand Up @@ -295,13 +290,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()
}

/**
Expand All @@ -320,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,
Expand All @@ -332,15 +327,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) {
Expand All @@ -351,30 +339,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)
}
}

Expand All @@ -383,7 +354,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)
Expand Down Expand Up @@ -442,3 +423,8 @@ class ViewNode2(
}
}
}


private fun Context.findActivity(): ComponentActivity? {
return generateSequence(this) { (it as? ContextWrapper)?.baseContext }.filterIsInstance<ComponentActivity>().firstOrNull()
}