[sysprof/wip/chergert/mem-preload: 34/43] memprof: start on visualizer
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof/wip/chergert/mem-preload: 34/43] memprof: start on visualizer
- Date: Sat, 8 Feb 2020 01:01:13 +0000 (UTC)
commit fae0644662db2381b3671c594ac8103e54adcbb0
Author: Christian Hergert <chergert redhat com>
Date: Thu Feb 6 12:26:58 2020 -0800
memprof: start on visualizer
This just gets some mechanics in place so that we can draw asynchronously
and scale the results until our next sized draw comes in (such as from
changing zoom level).
src/libsysprof-ui/sysprof-memprof-aid.c | 12 +-
src/libsysprof-ui/sysprof-memprof-visualizer.c | 340 ++++++++++++++++++++-----
2 files changed, 285 insertions(+), 67 deletions(-)
---
diff --git a/src/libsysprof-ui/sysprof-memprof-aid.c b/src/libsysprof-ui/sysprof-memprof-aid.c
index a0f9866..5b9fbda 100644
--- a/src/libsysprof-ui/sysprof-memprof-aid.c
+++ b/src/libsysprof-ui/sysprof-memprof-aid.c
@@ -27,6 +27,7 @@
#include "sysprof-memprof-aid.h"
#include "sysprof-memprof-page.h"
#include "sysprof-memprof-source.h"
+#include "sysprof-memprof-visualizer.h"
struct _SysprofMemprofAid
{
@@ -171,6 +172,7 @@ sysprof_memprof_aid_present_finish (SysprofAid *aid,
if (p->has_allocs)
{
SysprofVisualizerGroup *group;
+ SysprofVisualizer *row;
SysprofPage *page;
group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP,
@@ -180,7 +182,13 @@ sysprof_memprof_aid_present_finish (SysprofAid *aid,
"title", _("Memory"),
"visible", TRUE,
NULL);
- sysprof_display_add_group (p->display, group);
+
+ row = g_object_new (SYSPROF_TYPE_MEMPROF_VISUALIZER,
+ "title", _("Memory Allocations"),
+ "height-request", 35,
+ "visible", TRUE,
+ NULL);
+ sysprof_visualizer_group_insert (group, row, 0, FALSE);
page = g_object_new (SYSPROF_TYPE_MEMPROF_PAGE,
"title", _("Memory Allocations"),
@@ -195,6 +203,8 @@ sysprof_memprof_aid_present_finish (SysprofAid *aid,
G_CALLBACK (on_group_activated_cb),
page,
0);
+
+ sysprof_display_add_group (p->display, group);
}
return g_task_propagate_boolean (G_TASK (result), error);
diff --git a/src/libsysprof-ui/sysprof-memprof-visualizer.c b/src/libsysprof-ui/sysprof-memprof-visualizer.c
index cf97d85..1c2209d 100644
--- a/src/libsysprof-ui/sysprof-memprof-visualizer.c
+++ b/src/libsysprof-ui/sysprof-memprof-visualizer.c
@@ -26,15 +26,27 @@
typedef struct
{
- guint64 time;
- guint64 size;
-} Memstat;
+ cairo_surface_t *surface;
+ SysprofCaptureReader *reader;
+ GtkAllocation alloc;
+ gint64 begin_time;
+ gint64 duration;
+ GdkRGBA fg;
+ GdkRGBA bg;
+} DrawContext;
struct _SysprofMemprofVisualizer
{
SysprofVisualizer parent_instance;
SysprofCaptureReader *reader;
+ GCancellable *cancellable;
+
+ cairo_surface_t *surface;
+ gint surface_w;
+ gint surface_h;
+
+ guint queued_draw;
gint64 begin_time;
gint64 duration;
@@ -43,114 +55,310 @@ struct _SysprofMemprofVisualizer
G_DEFINE_TYPE (SysprofMemprofVisualizer, sysprof_memprof_visualizer, SYSPROF_TYPE_VISUALIZER)
static void
-load_data_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+draw_context_finalize (DrawContext *draw)
+{
+ g_clear_pointer (&draw->surface, cairo_surface_destroy);
+}
+
+static void
+draw_context_unref (DrawContext *draw)
+{
+ g_atomic_rc_box_release_full (draw, (GDestroyNotify)draw_context_finalize);
+}
+
+static DrawContext *
+draw_context_ref (DrawContext *draw)
+{
+ return g_atomic_rc_box_acquire (draw);
+}
+
+static void
+sysprof_memprof_visualizer_set_reader (SysprofVisualizer *visualizer,
+ SysprofCaptureReader *reader)
{
- SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)object;
+ SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)visualizer;
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
- g_assert (G_IS_TASK (result));
-#if 0
- if ((pc = g_task_propagate_pointer (G_TASK (result), NULL)))
- {
- g_clear_pointer (&self->cache, point_cache_unref);
- self->cache = g_steal_pointer (&pc);
- }
-#endif
+ if (reader == self->reader)
+ return;
+
+ g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
+
+ self->reader = sysprof_capture_reader_ref (reader);
+ self->begin_time = sysprof_capture_reader_get_start_time (reader);
+ self->duration = sysprof_capture_reader_get_end_time (reader)
+ - sysprof_capture_reader_get_start_time (reader);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
-static gboolean
-collect_allocs_cb (const SysprofCaptureFrame *frame,
- gpointer user_data)
+SysprofMemprofVisualizer *
+sysprof_memprof_visualizer_new (void)
{
- GArray *ar = user_data;
+ return g_object_new (SYSPROF_TYPE_MEMPROF_VISUALIZER, NULL);
+}
- g_assert (frame != NULL);
- g_assert (ar != NULL);
+static void
+draw_context_add (cairo_t *cr,
+ DrawContext *draw,
+ const SysprofCaptureAllocation *ev)
+{
+ gint x;
+ gint y;
+
+ g_assert (draw != NULL);
+ g_assert (ev != NULL);
+
+ x = (ev->frame.time - draw->begin_time) / (gdouble)draw->duration * draw->alloc.width;
+ y = 0;
- return TRUE;
+ cairo_rectangle (cr, x, y, 1, 1);
}
static void
-sysprof_memprof_visualizer_worker (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+sysprof_memprof_visualizer_draw_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
- SysprofMemprofVisualizer *self = source_object;
- SysprofCaptureCursor *cursor = task_data;
- GArray *ar;
+ DrawContext *draw = task_data;
+ SysprofCaptureFrameType type;
+ cairo_t *cr;
+ guint counter = 0;
g_assert (G_IS_TASK (task));
- g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
- g_assert (cursor != NULL);
+ g_assert (draw != NULL);
+ g_assert (draw->surface != NULL);
+ g_assert (draw->reader != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- ar = g_array_new (FALSE, FALSE, sizeof (Memstat));
- sysprof_capture_cursor_foreach (cursor, collect_allocs_cb, ar);
+ cr = cairo_create (draw->surface);
+
+ /* Fill background first */
+ gdk_cairo_rectangle (cr, &draw->alloc);
+ gdk_cairo_set_source_rgba (cr, &draw->bg);
+ cairo_fill (cr);
- g_task_return_pointer (task,
- g_steal_pointer (&ar),
- (GDestroyNotify) g_array_unref);
+ /* Now draw data points */
+ while (sysprof_capture_reader_peek_type (draw->reader, &type))
+ {
+ const SysprofCaptureAllocation *ev;
+
+ /* Cancellation check every 1000 frames */
+ if G_UNLIKELY (++counter == 1000)
+ {
+ if (g_task_return_error_if_cancelled (task))
+ {
+ cairo_destroy (cr);
+ return;
+ }
+
+ counter = 0;
+ }
+
+ /* We only care about memory frames here */
+ if (type != SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC &&
+ type != SYSPROF_CAPTURE_FRAME_MEMORY_FREE)
+ {
+ if (sysprof_capture_reader_skip (draw->reader))
+ continue;
+ break;
+ }
+
+ if (type == SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC)
+ ev = sysprof_capture_reader_read_memory_alloc (draw->reader);
+ else
+ ev = sysprof_capture_reader_read_memory_free (draw->reader);
+
+ if (ev == NULL)
+ break;
+
+ draw_context_add (cr, draw, ev);
+ }
+
+ /* Now fill our draw paths */
+ gdk_cairo_set_source_rgba (cr, &draw->fg);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ g_task_return_boolean (task, TRUE);
}
static void
-sysprof_memprof_visualizer_set_reader (SysprofVisualizer *visualizer,
- SysprofCaptureReader *reader)
+draw_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(SysprofMemprofVisualizer) self = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (object == NULL);
+ g_assert (G_IS_TASK (result));
+ g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
+
+ if (g_task_propagate_boolean (G_TASK (result), &error))
+ {
+ DrawContext *draw = g_task_get_task_data (G_TASK (result));
+
+ g_clear_pointer (&self->surface, cairo_surface_destroy);
+
+ self->surface = g_steal_pointer (&draw->surface);
+ self->surface_w = draw->alloc.width;
+ self->surface_h = draw->alloc.height;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static gboolean
+sysprof_memprof_visualizer_begin_draw (SysprofMemprofVisualizer *self)
{
- SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)visualizer;
- static const SysprofCaptureFrameType types[] = {
- SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC,
- SYSPROF_CAPTURE_FRAME_MEMORY_FREE,
- };
- g_autoptr(SysprofCaptureCursor) cursor = NULL;
g_autoptr(GTask) task = NULL;
- SysprofCaptureCondition *c;
+ GtkStyleContext *style_context;
+ GtkAllocation alloc;
+ GtkStateFlags state;
+ DrawContext *draw;
+ GdkWindow *window;
+ gint scale;
g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
- g_assert (reader != NULL);
- self->begin_time = sysprof_capture_reader_get_start_time (reader);
- self->duration = sysprof_capture_reader_get_end_time (reader)
- - sysprof_capture_reader_get_start_time (reader);
+ self->queued_draw = 0;
+
+ /* Make sure we even need to draw */
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+ if (self->reader == NULL ||
+ !gtk_widget_get_visible (GTK_WIDGET (self)) ||
+ !gtk_widget_get_mapped (GTK_WIDGET (self)) ||
+ alloc.width == 0 || alloc.height == 0)
+ return G_SOURCE_REMOVE;
+
+ draw = g_atomic_rc_box_new0 (DrawContext);
+ draw->alloc.width = alloc.width;
+ draw->alloc.height = alloc.height;
+ draw->reader = sysprof_capture_reader_copy (self->reader);
+ draw->begin_time = self->begin_time;
+ draw->duration = self->duration;
+
+ /* Create a surface to draw to */
+ window = gtk_widget_get_window (GTK_WIDGET (self));
+ scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ draw->surface = gdk_window_create_similar_image_surface (window, CAIRO_FORMAT_RGB24, alloc.width,
alloc.height, scale);
- cursor = sysprof_capture_cursor_new (reader);
- c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
- sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c));
+ /* Get our styles to render with so we can do off-main-thread */
+ state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_get_color (style_context, state, &draw->fg);
+ /* Takin the render cost for render_background() isn't worth it on the
+ * main thread. The main thing we want here is an opaque surface that we
+ * can quickly draw as a texture when rendering.
+ */
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gtk_style_context_get_background_color (style_context, state, &draw->bg);
+ G_GNUC_END_IGNORE_DEPRECATIONS
- task = g_task_new (self, NULL, load_data_cb, NULL);
- g_task_set_source_tag (task, sysprof_memprof_visualizer_set_reader);
- g_task_set_task_data (task,
- g_steal_pointer (&cursor),
- (GDestroyNotify)sysprof_capture_cursor_unref);
- g_task_run_in_thread (task, sysprof_memprof_visualizer_worker);
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ task = g_task_new (NULL, self->cancellable, draw_finished, g_object_ref (self));
+ g_task_set_source_tag (task, sysprof_memprof_visualizer_begin_draw);
+ g_task_set_task_data (task, g_steal_pointer (&draw), (GDestroyNotify)draw_context_unref);
+ g_task_run_in_thread (task, sysprof_memprof_visualizer_draw_worker);
+
+ return G_SOURCE_REMOVE;
}
-SysprofMemprofVisualizer *
-sysprof_memprof_visualizer_new (void)
+static void
+sysprof_memprof_visualizer_queue_redraw (SysprofMemprofVisualizer *self)
{
- return g_object_new (SYSPROF_TYPE_MEMPROF_VISUALIZER, NULL);
+ g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
+
+ if (self->queued_draw == 0)
+ self->queued_draw = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ (GSourceFunc) sysprof_memprof_visualizer_begin_draw,
+ g_object_ref (self),
+ g_object_unref);
}
static void
-sysprof_memprof_visualizer_finalize (GObject *object)
+sysprof_memprof_visualizer_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
{
- SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)object;
+ SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
+
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (alloc != NULL);
+
+ GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->size_allocate (widget, alloc);
+
+ sysprof_memprof_visualizer_queue_redraw (self);
+}
+
+static void
+sysprof_memprof_visualizer_destroy (GtkWidget *widget)
+{
+ SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
+
+ g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
+ g_clear_pointer (&self->surface, cairo_surface_destroy);
+ g_clear_handle_id (&self->queued_draw, g_source_remove);
+
+ GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->destroy (widget);
+}
+
+static gboolean
+sysprof_memprof_visualizer_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget;
+
+ g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self));
+ g_assert (cr != NULL);
+
+ if (self->surface != NULL)
+ {
+ GtkAllocation alloc;
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ cairo_save (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle (cr, 0, 0, alloc.width, alloc.height);
+
+ /* We might be drawing an updated image in the background, and this
+ * will take our current surface (which is the wrong size) and draw
+ * it stretched to fit the allocation. That gives us *something* that
+ * represents the end result even if it is a bit blurry in the mean
+ * time. Allocators take a while to render anyway.
+ */
+ if (self->surface_w != alloc.width || self->surface_h != alloc.height)
+ {
+ cairo_scale (cr,
+ (gdouble)alloc.width / (gdouble)self->surface_w,
+ (gdouble)alloc.height / (gdouble)self->surface_h);
+ }
+
+ cairo_set_source_surface (cr, self->surface, 0, 0);
+ cairo_fill (cr);
+ cairo_restore (cr);
+
+ return GDK_EVENT_PROPAGATE;
+ }
- G_OBJECT_CLASS (sysprof_memprof_visualizer_parent_class)->finalize (object);
+ return GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->draw (widget, cr);
}
static void
sysprof_memprof_visualizer_class_init (SysprofMemprofVisualizerClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass);
- object_class->finalize = sysprof_memprof_visualizer_finalize;
+ widget_class->destroy = sysprof_memprof_visualizer_destroy;
+ widget_class->draw = sysprof_memprof_visualizer_draw;
+ widget_class->size_allocate = sysprof_memprof_visualizer_size_allocate;
visualizer_class->set_reader = sysprof_memprof_visualizer_set_reader;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]