Compare commits

...
190 Commits
Author SHA1 Message Date
Cameron Gutman e7a2d9f44d Prepare for v6.0.0 2024-06-05 22:42:28 -05:00
Cameron Gutman ee5d0acc1f Rerun lupdate and lrelease 2024-06-05 00:28:11 -05:00
Cameron Gutman 6a5a69cabd Merge remote-tracking branch 'origin/weblate' 2024-06-05 00:26:59 -05:00
weng weng cd7391f908 Translated using Weblate (Chinese (Simplified))
Currently translated at 96.2% (231 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/zh_Hans/
2024-06-04 02:09:15 +00:00
Cameron Gutman d6d28f1afd Remove unused libplacebo libraries 2024-06-02 21:52:35 -05:00
Cameron Gutman 5a6620a9ae Fix missing installer window icon with WiX 5 2024-06-02 21:40:40 -05:00
Cameron Gutman 5a47ddca33 Use EGL on X11 for non-x86 platforms
In theory we could do this everywhere, but I'd rather not risk major behavior changes on x86 right before a release.
2024-06-02 14:01:54 -05:00
Cameron Gutman b58f021fc8 Only set QT_SSL_USE_TEMPORARY_KEYCHAIN on macOS
It's useless on other platforms.
2024-06-02 13:58:42 -05:00
Cameron Gutman 12b93337d1 Fix SDL_VIDEODRIVER override when the Qt platform plugin was manually set 2024-06-01 22:43:52 -05:00
Cameron Gutman 807fc6b31c Opt out of macOS Game Mode due to various OS bugs
Fixes #1170
Fixes #1297
2024-06-01 16:36:24 -05:00
Cameron Gutman 959080aa95 Update moonlight-common-c with higher IPv6 MTU 2024-06-01 16:30:45 -05:00
Cameron Gutman 18a772aeba Update prebuilt libraries
opus -> 1.5.2
FFmpeg -> 7.0.1
dav1d -> 1.4.2
2024-06-01 16:30:20 -05:00
Cameron Gutman 9bdd17f1c3 Properly clean libavformat files with clean-libs.sh 2024-06-01 16:25:46 -05:00
sanhoe 3d1967bd1e Translated using Weblate (Korean)
Currently translated at 100.0% (240 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/ko/
2024-06-01 16:41:32 +02:00
Cameron Gutman 469bd32bde Update AppImage to FFmpeg 7.0.1 and dav1d 1.4.2 2024-05-31 20:43:03 -05:00
comsyspro 351b4edf0c Translated using Weblate (German)
Currently translated at 95.8% (230 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/de/
2024-05-29 09:09:23 +02:00
Cameron Gutman a0a8b70bc1 Strip session keys and IVs from the logs
Logging these isn't a major issue because they change each session and
the privilege level to access the logs is the same as those to access the
private key of Moonlight client itself, but there's also no good reason to
log them either.
2024-05-27 22:02:02 -04:00
Cameron Gutman e07f069222 Always use the custom logger
It was already used everywhere except macOS Debug builds.
2024-05-27 21:40:18 -04:00
Empotik 47ccd1e481 Translated using Weblate (Thai)
Currently translated at 57.5% (138 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/th/
2024-05-27 23:09:14 +00:00
Cameron Gutman 96fba43186 Add missing AV1 option in CLI video codec options
Fixes #1290
2024-05-27 13:06:49 -04:00
Cameron Gutman 9e7c212c6d Update moonlight-common-c 2024-05-25 18:32:58 -05:00
Cameron Gutman e67d83e097 Move declaration under ifdef to eliminate unused variable warning 2024-05-25 17:01:46 -05:00
Cameron Gutman 4474f815af Only use fallback VDPAU/VAAPI driver paths on AppImages
Native packages and Flatpak/Snap packages both have properly set
driver path values embedded in libva.so/libvdpau.so. Let's not go
splunking for drivers in random folders on those systems.
2024-05-25 14:39:09 -05:00
Cameron Gutman e130a15037 Remove dependency on AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES for VDPAU wrapper identification
These quirks may be deprecated and no longer populated in an upcoming FFmpeg version.
2024-05-25 14:24:31 -05:00
Cameron Gutman 845ef147c9 Add new libva driver path values for Fedora 40 2024-05-25 14:22:43 -05:00
Cameron Gutman fb17c15cfd Update VCRedist 2024-05-25 11:54:29 -05:00
Marocco2 bb74cedf59 Translated using Weblate (Italian)
Currently translated at 100.0% (240 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/it/
2024-05-25 15:09:12 +02:00
Mike 9af6906380 Translated using Weblate (Polish)
Currently translated at 100.0% (240 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/pl/
2024-05-24 08:09:10 +00:00
gallegonovato c1ae3cceda Translated using Weblate (Spanish)
Currently translated at 100.0% (240 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/es/
2024-05-24 08:09:10 +00:00
Loïc Hesling e1e3bd512f Translated using Weblate (French)
Currently translated at 100.0% (240 of 240 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2024-05-24 08:09:09 +00:00
Cameron Gutman cf3ff32340 Rerun lupdate and lrelease 2024-05-23 00:28:10 -07:00
Cameron Gutman 2c98deeae3 Merge remote-tracking branch 'origin/weblate' 2024-05-23 00:26:16 -07:00
Cameron Gutman a572681737 Add --hdr and --no-hdr command-line arguments
Fixes #1287
2024-05-22 23:28:14 -07:00
Cameron Gutman e78b389bc0 Fix passing incorrect 'this' pointer to quitRunningApp()
This worked before due to Qt behavior that was changed in https://codereview.qt-project.org/c/qt/qtdeclarative/+/450393
to generate a warning in this situation. The old behavior remains the default, but this change avoids a warning in the logs.
2024-05-22 23:11:52 -07:00
Роман 83ec929758 Translated using Weblate (Russian)
Currently translated at 83.6% (199 of 238 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/ru/
2024-05-21 20:02:01 +02:00
Cameron Gutman 9117f6565e Don't clobber existing std handles when attaching to the parent console
Fixes #1270
2024-05-11 23:38:32 -05:00
Cameron Gutman aa33432c81 Detect whether to log to file based upon whether stderr was redirected 2024-05-11 23:24:36 -05:00
Cameron Gutman e25d9b0da2 Only use SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=0 for native notched resolution
Full-screen and borderless windowed are now fully indistinguishable on macOS to avoid user confusion.

Fixes #861
2024-05-11 19:58:36 -05:00
Cameron Gutman 0dfa8a7b53 Override fullscreen mode if necessary for native resolutions to work as expected
Fixes #1244
2024-05-11 19:55:08 -05:00
Cameron Gutman 85a9f85c54 Add support for listing notch and notchless native resolution options 2024-05-11 19:48:01 -05:00
Cameron Gutman ed68f920f1 Avoid polluting source tree with NuGet cache files during WiX build 2024-05-11 15:08:30 -05:00
Cameron Gutman f9a4498f9b Upgrade to WiX v5.0.0 2024-05-11 15:08:10 -05:00
Cameron Gutman 4d1acf2e30 Don't invoke signal handlers directly
This generates a runtime warning on Qt 6.7
2024-05-06 19:16:10 -07:00
Cameron Gutman db4739b82d Remove warn_off configuration for moonlight-common-c subproject
We can just turn off unused parameter warnings instead of all warnings.
2024-05-05 18:52:21 -05:00
gallegonovato 38bf833708 Translated using Weblate (Spanish)
Currently translated at 100.0% (238 of 238 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/es/
2024-05-05 07:07:20 +02:00
Cameron Gutman 364093aef8 Prevent a deadlock if the audio device stops consuming data 2024-05-04 21:22:38 -05:00
Cameron Gutman 6d220a9062 Allow audio to recover if no audio devices were present during stream start 2024-05-04 21:13:00 -05:00
Jorys Paulin abec8b3fe1 Translated using Weblate (French)
Currently translated at 100.0% (238 of 238 strings)

Co-authored-by: Jorys Paulin <paulinjorys5@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
Translation: Moonlight Game Streaming/moonlight-qt
2024-05-02 09:07:13 +02:00
Cameron Gutman 93ed985043 Rerun lupdate and lrelease 2024-04-30 22:53:23 -05:00
Cameron Gutman 23c737cab9 Split details dialog into multiple lines for easier translation 2024-04-30 22:53:23 -05:00
Cameron Gutman cb850f013a Add Central Kurdish language (disabled until it's completed) 2024-04-30 22:53:15 -05:00
Cameron Gutman 6bd964a094 Merge remote-tracking branch 'origin/weblate' 2024-04-30 22:27:09 -05:00
Cameron Gutman 419c788ab5 Remove save() call that is no longer necessary since d1ccd19 2024-04-30 21:42:38 -05:00
Jorys Paulin a412100a11 feat: added show performance overlay preference (#1209) 2024-04-30 21:37:52 -05:00
Cameron Gutman 0531666f38 Apply some minor cosmetic improvements to the details dialog 2024-04-30 21:36:59 -05:00
Jorys Paulin c096238998 Add "View details" to PC list (#1221)
* feat: add view pc details menu item

* feat: show mac address

* fix: cast mac byte array to string
2024-04-30 21:23:25 -05:00
Hans Gaiser 211fec0ff0 Send WOL packet when seeking a computer. 2024-04-30 21:05:33 -05:00
Dylan Frankcom 58e27875ae Fix Raised Black Levels on Steam Deck OLED (#1265)
Add getDecoderColorRange to plvk
2024-04-30 20:31:00 -05:00
Ahmed kurdish 954e6608a1 Added translation using Weblate (Kurdish (Central))
Co-authored-by: Ahmed kurdish <nikogaming500@gmail.com>
2024-04-29 21:06:00 +02:00
Cameron Gutman 1cd588f25c Don't set relative warp mode based on absolute/relative mouse mode
This breaks pointer capture on Wayland when we start in absolute mouse
mode (since warp is not supported on Wayland). It's also confusing to
users because it leads to different relative mouse acceleration curves
based on whether you start a stream in relative mode or not.
2024-04-27 17:13:02 -05:00
Cameron Gutman ebfe035a18 Set SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP=0 to resolve pointer lock issue on Wayland
SDL will try to lock the mouse cursor on Wayland if it's not visible in order to
support applications that assume they can warp the cursor (which isn't possible on
Wayland). We don't want this behavior because it interferes with seamless mouse
mode when toggling between windowed and fullscreen modes by unexpectedly locking
the mouse cursor.
2024-04-27 16:49:51 -05:00
Yutaro Urata 6f22ed13c1 Translated using Weblate (Japanese)
Currently translated at 86.0% (185 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/ja/
2024-04-24 21:07:16 +02:00
Cameron Gutman 4aa2b8570c Autorelease the system default Metal device 2024-04-19 00:29:51 -05:00
Cameron Gutman fe6562e31c Allow forcing Metal renderer on or off via VT_FORCE_METAL=1 and VT_FORCE_METAL=0 respectively 2024-04-19 00:27:45 -05:00
Cameron Gutman 654e386263 Only pace presentation if display sync is enabled 2024-04-19 00:17:13 -05:00
Cameron Gutman 76d0eb6b63 Avoid trying VDPAU when Vulkan is preferred
The VDPAU renderer causes interoperability issues with Vulkan.
2024-04-18 00:41:24 -05:00
Hans Gaiser 02d867fe09 Log to stderr instead of stdout. 2024-04-17 23:55:38 -05:00
Cameron Gutman 76ff53f09f Move warning dialog checks into the Window's onCompleted() function
The old issue with dialogs appearing behind the main window only impacts the old non-QC2 dialogs,
which we haven't used for several years.
2024-04-14 14:49:35 -05:00
Cameron Gutman 37d92dee16 Revert "Fix hiding the main UI window while streaming on Qt 6.7"
The original issue on Qt 6.7 was caused by conflicts with the window state set in main.qml
which was addressed by afbc49e39a.

The remaining issue with Qt 6.7 clobbering window state and position when hiding a window
looks like a legitimate Qt bug.

This reverts commit ebe270bec5.
2024-04-14 14:08:53 -05:00
Cameron Gutman afbc49e39a Apply the main window visibility property only at startup
Otherwise it can affect the state of the window when showing again streaming
2024-04-14 13:35:41 -05:00
Cameron Gutman d1ccd19fcc Make StreamingPreferences a proper singleton
This removes the need for several hacks in SettingsView to force updates and improves performance by not reloading preferences all over the place.
2024-04-14 13:01:30 -05:00
Cameron Gutman ebe270bec5 Fix hiding the main UI window while streaming on Qt 6.7 2024-04-13 00:05:49 -05:00
Cameron Gutman 784234c857 Fix updating bitrate text upon language change on Qt 6.7 2024-04-13 00:05:11 -05:00
Cameron Gutman 6a85996b4a Clear the minimized state on the Qt window if the SDL window is not minimized 2024-04-07 00:42:51 -05:00
Cameron Gutman c6710f93dc Enable Steam Controller detection on Steam Link
Fixes #697
2024-04-06 20:03:54 -05:00
Cameron Gutman 5e46c4ec2b Update Windows and Mac prebuilt libraries
SDL2 -> 1fa6142
FFmpeg -> 7.0
opus -> 1.5.1
dav1d -> 1.4.1
2024-04-06 19:56:38 -05:00
Cameron Gutman 011feab6ce Add workaround for broken Qt EGLFS card selection logic 2024-04-06 14:35:34 -05:00
Cameron Gutman f8c5d3c0ce Update AppImage libraries
FFmpeg -> 7.0
SDL2 -> 1fa61429
libplacebo -> 7b294350
2024-04-06 12:44:50 -05:00
Cameron Gutman 74d452bf35 Limit mDNS resolution retry count 2024-04-06 12:40:32 -05:00
Cameron Gutman fd2ff61a40 Support Vulkan AV1 decoding with FFmpeg 7.0 2024-04-06 12:31:14 -05:00
Cameron Gutman 8a87a09947 Flush stale gamepad events after checking for unmapped gamepads 2024-03-31 14:56:46 -05:00
Cameron Gutman cf544a8703 Don't reload preferences every time we poll for GUI gamepad input 2024-03-31 14:54:36 -05:00
Cameron Gutman 0e1190a059 Remove Optimus block for modern Nvidia drivers
Now that we use D3D11 and don't fall back to D3D9, it should be safe to allow Optimus again.

This will re-enable Optimus for D3D9 on Windows 8.1, but all of the reported problems were on Windows 10 so it will hopefully be a non-issue.

Fixes #1241
2024-03-28 21:06:15 -05:00
Cameron Gutman f2535f1e6e Avoid D3D9 fallback on lack of codec support unless a D3D11 FL11.0 GPU wasn't found
We'd rather not waste time (and risk crashes) loading the D3D9 driver if the GPU doesn't have the physical decoding hardware at all.
2024-03-26 23:59:18 -05:00
Cameron Gutman cefa567f95 Demote DXVA2 renderer to second pass of hwaccels 2024-03-26 23:04:54 -05:00
Cameron Gutman 9be782ae8e Fix some harmless compiler warnings 2024-03-26 21:59:39 -05:00
Cameron Gutman 610e07d4bd Improve state propagation between Qt and SDL windows 2024-03-26 21:42:13 -05:00
Marcos Del Sol Vives 75eb921a04 Improve VPN heuristics under Windows
The NDIS interface type will now be used also for matching, improving the
heuristics for multiple popular VPN implementations.

Fixes #1233
2024-03-26 01:20:53 -05:00
Cameron Gutman 97bd515b11 Remove leftover preferred Metal device logic 2024-03-26 01:16:57 -05:00
Cameron Gutman a45695a22a Rework Qt window handling during SDL window creation
- Avoids blindly enumeration all top-level windows
- Properly tracks screen changes during the connection process
- Starts stream window minimized if the Qt window was minimized

Fixes #1231
2024-03-24 21:38:54 -05:00
Cameron Gutman 0d67fd1ff0 Batch mouse motion events on the SDL side 2024-03-24 20:35:23 -05:00
Cameron Gutman a093a0ae59 Restore AVSampleDisplayLayer renderer for dGPU/eGPU systems
These sometimes have issues importing decoded frames for Metal rendering.
2024-03-24 17:47:29 -05:00
Cameron Gutman c9ad8ffa69 Stop using true fullscreen mode on Mac
It has numerous issues on modern Macs and seems to get worse every macOS release.
2024-03-24 13:55:43 -05:00
Cameron Gutman 9987914ebb Update SDL_GameControllerDB 2024-03-20 02:04:01 -05:00
Lukas Senionis fbfd6fb462 Update 0001-Vulkan-Don-t-try-to-reuse-old-swapchain.patch to compile with latest changes 2024-03-18 21:54:28 -05:00
Cameron Gutman bec053e3cf Update AppImage dependencies 2024-03-18 01:44:08 -05:00
Cameron Gutman d364b24378 Fix broken on-link detection that caused non-VPN WAN connections to use LAN MTU
Fixes #1101
2024-03-15 20:04:51 -05:00
Jorys Paulin 8c67586cd0 Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2024-03-11 20:01:49 +01:00
Cameron Gutman 89a628a0dd Use 'git apply' instead of 'git am' 2024-03-02 12:18:14 -06:00
Cameron Gutman 4fb9681b7f Update moonlight-common-c 2024-03-02 11:26:54 -06:00
Cameron Gutman c13128ca1d Add libplacebo patch from Chiaki4Deck to resolve AppImage hangs on Steam Deck 2024-03-02 11:25:53 -06:00
Cameron Gutman ceca83346b Use FIFO present mode for VSync 2024-03-02 11:19:52 -06:00
Jorys Paulin fc6c51c75a fix: add explicit width and height to discord icon 2024-02-18 15:28:47 -06:00
Jorys Paulin 161d91acf8 fix: fix discord icon on qt 5 2024-02-18 15:28:47 -06:00
Cameron Gutman 06ef10a2ad Update README to clarify requirements for Vulkan video
See #1186
2024-02-18 15:23:35 -06:00
Cameron Gutman 7b29b22192 Update dependencies
SDL -> df00a7dd4
SDL_ttf -> 2.22.0
FFmpeg -> 3e79074c4 (n6.1)
dav1d -> 1.4.0
libplacebo -> 3ba18d
2024-02-17 16:14:39 -06:00
Cameron Gutman 289a7165b3 Remove dSYM files from macOS app bundle
macdeployqt incorrectly copies these if they are installed, which bloats the final DMG significantly.

See https://bugreports.qt.io/browse/QTBUG-56590
2024-02-17 14:19:05 -06:00
Cameron Gutman 62011c37f4 Fix build with SDL 2.0.20 and earlier
Fixes #1186
2024-02-17 13:31:53 -06:00
Cameron Gutman 919e44b29b Fix __builtin_cpu_supports(aes) for GCC 6-9 2024-02-17 13:28:32 -06:00
Cameron Gutman 32be1b158b Update VCRedist 2024-02-16 23:06:52 -06:00
Cameron Gutman f635860afd Update to WiX 4.0.4 2024-02-16 23:05:58 -06:00
Cameron Gutman 3b078f095b Update AppVeyor image for macOS 2024-02-16 23:01:44 -06:00
Jorys Paulin 72fc7ea31a feat: update discord icon 2024-02-16 22:36:26 -06:00
Vag Ko f4edd1e63a Translated using Weblate (Greek)
Currently translated at 79.5% (171 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/el/
2024-02-12 22:02:05 +01:00
Jorys Paulin 6011384afe Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2024-02-11 10:01:59 +01:00
Cameron Gutman 896456a8ac Wait for command buffer completion before returning from renderFrame() 2024-02-11 00:15:32 -06:00
Cameron Gutman 8959ddbeb6 Rewrite overlay support in Metal renderer to avoid additional subviews
Subviews cause our Metal layer to fall out of the direct composition path on Intel hardware
2024-02-11 00:15:07 -06:00
Cameron Gutman 767215fce5 Rewrite old frame pacing logic for Metal renderer 2024-02-10 22:49:13 -06:00
Cameron Gutman 3ef05f3a35 Completely switch to D3D11VA over DXVA2 for supported GPUs in all scenarios
Fixes #1179
2024-02-09 01:24:03 -06:00
Cameron Gutman 12624bc5c4 Avoid the composited display path for our Metal layer 2024-02-08 18:19:01 -06:00
Cameron Gutman 5bd0dea85d Enable Metal validation in debug builds 2024-02-08 18:07:43 -06:00
Cameron Gutman 80242f2e90 Fix Metal debug layer validation errors 2024-02-08 18:06:46 -06:00
Cameron Gutman cf3e328f9d Add seamless resize support for Metal renderer 2024-02-08 18:05:47 -06:00
Bàn Hải Dương 150002d4d4 Translated using Weblate (Vietnamese)
Currently translated at 90.2% (194 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/vi/
2024-02-08 16:01:48 +01:00
Cameron Gutman b972a22459 Use SDL helpers to create Metal view 2024-02-07 03:26:09 -06:00
Cameron Gutman db06239018 Rewrite the macOS renderer using CAMetalLayer
This allows v-sync to be disabled on macOS and lets us remove a whole bunch of old hacks.

Further optimizations of the new renderer are still needed.
2024-02-07 02:40:35 -06:00
boogie bdc7d53515 Only give "pixel_format=nv12" option to v4l2m2m or v4l2requests decoders
pixel_format is a global option which changes avctx->pix_fmt to the
given format, and overrides the AV_PIX_FMT_DRM_PRIME requirement of a
decoder. So v4l2 out of tree patches has done some exception which is
not perfectly nice, therefore limit this option only if the given
decoder is v4l2 decoder. Otherwise rockchip mpp based ffmpeg decoders
can not work properly
2024-02-06 00:09:34 -06:00
Cameron Gutman 7f83b79c19 Update AppImage dependencies 2024-02-05 23:11:02 -06:00
Cameron Gutman 47b3aff352 Update moonlight-common-c with RTSP encryption 2024-02-05 23:01:25 -06:00
Cameron Gutman 679f237c1b Update bug report template 2024-02-05 22:16:49 -06:00
Cameron Gutman 3d8de56f25 Add fast AES detection for RISC-V using hwprobe() syscall 2024-01-23 02:28:28 -06:00
Vadym Nekhai 801cebbbc7 Translated using Weblate (Ukrainian)
Currently translated at 35.8% (77 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/uk/
2024-01-22 06:01:48 +01:00
Cameron Gutman 66fb0465c4 Update moonlight-common-c with finalized encryption changes 2024-01-19 19:05:59 -06:00
Cameron Gutman 8bbc9b2c57 Fix build with old ARM headers and opt-out of fast AES on RISC-V for now 2024-01-15 17:36:57 -06:00
Cameron Gutman be7694dd8b Improve fast AES heuristic for Linux ARM and unknown platforms 2024-01-15 14:16:56 -06:00
Cameron Gutman ce8b24dfa7 Implement fast AES detection logic for Windows and macOS 2024-01-15 13:40:13 -06:00
Cameron Gutman 75ddac54c5 Update moonlight-common-c with new encryption features 2024-01-15 13:35:39 -06:00
Cameron Gutman b01a83ff39 Send pen input as pen events on Windows 2024-01-12 23:40:24 -06:00
Cameron Gutman c3e886fbcc Use drmIoctl() instead of bare ioctl() to handle EINTR/EAGAIN 2024-01-05 18:33:17 -06:00
Cameron Gutman e20d56041e Fix rare race condition in overlay update code 2023-12-31 15:21:18 -06:00
Cameron Gutman 114074bfd8 Don't set HDR metadata on the DXGI swapchain
This is no longer recommended by Microsoft due inconsistencies with display support for HDR metadata.
2023-12-31 15:15:06 -06:00
Cameron Gutman e44e29e7eb Update moonlight-common-c with X-SS-Connect-Data support 2023-12-29 23:15:41 -06:00
Cameron Gutman 03663d5552 Fix integer promotion issue in gamepad state merging 2023-12-29 16:06:53 -06:00
Cameron Gutman dce2a857f7 Merge all gamepad state when in single controller mode 2023-12-28 16:28:40 -06:00
Cameron Gutman 61f71a318e Expose PLVK_DEBUG_EXTRA for enabling extra debug layers 2023-12-27 15:40:01 -06:00
Cameron Gutman 646ce2bf66 Handle occlusion of the Vulkan window without recreating the renderer over and over 2023-12-27 15:36:59 -06:00
Cameron Gutman 2bbae90e2c Revert "Unconditionally override host-reported minimum luminance value"
This reverts commit 770872238d.
2023-12-26 15:46:25 -06:00
Cameron Gutman bb8e229814 Swap buffers in waitToRender() instead of renderFrame() 2023-12-26 15:45:31 -06:00
Cameron Gutman 9ed8252cc6 Display error codes when the connection is terminated 2023-12-22 14:12:59 -06:00
Cameron Gutman 2cef09471b Remove manual hwframe ctx setup for D3D11VA
It was apparently never necessary, since FFmpeg's default behavior of
avcodec_get_hw_frames_parameters() + av_hwframe_ctx_init() makes a
context that exactly matches the one we constructed manually.
2023-12-22 02:45:11 -06:00
Cameron Gutman 2d15382e31 Fix libplacebo renderer build on Windows 2023-12-21 18:12:19 -06:00
Cameron Gutman 770872238d Unconditionally override host-reported minimum luminance value 2023-12-18 16:43:17 -06:00
Cameron Gutman 2a05b890d8 Enable transparent resizing and display changes for supported renderers 2023-12-17 21:15:40 -06:00
Cameron Gutman 481f23b6e9 Remove some dependencies on fixed window and frame sizes 2023-12-17 20:39:26 -06:00
Cameron Gutman 4e7107a09e Use the crop rect from the pl_frame instead of the raw AVFrame dimensions 2023-12-17 19:06:56 -06:00
Cameron Gutman 0223394725 Fix manual codec options when using Vulkan for HDR
For now, we can't switch between GL and Vulkan on a single window, so performing decoder
probes with 8-bit formats may activate EGLRenderer which breaks Vulkan on our window.

It also speeds up stream launch validation for HDR cases in general, since we don't
perform unnecessary decoder probes for 8-bit profiles when the user wanted 10-bit.
2023-12-17 18:53:13 -06:00
Cameron Gutman 058739321c Move vkAcquireNextImageKHR() into waitToRender() so it can overlap with new frame arrival 2023-12-17 16:47:38 -06:00
Cameron Gutman 708bec2937 Rewrite Vulkan device selection logic to handle multi-GPU systems better 2023-12-17 15:46:10 -06:00
Cameron Gutman c939537c5c Synchronize swapchain and pl_frame colorspace values 2023-12-16 17:36:02 -06:00
Cameron Gutman 9497657c83 Fix handling of zero minimum HDR luminance 2023-12-16 17:28:35 -06:00
Cameron Gutman d4050f3a4f Add HDR metadata to each AVFrame 2023-12-16 17:02:53 -06:00
Cameron Gutman c74c4ef041 Fix native Vulkan video decoding (non-imported frames) 2023-12-16 16:21:54 -06:00
Cameron Gutman d75663052d Fix a few minor Vulkan renderer bugs 2023-12-16 16:18:19 -06:00
Cameron Gutman 8f2db99406 Enable libdrm in AppImage FFmpeg build to support DRM-PRIME to Vulkan hwframe mapping 2023-12-14 17:48:45 -06:00
Cameron Gutman 6a58dbb05e Disable 10-bit SDR swapchains on Vulkan due to inconsistent dithering behavior 2023-12-14 17:46:06 -06:00
Cameron Gutman 8aeb976128 Require a test frame for Vulkan video decoding 2023-12-14 17:44:58 -06:00
Cameron Gutman 7d51a4b67d Fix incorrect overlay colors in Vulkan renderer 2023-12-14 17:43:54 -06:00
Cameron Gutman f7d412e3bf Fix FFmpeg configure args 2023-12-14 00:21:39 -06:00
Cameron Gutman 4469c7fae3 Build AppImage with libplacebo and Vulkan video support 2023-12-13 18:26:00 -06:00
Cameron Gutman ee95efc0f1 Improve Vulkan renderer debuggability 2023-12-13 01:08:32 -06:00
Cameron Gutman 3ddc8ad68a Fix crash if pl_vulkan_create() fails 2023-12-13 01:04:54 -06:00
Cameron Gutman 333d97160e Add opt-in for Vulkan rendering for SDR content with PREFER_VULKAN=1 2023-12-13 01:03:27 -06:00
Cameron Gutman aac5ad3fd0 Add a libplacebo-based Vulkan renderer for HDR and Vulkan video decoding support
Vulkan H.264/HEVC video decoding isn't supported with Fedora's Mesa binaries due to
patent issues, so only the VAAPI/DRM-PRIME import path was tested locally with ANV.

HDR video is getting tonemapped to my SDR monitor reasonably, so HDR output probably
works with GameScope but I haven't confirmed yet.

See #1117
2023-12-12 23:54:23 -06:00
Cameron Gutman 1ef083c952 Update moonlight-common-c to remove per-codec bitrate adjustments 2023-12-03 22:21:10 -06:00
Cameron Gutman c63b918bb2 Don't hardcode the server signature length 2023-11-29 21:53:49 -06:00
Jorys Paulin 4dba4ad538 Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2023-11-04 14:32:16 +01:00
NicolasS 83fcad6bd1 Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2023-11-03 13:43:42 +01:00
Jorys Paulin e08178474d Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2023-11-03 13:43:42 +01:00
Cameron Gutman f374e88bac Update README.md
Added new SBC packages and updated build requirements
2023-11-03 00:13:50 -05:00
Cameron Gutman 3cb169ea26 Fix CUDA build without KMSDRM enabled 2023-11-01 22:49:18 -05:00
Cameron Gutman 58f87e39a9 Prepare for v5.0.1 2023-11-01 21:49:03 -05:00
Cameron Gutman b1c8005d1a Disable libdrm in AppImage build 2023-11-01 21:44:44 -05:00
Cathy J. Fitzpatrick f8a2e22a18 Update moonlight-common-c to eb67a2fd (2023-10-26) 2023-11-01 20:31:19 -05:00
Cameron Gutman 7ea118fbae Fix dark mode on Windows with Qt 6.5+ 2023-11-01 20:30:34 -05:00
Cameron Gutman 2149024c35 Rerun lupdate and lrelease 2023-11-01 20:02:40 -05:00
Cameron Gutman f5d71c8c9a Merge remote-tracking branch 'origin/weblate' 2023-11-01 20:01:25 -05:00
Cameron Gutman e108a05895 Bump WiX SDK to 4.0.2 2023-11-01 19:58:29 -05:00
Cameron Gutman ea8bccd508 Fix typo causing libva-drm only to be enabled if libva-wayland is present 2023-11-01 17:39:24 -05:00
maicolbattistini bca919c48c Translated using Weblate (Italian)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/it/
2023-10-21 04:13:46 +00:00
Jorys Paulin 511ffe03b5 Translated using Weblate (French)
Currently translated at 100.0% (215 of 215 strings)

Translation: Moonlight Game Streaming/moonlight-qt
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-qt/fr/
2023-10-21 04:13:45 +00:00
136 changed files with 12723 additions and 5322 deletions
+4 -3
View File
@@ -37,12 +37,13 @@ List the games you've tried that exhibit the issue. To see if the issue is game-
- OS: [e.g. Windows 10 1809]
- Moonlight Version: [e.g. v0.9.0]
- GPU: [e.g. Intel HD Graphics 520]
- Linux package type (if applicable): [e.g. Flatpak]
**Server PC details (please complete the following information)**
- OS: [e.g. Windows 10 1809]
- GeForce Experience version: [e.g. 3.16.0.140]
- Nvidia GPU driver: [e.g. 417.35]
- Antivirus and firewall software: [e.g. Windows Defender and Windows Firewall]
- Sunshine or GeForce Experience version: [e.g. Sunshine v0.21.0]
- GPU: [e.g. AMD Radeon RX 7900 XT]
- GPU driver: [e.g. 24.1.1]
**Moonlight Logs (please attach)**
- On Windows, `Moonlight-###.log` files can be found in `%TEMP%`. Simply type that into the File Explorer path field to navigate there.
-11
View File
@@ -158,17 +158,6 @@ private:
L"EZFRD32.dll",
L"EZFRD64.dll",
// These are the newer dList DLLs for Optimus hybrid graphics DDI.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/hybrid-system-ddi
//
// We forcefully block them from loading because Optimus has a bug that
// deadlocks DXVA2 when we present with D3DPRESENT_DONOTWAIT. This will prevent
// Optimus from ever using the dGPU even if the user has requested it.
// https://github.com/moonlight-stream/moonlight-qt/issues/240
// https://github.com/moonlight-stream/moonlight-qt/issues/235
L"nvdlist.dll",
L"nvdlistx.dll",
// These are the older dList/AppInit DLLs for Optimus hybrid graphics DDI.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/hybrid-system-ddi
//
+10 -9
View File
@@ -26,36 +26,37 @@ You can follow development on our [Discord server](https://moonlight-stream.org/
- [Flatpak (for other Linux distros)](https://flathub.org/apps/details/com.moonlight_stream.Moonlight)
- [AppImage](https://github.com/moonlight-stream/moonlight-qt/releases)
- [Raspberry Pi 4](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Raspberry-Pi-4)
- [Generic ARM SBC packages for Debian](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-ARM%E2%80%90based-Single-Board-Computers) (not for Raspberry Pi)
- [Experimental RISC-V SBC packages for Debian](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-RISC%E2%80%90V-Single-Board-Computers)
- [NVIDIA Jetson and Nintendo Switch (Ubuntu L4T)](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Linux4Tegra-(L4T)-Ubuntu)
#### Special Thanks
[![Hosted By: Cloudsmith](https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith&style=flat-square)](https://cloudsmith.com)
Hosting for Moonlight's Raspberry Pi and L4T package repositories is graciously provided for free by [Cloudsmith](https://cloudsmith.com).
Hosting for Moonlight's Debian and L4T package repositories is graciously provided for free by [Cloudsmith](https://cloudsmith.com).
## Building
### Windows Build Requirements
* Qt 5.15 SDK or later
* Windows 7 or later
* Qt 5.15 SDK or later. Qt 6 is also supported for x64 and ARM64 builds.
* [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) (Community edition is fine)
* Select **MSVC** option during Qt installation. MinGW is not supported.
* [7-Zip](https://www.7-zip.org/) (only if building installers for non-development PCs)
### macOS Build Requirements
* Qt 6.2 SDK or later
* macOS Mojave (10.14) or later
* Qt 6.4 SDK or later
* Xcode 13 or later
* [create-dmg](https://github.com/sindresorhus/create-dmg) (only if building DMGs for use on non-development Macs)
### Linux/Unix Build Requirements
* Qt 5.9 SDK or later
* Qt 5.9 SDK or later. Qt 6 is also supported.
* GCC or Clang
* Install the required packages:
* Debian/Ubuntu: `libegl1-mesa-dev libgl1-mesa-dev libopus-dev libqt5svg5-dev libsdl2-dev libsdl2-ttf-dev libssl-dev libavcodec-dev libva-dev libvdpau-dev libxkbcommon-dev qtwayland5 qt5-qmake qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev wayland-protocols qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-window2 qml-module-qtquick2`
* Debian/Ubuntu: `libegl1-mesa-dev libgl1-mesa-dev libopus-dev libqt5svg5-dev libsdl2-dev libsdl2-ttf-dev libssl-dev libavcodec-dev libavformat-dev libva-dev libvdpau-dev libxkbcommon-dev qtwayland5 qt5-qmake qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev wayland-protocols qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-window2 qml-module-qtquick2`
* RedHat/Fedora: `openssl-devel SDL2-devel SDL2_ttf-devel ffmpeg-devel qt5-qtsvg-devel qt5-qtquickcontrols2-devel libva-devel libvdpau-devel opus-devel pulseaudio-libs-devel alsa-lib-devel`
* FFmpeg 4.0 or later is required to build. If your distro doesn't package FFmpeg 4.0 or later, you can build and install it from source on https://ffmpeg.org/
* Building the Vulkan HDR renderer requires a `libplacebo-dev`/`libplacebo-devel` version of at least v338.0 and FFmpeg 6.1 or later.
### Steam Link Build Requirements
* [Steam Link SDK](https://github.com/ValveSoftware/steamlink-sdk) cloned on your build system
@@ -65,7 +66,7 @@ Hosting for Moonlight's Raspberry Pi and L4T package repositories is graciously
1. Install the latest Qt SDK (and optionally, the Qt Creator IDE) from https://www.qt.io/download
* You can install Qt via Homebrew on macOS, but you will need to use `brew install qt --with-debug` to be able to create debug builds of Moonlight.
* You may also use your Linux distro's package manager for the Qt SDK as long as the packages are Qt 5.9 or later.
* This step is not required for building on Steam Link, because the Steam Link SDK includes Qt 5.9.
* This step is not required for building on Steam Link, because the Steam Link SDK includes Qt 5.14.
2. Run `git submodule update --init --recursive` from within `moonlight-qt/`
3. Open the project in Qt Creator or build from qmake on the command line.
* To build a binary for use on non-development machines, use the scripts in the `scripts` folder.
@@ -75,7 +76,7 @@ Hosting for Moonlight's Raspberry Pi and L4T package repositories is graciously
* To build from the command line for development use on macOS or Linux, run `qmake moonlight-qt.pro` then `make debug` or `make release`
* To create an embedded build for a single-purpose device, use `qmake "CONFIG+=embedded" moonlight-qt.pro` and build normally.
* This build will lack windowed mode, Discord/Help links, and other features that don't make sense on an embedded device.
* For platforms with poor GL performance, add `"CONFIG+=glslow"` to prefer direct KMSDRM rendering over EGL/GLES renderers. Direct KMSDRM rendering can use dedicated YUV/RGB conversion and scaling hardware rather than slower GPU shaders for these operations.
* For platforms with poor GPU performance, add `"CONFIG+=gpuslow"` to prefer direct KMSDRM rendering over GL/Vulkan renderers. Direct KMSDRM rendering can use dedicated YUV/RGB conversion and scaling hardware rather than slower GPU shaders for these operations.
## Contribute
1. Fork us
-2
View File
@@ -31,8 +31,6 @@
</array>
<key>GCSupportsControllerUserInteraction</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
+38 -6
View File
@@ -120,6 +120,13 @@ unix:!macx {
CONFIG += cuda
}
}
!disable-libplacebo {
packagesExist(libplacebo) {
PKGCONFIG += libplacebo
CONFIG += libplacebo
}
}
}
!disable-wayland {
@@ -145,8 +152,8 @@ win32:!winrt {
CONFIG += soundio discord-rpc
}
macx {
LIBS += -lssl -lcrypto -lavcodec.60 -lavutil.58 -lopus -framework SDL2 -framework SDL2_ttf
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal
LIBS += -lssl -lcrypto -lavcodec.61 -lavutil.59 -lopus -framework SDL2 -framework SDL2_ttf
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal -framework QuartzCore
# For libsoundio
LIBS += -framework CoreAudio -framework AudioUnit
@@ -267,7 +274,7 @@ libva-wayland {
PKGCONFIG += libva-wayland
DEFINES += HAVE_LIBVA_WAYLAND
}
libva-wayland {
libva-drm {
message(VAAPI DRM support enabled)
PKGCONFIG += libva-drm
@@ -291,7 +298,7 @@ mmal {
# significantly better performance than EGL on the Pi. Setting
# this option allows EGL usage even if built with MMAL support.
#
# It is highly recommended to also build with 'glslow' to avoid
# It is highly recommended to also build with 'gpuslow' to avoid
# EGL being preferred if direct DRM rendering is available.
allow-egl-with-mmal {
message(Allowing EGL usage with MMAL enabled)
@@ -318,6 +325,19 @@ cuda {
DEFINES += HAVE_CUDA
SOURCES += streaming/video/ffmpeg-renderers/cuda.cpp
HEADERS += streaming/video/ffmpeg-renderers/cuda.h
# ffnvcodec uses libdl in cuda_load_functions()/cuda_free_functions()
LIBS += -ldl
}
libplacebo {
message(Vulkan support enabled via libplacebo)
DEFINES += HAVE_LIBPLACEBO_VULKAN
SOURCES += \
streaming/video/ffmpeg-renderers/plvk.cpp \
streaming/video/ffmpeg-renderers/plvk_c.c
HEADERS += \
streaming/video/ffmpeg-renderers/plvk.h
}
config_EGL {
message(EGL renderer selected)
@@ -365,7 +385,8 @@ macx {
message(VideoToolbox renderer selected)
SOURCES += \
streaming/video/ffmpeg-renderers/vt.mm
streaming/video/ffmpeg-renderers/vt_avsamplelayer.mm \
streaming/video/ffmpeg-renderers/vt_metal.mm
HEADERS += \
streaming/video/ffmpeg-renderers/vt.h
@@ -393,6 +414,16 @@ glslow {
DEFINES += GL_IS_SLOW
}
vkslow {
message(Vulkan slow build)
DEFINES += VULKAN_IS_SLOW
}
gpuslow {
message(GPU slow build)
DEFINES += GL_IS_SLOW VULKAN_IS_SLOW
}
wayland {
message(Wayland extensions enabled)
@@ -429,7 +460,8 @@ TRANSLATIONS += \
languages/qml_pt_BR.ts \
languages/qml_pl.ts \
languages/qml_cs.ts \
languages/qml_he.ts
languages/qml_he.ts \
languages/qml_ckb.ts
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
+6 -9
View File
@@ -1,7 +1,7 @@
#include "computermanager.h"
#include "boxartmanager.h"
#include "nvhttp.h"
#include "settings/streamingpreferences.h"
#include "nvpairingmanager.h"
#include <Limelight.h>
#include <QtEndian>
@@ -144,8 +144,8 @@ private:
NvComputer* m_Computer;
};
ComputerManager::ComputerManager(QObject *parent)
: QObject(parent),
ComputerManager::ComputerManager(StreamingPreferences* prefs)
: m_Prefs(prefs),
m_PollingRef(0),
m_MdnsBrowser(nullptr),
m_CompatFetcher(nullptr),
@@ -352,9 +352,7 @@ void ComputerManager::startPolling()
return;
}
StreamingPreferences prefs;
if (prefs.enableMdns) {
if (m_Prefs->enableMdns) {
// Start an MDNS query for GameStream hosts
m_MdnsServer.reset(new QMdnsEngine::Server());
m_MdnsBrowser = new QMdnsEngine::Browser(m_MdnsServer.data(), "_nvstream._tcp.local.");
@@ -784,10 +782,9 @@ private:
return serverInfo;
} catch (...) {
if (!m_Mdns) {
StreamingPreferences prefs;
int portTestResult;
unsigned int portTestResult;
if (prefs.detectNetworkBlocking) {
if (m_ComputerManager->m_Prefs->detectNetworkBlocking) {
// We failed to connect to the specified PC. Let's test to make sure this network
// isn't blocking Moonlight, so we can tell the user about it.
portTestResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443,
+20 -6
View File
@@ -1,7 +1,7 @@
#pragma once
#include "nvcomputer.h"
#include "nvpairingmanager.h"
#include "settings/streamingpreferences.h"
#include "settings/compatfetcher.h"
#include <qmdnsengine/server.h>
@@ -72,9 +72,15 @@ private slots:
void handleResolvedTimeout()
{
if (m_Addresses.isEmpty()) {
// Try again
qInfo() << "Resolving" << hostname() << "timed out. Retrying...";
resolve();
if (m_Retries-- > 0) {
// Try again
qInfo() << "Resolving" << hostname() << "timed out. Retrying...";
resolve();
}
else {
qWarning() << "Giving up on resolving" << hostname() << "after repeated failures";
cleanup();
}
}
else {
Q_ASSERT(!m_Addresses.isEmpty());
@@ -92,7 +98,7 @@ signals:
void resolvedHost(MdnsPendingComputer*,QVector<QHostAddress>&);
private:
void resolve()
void cleanup()
{
// Delete our resolver, so we're guaranteed that nothing is referencing m_Server.
delete m_Resolver;
@@ -101,6 +107,12 @@ private:
// Now delete our strong reference that we held on behalf of m_Resolver.
// The server may be destroyed after we make this call.
m_Server.reset();
}
void resolve()
{
// Clean up any existing resolver object and server references
cleanup();
// Re-acquire a strong reference if the server still exists.
m_Server = m_ServerWeak.toStrongRef();
@@ -120,6 +132,7 @@ private:
QSharedPointer<QMdnsEngine::Server> m_Server;
QMdnsEngine::Resolver* m_Resolver;
QVector<QHostAddress> m_Addresses;
int m_Retries = 10;
};
class ComputerPollingEntry
@@ -205,7 +218,7 @@ class ComputerManager : public QObject
friend class DelayedFlushThread;
public:
explicit ComputerManager(QObject *parent = nullptr);
explicit ComputerManager(StreamingPreferences* prefs);
virtual ~ComputerManager();
@@ -257,6 +270,7 @@ private:
void startPollingComputer(NvComputer* computer);
StreamingPreferences* m_Prefs;
int m_PollingRef;
QReadWriteLock m_Lock;
QMap<QString, NvComputer*> m_KnownHosts;
+7
View File
@@ -6,6 +6,13 @@ ComputerSeeker::ComputerSeeker(ComputerManager *manager, QString computerName, Q
: QObject(parent), m_ComputerManager(manager), m_ComputerName(computerName),
m_TimeoutTimer(new QTimer(this))
{
// If we know this computer, send a WOL packet to wake it up in case it is asleep.
for (NvComputer * computer: m_ComputerManager->getComputers()) {
if (this->matchComputer(computer)) {
computer->wake();
}
}
m_TimeoutTimer->setSingleShot(true);
connect(m_TimeoutTimer, &QTimer::timeout,
this, &ComputerSeeker::onTimeout);
+16 -2
View File
@@ -373,6 +373,7 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
s.connectToHost(copyOfActiveAddress.address(), copyOfActiveAddress.port());
if (s.waitForConnected(3000)) {
Q_ASSERT(!s.localAddress().isNull());
Q_ASSERT(!s.peerAddress().isNull());
for (const QNetworkInterface& nic : QNetworkInterface::allInterfaces()) {
// Ensure the interface is up
@@ -406,6 +407,19 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
return ReachabilityType::RI_VPN;
}
#ifdef Q_OS_WINDOWS
if (nic.name().startsWith("iftype53_") || nic.name().startsWith("iftype131_")) {
// Match by NDIS interface type. These values are Microsoft's recommended values for VPN connections:
// https://learn.microsoft.com/en-US/troubleshoot/windows-client/networking/windows-connection-manager-disconnects-wlan#more-information
//
// The following VPNs use IF_TYPE_PROP_VIRTUAL under Windows:
// - WireguardNT VPNs
// - All WinTun-based VPNs (such as Slack Nebula)
// - OpenVPN with tap-windows6
return ReachabilityType::RI_VPN;
}
#endif
if (nic.hardwareAddress().startsWith("00:FF", Qt::CaseInsensitive)) {
// OpenVPN TAP interfaces have a MAC address starting with 00:FF on Windows
return ReachabilityType::RI_VPN;
@@ -421,9 +435,9 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
return ReachabilityType::RI_VPN;
}
// Didn't meet any of our VPN heuristics. Let's see if it's on-link.
// Didn't meet any of our VPN heuristics. Let's see if the peer address is on-link.
Q_ASSERT(addr.prefixLength() >= 0);
if (addr.prefixLength() >= 0 && s.localAddress().isInSubnet(addr.ip(), addr.prefixLength())) {
if (addr.prefixLength() >= 0 && s.localAddress().isInSubnet(s.peerAddress(), addr.prefixLength())) {
return ReachabilityType::RI_LAN;
}
+2 -1
View File
@@ -223,7 +223,8 @@ NvHTTP::startApp(QString verb,
"&surroundAudioInfo="+QString::number(SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(streamConfig->audioConfiguration))+
"&remoteControllersBitmap="+QString::number(gamepadMask)+
"&gcmap="+QString::number(gamepadMask)+
"&gcpersist="+QString::number(persistGameControllersOnDisconnect ? 1 : 0),
"&gcpersist="+QString::number(persistGameControllersOnDisconnect ? 1 : 0)+
LiGetLaunchUrlQueryParameters(),
LAUNCH_TIMEOUT_MS);
qInfo() << "Launch response:" << response;
+2 -2
View File
@@ -298,8 +298,8 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
}
QByteArray pairingSecret = NvHTTP::getXmlStringFromHex(respXml, "pairingsecret");
QByteArray serverSecret = QByteArray(pairingSecret.data(), 16);
QByteArray serverSignature = QByteArray(&pairingSecret.data()[16], 256);
QByteArray serverSecret = pairingSecret.left(16);
QByteArray serverSignature = pairingSecret.mid(16);
if (!verifySignature(serverSecret,
serverSignature,
+10 -1
View File
@@ -74,6 +74,7 @@ SystemProperties::SystemProperties()
Q_ASSERT(!monitorRefreshRates.isEmpty());
Q_ASSERT(!monitorNativeResolutions.isEmpty());
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
}
QRect SystemProperties::getNativeResolution(int displayIndex)
@@ -82,6 +83,12 @@ QRect SystemProperties::getNativeResolution(int displayIndex)
return monitorNativeResolutions.value(displayIndex);
}
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
{
// Returns default constructed QRect if out of bounds
return monitorSafeAreaResolutions.value(displayIndex);
}
int SystemProperties::getRefreshRate(int displayIndex)
{
// Returns 0 if out of bounds
@@ -199,10 +206,12 @@ void SystemProperties::refreshDisplaysInternal()
SDL_DisplayMode bestMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
SDL_DisplayMode desktopMode;
SDL_Rect safeArea;
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) {
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
monitorSafeAreaResolutions.insert(displayIndex, QRect(0, 0, safeArea.w, safeArea.h));
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
+2
View File
@@ -30,6 +30,7 @@ public:
Q_INVOKABLE void refreshDisplays();
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
Q_INVOKABLE QRect getSafeAreaResolution(int displayIndex);
Q_INVOKABLE int getRefreshRate(int displayIndex);
signals:
@@ -52,6 +53,7 @@ private:
QString unmappedGamepads;
QSize maximumResolution;
QList<QRect> monitorNativeResolutions;
QList<QRect> monitorSafeAreaResolutions;
QList<int> monitorRefreshRates;
QString versionString;
bool supportsHdr;
+9
View File
@@ -316,6 +316,7 @@ StreamCommandLineParser::StreamCommandLineParser()
{"auto", StreamingPreferences::VCC_AUTO},
{"H.264", StreamingPreferences::VCC_FORCE_H264},
{"HEVC", StreamingPreferences::VCC_FORCE_HEVC},
{"AV1", StreamingPreferences::VCC_FORCE_AV1},
};
m_VideoDecoderMap = {
{"auto", StreamingPreferences::VDS_AUTO},
@@ -371,6 +372,8 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
parser.addToggleOption("reverse-scroll-direction", "inverted scroll direction");
parser.addToggleOption("swap-gamepad-buttons", "swap A/B and X/Y gamepad buttons (Nintendo-style)");
parser.addToggleOption("keep-awake", "prevent display sleep while streaming");
parser.addToggleOption("performance-overlay", "show performance overlay");
parser.addToggleOption("hdr", "HDR streaming");
parser.addChoiceOption("capture-system-keys", "capture system key combos", m_CaptureSysKeysModeMap.keys());
parser.addChoiceOption("video-codec", "video codec", m_VideoCodecMap.keys());
parser.addChoiceOption("video-decoder", "video decoder", m_VideoDecoderMap.keys());
@@ -485,6 +488,12 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
// Resolve --keep-awake and --no-keep-awake options
preferences->keepAwake = parser.getToggleOptionValue("keep-awake", preferences->keepAwake);
// Resolve --performance-overlay and --no-performance-overlay options
preferences->showPerformanceOverlay = parser.getToggleOptionValue("performance-overlay", preferences->showPerformanceOverlay);
// Resolve --hdr and --no-hdr options
preferences->enableHdr = parser.getToggleOptionValue("hdr", preferences->enableHdr);
// Resolve --capture-system-keys option
if (parser.isSet("capture-system-keys")) {
preferences->captureSysKeysMode = mapValue(m_CaptureSysKeysModeMap, parser.getChoiceOptionValue("capture-system-keys"));
@@ -0,0 +1,40 @@
From 78261eb6e2fe728fa5d3843b5b8962b1ca559b7e Mon Sep 17 00:00:00 2001
From: David Rosca <nowrep@gmail.com>
Date: Sat, 16 Dec 2023 14:52:01 +0100
Subject: [PATCH] Vulkan: Don't try to reuse old swapchain
---
src/vulkan/swapchain.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/vulkan/swapchain.c b/src/vulkan/swapchain.c
index 0646e57c..c0b07ce4 100644
--- a/src/vulkan/swapchain.c
+++ b/src/vulkan/swapchain.c
@@ -612,13 +612,21 @@ static bool vk_sw_recreate(pl_swapchain sw, int w, int h)
}
#endif
+ {
+ pl_gpu_flush(gpu);
+ vk_wait_idle(vk);
+ for (int i = 0; i < vk->pool_graphics->num_queues; i++)
+ vk->QueueWaitIdle(vk->pool_graphics->queues[i]);
+ vk->DestroySwapchainKHR(vk->dev, p->swapchain, PL_VK_ALLOC);
+ }
+
// Calling `vkCreateSwapchainKHR` puts sinfo.oldSwapchain into a retired
// state whether the call succeeds or not, so we always need to garbage
// collect it afterwards - asynchronously as it may still be in use
- sinfo.oldSwapchain = p->swapchain;
+ /* sinfo.oldSwapchain = p->swapchain; */
p->swapchain = VK_NULL_HANDLE;
VkResult res = vk->CreateSwapchainKHR(vk->dev, &sinfo, PL_VK_ALLOC, &p->swapchain);
- vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain));
+ /* vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain)); */
PL_VK_ASSERT(res, "vk->CreateSwapchainKHR(...)");
// Get the new swapchain images
--
2.43.0
@@ -34,6 +34,53 @@
</screenshots>
<releases>
<release version="6.0.0" date="2024-06-06">
<description>
<p>New features:</p>
<ul>
<li>HDR is now supported on Steam Deck and other HDR-capable Linux systems using a new Vulkan-based renderer</li>
<li>Vulkan Video decoding of H.264, HEVC, and AV1 is now supported using the new Vulkan renderer</li>
<li>Full E2E stream encryption is now supported when streaming from Sunshine v0.22 or later</li>
<li>Initiating a stream via the command-line will now automatically wake the target machine if it is sleeping</li>
<li>Error codes are now displayed in the connection termination dialog</li>
<li>Detailed PC state information is now available via the new "View Details" context menu option (similar to the Android client)</li>
<li>The performance overlay can now be configured to be shown by default when starting a stream</li>
</ul>
<p>Behavior Changes:</p>
<ul>
<li>The selected bitrate is no longer silently adjusted by Moonlight based upon the video codec or HDR mode</li>
<li>The selected mouse mode when starting a stream no longer influences the acceleration curve when the mouse mode is toggled using the Ctrl+Alt+Shift+M key combo while streaming</li>
<li>Hardcoded fallback paths for VAAPI and VDPAU drivers are no longer used</li>
<li>Per-session keys (rikey and rikeyid values) are now automatically stripped from logs</li>
<li>Log messages will now be logged to stderr rather than stdout</li>
<li>EGL is now always used instead of GLX for loading OpenGL in X11 environments on non-x86 platforms</li>
</ul>
<p>Bugfixes:</p>
<ul>
<li>Improved reliability of streaming during momentary drops of WiFi connections</li>
<li>Improved performance with high polling rate mice</li>
<li>Improved input latency when a gamepad is connected</li>
<li>Added missing command-line options to control HDR and AV1 codec usage</li>
<li>Fixed instant connection termination error when streaming over the Internet behind certain Carrier-Grade NAT devices</li>
<li>Fixed incorrect MTU detection when streaming over the Internet without a VPN</li>
<li>Fixed recovery of audio when no audio output device is present when the stream starts</li>
<li>Fixed merging of gamepad state when multi-controller mode is disabled</li>
<li>Fixed indefinitely resending mDNS queries when a host could not be resolved</li>
<li>Fixed inability to pair when the host is not using a RSA-2048 key</li>
<li>Updated included gamepad mappings</li>
<li>Updated community-contributed translations from Weblate</li>
</ul>
</description>
</release>
<release version="5.0.1" date="2023-11-02">
<description>
<p>Bugfixes:</p>
<ul>
<li>Fixed connection issue streaming from some hosts running GeForce Experience or Sunshine v0.20 and earlier</li>
<li>Updated community-contributed translations from Weblate</li>
</ul>
</description>
</release>
<release version="5.0.0" date="2023-10-18">
<description>
<p>New features:</p>
+4
View File
@@ -5,6 +5,10 @@
# pairing data will be lost each reboot.
HOME=/usr/local/moonlight
# Enable Steam Controller detection in SDL
# https://github.com/moonlight-stream/moonlight-qt/issues/697
export SDL_ENABLE_STEAM_CONTROLLERS=1
# Renice PE_Single_CPU which seems to host A/V stuff
renice -10 -p $(pidof PE_Single_CPU)
+2 -2
View File
@@ -256,7 +256,7 @@ CenteredGridView {
anchors.fill: parent
acceptedButtons: Qt.RightButton;
onClicked: {
parent.onPressAndHold()
parent.pressAndHold()
}
}
@@ -350,7 +350,7 @@ CenteredGridView {
function quitApp() {
var component = Qt.createComponent("QuitSegue.qml")
var params = {"appName": appName, "quitRunningAppFn": appModel.quitRunningApp}
var params = {"appName": appName, "quitRunningAppFn": () => appModel.quitRunningApp()}
if (segueToStream) {
// Store the session and app name if we're going to stream after
// successfully quitting the old app.
+1 -1
View File
@@ -85,7 +85,7 @@ Item {
function quitApp() {
var component = Qt.createComponent("QuitSegue.qml")
var params = {"appName": appName, "quitRunningAppFn": launcher.quitRunningApp}
var params = {"appName": appName, "quitRunningAppFn": () => launcher.quitRunningApp()}
stackView.push(component.createObject(stackView, params))
}
+18 -1
View File
@@ -6,6 +6,7 @@ import ComputerModel 1.0
import ComputerManager 1.0
import StreamingPreferences 1.0
import SystemProperties 1.0
import SdlGamepadKeyNavigation 1.0
CenteredGridView {
@@ -215,6 +216,14 @@ CenteredGridView {
deletePcDialog.open()
}
}
NavigableMenuItem {
parentMenu: pcContextMenu
text: qsTr("View Details")
onTriggered: {
showPcDetailsDialog.pcDetails = model.details
showPcDetailsDialog.open()
}
}
}
}
@@ -262,7 +271,7 @@ CenteredGridView {
anchors.fill: parent
acceptedButtons: Qt.RightButton;
onClicked: {
parent.onPressAndHold()
parent.pressAndHold()
}
}
@@ -395,5 +404,13 @@ CenteredGridView {
}
}
NavigableMessageDialog {
id: showPcDetailsDialog
property string pcDetails : "";
text: showPcDetailsDialog.pcDetails
imageSrc: "qrc:/res/baseline-help_outline-24px.svg"
standardButtons: Dialog.Ok
}
ScrollBar.vertical: ScrollBar {}
}
+60 -52
View File
@@ -133,15 +133,45 @@ Flickable {
AutoResizingComboBox {
property int lastIndexValue
function addDetectedResolution(friendlyNamePrefix, rect) {
var indexToAdd = 0
for (var j = 0; j < resolutionComboBox.count; j++) {
var existing_width = parseInt(resolutionListModel.get(j).video_width);
var existing_height = parseInt(resolutionListModel.get(j).video_height);
if (rect.width === existing_width && rect.height === existing_height) {
// Duplicate entry, skip
indexToAdd = -1
break
}
else if (rect.width * rect.height > existing_width * existing_height) {
// Candidate entrypoint after this entry
indexToAdd = j + 1
}
}
// Insert this display's resolution if it's not a duplicate
if (indexToAdd >= 0) {
resolutionListModel.insert(indexToAdd,
{
"text": friendlyNamePrefix+" ("+rect.width+"x"+rect.height+")",
"video_width": ""+rect.width,
"video_height": ""+rect.height,
"is_custom": false
})
}
}
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
// Refresh display data before using it to build the list
SystemProperties.refreshDisplays()
// Add native resolutions for all attached displays
// Add native and safe area resolutions for all attached displays
var done = false
for (var displayIndex = 0; !done; displayIndex++) {
var screenRect = SystemProperties.getNativeResolution(displayIndex);
var safeAreaRect = SystemProperties.getSafeAreaResolution(displayIndex);
if (screenRect.width === 0) {
// Exceeded max count of displays
@@ -149,32 +179,8 @@ Flickable {
break
}
var indexToAdd = 0
for (var j = 0; j < resolutionComboBox.count; j++) {
var existing_width = parseInt(resolutionListModel.get(j).video_width);
var existing_height = parseInt(resolutionListModel.get(j).video_height);
if (screenRect.width === existing_width && screenRect.height === existing_height) {
// Duplicate entry, skip
indexToAdd = -1
break
}
else if (screenRect.width * screenRect.height > existing_width * existing_height) {
// Candidate entrypoint after this entry
indexToAdd = j + 1
}
}
// Insert this display's resolution if it's not a duplicate
if (indexToAdd >= 0) {
resolutionListModel.insert(indexToAdd,
{
"text": "Native ("+screenRect.width+"x"+screenRect.height+")",
"video_width": ""+screenRect.width,
"video_height": ""+screenRect.height,
"is_custom": false
})
}
addDetectedResolution(qsTr("Native"), screenRect)
addDetectedResolution(qsTr("Native (Excluding Notch)"), safeAreaRect)
}
// Prune resolutions that are over the decoder's maximum
@@ -210,7 +216,7 @@ Flickable {
if (!index_set) {
// We did not find a match. This must be a custom resolution.
resolutionListModel.append({
"text": "Custom ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")",
"text": qsTr("Custom")+" ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")",
"video_width": ""+StreamingPreferences.width,
"video_height": ""+StreamingPreferences.height,
"is_custom": true
@@ -219,7 +225,7 @@ Flickable {
}
else {
resolutionListModel.append({
"text": "Custom",
"text": qsTr("Custom"),
"video_width": "",
"video_height": "",
"is_custom": true
@@ -689,7 +695,7 @@ Flickable {
Component.onCompleted: {
// Refresh the text after translations change
languageChanged.connect(onValueChanged)
languageChanged.connect(valueChanged)
}
}
@@ -1052,6 +1058,10 @@ Flickable {
text: "עִבְרִית" // Hebrew
val: StreamingPreferences.LANG_HE
} */
/* ListElement {
text: "کرمانجیی خواروو" // Central Kurdish
val: StreamingPreferences.LANG_CKB
} */
}
// ::onActivated must be used, as it only listens for when the index is changed by a human
onActivated : {
@@ -1349,15 +1359,7 @@ Flickable {
font.pointSize: 12
checked: StreamingPreferences.swapFaceButtons
onCheckedChanged: {
// Check if the value changed (this is called on init too)
if (StreamingPreferences.swapFaceButtons !== checked) {
StreamingPreferences.swapFaceButtons = checked
// Save and restart SdlGamepadKeyNavigation so it can pull the new value
StreamingPreferences.save()
SdlGamepadKeyNavigation.disable()
SdlGamepadKeyNavigation.enable()
}
StreamingPreferences.swapFaceButtons = checked
}
ToolTip.delay: 1000
@@ -1621,10 +1623,6 @@ Flickable {
if (StreamingPreferences.enableMdns != checked) {
StreamingPreferences.enableMdns = checked
// We must save the updated preference to ensure
// ComputerManager can observe the change internally.
StreamingPreferences.save()
// Restart polling so the mDNS change takes effect
if (window.pollingActive) {
ComputerManager.stopPollingAsync()
@@ -1641,17 +1639,27 @@ Flickable {
font.pointSize: 12
checked: StreamingPreferences.detectNetworkBlocking
onCheckedChanged: {
// This is called on init, so only do the work if we've
// actually changed the value.
if (StreamingPreferences.detectNetworkBlocking != checked) {
StreamingPreferences.detectNetworkBlocking = checked
// We must save the updated preference to ensure
// ComputerManager can observe the change internally.
StreamingPreferences.save()
}
StreamingPreferences.detectNetworkBlocking = checked
}
}
CheckBox {
id: showPerformanceOverlay
width: parent.width
text: qsTr("Show performance stats while streaming")
font.pointSize: 12
checked: StreamingPreferences.showPerformanceOverlay
onCheckedChanged: {
StreamingPreferences.showPerformanceOverlay = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr("Display real-time stream performance information while streaming.") + "\n\n" +
qsTr("You can toggle it at any time while streaming using Ctrl+Alt+Shift+S or Select+L1+R1+X.") + "\n\n" +
qsTr("The performance overlay is not supported on Steam Link or Raspberry Pi.")
}
}
}
}
+1 -1
View File
@@ -176,7 +176,7 @@ Item {
gc()
// Run the streaming session to completion
session.exec(Screen.virtualX, Screen.virtualY)
session.exec(Window.window)
}
sourceComponent: Item {}
+41
View File
@@ -42,6 +42,46 @@ QVariant ComputerModel::data(const QModelIndex& index, int role) const
return computer->state == NvComputer::CS_UNKNOWN;
case ServerSupportedRole:
return computer->isSupportedServerVersion;
case DetailsRole: {
QString state, pairState;
switch (computer->state) {
case NvComputer::CS_ONLINE:
state = tr("Online");
break;
case NvComputer::CS_OFFLINE:
state = tr("Offline");
break;
default:
state = tr("Unknown");
break;
}
switch (computer->pairState) {
case NvComputer::PS_PAIRED:
pairState = tr("Paired");
break;
case NvComputer::PS_NOT_PAIRED:
pairState = tr("Unpaired");
break;
default:
pairState = tr("Unknown");
break;
}
return tr("Name: %1").arg(computer->name) + '\n' +
tr("Status: %1").arg(state) + '\n' +
tr("Active Address: %1").arg(computer->activeAddress.toString()) + '\n' +
tr("UUID: %1").arg(computer->uuid) + '\n' +
tr("Local Address: %1").arg(computer->localAddress.toString()) + '\n' +
tr("Remote Address: %1").arg(computer->remoteAddress.toString()) + '\n' +
tr("IPv6 Address: %1").arg(computer->ipv6Address.toString()) + '\n' +
tr("Manual Address: %1").arg(computer->manualAddress.toString()) + '\n' +
tr("MAC Address: %1").arg(computer->macAddress.isEmpty() ? tr("Unknown") : QString(computer->macAddress.toHex(':'))) + '\n' +
tr("Pair State: %1").arg(pairState) + '\n' +
tr("Running Game ID: %1").arg(computer->state == NvComputer::CS_ONLINE ? QString::number(computer->currentGameId) : tr("Unknown")) + '\n' +
tr("HTTPS Port: %1").arg(computer->state == NvComputer::CS_ONLINE ? QString::number(computer->activeHttpsPort) : tr("Unknown"));
}
default:
return QVariant();
}
@@ -69,6 +109,7 @@ QHash<int, QByteArray> ComputerModel::roleNames() const
names[WakeableRole] = "wakeable";
names[StatusUnknownRole] = "statusUnknown";
names[ServerSupportedRole] = "serverSupported";
names[DetailsRole] = "details";
return names;
}
+2 -1
View File
@@ -15,7 +15,8 @@ class ComputerModel : public QAbstractListModel
BusyRole,
WakeableRole,
StatusUnknownRole,
ServerSupportedRole
ServerSupportedRole,
DetailsRole
};
public:
+33 -46
View File
@@ -19,26 +19,48 @@ ApplicationWindow {
property bool clearOnBack: false
id: window
visible: true
width: 1280
height: 600
// Override the background color to Material 2 colors for Qt 6.5+
// in order to improve contrast between GFE's placeholder box art
// and the background of the app grid.
Component.onCompleted: {
// Override the background color to Material 2 colors for Qt 6.5+
// in order to improve contrast between GFE's placeholder box art
// and the background of the app grid.
if (SystemProperties.usesMaterial3Theme) {
Material.background = "#303030"
}
}
visibility: {
// Show the window according to the user's preferences
if (SystemProperties.hasDesktopEnvironment) {
if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_WINDOWED) return "Windowed"
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_MAXIMIZED) return "Maximized"
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_FULLSCREEN) return "FullScreen"
if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_MAXIMIZED) {
window.showMaximized()
}
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_FULLSCREEN) {
window.showFullScreen()
}
else {
window.show()
}
} else {
return "FullScreen"
window.showFullScreen()
}
// Display any modal dialogs for configuration warnings
if (SystemProperties.isWow64) {
wow64Dialog.open()
}
else if (!SystemProperties.hasHardwareAcceleration) {
if (SystemProperties.isRunningXWayland) {
xWaylandDialog.open()
}
else {
noHwDecoderDialog.open()
}
}
if (SystemProperties.unmappedGamepads) {
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
unmappedGamepadDialog.open()
}
}
@@ -156,41 +178,6 @@ ApplicationWindow {
}
}
property bool initialized: false
// BUG: Using onAfterSynchronizing: here causes very strange
// failures on Linux. Many shaders fail to compile and we
// eventually segfault deep inside the Qt OpenGL code.
onAfterRendering: {
// We use this callback to trigger dialog display because
// it only happens once the window is fully constructed.
// Doing it earlier can lead to the dialog appearing behind
// the window or otherwise without input focus.
if (!initialized) {
// Set initialized before calling anything else, because
// pumping the event loop can cause us to get another
// onAfterRendering call and potentially reenter this code.
initialized = true;
if (SystemProperties.isWow64) {
wow64Dialog.open()
}
else if (!SystemProperties.hasHardwareAcceleration) {
if (SystemProperties.isRunningXWayland) {
xWaylandDialog.open()
}
else {
noHwDecoderDialog.open()
}
}
if (SystemProperties.unmappedGamepads) {
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
unmappedGamepadDialog.open()
}
}
}
// Workaround for lack of instanceof in Qt 5.9.
//
// Based on https://stackoverflow.com/questions/13923794/how-to-do-a-is-a-typeof-or-instanceof-in-qml
@@ -283,7 +270,7 @@ ApplicationWindow {
visible: SystemProperties.hasBrowser &&
qmltypeof(stackView.currentItem, "SettingsView")
iconSource: "qrc:/res/Discord-Logo-White.svg"
iconSource: "qrc:/res/discord.svg"
ToolTip.delay: 1000
ToolTip.timeout: 3000
+4 -5
View File
@@ -4,13 +4,13 @@
#include <QGuiApplication>
#include <QWindow>
#include "settings/streamingpreferences.h"
#include "settings/mappingmanager.h"
#define AXIS_NAVIGATION_REPEAT_DELAY 150
SdlGamepadKeyNavigation::SdlGamepadKeyNavigation()
: m_Enabled(false),
SdlGamepadKeyNavigation::SdlGamepadKeyNavigation(StreamingPreferences* prefs)
: m_Prefs(prefs),
m_Enabled(false),
m_UiNavMode(false),
m_FirstPoll(false),
m_LastAxisNavigationEventTime(0)
@@ -93,7 +93,6 @@ void SdlGamepadKeyNavigation::disable()
void SdlGamepadKeyNavigation::onPollingTimerFired()
{
SDL_Event event;
StreamingPreferences prefs;
// Discard any pending button events on the first poll to avoid picking up
// stale input data from the stream session (like the quit combo).
@@ -121,7 +120,7 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
QEvent::Type::KeyPress : QEvent::Type::KeyRelease;
// Swap face buttons if needed
if (prefs.swapFaceButtons) {
if (m_Prefs->swapFaceButtons) {
switch (event.cbutton.button) {
case SDL_CONTROLLER_BUTTON_A:
event.cbutton.button = SDL_CONTROLLER_BUTTON_B;
+4 -1
View File
@@ -5,12 +5,14 @@
#include <SDL.h>
#include "settings/streamingpreferences.h"
class SdlGamepadKeyNavigation : public QObject
{
Q_OBJECT
public:
SdlGamepadKeyNavigation();
SdlGamepadKeyNavigation(StreamingPreferences* prefs);
~SdlGamepadKeyNavigation();
@@ -29,6 +31,7 @@ private slots:
void onPollingTimerFired();
private:
StreamingPreferences* m_Prefs;
QTimer* m_PollingTimer;
QList<SDL_GameController*> m_Gamepads;
bool m_Enabled;
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+381 -247
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+322 -188
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+355 -221
View File
File diff suppressed because it is too large Load Diff
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+338 -204
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+319 -185
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+316 -182
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+378 -244
View File
File diff suppressed because it is too large Load Diff
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+326 -192
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+328 -194
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+315 -181
View File
File diff suppressed because it is too large Load Diff
+101 -37
View File
@@ -10,7 +10,8 @@
#include <QFont>
#include <QCursor>
#include <QElapsedTimer>
#include <QFile>
#include <QTemporaryFile>
#include <QRegularExpression>
// Don't let SDL hook our main function, since Qt is already
// doing the same thing. This needs to be before any headers
@@ -44,26 +45,24 @@
#include "settings/streamingpreferences.h"
#include "gui/sdlgamepadkeynavigation.h"
#if !defined(QT_DEBUG) && defined(Q_OS_WIN32)
// Log to file for release Windows builds
#define USE_CUSTOM_LOGGER
#if defined(Q_OS_WIN32)
#define IS_UNSPECIFIED_HANDLE(x) ((x) == INVALID_HANDLE_VALUE || (x) == NULL)
// Log to file or console dynamically for Windows builds
#define LOG_TO_FILE
#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
// Use stdout logger on all Linux/BSD builds
#define USE_CUSTOM_LOGGER
#elif !defined(QT_DEBUG) && defined(Q_OS_DARWIN)
// Log to file for release Mac builds
#define USE_CUSTOM_LOGGER
#define LOG_TO_FILE
#else
// For debug Windows and Mac builds, use default logger
// Log to console for debug Mac builds
#endif
#ifdef USE_CUSTOM_LOGGER
static QElapsedTimer s_LoggerTime;
static QTextStream s_LoggerStream(stdout);
static QTextStream s_LoggerStream(stderr);
static QMutex s_LoggerLock;
static bool s_SuppressVerboseOutput;
static QRegularExpression k_RikeyRegex("&rikey=\\w+");
static QRegularExpression k_RikeyIdRegex("&rikeyid=[\\d-]+");
#ifdef LOG_TO_FILE
// Max log file size of 10 MB
#define MAX_LOG_SIZE_BYTES (10 * 1024 * 1024)
@@ -76,6 +75,10 @@ void logToLoggerStream(QString& message)
{
QMutexLocker lock(&s_LoggerLock);
// Strip session encryption keys and IVs from the logs
message.replace(k_RikeyRegex, "&rikey=REDACTED");
message.replace(k_RikeyIdRegex, "&rikeyid=REDACTED");
#ifdef LOG_TO_FILE
if (s_LogLimitReached) {
return;
@@ -216,8 +219,6 @@ void ffmpegLogToDiskHandler(void* ptr, int level, const char* fmt, va_list vl)
#endif
#endif
#ifdef Q_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
@@ -306,13 +307,25 @@ int main(int argc, char *argv[])
qputenv("QML_DISK_CACHE_PATH", Path::getQmlCacheDir().toUtf8());
}
#ifdef USE_CUSTOM_LOGGER
#ifdef Q_OS_WIN32
// Grab the original std handles before we potentially redirect them later
HANDLE oldConOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE oldConErr = GetStdHandle(STD_ERROR_HANDLE);
#endif
#ifdef LOG_TO_FILE
QDir tempDir(Path::getLogDir());
s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch())));
if (s_LoggerFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl;
s_LoggerStream.setDevice(s_LoggerFile);
#ifdef Q_OS_WIN32
// Only log to a file if the user didn't redirect stderr somewhere else
if (IS_UNSPECIFIED_HANDLE(oldConErr))
#endif
{
s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch())));
if (s_LoggerFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl;
s_LoggerStream.setDevice(s_LoggerFile);
}
}
#endif
@@ -324,8 +337,6 @@ int main(int argc, char *argv[])
av_log_set_callback(ffmpegLogToDiskHandler);
#endif
#endif
#ifdef Q_OS_WIN32
// Create a crash dump when we crash on Windows
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
@@ -351,6 +362,11 @@ int main(int argc, char *argv[])
SSL_free(nullptr);
#endif
// We keep this at function scope to ensure it stays around while we're running,
// becaue the Qt QPA will need to read it. Since the temporary file is only
// created when open() is called, this doesn't do any harm for other platforms.
QTemporaryFile eglfsConfigFile("eglfs_override_XXXXXX.conf");
// Avoid using High DPI on EGLFS. It breaks font rendering.
// https://bugreports.qt.io/browse/QTBUG-64377
//
@@ -384,6 +400,17 @@ int main(int argc, char *argv[])
qWarning() << "Unable to find a KMSDRM display device!";
qWarning() << "On the Raspberry Pi, you must enable the 'fake KMS' driver in raspi-config to use Moonlight outside of the GUI environment.";
}
else if (!qEnvironmentVariableIsSet("QT_QPA_EGLFS_KMS_CONFIG")) {
// HACK: Remove this when Qt is fixed to properly check for display support before picking a card
QString cardOverride = WMUtils::getDrmCardOverride();
if (!cardOverride.isEmpty()) {
if (eglfsConfigFile.open()) {
qInfo() << "Overriding default Qt EGLFS card selection to" << cardOverride;
QTextStream(&eglfsConfigFile) << "{ \"device\": \"" << cardOverride << "\" }";
qputenv("QT_QPA_EGLFS_KMS_CONFIG", eglfsConfigFile.fileName().toUtf8());
}
}
}
}
// EGLFS uses OpenGLES 2.0, so we will too. Some embedded platforms may not
@@ -393,9 +420,18 @@ int main(int argc, char *argv[])
#endif
}
#if !defined(Q_PROCESSOR_X86) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL)
// Some ARM and RISC-V embedded devices don't have working GLX which can cause
// SDL to fail to find a working OpenGL implementation at all. Let's force EGL
// on non-x86 platforms, since GLX is deprecated anyway.
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
#endif
#ifdef Q_OS_MACOS
// This avoids using the default keychain for SSL, which may cause
// password prompts on macOS.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
#endif
#if defined(Q_OS_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (!qEnvironmentVariableIsSet("QT_OPENGL")) {
@@ -418,6 +454,12 @@ int main(int argc, char *argv[])
qputenv("QSG_RENDER_LOOP", "basic");
#endif
#if defined(Q_OS_DARWIN) && defined(QT_DEBUG)
// Enable Metal valiation for debug builds
qputenv("MTL_DEBUG_LAYER", "1");
qputenv("MTL_SHADER_VALIDATION", "1");
#endif
// We don't want system proxies to apply to us
QNetworkProxyFactory::setUseSystemConfiguration(false);
@@ -496,6 +538,13 @@ int main(int argc, char *argv[])
// SDL doing it for us behind our backs.
SDL_SetHint("SDL_MOUSE_AUTO_CAPTURE", "0");
// SDL will try to lock the mouse cursor on Wayland if it's not visible in order to
// support applications that assume they can warp the cursor (which isn't possible
// on Wayland). We don't want this behavior because it interferes with seamless mouse
// mode when toggling between windowed and fullscreen modes by unexpectedly locking
// the mouse cursor.
SDL_SetHint("SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP", "0");
#ifdef QT_DEBUG
// Allow thread naming using exceptions on debug builds. SDL doesn't use SEH
// when throwing the exceptions, so we don't enable it for release builds out
@@ -505,25 +554,34 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
#ifndef STEAM_LINK
// Force use of the KMSDRM backend for SDL when using Qt platform plugins
// that directly draw to the display without a windowing system.
if (QGuiApplication::platformName() == "eglfs" || QGuiApplication::platformName() == "linuxfb") {
qputenv("SDL_VIDEODRIVER", "kmsdrm");
}
#endif
GlobalCommandLineParser parser;
GlobalCommandLineParser::ParseResult commandLineParserResult = parser.parse(app.arguments());
switch (commandLineParserResult) {
case GlobalCommandLineParser::ListRequested:
#ifdef USE_CUSTOM_LOGGER
// Don't log to the console since it will jumble the command output
s_SuppressVerboseOutput = true;
#endif
#ifdef Q_OS_WIN32
// Attach to the console to be able to print output.
// Since we're a /SUBSYSTEM:WINDOWS app, we won't be attached by default.
// If we don't have stdout or stderr handles (which will normally be the case
// since we're a /SUBSYSTEM:WINDOWS app), attach to our parent console and use
// that for stdout and stderr.
//
// If we do have stdout or stderr handles, that means the user has used standard
// handle redirection. In that case, we don't want to override those handles.
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
HANDLE conOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (conOut != INVALID_HANDLE_VALUE && conOut != NULL) {
// If we didn't have an old stdout/stderr handle, use the new CONOUT$ handle
if (IS_UNSPECIFIED_HANDLE(oldConOut)) {
freopen("CONOUT$", "w", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
}
HANDLE conErr = GetStdHandle(STD_ERROR_HANDLE);
if (conErr != INVALID_HANDLE_VALUE && conErr != NULL) {
if (IS_UNSPECIFIED_HANDLE(oldConErr)) {
freopen("CONOUT$", "w", stderr);
setvbuf(stderr, NULL, _IONBF, 0);
}
@@ -547,8 +605,7 @@ int main(int argc, char *argv[])
runtimeVersion.major, runtimeVersion.minor, runtimeVersion.patch);
// Apply the initial translation based on user preference
StreamingPreferences prefs;
prefs.retranslate();
StreamingPreferences::get()->retranslate();
// Trickily declare the translation for dialog buttons
QCoreApplication::translate("QPlatformTheme", "&Yes");
@@ -613,8 +670,8 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<Session>("Session", 1, 0, "Session", "Session cannot be created from QML");
qmlRegisterSingletonType<ComputerManager>("ComputerManager", 1, 0,
"ComputerManager",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new ComputerManager();
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
return new ComputerManager(StreamingPreferences::get(qmlEngine));
});
qmlRegisterSingletonType<AutoUpdateChecker>("AutoUpdateChecker", 1, 0,
"AutoUpdateChecker",
@@ -628,13 +685,13 @@ int main(int argc, char *argv[])
});
qmlRegisterSingletonType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0,
"SdlGamepadKeyNavigation",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new SdlGamepadKeyNavigation();
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
return new SdlGamepadKeyNavigation(StreamingPreferences::get(qmlEngine));
});
qmlRegisterSingletonType<StreamingPreferences>("StreamingPreferences", 1, 0,
"StreamingPreferences",
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
return new StreamingPreferences(qmlEngine);
return StreamingPreferences::get(qmlEngine);
});
// Create the identity manager on the main thread
@@ -665,7 +722,7 @@ int main(int argc, char *argv[])
case GlobalCommandLineParser::StreamRequested:
{
initialView = "qrc:/gui/CliStartStreamSegue.qml";
StreamingPreferences* preferences = new StreamingPreferences(&app);
StreamingPreferences* preferences = StreamingPreferences::get();
StreamCommandLineParser streamParser;
streamParser.parse(app.arguments(), preferences);
QString host = streamParser.getHost();
@@ -697,7 +754,7 @@ int main(int argc, char *argv[])
ListCommandLineParser listParser;
listParser.parse(app.arguments());
auto launcher = new CliListApps::Launcher(listParser.getHost(), listParser, &app);
launcher->execute(new ComputerManager(&app));
launcher->execute(new ComputerManager(StreamingPreferences::get()));
hasGUI = false;
break;
}
@@ -718,5 +775,12 @@ int main(int argc, char *argv[])
// sometimes freezing and blocking process exit.
QThreadPool::globalInstance()->waitForDone(30000);
#ifdef Q_OS_WIN32
// Without an explicit flush, console redirection for the list command
// doesn't work reliably (sometimes the target file contains no text).
fflush(stderr);
fflush(stdout);
#endif
return err;
}
View File
-1
View File
@@ -1 +0,0 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 245 240" fill="white"><path class="st0" d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"/><path class="st0" d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="white"><path d="M20.317 4.3698a19.7913 19.7913 0 0 0-4.8851-1.5152.0741.0741 0 0 0-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 0 0-.0785-.037 19.7363 19.7363 0 0 0-4.8852 1.515.0699.0699 0 0 0-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 0 0 .0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 0 0 .0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 0 0-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 0 1-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 0 1 .0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 0 1 .0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 0 1-.0066.1276 12.2986 12.2986 0 0 1-1.873.8914.0766.0766 0 0 0-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 0 0 .0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 0 0 .0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 0 0-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+4 -1
View File
@@ -17,7 +17,7 @@
<file>res/baseline-help_outline-24px.svg</file>
<file>res/baseline-error_outline-24px.svg</file>
<file>res/baseline-check_circle_outline-24px.svg</file>
<file>res/Discord-Logo-White.svg</file>
<file>res/discord.svg</file>
<file>languages/qml_de.ts</file>
<file>languages/qml_de.qm</file>
<file>languages/qml_fr.ts</file>
@@ -64,6 +64,8 @@
<file>languages/qml_cs.qm</file>
<file>languages/qml_he.ts</file>
<file>languages/qml_he.qm</file>
<file>languages/qml_ckb.ts</file>
<file>languages/qml_ckb.qm</file>
<!-- Don't include pt_BR until it is more complete -->
<!--file>languages/qml_pt_BR.qm</file-->
<!--file>languages/qml_pt_BR.ts</file-->
@@ -82,5 +84,6 @@
<file alias="d3d11_genyuv_pixel.fxc">shaders/d3d11_genyuv_pixel.fxc</file>
<file alias="d3d11_bt601lim_pixel.fxc">shaders/d3d11_bt601lim_pixel.fxc</file>
<file alias="d3d11_bt2020lim_pixel.fxc">shaders/d3d11_bt2020lim_pixel.fxc</file>
<file alias="vt_renderer.metal">shaders/vt_renderer.metal</file>
</qresource>
</RCC>
+45 -7
View File
@@ -5,6 +5,7 @@
#include <QTranslator>
#include <QCoreApplication>
#include <QLocale>
#include <QReadWriteLock>
#include <QtMath>
#include <QtDebug>
@@ -37,6 +38,7 @@
#define SER_DEFAULTVER "defaultver"
#define SER_PACKETSIZE "packetsize"
#define SER_DETECTNETBLOCKING "detectnetblocking"
#define SER_SHOWPERFOVERLAY "showperfoverlay"
#define SER_SWAPMOUSEBUTTONS "swapmousebuttons"
#define SER_MUTEONFOCUSLOSS "muteonfocusloss"
#define SER_BACKGROUNDGAMEPAD "backgroundgamepad"
@@ -48,18 +50,50 @@
#define CURRENT_DEFAULT_VER 2
StreamingPreferences::StreamingPreferences(QObject *parent)
: QObject(parent),
m_QmlEngine(nullptr)
static StreamingPreferences* s_GlobalPrefs;
static QReadWriteLock s_GlobalPrefsLock;
StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine)
: m_QmlEngine(qmlEngine)
{
reload();
}
StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent)
: QObject(parent),
m_QmlEngine(qmlEngine)
StreamingPreferences* StreamingPreferences::get(QQmlEngine *qmlEngine)
{
reload();
{
QReadLocker readGuard(&s_GlobalPrefsLock);
// If we have a preference object and it's associated with a QML engine or
// if the caller didn't specify a QML engine, return the existing object.
if (s_GlobalPrefs && (s_GlobalPrefs->m_QmlEngine || !qmlEngine)) {
// The lifetime logic here relies on the QML engine also being a singleton.
Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine);
return s_GlobalPrefs;
}
}
{
QWriteLocker writeGuard(&s_GlobalPrefsLock);
// If we already have an preference object but the QML engine is now available,
// associate the QML engine with the preferences.
if (s_GlobalPrefs) {
if (!s_GlobalPrefs->m_QmlEngine) {
s_GlobalPrefs->m_QmlEngine = qmlEngine;
}
else {
// We could reach this codepath if another thread raced with us
// and created the object while we were outside the pref lock.
Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine);
}
}
else {
s_GlobalPrefs = new StreamingPreferences(qmlEngine);
}
return s_GlobalPrefs;
}
}
void StreamingPreferences::reload()
@@ -97,6 +131,7 @@ void StreamingPreferences::reload()
richPresence = settings.value(SER_RICHPRESENCE, true).toBool();
gamepadMouse = settings.value(SER_GAMEPADMOUSE, true).toBool();
detectNetworkBlocking = settings.value(SER_DETECTNETBLOCKING, true).toBool();
showPerformanceOverlay = settings.value(SER_SHOWPERFOVERLAY, false).toBool();
packetSize = settings.value(SER_PACKETSIZE, 0).toInt();
swapMouseButtons = settings.value(SER_SWAPMOUSEBUTTONS, false).toBool();
muteOnFocusLoss = settings.value(SER_MUTEONFOCUSLOSS, false).toBool();
@@ -252,6 +287,8 @@ QString StreamingPreferences::getSuffixFromLanguage(StreamingPreferences::Langua
return "cs";
case LANG_HE:
return "he";
case LANG_CKB:
return "ckb";
case LANG_AUTO:
default:
return QLocale::system().name();
@@ -280,6 +317,7 @@ void StreamingPreferences::save()
settings.setValue(SER_GAMEPADMOUSE, gamepadMouse);
settings.setValue(SER_PACKETSIZE, packetSize);
settings.setValue(SER_DETECTNETBLOCKING, detectNetworkBlocking);
settings.setValue(SER_SHOWPERFOVERLAY, showPerformanceOverlay);
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
settings.setValue(SER_HDR, enableHdr);
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));
+7 -2
View File
@@ -9,8 +9,7 @@ class StreamingPreferences : public QObject
Q_OBJECT
public:
StreamingPreferences(QObject *parent = nullptr);
StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent = nullptr);
static StreamingPreferences* get(QQmlEngine *qmlEngine = nullptr);
Q_INVOKABLE static int
getDefaultBitrate(int width, int height, int fps);
@@ -92,6 +91,7 @@ public:
LANG_PL,
LANG_CS,
LANG_HE,
LANG_CKB,
};
Q_ENUM(Language);
@@ -120,6 +120,7 @@ public:
Q_PROPERTY(bool richPresence MEMBER richPresence NOTIFY richPresenceChanged)
Q_PROPERTY(bool gamepadMouse MEMBER gamepadMouse NOTIFY gamepadMouseChanged)
Q_PROPERTY(bool detectNetworkBlocking MEMBER detectNetworkBlocking NOTIFY detectNetworkBlockingChanged)
Q_PROPERTY(bool showPerformanceOverlay MEMBER showPerformanceOverlay NOTIFY showPerformanceOverlayChanged)
Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged)
Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged)
Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged)
@@ -156,6 +157,7 @@ public:
bool richPresence;
bool gamepadMouse;
bool detectNetworkBlocking;
bool showPerformanceOverlay;
bool swapMouseButtons;
bool muteOnFocusLoss;
bool backgroundGamepad;
@@ -196,6 +198,7 @@ signals:
void richPresenceChanged();
void gamepadMouseChanged();
void detectNetworkBlockingChanged();
void showPerformanceOverlayChanged();
void mouseButtonsChanged();
void muteOnFocusLossChanged();
void backgroundGamepadChanged();
@@ -206,6 +209,8 @@ signals:
void languageChanged();
private:
explicit StreamingPreferences(QQmlEngine *qmlEngine);
QString getSuffixFromLanguage(Language lang);
QQmlEngine* m_QmlEngine;
+41
View File
@@ -0,0 +1,41 @@
using namespace metal;
struct Vertex
{
float4 position [[ position ]];
float2 texCoords;
};
struct CscParams
{
float3 matrix[3];
float3 offsets;
};
constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::linear);
vertex Vertex vs_draw(constant Vertex *vertices [[ buffer(0) ]], uint id [[ vertex_id ]])
{
return vertices[id];
}
fragment float4 ps_draw_biplanar(Vertex v [[ stage_in ]],
constant CscParams &cscParams [[ buffer(0) ]],
texture2d<float> luminancePlane [[ texture(0) ]],
texture2d<float> chrominancePlane [[ texture(1) ]])
{
float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r, chrominancePlane.sample(s, v.texCoords).rg);
yuv -= cscParams.offsets;
float3 rgb;
rgb.r = dot(yuv, cscParams.matrix[0]);
rgb.g = dot(yuv, cscParams.matrix[1]);
rgb.b = dot(yuv, cscParams.matrix[2]);
return float4(rgb, 1.0f);
}
fragment float4 ps_draw_rgb(Vertex v [[ stage_in ]],
texture2d<float> rgbTexture [[ texture(0) ]])
{
return rgbTexture.sample(s, v.texCoords);
}
+58 -45
View File
@@ -64,6 +64,48 @@ IAudioRenderer* Session::createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATI
return nullptr;
}
bool Session::initializeAudioRenderer()
{
int error;
SDL_assert(m_OriginalAudioConfig.channelCount > 0);
SDL_assert(m_AudioRenderer == nullptr);
SDL_assert(m_OpusDecoder == nullptr);
m_AudioRenderer = createAudioRenderer(&m_OriginalAudioConfig);
// We may be unable to create an audio renderer right now
if (m_AudioRenderer == nullptr) {
return false;
}
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
m_ActiveAudioConfig = m_OriginalAudioConfig;
m_AudioRenderer->remapChannels(&m_ActiveAudioConfig);
// Create the Opus decoder with the renderer's preferred channel mapping
m_OpusDecoder =
opus_multistream_decoder_create(m_ActiveAudioConfig.sampleRate,
m_ActiveAudioConfig.channelCount,
m_ActiveAudioConfig.streams,
m_ActiveAudioConfig.coupledStreams,
m_ActiveAudioConfig.mapping,
&error);
if (m_OpusDecoder == nullptr) {
delete m_AudioRenderer;
m_AudioRenderer = nullptr;
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create decoder: %d",
error);
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio stream has %d channels",
m_ActiveAudioConfig.channelCount);
return true;
}
int Session::getAudioRendererCapabilities(int audioConfiguration)
{
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
@@ -108,39 +150,8 @@ int Session::arInit(int /* audioConfiguration */,
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
void* /* arContext */, int /* arFlags */)
{
int error;
SDL_memcpy(&s_ActiveSession->m_AudioConfig, opusConfig, sizeof(*opusConfig));
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
if (s_ActiveSession->m_AudioRenderer == nullptr) {
return -2;
}
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
s_ActiveSession->m_AudioRenderer->remapChannels(&s_ActiveSession->m_AudioConfig);
// Create the Opus decoder with the renderer's preferred channel mapping
s_ActiveSession->m_OpusDecoder =
opus_multistream_decoder_create(s_ActiveSession->m_AudioConfig.sampleRate,
s_ActiveSession->m_AudioConfig.channelCount,
s_ActiveSession->m_AudioConfig.streams,
s_ActiveSession->m_AudioConfig.coupledStreams,
s_ActiveSession->m_AudioConfig.mapping,
&error);
if (s_ActiveSession->m_OpusDecoder == NULL) {
delete s_ActiveSession->m_AudioRenderer;
s_ActiveSession->m_AudioRenderer = nullptr;
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create decoder: %d",
error);
return -1;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio stream has %d channels",
s_ActiveSession->m_AudioConfig.channelCount);
SDL_memcpy(&s_ActiveSession->m_OriginalAudioConfig, opusConfig, sizeof(*opusConfig));
s_ActiveSession->initializeAudioRenderer();
return 0;
}
@@ -194,7 +205,7 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
}
if (s_ActiveSession->m_AudioRenderer != nullptr) {
int desiredSize = sizeof(short) * s_ActiveSession->m_AudioConfig.samplesPerFrame * s_ActiveSession->m_AudioConfig.channelCount;
int desiredSize = sizeof(short) * s_ActiveSession->m_ActiveAudioConfig.samplesPerFrame * s_ActiveSession->m_ActiveAudioConfig.channelCount;
void* buffer = s_ActiveSession->m_AudioRenderer->getAudioBuffer(&desiredSize);
if (buffer == nullptr) {
return;
@@ -204,13 +215,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
(unsigned char*)sampleData,
sampleLength,
(short*)buffer,
desiredSize / sizeof(short) / s_ActiveSession->m_AudioConfig.channelCount,
desiredSize / sizeof(short) / s_ActiveSession->m_ActiveAudioConfig.channelCount,
0);
// Update desiredSize with the number of bytes actually populated by the decoding operation
if (samplesDecoded > 0) {
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount));
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount;
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount));
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount;
}
else {
desiredSize = 0;
@@ -220,6 +231,9 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Reinitializing audio renderer after failure");
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
s_ActiveSession->m_OpusDecoder = nullptr;
delete s_ActiveSession->m_AudioRenderer;
s_ActiveSession->m_AudioRenderer = nullptr;
}
@@ -234,14 +248,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
// to drop samples to account for the time we've spent blocking audio rendering
// so we return to real-time playback and don't accumulate latency.
Uint32 audioReinitStartTime = SDL_GetTicks();
if (s_ActiveSession->initializeAudioRenderer()) {
Uint32 audioReinitStopTime = SDL_GetTicks();
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
Uint32 audioReinitStopTime = SDL_GetTicks();
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio reinitialization took %d ms - starting drop window",
audioReinitStopTime - audioReinitStartTime);
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio reinitialization took %d ms - starting drop window",
audioReinitStopTime - audioReinitStartTime);
}
}
}
+14 -8
View File
@@ -100,12 +100,6 @@ void* SdlAudioRenderer::getAudioBuffer(int*)
bool SdlAudioRenderer::submitAudio(int bytesWritten)
{
// Our device may enter a permanent error status upon removal, so we need
// to recreate the audio device to pick up the new default audio device.
if (SDL_GetAudioDeviceStatus(m_AudioDevice) == SDL_AUDIO_STOPPED) {
return false;
}
if (bytesWritten == 0) {
// Nothing to do
return true;
@@ -118,8 +112,20 @@ bool SdlAudioRenderer::submitAudio(int bytesWritten)
}
// Provide backpressure on the queue to ensure too many frames don't build up
// in SDL's audio queue.
while (SDL_GetQueuedAudioSize(m_AudioDevice) / m_FrameSize > 10) {
// in SDL's audio queue, but don't wait forever to avoid a deadlock if the
// audio device fails.
for (int i = 0; i < 100; i++) {
// Our device may enter a permanent error status upon removal, so we need
// to recreate the audio device to pick up the new default audio device.
if (SDL_GetAudioDeviceStatus(m_AudioDevice) == SDL_AUDIO_STOPPED) {
return false;
}
// Only queue more samples where there are 10 frames or less in SDL's queue
if (SDL_GetQueuedAudioSize(m_AudioDevice) / m_FrameSize <= 10) {
break;
}
SDL_Delay(1);
}
+35 -8
View File
@@ -112,15 +112,42 @@ void SdlInputHandler::handleAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
pointerId = (uint32_t)event->fingerId;
}
// Try to send it as a native touch event, otherwise fall back to our touch emulation
if (LiSendTouchEvent(eventType, pointerId, vidrelx / dst.w, vidrely / dst.h, event->pressure,
0.0f, 0.0f, LI_ROT_UNKNOWN) == LI_ERR_UNSUPPORTED) {
emulateAbsoluteFingerEvent(event);
// Try to send it as a native pen/touch event, otherwise fall back to our touch emulation
if (LiGetHostFeatureFlags() & LI_FF_PEN_TOUCH_EVENTS) {
#if SDL_VERSION_ATLEAST(2, 0, 22)
bool isPen = false;
int numTouchDevices = SDL_GetNumTouchDevices();
for (int i = 0; i < numTouchDevices; i++) {
if (event->touchId == SDL_GetTouchDevice(i)) {
const char* touchName = SDL_GetTouchName(i);
// SDL will report "pen" as the name of pen input devices on Windows.
// https://github.com/libsdl-org/SDL/pull/5926
isPen = touchName && SDL_strcmp(touchName, "pen") == 0;
break;
}
}
if (isPen) {
LiSendPenEvent(eventType, LI_TOOL_TYPE_PEN, 0, vidrelx / dst.w, vidrely / dst.h, event->pressure,
0.0f, 0.0f, LI_ROT_UNKNOWN, LI_TILT_UNKNOWN);
}
else
#endif
{
LiSendTouchEvent(eventType, pointerId, vidrelx / dst.w, vidrely / dst.h, event->pressure,
0.0f, 0.0f, LI_ROT_UNKNOWN);
}
if (!m_DisabledTouchFeedback) {
// Disable touch feedback when passing touch natively
disableTouchFeedback();
m_DisabledTouchFeedback = true;
}
}
else if (!m_DisabledTouchFeedback) {
// Disable touch feedback when passing touch natively
disableTouchFeedback();
m_DisabledTouchFeedback = true;
else {
emulateAbsoluteFingerEvent(event);
}
}
+46 -9
View File
@@ -68,15 +68,48 @@ void SdlInputHandler::sendGamepadState(GamepadState* state)
}
}
unsigned char lt = state->lt;
unsigned char rt = state->rt;
short lsX = state->lsX;
short lsY = state->lsY;
short rsX = state->rsX;
short rsY = state->rsY;
// When in single controller mode, merge all gamepad state together
if (!m_MultiController) {
for (int i = 0; i < MAX_GAMEPADS; i++) {
if (m_GamepadState[i].index == state->index) {
buttons |= m_GamepadState[i].buttons;
if (lt < m_GamepadState[i].lt) {
lt = m_GamepadState[i].lt;
}
if (rt < m_GamepadState[i].rt) {
rt = m_GamepadState[i].rt;
}
// We use abs() here instead of qAbs() for get proper integer promotion to
// correctly handle abs(-32768), which is not representable in a short.
if (abs(lsX) < abs(m_GamepadState[i].lsX) || abs(lsY) < abs(m_GamepadState[i].lsY)) {
lsX = m_GamepadState[i].lsX;
lsY = m_GamepadState[i].lsY;
}
if (abs(rsX) < abs(m_GamepadState[i].rsX) || abs(rsY) < abs(m_GamepadState[i].rsY)) {
rsX = m_GamepadState[i].rsX;
rsY = m_GamepadState[i].rsY;
}
}
}
}
LiSendMultiControllerEvent(state->index,
m_GamepadMask,
buttons,
state->lt,
state->rt,
state->lsX,
state->lsY,
state->rsX,
state->rsY);
lt,
rt,
lsX,
lsY,
rsX,
rsY);
}
void SdlInputHandler::sendGamepadBatteryState(GamepadState* state, SDL_JoystickPowerLevel level)
@@ -124,11 +157,11 @@ Uint32 SdlInputHandler::mouseEmulationTimerCallback(Uint32 interval, void *param
{
auto gamepad = reinterpret_cast<GamepadState*>(param);
short rawX;
short rawY;
int rawX;
int rawY;
// Determine which analog stick is currently receiving the strongest input
if ((uint32_t)qAbs(gamepad->lsX) + qAbs(gamepad->lsY) > (uint32_t)qAbs(gamepad->rsX) + qAbs(gamepad->rsY)) {
if (abs(gamepad->lsX) + abs(gamepad->lsY) > abs(gamepad->rsX) + abs(gamepad->rsY)) {
rawX = gamepad->lsX;
rawY = -gamepad->lsY;
}
@@ -922,6 +955,10 @@ QString SdlInputHandler::getUnmappedGamepads()
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
// Flush stale events so they aren't processed by the main session event loop
SDL_FlushEvents(SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED);
SDL_FlushEvents(SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMAPPED);
return ret;
}
-8
View File
@@ -42,14 +42,6 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
// Allow gamepad input when the app doesn't have focus if requested
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, prefs.backgroundGamepad ? "1" : "0");
// If absolute mouse mode is enabled, use relative mode warp (which
// is via normal motion events that are influenced by mouse acceleration).
// Otherwise, we'll use raw input capture which is straight from the device
// without modification by the OS.
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
prefs.absoluteMouseMode ? "1" : "0",
SDL_HINT_OVERRIDE);
#if !SDL_VERSION_ATLEAST(2, 0, 15)
// For older versions of SDL (2.0.14 and earlier), use SDL_HINT_GRAB_KEYBOARD
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
+1 -1
View File
@@ -114,7 +114,7 @@ void SdlInputHandler::performSpecialKeyCombo(KeyCombo combo)
}
// Send this text to the PC
LiSendUtf8TextEvent(text, strlen(text));
LiSendUtf8TextEvent(text, (unsigned int)strlen(text));
// SDL_GetClipboardText() allocates, so we must free
SDL_free((void*)text);
+23 -8
View File
@@ -79,6 +79,24 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
return;
}
// Batch all pending mouse motion events to save CPU time
Sint32 x = event->x, y = event->y, xrel = event->xrel, yrel = event->yrel;
SDL_Event nextEvent;
while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
event = &nextEvent.motion;
// Ignore synthetic mouse events
if (event->which != SDL_TOUCH_MOUSEID) {
x = event->x;
y = event->y;
xrel += event->xrel;
yrel += event->yrel;
}
}
// We should not reference the original event anymore
event = nullptr;
if (m_AbsoluteMouseMode) {
int windowWidth, windowHeight;
SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight);
@@ -97,14 +115,11 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
// Use the stream and window sizes to determine the video region
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
mouseInVideoRegion = isMouseInVideoRegion(event->x,
event->y,
windowWidth,
windowHeight);
mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight);
// Clamp motion to the video region
short x = qMin(qMax(event->x - dst.x, 0), dst.w);
short y = qMin(qMax(event->y - dst.y, 0), dst.h);
x = qMin(qMax(x - dst.x, 0), dst.w);
y = qMin(qMax(y - dst.y, 0), dst.h);
// Send the mouse position update if one of the following is true:
// a) it is in the video region now
@@ -119,7 +134,7 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
}
}
if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
LiSendMousePositionEvent(x, y, dst.w, dst.h);
LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h);
}
// Adjust the cursor visibility if applicable
@@ -135,7 +150,7 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
m_MouseWasInVideoRegion = mouseInVideoRegion;
}
else {
LiSendMouseMoveEvent(event->xrel, event->yrel);
LiSendMouseMoveEvent(xrel, yrel);
}
}
+201 -61
View File
@@ -52,6 +52,7 @@
#include <QGuiApplication>
#include <QCursor>
#include <QWindow>
#include <QScreen>
#define CONN_TEST_SERVER "qt.conntest.moonlight-stream.org"
@@ -132,7 +133,11 @@ void Session::clConnectionTerminated(int errorCode)
default:
s_ActiveSession->m_UnexpectedTermination = true;
emit s_ActiveSession->displayLaunchError(tr("Connection terminated"));
// We'll assume large errors are hex values
bool hexError = qAbs(errorCode) > 1000;
emit s_ActiveSession->displayLaunchError(tr("Connection terminated") + "\n\n" +
tr("Error code: %1").arg(errorCode, hexError ? 8 : 0, hexError ? 16 : 10, QChar('0')));
break;
}
@@ -536,17 +541,15 @@ bool Session::populateDecoderProperties(SDL_Window* window)
}
Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *preferences)
: m_Preferences(preferences ? preferences : new StreamingPreferences(this)),
: m_Preferences(preferences ? preferences : StreamingPreferences::get()),
m_IsFullScreen(m_Preferences->windowMode != StreamingPreferences::WM_WINDOWED || !WMUtils::isRunningDesktopEnvironment()),
m_Computer(computer),
m_App(app),
m_Window(nullptr),
m_VideoDecoder(nullptr),
m_DecoderLock(0),
m_AudioDisabled(false),
m_AudioMuted(false),
m_DisplayOriginX(0),
m_DisplayOriginY(0),
m_QtWindow(nullptr),
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
m_InputHandler(nullptr),
m_MouseEmulationRefCount(0),
@@ -562,6 +565,40 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
bool Session::initialize()
{
#ifdef Q_OS_DARWIN
// If we have a notch and the user specified one of the two native display modes
// (notched or notchless), override the fullscreen mode to ensure it works as expected.
// - SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=0 will place the video underneath the notch
// - SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=1 will place the video below the notch
bool shouldUseFullScreenSpaces = true;
SDL_DisplayMode desktopMode;
SDL_Rect safeArea;
for (int displayIndex = 0; StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea); displayIndex++) {
// Check if this display has a notch (safeArea != desktopMode)
if (desktopMode.h != safeArea.h || desktopMode.w != safeArea.w) {
// Check if we're trying to stream at the full native resolution (including notch)
if (m_Preferences->width == desktopMode.w && m_Preferences->height == desktopMode.h) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Overriding default fullscreen mode for native fullscreen resolution");
shouldUseFullScreenSpaces = false;
break;
}
}
}
// Using modesetting on modern versions of macOS is extremely unreliable
// and leads to hangs, deadlocks, and other nasty stuff. The only time
// people seem to use it is to get the full screen on notched Macs,
// which setting SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=1 also accomplishes
// with much less headache.
//
// https://github.com/moonlight-stream/moonlight-qt/issues/973
// https://github.com/moonlight-stream/moonlight-qt/issues/999
// https://github.com/moonlight-stream/moonlight-qt/issues/1211
// https://github.com/moonlight-stream/moonlight-qt/issues/1218
SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, shouldUseFullScreenSpaces ? "1" : "0");
#endif
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
@@ -598,13 +635,18 @@ bool Session::initialize()
m_StreamConfig.height = m_Preferences->height;
m_StreamConfig.fps = m_Preferences->fps;
m_StreamConfig.bitrate = m_Preferences->bitrateKbps;
m_StreamConfig.hevcBitratePercentageMultiplier = 75;
m_StreamConfig.av1BitratePercentageMultiplier = 75;
#ifndef STEAM_LINK
// Enable audio encryption as long as we're not on Steam Link.
// That hardware can hardly handle Opus decoding at all.
m_StreamConfig.encryptionFlags = ENCFLG_AUDIO;
// Opt-in to all encryption features if we detect that the platform
// has AES cryptography acceleration instructions and more than 2 cores.
if (StreamUtils::hasFastAes() && SDL_GetCPUCount() > 2) {
m_StreamConfig.encryptionFlags = ENCFLG_ALL;
}
else {
// Enable audio encryption as long as we're not on Steam Link.
// That hardware can hardly handle Opus decoding at all.
m_StreamConfig.encryptionFlags = ENCFLG_AUDIO;
}
#endif
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
@@ -718,6 +760,15 @@ bool Session::initialize()
if (m_Preferences->enableHdr) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
}
// We'll try to fall back to HEVC first if AV1 fails. We'd rather not fall back
// straight to H.264 if the user asked for AV1 and the host doesn't support it.
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN8) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265;
}
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
}
break;
}
@@ -732,7 +783,12 @@ bool Session::initialize()
}
// Fall-through
case StreamingPreferences::WM_FULLSCREEN:
#ifdef Q_OS_DARWIN
// Don't use "real" fullscreen on macOS. See comments above.
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN_DESKTOP;
#else
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
#endif
break;
}
@@ -814,21 +870,13 @@ bool Session::validateLaunch(SDL_Window* testWindow)
emitLaunchWarning(tr("Your host software or GPU doesn't support encoding AV1."));
}
// We'll try to fall back to HEVC first if AV1 fails. We'd rather not fall back
// straight to H.264 if the user asked for AV1 and the host doesn't support it.
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN8) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265;
}
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
}
// Moonlight-common-c will handle this case already, but we want
// to set this explicitly here so we can do our hardware acceleration
// check below.
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_AV1;
}
else if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
else if (!m_Preferences->enableHdr && // HDR is checked below
m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
@@ -851,7 +899,8 @@ bool Session::validateLaunch(SDL_Window* testWindow)
// check below.
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_H265;
}
else if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
else if (!m_Preferences->enableHdr && // HDR is checked below
m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
@@ -962,8 +1011,7 @@ bool Session::validateLaunch(SDL_Window* testWindow)
}
// If nothing worked, warn the user that audio will not work
m_AudioDisabled = !audioTestPassed;
if (m_AudioDisabled) {
if (!audioTestPassed) {
emitLaunchWarning(tr("Failed to open audio device. Audio will be unavailable during this session."));
}
@@ -1086,26 +1134,38 @@ void Session::getWindowDimensions(int& x, int& y,
// Create our window on the same display that Qt's UI
// was being displayed on.
else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Qt UI screen is at (%d,%d)",
m_DisplayOriginX, m_DisplayOriginY);
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_Rect displayBounds;
Q_ASSERT(m_QtWindow != nullptr);
if (m_QtWindow != nullptr) {
QScreen* screen = m_QtWindow->screen();
if (screen != nullptr) {
QRect displayRect = screen->geometry();
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
if (displayBounds.x == m_DisplayOriginX &&
displayBounds.y == m_DisplayOriginY) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"SDL found matching display %d",
i);
displayIndex = i;
break;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Qt UI screen is at (%d,%d)",
displayRect.x(), displayRect.y());
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_Rect displayBounds;
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
if (displayBounds.x == displayRect.x() &&
displayBounds.y == displayRect.y()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"SDL found matching display %d",
i);
displayIndex = i;
break;
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
}
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
"Qt window is not associated with a QScreen!");
}
}
}
@@ -1159,7 +1219,8 @@ void Session::updateOptimalWindowDisplayMode()
// If this doesn't fit the selected resolution, use the native
// resolution of the panel (unscaled).
if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) {
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) {
SDL_Rect safeArea;
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
return;
}
}
@@ -1424,8 +1485,7 @@ bool Session::startConnectionAsync()
}
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
&m_VideoCallbacks,
m_AudioDisabled ? nullptr : &m_AudioCallbacks,
&m_VideoCallbacks, &m_AudioCallbacks,
NULL, 0, NULL, 0);
if (err != 0) {
// We already displayed an error dialog in the stage failure
@@ -1472,10 +1532,9 @@ public:
Session* m_Session;
};
void Session::exec(int displayOriginX, int displayOriginY)
void Session::exec(QWindow* qtWindow)
{
m_DisplayOriginX = displayOriginX;
m_DisplayOriginY = displayOriginY;
m_QtWindow = qtWindow;
// Use a separate thread for the streaming session on X11 or Wayland
// to ensure we don't stomp on Qt's GL context. This breaks when using
@@ -1579,16 +1638,26 @@ void Session::execInternal()
// We always want a resizable window with High DPI enabled
Uint32 defaultWindowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
// If we're starting in windowed mode and the Moonlight GUI is maximized,
// match that with the streaming window.
if (!m_IsFullScreen) {
QWindowList windows = QGuiApplication::topLevelWindows();
for (const QWindow* window : windows) {
if (window->windowState() & Qt::WindowMaximized) {
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
break;
}
// If we're starting in windowed mode and the Moonlight GUI is maximized or
// minimized, match that with the streaming window.
if (!m_IsFullScreen && m_QtWindow != nullptr) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// Qt 5.10+ can propagate multiple states together
if (m_QtWindow->windowStates() & Qt::WindowMaximized) {
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
}
if (m_QtWindow->windowStates() & Qt::WindowMinimized) {
defaultWindowFlags |= SDL_WINDOW_MINIMIZED;
}
#else
// Qt 5.9 only supports a single state at a time
if (m_QtWindow->windowState() == Qt::WindowMaximized) {
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
}
else if (m_QtWindow->windowState() == Qt::WindowMinimized) {
defaultWindowFlags |= SDL_WINDOW_MINIMIZED;
}
#endif
}
// We use only the computer name on macOS to match Apple conventions where the
@@ -1631,16 +1700,13 @@ void Session::execInternal()
// HACK: Remove once proper Dark Mode support lands in SDL
#ifdef Q_OS_WIN32
{
BOOL darkModeEnabled = FALSE;
if (m_QtWindow != nullptr) {
BOOL darkModeEnabled;
// Query whether dark mode is enabled for our Qt window (which tracks the OS dark mode state)
QWindowList windows = QGuiApplication::topLevelWindows();
for (const QWindow* window : windows) {
if (SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) ||
SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
break;
}
if (FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) &&
FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
darkModeEnabled = FALSE;
}
SDL_SysWMinfo info;
@@ -1772,6 +1838,9 @@ void Session::execInternal()
// Start rich presence to indicate we're in game
RichPresenceManager presence(*m_Preferences, m_App.name);
// Toggle the stats overlay if requested by the user
m_OverlayManager.setOverlayState(Overlay::OverlayDebug, m_Preferences->showPerformanceOverlay);
// Hijack this thread to be the SDL main thread. We have to do this
// because we want to suspend all Qt processing until the stream is over.
SDL_Event event;
@@ -1915,6 +1984,52 @@ void Session::execInternal()
break;
}
// Allow the renderer to handle the state change without being recreated
if (m_VideoDecoder) {
bool forceRecreation = false;
WINDOW_STATE_CHANGE_INFO windowChangeInfo = {};
windowChangeInfo.window = m_Window;
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_SIZE;
windowChangeInfo.width = event.window.data1;
windowChangeInfo.height = event.window.data2;
}
int newDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
if (newDisplayIndex != currentDisplayIndex) {
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_DISPLAY;
windowChangeInfo.displayIndex = newDisplayIndex;
// If the refresh rates have changed, we will need to go through the full
// decoder recreation path to ensure Pacer is switched to the new display
// and that we apply any V-Sync disablement rules that may be needed for
// this display.
SDL_DisplayMode oldMode, newMode;
if (SDL_GetCurrentDisplayMode(currentDisplayIndex, &oldMode) < 0 ||
SDL_GetCurrentDisplayMode(newDisplayIndex, &newMode) < 0 ||
oldMode.refresh_rate != newMode.refresh_rate) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Forcing renderer recreation due to refresh rate change between displays");
forceRecreation = true;
}
}
if (!forceRecreation && m_VideoDecoder->notifyWindowChanged(&windowChangeInfo)) {
// Update the window display mode based on our current monitor
// NB: Avoid a useless modeset by only doing this if it changed.
if (newDisplayIndex != currentDisplayIndex) {
currentDisplayIndex = newDisplayIndex;
updateOptimalWindowDisplayMode();
}
break;
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer for window event: %d (%d %d)",
event.window.event,
@@ -2087,6 +2202,31 @@ DispatchDeferredCleanup:
m_VideoDecoder = nullptr;
SDL_AtomicUnlock(&m_DecoderLock);
// Propagate state changes from the SDL window back to the Qt window
//
// NB: We're making a conscious decision not to propagate the maximized
// or normal state of the window here. The thinking is that users may
// routinely maximize the streaming window simply to view the stream
// in a larger window, but they don't necessarily want the UI in such
// a large window.
if (!m_IsFullScreen && m_QtWindow != nullptr && m_Window != nullptr) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
m_QtWindow->setWindowStates(m_QtWindow->windowStates() | Qt::WindowMinimized);
}
else if (m_QtWindow->windowStates() & Qt::WindowMinimized) {
m_QtWindow->setWindowStates(m_QtWindow->windowStates() & ~Qt::WindowMinimized);
}
#else
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
m_QtWindow->setWindowState(Qt::WindowMinimized);
}
else if (m_QtWindow->windowState() & Qt::WindowMinimized) {
m_QtWindow->setWindowState(Qt::WindowNoState);
}
#endif
}
// This must be called after the decoder is deleted, because
// the renderer may want to interact with the window
SDL_DestroyWindow(m_Window);
+7 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <QSemaphore>
#include <QWindow>
#include <Limelight.h>
#include <opus_multistream.h>
@@ -26,7 +27,7 @@ public:
// Use Session::exec() or DeferredSessionCleanupTask instead.
virtual ~Session() {};
Q_INVOKABLE void exec(int displayOriginX, int displayOriginY);
Q_INVOKABLE void exec(QWindow* qtWindow);
static
void getDecoderInfo(SDL_Window* window,
@@ -78,6 +79,8 @@ private:
IAudioRenderer* createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATION opusConfig);
bool initializeAudioRenderer();
bool testAudio(int audioConfiguration);
int getAudioRendererCapabilities(int audioConfiguration);
@@ -166,8 +169,7 @@ private:
bool m_AudioDisabled;
bool m_AudioMuted;
Uint32 m_FullScreenFlag;
int m_DisplayOriginX;
int m_DisplayOriginY;
QWindow* m_QtWindow;
bool m_ThreadedExec;
bool m_UnexpectedTermination;
SdlInputHandler* m_InputHandler;
@@ -184,7 +186,8 @@ private:
OpusMSDecoder* m_OpusDecoder;
IAudioRenderer* m_AudioRenderer;
OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig;
OPUS_MULTISTREAM_CONFIGURATION m_ActiveAudioConfig;
OPUS_MULTISTREAM_CONFIGURATION m_OriginalAudioConfig;
int m_AudioSampleCount;
Uint32 m_DropAudioEndTime;
+157 -12
View File
@@ -6,10 +6,67 @@
#include <ApplicationServices/ApplicationServices.h>
#endif
#ifdef Q_OS_WINDOWS
#include <Windows.h>
#endif
#ifdef Q_OS_LINUX
#include <sys/auxv.h>
#if defined(Q_PROCESSOR_ARM)
#ifndef HWCAP2_AES
#define HWCAP2_AES (1 << 0)
#endif
#elif defined(Q_PROCESSOR_RISCV)
#if __has_include(<sys/hwprobe.h>)
#include <sys/hwprobe.h>
#else
#include <unistd.h>
#if __has_include(<asm/hwprobe.h>)
#include <asm/hwprobe.h>
#include <sys/syscall.h>
#else
#define __NR_riscv_hwprobe 258
struct riscv_hwprobe {
int64_t key;
uint64_t value;
};
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
#endif
// RISC-V Scalar AES [E]ncryption and [D]ecryption
#ifndef RISCV_HWPROBE_EXT_ZKND
#define RISCV_HWPROBE_EXT_ZKND (1 << 11)
#define RISCV_HWPROBE_EXT_ZKNE (1 << 12)
#endif
// RISC-V Vector AES
#ifndef RISCV_HWPROBE_EXT_ZVKNED
#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21)
#endif
static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
size_t cpu_count, unsigned long *cpus,
unsigned int flags)
{
return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags);
}
#endif
#endif
#endif
Uint32 StreamUtils::getPlatformWindowFlags()
{
#ifdef Q_OS_DARWIN
#if defined(Q_OS_DARWIN)
return SDL_WINDOW_METAL;
#elif defined(HAVE_LIBPLACEBO_VULKAN)
// We'll fall back to GL if Vulkan fails
return SDL_WINDOW_VULKAN;
#else
return 0;
#endif
@@ -92,7 +149,51 @@ int StreamUtils::getDisplayRefreshRate(SDL_Window* window)
return mode.refresh_rate;
}
bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
bool StreamUtils::hasFastAes()
{
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if (__has_builtin(__builtin_cpu_supports) || (defined(__GNUC__) && __GNUC__ >= 6)) && defined(Q_PROCESSOR_X86)
return __builtin_cpu_supports("aes");
#elif defined(__BUILTIN_CPU_SUPPORTS__) && defined(Q_PROCESSOR_POWER)
return __builtin_cpu_supports("vcrypto");
#elif defined(Q_OS_WINDOWS) && defined(Q_PROCESSOR_ARM)
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
#elif defined(_MSC_VER) && defined(Q_PROCESSOR_X86)
int regs[4];
__cpuid(regs, 1);
return regs[2] & (1 << 25); // AES-NI
#elif defined(Q_OS_DARWIN)
// Everything that runs Catalina and later has AES-NI or ARMv8 crypto instructions
return true;
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM) && QT_POINTER_SIZE == 4
return getauxval(AT_HWCAP2) & HWCAP2_AES;
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM) && QT_POINTER_SIZE == 8
return getauxval(AT_HWCAP) & HWCAP_AES;
#elif defined(Q_PROCESSOR_RISCV)
riscv_hwprobe pairs[1] = {
{ RISCV_HWPROBE_KEY_IMA_EXT_0, 0 },
};
// If this syscall is not implemented, we'll get -ENOSYS
// and the value field will remain zero.
__riscv_hwprobe(pairs, SDL_arraysize(pairs), 0, nullptr, 0);
return (pairs[0].value & (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND)) ==
(RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND) ||
(pairs[0].value & RISCV_HWPROBE_EXT_ZVKNED);
#elif QT_POINTER_SIZE == 4
#warning Unknown 32-bit platform. Assuming AES is slow on this CPU.
return false;
#else
#warning Unknown 64-bit platform. Assuming AES is fast on this CPU.
return true;
#endif
}
bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea)
{
#ifdef Q_OS_DARWIN
#define MAX_DISPLAYS 16
@@ -100,9 +201,6 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
uint32_t displayCount = 0;
CGGetActiveDisplayList(MAX_DISPLAYS, displayIds, &displayCount);
if (displayIndex >= displayCount) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Too many displays: %d vs %d",
displayIndex, displayCount);
return false;
}
@@ -122,20 +220,61 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
break;
}
}
safeArea->x = 0;
safeArea->y = 0;
safeArea->w = mode->w;
safeArea->h = mode->h;
#if TARGET_CPU_ARM64
// Now that we found the native full-screen mode, let's look for one that matches along
// the width but not the height and we'll assume that's the safe area full-screen mode.
//
// There doesn't appear to be a CG API or flag that will tell us that a given mode
// is a "safe area" mode, so we have to use our own (brittle) heuristics. :(
//
// To avoid potential false positives, let's avoid checking for external displays, since
// we might have scenarios like a 1920x1200 display with an alternate 1920x1080 mode
// which would falsely trigger our notch detection here.
if (CGDisplayIsBuiltin(displayIds[displayIndex])) {
for (CFIndex i = 0; i < count; i++) {
auto cgMode = (CGDisplayModeRef)(CFArrayGetValueAtIndex(modeList, i));
auto cgModeWidth = static_cast<int>(CGDisplayModeGetWidth(cgMode));
auto cgModeHeight = static_cast<int>(CGDisplayModeGetHeight(cgMode));
// If the modes differ by more than 100, we'll assume it's not a notch mode
if (mode->w == cgModeWidth && mode->h != cgModeHeight && mode->h <= cgModeHeight + 100) {
safeArea->w = cgModeWidth;
safeArea->h = cgModeHeight;
}
}
}
#endif
CFRelease(modeList);
// Now find the SDL mode that matches the CG native mode
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
SDL_DisplayMode thisMode;
if (SDL_GetDisplayMode(displayIndex, i, &thisMode) == 0) {
if (thisMode.w == mode->w && thisMode.h == mode->h &&
// Special case for probing for notched displays prior to video subsystem initialization
// in Session::initialize() for Darwin only!
if (SDL_WasInit(SDL_INIT_VIDEO)) {
// Now find the SDL mode that matches the CG native mode
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
SDL_DisplayMode thisMode;
if (SDL_GetDisplayMode(displayIndex, i, &thisMode) == 0) {
if (thisMode.w == mode->w && thisMode.h == mode->h &&
thisMode.refresh_rate >= mode->refresh_rate) {
*mode = thisMode;
break;
*mode = thisMode;
break;
}
}
}
}
#else
SDL_assert(SDL_WasInit(SDL_INIT_VIDEO));
if (displayIndex >= SDL_GetNumVideoDisplays()) {
return false;
}
// We need to get the true display resolution without DPI scaling (since we use High DPI).
// Windows returns the real display resolution here, even if DPI scaling is enabled.
// macOS and Wayland report a resolution that includes the DPI scaling factor. Picking
@@ -157,7 +296,13 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
return false;
}
}
safeArea->x = 0;
safeArea->y = 0;
safeArea->w = mode->w;
safeArea->h = mode->h;
#endif
return true;
}
+4 -1
View File
@@ -29,8 +29,11 @@ public:
void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight);
static
bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode);
bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea);
static
int getDisplayRefreshRate(SDL_Window* window);
static
bool hasFastAes();
};
+16
View File
@@ -45,6 +45,21 @@ typedef struct _DECODER_PARAMETERS {
bool testOnly;
} DECODER_PARAMETERS, *PDECODER_PARAMETERS;
#define WINDOW_STATE_CHANGE_SIZE 0x01
#define WINDOW_STATE_CHANGE_DISPLAY 0x02
typedef struct _WINDOW_STATE_CHANGE_INFO {
SDL_Window* window;
uint32_t stateChangeFlags;
// Populated if WINDOW_STATE_CHANGE_SIZE is set
int width;
int height;
// Populated if WINDOW_STATE_CHANGE_DISPLAY is set
int displayIndex;
} WINDOW_STATE_CHANGE_INFO, *PWINDOW_STATE_CHANGE_INFO;
class IVideoDecoder {
public:
virtual ~IVideoDecoder() {}
@@ -59,4 +74,5 @@ public:
virtual int submitDecodeUnit(PDECODE_UNIT du) = 0;
virtual void renderFrameOnMainThread() = 0;
virtual void setHdrMode(bool enabled) = 0;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) = 0;
};
+70 -132
View File
@@ -75,6 +75,8 @@ static_assert(sizeof(CSC_CONST_BUF) % 16 == 0, "Constant buffer sizes must be a
D3D11VARenderer::D3D11VARenderer(int decoderSelectionPass)
: m_DecoderSelectionPass(decoderSelectionPass),
m_DevicesWithFL11Support(0),
m_DevicesWithCodecSupport(0),
m_Factory(nullptr),
m_Device(nullptr),
m_SwapChain(nullptr),
@@ -82,6 +84,7 @@ D3D11VARenderer::D3D11VARenderer(int decoderSelectionPass)
m_RenderTargetView(nullptr),
m_LastColorSpace(-1),
m_LastFullRange(false),
m_LastColorTrc(AVCOL_TRC_UNSPECIFIED),
m_AllowTearing(false),
m_VideoGenericPixelShader(nullptr),
m_VideoBt601LimPixelShader(nullptr),
@@ -90,8 +93,7 @@ D3D11VARenderer::D3D11VARenderer(int decoderSelectionPass)
m_VideoTexture(nullptr),
m_OverlayLock(0),
m_OverlayPixelShader(nullptr),
m_HwDeviceContext(nullptr),
m_HwFramesContext(nullptr)
m_HwDeviceContext(nullptr)
{
RtlZeroMemory(m_OverlayVertexBuffers, sizeof(m_OverlayVertexBuffers));
RtlZeroMemory(m_OverlayTextures, sizeof(m_OverlayTextures));
@@ -137,10 +139,6 @@ D3D11VARenderer::~D3D11VARenderer()
SAFE_COM_RELEASE(m_RenderTargetView);
SAFE_COM_RELEASE(m_SwapChain);
if (m_HwFramesContext != nullptr) {
av_buffer_unref(&m_HwFramesContext);
}
// Force destruction of the swapchain immediately
if (m_DeviceContext != nullptr) {
m_DeviceContext->ClearState();
@@ -164,6 +162,7 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
bool success = false;
IDXGIAdapter1* adapter = nullptr;
DXGI_ADAPTER_DESC1 adapterDesc;
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr;
SDL_assert(m_Device == nullptr);
@@ -213,7 +212,7 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
0,
D3D11_SDK_VERSION,
&m_Device,
nullptr,
&featureLevel,
&m_DeviceContext);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -221,6 +220,11 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
hr);
goto Exit;
}
else if (featureLevel >= D3D_FEATURE_LEVEL_11_0) {
// Remember that we found a non-software D3D11 devices with support for
// feature level 11.0 or later (Fermi, Terascale 2, or Ivy Bridge and later)
m_DevicesWithFL11Support++;
}
if (!checkDecoderSupport(adapter)) {
m_DeviceContext->Release();
@@ -230,6 +234,10 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
goto Exit;
}
else {
// Remember that we found a device with support for decoding this codec
m_DevicesWithCodecSupport++;
}
success = true;
@@ -421,10 +429,6 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
return false;
}
// Surfaces must be 16 pixel aligned for H.264 and 128 pixel aligned for everything else
// https://github.com/FFmpeg/FFmpeg/blob/a234e5cd80224c95a205c1f3e297d8c04a1374c3/libavcodec/dxva2.c#L609-L616
m_TextureAlignment = (params->videoFormat & VIDEO_FORMAT_MASK_H264) ? 16 : 128;
if (!setupRenderingResources()) {
return false;
}
@@ -458,43 +462,9 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
}
}
{
m_HwFramesContext = av_hwframe_ctx_alloc(m_HwDeviceContext);
if (!m_HwFramesContext) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to allocate D3D11VA frame context");
return false;
}
AVHWFramesContext* framesContext = (AVHWFramesContext*)m_HwFramesContext->data;
// We require NV12 or P010 textures for our shader
framesContext->format = AV_PIX_FMT_D3D11;
framesContext->sw_format = (params->videoFormat & VIDEO_FORMAT_MASK_10BIT) ?
AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
framesContext->width = FFALIGN(params->width, m_TextureAlignment);
framesContext->height = FFALIGN(params->height, m_TextureAlignment);
// We can have up to 16 reference frames plus a working surface
framesContext->initial_pool_size = 17;
AVD3D11VAFramesContext* d3d11vaFramesContext = (AVD3D11VAFramesContext*)framesContext->hwctx;
d3d11vaFramesContext->BindFlags = D3D11_BIND_DECODER;
int err = av_hwframe_ctx_init(m_HwFramesContext);
if (err < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to initialize D3D11VA frame context: %d",
err);
return false;
}
// Create our video texture and SRVs
if (!setupVideoTexture()) {
return false;
}
// Create our video texture and SRVs
if (!setupVideoTexture()) {
return false;
}
return true;
@@ -510,87 +480,6 @@ bool D3D11VARenderer::prepareDecoderContext(AVCodecContext* context, AVDictionar
return true;
}
bool D3D11VARenderer::prepareDecoderContextInGetFormat(AVCodecContext *context, AVPixelFormat)
{
// hw_frames_ctx must be initialized in ffGetFormat().
context->hw_frames_ctx = av_buffer_ref(m_HwFramesContext);
return true;
}
void D3D11VARenderer::setHdrMode(bool enabled)
{
HRESULT hr;
// According to MSDN, we need to lock the context even if we're just using DXGI functions
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-render-multi-thread-intro
lockContext(this);
if (enabled) {
DXGI_HDR_METADATA_HDR10 hdr10Metadata;
SS_HDR_METADATA sunshineHdrMetadata;
// Sunshine will have HDR metadata but GFE will not
if (!LiGetHdrMetadata(&sunshineHdrMetadata)) {
RtlZeroMemory(&sunshineHdrMetadata, sizeof(sunshineHdrMetadata));
}
hdr10Metadata.RedPrimary[0] = sunshineHdrMetadata.displayPrimaries[0].x;
hdr10Metadata.RedPrimary[1] = sunshineHdrMetadata.displayPrimaries[0].y;
hdr10Metadata.GreenPrimary[0] = sunshineHdrMetadata.displayPrimaries[1].x;
hdr10Metadata.GreenPrimary[1] = sunshineHdrMetadata.displayPrimaries[1].y;
hdr10Metadata.BluePrimary[0] = sunshineHdrMetadata.displayPrimaries[2].x;
hdr10Metadata.BluePrimary[1] = sunshineHdrMetadata.displayPrimaries[2].y;
hdr10Metadata.WhitePoint[0] = sunshineHdrMetadata.whitePoint.x;
hdr10Metadata.WhitePoint[1] = sunshineHdrMetadata.whitePoint.y;
hdr10Metadata.MaxMasteringLuminance = sunshineHdrMetadata.maxDisplayLuminance;
hdr10Metadata.MinMasteringLuminance = sunshineHdrMetadata.minDisplayLuminance;
hdr10Metadata.MaxContentLightLevel = sunshineHdrMetadata.maxContentLightLevel;
hdr10Metadata.MaxFrameAverageLightLevel = sunshineHdrMetadata.maxFrameAverageLightLevel;
hr = m_SwapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10Metadata), &hdr10Metadata);
if (SUCCEEDED(hr)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Set display HDR mode: enabled");
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to enter HDR mode: %x",
hr);
}
// Switch to Rec 2020 PQ (SMPTE ST 2084) colorspace for HDR10 rendering
hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGISwapChain::SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) failed: %x",
hr);
}
}
else {
// Restore default sRGB colorspace
hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGISwapChain::SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) failed: %x",
hr);
}
hr = m_SwapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0, nullptr);
if (SUCCEEDED(hr)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Set display HDR mode: disabled");
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to exit HDR mode: %x",
hr);
}
}
unlockContext(this);
}
void D3D11VARenderer::renderFrame(AVFrame* frame)
{
// Acquire the context lock for rendering to prevent concurrent
@@ -629,8 +518,33 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
flags = 0;
}
HRESULT hr;
if (frame->color_trc != m_LastColorTrc) {
if (frame->color_trc == AVCOL_TRC_SMPTE2084) {
// Switch to Rec 2020 PQ (SMPTE ST 2084) colorspace for HDR10 rendering
hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGISwapChain::SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) failed: %x",
hr);
}
}
else {
// Restore default sRGB colorspace
hr = m_SwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGISwapChain::SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) failed: %x",
hr);
}
}
m_LastColorTrc = frame->color_trc;
}
// Present according to the decoder parameters
HRESULT hr = m_SwapChain->Present(0, flags);
hr = m_SwapChain->Present(0, flags);
// Release the context lock
unlockContext(this);
@@ -813,7 +727,8 @@ void D3D11VARenderer::notifyOverlayUpdated(Overlay::OverlayType type)
HRESULT hr;
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
if (newSurface == nullptr && Session::get()->getOverlayManager().isOverlayEnabled(type)) {
bool overlayEnabled = Session::get()->getOverlayManager().isOverlayEnabled(type);
if (newSurface == nullptr && overlayEnabled) {
// The overlay is enabled and there is no new surface. Leave the old texture alone.
return;
}
@@ -834,7 +749,7 @@ void D3D11VARenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SAFE_COM_RELEASE(oldVertexBuffer);
// If the overlay is disabled, we're done
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
if (!overlayEnabled) {
SDL_FreeSurface(newSurface);
return;
}
@@ -1094,6 +1009,29 @@ bool D3D11VARenderer::needsTestFrame()
return true;
}
IFFmpegRenderer::InitFailureReason D3D11VARenderer::getInitFailureReason()
{
// In the specific case where we found at least one D3D11 hardware device but none of the
// enumerated devices have support for the specified codec, tell the FFmpeg decoder not to
// bother trying other hwaccels. We don't want to try loading D3D9 if the device doesn't
// even have hardware support for the codec.
//
// NB: We use feature level 11.0 support as a gate here because we want to avoid returning
// this failure reason in cases where we might have an extremely old GPU with support for
// DXVA2 on D3D9 but not D3D11VA on D3D11. I'm unsure if any such drivers/hardware exists,
// but better be safe than sorry.
//
// NB2: We're also assuming that no GPU exists which lacks any D3D11 driver but has drivers
// for non-DX APIs like Vulkan. I believe this is a Windows Logo requirement so it should be
// safe to assume.
if (m_DevicesWithFL11Support != 0 && m_DevicesWithCodecSupport == 0) {
return InitFailureReason::NoHardwareSupport;
}
else {
return InitFailureReason::Unknown;
}
}
void D3D11VARenderer::lockContext(void *lock_ctx)
{
auto me = (D3D11VARenderer*)lock_ctx;
@@ -1,7 +1,6 @@
#pragma once
#include "renderer.h"
#include "pacer/pacer.h"
#include <d3d11_1.h>
#include <dxgi1_5.h>
@@ -17,13 +16,12 @@ public:
virtual ~D3D11VARenderer() override;
virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary**) override;
virtual bool prepareDecoderContextInGetFormat(AVCodecContext* context, AVPixelFormat pixelFormat) override;
virtual void renderFrame(AVFrame* frame) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual void setHdrMode(bool enabled) override;
virtual int getRendererAttributes() override;
virtual int getDecoderCapabilities() override;
virtual bool needsTestFrame() override;
virtual InitFailureReason getInitFailureReason() override;
private:
static void lockContext(void* lock_ctx);
@@ -38,6 +36,8 @@ private:
bool createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound = nullptr);
int m_DecoderSelectionPass;
int m_DevicesWithFL11Support;
int m_DevicesWithCodecSupport;
IDXGIFactory5* m_Factory;
ID3D11Device* m_Device;
@@ -47,11 +47,11 @@ private:
SDL_mutex* m_ContextLock;
DECODER_PARAMETERS m_DecoderParams;
int m_TextureAlignment;
int m_DisplayWidth;
int m_DisplayHeight;
int m_LastColorSpace;
bool m_LastFullRange;
AVColorTransferCharacteristic m_LastColorTrc;
bool m_AllowTearing;
@@ -70,6 +70,5 @@ private:
ID3D11PixelShader* m_OverlayPixelShader;
AVBufferRef* m_HwDeviceContext;
AVBufferRef* m_HwFramesContext;
};
+5 -4
View File
@@ -4,6 +4,7 @@
#endif
#include "drm.h"
#include "string.h"
extern "C" {
#include <libavutil/hwcontext_drm.h>
@@ -11,7 +12,6 @@ extern "C" {
#include <libdrm/drm_fourcc.h>
#include <linux/dma-buf.h>
#include <sys/ioctl.h>
// Special Rockchip type
#ifndef DRM_FORMAT_NA12
@@ -158,7 +158,8 @@ bool DrmRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary**
{
// The out-of-tree LibreELEC patches use this option to control the type of the V4L2
// buffers that we get back. We only support NV12 buffers now.
av_dict_set_int(options, "pixel_format", AV_PIX_FMT_NV12, 0);
if(strstr(context->codec->name, "_v4l2") != NULL)
av_dict_set_int(options, "pixel_format", AV_PIX_FMT_NV12, 0);
// This option controls the pixel format for the h264_omx and hevc_omx decoders
// used by the JH7110 multimedia stack. This decoder gives us software frames,
@@ -844,7 +845,7 @@ bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedF
// Prepare to write to the dumb buffer from the CPU
struct dma_buf_sync sync;
sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE;
ioctl(drmFrame->primeFd, DMA_BUF_IOCTL_SYNC, &sync);
drmIoctl(drmFrame->primeFd, DMA_BUF_IOCTL_SYNC, &sync);
int lastPlaneSize = 0;
for (int i = 0; i < 4; i++) {
@@ -898,7 +899,7 @@ bool DrmRenderer::mapSoftwareFrame(AVFrame *frame, AVDRMFrameDescriptor *mappedF
// End the CPU write to the dumb buffer
sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE;
ioctl(drmFrame->primeFd, DMA_BUF_IOCTL_SYNC, &sync);
drmIoctl(drmFrame->primeFd, DMA_BUF_IOCTL_SYNC, &sync);
}
ret = true;
+8 -52
View File
@@ -21,6 +21,11 @@
DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
DEFINE_GUID(DXVA2_ModeAV1_VLD_Profile0,0xb8be4ccb,0xcf53,0x46ba,0x8d,0x59,0xd6,0xb8,0xa6,0xda,0x5d,0x2a);
// This was incorrectly removed from public headers in FFmpeg 7.0
#ifndef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO
#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2
#endif
#define SAFE_COM_RELEASE(x) if (x) { (x)->Release(); }
typedef struct _VERTEX
@@ -447,13 +452,6 @@ bool DXVA2Renderer::initializeQuirksForAdapter(IDirect3D9Ex* d3d9ex, int adapter
// For other GPUs, we'll avoid populating it as was our previous behavior.
m_DeviceQuirks |= DXVA2_QUIRK_SET_DEST_FORMAT;
}
// Tag this display device if it has a WDDM 2.0+ driver for the decoder selection logic
if (HIWORD(id.DriverVersion.HighPart) >= 20) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected WDDM 2.0 or later display driver");
m_DeviceQuirks |= DXVA2_QUIRK_WDDM_20_PLUS;
}
}
return true;
@@ -568,49 +566,6 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
return false;
}
// If we have a WDDM 2.0 or later display driver, prefer the D3D11VA renderer
// in all of the following cases:
// - Multi-GPU systems
// - Windowed and borderless windowed modes
// - Full-screen exclusive with V-sync off
//
// D3D11VA is better in this case because it can enable tearing in non-FSE
// modes when the user has V-Sync disabled. In non-FSE V-Sync cases, D3D11VA
// provides lower display latency on systems that support Independent Flip
// in windowed mode. When using D3D9, DWM will not promote us to IFlip unless
// we're full-screen (exclusive or not).
//
// We prefer D3D11VA in FSE multi-GPU cases due to a plethora of issues with
// D3D9Ex PresentEx()/D3DPRESENT_DONOTWAIT on hybrid graphics systems (See
// issues #235, #240, #386, and #951 on GitHub). Clearly this codepath is not
// well tested by Microsoft or GPU vendors, so stick to the more common
// D3D11-based renderer which is much more likely to behave.
//
// NB: The reason we only do this for WDDM 2.0 and later is because older
// AMD drivers (such as those for the HD 5570) render garbage when using
// the D3D11VA renderer.
if (m_DecoderSelectionPass == 0 &&
(m_DeviceQuirks & DXVA2_QUIRK_WDDM_20_PLUS)) {
if (!((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN)) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Defaulting to D3D11VA for non-FSE mode");
d3d9ex->Release();
return false;
}
else if (m_DeviceQuirks & DXVA2_QUIRK_MULTI_GPU) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Defaulting to D3D11VA for multi-GPU FSE mode");
d3d9ex->Release();
return false;
}
else if (!enableVsync) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Defaulting to D3D11VA for FSE V-Sync Off mode");
d3d9ex->Release();
return false;
}
}
D3DCAPS9 deviceCaps;
d3d9ex->GetDeviceCaps(adapterIndex, D3DDEVTYPE_HAL, &deviceCaps);
@@ -844,7 +799,8 @@ void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type)
HRESULT hr;
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
if (newSurface == nullptr && Session::get()->getOverlayManager().isOverlayEnabled(type)) {
bool overlayEnabled = Session::get()->getOverlayManager().isOverlayEnabled(type);
if (newSurface == nullptr && overlayEnabled) {
// The overlay is enabled and there is no new surface. Leave the old texture alone.
return;
}
@@ -861,7 +817,7 @@ void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type)
SAFE_COM_RELEASE(oldVertexBuffer);
// If the overlay is disabled, we're done
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
if (!overlayEnabled) {
SDL_FreeSurface(newSurface);
return;
}
+1 -1
View File
@@ -80,7 +80,7 @@ private:
#define DXVA2_QUIRK_NO_VP 0x01
#define DXVA2_QUIRK_SET_DEST_FORMAT 0x02
#define DXVA2_QUIRK_WDDM_20_PLUS 0x04
#define DXVA2_QUIRK_WDDM_20_PLUS 0x04 // Unused
#define DXVA2_QUIRK_MULTI_GPU 0x08
int m_DeviceQuirks;
};

Some files were not shown because too many files have changed in this diff Show More