mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 19:41:10 +00:00
Present Qualcomm Proton games through Gamescope's zero-copy WSI path
Games in Qualcomm sessions now hand Gamescope LINEAR DMA-BUFs exported by the driver instead of copying frames through X11 SHM. The ARM WSI layer is rebuilt with its Wayland backend and the private Gamescope gains a linux-dmabuf global without a DRM device. BL_QUALCOMM_SHM=1 keeps the shared-memory path; Turnip sessions are unchanged. (cherry picked from commit 1bd4c04f193f48bdaf4d4aad374010abaf48765d)
This commit is contained in:
1 parent
6c8dfe6201
commit
d24b824f25
14 files changed
+969
-13
No files matched your search
@@ -295,6 +295,9 @@ def stageQualcommRuntime = tasks.register('stageQualcommRuntime', Exec) {
|
||||
if (qualcommRuntime.mesa) {
|
||||
inputs.dir(rootProject.file(qualcommRuntime.mesa.directory))
|
||||
}
|
||||
if (qualcommRuntime.wsi?.build) {
|
||||
inputs.dir(rootProject.file(qualcommRuntime.wsi.build.directory))
|
||||
}
|
||||
inputs.property('ndkVersion', android.ndkVersion)
|
||||
outputs.dir(layout.buildDirectory.dir('qualcommAssets'))
|
||||
commandLine pythonExecutable, 'tools/qualcomm/fetch_runtime.py',
|
||||
|
||||
@@ -198,6 +198,14 @@ bl_libpath() {
|
||||
}
|
||||
"""
|
||||
|
||||
# Qualcomm sessions copy the Steam client's frames through X11 SHM; games hand Gamescope LINEAR DMA-BUFs instead.
|
||||
BL_PRESENT_SETUP = """
|
||||
bl_present() {
|
||||
[ "${DROIDDECK_SYSTEM_QUALCOMM:-0}" = 1 ] && [ "${BL_QUALCOMM_SHM:-0}" != 1 ] || return 0
|
||||
unset DISABLE_GAMESCOPE_WSI WSI_X11_FORCE_SHM
|
||||
}
|
||||
"""
|
||||
|
||||
BL_IDTECH3_SETUP = """
|
||||
bl_idtech3() {
|
||||
BL_EXTRA_ARGS=()
|
||||
@@ -364,6 +372,7 @@ if [ -n "$depot" ]; then
|
||||
bl_idtech3 "$@"
|
||||
bl_vacdiag "$@"
|
||||
bl_libpath
|
||||
bl_present
|
||||
BL_STORE_PROTON="$depot/proton"; export BL_STORE_PROTON
|
||||
bl_store_launch "$@"
|
||||
BL_STORE_ARGS=("$@")
|
||||
@@ -378,7 +387,7 @@ LAUNCHER_TEMPLATE = LAUNCHER_SH
|
||||
def launcher_sh(sources=SOURCES):
|
||||
return (LAUNCHER_TEMPLATE % " ".join('"%s"' % n for n in sources)).replace(
|
||||
"verb=$1; shift", "verb=$1; shift\n" + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP + BL_IDTECH3_SETUP
|
||||
+ BL_VACDIAG_SETUP + BL_COMPONENTS_SETUP + BL_LIBPATH_SETUP + BL_STORE_SETUP, 1)
|
||||
+ BL_VACDIAG_SETUP + BL_COMPONENTS_SETUP + BL_LIBPATH_SETUP + BL_PRESENT_SETUP + BL_STORE_SETUP, 1)
|
||||
|
||||
|
||||
LAUNCHER_SH = launcher_sh()
|
||||
@@ -419,6 +428,7 @@ bl_game_cpus
|
||||
bl_idtech3 "$@"
|
||||
bl_vacdiag "$@"
|
||||
bl_libpath
|
||||
bl_present
|
||||
BL_STORE_PROTON="$here/%s"; export BL_STORE_PROTON
|
||||
bl_store_launch "$@"
|
||||
BL_STORE_ARGS=("$@")
|
||||
@@ -650,7 +660,7 @@ def adopt_extras(tools):
|
||||
body = (EXTRA_WRAPPER_SH % entry_point).replace(
|
||||
'here=$(dirname "$0")',
|
||||
'here=$(dirname "$0")\n' + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP + BL_IDTECH3_SETUP
|
||||
+ BL_VACDIAG_SETUP + BL_COMPONENTS_SETUP + BL_LIBPATH_SETUP + BL_STORE_SETUP, 1)
|
||||
+ BL_VACDIAG_SETUP + BL_COMPONENTS_SETUP + BL_LIBPATH_SETUP + BL_PRESENT_SETUP + BL_STORE_SETUP, 1)
|
||||
write_changed(wrapper, body, 0o755)
|
||||
write_changed(manifest, EXTRA_MANIFEST_VDF)
|
||||
write_changed(compat_vdf, EXTRA_COMPAT_TOOL_VDF % (name, display_name(entry)))
|
||||
|
||||
@@ -72,9 +72,25 @@ Build the private components on ARM64 Linux with their normal development depend
|
||||
```sh
|
||||
python3 tools/qualcomm/build_gamescope.py
|
||||
python3 tools/qualcomm/build_zink.py
|
||||
python3 tools/qualcomm/build_wsi.py
|
||||
```
|
||||
|
||||
The recipes pin upstream source revisions and retain local outputs under `build/qualcomm-gamescope` and `build/qualcomm-zink/runtime`. Update `runtime.json` from the produced `build.json` hashes after deliberately rebuilding a component. The APK check verifies the pinned Gamescope, its source archive, WSI files, and private Mesa files alongside the full Linux runtime and all add-on packs. Native build outputs are prerequisites, just like the other prepared runtime components.
|
||||
The recipes pin upstream source revisions and retain local outputs under `build/qualcomm-gamescope`, `build/qualcomm-zink/runtime` and `build/qualcomm-wsi`. `build_wsi.py` takes the WSI source from the pinned WSI archive in `build/qualcomm-cache`, so stage it once with `fetch_runtime.py` first. Update `runtime.json` from the produced `build.json` hashes after deliberately rebuilding a component. The APK check verifies the pinned Gamescope, its source archive, WSI files, and private Mesa files alongside the full Linux runtime and all add-on packs. Native build outputs are prerequisites, just like the other prepared runtime components.
|
||||
|
||||
## Zero-copy game presentation
|
||||
|
||||
Proton games in Qualcomm sessions present through Gamescope's WSI layer instead of X11 shared memory. The Steam client, its browser and other session programs keep `WSI_X11_FORCE_SHM=1` and `DISABLE_GAMESCOPE_WSI=1`; the Proton wrapper's `bl_present` drops both for the game. Set `BL_QUALCOMM_SHM=1` in the session environment to keep games on the shared-memory path.
|
||||
|
||||
The Qualcomm HAL has DMA-BUF export but no `VK_EXT_image_drm_format_modifier`, and Android has no DRM device. The WSI layer is therefore rebuilt with its Wayland backend and `wsi-patches`:
|
||||
|
||||
- `0001` allocates swapchain images in the driver as LINEAR images with exportable dedicated memory, exports them with `vkGetMemoryFdKHR`, takes offset and stride from `vkGetImageSubresourceLayout`, and sends them as LINEAR `zwp_linux_dmabuf_v1` buffers. The modifier extension is no longer required at `vkCreateDevice`. X11 DRI3 keeps its previous behavior.
|
||||
- `0002` removes the `__STDC_VERSION__` workaround that GCC 16 rejects.
|
||||
- `0003` opens `/dev/dma_heap/system` read-only, the only access Android grants apps.
|
||||
- `0004` exposes `vkAcquireNextImage2KHR` on Vulkan 1.0 devices; Gamescope's WSI layer acquires every image through it.
|
||||
|
||||
`gamescope-linear-dmabuf.patch` makes the private Gamescope create a version 3 `linux-dmabuf` global without a DRM device (wlroots otherwise creates none), advertise LINEAR beside the implicit layout, and import LINEAR buffers as LINEAR images. Without modifiers the stride cannot be imposed, so an import is refused when the buffer's offset or stride differs from the driver's own LINEAR layout.
|
||||
|
||||
Verification on 2026-10-24 used the RedMagic 11 Pro, Adreno 840 and the imported v891.7 driver at 1590x720. `vkcube` (600 frames, uncapped mailbox) went from 131.5 to 198.0 fps; its own CPU time fell from 1.15 s to 0.23 s, Xwayland from 1.64 s to 0.69 s and Gamescope from 6.88 s to 4.00 s. DRAGON BALL: Sparking! ZERO at 60 fps was launched from the same installed APK after a 60-second Steam settle, with only `BL_QUALCOMM_SHM` toggled, and sampled over the same 12-second startup window: Gamescope used 0.35 cores against 0.43 and 0.44 over two shared-memory runs, Xwayland 0.14 against 0.20, and the game 1.28 against 1.21 and 1.22. Average GPU load ranged from 5% to 13% across these runs and does not separate the paths at this phase. The startup logos rendered correctly and the performance overlay hooked DXVK directly. Gamescope's remaining extra cost comes from its SDL backend, the nested-swapchain copy and bridged Vulkan calls. `vkcube` in FIFO mode presented 169.5 fps against a 144 Hz output, so FIFO pacing through this path still needs checking. Native Linux games are not covered by the Proton wrapper.
|
||||
|
||||
## APK packaging
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ def patches():
|
||||
base = ROOT / 'tools/gamescope/patches'
|
||||
extra = HERE / 'gamescope-patches'
|
||||
return sorted([*base.glob('*.patch'), *extra.glob('*.patch')], key=lambda p: p.name) + [
|
||||
HERE / 'constant-yuv-samplers.patch', HERE / 'gamescope-no-dmabuf.patch']
|
||||
HERE / 'constant-yuv-samplers.patch', HERE / 'gamescope-no-dmabuf.patch', HERE / 'gamescope-linear-dmabuf.patch']
|
||||
|
||||
def prepare(source):
|
||||
if source.exists():
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""Build the Qualcomm Vulkan WSI layer with Wayland presentation on ARM64 Linux."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
HERE = Path(__file__).resolve().parent
|
||||
OPTIONS = ['-DCMAKE_BUILD_TYPE=Release', '-DBUILD_WSI_WAYLAND=ON', '-DBUILD_WSI_X11=ON', '-DBUILD_WSI_HEADLESS=OFF',
|
||||
'-DBUILD_WSI_DISPLAY=OFF', '-DSELECT_EXTERNAL_ALLOCATOR=dma_buf_heaps', '-DWSIALLOC_MEMORY_HEAP_NAME=system']
|
||||
OUTPUTS = ['libVkLayer_window_system_integration.so', 'VkLayer_window_system_integration.json']
|
||||
|
||||
|
||||
def run(args, cwd=None):
|
||||
subprocess.run([str(arg) for arg in args], cwd=cwd, check=True)
|
||||
|
||||
|
||||
def patches():
|
||||
return sorted((HERE / 'wsi-patches').glob('*.patch'), key=lambda p: p.name)
|
||||
|
||||
|
||||
def pinned_archive(cache):
|
||||
lock = json.loads((HERE / 'runtime.json').read_text())['wsi']
|
||||
archive = cache / (lock['sha256'] + '.tzst')
|
||||
if not archive.is_file() or hashlib.sha256(archive.read_bytes()).hexdigest() != lock['sha256']:
|
||||
raise ValueError('Stage the pinned WSI archive first (tools/qualcomm/fetch_runtime.py): ' + str(archive))
|
||||
return archive
|
||||
|
||||
|
||||
def prepare(source, cache, zstd):
|
||||
if source.exists():
|
||||
raise ValueError('Source directory already exists; use a new directory or --prepared-source')
|
||||
source.mkdir(parents=True)
|
||||
with zipfile.ZipFile(pinned_archive(cache)) as package:
|
||||
packed = package.read('source.tar.zst')
|
||||
unpacked = subprocess.run([zstd, '-dc'], input=packed, capture_output=True, check=True).stdout
|
||||
tar_path = source.parent / (source.name + '.tar')
|
||||
tar_path.write_bytes(unpacked)
|
||||
try:
|
||||
with tarfile.open(tar_path) as archive:
|
||||
archive.extractall(source, filter='data')
|
||||
finally:
|
||||
tar_path.unlink()
|
||||
for patch in patches():
|
||||
run(['git', '-c', 'core.autocrlf=false', 'apply', '--ignore-whitespace', patch], source)
|
||||
|
||||
|
||||
def package(source, output, strip_tool='strip'):
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
for name in OUTPUTS:
|
||||
shutil.copyfile(source / 'build' / name, output / name)
|
||||
run([strip_tool, '--strip-unneeded', output / OUTPUTS[0]])
|
||||
for patch in patches():
|
||||
shutil.copyfile(patch, output / patch.name)
|
||||
names = OUTPUTS + [patch.name for patch in patches()]
|
||||
manifest = {'options': OPTIONS,
|
||||
'files': {name: {'sha256': hashlib.sha256((output / name).read_bytes()).hexdigest(),
|
||||
'size': (output / name).stat().st_size} for name in names}}
|
||||
(output / 'build.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--source', type=Path, default=ROOT / 'build/qualcomm-wsi/source')
|
||||
parser.add_argument('--output', type=Path, default=ROOT / 'build/qualcomm-wsi')
|
||||
parser.add_argument('--cache', type=Path, default=ROOT / 'build/qualcomm-cache')
|
||||
parser.add_argument('--zstd', default='zstd')
|
||||
parser.add_argument('--prepared-source', action='store_true')
|
||||
parser.add_argument('--prepare-only', action='store_true')
|
||||
parser.add_argument('--package-only', action='store_true')
|
||||
parser.add_argument('--strip', default='strip')
|
||||
parser.add_argument('--jobs', type=int, default=4)
|
||||
args = parser.parse_args()
|
||||
if not args.prepared_source and not args.package_only:
|
||||
prepare(args.source, args.cache, args.zstd)
|
||||
if not args.prepare_only:
|
||||
if not args.package_only:
|
||||
run(['cmake', '-S', '.', '-B', 'build', '-G', 'Ninja', *OPTIONS], args.source)
|
||||
run(['ninja', '-C', 'build', '-j' + str(args.jobs)], args.source)
|
||||
package(args.source, args.output, args.strip)
|
||||
@@ -75,12 +75,26 @@ def stage(output, cache, lock, ndk=None):
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(data)
|
||||
if 'wsi' in lock:
|
||||
built = None
|
||||
if 'build' in lock['wsi']:
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
built = (root / lock['wsi']['build']['directory']).resolve()
|
||||
if not built.is_relative_to(root / 'build'):
|
||||
raise ValueError('Invalid Qualcomm WSI build path')
|
||||
with zipfile.ZipFile(fetch(cache, lock['wsi'])) as package:
|
||||
for entry in lock['wsi']['files']:
|
||||
target = Path(entry['path'])
|
||||
if target.is_absolute() or '..' in target.parts:
|
||||
raise ValueError('Invalid WSI destination')
|
||||
data = package.read(entry['name'])
|
||||
if entry.get('built'):
|
||||
if built is None or Path(entry['name']).name != entry['name']:
|
||||
raise ValueError('Invalid Qualcomm WSI build member: ' + entry['name'])
|
||||
origin = built / entry['name']
|
||||
if not origin.is_file():
|
||||
raise FileNotFoundError('Build the Qualcomm WSI layer first with tools/qualcomm/build_wsi.py: ' + str(origin))
|
||||
data = origin.read_bytes()
|
||||
else:
|
||||
data = package.read(entry['name'])
|
||||
if len(data) != entry['size'] or hashlib.sha256(data).hexdigest() != entry['sha256']:
|
||||
raise ValueError('Qualcomm WSI member failed SHA-256 verification')
|
||||
destination = output / target
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
index 811d80c..5f181a9 100644
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -2245,6 +2245,18 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t depth, uin
|
||||
assert( drmFormat == pDMA->format );
|
||||
}
|
||||
|
||||
+ // Without DRM format modifiers the stride cannot be imposed, so it must match the driver's own LINEAR layout.
|
||||
+ const bool bImplicitLinearImport = !g_device.supportsModifiers() && pDMA && pDMA->modifier == DRM_FORMAT_MOD_LINEAR;
|
||||
+ if ( bImplicitLinearImport )
|
||||
+ {
|
||||
+ if ( pDMA->n_planes != 1 )
|
||||
+ {
|
||||
+ vk_log.errorf( "LINEAR DMA-BUF import without DRM format modifiers needs a single plane" );
|
||||
+ return false;
|
||||
+ }
|
||||
+ imageInfo.tiling = tiling = VK_IMAGE_TILING_LINEAR;
|
||||
+ }
|
||||
+
|
||||
if ( g_device.supportsModifiers() && pDMA && pDMA->modifier != DRM_FORMAT_MOD_INVALID )
|
||||
{
|
||||
VkExternalImageFormatProperties externalImageProperties = {
|
||||
@@ -2416,7 +2428,22 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t depth, uin
|
||||
vk_errorf( res, "vkCreateImage failed" );
|
||||
return false;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ if ( bImplicitLinearImport )
|
||||
+ {
|
||||
+ const VkImageSubresource subresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
|
||||
+ VkSubresourceLayout layout = {};
|
||||
+ g_device.vk.GetImageSubresourceLayout( g_device.device(), m_vkImage, &subresource, &layout );
|
||||
+ if ( layout.offset != pDMA->offset[0] || layout.rowPitch != pDMA->stride[0] )
|
||||
+ {
|
||||
+ vk_log.errorf( "LINEAR DMA-BUF layout (offset %" PRIu32 ", stride %" PRIu32 ") differs from the driver's (offset %" PRIu64 ", stride %" PRIu64 ")",
|
||||
+ pDMA->offset[0], pDMA->stride[0], uint64_t( layout.offset ), uint64_t( layout.rowPitch ) );
|
||||
+ g_device.vk.DestroyImage( g_device.device(), m_vkImage, nullptr );
|
||||
+ m_vkImage = VK_NULL_HANDLE;
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
VkMemoryRequirements memRequirements;
|
||||
g_device.vk.GetImageMemoryRequirements(g_device.device(), m_vkImage, &memRequirements);
|
||||
|
||||
@@ -2963,6 +2990,27 @@ static bool is_image_format_modifier_supported(VkFormat format, uint32_t drmForm
|
||||
return res == VK_SUCCESS;
|
||||
}
|
||||
|
||||
+static bool is_linear_dmabuf_importable( VkPhysicalDeviceImageFormatInfo2 imageFormatInfo )
|
||||
+{
|
||||
+ VkPhysicalDeviceExternalImageFormatInfo externalInfo = {
|
||||
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
|
||||
+ .pNext = imageFormatInfo.pNext,
|
||||
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
|
||||
+ };
|
||||
+ imageFormatInfo.pNext = &externalInfo;
|
||||
+ imageFormatInfo.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
+
|
||||
+ VkExternalImageFormatProperties externalProps = {
|
||||
+ .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
|
||||
+ };
|
||||
+ VkImageFormatProperties2 props = {
|
||||
+ .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
|
||||
+ .pNext = &externalProps,
|
||||
+ };
|
||||
+ return g_device.vk.GetPhysicalDeviceImageFormatProperties2( g_device.physDev(), &imageFormatInfo, &props ) == VK_SUCCESS &&
|
||||
+ ( externalProps.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT ) != 0;
|
||||
+}
|
||||
+
|
||||
bool vulkan_init_format(VkFormat format, uint32_t drmFormat)
|
||||
{
|
||||
// First, check whether the Vulkan format is supported
|
||||
@@ -3071,6 +3119,8 @@ bool vulkan_init_format(VkFormat format, uint32_t drmFormat)
|
||||
return false;
|
||||
|
||||
wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, DRM_FORMAT_MOD_INVALID );
|
||||
+ if ( is_linear_dmabuf_importable( imageFormatInfo ) )
|
||||
+ wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, DRM_FORMAT_MOD_LINEAR );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
|
||||
index 618794b..2f30851 100644
|
||||
--- a/src/wlserver.cpp
|
||||
+++ b/src/wlserver.cpp
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <wlr/types/wlr_seat.h>
|
||||
#include <wlr/types/wlr_touch.h>
|
||||
#include <wlr/types/wlr_drm.h>
|
||||
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/xwayland/server.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
@@ -2216,6 +2217,25 @@ bool wlserver_init( void ) {
|
||||
|
||||
wlr_renderer_init_wl_display(wlserver.wlr.renderer, wlserver.display);
|
||||
|
||||
+ // wlroots only creates linux-dmabuf for renderers with a DRM device, and Android exposes none.
|
||||
+ const wlr_drm_format_set *pDmabufFormats = wlr_renderer_get_texture_formats( wlserver.wlr.renderer, WLR_BUFFER_CAP_DMABUF );
|
||||
+ if ( wlr_renderer_get_drm_fd( wlserver.wlr.renderer ) < 0 && pDmabufFormats != nullptr && pDmabufFormats->len > 0 )
|
||||
+ {
|
||||
+ wlr_linux_dmabuf_feedback_v1 feedback = {};
|
||||
+ wl_array_init( &feedback.tranches );
|
||||
+ wlr_linux_dmabuf_feedback_v1_tranche *pTranche = wlr_linux_dmabuf_feedback_add_tranche( &feedback );
|
||||
+ bool bFormatsCopied = pTranche != nullptr;
|
||||
+ for ( size_t i = 0; bFormatsCopied && i < pDmabufFormats->len; i++ )
|
||||
+ {
|
||||
+ const wlr_drm_format &format = pDmabufFormats->formats[ i ];
|
||||
+ for ( size_t j = 0; bFormatsCopied && j < format.len; j++ )
|
||||
+ bFormatsCopied = wlr_drm_format_set_add( &pTranche->formats, format.format, format.modifiers[ j ] );
|
||||
+ }
|
||||
+ if ( !bFormatsCopied || !wlr_linux_dmabuf_v1_create( wlserver.display, 3, &feedback ) )
|
||||
+ wl_log.errorf( "Failed to create linux-dmabuf without a DRM device" );
|
||||
+ wlr_linux_dmabuf_feedback_v1_finish( &feedback );
|
||||
+ }
|
||||
+
|
||||
wlserver.wlr.compositor = wlr_compositor_create(wlserver.display, 5, wlserver.wlr.renderer);
|
||||
|
||||
wl_signal_add( &wlserver.wlr.compositor->events.new_surface, &new_surface_listener );
|
||||
diff --git a/subprojects/wlroots/types/wlr_linux_dmabuf_v1.c b/subprojects/wlroots/types/wlr_linux_dmabuf_v1.c
|
||||
index 0dc42f4..5bc9b74 100644
|
||||
--- a/subprojects/wlroots/types/wlr_linux_dmabuf_v1.c
|
||||
+++ b/subprojects/wlroots/types/wlr_linux_dmabuf_v1.c
|
||||
@@ -892,13 +892,13 @@ static bool set_default_feedback(struct wlr_linux_dmabuf_v1 *linux_dmabuf,
|
||||
}
|
||||
|
||||
drmDevice *device = NULL;
|
||||
- if (drmGetDeviceFromDevId(feedback->main_device, 0, &device) != 0) {
|
||||
+ int main_device_fd = -1;
|
||||
+ if (feedback->main_device == 0) {
|
||||
+ wlr_log(WLR_DEBUG, "No DRM device, skipping DMA-BUF import checks");
|
||||
+ } else if (drmGetDeviceFromDevId(feedback->main_device, 0, &device) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
|
||||
goto error_compiled;
|
||||
- }
|
||||
-
|
||||
- int main_device_fd = -1;
|
||||
- if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
|
||||
+ } else if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
|
||||
const char *name = device->nodes[DRM_NODE_RENDER];
|
||||
main_device_fd = open(name, O_RDWR | O_CLOEXEC);
|
||||
drmFreeDevice(&device);
|
||||
@@ -10,12 +10,12 @@
|
||||
},
|
||||
"gamescope": {
|
||||
"source_commit": "8f212644c46460549035971ab914421180f61a7c",
|
||||
"member_sha256": "1efe37d2bee38b86a051f84279802054d770aed6d9eff7d9379b4648705d0881",
|
||||
"member_sha256": "a7cce9f11bbb39da0543a14a6dcf56765117d2c577b3d7fac6caadda5039563c",
|
||||
"build": {
|
||||
"binary": "build/qualcomm-gamescope/gamescope",
|
||||
"source": "build/qualcomm-gamescope/source.tar.gz",
|
||||
"source_sha256": "11e9134ef9fe0616bc1c1ef8bdf61c52b48f473ce07e562108cf7476a017e531",
|
||||
"source_size": 10104613
|
||||
"source_sha256": "b9bec99913cebcd6d1a443f9b3263ff90a6f04574f116c8612d6f8ef86ab5f38",
|
||||
"source_size": 10104476
|
||||
}
|
||||
},
|
||||
"wsi": {
|
||||
@@ -24,15 +24,17 @@
|
||||
"mode": 420,
|
||||
"name": "libVkLayer_window_system_integration.so",
|
||||
"path": "wsi/libVkLayer_window_system_integration.so",
|
||||
"sha256": "d07b961181b9547fd48ad9f05cc6d3b808b824676eb5a186fe3feb8caa013f19",
|
||||
"size": 436888
|
||||
"sha256": "431bc41d8ccb4ae0f35b1aa76bb30770926159e3e3b045084910bf0da6bc6436",
|
||||
"size": 397384,
|
||||
"built": true
|
||||
},
|
||||
{
|
||||
"mode": 420,
|
||||
"name": "VkLayer_window_system_integration.json",
|
||||
"path": "wsi/VkLayer_window_system_integration.json",
|
||||
"sha256": "a1df8b1cee7315015d8fe82534451e373ae4d9cb2adfc232c68346ecbf2818ee",
|
||||
"size": 3683
|
||||
"sha256": "b80f1a621be6994e5cdb4760acee98bb013d0ff57fd63b1cc8de17cc764daaa2",
|
||||
"size": 3754,
|
||||
"built": true
|
||||
},
|
||||
{
|
||||
"mode": 420,
|
||||
@@ -75,6 +77,38 @@
|
||||
"path": "provenance/wsi-vulkan-headers-commit",
|
||||
"sha256": "16706d6cdbeb788426a90d66574dfd71068c4d993d39e76ec5c281b1f30dc7a6",
|
||||
"size": 41
|
||||
},
|
||||
{
|
||||
"built": true,
|
||||
"mode": 420,
|
||||
"name": "0001-wayland-icd-exported-linear-buffers.patch",
|
||||
"path": "licenses/wsi-patches/0001-wayland-icd-exported-linear-buffers.patch",
|
||||
"sha256": "fc1653c6d9ab718d296ece224f8a923f7c10cd0a3486f765af11306ca1c59f6c",
|
||||
"size": 19862
|
||||
},
|
||||
{
|
||||
"built": true,
|
||||
"mode": 420,
|
||||
"name": "0002-gcc16-stdc-version.patch",
|
||||
"path": "licenses/wsi-patches/0002-gcc16-stdc-version.patch",
|
||||
"sha256": "5a303b7f69c8ab819596bbf93ce9f68006e4473d2b383b6109941976da9d075a",
|
||||
"size": 2202
|
||||
},
|
||||
{
|
||||
"built": true,
|
||||
"mode": 420,
|
||||
"name": "0003-dma-heap-read-only-open.patch",
|
||||
"path": "licenses/wsi-patches/0003-dma-heap-read-only-open.patch",
|
||||
"sha256": "891f89c80310b2e5a7a3d8337632fddaaedad803b6337e69053048dd6c6ba3c0",
|
||||
"size": 623
|
||||
},
|
||||
{
|
||||
"built": true,
|
||||
"mode": 420,
|
||||
"name": "0004-acquire-next-image2-on-vulkan-1-0.patch",
|
||||
"path": "licenses/wsi-patches/0004-acquire-next-image2-on-vulkan-1-0.patch",
|
||||
"sha256": "30e968889e22f3ba4ea2e0c3b80e3752c4d61898e256230392a0242d125ec239",
|
||||
"size": 1541
|
||||
}
|
||||
],
|
||||
"id": "wsi",
|
||||
@@ -100,7 +134,10 @@
|
||||
},
|
||||
"sha256": "348f9ec42159c1ee6e2baba4312a5e845d26687238f29e3f4a1b0c3c97e13b3c",
|
||||
"size": 362717,
|
||||
"url": "https://github.com/wallentx/DroidDeck/releases/download/v0.3.2-progress.20261010.4/wsi-abe5861.zip"
|
||||
"url": "https://github.com/wallentx/DroidDeck/releases/download/v0.3.2-progress.20261010.4/wsi-abe5861.zip",
|
||||
"build": {
|
||||
"directory": "build/qualcomm-wsi"
|
||||
}
|
||||
},
|
||||
"shaders": {
|
||||
"url": "https://codeload.github.com/ValveSoftware/gamescope/tar.gz/8f212644c46460549035971ab914421180f61a7c",
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
diff --git a/wsi/display/swapchain.cpp b/wsi/display/swapchain.cpp
|
||||
index 12bd8d0..d4cfac8 100644
|
||||
--- a/wsi/display/swapchain.cpp
|
||||
+++ b/wsi/display/swapchain.cpp
|
||||
@@ -198,7 +198,7 @@ VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain
|
||||
|
||||
wsialloc_create_info_args wsialloc_args = { external_image_create_info.selected_format,
|
||||
external_image_create_info.flags, external_image_create_info.extent,
|
||||
- external_image_create_info.explicit_compression };
|
||||
+ external_image_create_info.explicit_compression, false };
|
||||
|
||||
auto backing_memory_creator =
|
||||
m_allocator.make_unique<external_image_backing_memory_creator>(m_device_data, *m_wsi_allocator, wsialloc_args);
|
||||
diff --git a/wsi/extensions/external_memory_extension.cpp b/wsi/extensions/external_memory_extension.cpp
|
||||
index c5bd7e8..f52c760 100644
|
||||
--- a/wsi/extensions/external_memory_extension.cpp
|
||||
+++ b/wsi/extensions/external_memory_extension.cpp
|
||||
@@ -35,10 +35,46 @@
|
||||
|
||||
#include "external_memory_extension.hpp"
|
||||
#include "wsi/image_usage.hpp"
|
||||
+#include "wsi/wsi_factory.hpp"
|
||||
+
|
||||
+#include <drm_fourcc.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
+bool linear_dma_buf_exportable(VkPhysicalDevice physical_device, const VkImageCreateInfo &image_create_info)
|
||||
+{
|
||||
+ VkPhysicalDeviceExternalImageFormatInfoKHR external_info = {};
|
||||
+ external_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
|
||||
+ external_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
+
|
||||
+ VkPhysicalDeviceImageFormatInfo2KHR image_info = {};
|
||||
+ image_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
|
||||
+ image_info.pNext = &external_info;
|
||||
+ image_info.format = image_create_info.format;
|
||||
+ image_info.type = image_create_info.imageType;
|
||||
+ image_info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
+ image_info.usage = image_create_info.usage;
|
||||
+ image_info.flags = image_create_info.flags;
|
||||
+
|
||||
+ VkExternalImageFormatPropertiesKHR external_props = {};
|
||||
+ external_props.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
|
||||
+ VkImageFormatProperties2KHR format_props = {};
|
||||
+ format_props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
|
||||
+ format_props.pNext = &external_props;
|
||||
+
|
||||
+ auto &instance_data = layer::instance_private_data::get(physical_device);
|
||||
+ if (instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(physical_device, &image_info, &format_props) !=
|
||||
+ VK_SUCCESS)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+ const auto &limits = format_props.imageFormatProperties.maxExtent;
|
||||
+ return (external_props.externalMemoryProperties.externalMemoryFeatures &
|
||||
+ VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT) != 0 &&
|
||||
+ limits.width >= image_create_info.extent.width && limits.height >= image_create_info.extent.height;
|
||||
+}
|
||||
+
|
||||
VkImageCreateFlags2KHR get_image_create_flags(const VkImageCreateInfo &image_create_info) noexcept
|
||||
{
|
||||
const auto *flags2 = util::find_extension<VkImageCreateFlags2CreateInfoKHR>(
|
||||
@@ -284,6 +320,39 @@ std::variant<VkResult, util::unique_ptr<swapchain_image_create_external_memory>>
|
||||
util::vector<VkDrmFormatModifierPropertiesEXT> drm_format_props(
|
||||
util::allocator(allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
|
||||
|
||||
+ util::extension_list device_extensions{ util::allocator(allocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND) };
|
||||
+ TRY_LOG_CALL(get_available_device_extensions(physical_device, device_extensions));
|
||||
+ if (!device_extensions.contains(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME))
|
||||
+ {
|
||||
+ const uint32_t fourcc = util::drm::vk_to_drm_format(image_create_info.format);
|
||||
+ bool linear_offered = false;
|
||||
+ for (const auto &format : surface_formats)
|
||||
+ {
|
||||
+ linear_offered = linear_offered || (format.fourcc == fourcc && format.modifier == DRM_FORMAT_MOD_LINEAR);
|
||||
+ }
|
||||
+ if (!linear_offered || !linear_dma_buf_exportable(physical_device, image_create_info))
|
||||
+ {
|
||||
+ WSI_LOG_ERROR("The driver has no DRM format modifiers and cannot export a LINEAR image the compositor accepts.");
|
||||
+ return VK_ERROR_INITIALIZATION_FAILED;
|
||||
+ }
|
||||
+
|
||||
+ wsialloc_allocate_result exported = {};
|
||||
+ exported.format.fourcc = fourcc;
|
||||
+ exported.format.modifier = DRM_FORMAT_MOD_LINEAR;
|
||||
+ for (auto &fd : exported.buffer_fds)
|
||||
+ {
|
||||
+ fd = -1;
|
||||
+ }
|
||||
+ auto ext_memory = allocator.make_unique<swapchain_image_create_external_memory>(
|
||||
+ image_create_info, compression, exported, util::vector<VkSubresourceLayout>(allocator), drm_format_props,
|
||||
+ true);
|
||||
+ if (ext_memory == nullptr)
|
||||
+ {
|
||||
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
+ }
|
||||
+ return ext_memory;
|
||||
+ }
|
||||
+
|
||||
TRY_LOG_CALL(get_surface_compatible_formats(image_create_info, physical_device, compression, surface_formats,
|
||||
importable_formats, exportable_modifiers, drm_format_props));
|
||||
|
||||
@@ -316,7 +385,7 @@ std::variant<VkResult, util::unique_ptr<swapchain_image_create_external_memory>>
|
||||
swapchain_image_create_external_memory::swapchain_image_create_external_memory(
|
||||
const VkImageCreateInfo &image_create_info, std::optional<image_create_compression_control> &compression,
|
||||
wsialloc_allocate_result wsialloc_selected_format, util::vector<VkSubresourceLayout> plane_layouts,
|
||||
- const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props)
|
||||
+ const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props, bool icd_export)
|
||||
: m_wsialloc_selected_format(wsialloc_selected_format)
|
||||
, m_create_flags()
|
||||
, m_create_extent()
|
||||
@@ -325,6 +394,7 @@ swapchain_image_create_external_memory::swapchain_image_create_external_memory(
|
||||
, m_plane_layouts(std::move(plane_layouts))
|
||||
, m_drm_mod_info()
|
||||
, m_external_info()
|
||||
+ , m_icd_export(icd_export)
|
||||
{
|
||||
if (compression)
|
||||
{
|
||||
@@ -354,6 +424,16 @@ VkResult swapchain_image_create_external_memory::extend_image_create_info(VkImag
|
||||
{
|
||||
assert(image_create_info != nullptr);
|
||||
|
||||
+ if (m_icd_export)
|
||||
+ {
|
||||
+ m_external_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
|
||||
+ m_external_info.pNext = image_create_info->pNext;
|
||||
+ m_external_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
+ image_create_info->pNext = &m_external_info;
|
||||
+ image_create_info->tiling = VK_IMAGE_TILING_LINEAR;
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
if (m_wsialloc_selected_format.is_disjoint)
|
||||
{
|
||||
auto *image_create_flags2 = util::find_extension<VkImageCreateFlags2CreateInfoKHR>(
|
||||
@@ -389,7 +469,7 @@ external_image_create_info swapchain_image_create_external_memory::get_external_
|
||||
assert(m_wsialloc_selected_format.format.fourcc != 0);
|
||||
|
||||
external_image_create_info image_info = { m_wsialloc_selected_format.format, m_create_flags, m_create_extent,
|
||||
- m_use_fixed_rate_compression };
|
||||
+ m_use_fixed_rate_compression, m_icd_export };
|
||||
return image_info;
|
||||
}
|
||||
|
||||
diff --git a/wsi/extensions/external_memory_extension.hpp b/wsi/extensions/external_memory_extension.hpp
|
||||
index 508de48..c4aad21 100644
|
||||
--- a/wsi/extensions/external_memory_extension.hpp
|
||||
+++ b/wsi/extensions/external_memory_extension.hpp
|
||||
@@ -52,6 +52,7 @@ struct external_image_create_info
|
||||
VkImageCreateFlags2KHR flags;
|
||||
VkExtent3D extent;
|
||||
bool explicit_compression;
|
||||
+ bool icd_export;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,12 +69,14 @@ public:
|
||||
* @param wsialloc_selected_format Selected format for allocation by WSIAlloc.
|
||||
* @param plane_layouts Plane layouts description for each plane.
|
||||
* @param drm_format_props A list of all device supported VkDrmFormatModifierPropertiesEXT.
|
||||
+ * @param icd_export Whether the driver allocates and exports a LINEAR image instead of importing one.
|
||||
*/
|
||||
swapchain_image_create_external_memory(const VkImageCreateInfo &image_create_info,
|
||||
std::optional<image_create_compression_control> &compression,
|
||||
wsialloc_allocate_result wsialloc_selected_format,
|
||||
util::vector<VkSubresourceLayout> plane_layouts,
|
||||
- const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props);
|
||||
+ const util::vector<VkDrmFormatModifierPropertiesEXT> &drm_format_props,
|
||||
+ bool icd_export = false);
|
||||
|
||||
/**
|
||||
* @brief Create swapchain_image_create_external_memory object.
|
||||
@@ -117,6 +120,7 @@ private:
|
||||
util::vector<VkSubresourceLayout> m_plane_layouts;
|
||||
VkImageDrmFormatModifierExplicitCreateInfoEXT m_drm_mod_info;
|
||||
VkExternalMemoryImageCreateInfoKHR m_external_info;
|
||||
+ bool m_icd_export;
|
||||
};
|
||||
|
||||
} /* namespace wsi */
|
||||
diff --git a/wsi/external_memory.cpp b/wsi/external_memory.cpp
|
||||
index 4c454fd..8e23412 100644
|
||||
--- a/wsi/external_memory.cpp
|
||||
+++ b/wsi/external_memory.cpp
|
||||
@@ -51,6 +51,18 @@ external_memory::~external_memory()
|
||||
}
|
||||
|
||||
auto &device_data = layer::device_private_data::get(m_device);
|
||||
+ if (m_memory_type == wsi_memory_type::ICD_EXPORTED_DMA_BUF)
|
||||
+ {
|
||||
+ if (m_memories[0] != VK_NULL_HANDLE)
|
||||
+ {
|
||||
+ device_data.disp.FreeMemory(m_device, m_memories[0], m_allocator.get_original_callbacks());
|
||||
+ }
|
||||
+ if (m_buffer_fds[0] >= 0)
|
||||
+ {
|
||||
+ close(m_buffer_fds[0]);
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
for (uint32_t plane = 0; plane < get_num_planes(); plane++)
|
||||
{
|
||||
auto &memory = m_memories[plane];
|
||||
@@ -158,6 +170,10 @@ VkResult external_memory::import_plane_memory(int fd, VkDeviceMemory *memory)
|
||||
VkResult external_memory::bind_swapchain_image_memory(const VkImage &image)
|
||||
{
|
||||
auto &device_data = layer::device_private_data::get(m_device);
|
||||
+ if (m_memory_type == wsi_memory_type::ICD_EXPORTED_DMA_BUF)
|
||||
+ {
|
||||
+ return device_data.disp.BindImageMemory(m_device, image, m_memories[0], 0);
|
||||
+ }
|
||||
if (is_disjoint())
|
||||
{
|
||||
util::vector<VkBindImageMemoryInfo> bind_img_mem_infos(m_allocator);
|
||||
@@ -324,6 +340,57 @@ VkResult external_memory::allocate_host_visible_and_bind(const VkImage &image, c
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
+VkResult external_memory::export_memory_and_bind_swapchain_image(const VkImage &image)
|
||||
+{
|
||||
+ auto &device_data = layer::device_private_data::get(m_device);
|
||||
+
|
||||
+ VkMemoryRequirements mem_requirements;
|
||||
+ device_data.disp.GetImageMemoryRequirements(m_device, image, &mem_requirements);
|
||||
+
|
||||
+ m_memory_type = wsi_memory_type::ICD_EXPORTED_DMA_BUF;
|
||||
+ m_optimal_props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
+ m_required_props = 0;
|
||||
+ uint32_t memory_type_index = 0;
|
||||
+ TRY_LOG_CALL(find_host_visible_memory_type(mem_requirements, &memory_type_index));
|
||||
+
|
||||
+ VkMemoryDedicatedAllocateInfo dedicated_info = {};
|
||||
+ dedicated_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
||||
+ dedicated_info.image = image;
|
||||
+
|
||||
+ VkExportMemoryAllocateInfo export_info = {};
|
||||
+ export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;
|
||||
+ export_info.pNext = &dedicated_info;
|
||||
+ export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
+
|
||||
+ VkMemoryAllocateInfo alloc_info = {};
|
||||
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
+ alloc_info.pNext = &export_info;
|
||||
+ alloc_info.allocationSize = mem_requirements.size;
|
||||
+ alloc_info.memoryTypeIndex = memory_type_index;
|
||||
+
|
||||
+ TRY_LOG(device_data.disp.AllocateMemory(m_device, &alloc_info, m_allocator.get_original_callbacks(), &m_memories[0]),
|
||||
+ "Failed to allocate exportable swapchain memory");
|
||||
+ TRY_LOG(device_data.disp.BindImageMemory(m_device, image, m_memories[0], 0),
|
||||
+ "Failed to bind exportable swapchain memory");
|
||||
+
|
||||
+ VkMemoryGetFdInfoKHR fd_info = {};
|
||||
+ fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
|
||||
+ fd_info.memory = m_memories[0];
|
||||
+ fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
+ TRY_LOG(device_data.disp.GetMemoryFdKHR(m_device, &fd_info, &m_buffer_fds[0]),
|
||||
+ "Failed to export swapchain memory as a dma-buf");
|
||||
+
|
||||
+ VkImageSubresource subresource = {};
|
||||
+ subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
+ VkSubresourceLayout layout = {};
|
||||
+ device_data.disp.GetImageSubresourceLayout(m_device, image, &subresource, &layout);
|
||||
+ m_offsets[0] = static_cast<uint32_t>(layout.offset);
|
||||
+ m_strides[0] = static_cast<int>(layout.rowPitch);
|
||||
+ m_num_planes = 1;
|
||||
+ m_num_memories = 1;
|
||||
+ return VK_SUCCESS;
|
||||
+}
|
||||
+
|
||||
VkResult external_memory::allocate_and_bind_image(const VkImage &image, const VkImageCreateInfo &image_info)
|
||||
{
|
||||
switch (m_memory_type)
|
||||
diff --git a/wsi/external_memory.hpp b/wsi/external_memory.hpp
|
||||
index 9b2e836..9837d50 100644
|
||||
--- a/wsi/external_memory.hpp
|
||||
+++ b/wsi/external_memory.hpp
|
||||
@@ -50,6 +50,7 @@ enum class wsi_memory_type
|
||||
{
|
||||
EXTERNAL_DMA_BUF, /* External file descriptors (dma-buf import; the default). */
|
||||
HOST_VISIBLE, /* Host-accessible memory, allocated and mapped by the layer. */
|
||||
+ ICD_EXPORTED_DMA_BUF, /* Driver-allocated memory exported as a dma-buf. */
|
||||
};
|
||||
|
||||
class external_memory
|
||||
@@ -192,6 +193,13 @@ public:
|
||||
*/
|
||||
VkResult import_memory_and_bind_swapchain_image(const VkImage &image);
|
||||
|
||||
+ /**
|
||||
+ * @brief Allocates dedicated driver memory for a LINEAR swapchain image, binds it and exports it as a dma-buf.
|
||||
+ *
|
||||
+ * The plane offset and stride come from the driver's own layout of @p image.
|
||||
+ */
|
||||
+ VkResult export_memory_and_bind_swapchain_image(const VkImage &image);
|
||||
+
|
||||
/* --- RK3588 host-visible memory (X11 SHM backend) --- */
|
||||
|
||||
/** @brief Configure this object to allocate host-visible memory for the image. */
|
||||
diff --git a/wsi/image_backing_memory_external.cpp b/wsi/image_backing_memory_external.cpp
|
||||
index 636b706..836429e 100644
|
||||
--- a/wsi/image_backing_memory_external.cpp
|
||||
+++ b/wsi/image_backing_memory_external.cpp
|
||||
@@ -37,6 +37,11 @@ namespace wsi
|
||||
|
||||
VkResult image_backing_memory_external::allocate()
|
||||
{
|
||||
+ if (m_create_info.icd_export)
|
||||
+ {
|
||||
+ return VK_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
allocation_params params = { (m_create_info.flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0,
|
||||
m_create_info.extent,
|
||||
&m_create_info.selected_format,
|
||||
@@ -72,6 +77,12 @@ VkResult image_backing_memory_external::allocate()
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
+VkResult image_backing_memory_external::export_and_bind(VkImage image)
|
||||
+{
|
||||
+ TRY_LOG(m_external_mem.export_memory_and_bind_swapchain_image(image), "Failed to export swapchain image memory");
|
||||
+ return VK_SUCCESS;
|
||||
+}
|
||||
+
|
||||
VkResult image_backing_memory_external::import_and_bind(VkImage image)
|
||||
{
|
||||
TRY_LOG(m_external_mem.import_memory_and_bind_swapchain_image(image),
|
||||
diff --git a/wsi/image_backing_memory_external.hpp b/wsi/image_backing_memory_external.hpp
|
||||
index 1182acc..e40285d 100644
|
||||
--- a/wsi/image_backing_memory_external.hpp
|
||||
+++ b/wsi/image_backing_memory_external.hpp
|
||||
@@ -53,6 +53,7 @@ struct wsialloc_create_info_args
|
||||
VkImageCreateFlags2KHR flags;
|
||||
VkExtent3D extent;
|
||||
bool explicit_compression;
|
||||
+ bool icd_export;
|
||||
};
|
||||
|
||||
class image_backing_memory_external : public image_backing_memory
|
||||
@@ -87,6 +88,15 @@ public:
|
||||
*/
|
||||
VkResult import_and_bind(VkImage image);
|
||||
|
||||
+ /** @brief Whether the image is allocated by the driver and exported instead of imported. */
|
||||
+ bool is_icd_exported() const
|
||||
+ {
|
||||
+ return m_create_info.icd_export;
|
||||
+ }
|
||||
+
|
||||
+ /** @brief Allocate driver memory for @p image, bind it and export it as a dma-buf. */
|
||||
+ VkResult export_and_bind(VkImage image);
|
||||
+
|
||||
/**
|
||||
* @brief Bind Vulkan image
|
||||
*
|
||||
diff --git a/wsi/wayland/swapchain.cpp b/wsi/wayland/swapchain.cpp
|
||||
index 9215b52..5fe16f7 100644
|
||||
--- a/wsi/wayland/swapchain.cpp
|
||||
+++ b/wsi/wayland/swapchain.cpp
|
||||
@@ -304,7 +304,8 @@ VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain
|
||||
|
||||
wsialloc_create_info_args wsialloc_args = { external_image_create_info.selected_format,
|
||||
external_image_create_info.flags, external_image_create_info.extent,
|
||||
- external_image_create_info.explicit_compression };
|
||||
+ external_image_create_info.explicit_compression,
|
||||
+ external_image_create_info.icd_export };
|
||||
|
||||
auto backing_memory_creator =
|
||||
m_allocator.make_unique<external_image_backing_memory_creator>(m_device_data, *m_wsi_allocator, wsialloc_args);
|
||||
@@ -414,6 +415,10 @@ VkResult swapchain::allocate_and_bind_swapchain_image(swapchain_image &image)
|
||||
|
||||
auto &backing_memory = swapchain_image_factory::get_backing_memory_from_image<image_backing_memory_external>(image);
|
||||
TRY_LOG_CALL(backing_memory.allocate());
|
||||
+ if (backing_memory.is_icd_exported())
|
||||
+ {
|
||||
+ TRY_LOG_CALL(backing_memory.export_and_bind(image.get_image()));
|
||||
+ }
|
||||
|
||||
wayland_owner<wl_buffer> buffer = nullptr;
|
||||
buffer = create_wl_buffer(backing_memory);
|
||||
@@ -423,7 +428,10 @@ VkResult swapchain::allocate_and_bind_swapchain_image(swapchain_image &image)
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
- TRY_LOG_CALL(backing_memory.import_and_bind(image.get_image()));
|
||||
+ if (!backing_memory.is_icd_exported())
|
||||
+ {
|
||||
+ TRY_LOG_CALL(backing_memory.import_and_bind(image.get_image()));
|
||||
+ }
|
||||
|
||||
auto image_data = m_allocator.make_unique<wayland_image_data>(std::move(buffer));
|
||||
if (image_data == nullptr)
|
||||
diff --git a/wsi/wsi_factory.cpp b/wsi/wsi_factory.cpp
|
||||
index 87795dc..53666be 100644
|
||||
--- a/wsi/wsi_factory.cpp
|
||||
+++ b/wsi/wsi_factory.cpp
|
||||
@@ -216,6 +216,12 @@ VkResult add_device_extensions_required_by_layer(VkPhysicalDevice phys_dev,
|
||||
TRY_LOG(props->get_required_device_extensions(extensions_required_by_layer, api_version),
|
||||
"Failed to acquire required device extensions");
|
||||
|
||||
+ /* Without DRM format modifiers, Wayland swapchains export driver-allocated LINEAR images instead. */
|
||||
+ if (!available_device_extensions.contains(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME))
|
||||
+ {
|
||||
+ extensions_required_by_layer.remove(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
|
||||
+ }
|
||||
+
|
||||
bool supported = available_device_extensions.contains(extensions_required_by_layer);
|
||||
if (!supported)
|
||||
{
|
||||
diff --git a/wsi/x11/swapchain.cpp b/wsi/x11/swapchain.cpp
|
||||
index a857035..fbe13c9 100644
|
||||
--- a/wsi/x11/swapchain.cpp
|
||||
+++ b/wsi/x11/swapchain.cpp
|
||||
@@ -442,11 +442,16 @@ VkResult swapchain::init_image_factory(const VkSwapchainCreateInfoKHR &swapchain
|
||||
std::get<util::unique_ptr<swapchain_image_create_external_memory>>(std::move(sc_img_create_ext_mem_result));
|
||||
|
||||
auto external_image_create_info = sc_img_create_ext_mem->get_external_image_create_info();
|
||||
+ if (external_image_create_info.icd_export)
|
||||
+ {
|
||||
+ WSI_LOG_ERROR("DRI3 presentation needs importable DRM format modifiers");
|
||||
+ return VK_ERROR_INITIALIZATION_FAILED;
|
||||
+ }
|
||||
TRY_LOG_CALL(image_handle_creator->add_extension(std::move(sc_img_create_ext_mem)));
|
||||
|
||||
wsialloc_create_info_args wsialloc_args = { external_image_create_info.selected_format,
|
||||
external_image_create_info.flags, external_image_create_info.extent,
|
||||
- external_image_create_info.explicit_compression };
|
||||
+ external_image_create_info.explicit_compression, false };
|
||||
auto backing_memory_creator =
|
||||
m_allocator.make_unique<external_image_backing_memory_creator>(m_device_data, *m_wsi_allocator, wsialloc_args);
|
||||
if (backing_memory_creator == nullptr)
|
||||
@@ -0,0 +1,84 @@
|
||||
diff --git a/wsi/wayland/surface.hpp b/wsi/wayland/surface.hpp
|
||||
index d49a48f..0e0139e 100644
|
||||
--- a/wsi/wayland/surface.hpp
|
||||
+++ b/wsi/wayland/surface.hpp
|
||||
@@ -28,9 +28,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
#include <sys/types.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
diff --git a/wsi/wayland/surface_properties.cpp b/wsi/wayland/surface_properties.cpp
|
||||
index 0783035..43ce2a7 100644
|
||||
--- a/wsi/wayland/surface_properties.cpp
|
||||
+++ b/wsi/wayland/surface_properties.cpp
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR 1
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <linux-dmabuf-unstable-v1-client-protocol.h>
|
||||
diff --git a/wsi/wayland/swapchain.hpp b/wsi/wayland/swapchain.hpp
|
||||
index e363bdc..4e8f6c3 100644
|
||||
--- a/wsi/wayland/swapchain.hpp
|
||||
+++ b/wsi/wayland/swapchain.hpp
|
||||
@@ -31,9 +31,6 @@ extern "C" {
|
||||
#include <vulkan/vk_icd.h>
|
||||
}
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
#include <wayland-client.h>
|
||||
#include <linux-dmabuf-unstable-v1-client-protocol.h>
|
||||
#include "surface.hpp"
|
||||
diff --git a/wsi/wayland/wl_helpers.hpp b/wsi/wayland/wl_helpers.hpp
|
||||
index 5bf507a..4cd7826 100644
|
||||
--- a/wsi/wayland/wl_helpers.hpp
|
||||
+++ b/wsi/wayland/wl_helpers.hpp
|
||||
@@ -26,9 +26,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "util/custom_allocator.hpp"
|
||||
diff --git a/wsi/wayland/wl_object_owner.hpp b/wsi/wayland/wl_object_owner.hpp
|
||||
index a3befef..3d23e13 100644
|
||||
--- a/wsi/wayland/wl_object_owner.hpp
|
||||
+++ b/wsi/wayland/wl_object_owner.hpp
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
#include <wayland-client.h>
|
||||
#include <linux-dmabuf-unstable-v1-client-protocol.h>
|
||||
#include <linux-explicit-synchronization-unstable-v1-protocol.h>
|
||||
diff --git a/wsi/x11/swapchain.hpp b/wsi/x11/swapchain.hpp
|
||||
index 3ddb38f..f1c4a8a 100644
|
||||
--- a/wsi/x11/swapchain.hpp
|
||||
+++ b/wsi/x11/swapchain.hpp
|
||||
@@ -37,9 +37,6 @@ extern "C" {
|
||||
#include <vulkan/vulkan.h>
|
||||
}
|
||||
|
||||
-#ifndef __STDC_VERSION__
|
||||
-#define __STDC_VERSION__ 0
|
||||
-#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/util/wsialloc/wsialloc_dma_buf_heaps.c b/util/wsialloc/wsialloc_dma_buf_heaps.c
|
||||
index 5339166..fd97587 100644
|
||||
--- a/util/wsialloc/wsialloc_dma_buf_heaps.c
|
||||
+++ b/util/wsialloc/wsialloc_dma_buf_heaps.c
|
||||
@@ -120,7 +120,7 @@ wsialloc_error wsialloc_new(wsialloc_allocator **allocator)
|
||||
return WSIALLOC_ERROR_NO_RESOURCE;
|
||||
}
|
||||
|
||||
- dma_buf_heaps->memory_fd = open("/dev/dma_heap/" STR(WSIALLOC_MEMORY_HEAP_NAME), O_RDWR);
|
||||
+ dma_buf_heaps->memory_fd = open("/dev/dma_heap/" STR(WSIALLOC_MEMORY_HEAP_NAME), O_RDONLY | O_CLOEXEC);
|
||||
dma_buf_heaps->protected_fd = -1;
|
||||
|
||||
if (dma_buf_heaps->memory_fd < 0)
|
||||
@@ -0,0 +1,33 @@
|
||||
diff --git a/layer/layer.cpp b/layer/layer.cpp
|
||||
index 6bda56c..4b6eaa7 100644
|
||||
--- a/layer/layer.cpp
|
||||
+++ b/layer/layer.cpp
|
||||
@@ -1269,10 +1269,8 @@ wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POS
|
||||
GET_PROC_ADDR(vkGetSwapchainImagesKHR);
|
||||
GET_PROC_ADDR(vkAcquireNextImageKHR);
|
||||
GET_PROC_ADDR(vkQueuePresentKHR);
|
||||
- if (device_data.is_device_extension_enabled(VK_KHR_DEVICE_GROUP_EXTENSION_NAME) || core_1_1)
|
||||
- {
|
||||
- GET_PROC_ADDR(vkAcquireNextImage2KHR);
|
||||
- }
|
||||
+ /* Gamescope's WSI layer acquires through vkAcquireNextImage2KHR even on Vulkan 1.0 devices. */
|
||||
+ GET_PROC_ADDR(vkAcquireNextImage2KHR);
|
||||
if (core_1_1)
|
||||
{
|
||||
GET_PROC_ADDR(vkGetDeviceGroupSurfacePresentModesKHR);
|
||||
diff --git a/layer/swapchain_api.cpp b/layer/swapchain_api.cpp
|
||||
index 0576d88..0e53a1a 100644
|
||||
--- a/layer/swapchain_api.cpp
|
||||
+++ b/layer/swapchain_api.cpp
|
||||
@@ -375,6 +375,11 @@ wsi_layer_vkAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKH
|
||||
|
||||
if (!device_data.layer_owns_swapchain(pAcquireInfo->swapchain))
|
||||
{
|
||||
+ if (!device_data.disp.get_fn<PFN_vkAcquireNextImage2KHR>("vkAcquireNextImage2KHR").has_value())
|
||||
+ {
|
||||
+ return device_data.disp.AcquireNextImageKHR(device, pAcquireInfo->swapchain, pAcquireInfo->timeout,
|
||||
+ pAcquireInfo->semaphore, pAcquireInfo->fence, pImageIndex);
|
||||
+ }
|
||||
return device_data.disp.AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import runpy
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
BIN = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin"
|
||||
COMPAT = runpy.run_path(str(BIN / "steam-compatibility"))
|
||||
SHOW = 'echo "DISABLE_GAMESCOPE_WSI=${DISABLE_GAMESCOPE_WSI-unset} WSI_X11_FORCE_SHM=${WSI_X11_FORCE_SHM-unset}"\n'
|
||||
SESSION_DEFAULT = "DISABLE_GAMESCOPE_WSI=1 WSI_X11_FORCE_SHM=1"
|
||||
|
||||
|
||||
class QualcommPresentTest(unittest.TestCase):
|
||||
def present(self, **extra):
|
||||
env = dict(os.environ, DISABLE_GAMESCOPE_WSI="1", WSI_X11_FORCE_SHM="1")
|
||||
env.pop("DROIDDECK_SYSTEM_QUALCOMM", None)
|
||||
env.pop("BL_QUALCOMM_SHM", None)
|
||||
env.update(extra)
|
||||
script = COMPAT["BL_PRESENT_SETUP"] + "\nbl_present\n" + SHOW
|
||||
return subprocess.run(["bash", "-c", script], env=env, capture_output=True, text=True, check=True).stdout.strip()
|
||||
|
||||
def test_qualcomm_games_present_through_gamescope_wsi(self):
|
||||
self.assertEqual(self.present(DROIDDECK_SYSTEM_QUALCOMM="1"), "DISABLE_GAMESCOPE_WSI=unset WSI_X11_FORCE_SHM=unset")
|
||||
|
||||
def test_turnip_sessions_and_the_shm_override_keep_the_session_default(self):
|
||||
for extra in ({}, {"DROIDDECK_SYSTEM_QUALCOMM": "0"}, {"DROIDDECK_SYSTEM_QUALCOMM": "1", "BL_QUALCOMM_SHM": "1"}):
|
||||
with self.subTest(extra=extra):
|
||||
self.assertEqual(self.present(**extra), SESSION_DEFAULT)
|
||||
|
||||
def test_every_proton_launch_selects_the_present_path(self):
|
||||
self.assertIn("bl_present()", COMPAT["LAUNCHER_SH"])
|
||||
self.assertEqual((BIN / "steam-compatibility").read_text().count("BL_LIBPATH_SETUP + BL_PRESENT_SETUP"), 2)
|
||||
for script in (COMPAT["LAUNCHER_SH"], COMPAT["EXTRA_WRAPPER_SH"] % "proton"):
|
||||
lines = [line.strip() for line in script.splitlines()]
|
||||
launches = [i for i, line in enumerate(lines) if line.startswith("bl_run") and "droiddeck-game-env" in line]
|
||||
self.assertTrue(launches)
|
||||
for index in launches:
|
||||
self.assertIn("bl_present", lines[max(0, index - 6):index])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -115,6 +115,47 @@ class QualcommRuntimeTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(FileNotFoundError, 'Build private Gamescope'):
|
||||
runtime.stage(output, cache, lock)
|
||||
|
||||
def test_built_wsi_members_come_from_the_pinned_local_build(self):
|
||||
bridge, binary, license_text, layer = b'bridge', b'gamescope', b'MIT', b'Wayland WSI layer'
|
||||
def package(members):
|
||||
output = io.BytesIO()
|
||||
with zipfile.ZipFile(output, 'w') as archive:
|
||||
for name, data in members:
|
||||
archive.writestr(name, data)
|
||||
return output.getvalue()
|
||||
gamescope_archive = package([('gamescope', binary)])
|
||||
wsi_archive = package([('LICENSE', license_text), ('layer.so', b'X11-only layer')])
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
built, cache = root / 'build/qualcomm-wsi', root / 'cache'
|
||||
built.mkdir(parents=True)
|
||||
cache.mkdir()
|
||||
(built / 'layer.so').write_bytes(layer)
|
||||
lock = self.lock(bridge)
|
||||
lock['gamescope'] = self.lock(gamescope_archive) | dict(member='gamescope', member_sha256=hashlib.sha256(binary).hexdigest())
|
||||
lock['wsi'] = self.lock(wsi_archive) | dict(build=dict(directory='build/qualcomm-wsi'), files=[
|
||||
dict(name='LICENSE', path='licenses/wsi-LICENSE', size=len(license_text), sha256=hashlib.sha256(license_text).hexdigest()),
|
||||
dict(name='layer.so', path='wsi/layer.so', size=len(layer), sha256=hashlib.sha256(layer).hexdigest(), built=True)])
|
||||
for data, entry in [(bridge, lock), (gamescope_archive, lock['gamescope']), (wsi_archive, lock['wsi'])]:
|
||||
(cache / (entry['sha256'] + '.tzst')).write_bytes(data)
|
||||
with patch.object(runtime, '__file__', str(root / 'tools/qualcomm/fetch_runtime.py')):
|
||||
output = root / 'output'
|
||||
runtime.stage(output, cache, lock)
|
||||
self.assertEqual((output / 'wsi/layer.so').read_bytes(), layer)
|
||||
self.assertEqual((output / 'licenses/wsi-LICENSE').read_bytes(), license_text)
|
||||
(built / 'layer.so').write_bytes(b'tampered layer')
|
||||
with self.assertRaisesRegex(ValueError, 'WSI member'):
|
||||
runtime.stage(root / 'tampered', cache, lock)
|
||||
(built / 'layer.so').unlink()
|
||||
with self.assertRaisesRegex(FileNotFoundError, 'build_wsi.py'):
|
||||
runtime.stage(root / 'missing', cache, lock)
|
||||
lock['wsi']['files'][1]['name'] = 'nested/layer.so'
|
||||
with self.assertRaisesRegex(ValueError, 'build member'):
|
||||
runtime.stage(root / 'nested', cache, lock)
|
||||
lock['wsi']['build']['directory'] = '../outside'
|
||||
with self.assertRaisesRegex(ValueError, 'build path'):
|
||||
runtime.stage(root / 'outside', cache, lock)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user