Compare commits

...
Author SHA1 Message Date
Cameron Gutman b100e674dd HACK: Wait longer before rendering a frame 2026-07-12 22:45:07 -05:00
Cameron Gutman c0c4d60565 Update AppImage libva to 2.24.1 2026-07-08 23:58:15 -05:00
Cameron Gutman 8b18d58aa0 Add workaround for interoperability issue with out-of-tree FFmpeg zero-copy v4l2m2m patches and libplacebo
Fixes #1786
2026-07-08 23:40:08 -05:00
dependabot[bot] 5e1ac6df87 Bump moonlight-common-c/moonlight-common-c from 1f76427 to 2ea4775
Bumps [moonlight-common-c/moonlight-common-c](https://github.com/moonlight-stream/moonlight-common-c) from `1f76427` to `2ea4775`.
- [Commits](https://github.com/moonlight-stream/moonlight-common-c/compare/1f764276e848ae2ec7815ef90d1c1748272e074a...2ea47752c3051d72a64bcca190024e8b354fa1ef)

---
updated-dependencies:
- dependency-name: moonlight-common-c/moonlight-common-c
  dependency-version: 2ea47752c3051d72a64bcca190024e8b354fa1ef
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-07-05 16:52:09 -04:00
Cameron Gutman 0c8970364c Switch to upstream nanors with native SIMD and GFNI runtime dispatching 2026-07-05 00:14:34 -04:00
Cameron Gutman 1516464f82 Modernize ASAN flags for MSVC 2026-07-04 15:37:21 -04:00
Cameron Gutman fd34a1fd28 Disable the text outline when line wrapping is required 2026-07-01 23:43:40 -05:00
Cameron Gutman 9067db8915 Switch to UTF-8 for overlay text rendering 2026-07-01 23:24:31 -05:00
MiraandCameron Gutman 177f845183 Add outline to stream overlay text (#1887)
Co-authored-by: Cameron Gutman <[email protected]>
2026-07-01 23:03:45 -05:00
Cameron Gutman c9b03f80f8 Partially revert "Use GL_NEAREST when possible without degrading quality"
The changes to GL_NEAREST for overlay textures and disabling alpha blending in SDL renderer are left intact.

This reverts commit 0f49dca4c0.
2026-07-01 21:56:54 -05:00
Cameron Gutman a27589fe35 Update SDL to 3.4.12 2026-07-01 21:47:49 -05:00
Cameron Gutman 561d824814 Move HDR toggle into the Basic Settings panel 2026-07-01 21:41:05 -05:00
12 changed files with 139 additions and 79 deletions
+2 -2
View File
@@ -42,7 +42,7 @@ jobs:
uses: actions/checkout@v5
with:
repository: libsdl-org/SDL
ref: a5e02a0927a2dd999718c1903f2d59440319ca99
ref: release-3.4.12
path: deps/SDL
- name: Build SDL3
@@ -86,7 +86,7 @@ jobs:
uses: actions/checkout@v5
with:
repository: intel/libva
ref: 2.23.0
ref: 2.24.1
path: deps/libva
- name: Build libva
+46 -43
View File
@@ -812,38 +812,64 @@ Flickable {
ToolTip.text: qsTr("Fullscreen generally provides the best performance, but borderless windowed may work better with features like macOS Spaces, Alt+Tab, screenshot tools, on-screen overlays, etc.")
}
CheckBox {
id: vsyncCheck
Row {
spacing: 5
width: parent.width
hoverEnabled: true
text: qsTr("V-Sync")
font.pointSize: 12
checked: StreamingPreferences.enableVsync
onCheckedChanged: {
StreamingPreferences.enableVsync = checked
CheckBox {
id: vsyncCheck
hoverEnabled: true
text: qsTr("V-Sync")
font.pointSize: 12
checked: StreamingPreferences.enableVsync
onCheckedChanged: {
StreamingPreferences.enableVsync = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr("Disabling V-Sync allows sub-frame rendering latency, but it can display visible tearing")
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr("Disabling V-Sync allows sub-frame rendering latency, but it can display visible tearing")
CheckBox {
id: framePacingCheck
hoverEnabled: true
text: qsTr("Frame pacing")
font.pointSize: 12
enabled: StreamingPreferences.enableVsync
checked: StreamingPreferences.enableVsync && StreamingPreferences.framePacing
onCheckedChanged: {
StreamingPreferences.framePacing = checked
}
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr("Frame pacing reduces micro-stutter by delaying frames that come in too early")
}
}
CheckBox {
id: framePacingCheck
id: enableHdr
width: parent.width
hoverEnabled: true
text: qsTr("Frame pacing")
font.pointSize: 12
enabled: StreamingPreferences.enableVsync
checked: StreamingPreferences.enableVsync && StreamingPreferences.framePacing
text: qsTr("Enable HDR")
font.pointSize: 12
enabled: SystemProperties.supportsHdr
checked: enabled && StreamingPreferences.enableHdr
onCheckedChanged: {
StreamingPreferences.framePacing = checked
StreamingPreferences.enableHdr = checked
}
// Updating StreamingPreferences.videoCodecConfig is handled above
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: qsTr("Frame pacing reduces micro-stutter by delaying frames that come in too early")
ToolTip.text: enabled ?
qsTr("The stream will be HDR-capable, but some games may require an HDR monitor on your host PC to enable HDR mode.")
:
qsTr("HDR streaming is not supported on this PC.")
}
}
}
@@ -1630,29 +1656,6 @@ Flickable {
}
}
CheckBox {
id: enableHdr
width: parent.width
text: qsTr("Enable HDR")
font.pointSize: 12
enabled: SystemProperties.supportsHdr
checked: enabled && StreamingPreferences.enableHdr
onCheckedChanged: {
StreamingPreferences.enableHdr = checked
}
// Updating StreamingPreferences.videoCodecConfig is handled above
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hovered
ToolTip.text: enabled ?
qsTr("The stream will be HDR-capable, but some games may require an HDR monitor on your host PC to enable HDR mode.")
:
qsTr("HDR streaming is not supported on this PC.")
}
CheckBox {
id: enableYUV444
width: parent.width
+12 -20
View File
@@ -627,6 +627,8 @@ bool EGLRenderer::setupVideoRenderingState() {
glGenTextures(EGL_MAX_PLANES, m_Textures);
for (size_t i = 0; i < EGL_MAX_PLANES; ++i) {
glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_Textures[i]);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
@@ -784,16 +786,6 @@ void EGLRenderer::renderFrame(AVFrame* frame)
}
}
int drawableWidth, drawableHeight;
SDL_GL_GetDrawableSize(m_Window, &drawableWidth, &drawableHeight);
SDL_Rect src, dst;
src.x = src.y = dst.x = dst.y = 0;
src.w = frame->width;
src.h = frame->height;
dst.w = drawableWidth;
dst.h = drawableHeight;
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
ssize_t plane_count = m_Backend->exportEGLImages(frame, m_EGLDisplay, imgs);
if (plane_count < 0)
return;
@@ -801,16 +793,6 @@ void EGLRenderer::renderFrame(AVFrame* frame)
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_Textures[i]);
m_glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, imgs[i]);
// Use GL_NEAREST to reduce sampling if the video region is a multiple of the frame size
if (dst.w % frame->width == 0 && dst.h % frame->height == 0) {
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else {
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
// We already called glClear() after last frame's SDL_GL_SwapWindow()
@@ -819,7 +801,17 @@ void EGLRenderer::renderFrame(AVFrame* frame)
glClear(GL_COLOR_BUFFER_BIT);
}
int drawableWidth, drawableHeight;
SDL_GL_GetDrawableSize(m_Window, &drawableWidth, &drawableHeight);
// Set the viewport to the size of the aspect-ratio-scaled video
SDL_Rect src, dst;
src.x = src.y = dst.x = dst.y = 0;
src.w = frame->width;
src.h = frame->height;
dst.w = drawableWidth;
dst.h = drawableHeight;
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
glViewport(dst.x, dst.y, dst.w, dst.h);
glUseProgram(m_ShaderProgram);
@@ -10,6 +10,7 @@
#include <SDL_vulkan.h>
extern "C" {
#include <libavutil/hwcontext_drm.h>
#include <libavutil/hwcontext_vulkan.h>
}
@@ -942,6 +943,8 @@ void PlVkRenderer::waitToRender()
}
endRenderTiming();
#endif
SDL_Delay(10);
}
}
@@ -1146,6 +1149,24 @@ bool PlVkRenderer::testRenderFrame(AVFrame *frame)
}
#endif
if (frame->format == AV_PIX_FMT_DRM_PRIME) {
auto drmFrame = (AVDRMFrameDescriptor*)frame->data[0];
// This can happen with out-of-tree FFmpeg patches if the V4L2
// format lacks a mapping to a DRM format.
if (drmFrame->nb_layers == 0) {
return false;
}
// Current versions of libplacebo only support one plane per layer
// and will assert if provided a frame that violates this constraint.
for (int i = 0; i < drmFrame->nb_layers; i++) {
if (drmFrame->layers[i].nb_planes != 1) {
return false;
}
}
}
// Test if the frame can be mapped to libplacebo
pl_frame mappedFrame;
if (!mapAvFrameToPlacebo(frame, &mappedFrame)) {
@@ -576,11 +576,6 @@ ReadbackRetry:
// Ensure the viewport is set to the desired video region
SDL_RenderSetViewport(m_Renderer, &dst);
// Use nearest pixel sampling if the video region size is a multiple of the frame size
SDL_SetTextureScaleMode(m_Texture,
dst.w % frame->width == 0 && dst.h % frame->height == 0 ?
SDL_ScaleModeNearest : SDL_ScaleModeLinear);
// Draw the video content itself
SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr);
+50 -4
View File
@@ -149,10 +149,12 @@ void OverlayManager::notifyOverlayUpdated(OverlayType type)
(void**)&m_Overlays[type].surface,
m_Overlays[type].enabled ?
// The _Wrapped variant is required for line breaks to work
TTF_RenderText_Blended_Wrapped(m_Overlays[type].font,
m_Overlays[type].text,
m_Overlays[type].color,
1024)
RenderTextOutlinedWrapped(m_Overlays[type].font,
m_Overlays[type].text,
m_Overlays[type].color,
{0, 0, 0, 255},
4,
1024)
: nullptr);
// Notify the renderer
@@ -163,3 +165,47 @@ void OverlayManager::notifyOverlayUpdated(OverlayType type)
SDL_FreeSurface(oldSurface);
}
}
SDL_Surface* OverlayManager::RenderTextOutlinedWrapped(TTF_Font* font, const char* text, SDL_Color textColor, SDL_Color outlineColor, int outlineWidth, int wrapWidth) {
if (text == nullptr || text[0] == '\0') {
return nullptr;
}
int oldOutline = TTF_GetFontOutline(font);
TTF_SetFontOutline(font, outlineWidth);
// Verify that the string won't require wrapping (which could cause the outline and the text
// to diverge due to different wrapping positions).
//
// FIXME: We do this rather than just disabling wrapping entirely (wrapWidth = 0) because we
// need further testing to ensure that all renderers can handle non-NPOT overlay textures.
for (const QString& line : QString(text).split('\n')) {
int extent, count;
if (TTF_MeasureUTF8(font, line.toUtf8(), wrapWidth, &extent, &count) == 0 && count < line.size()) {
// If it requires wrapping, render it without the outline
TTF_SetFontOutline(font, oldOutline);
return TTF_RenderUTF8_Blended_Wrapped(font, text, textColor, wrapWidth);
}
}
// Draw text twice, but outline is a bit bigger
auto outlineSurface = TTF_RenderUTF8_Blended_Wrapped(font, text, outlineColor, wrapWidth);
TTF_SetFontOutline(font, 0);
auto textSurface = TTF_RenderUTF8_Blended_Wrapped(font, text, textColor, wrapWidth);
TTF_SetFontOutline(font, oldOutline);
if (outlineSurface == nullptr || textSurface == nullptr) {
SDL_FreeSurface(outlineSurface);
SDL_FreeSurface(textSurface);
return nullptr;
}
// Merge the texts
SDL_Rect dst = { outlineWidth, outlineWidth, textSurface->w, textSurface->h };
SDL_BlitSurface(textSurface, nullptr, outlineSurface, &dst);
SDL_FreeSurface(textSurface);
return outlineSurface;
}
+1
View File
@@ -41,6 +41,7 @@ public:
private:
void notifyOverlayUpdated(OverlayType type);
SDL_Surface* RenderTextOutlinedWrapped(TTF_Font* font, const char* text, SDL_Color textColor, SDL_Color outlineColor, int outlineWidth, int wrapWidth);
struct {
bool enabled;
+1 -1
View File
@@ -26,7 +26,7 @@ CONFIG(release, debug|release) {
# Enable ASan for Windows
#QMAKE_CFLAGS += -fsanitize=address
#QMAKE_CXXFLAGS += -fsanitize=address
#QMAKE_LFLAGS += -incremental:no -wholearchive:clang_rt.asan_dynamic-x86_64.lib -wholearchive:clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
#QMAKE_LFLAGS += -incremental:no
# Propagate environment variable flags
QMAKE_CFLAGS += $$(CFLAGS)
+3 -1
View File
@@ -50,7 +50,9 @@ SOURCES += \
$$ENET_DIR/protocol.c \
$$ENET_DIR/unix.c \
$$ENET_DIR/win32.c \
$$COMMON_C_DIR/src/rswrapper.c \
$$COMMON_C_DIR/nanors/deps/obl/oblas_common.c \
$$COMMON_C_DIR/nanors/deps/obl/oblas_lite.c \
$$COMMON_C_DIR/nanors/rs.c \
$$COMMON_C_DIR/src/AudioStream.c \
$$COMMON_C_DIR/src/ByteBuffer.c \
$$COMMON_C_DIR/src/Connection.c \
+1 -1
View File
@@ -4,7 +4,7 @@ $Organization = "moonlight-stream"
$PrebuiltRepo = "moonlight-qt-deps"
$TargetDir = Join-Path $PSScriptRoot "libs\windows"
$Assets = @("windows-x64.zip", "windows-ARM64.zip")
$Tag = "v7.2"
$Tag = "v8"
if (Test-Path $TargetDir) {
Write-Host "Cleaning target directory..." -ForegroundColor Cyan
+1 -1
View File
@@ -7,7 +7,7 @@ import shutil
ORGANIZATION = "moonlight-stream"
PREBUILT_REPO = "moonlight-qt-deps"
TAG = "v7.2"
TAG = "v8"
def get_platform_config():
system = platform.system()