Loading...
Searching...
No Matches
EglDmaBufImport.hpp
1#pragma once
2
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// EGL DMA-BUF importer, sister to DMABufPlaneImporter, for the QRhi::OpenGLES2
6// backend: imports a Linux DMA-BUF file descriptor as an EGLImage and binds it
7// to a GL texture through glEGLImageTargetTexture2DOES, which
8// QRhiTexture::createFrom then wraps, so the render graph samples it with no
9// pixel copy.
10//
11// Requires EGL_EXT_image_dma_buf_import_modifiers, GL_OES_EGL_image or
12// GL_EXT_EGL_image_external, and an EGL-backed QOpenGLContext. That is always
13// the case on Wayland; on X11 it depends on whether Qt's XCB plugin picked the
14// EGL or the GLX integration, so isAvailable() probes through
15// QNativeInterface::QEGLContext and returns false for GLX.
16// QT_XCB_GL_INTEGRATION=xcb_egl forces EGL on X11.
17//
18// init(QRhi&) opens libEGL through dlopen, resolves the entry points and grabs
19// the current EGLDisplay. importPlane() creates an EGLImage per frame from the
20// FD, modifier, offset, pitch, drm_fourcc and size; cleanupPlane() destroys it,
21// and the caller manages a ring so the GPU has finished sampling first. score's
22// DRMPrimeDecoder owns one persistent GL texture id and re-targets it each frame.
23
24#if defined(__linux__)
25#include <ossia/detail/dylib_loader.hpp>
26
27#include <QOpenGLContext>
28#include <QtGui/qpa/qplatformnativeinterface.h>
29
30#if __has_include(<QtGui/qopenglextrafunctions.h>)
31#include <QOpenGLExtraFunctions>
32#endif
33
34#include <private/qrhi_p.h>
35
36#include <cstdint>
37#include <cstring>
38#include <optional>
39
40namespace score::gfx
41{
42
46struct EglDmaBufImporter
47{
48 // -- EGL constants (from <EGL/egl.h> + <EGL/eglext.h>) -------------
49 static constexpr uint32_t EGL_NONE_ = 0x3038;
50 static constexpr uint32_t EGL_WIDTH_ = 0x3057;
51 static constexpr uint32_t EGL_HEIGHT_ = 0x3056;
52 static constexpr uint32_t EGL_EXTENSIONS_ = 0x3055;
53 static constexpr uint32_t EGL_LINUX_DMA_BUF_EXT_ = 0x3270;
54 static constexpr uint32_t EGL_LINUX_DRM_FOURCC_EXT_ = 0x3271;
55 static constexpr uint32_t EGL_DMA_BUF_PLANE0_FD_EXT_ = 0x3272;
56 static constexpr uint32_t EGL_DMA_BUF_PLANE0_OFFSET_EXT_ = 0x3273;
57 static constexpr uint32_t EGL_DMA_BUF_PLANE0_PITCH_EXT_ = 0x3274;
58 static constexpr uint32_t EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT_ = 0x3443;
59 static constexpr uint32_t EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT_ = 0x3444;
60 // Plane 1 (for NV12/P010 UV plane, etc.)
61 static constexpr uint32_t EGL_DMA_BUF_PLANE1_FD_EXT_ = 0x3275;
62 static constexpr uint32_t EGL_DMA_BUF_PLANE1_OFFSET_EXT_ = 0x3276;
63 static constexpr uint32_t EGL_DMA_BUF_PLANE1_PITCH_EXT_ = 0x3277;
64 static constexpr uint32_t EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT_ = 0x3445;
65 static constexpr uint32_t EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT_ = 0x3446;
66 // Plane 2 (for I420 V plane)
67 static constexpr uint32_t EGL_DMA_BUF_PLANE2_FD_EXT_ = 0x3278;
68 static constexpr uint32_t EGL_DMA_BUF_PLANE2_OFFSET_EXT_ = 0x3279;
69 static constexpr uint32_t EGL_DMA_BUF_PLANE2_PITCH_EXT_ = 0x327A;
70 static constexpr uint32_t EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT_ = 0x3447;
71 static constexpr uint32_t EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT_ = 0x3448;
72
73 // -- EGL function pointer types -----------------------------------
74 // EGL handles are all opaque void*, used directly here so that no EGL
75 // header is needed.
76 using FN_eglGetCurrentDisplay = void* (*)(void);
77 using FN_eglGetCurrentContext = void* (*)(void);
78 using FN_eglQueryString = const char* (*)(void* dpy, int name);
79 using FN_eglCreateImage
80 = void* (*)(void* dpy, void* ctx, uint32_t target,
81 void* clientBuffer, const intptr_t* attribs);
82 using FN_eglCreateImageKHR
83 = void* (*)(void* dpy, void* ctx, uint32_t target,
84 void* clientBuffer, const int32_t* attribs);
85 using FN_eglDestroyImage = unsigned int (*)(void* dpy, void* image);
86 using FN_eglDestroyImageKHR = unsigned int (*)(void* dpy, void* image);
87 // GL extension function — resolved via QOpenGLContext::getProcAddress.
88 using FN_glEGLImageTargetTexture2DOES
89 = void (*)(unsigned int target, void* image);
90
91 // -- State ---------------------------------------------------------
92 // dylib_loader has no default ctor; defer construction to init().
93 std::optional<ossia::dylib_loader> m_lib;
94 void* m_display{nullptr};
95 bool m_useKhrEntry{false}; // true → eglCreateImageKHR/Destroy variants
96
97 FN_eglGetCurrentDisplay m_getCurrentDisplay{};
98 FN_eglQueryString m_queryString{};
99 FN_eglCreateImage m_createImage{};
100 FN_eglCreateImageKHR m_createImageKHR{};
101 FN_eglDestroyImage m_destroyImage{};
102 FN_eglDestroyImageKHR m_destroyImageKHR{};
103 FN_glEGLImageTargetTexture2DOES m_eglImageTargetTexture{};
104
108 struct PlaneImport
109 {
110 void* image{nullptr};
111 };
112
116 static bool isAvailable(QRhi& rhi) noexcept
117 {
118 if(rhi.backend() != QRhi::OpenGLES2)
119 return false;
120
121 auto* ctx = QOpenGLContext::currentContext();
122 if(!ctx)
123 return false;
124
125 // Only EGL-backed Qt GL contexts can interop with EGL. GLX
126 // contexts can't access EGL images even if libEGL is installed —
127 // they're independent stacks. Qt exposes this via the
128 // QEGLContext native interface, which returns non-null only when
129 // EGL is the actual backend.
130 if(!ctx->nativeInterface<QNativeInterface::QEGLContext>())
131 return false;
132
133 // Resolve eglGetCurrentDisplay via the GL context's procaddress.
134 // If libEGL isn't present at all, this returns null.
135 auto getCurDpy = reinterpret_cast<FN_eglGetCurrentDisplay>(
136 ctx->getProcAddress("eglGetCurrentDisplay"));
137 if(!getCurDpy)
138 return false;
139
140 void* dpy = getCurDpy();
141 if(!dpy)
142 return false;
143
144 auto queryString = reinterpret_cast<FN_eglQueryString>(
145 ctx->getProcAddress("eglQueryString"));
146 if(!queryString)
147 return false;
148 const char* exts = queryString(dpy, int(EGL_EXTENSIONS_));
149 if(!exts)
150 return false;
151
152 // The modifier-aware variant is required: without it only LINEAR
153 // imports work, and the producer serves whatever pipewire negotiated.
154 // score advertises LINEAR + INVALID, but producers may emit other
155 // modifiers anyway.
156 if(!std::strstr(exts, "EGL_EXT_image_dma_buf_import_modifiers"))
157 return false;
158
159 // GL_OES_EGL_image (the glEGLImageTargetTexture2DOES side) is
160 // present on essentially every EGL-backed GL context, but without it
161 // the import is useless, so check.
162 if(!ctx->hasExtension(QByteArrayLiteral("GL_OES_EGL_image")))
163 return false;
164
165 return true;
166 }
167
170 bool init(QRhi& rhi) noexcept
171 {
172 auto* ctx = QOpenGLContext::currentContext();
173 if(!ctx)
174 return false;
175
176 try
177 {
178 m_lib.emplace(std::vector<std::string_view>{
179 "libEGL.so.1", "libEGL.so"});
180 }
181 catch(...)
182 {
183 return false;
184 }
185
186 m_getCurrentDisplay
187 = m_lib->symbol<FN_eglGetCurrentDisplay>("eglGetCurrentDisplay");
188 m_queryString = m_lib->symbol<FN_eglQueryString>("eglQueryString");
189
190 // Modern EGL (1.5+) exposes eglCreateImage / eglDestroyImage as
191 // core. Older drivers have only the KHR-suffixed variants: prefer
192 // core, fall back to KHR.
193 m_createImage = m_lib->symbol<FN_eglCreateImage>("eglCreateImage");
194 m_destroyImage = m_lib->symbol<FN_eglDestroyImage>("eglDestroyImage");
195 if(!m_createImage || !m_destroyImage)
196 {
197 m_createImageKHR
198 = m_lib->symbol<FN_eglCreateImageKHR>("eglCreateImageKHR");
199 m_destroyImageKHR
200 = m_lib->symbol<FN_eglDestroyImageKHR>("eglDestroyImageKHR");
201 m_useKhrEntry = m_createImageKHR && m_destroyImageKHR;
202 if(!m_useKhrEntry)
203 return false;
204 }
205
206 // GL-side: glEGLImageTargetTexture2DOES is an extension function;
207 // QOpenGLContext::getProcAddress is the portable way to resolve
208 // it (delegates to eglGetProcAddress / glXGetProcAddress).
209 m_eglImageTargetTexture
210 = reinterpret_cast<FN_glEGLImageTargetTexture2DOES>(
211 ctx->getProcAddress("glEGLImageTargetTexture2DOES"));
212 if(!m_eglImageTargetTexture)
213 return false;
214
215 m_display = m_getCurrentDisplay ? m_getCurrentDisplay() : nullptr;
216 if(!m_display)
217 return false;
218
219 (void)rhi;
220 return true;
221 }
222
226 void cleanupPlane(PlaneImport& p) noexcept
227 {
228 if(!p.image)
229 return;
230 if(m_useKhrEntry)
231 {
232 if(m_destroyImageKHR && m_display)
233 m_destroyImageKHR(m_display, p.image);
234 }
235 else
236 {
237 if(m_destroyImage && m_display)
238 m_destroyImage(m_display, p.image);
239 }
240 p.image = nullptr;
241 }
242
249 bool bindPlane(unsigned int glTexture, const PlaneImport& p) noexcept
250 {
251 if(!p.image || !m_eglImageTargetTexture || glTexture == 0)
252 return false;
253 auto* ctx = QOpenGLContext::currentContext();
254 if(!ctx)
255 return false;
256 auto* funcs = ctx->extraFunctions();
257 if(!funcs)
258 return false;
259 constexpr unsigned int GL_TEXTURE_2D_v = 0x0DE1;
260 funcs->glBindTexture(GL_TEXTURE_2D_v, glTexture);
261 while(funcs->glGetError() != 0u)
262 ; // drain, so the check below reports only this call
263 m_eglImageTargetTexture(GL_TEXTURE_2D_v, p.image);
264 // A LINEAR buffer that the driver only exposes as an external image fails
265 // here with GL_INVALID_OPERATION and then samples pure black -- NVIDIA
266 // advertises AR24/LINEAR as external_only=1, for instance.
267 // Without this check the rung reports itself engaged and renders nothing.
268 if(const unsigned int err = funcs->glGetError(); err != 0u)
269 {
270 qWarning() << "EglDmaBufImporter: glEGLImageTargetTexture2DOES(GL_TEXTURE_2D)"
271 "failed with GL error"
272 << Qt::hex << err
273 << "- the buffer is likely samplable only as "
274 "GL_TEXTURE_EXTERNAL_OES";
275 return false;
276 }
277 return true;
278 }
279
285 bool importPlane(
286 PlaneImport& out, unsigned int glTexture, int fd, uint64_t modifier,
287 ptrdiff_t offset, ptrdiff_t pitch, uint32_t drm_fourcc, int w, int h)
288 {
289 out.image = nullptr;
290 if(!m_display || !m_eglImageTargetTexture || fd < 0)
291 return false;
292
293 // Pass the modifier even when it's LINEAR (0). Drivers that
294 // support EGL_EXT_image_dma_buf_import_modifiers expect the
295 // modifier attribs to be present; some require them for the
296 // import to succeed.
297 const uint32_t mod_lo = uint32_t(modifier & 0xFFFFFFFFu);
298 const uint32_t mod_hi = uint32_t((modifier >> 32) & 0xFFFFFFFFu);
299
300 // EGLAttrib (intptr_t) on the core entry point; EGLint (int)
301 // on the KHR entry point. Build both flavors.
302 intptr_t attribs[]
303 = {(intptr_t)EGL_WIDTH_,
304 w,
305 (intptr_t)EGL_HEIGHT_,
306 h,
307 (intptr_t)EGL_LINUX_DRM_FOURCC_EXT_,
308 intptr_t(drm_fourcc),
309 (intptr_t)EGL_DMA_BUF_PLANE0_FD_EXT_,
310 intptr_t(fd),
311 (intptr_t)EGL_DMA_BUF_PLANE0_OFFSET_EXT_,
312 intptr_t(offset),
313 (intptr_t)EGL_DMA_BUF_PLANE0_PITCH_EXT_,
314 intptr_t(pitch),
315 (intptr_t)EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT_,
316 intptr_t(mod_lo),
317 (intptr_t)EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT_,
318 intptr_t(mod_hi),
319 (intptr_t)EGL_NONE_};
320
321 void* img = nullptr;
322 if(m_useKhrEntry)
323 {
324 // KHR variant takes EGLint (int) attribs.
325 int khrAttribs[sizeof(attribs) / sizeof(attribs[0])];
326 for(std::size_t i = 0; i < sizeof(attribs) / sizeof(attribs[0]); ++i)
327 khrAttribs[i] = int(attribs[i]);
328 img = m_createImageKHR(
329 m_display, /*EGL_NO_CONTEXT=*/nullptr,
330 EGL_LINUX_DMA_BUF_EXT_, /*EGLClientBuffer=*/nullptr,
331 khrAttribs);
332 }
333 else
334 {
335 img = m_createImage(
336 m_display, /*EGL_NO_CONTEXT=*/nullptr,
337 EGL_LINUX_DMA_BUF_EXT_, /*EGLClientBuffer=*/nullptr, attribs);
338 }
339 if(!img)
340 return false;
341
342 // Bind to the supplied GL texture. The caller pre-created it via
343 // glGenTextures + glBindTexture; only its storage is re-targeted at
344 // the new EGLImage, so the same texture id serves every frame.
345 out.image = img;
346 bindPlane(glTexture, out);
347 return true;
348 }
349
361 bool importExternal(
362 PlaneImport& out, unsigned int glTexture, int fd, uint64_t modifier,
363 const std::uint32_t* offsets, const std::uint32_t* pitches,
364 std::uint32_t planeCount, uint32_t drm_fourcc, int w, int h)
365 {
366 out.image = nullptr;
367 if(!m_display || !m_eglImageTargetTexture || fd < 0 || planeCount == 0
368 || planeCount > 3)
369 return false;
370
371 const uint32_t mod_lo = uint32_t(modifier & 0xFFFFFFFFu);
372 const uint32_t mod_hi = uint32_t((modifier >> 32) & 0xFFFFFFFFu);
373
374 static constexpr uint32_t planeFd[3]
375 = {EGL_DMA_BUF_PLANE0_FD_EXT_, EGL_DMA_BUF_PLANE1_FD_EXT_,
376 EGL_DMA_BUF_PLANE2_FD_EXT_};
377 static constexpr uint32_t planeOff[3]
378 = {EGL_DMA_BUF_PLANE0_OFFSET_EXT_, EGL_DMA_BUF_PLANE1_OFFSET_EXT_,
379 EGL_DMA_BUF_PLANE2_OFFSET_EXT_};
380 static constexpr uint32_t planePitch[3]
381 = {EGL_DMA_BUF_PLANE0_PITCH_EXT_, EGL_DMA_BUF_PLANE1_PITCH_EXT_,
382 EGL_DMA_BUF_PLANE2_PITCH_EXT_};
383 static constexpr uint32_t planeModLo[3]
384 = {EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT_,
385 EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT_,
386 EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT_};
387 static constexpr uint32_t planeModHi[3]
388 = {EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT_,
389 EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT_,
390 EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT_};
391
392 intptr_t attribs[6 + 3 * 10 + 1];
393 std::size_t n = 0;
394 attribs[n++] = (intptr_t)EGL_WIDTH_;
395 attribs[n++] = w;
396 attribs[n++] = (intptr_t)EGL_HEIGHT_;
397 attribs[n++] = h;
398 attribs[n++] = (intptr_t)EGL_LINUX_DRM_FOURCC_EXT_;
399 attribs[n++] = intptr_t(drm_fourcc);
400 for(std::uint32_t i = 0; i < planeCount; ++i)
401 {
402 attribs[n++] = (intptr_t)planeFd[i];
403 attribs[n++] = intptr_t(fd);
404 attribs[n++] = (intptr_t)planeOff[i];
405 attribs[n++] = intptr_t(offsets[i]);
406 attribs[n++] = (intptr_t)planePitch[i];
407 attribs[n++] = intptr_t(pitches[i]);
408 attribs[n++] = (intptr_t)planeModLo[i];
409 attribs[n++] = intptr_t(mod_lo);
410 attribs[n++] = (intptr_t)planeModHi[i];
411 attribs[n++] = intptr_t(mod_hi);
412 }
413 attribs[n++] = (intptr_t)EGL_NONE_;
414
415 void* img = nullptr;
416 if(m_useKhrEntry)
417 {
418 int khrAttribs[sizeof(attribs) / sizeof(attribs[0])];
419 for(std::size_t i = 0; i < n; ++i)
420 khrAttribs[i] = int(attribs[i]);
421 img = m_createImageKHR(
422 m_display, nullptr, EGL_LINUX_DMA_BUF_EXT_, nullptr, khrAttribs);
423 }
424 else
425 {
426 img = m_createImage(
427 m_display, nullptr, EGL_LINUX_DMA_BUF_EXT_, nullptr, attribs);
428 }
429 if(!img)
430 return false;
431
432 out.image = img;
433 return bindExternal(glTexture, out);
434 }
435
437 bool bindExternal(unsigned int glTexture, const PlaneImport& p) noexcept
438 {
439 if(!p.image || !m_eglImageTargetTexture || glTexture == 0)
440 return false;
441 auto* ctx = QOpenGLContext::currentContext();
442 if(!ctx)
443 return false;
444 auto* funcs = ctx->extraFunctions();
445 if(!funcs)
446 return false;
447 funcs->glBindTexture(GL_TEXTURE_EXTERNAL_OES_, glTexture);
448 while(funcs->glGetError() != 0u)
449 ;
450 m_eglImageTargetTexture(GL_TEXTURE_EXTERNAL_OES_, p.image);
451 if(const unsigned int err = funcs->glGetError(); err != 0u)
452 {
453 qDebug() << "EglDmaBufImporter: external image target failed, GL error"
454 << Qt::hex << err;
455 return false;
456 }
457 return true;
458 }
459
460 static constexpr unsigned int GL_TEXTURE_EXTERNAL_OES_ = 0x8D65;
461
464 static unsigned int createExternalTexture() noexcept
465 {
466 auto* ctx = QOpenGLContext::currentContext();
467 if(!ctx)
468 return 0;
469 auto* f = ctx->extraFunctions();
470 if(!f)
471 return 0;
472 constexpr unsigned int GL_TEXTURE_MIN_FILTER_v = 0x2801;
473 constexpr unsigned int GL_TEXTURE_MAG_FILTER_v = 0x2800;
474 constexpr unsigned int GL_TEXTURE_WRAP_S_v = 0x2802;
475 constexpr unsigned int GL_TEXTURE_WRAP_T_v = 0x2803;
476 constexpr int GL_LINEAR_v = 0x2601;
477 constexpr int GL_CLAMP_TO_EDGE_v = 0x812F;
478 unsigned int tex = 0;
479 f->glGenTextures(1, &tex);
480 if(tex == 0)
481 return 0;
482 f->glBindTexture(GL_TEXTURE_EXTERNAL_OES_, tex);
483 f->glTexParameteri(GL_TEXTURE_EXTERNAL_OES_, GL_TEXTURE_MIN_FILTER_v, GL_LINEAR_v);
484 f->glTexParameteri(GL_TEXTURE_EXTERNAL_OES_, GL_TEXTURE_MAG_FILTER_v, GL_LINEAR_v);
485 f->glTexParameteri(GL_TEXTURE_EXTERNAL_OES_, GL_TEXTURE_WRAP_S_v, GL_CLAMP_TO_EDGE_v);
486 f->glTexParameteri(GL_TEXTURE_EXTERNAL_OES_, GL_TEXTURE_WRAP_T_v, GL_CLAMP_TO_EDGE_v);
487 return tex;
488 }
489
491 bool hasExternalImage() const noexcept
492 {
493 auto* ctx = QOpenGLContext::currentContext();
494 if(!ctx)
495 return false;
496 return ctx->hasExtension("GL_OES_EGL_image_external")
497 || ctx->hasExtension("GL_OES_EGL_image_external_essl3");
498 }
499
500 // -- Modifier-aware capability queries -------------------
501
509 std::vector<std::uint64_t>
510 supportedModifiers(std::uint32_t drm_fourcc) noexcept
511 {
512 std::vector<std::uint64_t> result;
513 if(!m_display)
514 return result;
515
516 auto* ctx = QOpenGLContext::currentContext();
517 if(!ctx)
518 return result;
519
520 using FN_eglQueryDmaBufModifiersEXT = unsigned int (*)(
521 void* dpy, int format, int max_modifiers, std::uint64_t* modifiers,
522 unsigned int* external_only, int* num_modifiers);
523 auto query = reinterpret_cast<FN_eglQueryDmaBufModifiersEXT>(
524 ctx->getProcAddress("eglQueryDmaBufModifiersEXT"));
525 if(!query)
526 {
527 // Extension entry not exposed (older driver). LINEAR is the
528 // only safe assumption.
529 result.push_back(0);
530 return result;
531 }
532
533 int count = 0;
534 if(!query(m_display, int(drm_fourcc), 0, nullptr, nullptr, &count)
535 || count <= 0)
536 {
537 result.push_back(0);
538 return result;
539 }
540
541 result.resize(static_cast<std::size_t>(count));
542 std::vector<unsigned int> external_only(count);
543 if(!query(
544 m_display, int(drm_fourcc), count, result.data(),
545 external_only.data(), &count))
546 {
547 result.clear();
548 result.push_back(0);
549 return result;
550 }
551 result.resize(static_cast<std::size_t>(count));
552
553 // Drop EXTERNAL-only modifiers — score binds these as
554 // GL_TEXTURE_2D, which forbids EXTERNAL_OES textures.
555 std::size_t w = 0;
556 for(std::size_t i = 0; i < result.size(); ++i)
557 if(external_only[i] == 0)
558 result[w++] = result[i];
559 result.resize(w);
560
561 // Always include LINEAR — driver may list non-LINEAR only when
562 // it has tiled support, but consumers expect LINEAR as a fallback.
563 bool has_linear = false;
564 for(auto m : result)
565 if(m == 0)
566 {
567 has_linear = true;
568 break;
569 }
570 if(!has_linear)
571 result.push_back(0);
572 return result;
573 }
574
588 bool canImportModifier(
589 std::uint32_t drm_fourcc, std::uint64_t modifier) noexcept
590 {
591 auto* ctx = QOpenGLContext::currentContext();
592 if(!m_display || !ctx)
593 return false;
594
595 using FN_eglQueryDmaBufModifiersEXT = unsigned int (*)(
596 void* dpy, int format, int max_modifiers, std::uint64_t* modifiers,
597 unsigned int* external_only, int* num_modifiers);
598 auto query = reinterpret_cast<FN_eglQueryDmaBufModifiersEXT>(
599 ctx->getProcAddress("eglQueryDmaBufModifiersEXT"));
600 if(!query)
601 return true;
602
603 int count = 0;
604 if(!query(m_display, int(drm_fourcc), 0, nullptr, nullptr, &count)
605 || count <= 0)
606 return false;
607
608 const auto n = static_cast<std::size_t>(count);
609 std::vector<std::uint64_t> mods(n);
610 std::vector<unsigned int> external_only(n);
611 if(!query(
612 m_display, int(drm_fourcc), count, mods.data(), external_only.data(),
613 &count))
614 return false;
615
616 for(int i = 0; i < count; ++i)
617 if(mods[std::size_t(i)] == modifier && external_only[std::size_t(i)] == 0)
618 return true;
619 return false;
620 }
621};
622
629inline unsigned int createLinearClampGlTexture2D() noexcept
630{
631 auto* ctx = QOpenGLContext::currentContext();
632 if(!ctx)
633 return 0;
634 auto* gl = ctx->extraFunctions();
635 unsigned int tex = 0;
636 gl->glGenTextures(1, &tex);
637 if(!tex)
638 return 0;
639 constexpr unsigned int GL_TEXTURE_2D_v = 0x0DE1;
640 constexpr unsigned int GL_TEXTURE_MIN_FILTER_v = 0x2801;
641 constexpr unsigned int GL_TEXTURE_MAG_FILTER_v = 0x2800;
642 constexpr unsigned int GL_TEXTURE_WRAP_S_v = 0x2802;
643 constexpr unsigned int GL_TEXTURE_WRAP_T_v = 0x2803;
644 constexpr unsigned int GL_LINEAR_v = 0x2601;
645 constexpr unsigned int GL_CLAMP_TO_EDGE_v = 0x812F;
646 gl->glBindTexture(GL_TEXTURE_2D_v, tex);
647 gl->glTexParameteri(GL_TEXTURE_2D_v, GL_TEXTURE_MIN_FILTER_v, GL_LINEAR_v);
648 gl->glTexParameteri(GL_TEXTURE_2D_v, GL_TEXTURE_MAG_FILTER_v, GL_LINEAR_v);
649 gl->glTexParameteri(
650 GL_TEXTURE_2D_v, GL_TEXTURE_WRAP_S_v, GL_CLAMP_TO_EDGE_v);
651 gl->glTexParameteri(
652 GL_TEXTURE_2D_v, GL_TEXTURE_WRAP_T_v, GL_CLAMP_TO_EDGE_v);
653 return tex;
654}
655
656} // namespace score::gfx
657#endif // __linux__
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11