Introduce intel-gpu-overlay
authorChris Wilson <chris@chris-wilson.co.uk>
Sat, 17 Aug 2013 10:12:07 +0000 (11:12 +0100)
committerChris Wilson <chris@chris-wilson.co.uk>
Sat, 17 Aug 2013 10:21:51 +0000 (11:21 +0100)
A realtime display of GPU activity. Note, this is just at the point of
minimum usability...

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
21 files changed:
Makefile.am
configure.ac
overlay/.gitignore [new file with mode: 0644]
overlay/Makefile.am [new file with mode: 0644]
overlay/README [new file with mode: 0644]
overlay/chart.c [new file with mode: 0644]
overlay/chart.h [new file with mode: 0644]
overlay/gem-objects.c [new file with mode: 0644]
overlay/gem-objects.h [new file with mode: 0644]
overlay/gpu-top.c [new file with mode: 0644]
overlay/gpu-top.h [new file with mode: 0644]
overlay/i915_pciids.h [new file with mode: 0644]
overlay/igfx.c [new file with mode: 0644]
overlay/igfx.h [new file with mode: 0644]
overlay/overlay.c [new file with mode: 0644]
overlay/overlay.h [new file with mode: 0644]
overlay/x11/dri2.c [new file with mode: 0644]
overlay/x11/dri2.h [new file with mode: 0644]
overlay/x11/rgb2yuv.c [new file with mode: 0644]
overlay/x11/rgb2yuv.h [new file with mode: 0644]
overlay/x11/x11-overlay.c [new file with mode: 0644]

index c4c8de1..d7a479c 100644 (file)
@@ -21,7 +21,7 @@
 
 ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4
 
-SUBDIRS = lib man tools scripts benchmarks demos
+SUBDIRS = lib man tools scripts benchmarks demos overlay
 
 if BUILD_ASSEMBLER
 SUBDIRS += assembler
index 6f23231..d6c4cc6 100644 (file)
@@ -72,6 +72,9 @@ AC_SUBST(ASSEMBLER_WARN_CFLAGS)
 
 PKG_CHECK_MODULES(DRM, [libdrm_intel >= 2.4.45 libdrm])
 PKG_CHECK_MODULES(PCIACCESS, [pciaccess >= 0.10])
+PKG_CHECK_MODULES(OVERLAY, [xv x11 xext], enable_overlay=yes, enable_overlay=no)
+
+AM_CONDITIONAL(BUILD_OVERLAY, [test "x$enable_overlay" = xyes])
 
 # for testdisplay
 PKG_CHECK_MODULES(CAIRO, [cairo >= 1.12.0])
@@ -184,6 +187,7 @@ AC_CONFIG_FILES([
                 assembler/doc/Makefile
                 assembler/test/Makefile
                 assembler/intel-gen4asm.pc
+                overlay/Makefile
                 ])
 AC_OUTPUT
 
@@ -200,6 +204,7 @@ echo " • Tools:"
 echo "       Assembler          : ${enable_assembler}"
 echo "       Debugger           : ${enable_debugger}"
 echo "       Python dumper      : ${DUMPER}"
+echo "       Overlay            : ${enable_overlay}"
 echo ""
 
 # vim: set ft=config ts=8 sw=8 tw=0 noet :
diff --git a/overlay/.gitignore b/overlay/.gitignore
new file mode 100644 (file)
index 0000000..0aa8184
--- /dev/null
@@ -0,0 +1 @@
+intel-gpu-overlay
diff --git a/overlay/Makefile.am b/overlay/Makefile.am
new file mode 100644 (file)
index 0000000..10820bd
--- /dev/null
@@ -0,0 +1,28 @@
+if BUILD_OVERLAY
+bin_PROGRAMS = intel-gpu-overlay
+endif
+
+AM_CPPFLAGS = -I.
+AM_CFLAGS = $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) $(CAIRO_CFLAGS) $(OVERLAY_CFLAGS)
+LDADD = $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(OVERLAY_LIBS)
+
+intel_gpu_overlay_SOURCES = \
+       i915_pciids.h \
+       overlay.h \
+       overlay.c \
+       chart.h \
+       chart.c \
+       gem-objects.h \
+       gem-objects.c \
+       gpu-top.h \
+       gpu-top.c \
+       igfx.h \
+       igfx.c \
+       x11/dri2.c \
+       x11/dri2.h \
+       x11/rgb2yuv.c \
+       x11/rgb2yuv.h \
+       x11/x11-overlay.c \
+       $(NULL)
+
+EXTRA_DIST=README
diff --git a/overlay/README b/overlay/README
new file mode 100644 (file)
index 0000000..c4ed970
--- /dev/null
@@ -0,0 +1,8 @@
+This is a simple overlay showing current GPU activity. An asynchronous
+overlay is used, rendered by the CPU to avoid introducing any extra work
+on the GPU that we wish to monitor.
+
+The x11-overlay backend requires xf86-video-intel 2.21.15 or later, with
+SNA enabled.
+
+As it requires access to debug information, it needs to be run as root.
diff --git a/overlay/chart.c b/overlay/chart.c
new file mode 100644 (file)
index 0000000..89b3f41
--- /dev/null
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <cairo.h>
+
+#include <stdio.h>
+
+#include "chart.h"
+
+int chart_init(struct chart *chart, const char *name, int num_samples)
+{
+       memset(chart, 0, sizeof(*chart));
+       chart->name = name;
+       chart->samples = malloc(sizeof(*chart->samples)*num_samples);
+       if (chart->samples == NULL)
+               return ENOMEM;
+
+       chart->num_samples = num_samples;
+       chart->range_automatic = 1;
+       return 0;
+}
+
+void chart_set_rgba(struct chart *chart, float red, float green, float blue, float alpha)
+{
+       chart->rgb[0] = red;
+       chart->rgb[1] = green;
+       chart->rgb[2] = blue;
+       chart->rgb[3] = alpha;
+}
+
+void chart_set_position(struct chart *chart, int x, int y)
+{
+       chart->x = x;
+       chart->y = y;
+}
+
+void chart_set_size(struct chart *chart, int w, int h)
+{
+       chart->w = w;
+       chart->h = h;
+}
+
+void chart_set_range(struct chart *chart, double min, double max)
+{
+       chart->range[0] = min;
+       chart->range[1] = max;
+       chart->range_automatic = 0;
+}
+
+void chart_add_sample(struct chart *chart, double value)
+{
+       int pos;
+
+       pos = chart->current_sample % chart->num_samples;
+       chart->samples[pos] = value;
+       chart->current_sample++;
+}
+
+static void chart_update_range(struct chart *chart)
+{
+       int n, max = chart->current_sample;
+       if (max > chart->num_samples)
+               max = chart->num_samples;
+       chart->range[0] = chart->range[1] = chart->samples[0];
+       for (n = 1; n < max; n++) {
+               if (chart->samples[n] < chart->range[0])
+                       chart->range[0] = chart->samples[n];
+               else if (chart->samples[n] > chart->range[1])
+                       chart->range[1] = chart->samples[n];
+       }
+}
+
+static double value_at(struct chart *chart, int n)
+{
+       if (n <= chart->current_sample - chart->num_samples)
+               n = chart->current_sample;
+       else if (n >= chart->current_sample)
+               n = chart->current_sample - 1;
+
+       return chart->samples[n % chart->num_samples];
+}
+
+static double gradient_at(struct chart *chart, int n)
+{
+       double y0, y1;
+
+       y0 = value_at(chart, n-1);
+       y1 = value_at(chart, n+1);
+
+       return (y1 - y0) / 2.;
+}
+
+void chart_draw(struct chart *chart, cairo_t *cr)
+{
+       int i, n, max, x;
+
+       if (chart->current_sample == 0)
+               return;
+
+       if (chart->range_automatic)
+               chart_update_range(chart);
+
+       cairo_save(cr);
+
+       cairo_translate(cr, chart->x, chart->y + chart->h);
+       cairo_scale(cr,
+                   chart->w / (double)chart->num_samples,
+                   -chart->h / (chart->range[1] - chart->range[0]));
+
+       x = 0;
+       max = chart->current_sample;
+       if (max >= chart->num_samples) {
+               max = chart->num_samples;
+               i = chart->current_sample - max;
+       } else {
+               i = 0;
+               x = chart->num_samples - max;
+       }
+       cairo_translate(cr, x, -chart->range[0]);
+
+       cairo_new_path(cr);
+       for (n = 0; n < max; n++) {
+               cairo_curve_to(cr,
+                              n-2/3., value_at(chart, i + n -1) + gradient_at(chart, i + n - 1)/3.,
+                              n-1/3., value_at(chart, i + n) - gradient_at(chart, i + n)/3.,
+                              n, value_at(chart, i + n));
+       }
+
+       cairo_identity_matrix(cr);
+       cairo_set_line_width(cr, 1);
+       cairo_set_source_rgba(cr, chart->rgb[0], chart->rgb[1], chart->rgb[2], chart->rgb[3]);
+       cairo_stroke(cr);
+
+       cairo_restore(cr);
+}
diff --git a/overlay/chart.h b/overlay/chart.h
new file mode 100644 (file)
index 0000000..c0f0065
--- /dev/null
@@ -0,0 +1,18 @@
+struct chart {
+       const char *name;
+       int x, y, w, h;
+       int num_samples;
+       int current_sample;
+       int range_automatic;
+       float rgb[4];
+       double range[2];
+       double *samples;
+};
+
+int chart_init(struct chart *chart, const char *name, int num_samples);
+void chart_set_rgba(struct chart *chart, float red, float green, float blue, float alpha);
+void chart_set_position(struct chart *chart, int x, int y);
+void chart_set_size(struct chart *chart, int w, int h);
+void chart_set_range(struct chart *chart, double min, double max);
+void chart_add_sample(struct chart *chart, double value);
+void chart_draw(struct chart *chart, cairo_t *cr);
diff --git a/overlay/gem-objects.c b/overlay/gem-objects.c
new file mode 100644 (file)
index 0000000..37d6726
--- /dev/null
@@ -0,0 +1,19 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "gem-objects.h"
+
+int gem_objects_update(char *buf, int buflen)
+{
+       int fd, len = -1;
+
+       fd = open("/sys/kernel/debug/dri/0/i915_gem_objects", 0);
+       if (fd >= 0) {
+               len = read(fd, buf, buflen-1);
+               if (len >= 0)
+                       buf[len] = '\0';
+               close(fd);
+       }
+
+       return len;
+}
diff --git a/overlay/gem-objects.h b/overlay/gem-objects.h
new file mode 100644 (file)
index 0000000..898d18f
--- /dev/null
@@ -0,0 +1 @@
+int gem_objects_update(char *buf, int buflen);
diff --git a/overlay/gpu-top.c b/overlay/gpu-top.c
new file mode 100644 (file)
index 0000000..400cbc5
--- /dev/null
@@ -0,0 +1,178 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "igfx.h"
+#include "gpu-top.h"
+
+#define RING_TAIL      0x00
+#define RING_HEAD      0x04
+#define ADDR_MASK      0x001FFFFC
+#define RING_CTL       0x0C
+#define   RING_WAIT            (1<<11)
+#define   RING_WAIT_SEMAPHORE  (1<<10)
+
+struct ring {
+       int id;
+       uint32_t mmio;
+       int idle, wait, sema;
+};
+
+static void *mmio;
+
+static uint32_t ring_read(struct ring *ring, uint32_t reg)
+{
+       return igfx_read(mmio, ring->mmio + reg);
+}
+
+static void ring_init(struct ring *ring)
+{
+       uint32_t ctl;
+
+       ctl = ring_read(ring, RING_CTL);
+       if ((ctl & 1) == 0)
+               ring->id = -1;
+}
+
+static void ring_reset(struct ring *ring)
+{
+       ring->idle = 0;
+       ring->wait = 0;
+       ring->sema = 0;
+}
+
+static void ring_sample(struct ring *ring)
+{
+       uint32_t head, tail, ctl;
+
+       if (ring->id == -1)
+               return;
+
+       head = ring_read(ring, RING_HEAD) & ADDR_MASK;
+       tail = ring_read(ring, RING_TAIL) & ADDR_MASK;
+       ring->idle += head == tail;
+
+       ctl = ring_read(ring, RING_CTL);
+       ring->wait += !!(ctl & RING_WAIT);
+       ring->sema += !!(ctl & RING_WAIT_SEMAPHORE);
+}
+
+static void ring_emit(struct ring *ring, int samples, union gpu_top_payload *payload)
+{
+       if (ring->id == -1)
+               return;
+
+       payload[ring->id].u.busy = 100 - 100 * ring->idle / samples;
+       payload[ring->id].u.wait = 100 * ring->wait / samples;
+       payload[ring->id].u.sema = 100 * ring->sema / samples;
+}
+
+void gpu_top_init(struct gpu_top *gt)
+{
+       struct ring render_ring = {
+               .mmio = 0x2030,
+               .id = 0,
+       }, bsd_ring = {
+               .mmio = 0x4030,
+               .id = 1,
+       }, bsd6_ring = {
+               .mmio = 0x12030,
+               .id = 1,
+       }, blt_ring = {
+               .mmio = 0x22030,
+               .id = 2,
+       };
+       const struct igfx_info *info;
+       struct pci_device *igfx;
+       int fd[2], i;
+
+       memset(gt, 0, sizeof(*gt));
+       gt->fd = -1;
+
+       igfx = igfx_get();
+       if (!igfx)
+               return;
+
+       if (pipe(fd) < 0)
+               return;
+
+       info = igfx_get_info(igfx);
+
+       switch (fork()) {
+       case -1: return;
+       default:
+                fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK);
+                gt->fd = fd[0];
+                gt->ring[0].name = "render";
+                gt->num_rings = 1;
+                if (info->gen >= 040) {
+                        gt->ring[1].name = "bitstream";
+                        gt->num_rings++;
+                }
+                if (info->gen >= 060) {
+                        gt->ring[2].name = "blt";
+                        gt->num_rings++;
+                }
+                close(fd[1]);
+                return;
+       case 0:
+                close(fd[0]);
+                break;
+       }
+
+       mmio = igfx_get_mmio(igfx);
+
+       ring_init(&render_ring);
+       if (info->gen >= 060) {
+               ring_init(&bsd6_ring);
+               ring_init(&blt_ring);
+       } else if (info->gen >= 040) {
+               ring_init(&bsd_ring);
+       }
+
+       for (;;) {
+               union gpu_top_payload payload[MAX_RINGS];
+
+               ring_reset(&render_ring);
+               ring_reset(&bsd_ring);
+               ring_reset(&bsd6_ring);
+               ring_reset(&blt_ring);
+
+               for (i = 0; i < 1000; i++) {
+                       ring_sample(&render_ring);
+                       ring_sample(&bsd_ring);
+                       ring_sample(&bsd6_ring);
+                       ring_sample(&blt_ring);
+                       usleep(1000);
+               }
+
+               ring_emit(&render_ring, 1000, payload);
+               ring_emit(&bsd_ring, 1000, payload);
+               ring_emit(&bsd6_ring, 1000, payload);
+               ring_emit(&blt_ring, 1000, payload);
+
+               write(fd[1], payload, sizeof(payload));
+       }
+}
+
+int gpu_top_update(struct gpu_top *gt)
+{
+       uint32_t data[1024];
+       int len, update = 0;
+
+       if (gt->fd < 0)
+               return update;
+
+       while ((len = read(gt->fd, data, sizeof(data))) > 0) {
+               uint32_t *ptr = &data[len/sizeof(uint32_t) - MAX_RINGS];
+               gt->ring[0].u.payload = ptr[0];
+               gt->ring[1].u.payload = ptr[1];
+               gt->ring[2].u.payload = ptr[2];
+               gt->ring[3].u.payload = ptr[3];
+               update = 1;
+       }
+
+       return update;
+}
diff --git a/overlay/gpu-top.h b/overlay/gpu-top.h
new file mode 100644 (file)
index 0000000..78f4daf
--- /dev/null
@@ -0,0 +1,22 @@
+#define MAX_RINGS 4
+
+#include <stdint.h>
+
+struct gpu_top {
+       int fd;
+       int num_rings;
+       struct gpu_top_ring {
+               const char *name;
+               union gpu_top_payload {
+                       struct {
+                               uint8_t busy;
+                               uint8_t wait;
+                               uint8_t sema;
+                       } u;
+                       uint32_t payload;
+               } u;
+       } ring[MAX_RINGS];
+};
+
+void gpu_top_init(struct gpu_top *gt);
+int gpu_top_update(struct gpu_top *gt);
diff --git a/overlay/i915_pciids.h b/overlay/i915_pciids.h
new file mode 100644 (file)
index 0000000..8a10f5c
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2013 Intel Corporation
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 _I915_PCIIDS_H
+#define _I915_PCIIDS_H
+
+/*
+ * A pci_device_id struct {
+ *     __u32 vendor, device;
+ *      __u32 subvendor, subdevice;
+ *     __u32 class, class_mask;
+ *     kernel_ulong_t driver_data;
+ * };
+ * Don't use C99 here because "class" is reserved and we want to
+ * give userspace flexibility.
+ */
+#define INTEL_VGA_DEVICE(id, info) {           \
+       0x8086, id,                             \
+       ~0, ~0,                                 \
+       0x030000, 0xff0000,                     \
+       (unsigned long) info }
+
+#define INTEL_QUANTA_VGA_DEVICE(info) {                \
+       0x8086, 0x16a,                          \
+       0x152d, 0x8990,                         \
+       0x030000, 0xff0000,                     \
+       (unsigned long) info }
+
+#define INTEL_I830_IDS(info)                           \
+       INTEL_VGA_DEVICE(0x3577, info)
+
+#define INTEL_I845G_IDS(info)                          \
+       INTEL_VGA_DEVICE(0x2562, info)
+
+#define INTEL_I85X_IDS(info)                           \
+       INTEL_VGA_DEVICE(0x3582, info), /* I855_GM */ \
+       INTEL_VGA_DEVICE(0x358e, info)
+
+#define INTEL_I865G_IDS(info)                          \
+       INTEL_VGA_DEVICE(0x2572, info) /* I865_G */
+
+#define INTEL_I915G_IDS(info)                          \
+       INTEL_VGA_DEVICE(0x2582, info), /* I915_G */ \
+       INTEL_VGA_DEVICE(0x258a, info)  /* E7221_G */
+
+#define INTEL_I915GM_IDS(info)                         \
+       INTEL_VGA_DEVICE(0x2592, info) /* I915_GM */
+
+#define INTEL_I945G_IDS(info)                          \
+       INTEL_VGA_DEVICE(0x2772, info) /* I945_G */
+
+#define INTEL_I945GM_IDS(info)                         \
+       INTEL_VGA_DEVICE(0x27a2, info), /* I945_GM */ \
+       INTEL_VGA_DEVICE(0x27ae, info)  /* I945_GME */
+
+#define INTEL_I965G_IDS(info)                          \
+       INTEL_VGA_DEVICE(0x2972, info), /* I946_GZ */   \
+       INTEL_VGA_DEVICE(0x2982, info), /* G35_G */     \
+       INTEL_VGA_DEVICE(0x2992, info), /* I965_Q */    \
+       INTEL_VGA_DEVICE(0x29a2, info)  /* I965_G */
+
+#define INTEL_G33_IDS(info)                            \
+       INTEL_VGA_DEVICE(0x29b2, info), /* Q35_G */ \
+       INTEL_VGA_DEVICE(0x29c2, info), /* G33_G */ \
+       INTEL_VGA_DEVICE(0x29d2, info)  /* Q33_G */
+
+#define INTEL_I965GM_IDS(info)                         \
+       INTEL_VGA_DEVICE(0x2a02, info), /* I965_GM */ \
+       INTEL_VGA_DEVICE(0x2a12, info)  /* I965_GME */
+
+#define INTEL_GM45_IDS(info)                           \
+       INTEL_VGA_DEVICE(0x2a42, info) /* GM45_G */
+
+#define INTEL_G45_IDS(info)                            \
+       INTEL_VGA_DEVICE(0x2e02, info), /* IGD_E_G */ \
+       INTEL_VGA_DEVICE(0x2e12, info), /* Q45_G */ \
+       INTEL_VGA_DEVICE(0x2e22, info), /* G45_G */ \
+       INTEL_VGA_DEVICE(0x2e32, info), /* G41_G */ \
+       INTEL_VGA_DEVICE(0x2e42, info), /* B43_G */ \
+       INTEL_VGA_DEVICE(0x2e92, info)  /* B43_G.1 */
+
+#define INTEL_PINEVIEW_IDS(info)                       \
+       INTEL_VGA_DEVICE(0xa001, info),                 \
+       INTEL_VGA_DEVICE(0xa011, info)
+
+#define INTEL_IRONLAKE_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0042, info)
+
+#define INTEL_IRONLAKE_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0046, info)
+
+#define INTEL_SNB_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0102, info), \
+       INTEL_VGA_DEVICE(0x0112, info), \
+       INTEL_VGA_DEVICE(0x0122, info), \
+       INTEL_VGA_DEVICE(0x010A, info)
+
+#define INTEL_SNB_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0106, info), \
+       INTEL_VGA_DEVICE(0x0116, info), \
+       INTEL_VGA_DEVICE(0x0126, info)
+
+#define INTEL_IVB_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0156, info), /* GT1 mobile */ \
+       INTEL_VGA_DEVICE(0x0166, info)  /* GT2 mobile */
+
+#define INTEL_IVB_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0152, info), /* GT1 desktop */ \
+       INTEL_VGA_DEVICE(0x0162, info), /* GT2 desktop */ \
+       INTEL_VGA_DEVICE(0x015a, info), /* GT1 server */ \
+       INTEL_VGA_DEVICE(0x016a, info)  /* GT2 server */
+
+#define INTEL_IVB_Q_IDS(info) \
+       INTEL_QUANTA_VGA_DEVICE(info) /* Quanta transcode */
+
+#define INTEL_HSW_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0402, info), /* GT1 desktop */ \
+       INTEL_VGA_DEVICE(0x0412, info), /* GT2 desktop */ \
+       INTEL_VGA_DEVICE(0x0422, info), /* GT3 desktop */ \
+       INTEL_VGA_DEVICE(0x040a, info), /* GT1 server */ \
+       INTEL_VGA_DEVICE(0x041a, info), /* GT2 server */ \
+       INTEL_VGA_DEVICE(0x042a, info), /* GT3 server */ \
+       INTEL_VGA_DEVICE(0x040B, info), /* GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x041B, info), /* GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x042B, info), /* GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x040E, info), /* GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x041E, info), /* GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x042E, info), /* GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0C02, info), /* SDV GT1 desktop */ \
+       INTEL_VGA_DEVICE(0x0C12, info), /* SDV GT2 desktop */ \
+       INTEL_VGA_DEVICE(0x0C22, info), /* SDV GT3 desktop */ \
+       INTEL_VGA_DEVICE(0x0C0A, info), /* SDV GT1 server */ \
+       INTEL_VGA_DEVICE(0x0C1A, info), /* SDV GT2 server */ \
+       INTEL_VGA_DEVICE(0x0C2A, info), /* SDV GT3 server */ \
+       INTEL_VGA_DEVICE(0x0C0B, info), /* SDV GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0C1B, info), /* SDV GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0C2B, info), /* SDV GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0C0E, info), /* SDV GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0C1E, info), /* SDV GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0C2E, info), /* SDV GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0A02, info), /* ULT GT1 desktop */ \
+       INTEL_VGA_DEVICE(0x0A12, info), /* ULT GT2 desktop */ \
+       INTEL_VGA_DEVICE(0x0A22, info), /* ULT GT3 desktop */ \
+       INTEL_VGA_DEVICE(0x0A0A, info), /* ULT GT1 server */ \
+       INTEL_VGA_DEVICE(0x0A1A, info), /* ULT GT2 server */ \
+       INTEL_VGA_DEVICE(0x0A2A, info), /* ULT GT3 server */ \
+       INTEL_VGA_DEVICE(0x0A0B, info), /* ULT GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0A1B, info), /* ULT GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0A2B, info), /* ULT GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0D02, info), /* CRW GT1 desktop */ \
+       INTEL_VGA_DEVICE(0x0D12, info), /* CRW GT2 desktop */ \
+       INTEL_VGA_DEVICE(0x0D22, info), /* CRW GT3 desktop */ \
+       INTEL_VGA_DEVICE(0x0D0A, info), /* CRW GT1 server */ \
+       INTEL_VGA_DEVICE(0x0D1A, info), /* CRW GT2 server */ \
+       INTEL_VGA_DEVICE(0x0D2A, info), /* CRW GT3 server */ \
+       INTEL_VGA_DEVICE(0x0D0B, info), /* CRW GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0D1B, info), /* CRW GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0D2B, info), /* CRW GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0D0E, info), /* CRW GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0D1E, info), /* CRW GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0D2E, info)  /* CRW GT3 reserved */ \
+
+#define INTEL_HSW_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0406, info), /* GT1 mobile */ \
+       INTEL_VGA_DEVICE(0x0416, info), /* GT2 mobile */ \
+       INTEL_VGA_DEVICE(0x0426, info), /* GT2 mobile */ \
+       INTEL_VGA_DEVICE(0x0C06, info), /* SDV GT1 mobile */ \
+       INTEL_VGA_DEVICE(0x0C16, info), /* SDV GT2 mobile */ \
+       INTEL_VGA_DEVICE(0x0C26, info), /* SDV GT3 mobile */ \
+       INTEL_VGA_DEVICE(0x0A06, info), /* ULT GT1 mobile */ \
+       INTEL_VGA_DEVICE(0x0A16, info), /* ULT GT2 mobile */ \
+       INTEL_VGA_DEVICE(0x0A26, info), /* ULT GT3 mobile */ \
+       INTEL_VGA_DEVICE(0x0A0E, info), /* ULT GT1 reserved */ \
+       INTEL_VGA_DEVICE(0x0A1E, info), /* ULT GT2 reserved */ \
+       INTEL_VGA_DEVICE(0x0A2E, info), /* ULT GT3 reserved */ \
+       INTEL_VGA_DEVICE(0x0D06, info), /* CRW GT1 mobile */ \
+       INTEL_VGA_DEVICE(0x0D16, info), /* CRW GT2 mobile */ \
+       INTEL_VGA_DEVICE(0x0D26, info)  /* CRW GT3 mobile */
+
+#define INTEL_VLV_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0f30, info), \
+       INTEL_VGA_DEVICE(0x0f31, info), \
+       INTEL_VGA_DEVICE(0x0f32, info), \
+       INTEL_VGA_DEVICE(0x0f33, info), \
+       INTEL_VGA_DEVICE(0x0157, info)
+
+#define INTEL_VLV_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0155, info)
+
+#endif /* _I915_PCIIDS_H */
diff --git a/overlay/igfx.c b/overlay/igfx.c
new file mode 100644 (file)
index 0000000..7fdbb51
--- /dev/null
@@ -0,0 +1,217 @@
+#include <pciaccess.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "igfx.h"
+#include "i915_pciids.h"
+
+static const struct igfx_info generic_info = {
+       .gen = -1,
+};
+
+static const struct igfx_info i81x_info = {
+       .gen = 010,
+};
+
+static const struct igfx_info i830_info = {
+       .gen = 020,
+};
+static const struct igfx_info i845_info = {
+       .gen = 020,
+};
+static const struct igfx_info i855_info = {
+       .gen = 021,
+};
+static const struct igfx_info i865_info = {
+       .gen = 022,
+};
+
+static const struct igfx_info i915_info = {
+       .gen = 030,
+};
+static const struct igfx_info i945_info = {
+       .gen = 031,
+};
+
+static const struct igfx_info g33_info = {
+       .gen = 033,
+};
+
+static const struct igfx_info i965_info = {
+       .gen = 040,
+};
+
+static const struct igfx_info g4x_info = {
+       .gen = 045,
+};
+
+static const struct igfx_info ironlake_info = {
+       .gen = 050,
+};
+
+static const struct igfx_info sandybridge_info = {
+       .gen = 060,
+};
+
+static const struct igfx_info ivybridge_info = {
+       .gen = 070,
+};
+
+static const struct igfx_info valleyview_info = {
+       .gen = 071,
+};
+
+static const struct igfx_info haswell_info = {
+       .gen = 075,
+};
+
+static const struct pci_id_match match[] = {
+#if 0
+       INTEL_VGA_DEVICE(PCI_CHIP_I810, &i81x_info),
+       INTEL_VGA_DEVICE(PCI_CHIP_I810_DC100, &i81x_info),
+       INTEL_VGA_DEVICE(PCI_CHIP_I810_E, &i81x_info),
+       INTEL_VGA_DEVICE(PCI_CHIP_I815, &i81x_info),
+#endif
+
+       INTEL_I830_IDS(&i830_info),
+       INTEL_I845G_IDS(&i830_info),
+       INTEL_I85X_IDS(&i855_info),
+       INTEL_I865G_IDS(&i865_info),
+
+       INTEL_I915G_IDS(&i915_info),
+       INTEL_I915GM_IDS(&i915_info),
+       INTEL_I945G_IDS(&i945_info),
+       INTEL_I945GM_IDS(&i945_info),
+
+       INTEL_G33_IDS(&g33_info),
+       INTEL_PINEVIEW_IDS(&g33_info),
+
+       INTEL_I965G_IDS(&i965_info),
+       INTEL_I965GM_IDS(&i965_info),
+
+       INTEL_G45_IDS(&g4x_info),
+       INTEL_GM45_IDS(&g4x_info),
+
+       INTEL_IRONLAKE_D_IDS(&ironlake_info),
+       INTEL_IRONLAKE_M_IDS(&ironlake_info),
+
+       INTEL_SNB_D_IDS(&sandybridge_info),
+       INTEL_SNB_M_IDS(&sandybridge_info),
+
+       INTEL_IVB_D_IDS(&ivybridge_info),
+       INTEL_IVB_M_IDS(&ivybridge_info),
+
+       INTEL_HSW_D_IDS(&haswell_info),
+       INTEL_HSW_M_IDS(&haswell_info),
+
+       INTEL_VLV_D_IDS(&valleyview_info),
+       INTEL_VLV_M_IDS(&valleyview_info),
+
+       INTEL_VGA_DEVICE(PCI_MATCH_ANY, &generic_info),
+
+       { 0, 0, 0 },
+};
+
+struct pci_device *igfx_get(void)
+{
+       struct pci_device *dev;
+
+       if (pci_system_init())
+               return 0;
+
+       dev = pci_device_find_by_slot(0, 0, 2, 0);
+       if (dev == NULL || dev->vendor_id != 0x8086) {
+               struct pci_device_iterator *iter;
+
+               iter = pci_id_match_iterator_create(match);
+               if (!iter)
+                       return 0;
+
+               dev = pci_device_next(iter);
+               pci_iterator_destroy(iter);
+       }
+
+       return dev;
+}
+
+const struct igfx_info *igfx_get_info(struct pci_device *dev)
+{
+       int i;
+
+       if (!dev)
+               return 0;
+
+       for (i = 0; match[i].device_id != PCI_MATCH_ANY; i++)
+               if (dev->device_id == match[i].device_id)
+                       return (const struct igfx_info *)match[i].match_data;
+
+       return &generic_info;
+}
+
+static int forcewake = -1;
+
+static void
+igfx_forcewake(void)
+{
+       char buf[1024];
+       const char *path[] = {
+               "/sys/kernel/debug/dri/",
+               "/debug/dri/",
+               0,
+       };
+       int i, j;
+
+       for (j = 0; path[j]; j++) {
+               struct stat st;
+
+               if (stat(path[j], &st))
+                       continue;
+
+               for (i = 0; i < 16; i++) {
+                       snprintf(buf, sizeof(buf),
+                                "%s/%i/i915_forcewake_user",
+                                path[j], i);
+                       forcewake = open(buf, 0);
+                       if (forcewake != -1)
+                               return;
+               }
+       }
+}
+
+void *igfx_get_mmio(struct pci_device *dev)
+{
+       const struct igfx_info *info;
+       int mmio_bar, mmio_size;
+       void *mmio;
+
+       info = igfx_get_info(dev);
+       if (info->gen >> 3 == 2)
+               mmio_bar = 1;
+       else
+               mmio_bar = 0;
+
+       if (info->gen < 030)
+               mmio_size = 512*1024;
+       else if (info->gen < 050)
+               mmio_size = 512*1024;
+       else
+               mmio_size = 2*1024*1024;
+
+       if (pci_device_probe(dev))
+               return 0;
+
+       if (pci_device_map_range(dev,
+                                dev->regions[mmio_bar].base_addr,
+                                mmio_size,
+                                PCI_DEV_MAP_FLAG_WRITABLE,
+                                &mmio))
+               return 0;
+
+       if (info->gen >= 060)
+               igfx_forcewake();
+
+       return mmio;
+}
+
diff --git a/overlay/igfx.h b/overlay/igfx.h
new file mode 100644 (file)
index 0000000..36e971b
--- /dev/null
@@ -0,0 +1,15 @@
+struct igfx_info {
+       int gen;
+};
+
+struct pci_device;
+
+struct pci_device *igfx_get(void);
+const struct igfx_info *igfx_get_info(struct pci_device *pci_dev);
+void *igfx_get_mmio(struct pci_device *pci_dev);
+
+static inline uint32_t
+igfx_read(void *mmio, uint32_t reg)
+{
+       return *(volatile uint32_t *)((volatile char *)mmio + reg);
+}
diff --git a/overlay/overlay.c b/overlay/overlay.c
new file mode 100644 (file)
index 0000000..62639b6
--- /dev/null
@@ -0,0 +1,196 @@
+#include <X11/Xlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <cairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "overlay.h"
+#include "gpu-top.h"
+#include "gem-objects.h"
+#include "chart.h"
+
+const cairo_user_data_key_t overlay_key;
+
+static void overlay_show(cairo_surface_t *surface)
+{
+       struct overlay *overlay;
+
+       overlay = cairo_surface_get_user_data(surface, &overlay_key);
+       if (overlay == NULL)
+               return;
+
+       overlay->show(overlay);
+}
+
+#if 0
+static void overlay_position(cairo_surface_t *surface, enum position p)
+{
+       struct overlay *overlay;
+
+       overlay = cairo_surface_get_user_data(surface, &overlay_key);
+       if (overlay == NULL)
+               return;
+
+       overlay->position(overlay, p);
+}
+
+static void overlay_hide(cairo_surface_t *surface)
+{
+       struct overlay *overlay;
+
+       overlay = cairo_surface_get_user_data(surface, &overlay_key);
+       if (overlay == NULL)
+               return;
+
+       overlay->hide(overlay);
+}
+#endif
+
+struct overlay_gpu_top {
+       struct gpu_top gpu_top;
+       struct chart chart[MAX_RINGS];
+};
+
+static void init_gpu_top(struct overlay_gpu_top *gt,
+                        cairo_surface_t *surface)
+{
+       const double rgba[][4] = {
+               { 1, 0, 0, 1 },
+               { 0, 1, 0, 1 },
+               { 0, 0, 1, 1 },
+               { 1, 1, 1, 1 },
+       };
+       int n;
+
+       gpu_top_init(&gt->gpu_top);
+
+       for (n = 0; n < gt->gpu_top.num_rings; n++) {
+               chart_init(&gt->chart[n],
+                          gt->gpu_top.ring[n].name,
+                          120);
+               chart_set_position(&gt->chart[n], 12, 12);
+               chart_set_size(&gt->chart[n],
+                              cairo_image_surface_get_width(surface)-24,
+                              100);
+               chart_set_rgba(&gt->chart[n],
+                              rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3]);
+               chart_set_range(&gt->chart[n], 0, 100);
+       }
+}
+
+static void show_gpu_top(cairo_t *cr, struct overlay_gpu_top *gt)
+{
+       int y, n, update;
+
+       update = gpu_top_update(&gt->gpu_top);
+       for (n = 0; n < gt->gpu_top.num_rings; n++) {
+               if (update)
+                       chart_add_sample(&gt->chart[n],
+                                        gt->gpu_top.ring[n].u.u.busy);
+               chart_draw(&gt->chart[n], cr);
+       }
+
+       cairo_set_source_rgb(cr, 1, 1, 1);
+
+       y = 12;
+       for (n = 0; n < gt->gpu_top.num_rings; n++) {
+               char txt[160];
+               int len;
+
+               len = sprintf(txt, "%s: %d%% busy",
+                             gt->gpu_top.ring[n].name,
+                             gt->gpu_top.ring[n].u.u.busy);
+               if (gt->gpu_top.ring[n].u.u.wait)
+                       len += sprintf(txt + len, ", %d%% wait",
+                                      gt->gpu_top.ring[n].u.u.wait);
+               if (gt->gpu_top.ring[n].u.u.sema)
+                       len += sprintf(txt + len, ", %d%% sema",
+                                      gt->gpu_top.ring[n].u.u.sema);
+
+               cairo_move_to(cr, 12, y);
+               cairo_show_text(cr, txt);
+               y += 14;
+       }
+}
+
+static void show_gem_objects(cairo_t *cr)
+{
+       char gem_objects[1024], *s, *t, *end;
+       int len, y;
+
+       len = gem_objects_update(gem_objects, sizeof(gem_objects));
+       if (len <= 0)
+               return;
+
+       y = 130;
+
+       s = gem_objects;
+       end = s + len - 1;
+       while (s < end) {
+               t = strchr(s, '\n');
+               if (t == NULL)
+                       t = end;
+               *t = '\0';
+
+               cairo_move_to(cr, 12, y);
+               cairo_show_text(cr, s);
+               y += 14;
+
+               s = t+1;
+       }
+}
+
+int main(int argc, char **argv)
+{
+       cairo_surface_t *surface;
+       struct overlay_gpu_top gpu_top;
+       int i = 0;
+
+       if (argc > 1) {
+               x11_overlay_stop();
+               return 0;
+       }
+
+       surface = x11_overlay_create(POS_TOP_RIGHT, 640, 480);
+       if (surface == NULL)
+               return ENOMEM;
+
+       init_gpu_top(&gpu_top, surface);
+
+       while (1) {
+               cairo_t *cr;
+
+               usleep(500*1000);
+
+               cr = cairo_create(surface);
+               cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+               cairo_paint(cr);
+               cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+               {
+                       char buf[80];
+                       cairo_text_extents_t extents;
+                       sprintf(buf, "%d", i++);
+                       cairo_set_source_rgb(cr, .5, .5, .5);
+                       cairo_text_extents(cr, buf, &extents);
+                       cairo_move_to(cr,
+                                     cairo_image_surface_get_width(surface)-extents.width-6,
+                                     6+extents.height);
+                       cairo_show_text(cr, buf);
+               }
+
+               show_gpu_top(cr, &gpu_top);
+               show_gem_objects(cr);
+
+               cairo_destroy(cr);
+
+               overlay_show(surface);
+       }
+
+       return 0;
+}
diff --git a/overlay/overlay.h b/overlay/overlay.h
new file mode 100644 (file)
index 0000000..7c5b2d8
--- /dev/null
@@ -0,0 +1,37 @@
+#include <cairo.h>
+
+enum position {
+       POS_UNSET = -1,
+
+       POS_LEFT = 0,
+       POS_CENTRE = 1,
+       POS_RIGHT = 2,
+
+       POS_TOP = 0 << 4,
+       POS_MIDDLE = 1 << 4,
+       POS_BOTTOM = 2 << 4,
+
+       POS_TOP_LEFT = POS_TOP | POS_LEFT,
+       POS_TOP_CENTRE = POS_TOP | POS_CENTRE,
+       POS_TOP_RIGHT = POS_TOP | POS_RIGHT,
+
+       POS_MIDDLE_LEFT = POS_MIDDLE | POS_LEFT,
+       POS_MIDDLE_CENTRE = POS_MIDDLE | POS_CENTRE,
+       POS_MIDDLE_RIGHT = POS_MIDDLE | POS_RIGHT,
+
+       POS_BOTTOM_LEFT = POS_BOTTOM | POS_LEFT,
+       POS_BOTTOM_CENTRE = POS_BOTTOM | POS_CENTRE,
+       POS_BOTTOM_RIGHT = POS_BOTTOM | POS_RIGHT,
+};
+
+struct overlay {
+       cairo_surface_t *surface;
+       void (*show)(struct overlay *);
+       void (*position)(struct overlay *, enum position);
+       void (*hide)(struct overlay *);
+};
+
+extern const cairo_user_data_key_t overlay_key;
+
+cairo_surface_t *x11_overlay_create(enum position pos, int max_width, int max_height);
+void x11_overlay_stop(void);
diff --git a/overlay/x11/dri2.c b/overlay/x11/dri2.c
new file mode 100644 (file)
index 0000000..0aa0cf5
--- /dev/null
@@ -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 <stdio.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/Xext.h>
+#include <X11/extensions/extutil.h>
+#include <X11/extensions/dri2proto.h>
+#include <X11/extensions/dri2tokens.h>
+#include <xf86drm.h>
+#include <drm.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..cb66b46
--- /dev/null
@@ -0,0 +1 @@
+int dri2_open(Display *dpy);
diff --git a/overlay/x11/rgb2yuv.c b/overlay/x11/rgb2yuv.c
new file mode 100644 (file)
index 0000000..8e0c080
--- /dev/null
@@ -0,0 +1,105 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..835899c
--- /dev/null
@@ -0,0 +1,7 @@
+#include <X11/Xlib.h>
+#include <X11/extensions/Xvlib.h>
+#include <cairo.h>
+#include <stdint.h>
+
+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 (file)
index 0000000..b914408
--- /dev/null
@@ -0,0 +1,383 @@
+#include <X11/Xlib.h>
+#include <X11/extensions/Xvlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <cairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <drm.h>
+#include <xf86drm.h>
+#include <i915_drm.h>
+#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);
+}