Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsd3d11texturebridge_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/private/qsystemerror_p.h>
#include <dxgi1_2.h>
QT_BEGIN_NAMESPACE
namespace QtMultimediaPrivate {
namespace {
Q_LOGGING_CATEGORY(lcD3D11TextureBridge, "qt.multimedia.windows.d3d11texturebridge");
}
// Timeout for keyed mutex acquisition. Using a finite timeout prevents
// deadlocks when the D3D device is lost between AcquireSync and ReleaseSync.
static constexpr DWORD kMutexTimeoutMs = 5000;
bool QWindowsD3D11TextureBridge::copyToSharedTex(ID3D11Device *dev, ID3D11DeviceContext *ctx,
const ComPtr<ID3D11Texture2D> &tex,
const QSize &frameSize, UINT index)
{
Q_ASSERT(dev);
Q_ASSERT(ctx);
Q_ASSERT(tex);
if (!ensureSrcTex(dev, tex, frameSize))
return false;
// Flush to ensure that texture is fully updated before we share it.
ctx->Flush();
if (const HRESULT hr = m_srcMutex->AcquireSync(kSrcKey, kMutexTimeoutMs); hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "AcquireSync failed:" << QSystemError::windowsComString(hr);
return false;
}
const UINT width = static_cast<UINT>(frameSize.width());
const UINT height = static_cast<UINT>(frameSize.height());
// A crop box is needed because the source texture may be bigger than the
// frame size, e.g. to account for a decoder's surface alignment requirements.
const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
m_srcMutex->ReleaseSync(kDestKey);
return true;
}
bool QWindowsD3D11TextureBridge::copyToSharedTex(const ComPtr<ID3D11Device> &device,
const ComPtr<ID3D11DeviceContext> &deviceContext,
const ComPtr<ID3D11Texture2D> &texture,
const QSize &frameSize, UINT index)
{
return copyToSharedTex(device.Get(), deviceContext.Get(), texture, frameSize, index);
}
ComPtr<ID3D11Texture2D>
QWindowsD3D11TextureBridge::copyFromSharedTex(const ComPtr<ID3D11Device1> &dev,
const ComPtr<ID3D11DeviceContext> &ctx)
{
if (!ensureDestTex(dev))
return {};
if (!copyFromSharedTex(dev, ctx, m_outputTex))
return {};
return m_outputTex;
}
bool QWindowsD3D11TextureBridge::copyFromSharedTex(const ComPtr<ID3D11Device1> &dev,
const ComPtr<ID3D11DeviceContext> &ctx,
const ComPtr<ID3D11Texture2D> &destination)
{
Q_ASSERT(destination);
if (!ensureDestTex(dev))
return false;
if (const HRESULT hr = m_destMutex->AcquireSync(kDestKey, kMutexTimeoutMs); hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "AcquireSync failed:" << QSystemError::windowsComString(hr);
return false;
}
ctx->CopySubresourceRegion(destination.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
m_destMutex->ReleaseSync(kSrcKey);
return true;
}
bool QWindowsD3D11TextureBridge::ensureDestTex(const ComPtr<ID3D11Device1> &dev)
{
if (m_destDevice != dev) {
// Destination device changed. Recreate texture.
m_destTex = nullptr;
m_destDevice = dev;
}
if (m_destTex)
return true;
if (const HRESULT hr =
m_destDevice->OpenSharedResource1(m_sharedHandle.get(), IID_PPV_ARGS(&m_destTex));
hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "OpenSharedResource1 failed:" << QSystemError::windowsComString(hr);
return false;
}
CD3D11_TEXTURE2D_DESC desc{};
m_destTex->GetDesc(&desc);
desc.MiscFlags = 0;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
if (const HRESULT hr =
m_destDevice->CreateTexture2D(&desc, nullptr, m_outputTex.ReleaseAndGetAddressOf());
hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "CreateTexture2D failed:" << QSystemError::windowsComString(hr);
return false;
}
if (const HRESULT hr = m_destTex.As(&m_destMutex); hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "QueryInterface(IDXGIKeyedMutex) failed:" << QSystemError::windowsComString(hr);
return false;
}
return true;
}
bool QWindowsD3D11TextureBridge::ensureSrcTex(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex,
const QSize &frameSize)
{
if (!isSrcInitialized(dev, tex, frameSize))
return recreateSrc(dev, tex, frameSize);
return true;
}
bool QWindowsD3D11TextureBridge::isSrcInitialized(const ID3D11Device *dev,
const ComPtr<ID3D11Texture2D> &tex,
const QSize &frameSize) const
{
if (!m_srcTex)
return false;
// Check if device has changed
ComPtr<ID3D11Device> texDevice;
m_srcTex->GetDevice(texDevice.GetAddressOf());
if (dev != texDevice.Get())
return false;
// Check if shared texture has correct size and format
CD3D11_TEXTURE2D_DESC inputDesc{};
tex->GetDesc(&inputDesc);
CD3D11_TEXTURE2D_DESC currentDesc{};
m_srcTex->GetDesc(&currentDesc);
if (inputDesc.Format != currentDesc.Format)
return false;
const UINT width = static_cast<UINT>(frameSize.width());
const UINT height = static_cast<UINT>(frameSize.height());
if (currentDesc.Width != width || currentDesc.Height != height)
return false;
return true;
}
bool QWindowsD3D11TextureBridge::recreateSrc(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex,
const QSize &frameSize)
{
m_sharedHandle.close();
CD3D11_TEXTURE2D_DESC desc{};
tex->GetDesc(&desc);
const UINT width = static_cast<UINT>(frameSize.width());
const UINT height = static_cast<UINT>(frameSize.height());
CD3D11_TEXTURE2D_DESC texDesc{ desc.Format, width, height };
texDesc.MipLevels = 1;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
if (const HRESULT hr =
dev->CreateTexture2D(&texDesc, nullptr, m_srcTex.ReleaseAndGetAddressOf());
hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "CreateTexture2D failed:" << QSystemError::windowsComString(hr);
return false;
}
ComPtr<IDXGIResource1> res;
if (const HRESULT hr = m_srcTex.As(&res); hr != S_OK) {
qCWarning(lcD3D11TextureBridge)
<< "QueryInterface(IDXGIResource1) failed:" << QSystemError::windowsComString(hr);
return false;
}
const HRESULT hr =
res->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &m_sharedHandle);
if (hr != S_OK || !m_sharedHandle) {
qCWarning(lcD3D11TextureBridge)
<< "CreateSharedHandle failed:" << QSystemError::windowsComString(hr);
return false;
}
if (const HRESULT mutexHr = m_srcTex.As(&m_srcMutex); mutexHr != S_OK || !m_srcMutex) {
qCWarning(lcD3D11TextureBridge) << "QueryInterface(IDXGIKeyedMutex) failed:"
<< QSystemError::windowsComString(mutexHr);
return false;
}
m_destTex = nullptr;
m_destMutex = nullptr;
return true;
}
} // namespace QtMultimediaPrivate
QT_END_NAMESPACE