murkl / d2launcher

Diablo II • Median XL • Mod Launcher for Linux
https://github.com/murkl/d2launcher/releases/latest
32 stars 8 forks source link

3dfx breaks rendering if fullscreen #20

Closed Melechtna closed 1 month ago

Melechtna commented 1 month ago

I'm guessing this is either an issue with wayland, or xwayland. Regardless, I've been trying to figure out where to insert gamescope, which SHOULD resolve the issue. Direct draw is....bad, very VERY bad and everything is blown out color wise. Gamescope should hypothetically resolve this, but the way the script is structured, I'd basically have to redo the entire structure to insert the call from what I can tell.

There's a number of reasons I'd rather use the glide renderer and have it fullscreen. So if you could maybe point me in the right direction, I'd appreciate it.

Melechtna commented 1 month ago

I apparently have brain damage or something, just running the script with gamescope works fine

Melechtna commented 1 month ago

Well, I've found a slightly different issue with doing it the way I'm doing it. I'd like it if the D2Statistics would still run in a regular window, while the game itself runs in Gamescope, but I can't seem to figure out how to insert gamescope into the script so it ONLY applies to Diablo2 itself.

Melechtna commented 1 month ago

d2launcher.zip This is what I've tried to do to impliment this myself, but it ends in an odd error

No D2Launcher update available
No Median XL update available
No CAP_SYS_NICE, falling back to regular-priority compute and threads.
Performance will be affected.
ATTENTION: default value of option vk_khr_present_wait overridden by environment.
ATTENTION: default value of option vk_khr_present_wait overridden by environment.
ATTENTION: default value of option vk_khr_present_wait overridden by environment.
vulkan: selecting physical device 'AMD Radeon RX 6800 (RADV NAVI21)': queue family 1 (general queue family 0)
vulkan: physical device supports DRM format modifiers
wlserver: [backend/headless/backend.c:67] Creating headless backend
vulkan: supported DRM formats for sampling usage:
vulkan:   AR24 (0x34325241)
vulkan:   XR24 (0x34325258)
vulkan:   AB24 (0x34324241)
vulkan:   XB24 (0x34324258)
vulkan:   RG16 (0x36314752)
vulkan:   NV12 (0x3231564E)
vulkan:   AB4H (0x48344241)
vulkan:   XB4H (0x48344258)
vulkan:   AB48 (0x38344241)
vulkan:   XB48 (0x38344258)
vulkan:   AB30 (0x30334241)
vulkan:   XB30 (0x30334258)
vulkan:   AR30 (0x30335241)
vulkan:   XR30 (0x30335258)
vulkan: Creating Gamescope nested swapchain with format 64 and colorspace 0
wlserver: Running compositor on wayland display 'gamescope-0'
wlserver: [backend/headless/backend.c:17] Starting headless backend
wlserver: [xwayland/server.c:108] Starting Xwayland on :1
The XKEYBOARD keymap compiler (xkbcomp) reports:
> Warning:          Could not resolve keysym XF86CameraAccessEnable
> Warning:          Could not resolve keysym XF86CameraAccessDisable
> Warning:          Could not resolve keysym XF86CameraAccessToggle
> Warning:          Could not resolve keysym XF86NextElement
> Warning:          Could not resolve keysym XF86PreviousElement
> Warning:          Could not resolve keysym XF86AutopilotEngageToggle
> Warning:          Could not resolve keysym XF86MarkWaypoint
> Warning:          Could not resolve keysym XF86Sos
> Warning:          Could not resolve keysym XF86NavChart
> Warning:          Could not resolve keysym XF86FishingChart
> Warning:          Could not resolve keysym XF86SingleRangeRadar
> Warning:          Could not resolve keysym XF86DualRangeRadar
> Warning:          Could not resolve keysym XF86RadarOverlay
> Warning:          Could not resolve keysym XF86TraditionalSonar
> Warning:          Could not resolve keysym XF86ClearvuSonar
> Warning:          Could not resolve keysym XF86SidevuSonar
> Warning:          Could not resolve keysym XF86NavInfo
Errors from xkbcomp are not fatal to the X server
wlserver: [types/wlr_compositor.c:692] New wlr_surface 0x560b40109820 (res 0x560b40110240)
wlserver: [xwayland/server.c:273] Xserver is ready
pipewire: stream state changed: connecting
pipewire: stream state changed: paused
pipewire: stream available on node ID: 110
vblank: Using timerfd.
xwm: execvp failed: No such file or directory
gamescope: children shut down!
(EE) failed to read Wayland events: Broken pipe

Thing is, I have no idea why this is a problem to begin with.

murkl commented 1 month ago

It is currently not supported to set a prefix (wine_init property) only for Diablo II itself. You can implement this, by adding a new function e.g. exec_wine_exe_diablo and set the prefix for gamescope inside this function. Then replace this new function in function exec_diablo2. For a better code overview, please create a fork/PR for code changes.

Melechtna commented 1 month ago

It is currently not supported to set a prefix (wine_init property) only for Diablo II itself. You can implement this, by adding a new function e.g. exec_wine_exe_diablo and set the prefix for gamescope inside this function. Then replace this new function in function exec_diablo2. For a better code overview, please create a fork/PR for code changes.

Well, if a new prefix was created, the statistics program wouldn't be able to attach itself to the Diablo 2 instance. gamescope doesn't directly do anything to the WINE prefix itself. Instead, it creates a "fake screen", which the game then renders on. The problem, is that if gamescope is applied to the entire script, then statistics will be stretched out to the entire screen along with Diablo 2.

What I'm trying to do, and I'd request you'd look at my attempts that I attached in my initial message, is make it so that, when wine spawns the Diablo 2 window, and the added gamescope value is true, it'll run the Diablo 2.exe with the gamescope program. The problem is, this results in an error I don't understand why occurs.

murkl commented 1 month ago

I see, but when you could start Diablo with gamescope (prefixed) and d2stats without, it should work or is'nt it (both in the same wineprefix)? Thats my approach with the new exec_wine_exe_diablo function. It is very hard to detect your code changes without git but what i have seen is line 803: exec_wine_exe is a function and not known outside the script (which comes after gamescope $gamescope_args -- exec_wine_exe....). This can be bypassed with the new exec_wine_exe_diablo function.

Melechtna commented 1 month ago

I see the problem

# /////////////////////////////////////////////////////
# CONFIGURATION
# /////////////////////////////////////////////////////

# Urls
wine_native_url="https://github.com/Kron4ek/Wine-Builds/releases/download/7.0-5-proton/wine-7.0-5-proton-amd64.tar.xz"
d2_stats_url="https://github.com/Zahariel1942/D2Stats/releases/latest/download/D2Stats.zip"
d2_sigma_loader_url="https://github.com/SyndromeDayna/diablo-2-median-xl-sigma-loader/releases/download/3/sigma-loader.exe"

# Wine
wine_default="$WINE_INTERNAL"
wineprefix="$WINE_INTERNAL_PREFIX"
wine_user="$WINE_INTERNAL_USER"
wine_init=""

# Gamescope
gamescope="true"
gamescope_args="-W 2560 -H 1440 -F fsr -b"

This was added to configuration, I'd like to add these two values to your config menu so users can set their own values, but I figured it should be working first.

# /////////////////////////////////////////////////////
# EXECUTION FUNCTIONS
# /////////////////////////////////////////////////////

exec_wine() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" $wine_init "$wine_default" "$@" 2>&1 | log
}

exec_wine_exe() {
    exec_wine start /unix "$@"
}

exec_diablo2() {
    check_d2_dir || return 1
    check_wine_install || return 1
    check_patch_installation || return 1
    if pgrep -x "$D2_STATS_EXE" >/dev/null; then
        # If D2Stats is running do this ...
        if [ ! -f "$d2_dir/$D2_SIGMALOADER_EXE" ]; then
            zenity_error "$d2_dir/$D2_SIGMALOADER_EXE not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            gamescope $gamescope_args -- exec_wine_exe "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        fi
    else
        if [ ! -f "$d2_dir/$d2_exe" ]; then
            zenity_error "$d2_dir/$d2_exe not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            gamescope $gamescope_args -- exec_wine_exe "$d2_dir/$d2_exe" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$d2_exe" "$d2_args"
        fi
    fi
    exit 0
}

And this is the part modified in execution functions, I've changed nothing else, as, as far as I can tell, this should be all that's needed. But if I'm understanding what you're suggesting, create a new method specifically with the gamescope method? I'm not entirely sure how that changes things, let alone resolves the issue, as functionally, the if else added just branches it to act as 2 seperate things anyway, while retaining what should be the ability to detect the running statistics program, and start Diablo two within the same wine instance.

murkl commented 1 month ago

Simply call only Diablo II with gamescope and the other tools without it (the old way). Something like this (not tested and no clean code, only to demonstrate):

exec_wine_exe_gamescope() {
    check_wine_install || return 1
    export WINEPREFIX="$wineprefix"
    if [ "$gamescope" = "true" ]; then
        gamescope "$gamescope_args" -- "$wine_init" "$wine_default" start /unix "$@" 2>&1 | log
    else
        gamescope "$gamescope_args" -- "$wine_init" "$wine_default" start /unix "$@" 2>&1 | log
    fi
}

exec_diablo2() {
    ...
    exec_wine_exe_gamescope "$d2_dir/$d2_exe" "$d2_args"
    ...
}
Melechtna commented 1 month ago

Wouldn't this just step the issue back to running the whole script with gamescope enabled? If I'm understanding your suggestion correctly, we're just wrapping the whole wine instance into a gamescope window, which would include D2Statistics. I'm guessing you've never used gamescope, but, if we did it this way, d2statistics wouldn't get its own window, and would instead be bound and stretched to the fake screen, along with the Diablo 2 program within the wine instance we call with gamescope, functionally creating the same problem that I'm trying to work around.

The reason only Diablo 2 needs the gamescope, is because it fixes a number of problems with the games rendering, as well as making it possible to do things like, upscale, borderless fullscreen, and a whole host of other things that really improve quality of life. Running this on D2statistics, just makes it really clunky and awkward, not unworkable, but definitely not what you'd want to happen.

murkl commented 1 month ago

No, this approach should start only Diablo II with gamescope. The statistics app should still executed with the current function (exec_wine_exe). Your're right, i never used gamescope so far. But sounds realy nice.

Melechtna commented 1 month ago

No, this approach should start only Diablo II with gamescope. The statistics app should still executed with the current function (exec_wine_exe). Your're right, i never used gamescope so far. But sounds realy nice.

I'll give this a try and report back if I encounter further issues, or what I feel like would happen, happens. Thank you for your time.

murkl commented 1 month ago

Your're welcome, good luck

Melechtna commented 1 month ago

It actually looks like that's almost working, but your example was a bit more than is really needed.

# /////////////////////////////////////////////////////
# EXECUTION FUNCTIONS
# /////////////////////////////////////////////////////

exec_wine() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" $wine_init "$wine_default" "$@" 2>&1 | log
}

exec_wine_exe_gamescope() {
    check_wine_install || return 1
    gamescope "$gamescope_args" -- WINEPREFIX="$wineprefix" "$wine_init" "$wine_default" start /unix "$@" 2>&1 | log
}

exec_wine_exe() {
    exec_wine start /unix "$@"
}

exec_diablo2() {
    check_d2_dir || return 1
    check_wine_install || return 1
    check_patch_installation || return 1
    if pgrep -x "$D2_STATS_EXE" >/dev/null; then
        # If D2Stats is running do this ...
        if [ ! -f "$d2_dir/$D2_SIGMALOADER_EXE" ]; then
            zenity_error "$d2_dir/$D2_SIGMALOADER_EXE not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            exec_wine_exe_gamescope "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        fi
    else
        if [ ! -f "$d2_dir/$d2_exe" ]; then
            zenity_error "$d2_dir/$d2_exe not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            exec_wine_exe_gamescope "$d2_dir/$d2_exe" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$d2_exe" "$d2_args"
        fi
    fi
    exit 0
}

I just needed to basically clone the exec_wine() and shove in the gamescope call, but my issue seems to be a problem parsing the gamescope arguements.

Cannot specify -W without -H
terminate called without an active exception

Which if you look at the config section, is obviously included, having it echo the gamescope_args variable, it clearly has the correct value, so I'm unsure why this error is occurring.

Melechtna commented 1 month ago

As a side note, using the echo method doesn't change the result, and I don't think that should be necessary.

Melechtna commented 1 month ago
exec_wine_exe_gamescope() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" gamescope "$gamescope_args" -- "$wine_init" "$wine_default" start /unix "$@" 2>&1 | log
}

Adjusted it to this, because, I have brain damage and for whatever reason forgot that you can still parse stuff like that before the gamescope call.

Melechtna commented 1 month ago

Caught another mistake I made

exec_wine_exe_gamescope() {
    check_wine_install || return 1

    # Debugging: Print the command to be executed
    echo "Running gamescope with arguments: $gamescope_args"
    echo "Full command: WINEPREFIX=\"$wineprefix\" gamescope $gamescope_args -- $wine_init \"$wine_default\" start /unix \"$@\""

    WINEPREFIX="$wineprefix" gamescope "$gamescope_args" -- $wine_init "$wine_default" start /unix "$@" 2>&1 | log
}

I didn't realize wine_init wasn't meant to be in quotes. This doesn't change the resulting error, but, handles something that would be an issue down the line.

Melechtna commented 1 month ago

Figured it out, gamescope_args ALSO should not be in quotes, and this ALMOST works. I can start statistics, I can start Diablo 2, but I can't start them together. Here's the code as it exists now.

# /////////////////////////////////////////////////////
# EXECUTION FUNCTIONS
# /////////////////////////////////////////////////////

exec_wine() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" $wine_init "$wine_default" "$@" 2>&1 | log
}

exec_wine_exe_gamescope() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" gamescope $gamescope_args -- $wine_init "$wine_default" start /unix "$@" 2>&1 | log
}

exec_wine_exe() {
    exec_wine start /unix "$@"
}

exec_diablo2() {
    check_d2_dir || return 1
    check_wine_install || return 1
    check_patch_installation || return 1
    if pgrep -x "$D2_STATS_EXE" >/dev/null; then
        # If D2Stats is running do this ...
        if [ ! -f "$d2_dir/$D2_SIGMALOADER_EXE" ]; then
            zenity_error "$d2_dir/$D2_SIGMALOADER_EXE not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            exec_wine_exe_gamescope "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$D2_SIGMALOADER_EXE" "$d2_args"
        fi
    else
        if [ ! -f "$d2_dir/$d2_exe" ]; then
            zenity_error "$d2_dir/$d2_exe not found"
            return 1
        fi
        if [ "$gamescope" = "true" ]; then
            exec_wine_exe_gamescope "$d2_dir/$d2_exe" "$d2_args"
        else
            exec_wine_exe "$d2_dir/$d2_exe" "$d2_args"
        fi
    fi
    exit 0
}

The configuration section is unchanged. But, the resulting error is the same as the first

wlserver: [types/wlr_compositor.c:692] New wlr_surface 0x55920aa91440 (res 0x55920aabb8c0)
0138:fixme:imm:ImeSetActiveContext (0x840330, 1): stub
0138:fixme:imm:ImmReleaseContext (0001026A, 00840330): stub
xwm: Unhandled initial NET_WM_STATE property: _NET_WM_STATE_ABOVE
xwm: got the same buffer committed twice, ignoring.
The XKEYBOARD keymap compiler (xkbcomp) reports:
> Warning:          Unsupported maximum keycode 708, clipping.
>                   X11 cannot support keycodes above 255.
> Warning:          Could not resolve keysym XF86CameraAccessEnable
> Warning:          Could not resolve keysym XF86CameraAccessDisable
> Warning:          Could not resolve keysym XF86CameraAccessToggle
> Warning:          Could not resolve keysym XF86NextElement
> Warning:          Could not resolve keysym XF86PreviousElement
> Warning:          Could not resolve keysym XF86AutopilotEngageToggle
> Warning:          Could not resolve keysym XF86MarkWaypoint
> Warning:          Could not resolve keysym XF86Sos
> Warning:          Could not resolve keysym XF86NavChart
> Warning:          Could not resolve keysym XF86FishingChart
> Warning:          Could not resolve keysym XF86SingleRangeRadar
> Warning:          Could not resolve keysym XF86DualRangeRadar
> Warning:          Could not resolve keysym XF86RadarOverlay
> Warning:          Could not resolve keysym XF86TraditionalSonar
> Warning:          Could not resolve keysym XF86ClearvuSonar
> Warning:          Could not resolve keysym XF86SidevuSonar
> Warning:          Could not resolve keysym XF86NavInfo
Errors from xkbcomp are not fatal to the X server
wlserver: [types/wlr_compositor.c:692] New wlr_surface 0x55920aa8edb0 (res 0x55920aabdf50)
wlserver: [types/wlr_compositor.c:692] New wlr_surface 0x55920aa8fea0 (res 0x55920aab9f70)
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
0138:fixme:wgl:X11DRV_wglBindTexImageARB partial stub!
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_ABOVE
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_VERT
xwm: Unhandled NET_WM_STATE property change: _NET_WM_STATE_MAXIMIZED_HORZ
X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  10 (X_UnmapWindow)
  Resource id in failed request:  0x4000001
  Serial number of failed request:  234
  Current serial number in output stream:  236
gamescope: children shut down!
xwm: error 3: BadWindow (invalid Window parameter) request 15 minor 0 serial 1160
(EE) failed to read Wayland events: Connection reset by peer

Ultimately what happens is, the statistics shows up with a normal window, then you try to launch Diablo 2, and statistics is now in a state where it doesn't have a title bar, like when it's in a gamescope window, but without the stretching, and the Diablo 2 window tries to spawn, but it crashes out with the above. So, I'm unsure if this is a gamescope limitation, a wine limitation, or something I'm not considering.

Melechtna commented 1 month ago

It's not IDEAL, but, we COULD just lump D2Statistics in with Diablo 2 when gamescope is true. It still works, it's just awkward. Beyond that, would you mind informing me of how I do the whole "fork, push request to master" thing for github? I've never understood that process. This way you can view the changes properly and make a decision.

Melechtna commented 1 month ago

https://github.com/ValveSoftware/gamescope/issues/437

It's a gamescope limitation.

I tried this

exec_wine_exe_gamescope_d2stats() {
    check_wine_install || return 1
    WINEPREFIX="$wineprefix" gamescope -W 775 -H 600 -- $wine_init "$wine_default" start /unix "$@" 2>&1 | log
}

And giving statistics an if/else check to run with those options in its own instance with predefined values, and this works...kind of, but Diablo 2 never actually renders to its own separate "screen", it's just black at all times. So for now, best I could do, is make it all run within the same gamescope window, and we'll just have to "deal with the awkwardness", until this issue is resolved to some extent.

Melechtna commented 1 month ago

It looks like I'd need to be added as a committer to make pull requests?

Forgive me, this isn't the kind of collaboration I've done directly like this before, so if I'm wrong, feel free to correct me. Regardless, the changes are made, it's been integrated into the configuration menu, and is (while not ideal) perfectly usable with how I have it now, with proper defaults that should "just work" for anyone that has gamescope if they choose to enable it (I have it set to opt-in rather than opt-out)

Melechtna commented 1 month ago

https://github.com/Melechtna/d2launcher

Figured out what I think you meant

Melechtna commented 1 month ago

Oh, whoops, I don't know why, or how, but D2Statistics is still trying to open in its own separate screen, instead of the same gamescope screen, and I legitimately don't know how or why.

Melechtna commented 1 month ago

It looks like until that gamescope limitation is resolved, it's an "all or nothing" situation. You CAN integrate this into the script, but unless there's some way to tell it to specifically run the two programs in the same screen, the implementation will always break because it spawns 2 screens no matter what, and right now, this breaks basically everything. So, the only REAL option, is to just run the launcher script the way I did initially, for now. I'll open this again if and when this gets fixed.