/* * Copyright 2024 Red Hat, Inc. * * SPDX-License-Identifier: MIT */ /* * Compatibility stub for Xorg. This responds to just enough of the legacy DRI * interface to allow the X server to initialize GLX and enable direct * rendering clients. It implements the screen creation hook and provides a * (static, unambitious) list of framebuffer configs. It will not create an * indirect context; Indirect contexts have been disabled by default since * 2014 and would be limited to GL 1.4 in any case, so this is no great loss. * * If you do want indirect contexts to work, you have options. This stub is * new with Mesa 24.1, so one option is to use an older Mesa release stream. * Another option is to use an X server that does not need this interface. For * Xwayland and Xephyr that's XX.X or newer, and for Xorg drivers using glamor * for acceleration that's YY.Y or newer. */ #include "main/glconfig.h" #include "main/mtypes.h" #include #include #include #include #include "gbm/main/gbm.h" #include "drm-uapi/drm_fourcc.h" #define EGL_PLATFORM_GBM_MESA 0x31D7 /* avoid needing X11 headers */ #define GLX_NONE 0x8000 #define GLX_DONT_CARE 0xFFFFFFFF #define CONFIG_ZS(color, zs) \ { \ .color_format = color, \ .zs_format = zs, \ } #define CONFIG(color) \ CONFIG_ZS(color, PIPE_FORMAT_S8_UINT), \ CONFIG_ZS(color, PIPE_FORMAT_Z24_UNORM_S8_UINT), \ CONFIG_ZS(color, PIPE_FORMAT_Z24X8_UNORM), \ CONFIG_ZS(color, PIPE_FORMAT_Z16_UNORM), \ CONFIG_ZS(color, PIPE_FORMAT_NONE) \ static const struct gl_config drilConfigs[] = { CONFIG(PIPE_FORMAT_R8G8B8A8_UNORM), CONFIG(PIPE_FORMAT_R8G8B8X8_UNORM), CONFIG(PIPE_FORMAT_B8G8R8A8_UNORM), CONFIG(PIPE_FORMAT_B8G8R8X8_UNORM), CONFIG(PIPE_FORMAT_R10G10B10A2_UNORM), CONFIG(PIPE_FORMAT_R10G10B10X2_UNORM), CONFIG(PIPE_FORMAT_B10G10R10A2_UNORM), CONFIG(PIPE_FORMAT_B10G10R10X2_UNORM), CONFIG(PIPE_FORMAT_R5G6B5_UNORM), CONFIG(PIPE_FORMAT_R5G5B5A1_UNORM), CONFIG(PIPE_FORMAT_R5G5B5X1_UNORM), CONFIG(PIPE_FORMAT_R4G4B4A4_UNORM), CONFIG(PIPE_FORMAT_R4G4B4X4_UNORM), CONFIG(PIPE_FORMAT_B5G6R5_UNORM), CONFIG(PIPE_FORMAT_B5G5R5A1_UNORM), CONFIG(PIPE_FORMAT_B5G5R5X1_UNORM), CONFIG(PIPE_FORMAT_B4G4R4A4_UNORM), CONFIG(PIPE_FORMAT_B4G4R4X4_UNORM), }; #define RGB UTIL_FORMAT_COLORSPACE_RGB #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 #define ZS UTIL_FORMAT_COLORSPACE_ZS #define DEPTH 0 #define STENCIL 1 #define CASE(ATTRIB, VALUE) \ case __DRI_ATTRIB_ ## ATTRIB : \ *value = VALUE; \ break; #define SIZE(f, cs, chan) (f ? util_format_get_component_bits(f, cs, chan) : 0) #define SHIFT(f, cs, chan) (f ? util_format_get_component_shift(f, cs, chan) : 0) #define MASK(f, cs, chan) \ (((1 << SIZE(f, cs, chan)) - 1) << SHIFT(f, cs, chan)) static int drilIndexConfigAttrib(const __DRIconfig *_config, int index, unsigned int *attrib, unsigned int *value) { struct gl_config *config = (void *)_config; enum pipe_format color_format = config->color_format; enum pipe_format zs_format = config->zs_format; enum pipe_format accum_format = config->accum_format; if (index >= __DRI_ATTRIB_MAX) return 0; switch (index) { case __DRI_ATTRIB_SAMPLE_BUFFERS: *value = !!config->samples; break; case __DRI_ATTRIB_BUFFER_SIZE: { unsigned int red = 0, green = 0, blue = 0, alpha = 0; drilIndexConfigAttrib(_config, __DRI_ATTRIB_RED_SIZE, attrib, &red); drilIndexConfigAttrib(_config, __DRI_ATTRIB_GREEN_SIZE, attrib, &green); drilIndexConfigAttrib(_config, __DRI_ATTRIB_BLUE_SIZE, attrib, &blue); drilIndexConfigAttrib(_config, __DRI_ATTRIB_ALPHA_SIZE, attrib, &alpha); *value = red + green + blue + alpha; break; } CASE(RED_SIZE, SIZE(color_format, RGB, 0)); CASE(GREEN_SIZE, SIZE(color_format, RGB, 1)); CASE(BLUE_SIZE, SIZE(color_format, RGB, 2)); CASE(ALPHA_SIZE, SIZE(color_format, RGB, 3)); CASE(DEPTH_SIZE, SIZE(zs_format, ZS, 0)); CASE(STENCIL_SIZE, SIZE(zs_format, ZS, 1)); CASE(ACCUM_RED_SIZE, SIZE(accum_format, RGB, 0)); CASE(ACCUM_GREEN_SIZE, SIZE(accum_format, RGB, 1)); CASE(ACCUM_BLUE_SIZE, SIZE(accum_format, RGB, 2)); CASE(ACCUM_ALPHA_SIZE, SIZE(accum_format, RGB, 3)); CASE(RENDER_TYPE, __DRI_ATTRIB_RGBA_BIT); CASE(CONFORMANT, GL_TRUE); CASE(DOUBLE_BUFFER, config->doubleBufferMode); CASE(SAMPLES, config->samples); CASE(FRAMEBUFFER_SRGB_CAPABLE, config->sRGBCapable); CASE(TRANSPARENT_TYPE, GLX_NONE); CASE(TRANSPARENT_INDEX_VALUE, GLX_NONE); CASE(TRANSPARENT_RED_VALUE, GLX_DONT_CARE); CASE(TRANSPARENT_GREEN_VALUE, GLX_DONT_CARE); CASE(TRANSPARENT_BLUE_VALUE, GLX_DONT_CARE); CASE(TRANSPARENT_ALPHA_VALUE, GLX_DONT_CARE); CASE(RED_MASK, MASK(color_format, RGB, 0)); CASE(GREEN_MASK, MASK(color_format, RGB, 1)); CASE(BLUE_MASK, MASK(color_format, RGB, 2)); CASE(ALPHA_MASK, MASK(color_format, RGB, 3)); CASE(SWAP_METHOD, __DRI_ATTRIB_SWAP_UNDEFINED); CASE(MAX_SWAP_INTERVAL, INT_MAX); CASE(BIND_TO_TEXTURE_RGB, GL_TRUE); CASE(BIND_TO_TEXTURE_RGBA, GL_TRUE); CASE(BIND_TO_TEXTURE_TARGETS, __DRI_ATTRIB_TEXTURE_1D_BIT | __DRI_ATTRIB_TEXTURE_2D_BIT | __DRI_ATTRIB_TEXTURE_RECTANGLE_BIT); CASE(YINVERTED, GL_TRUE); CASE(RED_SHIFT, SHIFT(color_format, RGB, 0)); CASE(GREEN_SHIFT, SHIFT(color_format, RGB, 1)); CASE(BLUE_SHIFT, SHIFT(color_format, RGB, 2)); CASE(ALPHA_SHIFT, SHIFT(color_format, RGB, 3)); default: *value = 0; break; } *attrib = index; return 1; } static void drilDestroyScreen(__DRIscreen *screen) { /* At the moment this is just the bounce table for the configs */ free(screen); } static const __DRI2flushControlExtension dri2FlushControlExtension = { .base = { __DRI2_FLUSH_CONTROL, 1 } }; static void dril_set_tex_buffer2(__DRIcontext *pDRICtx, GLint target, GLint format, __DRIdrawable *dPriv) { } static void dril_set_tex_buffer(__DRIcontext *pDRICtx, GLint target, __DRIdrawable *dPriv) { } const __DRItexBufferExtension driTexBufferExtension = { .base = { __DRI_TEX_BUFFER, 2 }, .setTexBuffer = dril_set_tex_buffer, .setTexBuffer2 = dril_set_tex_buffer2, .releaseTexBuffer = NULL, }; static const __DRIrobustnessExtension dri2Robustness = { .base = { __DRI2_ROBUSTNESS, 1 } }; static const __DRIextension *dril_extensions[] = { &dri2FlushControlExtension.base, &driTexBufferExtension.base, &dri2Robustness.base, NULL }; /* This has to return a pointer to NULL, not just NULL */ static const __DRIextension ** drilGetExtensions(__DRIscreen *screen) { return (void*)&dril_extensions; } static __DRIcontext * drilCreateContextAttribs(__DRIscreen *psp, int api, const __DRIconfig *config, __DRIcontext *shared, unsigned num_attribs, const uint32_t *attribs, unsigned *error, void *data) { return NULL; } static __DRIcontext * drilCreateNewContextForAPI(__DRIscreen *screen, int api, const __DRIconfig *config, __DRIcontext *shared, void *data) { return NULL; } static __DRIcontext * drilCreateNewContext(__DRIscreen *screen, const __DRIconfig *config, __DRIcontext *shared, void *data) { return NULL; } static void drilDestroyDrawable(__DRIdrawable *pdp) { } static const __DRIcoreExtension drilCoreExtension = { .base = { __DRI_CORE, 1 }, .destroyScreen = drilDestroyScreen, .getExtensions = drilGetExtensions, .getConfigAttrib = NULL, // XXX not actually used! .indexConfigAttrib = drilIndexConfigAttrib, .destroyDrawable = drilDestroyDrawable, .createNewContext = drilCreateNewContext, }; static int drilBindContext(__DRIcontext *pcp, __DRIdrawable *pdp, __DRIdrawable *prp) { return 0; // Success } static int drilUnbindContext(__DRIcontext *pcp) { return 0; // Success } static __DRIdrawable * drilCreateNewDrawable(__DRIscreen *psp, const __DRIconfig *config, void *data) { return NULL; } static enum pipe_format fourcc_to_pipe_format(int fourcc) { switch (fourcc) { case DRM_FORMAT_RGB565: return PIPE_FORMAT_B5G6R5_UNORM; case DRM_FORMAT_XRGB8888: return PIPE_FORMAT_BGRX8888_UNORM; case DRM_FORMAT_ARGB8888: return PIPE_FORMAT_BGRA8888_UNORM; case DRM_FORMAT_ABGR8888: return PIPE_FORMAT_RGBA8888_UNORM; case DRM_FORMAT_XBGR8888: return PIPE_FORMAT_RGBX8888_UNORM; case DRM_FORMAT_XRGB2101010: return PIPE_FORMAT_B10G10R10X2_UNORM; case DRM_FORMAT_ARGB2101010: return PIPE_FORMAT_B10G10R10A2_UNORM; case DRM_FORMAT_XBGR2101010: return PIPE_FORMAT_R10G10B10X2_UNORM; case DRM_FORMAT_ABGR2101010: return PIPE_FORMAT_R10G10B10A2_UNORM; case DRM_FORMAT_XBGR16161616F: return PIPE_FORMAT_R16G16B16A16_FLOAT; case DRM_FORMAT_ABGR16161616F: return PIPE_FORMAT_R16G16B16X16_FLOAT; case DRM_FORMAT_ARGB1555: return PIPE_FORMAT_B5G5R5A1_UNORM; case DRM_FORMAT_ABGR1555: return PIPE_FORMAT_R5G5B5A1_UNORM; case DRM_FORMAT_ARGB4444: return PIPE_FORMAT_B4G4R4A4_UNORM; case DRM_FORMAT_ABGR4444: return PIPE_FORMAT_R4G4B4A4_UNORM; default: return PIPE_FORMAT_NONE; } } static unsigned add_srgb_config(struct gl_config **configs, unsigned c, enum pipe_format last_pformat, unsigned last_start) { enum pipe_format srgb = util_format_srgb(last_pformat); if (!srgb) return c; unsigned end = c; for (unsigned j = last_start; j < end; j++) { configs[c] = mem_dup(configs[j], sizeof(drilConfigs[j])); struct gl_config *cfg = configs[c++]; cfg->color_format = srgb; cfg->sRGBCapable = 1; } return c; } /* DRI2 awfulness */ static const __DRIconfig ** init_dri2_configs(int fd) { void *egl = NULL; struct gl_config **configs = NULL; unsigned c = 0; enum pipe_format last_pformat = 0; unsigned last_start = 0; /* dlopen/dlsym to avoid linkage */ egl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL); if (!egl) return false; void * (*peglGetProcAddress)(const char *) = dlsym(egl, "eglGetProcAddress"); EGLDisplay (*peglGetPlatformDisplayEXT)(EGLenum, void *, const EGLint *) = peglGetProcAddress("eglGetPlatformDisplayEXT"); EGLBoolean (*peglInitialize)(EGLDisplay, int*, int*) = peglGetProcAddress("eglInitialize"); EGLBoolean (*peglTerminate)(EGLDisplay) = peglGetProcAddress("eglTerminate"); EGLBoolean (*peglGetConfigs)(EGLDisplay, EGLConfig*, EGLint, EGLint*) = peglGetProcAddress("eglGetConfigs"); EGLBoolean (*peglGetConfigAttrib)(EGLDisplay, EGLConfig, EGLint, EGLint *) = peglGetProcAddress("eglGetConfigAttrib"); const char *(*peglQueryString)(EGLDisplay, EGLint) = peglGetProcAddress("eglQueryString"); struct gbm_device *gbm = NULL; if (fd != -1) { /* try opening GBM for hardware driver info */ gbm = gbm_create_device(fd); if (!gbm) goto out; } EGLDisplay dpy = peglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, gbm ? gbm : EGL_DEFAULT_DISPLAY, NULL); if (!dpy) goto out_gbm; int maj, min; if (!peglInitialize(dpy, &maj, &min)) goto out_gbm; const char *egl_extension_list = peglQueryString(dpy, EGL_EXTENSIONS); bool has_srgb = strstr(egl_extension_list, "EGL_KHR_gl_colorspace"); int num_configs = 0; EGLConfig *eglconfigs = NULL; if (!peglGetConfigs(dpy, NULL, 0, &num_configs)) goto out_egl; eglconfigs = malloc(sizeof(EGLConfig) * num_configs); /* overestimate: num_configs * doubleBuffer * sRGB + NULL */ configs = calloc(num_configs * 2 * 2 + 1, sizeof(struct gl_config)); if (!peglGetConfigs(dpy, eglconfigs, num_configs, &num_configs)) goto out_egl; for (unsigned i = 0; i < num_configs; i++) { /* verify that this is the right format */ EGLint format, depth, stencil, samples; if (!peglGetConfigAttrib(dpy, eglconfigs[i], EGL_NATIVE_VISUAL_ID, &format) || !peglGetConfigAttrib(dpy, eglconfigs[i], EGL_DEPTH_SIZE, &depth) || !peglGetConfigAttrib(dpy, eglconfigs[i], EGL_STENCIL_SIZE, &stencil) || !peglGetConfigAttrib(dpy, eglconfigs[i], EGL_SAMPLES, &samples)) continue; enum pipe_format pformat = fourcc_to_pipe_format(format); /* srgb configs go after base configs */ if (last_pformat && has_srgb && pformat != last_pformat) c = add_srgb_config(configs, c, last_pformat, last_start); /* tracking for the number of srgb configs to create */ if (pformat != last_pformat) last_start = c; for (unsigned j = 0; j < ARRAY_SIZE(drilConfigs); j++) { unsigned depth_size = SIZE(drilConfigs[j].zs_format, ZS, 0); unsigned stencil_size = SIZE(drilConfigs[j].zs_format, ZS, 1); /* only copy supported configs */ if (pformat != drilConfigs[j].color_format || depth != depth_size || stencil != stencil_size) continue; /* always create single-buffered and double-buffered */ for (unsigned k = 0; k < 2; k++) { configs[c] = mem_dup(&drilConfigs[j], sizeof(drilConfigs[j])); struct gl_config *cfg = configs[c++]; cfg->samples = samples; cfg->doubleBufferMode = k; } break; } last_pformat = pformat; } /* check last format for srgbness too */ if (c && has_srgb) c = add_srgb_config(configs, c, last_pformat, last_start); out_egl: free(eglconfigs); /* don't forget cleanup */ peglTerminate(dpy); out_gbm: if (gbm) gbm_device_destroy(gbm); out: dlclose(egl); if (c) return (void*)configs; free(configs); return NULL; } static __DRIscreen * drilCreateNewScreen(int scrn, int fd, const __DRIextension **loader_extensions, const __DRIextension **driver_extensions, const __DRIconfig ***driver_configs, void *data) { const __DRIconfig **configs = init_dri2_configs(fd); if (!configs && fd == -1) { // otherwise set configs to point to our config list configs = calloc(ARRAY_SIZE(drilConfigs) * 2 + 1, sizeof(void *)); int c = 0; for (int i = 0; i < ARRAY_SIZE(drilConfigs); i++) { /* create normal config */ configs[c++] = mem_dup(&drilConfigs[i], sizeof(drilConfigs[i])); /* create double-buffered config */ configs[c] = mem_dup(&drilConfigs[i], sizeof(drilConfigs[i])); struct gl_config *cfg = (void*)configs[c++]; cfg->doubleBufferMode = 1; } } // outpointer it *driver_configs = configs; // This has to be a separate allocation from the configs. // If we had any additional screen state we'd need to do // something less hacky. return malloc(sizeof(int)); } const __DRIextension *__driDriverExtensions[]; static __DRIscreen * dril2CreateNewScreen(int scrn, int fd, const __DRIextension **extensions, const __DRIconfig ***driver_configs, void *data) { return drilCreateNewScreen(scrn, fd, extensions, __driDriverExtensions, driver_configs, data); } static __DRIscreen * drilSWCreateNewScreen(int scrn, const __DRIextension **extensions, const __DRIconfig ***driver_configs, void *data) { return drilCreateNewScreen(scrn, -1, extensions, __driDriverExtensions, driver_configs, data); } static __DRIscreen * drilSWCreateNewScreen2(int scrn, const __DRIextension **extensions, const __DRIextension **driver_extensions, const __DRIconfig ***driver_configs, void *data) { return drilCreateNewScreen(scrn, -1, extensions, __driDriverExtensions, driver_configs, data); } static int drilSWQueryBufferAge(__DRIdrawable *pdp) { return 0; } static const __DRIswrastExtension drilSWRastExtension = { .base = { __DRI_SWRAST, 5 }, .createNewScreen = drilSWCreateNewScreen, .createNewDrawable = drilCreateNewDrawable, .createNewContextForAPI = drilCreateNewContextForAPI, .createContextAttribs = drilCreateContextAttribs, .createNewScreen2 = drilSWCreateNewScreen2, .queryBufferAge = drilSWQueryBufferAge, }; const __DRIdri2Extension drilDRI2Extension = { .base = { __DRI_DRI2, 5 }, /* these are the methods used by the xserver */ .createNewScreen = dril2CreateNewScreen, .createNewDrawable = drilCreateNewDrawable, .createNewContext = drilCreateNewContext, .createContextAttribs = drilCreateContextAttribs, }; const __DRIextension *__driDriverExtensions[] = { &drilCoreExtension.base, &drilSWRastExtension.base, &drilDRI2Extension.base, NULL }; #include "util/detect_os.h" #include "target-helpers/drm_helper.h" #include "target-helpers/sw_helper.h" #define DEFINE_LOADER_DRM_ENTRYPOINT(drivername) \ const __DRIextension **__driDriverGetExtensions_##drivername(void); \ PUBLIC const __DRIextension **__driDriverGetExtensions_##drivername(void) \ { \ return __driDriverExtensions; \ } const __DRIextension **__driDriverGetExtensions_swrast(void); PUBLIC const __DRIextension **__driDriverGetExtensions_swrast(void) { return __driDriverExtensions; } const __DRIextension **__driDriverGetExtensions_kms_swrast(void); PUBLIC const __DRIextension **__driDriverGetExtensions_kms_swrast(void) { return __driDriverExtensions; } DEFINE_LOADER_DRM_ENTRYPOINT(i915) DEFINE_LOADER_DRM_ENTRYPOINT(iris) DEFINE_LOADER_DRM_ENTRYPOINT(crocus) DEFINE_LOADER_DRM_ENTRYPOINT(nouveau) DEFINE_LOADER_DRM_ENTRYPOINT(r300) DEFINE_LOADER_DRM_ENTRYPOINT(r600) DEFINE_LOADER_DRM_ENTRYPOINT(radeonsi) DEFINE_LOADER_DRM_ENTRYPOINT(vmwgfx) DEFINE_LOADER_DRM_ENTRYPOINT(msm) DEFINE_LOADER_DRM_ENTRYPOINT(kgsl) DEFINE_LOADER_DRM_ENTRYPOINT(virtio_gpu) DEFINE_LOADER_DRM_ENTRYPOINT(v3d) DEFINE_LOADER_DRM_ENTRYPOINT(vc4) DEFINE_LOADER_DRM_ENTRYPOINT(panfrost) DEFINE_LOADER_DRM_ENTRYPOINT(panthor) DEFINE_LOADER_DRM_ENTRYPOINT(asahi) DEFINE_LOADER_DRM_ENTRYPOINT(etnaviv) DEFINE_LOADER_DRM_ENTRYPOINT(tegra) DEFINE_LOADER_DRM_ENTRYPOINT(armada_drm) DEFINE_LOADER_DRM_ENTRYPOINT(exynos) DEFINE_LOADER_DRM_ENTRYPOINT(gm12u320) DEFINE_LOADER_DRM_ENTRYPOINT(hdlcd) DEFINE_LOADER_DRM_ENTRYPOINT(hx8357d) DEFINE_LOADER_DRM_ENTRYPOINT(ili9163) DEFINE_LOADER_DRM_ENTRYPOINT(ili9225) DEFINE_LOADER_DRM_ENTRYPOINT(ili9341) DEFINE_LOADER_DRM_ENTRYPOINT(ili9486) DEFINE_LOADER_DRM_ENTRYPOINT(imx_drm) DEFINE_LOADER_DRM_ENTRYPOINT(imx_dcss) DEFINE_LOADER_DRM_ENTRYPOINT(imx_lcdif) DEFINE_LOADER_DRM_ENTRYPOINT(ingenic_drm) DEFINE_LOADER_DRM_ENTRYPOINT(kirin) DEFINE_LOADER_DRM_ENTRYPOINT(komeda) DEFINE_LOADER_DRM_ENTRYPOINT(mali_dp) DEFINE_LOADER_DRM_ENTRYPOINT(mcde) DEFINE_LOADER_DRM_ENTRYPOINT(mediatek) DEFINE_LOADER_DRM_ENTRYPOINT(meson) DEFINE_LOADER_DRM_ENTRYPOINT(mi0283qt) DEFINE_LOADER_DRM_ENTRYPOINT(mxsfb_drm) DEFINE_LOADER_DRM_ENTRYPOINT(panel_mipi_dbi) DEFINE_LOADER_DRM_ENTRYPOINT(pl111) DEFINE_LOADER_DRM_ENTRYPOINT(rcar_du) DEFINE_LOADER_DRM_ENTRYPOINT(repaper) DEFINE_LOADER_DRM_ENTRYPOINT(rockchip) DEFINE_LOADER_DRM_ENTRYPOINT(rzg2l_du) DEFINE_LOADER_DRM_ENTRYPOINT(ssd130x) DEFINE_LOADER_DRM_ENTRYPOINT(st7586) DEFINE_LOADER_DRM_ENTRYPOINT(st7735r) DEFINE_LOADER_DRM_ENTRYPOINT(sti) DEFINE_LOADER_DRM_ENTRYPOINT(stm) DEFINE_LOADER_DRM_ENTRYPOINT(sun4i_drm) DEFINE_LOADER_DRM_ENTRYPOINT(udl) DEFINE_LOADER_DRM_ENTRYPOINT(zynqmp_dpsub) DEFINE_LOADER_DRM_ENTRYPOINT(lima) DEFINE_LOADER_DRM_ENTRYPOINT(d3d12) DEFINE_LOADER_DRM_ENTRYPOINT(zink)