Use separate render and decode D3D11Devices

See #1424
This commit is contained in:
Cameron Gutman 2026-01-19 14:25:08 -06:00
commit f6e08f8a43
2 changed files with 339 additions and 143 deletions

View file

@ -108,24 +108,144 @@ D3D11VARenderer::~D3D11VARenderer()
m_VideoBlendState.Reset(); m_VideoBlendState.Reset();
m_PreviousFrameRenderedFence.Reset(); m_PreviousFrameRenderedFence.Reset();
m_DecoderShaderBindFence.Reset(); m_DecodeD2RFence.Reset();
m_DecodeR2DFence.Reset();
m_RenderD2RFence.Reset();
m_RenderR2DFence.Reset();
m_RenderTargetView.Reset(); m_RenderTargetView.Reset();
m_SwapChain.Reset(); m_SwapChain.Reset();
m_RenderSharedTextureArray.Reset();
av_buffer_unref(&m_HwDeviceContext); av_buffer_unref(&m_HwDeviceContext);
m_DecodeDevice.Reset();
m_DecodeDeviceContext.Reset();
// Force destruction of the swapchain immediately // Force destruction of the swapchain immediately
if (m_DeviceContext != nullptr) { if (m_RenderDeviceContext != nullptr) {
m_DeviceContext->ClearState(); m_RenderDeviceContext->ClearState();
m_DeviceContext->Flush(); m_RenderDeviceContext->Flush();
} }
m_Device.Reset(); m_RenderDevice.Reset();
m_DeviceContext.Reset(); m_RenderDeviceContext.Reset();
m_Factory.Reset(); m_Factory.Reset();
} }
bool D3D11VARenderer::createSharedFencePair(UINT64 initialValue, ID3D11Device5* dev1, ID3D11Device5* dev2, ComPtr<ID3D11Fence>& dev1Fence, ComPtr<ID3D11Fence>& dev2Fence)
{
HRESULT hr;
hr = dev1->CreateFence(initialValue, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&dev1Fence));
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device5::CreateFence() failed: %x",
hr);
return false;
}
HANDLE fenceHandle;
hr = dev1Fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &fenceHandle);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Fence::CreateSharedHandle() failed: %x",
hr);
dev1Fence.Reset();
return false;
}
hr = dev2->OpenSharedFence(fenceHandle, IID_PPV_ARGS(&dev2Fence));
CloseHandle(fenceHandle);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device5::OpenSharedFence() failed: %x",
hr);
dev1Fence.Reset();
return false;
}
return true;
}
bool D3D11VARenderer::setupSharedDevice(IDXGIAdapter1* adapter)
{
const D3D_FEATURE_LEVEL supportedFeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 };
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr;
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext;
bool success = false;
// We don't support cross-device sharing without fences
if (m_FenceType == SupportedFenceType::None) {
return false;
}
// If we're going to use separate devices for decoding and rendering, create the decoding device
hr = D3D11CreateDevice(adapter,
D3D_DRIVER_TYPE_UNKNOWN,
nullptr,
D3D11_CREATE_DEVICE_VIDEO_SUPPORT
#ifdef QT_DEBUG
| D3D11_CREATE_DEVICE_DEBUG
#endif
,
supportedFeatureLevels,
ARRAYSIZE(supportedFeatureLevels),
D3D11_SDK_VERSION,
&device,
&featureLevel,
&deviceContext);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"D3D11CreateDevice() failed: %x",
hr);
return false;
}
hr = device.As(&m_DecodeDevice);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::QueryInterface(ID3D11Device1) failed: %x",
hr);
goto Exit;
}
hr = deviceContext.As(&m_DecodeDeviceContext);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11DeviceContext::QueryInterface(ID3D11DeviceContext1) failed: %x",
hr);
goto Exit;
}
// Create our decode->render fence
m_DecodeRenderSyncFenceValue = 0;
if (!createSharedFencePair(1, m_DecodeDevice.Get(), m_RenderDevice.Get(), m_DecodeD2RFence, m_RenderD2RFence)) {
goto Exit;
}
// Create our render->decode fence
if (!createSharedFencePair(0, m_DecodeDevice.Get(), m_RenderDevice.Get(), m_DecodeR2DFence, m_RenderR2DFence)) {
goto Exit;
}
success = true;
Exit:
if (!success) {
m_DecodeD2RFence.Reset();
m_RenderD2RFence.Reset();
m_DecodeR2DFence.Reset();
m_RenderR2DFence.Reset();
m_DecodeDevice.Reset();
m_RenderDevice.Reset();
}
return success;
}
bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound) bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound)
{ {
const D3D_FEATURE_LEVEL supportedFeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 }; const D3D_FEATURE_LEVEL supportedFeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 };
@ -134,10 +254,13 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
DXGI_ADAPTER_DESC1 adapterDesc; DXGI_ADAPTER_DESC1 adapterDesc;
D3D_FEATURE_LEVEL featureLevel; D3D_FEATURE_LEVEL featureLevel;
HRESULT hr; HRESULT hr;
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext; ComPtr<ID3D11DeviceContext> deviceContext;
SDL_assert(!m_Device); SDL_assert(!m_RenderDevice);
SDL_assert(!m_DeviceContext); SDL_assert(!m_RenderDeviceContext);
SDL_assert(!m_DecodeDevice);
SDL_assert(!m_DecodeDeviceContext);
hr = m_Factory->EnumAdapters1(adapterIndex, &adapter); hr = m_Factory->EnumAdapters1(adapterIndex, &adapter);
if (hr == DXGI_ERROR_NOT_FOUND) { if (hr == DXGI_ERROR_NOT_FOUND) {
@ -182,7 +305,7 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
supportedFeatureLevels, supportedFeatureLevels,
ARRAYSIZE(supportedFeatureLevels), ARRAYSIZE(supportedFeatureLevels),
D3D11_SDK_VERSION, D3D11_SDK_VERSION,
&m_Device, &device,
&featureLevel, &featureLevel,
&deviceContext); &deviceContext);
if (FAILED(hr)) { if (FAILED(hr)) {
@ -194,8 +317,6 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
else if (adapterDesc.VendorId == 0x8086 && featureLevel <= D3D_FEATURE_LEVEL_11_0 && !qEnvironmentVariableIntValue("D3D11VA_ENABLED")) { else if (adapterDesc.VendorId == 0x8086 && featureLevel <= D3D_FEATURE_LEVEL_11_0 && !qEnvironmentVariableIntValue("D3D11VA_ENABLED")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Avoiding D3D11VA on old pre-FL11.1 Intel GPU. Set D3D11VA_ENABLED=1 to override."); "Avoiding D3D11VA on old pre-FL11.1 Intel GPU. Set D3D11VA_ENABLED=1 to override.");
m_DeviceContext.Reset();
m_Device.Reset();
goto Exit; goto Exit;
} }
else if (featureLevel >= D3D_FEATURE_LEVEL_11_0) { else if (featureLevel >= D3D_FEATURE_LEVEL_11_0) {
@ -204,27 +325,22 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
m_DevicesWithFL11Support++; m_DevicesWithFL11Support++;
} }
hr = deviceContext.As(&m_DeviceContext); hr = device.As(&m_RenderDevice);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::QueryInterface(ID3D11Device1) failed: %x",
hr);
goto Exit;
}
hr = deviceContext.As(&m_RenderDeviceContext);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11DeviceContext::QueryInterface(ID3D11DeviceContext1) failed: %x", "ID3D11DeviceContext::QueryInterface(ID3D11DeviceContext1) failed: %x",
hr); hr);
m_DeviceContext.Reset();
m_Device.Reset();
goto Exit; goto Exit;
} }
if (Utils::getEnvironmentVariableOverride("D3D11VA_FORCE_BIND", &m_BindDecoderOutputTextures)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Using D3D11VA_FORCE_BIND to override default bind/copy logic");
}
else {
// Skip copying to our own internal texture on Intel GPUs due to
// significant performance impact of the extra copy. See:
// https://github.com/moonlight-stream/moonlight-qt/issues/1304
m_BindDecoderOutputTextures = adapterDesc.VendorId == 0x8086;
}
// Check which fence types are supported by this GPU // Check which fence types are supported by this GPU
{ {
m_FenceType = SupportedFenceType::None; m_FenceType = SupportedFenceType::None;
@ -245,62 +361,72 @@ bool D3D11VARenderer::createDeviceByAdapterIndex(int adapterIndex, bool* adapter
} }
if (m_FenceType != SupportedFenceType::None) { if (m_FenceType != SupportedFenceType::None) {
ComPtr<ID3D11Device5> device5; // If this GPU supports monitored fences, use one to wait until the previous frame
ComPtr<ID3D11DeviceContext4> deviceContext4; // has finished rendering before starting on the next one. This reduces latency by
if (SUCCEEDED(m_Device.As(&device5)) && SUCCEEDED(m_DeviceContext.As(&deviceContext4))) { // avoiding stalling during rendering after we've already grabbed the next frame
// If this GPU supports monitored fences, use one to wait until the previous frame // to render, and also avoids stalling the decoder by releasing a surface back to
// has finished rendering before starting on the next one. This reduces latency by // the pool before we've finished reading from it (causing a stall if the decoder
// avoiding stalling during rendering after we've already grabbed the next frame // tries to write again).
// to render, and also avoids stalling the decoder by releasing a surface back to if (m_FenceType == SupportedFenceType::Monitored) {
// the pool before we've finished reading from it (causing a stall if the decoder m_PreviousFrameRenderedFenceValue = 0;
// tries to write again). hr = m_RenderDevice->CreateFence(m_PreviousFrameRenderedFenceValue,
if (m_FenceType == SupportedFenceType::Monitored) { D3D11_FENCE_FLAG_NONE,
m_PreviousFrameRenderedFenceValue = 0; IID_PPV_ARGS(&m_PreviousFrameRenderedFence));
hr = device5->CreateFence(m_PreviousFrameRenderedFenceValue, if (FAILED(hr)) {
m_FenceType == SupportedFenceType::Monitored ? SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
D3D11_FENCE_FLAG_NONE : D3D11_FENCE_FLAG_NON_MONITORED, "ID3D11Device5::CreateFence() failed: %x",
IID_PPV_ARGS(&m_PreviousFrameRenderedFence)); hr);
if (FAILED(hr)) { // Non-fatal
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device5::CreateFence() failed: %x",
hr);
// Non-fatal
}
// Create an auto-reset event for our fence to signal
m_PreviousFrameRenderedEvent.Attach(CreateEvent(NULL, FALSE, TRUE, NULL));
} }
// If we're binding output textures, we signal and wait for a fence prior to rendering // Create an auto-reset event for our fence to signal
// as a workaround for some driver bugs that can cause the GPU driver to fail to insert m_PreviousFrameRenderedEvent.Attach(CreateEvent(NULL, FALSE, TRUE, NULL));
// a dependency between the decoder engine and the 3D engine. This seems to be a much
// less well-tested path in most drivers than the video->copy or video->video path.
if (m_BindDecoderOutputTextures) {
m_DecoderShaderBindFenceValue = 0;
hr = device5->CreateFence(m_DecoderShaderBindFenceValue,
m_FenceType == SupportedFenceType::Monitored ?
D3D11_FENCE_FLAG_NONE : D3D11_FENCE_FLAG_NON_MONITORED,
IID_PPV_ARGS(&m_DecoderShaderBindFence));
if (FAILED(hr)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device5::CreateFence() failed: %x",
hr);
// Non-fatal
}
}
} }
} }
} }
if (Utils::getEnvironmentVariableOverride("D3D11VA_FORCE_BIND", &m_BindDecoderOutputTextures)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Using D3D11VA_FORCE_BIND to override default bind/copy logic");
}
else {
// Skip copying to our own internal texture on Intel GPUs due to
// significant performance impact of the extra copy. See:
// https://github.com/moonlight-stream/moonlight-qt/issues/1304
m_BindDecoderOutputTextures = adapterDesc.VendorId == 0x8086;
}
bool separateDevices;
if (Utils::getEnvironmentVariableOverride("D3D11VA_FORCE_SEPARATE_DEVICES", &separateDevices)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Using D3D11VA_FORCE_SEPARATE_DEVICES to override default logic");
}
else {
D3D11_FEATURE_DATA_D3D11_OPTIONS d3d11Options;
// Check if cross-device sharing works for YUV textures and fences are supported
hr = m_RenderDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &d3d11Options, sizeof(d3d11Options));
separateDevices = SUCCEEDED(hr) && d3d11Options.ExtendedResourceSharing && m_FenceType != SupportedFenceType::None;
}
// If we're going to use separate devices for decoding and rendering, create the decoding device
if (!separateDevices || !setupSharedDevice(adapter.Get())) {
m_DecodeDevice = m_RenderDevice;
m_DecodeDeviceContext = m_RenderDeviceContext;
separateDevices = false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Decoder texture access: %s (fence: %s)", "Decoder texture access: %s (fence: %s)",
m_BindDecoderOutputTextures ? "bind" : "copy", m_BindDecoderOutputTextures ? "bind" : "copy",
m_FenceType == SupportedFenceType::Monitored ? "monitored" : m_FenceType == SupportedFenceType::Monitored ? "monitored" :
(m_FenceType == SupportedFenceType::NonMonitored ? "non-monitored" : "unsupported")); (m_FenceType == SupportedFenceType::NonMonitored ? "non-monitored" : "unsupported"));
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using %s device for decoding and rendering",
separateDevices ? "separate" : "shared");
if (!checkDecoderSupport(adapter.Get())) { if (!checkDecoderSupport(adapter.Get())) {
m_DeviceContext.Reset();
m_Device.Reset();
goto Exit; goto Exit;
} }
else { else {
@ -314,6 +440,12 @@ Exit:
if (adapterNotFound != nullptr) { if (adapterNotFound != nullptr) {
*adapterNotFound = !adapter; *adapterNotFound = !adapter;
} }
if (!success) {
m_RenderDeviceContext.Reset();
m_RenderDevice.Reset();
m_DecodeDeviceContext.Reset();
m_DecodeDevice.Reset();
}
return success; return success;
} }
@ -379,8 +511,8 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
} }
if (adapterNotFound) { if (adapterNotFound) {
SDL_assert(!m_Device); SDL_assert(!m_RenderDevice);
SDL_assert(!m_DeviceContext); SDL_assert(!m_RenderDeviceContext);
return false; return false;
} }
} }
@ -469,7 +601,7 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
// Always use windowed or borderless windowed mode.. SDL does mode-setting for us in // Always use windowed or borderless windowed mode.. SDL does mode-setting for us in
// full-screen exclusive mode (SDL_WINDOW_FULLSCREEN), so this actually works out okay. // full-screen exclusive mode (SDL_WINDOW_FULLSCREEN), so this actually works out okay.
ComPtr<IDXGISwapChain1> swapChain; ComPtr<IDXGISwapChain1> swapChain;
hr = m_Factory->CreateSwapChainForHwnd(m_Device.Get(), hr = m_Factory->CreateSwapChainForHwnd(m_RenderDevice.Get(),
info.info.win.window, info.info.win.window,
&swapChainDesc, &swapChainDesc,
nullptr, nullptr,
@ -514,8 +646,8 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
AVD3D11VADeviceContext* d3d11vaDeviceContext = (AVD3D11VADeviceContext*)deviceContext->hwctx; AVD3D11VADeviceContext* d3d11vaDeviceContext = (AVD3D11VADeviceContext*)deviceContext->hwctx;
// FFmpeg will take ownership of these pointers, so we use CopyTo() to bump the ref count // FFmpeg will take ownership of these pointers, so we use CopyTo() to bump the ref count
m_Device.CopyTo(&d3d11vaDeviceContext->device); m_DecodeDevice.CopyTo(&d3d11vaDeviceContext->device);
m_DeviceContext.CopyTo(&d3d11vaDeviceContext->device_context); m_DecodeDeviceContext.CopyTo(&d3d11vaDeviceContext->device_context);
// Set lock functions that we will use to synchronize with FFmpeg's usage of our device context // Set lock functions that we will use to synchronize with FFmpeg's usage of our device context
d3d11vaDeviceContext->lock = lockContext; d3d11vaDeviceContext->lock = lockContext;
@ -568,6 +700,11 @@ bool D3D11VARenderer::prepareDecoderContextInGetFormat(AVCodecContext *context,
d3d11vaFramesContext->BindFlags |= D3D11_BIND_SHADER_RESOURCE; d3d11vaFramesContext->BindFlags |= D3D11_BIND_SHADER_RESOURCE;
} }
// If we're using separate decode and render devices, we need to create shared textures
if (m_DecodeDevice != m_RenderDevice) {
d3d11vaFramesContext->MiscFlags |= D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
}
// Mimic the logic in ff_decode_get_hw_frames_ctx() which adds an extra 3 frames // Mimic the logic in ff_decode_get_hw_frames_ctx() which adds an extra 3 frames
if (framesContext->initial_pool_size) { if (framesContext->initial_pool_size) {
framesContext->initial_pool_size += 3; framesContext->initial_pool_size += 3;
@ -594,15 +731,17 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
{ {
// Acquire the context lock for rendering to prevent concurrent // Acquire the context lock for rendering to prevent concurrent
// access from inside FFmpeg's decoding code // access from inside FFmpeg's decoding code
lockContext(this); if (m_DecodeDevice == m_RenderDevice) {
lockContext(this);
}
// Clear the back buffer // Clear the back buffer
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 1.0f}; const float clearColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
m_DeviceContext->ClearRenderTargetView(m_RenderTargetView.Get(), clearColor); m_RenderDeviceContext->ClearRenderTargetView(m_RenderTargetView.Get(), clearColor);
// Bind the back buffer. This needs to be done each time, // Bind the back buffer. This needs to be done each time,
// because the render target view will be unbound by Present(). // because the render target view will be unbound by Present().
m_DeviceContext->OMSetRenderTargets(1, m_RenderTargetView.GetAddressOf(), nullptr); m_RenderDeviceContext->OMSetRenderTargets(1, m_RenderTargetView.GetAddressOf(), nullptr);
// Render our video frame with the aspect-ratio adjusted viewport // Render our video frame with the aspect-ratio adjusted viewport
renderVideo(frame); renderVideo(frame);
@ -656,8 +795,10 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
// Present according to the decoder parameters // Present according to the decoder parameters
hr = m_SwapChain->Present(0, flags); hr = m_SwapChain->Present(0, flags);
// Release the context lock if (m_DecodeDevice == m_RenderDevice) {
unlockContext(this); // Release the context lock
unlockContext(this);
}
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -701,16 +842,16 @@ void D3D11VARenderer::renderOverlay(Overlay::OverlayType type)
// Bind vertex buffer // Bind vertex buffer
UINT stride = sizeof(VERTEX); UINT stride = sizeof(VERTEX);
UINT offset = 0; UINT offset = 0;
m_DeviceContext->IASetVertexBuffers(0, 1, overlayVertexBuffer.GetAddressOf(), &stride, &offset); m_RenderDeviceContext->IASetVertexBuffers(0, 1, overlayVertexBuffer.GetAddressOf(), &stride, &offset);
// Bind pixel shader and resources // Bind pixel shader and resources
m_DeviceContext->PSSetShader(m_OverlayPixelShader.Get(), nullptr, 0); m_RenderDeviceContext->PSSetShader(m_OverlayPixelShader.Get(), nullptr, 0);
m_DeviceContext->PSSetShaderResources(0, 1, overlayTextureResourceView.GetAddressOf()); m_RenderDeviceContext->PSSetShaderResources(0, 1, overlayTextureResourceView.GetAddressOf());
// Draw the overlay with alpha blending // Draw the overlay with alpha blending
m_DeviceContext->OMSetBlendState(m_OverlayBlendState.Get(), nullptr, 0xffffffff); m_RenderDeviceContext->OMSetBlendState(m_OverlayBlendState.Get(), nullptr, 0xffffffff);
m_DeviceContext->DrawIndexed(6, 0, 0); m_RenderDeviceContext->DrawIndexed(6, 0, 0);
m_DeviceContext->OMSetBlendState(m_VideoBlendState.Get(), nullptr, 0xffffffff); m_RenderDeviceContext->OMSetBlendState(m_VideoBlendState.Get(), nullptr, 0xffffffff);
} }
void D3D11VARenderer::bindVideoVertexBuffer(bool frameChanged, AVFrame* frame) void D3D11VARenderer::bindVideoVertexBuffer(bool frameChanged, AVFrame* frame)
@ -754,7 +895,7 @@ void D3D11VARenderer::bindVideoVertexBuffer(bool frameChanged, AVFrame* frame)
D3D11_SUBRESOURCE_DATA vbData = {}; D3D11_SUBRESOURCE_DATA vbData = {};
vbData.pSysMem = verts; vbData.pSysMem = verts;
HRESULT hr = m_Device->CreateBuffer(&vbDesc, &vbData, &m_VideoVertexBuffer); HRESULT hr = m_RenderDevice->CreateBuffer(&vbDesc, &vbData, &m_VideoVertexBuffer);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateBuffer() failed: %x", "ID3D11Device::CreateBuffer() failed: %x",
@ -766,7 +907,7 @@ void D3D11VARenderer::bindVideoVertexBuffer(bool frameChanged, AVFrame* frame)
// Bind video rendering vertex buffer // Bind video rendering vertex buffer
UINT stride = sizeof(VERTEX); UINT stride = sizeof(VERTEX);
UINT offset = 0; UINT offset = 0;
m_DeviceContext->IASetVertexBuffers(0, 1, m_VideoVertexBuffer.GetAddressOf(), &stride, &offset); m_RenderDeviceContext->IASetVertexBuffers(0, 1, m_VideoVertexBuffer.GetAddressOf(), &stride, &offset);
} }
void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame) void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame)
@ -779,10 +920,10 @@ void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame)
switch (m_TextureFormat) switch (m_TextureFormat)
{ {
case DXGI_FORMAT_AYUV: case DXGI_FORMAT_AYUV:
m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_AYUV].Get(), nullptr, 0); m_RenderDeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_AYUV].Get(), nullptr, 0);
break; break;
case DXGI_FORMAT_Y410: case DXGI_FORMAT_Y410:
m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_Y410].Get(), nullptr, 0); m_RenderDeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_Y410].Get(), nullptr, 0);
break; break;
default: default:
SDL_assert(false); SDL_assert(false);
@ -790,7 +931,7 @@ void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame)
} }
else { else {
// We'll need to use the generic 4:2:0 shader for this colorspace and color range combo // We'll need to use the generic 4:2:0 shader for this colorspace and color range combo
m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_YUV_420].Get(), nullptr, 0); m_RenderDeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_YUV_420].Get(), nullptr, 0);
} }
// If nothing has changed since last frame, we're done // If nothing has changed since last frame, we're done
@ -835,9 +976,9 @@ void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame)
constData.pSysMem = &constBuf; constData.pSysMem = &constBuf;
ComPtr<ID3D11Buffer> constantBuffer; ComPtr<ID3D11Buffer> constantBuffer;
HRESULT hr = m_Device->CreateBuffer(&constDesc, &constData, &constantBuffer); HRESULT hr = m_RenderDevice->CreateBuffer(&constDesc, &constData, &constantBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->PSSetConstantBuffers(0, 1, constantBuffer.GetAddressOf()); m_RenderDeviceContext->PSSetConstantBuffers(0, 1, constantBuffer.GetAddressOf());
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -849,6 +990,18 @@ void D3D11VARenderer::bindColorConversion(bool frameChanged, AVFrame* frame)
void D3D11VARenderer::renderVideo(AVFrame* frame) void D3D11VARenderer::renderVideo(AVFrame* frame)
{ {
// Insert a fence to force the render context to wait for the decode context to finish writing
if (m_DecodeDevice != m_RenderDevice) {
SDL_assert(m_DecodeD2RFence);
SDL_assert(m_RenderD2RFence);
lockContext(this);
if (SUCCEEDED(m_DecodeDeviceContext->Signal(m_DecodeD2RFence.Get(), m_DecodeRenderSyncFenceValue))) {
m_RenderDeviceContext->Wait(m_RenderD2RFence.Get(), m_DecodeRenderSyncFenceValue++);
}
unlockContext(this);
}
UINT srvIndex; UINT srvIndex;
if (m_BindDecoderOutputTextures) { if (m_BindDecoderOutputTextures) {
// Our indexing logic depends on a direct mapping into m_VideoTextureResourceViews // Our indexing logic depends on a direct mapping into m_VideoTextureResourceViews
@ -861,25 +1014,13 @@ void D3D11VARenderer::renderVideo(AVFrame* frame)
srvIndex); srvIndex);
return; return;
} }
// Insert a fence to force proper synchronization between the video engine and
// 3D engine. Some GPU drivers (HD 4000, MTT S70) have bugs that prevent this
// data dependency from being handled properly on its own, which can lead to
// rendering artifacts and video lag (rendering old frames).
if (m_DecoderShaderBindFence) {
ComPtr<ID3D11DeviceContext4> deviceContext4;
if (SUCCEEDED(m_DeviceContext.As(&deviceContext4))) {
if (SUCCEEDED(deviceContext4->Signal(m_DecoderShaderBindFence.Get(), ++m_DecoderShaderBindFenceValue))) {
deviceContext4->Wait(m_DecoderShaderBindFence.Get(), m_DecoderShaderBindFenceValue);
}
}
}
} }
else { else {
// Copy this frame into our video texture // Copy this frame into our video texture
m_DeviceContext->CopySubresourceRegion1(m_VideoTexture.Get(), 0, 0, 0, 0, m_RenderDeviceContext->CopySubresourceRegion1(m_VideoTexture.Get(), 0, 0, 0, 0,
(ID3D11Resource*)frame->data[0], (int)(intptr_t)frame->data[1], m_RenderSharedTextureArray.Get(),
nullptr, D3D11_COPY_DISCARD); (int)(intptr_t)frame->data[1],
nullptr, D3D11_COPY_DISCARD);
// SRV 0 is always mapped to the video texture // SRV 0 is always mapped to the video texture
srvIndex = 0; srvIndex = 0;
@ -895,19 +1036,31 @@ void D3D11VARenderer::renderVideo(AVFrame* frame)
// Bind SRVs for this frame // Bind SRVs for this frame
ID3D11ShaderResourceView* frameSrvs[] = { m_VideoTextureResourceViews[srvIndex][0].Get(), m_VideoTextureResourceViews[srvIndex][1].Get() }; ID3D11ShaderResourceView* frameSrvs[] = { m_VideoTextureResourceViews[srvIndex][0].Get(), m_VideoTextureResourceViews[srvIndex][1].Get() };
m_DeviceContext->PSSetShaderResources(0, 2, frameSrvs); m_RenderDeviceContext->PSSetShaderResources(0, 2, frameSrvs);
// Draw the video // Draw the video
m_DeviceContext->DrawIndexed(6, 0, 0); m_RenderDeviceContext->DrawIndexed(6, 0, 0);
// Unbind SRVs for this frame // Unbind SRVs for this frame
ID3D11ShaderResourceView* nullSrvs[2] = {}; ID3D11ShaderResourceView* nullSrvs[2] = {};
m_DeviceContext->PSSetShaderResources(0, 2, nullSrvs); m_RenderDeviceContext->PSSetShaderResources(0, 2, nullSrvs);
// Insert a fence to force the decode context to wait for the render context to finish reading
if (m_DecodeDevice != m_RenderDevice) {
SDL_assert(m_DecodeR2DFence);
SDL_assert(m_RenderR2DFence);
if (SUCCEEDED(m_RenderDeviceContext->Signal(m_RenderR2DFence.Get(), m_DecodeRenderSyncFenceValue))) {
lockContext(this);
m_DecodeDeviceContext->Wait(m_DecodeR2DFence.Get(), m_DecodeRenderSyncFenceValue++);
unlockContext(this);
}
}
// Trigger our fence to signal after this video frame has been rendered // Trigger our fence to signal after this video frame has been rendered
if (m_PreviousFrameRenderedFence) { if (m_PreviousFrameRenderedFence) {
ComPtr<ID3D11DeviceContext4> deviceContext4; ComPtr<ID3D11DeviceContext4> deviceContext4;
if (SUCCEEDED(m_DeviceContext.As(&deviceContext4))) { if (SUCCEEDED(m_RenderDeviceContext.As(&deviceContext4))) {
if (SUCCEEDED(deviceContext4->Signal(m_PreviousFrameRenderedFence.Get(), m_PreviousFrameRenderedFenceValue + 1))) { if (SUCCEEDED(deviceContext4->Signal(m_PreviousFrameRenderedFence.Get(), m_PreviousFrameRenderedFenceValue + 1))) {
m_PreviousFrameRenderedFenceValue++; m_PreviousFrameRenderedFenceValue++;
} }
@ -962,7 +1115,7 @@ void D3D11VARenderer::notifyOverlayUpdated(Overlay::OverlayType type)
texData.SysMemPitch = newSurface->pitch; texData.SysMemPitch = newSurface->pitch;
ComPtr<ID3D11Texture2D> newTexture; ComPtr<ID3D11Texture2D> newTexture;
hr = m_Device->CreateTexture2D(&texDesc, &texData, &newTexture); hr = m_RenderDevice->CreateTexture2D(&texDesc, &texData, &newTexture);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_FreeSurface(newSurface); SDL_FreeSurface(newSurface);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -972,7 +1125,7 @@ void D3D11VARenderer::notifyOverlayUpdated(Overlay::OverlayType type)
} }
ComPtr<ID3D11ShaderResourceView> newTextureResourceView; ComPtr<ID3D11ShaderResourceView> newTextureResourceView;
hr = m_Device->CreateShaderResourceView((ID3D11Resource*)newTexture.Get(), nullptr, &newTextureResourceView); hr = m_RenderDevice->CreateShaderResourceView((ID3D11Resource*)newTexture.Get(), nullptr, &newTextureResourceView);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_FreeSurface(newSurface); SDL_FreeSurface(newSurface);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1038,7 +1191,7 @@ bool D3D11VARenderer::createOverlayVertexBuffer(Overlay::OverlayType type, int w
D3D11_SUBRESOURCE_DATA vbData = {}; D3D11_SUBRESOURCE_DATA vbData = {};
vbData.pSysMem = verts; vbData.pSysMem = verts;
HRESULT hr = m_Device->CreateBuffer(&vbDesc, &vbData, &newVertexBuffer); HRESULT hr = m_RenderDevice->CreateBuffer(&vbDesc, &vbData, &newVertexBuffer);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateBuffer() failed: %x", "ID3D11Device::CreateBuffer() failed: %x",
@ -1107,7 +1260,7 @@ bool D3D11VARenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO stateInfo)
// We must release all references to the back buffer // We must release all references to the back buffer
m_RenderTargetView.Reset(); m_RenderTargetView.Reset();
m_DeviceContext->Flush(); m_RenderDeviceContext->Flush();
HRESULT hr = m_SwapChain->ResizeBuffers(0, stateInfo->width, stateInfo->height, DXGI_FORMAT_UNKNOWN, swapchainDesc.Flags); HRESULT hr = m_SwapChain->ResizeBuffers(0, stateInfo->width, stateInfo->height, DXGI_FORMAT_UNKNOWN, swapchainDesc.Flags);
if (FAILED(hr)) { if (FAILED(hr)) {
@ -1175,7 +1328,7 @@ bool D3D11VARenderer::checkDecoderSupport(IDXGIAdapter* adapter)
} }
// Derive a ID3D11VideoDevice from our ID3D11Device. // Derive a ID3D11VideoDevice from our ID3D11Device.
hr = m_Device.As(&videoDevice); hr = m_RenderDevice.As(&videoDevice);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::QueryInterface(ID3D11VideoDevice) failed: %x", "ID3D11Device::QueryInterface(ID3D11VideoDevice) failed: %x",
@ -1394,16 +1547,16 @@ bool D3D11VARenderer::setupRenderingResources()
{ {
HRESULT hr; HRESULT hr;
m_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_RenderDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// We use a common vertex shader for all pixel shaders // We use a common vertex shader for all pixel shaders
{ {
QByteArray vertexShaderBytecode = Path::readDataFile("d3d11_vertex.fxc"); QByteArray vertexShaderBytecode = Path::readDataFile("d3d11_vertex.fxc");
ComPtr<ID3D11VertexShader> vertexShader; ComPtr<ID3D11VertexShader> vertexShader;
hr = m_Device->CreateVertexShader(vertexShaderBytecode.constData(), vertexShaderBytecode.length(), nullptr, &vertexShader); hr = m_RenderDevice->CreateVertexShader(vertexShaderBytecode.constData(), vertexShaderBytecode.length(), nullptr, &vertexShader);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->VSSetShader(vertexShader.Get(), nullptr, 0); m_RenderDeviceContext->VSSetShader(vertexShader.Get(), nullptr, 0);
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1418,9 +1571,9 @@ bool D3D11VARenderer::setupRenderingResources()
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
}; };
ComPtr<ID3D11InputLayout> inputLayout; ComPtr<ID3D11InputLayout> inputLayout;
hr = m_Device->CreateInputLayout(vertexDesc, ARRAYSIZE(vertexDesc), vertexShaderBytecode.constData(), vertexShaderBytecode.length(), &inputLayout); hr = m_RenderDevice->CreateInputLayout(vertexDesc, ARRAYSIZE(vertexDesc), vertexShaderBytecode.constData(), vertexShaderBytecode.length(), &inputLayout);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->IASetInputLayout(inputLayout.Get()); m_RenderDeviceContext->IASetInputLayout(inputLayout.Get());
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1433,7 +1586,7 @@ bool D3D11VARenderer::setupRenderingResources()
{ {
QByteArray overlayPixelShaderBytecode = Path::readDataFile("d3d11_overlay_pixel.fxc"); QByteArray overlayPixelShaderBytecode = Path::readDataFile("d3d11_overlay_pixel.fxc");
hr = m_Device->CreatePixelShader(overlayPixelShaderBytecode.constData(), overlayPixelShaderBytecode.length(), nullptr, &m_OverlayPixelShader); hr = m_RenderDevice->CreatePixelShader(overlayPixelShaderBytecode.constData(), overlayPixelShaderBytecode.length(), nullptr, &m_OverlayPixelShader);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreatePixelShader() failed: %x", "ID3D11Device::CreatePixelShader() failed: %x",
@ -1446,7 +1599,7 @@ bool D3D11VARenderer::setupRenderingResources()
{ {
QByteArray videoPixelShaderBytecode = Path::readDataFile(k_VideoShaderNames[i]); QByteArray videoPixelShaderBytecode = Path::readDataFile(k_VideoShaderNames[i]);
hr = m_Device->CreatePixelShader(videoPixelShaderBytecode.constData(), videoPixelShaderBytecode.length(), nullptr, &m_VideoPixelShaders[i]); hr = m_RenderDevice->CreatePixelShader(videoPixelShaderBytecode.constData(), videoPixelShaderBytecode.length(), nullptr, &m_VideoPixelShaders[i]);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreatePixelShader() failed: %x", "ID3D11Device::CreatePixelShader() failed: %x",
@ -1469,9 +1622,9 @@ bool D3D11VARenderer::setupRenderingResources()
samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
ComPtr<ID3D11SamplerState> sampler; ComPtr<ID3D11SamplerState> sampler;
hr = m_Device->CreateSamplerState(&samplerDesc, &sampler); hr = m_RenderDevice->CreateSamplerState(&samplerDesc, &sampler);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->PSSetSamplers(0, 1, sampler.GetAddressOf()); m_RenderDeviceContext->PSSetSamplers(0, 1, sampler.GetAddressOf());
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1497,9 +1650,9 @@ bool D3D11VARenderer::setupRenderingResources()
indexBufferData.SysMemPitch = sizeof(int); indexBufferData.SysMemPitch = sizeof(int);
ComPtr<ID3D11Buffer> indexBuffer; ComPtr<ID3D11Buffer> indexBuffer;
hr = m_Device->CreateBuffer(&indexBufferDesc, &indexBufferData, &indexBuffer); hr = m_RenderDevice->CreateBuffer(&indexBufferDesc, &indexBufferData, &indexBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->IASetIndexBuffer(indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); m_RenderDeviceContext->IASetIndexBuffer(indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0);
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1523,7 +1676,7 @@ bool D3D11VARenderer::setupRenderingResources()
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
hr = m_Device->CreateBlendState(&blendDesc, &m_OverlayBlendState); hr = m_RenderDevice->CreateBlendState(&blendDesc, &m_OverlayBlendState);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateBlendState() failed: %x", "ID3D11Device::CreateBlendState() failed: %x",
@ -1540,9 +1693,9 @@ bool D3D11VARenderer::setupRenderingResources()
blendDesc.RenderTarget[0].BlendEnable = FALSE; blendDesc.RenderTarget[0].BlendEnable = FALSE;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
hr = m_Device->CreateBlendState(&blendDesc, &m_VideoBlendState); hr = m_RenderDevice->CreateBlendState(&blendDesc, &m_VideoBlendState);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_DeviceContext->OMSetBlendState(m_VideoBlendState.Get(), nullptr, 0xffffffff); m_RenderDeviceContext->OMSetBlendState(m_VideoBlendState.Get(), nullptr, 0xffffffff);
} }
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -1566,7 +1719,7 @@ bool D3D11VARenderer::setupSwapchainDependentResources()
// Create our render target view // Create our render target view
{ {
ComPtr<ID3D11Resource> backBufferResource; ComPtr<ID3D11Resource> backBufferResource;
hr = m_SwapChain->GetBuffer(0, __uuidof(ID3D11Resource), (void**)&backBufferResource); hr = m_SwapChain->GetBuffer(0, IID_PPV_ARGS(&backBufferResource));
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGISwapChain::GetBuffer() failed: %x", "IDXGISwapChain::GetBuffer() failed: %x",
@ -1574,7 +1727,7 @@ bool D3D11VARenderer::setupSwapchainDependentResources()
return false; return false;
} }
hr = m_Device->CreateRenderTargetView(backBufferResource.Get(), nullptr, &m_RenderTargetView); hr = m_RenderDevice->CreateRenderTargetView(backBufferResource.Get(), nullptr, &m_RenderTargetView);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateRenderTargetView() failed: %x", "ID3D11Device::CreateRenderTargetView() failed: %x",
@ -1594,7 +1747,7 @@ bool D3D11VARenderer::setupSwapchainDependentResources()
viewport.MinDepth = 0; viewport.MinDepth = 0;
viewport.MaxDepth = 1; viewport.MaxDepth = 1;
m_DeviceContext->RSSetViewports(1, &viewport); m_RenderDeviceContext->RSSetViewports(1, &viewport);
} }
return true; return true;
@ -1605,9 +1758,44 @@ bool D3D11VARenderer::setupFrameRenderingResources(AVHWFramesContext* framesCont
{ {
auto d3d11vaFramesContext = (AVD3D11VAFramesContext*)framesContext->hwctx; auto d3d11vaFramesContext = (AVD3D11VAFramesContext*)framesContext->hwctx;
// Open the decoder texture array on the renderer device if we're using separate devices
if (m_DecodeDevice != m_RenderDevice) {
ComPtr<IDXGIResource1> dxgiDecoderResource;
HRESULT hr = d3d11vaFramesContext->texture_infos->texture->QueryInterface(IID_PPV_ARGS(&dxgiDecoderResource));
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Texture2D::QueryInterface(IDXGIResource1) failed: %x",
hr);
return false;
}
HANDLE sharedHandle;
hr = dxgiDecoderResource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &sharedHandle);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"IDXGIResource1::CreateSharedHandle() failed: %x",
hr);
return false;
}
hr = m_RenderDevice->OpenSharedResource1(sharedHandle, IID_PPV_ARGS(&m_RenderSharedTextureArray));
CloseHandle(sharedHandle);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device1::OpenSharedResource1() failed: %x",
hr);
return false;
}
}
else {
d3d11vaFramesContext->texture_infos->texture->AddRef();
m_RenderSharedTextureArray.Attach(d3d11vaFramesContext->texture_infos->texture);
}
// Query the format of the underlying texture array // Query the format of the underlying texture array
D3D11_TEXTURE2D_DESC textureDesc; D3D11_TEXTURE2D_DESC textureDesc;
d3d11vaFramesContext->texture_infos->texture->GetDesc(&textureDesc); m_RenderSharedTextureArray->GetDesc(&textureDesc);
m_TextureFormat = textureDesc.Format; m_TextureFormat = textureDesc.Format;
if (m_BindDecoderOutputTextures) { if (m_BindDecoderOutputTextures) {
@ -1660,7 +1848,7 @@ bool D3D11VARenderer::setupVideoTexture(AVHWFramesContext* framesContext)
texDesc.CPUAccessFlags = 0; texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0; texDesc.MiscFlags = 0;
hr = m_Device->CreateTexture2D(&texDesc, nullptr, &m_VideoTexture); hr = m_RenderDevice->CreateTexture2D(&texDesc, nullptr, &m_VideoTexture);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateTexture2D() failed: %x", "ID3D11Device::CreateTexture2D() failed: %x",
@ -1681,7 +1869,7 @@ bool D3D11VARenderer::setupVideoTexture(AVHWFramesContext* framesContext)
SDL_assert(srvIndex < m_VideoTextureResourceViews[0].size()); SDL_assert(srvIndex < m_VideoTextureResourceViews[0].size());
srvDesc.Format = srvFormat; srvDesc.Format = srvFormat;
hr = m_Device->CreateShaderResourceView(m_VideoTexture.Get(), &srvDesc, &m_VideoTextureResourceViews[0][srvIndex]); hr = m_RenderDevice->CreateShaderResourceView(m_VideoTexture.Get(), &srvDesc, &m_VideoTextureResourceViews[0][srvIndex]);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateShaderResourceView() failed: %x", "ID3D11Device::CreateShaderResourceView() failed: %x",
@ -1723,9 +1911,9 @@ bool D3D11VARenderer::setupTexturePoolViews(AVHWFramesContext* framesContext)
SDL_assert(srvIndex < m_VideoTextureResourceViews[i].size()); SDL_assert(srvIndex < m_VideoTextureResourceViews[i].size());
srvDesc.Format = srvFormat; srvDesc.Format = srvFormat;
hr = m_Device->CreateShaderResourceView(d3d11vaFramesContext->texture_infos[i].texture, hr = m_RenderDevice->CreateShaderResourceView(m_RenderSharedTextureArray.Get(),
&srvDesc, &srvDesc,
&m_VideoTextureResourceViews[i][srvIndex]); &m_VideoTextureResourceViews[i][srvIndex]);
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ID3D11Device::CreateShaderResourceView() failed: %x", "ID3D11Device::CreateShaderResourceView() failed: %x",

View file

@ -52,6 +52,12 @@ private:
void renderVideo(AVFrame* frame); void renderVideo(AVFrame* frame);
bool checkDecoderSupport(IDXGIAdapter* adapter); bool checkDecoderSupport(IDXGIAdapter* adapter);
bool createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound = nullptr); bool createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound = nullptr);
bool setupSharedDevice(IDXGIAdapter1* adapter);
static bool createSharedFencePair(UINT64 initialValue,
ID3D11Device5* dev1, ID3D11Device5* dev2,
Microsoft::WRL::ComPtr<ID3D11Fence>& dev1Fence,
Microsoft::WRL::ComPtr<ID3D11Fence>& dev2Fence);
int m_DecoderSelectionPass; int m_DecoderSelectionPass;
int m_DevicesWithFL11Support; int m_DevicesWithFL11Support;
@ -65,9 +71,10 @@ private:
Microsoft::WRL::ComPtr<IDXGIFactory5> m_Factory; Microsoft::WRL::ComPtr<IDXGIFactory5> m_Factory;
int m_AdapterIndex; int m_AdapterIndex;
Microsoft::WRL::ComPtr<ID3D11Device> m_Device; Microsoft::WRL::ComPtr<ID3D11Device5> m_RenderDevice, m_DecodeDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext4> m_RenderDeviceContext, m_DecodeDeviceContext;
Microsoft::WRL::ComPtr<ID3D11Texture2D> m_RenderSharedTextureArray;
Microsoft::WRL::ComPtr<IDXGISwapChain4> m_SwapChain; Microsoft::WRL::ComPtr<IDXGISwapChain4> m_SwapChain;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_DeviceContext;
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_RenderTargetView; Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_RenderTargetView;
Microsoft::WRL::ComPtr<ID3D11BlendState> m_VideoBlendState; Microsoft::WRL::ComPtr<ID3D11BlendState> m_VideoBlendState;
Microsoft::WRL::ComPtr<ID3D11BlendState> m_OverlayBlendState; Microsoft::WRL::ComPtr<ID3D11BlendState> m_OverlayBlendState;
@ -76,8 +83,9 @@ private:
Microsoft::WRL::ComPtr<ID3D11Fence> m_PreviousFrameRenderedFence; Microsoft::WRL::ComPtr<ID3D11Fence> m_PreviousFrameRenderedFence;
Microsoft::WRL::Wrappers::Event m_PreviousFrameRenderedEvent; Microsoft::WRL::Wrappers::Event m_PreviousFrameRenderedEvent;
UINT64 m_PreviousFrameRenderedFenceValue; UINT64 m_PreviousFrameRenderedFenceValue;
Microsoft::WRL::ComPtr<ID3D11Fence> m_DecoderShaderBindFence; Microsoft::WRL::ComPtr<ID3D11Fence> m_DecodeD2RFence, m_RenderD2RFence;
UINT64 m_DecoderShaderBindFenceValue; Microsoft::WRL::ComPtr<ID3D11Fence> m_DecodeR2DFence, m_RenderR2DFence;
UINT64 m_DecodeRenderSyncFenceValue;
SDL_mutex* m_ContextLock; SDL_mutex* m_ContextLock;
bool m_BindDecoderOutputTextures; bool m_BindDecoderOutputTextures;