mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-11 20:02:28 +00:00
Replace D3DX9 with SDL_ttf for overlay rendering
This commit is contained in:
parent
d58837421f
commit
1e7cb7f13e
5 changed files with 158 additions and 107 deletions
|
@ -34,7 +34,6 @@ You can follow development on our [Discord server](https://moonlight-stream.org/
|
|||
* Windows 7 or later
|
||||
* [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) (Community edition is fine)
|
||||
* Select **MSVC 2019** option during Qt installation. MinGW is not supported.
|
||||
* [DirectX SDK](https://www.microsoft.com/en-us/download/details.aspx?id=6812)
|
||||
* [7-Zip](https://www.7-zip.org/) (only if building installers for non-development PCs)
|
||||
* [WiX Toolset](https://wixtoolset.org/releases/) v3.11 or later (only if building installers for non-development PCs)
|
||||
|
||||
|
|
|
@ -36,16 +36,10 @@ win32 {
|
|||
INCLUDEPATH += $$PWD/../libs/windows/include
|
||||
|
||||
contains(QT_ARCH, i386) {
|
||||
INCLUDEPATH += $$(DXSDK_DIR)/Include
|
||||
LIBS += -L$$PWD/../libs/windows/lib/x86
|
||||
LIBS += -L$$(DXSDK_DIR)/Lib/x86 -ld3dx9
|
||||
DEFINES += HAS_D3DX9
|
||||
}
|
||||
contains(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += $$(DXSDK_DIR)/Include
|
||||
LIBS += -L$$PWD/../libs/windows/lib/x64
|
||||
LIBS += -L$$(DXSDK_DIR)/Lib/x64 -ld3dx9
|
||||
DEFINES += HAS_D3DX9
|
||||
}
|
||||
contains(QT_ARCH, arm64) {
|
||||
LIBS += -L$$PWD/../libs/windows/lib/arm64
|
||||
|
|
|
@ -21,6 +21,12 @@ DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0x
|
|||
|
||||
#define SAFE_COM_RELEASE(x) if (x) { (x)->Release(); }
|
||||
|
||||
typedef struct _VERTEX
|
||||
{
|
||||
float x, y, z, rhw;
|
||||
float tu, tv;
|
||||
} VERTEX, *PVERTEX;
|
||||
|
||||
DXVA2Renderer::DXVA2Renderer() :
|
||||
m_DecService(nullptr),
|
||||
m_Decoder(nullptr),
|
||||
|
@ -31,15 +37,14 @@ DXVA2Renderer::DXVA2Renderer() :
|
|||
m_ProcService(nullptr),
|
||||
m_Processor(nullptr),
|
||||
m_FrameIndex(0),
|
||||
#ifdef HAS_D3DX9
|
||||
m_DebugOverlayFont(nullptr),
|
||||
m_StatusOverlayFont(nullptr),
|
||||
#endif
|
||||
m_BlockingPresent(false)
|
||||
{
|
||||
RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces));
|
||||
RtlZeroMemory(&m_DXVAContext, sizeof(m_DXVAContext));
|
||||
|
||||
RtlZeroMemory(m_OverlaySurfaces, sizeof(m_OverlaySurfaces));
|
||||
RtlZeroMemory(m_OverlayTextures, sizeof(m_OverlayTextures));
|
||||
|
||||
// Use MMCSS scheduling for lower scheduling latency while we're streaming
|
||||
DwmEnableMMCSS(TRUE);
|
||||
}
|
||||
|
@ -55,10 +60,15 @@ DXVA2Renderer::~DXVA2Renderer()
|
|||
SAFE_COM_RELEASE(m_ProcService);
|
||||
SAFE_COM_RELEASE(m_Processor);
|
||||
|
||||
#ifdef HAS_D3DX9
|
||||
SAFE_COM_RELEASE(m_DebugOverlayFont);
|
||||
SAFE_COM_RELEASE(m_StatusOverlayFont);
|
||||
#endif
|
||||
for (int i = 0; i < ARRAYSIZE(m_OverlaySurfaces); i++) {
|
||||
if (m_OverlaySurfaces[i] != nullptr) {
|
||||
SDL_FreeSurface(m_OverlaySurfaces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(m_OverlayTextures); i++) {
|
||||
SAFE_COM_RELEASE(m_OverlayTextures[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) {
|
||||
SAFE_COM_RELEASE(m_DecSurfaces[i]);
|
||||
|
@ -332,6 +342,20 @@ bool DXVA2Renderer::initializeRenderer()
|
|||
}
|
||||
}
|
||||
|
||||
m_Device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
|
||||
m_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
m_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
|
||||
m_Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
m_Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
m_Device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
|
||||
m_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
m_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
m_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
|
||||
m_Device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -749,64 +773,127 @@ bool DXVA2Renderer::initialize(PDECODER_PARAMETERS params)
|
|||
|
||||
void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type)
|
||||
{
|
||||
#ifdef HAS_D3DX9
|
||||
HRESULT hr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Overlay::OverlayDebug:
|
||||
if (m_DebugOverlayFont == nullptr) {
|
||||
hr = D3DXCreateFontA(m_Device,
|
||||
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug),
|
||||
0,
|
||||
FW_HEAVY,
|
||||
1,
|
||||
false,
|
||||
ANSI_CHARSET,
|
||||
OUT_DEFAULT_PRECIS,
|
||||
DEFAULT_QUALITY,
|
||||
DEFAULT_PITCH | FF_DONTCARE,
|
||||
"",
|
||||
&m_DebugOverlayFont);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"D3DXCreateFontA() failed: %x",
|
||||
hr);
|
||||
m_DebugOverlayFont = nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Overlay::OverlayStatusUpdate:
|
||||
if (m_StatusOverlayFont == nullptr) {
|
||||
hr = D3DXCreateFontA(m_Device,
|
||||
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayStatusUpdate),
|
||||
0,
|
||||
FW_HEAVY,
|
||||
1,
|
||||
false,
|
||||
ANSI_CHARSET,
|
||||
OUT_DEFAULT_PRECIS,
|
||||
DEFAULT_QUALITY,
|
||||
DEFAULT_PITCH | FF_DONTCARE,
|
||||
"",
|
||||
&m_StatusOverlayFont);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"D3DXCreateFontA() failed: %x",
|
||||
hr);
|
||||
m_StatusOverlayFont = nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
SDL_assert(false);
|
||||
break;
|
||||
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
|
||||
if (newSurface == nullptr && Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||
// The overlay is enabled and there is no new surface. Leave the old texture alone.
|
||||
return;
|
||||
}
|
||||
|
||||
// We must free the old texture first because it references the memory stored in the old surface.
|
||||
IDirect3DTexture9* oldTexture = (IDirect3DTexture9*)SDL_AtomicSetPtr((void**)&m_OverlayTextures[type], nullptr);
|
||||
SAFE_COM_RELEASE(oldTexture);
|
||||
|
||||
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], nullptr);
|
||||
if (oldSurface != nullptr) {
|
||||
SDL_FreeSurface(oldSurface);
|
||||
}
|
||||
|
||||
// If the overlay is disabled, we're done
|
||||
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a dynamic texture to populate with our pixel data
|
||||
SDL_assert(!SDL_MUSTLOCK(newSurface));
|
||||
IDirect3DTexture9* newTexture = nullptr;
|
||||
hr = m_Device->CreateTexture(newSurface->w,
|
||||
newSurface->h,
|
||||
1,
|
||||
D3DUSAGE_DYNAMIC,
|
||||
D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_DEFAULT,
|
||||
&newTexture,
|
||||
nullptr);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"CreateTexture() failed: %x",
|
||||
hr);
|
||||
return;
|
||||
}
|
||||
|
||||
D3DLOCKED_RECT lockedRect;
|
||||
hr = newTexture->LockRect(0, &lockedRect, nullptr, D3DLOCK_DISCARD);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"LockRect() failed: %x",
|
||||
hr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newSurface->pitch == lockedRect.Pitch) {
|
||||
// If the pitch matches, we can take the fast path and use a single copy to transfer the pixels
|
||||
RtlCopyMemory(lockedRect.pBits, newSurface->pixels, newSurface->pitch * newSurface->h);
|
||||
}
|
||||
else {
|
||||
// If the pitch doesn't match, we'll need to copy each row separately
|
||||
int pitch = SDL_min(newSurface->pitch, lockedRect.Pitch);
|
||||
for (int i = 0; i < newSurface->h; i++) {
|
||||
RtlCopyMemory(((PUCHAR)lockedRect.pBits) + (lockedRect.Pitch * i),
|
||||
((PUCHAR)newSurface->pixels) + (newSurface->pitch * i),
|
||||
pitch);
|
||||
}
|
||||
}
|
||||
|
||||
newTexture->UnlockRect(0);
|
||||
|
||||
SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], newSurface);
|
||||
SDL_AtomicSetPtr((void**)&m_OverlayTextures[type], newTexture);
|
||||
}
|
||||
|
||||
void DXVA2Renderer::renderOverlay(Overlay::OverlayType type)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDirect3DTexture9* overlayTexture = (IDirect3DTexture9*)SDL_AtomicGetPtr((void**)&m_OverlayTextures[type]);
|
||||
SDL_Surface* overlaySurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
|
||||
|
||||
if (overlayTexture != nullptr && overlaySurface != nullptr) {
|
||||
hr = m_Device->SetTexture(0, overlayTexture);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"SetTexture() failed: %x",
|
||||
hr);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_FRect renderRect = {};
|
||||
|
||||
if (type == Overlay::OverlayStatusUpdate) {
|
||||
// Bottom Left
|
||||
renderRect.x = 0;
|
||||
renderRect.y = m_DisplayHeight - overlaySurface->h;
|
||||
}
|
||||
else if (type == Overlay::OverlayDebug) {
|
||||
// Top left
|
||||
renderRect.x = 0;
|
||||
renderRect.y = 0;
|
||||
}
|
||||
|
||||
renderRect.w = overlaySurface->w;
|
||||
renderRect.h = overlaySurface->h;
|
||||
|
||||
VERTEX verts[] =
|
||||
{
|
||||
{renderRect.x, renderRect.y, 0, 1, 0, 0},
|
||||
{renderRect.x, renderRect.y+renderRect.h, 0, 1, 0, 1},
|
||||
{renderRect.x+renderRect.w, renderRect.y+renderRect.h, 0, 1, 1, 1},
|
||||
{renderRect.x+renderRect.w, renderRect.y, 0, 1, 1, 0}
|
||||
};
|
||||
|
||||
hr = m_Device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(VERTEX));
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"DrawPrimitiveUP() failed: %x",
|
||||
hr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(type);
|
||||
#endif
|
||||
}
|
||||
|
||||
int DXVA2Renderer::getDecoderColorspace()
|
||||
|
@ -1011,32 +1098,11 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_D3DX9
|
||||
if (m_DebugOverlayFont != nullptr) {
|
||||
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)) {
|
||||
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayDebug);
|
||||
m_DebugOverlayFont->DrawTextA(nullptr,
|
||||
Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayDebug),
|
||||
-1,
|
||||
&sample.DstRect,
|
||||
DT_LEFT | DT_NOCLIP,
|
||||
D3DCOLOR_ARGB(color.a, color.r, color.g, color.b));
|
||||
}
|
||||
// Render overlays on top of the video stream
|
||||
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
||||
renderOverlay((Overlay::OverlayType)i);
|
||||
}
|
||||
|
||||
if (m_StatusOverlayFont != nullptr) {
|
||||
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayStatusUpdate)) {
|
||||
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayStatusUpdate);
|
||||
m_StatusOverlayFont->DrawTextA(nullptr,
|
||||
Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayStatusUpdate),
|
||||
-1,
|
||||
&sample.DstRect,
|
||||
DT_RIGHT | DT_NOCLIP,
|
||||
D3DCOLOR_ARGB(color.a, color.r, color.g, color.b));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
hr = m_Device->EndScene();
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
#include <d3d9.h>
|
||||
#include <dxva2api.h>
|
||||
|
||||
#ifdef HAS_D3DX9
|
||||
#include <d3dx9.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/dxva2.h>
|
||||
}
|
||||
|
@ -22,7 +18,7 @@ public:
|
|||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||
virtual void renderFrame(AVFrame* frame) override;
|
||||
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
||||
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
|
||||
virtual int getDecoderColorspace() override;
|
||||
|
||||
private:
|
||||
|
@ -31,6 +27,7 @@ private:
|
|||
bool initializeDevice(SDL_Window* window, bool enableVsync);
|
||||
bool isDecoderBlacklisted();
|
||||
bool isDXVideoProcessorAPIBlacklisted();
|
||||
void renderOverlay(Overlay::OverlayType type);
|
||||
|
||||
static
|
||||
AVBufferRef* ffPoolAlloc(void* opaque, int size);
|
||||
|
@ -56,6 +53,9 @@ private:
|
|||
int m_SurfacesUsed;
|
||||
AVBufferPool* m_Pool;
|
||||
|
||||
SDL_Surface* m_OverlaySurfaces[Overlay::OverlayMax];
|
||||
IDirect3DTexture9* m_OverlayTextures[Overlay::OverlayMax];
|
||||
|
||||
IDirect3DDevice9Ex* m_Device;
|
||||
IDirect3DSurface9* m_RenderTarget;
|
||||
IDirectXVideoProcessorService* m_ProcService;
|
||||
|
@ -66,9 +66,5 @@ private:
|
|||
DXVA2_ValueRange m_SaturationRange;
|
||||
DXVA2_VideoDesc m_Desc;
|
||||
REFERENCE_TIME m_FrameIndex;
|
||||
#ifdef HAS_D3DX9
|
||||
LPD3DXFONT m_DebugOverlayFont;
|
||||
LPD3DXFONT m_StatusOverlayFont;
|
||||
#endif
|
||||
bool m_BlockingPresent;
|
||||
};
|
||||
|
|
|
@ -140,10 +140,6 @@ if /I "%ARCH%" NEQ "ARM64" (
|
|||
echo Copying AntiHooking.dll
|
||||
copy %BUILD_FOLDER%\AntiHooking\%BUILD_CONFIG%\AntiHooking.dll %DEPLOY_FOLDER%
|
||||
if !ERRORLEVEL! NEQ 0 goto Error
|
||||
|
||||
echo Copying d3dx9_43.dll from DirectX SDK
|
||||
expand "%DXSDK_DIR%\Redist\Jun2010_d3dx9_43_%ARCH%.cab" -F:d3dx9_43.dll %DEPLOY_FOLDER%
|
||||
if !ERRORLEVEL! NEQ 0 goto Error
|
||||
)
|
||||
|
||||
echo Copying GC mapping list
|
||||
|
|
Loading…
Reference in a new issue