Skip to content

Enable B8G8R8A8 frame buffer emulation in the wrapper WSI for 32-bit games on Mali - #6

Merged
utkarshdalal merged 11 commits into
GameNative:wrapper-25from
leegao:rgba8-mali
Jul 22, 2026
Merged

Enable B8G8R8A8 frame buffer emulation in the wrapper WSI for 32-bit games on Mali#6
utkarshdalal merged 11 commits into
GameNative:wrapper-25from
leegao:rgba8-mali

Conversation

@leegao

@leegao leegao commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Investigation: leegao/bionic-vulkan-wrapper#157

Problems:

  1. 32-bit games black screen / crash during vkCreateSwapchain(KHR) on Android devices running on GKI < 5.10 with blitting due to lack of importable ion-fd textures on the B8G8R8A8 unorm/srgb format into Vulkan
  2. The MESA wrapper's WSI fails direct rendering on all Mali devices due to the lack of importable B8G8R8A8 formatted AHBs into Vulkan

This PR specifically tackles the 32-bit black screen/crash, but the design here should also enable the Direct Rendering (blit-less) path for all Mali devices as well, speeding up the rendering by eliminating one framebuffer copy per frame.

Additionally, we also allow blitting to work on win32 games for affected device by allowing VK_EXT_map_memory_placed emulation failures to be bypassed (meaning these swapchain images cannot be host-mapped anymore by the game)

See also:

  1. 32 bit directx games not working on mali g72 mp3 Pipetto-crypto/winlator#42
  2. placedaddr failing for DolphinVS.exe - mali g72 mp3 leegao/bionic-vulkan-wrapper#115

Design:

Mali's implementation of AHB supports both R8G8B8A8 and B8G8R8A8 formats when you gralloc for system memory, but its blob Vulkan driver does not support importing B8G8R8A8 buffers into Vulkan, as can be seen by calling vkGetAndroidHardwareBufferPropertiesANDROID

[004.12]           ! wsi_get_ahardware_buffer_blit_type ahardware_buffer_format_props: 	 (wrapped__wsi_get_ahardware_buffer_blit_type:58)
[004.12]           !     .format = 0 	 (wrapped__wsi_get_ahardware_buffer_blit_type:59)
[004.12]           !     .externalFormat = 47 	 (wrapped__wsi_get_ahardware_buffer_blit_type:60)
[004.12]           !     .formatFeatures = 397313 	 (wrapped__wsi_get_ahardware_buffer_blit_type:61)
[004.12]           !     .samplerYcbcrConversionComponents = 0 	 ((null):-1134620138)
[004.12]           !     .suggestedYcbcrModel = 0 	 (wrapped__wsi_get_ahardware_buffer_blit_type:63)
[004.12]           !     .suggestedYcbcrRange = 0 	 (wrapped__wsi_get_ahardware_buffer_blit_type:64)
[004.12]           !     .suggestedXChromaOffset = 1 	 (wrapped__wsi_get_ahardware_buffer_blit_type:65)
[004.12]           !     .suggestedYChromaOffset = 1 	 (wrapped__wsi_get_ahardware_buffer_blit_type:66)
[004.12]           ! wsi_get_ahardware_buffer_blit_type: Calling GetPhysicalDeviceImageFormatProperties2: format = 0 	 (wrapped__wsi_get_ahardware_buffer_blit_type:94)
[004.12]           [ERROR] dispatch->GetPhysicalDeviceImageFormatProperties2(physicalDevice: 0xb400007730836f20,pImageFormatInfo: 0x1000fe8a8,pImageFormatProperties: 0x1000fe850) failed with result: -11 (id=2709) 	 (wrapper_tramp_GetPhysicalDeviceImageFormatProperties2:2129)

Observe that there's a valid external format (47), but no vulkan importable format (0).

Instead, on these devices, only R8G8B8A8 ahb buffers are importable into Vulkan.

The TL;DR for this PR is to allocate AND present the frame buffer image in [R, G, B, A] format, but internally store color data in the swizzled [B, G, R, A] mode using VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT to trick the game/engine to still write the swizzled [B, G, R, A] data into this physically [R, G, B, A] formatted memory.


This PR specifically emulates a B8G8R8A8 frame buffer over a physical R8G8B8A8 buffer. This allows the buffer to be imported as an AHB resource into Vulkan, this enabling the fast direct rendering path.

In particular, we create the backing image as R8G8B8A8, and then rely on the WSI engine to create compatible (via the addition of the MUTABLE_FORMATS flag) B8G8R8A8 image views as the render target.

New flags:

  1. WRAPPER_EMULATE_BGRA8 is introduced that explicitly enables this behavior.
  2. WRAPPER_DONT_EMULATE_BGRA8 is a generic killswitch for this

In addition, 32bit games running on Mali will also enable this emulation paths automatically, so that by default, only 32bit games running on Mali are affected by this change.


Rendering Scenarios:

X11 expects the AHB memory to be in BGRA byte order (and will import it as a BGRA8 ahb)

VkImageView: OKAY

When DXVK creates a VkImageView with format VK_FORMAT_B8G8R8A8_UNORM (backing image is actually in [B, G, R, A] byte order):

  1. Since mutable image format is enabled, the underlying hardware just treats this as a [B, G, R, A] data even if the VkImage itself has format [R, G, B, A]
  2. The resulting memory in the swapchain/AHB remains [B, G, R, A] byte order (disguised as R,G,B,A), and X11 reads the correct [B, G, R, A]

vkCmdCopyImage(BGRA -> RGBA (emulated BGRA)): OKAY

When DXVK calls an explicit vkCmdCopyImage on a backing destination image in R8G8B8A8_UNORM (but with memory containing B, G, R, A ordered data), it just performs raw byte-for-byte copying:

  1. The source memory contains [B, G, R, A], vkCmdCopyImage copies [B, G, R, A] byte-for-byte into the destination swapchain image memory.
  2. X11 reads the correct [B, G, R, A] memory

note however that if the engine for some reason decides to render to a temporary frame buffer in RGBA mode, then copy it to what it would've originally have thought was a BGRA backend buffer, then we'll also get the wrong colors, but I can't see a single reason to do this.

vkCmdBlitImage(BGRA -> RGBA (emulated BGRA)): NEEDS SWIZZLE

When DXVK calls an explicit vkCmdBlitImage on a backing destination image in R8G8B8A8_UNORM (but with memory containing B, G, R, A ordered data), it actually swizzles B->R and R->B during the copy:

  1. The source memory contains [B, G, R, A], but vkCmdBlitImage swizzles it into [R, G, B, A] into the destination swapchain image.
  2. X11 reads the incorrect [R, G, B, A] memory

As a result, the vkCmdBlitImage case will actually require an additional swizzle (or just be translated tp a vkCmdCopyImage), as it will otherwise over-correct and switch the blue/red channels.

This is currently implemented as an error log.


Only tested on a G615, but I don't have a GKI <5.10 device)


  1. TODO(P1): we should still add an explicit kill switch to disable this feature.
  2. TODO(P1): implement blit-mode fixes for win32 games on <5.10 devices
  3. TODO(P2): implement the vkCmdBlitImage(BGRA->RGBA) -> vkCmdCopyImage pass (unclear if there are any engines that does this additional blit)
  4. TODO(P4): implement the vkCmdCopyImage(RGBA->RGBA) -> vkCmdBlitImage pass (this is almost certainly impossible, see above)

@leegao
leegao marked this pull request as ready for review July 21, 2026 17:55
@leegao
leegao force-pushed the rgba8-mali branch 2 times, most recently from c3f282f to 16f143e Compare July 22, 2026 10:52
leegao added 5 commits July 22, 2026 18:58
1. X11 requires framebuffers in bgra8 format.
2. Mali can only support direct-rendering on rgba8 format (unlike Qcom)
3. Blitting is also broken when opaque-fd is not available on pre-5.10
   GKI kernels

All of these can be solved by enabling DRI (AHB framebuffers) on Mali,
via emulating bgra8

Design sketch: allocate back buffer images backed physically by rgba8
device memory, but allow WSI to read this back as bgra8 (during
vkCreateImageView - by creating the backing images with
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT)

TODO: certain flows (direct memory writes) are still broken, and this is
still a bit brittle.
Design: check WINEPRELOADRESERVE to see if the base address is <
0xFFFFFFFF
leegao added 4 commits July 22, 2026 20:05
In particular, if EXT_map_memory_placed emulation fails for memory
backing a swapchain image, then we'll just skip it and fallback instead
of erroring.
@utkarshdalal
utkarshdalal merged commit f668997 into GameNative:wrapper-25 Jul 22, 2026
1 check passed
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

Successfully merging this pull request may close these issues.

2 participants