summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2014-05-09 11:08:15 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2014-06-02 08:32:11 +0100
commit6ab6734369fbd902a23109f4c3626df9d529891c (patch)
tree36dac823ac3fe0d9108bbd1e8575b450aae3f567
parentd6240d197be1e752c0de26fbf84fc8fa8d55383c (diff)
Add rudimentary tests for DRI3
This is a simple little test to create a pixmap from a local bo, copy it to a normal pixmap, then read it back by importing it into anther local bo. It tests the fundamental mechanisms of opening a DRI3 render device, importing into pixmaps, exporting into /buffers and a read-barrier. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--configure.ac3
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile.am14
-rw-r--r--test/dri3-test.c1100
-rw-r--r--test/dri3.c130
-rw-r--r--test/dri3.h50
6 files changed, 1298 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index ec237c45..e89e5a38 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,9 @@ if test "x$shm" = "xyes"; then
AC_MSG_RESULT(assuming no))
fi
+PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
+AM_CONDITIONAL(X11_DRI3, test "x$x11_dri3" = "xyes" -a "x$shm" = "xyes")
+
AC_ARG_ENABLE(tools,
AS_HELP_STRING([--disable-tools],
[Enable building and installing the miscellaneous tools [default=auto]]),
diff --git a/test/.gitignore b/test/.gitignore
index 979d8960..8e6b1bc6 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -22,3 +22,4 @@ vsync.avi
dri2-race
dri2-swap
dri2-test
+dri3-test
diff --git a/test/Makefile.am b/test/Makefile.am
index df022e28..7a8916c9 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -30,6 +30,11 @@ stress_TESTS += \
$(NULL)
endif
+if X11_DRI3
+stress_TESTS += \
+ dri3-test \
+ $(NULL)
+endif
check_PROGRAMS = $(stress_TESTS)
noinst_PROGRAMS = lowlevel-blt-bench
@@ -53,6 +58,15 @@ libtest_la_SOURCES += \
$(NULL)
endif
+if X11_DRI3
+libtest_la_SOURCES += \
+ dri3.c \
+ dri3.h \
+ $(NULL)
+AM_CFLAGS += $(X11_DRI3_CFLAGS)
+LDADD += $(X11_DRI3_LIBS)
+endif
+
vsync.avi: mkvsync.sh
./mkvsync.sh $@
diff --git a/test/dri3-test.c b/test/dri3-test.c
new file mode 100644
index 00000000..3fe011a4
--- /dev/null
+++ b/test/dri3-test.c
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/XShm.h>
+#if HAVE_X11_EXTENSIONS_SHMPROTO_H
+#include <X11/extensions/shmproto.h>
+#elif HAVE_X11_EXTENSIONS_SHMSTR_H
+#include <X11/extensions/shmstr.h>
+#else
+#error Failed to find the right header for X11 MIT-SHM protocol definitions
+#endif
+#include <xf86drm.h>
+#include <i915_drm.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <pciaccess.h>
+
+#include "dri3.h"
+#include "../src/i915_pciids.h"
+
+#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
+#define PAGE_ALIGN(x) ALIGN(x, 4096)
+
+#define GTT I915_GEM_DOMAIN_GTT
+#define CPU I915_GEM_DOMAIN_CPU
+
+static int _x_error_occurred;
+
+static const struct pci_id_match ids[] = {
+ INTEL_I830_IDS(020),
+ INTEL_I845G_IDS(021),
+ INTEL_I85X_IDS(022),
+ INTEL_I865G_IDS(023),
+
+ INTEL_I915G_IDS(030),
+ INTEL_I915GM_IDS(030),
+ INTEL_I945G_IDS(031),
+ INTEL_I945GM_IDS(031),
+
+ INTEL_G33_IDS(033),
+ INTEL_PINEVIEW_IDS(033),
+
+ INTEL_I965G_IDS(040),
+ INTEL_I965GM_IDS(040),
+
+ INTEL_G45_IDS(045),
+ INTEL_GM45_IDS(045),
+
+ INTEL_IRONLAKE_D_IDS(050),
+ INTEL_IRONLAKE_M_IDS(050),
+
+ INTEL_SNB_D_IDS(060),
+ INTEL_SNB_M_IDS(060),
+
+ INTEL_IVB_D_IDS(070),
+ INTEL_IVB_M_IDS(070),
+
+ INTEL_HSW_D_IDS(075),
+ INTEL_HSW_M_IDS(075),
+
+ INTEL_VLV_D_IDS(071),
+ INTEL_VLV_M_IDS(071),
+
+ INTEL_BDW_D_IDS(0100),
+ INTEL_BDW_M_IDS(0100),
+};
+
+static int i915_gen(int device)
+{
+ struct drm_i915_getparam gp;
+ int devid = 0;
+ int n;
+
+ gp.param = I915_PARAM_CHIPSET_ID;
+ gp.value = &devid;
+
+ if (drmIoctl(device, DRM_IOCTL_I915_GETPARAM, &gp))
+ return 0;
+
+ for (n = 0; n < sizeof(ids)/sizeof(ids[0]); n++) {
+ if (devid == ids[n].device_id)
+ return ids[n].match_data;
+ }
+
+ return 0;
+}
+
+static int is_i915_device(int fd)
+{
+ drm_version_t version;
+ char name[5] = "";
+
+ memset(&version, 0, sizeof(version));
+ version.name_len = 4;
+ version.name = name;
+
+ if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
+ return 0;
+
+ return strcmp("i915", name) == 0;
+}
+
+static int is_intel(int fd)
+{
+ struct drm_i915_getparam gp;
+ int ret;
+
+ /* Confirm that this is a i915.ko device with GEM/KMS enabled */
+ ret = is_i915_device(fd);
+ if (ret) {
+ gp.param = I915_PARAM_HAS_GEM;
+ gp.value = &ret;
+ if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+ ret = 0;
+ }
+ return ret;
+}
+
+static uint32_t gem_create(int fd, int size)
+{
+ struct drm_i915_gem_create create;
+
+ create.handle = 0;
+ create.size = size;
+ (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+
+ return create.handle;
+}
+
+struct local_i915_gem_caching {
+ uint32_t handle;
+ uint32_t caching;
+};
+
+#define LOCAL_I915_GEM_SET_CACHING 0x2f
+#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching)
+
+static int gem_set_caching(int fd, uint32_t handle, int caching)
+{
+ struct local_i915_gem_caching arg;
+
+ arg.handle = handle;
+ arg.caching = caching;
+
+ return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0;
+}
+
+static int gem_export(int fd, uint32_t handle)
+{
+ struct drm_prime_handle args;
+
+ args.handle = handle;
+ args.flags = O_CLOEXEC;
+
+ if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
+ return -1;
+
+ return args.fd;
+}
+
+static uint32_t gem_import(int fd, int name)
+{
+ struct drm_prime_handle args;
+
+ args.fd = name;
+ args.flags = 0;
+ if (drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args))
+ return 0;
+
+ return args.handle;
+}
+
+static int gem_write(int fd, uint32_t handle, int offset, void *data, int len)
+{
+ struct drm_i915_gem_pwrite gem_pwrite;
+
+ gem_pwrite.handle = handle;
+ gem_pwrite.offset = offset;
+ gem_pwrite.size = len;
+ gem_pwrite.data_ptr = (uintptr_t)data;
+ return drmIoctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &gem_pwrite);
+}
+
+static void *gem_mmap(int fd, uint32_t handle, int size, unsigned prot, int domain)
+{
+ struct drm_i915_gem_set_domain set_domain;
+ void *ptr;
+
+ if (domain == CPU) {
+ struct drm_i915_gem_mmap mmap_arg;
+
+ mmap_arg.handle = handle;
+ mmap_arg.offset = 0;
+ mmap_arg.size = size;
+ if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg))
+ return NULL;
+
+ ptr = (void *)(uintptr_t)mmap_arg.addr_ptr;
+ } else {
+ struct drm_i915_gem_mmap_gtt mmap_arg;
+
+ mmap_arg.handle = handle;
+ if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg))
+ return NULL;
+
+ ptr = mmap(0, size, prot, MAP_SHARED, fd, mmap_arg.offset);
+ if (ptr == MAP_FAILED)
+ return NULL;
+ }
+
+ set_domain.handle = handle;
+ set_domain.read_domains = domain;
+ set_domain.write_domain = prot & PROT_WRITE ? set_domain.read_domains : 0;
+ if (drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain)) {
+ munmap(ptr, size);
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static void gem_sync(int fd, uint32_t handle, int read)
+{
+ struct drm_i915_gem_set_domain set_domain;
+
+ set_domain.handle = handle;
+ set_domain.read_domains = read;
+ set_domain.write_domain = 0;
+ drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
+}
+
+static int gem_get_tiling(int fd, uint32_t handle)
+{
+ struct drm_i915_gem_get_tiling tiling;
+
+ tiling.handle = handle;
+ tiling.tiling_mode = -1;
+ (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_GET_TILING, &tiling);
+ return tiling.tiling_mode;
+}
+
+static void gem_close(int fd, uint32_t handle)
+{
+ struct drm_gem_close close;
+
+ close.handle = handle;
+ (void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
+}
+
+static void gem_fill(int fd, uint32_t handle, uint32_t pixel, uint32_t size, int domain)
+{
+ uint32_t *ptr, s;
+
+ ptr = gem_mmap(fd, handle, size, PROT_READ | PROT_WRITE, domain);
+ if (ptr == NULL)
+ return;
+
+ for (s = 0; s < size; s += 4)
+ ptr[s/4] = pixel;
+ munmap(ptr, size);
+}
+
+static int check_pixmap(Display *dpy, Pixmap pix,
+ int x, int y, uint32_t expected, int bpp)
+{
+ XImage *image;
+ int w = 32 / bpp;
+
+ image = XGetImage(dpy, pix, x - (x % w), y, w, 1, AllPlanes, ZPixmap);
+ if (image == NULL)
+ return 0;
+
+ if (*(uint32_t *)image->data != expected) {
+ printf("pixmap[%d, %d]:%d = %08x\n", x, y, bpp, *(uint32_t *)image->data);
+ return 0;
+ }
+ XDestroyImage(image);
+
+ return 1;
+}
+
+static int check_pixel(int fd, uint32_t handle, uint32_t stride, uint32_t size,
+ int x, int y, uint32_t expected, int bpp, int domain)
+{
+ uint32_t *ptr;
+ int w = 32 / bpp;
+
+ assert((stride & 3) == 0);
+
+ ptr = gem_mmap(fd, handle, size, PROT_READ, domain);
+ if (ptr == NULL)
+ return 0;
+
+ if (ptr[(y*stride + x - (x % w))/4] != expected) {
+ printf("pixel[%d, %d]:%d = %08x\n", x, y, bpp, ptr[(y * stride + x)/4]);
+ return 0;
+ }
+ munmap(ptr, size);
+
+ return 1;
+}
+
+static GC get_gc(Display *dpy, Drawable d, int depth)
+{
+ static GC gc[33];
+ if (gc[depth] == NULL) {
+ XGCValues gcv;
+
+ gcv.graphics_exposures = False;
+ gc[depth] = XCreateGC(dpy, d, GCGraphicsExposures, &gcv);
+ }
+ return gc[depth];
+}
+
+static int
+can_use_shm(Display *dpy)
+{
+ int major, minor, has_pixmap;
+
+ if (!XShmQueryExtension(dpy))
+ return 0;
+
+ XShmQueryVersion(dpy, &major, &minor, &has_pixmap);
+ return has_pixmap;
+}
+
+static int gpu_fill(int device, int handle, int width, int height, int pitch, int bpp, int tiling, uint32_t pixel)
+{
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_relocation_entry gem_reloc[2];
+ struct drm_i915_gem_exec_object2 gem_exec[2];
+ uint32_t batch[10];
+ int gen = i915_gen(device);
+ int len = 0;
+ int ret;
+
+ if (gen == 0)
+ return -ENODEV;
+
+ batch[0] = 2 << 29 | 0x50 << 22;
+ batch[0] |= (gen >= 0100 ? 5 : 4);
+ batch[1] = pitch;
+ if (gen >= 040 && tiling) {
+ batch[0] |= 1 << 11;
+ batch[1] >>= 2;
+ }
+
+ batch[1] |= 0xf0 << 16;
+ switch (bpp) {
+ default: assert(0);
+ case 32: batch[0] |= 1 << 21 | 1 << 20;
+ batch[1] |= 1 << 25; /* RGB8888 */
+ case 16: batch[1] |= 1 << 24; /* RGB565 */
+ case 8: break;
+ }
+
+ batch[2] = 0;
+ batch[3] = height << 16 | width;
+ batch[4] = 0;
+ len = 5;
+ if (gen >= 0100)
+ batch[len++] = 0;
+ batch[len++] = pixel;
+ batch[len++] = 0xA << 23;
+ if (len & 1)
+ len++;
+
+ gem_reloc[0].offset = 4 * sizeof(uint32_t);
+ gem_reloc[0].delta = 0;
+ gem_reloc[0].target_handle = handle;
+ gem_reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
+ gem_reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
+ gem_reloc[0].presumed_offset = 0;
+
+ memset(gem_exec, 0, sizeof(gem_exec));
+ gem_exec[0].handle = handle;
+ gem_exec[1].handle = gem_create(device, 4096);
+ gem_exec[1].relocation_count = 1;
+ gem_exec[1].relocs_ptr = (uintptr_t)gem_reloc;
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = (uintptr_t)gem_exec;
+ execbuf.buffer_count = 2;
+ execbuf.batch_len = len * sizeof(uint32_t);
+ execbuf.flags = gen >= 060 ? I915_EXEC_BLT : 0;
+
+ ret = gem_write(device, gem_exec[1].handle, 0, batch, execbuf.batch_len);
+ if (ret == 0)
+ ret = drmIoctl(device, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+ if (ret < 0)
+ ret = -errno;
+
+ gem_close(device, gem_exec[1].handle);
+ return ret;
+}
+
+static int test_shm(Display *dpy, int device,
+ int width, int height)
+{
+ const int x_loc[] = {0, width/2, width-1};
+ const int y_loc[] = {0, height/2, height-1};
+ uint32_t pixel = 0xffff00ff;
+ XShmSegmentInfo shm;
+ Pixmap pixmap;
+ uint32_t handle = 0;
+ uint32_t *ptr;
+ int stride, fd;
+ int x, y;
+
+ if (!can_use_shm(dpy))
+ return 0;
+
+ printf("Creating %dx%d SHM pixmap\n", width, height);
+ _x_error_occurred = 0;
+
+ shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666);
+ if (shm.shmid == -1)
+ return 0;
+
+ shm.shmaddr = shmat(shm.shmid, 0, 0);
+ if (shm.shmaddr == (char *) -1) {
+ shmctl(shm.shmid, IPC_RMID, NULL);
+ return 0;
+ }
+
+ shm.readOnly = False;
+ XShmAttach(dpy, &shm);
+
+ pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
+ shm.shmaddr, &shm, width, height, 24);
+ XSync(dpy, False);
+ shmctl(shm.shmid, IPC_RMID, NULL);
+
+ if (_x_error_occurred) {
+ XShmDetach(dpy, &shm);
+ shmdt(shm.shmaddr);
+ return 0;
+ }
+
+ printf("Testing write of %dx%d SHM pixmap via DRI3 fd\n", width, height);
+
+ fd = dri3_create_fd(dpy, pixmap, &stride);
+ if (fd < 0)
+ goto fail;
+
+ handle = gem_import(device, fd);
+ close(fd);
+ if (handle == 0)
+ goto fail;
+
+ if (gpu_fill(device, handle, width, height, stride, 32, I915_TILING_NONE, pixel))
+ goto fail;
+
+ gem_sync(device, handle, CPU);
+ ptr = (uint32_t *)shm.shmaddr;
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (ptr[y_loc[y]*width + x_loc[x]] != pixel) {
+ printf("pixel[%d, %d]:%d = %08x\n", x, y, 32, ptr[y_loc[y] * width + x_loc[x]]);
+ goto fail;
+ }
+
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, pixmap,
+ x_loc[x], y_loc[y],
+ pixel, 32))
+ goto fail;
+
+ if (_x_error_occurred)
+ goto fail;
+
+out:
+ gem_close(device, handle);
+ XFreePixmap(dpy, pixmap);
+ XShmDetach(dpy, &shm);
+ shmdt(shm.shmaddr);
+ return fd != -1;
+
+fail:
+ printf("%s failed at (%dx%d)\n",
+ __func__, width, height);
+ fd = -1;
+ goto out;
+}
+
+static int test_read_after_write(Display *dpy, int device,
+ int width, int height, int depth,
+ int domain)
+{
+ const uint32_t pixel = 0xffff00ff;
+ const int x_loc[] = {0, width/2, width-1};
+ const int y_loc[] = {0, height/2, height-1};
+ Window root = RootWindow(dpy, DefaultScreen(dpy));
+ uint32_t src, dst;
+ int src_fd, dst_fd;
+ int src_stride, src_size;
+ int dst_stride, dst_size;
+ Pixmap src_pix, dst_pix;
+ struct dri3_fence fence;
+ int x, y, bpp;
+
+ _x_error_occurred = 0;
+
+ switch (depth) {
+ case 8: bpp = 8; break;
+ case 16: bpp = 16; break;
+ case 24: bpp = 32; break;
+ case 32: bpp = 32; break;
+ default: return 0;
+ }
+
+ src_stride = width * bpp/8;
+ src_size = PAGE_ALIGN(src_stride * height);
+ printf("Creating %dx%d (source stride=%d, size=%d, domain=%d)\n",
+ width, height, src_stride, src_size, domain);
+
+ src = gem_create(device, src_size);
+ if (!src)
+ goto fail;
+
+ if (domain == CPU)
+ gem_set_caching(device, src, 1);
+
+ gem_fill(device, src, pixel, src_size, domain);
+
+ src_fd = gem_export(device, src);
+ if (src_fd < 0)
+ goto fail;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ width, height, depth,
+ src_fd, bpp, src_stride, src_size);
+
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, src_pix,
+ x_loc[x], y_loc[y],
+ pixel, bpp))
+ goto fail;
+ close(src_fd);
+
+ dst_pix = XCreatePixmap(dpy, root, width, height, depth);
+ if (dri3_create_fence(dpy, dst_pix, &fence))
+ goto fail;
+
+ dst_fd = dri3_create_fd(dpy, dst_pix, &dst_stride);
+ if (dst_fd < 0)
+ goto fail;
+ dst_size = lseek(dst_fd, 0, SEEK_END);
+ printf("Comparing %dx%d (destination stride=%d, size=%d)\n",
+ width, height, dst_stride, dst_size);
+ dst = gem_import(device, dst_fd);
+ if (dst == 0)
+ goto fail;
+ close(dst_fd);
+
+ XCopyArea(dpy, src_pix, dst_pix,
+ get_gc(dpy, dst_pix, depth),
+ 0, 0, width, height, 0, 0);
+ dri3_fence_sync(dpy, &fence);
+ dri3_fence_free(dpy, &fence);
+
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixel(device, dst, dst_stride, dst_size,
+ x_loc[x], y_loc[y],
+ pixel, bpp, GTT))
+ goto fail;
+
+ XFreePixmap(dpy, dst_pix);
+ XFreePixmap(dpy, src_pix);
+
+ gem_close(device, src);
+ gem_close(device, dst);
+
+ if (_x_error_occurred)
+ goto fail;
+
+ return 0;
+
+fail:
+ printf("%s failed at (%dx%d), depth=%d, domain=%d\n",
+ __func__, width, height, depth, domain);
+ return 1;
+}
+
+static XRenderPictFormat *format_for_depth(Display *dpy, int depth)
+{
+ switch (depth) {
+ case 8: return XRenderFindStandardFormat(dpy, PictStandardA8);
+ case 24: return XRenderFindStandardFormat(dpy, PictStandardRGB24);
+ case 32: return XRenderFindStandardFormat(dpy, PictStandardARGB32);
+ default: assert(0); return NULL;
+ }
+}
+
+static int test_read(Display *dpy, int device,
+ int width, int height,
+ int domain)
+{
+ const uint32_t pixel = 0xffff00ff;
+ const XRenderColor color = { 0xffff, 0x0000, 0xffff, 0xffff };
+ const int x_loc[] = {0, width/2, width-1};
+ const int y_loc[] = {0, height/2, height-1};
+ Window root = RootWindow(dpy, DefaultScreen(dpy));
+ uint32_t dst;
+ int dst_stride, dst_size, dst_fd;
+ Pixmap src_pix, dst_pix;
+ Picture src_pic;
+ struct dri3_fence fence;
+ int depth = 32, bpp = 32;
+ int x, y;
+
+ _x_error_occurred = 0;
+
+ dst_stride = width * bpp/8;
+ dst_size = PAGE_ALIGN(dst_stride * height);
+ printf("Creating %dx%d (destination stride=%d, size=%d, domain=%d)\n",
+ width, height, dst_stride, dst_size, domain);
+
+ dst = gem_create(device, dst_size);
+ if (!dst)
+ goto fail;
+
+ if (domain == CPU)
+ gem_set_caching(device, dst, 1);
+
+ gem_fill(device, dst, ~pixel, dst_size, domain);
+
+ dst_fd = gem_export(device, dst);
+ if (dst_fd < 0)
+ goto fail;
+
+ dst_pix = dri3_create_pixmap(dpy, root,
+ width, height, depth,
+ dst_fd, bpp, dst_stride, dst_size);
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+ if (dri3_create_fence(dpy, dst_pix, &fence))
+ goto fail;
+
+ src_pix = XCreatePixmap(dpy, root, width, height, depth);
+ src_pic = XRenderCreatePicture(dpy, src_pix, format_for_depth(dpy, depth), 0, NULL);
+ XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+ XCopyArea(dpy, src_pix, dst_pix,
+ get_gc(dpy, dst_pix, depth),
+ 0, 0, width, height, 0, 0);
+ dri3_fence_sync(dpy, &fence);
+ dri3_fence_free(dpy, &fence);
+
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixel(device, dst, dst_stride, dst_size,
+ x_loc[x], y_loc[y],
+ pixel, bpp, domain))
+ goto fail;
+
+ XFreePixmap(dpy, dst_pix);
+ XRenderFreePicture(dpy, src_pic);
+ XFreePixmap(dpy, src_pix);
+
+ gem_close(device, dst);
+
+ if (_x_error_occurred)
+ goto fail;
+
+ return 0;
+
+fail:
+ printf("%s failed at (%dx%d), depth=%d, domain=%d\n",
+ __func__, width, height, depth, domain);
+ return 1;
+}
+
+static int test_dup_pixmap(Display *dpy, int device)
+{
+ const uint32_t pixel = 0xffff00ff;
+ const XRenderColor color = { 0xffff, 0x0000, 0xffff, 0xffff };
+ const XRenderColor inverse = { 0, 0xffff, 0, 0 };
+ int width = 400, height = 400;
+ const int x_loc[] = {0, width/2, width-1};
+ const int y_loc[] = {0, height/2, height-1};
+ Window root = RootWindow(dpy, DefaultScreen(dpy));
+ uint32_t handle;
+ int stride, size, fd;
+ Pixmap src_pix, dst_pix;
+ Picture src_pic, dst_pic;
+ struct dri3_fence fence;
+ int depth = 32, bpp = 32;
+ int x, y;
+
+ _x_error_occurred = 0;
+
+ printf("%s: Creating %dx%d pixmap\n", __func__, width, height);
+ src_pix = XCreatePixmap(dpy, root, width, height, depth);
+ src_pic = XRenderCreatePicture(dpy, src_pix, format_for_depth(dpy, depth), 0, NULL);
+ fd = dri3_create_fd(dpy, src_pix, &stride);
+ if (fd < 0)
+ goto fail;
+
+ size = lseek(fd, 0, SEEK_END);
+ handle = gem_import(device, fd);
+
+ printf("%s: Creating duplicate from pixmap exported fd\n", __func__);
+ dst_pix = dri3_create_pixmap(dpy, root,
+ width, height, depth,
+ fd, bpp, stride, size);
+ dst_pic = XRenderCreatePicture(dpy, dst_pix, format_for_depth(dpy, depth), 0, NULL);
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+
+ printf("%s: Filling src with %08x, reading dst\n", __func__, pixel);
+ XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, dst_pix,
+ x_loc[x], y_loc[y],
+ pixel, 32))
+ goto fail;
+
+ printf("%s: Filling dst with %08x, reading src\n", __func__, ~pixel);
+ XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &inverse, 0, 0, width, height);
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, dst_pix,
+ x_loc[x], y_loc[y],
+ ~pixel, 32))
+ goto fail;
+
+ if (dri3_create_fence(dpy, src_pix, &fence))
+ goto fail;
+
+ printf("%s: Filling src with %08x, reading fd\n", __func__, pixel);
+ XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+ dri3_fence_sync(dpy, &fence);
+ dri3_fence_free(dpy, &fence);
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixel(device, handle, stride, size,
+ x_loc[x], y_loc[y],
+ pixel, bpp, GTT))
+ goto fail;
+
+ printf("%s: Filling fd with %08x, reading src\n", __func__, ~pixel);
+ if (gpu_fill(device, handle, width, height, stride, 32, gem_get_tiling(device, handle), ~pixel))
+ goto fail;
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, src_pix,
+ x_loc[x], y_loc[y],
+ ~pixel, 32))
+ goto fail;
+
+ if (dri3_create_fence(dpy, dst_pix, &fence))
+ goto fail;
+
+ printf("%s: Filling dst with %08x, reading fd\n", __func__, pixel);
+ XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &color, 0, 0, width, height);
+ dri3_fence_sync(dpy, &fence);
+ dri3_fence_free(dpy, &fence);
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixel(device, handle, stride, size,
+ x_loc[x], y_loc[y],
+ pixel, bpp, GTT))
+ goto fail;
+
+ printf("%s: Filling fd with %08x, reading dst\n", __func__, ~pixel);
+ if (gpu_fill(device, handle, width, height, stride, 32, gem_get_tiling(device, handle), ~pixel))
+ goto fail;
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixmap(dpy, dst_pix,
+ x_loc[x], y_loc[y],
+ ~pixel, 32))
+ goto fail;
+
+ XRenderFreePicture(dpy, src_pic);
+ XFreePixmap(dpy, src_pix);
+
+ if (dri3_create_fence(dpy, dst_pix, &fence))
+ goto fail;
+
+ printf("%s: Closed original src, filling dst with %08x, reading fd\n", __func__, pixel);
+ XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &color, 0, 0, width, height);
+ dri3_fence_sync(dpy, &fence);
+ dri3_fence_free(dpy, &fence);
+ for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+ for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+ if (!check_pixel(device, handle, stride, size,
+ x_loc[x], y_loc[y],
+ pixel, bpp, GTT))
+ goto fail;
+
+ XRenderFreePicture(dpy, dst_pic);
+ XFreePixmap(dpy, dst_pix);
+
+ gem_close(device, handle);
+
+ if (_x_error_occurred)
+ goto fail;
+
+ return 0;
+
+fail:
+ printf("%s failed at (%dx%d), depth=%d\n",
+ __func__, width, height, depth);
+ return 1;
+}
+
+static int test_bad_size(Display *dpy, int device)
+{
+ Window root = RootWindow(dpy, DefaultScreen(dpy));
+ uint32_t src;
+ int src_fd;
+ Pixmap src_pix;
+ int line = -1;
+
+ _x_error_occurred = 0;
+
+ src = gem_create(device, 4096);
+ if (!src)
+ goto fail;
+
+ src_fd = gem_export(device, src);
+ if (src_fd < 0)
+ goto fail;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 16, 16, 32,
+ dup(src_fd), 32, 16*4, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+ XFreePixmap(dpy, src_pix);
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 32, 32, 32,
+ dup(src_fd), 32, 32*4, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+ XFreePixmap(dpy, src_pix);
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 64, 64, 32,
+ dup(src_fd), 32, 64*4, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 64, 64, 32,
+ dup(src_fd), 32, 64*4, 64*64*4);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ INT16_MAX, INT16_MAX, 8,
+ dup(src_fd), 8, INT16_MAX, UINT32_MAX);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ close(src_fd);
+ gem_close(device, src);
+
+ return 0;
+
+fail:
+ printf("%s failed at line %d\n", __func__, line);
+ return 1;
+}
+
+static int test_bad_pitch(Display *dpy, int device)
+{
+ Window root = RootWindow(dpy, DefaultScreen(dpy));
+ uint32_t src;
+ int src_fd;
+ Pixmap src_pix;
+ int line = -1;
+
+ _x_error_occurred = 0;
+
+ src = gem_create(device, 4096);
+ if (!src)
+ goto fail;
+
+ src_fd = gem_export(device, src);
+ if (src_fd < 0)
+ goto fail;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 16, 16, 32,
+ dup(src_fd), 32, 16*4, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+ XFreePixmap(dpy, src_pix);
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 256, 2, 32,
+ dup(src_fd), 32, 256*4, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (_x_error_occurred)
+ goto fail;
+ XFreePixmap(dpy, src_pix);
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 256, 2, 32,
+ dup(src_fd), 32, 256, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 256, 2, 32,
+ dup(src_fd), 32, 16384, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 256, 2, 32,
+ dup(src_fd), 32, 1023, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ src_pix = dri3_create_pixmap(dpy, root,
+ 256, 2, 32,
+ dup(src_fd), 32, 1025, 4096);
+ line = __LINE__;
+ XSync(dpy, True);
+ if (!_x_error_occurred)
+ goto fail;
+ _x_error_occurred = 0;
+
+ close(src_fd);
+ gem_close(device, src);
+
+ return 0;
+
+fail:
+ printf("%s failed at line %d\n", __func__, line);
+ return 1;
+}
+
+static int
+_check_error_handler(Display *display,
+ XErrorEvent *event)
+{
+ printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
+ DisplayString(display),
+ event->serial,
+ event->error_code,
+ event->request_code,
+ event->minor_code);
+ _x_error_occurred++;
+ return False; /* ignored */
+}
+
+int main(void)
+{
+ Display *dpy;
+ int device;
+ int error = 0;
+
+ dpy = XOpenDisplay(NULL);
+ if (dpy == NULL)
+ return 77;
+
+ if (DefaultDepth(dpy, DefaultScreen(dpy)) != 24)
+ return 77;
+
+ XSetErrorHandler(_check_error_handler);
+
+ device = dri3_open(dpy);
+ if (device < 0)
+ return 127;
+
+ if (!is_intel(device))
+ return 77;
+
+ printf("Opened Intel DRI3 device\n");
+
+ error += test_bad_size(dpy, device);
+ error += test_bad_pitch(dpy, device);
+
+ error += test_shm(dpy, device, 400, 300);
+ error += test_shm(dpy, device, 300, 400);
+
+ error += test_read(dpy, device, 400, 200, GTT);
+ error += test_read(dpy, device, 4000, 20, GTT);
+ error += test_read(dpy, device, 16000, 10, GTT);
+ error += test_read(dpy, device, 30000, 10, GTT);
+
+ error += test_read(dpy, device, 200, 400, GTT);
+ error += test_read(dpy, device, 20, 4000, GTT);
+ error += test_read(dpy, device, 16, 16000, GTT);
+ error += test_read(dpy, device, 16, 30000, GTT);
+
+ error += test_read(dpy, device, 400, 200, CPU);
+ error += test_read(dpy, device, 4000, 20, CPU);
+ error += test_read(dpy, device, 16000, 10, CPU);
+ error += test_read(dpy, device, 30000, 10, CPU);
+
+ error += test_read(dpy, device, 200, 400, CPU);
+ error += test_read(dpy, device, 20, 4000, CPU);
+ error += test_read(dpy, device, 16, 16000, CPU);
+ error += test_read(dpy, device, 16, 30000, CPU);
+
+ error += test_read_after_write(dpy, device, 400, 200, 24, GTT);
+ error += test_read_after_write(dpy, device, 4000, 20, 24, GTT);
+ error += test_read_after_write(dpy, device, 16000, 10, 24, GTT);
+ error += test_read_after_write(dpy, device, 30000, 10, 24, GTT);
+ error += test_read_after_write(dpy, device, 30000, 10, 8, GTT);
+
+ error += test_read_after_write(dpy, device, 200, 400, 24, GTT);
+ error += test_read_after_write(dpy, device, 20, 4000, 24, GTT);
+ error += test_read_after_write(dpy, device, 16, 16000, 24, GTT);
+ error += test_read_after_write(dpy, device, 16, 30000, 24, GTT);
+
+ error += test_read_after_write(dpy, device, 400, 200, 24, CPU);
+ error += test_read_after_write(dpy, device, 4000, 20, 24, CPU);
+ error += test_read_after_write(dpy, device, 16000, 10, 24, CPU);
+ error += test_read_after_write(dpy, device, 30000, 10, 24, CPU);
+ error += test_read_after_write(dpy, device, 30000, 10, 8, CPU);
+
+ error += test_read_after_write(dpy, device, 200, 400, 24, CPU);
+ error += test_read_after_write(dpy, device, 20, 4000, 24, CPU);
+ error += test_read_after_write(dpy, device, 16, 16000, 24, CPU);
+ error += test_read_after_write(dpy, device, 16, 30000, 24, CPU);
+
+ error += test_dup_pixmap(dpy, device);
+
+ return !!error;
+}
diff --git a/test/dri3.c b/test/dri3.c
new file mode 100644
index 00000000..99503310
--- /dev/null
+++ b/test/dri3.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * 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 <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/xshmfence.h>
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/sync.h>
+#include <unistd.h>
+
+#include "dri3.h"
+
+Pixmap dri3_create_pixmap(Display *dpy,
+ Drawable draw,
+ int width, int height, int depth,
+ int fd, int bpp, int stride, int size)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+ xcb_pixmap_t pixmap = xcb_generate_id(c);
+ xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
+ return pixmap;
+}
+
+int dri3_create_fd(Display *dpy,
+ Pixmap pixmap,
+ int *stride)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+ xcb_dri3_buffer_from_pixmap_cookie_t cookie;
+ xcb_dri3_buffer_from_pixmap_reply_t *reply;
+
+ cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
+ reply = xcb_dri3_buffer_from_pixmap_reply(c, cookie, NULL);
+ if (!reply)
+ return -1;
+
+ if (reply->nfd != 1)
+ return -1;
+
+ *stride = reply->stride;
+ return xcb_dri3_buffer_from_pixmap_reply_fds(c, reply)[0];
+}
+
+int dri3_create_fence(Display *dpy, Pixmap pixmap, struct dri3_fence *fence)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+ struct dri3_fence f;
+ int fd;
+
+ fd = xshmfence_alloc_shm();
+ if (fd < 0)
+ return -1;
+
+ f.addr = xshmfence_map_shm(fd);
+ if (f.addr == NULL) {
+ close(fd);
+ return -1;
+ }
+
+ f.xid = xcb_generate_id(c);
+ xcb_dri3_fence_from_fd(c, pixmap, f.xid, 0, fd);
+
+ *fence = f;
+ return 0;
+}
+
+void dri3_fence_sync(Display *dpy, struct dri3_fence *fence)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+
+ xshmfence_reset(fence->addr);
+
+ xcb_sync_trigger_fence(c, fence->xid);
+ xcb_flush(c);
+
+ xshmfence_await(fence->addr);
+}
+
+void dri3_fence_free(Display *dpy, struct dri3_fence *fence)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+
+ xshmfence_unmap_shm(fence->addr);
+ xcb_sync_destroy_fence(c, fence->xid);
+}
+
+int dri3_open__full(Display *dpy, Window root, unsigned provider)
+{
+ xcb_connection_t *c = XGetXCBConnection(dpy);
+ xcb_dri3_open_cookie_t cookie;
+ xcb_dri3_open_reply_t *reply;
+
+ cookie = xcb_dri3_open(c, root, provider);
+ reply = xcb_dri3_open_reply(c, cookie, NULL);
+
+ if (!reply)
+ return -1;
+
+ if (reply->nfd != 1)
+ return -1;
+
+ return xcb_dri3_open_reply_fds(c, reply)[0];
+}
+
+int dri3_open(Display *dpy)
+{
+ return dri3_open__full(dpy, RootWindow(dpy, DefaultScreen(dpy)), None);
+}
diff --git a/test/dri3.h b/test/dri3.h
new file mode 100644
index 00000000..55d29436
--- /dev/null
+++ b/test/dri3.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * 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.
+ *
+ */
+
+#ifndef DRI3_H
+#define DRI3_H
+
+#include <X11/X.h>
+
+int dri3_open(Display *dpy);
+int dri3_open__full(Display *dpy, Window root, unsigned provider);
+
+Pixmap dri3_create_pixmap(Display *dpy,
+ Drawable draw,
+ int width, int height, int depth,
+ int fd, int bpp, int stride, int size);
+int dri3_create_fd(Display *dpy,
+ Pixmap pixmap,
+ int *stride);
+
+struct dri3_fence {
+ XID xid;
+ void *addr;
+};
+
+int dri3_create_fence(Display *dpy, Pixmap pixmap, struct dri3_fence *out);
+void dri3_fence_sync(Display *dpy, struct dri3_fence *fence);
+void dri3_fence_free(Display *dpy, struct dri3_fence *fence);
+
+#endif /* DRI3_H */