You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
11 KiB
382 lines
11 KiB
/*****************************************************************************
|
|
* 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
|
|
|
|
#ifndef QT_CORE_PRIVATE
|
|
#warning "QSystemLibrary private header is required for DirectComposition compositor."
|
|
#endif
|
|
|
|
#include <QOperatingSystemVersion>
|
|
|
|
#include <QtGui/qpa/qplatformnativeinterface.h>
|
|
#include <QtCore/private/qsystemlibrary_p.h>
|
|
|
|
#if __has_include(<dxgi1_6.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>)
|
|
#define QRhiD3D12_ACTIVE
|
|
#include <QtGui/private/qrhid3d12_p.h>
|
|
#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 <dwmapi.h>
|
|
|
|
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()
|
|
{
|
|
destroyMainInterface();
|
|
}
|
|
|
|
bool CompositorDirectComposition::preInit(qt_intf_t *intf)
|
|
{
|
|
#if !defined(QRhiD3D11_ACTIVE) && !defined(QRhiD3D12_ACTIVE)
|
|
msg_Warn(intf, "compositor_dcomp was not built with D3D11 or D3D12 headers. It will not work.");
|
|
return false;
|
|
#endif
|
|
|
|
QSystemLibrary dcomplib(QLatin1String("dcomp"));
|
|
|
|
typedef HRESULT (__stdcall *DCompositionCreateDeviceFuncPtr)(
|
|
_In_opt_ IDXGIDevice *dxgiDevice,
|
|
_In_ REFIID iid,
|
|
_Outptr_ void **dcompositionDevice);
|
|
DCompositionCreateDeviceFuncPtr func = reinterpret_cast<DCompositionCreateDeviceFuncPtr>(
|
|
dcomplib.resolve("DCompositionCreateDevice"));
|
|
|
|
IDCompositionDevice *device = nullptr;
|
|
if (!func || FAILED(func(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast<void **>(&device))))
|
|
{
|
|
msg_Warn(intf, "Can not create DCompositionDevice. CompositorDirectComposition will not work.");
|
|
return false;
|
|
}
|
|
|
|
if (device)
|
|
device->Release();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompositorDirectComposition::init()
|
|
{
|
|
{
|
|
const QString& platformName = qApp->platformName();
|
|
if (!(platformName == QLatin1String("windows") || platformName == QLatin1String("direct2d")))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CompositorDirectComposition::setup()
|
|
{
|
|
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);
|
|
|
|
if (rhi->backend() == QRhi::D3D11)
|
|
{
|
|
#ifdef QRhiD3D11_ACTIVE
|
|
m_dcompDevice = static_cast<QRhiD3D11*>(rhiImplementation)->dcompDevice;
|
|
m_dcompTarget = static_cast<QD3D11SwapChain*>(rhiSwapChain)->dcompTarget;
|
|
m_uiVisual = static_cast<QD3D11SwapChain*>(rhiSwapChain)->dcompVisual;
|
|
#endif
|
|
}
|
|
else if (rhi->backend() == QRhi::D3D12)
|
|
{
|
|
#ifdef QRhiD3D12_ACTIVE
|
|
m_dcompDevice = static_cast<QRhiD3D12*>(rhiImplementation)->dcompDevice;
|
|
m_dcompTarget = static_cast<QD3D12SwapChain*>(rhiSwapChain)->dcompTarget;
|
|
m_uiVisual = static_cast<QD3D12SwapChain*>(rhiSwapChain)->dcompVisual;
|
|
#endif
|
|
}
|
|
else
|
|
Q_UNREACHABLE();
|
|
|
|
assert(m_dcompDevice);
|
|
assert(m_dcompTarget);
|
|
assert(m_uiVisual);
|
|
|
|
HRESULT res;
|
|
res = m_dcompDevice->CreateVisual(&m_rootVisual);
|
|
assert(res == S_OK);
|
|
|
|
res = m_dcompTarget->SetRoot(m_rootVisual.Get());
|
|
assert(res == S_OK);
|
|
|
|
res = m_rootVisual->AddVisual(m_uiVisual, FALSE, NULL);
|
|
assert(res == S_OK);
|
|
|
|
m_dcompDevice->Commit();
|
|
|
|
if (!m_nativeAcrylicAvailable)
|
|
{
|
|
try
|
|
{
|
|
m_acrylicSurface = new 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);
|
|
delete m_acrylicSurface.data();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CompositorDirectComposition::makeMainInterface(MainCtx* mainCtx)
|
|
{
|
|
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);
|
|
|
|
connect(quickViewPtr,
|
|
&QQuickWindow::frameSwapped, // At this stage, we can be sure that QRhi and QRhiSwapChain are valid.
|
|
this,
|
|
&CompositorDirectComposition::setup,
|
|
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection | Qt::DirectConnection));
|
|
|
|
connect(quickViewPtr,
|
|
&QQuickWindow::sceneGraphInvalidated,
|
|
this,
|
|
[this]() {
|
|
m_videoVisual.Reset();
|
|
delete m_acrylicSurface.data();
|
|
m_rootVisual.Reset();
|
|
},
|
|
Qt::DirectConnection);
|
|
|
|
bool appropriateGraphicsApi = true;
|
|
|
|
QEventLoop eventLoop;
|
|
connect(quickViewPtr,
|
|
&QQuickWindow::sceneGraphInitialized,
|
|
&eventLoop,
|
|
[&eventLoop, &appropriateGraphicsApi]() {
|
|
if (!(QQuickWindow::graphicsApi() == QSGRendererInterface::Direct3D11 ||
|
|
QQuickWindow::graphicsApi() == QSGRendererInterface::Direct3D12)) {
|
|
appropriateGraphicsApi = false;
|
|
}
|
|
eventLoop.quit();
|
|
}, Qt::SingleShotConnection);
|
|
|
|
|
|
CompositorVideo::Flags flags = CompositorVideo::CAN_SHOW_PIP;
|
|
|
|
// If Windows 11 Build 22621, enable acrylic effect:
|
|
m_nativeAcrylicAvailable = QOperatingSystemVersion::current()
|
|
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 11, 0, 22621);
|
|
if (m_nativeAcrylicAvailable)
|
|
{
|
|
flags |= CompositorVideo::HAS_ACRYLIC;
|
|
}
|
|
|
|
const bool ret = commonGUICreate(quickViewPtr, quickViewPtr, flags);
|
|
|
|
m_quickView->create();
|
|
|
|
if (m_nativeAcrylicAvailable)
|
|
{
|
|
enum BackdropType
|
|
{
|
|
DWMSBT_TRANSIENTWINDOW = 3
|
|
} backdropType = DWMSBT_TRANSIENTWINDOW;
|
|
DwmSetWindowAttribute(reinterpret_cast<HWND>(m_quickView->winId()),
|
|
38 /* DWMWA_SYSTEMBACKDROP_TYPE */,
|
|
&backdropType,
|
|
sizeof(backdropType));
|
|
}
|
|
|
|
m_quickView->show();
|
|
|
|
if (!m_quickView->isSceneGraphInitialized())
|
|
eventLoop.exec();
|
|
return (ret && appropriateGraphicsApi);
|
|
}
|
|
|
|
void CompositorDirectComposition::onSurfacePositionChanged(const QPointF& position)
|
|
{
|
|
assert(m_videoVisual);
|
|
assert(m_dcompDevice);
|
|
|
|
m_videoVisual->SetOffsetX(position.x());
|
|
m_videoVisual->SetOffsetY(position.y());
|
|
m_dcompDevice->Commit();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
void CompositorDirectComposition::unloadGUI()
|
|
{
|
|
commonGUIDestroy();
|
|
}
|
|
|
|
bool CompositorDirectComposition::setupVoutWindow(vlc_window_t *p_wnd, VoutDestroyCb destroyCb)
|
|
{
|
|
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();
|
|
}
|
|
|
|
}
|
|
|