Edit

IABSD.fr/xenocara/driver/xf86-video-intel/test/present-test.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2015-04-12 19:41:54
    Hash : d031df27
    Message : Update to xf86-video-intel 2.99.916 Fixes a display bug seenby ajacoutot@, ok jsg@ and kettenis@. newer X.Org (2.99.917 or master) version cause corruption on older machines (X40, i965), probably caused by a bug in our kernel, under investigation by kettenis@.

  • driver/xf86-video-intel/test/present-test.c
  • /*
     * 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/Xlib-xcb.h>
    #include <X11/xshmfence.h>
    #include <X11/Xutil.h>
    #include <X11/Xlibint.h>
    #include <X11/extensions/randr.h>
    #include <X11/extensions/Xrandr.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 <xcb/xcb.h>
    #include <xcb/present.h>
    #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 <setjmp.h>
    #include <signal.h>
    
    #include <sys/mman.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <pciaccess.h>
    
    #include "dri3.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 uint32_t stamp;
    
    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 */
    }
    
    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 void *setup_msc(Display *dpy,  Window win)
    {
    	xcb_connection_t *c = XGetXCBConnection(dpy);
    	xcb_void_cookie_t cookie;
    	uint32_t id = xcb_generate_id(c);
    	xcb_generic_error_t *error;
    	void *q;
    
    	cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
    	q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp);
    
    	error = xcb_request_check(c, cookie);
    	assert(error == NULL);
    
    	return q;
    }
    
    static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc)
    {
    	xcb_connection_t *c = XGetXCBConnection(dpy);
    	uint64_t msc = 0;
    
    	xcb_present_notify_msc(c, win, 0, 0, 0, 0);
    	xcb_flush(c);
    
    	do {
    		xcb_present_complete_notify_event_t *ce;
    		xcb_generic_event_t *ev;
    
    		ev = xcb_wait_for_special_event(c, q);
    		if (ev == NULL)
    			break;
    
    		ce = (xcb_present_complete_notify_event_t *)ev;
    		if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP)
    			msc = ce->msc;
    		free(ev);
    	} while (msc == 0);
    
    	if (msc < last_msc) {
    		printf("Invalid MSC: was %llu, now %llu\n",
    		       (long long)last_msc, (long long)msc);
    	}
    
    	return msc;
    }
    
    static void teardown_msc(Display *dpy, void *q)
    {
    	xcb_unregister_for_special_event(XGetXCBConnection(dpy), q);
    }
    static int test_whole(Display *dpy)
    {
    	Pixmap pixmap;
    	struct dri3_fence fence;
    	Window root;
    	unsigned int width, height;
    	unsigned border, depth;
    	int x, y, ret = 1;
    
    	XGetGeometry(dpy, DefaultRootWindow(dpy),
    		     &root, &x, &y, &width, &height, &border, &depth);
    
    	if (dri3_create_fence(dpy, root, &fence))
    		return 0;
    
    	printf("Testing whole screen flip: %dx%d\n", width, height);
    	_x_error_occurred = 0;
    
    	xshmfence_reset(fence.addr);
    
    	pixmap = XCreatePixmap(dpy, root, width, height, depth);
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   root, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   None,
    			   None, /* wait fence */
    			   fence.xid,
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   0, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    
    	pixmap = XCreatePixmap(dpy, root, width, height, depth);
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   root, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   None,
    			   None, /* wait fence */
    			   None, /* sync fence */
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   0, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    	XFlush(dpy);
    
    	ret = !!xshmfence_await(fence.addr);
    	dri3_fence_free(dpy, &fence);
    
    	XSync(dpy, True);
    	ret += !!_x_error_occurred;
    
    	return ret;
    }
    
    static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
    {
    	XRRScreenResources *res;
    
    	res = XRRGetScreenResourcesCurrent(dpy, window);
    	if (res == NULL)
    		res = XRRGetScreenResources(dpy, window);
    
    	return res;
    }
    
    static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
    {
    	int i;
    
    	for (i = 0; i < res->nmode; i++) {
    		if (res->modes[i].id == id)
    			return &res->modes[i];
    	}
    
    	return NULL;
    }
    
    static int for_each_crtc(Display *dpy,
    			  int (*func)(Display *dpy,
    				      RRCrtc crtc,
    				      int width, int height,
    				      void *closure),
    			  void *closure)
    {
    	XRRScreenResources *res;
    	XRRCrtcInfo **original_crtc;
    	int i, j, err = 0;
    
    	if (!XRRQueryVersion(dpy, &i, &j))
    		return -1;
    
    	res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
    	if (res == NULL)
    		return -1;
    
    	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
    	for (i = 0; i < res->ncrtc; i++)
    		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
    
    	printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
    
    	for (i = 0; i < res->noutput; i++) {
    		XRROutputInfo *output;
    		XRRModeInfo *mode;
    
    		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
    		if (output == NULL)
    			continue;
    
    		mode = NULL;
    		if (res->nmode)
    			mode = lookup_mode(res, output->modes[0]);
    
    		for (j = 0; mode && j < output->ncrtc; j++) {
    			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
    			       i, j, (long)res->outputs[i], (long)output->crtcs[j]);
    			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
    					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
    			XSync(dpy, True);
    
    			err += func(dpy, output->crtcs[j], mode->width, mode->height, closure);
    
    			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
    					 0, 0, None, RR_Rotate_0, NULL, 0);
    			XSync(dpy, True);
    		}
    
    		XRRFreeOutputInfo(output);
    	}
    
    	for (i = 0; i < res->ncrtc; i++)
    		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
    				 original_crtc[i]->x,
    				 original_crtc[i]->y,
    				 original_crtc[i]->mode,
    				 original_crtc[i]->rotation,
    				 original_crtc[i]->outputs,
    				 original_crtc[i]->noutput);
    
    	free(original_crtc);
    	XRRFreeScreenResources(res);
    
    	return j;
    }
    
    struct test_crtc {
    	Window win;
    	int depth;
    	unsigned flags;
    
    	struct dri3_fence fence;
    	void *queue;
    	uint64_t msc;
    };
    #define SYNC 0x1
    
    static int __test_crtc(Display *dpy, RRCrtc crtc,
    		       int width, int height,
    		       void *closure)
    {
    	struct test_crtc *test = closure;
    	Pixmap pixmap;
    	int err = 0;
    
    	test->msc = check_msc(dpy, test->win, test->queue, test->msc);
    
    	if (test->flags & SYNC)
    		xshmfence_reset(test->fence.addr);
    
    	pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   test->win, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   crtc,
    			   None, /* wait fence */
    			   test->flags & SYNC ? test->fence.xid : None,
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   1, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    
    	if (test->flags & SYNC) {
    		pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
    		xcb_present_pixmap(XGetXCBConnection(dpy),
    				   test->win, pixmap,
    				   1, /* sbc */
    				   0, /* valid */
    				   0, /* update */
    				   0, /* x_off */
    				   0, /* y_off */
    				   crtc,
    				   None, /* wait fence */
    				   None, /* sync fence */
    				   XCB_PRESENT_OPTION_NONE,
    				   1, /* target msc */
    				   1, /* divisor */
    				   0, /* remainder */
    				   0, NULL);
    		XFreePixmap(dpy, pixmap);
    		XFlush(dpy);
    		err += !!xshmfence_await(test->fence.addr);
    	}
    
    	test->msc = check_msc(dpy, test->win, test->queue, test->msc);
    	return err;
    }
    
    static int test_crtc(Display *dpy, void *queue, uint64_t last_msc)
    {
    	struct test_crtc test;
    	int err = 0;
    
    	XSync(dpy, True);
    	_x_error_occurred = 0;
    
    	test.win = DefaultRootWindow(dpy);
    	test.depth = DefaultDepth(dpy, DefaultScreen(dpy));
    	if (dri3_create_fence(dpy, test.win, &test.fence))
    		return -1;
    	test.queue = queue;
    	test.msc = last_msc;
    
    	printf("Testing each crtc, without waiting for each flip\n");
    	test.flags = 0;
    	err += for_each_crtc(dpy, __test_crtc, &test);
    
    	printf("Testing each crtc, waiting for flips to complete\n");
    	test.flags = SYNC;
    	err += for_each_crtc(dpy, __test_crtc, &test);
    
    	test.msc = check_msc(dpy, test.win, test.queue, test.msc);
    	dri3_fence_free(dpy, &test.fence);
    
    	XSync(dpy, True);
    	err += !!_x_error_occurred;
    
    	if (err)
    		printf("%s: failures=%d\n", __func__, err);
    
    	return err;
    }
    
    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 test_shm(Display *dpy)
    {
    	Window win = DefaultRootWindow(dpy);
    	XShmSegmentInfo shm;
    	Pixmap pixmap;
    	Window root;
    	unsigned int width, height;
    	unsigned border, depth;
    	int x, y, ret = 1;
    
    	if (!can_use_shm(dpy))
    		return 0;
    
    	_x_error_occurred = 0;
    
    	XGetGeometry(dpy, win, &root, &x, &y,
    		     &width, &height, &border, &depth);
    
    	printf("Using %dx%d SHM\n", width, height);
    
    	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)
    		goto rmid;
    
    	shm.readOnly = False;
    	XShmAttach(dpy, &shm);
    
    	pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
    				  shm.shmaddr, &shm, width, height, 24);
    	if (_x_error_occurred)
    		goto detach;
    
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   win, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   None,
    			   None, /* wait fence */
    			   None,
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   0, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    
    	XSync(dpy, True);
    	if (_x_error_occurred)
    		goto detach;
    
    	ret = 0;
    detach:
    	XShmDetach(dpy, &shm);
    	shmdt(shm.shmaddr);
    	XSync(dpy, False);
    rmid:
    	shmctl(shm.shmid, IPC_RMID, NULL);
    	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 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 int test_dri3(Display *dpy)
    {
    	Window win = DefaultRootWindow(dpy);
    	Pixmap pixmap;
    	Window root;
    	unsigned int width, height;
    	unsigned border, depth;
    	unsigned stride, size;
    	int x, y, ret = 1;
    	int device, handle;
    	int bpp;
    
    	device = dri3_open(dpy);
    	if (device < 0)
    		return 0;
    
    	if (!is_intel(device))
    		return 0;
    
    	printf("Opened Intel DRI3 device\n");
    
    	XGetGeometry(dpy, win, &root, &x, &y,
    		     &width, &height, &border, &depth);
    
    	switch (depth) {
    	case 8: bpp = 8; break;
    	case 15: case 16: bpp = 16; break;
    	case 24: case 32: bpp = 32; break;
    	default: return 0;
    	}
    
    	stride = width * bpp/8;
    	size = PAGE_ALIGN(stride * height);
    	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
    	       width, height, stride, size);
    
    	pixmap = 0;
    	handle = gem_create(device, size);
    	if (handle) {
    		pixmap = dri3_create_pixmap(dpy, root,
    					     width, height, depth,
    					     gem_export(device, handle), bpp, stride, size);
    		gem_close(device, handle);
    	}
    	if (pixmap == 0)
    		goto fail;
    
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   win, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   None,
    			   None, /* wait fence */
    			   None,
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   0, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    
    	XSync(dpy, True);
    	if (_x_error_occurred)
    		goto fail;
    
    	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for CPU\n",
    	       width, height, stride, size);
    
    	pixmap = 0;
    	handle = gem_create(device, size);
    	if (handle) {
    		gem_set_caching(device, handle, CPU);
    		handle = dri3_create_pixmap(dpy, root,
    					     width, height, depth,
    					     gem_export(device, handle), bpp, stride, size);
    		gem_close(device, handle);
    	}
    	if (pixmap == 0)
    		goto fail;
    
    	xcb_present_pixmap(XGetXCBConnection(dpy),
    			   win, pixmap,
    			   0, /* sbc */
    			   0, /* valid */
    			   0, /* update */
    			   0, /* x_off */
    			   0, /* y_off */
    			   None,
    			   None, /* wait fence */
    			   None,
    			   XCB_PRESENT_OPTION_NONE,
    			   0, /* target msc */
    			   0, /* divisor */
    			   0, /* remainder */
    			   0, NULL);
    	XFreePixmap(dpy, pixmap);
    
    	XSync(dpy, True);
    	if (_x_error_occurred)
    		goto fail;
    
    	ret = 0;
    fail:
    	close(device);
    	return ret;
    }
    
    static int has_present(Display *dpy)
    {
    	xcb_connection_t *c = XGetXCBConnection(dpy);
    	xcb_present_query_version_reply_t *reply;
    	xcb_generic_error_t *error = NULL;
    
    	reply = xcb_present_query_version_reply(c,
    						xcb_present_query_version(c,
    									  XCB_PRESENT_MAJOR_VERSION,
    									  XCB_PRESENT_MINOR_VERSION),
    						&error);
    
    	free(reply);
    	free(error);
    
    	return reply != NULL;
    }
    
    int main(void)
    {
    	Display *dpy;
    	Window root;
    	int error = 0;
    	uint64_t last_msc;
    	void *queue;
    
    	dpy = XOpenDisplay(NULL);
    	if (dpy == NULL)
    		return 77;
    
    	if (!has_present(dpy))
    		return 77;
    
    	root = DefaultRootWindow(dpy);
    
    	signal(SIGALRM, SIG_IGN);
    	XSetErrorHandler(_check_error_handler);
    
    	queue = setup_msc(dpy, root);
    	last_msc = check_msc(dpy, root, queue, 0);
    
    	error += test_whole(dpy);
    	last_msc = check_msc(dpy, root, queue, last_msc);
    
    	error += test_crtc(dpy, queue, last_msc);
    	last_msc = check_msc(dpy, root, queue, last_msc);
    
    	error += test_shm(dpy);
    	last_msc = check_msc(dpy, root, queue, last_msc);
    
    	error += test_dri3(dpy);
    	last_msc = check_msc(dpy, root, queue, last_msc);
    
    	teardown_msc(dpy, queue);
    
    	return !!error;
    }