buildroot/package/mesa3d/0027-egl-add-null-platform....

1486 lines
44 KiB
Diff

From 6eec694571ed99854e69d1b5403cc19c7144fa5d Mon Sep 17 00:00:00 2001
From: Imagination Technologies <powervr@imgtec.com>
Date: Sun, 5 Jun 2016 12:04:40 +0100
Subject: [PATCH 027/168] egl: add "null" platform
---
meson.build | 10 +-
meson_options.txt | 2 +-
src/egl/drivers/dri2/egl_dri2.c | 11 +-
src/egl/drivers/dri2/egl_dri2.h | 59 +-
src/egl/drivers/dri2/platform_null.c | 1179 ++++++++++++++++++++++++++
src/egl/main/eglapi.c | 5 +-
src/egl/main/egldisplay.c | 1 +
src/egl/main/egldisplay.h | 1 +
src/egl/meson.build | 5 +
9 files changed, 1264 insertions(+), 9 deletions(-)
create mode 100644 src/egl/drivers/dri2/platform_null.c
diff --git a/meson.build b/meson.build
index 4d250401a74..9fb6dd81d26 100644
--- a/meson.build
+++ b/meson.build
@@ -354,7 +354,7 @@ endforeach
_platforms = get_option('platforms')
if _platforms.contains('auto')
if system_has_kms_drm
- _platforms = ['x11', 'wayland']
+ _platforms = ['x11', 'wayland', 'null']
elif ['darwin', 'cygwin'].contains(host_machine.system())
_platforms = ['x11']
elif ['haiku'].contains(host_machine.system())
@@ -372,6 +372,7 @@ with_platform_x11 = _platforms.contains('x11')
with_platform_wayland = _platforms.contains('wayland')
with_platform_haiku = _platforms.contains('haiku')
with_platform_windows = _platforms.contains('windows')
+with_platform_null = _platforms.contains('null')
with_glx = get_option('glx')
if with_glx == 'auto'
@@ -1003,6 +1004,10 @@ if with_platform_android
]
endif
+if with_platform_null
+ pre_args += '-DHAVE_NULL_PLATFORM'
+endif
+
prog_python = import('python').find_installation('python3')
has_mako = run_command(
prog_python, '-c',
@@ -1722,7 +1727,8 @@ with_gallium_drisw_kms = false
dep_libdrm = dependency(
'libdrm', version : '>=' + _drm_ver,
# GNU/Hurd includes egl_dri2, without drm.
- required : (with_dri2 and host_machine.system() != 'gnu') or with_dri3
+ required : (with_dri2 and host_machine.system() != 'gnu') or with_dri3 or
+ with_platform_null
)
if dep_libdrm.found()
pre_args += '-DHAVE_LIBDRM'
diff --git a/meson_options.txt b/meson_options.txt
index c3f0a0f3889..6fd37bbb100 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -23,7 +23,7 @@ option(
type : 'array',
value : ['auto'],
choices : [
- 'auto', 'x11', 'wayland', 'haiku', 'android', 'windows',
+ 'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', 'null',
],
description : 'window systems to support. If this is set to `auto`, all platforms applicable will be enabled.'
)
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index 5cfce95aa42..6337cb23aa4 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -1281,6 +1281,9 @@ dri2_initialize(_EGLDisplay *disp)
case _EGL_PLATFORM_DEVICE:
ret = dri2_initialize_device(disp);
break;
+ case _EGL_PLATFORM_NULL:
+ ret = dri2_initialize_null(disp);
+ break;
case _EGL_PLATFORM_X11:
case _EGL_PLATFORM_XCB:
ret = dri2_initialize_x11(disp);
@@ -1342,8 +1345,6 @@ dri2_display_destroy(_EGLDisplay *disp)
dri2_dpy->vtbl->close_screen_notify(disp);
dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
}
- if (dri2_dpy->fd >= 0)
- close(dri2_dpy->fd);
/* Don't dlclose the driver when building with the address sanitizer, so you
* get good symbols from the leak reports.
@@ -1369,11 +1370,17 @@ dri2_display_destroy(_EGLDisplay *disp)
case _EGL_PLATFORM_WAYLAND:
dri2_teardown_wayland(dri2_dpy);
break;
+ case _EGL_PLATFORM_NULL:
+ dri2_teardown_null(dri2_dpy);
+ break;
default:
/* TODO: add teardown for other platforms */
break;
}
+ if (dri2_dpy->fd >= 0)
+ close(dri2_dpy->fd);
+
/* The drm platform does not create the screen/driver_configs but reuses
* the ones from the gbm device. As such the gbm itself is responsible
* for the cleanup.
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index afb2d7297ca..89c48905c95 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -78,6 +78,10 @@ struct zwp_linux_dmabuf_feedback_v1;
#endif /* HAVE_ANDROID_PLATFORM */
+#ifdef HAVE_NULL_PLATFORM
+#include <xf86drmMode.h>
+#endif
+
#include "eglconfig.h"
#include "eglcontext.h"
#include "egldevice.h"
@@ -95,6 +99,22 @@ struct zwp_linux_dmabuf_feedback_v1;
struct wl_buffer;
+#ifdef HAVE_NULL_PLATFORM
+struct display_output {
+ bool in_use;
+ uint32_t connector_id;
+ drmModePropertyRes **connector_prop_res;
+ uint32_t crtc_id;
+ drmModePropertyRes **crtc_prop_res;
+ uint32_t plane_id;
+ drmModePropertyRes **plane_prop_res;
+ drmModeModeInfo mode;
+ uint32_t mode_blob_id;
+ unsigned formats;
+ drmModeAtomicReq *atomic_state;
+};
+#endif
+
struct dri2_egl_display_vtbl {
/* mandatory on Wayland, unused otherwise */
int (*authenticate)(_EGLDisplay *disp, uint32_t id);
@@ -296,6 +316,11 @@ struct dri2_egl_display
char *device_name;
#endif
+#ifdef HAVE_NULL_PLATFORM
+ bool atomic_enabled;
+ struct display_output output;
+#endif
+
#ifdef HAVE_ANDROID_PLATFORM
const gralloc_module_t *gralloc;
/* gralloc vendor usage bit for front rendering */
@@ -340,11 +365,14 @@ struct dri2_egl_surface
struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback;
struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback;
bool compositor_using_another_device;
- int format;
bool resized;
bool received_dmabuf_feedback;
#endif
+#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_NULL_PLATFORM)
+ int format;
+#endif
+
#ifdef HAVE_DRM_PLATFORM
struct gbm_dri_surface *gbm_surf;
#endif
@@ -352,12 +380,15 @@ struct dri2_egl_surface
/* EGL-owned buffers */
__DRIbuffer *local_buffers[__DRI_BUFFER_COUNT];
-#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM)
+#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM) || \
+ defined(HAVE_NULL_PLATFORM)
struct {
+#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_NULL_PLATFORM)
+ __DRIimage *dri_image;
+#endif
#ifdef HAVE_WAYLAND_PLATFORM
struct wl_buffer *wl_buffer;
bool wl_release;
- __DRIimage *dri_image;
/* for is_different_gpu case. NULL else */
__DRIimage *linear_copy;
/* for swrast */
@@ -366,6 +397,9 @@ struct dri2_egl_surface
#endif
#ifdef HAVE_DRM_PLATFORM
struct gbm_bo *bo;
+#endif
+#ifdef HAVE_NULL_PLATFORM
+ uint32_t fb_id;
#endif
bool locked;
int age;
@@ -402,6 +436,10 @@ struct dri2_egl_surface
void *swrast_front;
#endif
+#ifdef HAVE_NULL_PLATFORM
+ uint32_t front_fb_id;
+#endif
+
int out_fence_fd;
EGLBoolean enable_out_fence;
@@ -592,6 +630,21 @@ dri2_initialize_android(_EGLDisplay *disp)
EGLBoolean
dri2_initialize_surfaceless(_EGLDisplay *disp);
+#ifdef HAVE_NULL_PLATFORM
+EGLBoolean
+dri2_initialize_null(_EGLDisplay *disp);
+void
+dri2_teardown_null(struct dri2_egl_display *dri2_dpy);
+#else
+static inline EGLBoolean
+dri2_initialize_null(_EGLDisplay *disp)
+{
+ return _eglError(EGL_NOT_INITIALIZED, "Null platform not built");
+}
+static inline void
+dri2_teardown_null(struct dri2_egl_display *dri2_dpy) {}
+#endif
+
EGLBoolean
dri2_initialize_device(_EGLDisplay *disp);
static inline void
diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
new file mode 100644
index 00000000000..fb03ecc36fd
--- /dev/null
+++ b/src/egl/drivers/dri2/platform_null.c
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (c) Imagination Technologies Ltd.
+ *
+ * Parts based on platform_wayland, which has:
+ *
+ * Copyright © 2011-2012 Intel Corporation
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <drm_fourcc.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xf86drm.h>
+
+#include "egl_dri2.h"
+#include "loader.h"
+
+#define NULL_CARD_MINOR_MAX 63U
+
+/*
+ * Need at least version 4 for __DRI_IMAGE_ATTRIB_WIDTH and
+ * __DRI_IMAGE_ATTRIB_HEIGHT
+ */
+#define NULL_IMAGE_EXTENSION_VERSION_MIN 4
+
+struct object_property {
+ uint32_t object_id;
+ uint32_t prop_id;
+ uint64_t prop_value;
+};
+
+#define object_property_set_named(output, object_type, prop_name, value) \
+ { \
+ .object_id = (output)->object_type##_id, \
+ .prop_id = property_id_get_for_name((output)->object_type##_prop_res, \
+ prop_name), \
+ .prop_value = value, \
+ }
+
+/*
+ * The index of entries in this table is used as a bitmask in
+ * dri2_dpy->formats, which tracks the formats supported by the display.
+ */
+static const struct dri2_null_format {
+ uint32_t drm_format;
+ int dri_image_format;
+ int rgba_shifts[4];
+ unsigned int rgba_sizes[4];
+} dri2_null_formats[] = {
+ {
+ .drm_format = DRM_FORMAT_XRGB8888,
+ .dri_image_format = __DRI_IMAGE_FORMAT_XRGB8888,
+ .rgba_shifts = { 16, 8, 0, -1 },
+ .rgba_sizes = { 8, 8, 8, 0 },
+ },
+ {
+ .drm_format = DRM_FORMAT_ARGB8888,
+ .dri_image_format = __DRI_IMAGE_FORMAT_ARGB8888,
+ .rgba_shifts = { 16, 8, 0, 24 },
+ .rgba_sizes = { 8, 8, 8, 8 },
+ },
+ {
+ .drm_format = DRM_FORMAT_RGB565,
+ .dri_image_format = __DRI_IMAGE_FORMAT_RGB565,
+ .rgba_shifts = { 11, 5, 0, -1 },
+ .rgba_sizes = { 5, 6, 5, 0 },
+ },
+};
+
+
+static int
+format_idx_get_from_config(struct dri2_egl_display *dri2_dpy,
+ const __DRIconfig *config)
+{
+ int shifts[4];
+ unsigned int sizes[4];
+
+ dri2_get_shifts_and_sizes(dri2_dpy->core, config, shifts, sizes);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++) {
+ const struct dri2_null_format *format = &dri2_null_formats[i];
+
+ if (shifts[0] == format->rgba_shifts[0] &&
+ shifts[1] == format->rgba_shifts[1] &&
+ shifts[2] == format->rgba_shifts[2] &&
+ shifts[3] == format->rgba_shifts[3] &&
+ sizes[0] == format->rgba_sizes[0] &&
+ sizes[1] == format->rgba_sizes[1] &&
+ sizes[2] == format->rgba_sizes[2] &&
+ sizes[3] == format->rgba_sizes[3]) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int
+format_idx_get_from_dri_image_format(uint32_t dri_image_format)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++)
+ if (dri2_null_formats[i].dri_image_format == dri_image_format)
+ return i;
+
+ return -1;
+}
+
+static int
+format_idx_get_from_drm_format(uint32_t drm_format)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++)
+ if (dri2_null_formats[i].drm_format == drm_format)
+ return i;
+
+ return -1;
+}
+
+static int
+atomic_state_add_object_properties(drmModeAtomicReq *atomic_state,
+ const struct object_property *props,
+ const unsigned prop_count)
+{
+ for (unsigned i = 0; i < prop_count; i++) {
+ int err;
+
+ if (props[i].prop_id == 0)
+ return -EINVAL;
+
+ err = drmModeAtomicAddProperty(atomic_state, props[i].object_id,
+ props[i].prop_id, props[i].prop_value);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+static uint32_t
+property_id_get_for_name(drmModePropertyRes **prop_res, const char *prop_name)
+{
+ if (prop_res)
+ for (unsigned i = 0; prop_res[i]; i++)
+ if (!strcmp(prop_res[i]->name, prop_name))
+ return prop_res[i]->prop_id;
+
+ return 0;
+}
+
+static void
+flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+ unsigned int tv_usec, void *user_data)
+{
+ bool *plocked = user_data;
+
+ if (plocked)
+ *plocked = false;
+}
+
+static bool
+flip_process(int fd)
+{
+ static drmEventContext evctx =
+ {.version = 2, .page_flip_handler = flip_handler};
+ struct pollfd pfd = {.fd = fd, .events = POLLIN};
+ int ret;
+
+ do {
+ ret = poll(&pfd, 1, -1);
+ } while (ret > 0 && pfd.revents != pfd.events);
+
+ if (ret <= 0)
+ return false;
+
+ drmHandleEvent(fd, &evctx);
+
+ return true;
+}
+
+static drmModePropertyRes **
+object_get_property_resources(int fd, uint32_t object_id, uint32_t object_type)
+{
+ drmModeObjectProperties *props;
+ drmModePropertyRes **prop_res;
+
+ props = drmModeObjectGetProperties(fd, object_id, object_type);
+ if (!props)
+ return NULL;
+
+ prop_res = malloc((props->count_props + 1) * sizeof(*prop_res));
+ if (prop_res) {
+ prop_res[props->count_props] = NULL;
+
+ for (unsigned i = 0; i < props->count_props; i++) {
+ prop_res[i] = drmModeGetProperty(fd, props->props[i]);
+ if (!prop_res[i]) {
+ while (i--) {
+ drmModeFreeProperty(prop_res[i]);
+ free(prop_res);
+ prop_res = NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ drmModeFreeObjectProperties(props);
+
+ return prop_res;
+}
+
+static void
+object_free_property_resources(int fd, drmModePropertyRes **prop_res)
+{
+ for (unsigned i = 0; prop_res[i]; i++)
+ drmModeFreeProperty(prop_res[i]);
+ free(prop_res);
+}
+
+static bool
+object_property_value_for_name(int fd, uint32_t object_id, uint32_t object_type,
+ const char *prop_name, uint64_t *value_out)
+{
+ drmModeObjectProperties *plane_props;
+ bool found = false;
+
+ plane_props = drmModeObjectGetProperties(fd, object_id, object_type);
+ if (!plane_props)
+ return false;
+
+ for (unsigned i = 0; i < plane_props->count_props; i++) {
+ drmModePropertyRes *prop;
+
+ prop = drmModeGetProperty(fd, plane_props->props[i]);
+ if (!prop)
+ continue;
+
+ found = !strcmp(prop->name, prop_name);
+ drmModeFreeProperty(prop);
+ if (found) {
+ *value_out = plane_props->prop_values[i];
+ break;
+ }
+ }
+
+ drmModeFreeObjectProperties(plane_props);
+
+ return found;
+}
+
+static int
+connector_choose_mode(drmModeConnector *connector)
+{
+ if (!connector->count_modes)
+ return -1;
+
+ for (unsigned i = 0; i < connector->count_modes; i++) {
+ if (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)
+ continue;
+
+ if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED)
+ return i;
+ }
+
+ return 0;
+}
+
+static drmModeConnector *
+connector_get(int fd, drmModeRes *resources)
+{
+ /* Find the first connected connector */
+ for (unsigned i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(fd, resources->connectors[i]);
+ if (!connector)
+ continue;
+
+ if (connector->connection == DRM_MODE_CONNECTED)
+ return connector;
+
+ drmModeFreeConnector(connector);
+ }
+
+ return NULL;
+}
+
+static drmModeCrtc *
+crtc_get_for_connector(int fd, drmModeRes *resources,
+ drmModeConnector *connector)
+{
+ for (unsigned i = 0; i < connector->count_encoders; i++) {
+ drmModeEncoder *encoder;
+
+ encoder = drmModeGetEncoder(fd, connector->encoders[i]);
+ if (!encoder)
+ continue;
+
+ for (unsigned j = 0; j < resources->count_crtcs; j++) {
+ if (encoder->possible_crtcs & (1 << j)) {
+ drmModeCrtc *crtc;
+
+ crtc = drmModeGetCrtc(fd, resources->crtcs[j]);
+ if (crtc) {
+ drmModeFreeEncoder(encoder);
+ return crtc;
+ }
+ }
+ }
+
+ drmModeFreeEncoder(encoder);
+ }
+
+ return NULL;
+}
+
+static drmModePlane *
+primary_plane_get_for_crtc(int fd, drmModeRes *resources, drmModeCrtc *crtc)
+{
+ drmModePlaneRes *plane_resources;
+ unsigned crtc_idx;
+
+ plane_resources = drmModeGetPlaneResources(fd);
+ if (!plane_resources)
+ return NULL;
+
+ for (crtc_idx = 0; crtc_idx < resources->count_crtcs; crtc_idx++)
+ if (resources->crtcs[crtc_idx] == crtc->crtc_id)
+ break;
+ assert(crtc_idx != resources->count_crtcs);
+
+ for (unsigned i = 0; i < plane_resources->count_planes; i++) {
+ const uint32_t crtc_bit = 1 << crtc_idx;
+ drmModePlane *plane;
+
+ plane = drmModeGetPlane(fd, plane_resources->planes[i]);
+ if (!plane)
+ continue;
+
+ if (plane->possible_crtcs & crtc_bit) {
+ uint64_t type;
+ bool res;
+
+ res = object_property_value_for_name(fd, plane->plane_id,
+ DRM_MODE_OBJECT_PLANE,
+ "type", &type);
+ if (res && type == DRM_PLANE_TYPE_PRIMARY) {
+ drmModeFreePlaneResources(plane_resources);
+ return plane;
+ }
+ }
+
+ drmModeFreePlane(plane);
+ }
+
+ drmModeFreePlaneResources(plane_resources);
+
+ return NULL;
+}
+
+static bool
+display_output_atomic_init(int fd, struct display_output *output)
+{
+ drmModePropertyRes **connector_prop_res;
+ drmModePropertyRes **crtc_prop_res;
+ drmModePropertyRes **plane_prop_res;
+ int err;
+
+ connector_prop_res = object_get_property_resources(fd, output->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!connector_prop_res)
+ return false;
+
+ crtc_prop_res = object_get_property_resources(fd, output->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!crtc_prop_res)
+ goto err_free_connector_prop_res;
+
+ plane_prop_res = object_get_property_resources(fd, output->plane_id,
+ DRM_MODE_OBJECT_PLANE);
+ if (!plane_prop_res)
+ goto err_free_crtc_prop_res;
+
+ err = drmModeCreatePropertyBlob(fd, &output->mode, sizeof(output->mode),
+ &output->mode_blob_id);
+ if (err)
+ goto err_free_plane_prop_res;
+
+ output->atomic_state = drmModeAtomicAlloc();
+ if (!output->atomic_state)
+ goto err_destroy_mode_prop_blob;
+
+ output->connector_prop_res = connector_prop_res;
+ output->crtc_prop_res = crtc_prop_res;
+ output->plane_prop_res = plane_prop_res;
+
+ return true;
+
+err_destroy_mode_prop_blob:
+ drmModeDestroyPropertyBlob(fd, output->mode_blob_id);
+err_free_plane_prop_res:
+ object_free_property_resources(fd, plane_prop_res);
+err_free_crtc_prop_res:
+ object_free_property_resources(fd, crtc_prop_res);
+err_free_connector_prop_res:
+ object_free_property_resources(fd, connector_prop_res);
+ return false;
+}
+
+static int
+display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id,
+ uint32_t flags, void *flip_data)
+{
+ const struct object_property obj_props[] = {
+ object_property_set_named(output, plane, "FB_ID", fb_id),
+ };
+ int err;
+
+ /* Reset atomic state */
+ drmModeAtomicSetCursor(output->atomic_state, 0);
+
+ err = atomic_state_add_object_properties(output->atomic_state, obj_props,
+ ARRAY_SIZE(obj_props));
+ if (err)
+ return err;
+
+ /*
+ * Don't block - like drmModePageFlip, drmModeAtomicCommit will return
+ * -EBUSY if the commit can't be queued in the kernel.
+ */
+ flags |= DRM_MODE_ATOMIC_NONBLOCK;
+
+ return drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data);
+}
+
+static int
+display_output_atomic_modeset(int fd, struct display_output *output, uint32_t fb_id)
+{
+ /* SRC_W and SRC_H in 16.16 fixed point */
+ const struct object_property obj_props[] = {
+ object_property_set_named(output, connector, "CRTC_ID", output->crtc_id),
+ object_property_set_named(output, crtc, "ACTIVE", 1),
+ object_property_set_named(output, crtc, "MODE_ID", output->mode_blob_id),
+ object_property_set_named(output, plane, "FB_ID", fb_id),
+ object_property_set_named(output, plane, "CRTC_ID", output->crtc_id),
+ object_property_set_named(output, plane, "CRTC_X", 0),
+ object_property_set_named(output, plane, "CRTC_Y", 0),
+ object_property_set_named(output, plane, "CRTC_W", output->mode.hdisplay),
+ object_property_set_named(output, plane, "CRTC_H", output->mode.vdisplay),
+ object_property_set_named(output, plane, "SRC_X", 0),
+ object_property_set_named(output, plane, "SRC_Y", 0),
+ object_property_set_named(output, plane, "SRC_W", output->mode.hdisplay << 16),
+ object_property_set_named(output, plane, "SRC_H", output->mode.vdisplay << 16),
+ };
+ int err;
+
+ /* Reset atomic state */
+ drmModeAtomicSetCursor(output->atomic_state, 0);
+
+ err = atomic_state_add_object_properties(output->atomic_state, obj_props,
+ ARRAY_SIZE(obj_props));
+ if (err)
+ return false;
+
+ return drmModeAtomicCommit(fd, output->atomic_state,
+ DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+}
+
+static bool
+display_output_init(int fd, struct display_output *output, bool use_atomic)
+{
+ drmModeRes *resources;
+ drmModeConnector *connector;
+ drmModeCrtc *crtc;
+ drmModePlane *plane;
+ unsigned mode_idx;
+
+ resources = drmModeGetResources(fd);
+ if (!resources)
+ return false;
+
+ connector = connector_get(fd, resources);
+ if (!connector)
+ goto err_free_resources;
+
+ crtc = crtc_get_for_connector(fd, resources, connector);
+ if (!crtc)
+ goto err_free_connector;
+
+ plane = primary_plane_get_for_crtc(fd, resources, crtc);
+ if (!plane)
+ goto err_free_crtc;
+
+ mode_idx = connector_choose_mode(connector);
+ if (mode_idx < 0)
+ goto err_free_plane;
+ output->mode = connector->modes[mode_idx];
+
+ /* Record the display supported formats */
+ for (unsigned i = 0; i < plane->count_formats; i++) {
+ int format_idx;
+
+ format_idx = format_idx_get_from_drm_format(plane->formats[i]);
+ if (format_idx == -1)
+ continue;
+
+ output->formats |= (1 << format_idx);
+ }
+ if (!output->formats)
+ goto err_free_plane;
+
+ output->connector_id = connector->connector_id;
+ output->crtc_id = crtc->crtc_id;
+ output->plane_id = plane->plane_id;
+
+ drmModeFreePlane(plane);
+ drmModeFreeCrtc(crtc);
+ drmModeFreeConnector(connector);
+ drmModeFreeResources(resources);
+
+ if (use_atomic) {
+ if (!display_output_atomic_init(fd, output)) {
+ _eglLog(_EGL_DEBUG,
+ "failed to initialise atomic support (using legacy mode)");
+ }
+ }
+
+ return true;
+
+err_free_plane:
+ drmModeFreePlane(plane);
+err_free_crtc:
+ drmModeFreeCrtc(crtc);
+err_free_connector:
+ drmModeFreeConnector(connector);
+err_free_resources:
+ drmModeFreeResources(resources);
+ return false;
+}
+
+static int
+display_output_flip(int fd, struct display_output *output, uint32_t fb_id,
+ uint32_t flags, void *flip_data)
+{
+ if (output->atomic_state)
+ return display_output_atomic_flip(fd, output, fb_id, flags, flip_data);
+
+ return drmModePageFlip(fd, output->crtc_id, fb_id, flags, flip_data);
+}
+
+static int
+display_output_modeset(int fd, struct display_output *output, uint32_t fb_id)
+{
+ if (output->atomic_state)
+ return display_output_atomic_modeset(fd, output, fb_id);
+
+ return drmModeSetCrtc(fd, output->crtc_id, fb_id, 0, 0,
+ &output->connector_id, 1, &output->mode);
+}
+
+static bool
+add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+ uint32_t *fb_id_out)
+{
+ uint32_t handles[4] = {0};
+ uint32_t pitches[4] = {0};
+ uint32_t offsets[4] = {0};
+ int handle, stride, width, height, format;
+ int format_idx;
+
+ dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HANDLE, &handle);
+ dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &stride);
+ dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_WIDTH, &width);
+ dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HEIGHT, &height);
+ dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
+
+ handles[0] = (uint32_t) handle;
+ pitches[0] = (uint32_t) stride;
+
+ format_idx = format_idx_get_from_dri_image_format(format);
+ assert(format_idx != -1);
+
+ return !drmModeAddFB2(dri2_dpy->fd, width, height,
+ dri2_null_formats[format_idx].drm_format,
+ handles, pitches, offsets, fb_id_out, 0);
+}
+
+static bool
+get_front_bo(struct dri2_egl_surface *dri2_surf)
+{
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+ unsigned int use = 0;
+
+ if (dri2_surf->base.Type == EGL_WINDOW_BIT)
+ use |= __DRI_IMAGE_USE_SCANOUT;
+
+ dri2_surf->front = dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+ dri2_surf->base.Width,
+ dri2_surf->base.Height,
+ dri2_surf->format,
+ use,
+ NULL);
+ if (!dri2_surf->front)
+ return false;
+
+ if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
+ if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->front,
+ &dri2_surf->front_fb_id)) {
+ dri2_dpy->image->destroyImage(dri2_surf->front);
+ dri2_surf->front = NULL;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+get_back_bo(struct dri2_egl_surface *dri2_surf)
+{
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+
+ if (!dri2_surf->back) {
+ for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+ if (!dri2_surf->color_buffers[i].locked) {
+ dri2_surf->back = &dri2_surf->color_buffers[i];
+ break;
+ }
+ }
+ if (!dri2_surf->back)
+ return false;
+ }
+
+ if (!dri2_surf->back->dri_image) {
+ dri2_surf->back->dri_image =
+ dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+ dri2_surf->base.Width,
+ dri2_surf->base.Height,
+ dri2_surf->format,
+ __DRI_IMAGE_USE_SCANOUT,
+ NULL);
+ if (!dri2_surf->back->dri_image)
+ return false;
+ }
+
+ if (!dri2_surf->back->fb_id) {
+ if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->back->dri_image,
+ &dri2_surf->back->fb_id)) {
+ return false;
+ }
+ }
+
+ dri2_surf->back->locked = 1;
+
+ return true;
+}
+
+static _EGLSurface *
+create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+ const EGLint *attrib_list)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_config *dri2_config = dri2_egl_config(config);
+ struct dri2_egl_surface *dri2_surf;
+ const __DRIconfig *dri_config;
+ _EGLSurface *surf;
+ int format_idx;
+
+ dri2_surf = calloc(1, sizeof(*dri2_surf));
+ if (!dri2_surf) {
+ _eglError(EGL_BAD_ALLOC, "failed to create surface");
+ return NULL;
+ }
+ surf = &dri2_surf->base;
+
+ if (!dri2_init_surface(surf, disp, type, config, attrib_list, false, NULL))
+ goto err_free_surface;
+
+ dri_config = dri2_get_dri_config(dri2_config, type,
+ dri2_surf->base.GLColorspace);
+ if (!dri_config) {
+ _eglError(EGL_BAD_MATCH, "Unsupported surfacetype/colorspace configuration");
+ goto err_free_surface;
+ }
+
+ dri2_surf->dri_drawable =
+ dri2_dpy->image_driver->createNewDrawable(dri2_dpy->dri_screen,
+ dri_config, dri2_surf);
+ if (!dri2_surf->dri_drawable) {
+ _eglError(EGL_BAD_ALLOC, "failed to create drawable");
+ goto err_free_surface;
+ }
+
+ format_idx = format_idx_get_from_config(dri2_dpy, dri_config);
+ assert(format_idx != -1);
+
+ dri2_surf->format = dri2_null_formats[format_idx].dri_image_format;
+
+ return surf;
+
+err_free_surface:
+ free(dri2_surf);
+ return NULL;
+}
+
+static void
+destroy_surface(_EGLSurface *surf)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(surf->Resource.Display);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
+
+ dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
+ dri2_fini_surface(surf);
+ free(surf);
+}
+
+static EGLBoolean
+dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf);
+
+static _EGLSurface *
+dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
+ void *native_window, const EGLint *attrib_list)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf;
+ _EGLSurface *surf;
+ int err;
+
+ if (dri2_dpy->output.in_use) {
+ _eglError(EGL_BAD_NATIVE_WINDOW, "window in use");
+ return NULL;
+ }
+
+ surf = create_surface(disp, config, EGL_WINDOW_BIT, attrib_list);
+ if (!surf)
+ return NULL;
+ dri2_surf = dri2_egl_surface(surf);
+
+ dri2_surf->base.Width = dri2_dpy->output.mode.hdisplay;
+ dri2_surf->base.Height = dri2_dpy->output.mode.vdisplay;
+
+ if (!get_front_bo(dri2_surf)) {
+ _eglError(EGL_BAD_NATIVE_WINDOW, "window get buffer");
+ goto err_destroy_surface;
+ }
+
+ err = display_output_modeset(dri2_dpy->fd, &dri2_dpy->output,
+ dri2_surf->front_fb_id);
+ if (err) {
+ _eglError(EGL_BAD_NATIVE_WINDOW, "window set mode");
+ goto err_destroy_surface;
+ }
+
+ dri2_dpy->output.in_use = true;
+
+ return surf;
+
+err_destroy_surface:
+ dri2_null_destroy_surface(disp, surf);
+ return NULL;
+}
+
+static _EGLSurface *
+dri2_null_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *config,
+ const EGLint *attrib_list)
+{
+ return create_surface(disp, config, EGL_PBUFFER_BIT, attrib_list);
+}
+
+static EGLBoolean
+dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
+ EGLint type = surf->Type;
+
+ /* If there's a current surface then a page flip has been performed, so make
+ * sure we process the flip event.
+ */
+ if (dri2_surf->current)
+ flip_process(dri2_dpy->fd);
+
+ if (dri2_surf->front)
+ dri2_dpy->image->destroyImage(dri2_surf->front);
+
+ if (dri2_surf->front_fb_id)
+ drmModeRmFB(dri2_dpy->fd, dri2_surf->front_fb_id);
+
+ for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+ if (dri2_surf->color_buffers[i].fb_id)
+ drmModeRmFB(dri2_dpy->fd, dri2_surf->color_buffers[i].fb_id);
+ if (dri2_surf->color_buffers[i].dri_image)
+ dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+ }
+
+ destroy_surface(surf);
+
+ if (type == EGL_WINDOW_BIT)
+ dri2_dpy->output.in_use = false;
+
+ return EGL_TRUE;
+}
+
+static EGLBoolean
+dri2_null_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ bool *plocked = NULL;
+ uint32_t flags;
+ int err;
+
+ if (dri2_surf->base.Type != EGL_WINDOW_BIT)
+ return EGL_TRUE;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
+ if (dri2_surf->color_buffers[i].age > 0)
+ dri2_surf->color_buffers[i].age++;
+
+ /* Make sure we have a back buffer in case we're swapping without
+ * ever rendering. */
+ if (!get_back_bo(dri2_surf)) {
+ _eglError(EGL_BAD_ALLOC, "dri2_null_swap_buffers");
+ return EGL_FALSE;
+ }
+
+ dri2_flush_drawable_for_swapbuffers(disp, draw);
+ dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
+
+ if (dri2_surf->current) {
+ /* Wait for the previous flip to happen so the next one can be queued */
+ if (!flip_process(dri2_dpy->fd)) {
+ _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers process");
+ return EGL_FALSE;
+ }
+
+ plocked = &dri2_surf->current->locked;
+ }
+
+ flags = DRM_MODE_PAGE_FLIP_EVENT;
+ if (draw->SwapInterval == 0)
+ flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+
+ do {
+ err = display_output_flip(dri2_dpy->fd, &dri2_dpy->output,
+ dri2_surf->back->fb_id, flags, plocked);
+ } while (err == -EBUSY);
+
+ if (err) {
+ _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers flip");
+ dri2_surf->back->locked = false;
+ dri2_surf->back = NULL;
+ return EGL_FALSE;
+ }
+
+ dri2_surf->back->age = 1;
+ dri2_surf->current = dri2_surf->back;
+ dri2_surf->back = NULL;
+
+ return EGL_TRUE;
+}
+
+static EGLint
+dri2_null_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface)
+{
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
+
+ if (!get_back_bo(dri2_surf)) {
+ _eglError(EGL_BAD_ALLOC, "failed to get back buffer to query age");
+ return -1;
+ }
+
+ return dri2_surf->back->age;
+}
+
+static struct dri2_egl_display_vtbl dri2_null_display_vtbl = {
+ .create_window_surface = dri2_null_create_window_surface,
+ .create_pbuffer_surface = dri2_null_create_pbuffer_surface,
+ .destroy_surface = dri2_null_destroy_surface,
+ .create_image = dri2_create_image_khr,
+ .swap_buffers = dri2_null_swap_buffers,
+ .query_buffer_age = dri2_null_query_buffer_age,
+ .get_dri_drawable = dri2_surface_get_dri_drawable,
+};
+
+static int
+dri2_null_image_get_buffers(__DRIdrawable *driDrawable, unsigned int format,
+ uint32_t *stamp, void *loaderPrivate,
+ uint32_t buffer_mask, struct __DRIimageList *buffers)
+{
+ struct dri2_egl_surface *dri2_surf = loaderPrivate;
+
+ buffers->image_mask = 0;
+ buffers->back = NULL;
+ buffers->front = NULL;
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT)
+ if (!get_front_bo(dri2_surf))
+ return 0;
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_BACK)
+ if (!get_back_bo(dri2_surf))
+ return 0;
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+ buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
+ buffers->front = dri2_surf->front;
+ }
+
+ if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
+ buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
+ buffers->back = dri2_surf->back->dri_image;
+ }
+
+ return 1;
+}
+
+static void
+dri2_null_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+{
+ (void) driDrawable;
+ (void) loaderPrivate;
+}
+
+static const __DRIimageLoaderExtension image_loader_extension = {
+ .base = { __DRI_IMAGE_LOADER, 1 },
+
+ .getBuffers = dri2_null_image_get_buffers,
+ .flushFrontBuffer = dri2_null_flush_front_buffer,
+};
+
+static const __DRIextension *image_loader_extensions[] = {
+ &image_loader_extension.base,
+ &image_lookup_extension.base,
+ &use_invalidate.base,
+ NULL,
+};
+
+static bool
+dri2_null_device_is_kms(int fd)
+{
+ drmModeRes *resources;
+ bool is_kms;
+
+ resources = drmModeGetResources(fd);
+ if (!resources)
+ return false;
+
+ is_kms = resources->count_crtcs != 0 &&
+ resources->count_connectors != 0 &&
+ resources->count_encoders != 0;
+
+ drmModeFreeResources(resources);
+
+ return is_kms;
+}
+
+static bool
+dri2_null_probe_device(_EGLDisplay *disp)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+ dri2_dpy->fd = -1;
+
+ for (unsigned i = 0; i <= NULL_CARD_MINOR_MAX; i++) {
+ char *card_path;
+
+ if (asprintf(&card_path, DRM_DEV_NAME, DRM_DIR_NAME, i) < 0)
+ continue;
+
+ dri2_dpy->fd = loader_open_device(card_path);
+ free(card_path);
+ if (dri2_dpy->fd < 0)
+ continue;
+
+ if (dri2_null_device_is_kms(dri2_dpy->fd)) {
+ dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
+ if (dri2_dpy->driver_name) {
+ if (dri2_load_driver_dri3(disp)) {
+ _EGLDevice *dev = _eglAddDevice(dri2_dpy->fd, false);
+ if (!dev) {
+ dlclose(dri2_dpy->driver);
+ _eglLog(_EGL_WARNING, "DRI2: failed to find EGLDevice");
+ } else {
+ dri2_dpy->loader_extensions = image_loader_extensions;
+ dri2_dpy->own_device = 1;
+ disp->Device = dev;
+ return true;
+ }
+ }
+ free(dri2_dpy->driver_name);
+ dri2_dpy->driver_name = NULL;
+ }
+ }
+
+ close(dri2_dpy->fd);
+ dri2_dpy->fd = -1;
+ }
+
+ return false;
+}
+
+static EGLBoolean
+dri2_null_add_configs_for_formats(_EGLDisplay *disp)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ unsigned int count = 0;
+
+ for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
+ struct dri2_egl_config *dri2_conf;
+ int format_idx;
+
+ format_idx = format_idx_get_from_config(dri2_dpy,
+ dri2_dpy->driver_configs[i]);
+ if (format_idx == -1)
+ continue;
+
+ if (!(dri2_dpy->output.formats & (1 << format_idx))) {
+ _eglLog(_EGL_DEBUG, "unsupported drm format 0x%04x",
+ dri2_null_formats[format_idx].drm_format);
+ continue;
+ }
+
+ dri2_conf = dri2_add_config(disp,
+ dri2_dpy->driver_configs[i], count + 1,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ NULL, NULL, NULL);
+ if (dri2_conf)
+ count++;
+ }
+
+ return count != 0;
+}
+
+EGLBoolean
+dri2_initialize_null(_EGLDisplay *disp)
+{
+ struct dri2_egl_display *dri2_dpy;
+ uint64_t value;
+ int err;
+
+ dri2_dpy = calloc(1, sizeof(*dri2_dpy));
+ if (!dri2_dpy)
+ return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+
+ disp->DriverData = (void *) dri2_dpy;
+
+ if (!dri2_null_probe_device(disp)) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to load driver");
+ goto cleanup;
+ }
+
+ /*
+ * Try to use atomic modesetting if available and fallback to legacy kernel
+ * modesetting if not. If this succeeds then universal planes will also have
+ * been enabled.
+ */
+ err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ dri2_dpy->atomic_enabled = !err;
+
+ if (!dri2_dpy->atomic_enabled) {
+ /*
+ * Enable universal planes so that we can get the pixel formats for the
+ * primary plane
+ */
+ err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ if (err) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to enable universal planes");
+ goto cleanup;
+ }
+
+ dri2_dpy->atomic_enabled = false;
+ }
+
+ if (!dri2_create_screen(disp)) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to create screen");
+ goto cleanup;
+ }
+
+ if (!dri2_setup_extensions(disp)) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to find required DRI extensions");
+ goto cleanup;
+ }
+
+ dri2_setup_screen(disp);
+ dri2_setup_swap_interval(disp, 1);
+
+ err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+ if (err || value == 0)
+ dri2_dpy->min_swap_interval = 1;
+
+ if (dri2_dpy->image->base.version < NULL_IMAGE_EXTENSION_VERSION_MIN) {
+ _eglError(EGL_NOT_INITIALIZED, "image extension version too old");
+ goto cleanup;
+ }
+
+ if (!display_output_init(dri2_dpy->fd, &dri2_dpy->output,
+ dri2_dpy->atomic_enabled)) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to create output");
+ goto cleanup;
+ }
+
+ if (!dri2_null_add_configs_for_formats(disp)) {
+ _eglError(EGL_NOT_INITIALIZED, "failed to add configs");
+ goto cleanup;
+ }
+
+ disp->Extensions.EXT_buffer_age = EGL_TRUE;
+ disp->Extensions.KHR_image_base = EGL_TRUE;
+
+ /* Fill vtbl last to prevent accidentally calling virtual function during
+ * initialization.
+ */
+ dri2_dpy->vtbl = &dri2_null_display_vtbl;
+
+ return EGL_TRUE;
+
+cleanup:
+ dri2_display_destroy(disp);
+ return EGL_FALSE;
+}
+
+void
+dri2_teardown_null(struct dri2_egl_display *dri2_dpy)
+{
+ drmModeAtomicFree(dri2_dpy->output.atomic_state);
+
+ if (dri2_dpy->output.mode_blob_id)
+ drmModeDestroyPropertyBlob(dri2_dpy->fd, dri2_dpy->output.mode_blob_id);
+
+ if (dri2_dpy->output.plane_prop_res) {
+ for (unsigned i = 0; dri2_dpy->output.plane_prop_res[i]; i++)
+ drmModeFreeProperty(dri2_dpy->output.plane_prop_res[i]);
+ free(dri2_dpy->output.plane_prop_res);
+ }
+
+ if (dri2_dpy->output.crtc_prop_res) {
+ for (unsigned i = 0; dri2_dpy->output.crtc_prop_res[i]; i++)
+ drmModeFreeProperty(dri2_dpy->output.crtc_prop_res[i]);
+ free(dri2_dpy->output.crtc_prop_res);
+ }
+
+ if (dri2_dpy->output.connector_prop_res) {
+ for (unsigned i = 0; dri2_dpy->output.connector_prop_res[i]; i++)
+ drmModeFreeProperty(dri2_dpy->output.connector_prop_res[i]);
+ free(dri2_dpy->output.connector_prop_res);
+ }
+}
diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
index c4dac448b8b..f8380e139f5 100644
--- a/src/egl/main/eglapi.c
+++ b/src/egl/main/eglapi.c
@@ -1063,7 +1063,10 @@ _eglCreateWindowSurfaceCommon(_EGLDisplay *disp, EGLConfig config,
if (native_window == NULL)
- RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+#ifdef HAVE_NULL_PLATFORM
+ if (disp && disp->Platform != _EGL_PLATFORM_NULL)
+#endif
+ RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
if (disp && (disp->Platform == _EGL_PLATFORM_SURFACELESS ||
disp->Platform == _EGL_PLATFORM_DEVICE)) {
diff --git a/src/egl/main/egldisplay.c b/src/egl/main/egldisplay.c
index 39850e34f7c..a0920194153 100644
--- a/src/egl/main/egldisplay.c
+++ b/src/egl/main/egldisplay.c
@@ -85,6 +85,7 @@ static const struct {
{ _EGL_PLATFORM_SURFACELESS, "surfaceless" },
{ _EGL_PLATFORM_DEVICE, "device" },
{ _EGL_PLATFORM_WINDOWS, "windows" },
+ { _EGL_PLATFORM_NULL, "null" },
};
diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
index 1b9afdb343a..cedf3887c9d 100644
--- a/src/egl/main/egldisplay.h
+++ b/src/egl/main/egldisplay.h
@@ -56,6 +56,7 @@ enum _egl_platform_type {
_EGL_PLATFORM_SURFACELESS,
_EGL_PLATFORM_DEVICE,
_EGL_PLATFORM_WINDOWS,
+ _EGL_PLATFORM_NULL,
_EGL_NUM_PLATFORMS,
_EGL_INVALID_PLATFORM = -1
diff --git a/src/egl/meson.build b/src/egl/meson.build
index a7c6471ceb4..b20d85dc19f 100644
--- a/src/egl/meson.build
+++ b/src/egl/meson.build
@@ -152,6 +152,11 @@ elif with_platform_windows
incs_for_egl += [inc_wgl, inc_gallium, inc_gallium_aux]
link_for_egl += libgallium_wgl
endif
+if with_platform_null
+ files_egl += files('drivers/dri2/platform_null.c')
+ incs_for_egl += [inc_loader]
+ deps_for_egl += dep_libdrm
+endif
if cc.has_function('mincore')
c_args_for_egl += '-DHAVE_MINCORE'
--
2.17.1