27#include "compositor.h"
42#include <QOpenGLContext>
43#include <private/qtx11extras_p.h>
53#ifndef XCB_GLX_BUFFER_SWAP_COMPLETE
54#define XCB_GLX_BUFFER_SWAP_COMPLETE 1
71#include <drm_fourcc.h>
79 , m_drawable(drawable)
80 , m_glxDrawable(glxDrawable)
88 if (swapEvent->
drawable != m_drawable && swapEvent->
drawable != m_glxDrawable) {
94 const std::chrono::microseconds timestamp((uint64_t(swapEvent->
ust_hi) << 32) | swapEvent->
ust_lo);
114 m_backend->
endFrame(renderedRegion, damagedRegion);
130 , m_x11Display(display)
132 , m_layer(std::make_unique<
GlxLayer>(this))
137 QOpenGLContext::supportsThreadedOpenGL();
146 m_vsyncMonitor.reset();
155 m_overlayWindow->destroy();
165 glXDestroyWindow(
display(), glxWindow);
169 XDestroyWindow(
display(), window);
172 m_overlayWindow->destroy();
177static glXFuncPtr getProcAddress(
const char *name)
181 ret = glXGetProcAddress((
const GLubyte *)name);
184 if (ret ==
nullptr) {
195 if (!checkVersion()) {
196 setFailed(QStringLiteral(
"Requires at least GLX 1.3"));
203 if (
hasExtension(QByteArrayLiteral(
"GLX_MESA_swap_control"))) {
209 initVisualDepthHashTable();
212 setFailed(QStringLiteral(
"Could not initialize the buffer"));
218 setFailed(QStringLiteral(
"Could not initialize rendering context"));
241 m_fbo = std::make_unique<GLFramebuffer>(0,
workspace()->geometry().size());
243 bool supportsSwapEvent =
false;
245 if (
hasExtension(QByteArrayLiteral(
"GLX_INTEL_swap_event"))) {
246 if (qEnvironmentVariableIsSet(
"KWIN_USE_INTEL_SWAP_EVENT")) {
247 supportsSwapEvent = qEnvironmentVariable(
"KWIN_USE_INTEL_SWAP_EVENT") != QLatin1String(
"0");
253 supportsSwapEvent = !glPlatform->
isIntel();
258 m_haveMESACopySubBuffer =
hasExtension(QByteArrayLiteral(
"GLX_MESA_copy_sub_buffer"));
259 m_haveMESASwapControl =
hasExtension(QByteArrayLiteral(
"GLX_MESA_swap_control"));
260 m_haveEXTSwapControl =
hasExtension(QByteArrayLiteral(
"GLX_EXT_swap_control"));
261 m_haveSGISwapControl =
hasExtension(QByteArrayLiteral(
"GLX_SGI_swap_control"));
263 bool haveSwapInterval = m_haveMESASwapControl || m_haveEXTSwapControl || m_haveSGISwapControl;
267 if (
hasExtension(QByteArrayLiteral(
"GLX_EXT_buffer_age"))) {
268 const QByteArray useBufferAge = qgetenv(
"KWIN_USE_BUFFER_AGE");
270 if (useBufferAge !=
"0") {
278 supportsSwapEvent =
false;
281 static bool syncToVblankDisabled = qEnvironmentVariableIsSet(
"KWIN_X11_NO_SYNC_TO_VBLANK");
282 if (!syncToVblankDisabled) {
283 if (haveSwapInterval) {
286 qCWarning(KWIN_X11STANDALONE) <<
"glSwapInterval is unsupported";
296 glXQueryDrawable =
nullptr;
299 static bool forceSoftwareVsync = qEnvironmentVariableIntValue(
"KWIN_X11_FORCE_SOFTWARE_VSYNC");
300 if (supportsSwapEvent && !forceSoftwareVsync) {
303 m_swapEventFilter = std::make_unique<SwapEventFilter>(window, glxWindow);
304 glXSelectEvent(
display(), glxWindow, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
311 if (!forceSoftwareVsync) {
312 if (!m_vsyncMonitor) {
315 if (!m_vsyncMonitor) {
319 if (!m_vsyncMonitor) {
324 m->setRefreshRate(m_backend->renderLoop()->refreshRate());
326 m_vsyncMonitor = std::move(monitor);
333bool GlxBackend::checkVersion()
336 glXQueryVersion(
display(), &major, &minor);
340void GlxBackend::initExtensions()
342 const QByteArray
string = (
const char *)glXQueryExtensionsString(
display(), QX11Info::appScreen());
346bool GlxBackend::initBuffer()
348 if (!initFbConfig()) {
356 xcb_visualid_t visual;
357 glXGetFBConfigAttrib(
display(), fbconfig, GLX_VISUAL_ID, (
int *)&visual);
360 qCCritical(KWIN_X11STANDALONE) <<
"The GLXFBConfig does not have an associated X visual";
364 xcb_colormap_t colormap = xcb_generate_id(c);
365 xcb_create_colormap(c,
false, colormap,
rootWindow(), visual);
369 window = xcb_generate_id(c);
370 xcb_create_window(c, visualDepth(visual), window,
overlayWindow()->window(),
371 0, 0, size.
width(), size.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
372 visual, XCB_CW_COLORMAP, &colormap);
374 glxWindow = glXCreateWindow(
display(), fbconfig, window,
nullptr);
377 qCCritical(KWIN_X11STANDALONE) <<
"Failed to create overlay window";
384bool GlxBackend::initFbConfig()
386 const int attribs[] = {
387 GLX_RENDER_TYPE, GLX_RGBA_BIT,
388 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
395 GLX_CONFIG_CAVEAT, GLX_NONE,
396 GLX_DOUBLEBUFFER,
true,
399 const int attribs_srgb[] = {
400 GLX_RENDER_TYPE, GLX_RGBA_BIT,
401 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
408 GLX_CONFIG_CAVEAT, GLX_NONE,
409 GLX_DOUBLEBUFFER,
true,
410 GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB,
true,
414 if (Xcb::defaultDepth() == 24) {
421 if (fbconfig ==
nullptr) {
422 qCCritical(KWIN_X11STANDALONE) <<
"Failed to find a usable framebuffer configuration";
426 int fbconfig_id, visual_id, red, green, blue, alpha, depth, stencil, srgb;
427 glXGetFBConfigAttrib(
display(), fbconfig, GLX_FBCONFIG_ID, &fbconfig_id);
428 glXGetFBConfigAttrib(
display(), fbconfig, GLX_VISUAL_ID, &visual_id);
429 glXGetFBConfigAttrib(
display(), fbconfig, GLX_RED_SIZE, &red);
430 glXGetFBConfigAttrib(
display(), fbconfig, GLX_GREEN_SIZE, &green);
431 glXGetFBConfigAttrib(
display(), fbconfig, GLX_BLUE_SIZE, &blue);
432 glXGetFBConfigAttrib(
display(), fbconfig, GLX_ALPHA_SIZE, &alpha);
433 glXGetFBConfigAttrib(
display(), fbconfig, GLX_DEPTH_SIZE, &depth);
434 glXGetFBConfigAttrib(
display(), fbconfig, GLX_STENCIL_SIZE, &stencil);
435 glXGetFBConfigAttrib(
display(), fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgb);
437 qCDebug(KWIN_X11STANDALONE,
"Choosing GLXFBConfig %#x X visual %#x depth %d RGBA %d:%d:%d:%d ZS %d:%d sRGB: %d",
438 fbconfig_id, visual_id, visualDepth(visual_id), red, green, blue, alpha, depth, stencil, srgb);
443void GlxBackend::initVisualDepthHashTable()
445 const xcb_setup_t *setup = xcb_get_setup(
connection());
447 for (
auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) {
448 for (
auto depth = xcb_screen_allowed_depths_iterator(screen.data); depth.rem; xcb_depth_next(&depth)) {
449 const int len = xcb_depth_visuals_length(depth.data);
450 const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
452 for (
int i = 0; i < len; i++) {
453 m_visualDepthHash.insert(visuals[i].visual_id, depth.data->depth);
459int GlxBackend::visualDepth(xcb_visualid_t visual)
const
461 return m_visualDepthHash.value(visual);
464static inline int bitCount(uint32_t mask)
467 return __builtin_popcount(mask);
480const FBConfigInfo &GlxBackend::infoForVisual(xcb_visualid_t visual)
482 auto it = m_fbconfigHash.constFind(visual);
483 if (it != m_fbconfigHash.constEnd()) {
486 m_fbconfigHash[visual] = FBConfigInfo{
488 .bind_texture_format = 0,
489 .texture_targets = 0,
493 FBConfigInfo &info = m_fbconfigHash[visual];
499 qCCritical(KWIN_X11STANDALONE).nospace() <<
"Could not find a picture format for visual 0x" << Qt::hex << visual;
503 const int red_bits = bitCount(direct->red_mask);
504 const int green_bits = bitCount(direct->green_mask);
505 const int blue_bits = bitCount(direct->blue_mask);
506 const int alpha_bits = bitCount(direct->alpha_mask);
508 const int depth = visualDepth(visual);
510 const auto rgb_sizes = std::tie(red_bits, green_bits, blue_bits);
512 const int attribs[] = {
513 GLX_RENDER_TYPE, GLX_RGBA_BIT,
514 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
515 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
516 GLX_X_RENDERABLE,
true,
517 GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE),
518 GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, int(GLX_DONT_CARE),
519 GLX_BUFFER_SIZE, red_bits + green_bits + blue_bits + alpha_bits,
520 GLX_RED_SIZE, red_bits,
521 GLX_GREEN_SIZE, green_bits,
522 GLX_BLUE_SIZE, blue_bits,
523 GLX_ALPHA_SIZE, alpha_bits,
529 GLXFBConfig *configs = glXChooseFBConfig(
display(), DefaultScreen(
display()), attribs, &count);
532 qCCritical(KWIN_X11STANDALONE).nospace() <<
"Could not find a framebuffer configuration for visual 0x" << Qt::hex << visual;
544 std::deque<FBConfig> candidates;
546 for (
int i = 0; i < count; i++) {
547 int red, green, blue;
548 glXGetFBConfigAttrib(
display(), configs[i], GLX_RED_SIZE, &red);
549 glXGetFBConfigAttrib(
display(), configs[i], GLX_GREEN_SIZE, &green);
550 glXGetFBConfigAttrib(
display(), configs[i], GLX_BLUE_SIZE, &blue);
552 if (std::tie(red, green, blue) != rgb_sizes) {
556 xcb_visualid_t visual;
557 glXGetFBConfigAttrib(
display(), configs[i], GLX_VISUAL_ID, (
int *)&visual);
559 if (visualDepth(visual) != depth) {
563 int bind_rgb, bind_rgba;
564 glXGetFBConfigAttrib(
display(), configs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
565 glXGetFBConfigAttrib(
display(), configs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
567 if (!bind_rgb && !bind_rgba) {
572 glXGetFBConfigAttrib(
display(), configs[i], GLX_DEPTH_SIZE, &depth);
573 glXGetFBConfigAttrib(
display(), configs[i], GLX_STENCIL_SIZE, &stencil);
577 texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
579 texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
582 candidates.emplace_back(FBConfig{configs[i], depth, stencil, texture_format});
589 std::stable_sort(candidates.begin(), candidates.end(), [](
const FBConfig &left,
const FBConfig &right) {
590 if (left.depth < right.depth) {
594 if (left.stencil < right.stencil) {
601 if (candidates.size() > 0) {
602 const FBConfig &candidate = candidates.front();
604 int y_inverted, texture_targets;
605 glXGetFBConfigAttrib(display(), candidate.config, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
606 glXGetFBConfigAttrib(display(), candidate.config, GLX_Y_INVERTED_EXT, &y_inverted);
609 .fbconfig = candidate.config,
610 .bind_texture_format = candidate.format,
611 .texture_targets = texture_targets,
612 .y_inverted = y_inverted,
621 glXGetFBConfigAttrib(display(), info.fbconfig, GLX_FBCONFIG_ID, &fbc_id);
622 glXGetFBConfigAttrib(display(), info.fbconfig, GLX_VISUAL_ID, &visual_id);
624 qCDebug(KWIN_X11STANDALONE).nospace() <<
"Using FBConfig 0x" << Qt::hex << fbc_id <<
" for visual 0x" << Qt::hex << visual_id;
630void GlxBackend::setSwapInterval(
int interval)
632 if (m_haveEXTSwapControl) {
633 glXSwapIntervalEXT(display(), glxWindow, interval);
634 }
else if (m_haveMESASwapControl) {
636 }
else if (m_haveSGISwapControl) {
637 glXSwapIntervalSGI(interval);
641void GlxBackend::present(
const QRegion &damage)
644 const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
645 const bool fullRepaint = supportsBufferAge() || (damage == displayRegion);
648 glXSwapBuffers(display(), glxWindow);
649 if (supportsBufferAge()) {
650 glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *)&m_bufferAge);
652 }
else if (m_haveMESACopySubBuffer) {
653 for (
const QRect &r : damage) {
655 int y = screenSize.height() - r.y() - r.height();
656 glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
659 glDrawBuffer(GL_FRONT);
660 copyPixels(damage, screenSize);
661 glDrawBuffer(GL_BACK);
664 if (!supportsBufferAge()) {
670void GlxBackend::screenGeometryChanged()
675 XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
676 overlayWindow()->resize(size);
681 m_fbo = std::make_unique<GLFramebuffer>(0, size);
684std::unique_ptr<SurfaceTexture> GlxBackend::createSurfaceTextureX11(
SurfacePixmapX11 *pixmap)
686 return std::make_unique<GlxSurfaceTextureX11>(
this, pixmap);
694 m_query = std::make_unique<GLRenderTimeQuery>();
697 if (supportsBufferAge()) {
698 repaint = m_damageJournal.accumulate(m_bufferAge,
infiniteRegion());
704 m_query = std::make_unique<GLRenderTimeQuery>();
713void GlxBackend::endFrame(
const QRegion &renderedRegion,
const QRegion &damagedRegion)
717 if (supportsBufferAge()) {
718 m_damageJournal.add(damagedRegion);
720 m_lastRenderedRegion = renderedRegion;
723std::chrono::nanoseconds GlxBackend::queryRenderTime()
726 return m_query->result();
729void GlxBackend::present(
Output *output,
const std::shared_ptr<OutputFrame> &frame)
734 if (m_vsyncMonitor) {
735 m_vsyncMonitor->arm();
740 QRegion effectiveRenderedRegion = m_lastRenderedRegion;
741 if (!supportsBufferAge() && m_swapStrategy == Options::CopyFrontBuffer && m_lastRenderedRegion != displayRect) {
742 glReadBuffer(GL_FRONT);
743 copyPixels(QRegion(displayRect) - m_lastRenderedRegion, displayRect.size());
744 glReadBuffer(GL_BACK);
745 effectiveRenderedRegion = displayRect;
748 present(effectiveRenderedRegion);
750 if (overlayWindow()->window()) {
751 overlayWindow()->show();
755void GlxBackend::vblank(std::chrono::nanoseconds timestamp)
757 m_frame->presented(std::chrono::nanoseconds::zero(), timestamp, queryRenderTime(), PresentationMode::VSync);
761bool GlxBackend::makeCurrent()
763 return m_context->makeCurrent();
766void GlxBackend::doneCurrent()
768 m_context->doneCurrent();
773 return m_overlayWindow.get();
778 return m_layer.get();
812 if (m_glxPixmap !=
None) {
814 glXReleaseTexImageEXT(m_backend->
display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
816 glXDestroyPixmap(m_backend->
display(), m_glxPixmap);
833 d->m_target = GL_TEXTURE_2D;
834 d->m_scale.setWidth(1.0f /
d->m_size.width());
835 d->m_scale.setHeight(1.0f /
d->m_size.height());
839 d->m_target = GL_TEXTURE_RECTANGLE;
840 d->m_scale.setWidth(1.0f);
841 d->m_scale.setHeight(1.0f);
844 const int attrs[] = {
846 GLX_MIPMAP_TEXTURE_EXT,
false,
847 GLX_TEXTURE_TARGET_EXT,
d->m_target == GL_TEXTURE_2D ? GLX_TEXTURE_2D_EXT : GLX_TEXTURE_RECTANGLE_EXT,
853 d->m_canUseMipmaps =
false;
855 glGenTextures(1, &
d->m_texture);
861 glBindTexture(
d->m_target,
d->m_texture);
862 glXBindTexImageEXT(m_backend->
display(), m_glxPixmap, GLX_FRONT_LEFT_EXT,
nullptr);
868void GlxPixmapTexture::onDamage()
871 glXReleaseTexImageEXT(m_backend->
display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
872 glXBindTexImageEXT(m_backend->
display(), m_glxPixmap, GLX_FRONT_LEFT_EXT,
nullptr);
878#include "moc_x11_standalone_glx_backend.cpp"
static Compositor * self()
const std::unique_ptr< GLTexturePrivate > d
void setContentTransform(OutputTransform transform)
void setFilter(GLenum filter)
void setWrapMode(GLenum mode)
OpenGL Backend using GLX over an X overlay window.
OutputLayerBeginFrameInfo beginFrame()
GlxBackend(::Display *display, X11StandaloneBackend *backend)
std::chrono::nanoseconds queryRenderTime()
OverlayWindow * overlayWindow() const override
::Display * display() const
void doneCurrent() override
void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
static std::unique_ptr< GlxContext > create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow)
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
std::chrono::nanoseconds queryRenderTime() const override
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
GlxLayer(GlxBackend *backend)
GlxPixmapTexture(GlxBackend *backend)
void update(const QRegion ®ion) override
static std::unique_ptr< OMLSyncControlVsyncMonitor > create()
The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
void setSupportsBufferAge(bool value)
bool isFailed() const
Whether the creation of the Backend failed.
void setFailed(const QString &reason)
Sets the backend initialization to failed.
void setExtensions(const QList< QByteArray > &extensions)
bool hasExtension(const QByteArray &extension) const
bool supportsBufferAge() const
OpenGLSurfaceContents m_texture
OpenGLBackend * m_backend
OpenGLSurfaceContents texture() const
SurfacePixmapX11 * m_pixmap
GlSwapStrategy glPreferBufferSwap
bool isGlStrictBinding() const
virtual void resize(const QSize &size)=0
virtual void setup(xcb_window_t window)=0
Init overlay and the destination window in it.
void refreshRateChanged()
void setRefreshRate(int refreshRate)
static RenderLoopPrivate * get(RenderLoop *loop)
void notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime, PresentationMode mode=PresentationMode::VSync)
static std::unique_ptr< SGIVideoSyncVsyncMonitor > create()
static std::unique_ptr< SoftwareVsyncMonitor > create()
bool event(xcb_generic_event_t *event) override
SwapEventFilter(xcb_drawable_t drawable, xcb_glx_drawable_t glxDrawable)
void vblankOccurred(std::chrono::nanoseconds timestamp)
RenderLoop * renderLoop() const
const xcb_render_directformat_t * findPictFormatInfo(xcb_render_pictformat_t format)
xcb_render_pictformat_t findPictFormat(xcb_visualid_t visual)
KWIN_EXPORT QRect infiniteRegion()
KWIN_EXPORT xcb_window_t rootWindow()
GLXFBConfig chooseGlxFbConfig(::Display *display, const int attributes[])
void KWIN_EXPORT cleanupGL()
void initGL(const std::function< resolveFuncPtr(const char *)> &resolveFunction)
glXSwapIntervalMESA_func glXSwapIntervalMESA
KWIN_EXPORT xcb_connection_t * connection()
int(*)(unsigned int interval) glXSwapIntervalMESA_func
RenderTarget renderTarget
xcb_glx_drawable_t drawable
#define XCB_GLX_BUFFER_SWAP_COMPLETE
struct xcb_glx_buffer_swap_complete_event_t xcb_glx_buffer_swap_complete_event_t