mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-03 19:50:17 +01:00
Introduce cairo_surface_observer_t for performance analysis
Another logging passthrough surface that records the style of operations performed trying to categorise what is slow/fast/important. In combination with perf/cairo-analyse-trace it is very useful for understanding what a trace does. The next steps for this tool would be to identify the slow operations that the trace does. Baby steps. This should be generally useful in similar situations outside of perf/ and should be extensible to become an online performance probe. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
f6fc6f1ad0
commit
eee66899cd
15 changed files with 1904 additions and 12 deletions
|
|
@ -408,6 +408,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
|
|||
enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_recording_cxx_sources)
|
||||
enabled_cairo_boilerplate_sources += $(cairo_boilerplate_recording_sources)
|
||||
|
||||
supported_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
|
||||
all_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
|
||||
all_cairo_boilerplate_private += $(cairo_boilerplate_observer_private)
|
||||
all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_observer_cxx_sources)
|
||||
all_cairo_boilerplate_sources += $(cairo_boilerplate_observer_sources)
|
||||
enabled_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
|
||||
enabled_cairo_boilerplate_private += $(cairo_boilerplate_observer_private)
|
||||
enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_observer_cxx_sources)
|
||||
enabled_cairo_boilerplate_sources += $(cairo_boilerplate_observer_sources)
|
||||
|
||||
unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
|
||||
all_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
|
||||
all_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
|
|||
endif
|
||||
@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
|
||||
@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
|
||||
@echo "#define CAIRO_HAS_OBSERVER_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
|
||||
ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
|
||||
@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ AC_DEFUN([CAIRO_REPORT],
|
|||
echo "The following surface backends:"
|
||||
echo " Image: yes (always builtin)"
|
||||
echo " Recording: yes (always builtin)"
|
||||
echo " Observer: yes (always builtin)"
|
||||
echo " Tee: $use_tee"
|
||||
echo " XML: $use_xml"
|
||||
echo " Skia: $use_skia"
|
||||
|
|
|
|||
|
|
@ -621,6 +621,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
|
|||
dnl ===========================================================================
|
||||
|
||||
CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
|
||||
CAIRO_ENABLE_SURFACE_BACKEND(observer, observer, always)
|
||||
CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
|
||||
CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
|
||||
use_xml=$have_libz
|
||||
|
|
|
|||
1
perf/.gitignore
vendored
1
perf/.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
|||
TAGS
|
||||
tags
|
||||
cairo-analyse-trace
|
||||
cairo-perf
|
||||
cairo-perf-micro
|
||||
cairo-perf-print
|
||||
|
|
|
|||
|
|
@ -14,15 +14,22 @@ AM_LDFLAGS = $(CAIRO_LDFLAGS)
|
|||
|
||||
SUBDIRS = micro
|
||||
|
||||
noinst_PROGRAMS = cairo-perf-trace cairo-perf-micro
|
||||
noinst_PROGRAMS = \
|
||||
cairo-analyse-trace \
|
||||
cairo-perf-trace \
|
||||
cairo-perf-micro \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PROGRAMS += cairo-perf-micro \
|
||||
cairo-perf-trace \
|
||||
cairo-perf-diff-files \
|
||||
cairo-perf-print \
|
||||
cairo-perf-chart \
|
||||
cairo-perf-compare-backends \
|
||||
cairo-perf-graph-files
|
||||
EXTRA_PROGRAMS += \
|
||||
cairo-analyse-trace \
|
||||
cairo-perf-micro \
|
||||
cairo-perf-trace \
|
||||
cairo-perf-diff-files \
|
||||
cairo-perf-print \
|
||||
cairo-perf-chart \
|
||||
cairo-perf-compare-backends \
|
||||
cairo-perf-graph-files \
|
||||
$(NULL)
|
||||
EXTRA_DIST += cairo-perf-diff COPYING
|
||||
EXTRA_LTLIBRARIES += libcairoperf.la
|
||||
|
||||
|
|
@ -43,6 +50,16 @@ libcairoperf_la_SOURCES = \
|
|||
$(libcairoperf_headers)
|
||||
libcairoperf_la_LIBADD = $(CAIROPERF_LIBS)
|
||||
|
||||
cairo_analyse_trace_SOURCES = \
|
||||
$(cairo_analyse_trace_sources) \
|
||||
$(cairo_analyse_trace_external_sources)
|
||||
cairo_analyse_trace_LDADD = \
|
||||
$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
|
||||
$(LDADD)
|
||||
cairo_analyse_trace_DEPENDENCIES = \
|
||||
$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
|
||||
$(LDADD)
|
||||
|
||||
cairo_perf_trace_SOURCES = \
|
||||
$(cairo_perf_trace_sources) \
|
||||
$(cairo_perf_trace_external_sources)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ libcairoperf_headers = \
|
|||
cairo-stats.h \
|
||||
$(NULL)
|
||||
|
||||
cairo_analyse_trace_sources = cairo-analyse-trace.c
|
||||
cairo_analyse_trace_external_sources = ../src/cairo-error.c
|
||||
|
||||
cairo_perf_trace_sources = cairo-perf-trace.c
|
||||
cairo_perf_trace_external_sources = \
|
||||
../src/cairo-error.c \
|
||||
|
|
|
|||
646
perf/cairo-analyse-trace.c
Normal file
646
perf/cairo-analyse-trace.c
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
||||
/*
|
||||
* Copyright © 2006 Mozilla Corporation
|
||||
* Copyright © 2006 Red Hat, Inc.
|
||||
* Copyright © 2009 Chris Wilson
|
||||
* Copyright © 2011 Intel Corporation
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without
|
||||
* fee, provided that the above copyright notice appear in all copies
|
||||
* and that both that copyright notice and this permission notice
|
||||
* appear in supporting documentation, and that the name of
|
||||
* the authors not be used in advertising or publicity pertaining to
|
||||
* distribution of the software without specific, written prior
|
||||
* permission. The authors make no representations about the
|
||||
* suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
|
||||
* INDIRECT OR CONSEQUENTIAL 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 PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Authors: Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Carl Worth <cworth@cworth.org>
|
||||
* Chris Wilson <chris@chris-wilson.co.uk>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1 /* for sched_getaffinity() and getline() */
|
||||
|
||||
#include "../cairo-version.h" /* for the real version */
|
||||
|
||||
#include "cairo-perf.h"
|
||||
#include "cairo-stats.h"
|
||||
|
||||
#include "cairo-boilerplate-getopt.h"
|
||||
#include <cairo-script-interpreter.h>
|
||||
|
||||
/* rudely reuse bits of the library... */
|
||||
#include "../src/cairo-error-private.h"
|
||||
|
||||
/* For basename */
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#include <ctype.h> /* isspace() */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "dirent-win32.h"
|
||||
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
static char *
|
||||
basename_no_ext (char *path)
|
||||
{
|
||||
static char name[_MAX_FNAME + 1];
|
||||
|
||||
_splitpath (path, NULL, NULL, name, NULL);
|
||||
|
||||
name[_MAX_FNAME] = '\0';
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#include <dirent.h>
|
||||
|
||||
static char *
|
||||
basename_no_ext (char *path)
|
||||
{
|
||||
char *dot, *name;
|
||||
|
||||
name = basename (path);
|
||||
|
||||
dot = strchr (name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#if HAVE_FCFINI
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#endif
|
||||
|
||||
struct trace {
|
||||
const cairo_boilerplate_target_t *target;
|
||||
void *closure;
|
||||
cairo_surface_t *surface;
|
||||
};
|
||||
|
||||
cairo_bool_t
|
||||
cairo_perf_can_run (cairo_perf_t *perf,
|
||||
const char *name,
|
||||
cairo_bool_t *is_explicit)
|
||||
{
|
||||
unsigned int i;
|
||||
char *copy, *dot;
|
||||
cairo_bool_t ret;
|
||||
|
||||
if (is_explicit)
|
||||
*is_explicit = FALSE;
|
||||
|
||||
if (perf->exact_names) {
|
||||
if (is_explicit)
|
||||
*is_explicit = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (perf->num_names == 0 && perf->num_exclude_names == 0)
|
||||
return TRUE;
|
||||
|
||||
copy = xstrdup (name);
|
||||
dot = strchr (copy, '.');
|
||||
if (dot != NULL)
|
||||
*dot = '\0';
|
||||
|
||||
if (perf->num_names) {
|
||||
ret = TRUE;
|
||||
for (i = 0; i < perf->num_names; i++)
|
||||
if (strstr (copy, perf->names[i])) {
|
||||
if (is_explicit)
|
||||
*is_explicit = strcmp (copy, perf->names[i]) == 0;
|
||||
goto check_exclude;
|
||||
}
|
||||
|
||||
ret = FALSE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
check_exclude:
|
||||
if (perf->num_exclude_names) {
|
||||
ret = FALSE;
|
||||
for (i = 0; i < perf->num_exclude_names; i++)
|
||||
if (strstr (copy, perf->exclude_names[i])) {
|
||||
if (is_explicit)
|
||||
*is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
free (copy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
surface_create (void *closure,
|
||||
cairo_content_t content,
|
||||
double width,
|
||||
double height,
|
||||
long uid)
|
||||
{
|
||||
struct trace *args = closure;
|
||||
return cairo_surface_create_similar (args->surface, content, width, height);
|
||||
}
|
||||
|
||||
static int user_interrupt;
|
||||
|
||||
static void
|
||||
interrupt (int sig)
|
||||
{
|
||||
if (user_interrupt) {
|
||||
signal (sig, SIG_DFL);
|
||||
raise (sig);
|
||||
}
|
||||
|
||||
user_interrupt = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
describe (cairo_perf_t *perf,
|
||||
void *closure)
|
||||
{
|
||||
char *description = NULL;
|
||||
|
||||
if (perf->has_described_backend)
|
||||
return;
|
||||
perf->has_described_backend = TRUE;
|
||||
|
||||
if (perf->target->describe)
|
||||
description = perf->target->describe (closure);
|
||||
|
||||
if (description == NULL)
|
||||
return;
|
||||
|
||||
free (description);
|
||||
}
|
||||
|
||||
static void
|
||||
execute (cairo_perf_t *perf,
|
||||
struct trace *args,
|
||||
const char *trace)
|
||||
{
|
||||
char *trace_cpy, *name;
|
||||
const cairo_script_interpreter_hooks_t hooks = {
|
||||
.closure = args,
|
||||
.surface_create = surface_create,
|
||||
};
|
||||
|
||||
trace_cpy = xstrdup (trace);
|
||||
name = basename_no_ext (trace_cpy);
|
||||
|
||||
if (perf->list_only) {
|
||||
printf ("%s\n", name);
|
||||
free (trace_cpy);
|
||||
return;
|
||||
}
|
||||
|
||||
describe (perf, args->closure);
|
||||
|
||||
{
|
||||
cairo_script_interpreter_t *csi;
|
||||
cairo_status_t status;
|
||||
unsigned int line_no;
|
||||
|
||||
csi = cairo_script_interpreter_create ();
|
||||
cairo_script_interpreter_install_hooks (csi, &hooks);
|
||||
|
||||
cairo_script_interpreter_run (csi, trace);
|
||||
|
||||
cairo_script_interpreter_finish (csi);
|
||||
|
||||
line_no = cairo_script_interpreter_get_line_number (csi);
|
||||
status = cairo_script_interpreter_destroy (csi);
|
||||
if (status) {
|
||||
/* XXXX cairo_status_to_string is just wrong! */
|
||||
fprintf (stderr, "Error during replay, line %d: %s\n",
|
||||
line_no, cairo_status_to_string (status));
|
||||
}
|
||||
}
|
||||
user_interrupt = 0;
|
||||
|
||||
free (trace_cpy);
|
||||
}
|
||||
|
||||
static void
|
||||
usage (const char *argv0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ... | traces ...]\n"
|
||||
" %s -l\n"
|
||||
"\n"
|
||||
"Run the cairo performance test suite over the given tests (all by default)\n"
|
||||
"The command-line arguments are interpreted as follows:\n"
|
||||
"\n"
|
||||
" -v verbose\n"
|
||||
" -x exclude; specify a file to read a list of traces to exclude\n"
|
||||
" -l list only; just list selected test case names without executing\n"
|
||||
"\n"
|
||||
"If test names are given they are used as sub-string matches so a command\n"
|
||||
"such as \"cairo-perf-trace firefox\" can be used to run all firefox traces.\n"
|
||||
"Alternatively, you can specify a list of filenames to execute.\n",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
||||
#ifndef __USE_GNU
|
||||
#define POORMANS_GETLINE_BUFFER_SIZE (65536)
|
||||
static ssize_t
|
||||
getline (char **lineptr,
|
||||
size_t *n,
|
||||
FILE *stream)
|
||||
{
|
||||
if (!*lineptr)
|
||||
{
|
||||
*n = POORMANS_GETLINE_BUFFER_SIZE;
|
||||
*lineptr = (char *) malloc (*n);
|
||||
}
|
||||
|
||||
if (!fgets (*lineptr, *n, stream))
|
||||
return -1;
|
||||
|
||||
if (!feof (stream) && !strchr (*lineptr, '\n'))
|
||||
{
|
||||
fprintf (stderr, "The poor man's implementation of getline in "
|
||||
__FILE__ " needs a bigger buffer. Perhaps it's "
|
||||
"time for a complete implementation of getline.\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
return strlen (*lineptr);
|
||||
}
|
||||
#undef POORMANS_GETLINE_BUFFER_SIZE
|
||||
|
||||
static char *
|
||||
strndup (const char *s,
|
||||
size_t n)
|
||||
{
|
||||
size_t len;
|
||||
char *sdup;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
len = strlen (s);
|
||||
len = (n < len ? n : len);
|
||||
sdup = (char *) malloc (len + 1);
|
||||
if (sdup)
|
||||
{
|
||||
memcpy (sdup, s, len);
|
||||
sdup[len] = '\0';
|
||||
}
|
||||
|
||||
return sdup;
|
||||
}
|
||||
#endif /* ifndef __USE_GNU */
|
||||
|
||||
static cairo_bool_t
|
||||
read_excludes (cairo_perf_t *perf,
|
||||
const char *filename)
|
||||
{
|
||||
FILE *file;
|
||||
char *line = NULL;
|
||||
size_t line_size = 0;
|
||||
char *s, *t;
|
||||
|
||||
file = fopen (filename, "r");
|
||||
if (file == NULL)
|
||||
return FALSE;
|
||||
|
||||
while (getline (&line, &line_size, file) != -1) {
|
||||
/* terminate the line at a comment marker '#' */
|
||||
s = strchr (line, '#');
|
||||
if (s)
|
||||
*s = '\0';
|
||||
|
||||
/* whitespace delimits */
|
||||
s = line;
|
||||
while (*s != '\0' && isspace (*s))
|
||||
s++;
|
||||
|
||||
t = s;
|
||||
while (*t != '\0' && ! isspace (*t))
|
||||
t++;
|
||||
|
||||
if (s != t) {
|
||||
int i = perf->num_exclude_names;
|
||||
perf->exclude_names = xrealloc (perf->exclude_names,
|
||||
sizeof (char *) * (i+1));
|
||||
perf->exclude_names[i] = strndup (s, t-s);
|
||||
perf->num_exclude_names++;
|
||||
}
|
||||
}
|
||||
free (line);
|
||||
|
||||
fclose (file);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_options (cairo_perf_t *perf,
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
char *end;
|
||||
int c;
|
||||
|
||||
perf->list_only = FALSE;
|
||||
perf->names = NULL;
|
||||
perf->num_names = 0;
|
||||
perf->exclude_names = NULL;
|
||||
perf->num_exclude_names = 0;
|
||||
|
||||
while (1) {
|
||||
c = _cairo_getopt (argc, argv, "i:x:lrvc");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'i':
|
||||
perf->exact_iterations = TRUE;
|
||||
perf->iterations = strtoul (optarg, &end, 10);
|
||||
if (*end != '\0') {
|
||||
fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
|
||||
optarg);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
perf->list_only = TRUE;
|
||||
break;
|
||||
case 'x':
|
||||
if (! read_excludes (perf, optarg)) {
|
||||
fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
|
||||
optarg);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr, "Internal error: unhandled option: %c\n", c);
|
||||
/* fall-through */
|
||||
case '?':
|
||||
usage (argv[0]);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
perf->names = &argv[optind];
|
||||
perf->num_names = argc - optind;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_perf_fini (cairo_perf_t *perf)
|
||||
{
|
||||
cairo_boilerplate_free_targets (perf->targets);
|
||||
cairo_boilerplate_fini ();
|
||||
|
||||
free (perf->times);
|
||||
cairo_debug_reset_static_data ();
|
||||
#if HAVE_FCFINI
|
||||
FcFini ();
|
||||
#endif
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
have_trace_filenames (cairo_perf_t *perf)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (perf->num_names == 0)
|
||||
return FALSE;
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
for (i = 0; i < perf->num_names; i++)
|
||||
if (access (perf->names[i], R_OK) == 0)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
print (void *closure, const unsigned char *data, unsigned int length)
|
||||
{
|
||||
fwrite (data, length, 1, closure);
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_perf_trace (cairo_perf_t *perf,
|
||||
const cairo_boilerplate_target_t *target,
|
||||
const char *trace)
|
||||
{
|
||||
struct trace args;
|
||||
cairo_surface_t *real;
|
||||
|
||||
args.target = target;
|
||||
real = target->create_surface (NULL,
|
||||
CAIRO_CONTENT_COLOR_ALPHA,
|
||||
1, 1,
|
||||
1, 1,
|
||||
CAIRO_BOILERPLATE_MODE_PERF,
|
||||
0,
|
||||
&args.closure);
|
||||
args.surface = cairo_surface_create_observer (real);
|
||||
cairo_surface_destroy (real);
|
||||
if (cairo_surface_status (args.surface)) {
|
||||
fprintf (stderr,
|
||||
"Error: Failed to create target surface: %s\n",
|
||||
target->name);
|
||||
return;
|
||||
}
|
||||
|
||||
printf ("Observing '%s'...", trace);
|
||||
fflush (stdout);
|
||||
|
||||
execute (perf, &args, trace);
|
||||
|
||||
printf ("\n");
|
||||
cairo_device_observer_print (cairo_surface_get_device (args.surface),
|
||||
print, stdout);
|
||||
fflush (stdout);
|
||||
|
||||
cairo_surface_destroy (args.surface);
|
||||
|
||||
if (target->cleanup)
|
||||
target->cleanup (args.closure);
|
||||
}
|
||||
|
||||
static void
|
||||
warn_no_traces (const char *message,
|
||||
const char *trace_dir)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Error: %s '%s'.\n"
|
||||
"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
|
||||
" git clone git://anongit.freedesktop.org/cairo-traces\n"
|
||||
" cd cairo-traces && make\n"
|
||||
"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
|
||||
message, trace_dir);
|
||||
}
|
||||
|
||||
static int
|
||||
cairo_perf_trace_dir (cairo_perf_t *perf,
|
||||
const cairo_boilerplate_target_t *target,
|
||||
const char *dirname)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int num_traces = 0;
|
||||
cairo_bool_t force;
|
||||
cairo_bool_t is_explicit;
|
||||
|
||||
dir = opendir (dirname);
|
||||
if (dir == NULL)
|
||||
return 0;
|
||||
|
||||
force = FALSE;
|
||||
if (cairo_perf_can_run (perf, dirname, &is_explicit))
|
||||
force = is_explicit;
|
||||
|
||||
while ((de = readdir (dir)) != NULL) {
|
||||
char *trace;
|
||||
struct stat st;
|
||||
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
xasprintf (&trace, "%s/%s", dirname, de->d_name);
|
||||
if (stat (trace, &st) != 0)
|
||||
goto next;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
num_traces += cairo_perf_trace_dir (perf, target, trace);
|
||||
} else {
|
||||
const char *dot;
|
||||
|
||||
dot = strrchr (de->d_name, '.');
|
||||
if (dot == NULL)
|
||||
goto next;
|
||||
if (strcmp (dot, ".trace"))
|
||||
goto next;
|
||||
|
||||
num_traces++;
|
||||
if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
|
||||
goto next;
|
||||
|
||||
cairo_perf_trace (perf, target, trace);
|
||||
}
|
||||
next:
|
||||
free (trace);
|
||||
|
||||
}
|
||||
closedir (dir);
|
||||
|
||||
return num_traces;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
cairo_perf_t perf;
|
||||
const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces";
|
||||
unsigned int n;
|
||||
int i;
|
||||
|
||||
parse_options (&perf, argc, argv);
|
||||
|
||||
signal (SIGINT, interrupt);
|
||||
|
||||
if (getenv ("CAIRO_TRACE_DIR") != NULL)
|
||||
trace_dir = getenv ("CAIRO_TRACE_DIR");
|
||||
|
||||
perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
|
||||
perf.times = xmalloc (perf.iterations * sizeof (cairo_perf_ticks_t));
|
||||
|
||||
/* do we have a list of filenames? */
|
||||
perf.exact_names = have_trace_filenames (&perf);
|
||||
|
||||
for (i = 0; i < perf.num_targets; i++) {
|
||||
const cairo_boilerplate_target_t *target = perf.targets[i];
|
||||
|
||||
if (! perf.list_only && ! target->is_measurable)
|
||||
continue;
|
||||
|
||||
perf.target = target;
|
||||
perf.has_described_backend = FALSE;
|
||||
|
||||
if (perf.exact_names) {
|
||||
for (n = 0; n < perf.num_names; n++) {
|
||||
struct stat st;
|
||||
|
||||
if (stat (perf.names[n], &st) == 0) {
|
||||
if (S_ISDIR (st.st_mode)) {
|
||||
cairo_perf_trace_dir (&perf, target, perf.names[n]);
|
||||
} else
|
||||
cairo_perf_trace (&perf, target, perf.names[n]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int num_traces = 0;
|
||||
const char *dir;
|
||||
|
||||
dir = trace_dir;
|
||||
do {
|
||||
char buf[1024];
|
||||
const char *end = strchr (dir, ':');
|
||||
if (end != NULL) {
|
||||
memcpy (buf, dir, end-dir);
|
||||
buf[end-dir] = '\0';
|
||||
end++;
|
||||
|
||||
dir = buf;
|
||||
}
|
||||
|
||||
num_traces += cairo_perf_trace_dir (&perf, target, dir);
|
||||
dir = end;
|
||||
} while (dir != NULL);
|
||||
|
||||
if (num_traces == 0) {
|
||||
warn_no_traces ("Found no traces in", trace_dir);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (perf.list_only)
|
||||
break;
|
||||
}
|
||||
|
||||
cairo_perf_fini (&perf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -100,6 +100,7 @@ cairo_private = \
|
|||
cairo-surface-fallback-private.h \
|
||||
cairo-surface-private.h \
|
||||
cairo-surface-clipper-private.h \
|
||||
cairo-surface-observer-private.h \
|
||||
cairo-surface-offset-private.h \
|
||||
cairo-surface-subsurface-private.h \
|
||||
cairo-surface-snapshot-private.h \
|
||||
|
|
@ -181,6 +182,7 @@ cairo_sources = \
|
|||
cairo-surface.c \
|
||||
cairo-surface-fallback.c \
|
||||
cairo-surface-clipper.c \
|
||||
cairo-surface-observer.c \
|
||||
cairo-surface-offset.c \
|
||||
cairo-surface-snapshot.c \
|
||||
cairo-surface-subsurface.c \
|
||||
|
|
|
|||
|
|
@ -530,6 +530,16 @@ enabled_cairo_private += $(cairo_recording_private)
|
|||
enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources)
|
||||
enabled_cairo_sources += $(cairo_recording_sources)
|
||||
|
||||
supported_cairo_headers += $(cairo_observer_headers)
|
||||
all_cairo_headers += $(cairo_observer_headers)
|
||||
all_cairo_private += $(cairo_observer_private)
|
||||
all_cairo_cxx_sources += $(cairo_observer_cxx_sources)
|
||||
all_cairo_sources += $(cairo_observer_sources)
|
||||
enabled_cairo_headers += $(cairo_observer_headers)
|
||||
enabled_cairo_private += $(cairo_observer_private)
|
||||
enabled_cairo_cxx_sources += $(cairo_observer_cxx_sources)
|
||||
enabled_cairo_sources += $(cairo_observer_sources)
|
||||
|
||||
unsupported_cairo_headers += $(cairo_tee_headers)
|
||||
all_cairo_headers += $(cairo_tee_headers)
|
||||
all_cairo_private += $(cairo_tee_private)
|
||||
|
|
|
|||
168
src/cairo-surface-observer-private.h
Normal file
168
src/cairo-surface-observer-private.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2011 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Intel Corporation.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Wilson <chris@chris-wilson.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
|
||||
#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
|
||||
|
||||
#include "cairo-device-private.h"
|
||||
#include "cairo-surface-private.h"
|
||||
|
||||
struct stat {
|
||||
double min, max, sum, sum_sq;
|
||||
unsigned count;
|
||||
};
|
||||
|
||||
#define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1)
|
||||
#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1)
|
||||
#define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1)
|
||||
#define NUM_ANTIALIAS (CAIRO_ANTIALIAS_SUBPIXEL+1)
|
||||
#define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1)
|
||||
|
||||
struct extents {
|
||||
struct stat area;
|
||||
unsigned int bounded, unbounded;
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
unsigned int type[7]; /* native/record/other surface/gradients */
|
||||
};
|
||||
|
||||
struct path {
|
||||
unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */
|
||||
};
|
||||
|
||||
struct clip {
|
||||
unsigned int type[4]; /* none, region, boxes, general */
|
||||
};
|
||||
|
||||
typedef struct _cairo_observation cairo_observation_t;
|
||||
typedef struct _cairo_device_observer cairo_device_observer_t;
|
||||
|
||||
struct _cairo_observation {
|
||||
int num_surfaces;
|
||||
int num_contexts;
|
||||
int num_sources_acquired;
|
||||
|
||||
/* XXX put interesting stats here! */
|
||||
|
||||
struct paint {
|
||||
unsigned int count;
|
||||
struct extents extents;
|
||||
unsigned int operators[NUM_OPERATORS];
|
||||
struct pattern source;
|
||||
struct clip clip;
|
||||
unsigned int noop;
|
||||
} paint;
|
||||
|
||||
struct mask {
|
||||
unsigned int count;
|
||||
struct extents extents;
|
||||
unsigned int operators[NUM_OPERATORS];
|
||||
struct pattern source;
|
||||
struct pattern mask;
|
||||
struct clip clip;
|
||||
unsigned int noop;
|
||||
} mask;
|
||||
|
||||
struct fill {
|
||||
unsigned int count;
|
||||
struct extents extents;
|
||||
unsigned int operators[NUM_OPERATORS];
|
||||
struct pattern source;
|
||||
struct path path;
|
||||
unsigned int antialias[NUM_ANTIALIAS];
|
||||
unsigned int fill_rule[NUM_FILL_RULE];
|
||||
struct clip clip;
|
||||
unsigned int noop;
|
||||
} fill;
|
||||
|
||||
struct stroke {
|
||||
unsigned int count;
|
||||
struct extents extents;
|
||||
unsigned int operators[NUM_OPERATORS];
|
||||
unsigned int caps[NUM_CAPS];
|
||||
unsigned int joins[NUM_CAPS];
|
||||
unsigned int antialias[NUM_ANTIALIAS];
|
||||
struct pattern source;
|
||||
struct path path;
|
||||
struct stat line_width;
|
||||
struct clip clip;
|
||||
unsigned int noop;
|
||||
} stroke;
|
||||
|
||||
struct glyphs {
|
||||
unsigned int count;
|
||||
struct extents extents;
|
||||
unsigned int operators[NUM_OPERATORS];
|
||||
struct pattern source;
|
||||
struct clip clip;
|
||||
unsigned int noop;
|
||||
} glyphs;
|
||||
};
|
||||
|
||||
struct _cairo_device_observer {
|
||||
cairo_device_t base;
|
||||
cairo_device_t *target;
|
||||
|
||||
cairo_observation_t log;
|
||||
};
|
||||
|
||||
struct _cairo_surface_observer {
|
||||
cairo_surface_t base;
|
||||
cairo_surface_t *target;
|
||||
|
||||
cairo_observation_t log;
|
||||
};
|
||||
|
||||
static inline cairo_surface_t *
|
||||
_cairo_surface_observer_get_target (cairo_surface_t *surface)
|
||||
{
|
||||
return ((cairo_surface_observer_t *) surface)->target;
|
||||
}
|
||||
|
||||
static inline cairo_bool_t
|
||||
_cairo_surface_is_observer (cairo_surface_t *surface)
|
||||
{
|
||||
return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER;
|
||||
}
|
||||
|
||||
static inline cairo_bool_t
|
||||
_cairo_device_is_observer (cairo_device_t *device)
|
||||
{
|
||||
return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER;
|
||||
}
|
||||
|
||||
|
||||
#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
|
||||
1009
src/cairo-surface-observer.c
Normal file
1009
src/cairo-surface-observer.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -562,6 +562,8 @@ cairo_surface_create_similar_image (cairo_surface_t *other,
|
|||
int width,
|
||||
int height)
|
||||
{
|
||||
cairo_surface_t *image;
|
||||
|
||||
if (other->status)
|
||||
return _cairo_surface_create_in_error (other->status);
|
||||
if (unlikely (other->finished))
|
||||
|
|
@ -572,11 +574,13 @@ cairo_surface_create_similar_image (cairo_surface_t *other,
|
|||
if (unlikely (! CAIRO_FORMAT_VALID (format)))
|
||||
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
|
||||
|
||||
image = NULL;
|
||||
if (other->backend->create_similar_image)
|
||||
return other->backend->create_similar_image (other,
|
||||
format, width, height);
|
||||
|
||||
return cairo_image_surface_create (format, width, height);
|
||||
image = other->backend->create_similar_image (other,
|
||||
format, width, height);
|
||||
if (image == NULL)
|
||||
image = cairo_image_surface_create (format, width, height);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t;
|
|||
typedef struct _cairo_solid_pattern cairo_solid_pattern_t;
|
||||
typedef struct _cairo_surface_attributes cairo_surface_attributes_t;
|
||||
typedef struct _cairo_surface_backend cairo_surface_backend_t;
|
||||
typedef struct _cairo_surface_observer cairo_surface_observer_t;
|
||||
typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t;
|
||||
typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t;
|
||||
typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t;
|
||||
|
|
@ -231,6 +232,7 @@ typedef enum _cairo_internal_surface_type {
|
|||
CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
|
||||
CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING,
|
||||
|
|
@ -238,6 +240,10 @@ typedef enum _cairo_internal_surface_type {
|
|||
CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH
|
||||
} cairo_internal_surface_type_t;
|
||||
|
||||
typedef enum _cairo_internal_device_type {
|
||||
CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000,
|
||||
} cairo_device_surface_type_t;
|
||||
|
||||
#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1
|
||||
#define CAIRO_HAS_TEST_NULL_SURFACE 1
|
||||
#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1
|
||||
|
|
|
|||
13
src/cairo.h
13
src/cairo.h
|
|
@ -2095,6 +2095,19 @@ cairo_surface_create_for_rectangle (cairo_surface_t *target,
|
|||
double width,
|
||||
double height);
|
||||
|
||||
cairo_public cairo_surface_t *
|
||||
cairo_surface_create_observer (cairo_surface_t *target);
|
||||
|
||||
cairo_public void
|
||||
cairo_surface_observer_print (cairo_surface_t *surface,
|
||||
cairo_write_func_t write_func,
|
||||
void *closure);
|
||||
|
||||
cairo_public void
|
||||
cairo_device_observer_print (cairo_device_t *device,
|
||||
cairo_write_func_t write_func,
|
||||
void *closure);
|
||||
|
||||
cairo_public cairo_surface_t *
|
||||
cairo_surface_reference (cairo_surface_t *surface);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue