/* * 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 #include #include #include #include #if HAVE_X11_EXTENSIONS_SHMPROTO_H #include #elif HAVE_X11_EXTENSIONS_SHMSTR_H #include #else #error Failed to find the right header for X11 MIT-SHM protocol definitions #endif #include #include #include #include #include #include #include #include #include #include #include #include #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_G_IDS(033), INTEL_PINEVIEW_M_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_IDS(075), INTEL_VLV_IDS(071), INTEL_BDW_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; int line; 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) { line = __LINE__; goto fail; } handle = gem_import(device, fd); close(fd); if (handle == 0) { line = __LINE__; goto fail; } if (gpu_fill(device, handle, width, height, stride, 32, I915_TILING_NONE, pixel)) { line = __LINE__; 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]]); line = __LINE__; 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)) { line = __LINE__; goto fail; } if (_x_error_occurred) { line = __LINE__; 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), line %d\n", __func__, width, height, line); 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 gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) { struct drm_i915_gem_set_tiling set_tiling; set_tiling.handle = handle; set_tiling.tiling_mode = tiling; set_tiling.stride = stride; return drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0; } static int test_tiling(Display *dpy, int device) { Window root = RootWindow(dpy, DefaultScreen(dpy)); const int tiling[] = { I915_TILING_NONE, I915_TILING_X, I915_TILING_Y }; int line = -1; int t; _x_error_occurred = 0; for (t = 0; t < sizeof(tiling)/sizeof(tiling[0]); t++) { uint32_t src; int src_fd; Pixmap src_pix; src = gem_create(device, 4*4096); if (!src) { line = __LINE__; goto fail; } gem_set_tiling(device, src, tiling[t], 512); src_fd = gem_export(device, src); if (src_fd < 0) { line = __LINE__; goto fail; } src_pix = dri3_create_pixmap(dpy, root, 128, 32, 32, src_fd, 32, 512, 4*4096); XSync(dpy, True); if (_x_error_occurred) { line = __LINE__; goto fail; } XFreePixmap(dpy, src_pix); _x_error_occurred = 0; close(src_fd); gem_close(device, src); } return 0; fail: printf("%s failed with tiling %d, line %d\n", __func__, tiling[t], 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_tiling(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; }