rhi: d3d11: Rework swapchain effect handling

Use FLIP_DISCARD swapchains only on Win10, stick
with DISCARD otherwise. This may fix the swapchain
creation problems on Windows 7.

Add a QT_D3D_NO_FLIP env.var. to make it possible to
disable using FLIP_DISCARD even on Win10. This is there
for troubleshooting purposes.

Finally, fix the backbuffer handling. What we originally
ported from the D3D12 backend of Qt Quick is not quite how
DXGI used to work with D3D11 and earlier. GetBuffer() can
only be used to query index 0, and that's the backbuffer,
the rest is managed internally. Follow this model.

As an added bonus, disable Alt+Enter.

Change-Id: Ie5c7a1e813864e7f873d55bc72cb22fc09213a05
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: André de la Rocha <andre.rocha@qt.io>
Reviewed-by: Christian Strømme <christian.stromme@qt.io>
This commit is contained in:
Laszlo Agocs 2019-08-12 16:15:40 +02:00
parent 60599486e8
commit 1ae39cc72b
2 changed files with 75 additions and 43 deletions

View File

@ -196,14 +196,22 @@ bool QRhiD3D11::create(QRhi::Flags flags)
devFlags |= D3D11_CREATE_DEVICE_DEBUG;
dxgiFactory = createDXGIFactory2();
if (dxgiFactory != nullptr)
if (dxgiFactory != nullptr) {
hasDxgi2 = true;
else
supportsFlipDiscardSwapchain = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
&& !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
} else {
dxgiFactory = createDXGIFactory1();
hasDxgi2 = false;
supportsFlipDiscardSwapchain = false;
}
if (dxgiFactory == nullptr)
return false;
qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s",
hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false");
if (!importedDevice) {
IDXGIAdapter1 *adapterToUse = nullptr;
IDXGIAdapter1 *adapter;
@ -916,7 +924,7 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->cb.resetState();
swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot];
swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
QRHI_PROF_F(beginSwapChainFrame(swapChain));
@ -945,7 +953,7 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
executeCommandBuffer(&swapChainD->cb);
if (swapChainD->sampleDesc.Count > 1) {
context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0,
context->ResolveSubresource(swapChainD->backBufferTex, 0,
swapChainD->msaaTex[currentFrameSlot], 0,
swapChainD->colorFormat);
}
@ -1329,14 +1337,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
// has to be supported. Insert a resolve.
QD3D11CommandBuffer::Command rcmd;
rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot];
rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
rcmd.args.resolveSubRes.dstSubRes = 0;
rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
rcmd.args.resolveSubRes.srcSubRes = 0;
rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
cbD->commands.append(rcmd);
}
src = swapChainD->tex[swapChainD->currentFrameSlot];
src = swapChainD->backBufferTex;
dxgiFormat = swapChainD->colorFormat;
pixelSize = swapChainD->pixelSize;
format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
@ -3528,9 +3536,9 @@ QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
rt(rhi),
cb(rhi)
{
backBufferTex = nullptr;
backBufferRtv = nullptr;
for (int i = 0; i < BUFFER_COUNT; ++i) {
tex[i] = nullptr;
rtv[i] = nullptr;
msaaTex[i] = nullptr;
msaaRtv[i] = nullptr;
timestampActive[i] = false;
@ -3547,15 +3555,15 @@ QD3D11SwapChain::~QD3D11SwapChain()
void QD3D11SwapChain::releaseBuffers()
{
if (backBufferRtv) {
backBufferRtv->Release();
backBufferRtv = nullptr;
}
if (backBufferTex) {
backBufferTex->Release();
backBufferTex = nullptr;
}
for (int i = 0; i < BUFFER_COUNT; ++i) {
if (rtv[i]) {
rtv[i]->Release();
rtv[i] = nullptr;
}
if (tex[i]) {
tex[i]->Release();
tex[i] = nullptr;
}
if (msaaRtv[i]) {
msaaRtv[i]->Release();
msaaRtv[i] = nullptr;
@ -3681,18 +3689,19 @@ bool QD3D11SwapChain::buildOrResize()
const UINT swapChainFlags = 0;
QRHI_RES_RHI(QRhiD3D11);
const bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain;
if (!swapChain) {
HWND hwnd = reinterpret_cast<HWND>(window->winId());
sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
// We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
// old DISCARD with back buffer count == 1). This makes no difference for
// the rest of the stuff except that automatic MSAA is unsupported and
// needs to be implemented via a custom multisample render target and an
// explicit resolve.
HRESULT hr;
if (rhiD->hasDxgi2) {
if (useFlipDiscard) {
// We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
// old DISCARD with back buffer count == 1). This makes no difference for
// the rest of the stuff except that automatic MSAA is unsupported and
// needs to be implemented via a custom multisample render target and an
// explicit resolve.
DXGI_SWAP_CHAIN_DESC1 desc;
memset(&desc, 0, sizeof(desc));
desc.Width = pixelSize.width();
@ -3715,7 +3724,10 @@ bool QD3D11SwapChain::buildOrResize()
if (SUCCEEDED(hr))
swapChain = sc1;
} else {
// Windows 7
// Windows 7 for instance. Use DISCARD mode. Regardless, keep on
// using our manual resolve for symmetry with the FLIP_DISCARD code
// path when MSAA is requested.
DXGI_SWAP_CHAIN_DESC desc;
memset(&desc, 0, sizeof(desc));
desc.BufferDesc.Width = pixelSize.width();
@ -3725,10 +3737,10 @@ bool QD3D11SwapChain::buildOrResize()
desc.BufferDesc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = BUFFER_COUNT;
desc.BufferCount = 1;
desc.OutputWindow = hwnd;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = swapChainFlags;
hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
@ -3737,30 +3749,49 @@ bool QD3D11SwapChain::buildOrResize()
qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
} else {
releaseBuffers();
HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags);
const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
HRESULT hr = swapChain->ResizeBuffers(count, pixelSize.width(), pixelSize.height(),
colorFormat, swapChainFlags);
if (FAILED(hr)) {
qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
}
// This looks odd (for FLIP_DISCARD, esp. compared with backends for Vulkan
// & co.) but the backbuffer is always at index 0, with magic underneath.
// Some explanation from
// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
//
// "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
// Every call to Present implicitly changed the resource identity of the
// returned interface. Direct3D 12 no longer supports that implicit
// resource identity change, due to the CPU overhead required and the
// flexible resource descriptor design. As a result, the application must
// manually call GetBuffer for every each buffer created with the
// swapchain."
// So just query index 0 once (per resize) and be done with it.
HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&backBufferTex));
if (FAILED(hr)) {
qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
return false;
}
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
memset(&rtvDesc, 0, sizeof(rtvDesc));
rtvDesc.Format = srgbAdjustedFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
if (FAILED(hr)) {
qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
return false;
}
// Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
for (int i = 0; i < BUFFER_COUNT; ++i) {
HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&tex[i]));
if (FAILED(hr)) {
qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
return false;
}
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
memset(&rtvDesc, 0, sizeof(rtvDesc));
rtvDesc.Format = srgbAdjustedFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]);
if (FAILED(hr)) {
qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
return false;
}
if (sampleDesc.Count > 1) {
if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
return false;

View File

@ -523,8 +523,8 @@ struct QD3D11SwapChain : public QRhiSwapChain
DXGI_FORMAT colorFormat;
IDXGISwapChain *swapChain = nullptr;
static const int BUFFER_COUNT = 2;
ID3D11Texture2D *tex[BUFFER_COUNT];
ID3D11RenderTargetView *rtv[BUFFER_COUNT];
ID3D11Texture2D *backBufferTex;
ID3D11RenderTargetView *backBufferRtv;
ID3D11Texture2D *msaaTex[BUFFER_COUNT];
ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
DXGI_SAMPLE_DESC sampleDesc;
@ -656,6 +656,7 @@ public:
ID3DUserDefinedAnnotation *annotations = nullptr;
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
bool supportsFlipDiscardSwapchain = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
struct {