Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  compositor_dcomp.cpp   Sprache: C

 

/*****************************************************************************
 * Copyright (C) 2020 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "compositor_dcomp.hpp"

#include "maininterface/mainctx_win32.hpp"

#include <vlc_window.h>

#ifndef QT_GUI_PRIVATE
#warning "QRhiD3D11 and QRhi headers are required for DirectComposition compositor."
#endif

#include <QtGui/qpa/qplatformnativeinterface.h>

#if __has_include(<d3d11_1.h>)
#define QRhiD3D11_ACTIVE
#include <QtGui/private/qrhid3d11_p.h>
#endif

#if __has_include(<d3d12.h>) && __has_include(<d3d12sdklayers.h>)
#include <QtGui/private/qrhid3d12_p.h>
#ifdef QRHI_D3D12_AVAILABLE
#define QRhiD3D12_ACTIVE
#endif
#endif

#if !defined(QRhiD3D11_ACTIVE) && !defined(QRhiD3D12_ACTIVE)
#warning "Neither D3D11 nor D3D12 headers are available. compositor_dcomp will not work."
#endif

#include "compositor_dcomp_acrylicsurface.hpp"
#include "maininterface/interface_window_handler.hpp"

#include <memory>
#include <type_traits>

namespace vlc {

int CompositorDirectComposition::windowEnable(const vlc_window_cfg_t *)
{
    assert(m_dcompDevice);
    assert(m_rootVisual);
    assert(m_videoVisual);
    assert(m_uiVisual);

    commonWindowEnable();

    const auto ret = m_rootVisual->AddVisual(m_videoVisual.Get(), FALSE, m_uiVisual);
    m_dcompDevice->Commit();

    if (ret == S_OK)
        return VLC_SUCCESS;
    else
        return VLC_EGENERIC;
}

void CompositorDirectComposition::windowDisable()
{
    assert(m_dcompDevice);
    assert(m_rootVisual);
    assert(m_videoVisual);

    commonWindowDisable();

    const auto ret = m_rootVisual->RemoveVisual(m_videoVisual.Get());
    assert(ret == S_OK);
    m_dcompDevice->Commit();
}

void CompositorDirectComposition::windowDestroy()
{
    m_videoVisual.Reset();
    CompositorVideo::windowDestroy();
}

CompositorDirectComposition::CompositorDirectComposition( qt_intf_t* p_intf,  QObject *parent)
    : CompositorVideo(p_intf, parent)
{
}

CompositorDirectComposition::~CompositorDirectComposition()
{
    cleanup();

    //m_acrylicSurface should be released before the RHI context is destroyed
    assert(!m_acrylicSurface);
}

bool CompositorDirectComposition::init()
{
#if !defined(QRhiD3D11_ACTIVE) && !defined(QRhiD3D12_ACTIVE)
    msg_Warn(m_intf, "compositor_dcomp was not built with D3D11 or D3D12 headers. It will not work.");
    return false;
#endif

    {
        const QString& platformName = qApp->platformName();
        if (!(platformName == QLatin1String("windows") || platformName == QLatin1String("direct2d")))
            return false;
    }

    QString sceneGraphBackend = qEnvironmentVariable("QT_QUICK_BACKEND");
    if (!sceneGraphBackend.isEmpty() /* if empty, RHI is used */ &&
        sceneGraphBackend != QLatin1String("rhi"))
    {
        // No RHI means no D3D11 or D3D12, the graphics API check
        // below is only relevant when RHI is in use.
        // If QT_QUICK_BACKEND is set to software or openvg, then
        // `QQuickWindow::graphicsApi()` might still report D3D11 or
        // D3D12 until the scene graph is initialized.
        // Unlike `QQuickWindow::graphicsApi()`, `sceneGraphBackend()`
        // is only valid after the window is constructed, so instead
        // of using `QQuickWindow::sceneGraphBackend()`, simply probe
        // the environment variable.
        return false;
    }
    else
    {
        // If `QQuickWindow::setGraphicsApi()` is used with a non-RHI
        // mode such as `software`, Qt at the moment adjusts the scene
        // graph backend but not the `QQuickWindow::graphicsApi()`.
        // Until Qt does that, we need to check it here additionally:
        sceneGraphBackend = QQuickWindow::sceneGraphBackend();
        if (!sceneGraphBackend.isEmpty() && sceneGraphBackend != QLatin1String("rhi"))
            return false;
    }

    const auto graphicsApi = QQuickWindow::graphicsApi();
    if (graphicsApi != QSGRendererInterface::Direct3D11 &&
        graphicsApi != QSGRendererInterface::Direct3D12)
        return false;

    if (Q_UNLIKELY(qEnvironmentVariableIntValue("QT_D3D_NO_FLIP")))
    {
        // Qt does not use Direct Composition with the legacy swapchain model.
        return false;
    }

    std::unique_ptr<std::remove_pointer_t<HMODULE>, BOOL WINAPI (*)(HMODULE)>
        dcomplib(::LoadLibraryEx(TEXT("dcomp.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32), ::FreeLibrary);

    typedef HRESULT (__stdcall *DCompositionCreateDeviceFuncPtr)(
        _In_opt_ IDXGIDevice *dxgiDevice,
        _In_ REFIID iid,
        _Outptr_ void **dcompositionDevice);
    DCompositionCreateDeviceFuncPtr func = nullptr;
    if (dcomplib)
    {
        func = reinterpret_cast<DCompositionCreateDeviceFuncPtr>(
            GetProcAddress(dcomplib.get(), "DCompositionCreateDevice"));
    }

    Microsoft::WRL::ComPtr<IDCompositionDevice> device;
    if (!func || FAILED(func(nullptr, IID_PPV_ARGS(&device))))
    {
        msg_Warn(m_intf, "Can not create DCompositionDevice. CompositorDirectComposition will not work.");
        return false;
    }

    return true;
}

bool CompositorDirectComposition::setup()
{
    // WARNING: This lock is released explicitly as soon as we are ready to signal readiness:
    QMutexLocker lock(&m_setupStateLock);

    switch (m_setupState)
    {
    /* [[unlikely]] */ case SetupState::Fail:
        return false;
    /* [[unlikely]] */ case SetupState::Success:
        return true; // Already called
    /* [[likely]] */ default:
        break;
    }

    //Setup shouldn't be called once we are shutting down
    assert(m_mainCtx);
    assert(m_quickView);
    const auto rhi = m_quickView->rhi();
    assert(rhi);

    QRhiImplementation* const rhiImplementation = rhi->implementation();
    assert(rhiImplementation);
    QRhiSwapChain* const rhiSwapChain = m_quickView->swapChain();
    assert(rhiSwapChain);

    assert(m_quickView->rhi()->backend() == QRhi::D3D11 || m_quickView->rhi()->backend() == QRhi::D3D12);

    IDCompositionTarget* dcompTarget;
    if (rhi->backend() == QRhi::D3D11)
    {
#ifdef QRhiD3D11_ACTIVE
        m_dcompDevice = static_cast<QRhiD3D11*>(rhiImplementation)->dcompDevice;
        auto qswapchain = static_cast<QD3D11SwapChain*>(rhiSwapChain);
        dcompTarget = qswapchain->dcompTarget;
        m_uiVisual = qswapchain->dcompVisual;
#endif
    }
    else if (rhi->backend() == QRhi::D3D12)
    {
#ifdef QRhiD3D12_ACTIVE
        m_dcompDevice = static_cast<QRhiD3D12*>(rhiImplementation)->dcompDevice;
        auto qswapchain = static_cast<QD3D12SwapChain*>(rhiSwapChain);
        dcompTarget = qswapchain->dcompTarget;
        m_uiVisual = qswapchain->dcompVisual;
#endif
    }
    else
        Q_UNREACHABLE();

    assert(m_dcompDevice);
    assert(dcompTarget);
    assert(m_uiVisual);

    HRESULT res;
    res = m_dcompDevice->CreateVisual(&m_rootVisual);
    assert(res == S_OK);

    res = dcompTarget->SetRoot(m_rootVisual.Get());
    assert(res == S_OK);

    res = m_rootVisual->AddVisual(m_uiVisual, FALSE, NULL);
    assert(res == S_OK);

    m_setupState = SetupState::Success;
    m_setupStateCond.notify_all();
    lock.unlock();

    m_dcompDevice->Commit();

    if (!m_mainCtx->hasAcrylicSurface())
    {
        if (var_InheritBool(m_intf, "qt-backdrop-blur"))
        {
            QMetaObject::invokeMethod(this, [this]() {
                if (Q_UNLIKELY(!m_dcompDevice || !interfaceMainWindow()))
                    return;

                try
                {
                    m_acrylicSurface = std::make_unique<CompositorDCompositionAcrylicSurface>(m_intf, this, m_mainCtx, m_dcompDevice);
                }
                catch (const std::exception& exception)
                {
                    if (const auto what = exception.what())
                        msg_Warn(m_intf, "%s", what);
                }
            }, Qt::QueuedConnection);
        }
    }

    return true;
}

void CompositorDirectComposition::cleanup()
{
    m_videoVisual.Reset();
    m_acrylicSurface.reset();

    if (m_rootVisual && m_uiVisual)
    {
        // Just in case root visual deletes its children
        // when it is deleted: (Qt's UI visual should be
        // deleted by Qt itself)
        m_rootVisual->RemoveVisual(m_uiVisual);
    }

    m_rootVisual.Reset();

    // When the window receives the event `SurfaceAboutToBeDestroyed`,
    // the RHI and the RHI swap chain are going to be destroyed.
    // It should be noted that event filters receive events
    // before the watched object receives them.
    // Since these objects belong to Qt, we should clear them
    // in order to prevent potential dangling pointer dereference:
    m_dcompDevice = nullptr;
    m_uiVisual = nullptr;
}

bool CompositorDirectComposition::makeMainInterface(MainCtx* mainCtx, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback)
{
    assert(mainCtx);
    m_mainCtx = mainCtx;

    m_quickView = std::make_unique<QQuickView>();
    const auto quickViewPtr = m_quickView.get();

    m_quickView->setResizeMode(QQuickView::SizeRootObjectToView);
    m_quickView->setColor(Qt::transparent);

    m_quickView->installEventFilter(this);

    connect(quickViewPtr,
            &QQuickWindow::frameSwapped, // At this stage, we can be sure that QRhi and QRhiSwapChain are valid.
            this,
            [this]() {
                if (!setup())
                {
                    // Similar to `QQuickWindow::sceneGraphError()` handling:
                    if (m_mainCtx)
                        m_mainCtx->askToQuit();
                }
            },
            Qt::SingleShotConnection);

    const bool noRedirectionSurface = MainCtxWin32::createWindowWithoutRedirectionSurfaceImpl(m_quickView.get());
    if (!noRedirectionSurface)
        qDebug() << "MainCtxWin32::createWindowWithoutRedirectionSurfaceImpl() returned false for window" << m_quickView.get();

    const bool ret = commonGUICreate(quickViewPtr, quickViewPtr, CompositorVideo::CAN_SHOW_PIP | CompositorVideo::HAS_ACRYLIC);

    if (!ret)
        return false;

    connect(quickViewPtr,
            &QQuickWindow::sceneGraphError,
            this,
            [this](QQuickWindow::SceneGraphError error, const QString &message) {
                qWarning() << "CompositorDComp: Scene Graph Error: " << error << ", Message: " << message;
                QMutexLocker lock(&m_setupStateLock);
                m_setupState = SetupState::Fail;
                m_setupStateCond.notify_all();
            }, static_cast<Qt::ConnectionType>(Qt::SingleShotConnection | Qt::DirectConnection));

    // Qt "terminates the application" by default if there is no connection made to the signal
    // QQuickWindow::sceneGraphError(). We need to do the same, because by the time the error
    // is reported, it will likely be too late (`makeMainInterface()` already returned true,
    // which is the latest point recovery is still possible) to recover from that error and
    // the interface will remain unfunctional. It was proposed to wait here until the scene
    // graph is done, but that was not changed in order not to slow down the application
    // start up.
    connect(quickViewPtr,
            &QQuickWindow::sceneGraphError,
            m_mainCtx,
            &MainCtx::askToQuit);

    if (aboutToShowQuickWindowCallback)
        aboutToShowQuickWindowCallback(quickViewPtr);

    m_quickView->setVisible(true);
    return true;
}

void CompositorDirectComposition::onSurfacePositionChanged(const QPointF& position)
{
    assert(m_videoVisual);
    assert(m_dcompDevice);

    m_videoVisual->SetOffsetX(position.x());
    m_videoVisual->SetOffsetY(position.y());

    // WARNING: Commit is requested explicitly through `::commitSurface()` after size
    //          and position changes are applied.
}

void CompositorDirectComposition::onSurfaceSizeChanged(const QSizeF&)
{
    //N/A
}

void CompositorDirectComposition::destroyMainInterface()
{
    if (m_videoVisual)
        msg_Err(m_intf, "video surface still active while destroying main interface");

    commonIntfDestroy();

    // We still need to call this explicitly, as it seems Qt does not send
    // the `QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed` event if we
    // delete the window ourselves:
    cleanup();

    m_quickView.reset();

    // These can no longer be assumed to be valid anymore, since they are
    // managed by Qt and the window is already gone:
    assert(!m_dcompDevice);
    assert(!m_uiVisual);
}

void CompositorDirectComposition::unloadGUI()
{
    m_acrylicSurface.reset();
    m_interfaceWindowHandler.reset();

    {
        QMutexLocker lock(&m_setupStateLock);
        if (m_quickView) {
            disconnect(m_quickView.get(), &QQuickWindow::frameSwapped,
                       this, nullptr);
        }
        m_setupState = SetupState::Fail;
        m_setupStateCond.notify_all();
    }

    //at this point we need to unload the QML content but the window still need to
    //be valid as it may still be used by the vout window.
    //we cant' just delete the qmlEngine as the QmlView as the root item is parented to the QmlView
    //setSource() to nothing will effectively destroy the root item
    if (Q_LIKELY(m_quickView))
        m_quickView->setSource(QUrl());

    commonGUIDestroy();
}

bool CompositorDirectComposition::setupVoutWindow(vlc_window_t *p_wnd, VoutDestroyCb destroyCb)
{
    if (m_wnd)
        return false;

    {
        QMutexLocker lock(&m_setupStateLock);
        while (m_setupState == SetupState::Uninitialized)
        {
            const bool ret = m_setupStateCond.wait(&m_setupStateLock);
            if (!ret)
                return false;
        }
        if (m_setupState != SetupState::Success)
        {
            return false;
        }
    }

    assert(m_dcompDevice);

    const HRESULT hr = m_dcompDevice->CreateVisual(&m_videoVisual);

    if (FAILED(hr))
    {
        msg_Err(p_wnd, "create to create DComp video visual");
        return false;
    }

    commonSetupVoutWindow(p_wnd, destroyCb);
    p_wnd->type = VLC_WINDOW_TYPE_DCOMP;

    p_wnd->display.dcomp_device = m_dcompDevice;
    p_wnd->handle.dcomp_visual = m_videoVisual.Get();

    return true;
}

QWindow *CompositorDirectComposition::interfaceMainWindow() const
{
    return m_quickView.get();
}

Compositor::Type CompositorDirectComposition::type() const
{
    return Compositor::DirectCompositionCompositor;
}

void CompositorDirectComposition::addVisual(IDCompositionVisual *visual)
{
    assert(visual);
    assert(m_rootVisual);
    assert(m_dcompDevice);

    HRESULT hr = m_rootVisual->AddVisual(visual, TRUE, NULL);
    if (FAILED(hr))
        msg_Err(m_intf, "failed to add visual, code: 0x%lX", hr);

    m_dcompDevice->Commit();
}

void CompositorDirectComposition::removeVisual(IDCompositionVisual *visual)
{
    assert(visual);
 assert(m_rootVisual);
 assert(m_dcompDevice);

    auto hr = m_rootVisual->RemoveVisual(visual);
    if (FAILED(hr))
        msg_Err(m_intf, "failed to remove visual, code: 0x%lX", hr);

    m_dcompDevice->Commit();
}

QQuickItem * CompositorDirectComposition::activeFocusItem() const /* override */
{
    return m_quickView->activeFocusItem();
}

bool CompositorDirectComposition::eventFilter(QObject *watched, QEvent *event)
{
    switch (event->type())
    {
    case QEvent::PlatformSurface:
        if (watched == m_quickView.get() &&
            static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
        {
            cleanup();
        }
        break;
    default:
        break;
    }

    return QObject::eventFilter(watched, event);
}

void CompositorDirectComposition::commitSurface()
{
    assert(m_dcompDevice);
    m_dcompDevice->Commit();
}

}

Messung
CHZG
851009994

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=417989