Skip to content

Gpu interop with ImportImage doesn't preserve image layout and loses texture data on some devices/drivers #21721

Description

@nateglasser

Describe the bug

When using the Vulkan backend and importing an image with ICompositionGpuInterop.ImportImage, the layout of the receiving image is set to VK_IMAGE_LAYOUT_UNDEFINED:

initialLayout = VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED,

On at least some Nvidia hardware (GTX 1080 with 582.66 drivers, Windows 10) this causes the texture data to get lost after the first layout transition to VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL on the interop side:

TransitionLayout(VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VkAccessFlags.VK_ACCESS_TRANSFER_READ_BIT);

Presumably the driver is discarding the old texture data as it is allowed to do for an undefined initial layout. This is visible in the GpuInterop demo, where the first frame doesn't render correctly and only a white background is displayed:

Image

As a quick hack, if I change the initial layout in the VulkanImage constructor to be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL that the VulkanSwapchainImage first gets transitioned to, then I get the expected image:

Image

To Reproduce

Run the GpuInterop demo on select Nvidia hardware (GTX 1080 with 582.66 drivers, Windows 10).

Note I have not observed this behavior on other devices (older Intel and newer AMD integrated GPUs). For these, the demo works as expected.

Expected behavior

The GpuInterop demo should render the teapot cleanly on the first frame on all supported devices.

Avalonia version

12.0.5

OS

Windows

Additional context

A proper fix requires a way to pass the layout of the exported image to ImportImage. The simplest place is the PlatformGraphicsExternalMemory struct that takes in other image properties. It could be a simple uint that gets casted to the proper enum on the receiving end for Vulkan. However, The flag wouldn't make sense for OpenGL interop, and I am not sure about Metal, so I don't know if this is the best long-term solution.

Also, and I am not completely sure here, but a fully robust solution might also need to respect the queue family flags that are currently ignored (though this doesn't seem to matter on any device I've tried):

srcQueueFamilyIndex = VulkanHelpers.QueueFamilyIgnored,
dstQueueFamilyIndex = VulkanHelpers.QueueFamilyIgnored,

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions