[vlc-commits] [Git][videolan/vlc][master] 3 commits: qt: initialize compositor in two phases

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Fri Aug 6 17:18:08 UTC 2021



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
6725dc70 by Pierre Lamot at 2021-08-06T16:58:27+00:00
qt: initialize compositor in two phases

some compositor requires to set Qt Attributes before Qt application
is started such as selecting the right graphical backend (ie: dcomp needs to set
OpenGLES). And some compositor needs know wich QPA is in use (ie: x11 vs
wayland), this can only be known after QApp is started.

Co-authored-by: Guillaume Nicolussi Castellan <nullgemm at videolabs.io>

- - - - -
7ac06259 by Pierre Lamot at 2021-08-06T16:58:27+00:00
qt: perform sanity checks before initialising dcomp compositor

 ensure that the platform is able to use direct composition and can use
 shared textures before initialising it.

 fix: #24847

- - - - -
bb14abfc by Pierre Lamot at 2021-08-06T16:58:27+00:00
qt: allow to force the compositor from command line

- - - - -


9 changed files:

- modules/gui/qt/maininterface/compositor.cpp
- modules/gui/qt/maininterface/compositor.hpp
- modules/gui/qt/maininterface/compositor_dcomp.cpp
- modules/gui/qt/maininterface/compositor_dcomp.hpp
- modules/gui/qt/maininterface/compositor_dummy.cpp
- modules/gui/qt/maininterface/compositor_dummy.hpp
- modules/gui/qt/maininterface/compositor_win7.cpp
- modules/gui/qt/maininterface/compositor_win7.hpp
- modules/gui/qt/qt.cpp


Changes:

=====================================
modules/gui/qt/maininterface/compositor.cpp
=====================================
@@ -28,26 +28,62 @@
 
 namespace vlc {
 
-Compositor* Compositor::createCompositor(qt_intf_t *p_intf)
-{
-    bool ret;
-    VLC_UNUSED(ret);
+template<typename T>
+static Compositor* instanciateCompositor(qt_intf_t *p_intf) {
+    return new T(p_intf);
+}
+
+template<typename T>
+static bool preInit(qt_intf_t *p_intf) {
+    return T::preInit(p_intf);
+}
+
+struct {
+    const char* name;
+    Compositor* (*instanciate)(qt_intf_t *p_intf);
+    bool (*preInit)(qt_intf_t *p_intf);
+} static compositorList[] = {
 #ifdef _WIN32
 #ifdef HAVE_DCOMP_H
-    CompositorDirectComposition* dcomp_compositor = new CompositorDirectComposition(p_intf);
-    ret = dcomp_compositor->init();
-    if (ret)
-        return dcomp_compositor;
-    delete dcomp_compositor;
-    msg_Dbg(p_intf, "failed to create DirectComposition backend, use fallback");
+    {"dcomp", &instanciateCompositor<CompositorDirectComposition>, &preInit<CompositorDirectComposition> },
 #endif
-    CompositorWin7* win7_compositor = new CompositorWin7(p_intf);
-    if (win7_compositor->init())
-        return win7_compositor;
-    delete win7_compositor;
-    msg_Dbg(p_intf, "failed to create Win7 compositor backend, use fallback");
+    {"win7", &instanciateCompositor<CompositorWin7>, &preInit<CompositorWin7> },
 #endif
-    return new CompositorDummy(p_intf);
+    {"dummy", &instanciateCompositor<CompositorDummy>, &preInit<CompositorDummy> }
+};
+
+CompositorFactory::CompositorFactory(qt_intf_t *p_intf, const char* compositor)
+    : m_intf(p_intf)
+    , m_compositorName(compositor)
+{
+}
+
+bool CompositorFactory::preInit()
+{
+    for (; m_compositorIndex < ARRAY_SIZE(compositorList); m_compositorIndex++)
+    {
+        if (m_compositorName == "auto" || m_compositorName == compositorList[m_compositorIndex].name)
+        {
+            if (compositorList[m_compositorIndex].preInit(m_intf))
+                return true;
+        }
+    }
+    return false;
+}
+
+Compositor* CompositorFactory::createCompositor()
+{
+    for (; m_compositorIndex < ARRAY_SIZE(compositorList); m_compositorIndex++)
+    {
+        if (m_compositorName == "auto" || m_compositorName == compositorList[m_compositorIndex].name)
+        {
+            Compositor* compositor = compositorList[m_compositorIndex].instanciate(m_intf);
+            if (compositor->init())
+                return compositor;
+        }
+    }
+    msg_Err(m_intf, "no suitable compositor found");
+    return nullptr;
 }
 
 void Compositor::onWindowDestruction(vout_window_t *p_wnd)


=====================================
modules/gui/qt/maininterface/compositor.hpp
=====================================
@@ -48,6 +48,8 @@ public:
 public:
     virtual ~Compositor() = default;
 
+    virtual bool init() = 0;
+
     virtual MainInterface* makeMainInterface() = 0;
     virtual void destroyMainInterface() = 0;
 
@@ -57,15 +59,56 @@ public:
 
     virtual Type type() const = 0;
 
-    //factory
-    static Compositor* createCompositor(qt_intf_t *p_intf);
-
 protected:
     void onWindowDestruction(vout_window_t *p_wnd);
 
     VoutDestroyCb m_destroyCb = nullptr;
 };
 
+/**
+ * @brief The CompositorFactory class will instanciate a compositor
+ * in auto mode, compositor will be instantiated from the list by order declaration,
+ * compositor can be explicitly defined by passing its name.
+ *
+ * the usual scenario is:
+ *
+ *   - call to preInit that will try to preInit compositors from list until we find
+ *     a matching candidate
+ *
+ *   - start Qt main loop
+ *
+ *   - call to createCompositor to instantiate the compositor, if it fails it will
+ *     try to initialize the next compositors from the list
+ */
+class CompositorFactory {
+public:
+
+    CompositorFactory(qt_intf_t *p_intf, const char* compositor = "auto");
+
+    /**
+     * @brief preInit will check whether a compositor can be used, before starting Qt,
+     * each candidate will may perform some basic checks and can setup Qt enviroment variable if required
+     *
+     * @note if a compositor return true on preinit but fails to initialize afterwards, next
+     * compositor in chain will be initialized without the preinit phaze (as Qt will be already started)
+     * this might lead to an unstable configuration if incompatible operations are done in the preInit phase
+     *
+     * @return true if a compositor can be instantiated
+     */
+    bool preInit();
+
+    /**
+     * @brief createCompositor will instantiate a compositor
+     *
+     * @return the instantaied compositor, null if no compsitor can be instanciated
+     */
+    Compositor* createCompositor();
+
+private:
+    qt_intf_t* m_intf = nullptr;
+    QString m_compositorName;
+    size_t m_compositorIndex = 0;
+};
 
 }
 


=====================================
modules/gui/qt/maininterface/compositor_dcomp.cpp
=====================================
@@ -29,6 +29,7 @@
 #include <QApplication>
 #include <QDesktopWidget>
 #include <QQuickWidget>
+#include <QLibrary>
 
 #include <QOpenGLFunctions>
 #include <QOpenGLFramebufferObject>
@@ -140,6 +141,98 @@ CompositorDirectComposition::~CompositorDirectComposition()
         FreeLibrary(m_dcomp_dll);
 }
 
+bool CompositorDirectComposition::preInit(qt_intf_t * p_intf)
+{
+    //import DirectComposition API (WIN8+)
+    QLibrary dcompDll("DCOMP.dll");
+    if (!dcompDll.load())
+        return false;
+    DCompositionCreateDeviceFun myDCompositionCreateDevice = (DCompositionCreateDeviceFun)dcompDll.resolve("DCompositionCreateDevice");
+    if (!myDCompositionCreateDevice)
+    {
+        msg_Dbg(p_intf, "Direct Composition is not present, can't initialize direct composition");
+        return false;
+    }
+
+    //check whether D3DCompiler is available. whitout it Angle won't work
+    QLibrary d3dCompilerDll;
+    for (int i = 47; i > 41; --i)
+    {
+        d3dCompilerDll.setFileName(QString("D3DCOMPILER_%1.dll").arg(i));
+        if (d3dCompilerDll.load())
+            break;
+    }
+    if (!d3dCompilerDll.isLoaded())
+    {
+        msg_Dbg(p_intf, "can't find d3dcompiler_xx.dll, can't initialize direct composition");
+        return false;
+    }
+
+    HRESULT hr;
+    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT
+        //| D3D11_CREATE_DEVICE_DEBUG
+            ;
+
+    D3D_FEATURE_LEVEL requestedFeatureLevels[] = {
+        D3D_FEATURE_LEVEL_11_1,
+        D3D_FEATURE_LEVEL_11_0,
+    };
+
+    ComPtr<ID3D11Device> d3dDevice;
+    hr = D3D11CreateDevice(
+        nullptr,    // Adapter
+        D3D_DRIVER_TYPE_HARDWARE,
+        nullptr,    // Module
+        creationFlags,
+        requestedFeatureLevels,
+        ARRAY_SIZE(requestedFeatureLevels),
+        D3D11_SDK_VERSION,
+        d3dDevice.GetAddressOf(),
+        nullptr,    // Actual feature level
+        nullptr);
+
+    if (FAILED(hr))
+    {
+        msg_Dbg(p_intf, "can't create D3D11 device, can't initialize direct composition");
+        return false;
+    }
+
+    //check that we can create a shared texture
+    D3D11_FEATURE_DATA_D3D11_OPTIONS d3d11Options;
+    HRESULT checkFeatureHR = d3dDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &d3d11Options, sizeof(d3d11Options));
+
+    D3D11_TEXTURE2D_DESC texDesc = { };
+    texDesc.MipLevels = 1;
+    texDesc.SampleDesc.Count = 1;
+    texDesc.MiscFlags = 0;
+    texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+    texDesc.Usage = D3D11_USAGE_DEFAULT;
+    texDesc.CPUAccessFlags = 0;
+    texDesc.ArraySize = 1;
+    texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    texDesc.Height = 16;
+    texDesc.Width  = 16;
+    if (SUCCEEDED(checkFeatureHR) && d3d11Options.ExtendedResourceSharing) //D3D11.1 feature
+        texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
+    else
+        texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+
+    ComPtr<ID3D11Texture2D> d3dTmpTexture;
+    hr = d3dDevice->CreateTexture2D( &texDesc, NULL, &d3dTmpTexture );
+    if (FAILED(hr))
+    {
+        msg_Dbg(p_intf, "can't create shared texture, can't initialize direct composition");
+        return false;
+    }
+
+    //sanity check succeeded, we can now setup global Qt settings
+
+    //force usage of ANGLE backend
+    QApplication::setAttribute( Qt::AA_UseOpenGLES );
+
+    return true;
+}
+
 bool CompositorDirectComposition::init()
 {
     //import DirectComposition API (WIN8+)
@@ -187,8 +280,6 @@ bool CompositorDirectComposition::init()
     if (FAILED(hr))
         return false;
 
-    QApplication::setAttribute( Qt::AA_UseOpenGLES ); //force usage of ANGLE backend
-
     return true;
 }
 


=====================================
modules/gui/qt/maininterface/compositor_dcomp.hpp
=====================================
@@ -45,7 +45,8 @@ public:
     CompositorDirectComposition(qt_intf_t *p_intf, QObject* parent = nullptr);
     ~CompositorDirectComposition();
 
-    bool init();
+    static bool preInit(qt_intf_t *);
+    bool init() override;
 
     MainInterface *makeMainInterface() override;
     void destroyMainInterface() override;
@@ -70,6 +71,7 @@ private:
     qt_intf_t *m_intf = nullptr;
 
     MainInterface* m_rootWindow = nullptr;
+
     std::unique_ptr<CompositorDCompositionUISurface> m_uiSurface;
     vout_window_t *m_window = nullptr;
     std::unique_ptr<MainUI> m_ui;


=====================================
modules/gui/qt/maininterface/compositor_dummy.cpp
=====================================
@@ -29,6 +29,16 @@ CompositorDummy::CompositorDummy(qt_intf_t *p_intf, QObject* parent)
 {
 }
 
+bool CompositorDummy::preInit(qt_intf_t *)
+{
+    return true;
+}
+
+bool CompositorDummy::init()
+{
+    return true;
+}
+
 MainInterface* CompositorDummy::makeMainInterface()
 {
     m_rootWindow = new MainInterface(m_intf);


=====================================
modules/gui/qt/maininterface/compositor_dummy.hpp
=====================================
@@ -37,6 +37,9 @@ public:
     CompositorDummy(qt_intf_t *p_intf, QObject* parent = nullptr);
     virtual ~CompositorDummy() = default;
 
+    static bool preInit(qt_intf_t*);
+    virtual bool init() override;
+
     virtual MainInterface *makeMainInterface() override;
 
     /**


=====================================
modules/gui/qt/maininterface/compositor_win7.cpp
=====================================
@@ -96,7 +96,7 @@ CompositorWin7::~CompositorWin7()
         delete m_stable;
 }
 
-bool CompositorWin7::init()
+bool CompositorWin7::preInit(qt_intf_t *p_intf)
 {
     //check whether D3DCompiler is available. whitout it Angle won't work
     QLibrary d3dCompilerDll;
@@ -134,13 +134,18 @@ bool CompositorWin7::init()
     //otherwise Qt will load angle and fail.
     if (!d3dCompilerDll.isLoaded() || FAILED(hr))
     {
-        msg_Info(m_intf, "no D3D support, use software backend");
+        msg_Info(p_intf, "no D3D support, use software backend");
         QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
     }
 
     return true;
 }
 
+bool CompositorWin7::init()
+{
+    return true;
+}
+
 MainInterface* CompositorWin7::makeMainInterface()
 {
     //Tool flag needs to be passed in the window constructor otherwise the


=====================================
modules/gui/qt/maininterface/compositor_win7.hpp
=====================================
@@ -46,7 +46,8 @@ public:
 
     virtual ~CompositorWin7();
 
-    bool init();
+    static bool preInit(qt_intf_t *p_intf);
+    virtual bool init() override;
 
     virtual MainInterface *makeMainInterface() override;
     virtual void destroyMainInterface() override;


=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -243,6 +243,9 @@ static void ShowDialog   ( intf_thread_t *, int, int, intf_dialog_args_t * );
 
 #define FULLSCREEN_CONTROL_PIXELS N_( "Fullscreen controller mouse sensitivity" )
 
+#define QT_COMPOSITOR_TEXT N_("Select Qt video intergration backend")
+#define QT_COMPOSITOR_LONGTEXT N_("Select Qt video intergration backend. Use with care, the interface may not start if an incompatible compositor is selected")
+
 static const int i_notification_list[] =
     { NOTIFICATION_NEVER, NOTIFICATION_MINIMIZED, NOTIFICATION_ALWAYS };
 
@@ -256,6 +259,27 @@ static const int i_raise_list[] =
 static const char *const psz_raise_list_text[] =
     { N_( "Never" ), N_( "Video" ), N_( "Audio" ), _( "Audio/Video" ) };
 
+static const char *const compositor_vlc[] = {
+    "auto",
+#ifdef _WIN32
+#ifdef HAVE_DCOMP_H
+    "dcomp"
+#endif
+    "win7",
+#endif
+    "dummy"
+};
+static const char *const compositor_user[] = {
+    N_("Automatic"),
+#ifdef _WIN32
+#ifdef HAVE_DCOMP_H
+    "Direct Composition",
+#endif
+    "Windows 7",
+#endif
+    N_("Dummy"),
+};
+
 /**********************************************************************/
 vlc_module_begin ()
     set_shortname( "Qt" )
@@ -300,6 +324,9 @@ vlc_module_begin ()
     add_bool( "qt-fs-controller", true, QT_FULLSCREEN_TEXT,
               nullptr )
 
+    add_string("qt-compositor", "auto", QT_COMPOSITOR_TEXT, QT_COMPOSITOR_LONGTEXT)
+            change_string_list(compositor_vlc, compositor_user)
+
     add_bool( "qt-recentplay", true, RECENTPLAY_TEXT,
               nullptr )
     add_string( "qt-recentplay-filter", "",
@@ -647,7 +674,9 @@ static void *Thread( void *obj )
 
     Q_INIT_RESOURCE( vlc );
 
-    p_intf->p_compositor = vlc::Compositor::createCompositor(p_intf);
+    vlc::CompositorFactory compositorFactory(p_intf, var_InheritString(p_intf, "qt-compositor"));
+
+    compositorFactory.preInit();
 
     QApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
     QApplication::setAttribute( Qt::AA_UseHighDpiPixmaps );
@@ -730,7 +759,12 @@ static void *Thread( void *obj )
 
     if( !p_intf->b_isDialogProvider )
     {
-        p_mi = p_intf->p_compositor->makeMainInterface();
+        do {
+            p_intf->p_compositor = compositorFactory.createCompositor();
+            if (! p_intf->p_compositor)
+                break;
+            p_mi = p_intf->p_compositor->makeMainInterface();
+        } while(p_mi == nullptr);
         p_intf->p_mi = p_mi;
 
         if (!p_mi)



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c17d3ae27f2d9af2a6408b0841b1776a30f8213b...bb14abfc422e64c274157638bd5cd17ebaf42faf

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c17d3ae27f2d9af2a6408b0841b1776a30f8213b...bb14abfc422e64c274157638bd5cd17ebaf42faf
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list