summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2013-08-17 11:12:07 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2013-08-17 11:21:51 +0100
commitf9a50de3dcc501e930de6c60983a4feb57121e7e (patch)
treee2a65ef9a1ce5c15faf8e95e0337e821e2579ac7
parent7df9caeea1606b4f0272de35f0d7f70eedd5ec30 (diff)
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 <chris@chris-wilson.co.uk>
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac5
-rw-r--r--overlay/.gitignore1
-rw-r--r--overlay/Makefile.am28
-rw-r--r--overlay/README8
-rw-r--r--overlay/chart.c135
-rw-r--r--overlay/chart.h18
-rw-r--r--overlay/gem-objects.c19
-rw-r--r--overlay/gem-objects.h1
-rw-r--r--overlay/gpu-top.c178
-rw-r--r--overlay/gpu-top.h22
-rw-r--r--overlay/i915_pciids.h211
-rw-r--r--overlay/igfx.c217
-rw-r--r--overlay/igfx.h15
-rw-r--r--overlay/overlay.c196
-rw-r--r--overlay/overlay.h37
-rw-r--r--overlay/x11/dri2.c169
-rw-r--r--overlay/x11/dri2.h1
-rw-r--r--overlay/x11/rgb2yuv.c105
-rw-r--r--overlay/x11/rgb2yuv.h7
-rw-r--r--overlay/x11/x11-overlay.c383
21 files changed, 1757 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index c4c8de19..d7a479c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/configure.ac b/configure.ac
index 6f23231b..d6c4cc65 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
index 00000000..0aa81847
--- /dev/null
+++ b/overlay/.gitignore
@@ -0,0 +1 @@
+intel-gpu-overlay
diff --git a/overlay/Makefile.am b/overlay/Makefile.am
new file mode 100644
index 00000000..10820bdf
--- /dev/null
+++ b/overlay/Makefile.am
@@ -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
index 00000000..c4ed9702
--- /dev/null
+++ b/overlay/README
@@ -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
index 00000000..89b3f414
--- /dev/null
+++ b/overlay/chart.c
@@ -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
index 00000000..c0f0065c
--- /dev/null
+++ b/overlay/chart.h
@@ -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
index 00000000..37d67263
--- /dev/null
+++ b/overlay/gem-objects.c
@@ -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
index 00000000..898d18f0
--- /dev/null
+++ b/overlay/gem-objects.h
@@ -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
index 00000000..400cbc58
--- /dev/null
+++ b/overlay/gpu-top.c
@@ -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
index 00000000..78f4daf2
--- /dev/null
+++ b/overlay/gpu-top.h
@@ -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
index 00000000..8a10f5c3
--- /dev/null
+++ b/overlay/i915_pciids.h
@@ -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
index 00000000..7fdbb51c
--- /dev/null
+++ b/overlay/igfx.c
@@ -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
index 00000000..36e971ba
--- /dev/null
+++ b/overlay/igfx.h
@@ -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
index 00000000..62639b61
--- /dev/null
+++ b/overlay/overlay.c
@@ -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
index 00000000..7c5b2d86
--- /dev/null
+++ b/overlay/overlay.h
@@ -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
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 <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
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 <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
index 00000000..835899c5
--- /dev/null
+++ b/overlay/x11/rgb2yuv.h
@@ -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
index 00000000..b9144087
--- /dev/null
+++ b/overlay/x11/x11-overlay.c
@@ -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);
+}