From f9a50de3dcc501e930de6c60983a4feb57121e7e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 17 Aug 2013 11:12:07 +0100 Subject: Introduce intel-gpu-overlay A realtime display of GPU activity. Note, this is just at the point of minimum usability... Signed-off-by: Chris Wilson --- Makefile.am | 2 +- configure.ac | 5 + overlay/.gitignore | 1 + overlay/Makefile.am | 28 ++++ overlay/README | 8 + overlay/chart.c | 135 ++++++++++++++++ overlay/chart.h | 18 +++ overlay/gem-objects.c | 19 +++ overlay/gem-objects.h | 1 + overlay/gpu-top.c | 178 +++++++++++++++++++++ overlay/gpu-top.h | 22 +++ overlay/i915_pciids.h | 211 +++++++++++++++++++++++++ overlay/igfx.c | 217 ++++++++++++++++++++++++++ overlay/igfx.h | 15 ++ overlay/overlay.c | 196 ++++++++++++++++++++++++ overlay/overlay.h | 37 +++++ overlay/x11/dri2.c | 169 ++++++++++++++++++++ overlay/x11/dri2.h | 1 + overlay/x11/rgb2yuv.c | 105 +++++++++++++ overlay/x11/rgb2yuv.h | 7 + overlay/x11/x11-overlay.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++ 21 files changed, 1757 insertions(+), 1 deletion(-) create mode 100644 overlay/.gitignore create mode 100644 overlay/Makefile.am create mode 100644 overlay/README create mode 100644 overlay/chart.c create mode 100644 overlay/chart.h create mode 100644 overlay/gem-objects.c create mode 100644 overlay/gem-objects.h create mode 100644 overlay/gpu-top.c create mode 100644 overlay/gpu-top.h create mode 100644 overlay/i915_pciids.h create mode 100644 overlay/igfx.c create mode 100644 overlay/igfx.h create mode 100644 overlay/overlay.c create mode 100644 overlay/overlay.h create mode 100644 overlay/x11/dri2.c create mode 100644 overlay/x11/dri2.h create mode 100644 overlay/x11/rgb2yuv.c create mode 100644 overlay/x11/rgb2yuv.h create mode 100644 overlay/x11/x11-overlay.c 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 +#include +#include +#include + +#include + +#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 +#include + +#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 +#include +#include +#include +#include + +#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 + +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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(>->gpu_top); + + for (n = 0; n < gt->gpu_top.num_rings; n++) { + chart_init(>->chart[n], + gt->gpu_top.ring[n].name, + 120); + chart_set_position(>->chart[n], 12, 12); + chart_set_size(>->chart[n], + cairo_image_surface_get_width(surface)-24, + 100); + chart_set_rgba(>->chart[n], + rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3]); + chart_set_range(>->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(>->gpu_top); + for (n = 0; n < gt->gpu_top.num_rings; n++) { + if (update) + chart_add_sample(>->chart[n], + gt->gpu_top.ring[n].u.u.busy); + chart_draw(>->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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dri2.h" + +static char dri2ExtensionName[] = DRI2_NAME; +static XExtensionInfo *dri2Info; +static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info) + +static /* const */ XExtensionHooks dri2ExtensionHooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + DRI2CloseDisplay, /* close_display */ +}; + +static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay, + dri2Info, + dri2ExtensionName, + &dri2ExtensionHooks, + 0, NULL) + +static Bool +DRI2Connect(Display *dpy, XID window, char **driverName, char **deviceName) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2ConnectReply rep; + xDRI2ConnectReq *req; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Connect, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Connect; + req->window = window; + req->driverType = DRI2DriverDRI; + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + *driverName = Xmalloc(rep.driverNameLength + 1); + if (*driverName == NULL) { + _XEatData(dpy, + ((rep.driverNameLength + 3) & ~3) + + ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *driverName, rep.driverNameLength); + (*driverName)[rep.driverNameLength] = '\0'; + + *deviceName = Xmalloc(rep.deviceNameLength + 1); + if (*deviceName == NULL) { + Xfree(*driverName); + _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *deviceName, rep.deviceNameLength); + (*deviceName)[rep.deviceNameLength] = '\0'; + + UnlockDisplay(dpy); + SyncHandle(); + + return True; +} + +static Bool +DRI2Authenticate(Display * dpy, XID window, unsigned int magic) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2AuthenticateReq *req; + xDRI2AuthenticateReply rep; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Authenticate, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Authenticate; + req->window = window; + req->magic = magic; + + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + UnlockDisplay(dpy); + SyncHandle(); + + return rep.authenticated; +} + +int dri2_open(Display *dpy) +{ + drm_auth_t auth; + char *driver, *device; + int fd; + + if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) + return -1; + + fd = open(device, O_RDWR); + if (fd < 0) + return -1; + + if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) + return -1; + + if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic)) + return -1; + + return fd; +} diff --git a/overlay/x11/dri2.h b/overlay/x11/dri2.h new file mode 100644 index 00000000..cb66b469 --- /dev/null +++ b/overlay/x11/dri2.h @@ -0,0 +1 @@ +int dri2_open(Display *dpy); diff --git a/overlay/x11/rgb2yuv.c b/overlay/x11/rgb2yuv.c new file mode 100644 index 00000000..8e0c080e --- /dev/null +++ b/overlay/x11/rgb2yuv.c @@ -0,0 +1,105 @@ +#include +#include + +#include "rgb2yuv.h" + +static int RGB2YUV_YR[256], RGB2YUV_YG[256], RGB2YUV_YB[256]; +static int RGB2YUV_UR[256], RGB2YUV_UG[256], RGB2YUV_UBVR[256]; +static int RGB2YUV_VG[256], RGB2YUV_VB[256]; + +void rgb2yuv_init(void) +{ + int i; + + for (i = 0; i < 256; i++) + RGB2YUV_YR[i] = 65.481 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_YG[i] = 128.553 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_YB[i] = 24.966 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UR[i] = 37.797 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UG[i] = 74.203 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_VG[i] = 93.786 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_VB[i] = 18.214 * (i << 8); + + for (i = 0; i < 256; i++) + RGB2YUV_UBVR[i] = 112 * (i << 8); +} + +int rgb2yuv(cairo_surface_t *surface, XvImage *image, uint8_t *yuv) +{ + uint8_t *data = cairo_image_surface_get_data(surface); + int rgb_stride = cairo_image_surface_get_stride(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + int y_stride = image->pitches[0]; + int uv_stride = image->pitches[1]; + uint8_t *tmp, *tl, *tr, *bl, *br; + int i, j; + + tmp = malloc(2*width*height); + if (tmp == NULL) + return 0; + + tl = tmp; + bl = tmp + width*height; + + for (i = 0; i < height; i++) { + uint16_t *rgb = (uint16_t *)(data + i * rgb_stride); + for (j = 0; j < width; j++) { + uint8_t r = (rgb[j] >> 11) & 0x1f; + uint8_t g = (rgb[j] >> 5) & 0x3f; + uint8_t b = (rgb[j] >> 0) & 0x1f; + + r = r<<3 | r>>2; + g = g<<2 | g>>4; + b = b<<3 | b>>2; + + yuv[j] = (RGB2YUV_YR[r] + RGB2YUV_YG[g] + RGB2YUV_YB[b] + 1048576) >> 16; + *tl++ = (-RGB2YUV_UR[r] - RGB2YUV_UG[g] + RGB2YUV_UBVR[b] + 8388608) >> 16; + *bl++ = (RGB2YUV_UBVR[r] - RGB2YUV_VG[g] - RGB2YUV_VB[b] + 8388608) >> 16; + } + yuv += y_stride; + } + + tl = tmp; tr = tl + 1; + bl = tl + width; br = bl + 1; + for (i = 0; i < height/2; i ++) { + for (j = 0; j < width/2; j ++) { + yuv[j] = ((int)*tl + *tr + *bl + *br) >> 2; + tl += 2; tr += 2; + bl += 2; br += 2; + } + yuv += uv_stride; + + tl += width; tr += width; + bl += width; br += width; + } + + tl = tmp + width*height; tr = tl + 1; + bl = tl + width; br = bl + 1; + for (i = 0; i < height/2; i++) { + for (j = 0; j < width/2; j++) { + yuv[j] = ((int)*tl + *tr + *bl + *br) >> 2; + tl += 2; tr += 2; + bl += 2; br += 2; + } + yuv += uv_stride; + + tl += width; tr += width; + bl += width; br += width; + } + + free(tmp); + return 1; +} diff --git a/overlay/x11/rgb2yuv.h b/overlay/x11/rgb2yuv.h new file mode 100644 index 00000000..835899c5 --- /dev/null +++ b/overlay/x11/rgb2yuv.h @@ -0,0 +1,7 @@ +#include +#include +#include +#include + +void rgb2yuv_init(void); +int rgb2yuv(cairo_surface_t *rgb, XvImage *image, uint8_t *yuv); diff --git a/overlay/x11/x11-overlay.c b/overlay/x11/x11-overlay.c new file mode 100644 index 00000000..b9144087 --- /dev/null +++ b/overlay/x11/x11-overlay.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../overlay.h" +#include "dri2.h" +#include "rgb2yuv.h" + +#ifndef ALIGN +#define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1)) +#endif + +#define FOURCC_XVMC (('C' << 24) + ('M' << 16) + ('V' << 8) + 'X') +#define FOURCC_RGB565 ((16 << 24) + ('B' << 16) + ('G' << 8) + 'R') +#define FOURCC_RGB888 ((24 << 24) + ('B' << 16) + ('G' << 8) + 'R') + +struct x11_overlay { + struct overlay base; + Display *dpy; + GC gc; + XvPortID port; + XvImage *image; + void *map, *mem; + int size; + unsigned name; + int x, y; + int visible; +}; +static inline struct x11_overlay *to_x11_overlay(struct overlay *o) +{ + return (struct x11_overlay *)o; +} + +static int noop(Display *dpy, XErrorEvent *event) +{ + return 0; +} + +static void x11_overlay_show(struct overlay *overlay) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + + if (priv->image->id == FOURCC_XVMC) + rgb2yuv(priv->base.surface, priv->image, priv->map); + else + memcpy(priv->map, priv->mem, priv->size); + + if (!priv->visible) { + XvPutImage(priv->dpy, priv->port, DefaultRootWindow(priv->dpy), + priv->gc, priv->image, + 0, 0, + priv->image->width, priv->image->height, + priv->x, priv->y, + priv->image->width, priv->image->height); + XFlush(priv->dpy); + priv->visible = true; + } +} + +static void x11_overlay_position(struct overlay *overlay, + enum position p) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + Screen *scr = ScreenOfDisplay(priv->dpy, DefaultScreen(priv->dpy)); + + switch (p & 7) { + default: + case 0: priv->x = 0; break; + case 1: priv->x = (scr->width - priv->image->width)/2; break; + case 2: priv->x = scr->width - priv->image->width; break; + } + + switch ((p >> 4) & 7) { + default: + case 0: priv->y = 0; break; + case 1: priv->y = (scr->height - priv->image->height)/2; break; + case 2: priv->y = scr->height - priv->image->height; break; + } + + if (priv->visible) { + XvPutImage(priv->dpy, priv->port, DefaultRootWindow(priv->dpy), + priv->gc, priv->image, + 0, 0, + priv->image->width, priv->image->height, + priv->x, priv->y, + priv->image->width, priv->image->height); + XFlush(priv->dpy); + } +} + +static void x11_overlay_hide(struct overlay *overlay) +{ + struct x11_overlay *priv = to_x11_overlay(overlay); + if (priv->visible) { + XClearWindow(priv->dpy, DefaultRootWindow(priv->dpy)); + XFlush(priv->dpy); + priv->visible = false; + } +} + +static void x11_overlay_destroy(void *data) +{ + struct x11_overlay *priv = data; + munmap(priv->map, priv->size); + free(priv->mem); + XCloseDisplay(priv->dpy); + free(priv); +} + +cairo_surface_t * +x11_overlay_create(enum position position, int max_width, int max_height) +{ + Display *dpy; + Screen *scr; + cairo_surface_t *surface; + struct drm_i915_gem_create create; + struct drm_gem_flink flink; + struct drm_i915_gem_mmap_gtt map; + struct x11_overlay *priv; + unsigned int count, i, j; + int fd, w, h; + XvAdaptorInfo *info; + XvImage *image; + XvPortID port = -1; + void *ptr, *mem; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return NULL; + + scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + + fd = dri2_open(dpy); + if (fd < 0) + goto err_dpy; + + if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &count, &info) != Success) + goto err_fd; + + for (i = 0; i < count; i++) { + unsigned long visual = 0; + + if (info[i].num_ports != 1) + continue; + + for (j = 0; j < info[j].num_formats; j++) { + if (info[i].formats[j].depth == 24) { + visual = info[i].formats[j].visual_id; + break; + } + } + + if (visual == 0) + continue; + + port = info[i].base_id; + } + XvFreeAdaptorInfo(info); + if (port == -1) + goto err_fd; + + XSetErrorHandler(noop); + + w = scr->width; + switch (position & 7) { + default: + case 0: + case 2: w >>= 1; break; + } + if (max_width > 0 && w > max_width) + w = max_width; + + h = scr->height; + switch ((position >> 4) & 7) { + default: + case 0: + case 2: h >>= 1; break; + } + if (max_height > 0 && h > max_height) + h = max_height; + + image = XvCreateImage(dpy, port, FOURCC_RGB565, NULL, w, h); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_RGB888, NULL, w, h); + if (image == NULL) { + image = XvCreateImage(dpy, port, FOURCC_XVMC, NULL, w, h); + if (image->pitches[0] == 4) { + image->pitches[0] = ALIGN(image->width, 1024); + image->pitches[1] = ALIGN(image->width/2, 1024); + image->pitches[2] = ALIGN(image->width/2, 1024); + image->offsets[0] = 0; + image->offsets[1] = image->pitches[0] * image->height; + image->offsets[2] = image->offsets[1] + image->pitches[1] * image->height/2; + } + rgb2yuv_init(); + } + if (image == NULL) + goto err_fd; + + switch (image->id) { + case FOURCC_RGB888: + case FOURCC_RGB565: + create.size = image->pitches[0] * image->height; + break; + case FOURCC_XVMC: + create.size = image->pitches[0] * image->height; + create.size += image->pitches[1] * image->height; + break; + } + + create.handle = 0; + create.size = ALIGN(create.size, 4096); + drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + if (create.handle == 0) + goto err_image; + + flink.handle = create.handle; + if (drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink)) + goto err_create; + + map.handle = create.handle; + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map)) + goto err_create; + + ptr = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map.offset); + if (ptr == (void *)-1) + goto err_create; + + mem = malloc(create.size); + if (mem == NULL) + goto err_map; + + switch (image->id) { + default: + case FOURCC_RGB888: + i = CAIRO_FORMAT_RGB24; + j = image->pitches[0]; + break; + case FOURCC_RGB565: + i = CAIRO_FORMAT_RGB16_565; + j = image->pitches[0]; + break; + case FOURCC_XVMC: + i = CAIRO_FORMAT_RGB16_565; + j = cairo_format_stride_for_width(i, image->width); + break; + } + + surface = cairo_image_surface_create_for_data(mem, i, image->width, image->height, j); + if (cairo_surface_status(surface)) + goto err_mem; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) + goto err_surface; + + priv->base.surface = surface; + priv->base.show = x11_overlay_show; + priv->base.position = x11_overlay_position; + priv->base.hide = x11_overlay_hide; + + priv->dpy = dpy; + priv->gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL); + priv->port = port; + priv->map = ptr; + priv->mem = mem; + priv->size = create.size; + priv->name = flink.name; + priv->visible = false; + + switch (position & 7) { + default: + case 0: priv->x = 0; break; + case 1: priv->x = (scr->width - image->width)/2; break; + case 2: priv->x = scr->width - image->width; break; + } + + switch ((position >> 4) & 7) { + default: + case 0: priv->y = 0; break; + case 1: priv->y = (scr->height - image->height)/2; break; + case 2: priv->y = scr->height - image->height; break; + } + + + priv->image = image; + priv->image->data = (void *)&priv->name; + + cairo_surface_set_user_data(surface, &overlay_key, priv, x11_overlay_destroy); + + XvSetPortAttribute(dpy, port, XInternAtom(dpy, "XV_ALWAYS_ON_TOP", True), 1); + + close(fd); + return surface; + +err_surface: + cairo_surface_destroy(surface); +err_mem: + free(mem); +err_map: + munmap(ptr, create.size); +err_create: + drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &create.handle); +err_image: +err_fd: + close(fd); +err_dpy: + XCloseDisplay(dpy); + return NULL; +} + +void x11_overlay_stop(void) +{ + Display *dpy; + unsigned int count, i, j; + XvAdaptorInfo *info; + XvImage *image; + XvPortID port = -1; + uint32_t name; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return; + + if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &count, &info) != Success) + goto close; + + for (i = 0; i < count; i++) { + unsigned long visual = 0; + + if (info[i].num_ports != 1) + continue; + + for (j = 0; j < info[j].num_formats; j++) { + if (info[i].formats[j].depth == 24) { + visual = info[i].formats[j].visual_id; + break; + } + } + + if (visual == 0) + continue; + + port = info[i].base_id; + } + XvFreeAdaptorInfo(info); + if (port == -1) + goto close; + + XSetErrorHandler(noop); + + image = XvCreateImage(dpy, port, FOURCC_RGB565, NULL, 16, 16); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_RGB888, NULL, 16, 16); + if (image == NULL) + image = XvCreateImage(dpy, port, FOURCC_XVMC, NULL, 16, 16); + if (image == NULL) + goto close; + + name = 0; + image->data = (void *)&name; + + XvPutImage(dpy, port, DefaultRootWindow(dpy), + XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL), image, + 0, 0, + 1, 1, + 0, 0, + 1, 1); + XSync(dpy, True); + +close: + XCloseDisplay(dpy); +} -- cgit v1.2.3