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 · 9 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.

@ArcaNO93
Copy link

ArcaNO93 commented Dec 4, 2024

hey @takahirom
i also encountered this issue when using shaders
renderEffect = shader.asComposeRenderEffect()

setting it.systemProperties["robolectric.pixelCopyRenderMode"] = "hardware" does not seem to help
am i missing something here?

@takahirom
Copy link
Owner

takahirom commented Dec 4, 2024

@ArcaNO93
I suggest first checking the Robolectric's SDK version and the Robolectric version. However, if you could share a composable function that reproduces the issue, that would be greatly appreciated.

@ArcaNO93
Copy link

ArcaNO93 commented Dec 4, 2024

hey
cant really share much cause of nda
but
drawLayer(layer) causes this issue, not renderEffect (it causes different issue xD)
so we do some stuff with obtainGraphicsLayer and then pass it into canvas via drawLayer
im using the latest roborazzi (1.34.0) and robolectric (4.14.1)

@ArcaNO93
Copy link

ArcaNO93 commented Dec 4, 2024

we basically have two implementations for our skeleton
one based on renderEffect = shader.asComposeRenderEffect() -> for sdk 33+
and one for <33 based on drawLayer()

render effect does not work with sdk < 35, but this is most likely related to https://github.com/robolectric/robolectric/issues/9691

@takahirom
Copy link
Owner

Thanks. Do you mean it works if you use SDK 35?

@ArcaNO93
Copy link

ArcaNO93 commented Dec 4, 2024

i havent checked drawLayer() in sdk 35
but renderEffect works with 35 yes

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

3 participants