mirror of
https://github.com/Droid-Deck/DroidDeck-Proton.git
synced 2026-11-07 02:36:05 -10:00
Merge GE-Proton11-9 01bfbf67 (ge-master) into main
# Conflicts: # README.md
This commit is contained in:
commit
de0c57f4a5
18 files changed
+1360
-13
No files matched your search
+34
-11
@@ -473,24 +473,45 @@ The build will be placed within the build directory as SOME-BUILD-NAME-HERE.tar.
|
||||
## Options
|
||||
|
||||
|
||||
###### Enable HDR
|
||||
<a id="enable-hdr"></a>
|
||||
|
||||
It should work if you have:
|
||||
###### HDR
|
||||
|
||||
- A compositor with HDR support
|
||||
- A game with HDR support.
|
||||
- A monitor with HDR support.
|
||||
HDR is detected automatically; `PROTON_ENABLE_HDR=1` and `PROTON_USE_HDR=1`
|
||||
are no longer required. The game, monitor, compositor, and Vulkan presentation
|
||||
path must support HDR, and HDR must be enabled in the desktop or Gamescope.
|
||||
|
||||
This covers DX11 through DXVK and DX12 through VKD3D-Proton: both use the
|
||||
DXVK-provided DXGI for output queries, with their respective Vulkan presenters
|
||||
checking HDR surface support.
|
||||
|
||||
- Native Wine Wayland uses the compositor's per-output color-management state.
|
||||
Select that driver with `PROTON_ENABLE_WAYLAND=1` when using a native Wayland
|
||||
desktop outside Gamescope.
|
||||
- Gamescope's XWayland path uses its HDR output feedback. Ordinary X11/XWayland
|
||||
paths without HDR feedback default to SDR; this does not add HDR support to them.
|
||||
- SDR outputs remain SDR, including when other connected monitors use HDR.
|
||||
|
||||
For native Wine Wayland without Gamescope, use:
|
||||
|
||||
```bash
|
||||
PROTON_ENABLE_HDR=1 %command%
|
||||
PROTON_ENABLE_WAYLAND=1 %command%
|
||||
```
|
||||
|
||||
Remove `PROTON_ENABLE_HDR=1`, `PROTON_USE_HDR=1`, and `DXVK_HDR=1` from the
|
||||
game's launch options to use automatic detection. This applies to both DX11
|
||||
and DX12. When using Gamescope, keep its HDR configuration and use Wine's
|
||||
default XWayland driver instead of adding the Wayland option above.
|
||||
|
||||
Use `DXVK_NO_HDR=1 %command%` to disable HDR reporting. The old Proton HDR
|
||||
options are still accepted for compatibility; setting either to `0` disables
|
||||
HDR (if both are set, `PROTON_USE_HDR` takes precedence). Explicit HDR opt-in
|
||||
is retained for older X11 HDR WSI implementations without output feedback.
|
||||
|
||||
> [!NOTE]
|
||||
> Setting PROTON_ENABLE_HDR=1 does NOT enable the Wine Wayland driver. Gamescope
|
||||
> can use PROTON_ENABLE_HDR, and automatically enabling native Wine Wayland
|
||||
> here would break that path. For HDR outside of gamescope, enable PROTON_ENABLE_HDR=1
|
||||
> AND PROTON_ENABLE_WAYLAND=1 together only when using a native wayland desktop and
|
||||
> not using gamescope.
|
||||
> Automatic detection does not change your desktop's HDR setting, select Wine
|
||||
> Wayland automatically, or force the game's own HDR setting on. Gamescope's
|
||||
> HDR mode and WSI support still need to be configured normally.
|
||||
|
||||
##### Enabling NTSync
|
||||
For NTSync to work, your kernel must be version 6.14 or newer and built with `CONFIG_NTSYNC=y` or `CONFIG_NTSYNC=m`. On non-systemd systems, you must also have a `ulimit -Hn` of 524288 or higher.
|
||||
@@ -585,6 +606,8 @@ Without HIDRAW access, Wine retains its normal SDL fallback, which does not prov
|
||||
| | `PROTON_STEAMINPUT_LAYOUT_DS4` | Force the fallback controller to use the DualShock 4 profile instead of automatic layout detection. |
|
||||
| | `PROTON_STEAMINPUT_LAYOUT_DS5` | Force the fallback controller to use the DualSense profile instead of automatic layout detection. |
|
||||
| `wayland` | `PROTON_USE_WAYLAND` or `PROTON_ENABLE_WAYLAND` | Enables the Wayland driver. |
|
||||
| `hdr` | `PROTON_ENABLE_HDR` or `PROTON_USE_HDR` | Legacy HDR overrides; no longer required for automatic detection in DX11 or DX12. Set to `0` to disable HDR reporting. If both are set, `PROTON_USE_HDR` takes precedence. Does not select the Wayland driver. See [HDR](#hdr). |
|
||||
| | `DXVK_NO_HDR` | Set to `1` to disable HDR reporting on both Wine display drivers, including for DX12 through the shared DXGI implementation. Takes precedence over HDR opt-in flags. |
|
||||
| | `PROTON_USE_X11_EXCLUSIVE` | Under Wine-Wayland, use `winex11.drv` only for the executable specified by this value. Accepts an exact executable basename such as `Launcher.exe` or a case-insensitive Windows path fragment such as `Vendor\\Launcher.exe`; other processes remain on Wine-Wayland. |
|
||||
| `winebth` | `PROTON_ENABLE_WINEBTH` | Enables `winebth.sys`, Wine's Bluetooth driver, which is disabled by default. Needed for Bluetooth LE devices such as smart trainers and controllers in Zwift or Rouvy. Requires BlueZ. |
|
||||
| `wow64` | `PROTON_USE_WOW64` | Enables wow64. |
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] dxgi: Detect output HDR state by default on Win32.
|
||||
|
||||
The active-output checks in dxgi-respect-output-hdr-state.patch already
|
||||
prevent HDR10 from being reported for SDR or unqueryable outputs. Default
|
||||
the Win32 HDR policy to enabled so these checks can detect HDR without
|
||||
requiring PROTON_ENABLE_HDR, PROTON_USE_HDR, or DXVK_HDR.
|
||||
|
||||
Keep explicit DXVK_HDR and dxgi.enableHDR settings, and let DXVK_NO_HDR=1
|
||||
disable HDR even when a configuration file enables it. Preserve the UE4
|
||||
D3D11 compatibility exclusions, the Vulkan surface-format requirement,
|
||||
and the existing opt-in policy for non-Win32 WSI backends.
|
||||
|
||||
Apply after dxgi-respect-output-hdr-state.patch. Wine Wayland already
|
||||
reports compositor color-management state; the matching Wine fixup adds
|
||||
Gamescope XWayland feedback and consistent disable handling.
|
||||
|
||||
Manual validation after rebuilding:
|
||||
- Native Wayland HDR output, no HDR flags: game HDR becomes available.
|
||||
- SDR output and mixed SDR/HDR monitors: only the HDR output reports HDR.
|
||||
- Gamescope HDR on/off and ordinary XWayland SDR.
|
||||
- DXVK_HDR=0, DXVK_NO_HDR=1, and dxgi.enableHDR=False.
|
||||
- DX11, DX12 (which shares DXGI), and native Vulkan games using NVAPI.
|
||||
---
|
||||
|
||||
diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp
|
||||
--- a/src/dxgi/dxgi_options.cpp
|
||||
+++ b/src/dxgi/dxgi_options.cpp
|
||||
@@ -199,7 +199,14 @@
|
||||
this->hideIntelGpu = true;
|
||||
}
|
||||
|
||||
- this->enableHDR = config.getOption<bool>("dxgi.enableHDR", env::getEnvVar("DXVK_HDR") == "1");
|
||||
+ const std::string hdr = env::getEnvVar("DXVK_HDR");
|
||||
+ bool enableHdrDefault = hdr == "1";
|
||||
+#ifdef _WIN32
|
||||
+ // Wine reports the active output state; HDR10 also needs surface support.
|
||||
+ enableHdrDefault |= hdr.empty();
|
||||
+#endif
|
||||
+ this->enableHDR = config.getOption<bool>("dxgi.enableHDR", enableHdrDefault)
|
||||
+ && env::getEnvVar("DXVK_NO_HDR") != "1";
|
||||
|
||||
bool enableUe4Workarounds = config.getOption<bool>("dxgi.enableUe4Workarounds", false);
|
||||
|
||||
|
||||
diff --git a/dxvk.conf b/dxvk.conf
|
||||
--- a/dxvk.conf
|
||||
+++ b/dxvk.conf
|
||||
@@ -11,7 +11,10 @@
|
||||
# Many (broken) games will need this to be set to consider exposing HDR output
|
||||
# as determine it based on the DXGIOutput's current ColorSpace instead of
|
||||
# using CheckColorSpaceSupport.
|
||||
-# This defaults to the value of the DXVK_HDR environment variable.
|
||||
+# On Win32 this defaults to automatic detection of the active output's HDR state.
|
||||
+# HDR10 presentation also requires support from the Vulkan surface.
|
||||
+# DXVK_HDR=0 or dxgi.enableHDR=False disables it; DXVK_NO_HDR=1 takes precedence.
|
||||
+# Other WSI backends retain the DXVK_HDR=1 opt-in.
|
||||
#
|
||||
# Supported values: True, False
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] d3d9: Disable constant buffer streaming for StarCraft II.
|
||||
|
||||
StarCraft II intermittently hangs the GPU on RADV with an RX 5700 XT
|
||||
using GE-Proton11-7. Replacing only DXVK with the GE-Proton10-34 version
|
||||
avoids the failure. Disabling descriptor heaps and descriptor buffers was
|
||||
not sufficient, but the reporter now reports success with only
|
||||
d3d9.deviceLocalConstantBuffers = False on the newer build.
|
||||
|
||||
Disable constant buffer streaming in the application profile for SC2.exe
|
||||
and SC2_x64.exe. Leave descriptor selection, Battle.net, other games and
|
||||
the global default unchanged. User configuration can still override this
|
||||
application default. This is a targeted compatibility workaround; the
|
||||
underlying DXVK/driver failure has not been bisected to a specific commit.
|
||||
|
||||
Bug: https://github.com/GloriousEggroll/proton-ge-custom/issues/790
|
||||
---
|
||||
diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp
|
||||
--- a/src/util/config/config.cpp
|
||||
+++ b/src/util/config/config.cpp
|
||||
@@ -534,6 +534,10 @@
|
||||
}} },
|
||||
|
||||
+ /* StarCraft II: avoid GPU hangs with constant buffer streaming. */
|
||||
+ { R"(\\SC2(_x64)?\.exe$)", {{
|
||||
+ { "d3d9.deviceLocalConstantBuffers", "False" },
|
||||
+ }} },
|
||||
/* A Hat in Time */
|
||||
{ R"(\\HatinTimeGame\.exe$)", {{
|
||||
{ "d3d9.lenientClear", "True" },
|
||||
}} },
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alexandre Derumier <[email protected]>
|
||||
Date: Sun, 11 Oct 2026 18:40:00 +0200
|
||||
Subject: [PATCH] winedmo: Flush downstream before locking the pins when
|
||||
seeking.
|
||||
|
||||
dmo_seeking_SetPositions() took each output pin's flushing_cs before
|
||||
sending BeginFlush downstream. The stream thread holds that lock while it
|
||||
delivers a sample (IMemInputPin_Receive), and a renderer may block in
|
||||
Receive until it is flushed: the seek then waits for the stream thread,
|
||||
which waits for the renderer, which waits for the flush. SNK Heroines Tag
|
||||
Team Frenzy (its own renderer, looping movies) hung right after START.
|
||||
|
||||
Send BeginFlush first, as the Windows parsers and Wine's other parsers
|
||||
do: the renderer returns from Receive, the stream thread releases the
|
||||
lock, and the seek goes on.
|
||||
---
|
||||
dlls/winedmo/winedmo_quartz_parser.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/dlls/winedmo/winedmo_quartz_parser.c b/dlls/winedmo/winedmo_quartz_parser.c
|
||||
--- a/dlls/winedmo/winedmo_quartz_parser.c
|
||||
+++ b/dlls/winedmo/winedmo_quartz_parser.c
|
||||
@@ -636,6 +636,11 @@
|
||||
for (i = 0; i < filter->source_count; ++i)
|
||||
WakeConditionVariable(&filter->sources[i]->queue_cv);
|
||||
|
||||
+ /* Flush downstream first: a stream thread holds its pin's flushing_cs during Receive, and
|
||||
+ * a renderer may block in Receive until it is flushed (Windows parsers do the same). */
|
||||
+ if (!(current_flags & AM_SEEKING_NoFlush))
|
||||
+ dmo_parser_begin_downstream_flush(filter);
|
||||
+
|
||||
/* Acquire per-pin flushing_cs to ensure no Receive is in progress. */
|
||||
for (i = 0; i < filter->source_count; ++i)
|
||||
{
|
||||
@@ -643,9 +648,6 @@
|
||||
EnterCriticalSection(&filter->sources[i]->flushing_cs);
|
||||
}
|
||||
|
||||
- if (!(current_flags & AM_SEEKING_NoFlush))
|
||||
- dmo_parser_begin_downstream_flush(filter);
|
||||
-
|
||||
SourceSeekingImpl_SetPositions(iface, current, current_flags, stop, stop_flags);
|
||||
|
||||
/* Perform the actual seek. */
|
||||
--
|
||||
2.51.0
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] mfmediaengine: Defer loop restarts until the session has ended.
|
||||
|
||||
Crimson Desert can stall at the title menu after titleview_03.mp4 reaches
|
||||
EOF. Its media engine has looping enabled when MEEndOfPresentation arrives.
|
||||
The handler immediately seeks back to zero, which resets the video sink's
|
||||
sample queue before submitting the replay to the media session.
|
||||
|
||||
Patch 0059 correctly serializes replay against the old presentation's
|
||||
draining. Resetting the sink before that drain completes can discard the
|
||||
samples needed to request the remaining decoder output and end marker,
|
||||
leaving both the drain and the queued replay waiting indefinitely.
|
||||
|
||||
In the failing trace, the replay is submitted at 156.587 but never reaches
|
||||
session_commands_callback_Invoke. The game disables looping at 156.826,
|
||||
then polls OnVideoStreamTick without receiving another frame for the rest
|
||||
of the capture.
|
||||
|
||||
Do not restart loops on the source-level MEEndOfPresentation event. Keep
|
||||
draining and let MESessionEnded perform the loop seek instead. If the
|
||||
application disables looping meanwhile, that handler reports normal
|
||||
completion. Preserve the early ended notification for non-looping sources,
|
||||
which existing media-engine clients rely on, and keep session serialization.
|
||||
|
||||
Add coverage for two video-loop restarts followed by disabling looping and
|
||||
waiting for normal completion.
|
||||
|
||||
References:
|
||||
https://learn.microsoft.com/en-us/windows/win32/medfound/meendofpresentation
|
||||
https://learn.microsoft.com/en-us/windows/win32/medfound/mesessionended
|
||||
---
|
||||
|
||||
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c
|
||||
--- a/dlls/mfmediaengine/main.c
|
||||
+++ b/dlls/mfmediaengine/main.c
|
||||
@@ -1170,7 +1170,10 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
|
||||
/* Media Engine users may wait on ended state before replacing the
|
||||
* source. Network streams can deliver source end-of-presentation
|
||||
* before every renderer has drained enough to produce MESessionEnded. */
|
||||
- ended_event = media_engine_handle_ended(engine);
|
||||
+ /* Loop seeks reset the sink's sample queue. Wait for MESessionEnded
|
||||
+ * before doing that, otherwise the queued replay can wait forever
|
||||
+ * for the samples and end-of-segment marker we still need to drain. */
|
||||
+ ended_event = !(engine->flags & FLAGS_ENGINE_LOOP) && media_engine_handle_ended(engine);
|
||||
LeaveCriticalSection(&engine->cs);
|
||||
if (ended_event)
|
||||
IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_ENDED, 0, 0);
|
||||
|
||||
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c
|
||||
index 0b815c7b8ed..ad32cfdf534 100644
|
||||
--- a/dlls/mfmediaengine/tests/mfmediaengine.c
|
||||
+++ b/dlls/mfmediaengine/tests/mfmediaengine.c
|
||||
@@ -2979,6 +2979,78 @@ done:
|
||||
IMFByteStream_Release(stream);
|
||||
}
|
||||
|
||||
+static void test_looped_playback(void)
|
||||
+{
|
||||
+ struct test_seek_notify *notify = create_seek_notify();
|
||||
+ IMFMediaEngineEx *media_engine = NULL;
|
||||
+ IMFByteStream *stream = NULL;
|
||||
+ LONGLONG pts, last_pts = -1;
|
||||
+ unsigned int loops = 0;
|
||||
+ DWORD start, res;
|
||||
+ HRESULT hr;
|
||||
+ BSTR url = NULL;
|
||||
+
|
||||
+ hr = create_media_engine(¬ify->IMFMediaEngineNotify_iface, NULL, FALSE, DXGI_FORMAT_B8G8R8X8_UNORM,
|
||||
+ &IID_IMFMediaEngineEx, (void **)&media_engine);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ if (FAILED(hr)) goto done;
|
||||
+
|
||||
+ stream = load_resource(L"i420-64x64.avi", L"video/avi");
|
||||
+ url = SysAllocString(L"i420-64x64.avi");
|
||||
+ hr = IMFMediaEngineEx_SetLoop(media_engine, TRUE);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, stream, url);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ if (FAILED(hr)) goto done;
|
||||
+
|
||||
+ hr = IMFMediaEngineEx_Play(media_engine);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ res = WaitForSingleObject(notify->playing_event, 5000);
|
||||
+ ok(res == WAIT_OBJECT_0, "Waiting for playback returned %#lx.\n", res);
|
||||
+ if (res != WAIT_OBJECT_0 || FAILED(notify->error)) goto done;
|
||||
+
|
||||
+ /* Consuming the final samples must allow replay to start after the sink drains. */
|
||||
+ start = GetTickCount();
|
||||
+ while (loops < 2 && GetTickCount() - start < 5000)
|
||||
+ {
|
||||
+ hr = IMFMediaEngineEx_OnVideoStreamTick(media_engine, &pts);
|
||||
+ ok(hr == S_OK || hr == S_FALSE, "Unexpected tick hr %#lx.\n", hr);
|
||||
+ if (FAILED(hr)) break;
|
||||
+ if (hr == S_OK)
|
||||
+ {
|
||||
+ if (last_pts >= 0 && pts < last_pts) ++loops;
|
||||
+ last_pts = pts;
|
||||
+ }
|
||||
+ Sleep(1);
|
||||
+ }
|
||||
+ ok(loops == 2, "Video did not restart twice, observed %u loops.\n", loops);
|
||||
+ ok(!IMFMediaEngineEx_IsEnded(media_engine), "Looping playback reported ended.\n");
|
||||
+
|
||||
+ /* Turning looping off must let the current iteration complete normally. */
|
||||
+ hr = IMFMediaEngineEx_SetLoop(media_engine, FALSE);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ start = GetTickCount();
|
||||
+ while (!IMFMediaEngineEx_IsEnded(media_engine) && GetTickCount() - start < 5000)
|
||||
+ {
|
||||
+ hr = IMFMediaEngineEx_OnVideoStreamTick(media_engine, &pts);
|
||||
+ ok(hr == S_OK || hr == S_FALSE, "Unexpected tick hr %#lx.\n", hr);
|
||||
+ if (FAILED(hr)) break;
|
||||
+ Sleep(1);
|
||||
+ }
|
||||
+ ok(IMFMediaEngineEx_IsEnded(media_engine), "Playback did not end after disabling looping.\n");
|
||||
+
|
||||
+done:
|
||||
+ if (media_engine)
|
||||
+ {
|
||||
+ hr = IMFMediaEngineEx_Shutdown(media_engine);
|
||||
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
|
||||
+ IMFMediaEngineEx_Release(media_engine);
|
||||
+ }
|
||||
+ if (stream) IMFByteStream_Release(stream);
|
||||
+ SysFreeString(url);
|
||||
+ IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface);
|
||||
+}
|
||||
+
|
||||
START_TEST(mfmediaengine)
|
||||
{
|
||||
HRESULT hr;
|
||||
@@ -3027,6 +3099,7 @@ START_TEST(mfmediaengine)
|
||||
test_GetSeekable(TRUE);
|
||||
test_media_extension();
|
||||
test_SetCurrentTime();
|
||||
+ test_looped_playback();
|
||||
|
||||
IMFMediaEngineClassFactory_Release(factory);
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Date: Mon, 12 Oct 2026 10:00:00 -0600
|
||||
Subject: [PATCH] mmdevapi: Preserve shared Sony endpoint properties on startup.
|
||||
|
||||
Patch 0019 avoids preloading persisted Sony endpoints so that current driver
|
||||
enumeration, rather than stale registry records, determines which controllers
|
||||
are present. However, it also deletes the endpoint's MMDevices registry key.
|
||||
This runs in every process loading mmdevapi, while the registry is shared.
|
||||
A second process can therefore delete the properties of an endpoint that the
|
||||
game has already enumerated and still holds in its process-local device list.
|
||||
|
||||
The R2 trace in issue #750 shows this failure in Arknights: Endfield:
|
||||
|
||||
468691.227: PID 0140 opens the controller endpoint
|
||||
{1bd72244-d350-40a7-a443-4f2e33b20d56}.
|
||||
468694.168-173: PID 0474 loads mmdevapi and removes three persisted
|
||||
DualSense endpoints from the shared registry.
|
||||
468706.622 onward: PID 0140 repeatedly fails to read the original
|
||||
endpoint's container ID and device format with error 2.
|
||||
|
||||
Replugging publishes a fresh endpoint and allows audio haptics to work. The
|
||||
initial streams were stopped and released before the registry deletion, so
|
||||
this change addresses the broken rediscovery/retry path, not the application's
|
||||
reason for closing those first streams. Absence of the one-second PCM peak
|
||||
trace on those short-lived streams is not evidence of a stalled audio pump.
|
||||
|
||||
Skip persisted Sony endpoints without deleting their shared property stores.
|
||||
They remain absent from this process's collection until live driver discovery
|
||||
publishes them, preserving the purpose of 0019. Keep normal-device preloading,
|
||||
physical hotplug cleanup, routing, controller identity, and Steam Input
|
||||
selection unchanged. The registry enumeration index must advance normally
|
||||
because no key is removed from the enumeration.
|
||||
|
||||
Bug: https://github.com/GloriousEggroll/proton-ge-custom/issues/750#issuecomment-6126806805
|
||||
---
|
||||
dlls/mmdevapi/devenum.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/dlls/mmdevapi/devenum.c b/dlls/mmdevapi/devenum.c
|
||||
--- a/dlls/mmdevapi/devenum.c
|
||||
+++ b/dlls/mmdevapi/devenum.c
|
||||
@@ -2012,9 +2012,11 @@
|
||||
{
|
||||
if (is_dualsense_endpoint_name( pv.pwszVal ))
|
||||
{
|
||||
- TRACE("Removing persisted DualSense endpoint %s.\n", debugstr_w(pv.pwszVal));
|
||||
- if (RegDeleteTreeW(cur, guidvalue) == ERROR_SUCCESS)
|
||||
- i--;
|
||||
+ /* A different process may still use this shared property store.
|
||||
+ * Skip the cached endpoint locally; only live driver discovery
|
||||
+ * should publish it in this process. */
|
||||
+ TRACE("Skipping persisted Sony endpoint %s (%s); keeping shared properties.\n",
|
||||
+ debugstr_w(pv.pwszVal), debugstr_guid(&guid));
|
||||
}
|
||||
else
|
||||
MMDevice_Create(pv.pwszVal, &guid, curflow,
|
||||
@@ -35,6 +35,10 @@ apply_all_in_dir() {
|
||||
# Swapchain colorspace instance-extension support is upstream in DXVK 3c40ca1b.
|
||||
# Do not advertise HDR when Wine reports an SDR output, even with DXVK_HDR=1.
|
||||
apply_patch "../patches/dxvk/dxgi-respect-output-hdr-state.patch"
|
||||
# Query the active output by default instead of requiring an HDR opt-in.
|
||||
apply_patch "../patches/dxvk/dxgi-auto-detect-hdr.patch"
|
||||
# StarCraft II: avoid GPU hangs with D3D9 constant buffer streaming (#790).
|
||||
apply_patch "../patches/dxvk/starcraft2-disable-constant-buffer-streaming.patch"
|
||||
popd
|
||||
|
||||
pushd vkd3d-proton
|
||||
@@ -151,6 +155,7 @@ apply_all_in_dir() {
|
||||
-W dbghelp-Debug_Symbols \
|
||||
-W version-VerQueryValue \
|
||||
-W mf_http_support \
|
||||
-W ntdll-Serial_Port_Detection \
|
||||
-W server-PeekMessage \
|
||||
-W msxml3-FreeThreadedXMLHTTP60 \
|
||||
-W ntdll-ForceBottomUpAlloc \
|
||||
@@ -185,6 +190,10 @@ apply_all_in_dir() {
|
||||
# dbghelp-Debug_Symbols - Ubisoft Connect games (3/3 I had installed and could test) will crash inside pe_load_debug_info function with this enabled
|
||||
# version-VerQueryValue - just a test and doesn't apply cleanly. not relevant for gaming
|
||||
# mf_http_support - disabled in favor of custom ffmpeg backend video playback solution
|
||||
# ntdll-Serial_Port_Detection - its mountmgr probe opens every /dev/ttyS*, ttyUSB*, ttyACM* without O_NOCTTY
|
||||
# from winedevice.exe (a session leader without a terminal): the first one becomes its controlling tty and
|
||||
# is hung up when the prefix shuts down, breaking the program that had it open (e.g. a Sinden lightgun's
|
||||
# serial link to its driver: the gun stops working after each game)
|
||||
|
||||
# server-PeekMessage - already applied
|
||||
# msxml3-FreeThreadedXMLHTTP60 - already applied
|
||||
@@ -366,6 +375,9 @@ apply_all_in_dir() {
|
||||
echo "WINE: -HOTFIX- Fall back when GnuTLS lacks NO_SHUFFLE_EXTENSIONS"
|
||||
apply_patch "../patches/wine-hotfixes/pending/secur32-fallback-without-no-shuffle-extensions.patch"
|
||||
|
||||
echo "WINE: -HOTFIX- Backport schannel SECBUFFER_MISSING size fix for incomplete TLS records (upstream 9aee0e71, 35b1e7eb)"
|
||||
apply_patch "../patches/wine-hotfixes/pending/secur32-schannel-fix-incomplete-message-missing-size.patch"
|
||||
|
||||
echo "WINE: -HOTFIX- Preserve driver-reported OpenGL GPU identity"
|
||||
apply_patch "../patches/wine-hotfixes/pending/wined3d-preserve-runtime-opengl-gpu-description.patch"
|
||||
|
||||
@@ -375,6 +387,12 @@ apply_all_in_dir() {
|
||||
echo "WINE: -HOTFIX- Keep Forza background windows unmapped on wlroots"
|
||||
apply_patch "../patches/wine-hotfixes/pending/winex11-keep-forza-background-windows-unmapped-on-wlroots.patch"
|
||||
|
||||
echo "WINE: -HOTFIX- Reconcile unsupported minimization on xwayland-satellite"
|
||||
apply_patch "../patches/wine-hotfixes/pending/winex11-handle-unsupported-minimization-on-satellite.patch"
|
||||
|
||||
echo "WINE: -HOTFIX- Do not paint off-desktop minimized frames into retained host windows"
|
||||
apply_patch "../patches/wine-hotfixes/pending/server-clip-off-desktop-minimized-window-paint.patch"
|
||||
|
||||
echo "WINE: -HOTFIX- Share selected cursor images across processes"
|
||||
apply_patch "../patches/wine-hotfixes/pending/win32u-share-selected-cursors-across-processes.patch"
|
||||
|
||||
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Date: Sun, 11 Oct 2026 12:00:00 -0600
|
||||
Subject: [PATCH] winewayland: Keep windows recoverable without host minimization.
|
||||
|
||||
Deadlock can minimize its window during startup on Niri before creating
|
||||
its Vulkan surface. Niri advertises maximize and fullscreen capabilities,
|
||||
but not minimize. The capability guard in the minimized-toplevel check
|
||||
then lets the offscreen Win32 iconic rectangle destroy the xdg_toplevel.
|
||||
The resulting keyboard leave cannot restore a window which is no longer
|
||||
mapped. The same sequence occurs with the Steam overlay disabled.
|
||||
|
||||
Keep established, visible minimized toplevels mapped on these compositors
|
||||
as well. Preserve their last usable geometry, decorations and input region
|
||||
instead of applying the iconic rectangle. Do not send unsupported minimize
|
||||
requests or change fullscreen state, but continue processing compositor
|
||||
configures. Explicit hides, destruction and non-toplevel windows retain
|
||||
their existing behavior.
|
||||
|
||||
Queue the existing window-state handler on the unsupported transition.
|
||||
Restore only when the toplevel actually has Wayland keyboard focus, even
|
||||
if Wine's foreground HWND has not changed. Otherwise keep the window
|
||||
available for the user to focus and restore later. Do not reposition its
|
||||
iconic Win32 rectangle from an output-enter notification in the meantime.
|
||||
Unknown capabilities and compositors which support minimization retain
|
||||
their existing minimize/restore behavior.
|
||||
|
||||
Complements the renderer preservation in fixup 0067. The original
|
||||
minimized-toplevel implementation was authored by
|
||||
Erhan Bilgili <[email protected]>:
|
||||
https://github.com/nanomatters/wine-salkim/commit/7d3c16911a81d263c5f7a4769a8d57d17b3f1151
|
||||
|
||||
Bug: https://github.com/GloriousEggroll/proton-ge-custom/issues/762
|
||||
---
|
||||
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c
|
||||
--- a/dlls/winewayland.drv/wayland_surface.c
|
||||
+++ b/dlls/winewayland.drv/wayland_surface.c
|
||||
@@ -156,6 +156,10 @@
|
||||
{
|
||||
HRGN shape_region, owned_shape_region = 0;
|
||||
|
||||
+ /* Retain a focusable host window when the compositor cannot minimize it. */
|
||||
+ if (wayland_surface_is_toplevel(surface) && surface->window.minimized &&
|
||||
+ !wayland_surface_supports_minimize(surface)) return;
|
||||
+
|
||||
if (window_surface && window_surface->shape_region)
|
||||
shape_region = window_surface->shape_region;
|
||||
else
|
||||
@@ -1181,7 +1185,7 @@
|
||||
if (!(cap & WAYLAND_SURFACE_WM_CAPS_MAXIMIZE))
|
||||
WARN("Compositor does not support maximize!\n");
|
||||
if (!(cap & WAYLAND_SURFACE_WM_CAPS_MINIMIZE))
|
||||
- WARN("Compositor does not support minimize, cannot implement window focus loss!\n");
|
||||
+ TRACE("Compositor does not support minimize, keeping minimized toplevels mapped.\n");
|
||||
|
||||
if (!(data = wayland_win_data_get(hwnd))) return;
|
||||
|
||||
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h
|
||||
--- a/dlls/winewayland.drv/waylanddrv.h
|
||||
+++ b/dlls/winewayland.drv/waylanddrv.h
|
||||
@@ -649,6 +649,18 @@
|
||||
return surface->role == WAYLAND_SURFACE_ROLE_TOPLEVEL && surface->xdg_toplevel;
|
||||
}
|
||||
|
||||
+static inline BOOL wayland_surface_supports_minimize(const struct wayland_surface *surface)
|
||||
+{
|
||||
+ enum wayland_surface_wm_caps caps = surface->pending.caps;
|
||||
+
|
||||
+ /* Capabilities may arrive before the configure carrying them is applied. */
|
||||
+ if (!caps) caps = surface->requested.caps;
|
||||
+ if (!caps) caps = surface->processing.caps;
|
||||
+ if (!caps) caps = surface->current.caps;
|
||||
+
|
||||
+ return !caps || (caps & WAYLAND_SURFACE_WM_CAPS_MINIMIZE);
|
||||
+}
|
||||
+
|
||||
static inline BOOL wayland_surface_is_popup(struct wayland_surface *surface)
|
||||
{
|
||||
return surface->role == WAYLAND_SURFACE_ROLE_POPUP && surface->xdg_popup;
|
||||
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c
|
||||
--- a/dlls/winewayland.drv/window.c
|
||||
+++ b/dlls/winewayland.drv/window.c
|
||||
@@ -298,10 +298,7 @@
|
||||
DWORD style)
|
||||
{
|
||||
if (!surface || surface->role != WAYLAND_SURFACE_ROLE_TOPLEVEL) return FALSE;
|
||||
- if (!(style & WS_MINIMIZE)) return FALSE;
|
||||
-
|
||||
- return !surface->current.caps ||
|
||||
- (surface->current.caps & WAYLAND_SURFACE_WM_CAPS_MINIMIZE);
|
||||
+ return !!(style & WS_MINIMIZE);
|
||||
}
|
||||
|
||||
static BOOL is_menu_popup_candidate_style(DWORD style, DWORD exstyle)
|
||||
@@ -363,7 +360,7 @@
|
||||
struct wayland_client_surface *client = data->client_surface;
|
||||
struct wayland_surface *surface;
|
||||
enum wayland_surface_role role;
|
||||
- BOOL visible, layer_set, server_decor = FALSE;
|
||||
+ BOOL visible, layer_set, keep_minimized_geometry, server_decor = FALSE;
|
||||
DWORD exstyle = NtUserGetWindowLongW(data->hwnd, GWL_EXSTYLE);
|
||||
DWORD style = NtUserGetWindowLongW(data->hwnd, GWL_STYLE);
|
||||
|
||||
@@ -438,7 +435,22 @@
|
||||
}
|
||||
|
||||
surface->window.visible = visible;
|
||||
- wayland_win_data_get_config(data, &surface->window);
|
||||
+ keep_minimized_geometry = role == WAYLAND_SURFACE_ROLE_TOPLEVEL &&
|
||||
+ should_keep_minimized_toplevel_mapped(surface, style) &&
|
||||
+ !wayland_surface_supports_minimize(surface);
|
||||
+ if (keep_minimized_geometry)
|
||||
+ {
|
||||
+ /* Without host minimization, the existing window is the only way to
|
||||
+ * focus and restore the app. Do not replace its usable geometry with
|
||||
+ * Win32's offscreen iconic rectangle or empty client/input region. */
|
||||
+ if (!surface->window.minimized)
|
||||
+ {
|
||||
+ TRACE("hwnd=%p keeping minimized toplevel without host minimize support\n", data->hwnd);
|
||||
+ NtUserPostMessage(data->hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0);
|
||||
+ }
|
||||
+ surface->window.minimized = TRUE;
|
||||
+ }
|
||||
+ else wayland_win_data_get_config(data, &surface->window);
|
||||
|
||||
/* If the window is a visible toplevel make it a wayland
|
||||
* xdg_toplevel. Otherwise keep it role-less to avoid polluting the
|
||||
@@ -459,7 +471,7 @@
|
||||
stack_below_parent);
|
||||
break;
|
||||
case WAYLAND_SURFACE_ROLE_TOPLEVEL:
|
||||
- wayland_surface_make_toplevel(surface, server_decor);
|
||||
+ if (!keep_minimized_geometry) wayland_surface_make_toplevel(surface, server_decor);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -495,6 +507,9 @@
|
||||
processing_config ? "processing" : "current",
|
||||
processing_config ? surface->processing.state : surface->current.state);
|
||||
|
||||
+ if (!processing_config && surface->window.minimized &&
|
||||
+ !wayland_surface_supports_minimize(surface)) return;
|
||||
+
|
||||
/* update the parent here as well to ensure that its not stale if the owner is updated
|
||||
* with no new contents comitted or state change */
|
||||
wayland_surface_update_toplevel_parent(surface);
|
||||
@@ -1643,7 +1658,7 @@
|
||||
struct wayland_win_data *data;
|
||||
DWORD style;
|
||||
HWND focused_hwnd, old_foreground;
|
||||
- BOOL ret;
|
||||
+ BOOL ret, host_minimize_supported = TRUE;
|
||||
|
||||
/* win32u uses a second call with NULL outputs to release any host state
|
||||
* lock acquired by the first call. Wine-Wayland does not acquire one. */
|
||||
@@ -1670,6 +1685,9 @@
|
||||
SetRectEmpty(&data->state_update_rect);
|
||||
data->state_update_foreground = NULL;
|
||||
|
||||
+ if ((surface = data->wayland_surface) && wayland_surface_is_toplevel(surface))
|
||||
+ host_minimize_supported = wayland_surface_supports_minimize(surface);
|
||||
+
|
||||
wayland_win_data_release(data);
|
||||
|
||||
/* in these cases we dont need to update the window focus, borrowed from winemac */
|
||||
@@ -1689,8 +1707,11 @@
|
||||
if (!*foreground && old_foreground != focused_hwnd) *foreground = focused_hwnd;
|
||||
|
||||
/* we can't track if the host window is minimized or unminimized, but
|
||||
- * if we have keyboard focus on this window we can treat it as restored. */
|
||||
- if (!*state_cmd && (style & WS_MINIMIZE) && focused_hwnd == hwnd && old_foreground != hwnd)
|
||||
+ * if we have keyboard focus on this window we can treat it as restored.
|
||||
+ * An unsupported minimize may never have changed the host keyboard focus,
|
||||
+ * so do not require a foreground transition in that case. */
|
||||
+ if (!*state_cmd && (style & WS_MINIMIZE) && focused_hwnd == hwnd &&
|
||||
+ (old_foreground != hwnd || !host_minimize_supported))
|
||||
*state_cmd = MAKELONG(SC_RESTORE, 1);
|
||||
else if (!*state_cmd && !*swp_flags && (data = wayland_win_data_get(hwnd)))
|
||||
{
|
||||
@@ -1698,6 +1719,7 @@
|
||||
|
||||
if (!(surface = data->wayland_surface)) goto skip;
|
||||
if (!wayland_surface_is_toplevel(surface)) goto skip;
|
||||
+ if ((style & WS_MINIMIZE) && !host_minimize_supported) goto skip;
|
||||
if (wl_list_empty(&surface->output_list)) goto skip;
|
||||
/* the output hint syncs the win32u position to the toplevel position */
|
||||
if (surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) goto skip;
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] wine: Detect Gamescope HDR and honor HDR opt-outs.
|
||||
|
||||
Native Wayland already reports the compositor's per-output HDR state.
|
||||
X11 still reports HDR solely from DXVK_HDR, requiring an opt-in even
|
||||
when Gamescope can describe the current HDR path.
|
||||
|
||||
Read GAMESCOPE_HDR_OUTPUT_FEEDBACK from the X root, the same property
|
||||
used by Gamescope's WSI layer. Validate its type, format, and length.
|
||||
A reported SDR state takes precedence over an inherited DXVK_HDR=1.
|
||||
Refresh display state when the desktop thread receives feedback changes.
|
||||
|
||||
Without feedback, default to SDR but preserve an explicit DXVK_HDR=1
|
||||
for older HDR WSI implementations. This does not infer enabled HDR from
|
||||
EDID capability, the presence of a Wayland session, or another monitor.
|
||||
|
||||
Honor DXVK_HDR=0 and DXVK_NO_HDR=1 in X11 and native Wayland. Apply the
|
||||
Wayland opt-out before generating HDR EDID metadata.
|
||||
|
||||
Together with dxgi-auto-detect-hdr.patch, this removes the need for the
|
||||
Proton HDR launch flags without advertising HDR on unknown SDR paths.
|
||||
No compositor configuration or graphics-driver selection is changed.
|
||||
|
||||
References:
|
||||
https://github.com/ValveSoftware/gamescope/blob/master/src/steamcompmgr.cpp
|
||||
https://github.com/ValveSoftware/gamescope/blob/master/layer/VkLayer_FROG_gamescope_wsi.cpp
|
||||
|
||||
Manual validation after rebuilding:
|
||||
- Gamescope HDR enabled/disabled, both without DXVK_HDR.
|
||||
- Change Gamescope HDR state while the application is open.
|
||||
- Ordinary X11/XWayland without feedback defaults to SDR.
|
||||
- Explicit HDR disable on both Wine drivers.
|
||||
- Existing Wayland output detection and legacy X11 HDR opt-in.
|
||||
---
|
||||
|
||||
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
|
||||
--- a/dlls/winex11.drv/display.c
|
||||
+++ b/dlls/winex11.drv/display.c
|
||||
@@ -482,6 +482,33 @@
|
||||
return !!host_handler.register_event_handlers;
|
||||
}
|
||||
|
||||
+static BOOL is_hdr_enabled(void)
|
||||
+{
|
||||
+ unsigned long count, remaining;
|
||||
+ unsigned char *data = NULL;
|
||||
+ BOOL enabled, valid;
|
||||
+ const char *env;
|
||||
+ Atom type;
|
||||
+ int format;
|
||||
+
|
||||
+ if ((env = getenv("DXVK_NO_HDR")) && *env == '1') return FALSE;
|
||||
+ if ((env = getenv("DXVK_HDR")) && *env == '0') return FALSE;
|
||||
+
|
||||
+ /* Gamescope publishes this on each of its Xwayland roots. A known SDR
|
||||
+ * result must win over the legacy HDR opt-in inherited from the session. */
|
||||
+ valid = !XGetWindowProperty( gdi_display, DefaultRootWindow(gdi_display),
|
||||
+ x11drv_atom(GAMESCOPE_HDR_OUTPUT_FEEDBACK), 0, 1, False,
|
||||
+ XA_CARDINAL, &type, &format, &count, &remaining, &data );
|
||||
+ valid = valid && type == XA_CARDINAL && format == 32 && count == 1 && !remaining;
|
||||
+ enabled = valid && *(unsigned long *)data != 0;
|
||||
+ if (data) XFree( data );
|
||||
+ if (valid) return enabled;
|
||||
+
|
||||
+ /* Preserve explicit opt-in for older HDR WSI implementations. Unknown
|
||||
+ * X11/Xwayland outputs remain SDR when no override was requested. */
|
||||
+ return (env = getenv("DXVK_HDR")) && *env == '1';
|
||||
+}
|
||||
+
|
||||
UINT X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, void *param )
|
||||
{
|
||||
INT gpu_count, adapter_count, monitor_count, current_adapter_count = 0;
|
||||
@@ -490,13 +517,12 @@
|
||||
struct x11drv_gpu *gpus;
|
||||
INT gpu, adapter, monitor;
|
||||
struct x11drv_mode *modes;
|
||||
- const char *env;
|
||||
BOOL hdr_enabled;
|
||||
UINT mode_count;
|
||||
|
||||
TRACE( "via %s\n", debugstr_a(host_handler.name) );
|
||||
|
||||
- hdr_enabled = (env = getenv("DXVK_HDR")) && *env == '1';
|
||||
+ hdr_enabled = is_hdr_enabled();
|
||||
TRACE( "hdr_enabled %d.\n", hdr_enabled );
|
||||
|
||||
/* Initialize GPUs */
|
||||
|
||||
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
|
||||
--- a/dlls/winex11.drv/event.c
|
||||
+++ b/dlls/winex11.drv/event.c
|
||||
@@ -1497,6 +1497,10 @@
|
||||
if (event->atom == x11drv_atom(_NET_SUPPORTED)) handle_net_supported_notify( event );
|
||||
if (event->atom == x11drv_atom(_NET_ACTIVE_WINDOW)) handle_net_active_window( event );
|
||||
if (event->atom == x11drv_atom(_NET_SUPPORTING_WM_CHECK)) handle_net_supporting_wm_check_notify( event );
|
||||
+ if (event->atom == x11drv_atom(GAMESCOPE_HDR_OUTPUT_FEEDBACK) &&
|
||||
+ event->window == DefaultRootWindow(event->display) && hwnd == NtUserGetDesktopWindow() &&
|
||||
+ NtUserGetWindowThread( hwnd, NULL ) == GetCurrentThreadId())
|
||||
+ NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
|
||||
--- a/dlls/winex11.drv/x11drv.h
|
||||
+++ b/dlls/winex11.drv/x11drv.h
|
||||
@@ -598,6 +598,7 @@
|
||||
XATOM_text_uri_list,
|
||||
XATOM_GAMESCOPE_XALIA_OVERLAY,
|
||||
XATOM_GAMESCOPE_DISPLAY_EDID_PATH,
|
||||
+ XATOM_GAMESCOPE_HDR_OUTPUT_FEEDBACK,
|
||||
NB_XATOMS
|
||||
};
|
||||
|
||||
|
||||
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
|
||||
--- a/dlls/winex11.drv/x11drv_main.c
|
||||
+++ b/dlls/winex11.drv/x11drv_main.c
|
||||
@@ -210,6 +210,7 @@
|
||||
"text/uri-list",
|
||||
"GAMESCOPE_XALIA_OVERLAY",
|
||||
"GAMESCOPE_DISPLAY_EDID_PATH",
|
||||
+ "GAMESCOPE_HDR_OUTPUT_FEEDBACK",
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
diff --git a/dlls/winewayland.drv/display.c b/dlls/winewayland.drv/display.c
|
||||
--- a/dlls/winewayland.drv/display.c
|
||||
+++ b/dlls/winewayland.drv/display.c
|
||||
@@ -385,6 +385,8 @@
|
||||
/* An HDR request cannot enable HDR on an SDR compositor output. */
|
||||
if ((env = getenv("DXVK_NO_HDR")) && *env == '1')
|
||||
monitor.hdr_enabled = FALSE;
|
||||
+ if ((env = getenv("DXVK_HDR")) && *env == '0')
|
||||
+ monitor.hdr_enabled = FALSE;
|
||||
|
||||
info->flags = MONITOR_INFO_HAS_PRIMARIES | MONITOR_INFO_HAS_PREFERRED_MODE
|
||||
| MONITOR_INFO_HAS_PHYSICAL_DIMENSIONS | MONITOR_INFO_HAS_MONITOR_NAME;
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] winewayland: Detect HDR independently of Windows image descriptions.
|
||||
|
||||
Dragon's Dogma 2 no longer exposes HDR after 58448e1e added DXGI checks
|
||||
against Wine's active output state. Replacing DXVK with the GE-Proton11-7
|
||||
release version avoids the check, but does not correct the output state.
|
||||
|
||||
The em11 color-management code requires supports_win_scrgb before marking
|
||||
an output as HDR. These Windows-specific image descriptions are optional:
|
||||
the Vulkan driver can present HDR10 through standard Wayland parametric
|
||||
color management without either Windows-specific feature.
|
||||
|
||||
On the affected labwc session, both outputs advertise BT.2020/PQ with
|
||||
max_lum=10000 and ref_lum=203, and RADV exposes HDR10_ST2084 on Wayland
|
||||
surfaces. Neither Windows image description is advertised, so Wine
|
||||
incorrectly reports SDR and DXGI rejects HDR before presentation.
|
||||
|
||||
Derive output HDR state from its reported luminance headroom, independently
|
||||
of the optional presentation path. Keep Windows image-description support
|
||||
as the gate for Wine's passthrough mapping in vulkan.c; otherwise the
|
||||
original colorspace is passed to Vulkan WSI. Surface-format support checks,
|
||||
per-output SDR fallback, and explicit HDR opt-outs remain unchanged.
|
||||
|
||||
Downgrade the missing optional-feature messages to traces: their absence
|
||||
does not imply broken HDR when Vulkan WSI supports the requested colorspace.
|
||||
|
||||
Follow-up to the color-management implementation by Etaash Mathamsetty
|
||||
<[email protected]>.
|
||||
|
||||
Validation before rebuilding:
|
||||
- wayland-info: HDR output luminances, no windows_scrgb/windows_bt2100.
|
||||
- vulkaninfo: RADV exposes HDR10_ST2084 for VK_KHR_wayland_surface.
|
||||
|
||||
Manual validation needed after rebuilding:
|
||||
- Dragon's Dogma 2 with native Wayland and HDR enabled, without HDR flags.
|
||||
- HDR disabled and mixed SDR/HDR outputs must retain SDR fallback.
|
||||
- DXVK_NO_HDR=1 and DXVK_HDR=0 must still disable HDR reporting.
|
||||
- Compositors with Windows image descriptions retain passthrough mapping.
|
||||
---
|
||||
|
||||
diff --git a/dlls/winewayland.drv/wayland_output.c b/dlls/winewayland.drv/wayland_output.c
|
||||
--- a/dlls/winewayland.drv/wayland_output.c
|
||||
+++ b/dlls/winewayland.drv/wayland_output.c
|
||||
@@ -196,8 +196,9 @@ static void wayland_output_done(struct wayland_output *output)
|
||||
output->current.ref_lum = output->pending.ref_lum;
|
||||
}
|
||||
|
||||
- output->current.supports_hdr = process_wayland.supports_win_scrgb &&
|
||||
- (output->current.max_lum > output->current.ref_lum);
|
||||
+ /* Output HDR state is independent of the optional Windows image descriptions.
|
||||
+ * Vulkan WSI can handle HDR colorspaces through standard color management. */
|
||||
+ output->current.supports_hdr = output->current.max_lum > output->current.ref_lum;
|
||||
|
||||
output->current.flags |= output->pending.flags;
|
||||
output->pending.flags = 0;
|
||||
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c
|
||||
--- a/dlls/winewayland.drv/wayland.c
|
||||
+++ b/dlls/winewayland.drv/wayland.c
|
||||
@@ -745,10 +745,10 @@ BOOL wayland_process_init(void)
|
||||
ERR("Wayland compositor doesn't support xdg_activation_v1! (Window Activation will not be supported)\n");
|
||||
|
||||
if (!process_wayland.supports_win_pq)
|
||||
- ERR("Wayland compositor doesn't expose windows_bt2100 image description (HDR may look broken)!\n");
|
||||
+ TRACE("No windows_bt2100 image description; using Vulkan WSI for HDR10.\n");
|
||||
|
||||
if (!process_wayland.supports_win_scrgb)
|
||||
- ERR("Wayland compositor doesn't expose windows_scrgb image description (HDR may look broken)!\n");
|
||||
+ TRACE("No windows_scrgb image description; using Vulkan WSI for scRGB.\n");
|
||||
|
||||
if (!process_wayland.zwp_linux_dmabuf_v1)
|
||||
WARN("Wayland compositor doesn't support optional zwp_linux_dmabuf_v1 (HWND dmabuf forwarding won't work)\n");
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
From 9aee0e713e004256419e3a253a3634364a104e18 Mon Sep 17 00:00:00 2001
|
||||
From: Nikolay Sivov <[email protected]>
|
||||
Date: Tue, 24 Mar 2026 21:14:15 +0100
|
||||
Subject: [PATCH] secur32/tests: Test required buffer size in case of
|
||||
SEC_E_INCOMPLETE_MESSAGE.
|
||||
|
||||
Required remaining size is returned for both SECBUFFER_DATA and the first SECBUFFER_EMPTY buffer.
|
||||
|
||||
Signed-off-by: Nikolay Sivov <[email protected]>
|
||||
---
|
||||
dlls/secur32/tests/schannel.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c
|
||||
index 2f693c6f871..66e995ba21c 100644
|
||||
--- a/dlls/secur32/tests/schannel.c
|
||||
+++ b/dlls/secur32/tests/schannel.c
|
||||
@@ -1371,11 +1371,15 @@ static void test_communication(void)
|
||||
|
||||
/* Incomplete data */
|
||||
buffers[0].pBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||||
+ buffers[0].pBuffers[1].cbBuffer = 1;
|
||||
buffers[0].pBuffers[0].cbBuffer = (data[3]<<8) | data[4];
|
||||
status = DecryptMessage(&context, &buffers[0], 0, NULL);
|
||||
ok(status == SEC_E_INCOMPLETE_MESSAGE, "Expected SEC_E_INCOMPLETE_MESSAGE, got %08lx\n", status);
|
||||
ok(buffers[0].pBuffers[0].BufferType == SECBUFFER_MISSING, "Expected first buffer to be SECBUFFER_MISSING\n");
|
||||
ok(buffers[0].pBuffers[0].cbBuffer == 5, "Expected first buffer to be a five bytes\n");
|
||||
+ ok(buffers[0].pBuffers[1].BufferType == SECBUFFER_MISSING, "Unexpected buffer type\n");
|
||||
+ todo_wine
|
||||
+ ok(buffers[0].pBuffers[1].cbBuffer == 5, "Unexpected buffer size\n");
|
||||
|
||||
/* Renegotiate */
|
||||
buffers[0].pBuffers[0].BufferType = SECBUFFER_DATA;
|
||||
--
|
||||
GitLab
|
||||
|
||||
From 35b1e7eb9a7e66c49fac8583e50186c6c21736e6 Mon Sep 17 00:00:00 2001
|
||||
From: Nikolay Sivov <[email protected]>
|
||||
Date: Tue, 24 Mar 2026 22:11:38 +0100
|
||||
Subject: [PATCH] secur32/schannel: Fix returned required buffer size for
|
||||
incomplete messages in DecryptMessage().
|
||||
|
||||
Signed-off-by: Nikolay Sivov <[email protected]>
|
||||
---
|
||||
dlls/secur32/schannel.c | 6 +++---
|
||||
dlls/secur32/tests/schannel.c | 1 -
|
||||
2 files changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c
|
||||
index 3ad6be5e738..83aea0b4737 100644
|
||||
--- a/dlls/secur32/schannel.c
|
||||
+++ b/dlls/secur32/schannel.c
|
||||
@@ -1486,13 +1486,13 @@ static void schan_decrypt_fill_buffer(PSecBufferDesc message, ULONG buffer_type,
|
||||
static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle,
|
||||
PSecBufferDesc message, ULONG message_seq_no, PULONG quality)
|
||||
{
|
||||
+ unsigned expected_size, remaining_size;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
struct schan_context *ctx;
|
||||
struct recv_params params;
|
||||
SecBuffer *buffer;
|
||||
SIZE_T data_size;
|
||||
char *data;
|
||||
- unsigned expected_size;
|
||||
ULONG received = 0;
|
||||
int idx;
|
||||
unsigned char *buf_ptr;
|
||||
@@ -1517,13 +1517,13 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
|
||||
{
|
||||
TRACE("Expected %u bytes, but buffer only contains %lu bytes\n", expected_size, buffer->cbBuffer);
|
||||
buffer->BufferType = SECBUFFER_MISSING;
|
||||
- buffer->cbBuffer = expected_size - buffer->cbBuffer;
|
||||
+ buffer->cbBuffer = remaining_size = expected_size - buffer->cbBuffer;
|
||||
|
||||
/* This is a bit weird, but windows does it too */
|
||||
idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
|
||||
buffer = &message->pBuffers[idx];
|
||||
buffer->BufferType = SECBUFFER_MISSING;
|
||||
- buffer->cbBuffer = expected_size - buffer->cbBuffer;
|
||||
+ buffer->cbBuffer = remaining_size;
|
||||
|
||||
TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c
|
||||
index 66e995ba21c..04ef9ca6880 100644
|
||||
--- a/dlls/secur32/tests/schannel.c
|
||||
+++ b/dlls/secur32/tests/schannel.c
|
||||
@@ -1378,7 +1378,6 @@ static void test_communication(void)
|
||||
ok(buffers[0].pBuffers[0].BufferType == SECBUFFER_MISSING, "Expected first buffer to be SECBUFFER_MISSING\n");
|
||||
ok(buffers[0].pBuffers[0].cbBuffer == 5, "Expected first buffer to be a five bytes\n");
|
||||
ok(buffers[0].pBuffers[1].BufferType == SECBUFFER_MISSING, "Unexpected buffer type\n");
|
||||
- todo_wine
|
||||
ok(buffers[0].pBuffers[1].cbBuffer == 5, "Unexpected buffer size\n");
|
||||
|
||||
/* Renegotiate */
|
||||
--
|
||||
GitLab
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Date: Tue, 13 Oct 2026 01:00:00 -0600
|
||||
Subject: [PATCH] server: Clip painting of off-desktop minimized windows.
|
||||
|
||||
Deadlock briefly paints a white 160x31 rectangle at startup on Niri,
|
||||
even after unsupported minimization is reconciled and with Steam's
|
||||
overlay disabled. The existing X11 traces show SW_MINIMIZE parking the
|
||||
Win32 window at (-32000,-32000) with exactly that iconic size, followed
|
||||
by non-client GDI painting into the still-mapped full-size X drawable.
|
||||
|
||||
Top-level visible regions normally rely on the native window manager
|
||||
for clipping. That assumption fails when the host does not implement
|
||||
minimization and keeps the normal-sized window mapped while Wine waits
|
||||
to restore its Win32 state.
|
||||
|
||||
Return an empty GDI visible region for minimized top-level windows whose
|
||||
iconic rectangle lies wholly outside the desktop. Map the desktop bounds
|
||||
to the window's DPI before comparing them. Keep child windows, on-screen
|
||||
minimized icons (including virtual desktops), and non-minimized windows
|
||||
on the existing path. Normal positioning invalidates DCs and restores
|
||||
their visible region when the window is restored.
|
||||
|
||||
Do this in the shared server path so buffered GDI painting and direct
|
||||
X11 drawing are clipped consistently, including Wine-Wayland. Do not
|
||||
change Vulkan surface lifetime, host mapping, or focus reconciliation.
|
||||
Keep the existing Wine-Wayland 0068 and satellite minimization fixes.
|
||||
|
||||
Add coverage for cached and own DCs across minimize/restore, including
|
||||
the non-client DC used for the iconic frame. Skip the off-desktop checks
|
||||
when the host restores the window or displays its minimized icon.
|
||||
|
||||
Bug: https://github.com/GloriousEggroll/proton-ge-custom/issues/762
|
||||
---
|
||||
diff --git a/server/window.c b/server/window.c
|
||||
--- a/server/window.c
|
||||
+++ b/server/window.c
|
||||
@@ -1453,6 +1453,17 @@
|
||||
return region;
|
||||
}
|
||||
|
||||
+ /* Native window managers normally clip top-level windows, but may keep an
|
||||
+ * unsupported minimize mapped. Do not paint its off-desktop iconic frame
|
||||
+ * into the retained host window. On-screen minimized icons still paint. */
|
||||
+ if ((win->style & WS_MINIMIZE) && is_desktop_window( win->parent ))
|
||||
+ {
|
||||
+ struct rectangle desktop = win->parent->client_rect;
|
||||
+
|
||||
+ map_dpi_rect( win, &desktop, get_window_dpi( win->parent ), get_window_dpi( win ) );
|
||||
+ if (!intersect_rect( &desktop, &desktop, &win->visible_rect )) return region;
|
||||
+ }
|
||||
+
|
||||
/* create a region relative to the window itself */
|
||||
|
||||
if ((flags & DCX_PARENTCLIP) && !is_desktop_window( win->parent ))
|
||||
diff --git a/dlls/user32/tests/dce.c b/dlls/user32/tests/dce.c
|
||||
--- a/dlls/user32/tests/dce.c
|
||||
+++ b/dlls/user32/tests/dce.c
|
||||
@@ -711,6 +711,61 @@
|
||||
DestroyWindow(hwnd_cache_rtl);
|
||||
}
|
||||
|
||||
+static void test_minimized_dc_visrgn(void)
|
||||
+{
|
||||
+ static const char *classes[] = { "cache_class", "owndc_class" };
|
||||
+ RECT desktop, rect, intersect;
|
||||
+ HWND hwnd;
|
||||
+ HDC hdc, window_dc;
|
||||
+ unsigned int i;
|
||||
+ int ret;
|
||||
+
|
||||
+ SetRect( &desktop, GetSystemMetrics( SM_XVIRTUALSCREEN ), GetSystemMetrics( SM_YVIRTUALSCREEN ), 0, 0 );
|
||||
+ desktop.right = desktop.left + GetSystemMetrics( SM_CXVIRTUALSCREEN );
|
||||
+ desktop.bottom = desktop.top + GetSystemMetrics( SM_CYVIRTUALSCREEN );
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(classes); ++i)
|
||||
+ {
|
||||
+ winetest_push_context( "%s", classes[i] );
|
||||
+ hwnd = CreateWindowA( classes[i], NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
+ 0, 0, 200, 150, 0, 0, GetModuleHandleA(0), NULL );
|
||||
+ ok( !!hwnd, "CreateWindow failed, error %lu\n", GetLastError() );
|
||||
+ if (!hwnd)
|
||||
+ {
|
||||
+ winetest_pop_context();
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ hdc = GetDC( hwnd );
|
||||
+ ok( !!hdc, "GetDC failed\n" );
|
||||
+ ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
|
||||
+ GetWindowRect( hwnd, &rect );
|
||||
+ if (!IsIconic( hwnd ) || IntersectRect( &intersect, &desktop, &rect ))
|
||||
+ skip( "Window manager did not park the minimized window outside the desktop\n" );
|
||||
+ else
|
||||
+ {
|
||||
+ ret = GetClipBox( hdc, &rect );
|
||||
+ ok( ret == NULLREGION, "Minimized client region %d, %s\n", ret, wine_dbgstr_rect(&rect) );
|
||||
+ window_dc = GetWindowDC( hwnd );
|
||||
+ ok( !!window_dc, "GetWindowDC failed\n" );
|
||||
+ ret = GetClipBox( window_dc, &rect );
|
||||
+ ok( ret == NULLREGION, "Minimized window region %d, %s\n", ret, wine_dbgstr_rect(&rect) );
|
||||
+ ReleaseDC( hwnd, window_dc );
|
||||
+ }
|
||||
+
|
||||
+ ShowWindow( hwnd, SW_RESTORE );
|
||||
+ ret = GetClipBox( hdc, &rect );
|
||||
+ ok( ret > NULLREGION, "Restored client region %d, %s\n", ret, wine_dbgstr_rect(&rect) );
|
||||
+ ReleaseDC( hwnd, hdc );
|
||||
+ window_dc = GetWindowDC( hwnd );
|
||||
+ ret = GetClipBox( window_dc, &rect );
|
||||
+ ok( ret > NULLREGION, "Restored window region %d, %s\n", ret, wine_dbgstr_rect(&rect) );
|
||||
+ ReleaseDC( hwnd, window_dc );
|
||||
+ DestroyWindow( hwnd );
|
||||
+ winetest_pop_context();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void test_destroyed_window(void)
|
||||
{
|
||||
HDC dc, old_dc;
|
||||
@@ -833,6 +888,7 @@
|
||||
test_scroll_window();
|
||||
test_invisible_create();
|
||||
test_dc_layout();
|
||||
+ test_minimized_dc_visrgn();
|
||||
/* this should be last */
|
||||
test_destroyed_window();
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Date: Mon, 12 Oct 2026 01:00:00 -0600
|
||||
Subject: [PATCH] winex11: Reconcile unsupported minimization on satellite.
|
||||
|
||||
Both X11 traces for Deadlock on Niri show the same startup transition,
|
||||
with and without the user's window rule. The game calls SW_MINIMIZE
|
||||
before creating its Vulkan surface. Wine requests NormalState ->
|
||||
IconicState, but the window manager does not acknowledge that request.
|
||||
The iconic Win32 client rectangle then produces a 1x1 Vulkan child.
|
||||
Refocusing causes WM_STATE=NormalState to arrive, clears the pending
|
||||
request and restores the child to 3440x1440.
|
||||
|
||||
xwayland-satellite does not handle the WM_CHANGE_STATE message sent by
|
||||
XIconifyWindow. It writes NormalState on mapping and focusing instead:
|
||||
https://github.com/Supreeeme/xwayland-satellite/blob/ae88928f7334556d298b8d9552abdf395931931b/src/xstate/mod.rs
|
||||
|
||||
First recognize its WM name: satellite writes _NET_WM_NAME as STRING
|
||||
rather than UTF8_STRING. Request either string type and validate the
|
||||
format; otherwise Xlib returns success with an empty buffer for the type
|
||||
mismatch and Wine identifies the window manager as "".
|
||||
|
||||
For managed, non-embedded satellite windows without advertised HIDDEN
|
||||
support, keep the desired host state NormalState rather than queueing an
|
||||
unsupported iconify request. Post the normal window-state reconciliation
|
||||
message, and repeat it if restoration had to wait for decoration hints.
|
||||
Only restore the Win32 minimized state while the host reports the window
|
||||
active, so a background game's focus-loss minimize cannot steal focus.
|
||||
|
||||
Do not infer unsupported minimization from absent EWMH capabilities on
|
||||
other window managers: legacy ICCCM WMs can minimize without those hints.
|
||||
Preserve hidden windows, embedded windows, virtual desktops, and the
|
||||
existing paths for other WMs or satellite advertising HIDDEN support.
|
||||
This complements Wine-Wayland fixup 0068; it does not replace it.
|
||||
|
||||
Bug: https://github.com/GloriousEggroll/proton-ge-custom/issues/762
|
||||
Logs: https://github.com/GloriousEggroll/proton-ge-custom/issues/762#issuecomment-6116456428
|
||||
---
|
||||
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
|
||||
--- a/dlls/winex11.drv/window.c
|
||||
+++ b/dlls/winex11.drv/window.c
|
||||
@@ -1803,6 +1803,15 @@
|
||||
return cached;
|
||||
}
|
||||
|
||||
+static BOOL window_minimize_unsupported( struct x11drv_win_data *data )
|
||||
+{
|
||||
+ /* Satellite ignores WM_CHANGE_STATE. Do not infer this for other WMs from
|
||||
+ * missing EWMH hints alone, since legacy ICCCM WMs can still minimize. */
|
||||
+ return data->managed && !data->embedded && !is_virtual_desktop() &&
|
||||
+ X11DRV_HasWindowManager( "xwayland-satellite" ) &&
|
||||
+ !is_net_supported( x11drv_atom(_NET_WM_STATE_HIDDEN) );
|
||||
+}
|
||||
+
|
||||
static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate )
|
||||
{
|
||||
UINT old_state = data->pending_state.wm_state;
|
||||
@@ -1818,6 +1827,17 @@
|
||||
activate = FALSE;
|
||||
}
|
||||
|
||||
+ if (new_state == IconicState && window_minimize_unsupported( data ))
|
||||
+ {
|
||||
+ /* Keep the mapped host state instead of waiting for an acknowledgment
|
||||
+ * that will never arrive. Reconcile Win32 state on the window thread. */
|
||||
+ TRACE( "window %p/%lx, host minimization unsupported, keeping NormalState\n",
|
||||
+ data->hwnd, data->whole_window );
|
||||
+ new_state = NormalState;
|
||||
+ activate = FALSE;
|
||||
+ NtUserPostMessage( data->hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 );
|
||||
+ }
|
||||
+
|
||||
data->desired_state.wm_state = new_state;
|
||||
data->desired_state.activate = activate;
|
||||
if (data->state_locks) return; /* win32 state is being updated, delay the change */
|
||||
@@ -1983,6 +2003,12 @@
|
||||
if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */
|
||||
if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */
|
||||
|
||||
+ /* An ignored minimize must not bring a background window back to the
|
||||
+ * foreground. Satellite sends WM_STATE again when the user focuses it. */
|
||||
+ if ((old_style & WS_MINIMIZE) && window_minimize_unsupported( data ) &&
|
||||
+ get_net_active_window( data->display ) != data->whole_window)
|
||||
+ return 0;
|
||||
+
|
||||
new_style = old_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE);
|
||||
if (data->current_state.wm_state != WithdrawnState) new_style |= WS_VISIBLE;
|
||||
if (data->current_state.wm_state == IconicState) new_style |= WS_MINIMIZE;
|
||||
@@ -2301,6 +2327,11 @@
|
||||
|
||||
/* send any pending changes from the desired state */
|
||||
window_request_desired_state( data );
|
||||
+
|
||||
+ /* The unsupported-minimize restore may have waited for these hints. */
|
||||
+ if ((NtUserGetWindowLongW( data->hwnd, GWL_STYLE ) & WS_MINIMIZE) &&
|
||||
+ window_minimize_unsupported( data ))
|
||||
+ NtUserPostMessage( data->hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 );
|
||||
}
|
||||
|
||||
void window_wm_normal_hints_notify( struct x11drv_win_data *data, unsigned long serial, const XSizeHints *value )
|
||||
@@ -2393,9 +2424,15 @@
|
||||
|
||||
*name = NULL;
|
||||
X11DRV_expect_error( display, host_window_error, NULL );
|
||||
- ret = XGetWindowProperty( display, window, x11drv_atom(_NET_WM_NAME), 0, 65536 / sizeof(CARD32), False, x11drv_atom(UTF8_STRING),
|
||||
+ /* Satellite supplies its ASCII WM name as STRING rather than UTF8_STRING.
|
||||
+ * A type mismatch otherwise succeeds with an empty buffer from Xlib. */
|
||||
+ ret = XGetWindowProperty( display, window, x11drv_atom(_NET_WM_NAME), 0, 65536 / sizeof(CARD32), False, AnyPropertyType,
|
||||
&type, &format, &count, &remaining, (unsigned char **)name );
|
||||
- return !X11DRV_check_error() && !ret && *name;
|
||||
+ if (!X11DRV_check_error() && !ret && *name && format == 8 && count &&
|
||||
+ (type == x11drv_atom(UTF8_STRING) || type == XA_STRING)) return TRUE;
|
||||
+ XFree( *name );
|
||||
+ *name = NULL;
|
||||
+ return FALSE;
|
||||
}
|
||||
|
||||
static BOOL get_window_wm_name( Display *display, Window window, char **name )
|
||||
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
|
||||
--- a/dlls/winex11.drv/x11drv.h
|
||||
+++ b/dlls/winex11.drv/x11drv.h
|
||||
@@ -545,6 +545,7 @@
|
||||
XATOM__NET_WM_STATE_BELOW,
|
||||
XATOM__NET_WM_STATE_DEMANDS_ATTENTION,
|
||||
XATOM__NET_WM_STATE_FULLSCREEN,
|
||||
+ XATOM__NET_WM_STATE_HIDDEN,
|
||||
XATOM__NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
XATOM__NET_WM_STATE_MAXIMIZED_VERT,
|
||||
XATOM__NET_WM_STATE_SKIP_PAGER,
|
||||
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
|
||||
--- a/dlls/winex11.drv/x11drv_main.c
|
||||
+++ b/dlls/winex11.drv/x11drv_main.c
|
||||
@@ -157,6 +157,7 @@
|
||||
"_NET_WM_STATE_BELOW",
|
||||
"_NET_WM_STATE_DEMANDS_ATTENTION",
|
||||
"_NET_WM_STATE_FULLSCREEN",
|
||||
+ "_NET_WM_STATE_HIDDEN",
|
||||
"_NET_WM_STATE_MAXIMIZED_HORZ",
|
||||
"_NET_WM_STATE_MAXIMIZED_VERT",
|
||||
"_NET_WM_STATE_SKIP_PAGER",
|
||||
@@ -0,0 +1,54 @@
|
||||
From: GloriousEggroll <[email protected]>
|
||||
Subject: [PATCH] mini: order publication and consumption of delegate trampoline caches.
|
||||
|
||||
The Ampere ARM64 build of Wine-Mono 11.3.0 crashes in Roslyn while compiling
|
||||
mscorlib.dll. The saved core contains an Action for CompileNamedTypeAsync
|
||||
with the optimized invoke_impl but a NULL method_ptr. The shared
|
||||
MonoDelegateTrampInfo already contains both valid pointers. The ARM64
|
||||
invoke stub loads method_ptr and branches to zero.
|
||||
|
||||
mono_delegate_trampoline publishes method_ptr followed by invoke_impl,
|
||||
and the inlined constructor reads invoke_impl followed by method_ptr.
|
||||
Neither side orders those accesses. On weakly ordered CPUs a constructor
|
||||
can therefore copy the new invoke_impl together with the old NULL
|
||||
method_ptr, permanently skipping lazy initialization for that delegate.
|
||||
|
||||
Publish the target before the optimized entry point with a write barrier,
|
||||
and acquire the entry point before reading the target in the generated
|
||||
constructor. Keep the invoke_impl-first read order: observing the old lazy
|
||||
entry with a new target is safe, but the inverse is not. Also order the
|
||||
target before publishing the optimized entry on the delegate itself.
|
||||
|
||||
This fixes the runtime protocol rather than disabling Roslyn parallelism
|
||||
or changing the compiler or class-library profile. The existing prep loop
|
||||
applies it to both the native host Mono and the Windows runtime builds.
|
||||
---
|
||||
diff --git a/mono/mini/mini-trampolines.c b/mono/mini/mini-trampolines.c
|
||||
--- a/mono/mini/mini-trampolines.c
|
||||
+++ b/mono/mini/mini-trampolines.c
|
||||
@@ -1221,9 +1221,13 @@ mono_delegate_trampoline (host_mgreg_t *regs, guint8 *code, gpointer *arg, guint8* tramp)
|
||||
code = (guint8 *)mini_add_method_trampoline (m, code, mono_method_needs_static_rgctx_invoke (m, FALSE), FALSE);
|
||||
}
|
||||
|
||||
+ /* The optimized entry point uses method_ptr without lazy initialization. */
|
||||
+ mono_memory_write_barrier ();
|
||||
delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
|
||||
if (enable_caching && !callvirt && tramp_info->method) {
|
||||
tramp_info->method_ptr = delegate->method_ptr;
|
||||
+ /* Pair with the acquire in the inlined delegate constructor. */
|
||||
+ mono_memory_write_barrier ();
|
||||
tramp_info->invoke_impl = delegate->invoke_impl;
|
||||
}
|
||||
|
||||
diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c
|
||||
--- a/mono/mini/method-to-ir.c
|
||||
+++ b/mono/mini/method-to-ir.c
|
||||
@@ -3699,6 +3699,8 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int target_method_context_used, int invoke_context_used, gboolean virtual_)
|
||||
} else {
|
||||
dreg = alloc_preg (cfg);
|
||||
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
|
||||
+ /* A cached optimized entry must not be paired with a stale method_ptr. */
|
||||
+ mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
|
||||
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
|
||||
|
||||
dreg = alloc_preg (cfg);
|
||||
@@ -69,6 +69,48 @@ Source/metadata inspection, patch validation, and diagnostic launches of
|
||||
the user's rebuilt runtime have been performed. The regression fixtures
|
||||
have not been assembled or run.
|
||||
|
||||
## ARM64 Compiler Delegate Cache Race
|
||||
|
||||
`0004-mini-order-delegate-trampoline-cache-publication.patch` adds matching
|
||||
publication/consumption barriers to the JIT delegate trampoline cache.
|
||||
The 2026-10-13 Ampere `GE-Proton11-8-aarch64` build aborted with SIGSEGV
|
||||
while Roslyn compiled `net_4_x-linux/mscorlib.dll` during the System.Drawing
|
||||
bootstrap (`Makefile.in`'s drawing stamp, `mono.make:181`, error 134).
|
||||
This is the native ARM64 host Mono, not an emulated Windows compiler.
|
||||
|
||||
Offline inspection of the saved core found an `Action` for Roslyn's
|
||||
`<CompileNamedTypeAsync>b__0` with a valid optimized `invoke_impl` and a
|
||||
NULL `method_ptr`. Its shared `MonoDelegateTrampInfo` had both valid pointers.
|
||||
Disassembly confirmed that the constructor copies `invoke_impl` and then
|
||||
`method_ptr` without an acquire barrier, while the runtime publishes those
|
||||
fields without a release barrier. The optimized ARM64 invoke stub loads
|
||||
the NULL target and branches to address zero. Ordering the cache accesses
|
||||
prevents this mismatched snapshot; a barrier also precedes publication of
|
||||
the delegate's own optimized entry point. No compiler parallelism, profile,
|
||||
game overrides, or graphics code is changed.
|
||||
|
||||
The existing prep loop picks up patch 0004 automatically. Re-run prep before
|
||||
the next build so the host runtime is rebuilt as well as the Windows DLLs.
|
||||
Do not apply it only to the copied build tree: that tree is regenerated.
|
||||
The core and source establish the missing synchronization; the fixed ARM64
|
||||
build still requires validation by the user.
|
||||
|
||||
`tests/delegate-cache-publication.cs` stresses simultaneous construction
|
||||
and invocation of instance, shared-generic, and static delegates, creating
|
||||
fresh AppDomains to exercise cold caches. It is probabilistic and a single
|
||||
successful run does not establish that an unpatched runtime is safe.
|
||||
After building the patched host Mono, run it manually using that runtime
|
||||
and its matching class libraries, for example from the Wine-Mono source root:
|
||||
|
||||
```sh
|
||||
build/mono-unix/runtime/mono-wrapper mono/mcs/class/lib/net_4_x-linux/mcs.exe \
|
||||
-out:/tmp/delegate-cache-publication.exe /path/to/patches/wine-mono/tests/delegate-cache-publication.cs
|
||||
MONO_PATH="$PWD/mono/mcs/class/lib/net_4_x-linux" \
|
||||
build/mono-unix/runtime/mono-wrapper /tmp/delegate-cache-publication.exe
|
||||
```
|
||||
|
||||
The agent has not compiled or run this fixture, nor restarted the Ampere build.
|
||||
|
||||
## FNA Stencil Reference Ordering
|
||||
|
||||
`fna/0001-graphicsdevice-preserve-stencil-reference-order.patch` corrects
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
// Each AppDomain has a cold trampoline cache; race construction against its
|
||||
// first invocations. This is a stress test, not a deterministic race detector.
|
||||
class DelegateCachePublication
|
||||
{
|
||||
const int WorkerCount = 16;
|
||||
const int Iterations = 4096;
|
||||
static int staticCalls;
|
||||
|
||||
sealed class Target<T>
|
||||
{
|
||||
public int Calls;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
void Invoke()
|
||||
{
|
||||
Calls++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public Action Create()
|
||||
{
|
||||
return Invoke;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static void InvokeStatic()
|
||||
{
|
||||
Interlocked.Increment(ref staticCalls);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static Action CreateStatic()
|
||||
{
|
||||
return InvokeStatic;
|
||||
}
|
||||
|
||||
static void RunRound()
|
||||
{
|
||||
Exception failure = null;
|
||||
staticCalls = 0;
|
||||
using (var ready = new CountdownEvent(WorkerCount))
|
||||
using (var start = new ManualResetEvent(false))
|
||||
{
|
||||
var workers = new Thread[WorkerCount];
|
||||
for (int i = 0; i < workers.Length; i++)
|
||||
{
|
||||
workers[i] = new Thread(() =>
|
||||
{
|
||||
var valueTarget = new Target<int>();
|
||||
var sharedTarget = new Target<string>();
|
||||
ready.Signal();
|
||||
start.WaitOne();
|
||||
try
|
||||
{
|
||||
for (int j = 0; j < Iterations; j++)
|
||||
{
|
||||
valueTarget.Create()();
|
||||
sharedTarget.Create()();
|
||||
CreateStatic()();
|
||||
}
|
||||
if (valueTarget.Calls != Iterations || sharedTarget.Calls != Iterations)
|
||||
throw new Exception("A delegate invoked the wrong target.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Interlocked.CompareExchange(ref failure, e, null);
|
||||
}
|
||||
});
|
||||
workers[i].Start();
|
||||
}
|
||||
ready.Wait();
|
||||
start.Set();
|
||||
foreach (var worker in workers)
|
||||
worker.Join();
|
||||
}
|
||||
if (failure != null)
|
||||
throw new Exception("Delegate cache stress failed.", failure);
|
||||
if (staticCalls != WorkerCount * Iterations)
|
||||
throw new Exception("A static delegate invocation was lost.");
|
||||
}
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
int rounds = args.Length == 0 ? 64 : int.Parse(args[0]);
|
||||
if (rounds < 1)
|
||||
throw new ArgumentOutOfRangeException("rounds");
|
||||
for (int i = 0; i < rounds; i++)
|
||||
{
|
||||
var domain = AppDomain.CreateDomain("delegate-cache-" + i);
|
||||
try
|
||||
{
|
||||
domain.DoCallBack(RunRound);
|
||||
}
|
||||
finally
|
||||
{
|
||||
AppDomain.Unload(domain);
|
||||
}
|
||||
}
|
||||
Console.WriteLine("Delegate cache publication stress passed ({0} rounds).", rounds);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ import protonfixes
|
||||
#and edit it if needed.
|
||||
import utilities
|
||||
|
||||
CURRENT_PREFIX_VERSION="GE-Proton11-7"
|
||||
CURRENT_PREFIX_VERSION="GE-Proton11-9"
|
||||
|
||||
PFX="Proton: "
|
||||
ld_path_var = "LD_LIBRARY_PATH"
|
||||
@@ -2318,6 +2318,8 @@ class Session:
|
||||
self.check_environment("PROTON_USE_OPTISCALER", "optiscaler")
|
||||
|
||||
self.check_environment("PROTON_NATIVE_AGS", "nativeags")
|
||||
# Legacy overrides only; HDR detection no longer requires either flag.
|
||||
# Preserve PROTON_USE_HDR precedence when both aliases are set.
|
||||
self.check_environment("PROTON_ENABLE_HDR", "hdr")
|
||||
self.check_environment("PROTON_USE_HDR", "hdr")
|
||||
self.check_environment("PROTON_NO_NTSYNC", "nontsync")
|
||||
@@ -2564,8 +2566,14 @@ class Session:
|
||||
if "SDL_GAMECONTROLLER_IGNORE_DEVICES" in self.env:
|
||||
del self.env["SDL_GAMECONTROLLER_IGNORE_DEVICES"]
|
||||
|
||||
# Leave DXVK_HDR unset by default: shared DXGI detects HDR for DX11/DX12
|
||||
# from Wine Wayland output state or Gamescope XWayland feedback.
|
||||
# Retain legacy opt-ins/opt-outs without changing the display driver.
|
||||
if "hdr" in self.compat_config:
|
||||
self.env["DXVK_HDR"] = "1"
|
||||
elif "PROTON_ENABLE_HDR" in self.env or "PROTON_USE_HDR" in self.env:
|
||||
self.env["DXVK_HDR"] = "0"
|
||||
self.env["DXVK_NO_HDR"] = "1"
|
||||
|
||||
if "fnad3d11" in self.compat_config and "FNA3D_FORCE_DRIVER" not in self.env:
|
||||
self.env["FNA3D_FORCE_DRIVER"] = "D3D11"
|
||||
|
||||
Reference in new issue
Block a user