Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<string>MoonlightVibe</string>
<key>LSSupportsGameMode</key>
<true/>
<key>NSApplicationUpdateCycleEnabled</key>
<false/>
<key>CFBundleGetInfoString</key>
<string>Stream games and other applications from another PC</string>
<key>CFBundleIconFile</key>
Expand Down
67 changes: 44 additions & 23 deletions app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 = {};
Expand All @@ -115,7 +119,7 @@ void DxVsyncSource::waitForVsync()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"CreateDC() failed: %d",
GetLastError());
return;
return false;
}

// Open the new adapter
Expand All @@ -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;
}
4 changes: 3 additions & 1 deletion app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
52 changes: 29 additions & 23 deletions app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}
Expand Down Expand Up @@ -249,7 +257,7 @@ void Pacer::handleVsync(int timeUntilNextVsyncMillis)
return;
}

if (m_Stopping) {
if (m_Stopping || m_PacingQueue.isEmpty()) {
m_FrameQueueLock.unlock();
return;
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<AVFrame*>& queue)
Expand Down
3 changes: 2 additions & 1 deletion app/streaming/video/ffmpeg-renderers/pacer/pacer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};

Expand Down
4 changes: 2 additions & 2 deletions h264bitstream/bs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}
Expand Down
Loading