mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dc30f0293 | ||
|
|
8ab0c1041e | ||
|
|
de84fda5cf | ||
|
|
b81f4a0cfa | ||
|
|
5ae646bae8 | ||
|
|
ed7f532db4 | ||
|
|
ba59724632 |
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# Patch context includes the indentation and blank lines of the source being patched.
|
||||
tools/gamescope/patches/*.patch -whitespace
|
||||
@@ -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);
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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,18 @@ 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. */
|
||||
/* Keep the existing 1.3 request unless the selected loader positively reports an
|
||||
* older usable API. Custom ICDs may omit this optional global entry point. */
|
||||
uint32_t loader_api = VK_API_VERSION_1_3;
|
||||
if (g_vk.EnumerateInstanceVersion) {
|
||||
uint32_t reported_api = VK_API_VERSION_1_3;
|
||||
if (g_vk.EnumerateInstanceVersion(&reported_api) == VK_SUCCESS &&
|
||||
reported_api >= VK_API_VERSION_1_1)
|
||||
loader_api = reported_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 +458,26 @@ 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])) {
|
||||
g_dmabuf_import = 0;
|
||||
banner_log("gpu", "driver has no %s; shared-memory clients remain available", import_exts[i]);
|
||||
}
|
||||
}
|
||||
if (g_dmabuf_import)
|
||||
for (unsigned i = 0; i < sizeof(import_exts) / sizeof(import_exts[0]); i++)
|
||||
dev_exts[n_dev_exts++] = 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 +554,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 +916,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 +958,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). */
|
||||
|
||||
@@ -24,11 +24,12 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* The Vulkan driver the in-app compositor runs on. It has to be Turnip: the system Adreno driver
|
||||
* The Vulkan driver the in-app compositor runs on. Adreno needs Turnip: its system driver
|
||||
* does not implement VK_EXT_image_drm_format_modifier, so importing the dma-bufs gamescope hands
|
||||
* over fails and the session renders nothing. Two builds ship in the apk, one per Adreno
|
||||
* generation, and the GPU decides which is unpacked - unless the user has imported an AdrenoTools
|
||||
* zip of their own and chosen it, which then wins.
|
||||
* zip of their own and chosen it, which then wins. A positively identified Mali kbase GPU
|
||||
* instead uses system Vulkan for shared-memory presentation; this does not supply a Linux ICD.
|
||||
*
|
||||
* <p>The guest's own Turnip is a different copy entirely - a glibc build inside the rootfs, or an
|
||||
* imported one ({@link LinuxVulkanDriverManager}). This one is the bionic build the app process
|
||||
@@ -338,6 +339,12 @@ public final class TurnipDriver {
|
||||
if (forced.startsWith("a7")) return DRIVER_A7XX;
|
||||
}
|
||||
String model = gpuModel();
|
||||
// A Mali kbase node is positive evidence. Unknown hardware keeps the existing default.
|
||||
if (model == null && new File("/dev/mali0").exists()
|
||||
&& !com.droiddeck.launcher.core.DeviceSupport.INSTANCE.adreno()) {
|
||||
Log.i(TAG, "Mali kbase GPU: using system Vulkan for the compositor");
|
||||
return null;
|
||||
}
|
||||
Log.i(TAG, "gpu model: " + (model == null ? "unknown" : model));
|
||||
if (model != null) {
|
||||
// "Adreno750", "adreno_830" - the generation is the first digit of the three.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Opt-in software Steam compatibility
|
||||
|
||||
This change provides an explicit software fallback for the Linux Steam/Gamescope
|
||||
session. It does not enable fallback automatically or install a Mali Linux ICD.
|
||||
The Android presenter still uses Vulkan; a positively identified Mali kbase
|
||||
GPU uses the system Android driver. Adreno and unknown devices keep the existing
|
||||
selection, and explicit Android driver choices retain precedence.
|
||||
|
||||
## Enable and recover
|
||||
|
||||
Set `/sdcard/Download/droiddeck-env` to include:
|
||||
|
||||
```sh
|
||||
BL_STEAM_SOFTWARE=1
|
||||
```
|
||||
|
||||
Remove that entry or set it to `0` to return to normal session rendering. No
|
||||
preference is migrated or enabled by this branch. Only the exact value `1`
|
||||
activates the fallback in the scripts and native Gamescope.
|
||||
|
||||
The fallback selects Lavapipe/llvmpipe, Gamescope's SDL Wayland swapchain,
|
||||
software Steam CEF/SDL rendering, and disables mangoapp for that session.
|
||||
An explicitly requested software session takes precedence over `BL_VK_DRIVER`;
|
||||
with the mode off, imported Linux drivers retain their existing behavior.
|
||||
Desktop Steam can also use this mode on a pixman desktop.
|
||||
|
||||
The optional present timing, local texture allocation, SHM refresh and ready
|
||||
fence handling in the new Gamescope patches apply only in this mode. Normal
|
||||
sessions retain the original dma-buf, buffer-cache and presentation paths.
|
||||
The Android presenter lowers its 1.3 request when the loader positively reports
|
||||
an older usable API. Custom ICDs that cannot report a version retain the original
|
||||
request. Dma-buf import is advertised only with the complete extension set.
|
||||
|
||||
## Validation
|
||||
|
||||
`tools/tests/test_software_graphics.py` exercises the actual outer session
|
||||
launcher with a capturing Gamescope executable: default accelerated backend,
|
||||
custom-driver selection, software-only activation, precedence, overlay, HDR,
|
||||
refresh rate and argument preservation. The native component is built against
|
||||
all existing patches and checked against the runtime's shared-library list.
|
||||
`gamescope-3.16.29-p6` is a versioned component release; the existing release
|
||||
download/checksum path is unchanged. There is no CI artifact pin or extra build
|
||||
workflow permission.
|
||||
|
||||
Local validation passed all 121 JVM tests and the macOS-applicable Python tests
|
||||
(62 discovered, 13 Linux-only skipped). The corrected native build passed
|
||||
[run 37161255955](https://github.com/Droid-Deck/DroidDeck/actions/runs/37161255955).
|
||||
The final code's APK build, Python and JVM tests, and native dependency closure
|
||||
check passed [run 37162182731](https://github.com/Droid-Deck/DroidDeck/actions/runs/37162182731).
|
||||
The installed Gamescope binary matches the component's contents
|
||||
(`2c92c155f7836a798c2a04eb1db5779e149680fcce888cd9a1ebe3fef7e5a2af`).
|
||||
|
||||
The cleanup build was installed in place on the wired Mali device using the
|
||||
release-signed debug APK. Automatic selection logged the positive Mali kbase
|
||||
identification, and Steam Big Picture reached the logged-in home screen through
|
||||
the new software-compatible Gamescope. Animated frames and input worked. A
|
||||
Geometry Wars retest stalled during Wine prefix startup; successful gameplay
|
||||
below belongs to the earlier experiment, not this cleanup validation. No
|
||||
physical Adreno device was updated during this cleanup.
|
||||
|
||||
Earlier experiment validation on the wired Unisoc T618 / Mali-G52 MC2 device
|
||||
(Android 12, kbase JM 11.31) reached Steam Big Picture sign-in and downloaded
|
||||
Geometry Wars: Retro Evolved. Its software-rendered menu, movement, shooting and
|
||||
pause worked, at roughly 3–5 displayed FPS. The per-game workaround was:
|
||||
|
||||
```sh
|
||||
DISABLE_GAMESCOPE_WSI=1 %command%
|
||||
```
|
||||
|
||||
This option is not applied globally. Software gameplay remains too slow for
|
||||
normal play. The previous experimental branch `mali-software-steam` retains
|
||||
the private PanVK build and GPU proof: compute, draw/readback, indexed draw and
|
||||
visible VKCube worked, but accelerated Geometry Wars lost the GPU device.
|
||||
That driver lab is intentionally outside this compatibility change.
|
||||
|
||||
Before a PR, validate default rendering on supported Adreno devices, explicit
|
||||
Android/Linux driver choices, overlays and supported HDR/frame-generation
|
||||
behavior as well as the opted-in wired Mali session. Passing launcher checks
|
||||
alone is not proof of physical Adreno regression safety.
|
||||
@@ -49,3 +49,13 @@ library list, before anything is published.
|
||||
|
||||
Sixteen more of Armada's patches are DRM/lease/HDR-on-KMS work for a native display, which this
|
||||
app's Wayland-hosted gamescope never reaches, or need a newer gamescope than the runtime has.
|
||||
|
||||
- `0114-software-swapchain-compat.patch` - this app: only with
|
||||
`BL_STEAM_SOFTWARE=1`, allow the SDL Vulkan swapchain to run without present-ID/wait
|
||||
support and keep local textures private. Explicit exports and dma-buf imports retain
|
||||
their allocation requirements. With the mode unset, extension requests, feature chains,
|
||||
texture allocation and present-wait pacing follow the existing path.
|
||||
- `0115-software-shm-commits.patch` - this app: only in that compatibility mode,
|
||||
refresh reused shared-memory buffers on every commit and treat their completed
|
||||
synchronous uploads as ready. Normal sessions retain buffer memoization and fence
|
||||
handling; dma-buf commits retain both in either mode.
|
||||
@@ -0,0 +1,157 @@
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
index 54e4efc..4777f7c 100644
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -57,6 +57,12 @@
|
||||
|
||||
#include "reshade_effect_manager.hpp"
|
||||
|
||||
+// Opt-in compatibility mode; an unset or other value preserves the normal backend.
|
||||
+const bool g_bSoftwareCompat = [] {
|
||||
+ const char *value = getenv( "BL_STEAM_SOFTWARE" );
|
||||
+ return value && strcmp( value, "1" ) == 0;
|
||||
+}();
|
||||
+
|
||||
extern bool g_bWasPartialComposite;
|
||||
extern bool g_bAllowDeferredBackend;
|
||||
|
||||
@@ -450,7 +456,16 @@ 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 ( g_bSoftwareCompat )
|
||||
+ {
|
||||
+ 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 +533,26 @@ bool CVulkanDevice::createDevice()
|
||||
m_bSupportsModifiers = false;
|
||||
}
|
||||
|
||||
+ m_bSupportsPresentWait = !g_bSoftwareCompat;
|
||||
+ if ( g_bSoftwareCompat && 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 +605,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 +684,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 +2120,11 @@ 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 )
|
||||
{
|
||||
+ // Software swapchains need local cursor, blank and wl_shm textures, not scan-out.
|
||||
+ // Explicit exports and dma-buf imports retain their original allocation requirements.
|
||||
+ if ( g_bSoftwareCompat && GetBackend()->UsesVulkanSwapchain() && !pDMA && !flags.bExportable )
|
||||
+ flags.bFlippable = false;
|
||||
+
|
||||
m_pBackendFb = std::move( pBackendFb );
|
||||
m_drmFormat = drmFormat;
|
||||
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
|
||||
@@ -3086,7 +3129,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 +3137,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 +3708,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..a784c29 100644
|
||||
--- a/src/rendervulkan.hpp
|
||||
+++ b/src/rendervulkan.hpp
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "shaders/descriptor_set_constants.h"
|
||||
|
||||
+extern const bool g_bSoftwareCompat;
|
||||
+
|
||||
class CVulkanCmdBuffer;
|
||||
|
||||
// 1: Fade Plane (Fade outs between switching focus)
|
||||
@@ -862,6 +864,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 +933,7 @@ protected:
|
||||
uint32_t m_uVendorID = 0;
|
||||
bool m_bHasDrmPrimaryDevId = false;
|
||||
bool m_bSupportsModifiers = false;
|
||||
+ bool m_bSupportsPresentWait = true;
|
||||
bool m_bInitialized = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
|
||||
index 18b799e..d854a68 100644
|
||||
--- a/src/steamcompmgr.cpp
|
||||
+++ b/src/steamcompmgr.cpp
|
||||
@@ -1850,30 +1850,32 @@ import_commit (
|
||||
commit->fifo = false;
|
||||
}
|
||||
|
||||
- if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
|
||||
+ struct wlr_dmabuf_attributes dmabuf = {0};
|
||||
+ // Only the software compatibility path refreshes private wl_shm copies.
|
||||
+ // Normal sessions retain the original memo lookup before probing the buffer.
|
||||
+ const bool bRefreshShm = g_bSoftwareCompat && !wlr_buffer_get_dmabuf( buf, &dmabuf );
|
||||
+ if ( !bRefreshShm )
|
||||
{
|
||||
- // Going from OwningRc -> Rc now.
|
||||
- commit->vulkanTex = pTexture;
|
||||
- return commit;
|
||||
+ if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
|
||||
+ {
|
||||
+ commit->vulkanTex = pTexture;
|
||||
+ return commit;
|
||||
+ }
|
||||
}
|
||||
|
||||
- struct wlr_dmabuf_attributes dmabuf = {0};
|
||||
gamescope::OwningRc<gamescope::IBackendFb> pBackendFb;
|
||||
if ( wlr_buffer_get_dmabuf( buf, &dmabuf ) )
|
||||
- {
|
||||
pBackendFb = GetBackend()->ImportDmabufToBackend( &dmabuf );
|
||||
- }
|
||||
|
||||
gamescope::OwningRc<CVulkanTexture> pOwnedTexture = vulkan_create_texture_from_wlr_buffer( buf, std::move( pBackendFb ) );
|
||||
|
||||
if ( pOwnedTexture == nullptr ) {
|
||||
- // Failed to create Vulkan texture from Wayland buffer for some reason.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
commit->vulkanTex = pOwnedTexture;
|
||||
-
|
||||
- s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
|
||||
+ if ( !bRefreshShm )
|
||||
+ s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
|
||||
|
||||
return commit;
|
||||
}
|
||||
@@ -8504,6 +8506,13 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
|
||||
{
|
||||
fence = dup( dmabuf.fd[0] );
|
||||
}
|
||||
+ else if ( g_bSoftwareCompat && GetBackend()->UsesVulkanSwapchain() &&
|
||||
+ !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();
|
||||
@@ -1,2 +1,2 @@
|
||||
GAMESCOPE_TAG=gamescope-3.16.29-p5
|
||||
GAMESCOPE_SHA256=188144e5564e020d0bc485a1f4f90e1c5fd61d0d1d6dd29db21e8b019036a5c6
|
||||
GAMESCOPE_TAG=gamescope-3.16.29-p6
|
||||
GAMESCOPE_SHA256=b34294a5c5358b8d0bb7a2d7d45c92595dad6d8257eacc5ef36b980b8b7931f0
|
||||
@@ -53,7 +53,15 @@ trap 'collect_steam_logs 2>/dev/null || true' TERM INT HUP
|
||||
# VK_DRIVER_FILES REPLACES the loader's search, so a manifest whose library is unreadable would
|
||||
# leave the session with no Vulkan at all - check both before setting it, and say so in the log
|
||||
# rather than fail silently half a minute later.
|
||||
if [ -n "${BL_VK_DRIVER:-}" ]; then
|
||||
# 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)"
|
||||
elif [ -n "${BL_VK_DRIVER:-}" ]; then
|
||||
bl_icd_lib=$(sed -n 's/.*"library_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$BL_VK_DRIVER" 2>/dev/null | head -1)
|
||||
# JSON may escape the separator: org.json writes "\/data\/user\/0\/...". The Vulkan loader
|
||||
# parses that correctly; this check has to as well, or an absolute path looks relative and gets
|
||||
@@ -148,7 +156,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 +193,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 +654,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-ozone-platform=x11 -cef-use-gl=angle -cef-use-angle=vulkan)
|
||||
if [ "${BL_STEAM_SOFTWARE:-0}" = 1 ]; then
|
||||
_bl_cef=(-cef-disable-gpu -cef-disable-gpu-compositing -cef-ozone-platform=x11)
|
||||
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 +722,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 ${_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 +769,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 "${_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
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
SESSION = Path(__file__).resolve().parents[1] / 'linuxfs/overlay/usr/local/bin/bannerlator-session'
|
||||
GRAPHICS_ENV = ('VK_DRIVER_FILES', 'VK_ICD_FILENAMES', 'LIBGL_ALWAYS_SOFTWARE',
|
||||
'GALLIUM_DRIVER', 'MESA_LOADER_DRIVER_OVERRIDE', 'SDL_VIDEODRIVER')
|
||||
|
||||
|
||||
class SoftwareGraphicsTest(unittest.TestCase):
|
||||
def launch(self, **settings):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
home = Path(tmp)
|
||||
binary = home / 'bin'
|
||||
binary.mkdir()
|
||||
capture = home / 'capture.json'
|
||||
gamescope = binary / 'gamescope'
|
||||
gamescope.write_text(f'#!{sys.executable}\nimport json, os, sys\n'
|
||||
'from pathlib import Path\n'
|
||||
'Path(os.environ["TEST_CAPTURE"]).write_text(json.dumps({"argv": sys.argv[1:], "env": dict(os.environ)}))\n')
|
||||
gamescope.chmod(0o755)
|
||||
for name in ('wayland-info', 'mangoapp'):
|
||||
probe = binary / name
|
||||
probe.write_text('#!/bin/sh\nexit 0\n')
|
||||
probe.chmod(0o755)
|
||||
library = home / 'custom.so'
|
||||
library.touch()
|
||||
manifest = home / 'icd.json'
|
||||
manifest.write_text(json.dumps({'ICD': {'library_path': str(library)}}))
|
||||
env = {'PATH': str(binary) + os.pathsep + os.defpath, 'HOME': str(home),
|
||||
'TEST_CAPTURE': str(capture), 'BL_WIDTH': '960', 'BL_HEIGHT': '544',
|
||||
'BL_STEAMDECK': '1', 'BL_REFRESH': '120', 'BL_HDR': '1'}
|
||||
env.update(settings)
|
||||
if env.pop('TEST_CUSTOM_DRIVER', ''):
|
||||
env['BL_VK_DRIVER'] = str(manifest)
|
||||
subprocess.run([shutil.which('bash'), str(SESSION), 'steam', 'argument with spaces'],
|
||||
env=env, check=True, capture_output=True, text=True)
|
||||
result = json.loads(capture.read_text())
|
||||
config = result['env'].get('MANGOHUD_CONFIGFILE')
|
||||
if config:
|
||||
Path(config).unlink()
|
||||
return result, str(manifest)
|
||||
|
||||
def test_default_keeps_accelerated_backend_overlay_and_session_options(self):
|
||||
result, _ = self.launch(VK_DRIVER_FILES='/selected/turnip.json', GALLIUM_DRIVER='zink')
|
||||
args, env = result['argv'], result['env']
|
||||
self.assertEqual(args[:2], ['--backend', 'wayland'])
|
||||
self.assertEqual(env['VK_DRIVER_FILES'], '/selected/turnip.json')
|
||||
self.assertEqual(env['GALLIUM_DRIVER'], 'zink')
|
||||
self.assertNotIn('LIBGL_ALWAYS_SOFTWARE', env)
|
||||
self.assertNotIn('MESA_LOADER_DRIVER_OVERRIDE', env)
|
||||
self.assertNotIn('SDL_VIDEODRIVER', env)
|
||||
self.assertIn('--mangoapp', args)
|
||||
self.assertIn('--hdr-enabled', args)
|
||||
self.assertEqual(args[args.index('-r') + 1], '120')
|
||||
self.assertEqual(args[-1], 'argument with spaces')
|
||||
|
||||
def test_only_explicit_one_selects_software(self):
|
||||
for value in ('', '0', 'true', 'yes'):
|
||||
with self.subTest(value=value):
|
||||
result, _ = self.launch(BL_STEAM_SOFTWARE=value)
|
||||
self.assertEqual(result['argv'][:2], ['--backend', 'wayland'])
|
||||
self.assertNotIn('LIBGL_ALWAYS_SOFTWARE', result['env'])
|
||||
self.assertIn('--mangoapp', result['argv'])
|
||||
result, _ = self.launch(BL_STEAM_SOFTWARE='1')
|
||||
env = result['env']
|
||||
self.assertEqual(result['argv'][:2], ['--backend', 'sdl'])
|
||||
self.assertNotIn('--mangoapp', result['argv'])
|
||||
self.assertEqual(env['VK_DRIVER_FILES'], '/usr/share/vulkan/icd.d/lvp_icd.json')
|
||||
self.assertEqual(env['VK_ICD_FILENAMES'], env['VK_DRIVER_FILES'])
|
||||
self.assertEqual(env['LIBGL_ALWAYS_SOFTWARE'], '1')
|
||||
self.assertEqual(env['GALLIUM_DRIVER'], 'llvmpipe')
|
||||
self.assertEqual(env['SDL_VIDEODRIVER'], 'wayland')
|
||||
|
||||
def test_custom_driver_is_preserved_except_when_software_is_requested(self):
|
||||
for software in ('0', '1'):
|
||||
result, manifest = self.launch(TEST_CUSTOM_DRIVER='1', BL_STEAM_SOFTWARE=software)
|
||||
expected = manifest if software == '0' else '/usr/share/vulkan/icd.d/lvp_icd.json'
|
||||
self.assertEqual(result['env']['VK_DRIVER_FILES'], expected)
|
||||
self.assertEqual(result['env']['VK_ICD_FILENAMES'], expected)
|
||||
|
||||
def test_invalid_custom_driver_does_not_replace_inherited_selection(self):
|
||||
result, _ = self.launch(BL_VK_DRIVER='/missing/manifest.json', VK_DRIVER_FILES='/selected/turnip.json')
|
||||
self.assertEqual(result['env']['VK_DRIVER_FILES'], '/selected/turnip.json')
|
||||
self.assertEqual(result['argv'][:2], ['--backend', 'wayland'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user