mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-17 06:01:12 +00:00
Add PoC VsyncSource for Windows using DwmFlush(). Soon to be replaced with D3DKMTWaitForVerticalBlankEvent().
This commit is contained in:
+8
-3
@@ -34,7 +34,7 @@ win32 {
|
|||||||
LIBS += -L$$PWD/../libs/windows/lib/x64
|
LIBS += -L$$PWD/../libs/windows/lib/x64
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib
|
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib dwmapi.lib
|
||||||
}
|
}
|
||||||
macx {
|
macx {
|
||||||
INCLUDEPATH += $$PWD/../libs/mac/include $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers
|
INCLUDEPATH += $$PWD/../libs/mac/include $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers
|
||||||
@@ -159,8 +159,13 @@ config_SLVideo {
|
|||||||
win32 {
|
win32 {
|
||||||
message(DXVA2 renderer selected)
|
message(DXVA2 renderer selected)
|
||||||
|
|
||||||
SOURCES += streaming/video/ffmpeg-renderers/dxva2.cpp
|
SOURCES += \
|
||||||
HEADERS += streaming/video/ffmpeg-renderers/dxva2.h
|
streaming/video/ffmpeg-renderers/dxva2.cpp \
|
||||||
|
streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
streaming/video/ffmpeg-renderers/dxva2.h \
|
||||||
|
streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h
|
||||||
}
|
}
|
||||||
macx {
|
macx {
|
||||||
message(VideoToolbox renderer selected)
|
message(VideoToolbox renderer selected)
|
||||||
|
|||||||
@@ -821,7 +821,7 @@ void Session::exec()
|
|||||||
// Destroy the old decoder
|
// Destroy the old decoder
|
||||||
delete m_VideoDecoder;
|
delete m_VideoDecoder;
|
||||||
|
|
||||||
// Chose a new decoder (hopefully the same one, but possibly
|
// Choose a new decoder (hopefully the same one, but possibly
|
||||||
// not if a GPU was removed or something).
|
// not if a GPU was removed or something).
|
||||||
if (!chooseDecoder(m_Preferences.videoDecoderSelection,
|
if (!chooseDecoder(m_Preferences.videoDecoderSelection,
|
||||||
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
|
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0x
|
|||||||
|
|
||||||
DXVA2Renderer::DXVA2Renderer() :
|
DXVA2Renderer::DXVA2Renderer() :
|
||||||
m_SdlRenderer(nullptr),
|
m_SdlRenderer(nullptr),
|
||||||
|
m_Pacer(this),
|
||||||
m_DecService(nullptr),
|
m_DecService(nullptr),
|
||||||
m_Decoder(nullptr),
|
m_Decoder(nullptr),
|
||||||
m_SurfacesUsed(0),
|
m_SurfacesUsed(0),
|
||||||
@@ -27,6 +28,8 @@ DXVA2Renderer::DXVA2Renderer() :
|
|||||||
|
|
||||||
DXVA2Renderer::~DXVA2Renderer()
|
DXVA2Renderer::~DXVA2Renderer()
|
||||||
{
|
{
|
||||||
|
m_Pacer.drain();
|
||||||
|
|
||||||
SAFE_COM_RELEASE(m_DecService);
|
SAFE_COM_RELEASE(m_DecService);
|
||||||
SAFE_COM_RELEASE(m_Decoder);
|
SAFE_COM_RELEASE(m_Decoder);
|
||||||
SAFE_COM_RELEASE(m_Device);
|
SAFE_COM_RELEASE(m_Device);
|
||||||
@@ -430,12 +433,16 @@ bool DXVA2Renderer::isDecoderBlacklisted()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, int height, int)
|
bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, int height, int maxFps)
|
||||||
{
|
{
|
||||||
m_VideoFormat = videoFormat;
|
m_VideoFormat = videoFormat;
|
||||||
m_VideoWidth = width;
|
m_VideoWidth = width;
|
||||||
m_VideoHeight = height;
|
m_VideoHeight = height;
|
||||||
|
|
||||||
|
if (!m_Pacer.initialize(window, maxFps)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// FFmpeg will be decoding on different threads than the main thread that we're
|
// FFmpeg will be decoding on different threads than the main thread that we're
|
||||||
// currently running on right now. We must set this hint so SDL will pass
|
// currently running on right now. We must set this hint so SDL will pass
|
||||||
// D3DCREATE_MULTITHREADED to IDirect3D9::CreateDevice().
|
// D3DCREATE_MULTITHREADED to IDirect3D9::CreateDevice().
|
||||||
@@ -493,6 +500,11 @@ bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DXVA2Renderer::renderFrame(AVFrame* frame)
|
void DXVA2Renderer::renderFrame(AVFrame* frame)
|
||||||
|
{
|
||||||
|
m_Pacer.submitFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DXVA2Renderer::renderFrameAtVsync(AVFrame *frame)
|
||||||
{
|
{
|
||||||
IDirect3DSurface9* surface = reinterpret_cast<IDirect3DSurface9*>(frame->data[3]);
|
IDirect3DSurface9* surface = reinterpret_cast<IDirect3DSurface9*>(frame->data[3]);
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
@@ -632,7 +644,6 @@ void DXVA2Renderer::renderFrame(AVFrame* frame)
|
|||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"SDL_RenderClear() failed: %s",
|
"SDL_RenderClear() failed: %s",
|
||||||
SDL_GetError());
|
SDL_GetError());
|
||||||
av_frame_free(&frame);
|
|
||||||
|
|
||||||
// We're going to cheat a little bit here. It seems SDL's
|
// We're going to cheat a little bit here. It seems SDL's
|
||||||
// renderer may flake out in scenarios like moving the window
|
// renderer may flake out in scenarios like moving the window
|
||||||
@@ -646,8 +657,6 @@ void DXVA2Renderer::renderFrame(AVFrame* frame)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hr = m_Processor->VideoProcessBlt(m_RenderTarget, &bltParams, &sample, 1, nullptr);
|
hr = m_Processor->VideoProcessBlt(m_RenderTarget, &bltParams, &sample, 1, nullptr);
|
||||||
av_frame_free(&frame);
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"VideoProcessBlt() failed: %x",
|
"VideoProcessBlt() failed: %x",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
|
#include "pacer/pacer.h"
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include <dxva2api.h>
|
#include <dxva2api.h>
|
||||||
@@ -9,7 +10,7 @@ extern "C" {
|
|||||||
#include <libavcodec/dxva2.h>
|
#include <libavcodec/dxva2.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
class DXVA2Renderer : public IFFmpegRenderer
|
class DXVA2Renderer : public IFFmpegRenderer, public IVsyncRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DXVA2Renderer();
|
DXVA2Renderer();
|
||||||
@@ -21,6 +22,7 @@ public:
|
|||||||
int maxFps);
|
int maxFps);
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context);
|
virtual bool prepareDecoderContext(AVCodecContext* context);
|
||||||
virtual void renderFrame(AVFrame* frame);
|
virtual void renderFrame(AVFrame* frame);
|
||||||
|
virtual void renderFrameAtVsync(AVFrame* frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initializeDecoder();
|
bool initializeDecoder();
|
||||||
@@ -44,6 +46,7 @@ private:
|
|||||||
int m_DisplayHeight;
|
int m_DisplayHeight;
|
||||||
|
|
||||||
SDL_Renderer* m_SdlRenderer;
|
SDL_Renderer* m_SdlRenderer;
|
||||||
|
Pacer m_Pacer;
|
||||||
|
|
||||||
struct dxva_context m_DXVAContext;
|
struct dxva_context m_DXVAContext;
|
||||||
IDirect3DSurface9* m_DecSurfaces[19];
|
IDirect3DSurface9* m_DecSurfaces[19];
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "dxvsyncsource.h"
|
||||||
|
|
||||||
|
#include <dwmapi.h>
|
||||||
|
|
||||||
|
DxVsyncSource::DxVsyncSource(Pacer* pacer) :
|
||||||
|
m_Pacer(pacer),
|
||||||
|
m_Thread(nullptr)
|
||||||
|
{
|
||||||
|
SDL_AtomicSet(&m_Stopping, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DxVsyncSource::~DxVsyncSource()
|
||||||
|
{
|
||||||
|
if (m_Thread != nullptr) {
|
||||||
|
SDL_AtomicSet(&m_Stopping, 1);
|
||||||
|
SDL_WaitThread(m_Thread, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxVsyncSource::initialize(SDL_Window* window)
|
||||||
|
{
|
||||||
|
m_Thread = SDL_CreateThread(vsyncThread, "DX Vsync Thread", this);
|
||||||
|
if (m_Thread == nullptr) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Unable to create DX V-sync thread: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DxVsyncSource::vsyncThread(void* context)
|
||||||
|
{
|
||||||
|
DxVsyncSource* me = reinterpret_cast<DxVsyncSource*>(context);
|
||||||
|
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
|
||||||
|
while (SDL_AtomicGet(&me->m_Stopping) == 0) {
|
||||||
|
// FIXME: We should really use D3DKMTWaitForVerticalBlankEvent() instead!
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=467617
|
||||||
|
// https://chromium.googlesource.com/chromium/src.git/+/c564f2fe339b2b2abb0c8773c90c83215670ea71/gpu/ipc/service/gpu_vsync_provider_win.cc
|
||||||
|
DwmFlush();
|
||||||
|
|
||||||
|
me->m_Pacer->vsyncCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pacer.h"
|
||||||
|
|
||||||
|
class DxVsyncSource : public IVsyncSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DxVsyncSource(Pacer* pacer);
|
||||||
|
|
||||||
|
virtual ~DxVsyncSource();
|
||||||
|
|
||||||
|
virtual bool initialize(SDL_Window* window);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int vsyncThread(void* context);
|
||||||
|
|
||||||
|
Pacer* m_Pacer;
|
||||||
|
SDL_Thread* m_Thread;
|
||||||
|
SDL_atomic_t m_Stopping;
|
||||||
|
};
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
#include "displaylinkvsyncsource.h"
|
#include "displaylinkvsyncsource.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#include "dxvsyncsource.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define FRAME_HISTORY_ENTRIES 8
|
#define FRAME_HISTORY_ENTRIES 8
|
||||||
|
|
||||||
Pacer::Pacer(IVsyncRenderer* renderer) :
|
Pacer::Pacer(IVsyncRenderer* renderer) :
|
||||||
@@ -18,9 +22,6 @@ Pacer::Pacer(IVsyncRenderer* renderer) :
|
|||||||
|
|
||||||
Pacer::~Pacer()
|
Pacer::~Pacer()
|
||||||
{
|
{
|
||||||
// Stop V-sync callbacks
|
|
||||||
delete m_VsyncSource;
|
|
||||||
|
|
||||||
drain();
|
drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +106,10 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps)
|
|||||||
"Frame pacing: target %d Hz with %d FPS stream",
|
"Frame pacing: target %d Hz with %d FPS stream",
|
||||||
m_DisplayFps, m_MaxVideoFps);
|
m_DisplayFps, m_MaxVideoFps);
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#if defined(Q_OS_DARWIN)
|
||||||
m_VsyncSource = new DisplayLinkVsyncSource(this);
|
m_VsyncSource = new DisplayLinkVsyncSource(this);
|
||||||
|
#elif defined(Q_OS_WIN32)
|
||||||
|
m_VsyncSource = new DxVsyncSource(this);
|
||||||
#else
|
#else
|
||||||
SDL_assert(false);
|
SDL_assert(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -126,6 +129,10 @@ void Pacer::submitFrame(AVFrame* frame)
|
|||||||
|
|
||||||
void Pacer::drain()
|
void Pacer::drain()
|
||||||
{
|
{
|
||||||
|
// Stop V-sync callbacks
|
||||||
|
delete m_VsyncSource;
|
||||||
|
m_VsyncSource = nullptr;
|
||||||
|
|
||||||
while (!m_FrameQueue.isEmpty()) {
|
while (!m_FrameQueue.isEmpty()) {
|
||||||
AVFrame* frame = m_FrameQueue.dequeue();
|
AVFrame* frame = m_FrameQueue.dequeue();
|
||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
|
|||||||
@@ -96,10 +96,10 @@ void FFmpegVideoDecoder::reset()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avcodec_free_context(&m_VideoDecoderCtx);
|
|
||||||
|
|
||||||
delete m_Renderer;
|
delete m_Renderer;
|
||||||
m_Renderer = nullptr;
|
m_Renderer = nullptr;
|
||||||
|
|
||||||
|
avcodec_free_context(&m_VideoDecoderCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegVideoDecoder::completeInitialization(AVCodec* decoder, int videoFormat, int width, int height, bool testOnly)
|
bool FFmpegVideoDecoder::completeInitialization(AVCodec* decoder, int videoFormat, int width, int height, bool testOnly)
|
||||||
|
|||||||
Reference in New Issue
Block a user