diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2014-05-09 11:08:15 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2014-06-02 08:32:11 +0100 |
commit | 6ab6734369fbd902a23109f4c3626df9d529891c (patch) | |
tree | 36dac823ac3fe0d9108bbd1e8575b450aae3f567 | |
parent | d6240d197be1e752c0de26fbf84fc8fa8d55383c (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.ac | 3 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 14 | ||||
-rw-r--r-- | test/dri3-test.c | 1100 | ||||
-rw-r--r-- | test/dri3.c | 130 | ||||
-rw-r--r-- | test/dri3.h | 50 |
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 */ |