Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-120276

Crash when reparent a native child to a different tlw if QT_WIDGETS_RHI=1 is set on Windows

    XMLWordPrintable

Details

    • Windows
    • cff5a49cc (dev), eb3529ea3 (6.7), 87d075f7b (6.6), 51886d7c9 (tqtc/lts-6.5), 1bd755465 (dev), 81581819c (6.7), 4f2c36dd4 (6.6), 294fb8e9b (tqtc/lts-6.5)

    Description

      Please use this code to reproduce.

      #include <QtWidgets/QApplication>
      #include <QtWidgets/QWidget>
      #include <QtCore/QTimer>
      
      int main(int argc, char** argv)
      {
          // QT_WIDGETS_RHI is the suggested workaround to avoid re-creating native windows in Qt 6.5.
          // Windows: DX11 RHI. This is the default. Just to make sure DX11 is used.
          // All native windows will have a DX11 swapchain.
          qputenv("QT_WIDGETS_RHI", "1");
          qputenv("QT_WIDGETS_RHI_BACKEND", "d3d11");
      
          QApplication app(argc, argv);
      
          // The first top-level window with a native child.
          QWidget tlw1;
          tlw1.resize(640, 480);
          tlw1.setStyleSheet("QWidget { background-color: red; }");
      
          // The child is a native window. QT_WIDGETS_RHI=1 so it's RHI flush.
          QWidget child(&tlw1);
          child.setGeometry(0, 0, 640, 480);
          child.setStyleSheet("QWidget { background-color: green; }");
          child.winId();
      
          // Wait for the child to show. RHI flush will create a DX11 swapchain for the child native window.
          // But the swap chain is owned by tlw1.
          QTimer::singleShot(1000, [&]() {
              // The second top-level window.
              QWidget* tlw2 = new QWidget();
              tlw2->resize(640, 480);
              tlw2->setStyleSheet("QWidget { background-color: blue; }");
      
              // Now reparent the native child from tlw1 into tlw2.
              child.setParent(tlw2);
              child.setGeometry(0, 0, 640, 480);
      
              // Show the second top-level window.
              tlw2->show();
      
              // Qt will create a new DX11 swap chain for the native child window in tlw2. But there is already a
              // swap chain and it's not released.
              // Windows will return Access Denied error when calling CreateSwapChainForHwnd().
      
              // Failed to create D3D11 swapchain: COM error 0x80070005: Access is denied.
              // Failed to create swapchain for window flushed with an RHI - enabled backingstore
              // Crash in RHI flush.
          });
      
          tlw1.show();
          return app.exec();
      }
      

      This can also be reproduced without any environment variable override

      #include <QtWidgets/QApplication>
      #include <QtWidgets/QWidget>
      #include <QtCore/QTimer>
      #include <QtWebEngineWidgets/QWebEngineView>
      
      int main(int argc, char** argv)
      {
          QApplication app(argc, argv);
      
          // The first top-level window with a native child.
          QWidget tlw1;
          tlw1.resize(640, 480);
          tlw1.setStyleSheet("QWidget { background-color: red; }");
      
          // The child is a native window.
          QWidget child(&tlw1);
          child.setGeometry(0, 0, 640, 480);
          child.setStyleSheet("QWidget { background-color: green; }");
          child.winId();
      
          // This will cause both tlw and the native child to be RHI flush.
          QWebEngineView web(&child);
          web.setGeometry(0, 0, 640, 480);
          web.setUrl(QUrl("chrome://gpu"));
      
          // Wait for the child to show. RHI flush will create a DX11 swapchain for the child native window.
          // But the swap chain is owned by tlw1.
          QTimer::singleShot(1000, [&]() {
              // The second top-level window.
              QWidget* tlw2 = new QWidget();
              tlw2->resize(640, 480);
              tlw2->setStyleSheet("QWidget { background-color: blue; }");
      
              // Now reparent the native child from tlw1 into tlw2.
              child.setParent(tlw2);
              child.setGeometry(0, 0, 640, 480);
      
              // Show the second top-level window.
              tlw2->show();
      
              // Qt will create a new DX11 swap chain for the native child window in tlw2. But there is already a
              // swap chain and it's not released.
              // Windows will return Access Denied error when calling CreateSwapChainForHwnd().
      
              // Failed to create D3D11 swapchain: COM error 0x80070005: Access is denied.
              // Failed to create swapchain for window flushed with an RHI - enabled backingstore
              // Crash in RHI flush.
          });
      
          tlw1.show();
          return app.exec();
      }
      

      The previous swap chain is not released. Even all references are released, the swap chain will not be removed immediately. FYI: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush#deferred-destruction-issues-with-flip-presentation-swap-chains

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-120276
          # Subject Branch Project Status CR V

          Activity

            People

              vestbo Tor Arne Vestbø
              mingxiang Mingxiang Xu
              Votes:
              3 Vote for this issue
              Watchers:
              9 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: