KPipewire

glhelpers.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "glhelpers.h"
8#include <QList>
9#include <QVersionNumber>
10#include <epoxy/gl.h>
11#include <libdrm/drm_fourcc.h>
12#include <logging.h>
13#include <mutex>
14
15#include <gbm.h>
16
17namespace GLHelpers
18{
19
20void initDebugOutputOnce()
21{
22 auto callback = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
23 Q_UNUSED(source)
24 Q_UNUSED(severity)
25 Q_UNUSED(userParam)
26 while (length && std::isspace(message[length - 1])) {
27 --length;
28 }
29
30 switch (type) {
31 case GL_DEBUG_TYPE_ERROR:
32 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
33 qCWarning(PIPEWIRE_LOGGING, "%#x: %.*s", id, length, message);
34 break;
35
36 case GL_DEBUG_TYPE_OTHER:
37 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
38 case GL_DEBUG_TYPE_PORTABILITY:
39 case GL_DEBUG_TYPE_PERFORMANCE:
40 default:
41 qCDebug(PIPEWIRE_LOGGING, "%#x: %.*s", id, length, message);
42 break;
43 }
44 };
45 glDebugMessageCallback(callback, nullptr);
46 glEnable(GL_DEBUG_OUTPUT);
47}
48
49static std::once_flag initDebugOnce;
50void initDebugOutput()
51{
52 if (!PIPEWIRE_LOGGING().isDebugEnabled()) {
53 return;
54 }
55
56 if (!eglGetCurrentDisplay()) {
57 // Epoxy gets very confused and it will crash
58 return;
59 }
60
61 std::call_once(initDebugOnce, initDebugOutputOnce);
62}
63
64#define ENUM_STRING(x) case x: return #x;
65
66QByteArray formatGLError(GLenum err)
67{
68 switch (err) {
69 ENUM_STRING(GL_NO_ERROR)
70 ENUM_STRING(GL_INVALID_ENUM)
71 ENUM_STRING(GL_INVALID_VALUE)
72 ENUM_STRING(GL_INVALID_OPERATION)
73 ENUM_STRING(GL_STACK_OVERFLOW)
74 ENUM_STRING(GL_STACK_UNDERFLOW)
75 ENUM_STRING(GL_OUT_OF_MEMORY)
76 default:
77 return QByteArray("0x") + QByteArray::number(err, 16);
78 }
79}
80
81QByteArray formatEGLError(GLenum err)
82{
83 switch (err) {
84 ENUM_STRING(EGL_SUCCESS)
85 ENUM_STRING(EGL_BAD_DISPLAY)
86 ENUM_STRING(EGL_BAD_CONTEXT)
87 ENUM_STRING(EGL_BAD_PARAMETER)
88 ENUM_STRING(EGL_BAD_MATCH)
89 ENUM_STRING(EGL_BAD_ACCESS)
90 ENUM_STRING(EGL_BAD_ALLOC)
91 ENUM_STRING(EGL_BAD_CONFIG)
92 default:
93 return QByteArray("0x") + QByteArray::number(err, 16);
94 }
95}
96
97EGLImage createImage(EGLDisplay display, const DmaBufAttributes &dmabufAttribs, uint32_t format, const QSize &size, gbm_device *gbmDevice)
98{
99 Q_ASSERT(!size.isEmpty());
100 gbm_bo *imported = nullptr;
101 if (gbmDevice) {
102 gbm_import_fd_data importInfo = {static_cast<int>(dmabufAttribs.planes[0].fd),
103 static_cast<uint32_t>(size.width()),
104 static_cast<uint32_t>(size.height()),
105 static_cast<uint32_t>(dmabufAttribs.planes[0].stride),
106 GBM_BO_FORMAT_ARGB8888};
107 imported = gbm_bo_import(gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT);
108 if (!imported) {
109 qCWarning(PIPEWIRE_LOGGING) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno);
110 return EGL_NO_IMAGE_KHR;
111 }
112 }
113
114 const bool hasModifiers = dmabufAttribs.modifier != DRM_FORMAT_MOD_INVALID;
115
116 static int lastSize = 37; // 37 is what we get on a normal system with working dmabuf, it's just a reasonable default
117 QList<EGLint> attribs;
118 attribs.reserve(lastSize);
119 attribs << EGL_WIDTH << size.width() << EGL_HEIGHT << size.height() << EGL_LINUX_DRM_FOURCC_EXT << EGLint(format)
120
121 << EGL_DMA_BUF_PLANE0_FD_EXT << dmabufAttribs.planes[0].fd << EGL_DMA_BUF_PLANE0_OFFSET_EXT << EGLint(dmabufAttribs.planes[0].offset)
122 << EGL_DMA_BUF_PLANE0_PITCH_EXT << EGLint(dmabufAttribs.planes[0].stride);
123
124 if (hasModifiers) {
125 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT
126 << EGLint(dmabufAttribs.modifier >> 32);
127 }
128
129 if (dmabufAttribs.planes.count() > 1) {
130 attribs << EGL_DMA_BUF_PLANE1_FD_EXT << dmabufAttribs.planes[1].fd << EGL_DMA_BUF_PLANE1_OFFSET_EXT << EGLint(dmabufAttribs.planes[1].offset)
131 << EGL_DMA_BUF_PLANE1_PITCH_EXT << EGLint(dmabufAttribs.planes[1].stride);
132
133 if (hasModifiers) {
134 attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT
135 << EGLint(dmabufAttribs.modifier >> 32);
136 }
137 }
138
139 if (dmabufAttribs.planes.count() > 2) {
140 attribs << EGL_DMA_BUF_PLANE2_FD_EXT << dmabufAttribs.planes[2].fd << EGL_DMA_BUF_PLANE2_OFFSET_EXT << EGLint(dmabufAttribs.planes[2].offset)
141 << EGL_DMA_BUF_PLANE2_PITCH_EXT << EGLint(dmabufAttribs.planes[2].stride);
142
143 if (hasModifiers) {
144 attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT
145 << EGLint(dmabufAttribs.modifier >> 32);
146 }
147 }
148
149 if (dmabufAttribs.planes.count() > 3) {
150 attribs << EGL_DMA_BUF_PLANE3_FD_EXT << dmabufAttribs.planes[3].fd << EGL_DMA_BUF_PLANE3_OFFSET_EXT << EGLint(dmabufAttribs.planes[3].offset)
151 << EGL_DMA_BUF_PLANE3_PITCH_EXT << EGLint(dmabufAttribs.planes[3].stride);
152
153 if (hasModifiers) {
154 attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT
155 << EGLint(dmabufAttribs.modifier >> 32);
156 }
157 }
158
159 attribs << EGL_NONE;
160 lastSize = attribs.size();
161
162 static auto eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
163
164 /* MESA says:
165 * "If <target> is EGL_LINUX_DMA_BUF_EXT, <dpy> must be a valid display,
166 * <ctx> must be EGL_NO_CONTEXT..."
167 */
168 EGLImage ret = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, imported, attribs.data());
169 if (ret == EGL_NO_IMAGE_KHR) {
170 qCWarning(PIPEWIRE_LOGGING) << "invalid image" << GLHelpers::formatEGLError(eglGetError());
171 }
172 if (imported) {
173 gbm_bo_destroy(imported);
174 }
175 return ret;
176}
177}
Type type(const QSqlDatabase &db)
QByteArray number(double n, char format, int precision)
qsizetype count() const const
pointer data()
void reserve(qsizetype size)
qsizetype size() const const
int height() const const
bool isEmpty() const const
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:05:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.