Switch DXVA2 to IDirect3D9Ex APIs for more detailed control of rendering

This commit is contained in:
Cameron Gutman 2018-08-19 21:53:39 -07:00
parent 57f2fb07f0
commit 62f765b2b4
4 changed files with 123 additions and 59 deletions

View file

@ -34,7 +34,7 @@ win32 {
LIBS += -L$$PWD/../libs/windows/lib/x64
}
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib gdi32.lib user32.lib
LIBS += ws2_32.lib winmm.lib dxva2.lib ole32.lib gdi32.lib user32.lib d3d9.lib dwmapi.lib
}
macx {
INCLUDEPATH += $$PWD/../libs/mac/include $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers

View file

@ -3,6 +3,9 @@
#include "../ffmpeg.h"
#include <streaming/streamutils.h>
#include <SDL_syswm.h>
#include <dwmapi.h>
#include <Limelight.h>
DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
@ -10,7 +13,6 @@ DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0x
#define SAFE_COM_RELEASE(x) if (x) { (x)->Release(); }
DXVA2Renderer::DXVA2Renderer() :
m_SdlRenderer(nullptr),
m_DecService(nullptr),
m_Decoder(nullptr),
m_SurfacesUsed(0),
@ -41,10 +43,6 @@ DXVA2Renderer::~DXVA2Renderer()
if (m_Pool != nullptr) {
av_buffer_pool_uninit(&m_Pool);
}
if (m_SdlRenderer != nullptr) {
SDL_DestroyRenderer(m_SdlRenderer);
}
}
void DXVA2Renderer::ffPoolDummyDelete(void*, uint8_t*)
@ -430,38 +428,108 @@ bool DXVA2Renderer::isDecoderBlacklisted()
return result;
}
bool DXVA2Renderer::initializeDevice(SDL_Window* window)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(window, &info);
IDirect3D9Ex* d3d9ex;
HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Direct3DCreate9Ex() failed: %x",
hr);
return false;
}
int adapterIndex = SDL_Direct3D9GetAdapterIndex(SDL_GetWindowDisplayIndex(window));
Uint32 windowFlags = SDL_GetWindowFlags(window);
D3DDISPLAYMODEEX currentMode;
currentMode.Size = sizeof(currentMode);
d3d9ex->GetAdapterDisplayModeEx(adapterIndex, &currentMode, nullptr);
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.hDeviceWindow = info.info.win.window;
d3dpp.BackBufferCount = 1;
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
if ((windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
d3dpp.Windowed = false;
d3dpp.BackBufferWidth = currentMode.Width;
d3dpp.BackBufferHeight = currentMode.Height;
d3dpp.FullScreen_RefreshRateInHz = currentMode.RefreshRate;
d3dpp.BackBufferFormat = currentMode.Format;
}
else {
d3dpp.Windowed = true;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
SDL_GetWindowSize(window, (int*)&d3dpp.BackBufferWidth, (int*)&d3dpp.BackBufferHeight);
}
BOOL dwmEnabled;
DwmIsCompositionEnabled(&dwmEnabled);
if (d3dpp.Windowed && dwmEnabled) {
// If composition enabled, disable v-sync and let DWM manage things
// to reduce latency by avoiding double v-syncing.
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// Use FlipEx mode if DWM is running to increase efficiency
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Windowed mode with DWM running");
}
else {
// Uncomposited desktop or full-screen exclusive mode
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"V-Sync enabled");
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Windowed: %d | Present Interval: %d",
d3dpp.Windowed, d3dpp.PresentationInterval);
hr = d3d9ex->CreateDeviceEx(adapterIndex,
D3DDEVTYPE_HAL,
d3dpp.hDeviceWindow,
D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
d3dpp.Windowed ? nullptr : &currentMode,
&m_Device);
d3d9ex->Release();
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"CreateDeviceEx() failed: %x",
hr);
return false;
}
hr = m_Device->SetMaximumFrameLatency(1);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SetMaximumFrameLatency() failed: %x",
hr);
return false;
}
return true;
}
bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, int height, int)
{
m_VideoFormat = videoFormat;
m_VideoWidth = width;
m_VideoHeight = height;
// 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
// D3DCREATE_MULTITHREADED to IDirect3D9::CreateDevice().
SDL_SetHint(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, "1");
m_SdlRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!m_SdlRenderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_CreateRenderer() failed: %s",
SDL_GetError());
return false;
}
m_Device = SDL_RenderGetD3D9Device(m_SdlRenderer);
if (m_Device == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_RenderGetD3D9Device() failed: %s",
SDL_GetError());
return false;
}
// Draw a black frame until the video stream starts rendering
SDL_SetRenderDrawColor(m_SdlRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(m_SdlRenderer);
SDL_RenderPresent(m_SdlRenderer);
RtlZeroMemory(&m_Desc, sizeof(m_Desc));
int alignment;
@ -485,6 +553,10 @@ bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, i
m_Desc.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
m_Desc.Format = (D3DFORMAT)MAKEFOURCC('N','V','1','2');
if (!initializeDevice(window)) {
return false;
}
if (!initializeDecoder()) {
return false;
}
@ -639,30 +711,27 @@ void DXVA2Renderer::renderFrameAtVsync(AVFrame *frame)
bltParams.Alpha = DXVA2_Fixed32OpaqueAlpha();
if (SDL_RenderClear(m_SdlRenderer) != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_RenderClear() failed: %s",
SDL_GetError());
// We're going to cheat a little bit here. It seems SDL's
// renderer may flake out in scenarios like moving the window
// between monitors, so generate a synthetic reset event for
// the main loop to consume.
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
m_Device->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0.0f, 0);
hr = m_Processor->VideoProcessBlt(m_RenderTarget, &bltParams, &sample, 1, nullptr);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VideoProcessBlt() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
// We must try to present to trigger SDL's logic to recover the render target,
// even if VideoProcessBlt() fails.
SDL_RenderPresent(m_SdlRenderer);
hr = m_Device->PresentEx(nullptr, nullptr, nullptr, nullptr, 0);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"PresentEx() failed: %x",
hr);
SDL_Event event;
event.type = SDL_RENDER_TARGETS_RESET;
SDL_PushEvent(&event);
return;
}
}

View file

@ -27,6 +27,7 @@ public:
private:
bool initializeDecoder();
bool initializeRenderer();
bool initializeDevice(SDL_Window* window);
bool isDecoderBlacklisted();
static
@ -45,8 +46,6 @@ private:
int m_DisplayWidth;
int m_DisplayHeight;
SDL_Renderer* m_SdlRenderer;
struct dxva_context m_DXVAContext;
IDirect3DSurface9* m_DecSurfaces[19];
DXVA2_ConfigPictureDecode m_Config;
@ -55,7 +54,7 @@ private:
int m_SurfacesUsed;
AVBufferPool* m_Pool;
IDirect3DDevice9* m_Device;
IDirect3DDevice9Ex* m_Device;
IDirect3DSurface9* m_RenderTarget;
IDirectXVideoProcessorService* m_ProcService;
IDirectXVideoProcessor* m_Processor;

View file

@ -144,6 +144,8 @@ int DxVsyncSource::vsyncThread(void* context)
waitForVblankEventParams.hDevice = 0;
waitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
me->m_Pacer->vsyncCallback();
status = me->m_D3DKMTWaitForVerticalBlankEvent(&waitForVblankEventParams);
if (status != STATUS_SUCCESS) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -152,12 +154,6 @@ int DxVsyncSource::vsyncThread(void* context)
SDL_Delay(10);
continue;
}
// Try to delay to the middle of the V-sync period to give the frame
// from the host time to arrive.
SDL_Delay(500 / monitorMode.dmDisplayFrequency);
me->m_Pacer->vsyncCallback();
}
if (openAdapterParams.hAdapter != 0) {