mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-10 05:34:17 +00:00
Avoid D3D9 fallback on lack of codec support unless a D3D11 FL11.0 GPU wasn't found
We'd rather not waste time (and risk crashes) loading the D3D9 driver if the GPU doesn't have the physical decoding hardware at all.
This commit is contained in:
parent
cefa567f95
commit
f2535f1e6e
5 changed files with 116 additions and 8 deletions
|
@ -75,6 +75,8 @@ static_assert(sizeof(CSC_CONST_BUF) % 16 == 0, "Constant buffer sizes must be a
|
|||
|
||||
D3D11VARenderer::D3D11VARenderer(int decoderSelectionPass)
|
||||
: m_DecoderSelectionPass(decoderSelectionPass),
|
||||
m_DevicesWithFL11Support(0),
|
||||
m_DevicesWithCodecSupport(0),
|
||||
m_Factory(nullptr),
|
||||
m_Device(nullptr),
|
||||
m_SwapChain(nullptr),
|
||||
|
@ -160,6 +162,7 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
|
|||
bool success = false;
|
||||
IDXGIAdapter1* adapter = nullptr;
|
||||
DXGI_ADAPTER_DESC1 adapterDesc;
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
HRESULT hr;
|
||||
|
||||
SDL_assert(m_Device == nullptr);
|
||||
|
@ -209,7 +212,7 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
|
|||
0,
|
||||
D3D11_SDK_VERSION,
|
||||
&m_Device,
|
||||
nullptr,
|
||||
&featureLevel,
|
||||
&m_DeviceContext);
|
||||
if (FAILED(hr)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
|
@ -217,6 +220,11 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
|
|||
hr);
|
||||
goto Exit;
|
||||
}
|
||||
else if (featureLevel >= D3D_FEATURE_LEVEL_11_0) {
|
||||
// Remember that we found a non-software D3D11 devices with support for
|
||||
// feature level 11.0 or later (Fermi, Terascale 2, or Ivy Bridge and later)
|
||||
m_DevicesWithFL11Support++;
|
||||
}
|
||||
|
||||
if (!checkDecoderSupport(adapter)) {
|
||||
m_DeviceContext->Release();
|
||||
|
@ -226,6 +234,10 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
|
|||
|
||||
goto Exit;
|
||||
}
|
||||
else {
|
||||
// Remember that we found a device with support for decoding this codec
|
||||
m_DevicesWithCodecSupport++;
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
|
@ -997,6 +1009,29 @@ bool D3D11VARenderer::needsTestFrame()
|
|||
return true;
|
||||
}
|
||||
|
||||
IFFmpegRenderer::InitFailureReason D3D11VARenderer::getInitFailureReason()
|
||||
{
|
||||
// In the specific case where we found at least one D3D11 hardware device but none of the
|
||||
// enumerated devices have support for the specified codec, tell the FFmpeg decoder not to
|
||||
// bother trying other hwaccels. We don't want to try loading D3D9 if the device doesn't
|
||||
// even have hardware support for the codec.
|
||||
//
|
||||
// NB: We use feature level 11.0 support as a gate here because we want to avoid returning
|
||||
// this failure reason in cases where we might have an extremely old GPU with support for
|
||||
// DXVA2 on D3D9 but not D3D11VA on D3D11. I'm unsure if any such drivers/hardware exists,
|
||||
// but better be safe than sorry.
|
||||
//
|
||||
// NB2: We're also assuming that no GPU exists which lacks any D3D11 driver but has drivers
|
||||
// for non-DX APIs like Vulkan. I believe this is a Windows Logo requirement so it should be
|
||||
// safe to assume.
|
||||
if (m_DevicesWithFL11Support != 0 && m_DevicesWithCodecSupport == 0) {
|
||||
return InitFailureReason::NoHardwareSupport;
|
||||
}
|
||||
else {
|
||||
return InitFailureReason::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D11VARenderer::lockContext(void *lock_ctx)
|
||||
{
|
||||
auto me = (D3D11VARenderer*)lock_ctx;
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
virtual int getRendererAttributes() override;
|
||||
virtual int getDecoderCapabilities() override;
|
||||
virtual bool needsTestFrame() override;
|
||||
virtual InitFailureReason getInitFailureReason() override;
|
||||
|
||||
private:
|
||||
static void lockContext(void* lock_ctx);
|
||||
|
@ -35,6 +36,8 @@ private:
|
|||
bool createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound = nullptr);
|
||||
|
||||
int m_DecoderSelectionPass;
|
||||
int m_DevicesWithFL11Support;
|
||||
int m_DevicesWithCodecSupport;
|
||||
|
||||
IDXGIFactory5* m_Factory;
|
||||
ID3D11Device* m_Device;
|
||||
|
|
|
@ -128,6 +128,24 @@ public:
|
|||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
|
||||
virtual void renderFrame(AVFrame* frame) = 0;
|
||||
|
||||
enum class InitFailureReason
|
||||
{
|
||||
Unknown,
|
||||
|
||||
// Only return this reason code if the hardware physically lacks support for
|
||||
// the specified codec. If the FFmpeg decoder code sees this value, it will
|
||||
// assume trying additional hwaccel renderers will useless and give up.
|
||||
//
|
||||
// NB: This should only be used under very special circumstances for cases
|
||||
// where trying additional hwaccels may be undesirable since it could lead
|
||||
// to incorrectly skipping working hwaccels.
|
||||
NoHardwareSupport,
|
||||
};
|
||||
|
||||
virtual InitFailureReason getInitFailureReason() {
|
||||
return InitFailureReason::Unknown;
|
||||
}
|
||||
|
||||
// Called for threaded renderers to allow them to wait prior to us latching
|
||||
// the next frame for rendering (as opposed to waiting on buffer swap with
|
||||
// an older frame already queued for display).
|
||||
|
|
|
@ -888,6 +888,7 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
enum AVPixelFormat requiredFormat,
|
||||
PDECODER_PARAMETERS params,
|
||||
const AVCodecHWConfig* hwConfig,
|
||||
IFFmpegRenderer::InitFailureReason* failureReason, // Out - Optional
|
||||
std::function<IFFmpegRenderer*()> createRendererFunc)
|
||||
{
|
||||
DECODER_PARAMETERS testFrameDecoderParams = *params;
|
||||
|
@ -905,6 +906,10 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
|
||||
m_HwDecodeCfg = hwConfig;
|
||||
|
||||
if (failureReason != nullptr) {
|
||||
*failureReason = IFFmpegRenderer::InitFailureReason::Unknown;
|
||||
}
|
||||
|
||||
// i == 0 - Indirect via EGL or DRM frontend with zero-copy DMA-BUF passing
|
||||
// i == 1 - Direct rendering or indirect via SDL read-back
|
||||
#ifdef HAVE_EGL
|
||||
|
@ -936,6 +941,11 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
else {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Decoder failed to initialize after successful test");
|
||||
|
||||
if (m_BackendRenderer != nullptr && failureReason != nullptr) {
|
||||
*failureReason = m_BackendRenderer->getInitFailureReason();
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
@ -945,6 +955,10 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (m_BackendRenderer != nullptr && failureReason != nullptr) {
|
||||
*failureReason = m_BackendRenderer->getInitFailureReason();
|
||||
}
|
||||
|
||||
// Failed to initialize, so keep looking
|
||||
reset();
|
||||
}
|
||||
|
@ -962,7 +976,7 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
|
||||
"Trying " #RENDERER_TYPE " for codec %s due to preferred pixel format: 0x%x", \
|
||||
decoder->name, decoder->pix_fmts[i]); \
|
||||
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, \
|
||||
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, nullptr, \
|
||||
[]() -> IFFmpegRenderer* { return new RENDERER_TYPE(); })) { \
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
|
||||
"Chose " #RENDERER_TYPE " for codec %s due to preferred pixel format: 0x%x", \
|
||||
|
@ -980,7 +994,7 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(const AVCodec* decoder,
|
|||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
|
||||
"Trying " #RENDERER_TYPE " for codec %s due to compatible pixel format: 0x%x", \
|
||||
decoder->name, decoder->pix_fmts[i]); \
|
||||
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, \
|
||||
if (tryInitializeRenderer(decoder, decoder->pix_fmts[i], params, nullptr, nullptr, \
|
||||
[]() -> IFFmpegRenderer* { return new RENDERER_TYPE(); })) { \
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, \
|
||||
"Chose " #RENDERER_TYPE " for codec %s due to compatible pixel format: 0x%x", \
|
||||
|
@ -1008,10 +1022,16 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
|
|||
}
|
||||
|
||||
// Initialize the hardware codec and submit a test frame if the renderer needs it
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config,
|
||||
IFFmpegRenderer::InitFailureReason failureReason;
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config, &failureReason,
|
||||
[config]() -> IFFmpegRenderer* { return createHwAccelRenderer(config, 0); })) {
|
||||
return true;
|
||||
}
|
||||
else if (failureReason == IFFmpegRenderer::InitFailureReason::NoHardwareSupport) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Skipping remaining hwaccels due lack of hardware support for specified codec");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1019,13 +1039,13 @@ bool FFmpegVideoDecoder::tryInitializeRendererForUnknownDecoder(const AVCodec* d
|
|||
// Supported output pixel formats are unknown. We'll just try DRM/SDL and hope it can cope.
|
||||
|
||||
#if defined(HAVE_DRM) && defined(GL_IS_SLOW)
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr,
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr, nullptr,
|
||||
[]() -> IFFmpegRenderer* { return new DrmRenderer(); })) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr,
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, nullptr, nullptr,
|
||||
[]() -> IFFmpegRenderer* { return new SdlRenderer(); })) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1178,6 +1198,8 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
|
||||
// Look for a hardware decoder first unless software-only
|
||||
if (params->vds != StreamingPreferences::VDS_FORCE_SOFTWARE) {
|
||||
QSet<const AVCodec*> terminallyFailedHardwareDecoders;
|
||||
|
||||
// Iterate through tier-1 hwaccel decoders
|
||||
codecIterator = NULL;
|
||||
while ((decoder = av_codec_iterate(&codecIterator))) {
|
||||
|
@ -1203,6 +1225,11 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Skip hardware decoders that have returned a terminal failure status
|
||||
if (terminallyFailedHardwareDecoders.contains(decoder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for the first matching hwaccel hardware decoder (pass 0)
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);
|
||||
|
@ -1212,10 +1239,17 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
}
|
||||
|
||||
// Initialize the hardware codec and submit a test frame if the renderer needs it
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config,
|
||||
IFFmpegRenderer::InitFailureReason failureReason;
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config, &failureReason,
|
||||
[config]() -> IFFmpegRenderer* { return createHwAccelRenderer(config, 0); })) {
|
||||
return true;
|
||||
}
|
||||
else if (failureReason == IFFmpegRenderer::InitFailureReason::NoHardwareSupport) {
|
||||
terminallyFailedHardwareDecoders.insert(decoder);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Skipping remaining hwaccels due lack of hardware support for specified codec");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1244,6 +1278,11 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Skip hardware decoders that have returned a terminal failure status
|
||||
if (terminallyFailedHardwareDecoders.contains(decoder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to initialize this decoder both as hwaccel and non-hwaccel
|
||||
if (tryInitializeRendererForUnknownDecoder(decoder, params, true)) {
|
||||
return true;
|
||||
|
@ -1270,6 +1309,11 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Skip hardware decoders that have returned a terminal failure status
|
||||
if (terminallyFailedHardwareDecoders.contains(decoder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for the first matching hwaccel hardware decoder (pass 1)
|
||||
// This picks up "second-tier" hwaccels like CUDA.
|
||||
for (int i = 0;; i++) {
|
||||
|
@ -1280,10 +1324,17 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params)
|
|||
}
|
||||
|
||||
// Initialize the hardware codec and submit a test frame if the renderer needs it
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config,
|
||||
IFFmpegRenderer::InitFailureReason failureReason;
|
||||
if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config, &failureReason,
|
||||
[config]() -> IFFmpegRenderer* { return createHwAccelRenderer(config, 1); })) {
|
||||
return true;
|
||||
}
|
||||
else if (failureReason == IFFmpegRenderer::InitFailureReason::NoHardwareSupport) {
|
||||
terminallyFailedHardwareDecoders.insert(decoder);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Skipping remaining hwaccels due lack of hardware support for specified codec");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ private:
|
|||
enum AVPixelFormat requiredFormat,
|
||||
PDECODER_PARAMETERS params,
|
||||
const AVCodecHWConfig* hwConfig,
|
||||
IFFmpegRenderer::InitFailureReason* failureReason,
|
||||
std::function<IFFmpegRenderer*()> createRendererFunc);
|
||||
|
||||
static IFFmpegRenderer* createHwAccelRenderer(const AVCodecHWConfig* hwDecodeCfg, int pass);
|
||||
|
|
Loading…
Reference in a new issue