diff --git a/app/Info.plist b/app/Info.plist
index 0665ee200a..a9389538ac 100644
--- a/app/Info.plist
+++ b/app/Info.plist
@@ -8,6 +8,8 @@
MoonlightVibe
LSSupportsGameMode
+ NSApplicationUpdateCycleEnabled
+
CFBundleGetInfoString
Stream games and other applications from another PC
CFBundleIconFile
diff --git a/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp b/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp
index 2d30aadb81..dbfb0a398a 100644
--- a/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp
+++ b/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp
@@ -10,6 +10,8 @@ DxVsyncSource::DxVsyncSource(Pacer* pacer) :
m_LastMonitor(nullptr)
{
SDL_zero(m_WaitForVblankEventParams);
+ m_WaitForVblankEventParams.VidPnSourceId = D3DDDI_ID_UNINITIALIZED;
+ SDL_zero(m_AdapterLuid);
}
DxVsyncSource::~DxVsyncSource()
@@ -72,41 +74,43 @@ bool DxVsyncSource::isAsync()
return false;
}
-void DxVsyncSource::waitForVsync()
+bool DxVsyncSource::waitForVsync()
{
NTSTATUS status;
// If the monitor has changed from last time, open the new adapter
HMONITOR currentMonitor = MonitorFromWindow(m_Window, MONITOR_DEFAULTTONEAREST);
- if (currentMonitor != m_LastMonitor) {
+ if (!currentMonitor || currentMonitor != m_LastMonitor) {
+ // Close the old adapter
+ if (m_WaitForVblankEventParams.hAdapter != 0) {
+ D3DKMT_CLOSEADAPTER closeAdapterParams = {};
+ closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
+ m_D3DKMTCloseAdapter(&closeAdapterParams);
+ m_WaitForVblankEventParams.hAdapter = 0;
+ }
+ m_LastMonitor = nullptr;
+
+ // We can't wait if we're not on any monitor
+ if (!currentMonitor) {
+ return false;
+ }
+
MONITORINFOEXA monitorInfo = {};
monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"GetMonitorInfo() failed: %d",
GetLastError());
- return;
+ return false;
}
- DEVMODEA monitorMode;
+ DEVMODEA monitorMode = {};
monitorMode.dmSize = sizeof(monitorMode);
if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"EnumDisplaySettings() failed: %d",
GetLastError());
- return;
- }
-
- SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
- "Monitor changed: %s %d Hz",
- monitorInfo.szDevice,
- monitorMode.dmDisplayFrequency);
-
- // Close the old adapter
- if (m_WaitForVblankEventParams.hAdapter != 0) {
- D3DKMT_CLOSEADAPTER closeAdapterParams = {};
- closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
- m_D3DKMTCloseAdapter(&closeAdapterParams);
+ return false;
}
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
@@ -115,7 +119,7 @@ void DxVsyncSource::waitForVsync()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"CreateDC() failed: %d",
GetLastError());
- return;
+ return false;
}
// Open the new adapter
@@ -126,21 +130,38 @@ void DxVsyncSource::waitForVsync()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"D3DKMTOpenAdapterFromHdc() failed: %x",
status);
- return;
+ return false;
+ }
+
+ // VidPnSourceId is scoped to the adapter, so we must also check the adapter LUID
+ if (m_AdapterLuid.LowPart != openAdapterParams.AdapterLuid.LowPart ||
+ m_AdapterLuid.HighPart != openAdapterParams.AdapterLuid.HighPart ||
+ m_WaitForVblankEventParams.VidPnSourceId != openAdapterParams.VidPnSourceId) {
+ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
+ "Monitor changed: %s %d Hz",
+ monitorInfo.szDevice,
+ monitorMode.dmDisplayFrequency);
}
m_WaitForVblankEventParams.hAdapter = openAdapterParams.hAdapter;
m_WaitForVblankEventParams.hDevice = 0;
m_WaitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
+ m_AdapterLuid = openAdapterParams.AdapterLuid;
m_LastMonitor = currentMonitor;
}
status = m_D3DKMTWaitForVerticalBlankEvent(&m_WaitForVblankEventParams);
if (status != STATUS_SUCCESS) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
- "D3DKMTWaitForVerticalBlankEvent() failed: %x",
- status);
- return;
+ if (status != STATUS_GRAPHICS_PRESENT_OCCLUDED) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
+ "D3DKMTWaitForVerticalBlankEvent() failed: %x",
+ status);
+ }
+
+ m_LastMonitor = nullptr;
+ return false;
}
+
+ return true;
}
diff --git a/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h b/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h
index f126df152f..836b8da9bb 100644
--- a/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h
+++ b/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h
@@ -10,6 +10,7 @@ typedef UINT D3DKMT_HANDLE;
typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L)
+#define D3DDDI_ID_UNINITIALIZED (UINT)(~0)
typedef struct _D3DKMT_OPENADAPTERFROMHDC {
HDC hDc;
D3DKMT_HANDLE hAdapter;
@@ -39,13 +40,14 @@ class DxVsyncSource : public IVsyncSource
virtual bool isAsync() override;
- virtual void waitForVsync() override;
+ virtual bool waitForVsync() override;
private:
Pacer* m_Pacer;
HMODULE m_Gdi32Handle;
HWND m_Window;
HMONITOR m_LastMonitor;
+ LUID m_AdapterLuid;
D3DKMT_WAITFORVERTICALBLANKEVENT m_WaitForVblankEventParams;
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
diff --git a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
index b2a29a30e9..21dee7a6a0 100644
--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
+++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
@@ -115,13 +115,20 @@ int Pacer::vsyncThread(void *context)
while (!me->m_Stopping) {
if (async) {
// Wait for the VSync source to invoke signalVsync() or 100ms to elapse
- me->m_FrameQueueLock.lock();
+ QMutexLocker locker(&me->m_FrameQueueLock);
me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 100);
- me->m_FrameQueueLock.unlock();
}
else {
// Let the VSync source wait in the context of our thread
- me->m_VsyncSource->waitForVsync();
+ if (!me->m_VsyncSource->waitForVsync()) {
+ // Fall back to a fixed delay if the VSync source fails
+ //
+ // NB: We don't actually expect signalVsync() to be called, since
+ // this is a synchronous VSync source, but we wait on m_VsyncSignalled
+ // anyway so that the destructor can immediately wake us on shutdown.
+ QMutexLocker locker(&me->m_FrameQueueLock);
+ me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 1000 / me->m_DisplayFps);
+ }
}
if (me->m_Stopping) {
@@ -148,23 +155,24 @@ int Pacer::renderThread(void* context)
// Wait for the renderer to be ready for the next frame
me->m_VsyncRenderer->waitToRender();
- // Acquire the frame queue lock to protect the queue and
- // the not empty condition
- me->m_FrameQueueLock.lock();
+ AVFrame* frame;
+ {
+ // Acquire the frame queue lock to protect the queue and
+ // the not empty condition
+ QMutexLocker locker(&me->m_FrameQueueLock);
- // Wait for a frame to be ready to render
- while (!me->m_Stopping && me->m_RenderQueue.isEmpty()) {
- me->m_RenderQueueNotEmpty.wait(&me->m_FrameQueueLock);
- }
+ // Wait for a frame to be ready to render
+ while (!me->m_Stopping && me->m_RenderQueue.isEmpty()) {
+ me->m_RenderQueueNotEmpty.wait(&me->m_FrameQueueLock);
+ }
- if (me->m_Stopping) {
- // Exit this thread
- me->m_FrameQueueLock.unlock();
- break;
- }
+ if (me->m_Stopping) {
+ // Exit this thread
+ break;
+ }
- AVFrame* frame = me->m_RenderQueue.dequeue();
- me->m_FrameQueueLock.unlock();
+ frame = me->m_RenderQueue.dequeue();
+ }
me->renderFrame(frame);
}
@@ -249,7 +257,7 @@ void Pacer::handleVsync(int timeUntilNextVsyncMillis)
return;
}
- if (m_Stopping) {
+ if (m_Stopping || m_PacingQueue.isEmpty()) {
m_FrameQueueLock.unlock();
return;
}
@@ -349,7 +357,7 @@ void Pacer::renderFrame(AVFrame* frame)
av_frame_free(&frame);
// Drop frames if we have too many queued up for a while
- m_FrameQueueLock.lock();
+ QMutexLocker frameQueueLocker(&m_FrameQueueLock);
int frameDropTarget;
@@ -382,13 +390,11 @@ void Pacer::renderFrame(AVFrame* frame)
AVFrame* frame = m_RenderQueue.dequeue();
// Drop the lock while we call av_frame_free()
- m_FrameQueueLock.unlock();
+ frameQueueLocker.unlock();
m_VideoStats->pacerDroppedFrames++;
av_frame_free(&frame);
- m_FrameQueueLock.lock();
+ frameQueueLocker.relock();
}
-
- m_FrameQueueLock.unlock();
}
void Pacer::dropFrameForEnqueue(QQueue& queue)
diff --git a/app/streaming/video/ffmpeg-renderers/pacer/pacer.h b/app/streaming/video/ffmpeg-renderers/pacer/pacer.h
index 51044c55f3..4ec9055318 100644
--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.h
+++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.h
@@ -22,9 +22,10 @@ class IVsyncSource {
// sources require calls to waitForVsync().
virtual bool isAsync() = 0;
- virtual void waitForVsync() {
+ virtual bool waitForVsync() {
// Synchronous sources must implement waitForVsync()!
SDL_assert(false);
+ return false;
}
};
diff --git a/h264bitstream/bs.h b/h264bitstream/bs.h
index fc8fbf4462..fad69b0860 100644
--- a/h264bitstream/bs.h
+++ b/h264bitstream/bs.h
@@ -101,7 +101,7 @@ static inline int bs_eof(bs_t* b) { if (b->p >= b->end) { return 1; } else { ret
static inline int bs_overrun(bs_t* b) { if (b->p > b->end) { return 1; } else { return 0; } }
-static inline int bs_pos(bs_t* b) { if (b->p > b->end) { return (b->end - b->start); } else { return (b->p - b->start); } }
+static inline int bs_pos(bs_t* b) { if (b->p > b->end) { return (int)(b->end - b->start); } else { return (int)(b->p - b->start); } }
static inline uint32_t bs_read_u1(bs_t* b)
{
@@ -217,7 +217,7 @@ static inline void bs_write_u8(bs_t* b, uint32_t v)
#ifdef FAST_U8
if (b->bits_left == 8 && ! bs_eof(b)) // can do fast write
{
- b->p[0] = v;
+ b->p[0] = (uint8_t)v;
b->p++;
return;
}
diff --git a/moonlight-common-c/moonlight-common-c b/moonlight-common-c/moonlight-common-c
index bb274819d6..bd3443c790 160000
--- a/moonlight-common-c/moonlight-common-c
+++ b/moonlight-common-c/moonlight-common-c
@@ -1 +1 @@
-Subproject commit bb274819d682dc1fc3c17711440162067dd23dee
+Subproject commit bd3443c790d07597f927d5a3d1560ca107a3ce22