2018-04-20 20:38:03 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2011-2014 Intel Corporation
|
|
|
|
* Copyright © 2017 Red Hat Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
|
* obtaining a copy of this software and associated documentation
|
|
|
|
* files (the "Software"), to deal in the Software without
|
|
|
|
* restriction, including without limitation the rights to use, copy,
|
|
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including
|
|
|
|
* the next paragraph) shall be included in all copies or substantial
|
|
|
|
* portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Lyude Paul <lyude@redhat.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xwayland.h"
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <xf86drm.h>
|
|
|
|
#include <drm_fourcc.h>
|
|
|
|
|
|
|
|
#define MESA_EGL_NO_X11_HEADERS
|
|
|
|
#include <gbm.h>
|
|
|
|
#include <glamor_egl.h>
|
|
|
|
|
|
|
|
#include <glamor.h>
|
|
|
|
#include <glamor_context.h>
|
|
|
|
#include <dri3.h>
|
|
|
|
#include "drm-client-protocol.h"
|
|
|
|
|
|
|
|
struct xwl_gbm_private {
|
|
|
|
char *device_name;
|
|
|
|
struct gbm_device *gbm;
|
|
|
|
struct wl_drm *drm;
|
|
|
|
struct zwp_linux_dmabuf_v1 *dmabuf;
|
|
|
|
int drm_fd;
|
|
|
|
int fd_render_node;
|
|
|
|
Bool drm_authenticated;
|
|
|
|
uint32_t capabilities;
|
|
|
|
int dmabuf_capable;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct xwl_pixmap {
|
|
|
|
struct wl_buffer *buffer;
|
|
|
|
EGLImage image;
|
|
|
|
unsigned int texture;
|
|
|
|
struct gbm_bo *bo;
|
|
|
|
};
|
|
|
|
|
|
|
|
static DevPrivateKeyRec xwl_gbm_private_key;
|
|
|
|
static DevPrivateKeyRec xwl_auth_state_private_key;
|
|
|
|
|
|
|
|
static inline struct xwl_gbm_private *
|
|
|
|
xwl_gbm_get(struct xwl_screen *xwl_screen)
|
|
|
|
{
|
|
|
|
return dixLookupPrivate(&xwl_screen->screen->devPrivates,
|
|
|
|
&xwl_gbm_private_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
gbm_format_for_depth(int depth)
|
|
|
|
{
|
|
|
|
switch (depth) {
|
|
|
|
case 16:
|
|
|
|
return GBM_FORMAT_RGB565;
|
|
|
|
case 24:
|
|
|
|
return GBM_FORMAT_XRGB8888;
|
|
|
|
case 30:
|
|
|
|
return GBM_FORMAT_ARGB2101010;
|
|
|
|
default:
|
|
|
|
ErrorF("unexpected depth: %d\n", depth);
|
|
|
|
case 32:
|
|
|
|
return GBM_FORMAT_ARGB8888;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
wl_drm_format_for_depth(int depth)
|
|
|
|
{
|
|
|
|
switch (depth) {
|
|
|
|
case 15:
|
|
|
|
return WL_DRM_FORMAT_XRGB1555;
|
|
|
|
case 16:
|
|
|
|
return WL_DRM_FORMAT_RGB565;
|
|
|
|
case 24:
|
|
|
|
return WL_DRM_FORMAT_XRGB8888;
|
|
|
|
case 30:
|
|
|
|
return WL_DRM_FORMAT_ARGB2101010;
|
|
|
|
default:
|
|
|
|
ErrorF("unexpected depth: %d\n", depth);
|
|
|
|
case 32:
|
|
|
|
return WL_DRM_FORMAT_ARGB8888;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
is_fd_render_node(int fd)
|
|
|
|
{
|
|
|
|
struct stat render;
|
|
|
|
|
|
|
|
if (fstat(fd, &render))
|
|
|
|
return 0;
|
|
|
|
if (!S_ISCHR(render.st_mode))
|
|
|
|
return 0;
|
|
|
|
if (render.st_rdev & 0x80)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PixmapPtr
|
|
|
|
xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
|
|
|
|
int depth)
|
|
|
|
{
|
|
|
|
PixmapPtr pixmap;
|
|
|
|
struct xwl_pixmap *xwl_pixmap;
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
|
|
|
|
xwl_pixmap = malloc(sizeof *xwl_pixmap);
|
|
|
|
if (xwl_pixmap == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pixmap = glamor_create_pixmap(screen,
|
|
|
|
gbm_bo_get_width(bo),
|
|
|
|
gbm_bo_get_height(bo),
|
|
|
|
depth,
|
|
|
|
GLAMOR_CREATE_PIXMAP_NO_TEXTURE);
|
|
|
|
if (!pixmap) {
|
|
|
|
free(xwl_pixmap);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
xwayland: Add glamor egl_backend for EGLStreams
This adds initial support for displaying Xwayland applications through
the use of EGLStreams and nvidia's custom wayland protocol by adding
another egl_backend driver. This also adds some additional egl_backend
hooks that are required to make things work properly.
EGLStreams work a lot differently then the traditional way of handling
buffers with wayland. Unfortunately, there are also a LOT of various
pitfalls baked into it's design that need to be explained.
This has a very large and unfortunate implication: direct rendering is,
for the time being at least, impossible to do through EGLStreams. The
main reason being that the EGLStream spec mandates that we lose the
entire color buffer contents with each eglSwapBuffers(), which goes
against X's requirement of not losing data with pixmaps. no way to use
an allocated EGLSurface as the storage for glamor rendering like we do
with GBM, we have to rely on blitting each pixmap to it's respective
EGLSurface producer each frame. In order to pull this off, we add two
different additional egl_backend hooks that GBM opts out of
implementing:
- egl_backend.allow_commits for holding off displaying any EGLStream
backed pixmaps until the point where it's stream is completely
initialized and ready for use
- egl_backend.post_damage for blitting the content of the EGLStream
surface producer before Xwayland actually damages and commits the
wl_surface to the screen.
The other big pitfall here is that using nvidia's wayland-eglstreams
helper library is also not possible for the most part. All of it's API
for creating and destroying streams rely on being able to perform a
roundtrip in order to bring each stream to completion since the wayland
compositor must perform it's job of connecting a consumer to each
EGLstream. Because Xwayland has to potentially handle both responding to
the wayland compositor and it's own X clients, the situation of the
wayland compositor being one of our X clients must be considered. If we
perform a roundtrip with the Wayland compositor, it's possible that the
wayland compositor might currently be connected to us as an X client and
thus hang while both Xwayland and the wayland compositor await responses
from eachother. To avoid this, we work directly with the wayland
protocol and use wl_display_sync() events along with release() events to
set up and destroy EGLStreams asynchronously alongside handling X
clients.
Additionally, since setting up EGLStreams is not an atomic operation we
have to take into consideration the fact that an EGLStream can
potentially be created in response to a window resize, then immediately
deleted due to another pending window resize in the same X client's
pending reqests before Xwayland hits the part of it's event loop where
we read from the wayland compositor. To make this even more painful, we
also have to take into consideration that since EGLStreams are not
atomic that it's possible we could delete wayland resources for an
EGLStream before the compositor even finishes using them and thus run
into errors. So, we use quite a bit of tracking logic to keep EGLStream
objects alive until we know the compositor isn't using them (even if
this means the stream outlives the pixmap it backed).
While the default backend for glamor remains GBM, this patch exists for
users who have had to deal with the reprecussion of their GPU
manufacturers ignoring the advice of upstream and the standardization of
GBM across most major GPU manufacturers. It is not intended to be a
final solution to the GBM debate, but merely a baindaid so our users
don't have to suffer from the consequences of companies avoiding working
upstream. New drivers are strongly encouraged not to use this as a
backend, and use GBM like everyone else. We even spit this out as an
error from Xwayland when using the eglstream backend.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Acked-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
2018-04-20 20:38:05 +02:00
|
|
|
xwl_glamor_egl_make_current(xwl_screen);
|
2018-04-20 20:38:03 +02:00
|
|
|
xwl_pixmap->bo = bo;
|
|
|
|
xwl_pixmap->buffer = NULL;
|
|
|
|
xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
|
|
|
|
xwl_screen->egl_context,
|
|
|
|
EGL_NATIVE_PIXMAP_KHR,
|
|
|
|
xwl_pixmap->bo, NULL);
|
|
|
|
|
|
|
|
glGenTextures(1, &xwl_pixmap->texture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
|
|
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
xwl_pixmap_set_private(pixmap, xwl_pixmap);
|
|
|
|
|
|
|
|
glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture);
|
|
|
|
glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
|
|
|
|
|
|
|
|
return pixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PixmapPtr
|
|
|
|
xwl_glamor_gbm_create_pixmap(ScreenPtr screen,
|
|
|
|
int width, int height, int depth,
|
|
|
|
unsigned int hint)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
struct gbm_bo *bo;
|
|
|
|
|
|
|
|
if (width > 0 && height > 0 && depth >= 15 &&
|
|
|
|
(hint == 0 ||
|
|
|
|
hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP ||
|
|
|
|
hint == CREATE_PIXMAP_USAGE_SHARED)) {
|
|
|
|
uint32_t format = gbm_format_for_depth(depth);
|
|
|
|
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
|
|
if (xwl_gbm->dmabuf_capable) {
|
|
|
|
uint32_t num_modifiers;
|
|
|
|
uint64_t *modifiers = NULL;
|
|
|
|
|
|
|
|
glamor_get_modifiers(screen, format, &num_modifiers, &modifiers);
|
|
|
|
bo = gbm_bo_create_with_modifiers(xwl_gbm->gbm, width, height,
|
|
|
|
format, modifiers, num_modifiers);
|
|
|
|
free(modifiers);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
bo = gbm_bo_create(xwl_gbm->gbm, width, height, format,
|
|
|
|
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bo)
|
|
|
|
return xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
|
|
|
|
}
|
|
|
|
|
|
|
|
return glamor_create_pixmap(screen, width, height, depth, hint);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
xwl_glamor_gbm_destroy_pixmap(PixmapPtr pixmap)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
|
|
|
|
|
|
if (xwl_pixmap && pixmap->refcnt == 1) {
|
|
|
|
if (xwl_pixmap->buffer)
|
|
|
|
wl_buffer_destroy(xwl_pixmap->buffer);
|
|
|
|
|
|
|
|
eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
|
|
|
|
if (xwl_pixmap->bo)
|
|
|
|
gbm_bo_destroy(xwl_pixmap->bo);
|
|
|
|
free(xwl_pixmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
return glamor_destroy_pixmap(pixmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct wl_buffer *
|
|
|
|
xwl_glamor_gbm_get_wl_buffer_for_pixmap(PixmapPtr pixmap,
|
|
|
|
unsigned short width,
|
|
|
|
unsigned short height,
|
|
|
|
Bool *created)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
int prime_fd;
|
|
|
|
int num_planes;
|
|
|
|
uint32_t strides[4];
|
|
|
|
uint32_t offsets[4];
|
|
|
|
uint64_t modifier;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (xwl_pixmap->buffer) {
|
|
|
|
/* Buffer already exists. Return it and inform caller if interested. */
|
|
|
|
if (created)
|
|
|
|
*created = FALSE;
|
|
|
|
return xwl_pixmap->buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Buffer does not exist yet. Create now and inform caller if interested. */
|
|
|
|
if (created)
|
|
|
|
*created = TRUE;
|
|
|
|
|
|
|
|
if (!xwl_pixmap->bo)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prime_fd = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
|
|
if (prime_fd == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
|
|
num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
|
|
|
|
modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
|
|
|
|
for (i = 0; i < num_planes; i++) {
|
|
|
|
strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
|
|
|
|
offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
num_planes = 1;
|
|
|
|
modifier = DRM_FORMAT_MOD_INVALID;
|
|
|
|
strides[0] = gbm_go_get_stride(xwl_pixmap->bo);
|
|
|
|
offsets[0] = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (xwl_gbm->dmabuf && modifier != DRM_FORMAT_MOD_INVALID) {
|
|
|
|
struct zwp_linux_buffer_params_v1 *params;
|
|
|
|
|
|
|
|
params = zwp_linux_dmabuf_v1_create_params(xwl_gbm->dmabuf);
|
|
|
|
for (i = 0; i < num_planes; i++) {
|
|
|
|
zwp_linux_buffer_params_v1_add(params, prime_fd, i,
|
|
|
|
offsets[i], strides[i],
|
|
|
|
modifier >> 32, modifier & 0xffffffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_pixmap->buffer =
|
|
|
|
zwp_linux_buffer_params_v1_create_immed(params, width, height,
|
|
|
|
wl_drm_format_for_depth(pixmap->drawable.depth),
|
|
|
|
0);
|
|
|
|
zwp_linux_buffer_params_v1_destroy(params);
|
|
|
|
} else if (num_planes == 1) {
|
|
|
|
xwl_pixmap->buffer =
|
|
|
|
wl_drm_create_prime_buffer(xwl_gbm->drm, prime_fd, width, height,
|
|
|
|
wl_drm_format_for_depth(pixmap->drawable.depth),
|
|
|
|
0, gbm_bo_get_stride(xwl_pixmap->bo),
|
|
|
|
0, 0,
|
|
|
|
0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(prime_fd);
|
|
|
|
return xwl_pixmap->buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_glamor_gbm_cleanup(struct xwl_screen *xwl_screen)
|
|
|
|
{
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
|
|
|
|
if (xwl_gbm->device_name)
|
|
|
|
free(xwl_gbm->device_name);
|
|
|
|
if (xwl_gbm->drm_fd)
|
|
|
|
close(xwl_gbm->drm_fd);
|
|
|
|
if (xwl_gbm->drm)
|
|
|
|
wl_drm_destroy(xwl_gbm->drm);
|
|
|
|
if (xwl_gbm->gbm)
|
|
|
|
gbm_device_destroy(xwl_gbm->gbm);
|
|
|
|
|
|
|
|
free(xwl_gbm);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct xwl_auth_state {
|
|
|
|
int fd;
|
|
|
|
ClientPtr client;
|
|
|
|
struct wl_callback *callback;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state)
|
|
|
|
{
|
|
|
|
dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL);
|
|
|
|
if (state) {
|
|
|
|
wl_callback_destroy(state->callback);
|
|
|
|
free(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data)
|
|
|
|
{
|
|
|
|
NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
|
|
|
|
ClientPtr pClient = clientinfo->client;
|
|
|
|
struct xwl_auth_state *state;
|
|
|
|
|
|
|
|
switch (pClient->clientState) {
|
|
|
|
case ClientStateGone:
|
|
|
|
case ClientStateRetained:
|
|
|
|
state = dixLookupPrivate(&pClient->devPrivates,
|
|
|
|
&xwl_auth_state_private_key);
|
|
|
|
free_xwl_auth_state(pClient, state);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
|
|
|
|
{
|
|
|
|
struct xwl_auth_state *state = data;
|
|
|
|
ClientPtr client = state->client;
|
|
|
|
|
|
|
|
/* if the client is gone, the callback is cancelled so it's safe to
|
|
|
|
* assume the client is still in ClientStateRunning at this point...
|
|
|
|
*/
|
|
|
|
dri3_send_open_reply(client, state->fd);
|
|
|
|
AttendClient(client);
|
|
|
|
free_xwl_auth_state(client, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wl_callback_listener sync_listener = {
|
|
|
|
sync_callback
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
xwl_dri3_open_client(ClientPtr client,
|
|
|
|
ScreenPtr screen,
|
|
|
|
RRProviderPtr provider,
|
|
|
|
int *pfd)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
struct xwl_auth_state *state;
|
|
|
|
drm_magic_t magic;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
|
|
|
|
if (fd < 0)
|
|
|
|
return BadAlloc;
|
|
|
|
if (xwl_gbm->fd_render_node) {
|
|
|
|
*pfd = fd;
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = malloc(sizeof *state);
|
|
|
|
if (state == NULL) {
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->client = client;
|
|
|
|
state->fd = fd;
|
|
|
|
|
|
|
|
if (drmGetMagic(state->fd, &magic) < 0) {
|
|
|
|
close(state->fd);
|
|
|
|
free(state);
|
|
|
|
return BadMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_drm_authenticate(xwl_gbm->drm, magic);
|
|
|
|
state->callback = wl_display_sync(xwl_screen->display);
|
|
|
|
wl_callback_add_listener(state->callback, &sync_listener, state);
|
|
|
|
dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state);
|
|
|
|
|
|
|
|
IgnoreClient(client);
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
_X_EXPORT PixmapPtr
|
|
|
|
glamor_pixmap_from_fds(ScreenPtr screen, CARD8 num_fds, const int *fds,
|
|
|
|
CARD16 width, CARD16 height,
|
|
|
|
const CARD32 *strides, const CARD32 *offsets,
|
|
|
|
CARD8 depth, CARD8 bpp, uint64_t modifier)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
struct gbm_bo *bo = NULL;
|
|
|
|
PixmapPtr pixmap;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (width == 0 || height == 0 || num_fds == 0 ||
|
|
|
|
depth < 15 || bpp != BitsPerPixel(depth) ||
|
|
|
|
strides[0] < width * bpp / 8)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (xwl_gbm->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
|
|
struct gbm_import_fd_modifier_data data;
|
|
|
|
|
|
|
|
data.width = width;
|
|
|
|
data.height = height;
|
|
|
|
data.num_fds = num_fds;
|
|
|
|
data.format = gbm_format_for_depth(depth);
|
|
|
|
data.modifier = modifier;
|
|
|
|
for (i = 0; i < num_fds; i++) {
|
|
|
|
data.fds[i] = fds[i];
|
|
|
|
data.strides[i] = strides[i];
|
|
|
|
data.offsets[i] = offsets[i];
|
|
|
|
}
|
|
|
|
bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0);
|
|
|
|
#endif
|
|
|
|
} else if (num_fds == 1) {
|
|
|
|
struct gbm_import_fd_data data;
|
|
|
|
|
|
|
|
data.fd = fds[0];
|
|
|
|
data.width = width;
|
|
|
|
data.height = height;
|
|
|
|
data.stride = strides[0];
|
|
|
|
data.format = gbm_format_for_depth(depth);
|
|
|
|
bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD, &data,
|
|
|
|
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
|
|
|
} else {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bo == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
|
|
|
|
if (pixmap == NULL) {
|
|
|
|
gbm_bo_destroy(bo);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixmap;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
_X_EXPORT int
|
|
|
|
glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
|
|
|
|
uint32_t *strides, uint32_t *offsets,
|
|
|
|
uint64_t *modifier)
|
|
|
|
{
|
|
|
|
struct xwl_pixmap *xwl_pixmap;
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
|
|
uint32_t num_fds;
|
|
|
|
int i;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
|
|
|
|
|
|
if (!xwl_pixmap->bo)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
|
|
num_fds = gbm_bo_get_plane_count(xwl_pixmap->bo);
|
|
|
|
*modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
|
|
|
|
|
|
|
|
for (i = 0; i < num_fds; i++) {
|
|
|
|
fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
|
|
strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
|
|
|
|
offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return num_fds;
|
|
|
|
#else
|
|
|
|
*modifier = DRM_FORMAT_MOD_INVALID;
|
|
|
|
fds[0] = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
|
|
strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
|
|
|
|
offsets[0] = 0;
|
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
_X_EXPORT Bool
|
|
|
|
glamor_get_formats(ScreenPtr screen,
|
|
|
|
CARD32 *num_formats, CARD32 **formats)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Explicitly zero the count as the caller may ignore the return value */
|
|
|
|
*num_formats = 0;
|
|
|
|
|
|
|
|
if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (xwl_screen->num_formats == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
*formats = calloc(xwl_screen->num_formats, sizeof(CARD32));
|
|
|
|
if (*formats == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < xwl_screen->num_formats; i++)
|
|
|
|
(*formats)[i] = xwl_screen->formats[i].format;
|
|
|
|
*num_formats = xwl_screen->num_formats;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
_X_EXPORT Bool
|
|
|
|
glamor_get_modifiers(ScreenPtr screen, CARD32 format,
|
|
|
|
CARD32 *num_modifiers, uint64_t **modifiers)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
struct xwl_format *xwl_format = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Explicitly zero the count as the caller may ignore the return value */
|
|
|
|
*num_modifiers = 0;
|
|
|
|
|
|
|
|
if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (xwl_screen->num_formats == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
for (i = 0; i < xwl_screen->num_formats; i++) {
|
|
|
|
if (xwl_screen->formats[i].format == format) {
|
|
|
|
xwl_format = &xwl_screen->formats[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!xwl_format)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
*modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t));
|
|
|
|
if (*modifiers == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < xwl_format->num_modifiers; i++)
|
|
|
|
(*modifiers)[i] = xwl_format->modifiers[i];
|
|
|
|
*num_modifiers = xwl_format->num_modifiers;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const dri3_screen_info_rec xwl_dri3_info = {
|
|
|
|
.version = 2,
|
|
|
|
.open = NULL,
|
|
|
|
.pixmap_from_fds = glamor_pixmap_from_fds,
|
|
|
|
.fds_from_pixmap = glamor_fds_from_pixmap,
|
|
|
|
.open_client = xwl_dri3_open_client,
|
|
|
|
.get_formats = glamor_get_formats,
|
|
|
|
.get_modifiers = glamor_get_modifiers,
|
|
|
|
.get_drawable_modifiers = glamor_get_drawable_modifiers,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = data;
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
drm_magic_t magic;
|
|
|
|
|
|
|
|
xwl_gbm->device_name = strdup(device);
|
|
|
|
if (!xwl_gbm->device_name) {
|
|
|
|
xwl_glamor_gbm_cleanup(xwl_screen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_gbm->drm_fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
|
|
|
|
if (xwl_gbm->drm_fd == -1) {
|
|
|
|
ErrorF("wayland-egl: could not open %s (%s)\n",
|
|
|
|
xwl_gbm->device_name, strerror(errno));
|
|
|
|
xwl_glamor_gbm_cleanup(xwl_screen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_fd_render_node(xwl_gbm->drm_fd)) {
|
|
|
|
xwl_gbm->fd_render_node = 1;
|
|
|
|
xwl_screen->expecting_event--;
|
|
|
|
} else {
|
|
|
|
drmGetMagic(xwl_gbm->drm_fd, &magic);
|
|
|
|
wl_drm_authenticate(xwl_gbm->drm, magic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_drm_handle_authenticated(void *data, struct wl_drm *drm)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = data;
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
|
|
|
|
xwl_gbm->drm_authenticated = TRUE;
|
|
|
|
xwl_screen->expecting_event--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value)
|
|
|
|
{
|
|
|
|
xwl_gbm_get(data)->capabilities = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wl_drm_listener xwl_drm_listener = {
|
|
|
|
xwl_drm_handle_device,
|
|
|
|
xwl_drm_handle_format,
|
|
|
|
xwl_drm_handle_authenticated,
|
|
|
|
xwl_drm_handle_capabilities
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
|
|
|
|
uint32_t format)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
|
|
|
|
uint32_t format, uint32_t modifier_hi,
|
|
|
|
uint32_t modifier_lo)
|
|
|
|
{
|
|
|
|
struct xwl_screen *xwl_screen = data;
|
|
|
|
struct xwl_format *xwl_format = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < xwl_screen->num_formats; i++) {
|
|
|
|
if (xwl_screen->formats[i].format == format) {
|
|
|
|
xwl_format = &xwl_screen->formats[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xwl_format == NULL) {
|
|
|
|
xwl_screen->num_formats++;
|
|
|
|
xwl_screen->formats = realloc(xwl_screen->formats,
|
|
|
|
xwl_screen->num_formats * sizeof(*xwl_format));
|
|
|
|
if (!xwl_screen->formats)
|
|
|
|
return;
|
|
|
|
xwl_format = &xwl_screen->formats[xwl_screen->num_formats - 1];
|
|
|
|
xwl_format->format = format;
|
|
|
|
xwl_format->num_modifiers = 0;
|
|
|
|
xwl_format->modifiers = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_format->num_modifiers++;
|
|
|
|
xwl_format->modifiers = realloc(xwl_format->modifiers,
|
|
|
|
xwl_format->num_modifiers * sizeof(uint64_t));
|
|
|
|
if (!xwl_format->modifiers)
|
|
|
|
return;
|
|
|
|
xwl_format->modifiers[xwl_format->num_modifiers - 1] = (uint64_t) modifier_lo;
|
|
|
|
xwl_format->modifiers[xwl_format->num_modifiers - 1] |= (uint64_t) modifier_hi << 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = {
|
|
|
|
.format = xwl_dmabuf_handle_format,
|
|
|
|
.modifier = xwl_dmabuf_handle_modifier
|
|
|
|
};
|
|
|
|
|
|
|
|
Bool
|
|
|
|
xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
|
|
|
|
uint32_t id, uint32_t version)
|
|
|
|
{
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
|
|
|
|
if (version < 2)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
xwl_gbm->drm =
|
|
|
|
wl_registry_bind(xwl_screen->registry, id, &wl_drm_interface, 2);
|
|
|
|
wl_drm_add_listener(xwl_gbm->drm, &xwl_drm_listener, xwl_screen);
|
|
|
|
xwl_screen->expecting_event++;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bool
|
|
|
|
xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
|
|
|
|
uint32_t id, uint32_t version)
|
|
|
|
{
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
|
|
|
|
if (version < 3)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
xwl_gbm->dmabuf =
|
|
|
|
wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, 3);
|
|
|
|
zwp_linux_dmabuf_v1_add_listener(xwl_gbm->dmabuf, &xwl_dmabuf_listener, xwl_screen);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xwl_glamor_gbm_init_wl_registry(struct xwl_screen *xwl_screen,
|
|
|
|
struct wl_registry *wl_registry,
|
|
|
|
const char *name,
|
|
|
|
uint32_t id, uint32_t version)
|
|
|
|
{
|
|
|
|
if (strcmp(name, "wl_drm") == 0)
|
|
|
|
xwl_screen_set_drm_interface(xwl_screen, id, version);
|
|
|
|
else if (strcmp(name, "zwp_linux_dmabuf_v1") == 0)
|
|
|
|
xwl_screen_set_dmabuf_interface(xwl_screen, id, version);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
|
|
|
|
{
|
|
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
EGLint major, minor;
|
|
|
|
Bool egl_initialized = FALSE;
|
|
|
|
static const EGLint config_attribs_core[] = {
|
|
|
|
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
|
|
|
|
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
|
|
|
|
EGL_CONTEXT_MAJOR_VERSION_KHR,
|
|
|
|
GLAMOR_GL_CORE_VER_MAJOR,
|
|
|
|
EGL_CONTEXT_MINOR_VERSION_KHR,
|
|
|
|
GLAMOR_GL_CORE_VER_MINOR,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!xwl_gbm->fd_render_node && !xwl_gbm->drm_authenticated) {
|
|
|
|
ErrorF("Failed to get wl_drm, disabling Glamor and DRI3\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_gbm->gbm = gbm_create_device(xwl_gbm->drm_fd);
|
|
|
|
if (!xwl_gbm->gbm) {
|
|
|
|
ErrorF("couldn't create gbm device\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
|
|
|
|
xwl_gbm->gbm);
|
|
|
|
if (xwl_screen->egl_display == EGL_NO_DISPLAY) {
|
|
|
|
ErrorF("glamor_egl_get_display() failed\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
egl_initialized = eglInitialize(xwl_screen->egl_display, &major, &minor);
|
|
|
|
if (!egl_initialized) {
|
|
|
|
ErrorF("eglInitialize() failed\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
|
|
|
|
|
|
xwl_screen->egl_context = eglCreateContext(
|
|
|
|
xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, config_attribs_core);
|
|
|
|
if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
|
|
|
|
xwl_screen->egl_context = eglCreateContext(
|
|
|
|
xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
|
|
|
|
ErrorF("Failed to create EGL context\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!eglMakeCurrent(xwl_screen->egl_display,
|
|
|
|
EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
|
|
xwl_screen->egl_context)) {
|
|
|
|
ErrorF("Failed to make EGL context current\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!epoxy_has_gl_extension("GL_OES_EGL_image"))
|
|
|
|
ErrorF("GL_OES_EGL_image not available\n");
|
|
|
|
|
|
|
|
if (epoxy_has_egl_extension(xwl_screen->egl_display,
|
|
|
|
"EXT_image_dma_buf_import") &&
|
|
|
|
epoxy_has_egl_extension(xwl_screen->egl_display,
|
|
|
|
"EXT_image_dma_buf_import_modifiers"))
|
|
|
|
xwl_gbm->dmabuf_capable = TRUE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
if (xwl_screen->egl_context != EGL_NO_CONTEXT) {
|
|
|
|
eglDestroyContext(xwl_screen->egl_display, xwl_screen->egl_context);
|
|
|
|
xwl_screen->egl_context = EGL_NO_CONTEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
|
|
|
|
eglTerminate(xwl_screen->egl_display);
|
|
|
|
xwl_screen->egl_display = EGL_NO_DISPLAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_glamor_gbm_cleanup(xwl_screen);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen)
|
|
|
|
{
|
|
|
|
if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
|
|
|
|
ErrorF("Failed to initialize dri3\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT,
|
|
|
|
0)) {
|
|
|
|
ErrorF("Failed to register private key\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback,
|
|
|
|
NULL)) {
|
|
|
|
ErrorF("Failed to add client state callback\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
xwl_screen->screen->CreatePixmap = xwl_glamor_gbm_create_pixmap;
|
|
|
|
xwl_screen->screen->DestroyPixmap = xwl_glamor_gbm_destroy_pixmap;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
xwl_glamor_gbm_cleanup(xwl_screen);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bool
|
|
|
|
xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
|
|
|
|
{
|
|
|
|
struct xwl_gbm_private *xwl_gbm;
|
|
|
|
|
|
|
|
if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
xwl_gbm = calloc(sizeof(*xwl_gbm), 1);
|
|
|
|
if (!xwl_gbm) {
|
|
|
|
ErrorF("glamor: Not enough memory to setup GBM, disabling\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key,
|
|
|
|
xwl_gbm);
|
|
|
|
|
|
|
|
xwl_screen->egl_backend.init_wl_registry = xwl_glamor_gbm_init_wl_registry;
|
|
|
|
xwl_screen->egl_backend.init_egl = xwl_glamor_gbm_init_egl;
|
|
|
|
xwl_screen->egl_backend.init_screen = xwl_glamor_gbm_init_screen;
|
|
|
|
xwl_screen->egl_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|