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