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

"IllegalArgumentException: Software rendering doesn't support drawRenderNode" caused by Modifier.animateItem() in compose 1.7.0-alpha06 #290

Open
alexjlockwood opened this issue Apr 4, 2024 · 3 comments

Comments

@alexjlockwood
Copy link

alexjlockwood commented Apr 4, 2024

I have a sample project (which you can download here in RoborazziSample.zip) that runs the following test:

@Test
fun lazyColumnTest() {
    composeTestRule.setContent {
        LazyColumn {
            item {
                Text(
                    modifier = Modifier.animateItem(),
                    text = "Text",
                )
            }
        }
    }
}

When I run this test, I get an exception:

java.lang.IllegalArgumentException: Software rendering doesn't support drawRenderNode

My sample project uses the following versions:

  • Compose 1.7.0-alpha06
  • Roborazzi 1.11.0
  • Robolectric 4.12.1

The issue seems to be related to Modifier.animateItem() in Compose 1.7.0-alpha06, as this test works fine if I downgrade to Compose 1.7.0-alpha05 and replace the modifier with the older Modifier.animateItemPlacement(). (If you are unfamiliar with these APIs, basically they allow for automatic fade in/out/reordering animations in a compose list).

I am uncertain of the details causing this exception, but I do notice that in the latest Compose changelog there is mention of changes to GraphicsLayer and I noticed that there's also mention of GraphicsLayer in the PR that introduced this new Modifier.animateLayer() API.

Full stack trace:

java.lang.IllegalArgumentException: Software rendering doesn't support drawRenderNode
	at android.graphics.Canvas.drawRenderNode(Canvas.java:2329)
	at androidx.compose.ui.graphics.layer.GraphicsLayerV29.draw(GraphicsLayerV29.android.kt:233)
	at androidx.compose.ui.graphics.layer.GraphicsLayer.draw$ui_graphics_release(AndroidGraphicsLayer.android.kt:510)
	at androidx.compose.ui.graphics.layer.GraphicsLayerKt.drawLayer(GraphicsLayer.kt:55)
	at androidx.compose.ui.platform.GraphicsLayerOwnerLayer.drawLayer(GraphicsLayerOwnerLayer.android.kt:107)
	at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:415)
	at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:964)
	at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:196)
	at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:68)
	at androidx.compose.foundation.DrawStretchOverscrollModifier.draw(AndroidOverscroll.android.kt:156)
	at androidx.compose.ui.node.BackwardsCompatNode.draw(BackwardsCompatNode.kt:350)
	at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:110)
	at androidx.compose.ui.node.LayoutNodeDrawScope.draw-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:89)
	at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:431)
	at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:58)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:450)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:449)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:488)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator.kt:449)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator.kt:447)
	at androidx.compose.ui.platform.RenderNodeLayer.drawLayer(RenderNodeLayer.android.kt:317)
	at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:415)
	at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:279)
	at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:68)
	at androidx.compose.foundation.lazy.LazyListItemAnimator$DisplayingDisappearingItemsNode.draw(LazyListItemAnimator.kt:447)
	at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:110)
	at androidx.compose.ui.node.LayoutNodeDrawScope.draw-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:89)
	at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:431)
	at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:58)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:450)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:449)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2408)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator.kt:449)
	at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator.kt:447)
	at androidx.compose.ui.platform.RenderNodeLayer.drawLayer(RenderNodeLayer.android.kt:317)
	at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:415)
	at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:964)
	at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:196)
	at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:428)
	at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:420)
	at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:964)
	at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1505)
	at android.view.View.draw(View.java:23892)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.robolectric.shadows.ShadowView$_View_$$Reflector21.draw(Unknown Source)
	at org.robolectric.shadows.ShadowView.draw(ShadowView.java:261)
	at android.view.View.draw(View.java)
	at android.view.View.draw(View.java:23762)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
	at android.view.View.draw(View.java:23760)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
	at android.view.View.draw(View.java:23760)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
	at android.view.View.draw(View.java:23760)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
	at android.view.View.draw(View.java:23892)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.robolectric.shadows.ShadowView$_View_$$Reflector21.draw(Unknown Source)
	at org.robolectric.shadows.ShadowView.draw(ShadowView.java:261)
	at android.view.View.draw(View.java)
	at com.android.internal.policy.DecorView.draw(DecorView.java:809)
	at org.robolectric.shadows.ShadowPixelCopy.takeScreenshot(ShadowPixelCopy.java:169)
	at org.robolectric.shadows.ShadowPixelCopy.request(ShadowPixelCopy.java:96)
	at android.view.PixelCopy.request(PixelCopy.java)
	at com.github.takahirom.roborazzi.ViewScreenshotKt.generateBitmapFromPixelCopy(ViewScreenshot.kt:204)
	at com.github.takahirom.roborazzi.ViewScreenshotKt.generateBitmapFromPixelCopy(ViewScreenshot.kt:187)
	at com.github.takahirom.roborazzi.ViewScreenshotKt.generateBitmap(ViewScreenshot.kt:124)
	at com.github.takahirom.roborazzi.ViewScreenshotKt.fetchImage(ViewScreenshot.kt:23)
	at com.github.takahirom.roborazzi.ComposeScreenshotKt.fetchImage(ComposeScreenshot.kt:59)
	at com.github.takahirom.roborazzi.RoboComponent$Compose.<init>(capture.kt:184)
	at com.github.takahirom.roborazzi.RoboComponent$Compose.<init>(capture.kt:176)
	at com.github.takahirom.roborazzi.RoborazziKt.captureRoboImage(Roborazzi.kt:277)
	at com.github.takahirom.roborazzi.RoborazziRule.runTest(RoborazziRule.kt:225)
	at com.github.takahirom.roborazzi.RoborazziRule.access$runTest(RoborazziRule.kt:27)
	at com.github.takahirom.roborazzi.RoborazziRule$apply$1.evaluate(RoborazziRule.kt:132)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54)
	at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:272)
	at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:271)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.withDisposableContent(ComposeUiTest.android.kt:505)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:333)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withComposeIdlingResource(ComposeUiTest.android.kt:385)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withComposeIdlingResource(ComposeUiTest.android.kt:219)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(ComposeUiTest.android.kt:332)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withWindowRecomposer(ComposeUiTest.android.kt:359)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withWindowRecomposer(ComposeUiTest.android.kt:219)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1.invoke(ComposeUiTest.android.kt:331)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTestCoroutines(ComposeUiTest.android.kt:372)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTestCoroutines(ComposeUiTest.android.kt:219)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1.invoke(ComposeUiTest.android.kt:330)
	at androidx.compose.ui.test.IdlingStrategy.withStrategy(IdlingStrategy.android.kt:52)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(ComposeUiTest.android.kt:329)
	at androidx.compose.ui.test.IdlingResourceRegistry.withRegistry(IdlingResourceRegistry.jvm.kt:155)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1.invoke(ComposeUiTest.android.kt:328)
	at androidx.compose.ui.test.ComposeRootRegistry.withRegistry(ComposeRootRegistry.android.kt:146)
	at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(ComposeUiTest.android.kt:327)
	at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1.evaluate(AndroidComposeTestRule.android.kt:271)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:588)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:101)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
@takahirom
Copy link
Owner

Thank you for reporting this issue. I was unable to reproduce the issue using your sample project.
image

It may or may not be related, but I'm using an Apple M1 Max, version 13.5.1.

I suggest trying to set the robolectric.screenshot.hwrdr.native property to true as a possible solution to the issue:

  init {
    val USE_HARDWARE_RENDERER_NATIVE_ENV = "robolectric.screenshot.hwrdr.native"
    System.setProperty(USE_HARDWARE_RENDERER_NATIVE_ENV, "true")
  }

@takahirom
Copy link
Owner

takahirom commented Apr 6, 2024

Sorry, I wasn't using the recordRoborazziDebug task, so I'm able to reproduce the issue. Additionally, I managed to fix the issue using the hardware rendering option. Deciding whether we can utilize it is also challenging for me though.

@takahirom
Copy link
Owner

@alexjlockwood
I was able to reproduce this issue without Roborazzi.

@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class ExampleUnitTest {
  @get:Rule
  val composeTestRule = createAndroidComposeRule<ComponentActivity>()

  @Test
  fun lazyColumnTest() {
    composeTestRule.setContent {
      LazyColumn {
        item {
          Text(
            modifier = Modifier.animateItem(),
            text = "Text",
          )
        }
      }
    }
    val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    PixelCopy.request(
      composeTestRule.activity.window, bitmap, PixelCopy.OnPixelCopyFinishedListener {
      }, Handler(Looper.getMainLooper())
    )
  }
}

The issue likely originates not from Roborazzi but from Robolectric, as Robolectric may need to incorporate the drawRenderNode() method in its shadow. Using hardware rendering serves as a potential workaround; should this be the advised approach for resolving the issue, we might need to implement hardware rendering.
Therefore, if you could report this issue to Robolectric, it would be greatly appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants