From f9a50de3dcc501e930de6c60983a4feb57121e7e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 17 Aug 2013 11:12:07 +0100 Subject: Introduce intel-gpu-overlay A realtime display of GPU activity. Note, this is just at the point of minimum usability... Signed-off-by: Chris Wilson --- overlay/x11/dri2.c | 169 ++++++++++++++++++++ overlay/x11/dri2.h | 1 + overlay/x11/rgb2yuv.c | 105 +++++++++++++ overlay/x11/rgb2yuv.h | 7 + overlay/x11/x11-overlay.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 665 insertions(+) create mode 100644 overlay/x11/dri2.c create mode 100644 overlay/x11/dri2.h create mode 100644 overlay/x11/rgb2yuv.c create mode 100644 overlay/x11/rgb2yuv.h create mode 100644 overlay/x11/x11-overlay.c (limited to 'overlay/x11') diff --git a/overlay/x11/dri2.c b/overlay/x11/dri2.c new file mode 100644 index 00000000..0aa0cf53 --- /dev/null +++ b/overlay/x11/dri2.c @@ -0,0 +1,169 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Soft- + * ware"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, provided that the above copyright + * notice(s) and this permission notice appear in all copies of the Soft- + * ware and that both the above copyright notice(s) and this permission + * notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- + * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY + * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN + * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- + * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- + * MANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization of + * the copyright holder. + * + * Authors: + * Kristian Høgsberg (krh@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dri2.h" + +static char dri2ExtensionName[] = DRI2_NAME; +static XExtensionInfo *dri2Info; +static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info) + +static /* const */ XExtensionHooks dri2ExtensionHooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + DRI2CloseDisplay, /* close_display */ +}; + +static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay, + dri2Info, + dri2ExtensionName, + &dri2ExtensionHooks, + 0, NULL) + +static Bool +DRI2Connect(Display *dpy, XID window, char **driverName, char **deviceName) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2ConnectReply rep; + xDRI2ConnectReq *req; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Connect, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Connect; + req->window = window; + req->driverType = DRI2DriverDRI; + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + *driverName = Xmalloc(rep.driverNameLength + 1); + if (*driverName == NULL) { + _XEatData(dpy, + ((rep.driverNameLength + 3) & ~3) + + ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *driverName, rep.driverNameLength); + (*driverName)[rep.driverNameLength] = '\0'; + + *deviceName = Xmalloc(rep.deviceNameLength + 1); + if (*deviceName == NULL) { + Xfree(*driverName); + _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *deviceName, rep.deviceNameLength); + (*deviceName)[rep.deviceNameLength] = '\0'; + + UnlockDisplay(dpy); + SyncHandle(); + + return True; +} + +static Bool +DRI2Authenticate(Display * dpy, XID window, unsigned int magic) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2AuthenticateReq *req; + xDRI2AuthenticateReply rep; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Authenticate, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Authenticate; + req->window = window; + req->magic = magic; + + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + UnlockDisplay(dpy); + SyncHandle(); + + return rep.authenticated; +} + +int dri2_open(Display *dpy) +{ + drm_auth_t auth; + char *driver, *device; + int fd; + + if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) + return -1; + + fd = open(device, O_RDWR); + if (fd < 0) + return -1; + + if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) + return -1; + + if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic)) + return -1; + + return fd; +} diff --git a/overlay/x11/dri2.h b/overlay/x11/dri2.h new file mode 100644 index 00000000..cb66b469 --- /dev/null +++ b/overlay/x11/dri2.h @@ -0,0 +1 @@ +int dri2_open(Display *dpy); diff --git a/overlay/x11/rgb2yuv.c b/overlay/x11/rgb2yuv.c new file mode 100644 index 00000000..8e0c080e --- /dev/null +++ b/overlay/x11/rgb2yuv.c @@ -0,0 +1,105 @@ +#include +#include + +#include "rgb2yuv.h" + +static int RGB2YUV_YR[256], RGB2YUV_YG[256], RGB2YUV_YB[256]; +static int RGB2YUV_UR[256], RGB2YUV_UG[256], RGB2YUV_UBVR[256]; +static int RGB2YUV_VG[256], RGB2YUV_VB[256]; + +void rgb2yuv_init(void) +{ + int i; + + for (i = 0; i < 256; i++) + RGB2YUV_YR[i] = 65.481 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_YG[i] = 128.553 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_YB[i] = 24.966 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UR[i] = 37.797 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UG[i] = 74.203 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_VG[i] = 93.786 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_VB[i] = 18.214 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UBVR[i] = 112 * (i << 8); +} + +int rgb2yuv(cairo_surface_t *surface, XvImage *image, uint8_t *yuv) +{ + uint8_t *data = cairo_image_surface_get_data(surface); + int rgb_stride = cairo_image_surface_get_stride(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + int y_stride = image->pitches[0]; + int uv_stride = image->pitches[1]; + uint8_t *tmp, *tl, *tr, *bl, *br; + int i, j; + + tmp = malloc(2*width*height); + if (tmp == NULL) + return 0; + + tl = tmp; + bl = tmp + width*height; + + for (i = 0; i < height; i++) { + uint16_t *rgb = (uint16_t *)(data + i * rgb_stride); + for (j = 0; j < width; j++) { + uint8_t r = (rgb[j] >> 11) & 0x1f; + uint8_t g = (rgb[j] >> 5) & 0x3f; + uint8_t b = (rgb[j] >> 0) & 0x1f; + + r = r<<3 | r>>2; + g = g<<2 | g>>4; + b = b<<3 | b>>2; + + yuv[j] = (RGB2YUV_YR[r] + RGB2YUV_YG[g] + RGB2YUV_YB[b] + 1048576) >> 16; + *tl++ = (-RGB2YUV_UR[r] - RGB2YUV_UG[g] + RGB2YUV_UBVR[b] + 8388608) >> 16; + *bl++ = (RGB2YUV_UBVR[r] - RGB2YUV_VG[g] - RGB2YUV_VB[b] + 8388608) >> 16; + } + yuv += y_stride; + } + + tl = tmp; tr = tl + 1; + bl = tl + width; br = bl + 1; + for (i = 0; i < height/2; i ++) { + for (j = 0; j < width/2; j ++) { + yuv[j] = ((int)*tl + *tr + *bl + *br) >> 2; + tl += 2; tr += 2; + bl += 2; br += 2; + } + yuv += uv_stride; + + tl += width; tr += width; + bl += width; br += width; + } + + tl = tmp + width*height; tr = tl + 1; + bl = tl + width; br = bl + 1; + for (i = 0; i < height/2; i++) { + for (j = 0; j < width/2; j++) { + yuv[j] = ((int)*tl + *tr + *bl + *br) >> 2; + tl += 2; tr += 2; + bl += 2; br += 2; + } + yuv += uv_stride; + + tl += width; tr += width; + bl += width; br += width; + } + + free(tmp); + return 1; +} diff --git a/overlay/x11/rgb2yuv.h b/overlay/x11/rgb2yuv.h new file mode 100644 index 00000000..835899c5 --- /dev/null +++ b/overlay/x11/rgb2yuv.h @@ -0,0 +1,7 @@ +#include +#include +#include +#include + +void rgb2yuv_init(void); +int rgb2yuv(cairo_surface_t *rgb, XvImage *image, uint8_t *yuv); diff --git a/overlay/x11/x11-overlay.c b/overlay/x11/x11-overlay.c new file mode 100644 index 00000000..b9144087 --- /dev/null +++ b/overlay/x11/x11-overlay.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../overlay.h" +#include "dri2.h" +#include "rgb2yuv.h" + +#ifndef ALIGN +#define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1)) +#endif + +#define FOURCC_XVMC (('C' << 24) + ('M' << 16) + ('V' << 8) + 'X') +#define FOURCC_RGB565 ((16 << 24) + ('B' << 16) + ('G' << 8) + 'R') +#define FOURCC_RGB888 ((24 << 24) + ('B' << 16) + ('G' << 8) + 'R') + +struct x11_overlay { + struct overlay base; + Display *dpy; + GC gc; + XvPortID port; + XvImage *image; + void *map, *mem; + int size; + unsigned name; + int x, y; + int visible; +}; +static inline struct x11_overlay *to_x11_overlay(struct overlay *o) +{ + return (struct x11_overlay *)o; +} + +static int noop(Display *dpy, XErrorEvent *event) +{ + return 0; +} + +static void x11_overlay_show(struct overlay *overlay) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + + if (priv->image->id == FOURCC_XVMC) + rgb2yuv(priv->base.surface, priv->image, priv->map); + else + memcpy(priv->map, priv->mem, priv->size); + + if (!priv->visible) { + XvPutImage(priv->dpy, priv->port, DefaultRootWindow(priv->dpy), + priv->gc, priv->image, + 0, 0, + priv->image->width, priv->image->height, + priv->x, priv->y, + priv->image->width, priv->image->height); + XFlush(priv->dpy); + priv->visible = true; + } +} + +static void x11_overlay_position(struct overlay *overlay, + enum position p) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + Screen *scr = ScreenOfDisplay(priv->dpy, DefaultScreen(priv->dpy)); + + switch (p & 7) { + default: + case 0: priv->x = 0; break; + case 1: priv->x = (scr->width - priv->image->width)/2; break; + case 2: priv->x = scr->width - priv->image->width; break; + } + + switch ((p >> 4) & 7) { + default: + case 0: priv->y = 0; break; + case 1: priv->y = (scr->height - priv->image->height)/2; break; + case 2: priv->y = scr->height - priv->image->height; break; + } + + if (priv->visible) { + XvPutImage(priv->dpy, priv->port, DefaultRootWindow(priv->dpy), + priv->gc, priv->image, + 0, 0, + priv->image->width, priv->image->height, + priv->x, priv->y, + priv->image->width, priv->image->height); + XFlush(priv->dpy); + } +} + +static void x11_overlay_hide(struct overlay *overlay) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + if (priv->visible) { + XClearWindow(priv->dpy, DefaultRootWindow(priv->dpy)); + XFlush(priv->dpy); + priv->visible = false; + } +} + +static void x11_overlay_destroy(void *data) +{ + struct x11_overlay *priv = data; + munmap(priv->map, priv->size); + free(priv->mem); + XCloseDisplay(priv->dpy); + free(priv); +} + +cairo_surface_t * +x11_overlay_create(enum position position, int max_width, int max_height) +{ + Display *dpy; + Screen *scr; + cairo_surface_t *surface; + struct drm_i915_gem_create create; + struct drm_gem_flink flink; + struct drm_i915_gem_mmap_gtt map; + struct x11_overlay *priv; + unsigned int count, i, j; + int fd, w, h; + XvAdaptorInfo *info; + XvImage *image; + XvPortID port = -1; + void *ptr, *mem; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return NULL; + + scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + + fd = dri2_open(dpy); + if (fd < 0) + goto err_dpy; + + if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &count, &info) != Success) + goto err_fd; + + for (i = 0; i < count; i++) { + unsigned long visual = 0; + + if (info[i].num_ports != 1) + continue; + + for (j = 0; j < info[j].num_formats; j++) { + if (info[i].formats[j].depth == 24) { + visual = info[i].formats[j].visual_id; + break; + } + } + + if (visual == 0) + continue; + + port = info[i].base_id; + } + XvFreeAdaptorInfo(info); + if (port == -1) + goto err_fd; + + XSetErrorHandler(noop); + + w = scr->width; + switch (position & 7) { + default: + case 0: + case 2: w >>= 1; break; + } + if (max_width > 0 && w > max_width) + w = max_width; + + h = scr->height; + switch ((position >> 4) & 7) { + default: + case 0: + case 2: h >>= 1; break; + } + if (max_height > 0 && h > max_height) + h = max_height; + + image = XvCreateImage(dpy, port, FOURCC_RGB565, NULL, w, h); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_RGB888, NULL, w, h); + if (image == NULL) { + image = XvCreateImage(dpy, port, FOURCC_XVMC, NULL, w, h); + if (image->pitches[0] == 4) { + image->pitches[0] = ALIGN(image->width, 1024); + image->pitches[1] = ALIGN(image->width/2, 1024); + image->pitches[2] = ALIGN(image->width/2, 1024); + image->offsets[0] = 0; + image->offsets[1] = image->pitches[0] * image->height; + image->offsets[2] = image->offsets[1] + image->pitches[1] * image->height/2; + } + rgb2yuv_init(); + } + if (image == NULL) + goto err_fd; + + switch (image->id) { + case FOURCC_RGB888: + case FOURCC_RGB565: + create.size = image->pitches[0] * image->height; + break; + case FOURCC_XVMC: + create.size = image->pitches[0] * image->height; + create.size += image->pitches[1] * image->height; + break; + } + + create.handle = 0; + create.size = ALIGN(create.size, 4096); + drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + if (create.handle == 0) + goto err_image; + + flink.handle = create.handle; + if (drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink)) + goto err_create; + + map.handle = create.handle; + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map)) + goto err_create; + + ptr = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map.offset); + if (ptr == (void *)-1) + goto err_create; + + mem = malloc(create.size); + if (mem == NULL) + goto err_map; + + switch (image->id) { + default: + case FOURCC_RGB888: + i = CAIRO_FORMAT_RGB24; + j = image->pitches[0]; + break; + case FOURCC_RGB565: + i = CAIRO_FORMAT_RGB16_565; + j = image->pitches[0]; + break; + case FOURCC_XVMC: + i = CAIRO_FORMAT_RGB16_565; + j = cairo_format_stride_for_width(i, image->width); + break; + } + + surface = cairo_image_surface_create_for_data(mem, i, image->width, image->height, j); + if (cairo_surface_status(surface)) + goto err_mem; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) + goto err_surface; + + priv->base.surface = surface; + priv->base.show = x11_overlay_show; + priv->base.position = x11_overlay_position; + priv->base.hide = x11_overlay_hide; + + priv->dpy = dpy; + priv->gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL); + priv->port = port; + priv->map = ptr; + priv->mem = mem; + priv->size = create.size; + priv->name = flink.name; + priv->visible = false; + + switch (position & 7) { + default: + case 0: priv->x = 0; break; + case 1: priv->x = (scr->width - image->width)/2; break; + case 2: priv->x = scr->width - image->width; break; + } + + switch ((position >> 4) & 7) { + default: + case 0: priv->y = 0; break; + case 1: priv->y = (scr->height - image->height)/2; break; + case 2: priv->y = scr->height - image->height; break; + } + + + priv->image = image; + priv->image->data = (void *)&priv->name; + + cairo_surface_set_user_data(surface, &overlay_key, priv, x11_overlay_destroy); + + XvSetPortAttribute(dpy, port, XInternAtom(dpy, "XV_ALWAYS_ON_TOP", True), 1); + + close(fd); + return surface; + +err_surface: + cairo_surface_destroy(surface); +err_mem: + free(mem); +err_map: + munmap(ptr, create.size); +err_create: + drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &create.handle); +err_image: +err_fd: + close(fd); +err_dpy: + XCloseDisplay(dpy); + return NULL; +} + +void x11_overlay_stop(void) +{ + Display *dpy; + unsigned int count, i, j; + XvAdaptorInfo *info; + XvImage *image; + XvPortID port = -1; + uint32_t name; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return; + + if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &count, &info) != Success) + goto close; + + for (i = 0; i < count; i++) { + unsigned long visual = 0; + + if (info[i].num_ports != 1) + continue; + + for (j = 0; j < info[j].num_formats; j++) { + if (info[i].formats[j].depth == 24) { + visual = info[i].formats[j].visual_id; + break; + } + } + + if (visual == 0) + continue; + + port = info[i].base_id; + } + XvFreeAdaptorInfo(info); + if (port == -1) + goto close; + + XSetErrorHandler(noop); + + image = XvCreateImage(dpy, port, FOURCC_RGB565, NULL, 16, 16); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_RGB888, NULL, 16, 16); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_XVMC, NULL, 16, 16); + if (image == NULL) + goto close; + + name = 0; + image->data = (void *)&name; + + XvPutImage(dpy, port, DefaultRootWindow(dpy), + XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL), image, + 0, 0, + 1, 1, + 0, 0, + 1, 1); + XSync(dpy, True); + +close: + XCloseDisplay(dpy); +} -- cgit v1.2.3