Compare commits

...
19 changed files with 668 additions and 25 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# Patch context includes the indentation and blank lines of the source being patched.
tools/gamescope/patches/*.patch -whitespace
tools/panvk/patches/*.patch -whitespace
+8 -1
View File
@@ -18,6 +18,7 @@ on:
workflow_dispatch:
permissions:
actions: read
contents: read
pull-requests: write
@@ -190,7 +191,13 @@ jobs:
done
if [ -f tools/gamescope/release.env ]; then
. tools/gamescope/release.env
gh release download "${GAMESCOPE_TAG}" -R "${GITHUB_REPOSITORY}" -p gamescope.tzst -O gamescope.tzst
if [ -n "${GAMESCOPE_ARTIFACT_RUN:-}" ]; then
gh run download "${GAMESCOPE_ARTIFACT_RUN}" -R "${GITHUB_REPOSITORY}" -n gamescope-patched -D gamescope-artifact
mv gamescope-artifact/gamescope.tzst gamescope.tzst
rm -r gamescope-artifact
else
gh release download "${GAMESCOPE_TAG}" -R "${GITHUB_REPOSITORY}" -p gamescope.tzst -O gamescope.tzst
fi
echo "${GAMESCOPE_SHA256} gamescope.tzst" | sha256sum -c -
tar --use-compress-program=unzstd -xf gamescope.tzst -C "$D"
test -f "$D/usr/local/bin/gamescope"
@@ -3478,7 +3478,8 @@ int banner_wayland_run(void) {
/* libdecor binds wl_output at 4; a lower version is a protocol error for the client. */
wl_global_create(display, &wl_output_interface, 4, NULL, bind_output);
wl_global_create(display, &xdg_wm_base_interface, 1, NULL, bind_xdg_wm_base);
wl_global_create(display, &zwp_linux_dmabuf_v1_interface, 4, NULL, bind_dmabuf);
if (vkp_dmabuf_supported())
wl_global_create(display, &zwp_linux_dmabuf_v1_interface, 4, NULL, bind_dmabuf);
/* gamescope's Wayland backend refuses a seat older than 8. */
wl_global_create(display, &wl_seat_interface, 9, NULL, bind_seat);
wl_global_create(display, &banner_desktop_v1_interface, 1, NULL, bind_desktop);
+1 -1
View File
@@ -9,7 +9,7 @@
#define VK_USE_PLATFORM_ANDROID_KHR
#include <vulkan/vulkan.h>
#define VK_GLOBAL_FUNCS(X) X(CreateInstance) X(EnumerateInstanceExtensionProperties)
#define VK_GLOBAL_FUNCS(X) X(CreateInstance) X(EnumerateInstanceExtensionProperties) X(EnumerateInstanceVersion)
#define VK_INSTANCE_FUNCS(X) \
X(EnumeratePhysicalDevices) X(GetPhysicalDeviceProperties) \
+24 -13
View File
@@ -40,6 +40,7 @@ struct vkp_image {
static ANativeWindow *g_window; /* the window frames go to; compositor thread only */
static char *g_driver_path, *g_library_name, *g_native_lib_dir;
static int g_dev_state; /* 0 = not yet, 1 = ok, -1 = failed, -2 = lost (VK_ERROR_DEVICE_LOST) */
static int g_dmabuf_import;
static VkInstance g_inst;
static VkPhysicalDevice g_pd;
static VkDevice g_dev;
@@ -411,11 +412,13 @@ static int dev_init(void) {
"generation %s)", g_colorspace_ext ? "enables" : "has no",
g_colorspace_ext ? "is possible where the screen surface offers one" : "is not possible: tone-mapped instead");
}
/* 1.3 like the X11 renderer's instance: the frame-generation probe (framegen_engine.cpp)
* queries VkPhysicalDeviceVulkan12Features, and a 1.1 instance may be answered as 1.1. */
/* Prefer 1.3 for the frame-generation feature probe, but respect older system loaders. */
uint32_t loader_api = VK_API_VERSION_1_0;
if (g_vk.EnumerateInstanceVersion)
g_vk.EnumerateInstanceVersion(&loader_api);
VkApplicationInfo app = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "banner-wayland-present",
.apiVersion = VK_API_VERSION_1_3};
.apiVersion = loader_api < VK_API_VERSION_1_3 ? loader_api : VK_API_VERSION_1_3};
VkInstanceCreateInfo ici = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app,
.enabledExtensionCount = n_inst_exts,
@@ -450,18 +453,25 @@ static int dev_init(void) {
}
g_vk.GetPhysicalDeviceMemoryProperties(g_pd, &g_memprops);
/* Verify the dmabuf-import extensions are present, and log any that are missing. */
const char *dev_exts[7] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, "VK_KHR_external_memory_fd",
"VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier",
"VK_KHR_image_format_list", NULL, NULL};
uint32_t n_dev_exts = 5;
/* Shared-memory clients need only a swapchain. Advertise dma-buf import only when its
* complete extension set is available, rather than failing device creation on older GPUs. */
const char *import_exts[] = {"VK_KHR_external_memory_fd", "VK_EXT_external_memory_dma_buf",
"VK_EXT_image_drm_format_modifier", "VK_KHR_image_format_list"};
const char *dev_exts[7] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
uint32_t n_dev_exts = 1;
uint32_t ne = 0;
g_vk.EnumerateDeviceExtensionProperties(g_pd, NULL, &ne, NULL);
VkExtensionProperties *exts = calloc(ne ? ne : 1, sizeof(*exts));
g_vk.EnumerateDeviceExtensionProperties(g_pd, NULL, &ne, exts);
for (unsigned i = 0; i < 5; i++)
if (!has_ext(exts, ne, dev_exts[i]))
LOGE("present: driver MISSING %s (dmabuf import will fail)", dev_exts[i]);
g_dmabuf_import = 1;
for (unsigned i = 0; i < sizeof(import_exts) / sizeof(import_exts[0]); i++) {
if (has_ext(exts, ne, import_exts[i]))
dev_exts[n_dev_exts++] = import_exts[i];
else {
g_dmabuf_import = 0;
banner_log("gpu", "driver has no %s; shared-memory clients remain available", import_exts[i]);
}
}
/* HDR sessions only: the HDR10 swapchain (frame generation) can carry the game's metadata. */
int want_hdr_md = 0;
if (banner_color_requested()) {
@@ -538,6 +548,7 @@ static int dev_init(void) {
}
int vkp_ready(void) { return dev_init(); }
int vkp_dmabuf_supported(void) { return dev_init() == 0 && g_dmabuf_import; }
static int swap_init_locked(void);
@@ -899,7 +910,7 @@ static int modifier_importable(VkFormat fmt, uint64_t modifier, VkImageUsageFlag
}
int vkp_dmabuf_modifiers(uint32_t drm_format, uint64_t *out, int max) {
if (max <= 0 || dev_init() != 0 || !g_vk.GetPhysicalDeviceFormatProperties2) return 0;
if (max <= 0 || !vkp_dmabuf_supported() || !g_vk.GetPhysicalDeviceFormatProperties2) return 0;
VkFormat fmt = drm_to_vk(drm_format);
VkDrmFormatModifierPropertiesListEXT list = {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT};
@@ -941,7 +952,7 @@ int vkp_image_is_dmabuf(const struct vkp_image *img) { return img && img->dmabuf
struct vkp_image *vkp_image_import_dmabuf(int fd, uint32_t drm_format, uint64_t modifier, int w, int h,
uint32_t stride, uint32_t offset, int as_blit_dst) {
if (modifier == MOD_INVALID || w <= 0 || h <= 0) return NULL;
if (dev_init() != 0) return NULL;
if (!vkp_dmabuf_supported()) return NULL;
struct vkp_image *img = calloc(1, sizeof(*img));
if (!img) return NULL;
@@ -87,6 +87,8 @@ const char *vkp_gpu_name(void);
// 0 if the renderer can create images (device up), -1 otherwise.
int vkp_ready(void);
/* Whether the selected driver supports the complete dma-buf import extension set. */
int vkp_dmabuf_supported(void);
/* Whether an output window is attached (or requested); without one vkp_render() draws nothing. */
int vkp_has_window(void);
/* 1 once the Vulkan device was lost: nothing is presented any more (the session must restart). */
@@ -337,6 +337,10 @@ public final class TurnipDriver {
if (forced.startsWith("a8")) return DRIVER_A8XX;
if (forced.startsWith("a7")) return DRIVER_A7XX;
}
if (!com.droiddeck.launcher.core.DeviceSupport.INSTANCE.adreno()) {
Log.i(TAG, "non-Adreno GPU: using system Vulkan for the compositor");
return null;
}
String model = gpuModel();
Log.i(TAG, "gpu model: " + (model == null ? "unknown" : model));
if (model != null) {
+72
View File
@@ -0,0 +1,72 @@
# Mali software Steam experiment
This branch adds an opt-in CPU rendering path for Steam and Gamescope. Android's compositor selects system Vulkan automatically on non-Adreno hardware; explicit custom driver choices are retained. Linux uses Lavapipe for Gamescope and llvmpipe/software SDL for Steam. This is a UI compatibility fallback, not proof of accelerated game support.
Set `/sdcard/Download/droiddeck-env` to:
```sh
BL_STEAM_SOFTWARE=1
```
Launch **Steam → Play Steam**. The session selects Gamescope's SDL Wayland Vulkan swapchain, which can present Lavapipe frames through `wl_shm`. Gamescope's native Wayland backend requires exportable dma-bufs even for its blank texture. The new patch keeps locally sampled textures, including cursors and shared-memory client frames, private on swapchain backends and makes `VK_KHR_present_id`/`VK_KHR_present_wait` optional, using the existing timer pacing when they are unavailable. Drivers that support both features retain the present-wait thread. Shared-memory pixels are uploaded on every commit because clients reuse their buffers; the private textures are marked ready after the synchronous copy, avoiding an invalid dma-buf fence lookup.
Steam's embedded browser disables GPU rendering, and the native client uses the SDL software renderer and X11. The performance overlay is disabled in this mode.
## Building this branch
This experimental branch pins native CI run [37146942306](https://github.com/Droid-Deck/DroidDeck/actions/runs/37146942306) and its bundle checksum in `tools/gamescope/release.env`. Both the APK workflow and ordinary local build fetch and verify that artifact. No component release is published.
```sh
DROIDDECK_BUILD_VARIANT=debug tools/build_local.sh
```
To test a different successful native build, download its **gamescope-patched** artifact and verify the accompanying checksum:
```sh
gh run download <run-id> -R Droid-Deck/DroidDeck -n gamescope-patched -D /tmp/mali-gamescope
(cd /tmp/mali-gamescope && shasum -a 256 -c gamescope.tzst.sha256)
DROIDDECK_GAMESCOPE_BUNDLE=/tmp/mali-gamescope/gamescope.tzst \
DROIDDECK_BUILD_VARIANT=debug tools/build_local.sh
```
`DROIDDECK_GAMESCOPE_BUNDLE` overrides the pin for that local build. Before shipping, publish the native component and replace the temporary artifact pin with its release tag and checksum. CI artifacts have limited retention; this branch is an experiment, not a permanent release source.
The desktop path can also use the software Steam settings. On the tested G52 device, its labwc renderer requires `/sdcard/Download/droiddeck-wlr-renderer` containing `pixman`. Add `BL_DESKTOP_STEAM=1` only to start Steam automatically on the desktop.
## Device validation
Validated 2026-10-03 on the wired Unisoc T618 / Mali-G52 MC2 device (`RGB05001607258`, Android 12, kernel 5.4, 4 KiB pages). **Steam → Play Steam reaches the Big Picture QR sign-in screen inside Gamescope with only `BL_STEAM_SOFTWARE=1`.** The selected Deck mode was retained: the client command includes `-gamepadui -steamdeck -steamos3`. A test string was entered and cleared in the account field without submitting it; no account was signed in during that initial test. The desktop and desktop Steam QR screen also worked in the earlier test.
- Native bundle SHA-256: `22df550d60fcd62e351790eb775ee857e037fcd49fcc23a5bfa4638377e6b4b7`; the installed Gamescope binary matches the bundle (`41a067202e94af16b6f09b819208450e667dcc709480a2b97405d59a0e93cb69`).
- Signed debug APK SHA-256: `7a19928dc93e72c81b553c62515f3881a249eb037a81fbb6296e05bb9edd23b3`, built from commit `e427c08` using the ordinary artifact-pin path.
- Device logs: `Download/DroidDeck/2026-10-03-10-steam/`. They identify Lavapipe, SDL swapchain creation, timer pacing, and the outer window `Steam Big Picture Mode` drawing through the system Mali-G52 presenter.
- The native ARM build, full local Android build, bundle checksum, installed binary identity, shell syntax, workflow YAML, and diff checks passed. App data and the primary checkout were preserved.
The shared-memory commit fixes are both needed: without a ready fence, commits never display; with the old buffer memoization, a reused buffer keeps displaying its first copied pixels (the boot logo or black) despite ongoing frame delivery. Refreshing the copy also makes the account-field edits visible.
## Geometry Wars gameplay follow-up
After the user signed in, Geometry Wars: Retro Evolved (`8400`) was downloaded through the native Steam client on the same device and APK. Steam verified all seven files (66,019,943 bytes, build `251921`, depot `8401`). The first install confirmation crashed Steam with exit 139 while it queued additional compatibility runtimes. Restarting the session retained the install request; the download then completed successfully. The cause of that client crash is unverified.
The game launches through `bannerlator-proton-arm64` using the installed Proton Experimental (ARM64), Wine and DXVK, with the guest still using Lavapipe. It reached the animated menu and an active round; the on-screen A button starts a round, the left stick moves the ship, the right stick fires in different directions, and Start pauses it.
The first run was cropped and repeatedly recreated its swapchain. Turning off **Stretch games to fill** did not resolve the cropping. A game-specific Steam launch option did:
```sh
DISABLE_GAMESCOPE_WSI=1 %command%
```
That disables the Vulkan Xwayland-bypass layer for the game while keeping the outer Gamescope session. The subsequent menu and gameplay show the whole 4:3 image, including the high-score column and all menu entries. Fullscreen stretching remains off on the test device, and on-screen controls are set to **Always**. These are runtime settings; the branch does not automatically apply this per-game workaround.
The Android presenter reported roughly 3–5 displayed frames per second in representative gameplay samples. Movement and shooting work, but CPU-rendered performance is insufficient for normal play. Audio was not verified. The game was left paused with scrcpy running. Follow-up logs are in `Download/DroidDeck/2026-10-03-11-steam/`; screenshots and raw logs are retained locally, outside Git.
A guest glibc Mali ICD and compatible image transport remain necessary for hardware acceleration. This patch does not install Steam-ARM's feature layers or validate other Mali generations; the gameplay result establishes one Windows D3D9 title through software rendering, not general Proton/DXVK compatibility. The Android presenter also caps its requested Vulkan API to the loader's supported version and withholds dma-buf advertisement when the complete import extension set is missing; that missing-extension branch has not been exercised on this G52 driver.
## GPU acceleration follow-up
The [kbase/JM driver lab](../../tools/panvk/README.md) now includes a pinned
source build and three fixes validated on this wired G52. GPU compute,
offscreen draw/readback, indexed draw and visible VKCube presentation work.
Geometry Wars remains blocked: the bundled DXVK requires an unsupported
feature, while an isolated older DXVK reaches rendering and loses the GPU
device. The software Steam/Gamescope settings remain the recovery path.
+21 -2
View File
@@ -207,10 +207,29 @@ cached() {
echo "${out}"
}
# Experimental branches can pin a CI artifact until their native component is released.
download_gamescope_artifact() (
set -e
local artifact_dir
artifact_dir=$(mktemp -d)
trap 'rm -rf "$artifact_dir"' EXIT
gh run download "$1" -R "$2" -n gamescope-patched -D "$artifact_dir"
cp "$artifact_dir/gamescope.tzst" "$out"
)
if [[ -f "${repo_root}/tools/gamescope/release.env" ]]; then
. "${repo_root}/tools/gamescope/release.env"
gamescope_archive=$(cached "${GAMESCOPE_SHA256}" gamescope.tzst \
bash -c 'gh release download "$0" -R "$1" -p gamescope.tzst -O "$out"' "${GAMESCOPE_TAG}" "${github_repo}")
if [[ -n "${DROIDDECK_GAMESCOPE_BUNDLE:-}" ]]; then
gamescope_archive=${DROIDDECK_GAMESCOPE_BUNDLE}
test -f "${gamescope_archive}"
echo "Using local Gamescope bundle: ${gamescope_archive}"
elif [[ -n "${GAMESCOPE_ARTIFACT_RUN:-}" ]]; then
gamescope_archive=$(cached "${GAMESCOPE_SHA256}" gamescope.tzst \
download_gamescope_artifact "${GAMESCOPE_ARTIFACT_RUN}" "${github_repo}")
else
gamescope_archive=$(cached "${GAMESCOPE_SHA256}" gamescope.tzst \
bash -c 'gh release download "$0" -R "$1" -p gamescope.tzst -O "$out"' "${GAMESCOPE_TAG}" "${github_repo}")
fi
zstd -dc "${gamescope_archive}" | tar -xf - -C "${linuxfs_dir}"
test -f "${linuxfs_dir}/usr/local/bin/gamescope"
fi
@@ -0,0 +1,132 @@
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
index 54e4efc..88a6353 100644
--- a/src/rendervulkan.cpp
+++ b/src/rendervulkan.cpp
@@ -450,7 +450,13 @@ bool CVulkanDevice::createDevice()
bool hasDrmProps = vulkan_has_drm_props();
bool supportsForeignQueue = false;
bool supportsHDRMetadata = false;
+ bool supportsPresentId = false;
+ bool supportsPresentWait = false;
for (const auto& ext : m_supportedExts) {
+ if ( strcmp(ext.extensionName, VK_KHR_PRESENT_ID_EXTENSION_NAME) == 0 )
+ supportsPresentId = true;
+ if ( strcmp(ext.extensionName, VK_KHR_PRESENT_WAIT_EXTENSION_NAME) == 0 )
+ supportsPresentWait = true;
if ( strcmp(ext.extensionName, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME) == 0 )
m_bSupportsModifiers = true;
@@ -518,6 +524,25 @@ bool CVulkanDevice::createDevice()
m_bSupportsModifiers = false;
}
+ if ( supportsPresentId && supportsPresentWait )
+ {
+ VkPhysicalDevicePresentWaitFeaturesKHR waitFeatures = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR,
+ };
+ VkPhysicalDevicePresentIdFeaturesKHR idFeatures = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR,
+ .pNext = &waitFeatures,
+ };
+ VkPhysicalDeviceFeatures2 features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ .pNext = &idFeatures,
+ };
+ vk.GetPhysicalDeviceFeatures2( physDev(), &features );
+ m_bSupportsPresentWait = idFeatures.presentId && waitFeatures.presentWait;
+ }
+ if ( GetBackend()->UsesVulkanSwapchain() && !m_bSupportsPresentWait )
+ vk_log.infof( "Present ID/wait unavailable; using timer-based pacing." );
+
{
VkPhysicalDeviceVulkan12Features vulkan12Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
@@ -570,8 +595,11 @@ bool CVulkanDevice::createDevice()
enabledExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME );
enabledExtensions.push_back( VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME );
- enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME );
- enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME );
+ if ( m_bSupportsPresentWait )
+ {
+ enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME );
+ enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME );
+ }
if ( supportsHDRMetadata )
enabledExtensions.push_back( VK_EXT_HDR_METADATA_EXTENSION_NAME );
@@ -646,7 +674,7 @@ bool CVulkanDevice::createDevice()
VkPhysicalDeviceFeatures2 features2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
- .pNext = &presentIdFeatures,
+ .pNext = m_bSupportsPresentWait ? static_cast<void *>( &presentIdFeatures ) : static_cast<void *>( &features13 ),
.features = {
.shaderInt16 = m_bSupportsFp16,
},
@@ -2082,6 +2110,12 @@ static VkImageViewType VulkanImageTypeToViewType(VkImageType type)
bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t depth, uint32_t drmFormat, createFlags flags, wlr_dmabuf_attributes *pDMA /* = nullptr */, uint32_t contentWidth /* = 0 */, uint32_t contentHeight /* = 0 */, CVulkanTexture *pExistingImageToReuseMemory, gamescope::OwningRc<gamescope::IBackendFb> pBackendFb )
{
+ // Swapchain backends composite into their own images; textures are never scanned out.
+ // Keep explicit exports and dma-buf imports, but do not require a DRM export for local
+ // cursor, blank, or wl_shm textures (Lavapipe has no DRM allocator).
+ if ( GetBackend()->UsesVulkanSwapchain() )
+ flags.bFlippable = false;
+
m_pBackendFb = std::move( pBackendFb );
m_drmFormat = drmFormat;
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
@@ -3086,7 +3120,7 @@ void vulkan_present_to_window( void )
VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
- .pNext = &presentIdInfo,
+ .pNext = g_device.supportsPresentWait() ? &presentIdInfo : nullptr,
.swapchainCount = 1,
.pSwapchains = &g_output.swapChain,
.pImageIndices = &g_output.nOutImage,
@@ -3094,8 +3128,11 @@ void vulkan_present_to_window( void )
if ( g_device.vk.QueuePresentKHR( g_device.queue(), &presentInfo ) == VK_SUCCESS )
{
- g_currentPresentWaitId = presentId;
- g_currentPresentWaitId.notify_all();
+ if ( g_device.supportsPresentWait() )
+ {
+ g_currentPresentWaitId = presentId;
+ g_currentPresentWaitId.notify_all();
+ }
}
else
vulkan_remake_swapchain();
@@ -3662,7 +3699,7 @@ bool vulkan_init( VkInstance instance, VkSurfaceKHR surface )
if (!init_nis_data())
return false;
- if ( GetBackend()->UsesVulkanSwapchain() )
+ if ( GetBackend()->UsesVulkanSwapchain() && g_device.supportsPresentWait() )
{
std::thread present_wait_thread( present_wait_thread_func );
present_wait_thread.detach();
diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp
index fa519db..91f7793 100644
--- a/src/rendervulkan.hpp
+++ b/src/rendervulkan.hpp
@@ -862,6 +862,7 @@ public:
inline VkPipelineLayout pipelineLayout() {return m_pipelineLayout;}
inline int drmRenderFd() {return m_drmRendererFd;}
inline bool supportsModifiers() {return m_bSupportsModifiers;}
+ inline bool supportsPresentWait() {return m_bSupportsPresentWait;}
inline bool hasDrmPrimaryDevId() {return m_bHasDrmPrimaryDevId;}
inline dev_t primaryDevId() {return m_drmPrimaryDevId;}
inline bool supportsFp16() {return m_bSupportsFp16;}
@@ -930,6 +931,7 @@ protected:
uint32_t m_uVendorID = 0;
bool m_bHasDrmPrimaryDevId = false;
bool m_bSupportsModifiers = false;
+ bool m_bSupportsPresentWait = false;
bool m_bInitialized = false;
@@ -0,0 +1,56 @@
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index 18b799e..26cf64e 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -1850,19 +1850,20 @@ import_commit (
commit->fifo = false;
}
- if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
- {
- // Going from OwningRc -> Rc now.
- commit->vulkanTex = pTexture;
- return commit;
- }
-
struct wlr_dmabuf_attributes dmabuf = {0};
+ const bool bHasDmabuf = wlr_buffer_get_dmabuf( buf, &dmabuf );
gamescope::OwningRc<gamescope::IBackendFb> pBackendFb;
- if ( wlr_buffer_get_dmabuf( buf, &dmabuf ) )
+ if ( bHasDmabuf )
{
+ if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
+ {
+ commit->vulkanTex = pTexture;
+ return commit;
+ }
pBackendFb = GetBackend()->ImportDmabufToBackend( &dmabuf );
}
+ // A dma-buf image aliases its backing storage. A wl_shm texture is a private copy:
+ // upload each commit, since clients reuse the same buffer with different pixels.
gamescope::OwningRc<CVulkanTexture> pOwnedTexture = vulkan_create_texture_from_wlr_buffer( buf, std::move( pBackendFb ) );
@@ -1873,7 +1874,8 @@ import_commit (
commit->vulkanTex = pOwnedTexture;
- s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
+ if ( bHasDmabuf )
+ s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
return commit;
}
@@ -8504,6 +8506,12 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
{
fence = dup( dmabuf.fd[0] );
}
+ else if ( !newCommit->vulkanTex->externalImage() )
+ {
+ // wl_shm uploads wait for their copy to finish in vulkan_create_texture_from_wlr_buffer.
+ // A private swapchain texture has no dma-buf to poll; it is already ready.
+ bKnownReady = true;
+ }
else
{
fence = newCommit->vulkanTex->memoryFence();
+4 -1
View File
@@ -1,2 +1,5 @@
# Experimental branch pin: build the tested native patch without publishing a component release.
# Before shipping, publish a Gamescope release and remove GAMESCOPE_ARTIFACT_RUN.
GAMESCOPE_ARTIFACT_RUN=37146942306
GAMESCOPE_SHA256=22df550d60fcd62e351790eb775ee857e037fcd49fcc23a5bfa4638377e6b4b7
GAMESCOPE_TAG=gamescope-3.16.29-p5
GAMESCOPE_SHA256=188144e5564e020d0bc485a1f4f90e1c5fd61d0d1d6dd29db21e8b019036a5c6
@@ -73,6 +73,15 @@ if [ -n "${BL_VK_DRIVER:-}" ]; then
fi
unset bl_icd_lib
fi
# Compatibility mode: the Android presenter still uses its selected Vulkan driver, while the
# Linux session renders on the CPU. Gamescope's SDL swapchain can carry these frames as wl_shm;
# its Wayland backend requires exportable dma-bufs even for its dummy black texture.
if [ "${BL_STEAM_SOFTWARE:-0}" = 1 ]; then
export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json
export VK_ICD_FILENAMES=$VK_DRIVER_FILES
export LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe MESA_LOADER_DRIVER_OVERRIDE=swrast
echo "== linux graphics: software rendering requested (Lavapipe/llvmpipe)"
fi
# The app's gamescope build (tools/gamescope) asks for realtime-priority Vulkan queues on this
# switch instead of on CAP_SYS_NICE, which proot never has; it falls back to normal priority
# where the driver refuses, and the runtime's own build ignores it. On KGSL Turnip (Mesa 26.2) it
@@ -148,7 +157,13 @@ if [ -z "${BL_INSIDE:-}" ]; then
echo "== gamescope: games at ${nested}x${height} stretched to the ${width}x${height} output"
fi
fi
args=(--backend wayland --expose-wayland -f -W "$width" -H "$height" -w "$nested" -h "$height")
backend=wayland
if [ "${BL_STEAM_SOFTWARE:-0}" = 1 ]; then
backend=sdl
export SDL_VIDEODRIVER=wayland
echo "== gamescope: SDL Wayland swapchain for software rendering"
fi
args=(--backend "$backend" --expose-wayland -f -W "$width" -H "$height" -w "$nested" -h "$height")
[ "$nested" != "$width" ] && args+=(-S stretch)
# -r is what gamescope advertises as the refresh rate, and what a game reads as the display's.
# Left unset it defaults to 60, so a 120 Hz panel offers only 60 Hz in game settings. The FPS
@@ -179,7 +194,8 @@ if [ -z "${BL_INSIDE:-}" ]; then
# MANGOHUD_CONFIGFILE. The apk stages Valve's aarch64 build (tools/mangoapp) - without it the menu
# still offers the levels and nothing appears. Starts hidden until the client writes a level.
# BL_MANGOAPP=0 turns it off (one community build found Valve's mangoapp crashing on Turnip).
if [ "$mode" = steam ] && [ "${BL_STEAMDECK:-0}" = 1 ] && [ "${BL_MANGOAPP:-1}" = 1 ] \
if [ "$mode" = steam ] && [ "${BL_STEAM_SOFTWARE:-0}" != 1 ] \
&& [ "${BL_STEAMDECK:-0}" = 1 ] && [ "${BL_MANGOAPP:-1}" = 1 ] \
&& [ "${BL_STEAM_UI:-}" != desktop ] && command -v mangoapp >/dev/null 2>&1; then
MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)
echo no_display > "$MANGOHUD_CONFIGFILE"
@@ -639,6 +655,15 @@ BUSCONF
# desktop's launchers ask for; gamescope shows one window at a time and the game takes over.
_bl_ui=(-gamepadui)
[ "${BL_STEAM_UI:-}" = desktop ] && _bl_ui=()
# Software compatibility mode: CEF and Steam use CPU frames through Xwayland,
# inside either the desktop or the software Gamescope session.
_bl_cef=(-cef-force-gpu -cef-use-gl=angle -cef-use-angle=vulkan)
if [ "${BL_STEAM_SOFTWARE:-0}" = 1 ]; then
_bl_cef=(-cef-disable-gpu -cef-disable-gpu-compositing)
export LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe
export MESA_LOADER_DRIVER_OVERRIDE=swrast SDL_RENDER_DRIVER=software SDL_VIDEODRIVER=x11
echo "== steam: software rendering requested"
fi
# Deck mode (BL_STEAMDECK=1, the Steam page's Client settings): -steamdeck is the flag that puts
# the Quick Access Menu and the Deck settings pages on screen; the QAM's performance overlay
# controls stay greyed out without -steamos3, as SteamOS and Armada launch it. -steamos3 makes
@@ -698,7 +723,7 @@ BUSCONF
done
) &
fi
echo "== steam args: ${_bl_ui[*]} -clientbeta $steam_channel -no-cef-sandbox -cef-force-gpu -cef-ozone-platform=x11 -cef-use-gl=angle -cef-use-angle=vulkan $*"
echo "== steam args: ${_bl_ui[*]} -clientbeta $steam_channel -no-cef-sandbox -cef-ozone-platform=x11 ${_bl_cef[*]} $*"
# An update that cannot install asks for a restart (rc=42) every time; the count is capped so a
# session can never again loop in silence, which reads as a menu opening and closing by itself.
steam_updates=0
@@ -745,8 +770,7 @@ BUSCONF
fi
step "starting the Steam client${BL_STEAM_UI:+ ($BL_STEAM_UI UI)}"
"$steam_root/steamrtarm64/steam" "${_bl_ui[@]}" -clientbeta "$steam_channel" \
-no-cef-sandbox -cef-force-gpu -cef-ozone-platform=x11 \
-cef-use-gl=angle -cef-use-angle=vulkan "$@"
-no-cef-sandbox -cef-ozone-platform=x11 "${_bl_cef[@]}" "$@"
rc=$?
echo "== steam exited rc=$rc"
collect_steam_logs
@@ -13,6 +13,7 @@
# On a software desktop (pixman: the GPU renderers failed on this device) there is no dma-buf, so a
# game - or the client's own Vulkan window - would die at its first frame. There the request goes
# to the app, which ends the desktop and starts a Steam session under gamescope in its place.
# BL_STEAM_SOFTWARE=1 opts into a CPU-rendered client on that desktop for compatibility testing.
set -u
ui=${1:-desktop}
target=${2:-}
@@ -28,7 +29,8 @@ esac
steam_root=${XDG_DATA_HOME:-${HOME:-/root}/.local/share}/Steam
client=$steam_root/steamrtarm64/steam
if [ "${BL_DESKTOP:-}" = 1 ] && [ -n "${WLR_RENDERER:-}" ] && [ "$WLR_RENDERER" != pixman ]; then
if [ "${BL_DESKTOP:-}" = 1 ] && [ -n "${WLR_RENDERER:-}" ] \
&& { [ "$WLR_RENDERER" != pixman ] || [ "${BL_STEAM_SOFTWARE:-0}" = 1 ]; }; then
log=${BL_DEBUG_DIR:-$HOME/.cache/droiddeck}/steam-desktop.log
mkdir -p "$(dirname "$log")" 2>/dev/null
if pgrep -f 'steamrtarm64/steam( |$)' >/dev/null 2>&1 && [ -x "$client" ]; then
+118
View File
@@ -0,0 +1,118 @@
# Experimental Mali kbase/JM driver
This lab builds the Linux Vulkan ICD from
[FristOneRR-Panvk-Source](https://github.com/FristOneRR-Admin/FristOneRR-Panvk-Source)
at `efd07bab401b263a9ac114560ecdb9a2934f5d43`. The fork includes the older Job
Manager submission path needed by Mali-G52. It talks to `/dev/mali0` through
the proprietary kbase kernel interface; upstream DRM-only PanVK cannot use
that device node.
The local patches make Android `liblog` conditional, fix the JM atom ABI,
and withhold placed mapping on kbase.
The original fork padded its v2 layout to 64 bytes, which this kernel interprets
as v3 with an eight-byte sequence-number prefix. Jobs arrived under atom 0 and
timed out after a configuration fault. The patch submits the actual 56-byte v2
structure on JM 11.19 and later (48 bytes on older JM), as specified in
[Arm's kbase UAPI header](https://nest-open-source.googlesource.com/manifest_repos/mali-driver/+/0f8397eced2de6bc649a9cc32d0fae77a1dc34dc/bifrost/r44p0/kernel/include/uapi/gpu/arm/midgard/jm/mali_base_jm_kernel.h).
Build inside an AArch64 glibc environment with Clang/LLVM 22, Meson, Ninja,
libclc, SPIRV-Tools, SPIRV-LLVM-Translator, Vulkan headers, Python Mako/YAML,
and the X11/Wayland development dependencies installed. The tested DroidDeck
runtime already supplied several of these dependencies. Avoid installing an
entire development toolchain into a user's runtime by default.
```sh
git clone https://github.com/FristOneRR-Admin/FristOneRR-Panvk-Source.git panvk-src
git -C panvk-src checkout efd07bab401b263a9ac114560ecdb9a2934f5d43
JOBS=2 tools/panvk/build-in-runtime.sh "$PWD/panvk-src" "$PWD/panvk-build"
```
The build stays under `panvk-build`; it does not install or replace a global
ICD. Its manifest selects the newly built driver explicitly:
```sh
PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1 PANVK_KBASE_DRI3=0 \
VK_DRIVER_FILES="$PWD/panvk-build/panvk-g52-icd.json" \
VK_ICD_FILENAMES="$PWD/panvk-build/panvk-g52-icd.json" \
vulkaninfo --summary
```
To launch the tested GPU cube from the Mac against this device's lab build:
```sh
ADB_SERIAL=RGB05001607258 tools/droiddeckctl run /usr/bin/env -- \
PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1 PANVK_KBASE_DRI3=0 \
VK_DRIVER_FILES=/opt/panvk-g52-lab/panvk-g52-icd.json \
VK_ICD_FILENAMES=/opt/panvk-g52-lab/panvk-g52-icd.json \
DISABLE_GAMESCOPE_WSI=1 LD_LIBRARY_PATH=/usr/lib \
/usr/bin/vkcube --wsi xcb --width 640 --height 480
```
Stop the current session before using `droiddeckctl run`.
`PANVK_KBASE_DRI3=0` selects shared-memory presentation. DroidDeck's Xwayland
does not implement the fork's private Termux:X11 raw dma-buf protocol. GPU
rendering and CPU presentation copies are separate: choosing this presentation
path does not select Lavapipe.
Before selecting this driver for a session, validate GPU compute and offscreen
render readback, then X11 presentation, then the desired game. Device detection
alone is insufficient. The wired Unisoc T618 / Mali-G52 MC2 reports kbase UAPI
11.31, while the separate
[G52 reference project](https://github.com/LukeValen/panvk-mali-g52) was tested
on MediaTek with UAPI 11.38. Keep `BL_STEAM_SOFTWARE=1` available for recovery.
The Linux ICD is distinct from the Android Vulkan driver used by DroidDeck's
presenter. Android/Bionic driver packages cannot be loaded by the glibc Steam
runtime.
## Wired G52 results (2026-10-03)
Built natively in DroidDeck's AArch64 glibc runtime with Clang/LLVM 22.1.8.
The app UID can open `/dev/mali0` and submit jobs without root. No DRM render
node or dma-heap was available. This is experimental driver work, with the
Steam UI and outer Gamescope still running in software mode.
| Test | Result |
| --- | --- |
| Vulkan enumeration | Mali-G52 r1 MC2, integrated GPU, Vulkan 1.3.354 |
| Compute shader and host readback | Expected integer 777 returned |
| Vertex/fragment draw and image readback | Expected red pixel 255,0,0,255 returned |
| Indexed triangle and image readback | Expected red pixel returned |
| X11 VKCube inside a DroidDeck Gamescope session | Rotating cube visible; about 15–17 displayed FPS through SHM |
| Geometry Wars, bundled DXVK 3.1.1 | Adapter rejected: `multiDrawIndirect` is unsupported |
| Geometry Wars, isolated DXVK 1.10.3 | Device and 640×480 swapchain created; rendering loses the GPU device |
The fork advertises `VK_EXT_map_memory_placed` although its kbase mapping code
rejects caller-selected addresses for ordinary SAME_VA allocations. Wine's
WoW64 Vulkan path selects that extension and DXVK buffer mapping fails with
`VK_ERROR_MEMORY_MAP_FAILED`. The third patch withholds the extension and
features on kbase, keeping them on DRM. Wine then uses its fallback mapping
path and reaches real game submissions.
The remaining Geometry Wars failure includes terminated jobs (`0x04`) and
`BASE_JD_EVENT_JOB_READ_FAULT` (`0x42`), followed by
`VK_ERROR_DEVICE_LOST`. A diagnostic build that waits after each batch showed
successful uploads and clears, then termination of the first vertex/tiler
chain. Its compute job completed, but its vertex job, tiler job and dependent
fragment job did not complete. Basic indexed rendering also passes with IDVS
disabled, so a generic indexed-draw or separate-vertex-job failure was not
reproduced. The complex vertex workload remains unresolved. Disabling submission overlap and compiler IDVS or
optimizations did not recover rendering. Native draw/readback and VKCube
success therefore do not establish working Proton game acceleration.
Do not advertise missing Vulkan features just to pass DXVK's adapter check.
Keep game overrides and legacy DXVK trials isolated from the Proton depot,
and restore the working software launch option when the trial fails:
```sh
DISABLE_GAMESCOPE_WSI=1 %command%
```
The tested three-patch build has SHA-256
`5fc3f57fe335ebf524871ca24fd38b3c384c31baaebe9a2220e8e482e09e5ad6`.
That identifies the retained evidence binary; rebuilds need not be byte-identical.
The patched driver remains a private lab ICD. It is not bundled in the APK,
selected globally, or production-ready. A compatible DXVK path and reliable
complex rendering must be validated before adding automatic Mali selection.
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
# Experimental kbase/JM PanVK build. Run inside an AArch64 glibc environment.
# Output stays private to the supplied build directory; no system ICD is installed.
set -euo pipefail
source_dir=${1:?Usage: build-in-runtime.sh SOURCE_DIR BUILD_DIR}
build_dir=${2:?Usage: build-in-runtime.sh SOURCE_DIR BUILD_DIR}
revision=efd07bab401b263a9ac114560ecdb9a2934f5d43
script_dir=$(cd -- "$(dirname -- "$0")" && pwd)
source_dir=$(cd -- "$source_dir" && pwd)
mkdir -p "$build_dir"
build_dir=$(cd -- "$build_dir" && pwd)
if [ "$(uname -m)" != aarch64 ]; then
echo 'This script needs a native AArch64 Linux build environment.' >&2
exit 1
fi
if [ "$(git -C "$source_dir" rev-parse HEAD)" != "$revision" ]; then
echo "Expected FristOneRR-Panvk-Source revision $revision." >&2
exit 1
fi
for patch_file in "$script_dir"/patches/*.patch; do
if git -C "$source_dir" apply --reverse --check "$patch_file" 2>/dev/null; then
continue
fi
git -C "$source_dir" apply --check "$patch_file"
git -C "$source_dir" apply "$patch_file"
done
# The fork's SPIRV-Tools probe contains Clang-only warning options.
export CC=clang CXX=clang++
meson setup "$build_dir" "$source_dir" --buildtype=release \
--prefix="$build_dir/install" --libdir=lib \
-Dbuild-tests=false -Dgallium-drivers= -Dvulkan-drivers=panfrost \
-Dpanfrost-kmds=kbase -Dplatforms=x11,wayland \
-Dglx=disabled -Degl=disabled -Dgbm=disabled \
-Dgles1=disabled -Dgles2=disabled -Dopengl=false \
-Dllvm=enabled -Dmesa-clc=enabled -Dprecomp-compiler=enabled \
-Dpanfrost-rust=false -Dvalgrind=disabled -Dzstd=enabled
ninja -C "$build_dir" -j"${JOBS:-2}" src/panfrost/vulkan/libvulkan_panfrost.so
python3 - "$build_dir" <<'PY'
import json, pathlib, sys
root = pathlib.Path(sys.argv[1])
driver = root / 'src/panfrost/vulkan/libvulkan_panfrost.so'
manifest = {'file_format_version': '1.0.0', 'ICD': {
'library_path': str(driver), 'api_version': '1.4.0'}}
(root / 'panvk-g52-icd.json').write_text(json.dumps(manifest, indent=2) + '\n')
PY
sha256sum "$build_dir/src/panfrost/vulkan/libvulkan_panfrost.so"
echo "Private ICD: $build_dir/panvk-g52-icd.json"
+17
View File
@@ -0,0 +1,17 @@
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -16,10 +16,9 @@ project(
'build.rust_std=2021',
],
)
-add_project_link_arguments(
- '-llog',
- language : ['c', 'cpp'],
-)
+if host_machine.system() == 'android'
+ add_project_link_arguments('-llog', language : ['c', 'cpp'])
+endif
project_version = meson.project_version()
if host_machine.system() == 'darwin'
@@ -0,0 +1,95 @@
diff --git a/src/panfrost/lib/kmod/kbase_kmod.c b/src/panfrost/lib/kmod/kbase_kmod.c
--- a/src/panfrost/lib/kmod/kbase_kmod.c
+++ b/src/panfrost/lib/kmod/kbase_kmod.c
@@ -97,6 +97,7 @@
struct base_jd_udata { __u64 blob[2]; };
+/* kbase atom v2: 56 bytes, without the v3 sequence-number prefix. */
struct base_jd_atom {
__u64 jc;
struct base_jd_udata udata;
@@ -110,11 +111,12 @@
__u8 jobslot;
__u32 core_req;
__u8 renderpass_id;
- __u8 padding[15]; /* pad struct to 64 bytes total; this kernel rejects any other JOB_SUBMIT stride */
+ __u8 padding[7];
};
-/* Some kbase builds (e.g. r49 on 6.6 kernels) expect a 56-byte atom
- * stride instead of 64. PANVK_ATOM_STRIDE overrides the default. */
+/* JM 11.19 added renderpass_id and extended v2 from 48 to 56 bytes.
+ * A 64-byte atom is v3: it has a leading seq_nr and a different layout.
+ * Do not submit a padded v2 as v3. */
static unsigned g_atom_stride;
static unsigned
@@ -123,7 +125,7 @@
if (!g_atom_stride) {
const char *e = getenv("PANVK_ATOM_STRIDE");
unsigned v = e ? (unsigned)atoi(e) : 0;
- g_atom_stride = (v == 48 || v == 56 || v == 64) ? v : 64;
+ g_atom_stride = (v == 48 || v == 56) ? v : 56;
if (e)
dprintf(2, "[FristOneRR] atom stride = %u\n", g_atom_stride);
}
@@ -1223,43 +1225,6 @@
}
-/* Probe the JOB_SUBMIT atom stride once per process: submit an empty
- * dependency-only atom (no GPU work) with stride 64. Kernels that expect
- * 56 (e.g. r49 on 6.6) answer with a config fault (0x40) or reject the
- * ioctl; then we switch to 56. PANVK_ATOM_STRIDE always wins. */
-static void
-kbase_probe_atom_stride(int fd)
-{
- if (g_atom_stride || getenv("PANVK_ATOM_STRIDE")) {
- (void)kbase_atom_stride();
- return;
- }
-
- struct base_jd_atom atom = { .atom_number = 255, .core_req = 0 };
- struct kbase_ioctl_job_submit sub = {
- .addr = (uint64_t)(uintptr_t)&atom,
- .nr_atoms = 1,
- .stride = 64,
- };
- unsigned result = 64;
-
- if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &sub) < 0) {
- result = 56;
- } else {
- struct pollfd pfd = { .fd = fd, .events = POLLIN };
- if (poll(&pfd, 1, 1000) > 0) {
- struct base_jd_event_v2 ev = { 0 };
- if (read(fd, &ev, sizeof(ev)) == (ssize_t)sizeof(ev) &&
- (ev.event_code & BASE_JD_EVENT_ERR_MASK))
- result = 56;
- }
- }
-
- g_atom_stride = result;
- if (result != 64)
- dprintf(2, "[FristOneRR] atom stride auto-detected: %u\n", result);
-}
-
static struct pan_kmod_dev *
kbase_kmod_dev_create(int fd, uint32_t flags,
UNUSED const struct pan_kmod_driver *drv_info,
@@ -1307,8 +1272,12 @@
return NULL;
}
- if (!is_csf)
- kbase_probe_atom_stride(fd);
+ STATIC_ASSERT(sizeof(struct base_jd_atom) == 56);
+ if (!is_csf) {
+ if (!g_atom_stride && !getenv("PANVK_ATOM_STRIDE"))
+ g_atom_stride = ver.minor < 19 ? 48 : 56;
+ (void)kbase_atom_stride();
+ }
/* Empirically, this Mali-G57 kbase JM backend needs ~2s of real
* wall-clock time after context creation before the GPU power domain
@@ -0,0 +1,26 @@
diff --git a/src/panfrost/vulkan/panvk_vX_physical_device.c b/src/panfrost/vulkan/panvk_vX_physical_device.c
--- a/src/panfrost/vulkan/panvk_vX_physical_device.c
+++ b/src/panfrost/vulkan/panvk_vX_physical_device.c
@@ -198,7 +198,9 @@
.EXT_legacy_dithering = true,
.EXT_line_rasterization = true,
.EXT_load_store_op_none = true,
- .EXT_map_memory_placed = true,
+ /* kbase SAME_VA allocations cannot be mapped at a caller-chosen
+ * address. Wine WoW64 selects that path when this extension exists. */
+ .EXT_map_memory_placed = !device->kbase_node_path[0],
.EXT_external_memory_host = true,
.EXT_nested_command_buffer = PAN_ARCH >= 10,
.EXT_memory_budget = true,
@@ -729,9 +731,9 @@
.nestedCommandBufferSimultaneousUse = PAN_ARCH >= 10,
/* VK_EXT_map_memory_placed */
- .memoryMapPlaced = true,
+ .memoryMapPlaced = !device->kbase_node_path[0],
.memoryMapRangePlaced = false,
- .memoryUnmapReserve = true,
+ .memoryUnmapReserve = !device->kbase_node_path[0],
/* VK_EXT_non_seamless_cube_map */
.nonSeamlessCubeMap = true,