summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/gbm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/windowing/gbm
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--xbmc/windowing/gbm/CMakeLists.txt26
-rw-r--r--xbmc/windowing/gbm/GBMDPMSSupport.cpp43
-rw-r--r--xbmc/windowing/gbm/GBMDPMSSupport.h22
-rw-r--r--xbmc/windowing/gbm/GBMUtils.cpp107
-rw-r--r--xbmc/windowing/gbm/GBMUtils.h177
-rw-r--r--xbmc/windowing/gbm/OptionalsReg.cpp139
-rw-r--r--xbmc/windowing/gbm/OptionalsReg.h36
-rw-r--r--xbmc/windowing/gbm/VideoLayerBridge.h27
-rw-r--r--xbmc/windowing/gbm/VideoSyncGbm.cpp131
-rw-r--r--xbmc/windowing/gbm/VideoSyncGbm.h43
-rw-r--r--xbmc/windowing/gbm/WinSystemGbm.cpp445
-rw-r--r--xbmc/windowing/gbm/WinSystemGbm.h96
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp141
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmEGLContext.h58
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLContext.cpp174
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLContext.h48
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp167
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLESContext.h48
-rw-r--r--xbmc/windowing/gbm/drm/CMakeLists.txt21
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.cpp344
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.h81
-rw-r--r--xbmc/windowing/gbm/drm/DRMConnector.cpp99
-rw-r--r--xbmc/windowing/gbm/drm/DRMConnector.h53
-rw-r--r--xbmc/windowing/gbm/drm/DRMCrtc.cpp26
-rw-r--r--xbmc/windowing/gbm/drm/DRMCrtc.h46
-rw-r--r--xbmc/windowing/gbm/drm/DRMEncoder.cpp23
-rw-r--r--xbmc/windowing/gbm/drm/DRMEncoder.h43
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.cpp141
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.h39
-rw-r--r--xbmc/windowing/gbm/drm/DRMObject.cpp133
-rw-r--r--xbmc/windowing/gbm/drm/DRMObject.h71
-rw-r--r--xbmc/windowing/gbm/drm/DRMPlane.cpp118
-rw-r--r--xbmc/windowing/gbm/drm/DRMPlane.h58
-rw-r--r--xbmc/windowing/gbm/drm/DRMUtils.cpp740
-rw-r--r--xbmc/windowing/gbm/drm/DRMUtils.h103
-rw-r--r--xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp52
-rw-r--r--xbmc/windowing/gbm/drm/OffScreenModeSetting.h38
37 files changed, 4157 insertions, 0 deletions
diff --git a/xbmc/windowing/gbm/CMakeLists.txt b/xbmc/windowing/gbm/CMakeLists.txt
new file mode 100644
index 0000000..254b2db
--- /dev/null
+++ b/xbmc/windowing/gbm/CMakeLists.txt
@@ -0,0 +1,26 @@
+add_subdirectory(drm)
+
+set(SOURCES OptionalsReg.cpp
+ WinSystemGbm.cpp
+ VideoSyncGbm.cpp
+ GBMUtils.cpp
+ WinSystemGbmEGLContext.cpp
+ GBMDPMSSupport.cpp)
+
+set(HEADERS OptionalsReg.h
+ WinSystemGbm.h
+ VideoSyncGbm.h
+ GBMUtils.h
+ WinSystemGbmEGLContext.h
+ GBMDPMSSupport.h)
+
+if (OPENGL_FOUND)
+ list(APPEND SOURCES WinSystemGbmGLContext.cpp)
+ list(APPEND HEADERS WinSystemGbmGLContext.h)
+endif()
+if(OPENGLES_FOUND)
+ list(APPEND SOURCES WinSystemGbmGLESContext.cpp)
+ list(APPEND HEADERS WinSystemGbmGLESContext.h)
+endif()
+
+core_add_library(windowing_gbm)
diff --git a/xbmc/windowing/gbm/GBMDPMSSupport.cpp b/xbmc/windowing/gbm/GBMDPMSSupport.cpp
new file mode 100644
index 0000000..6544587
--- /dev/null
+++ b/xbmc/windowing/gbm/GBMDPMSSupport.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GBMDPMSSupport.h"
+
+#include "ServiceBroker.h"
+#include "windowing/gbm/WinSystemGbm.h"
+
+using namespace KODI::WINDOWING::GBM;
+
+CGBMDPMSSupport::CGBMDPMSSupport()
+{
+ m_supportedModes.push_back(OFF);
+}
+
+bool CGBMDPMSSupport::EnablePowerSaving(PowerSavingMode mode)
+{
+ auto winSystem = dynamic_cast<CWinSystemGbm*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return false;
+
+ switch (mode)
+ {
+ case OFF:
+ return winSystem->Hide();
+ default:
+ return false;
+ }
+}
+
+bool CGBMDPMSSupport::DisablePowerSaving()
+{
+ auto winSystem = dynamic_cast<CWinSystemGbm*>(CServiceBroker::GetWinSystem());
+ if (!winSystem)
+ return false;
+
+ return winSystem->Show();
+}
diff --git a/xbmc/windowing/gbm/GBMDPMSSupport.h b/xbmc/windowing/gbm/GBMDPMSSupport.h
new file mode 100644
index 0000000..f5fabf8
--- /dev/null
+++ b/xbmc/windowing/gbm/GBMDPMSSupport.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "powermanagement/DPMSSupport.h"
+
+#include <memory>
+
+class CGBMDPMSSupport : public CDPMSSupport
+{
+public:
+ CGBMDPMSSupport();
+ ~CGBMDPMSSupport() override = default;
+
+protected:
+ bool EnablePowerSaving(PowerSavingMode mode) override;
+ bool DisablePowerSaving() override;
+};
diff --git a/xbmc/windowing/gbm/GBMUtils.cpp b/xbmc/windowing/gbm/GBMUtils.cpp
new file mode 100644
index 0000000..5267c93
--- /dev/null
+++ b/xbmc/windowing/gbm/GBMUtils.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GBMUtils.h"
+
+#include "utils/log.h"
+
+#include <mutex>
+
+using namespace KODI::WINDOWING::GBM;
+
+namespace
+{
+std::once_flag flag;
+}
+
+bool CGBMUtils::CreateDevice(int fd)
+{
+ auto device = gbm_create_device(fd);
+ if (!device)
+ {
+ CLog::Log(LOGERROR, "CGBMUtils::{} - failed to create device: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ m_device.reset(new CGBMDevice(device));
+
+ return true;
+}
+
+CGBMUtils::CGBMDevice::CGBMDevice(gbm_device* device) : m_device(device)
+{
+}
+
+bool CGBMUtils::CGBMDevice::CreateSurface(
+ int width, int height, uint32_t format, const uint64_t* modifiers, const int modifiers_count)
+{
+ gbm_surface* surface{nullptr};
+#if defined(HAS_GBM_MODIFIERS)
+ if (modifiers)
+ {
+ surface = gbm_surface_create_with_modifiers(m_device, width, height, format, modifiers,
+ modifiers_count);
+ }
+#endif
+ if (!surface)
+ {
+ surface = gbm_surface_create(m_device, width, height, format,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ }
+
+ if (!surface)
+ {
+ CLog::Log(LOGERROR, "CGBMUtils::{} - failed to create surface: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CGBMUtils::{} - created surface with size {}x{}", __FUNCTION__, width,
+ height);
+
+ m_surface.reset(new CGBMSurface(surface));
+
+ return true;
+}
+
+CGBMUtils::CGBMDevice::CGBMSurface::CGBMSurface(gbm_surface* surface) : m_surface(surface)
+{
+}
+
+CGBMUtils::CGBMDevice::CGBMSurface::CGBMSurfaceBuffer* CGBMUtils::CGBMDevice::CGBMSurface::
+ LockFrontBuffer()
+{
+ m_buffers.emplace(std::make_unique<CGBMSurfaceBuffer>(m_surface));
+
+ if (!static_cast<bool>(gbm_surface_has_free_buffers(m_surface)))
+ {
+ /*
+ * We want to use call_once here because we want it to be logged the first time that
+ * we have to release buffers. This means that the maximum amount of buffers had been reached.
+ * For mesa this should be 4 buffers but it may vary across other implementations.
+ */
+ std::call_once(
+ flag, [this]() { CLog::Log(LOGDEBUG, "CGBMUtils - using {} buffers", m_buffers.size()); });
+
+ m_buffers.pop();
+ }
+
+ return m_buffers.back().get();
+}
+
+CGBMUtils::CGBMDevice::CGBMSurface::CGBMSurfaceBuffer::CGBMSurfaceBuffer(gbm_surface* surface)
+ : m_surface(surface), m_buffer(gbm_surface_lock_front_buffer(surface))
+{
+}
+
+CGBMUtils::CGBMDevice::CGBMSurface::CGBMSurfaceBuffer::~CGBMSurfaceBuffer()
+{
+ if (m_surface && m_buffer)
+ gbm_surface_release_buffer(m_surface, m_buffer);
+}
diff --git a/xbmc/windowing/gbm/GBMUtils.h b/xbmc/windowing/gbm/GBMUtils.h
new file mode 100644
index 0000000..291a93a
--- /dev/null
+++ b/xbmc/windowing/gbm/GBMUtils.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <queue>
+
+#include <gbm.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+/**
+ * @brief A wrapper for gbm c classes to allow OOP and RAII.
+ *
+ */
+class CGBMUtils
+{
+public:
+ CGBMUtils(const CGBMUtils&) = delete;
+ CGBMUtils& operator=(const CGBMUtils&) = delete;
+ CGBMUtils() = default;
+ ~CGBMUtils() = default;
+
+ /**
+ * @brief Create a gbm device for allocating buffers
+ *
+ * @param fd The file descriptor for a backend device
+ * @return true The device creation succeeded
+ * @return false The device creation failed
+ */
+ bool CreateDevice(int fd);
+
+ /**
+ * @brief A wrapper for gbm_device to allow OOP and RAII
+ *
+ */
+ class CGBMDevice
+ {
+ public:
+ CGBMDevice(const CGBMDevice&) = delete;
+ CGBMDevice& operator=(const CGBMDevice&) = delete;
+ explicit CGBMDevice(gbm_device* device);
+ ~CGBMDevice() = default;
+
+ /**
+ * @brief Create a gbm surface
+ *
+ * @param width The width to use for the surface
+ * @param height The height to use for the surface
+ * @param format The format to use for the surface
+ * @param modifiers The modifiers to use for the surface
+ * @param modifiers_count The amount of modifiers in the modifiers param
+ * @return true The surface creation succeeded
+ * @return false The surface creation failed
+ */
+ bool CreateSurface(int width,
+ int height,
+ uint32_t format,
+ const uint64_t* modifiers,
+ const int modifiers_count);
+
+ /**
+ * @brief Get the underlying gbm_device
+ *
+ * @return gbm_device* A pointer to the underlying gbm_device
+ */
+ gbm_device* Get() const { return m_device; }
+
+ /**
+ * @brief A wrapper for gbm_surface to allow OOP and RAII
+ *
+ */
+ class CGBMSurface
+ {
+ public:
+ CGBMSurface(const CGBMSurface&) = delete;
+ CGBMSurface& operator=(const CGBMSurface&) = delete;
+ explicit CGBMSurface(gbm_surface* surface);
+ ~CGBMSurface() = default;
+
+ /**
+ * @brief Get the underlying gbm_surface
+ *
+ * @return gbm_surface* A pointer to the underlying gbm_surface
+ */
+ gbm_surface* Get() const { return m_surface; }
+
+ /**
+ * @brief A wrapper for gbm_bo to allow OOP and RAII
+ *
+ */
+ class CGBMSurfaceBuffer
+ {
+ public:
+ CGBMSurfaceBuffer(const CGBMSurfaceBuffer&) = delete;
+ CGBMSurfaceBuffer& operator=(const CGBMSurfaceBuffer&) = delete;
+ explicit CGBMSurfaceBuffer(gbm_surface* surface);
+ ~CGBMSurfaceBuffer();
+
+ /**
+ * @brief Get the underlying gbm_bo
+ *
+ * @return gbm_bo* A pointer to the underlying gbm_bo
+ */
+ gbm_bo* Get() const { return m_buffer; }
+
+ private:
+ gbm_surface* m_surface{nullptr};
+ gbm_bo* m_buffer{nullptr};
+ };
+
+ /**
+ * @brief Lock the surface's current front buffer.
+ *
+ * @return CGBMSurfaceBuffer* A pointer to a CGBMSurfaceBuffer object
+ */
+ CGBMSurfaceBuffer* LockFrontBuffer();
+
+ private:
+ gbm_surface* m_surface{nullptr};
+ std::queue<std::unique_ptr<CGBMSurfaceBuffer>> m_buffers;
+ };
+
+ /**
+ * @brief Get the CGBMSurface object
+ *
+ * @return CGBMSurface* A pointer to the CGBMSurface object
+ */
+ CGBMDevice::CGBMSurface* GetSurface() const { return m_surface.get(); }
+
+ private:
+ gbm_device* m_device{nullptr};
+
+ struct CGBMSurfaceDeleter
+ {
+ void operator()(CGBMSurface* p) const
+ {
+ if (p)
+ gbm_surface_destroy(p->Get());
+ }
+ };
+ std::unique_ptr<CGBMSurface, CGBMSurfaceDeleter> m_surface;
+ };
+
+ /**
+ * @brief Get the CGBMDevice object
+ *
+ * @return CGBMDevice* A pointer to the CGBMDevice object
+ */
+ CGBMUtils::CGBMDevice* GetDevice() const { return m_device.get(); }
+
+private:
+ struct CGBMDeviceDeleter
+ {
+ void operator()(CGBMDevice* p) const
+ {
+ if (p)
+ gbm_device_destroy(p->Get());
+ }
+ };
+ std::unique_ptr<CGBMDevice, CGBMDeviceDeleter> m_device;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/OptionalsReg.cpp b/xbmc/windowing/gbm/OptionalsReg.cpp
new file mode 100644
index 0000000..9f5076f
--- /dev/null
+++ b/xbmc/windowing/gbm/OptionalsReg.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OptionalsReg.h"
+
+//-----------------------------------------------------------------------------
+// VAAPI
+//-----------------------------------------------------------------------------
+#if defined (HAVE_LIBVA)
+#include <va/va_drm.h>
+#include "cores/VideoPlayer/DVDCodecs/Video/VAAPI.h"
+#if defined(HAS_GL)
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.h"
+#endif
+#if defined(HAS_GLES)
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.h"
+#endif
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CVaapiProxy : public VAAPI::IVaapiWinSystem
+{
+public:
+ CVaapiProxy(int fd) : m_fd(fd) {};
+ virtual ~CVaapiProxy() = default;
+ VADisplay GetVADisplay() override;
+ void *GetEGLDisplay() override { return eglDisplay; };
+
+ VADisplay vaDpy;
+ void *eglDisplay;
+
+private:
+ int m_fd{-1};
+};
+
+VADisplay CVaapiProxy::GetVADisplay()
+{
+ return vaGetDisplayDRM(m_fd);
+}
+
+CVaapiProxy* VaapiProxyCreate(int fd)
+{
+ return new CVaapiProxy(fd);
+}
+
+void VaapiProxyDelete(CVaapiProxy *proxy)
+{
+ delete proxy;
+}
+
+void VaapiProxyConfig(CVaapiProxy *proxy, void *eglDpy)
+{
+ proxy->vaDpy = proxy->GetVADisplay();
+ proxy->eglDisplay = eglDpy;
+}
+
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor)
+{
+ VAAPI::CDecoder::Register(winSystem, deepColor);
+}
+
+#if defined(HAS_GL)
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+ CRendererVAAPIGL::Register(winSystem, winSystem->vaDpy, winSystem->eglDisplay, general,
+ deepColor);
+}
+#endif
+
+#if defined(HAS_GLES)
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+ CRendererVAAPIGLES::Register(winSystem, winSystem->vaDpy, winSystem->eglDisplay, general,
+ deepColor);
+}
+#endif
+}
+}
+}
+
+#else
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CVaapiProxy
+{
+};
+
+CVaapiProxy* VaapiProxyCreate(int fd)
+{
+ return nullptr;
+}
+
+void VaapiProxyDelete(CVaapiProxy *proxy)
+{
+}
+
+void VaapiProxyConfig(CVaapiProxy *proxy, void *eglDpy)
+{
+
+}
+
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor)
+{
+
+}
+
+#if defined(HAS_GL)
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+
+}
+#endif
+
+#if defined(HAS_GLES)
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor)
+{
+}
+#endif
+}
+}
+}
+
+#endif
diff --git a/xbmc/windowing/gbm/OptionalsReg.h b/xbmc/windowing/gbm/OptionalsReg.h
new file mode 100644
index 0000000..ee459f6
--- /dev/null
+++ b/xbmc/windowing/gbm/OptionalsReg.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+
+//-----------------------------------------------------------------------------
+// VAAPI
+//-----------------------------------------------------------------------------
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+class CVaapiProxy;
+
+CVaapiProxy* VaapiProxyCreate(int fd);
+void VaapiProxyDelete(CVaapiProxy *proxy);
+void VaapiProxyConfig(CVaapiProxy *proxy, void *eglDpy);
+void VAAPIRegister(CVaapiProxy *winSystem, bool deepColor);
+#if defined(HAS_GL)
+void VAAPIRegisterRenderGL(CVaapiProxy* winSystem, bool& general, bool& deepColor);
+#endif
+#if defined(HAS_GLES)
+void VAAPIRegisterRenderGLES(CVaapiProxy* winSystem, bool& general, bool& deepColor);
+#endif
+}
+}
+}
diff --git a/xbmc/windowing/gbm/VideoLayerBridge.h b/xbmc/windowing/gbm/VideoLayerBridge.h
new file mode 100644
index 0000000..7070567
--- /dev/null
+++ b/xbmc/windowing/gbm/VideoLayerBridge.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CVideoLayerBridge
+{
+public:
+ virtual ~CVideoLayerBridge() = default;
+ virtual void Disable() {}
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/VideoSyncGbm.cpp b/xbmc/windowing/gbm/VideoSyncGbm.cpp
new file mode 100644
index 0000000..d113c90
--- /dev/null
+++ b/xbmc/windowing/gbm/VideoSyncGbm.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2005-2021 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoSyncGbm.h"
+
+#include "ServiceBroker.h"
+#include "threads/Thread.h"
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WinSystem.h"
+#include "windowing/gbm/WinSystemGbm.h"
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+
+CVideoSyncGbm::CVideoSyncGbm(void* clock)
+ : CVideoSync(clock), m_winSystem(CServiceBroker::GetWinSystem())
+{
+ if (!m_winSystem)
+ throw std::runtime_error("window system not available");
+}
+
+bool CVideoSyncGbm::Setup(PUPDATECLOCK func)
+{
+ UpdateClock = func;
+ m_abort = false;
+ m_winSystem->Register(this);
+ CLog::Log(LOGDEBUG, "CVideoSyncGbm::{} setting up", __FUNCTION__);
+
+ auto winSystemGbm = dynamic_cast<KODI::WINDOWING::GBM::CWinSystemGbm*>(m_winSystem);
+ if (!winSystemGbm)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: failed to get winSystem", __FUNCTION__);
+ return false;
+ }
+
+ auto drm = winSystemGbm->GetDrm();
+ if (!drm)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: failed to get drm", __FUNCTION__);
+ return false;
+ }
+
+ auto crtc = drm->GetCrtc();
+ if (!crtc)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: failed to get crtc", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t ns = 0;
+ m_crtcId = crtc->GetCrtcId();
+ m_fd = drm->GetFileDescriptor();
+ int s = drmCrtcGetSequence(m_fd, m_crtcId, &m_sequence, &ns);
+ m_offset = CurrentHostCounter() - ns;
+ if (s != 0)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: drmCrtcGetSequence failed ({})", __FUNCTION__, s);
+ return false;
+ }
+
+ CLog::Log(LOGINFO, "CVideoSyncGbm::{}: opened (fd:{} crtc:{} seq:{} ns:{}:{})", __FUNCTION__,
+ m_fd, m_crtcId, m_sequence, ns, m_offset + ns);
+ return true;
+}
+
+void CVideoSyncGbm::Run(CEvent& stopEvent)
+{
+ /* This shouldn't be very busy and timing is important so increase priority */
+ CThread::GetCurrentThread()->SetPriority(ThreadPriority::ABOVE_NORMAL);
+
+ if (m_fd < 0)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: failed to open device ({})", __FUNCTION__, m_fd);
+ return;
+ }
+ CLog::Log(LOGDEBUG, "CVideoSyncGbm::{}: started {}", __FUNCTION__, m_fd);
+
+ while (!stopEvent.Signaled() && !m_abort)
+ {
+ uint64_t sequence = 0, ns = 0;
+ usleep(1000);
+ int s = drmCrtcGetSequence(m_fd, m_crtcId, &sequence, &ns);
+ if (s != 0)
+ {
+ CLog::Log(LOGWARNING, "CVideoSyncGbm::{}: drmCrtcGetSequence failed ({})", __FUNCTION__, s);
+ break;
+ }
+
+ if (sequence == m_sequence)
+ continue;
+
+ UpdateClock(sequence - m_sequence, m_offset + ns, m_refClock);
+ m_sequence = sequence;
+ }
+}
+
+void CVideoSyncGbm::Cleanup()
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncGbm::{}: cleaning up", __FUNCTION__);
+ m_winSystem->Unregister(this);
+}
+
+float CVideoSyncGbm::GetFps()
+{
+ m_fps = m_winSystem->GetGfxContext().GetFPS();
+ CLog::Log(LOGDEBUG, "CVideoSyncGbm::{}: fps:{}", __FUNCTION__, m_fps);
+ return m_fps;
+}
+
+void CVideoSyncGbm::OnResetDisplay()
+{
+ m_abort = true;
+}
+
+void CVideoSyncGbm::RefreshChanged()
+{
+ if (m_fps != m_winSystem->GetGfxContext().GetFPS())
+ m_abort = true;
+}
diff --git a/xbmc/windowing/gbm/VideoSyncGbm.h b/xbmc/windowing/gbm/VideoSyncGbm.h
new file mode 100644
index 0000000..610d988
--- /dev/null
+++ b/xbmc/windowing/gbm/VideoSyncGbm.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005-2021 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "windowing/VideoSync.h"
+
+#include <atomic>
+
+class CWinSystemBase;
+
+class CVideoSyncGbm : public CVideoSync, IDispResource
+{
+public:
+ explicit CVideoSyncGbm(void* clock);
+ CVideoSyncGbm() = delete;
+ ~CVideoSyncGbm() override = default;
+
+ // CVideoSync overrides
+ bool Setup(PUPDATECLOCK func) override;
+ void Run(CEvent& stopEvent) override;
+ void Cleanup() override;
+ float GetFps() override;
+ void RefreshChanged() override;
+
+ // IDispResource overrides
+ void OnResetDisplay() override;
+
+private:
+ int m_fd = -1;
+ uint32_t m_crtcId = 0;
+ uint64_t m_sequence = 0;
+ uint64_t m_offset = 0;
+ std::atomic<bool> m_abort{false};
+
+ CWinSystemBase* m_winSystem;
+};
diff --git a/xbmc/windowing/gbm/WinSystemGbm.cpp b/xbmc/windowing/gbm/WinSystemGbm.cpp
new file mode 100644
index 0000000..4fd2da4
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbm.cpp
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemGbm.h"
+
+#include "GBMDPMSSupport.h"
+#include "OptionalsReg.h"
+#include "ServiceBroker.h"
+#include "VideoSyncGbm.h"
+#include "cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h"
+#include "drm/DRMAtomic.h"
+#include "drm/DRMLegacy.h"
+#include "drm/OffScreenModeSetting.h"
+#include "messaging/ApplicationMessenger.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include <mutex>
+#include <string.h>
+
+#ifndef HAVE_HDR_OUTPUT_METADATA
+// HDR structs is copied from linux include/linux/hdmi.h
+struct hdr_metadata_infoframe
+{
+ uint8_t eotf;
+ uint8_t metadata_type;
+ struct
+ {
+ uint16_t x, y;
+ } display_primaries[3];
+ struct
+ {
+ uint16_t x, y;
+ } white_point;
+ uint16_t max_display_mastering_luminance;
+ uint16_t min_display_mastering_luminance;
+ uint16_t max_cll;
+ uint16_t max_fall;
+};
+struct hdr_output_metadata
+{
+ uint32_t metadata_type;
+ union
+ {
+ struct hdr_metadata_infoframe hdmi_metadata_type1;
+ };
+};
+#endif
+
+using namespace KODI::WINDOWING::GBM;
+
+using namespace std::chrono_literals;
+
+CWinSystemGbm::CWinSystemGbm() :
+ m_DRM(nullptr),
+ m_GBM(new CGBMUtils),
+ m_libinput(new CLibInputHandler)
+{
+ m_dpms = std::make_shared<CGBMDPMSSupport>();
+ m_libinput->Start();
+}
+
+bool CWinSystemGbm::InitWindowSystem()
+{
+ const char* x11 = getenv("DISPLAY");
+ const char* wayland = getenv("WAYLAND_DISPLAY");
+ if (x11 || wayland)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemGbm::{} - not allowed to run GBM under a window manager",
+ __FUNCTION__);
+ return false;
+ }
+
+ m_DRM = std::make_shared<CDRMAtomic>();
+
+ if (!m_DRM->InitDrm())
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbm::{} - failed to initialize Atomic DRM", __FUNCTION__);
+ m_DRM.reset();
+
+ m_DRM = std::make_shared<CDRMLegacy>();
+
+ if (!m_DRM->InitDrm())
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbm::{} - failed to initialize Legacy DRM", __FUNCTION__);
+ m_DRM.reset();
+
+ m_DRM = std::make_shared<COffScreenModeSetting>();
+ if (!m_DRM->InitDrm())
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbm::{} - failed to initialize off screen DRM",
+ __FUNCTION__);
+ m_DRM.reset();
+ return false;
+ }
+ }
+ }
+
+ if (!m_GBM->CreateDevice(m_DRM->GetFileDescriptor()))
+ {
+ m_GBM.reset();
+ return false;
+ }
+
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return false;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return false;
+
+ auto setting = settings->GetSetting(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE);
+ if (setting)
+ setting->SetVisible(true);
+
+ setting = settings->GetSetting("videoscreen.limitguisize");
+ if (setting)
+ setting->SetVisible(true);
+
+ CLog::Log(LOGDEBUG, "CWinSystemGbm::{} - initialized DRM", __FUNCTION__);
+ return CWinSystemBase::InitWindowSystem();
+}
+
+bool CWinSystemGbm::DestroyWindowSystem()
+{
+ CLog::Log(LOGDEBUG, "CWinSystemGbm::{} - deinitialized DRM", __FUNCTION__);
+
+ m_libinput.reset();
+
+ return true;
+}
+
+void CWinSystemGbm::UpdateResolutions()
+{
+ RESOLUTION_INFO current = m_DRM->GetCurrentMode();
+
+ auto resolutions = m_DRM->GetModes();
+ if (resolutions.empty())
+ {
+ CLog::Log(LOGWARNING, "CWinSystemGbm::{} - Failed to get resolutions", __FUNCTION__);
+ }
+ else
+ {
+ CDisplaySettings::GetInstance().ClearCustomResolutions();
+
+ for (auto &res : resolutions)
+ {
+ CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
+ CDisplaySettings::GetInstance().AddResolutionInfo(res);
+
+ if (current.iScreenWidth == res.iScreenWidth &&
+ current.iScreenHeight == res.iScreenHeight &&
+ current.iWidth == res.iWidth &&
+ current.iHeight == res.iHeight &&
+ current.fRefreshRate == res.fRefreshRate &&
+ current.dwFlags == res.dwFlags)
+ {
+ CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP) = res;
+ }
+
+ CLog::Log(LOGINFO, "Found resolution {}x{} with {}x{}{} @ {:f} Hz", res.iWidth, res.iHeight,
+ res.iScreenWidth, res.iScreenHeight,
+ res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "", res.fRefreshRate);
+ }
+ }
+
+ CDisplaySettings::GetInstance().ApplyCalibrations();
+}
+
+bool CWinSystemGbm::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ return true;
+}
+
+bool CWinSystemGbm::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ // Notify other subsystems that we will change resolution
+ OnLostDevice();
+
+ if(!m_DRM->SetMode(res))
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbm::{} - failed to set DRM mode", __FUNCTION__);
+ return false;
+ }
+
+ struct gbm_bo *bo = nullptr;
+
+ if (!std::dynamic_pointer_cast<CDRMAtomic>(m_DRM))
+ {
+ bo = m_GBM->GetDevice()->GetSurface()->LockFrontBuffer()->Get();
+ }
+
+ auto result = m_DRM->SetVideoMode(res, bo);
+
+ auto delay =
+ std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ "videoscreen.delayrefreshchange") *
+ 100);
+ if (delay > 0ms)
+ m_dispResetTimer.Set(delay);
+
+ return result;
+}
+
+bool CWinSystemGbm::DisplayHardwareScalingEnabled()
+{
+ auto drmAtomic = std::dynamic_pointer_cast<CDRMAtomic>(m_DRM);
+ if (drmAtomic && drmAtomic->DisplayHardwareScalingEnabled())
+ return true;
+
+ return false;
+}
+
+void CWinSystemGbm::UpdateDisplayHardwareScaling(const RESOLUTION_INFO& resInfo)
+{
+ if (!DisplayHardwareScalingEnabled())
+ return;
+
+ //! @todo The PR that made the res struct constant was abandoned due to drama.
+ // It should be const-corrected and changed here.
+ RESOLUTION_INFO& resMutable = const_cast<RESOLUTION_INFO&>(resInfo);
+
+ SetFullScreen(true, resMutable, false);
+}
+
+void CWinSystemGbm::FlipPage(bool rendered, bool videoLayer)
+{
+ if (m_videoLayerBridge && !videoLayer)
+ {
+ // disable video plane when video layer no longer is active
+ m_videoLayerBridge->Disable();
+ }
+
+ struct gbm_bo *bo = nullptr;
+
+ if (rendered)
+ {
+ bo = m_GBM->GetDevice()->GetSurface()->LockFrontBuffer()->Get();
+ }
+
+ m_DRM->FlipPage(bo, rendered, videoLayer);
+
+ if (m_videoLayerBridge && !videoLayer)
+ {
+ // delete video layer bridge when video layer no longer is active
+ m_videoLayerBridge.reset();
+ }
+}
+
+bool CWinSystemGbm::UseLimitedColor()
+{
+ return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE);
+}
+
+bool CWinSystemGbm::Hide()
+{
+ bool ret = m_DRM->SetActive(false);
+ FlipPage(false, false);
+ return ret;
+}
+
+bool CWinSystemGbm::Show(bool raise)
+{
+ bool ret = m_DRM->SetActive(true);
+ FlipPage(false, false);
+ return ret;
+}
+
+void CWinSystemGbm::Register(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void CWinSystemGbm::Unregister(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ {
+ m_resources.erase(i);
+ }
+}
+
+void CWinSystemGbm::OnLostDevice()
+{
+ CLog::Log(LOGDEBUG, "{} - notify display change event", __FUNCTION__);
+ m_dispReset = true;
+
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ for (auto resource : m_resources)
+ resource->OnLostDisplay();
+}
+
+std::unique_ptr<CVideoSync> CWinSystemGbm::GetVideoSync(void* clock)
+{
+ return std::make_unique<CVideoSyncGbm>(clock);
+}
+
+std::vector<std::string> CWinSystemGbm::GetConnectedOutputs()
+{
+ return m_DRM->GetConnectedConnectorNames();
+}
+
+bool CWinSystemGbm::SetHDR(const VideoPicture* videoPicture)
+{
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return false;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return false;
+
+ if (!settings->GetBool(SETTING_WINSYSTEM_IS_HDR_DISPLAY))
+ return false;
+
+ auto drm = std::dynamic_pointer_cast<CDRMAtomic>(m_DRM);
+ if (!drm)
+ return false;
+
+ if (!videoPicture)
+ {
+ auto connector = drm->GetConnector();
+ if (connector->SupportsProperty("HDR_OUTPUT_METADATA"))
+ {
+ drm->AddProperty(connector, "HDR_OUTPUT_METADATA", 0);
+ drm->SetActive(true);
+
+ if (m_hdr_blob_id)
+ drmModeDestroyPropertyBlob(drm->GetFileDescriptor(), m_hdr_blob_id);
+ m_hdr_blob_id = 0;
+ }
+
+ return true;
+ }
+
+ auto connector = drm->GetConnector();
+ if (connector->SupportsProperty("HDR_OUTPUT_METADATA"))
+ {
+ hdr_output_metadata hdr_metadata = {};
+
+ hdr_metadata.metadata_type = DRMPRIME::HDMI_STATIC_METADATA_TYPE1;
+ hdr_metadata.hdmi_metadata_type1.eotf = DRMPRIME::GetEOTF(*videoPicture);
+ hdr_metadata.hdmi_metadata_type1.metadata_type = DRMPRIME::HDMI_STATIC_METADATA_TYPE1;
+
+ if (m_hdr_blob_id)
+ drmModeDestroyPropertyBlob(drm->GetFileDescriptor(), m_hdr_blob_id);
+ m_hdr_blob_id = 0;
+
+ if (hdr_metadata.hdmi_metadata_type1.eotf)
+ {
+ const AVMasteringDisplayMetadata* mdmd = DRMPRIME::GetMasteringDisplayMetadata(*videoPicture);
+ if (mdmd && mdmd->has_primaries)
+ {
+ // Convert to unsigned 16-bit values in units of 0.00002,
+ // where 0x0000 represents zero and 0xC350 represents 1.0000
+ for (int i = 0; i < 3; i++)
+ {
+ hdr_metadata.hdmi_metadata_type1.display_primaries[i].x =
+ std::round(av_q2d(mdmd->display_primaries[i][0]) * 50000.0);
+ hdr_metadata.hdmi_metadata_type1.display_primaries[i].y =
+ std::round(av_q2d(mdmd->display_primaries[i][1]) * 50000.0);
+
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - display_primaries[{}].x: {}",
+ __FUNCTION__, i, hdr_metadata.hdmi_metadata_type1.display_primaries[i].x);
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - display_primaries[{}].y: {}",
+ __FUNCTION__, i, hdr_metadata.hdmi_metadata_type1.display_primaries[i].y);
+ }
+ hdr_metadata.hdmi_metadata_type1.white_point.x =
+ std::round(av_q2d(mdmd->white_point[0]) * 50000.0);
+ hdr_metadata.hdmi_metadata_type1.white_point.y =
+ std::round(av_q2d(mdmd->white_point[1]) * 50000.0);
+
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - white_point.x: {}", __FUNCTION__,
+ hdr_metadata.hdmi_metadata_type1.white_point.x);
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - white_point.y: {}", __FUNCTION__,
+ hdr_metadata.hdmi_metadata_type1.white_point.y);
+ }
+ if (mdmd && mdmd->has_luminance)
+ {
+ // Convert to unsigned 16-bit value in units of 1 cd/m2,
+ // where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2
+ hdr_metadata.hdmi_metadata_type1.max_display_mastering_luminance =
+ std::round(av_q2d(mdmd->max_luminance));
+
+ // Convert to unsigned 16-bit value in units of 0.0001 cd/m2,
+ // where 0x0001 represents 0.0001 cd/m2 and 0xFFFF represents 6.5535 cd/m2
+ hdr_metadata.hdmi_metadata_type1.min_display_mastering_luminance =
+ std::round(av_q2d(mdmd->min_luminance) * 10000.0);
+
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - max_display_mastering_luminance: {}",
+ __FUNCTION__, hdr_metadata.hdmi_metadata_type1.max_display_mastering_luminance);
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - min_display_mastering_luminance: {}",
+ __FUNCTION__, hdr_metadata.hdmi_metadata_type1.min_display_mastering_luminance);
+ }
+
+ const AVContentLightMetadata* clmd = DRMPRIME::GetContentLightMetadata(*videoPicture);
+ if (clmd)
+ {
+ hdr_metadata.hdmi_metadata_type1.max_cll = clmd->MaxCLL;
+ hdr_metadata.hdmi_metadata_type1.max_fall = clmd->MaxFALL;
+
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - max_cll: {}", __FUNCTION__,
+ hdr_metadata.hdmi_metadata_type1.max_cll);
+ CLog::Log(LOGDEBUG, LOGVIDEO, "CWinSystemGbm::{} - max_fall: {}", __FUNCTION__,
+ hdr_metadata.hdmi_metadata_type1.max_fall);
+ }
+
+ drmModeCreatePropertyBlob(drm->GetFileDescriptor(), &hdr_metadata, sizeof(hdr_metadata),
+ &m_hdr_blob_id);
+ }
+
+ drm->AddProperty(connector, "HDR_OUTPUT_METADATA", m_hdr_blob_id);
+ drm->SetActive(true);
+ }
+
+ return true;
+}
+
+bool CWinSystemGbm::IsHDRDisplay()
+{
+ auto drm = std::dynamic_pointer_cast<CDRMAtomic>(m_DRM);
+ if (!drm)
+ return false;
+
+ auto connector = drm->GetConnector();
+ if (!connector)
+ return false;
+
+ //! @todo: improve detection (edid?)
+ // we have no way to know if the display is actually HDR capable and we blindly set the HDR metadata
+ return connector->SupportsProperty("HDR_OUTPUT_METADATA");
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbm.h b/xbmc/windowing/gbm/WinSystemGbm.h
new file mode 100644
index 0000000..b313d33
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbm.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "VideoLayerBridge.h"
+#include "drm/DRMUtils.h"
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+#include "windowing/WinSystem.h"
+
+#include "platform/linux/input/LibInputHandler.h"
+
+#include <utility>
+
+#include <gbm.h>
+
+class IDispResource;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CWinSystemGbm : public CWinSystemBase
+{
+public:
+ CWinSystemGbm();
+ ~CWinSystemGbm() override = default;
+
+ const std::string GetName() override { return "gbm"; }
+
+ bool InitWindowSystem() override;
+ bool DestroyWindowSystem() override;
+
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ bool DisplayHardwareScalingEnabled() override;
+ void UpdateDisplayHardwareScaling(const RESOLUTION_INFO& resInfo) override;
+
+ void FlipPage(bool rendered, bool videoLayer);
+
+ bool CanDoWindowed() override { return false; }
+ void UpdateResolutions() override;
+
+ bool UseLimitedColor() override;
+
+ bool Hide() override;
+ bool Show(bool raise = true) override;
+ void Register(IDispResource* resource) override;
+ void Unregister(IDispResource* resource) override;
+
+ bool SetHDR(const VideoPicture* videoPicture) override;
+ bool IsHDRDisplay() override;
+
+ std::shared_ptr<CVideoLayerBridge> GetVideoLayerBridge() const { return m_videoLayerBridge; }
+ void RegisterVideoLayerBridge(std::shared_ptr<CVideoLayerBridge> bridge)
+ {
+ m_videoLayerBridge = std::move(bridge);
+ };
+
+ CGBMUtils::CGBMDevice* GetGBMDevice() const { return m_GBM->GetDevice(); }
+ std::shared_ptr<CDRMUtils> GetDrm() const { return m_DRM; }
+
+ std::vector<std::string> GetConnectedOutputs() override;
+
+protected:
+ void OnLostDevice();
+
+ std::unique_ptr<CVideoSync> GetVideoSync(void* clock) override;
+
+ std::shared_ptr<CDRMUtils> m_DRM;
+ std::unique_ptr<CGBMUtils> m_GBM;
+ std::shared_ptr<CVideoLayerBridge> m_videoLayerBridge;
+
+ CCriticalSection m_resourceSection;
+ std::vector<IDispResource*> m_resources;
+
+ bool m_dispReset = false;
+ XbmcThreads::EndTime<> m_dispResetTimer;
+ std::unique_ptr<CLibInputHandler> m_libinput;
+
+private:
+ uint32_t m_hdr_blob_id = 0;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp b/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp
new file mode 100644
index 0000000..83a5941
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemGbmEGLContext.h"
+
+#include "OptionalsReg.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "utils/log.h"
+
+using namespace KODI::WINDOWING::GBM;
+using namespace KODI::WINDOWING::LINUX;
+
+bool CWinSystemGbmEGLContext::InitWindowSystemEGL(EGLint renderableType, EGLint apiType)
+{
+ if (!CWinSystemGbm::InitWindowSystem())
+ {
+ return false;
+ }
+
+ if (!m_eglContext.CreatePlatformDisplay(m_GBM->GetDevice()->Get(), m_GBM->GetDevice()->Get()))
+ {
+ return false;
+ }
+
+ if (!m_eglContext.InitializeDisplay(apiType))
+ {
+ return false;
+ }
+
+ auto plane = m_DRM->GetGuiPlane();
+ uint32_t visualId = plane != nullptr ? plane->GetFormat() : DRM_FORMAT_XRGB2101010;
+
+ // prefer alpha visual id, fallback to non-alpha visual id
+ if (!m_eglContext.ChooseConfig(renderableType, CDRMUtils::FourCCWithAlpha(visualId)) &&
+ !m_eglContext.ChooseConfig(renderableType, CDRMUtils::FourCCWithoutAlpha(visualId)))
+ {
+ // fallback to 8bit format if no EGL config was found for 10bit
+ if (plane)
+ plane->SetFormat(DRM_FORMAT_XRGB8888);
+
+ visualId = plane != nullptr ? plane->GetFormat() : DRM_FORMAT_XRGB8888;
+
+ if (!m_eglContext.ChooseConfig(renderableType, CDRMUtils::FourCCWithAlpha(visualId)) &&
+ !m_eglContext.ChooseConfig(renderableType, CDRMUtils::FourCCWithoutAlpha(visualId)))
+ {
+ return false;
+ }
+ }
+
+ if (!CreateContext())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool CWinSystemGbmEGLContext::CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res)
+{
+ //Notify other subsystems that we change resolution
+ OnLostDevice();
+
+ if (!DestroyWindow())
+ {
+ return false;
+ }
+
+ if (!m_DRM->SetMode(res))
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbmEGLContext::{} - failed to set DRM mode", __FUNCTION__);
+ return false;
+ }
+
+ uint32_t format = m_eglContext.GetConfigAttrib(EGL_NATIVE_VISUAL_ID);
+
+ std::vector<uint64_t> modifiers;
+
+ auto plane = m_DRM->GetGuiPlane();
+ if (plane)
+ modifiers = plane->GetModifiersForFormat(format);
+
+ if (!m_GBM->GetDevice()->CreateSurface(res.iWidth, res.iHeight, format, modifiers.data(),
+ modifiers.size()))
+ {
+ CLog::Log(LOGERROR, "CWinSystemGbmEGLContext::{} - failed to initialize GBM", __FUNCTION__);
+ return false;
+ }
+
+ // This check + the reinterpret cast is for security reason, if the user has outdated platform header files which often is the case
+ static_assert(sizeof(EGLNativeWindowType) == sizeof(gbm_surface*), "Declaration specifier differs in size");
+
+ if (!m_eglContext.CreatePlatformSurface(
+ m_GBM->GetDevice()->GetSurface()->Get(),
+ reinterpret_cast<khronos_uintptr_t>(m_GBM->GetDevice()->GetSurface()->Get())))
+ {
+ return false;
+ }
+
+ if (!m_eglContext.BindContext())
+ {
+ return false;
+ }
+
+ m_bFullScreen = fullScreen;
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_fRefreshRate = res.fRefreshRate;
+
+ CLog::Log(LOGDEBUG, "CWinSystemGbmEGLContext::{} - initialized GBM", __FUNCTION__);
+ return true;
+}
+
+bool CWinSystemGbmEGLContext::DestroyWindow()
+{
+ m_eglContext.DestroySurface();
+
+ CLog::Log(LOGDEBUG, "CWinSystemGbmEGLContext::{} - deinitialized GBM", __FUNCTION__);
+ return true;
+}
+
+bool CWinSystemGbmEGLContext::DestroyWindowSystem()
+{
+ CDVDFactoryCodec::ClearHWAccels();
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ m_eglContext.Destroy();
+
+ return CWinSystemGbm::DestroyWindowSystem();
+}
+
+void CWinSystemGbmEGLContext::delete_CVaapiProxy::operator()(CVaapiProxy *p) const
+{
+ VaapiProxyDelete(p);
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmEGLContext.h b/xbmc/windowing/gbm/WinSystemGbmEGLContext.h
new file mode 100644
index 0000000..84f863d
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmEGLContext.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "WinSystemGbm.h"
+#include "utils/EGLUtils.h"
+#include "windowing/linux/WinSystemEGL.h"
+
+#include <memory>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CVaapiProxy;
+
+class CWinSystemGbmEGLContext : public KODI::WINDOWING::LINUX::CWinSystemEGL, public CWinSystemGbm
+{
+public:
+ ~CWinSystemGbmEGLContext() override = default;
+
+ bool DestroyWindowSystem() override;
+ bool CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res) override;
+ bool DestroyWindow() override;
+
+protected:
+ CWinSystemGbmEGLContext(EGLenum platform, std::string const& platformExtension)
+ : CWinSystemEGL{platform, platformExtension}
+ {}
+
+ /**
+ * Inheriting classes should override InitWindowSystem() without parameters
+ * and call this function there with appropriate parameters
+ */
+ bool InitWindowSystemEGL(EGLint renderableType, EGLint apiType);
+ virtual bool CreateContext() = 0;
+
+ struct delete_CVaapiProxy
+ {
+ void operator()(CVaapiProxy *p) const;
+ };
+ std::unique_ptr<CVaapiProxy, delete_CVaapiProxy> m_vaapiProxy;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp b/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp
new file mode 100644
index 0000000..e4ff49c
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemGbmGLContext.h"
+
+#include "OptionalsReg.h"
+#include "cores/RetroPlayer/process/gbm/RPProcessInfoGbm.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererDMA.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "rendering/gl/ScreenshotSurfaceGL.h"
+#include "utils/BufferObjectFactory.h"
+#include "utils/DMAHeapBufferObject.h"
+#include "utils/DumbBufferObject.h"
+#include "utils/GBMBufferObject.h"
+#include "utils/UDMABufferObject.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include <mutex>
+
+#include <EGL/eglext.h>
+
+using namespace KODI::WINDOWING::GBM;
+
+using namespace std::chrono_literals;
+
+CWinSystemGbmGLContext::CWinSystemGbmGLContext()
+: CWinSystemGbmEGLContext(EGL_PLATFORM_GBM_MESA, "EGL_MESA_platform_gbm")
+{}
+
+void CWinSystemGbmGLContext::Register()
+{
+ CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem, "gbm");
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemGbmGLContext::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemGbmGLContext>();
+}
+
+bool CWinSystemGbmGLContext::InitWindowSystem()
+{
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CDVDFactoryCodec::ClearHWAccels();
+ CLinuxRendererGL::Register();
+ RETRO::CRPProcessInfoGbm::Register();
+ RETRO::CRPProcessInfoGbm::RegisterRendererFactory(new RETRO::CRendererFactoryDMA);
+ RETRO::CRPProcessInfoGbm::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGL);
+
+ if (!CWinSystemGbmEGLContext::InitWindowSystemEGL(EGL_OPENGL_BIT, EGL_OPENGL_API))
+ {
+ return false;
+ }
+
+ bool general, deepColor;
+ m_vaapiProxy.reset(VaapiProxyCreate(m_DRM->GetRenderNodeFileDescriptor()));
+ VaapiProxyConfig(m_vaapiProxy.get(), m_eglContext.GetEGLDisplay());
+ VAAPIRegisterRenderGL(m_vaapiProxy.get(), general, deepColor);
+
+ if (general)
+ {
+ VAAPIRegister(m_vaapiProxy.get(), deepColor);
+ }
+
+ CScreenshotSurfaceGL::Register();
+
+ CBufferObjectFactory::ClearBufferObjects();
+ CDumbBufferObject::Register();
+#if defined(HAS_GBM_BO_MAP)
+ CGBMBufferObject::Register();
+#endif
+#if defined(HAVE_LINUX_MEMFD) && defined(HAVE_LINUX_UDMABUF)
+ CUDMABufferObject::Register();
+#endif
+#if defined(HAVE_LINUX_DMA_HEAP)
+ CDMAHeapBufferObject::Register();
+#endif
+
+ return true;
+}
+
+bool CWinSystemGbmGLContext::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ if (res.iWidth != m_nWidth ||
+ res.iHeight != m_nHeight)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemGbmGLContext::{} - resolution changed, creating a new window",
+ __FUNCTION__);
+ CreateNewWindow("", fullScreen, res);
+ }
+
+ if (!m_eglContext.TrySwapBuffers())
+ {
+ CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
+ throw std::runtime_error("eglSwapBuffers failed");
+ }
+
+ CWinSystemGbm::SetFullScreen(fullScreen, res, blankOtherDisplays);
+ CRenderSystemGL::ResetRenderSystem(res.iWidth, res.iHeight);
+
+ return true;
+}
+
+void CWinSystemGbmGLContext::PresentRender(bool rendered, bool videoLayer)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ if (rendered || videoLayer)
+ {
+ if (rendered)
+ {
+ if (!m_eglContext.TrySwapBuffers())
+ {
+ CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
+ throw std::runtime_error("eglSwapBuffers failed");
+ }
+ }
+ CWinSystemGbm::FlipPage(rendered, videoLayer);
+
+ if (m_dispReset && m_dispResetTimer.IsTimePast())
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemGbmGLContext::{} - Sending display reset to all clients",
+ __FUNCTION__);
+ m_dispReset = false;
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+
+ for (auto resource : m_resources)
+ resource->OnResetDisplay();
+ }
+ }
+ else
+ {
+ KODI::TIME::Sleep(10ms);
+ }
+}
+
+bool CWinSystemGbmGLContext::CreateContext()
+{
+ const EGLint glMajor = 3;
+ const EGLint glMinor = 2;
+
+ CEGLAttributesVec contextAttribs;
+ contextAttribs.Add({{EGL_CONTEXT_MAJOR_VERSION_KHR, glMajor},
+ {EGL_CONTEXT_MINOR_VERSION_KHR, glMinor},
+ {EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR}});
+
+ if (!m_eglContext.CreateContext(contextAttribs))
+ {
+ CEGLAttributesVec fallbackContextAttribs;
+ fallbackContextAttribs.Add({{EGL_CONTEXT_CLIENT_VERSION, 2}});
+
+ if (!m_eglContext.CreateContext(fallbackContextAttribs))
+ {
+ CLog::Log(LOGERROR, "EGL context creation failed");
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "Your OpenGL drivers do not support OpenGL {}.{} core profile. Kodi will run in compatibility mode, but performance may suffer.", glMajor, glMinor);
+ }
+ }
+
+ return true;
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLContext.h b/xbmc/windowing/gbm/WinSystemGbmGLContext.h
new file mode 100644
index 0000000..8994ce6
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmGLContext.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "WinSystemGbmEGLContext.h"
+#include "rendering/gl/RenderSystemGL.h"
+#include "utils/EGLUtils.h"
+
+#include <memory>
+
+class CVaapiProxy;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CWinSystemGbmGLContext : public CWinSystemGbmEGLContext, public CRenderSystemGL
+{
+public:
+ CWinSystemGbmGLContext();
+ ~CWinSystemGbmGLContext() override = default;
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystemBase via CWinSystemGbm
+ CRenderSystemBase *GetRenderSystem() override { return this; }
+ bool InitWindowSystem() override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ void PresentRender(bool rendered, bool videoLayer) override;
+protected:
+ void SetVSyncImpl(bool enable) override {}
+ void PresentRenderImpl(bool rendered) override {};
+ bool CreateContext() override;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp
new file mode 100644
index 0000000..0d071c3
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemGbmGLESContext.h"
+
+#include "OptionalsReg.h"
+#include "cores/RetroPlayer/process/gbm/RPProcessInfoGbm.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererDMA.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h"
+#include "cores/VideoPlayer/Process/gbm/ProcessInfoGBM.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.h"
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "rendering/gles/ScreenshotSurfaceGLES.h"
+#include "utils/BufferObjectFactory.h"
+#include "utils/DMAHeapBufferObject.h"
+#include "utils/DumbBufferObject.h"
+#include "utils/GBMBufferObject.h"
+#include "utils/UDMABufferObject.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include <mutex>
+
+#include <gbm.h>
+
+using namespace KODI::WINDOWING::GBM;
+
+using namespace std::chrono_literals;
+
+CWinSystemGbmGLESContext::CWinSystemGbmGLESContext()
+: CWinSystemGbmEGLContext(EGL_PLATFORM_GBM_MESA, "EGL_MESA_platform_gbm")
+{}
+
+void CWinSystemGbmGLESContext::Register()
+{
+ CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem, "gbm");
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemGbmGLESContext::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemGbmGLESContext>();
+}
+
+bool CWinSystemGbmGLESContext::InitWindowSystem()
+{
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CDVDFactoryCodec::ClearHWAccels();
+ CLinuxRendererGLES::Register();
+ RETRO::CRPProcessInfoGbm::Register();
+ RETRO::CRPProcessInfoGbm::RegisterRendererFactory(new RETRO::CRendererFactoryDMA);
+ RETRO::CRPProcessInfoGbm::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
+
+ if (!CWinSystemGbmEGLContext::InitWindowSystemEGL(EGL_OPENGL_ES2_BIT, EGL_OPENGL_ES_API))
+ {
+ return false;
+ }
+
+ bool general, deepColor;
+ m_vaapiProxy.reset(GBM::VaapiProxyCreate(m_DRM->GetRenderNodeFileDescriptor()));
+ GBM::VaapiProxyConfig(m_vaapiProxy.get(), m_eglContext.GetEGLDisplay());
+ GBM::VAAPIRegisterRenderGLES(m_vaapiProxy.get(), general, deepColor);
+
+ if (general)
+ {
+ GBM::VAAPIRegister(m_vaapiProxy.get(), deepColor);
+ }
+
+ CRendererDRMPRIMEGLES::Register();
+ CRendererDRMPRIME::Register();
+ CDVDVideoCodecDRMPRIME::Register();
+ VIDEOPLAYER::CProcessInfoGBM::Register();
+
+ CScreenshotSurfaceGLES::Register();
+
+ CBufferObjectFactory::ClearBufferObjects();
+ CDumbBufferObject::Register();
+#if defined(HAS_GBM_BO_MAP)
+ CGBMBufferObject::Register();
+#endif
+#if defined(HAVE_LINUX_MEMFD) && defined(HAVE_LINUX_UDMABUF)
+ CUDMABufferObject::Register();
+#endif
+#if defined(HAVE_LINUX_DMA_HEAP)
+ CDMAHeapBufferObject::Register();
+#endif
+
+ return true;
+}
+
+bool CWinSystemGbmGLESContext::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ if (res.iWidth != m_nWidth ||
+ res.iHeight != m_nHeight)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemGbmGLESContext::{} - resolution changed, creating a new window",
+ __FUNCTION__);
+ CreateNewWindow("", fullScreen, res);
+ }
+
+ if (!m_eglContext.TrySwapBuffers())
+ {
+ CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
+ throw std::runtime_error("eglSwapBuffers failed");
+ }
+
+ CWinSystemGbm::SetFullScreen(fullScreen, res, blankOtherDisplays);
+ CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
+
+ return true;
+}
+
+void CWinSystemGbmGLESContext::PresentRender(bool rendered, bool videoLayer)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ if (rendered || videoLayer)
+ {
+ if (rendered)
+ {
+ if (!m_eglContext.TrySwapBuffers())
+ {
+ CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
+ throw std::runtime_error("eglSwapBuffers failed");
+ }
+ }
+ CWinSystemGbm::FlipPage(rendered, videoLayer);
+
+ if (m_dispReset && m_dispResetTimer.IsTimePast())
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemGbmGLESContext::{} - Sending display reset to all clients",
+ __FUNCTION__);
+ m_dispReset = false;
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+
+ for (auto resource : m_resources)
+ resource->OnResetDisplay();
+ }
+ }
+ else
+ {
+ KODI::TIME::Sleep(10ms);
+ }
+}
+
+bool CWinSystemGbmGLESContext::CreateContext()
+{
+ CEGLAttributesVec contextAttribs;
+ contextAttribs.Add({{EGL_CONTEXT_CLIENT_VERSION, 2}});
+
+ if (!m_eglContext.CreateContext(contextAttribs))
+ {
+ CLog::Log(LOGERROR, "EGL context creation failed");
+ return false;
+ }
+ return true;
+}
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLESContext.h b/xbmc/windowing/gbm/WinSystemGbmGLESContext.h
new file mode 100644
index 0000000..8b9de77
--- /dev/null
+++ b/xbmc/windowing/gbm/WinSystemGbmGLESContext.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "WinSystemGbmEGLContext.h"
+#include "rendering/gles/RenderSystemGLES.h"
+#include "utils/EGLUtils.h"
+
+#include <memory>
+
+class CVaapiProxy;
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CWinSystemGbmGLESContext : public CWinSystemGbmEGLContext, public CRenderSystemGLES
+{
+public:
+ CWinSystemGbmGLESContext();
+ ~CWinSystemGbmGLESContext() override = default;
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystemBase via CWinSystemGbm
+ CRenderSystemBase *GetRenderSystem() override { return this; }
+ bool InitWindowSystem() override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+ void PresentRender(bool rendered, bool videoLayer) override;
+protected:
+ void SetVSyncImpl(bool enable) override {}
+ void PresentRenderImpl(bool rendered) override {};
+ bool CreateContext() override;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/drm/CMakeLists.txt b/xbmc/windowing/gbm/drm/CMakeLists.txt
new file mode 100644
index 0000000..8bd07ea
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(SOURCES DRMAtomic.cpp
+ DRMConnector.cpp
+ DRMCrtc.cpp
+ DRMEncoder.cpp
+ DRMLegacy.cpp
+ DRMObject.cpp
+ DRMPlane.cpp
+ DRMUtils.cpp
+ OffScreenModeSetting.cpp)
+
+set(HEADERS DRMAtomic.h
+ DRMConnector.h
+ DRMCrtc.h
+ DRMEncoder.h
+ DRMLegacy.h
+ DRMObject.h
+ DRMPlane.h
+ DRMUtils.h
+ OffScreenModeSetting.h)
+
+core_add_library(windowing_gbm_drm)
diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.cpp b/xbmc/windowing/gbm/drm/DRMAtomic.cpp
new file mode 100644
index 0000000..5d61a69
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMAtomic.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMAtomic.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/log.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <drm_fourcc.h>
+#include <drm_mode.h>
+#include <unistd.h>
+
+using namespace KODI::WINDOWING::GBM;
+
+namespace
+{
+
+const auto SETTING_VIDEOSCREEN_HW_SCALING_FILTER = "videoscreen.hwscalingfilter";
+
+uint32_t GetScalingFactor(uint32_t srcWidth,
+ uint32_t srcHeight,
+ uint32_t destWidth,
+ uint32_t destHeight)
+{
+ uint32_t factor_W = destWidth / srcWidth;
+ uint32_t factor_H = destHeight / srcHeight;
+ if (factor_W != factor_H)
+ return (factor_W < factor_H) ? factor_W : factor_H;
+ return factor_W;
+}
+
+} // namespace
+
+bool CDRMAtomic::SetScalingFilter(CDRMObject* object, const char* name, const char* type)
+{
+ bool result;
+ uint64_t value;
+ std::tie(result, value) = m_gui_plane->GetPropertyValue(name, type);
+ if (!result)
+ return false;
+
+ if (!AddProperty(object, name, value))
+ return false;
+
+ uint32_t mar_scale_factor =
+ GetScalingFactor(m_width, m_height, m_mode->hdisplay, m_mode->vdisplay);
+ AddProperty(object, "CRTC_W", (mar_scale_factor * m_width));
+ AddProperty(object, "CRTC_H", (mar_scale_factor * m_height));
+
+ return true;
+}
+
+void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer)
+{
+ uint32_t blob_id;
+
+ if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
+ {
+ if (!AddProperty(m_connector, "CRTC_ID", m_crtc->GetCrtcId()))
+ return;
+
+ if (drmModeCreatePropertyBlob(m_fd, m_mode, sizeof(*m_mode), &blob_id) != 0)
+ return;
+
+ if (m_active && m_orig_crtc && m_orig_crtc->GetCrtcId() != m_crtc->GetCrtcId())
+ {
+ // if using a different CRTC than the original, disable original to avoid EINVAL
+ if (!AddProperty(m_orig_crtc, "MODE_ID", 0))
+ return;
+
+ if (!AddProperty(m_orig_crtc, "ACTIVE", 0))
+ return;
+ }
+
+ if (!AddProperty(m_crtc, "MODE_ID", blob_id))
+ return;
+
+ if (!AddProperty(m_crtc, "ACTIVE", m_active ? 1 : 0))
+ return;
+ }
+
+ if (rendered)
+ {
+ AddProperty(m_gui_plane, "FB_ID", fb_id);
+ AddProperty(m_gui_plane, "CRTC_ID", m_crtc->GetCrtcId());
+ AddProperty(m_gui_plane, "SRC_X", 0);
+ AddProperty(m_gui_plane, "SRC_Y", 0);
+ AddProperty(m_gui_plane, "SRC_W", m_width << 16);
+ AddProperty(m_gui_plane, "SRC_H", m_height << 16);
+ AddProperty(m_gui_plane, "CRTC_X", 0);
+ AddProperty(m_gui_plane, "CRTC_Y", 0);
+ //! @todo: disabled until upstream kernel changes are merged
+ // if (DisplayHardwareScalingEnabled())
+ // {
+ // SetScalingFilter(m_gui_plane, "SCALING_FILTER", "Nearest Neighbor");
+ // }
+ // else
+ {
+ AddProperty(m_gui_plane, "CRTC_W", m_mode->hdisplay);
+ AddProperty(m_gui_plane, "CRTC_H", m_mode->vdisplay);
+ }
+
+ }
+ else if (videoLayer && !CServiceBroker::GetGUI()->GetWindowManager().HasVisibleControls())
+ {
+ // disable gui plane when video layer is active and gui has no visible controls
+ AddProperty(m_gui_plane, "FB_ID", 0);
+ AddProperty(m_gui_plane, "CRTC_ID", 0);
+ }
+
+ if (CServiceBroker::GetLogging().CanLogComponent(LOGWINDOWING))
+ m_req->LogAtomicRequest();
+
+ auto ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags | DRM_MODE_ATOMIC_TEST_ONLY, nullptr);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR,
+ "CDRMAtomic::{} - test commit failed: ({}) - falling back to last successful atomic "
+ "request",
+ __FUNCTION__, strerror(errno));
+
+ auto oldRequest = m_atomicRequestQueue.front().get();
+ CDRMAtomicRequest::LogAtomicDiff(m_req, oldRequest);
+ m_req = oldRequest;
+
+ // update the old atomic request with the new fb id to avoid tearing
+ if (rendered)
+ AddProperty(m_gui_plane, "FB_ID", fb_id);
+ }
+
+ ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags, nullptr);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__,
+ strerror(errno));
+ }
+
+ if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
+ {
+ if (drmModeDestroyPropertyBlob(m_fd, blob_id) != 0)
+ CLog::Log(LOGERROR, "CDRMAtomic::{} - failed to destroy property blob: {}", __FUNCTION__,
+ strerror(errno));
+ }
+
+ if (m_atomicRequestQueue.size() > 1)
+ m_atomicRequestQueue.pop_back();
+
+ m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>());
+ m_req = m_atomicRequestQueue.back().get();
+}
+
+void CDRMAtomic::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer)
+{
+ struct drm_fb *drm_fb = nullptr;
+
+ if (rendered)
+ {
+ if (videoLayer)
+ m_gui_plane->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane->GetFormat()));
+ else
+ m_gui_plane->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane->GetFormat()));
+
+ drm_fb = CDRMUtils::DrmFbGetFromBo(bo);
+ if (!drm_fb)
+ {
+ CLog::Log(LOGERROR, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__);
+ return;
+ }
+ }
+
+ uint32_t flags = 0;
+
+ if (m_need_modeset)
+ {
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ m_need_modeset = false;
+ CLog::Log(LOGDEBUG, "CDRMAtomic::{} - Execute modeset at next commit", __FUNCTION__);
+ }
+
+ DrmAtomicCommit(!drm_fb ? 0 : drm_fb->fb_id, flags, rendered, videoLayer);
+}
+
+bool CDRMAtomic::InitDrm()
+{
+ if (!CDRMUtils::OpenDrm(true))
+ return false;
+
+ auto ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>());
+ m_req = m_atomicRequestQueue.back().get();
+
+ if (!CDRMUtils::InitDrm())
+ return false;
+
+ for (auto& plane : m_planes)
+ {
+ AddProperty(plane.get(), "FB_ID", 0);
+ AddProperty(plane.get(), "CRTC_ID", 0);
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMAtomic::{} - initialized atomic DRM", __FUNCTION__);
+
+ //! @todo: disabled until upstream kernel changes are merged
+ // if (m_gui_plane->SupportsProperty("SCALING_FILTER"))
+ // {
+ // const std::shared_ptr<CSettings> settings =
+ // CServiceBroker::GetSettingsComponent()->GetSettings();
+ // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true);
+ // }
+
+ return true;
+}
+
+void CDRMAtomic::DestroyDrm()
+{
+ CDRMUtils::DestroyDrm();
+}
+
+bool CDRMAtomic::SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo)
+{
+ m_need_modeset = true;
+
+ return true;
+}
+
+bool CDRMAtomic::SetActive(bool active)
+{
+ m_need_modeset = true;
+ m_active = active;
+
+ return true;
+}
+
+bool CDRMAtomic::AddProperty(CDRMObject* object, const char* name, uint64_t value)
+{
+ return m_req->AddProperty(object, name, value);
+}
+
+bool CDRMAtomic::DisplayHardwareScalingEnabled()
+{
+ auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ if (settings && settings->GetBool(SETTING_VIDEOSCREEN_HW_SCALING_FILTER))
+ return true;
+
+ return false;
+}
+
+CDRMAtomic::CDRMAtomicRequest::CDRMAtomicRequest() : m_atomicRequest(drmModeAtomicAlloc())
+{
+}
+
+bool CDRMAtomic::CDRMAtomicRequest::AddProperty(CDRMObject* object, const char* name, uint64_t value)
+{
+ uint32_t propertyId = object->GetPropertyId(name);
+ if (propertyId == 0)
+ return false;
+
+ int ret = drmModeAtomicAddProperty(m_atomicRequest.get(), object->GetId(), propertyId, value);
+ if (ret < 0)
+ return false;
+
+ m_atomicRequestItems[object][propertyId] = value;
+ return true;
+}
+
+void CDRMAtomic::CDRMAtomicRequest::LogAtomicDiff(CDRMAtomicRequest* current,
+ CDRMAtomicRequest* old)
+{
+ std::map<CDRMObject*, std::map<uint32_t, uint64_t>> atomicDiff;
+
+ for (const auto& object : current->m_atomicRequestItems)
+ {
+ auto sameObject = old->m_atomicRequestItems.find(object.first);
+ if (sameObject != old->m_atomicRequestItems.end())
+ {
+ std::map<uint32_t, uint64_t> propertyDiff;
+
+ std::set_difference(current->m_atomicRequestItems[object.first].begin(),
+ current->m_atomicRequestItems[object.first].end(),
+ old->m_atomicRequestItems[object.first].begin(),
+ old->m_atomicRequestItems[object.first].end(),
+ std::inserter(propertyDiff, propertyDiff.begin()));
+
+ atomicDiff[object.first] = propertyDiff;
+ }
+ else
+ {
+ atomicDiff[object.first] = current->m_atomicRequestItems[object.first];
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request Diff:", __FUNCTION__);
+
+ LogAtomicRequest(LOGERROR, atomicDiff);
+}
+
+void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest()
+{
+ CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request:", __FUNCTION__);
+ LogAtomicRequest(LOGDEBUG, m_atomicRequestItems);
+}
+
+void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest(
+ uint8_t logLevel, std::map<CDRMObject*, std::map<uint32_t, uint64_t>>& atomicRequestItems)
+{
+ std::string message;
+ for (const auto& object : atomicRequestItems)
+ {
+ message.append("\nObject: " + object.first->GetTypeName() +
+ "\tID: " + std::to_string(object.first->GetId()));
+ for (const auto& property : object.second)
+ message.append("\n Property: " + object.first->GetPropertyName(property.first) +
+ "\tID: " + std::to_string(property.first) +
+ "\tValue: " + std::to_string(property.second));
+ }
+
+ CLog::Log(logLevel, "{}", message);
+}
+
+void CDRMAtomic::CDRMAtomicRequest::DrmModeAtomicReqDeleter::operator()(drmModeAtomicReqPtr p) const
+{
+ drmModeAtomicFree(p);
+};
diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.h b/xbmc/windowing/gbm/drm/DRMAtomic.h
new file mode 100644
index 0000000..ca2cd9a
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMAtomic.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMUtils.h"
+
+#include <cstdint>
+#include <deque>
+#include <map>
+#include <memory>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMAtomic : public CDRMUtils
+{
+public:
+ CDRMAtomic() = default;
+ ~CDRMAtomic() override = default;
+ void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override;
+ bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override;
+ bool SetActive(bool active) override;
+ bool InitDrm() override;
+ void DestroyDrm() override;
+ bool AddProperty(CDRMObject* object, const char* name, uint64_t value);
+
+ bool DisplayHardwareScalingEnabled();
+
+private:
+ void DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer);
+
+ bool SetScalingFilter(CDRMObject* object, const char* name, const char* type);
+
+ bool m_need_modeset;
+ bool m_active = true;
+
+ class CDRMAtomicRequest
+ {
+ public:
+ CDRMAtomicRequest();
+ ~CDRMAtomicRequest() = default;
+ CDRMAtomicRequest(const CDRMAtomicRequest& right) = delete;
+
+ drmModeAtomicReqPtr Get() const { return m_atomicRequest.get(); }
+
+ bool AddProperty(CDRMObject* object, const char* name, uint64_t value);
+ void LogAtomicRequest();
+
+ static void LogAtomicDiff(CDRMAtomicRequest* current, CDRMAtomicRequest* old);
+
+ private:
+ static void LogAtomicRequest(
+ uint8_t logLevel, std::map<CDRMObject*, std::map<uint32_t, uint64_t>>& atomicRequestItems);
+
+ std::map<CDRMObject*, std::map<uint32_t, uint64_t>> m_atomicRequestItems;
+
+ struct DrmModeAtomicReqDeleter
+ {
+ void operator()(drmModeAtomicReqPtr p) const;
+ };
+
+ std::unique_ptr<drmModeAtomicReq, DrmModeAtomicReqDeleter> m_atomicRequest;
+ };
+
+ CDRMAtomicRequest* m_req = nullptr;
+ std::deque<std::unique_ptr<CDRMAtomicRequest>> m_atomicRequestQueue;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/drm/DRMConnector.cpp b/xbmc/windowing/gbm/drm/DRMConnector.cpp
new file mode 100644
index 0000000..fc76b90
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMConnector.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMConnector.h"
+
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+
+#include <map>
+
+using namespace KODI::WINDOWING::GBM;
+
+using namespace std::chrono_literals;
+
+namespace
+{
+
+std::map<int, std::string> connectorTypeNames = {
+ {DRM_MODE_CONNECTOR_Unknown, "unknown"},
+ {DRM_MODE_CONNECTOR_VGA, "VGA"},
+ {DRM_MODE_CONNECTOR_DVII, "DVI-I"},
+ {DRM_MODE_CONNECTOR_DVID, "DVI-D"},
+ {DRM_MODE_CONNECTOR_DVIA, "DVI-A"},
+ {DRM_MODE_CONNECTOR_Composite, "composite"},
+ {DRM_MODE_CONNECTOR_SVIDEO, "s-video"},
+ {DRM_MODE_CONNECTOR_LVDS, "LVDS"},
+ {DRM_MODE_CONNECTOR_Component, "component"},
+ {DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN"},
+ {DRM_MODE_CONNECTOR_DisplayPort, "DP"},
+ {DRM_MODE_CONNECTOR_HDMIA, "HDMI-A"},
+ {DRM_MODE_CONNECTOR_HDMIB, "HDMI-B"},
+ {DRM_MODE_CONNECTOR_TV, "TV"},
+ {DRM_MODE_CONNECTOR_eDP, "eDP"},
+ {DRM_MODE_CONNECTOR_VIRTUAL, "Virtual"},
+ {DRM_MODE_CONNECTOR_DSI, "DSI"},
+ {DRM_MODE_CONNECTOR_DPI, "DPI"},
+};
+
+std::map<drmModeConnection, std::string> connectorStatusNames = {
+ {DRM_MODE_CONNECTED, "connected"},
+ {DRM_MODE_DISCONNECTED, "disconnected"},
+ {DRM_MODE_UNKNOWNCONNECTION, "unknown"},
+};
+
+} // namespace
+
+CDRMConnector::CDRMConnector(int fd, uint32_t connector)
+ : CDRMObject(fd), m_connector(drmModeGetConnector(m_fd, connector))
+{
+ if (!m_connector)
+ throw std::runtime_error("drmModeGetConnector failed: " + std::string{strerror(errno)});
+
+ if (!GetProperties(m_connector->connector_id, DRM_MODE_OBJECT_CONNECTOR))
+ throw std::runtime_error("failed to get properties for connector: " +
+ std::to_string(m_connector->connector_id));
+}
+
+bool CDRMConnector::CheckConnector()
+{
+ unsigned retryCnt = 7;
+ while (!IsConnected() && retryCnt > 0)
+ {
+ CLog::Log(LOGDEBUG, "CDRMConnector::{} - connector is disconnected", __FUNCTION__);
+ retryCnt--;
+ KODI::TIME::Sleep(1s);
+
+ m_connector.reset(drmModeGetConnector(m_fd, m_connector->connector_id));
+ }
+
+ return m_connector->connection == DRM_MODE_CONNECTED;
+}
+
+std::string CDRMConnector::GetType()
+{
+ auto typeName = connectorTypeNames.find(m_connector->connector_type);
+ if (typeName == connectorTypeNames.end())
+ return connectorTypeNames[DRM_MODE_CONNECTOR_Unknown];
+
+ return typeName->second;
+}
+
+std::string CDRMConnector::GetStatus()
+{
+ auto statusName = connectorStatusNames.find(m_connector->connection);
+ if (statusName == connectorStatusNames.end())
+ return connectorStatusNames[DRM_MODE_UNKNOWNCONNECTION];
+
+ return statusName->second;
+}
+
+std::string CDRMConnector::GetName()
+{
+ return GetType() + "-" + std::to_string(m_connector->connector_type_id);
+}
diff --git a/xbmc/windowing/gbm/drm/DRMConnector.h b/xbmc/windowing/gbm/drm/DRMConnector.h
new file mode 100644
index 0000000..5a29222
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMConnector.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMObject.h"
+
+#include <string>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMConnector : public CDRMObject
+{
+public:
+ explicit CDRMConnector(int fd, uint32_t connector);
+ CDRMConnector(const CDRMConnector&) = delete;
+ CDRMConnector& operator=(const CDRMConnector&) = delete;
+ ~CDRMConnector() = default;
+
+ std::string GetType();
+ std::string GetStatus();
+ std::string GetName();
+
+ uint32_t GetEncoderId() const { return m_connector->encoder_id; }
+ uint32_t* GetConnectorId() const { return &m_connector->connector_id; }
+ int GetModesCount() const { return m_connector->count_modes; }
+ drmModeModeInfoPtr GetModeForIndex(int index) const { return &m_connector->modes[index]; }
+
+ bool IsConnected() { return m_connector->connection == DRM_MODE_CONNECTED; }
+ bool CheckConnector();
+
+private:
+ struct DrmModeConnectorDeleter
+ {
+ void operator()(drmModeConnector* p) { drmModeFreeConnector(p); }
+ };
+
+ std::unique_ptr<drmModeConnector, DrmModeConnectorDeleter> m_connector;
+};
+
+} // namespace GBM
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/gbm/drm/DRMCrtc.cpp b/xbmc/windowing/gbm/drm/DRMCrtc.cpp
new file mode 100644
index 0000000..426fd95
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMCrtc.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMCrtc.h"
+
+#include <cstring>
+#include <errno.h>
+#include <stdexcept>
+#include <string>
+
+using namespace KODI::WINDOWING::GBM;
+
+CDRMCrtc::CDRMCrtc(int fd, uint32_t crtc) : CDRMObject(fd), m_crtc(drmModeGetCrtc(m_fd, crtc))
+{
+ if (!m_crtc)
+ throw std::runtime_error("drmModeGetCrtc failed: " + std::string{strerror(errno)});
+
+ if (!GetProperties(m_crtc->crtc_id, DRM_MODE_OBJECT_CRTC))
+ throw std::runtime_error("failed to get properties for crtc: " +
+ std::to_string(m_crtc->crtc_id));
+}
diff --git a/xbmc/windowing/gbm/drm/DRMCrtc.h b/xbmc/windowing/gbm/drm/DRMCrtc.h
new file mode 100644
index 0000000..a1aadc2
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMCrtc.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMObject.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMCrtc : public CDRMObject
+{
+public:
+ explicit CDRMCrtc(int fd, uint32_t crtc);
+ CDRMCrtc(const CDRMCrtc&) = delete;
+ CDRMCrtc& operator=(const CDRMCrtc&) = delete;
+ ~CDRMCrtc() = default;
+
+ uint32_t GetCrtcId() const { return m_crtc->crtc_id; }
+ uint32_t GetBufferId() const { return m_crtc->buffer_id; }
+ uint32_t GetX() const { return m_crtc->x; }
+ uint32_t GetY() const { return m_crtc->y; }
+ drmModeModeInfoPtr GetMode() const { return &m_crtc->mode; }
+ bool GetModeValid() const { return m_crtc->mode_valid != 0; }
+
+private:
+ struct DrmModeCrtcDeleter
+ {
+ void operator()(drmModeCrtc* p) { drmModeFreeCrtc(p); }
+ };
+
+ std::unique_ptr<drmModeCrtc, DrmModeCrtcDeleter> m_crtc;
+};
+
+} // namespace GBM
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/gbm/drm/DRMEncoder.cpp b/xbmc/windowing/gbm/drm/DRMEncoder.cpp
new file mode 100644
index 0000000..e4289c6
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMEncoder.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMEncoder.h"
+
+#include <cstring>
+#include <errno.h>
+#include <stdexcept>
+#include <string>
+
+using namespace KODI::WINDOWING::GBM;
+
+CDRMEncoder::CDRMEncoder(int fd, uint32_t encoder)
+ : CDRMObject(fd), m_encoder(drmModeGetEncoder(m_fd, encoder))
+{
+ if (!m_encoder)
+ throw std::runtime_error("drmModeGetEncoder failed: " + std::string{strerror(errno)});
+}
diff --git a/xbmc/windowing/gbm/drm/DRMEncoder.h b/xbmc/windowing/gbm/drm/DRMEncoder.h
new file mode 100644
index 0000000..aceb276
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMEncoder.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMObject.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMEncoder : public CDRMObject
+{
+public:
+ explicit CDRMEncoder(int fd, uint32_t encoder);
+ CDRMEncoder(const CDRMEncoder&) = delete;
+ CDRMEncoder& operator=(const CDRMEncoder&) = delete;
+ ~CDRMEncoder() = default;
+
+ uint32_t GetEncoderId() const { return m_encoder->encoder_id; }
+ uint32_t GetCrtcId() const { return m_encoder->crtc_id; }
+ uint32_t GetPossibleCrtcs() const { return m_encoder->possible_crtcs; }
+
+private:
+ struct DrmModeEncoderDeleter
+ {
+ void operator()(drmModeEncoder* p) { drmModeFreeEncoder(p); }
+ };
+
+ std::unique_ptr<drmModeEncoder, DrmModeEncoderDeleter> m_encoder;
+};
+
+} // namespace GBM
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.cpp b/xbmc/windowing/gbm/drm/DRMLegacy.cpp
new file mode 100644
index 0000000..418d067
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMLegacy.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMLegacy.h"
+
+#include "guilib/gui3d.h"
+#include "settings/Settings.h"
+#include "utils/log.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <drm_mode.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+using namespace KODI::WINDOWING::GBM;
+
+static int flip_happening = 0;
+
+bool CDRMLegacy::SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo)
+{
+ struct drm_fb *drm_fb = DrmFbGetFromBo(bo);
+
+ auto ret = drmModeSetCrtc(m_fd, m_crtc->GetCrtcId(), drm_fb->fb_id, 0, 0,
+ m_connector->GetConnectorId(), 1, m_mode);
+
+ if(ret < 0)
+ {
+ CLog::Log(LOGERROR, "CDRMLegacy::{} - failed to set crtc mode: {}x{}{} @ {} Hz", __FUNCTION__,
+ m_mode->hdisplay, m_mode->vdisplay,
+ m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh);
+
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMLegacy::{} - set crtc mode: {}x{}{} @ {} Hz", __FUNCTION__,
+ m_mode->hdisplay, m_mode->vdisplay, m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "",
+ m_mode->vrefresh);
+
+ return true;
+}
+
+void CDRMLegacy::PageFlipHandler(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ (void) fd, (void) frame, (void) sec, (void) usec;
+
+ int *flip_happening = static_cast<int *>(data);
+ *flip_happening = 0;
+}
+
+bool CDRMLegacy::WaitingForFlip()
+{
+ if (!flip_happening)
+ return false;
+
+ struct pollfd drm_fds =
+ {
+ m_fd,
+ POLLIN,
+ 0,
+ };
+
+ drmEventContext drm_evctx{};
+ drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
+ drm_evctx.page_flip_handler = PageFlipHandler;
+
+ while(flip_happening)
+ {
+ auto ret = poll(&drm_fds, 1, -1);
+
+ if(ret < 0)
+ return true;
+
+ if(drm_fds.revents & (POLLHUP | POLLERR))
+ return true;
+
+ if(drm_fds.revents & POLLIN)
+ drmHandleEvent(m_fd, &drm_evctx);
+ }
+
+ return false;
+}
+
+bool CDRMLegacy::QueueFlip(struct gbm_bo *bo)
+{
+ struct drm_fb *drm_fb = DrmFbGetFromBo(bo);
+
+ auto ret = drmModePageFlip(m_fd, m_crtc->GetCrtcId(), drm_fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT,
+ &flip_happening);
+
+ if(ret)
+ {
+ CLog::Log(LOGDEBUG, "CDRMLegacy::{} - failed to queue DRM page flip", __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
+
+void CDRMLegacy::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer)
+{
+ if (rendered || videoLayer)
+ {
+ flip_happening = QueueFlip(bo);
+ WaitingForFlip();
+ }
+}
+
+bool CDRMLegacy::InitDrm()
+{
+ if (!CDRMUtils::OpenDrm(true))
+ return false;
+
+ if (!CDRMUtils::InitDrm())
+ return false;
+
+ CLog::Log(LOGDEBUG, "CDRMLegacy::{} - initialized legacy DRM", __FUNCTION__);
+ return true;
+}
+
+bool CDRMLegacy::SetActive(bool active)
+{
+ if (!m_connector->SetProperty("DPMS", active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF))
+ {
+ CLog::Log(LOGDEBUG, "CDRMLegacy::{} - failed to set DPMS property", __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.h b/xbmc/windowing/gbm/drm/DRMLegacy.h
new file mode 100644
index 0000000..2b7ff45
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMLegacy.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMUtils.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMLegacy : public CDRMUtils
+{
+public:
+ CDRMLegacy() = default;
+ ~CDRMLegacy() override = default;
+ void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override;
+ bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override;
+ bool SetActive(bool active) override;
+ bool InitDrm() override;
+
+private:
+ bool WaitingForFlip();
+ bool QueueFlip(struct gbm_bo *bo);
+ static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data);
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/drm/DRMObject.cpp b/xbmc/windowing/gbm/drm/DRMObject.cpp
new file mode 100644
index 0000000..599bb61
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMObject.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMObject.h"
+
+#include "utils/log.h"
+
+#include <algorithm>
+#include <array>
+
+using namespace KODI::WINDOWING::GBM;
+
+namespace
+{
+
+constexpr std::array<std::pair<uint32_t, const char*>, 8> DrmModeObjectTypes = {
+ {{DRM_MODE_OBJECT_CRTC, "crtc"},
+ {DRM_MODE_OBJECT_CONNECTOR, "connector"},
+ {DRM_MODE_OBJECT_ENCODER, "encoder"},
+ {DRM_MODE_OBJECT_MODE, "mode"},
+ {DRM_MODE_OBJECT_PROPERTY, "property"},
+ {DRM_MODE_OBJECT_FB, "framebuffer"},
+ {DRM_MODE_OBJECT_BLOB, "blob"},
+ {DRM_MODE_OBJECT_PLANE, "plane"}}};
+}
+
+CDRMObject::CDRMObject(int fd) : m_fd(fd)
+{
+}
+
+std::string CDRMObject::GetTypeName() const
+{
+ auto name = std::find_if(DrmModeObjectTypes.begin(), DrmModeObjectTypes.end(),
+ [this](const auto& p) { return p.first == m_type; });
+ if (name != DrmModeObjectTypes.end())
+ return name->second;
+
+ return "invalid type";
+}
+
+std::string CDRMObject::GetPropertyName(uint32_t propertyId) const
+{
+ auto prop = std::find_if(m_propsInfo.begin(), m_propsInfo.end(),
+ [&propertyId](const auto& p) { return p->prop_id == propertyId; });
+ if (prop != m_propsInfo.end())
+ return prop->get()->name;
+
+ return "invalid property";
+}
+
+uint32_t CDRMObject::GetPropertyId(const std::string& name) const
+{
+ auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(),
+ [&name](const auto& prop) { return prop->name == name; });
+
+ if (property != m_propsInfo.end())
+ return property->get()->prop_id;
+
+ return 0;
+}
+
+bool CDRMObject::GetProperties(uint32_t id, uint32_t type)
+{
+ m_props.reset(drmModeObjectGetProperties(m_fd, id, type));
+ if (!m_props)
+ return false;
+
+ m_id = id;
+ m_type = type;
+
+ for (uint32_t i = 0; i < m_props->count_props; i++)
+ m_propsInfo.emplace_back(std::unique_ptr<drmModePropertyRes, DrmModePropertyResDeleter>(
+ drmModeGetProperty(m_fd, m_props->props[i])));
+
+ return true;
+}
+
+//! @todo: improve with c++17
+std::tuple<bool, uint64_t> CDRMObject::GetPropertyValue(const std::string& name,
+ const std::string& valueName) const
+{
+ auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(),
+ [&name](const auto& prop) { return prop->name == name; });
+
+ if (property == m_propsInfo.end())
+ return std::make_tuple(false, 0);
+
+ auto prop = property->get();
+
+ if (!static_cast<bool>(drm_property_type_is(prop, DRM_MODE_PROP_ENUM)))
+ return std::make_tuple(false, 0);
+
+ for (int j = 0; j < prop->count_enums; j++)
+ {
+ if (prop->enums[j].name != valueName)
+ continue;
+
+ return std::make_tuple(true, prop->enums[j].value);
+ }
+
+ return std::make_tuple(false, 0);
+}
+
+bool CDRMObject::SetProperty(const std::string& name, uint64_t value)
+{
+ auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(),
+ [&name](const auto& prop) { return prop->name == name; });
+
+ if (property != m_propsInfo.end())
+ {
+ int ret = drmModeObjectSetProperty(m_fd, m_id, m_type, property->get()->prop_id, value);
+ if (ret == 0)
+ return true;
+ }
+
+ return false;
+}
+
+bool CDRMObject::SupportsProperty(const std::string& name)
+{
+ auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(),
+ [&name](const auto& prop) { return prop->name == name; });
+
+ if (property != m_propsInfo.end())
+ return true;
+
+ return false;
+}
diff --git a/xbmc/windowing/gbm/drm/DRMObject.h b/xbmc/windowing/gbm/drm/DRMObject.h
new file mode 100644
index 0000000..e2ae326
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMObject.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <xf86drmMode.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMObject
+{
+public:
+ CDRMObject(const CDRMObject&) = delete;
+ CDRMObject& operator=(const CDRMObject&) = delete;
+ virtual ~CDRMObject() = default;
+
+ std::string GetTypeName() const;
+ std::string GetPropertyName(uint32_t propertyId) const;
+
+ uint32_t GetId() const { return m_id; }
+ uint32_t GetPropertyId(const std::string& name) const;
+ std::tuple<bool, uint64_t> GetPropertyValue(const std::string& name,
+ const std::string& valueName) const;
+
+ bool SetProperty(const std::string& name, uint64_t value);
+ bool SupportsProperty(const std::string& name);
+
+protected:
+ explicit CDRMObject(int fd);
+
+ bool GetProperties(uint32_t id, uint32_t type);
+
+ struct DrmModeObjectPropertiesDeleter
+ {
+ void operator()(drmModeObjectProperties* p) { drmModeFreeObjectProperties(p); }
+ };
+
+ std::unique_ptr<drmModeObjectProperties, DrmModeObjectPropertiesDeleter> m_props;
+
+ struct DrmModePropertyResDeleter
+ {
+ void operator()(drmModePropertyRes* p) { drmModeFreeProperty(p); }
+ };
+
+ std::vector<std::unique_ptr<drmModePropertyRes, DrmModePropertyResDeleter>> m_propsInfo;
+
+ int m_fd{-1};
+
+private:
+ uint32_t m_id{0};
+ uint32_t m_type{0};
+};
+
+} // namespace GBM
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/gbm/drm/DRMPlane.cpp b/xbmc/windowing/gbm/drm/DRMPlane.cpp
new file mode 100644
index 0000000..90b5660
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMPlane.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMPlane.h"
+
+#include "DRMUtils.h"
+#include "utils/DRMHelpers.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+using namespace KODI::WINDOWING::GBM;
+
+CDRMPlane::CDRMPlane(int fd, uint32_t plane) : CDRMObject(fd), m_plane(drmModeGetPlane(m_fd, plane))
+{
+ if (!m_plane)
+ throw std::runtime_error("drmModeGetPlane failed: " + std::string{strerror(errno)});
+
+ if (!GetProperties(m_plane->plane_id, DRM_MODE_OBJECT_PLANE))
+ throw std::runtime_error("failed to get properties for plane: " +
+ std::to_string(m_plane->plane_id));
+}
+
+bool CDRMPlane::SupportsFormat(uint32_t format)
+{
+ for (uint32_t i = 0; i < m_plane->count_formats; i++)
+ if (m_plane->formats[i] == format)
+ return true;
+
+ return false;
+}
+
+bool CDRMPlane::SupportsFormatAndModifier(uint32_t format, uint64_t modifier)
+{
+ /*
+ * Some broadcom modifiers have parameters encoded which need to be
+ * masked out before comparing with reported modifiers.
+ */
+ if (modifier >> 56 == DRM_FORMAT_MOD_VENDOR_BROADCOM)
+ modifier = fourcc_mod_broadcom_mod(modifier);
+
+ if (modifier == DRM_FORMAT_MOD_LINEAR)
+ {
+ if (!SupportsFormat(format))
+ {
+ CLog::Log(LOGDEBUG, "CDRMPlane::{} - format not supported: {}", __FUNCTION__,
+ DRMHELPERS::FourCCToString(format));
+ return false;
+ }
+ }
+ else
+ {
+ auto formatModifiers = &m_modifiers_map[format];
+ if (formatModifiers->empty())
+ {
+ CLog::Log(LOGDEBUG, "CDRMPlane::{} - format not supported: {}", __FUNCTION__,
+ DRMHELPERS::FourCCToString(format));
+ return false;
+ }
+
+ auto formatModifier = std::find(formatModifiers->begin(), formatModifiers->end(), modifier);
+ if (formatModifier == formatModifiers->end())
+ {
+ CLog::Log(LOGDEBUG, "CDRMPlane::{} - modifier ({}) not supported for format ({})",
+ __FUNCTION__, DRMHELPERS::ModifierToString(modifier),
+ DRMHELPERS::FourCCToString(format));
+ return false;
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMPlane::{} - found plane format ({}) and modifier ({})", __FUNCTION__,
+ DRMHELPERS::FourCCToString(format), DRMHELPERS::ModifierToString(modifier));
+
+ return true;
+}
+
+void CDRMPlane::FindModifiers()
+{
+ auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), [](auto& prop) {
+ return StringUtils::EqualsNoCase(prop->name, "IN_FORMATS");
+ });
+
+ uint64_t blob_id = 0;
+ if (property != m_propsInfo.end())
+ blob_id = m_props->prop_values[std::distance(m_propsInfo.begin(), property)];
+
+ if (blob_id == 0)
+ return;
+
+ drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(m_fd, blob_id);
+ if (!blob)
+ return;
+
+ drm_format_modifier_blob* header = static_cast<drm_format_modifier_blob*>(blob->data);
+ uint32_t* formats =
+ reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(header) + header->formats_offset);
+ drm_format_modifier* mod = reinterpret_cast<drm_format_modifier*>(
+ reinterpret_cast<char*>(header) + header->modifiers_offset);
+
+ for (uint32_t i = 0; i < header->count_formats; i++)
+ {
+ std::vector<uint64_t> modifiers;
+ for (uint32_t j = 0; j < header->count_modifiers; j++)
+ {
+ if (mod[j].formats & 1ULL << i)
+ modifiers.emplace_back(mod[j].modifier);
+ }
+
+ m_modifiers_map.emplace(formats[i], modifiers);
+ }
+
+ if (blob)
+ drmModeFreePropertyBlob(blob);
+}
diff --git a/xbmc/windowing/gbm/drm/DRMPlane.h b/xbmc/windowing/gbm/drm/DRMPlane.h
new file mode 100644
index 0000000..e077fc3
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMPlane.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005-2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMObject.h"
+
+#include <map>
+
+#include <drm_fourcc.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class CDRMPlane : public CDRMObject
+{
+public:
+ explicit CDRMPlane(int fd, uint32_t plane);
+ CDRMPlane(const CDRMPlane&) = delete;
+ CDRMPlane& operator=(const CDRMPlane&) = delete;
+ ~CDRMPlane() = default;
+
+ uint32_t GetPlaneId() const { return m_plane->plane_id; }
+ uint32_t GetPossibleCrtcs() const { return m_plane->possible_crtcs; }
+
+ void FindModifiers();
+
+ void SetFormat(const uint32_t newFormat) { m_format = newFormat; }
+ uint32_t GetFormat() const { return m_format; }
+ std::vector<uint64_t>& GetModifiersForFormat(uint32_t format) { return m_modifiers_map[format]; }
+
+ bool SupportsFormat(uint32_t format);
+ bool SupportsFormatAndModifier(uint32_t format, uint64_t modifier);
+
+private:
+ struct DrmModePlaneDeleter
+ {
+ void operator()(drmModePlane* p) { drmModeFreePlane(p); }
+ };
+
+ std::unique_ptr<drmModePlane, DrmModePlaneDeleter> m_plane;
+
+ std::map<uint32_t, std::vector<uint64_t>> m_modifiers_map;
+ uint32_t m_format{DRM_FORMAT_XRGB8888};
+};
+
+} // namespace GBM
+} // namespace WINDOWING
+} // namespace KODI
diff --git a/xbmc/windowing/gbm/drm/DRMUtils.cpp b/xbmc/windowing/gbm/drm/DRMUtils.cpp
new file mode 100644
index 0000000..6b61403
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMUtils.cpp
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DRMUtils.h"
+
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/DRMHelpers.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include "PlatformDefs.h"
+
+using namespace KODI::WINDOWING::GBM;
+
+namespace
+{
+const std::string SETTING_VIDEOSCREEN_LIMITGUISIZE = "videoscreen.limitguisize";
+
+void DrmFbDestroyCallback(gbm_bo* bo, void* data)
+{
+ drm_fb* fb = static_cast<drm_fb*>(data);
+
+ if (fb->fb_id > 0)
+ {
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - removing framebuffer: {}", __FUNCTION__, fb->fb_id);
+ int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
+ drmModeRmFB(drm_fd, fb->fb_id);
+ }
+
+ delete fb;
+}
+}
+
+CDRMUtils::~CDRMUtils()
+{
+ DestroyDrm();
+}
+
+bool CDRMUtils::SetMode(const RESOLUTION_INFO& res)
+{
+ if (!m_connector->CheckConnector())
+ return false;
+
+ m_mode = m_connector->GetModeForIndex(std::atoi(res.strId.c_str()));
+ m_width = res.iWidth;
+ m_height = res.iHeight;
+
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - found crtc mode: {}x{}{} @ {} Hz", __FUNCTION__,
+ m_mode->hdisplay, m_mode->vdisplay, m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "",
+ m_mode->vrefresh);
+
+ return true;
+}
+
+drm_fb * CDRMUtils::DrmFbGetFromBo(struct gbm_bo *bo)
+{
+ {
+ struct drm_fb *fb = static_cast<drm_fb *>(gbm_bo_get_user_data(bo));
+ if(fb)
+ {
+ if (m_gui_plane->GetFormat() == fb->format)
+ return fb;
+ else
+ DrmFbDestroyCallback(bo, gbm_bo_get_user_data(bo));
+ }
+ }
+
+ struct drm_fb *fb = new drm_fb;
+ fb->bo = bo;
+ fb->format = m_gui_plane->GetFormat();
+
+ uint32_t width,
+ height,
+ handles[4] = {0},
+ strides[4] = {0},
+ offsets[4] = {0};
+
+ uint64_t modifiers[4] = {0};
+
+ width = gbm_bo_get_width(bo);
+ height = gbm_bo_get_height(bo);
+
+#if defined(HAS_GBM_MODIFIERS)
+ for (int i = 0; i < gbm_bo_get_plane_count(bo); i++)
+ {
+ handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32;
+ strides[i] = gbm_bo_get_stride_for_plane(bo, i);
+ offsets[i] = gbm_bo_get_offset(bo, i);
+ modifiers[i] = gbm_bo_get_modifier(bo);
+ }
+#else
+ handles[0] = gbm_bo_get_handle(bo).u32;
+ strides[0] = gbm_bo_get_stride(bo);
+ memset(offsets, 0, 16);
+#endif
+
+ uint32_t flags = 0;
+
+ if (modifiers[0] && modifiers[0] != DRM_FORMAT_MOD_INVALID)
+ {
+ flags |= DRM_MODE_FB_MODIFIERS;
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - using modifier: {}", __FUNCTION__,
+ DRMHELPERS::ModifierToString(modifiers[0]));
+ }
+
+ int ret = drmModeAddFB2WithModifiers(m_fd,
+ width,
+ height,
+ fb->format,
+ handles,
+ strides,
+ offsets,
+ modifiers,
+ &fb->fb_id,
+ flags);
+
+ if(ret < 0)
+ {
+ ret = drmModeAddFB2(m_fd,
+ width,
+ height,
+ fb->format,
+ handles,
+ strides,
+ offsets,
+ &fb->fb_id,
+ flags);
+
+ if (ret < 0)
+ {
+ delete (fb);
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to add framebuffer: {} ({})", __FUNCTION__, strerror(errno), errno);
+ return nullptr;
+ }
+ }
+
+ gbm_bo_set_user_data(bo, fb, DrmFbDestroyCallback);
+
+ return fb;
+}
+
+bool CDRMUtils::FindPreferredMode()
+{
+ if (m_mode)
+ return true;
+
+ for (int i = 0, area = 0; i < m_connector->GetModesCount(); i++)
+ {
+ drmModeModeInfo* current_mode = m_connector->GetModeForIndex(i);
+
+ if(current_mode->type & DRM_MODE_TYPE_PREFERRED)
+ {
+ m_mode = current_mode;
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - found preferred mode: {}x{}{} @ {} Hz", __FUNCTION__,
+ m_mode->hdisplay, m_mode->vdisplay,
+ m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh);
+ break;
+ }
+
+ auto current_area = current_mode->hdisplay * current_mode->vdisplay;
+ if (current_area > area)
+ {
+ m_mode = current_mode;
+ area = current_area;
+ }
+ }
+
+ if(!m_mode)
+ {
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find preferred mode", __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
+
+bool CDRMUtils::FindPlanes()
+{
+ for (size_t i = 0; i < m_crtcs.size(); i++)
+ {
+ if (!(m_encoder->GetPossibleCrtcs() & (1 << i)))
+ continue;
+
+ auto videoPlane = std::find_if(m_planes.begin(), m_planes.end(), [&i](auto& plane) {
+ if (plane->GetPossibleCrtcs() & (1 << i))
+ {
+ return plane->SupportsFormat(DRM_FORMAT_NV12);
+ }
+ return false;
+ });
+
+ uint32_t videoPlaneId{0};
+
+ if (videoPlane != m_planes.end())
+ videoPlaneId = videoPlane->get()->GetPlaneId();
+
+ auto guiPlane =
+ std::find_if(m_planes.begin(), m_planes.end(), [&i, &videoPlaneId](auto& plane) {
+ if (plane->GetPossibleCrtcs() & (1 << i))
+ {
+ return (plane->GetPlaneId() != videoPlaneId &&
+ (videoPlaneId == 0 || plane->SupportsFormat(DRM_FORMAT_ARGB8888)) &&
+ (plane->SupportsFormat(DRM_FORMAT_XRGB2101010) ||
+ plane->SupportsFormat(DRM_FORMAT_XRGB8888)));
+ }
+ return false;
+ });
+
+ if (videoPlane != m_planes.end() && guiPlane != m_planes.end())
+ {
+ m_crtc = m_crtcs[i].get();
+ m_video_plane = videoPlane->get();
+ m_gui_plane = guiPlane->get();
+ break;
+ }
+
+ if (guiPlane != m_planes.end())
+ {
+ if (!m_crtc && m_encoder->GetCrtcId() == m_crtcs[i]->GetCrtcId())
+ {
+ m_crtc = m_crtcs[i].get();
+ m_gui_plane = guiPlane->get();
+ m_video_plane = nullptr;
+ }
+ }
+ }
+
+ CLog::Log(LOGINFO, "CDRMUtils::{} - using crtc: {}", __FUNCTION__, m_crtc->GetCrtcId());
+
+ // video plane may not be available
+ if (m_video_plane)
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - using video plane {}", __FUNCTION__,
+ m_video_plane->GetPlaneId());
+
+ if (m_gui_plane->SupportsFormat(DRM_FORMAT_XRGB2101010))
+ {
+ m_gui_plane->SetFormat(DRM_FORMAT_XRGB2101010);
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - using 10bit gui plane {}", __FUNCTION__,
+ m_gui_plane->GetPlaneId());
+ }
+ else
+ {
+ m_gui_plane->SetFormat(DRM_FORMAT_XRGB8888);
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - using gui plane {}", __FUNCTION__,
+ m_gui_plane->GetPlaneId());
+ }
+
+ return true;
+}
+
+void CDRMUtils::PrintDrmDeviceInfo(drmDevicePtr device)
+{
+ std::string message;
+
+ // clang-format off
+ message.append(fmt::format("CDRMUtils::{} - DRM Device Info:", __FUNCTION__));
+ message.append(fmt::format("\n available_nodes: {:#04x}", device->available_nodes));
+ message.append("\n nodes:");
+
+ for (int i = 0; i < DRM_NODE_MAX; i++)
+ {
+ if (device->available_nodes & 1 << i)
+ message.append(fmt::format("\n nodes[{}]: {}", i, device->nodes[i]));
+ }
+
+ message.append(fmt::format("\n bustype: {:#04x}", device->bustype));
+
+ if (device->bustype == DRM_BUS_PCI)
+ {
+ message.append("\n pci:");
+ message.append(fmt::format("\n domain: {:#04x}", device->businfo.pci->domain));
+ message.append(fmt::format("\n bus: {:#02x}", device->businfo.pci->bus));
+ message.append(fmt::format("\n dev: {:#02x}", device->businfo.pci->dev));
+ message.append(fmt::format("\n func: {:#1}", device->businfo.pci->func));
+
+ message.append("\n deviceinfo:");
+ message.append("\n pci:");
+ message.append(fmt::format("\n vendor_id: {:#04x}", device->deviceinfo.pci->vendor_id));
+ message.append(fmt::format("\n device_id: {:#04x}", device->deviceinfo.pci->device_id));
+ message.append(fmt::format("\n subvendor_id: {:#04x}", device->deviceinfo.pci->subvendor_id));
+ message.append(fmt::format("\n subdevice_id: {:#04x}", device->deviceinfo.pci->subdevice_id));
+ }
+ else if (device->bustype == DRM_BUS_USB)
+ {
+ message.append("\n usb:");
+ message.append(fmt::format("\n bus: {:#03x}", device->businfo.usb->bus));
+ message.append(fmt::format("\n dev: {:#03x}", device->businfo.usb->dev));
+
+ message.append("\n deviceinfo:");
+ message.append("\n usb:");
+ message.append(fmt::format("\n vendor: {:#04x}", device->deviceinfo.usb->vendor));
+ message.append(fmt::format("\n product: {:#04x}", device->deviceinfo.usb->product));
+ }
+ else if (device->bustype == DRM_BUS_PLATFORM)
+ {
+ message.append("\n platform:");
+ message.append(fmt::format("\n fullname: {}", device->businfo.platform->fullname));
+ }
+ else if (device->bustype == DRM_BUS_HOST1X)
+ {
+ message.append("\n host1x:");
+ message.append(fmt::format("\n fullname: {}", device->businfo.host1x->fullname));
+ }
+ else
+ message.append("\n unhandled bus type");
+ // clang-format on
+
+ CLog::Log(LOGDEBUG, "{}", message);
+}
+
+bool CDRMUtils::OpenDrm(bool needConnector)
+{
+ int numDevices = drmGetDevices2(0, nullptr, 0);
+ if (numDevices <= 0)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - no drm devices found: ({})", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - drm devices found: {}", __FUNCTION__, numDevices);
+
+ std::vector<drmDevicePtr> devices(numDevices);
+
+ int ret = drmGetDevices2(0, devices.data(), devices.size());
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - drmGetDevices2 return an error: ({})", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ for (const auto device : devices)
+ {
+ if (!(device->available_nodes & 1 << DRM_NODE_PRIMARY))
+ continue;
+
+ close(m_fd);
+ m_fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
+ if (m_fd < 0)
+ continue;
+
+ if (needConnector)
+ {
+ auto resources = drmModeGetResources(m_fd);
+ if (!resources)
+ continue;
+
+ m_connectors.clear();
+ for (int i = 0; i < resources->count_connectors; i++)
+ m_connectors.emplace_back(std::make_unique<CDRMConnector>(m_fd, resources->connectors[i]));
+
+ drmModeFreeResources(resources);
+
+ if (!FindConnector())
+ continue;
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - opened device: {}", __FUNCTION__,
+ device->nodes[DRM_NODE_PRIMARY]);
+
+ PrintDrmDeviceInfo(device);
+
+ const char* renderPath = drmGetRenderDeviceNameFromFd(m_fd);
+
+ if (!renderPath)
+ renderPath = drmGetDeviceNameFromFd2(m_fd);
+
+ if (!renderPath)
+ renderPath = drmGetDeviceNameFromFd(m_fd);
+
+ if (renderPath)
+ {
+ m_renderDevicePath = renderPath;
+ m_renderFd = open(renderPath, O_RDWR | O_CLOEXEC);
+ if (m_renderFd != 0)
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - opened render node: {}", __FUNCTION__, renderPath);
+ }
+
+ drmFreeDevices(devices.data(), devices.size());
+ return true;
+ }
+
+ drmFreeDevices(devices.data(), devices.size());
+ return false;
+}
+
+bool CDRMUtils::InitDrm()
+{
+ if (m_fd < 0)
+ return false;
+
+ /* caps need to be set before allocating connectors, encoders, crtcs, and planes */
+ int ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set universal planes capability: {}",
+ __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_STEREO_3D, 1);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set stereo 3d capability: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+#if defined(DRM_CLIENT_CAP_ASPECT_RATIO)
+ ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ASPECT_RATIO, 0);
+ if (ret != 0)
+ CLog::Log(LOGERROR, "CDRMUtils::{} - aspect ratio capability is not supported: {}",
+ __FUNCTION__, strerror(errno));
+#endif
+
+ auto resources = drmModeGetResources(m_fd);
+ if (!resources)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm resources: {}", __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ m_connectors.clear();
+ for (int i = 0; i < resources->count_connectors; i++)
+ m_connectors.emplace_back(std::make_unique<CDRMConnector>(m_fd, resources->connectors[i]));
+
+ m_encoders.clear();
+ for (int i = 0; i < resources->count_encoders; i++)
+ m_encoders.emplace_back(std::make_unique<CDRMEncoder>(m_fd, resources->encoders[i]));
+
+ m_crtcs.clear();
+ for (int i = 0; i < resources->count_crtcs; i++)
+ m_crtcs.emplace_back(std::make_unique<CDRMCrtc>(m_fd, resources->crtcs[i]));
+
+ drmModeFreeResources(resources);
+
+ auto planeResources = drmModeGetPlaneResources(m_fd);
+ if (!planeResources)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm plane resources: {}", __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ m_planes.clear();
+ for (uint32_t i = 0; i < planeResources->count_planes; i++)
+ {
+ m_planes.emplace_back(std::make_unique<CDRMPlane>(m_fd, planeResources->planes[i]));
+ m_planes[i]->FindModifiers();
+ }
+
+ drmModeFreePlaneResources(planeResources);
+
+ if (!FindConnector())
+ return false;
+
+ if (!FindEncoder())
+ return false;
+
+ if (!FindCrtc())
+ return false;
+
+ if (!FindPlanes())
+ return false;
+
+ if (!FindPreferredMode())
+ return false;
+
+ ret = drmSetMaster(m_fd);
+ if (ret < 0)
+ {
+ CLog::Log(LOGDEBUG,
+ "CDRMUtils::{} - failed to set drm master, will try to authorize instead: {}",
+ __FUNCTION__, strerror(errno));
+
+ drm_magic_t magic;
+
+ ret = drmGetMagic(m_fd, &magic);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm magic: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ ret = drmAuthMagic(m_fd, magic);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to authorize drm magic: {}", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+
+ CLog::Log(LOGINFO, "CDRMUtils::{} - successfully authorized drm magic", __FUNCTION__);
+ }
+
+ return true;
+}
+
+bool CDRMUtils::FindConnector()
+{
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return false;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return false;
+
+ std::vector<std::unique_ptr<CDRMConnector>>::iterator connector;
+
+ std::string connectorName = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ if (connectorName != "Default")
+ {
+ connector = std::find_if(m_connectors.begin(), m_connectors.end(),
+ [&connectorName](auto& connector)
+ {
+ return connector->GetEncoderId() > 0 && connector->IsConnected() &&
+ connector->GetName() == connectorName;
+ });
+ }
+
+ if (connector == m_connectors.end())
+ {
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find specified connector: {}, trying default",
+ __FUNCTION__, connectorName);
+ connectorName = "Default";
+ }
+
+ if (connectorName == "Default")
+ {
+ connector = std::find_if(m_connectors.begin(), m_connectors.end(),
+ [](auto& connector)
+ { return connector->GetEncoderId() > 0 && connector->IsConnected(); });
+ }
+
+ if (connector == m_connectors.end())
+ {
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find connected connector", __FUNCTION__);
+ return false;
+ }
+
+ CLog::Log(LOGINFO, "CDRMUtils::{} - using connector: {}", __FUNCTION__,
+ connector->get()->GetName());
+
+ m_connector = connector->get();
+ return true;
+}
+
+bool CDRMUtils::FindEncoder()
+{
+ auto encoder = std::find_if(m_encoders.begin(), m_encoders.end(), [this](auto& encoder) {
+ return encoder->GetEncoderId() == m_connector->GetEncoderId();
+ });
+
+ if (encoder == m_encoders.end())
+ {
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find encoder for connector id: {}", __FUNCTION__,
+ *m_connector->GetConnectorId());
+ return false;
+ }
+
+ CLog::Log(LOGINFO, "CDRMUtils::{} - using encoder: {}", __FUNCTION__,
+ encoder->get()->GetEncoderId());
+
+ m_encoder = encoder->get();
+ return true;
+}
+
+bool CDRMUtils::FindCrtc()
+{
+ for (size_t i = 0; i < m_crtcs.size(); i++)
+ {
+ if (m_encoder->GetPossibleCrtcs() & (1 << i))
+ {
+ if (m_crtcs[i]->GetCrtcId() == m_encoder->GetCrtcId())
+ {
+ m_orig_crtc = m_crtcs[i].get();
+ if (m_orig_crtc->GetModeValid())
+ {
+ m_mode = m_orig_crtc->GetMode();
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - original crtc mode: {}x{}{} @ {} Hz", __FUNCTION__,
+ m_mode->hdisplay, m_mode->vdisplay,
+ m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh);
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CDRMUtils::RestoreOriginalMode()
+{
+ if(!m_orig_crtc)
+ {
+ return false;
+ }
+
+ auto ret = drmModeSetCrtc(m_fd, m_orig_crtc->GetCrtcId(), m_orig_crtc->GetBufferId(),
+ m_orig_crtc->GetX(), m_orig_crtc->GetY(), m_connector->GetConnectorId(),
+ 1, m_orig_crtc->GetMode());
+
+ if(ret)
+ {
+ CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set original crtc mode", __FUNCTION__);
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CDRMUtils::{} - set original crtc mode", __FUNCTION__);
+
+ return true;
+}
+
+void CDRMUtils::DestroyDrm()
+{
+ RestoreOriginalMode();
+
+ if (drmAuthMagic(m_fd, 0) == EINVAL)
+ drmDropMaster(m_fd);
+
+ close(m_renderFd);
+ close(m_fd);
+
+ m_connector = nullptr;
+ m_encoder = nullptr;
+ m_crtc = nullptr;
+ m_orig_crtc = nullptr;
+ m_video_plane = nullptr;
+ m_gui_plane = nullptr;
+}
+
+RESOLUTION_INFO CDRMUtils::GetResolutionInfo(drmModeModeInfoPtr mode)
+{
+ RESOLUTION_INFO res;
+ res.iScreenWidth = mode->hdisplay;
+ res.iScreenHeight = mode->vdisplay;
+ res.iWidth = res.iScreenWidth;
+ res.iHeight = res.iScreenHeight;
+
+ int limit = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ SETTING_VIDEOSCREEN_LIMITGUISIZE);
+ if (limit > 0 && res.iScreenWidth > 1920 && res.iScreenHeight > 1080)
+ {
+ switch (limit)
+ {
+ case 1: // 720p
+ res.iWidth = 1280;
+ res.iHeight = 720;
+ break;
+ case 2: // 1080p / 720p (>30hz)
+ res.iWidth = mode->vrefresh > 30 ? 1280 : 1920;
+ res.iHeight = mode->vrefresh > 30 ? 720 : 1080;
+ break;
+ case 3: // 1080p
+ res.iWidth = 1920;
+ res.iHeight = 1080;
+ break;
+ case 4: // Unlimited / 1080p (>30hz)
+ res.iWidth = mode->vrefresh > 30 ? 1920 : res.iScreenWidth;
+ res.iHeight = mode->vrefresh > 30 ? 1080 : res.iScreenHeight;
+ break;
+ }
+ }
+
+ if (mode->clock % 5 != 0)
+ res.fRefreshRate = static_cast<float>(mode->vrefresh) * (1000.0f/1001.0f);
+ else
+ res.fRefreshRate = mode->vrefresh;
+ res.iSubtitles = res.iHeight;
+ res.fPixelRatio = 1.0f;
+ res.bFullScreen = true;
+
+ if (mode->flags & DRM_MODE_FLAG_3D_MASK)
+ {
+ if (mode->flags & DRM_MODE_FLAG_3D_TOP_AND_BOTTOM)
+ res.dwFlags = D3DPRESENTFLAG_MODE3DTB;
+ else if (mode->flags & DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF)
+ res.dwFlags = D3DPRESENTFLAG_MODE3DSBS;
+ }
+ else if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ res.dwFlags = D3DPRESENTFLAG_INTERLACED;
+ else
+ res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+
+ res.strMode =
+ StringUtils::Format("{}x{}{} @ {:.6f} Hz", res.iScreenWidth, res.iScreenHeight,
+ res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "", res.fRefreshRate);
+ return res;
+}
+
+RESOLUTION_INFO CDRMUtils::GetCurrentMode()
+{
+ return GetResolutionInfo(m_mode);
+}
+
+std::vector<RESOLUTION_INFO> CDRMUtils::GetModes()
+{
+ std::vector<RESOLUTION_INFO> resolutions;
+ resolutions.reserve(m_connector->GetModesCount());
+
+ for (auto i = 0; i < m_connector->GetModesCount(); i++)
+ {
+ RESOLUTION_INFO res = GetResolutionInfo(m_connector->GetModeForIndex(i));
+ res.strId = std::to_string(i);
+ resolutions.push_back(res);
+ }
+
+ return resolutions;
+}
+
+std::vector<std::string> CDRMUtils::GetConnectedConnectorNames()
+{
+ std::vector<std::string> connectorNames;
+ for (const auto& connector : m_connectors)
+ {
+ if (connector->IsConnected())
+ connectorNames.emplace_back(connector->GetName());
+ }
+
+ return connectorNames;
+}
+
+uint32_t CDRMUtils::FourCCWithAlpha(uint32_t fourcc)
+{
+ return (fourcc & 0xFFFFFF00) | static_cast<uint32_t>('A');
+}
+
+uint32_t CDRMUtils::FourCCWithoutAlpha(uint32_t fourcc)
+{
+ return (fourcc & 0xFFFFFF00) | static_cast<uint32_t>('X');
+}
diff --git a/xbmc/windowing/gbm/drm/DRMUtils.h b/xbmc/windowing/gbm/drm/DRMUtils.h
new file mode 100644
index 0000000..5327e35
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/DRMUtils.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMConnector.h"
+#include "DRMCrtc.h"
+#include "DRMEncoder.h"
+#include "DRMPlane.h"
+#include "windowing/Resolution.h"
+#include "windowing/gbm/GBMUtils.h"
+
+#include <vector>
+
+#include <gbm.h>
+#include <xf86drm.h>
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+struct drm_fb
+{
+ struct gbm_bo *bo = nullptr;
+ uint32_t fb_id;
+ uint32_t format;
+};
+
+class CDRMUtils
+{
+public:
+ CDRMUtils() = default;
+ virtual ~CDRMUtils();
+ virtual void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) {}
+ virtual bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) { return false; }
+ virtual bool SetActive(bool active) { return false; }
+ virtual bool InitDrm();
+ virtual void DestroyDrm();
+
+ int GetFileDescriptor() const { return m_fd; }
+ int GetRenderNodeFileDescriptor() const { return m_renderFd; }
+ const char* GetRenderDevicePath() const { return m_renderDevicePath; }
+ CDRMPlane* GetVideoPlane() const { return m_video_plane; }
+ CDRMPlane* GetGuiPlane() const { return m_gui_plane; }
+ CDRMCrtc* GetCrtc() const { return m_crtc; }
+ CDRMConnector* GetConnector() const { return m_connector; }
+
+ std::vector<std::string> GetConnectedConnectorNames();
+
+ virtual RESOLUTION_INFO GetCurrentMode();
+ virtual std::vector<RESOLUTION_INFO> GetModes();
+ virtual bool SetMode(const RESOLUTION_INFO& res);
+
+ static uint32_t FourCCWithAlpha(uint32_t fourcc);
+ static uint32_t FourCCWithoutAlpha(uint32_t fourcc);
+
+protected:
+ bool OpenDrm(bool needConnector);
+ drm_fb* DrmFbGetFromBo(struct gbm_bo *bo);
+
+ int m_fd;
+ CDRMConnector* m_connector{nullptr};
+ CDRMEncoder* m_encoder{nullptr};
+ CDRMCrtc* m_crtc{nullptr};
+ CDRMCrtc* m_orig_crtc{nullptr};
+ CDRMPlane* m_video_plane{nullptr};
+ CDRMPlane* m_gui_plane{nullptr};
+ drmModeModeInfo *m_mode = nullptr;
+
+ int m_width = 0;
+ int m_height = 0;
+
+ std::vector<std::unique_ptr<CDRMPlane>> m_planes;
+
+private:
+ bool FindConnector();
+ bool FindEncoder();
+ bool FindCrtc();
+ bool FindPlanes();
+ bool FindPreferredMode();
+ bool RestoreOriginalMode();
+ RESOLUTION_INFO GetResolutionInfo(drmModeModeInfoPtr mode);
+ void PrintDrmDeviceInfo(drmDevicePtr device);
+
+ int m_renderFd;
+ const char* m_renderDevicePath{nullptr};
+
+ std::vector<std::unique_ptr<CDRMConnector>> m_connectors;
+ std::vector<std::unique_ptr<CDRMEncoder>> m_encoders;
+ std::vector<std::unique_ptr<CDRMCrtc>> m_crtcs;
+};
+
+}
+}
+}
diff --git a/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp b/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp
new file mode 100644
index 0000000..dc69fdc
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OffScreenModeSetting.h"
+
+#include "utils/log.h"
+
+using namespace KODI::WINDOWING::GBM;
+
+namespace
+{
+constexpr int DISPLAY_WIDTH = 1280;
+constexpr int DISPLAY_HEIGHT = 720;
+constexpr float DISPLAY_REFRESH = 50.0f;
+} // namespace
+
+bool COffScreenModeSetting::InitDrm()
+{
+ if (!CDRMUtils::OpenDrm(false))
+ return false;
+
+ CLog::Log(LOGDEBUG, "COffScreenModeSetting::{} - initialized offscreen DRM", __FUNCTION__);
+ return true;
+}
+
+std::vector<RESOLUTION_INFO> COffScreenModeSetting::GetModes()
+{
+ std::vector<RESOLUTION_INFO> resolutions;
+ resolutions.push_back(GetCurrentMode());
+ return resolutions;
+}
+
+RESOLUTION_INFO COffScreenModeSetting::GetCurrentMode()
+{
+ RESOLUTION_INFO res;
+ res.iScreenWidth = DISPLAY_WIDTH;
+ res.iWidth = DISPLAY_WIDTH;
+ res.iScreenHeight = DISPLAY_HEIGHT;
+ res.iHeight = DISPLAY_HEIGHT;
+ res.fRefreshRate = DISPLAY_REFRESH;
+ res.iSubtitles = res.iHeight;
+ res.fPixelRatio = 1.0f;
+ res.bFullScreen = true;
+ res.strId = "0";
+
+ return res;
+}
diff --git a/xbmc/windowing/gbm/drm/OffScreenModeSetting.h b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h
new file mode 100644
index 0000000..4270d4e
--- /dev/null
+++ b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DRMUtils.h"
+
+namespace KODI
+{
+namespace WINDOWING
+{
+namespace GBM
+{
+
+class COffScreenModeSetting : public CDRMUtils
+{
+public:
+ COffScreenModeSetting() = default;
+ ~COffScreenModeSetting() override = default;
+ void FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) override {}
+ bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) override { return false; }
+ bool SetActive(bool active) override { return false; }
+ bool InitDrm() override;
+ void DestroyDrm() override {}
+
+ RESOLUTION_INFO GetCurrentMode() override;
+ std::vector<RESOLUTION_INFO> GetModes() override;
+ bool SetMode(const RESOLUTION_INFO& res) override { return true; }
+};
+
+}
+}
+}