diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 5713a3e01..2a5d783b3 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -80,6 +80,14 @@ struct weston_commit_timing_target { struct timespec time; }; +struct weston_cached_string { + bool is_up_to_date; + char *cached_str; + + /* assumes FP opened, use it to (re-)compose the string */ + void (*regen)(FILE *fp, void *data); +}; + struct weston_compositor; struct weston_surface; struct weston_buffer; @@ -1588,6 +1596,8 @@ struct weston_buffer { const struct pixel_format_info *pixel_format; uint64_t format_modifier; + + struct weston_cached_string scene_graph_record; }; enum weston_buffer_reference_type { @@ -1771,6 +1781,8 @@ struct weston_view { bool is_mapped; struct weston_log_pacer subsurface_parent_log_pacer; + + struct weston_cached_string scene_graph_record; }; enum weston_surface_status { @@ -2042,6 +2054,7 @@ struct weston_surface { /** commit_timing_v1 */ struct weston_commit_timer *commit_timer; + struct weston_cached_string scene_graph_record; }; struct weston_subsurface { @@ -2822,6 +2835,12 @@ weston_output_get_supported_color_formats(struct weston_output *output); void weston_output_set_ready(struct weston_output *output); +const char * +weston_cached_str_get(struct weston_cached_string *s, void *data); + +void +weston_cached_str_invalidate(struct weston_cached_string *s); + #ifdef __cplusplus } #endif diff --git a/libweston/compositor.c b/libweston/compositor.c index cbfb2cdac..3fd10f8dc 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -104,6 +104,9 @@ #define DEFAULT_REPAINT_WINDOW 7 /* milliseconds */ +static struct weston_layer * +get_view_layer(struct weston_view *view); + static void weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale); @@ -376,6 +379,29 @@ paint_node_update_late(struct weston_paint_node *pnode) paint_node_validate_ready(pnode); } +static void +weston_paint_node_debug_string_regenerate(FILE *fp, void *data) +{ + struct weston_paint_node *pnode = data; + struct weston_view *view = NULL; + struct weston_output *output = NULL; + + if (!pnode) { + fprintf(fp, "\t\t\tpaint node [pending repaint]:\n"); + return; + } + + fprintf(fp, "\t\t\tpaint node %p:\n", pnode); + + view = pnode->view; + output = pnode->output; + + fprintf(fp, "\t\t\t\toutput: %d (%s)%s\n", + output->id, output->name, + (view->output == output) ? " (primary)" : ""); + +} + static struct weston_paint_node * weston_paint_node_create(struct weston_surface *surface, struct weston_view *view, @@ -429,6 +455,9 @@ weston_paint_node_create(struct weston_surface *surface, pnode->status = WESTON_PAINT_NODE_ALL_DIRTY & ~WESTON_PAINT_NODE_PLANE_DIRTY; + pnode->scene_graph_record.regen = + weston_paint_node_debug_string_regenerate; + return pnode; } @@ -480,6 +509,7 @@ weston_paint_node_destroy(struct weston_paint_node *pnode) pixman_region32_fini(&pnode->visible); pixman_region32_fini(&pnode->visible_previous); pixman_region32_fini(&pnode->clipped_view); + free(pnode->scene_graph_record.cached_str); free(pnode); } @@ -733,6 +763,39 @@ weston_output_mode_switch_to_temporary(struct weston_output *output, return 0; } +static void +weston_view_debug_string_regenerate(FILE *fp, void *data) +{ + struct weston_view *ev = data; + pixman_box32_t *box; + + if (!weston_view_is_mapped(ev)) + fprintf(fp, "\t[view is not mapped!]\n"); + if (wl_list_empty(&ev->layer_link.link)) { + if (!get_view_layer(ev)) + fprintf(fp, "\t[view is not part of any layer]\n"); + else + fprintf(fp, "\t[view is under parent view layer]\n"); + } + + box = pixman_region32_extents(&ev->transform.boundingbox); + fprintf(fp, "\t\tposition: (%d, %d) -> (%d, %d)\n", + box->x1, box->y1, box->x2, box->y2); + box = pixman_region32_extents(&ev->transform.opaque); + + if (weston_view_is_opaque(ev, &ev->transform.boundingbox)) { + fprintf(fp, "\t\t[fully opaque]\n"); + } else if (!pixman_region32_not_empty(&ev->transform.opaque)) { + fprintf(fp, "\t\t[not opaque]\n"); + } else { + fprintf(fp, "\t\t[opaque: (%d, %d) -> (%d, %d)]\n", + box->x1, box->y1, box->x2, box->y2); + } + + if (ev->alpha < 1.0) + fprintf(fp, "\t\talpha: %f\n", ev->alpha); +} + static struct weston_view * weston_view_create_internal(struct weston_surface *surface) { @@ -765,6 +828,9 @@ weston_view_create_internal(struct weston_surface *surface) pixman_region32_init(&view->geometry.scissor); pixman_region32_init(&view->transform.boundingbox); view->transform.dirty = 1; + + view->scene_graph_record.regen = + weston_view_debug_string_regenerate; weston_view_update_transform(view); return view; @@ -962,6 +1028,33 @@ weston_surface_update_preferred_color_profile(struct weston_surface *surface) weston_surface_send_preferred_image_description_changed(surface); } +static void +weston_surface_debug_string_regenerate(FILE *fp, void *data) +{ + struct weston_surface *surface = data; + uint32_t surface_id = 0; + pid_t pid = 0; + char desc[512]; + + if (surface->resource) { + struct wl_resource *resource = surface->resource; + wl_client_get_credentials(wl_resource_get_client(resource), + &pid, NULL, NULL); + surface_id = wl_resource_get_id(surface->resource); + } + + if (!surface->get_label || + surface->get_label(surface, desc, sizeof(desc)) < 0) { + strcpy(desc, "[no description available]"); + } + + fprintf(fp, "role %s, PID %d, surface ID %u, %s", + weston_surface_get_role(surface), pid, surface_id, desc); + + if (!weston_surface_is_mapped(surface)) + fprintf(fp, "\t[surface is not mapped!]\n"); +} + WL_EXPORT struct weston_surface * weston_surface_create(struct weston_compositor *compositor) { @@ -1029,6 +1122,9 @@ weston_surface_create(struct weston_compositor *compositor) wl_list_init(&surface->fifo_barrier_link); + surface->scene_graph_record.regen = + weston_surface_debug_string_regenerate; + return surface; } @@ -1958,6 +2054,8 @@ weston_view_update_transform_internal(struct weston_view *view) geometry.parent_link) { weston_view_update_transform(child); } + + weston_cached_str_invalidate(&view->scene_graph_record); } WL_EXPORT void @@ -2593,6 +2691,7 @@ weston_view_unmap(struct weston_view *view) } weston_view_destroy_paint_nodes(view); + weston_cached_str_invalidate(&view->scene_graph_record); wl_signal_emit_mutable(&view->unmap_signal, view); view->surface->compositor->view_list_needs_rebuild = true; @@ -2605,6 +2704,8 @@ static void weston_surface_start_mapping(struct weston_surface *surface) surface->is_mapping = true; surface->is_mapped = true; surface->compositor->view_list_needs_rebuild = true; + + weston_cached_str_invalidate(&surface->scene_graph_record); wl_signal_emit_mutable(&surface->map_signal, surface); } @@ -2628,6 +2729,8 @@ weston_surface_unmap(struct weston_surface *surface) wl_list_for_each(view, &surface->views, surface_link) weston_view_unmap(view); surface->output = NULL; + + weston_cached_str_invalidate(&surface->scene_graph_record); wl_signal_emit_mutable(&surface->unmap_signal, surface); } @@ -2660,6 +2763,7 @@ weston_view_destroy(struct weston_view *view) wl_list_remove(&view->surface_link); + free(view->scene_graph_record.cached_str); free(view); } @@ -2747,6 +2851,7 @@ weston_surface_unref(struct weston_surface *surface) wl_list_remove(&surface->fifo_barrier_link); + free(surface->scene_graph_record.cached_str); free(surface); } @@ -2808,9 +2913,60 @@ weston_buffer_destroy_handler(struct wl_listener *listener, void *data) return; wl_signal_emit_mutable(&buffer->destroy_signal, buffer); + free(buffer->scene_graph_record.cached_str); free(buffer); } +static void +weston_buffer_debug_string_regenerate(FILE *fp, void *data) +{ + struct weston_buffer *buffer = data; + char *modifier_name; + + switch (buffer->type) { + case WESTON_BUFFER_SHM: + fprintf(fp, "\t\tSHM buffer\n"); + break; + case WESTON_BUFFER_DMABUF: + fprintf(fp, "\t\tdmabuf buffer\n"); + break; + case WESTON_BUFFER_SOLID: + fprintf(fp, "\t\tsolid-colour buffer\n"); + fprintf(fp, "\t\t\t[R %f, G %f, B %f, A %f]\n", + buffer->solid.r, buffer->solid.g, buffer->solid.b, + buffer->solid.a); + break; + case WESTON_BUFFER_RENDERER_OPAQUE: + fprintf(fp, "\t\tEGL buffer:\n"); + fprintf(fp, "\t\t\t[format may be inaccurate]\n"); + break; + } + + + if (buffer->pixel_format) { + fprintf(fp, "\t\t\tformat: 0x%lx %s\n", + (unsigned long) buffer->pixel_format->format, + buffer->pixel_format->drm_format_name); + } else { + fprintf(fp, "\t\t\t[unknown format]\n"); + } + + modifier_name = pixel_format_get_modifier(buffer->format_modifier); + fprintf(fp, "\t\t\tmodifier: %s\n", + modifier_name ? + modifier_name : "Failed to convert to a modifier name"); + free(modifier_name); + + fprintf(fp, "\t\t\twidth: %d, height: %d\n", + buffer->width, buffer->height); + if (buffer->buffer_origin == ORIGIN_BOTTOM_LEFT) + fprintf(fp, "\t\t\tbottom-left origin\n"); + + if (buffer->direct_display) + fprintf(fp, "\t\t\tdirect-display buffer (no renderer access)\n"); +} + + WL_EXPORT struct weston_buffer * weston_buffer_from_resource(struct weston_compositor *ec, struct wl_resource *resource) @@ -2833,6 +2989,8 @@ weston_buffer_from_resource(struct weston_compositor *ec, return NULL; buffer->resource = resource; + buffer->scene_graph_record.regen = + weston_buffer_debug_string_regenerate; wl_signal_init(&buffer->destroy_signal); buffer->destroy_listener.notify = weston_buffer_destroy_handler; wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); @@ -2901,6 +3059,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, fail: wl_list_remove(&buffer->destroy_listener.link); + free(buffer->scene_graph_record.cached_str); free(buffer); return NULL; } @@ -2958,6 +3117,7 @@ weston_buffer_reference(struct weston_buffer_reference *ref, !old_ref.buffer->resource) { wl_signal_emit_mutable(&old_ref.buffer->destroy_signal, old_ref.buffer); + free(old_ref.buffer->scene_graph_record.cached_str); free(old_ref.buffer); } } @@ -3049,6 +3209,8 @@ weston_buffer_create_solid_rgba(struct weston_compositor *compositor, buffer->solid.g = g; buffer->solid.b = b; buffer->solid.a = a; + buffer->scene_graph_record.regen = + weston_buffer_debug_string_regenerate; if (a == 1.0) { buffer->pixel_format = @@ -5389,6 +5551,8 @@ weston_surface_set_label_func(struct weston_surface *surface, surface->get_label = desc; weston_timeline_refresh_subscription_objects(surface->compositor, surface); + + weston_cached_str_invalidate(&surface->scene_graph_record); } /** Get the size of surface contents @@ -9422,31 +9586,13 @@ static void debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) { struct weston_buffer *buffer = view->surface->buffer_ref.buffer; - char *modifier_name; if (!buffer) { fprintf(fp, "\t\t[buffer not available]\n"); return; } - switch (buffer->type) { - case WESTON_BUFFER_SHM: - fprintf(fp, "\t\tSHM buffer\n"); - break; - case WESTON_BUFFER_DMABUF: - fprintf(fp, "\t\tdmabuf buffer\n"); - break; - case WESTON_BUFFER_SOLID: - fprintf(fp, "\t\tsolid-colour buffer\n"); - fprintf(fp, "\t\t\t[R %f, G %f, B %f, A %f]\n", - buffer->solid.r, buffer->solid.g, buffer->solid.b, - buffer->solid.a); - break; - case WESTON_BUFFER_RENDERER_OPAQUE: - fprintf(fp, "\t\tEGL buffer:\n"); - fprintf(fp, "\t\t\t[format may be inaccurate]\n"); - break; - } + fputs(weston_cached_str_get(&buffer->scene_graph_record, buffer), fp); if (buffer->busy_count > 0) { fprintf(fp, "\t\t\t[%d references may use buffer content]\n", @@ -9455,27 +9601,6 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) fprintf(fp, "\t\t\t[buffer has been released to client]\n"); } - if (buffer->pixel_format) { - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) buffer->pixel_format->format, - buffer->pixel_format->drm_format_name); - } else { - fprintf(fp, "\t\t\t[unknown format]\n"); - } - - modifier_name = pixel_format_get_modifier(buffer->format_modifier); - fprintf(fp, "\t\t\tmodifier: %s\n", - modifier_name ? - modifier_name : "Failed to convert to a modifier name"); - free(modifier_name); - - fprintf(fp, "\t\t\twidth: %d, height: %d\n", - buffer->width, buffer->height); - if (buffer->buffer_origin == ORIGIN_BOTTOM_LEFT) - fprintf(fp, "\t\t\tbottom-left origin\n"); - - if (buffer->direct_display) - fprintf(fp, "\t\t\tdirect-display buffer (no renderer access)\n"); } static const struct weston_enum_map transforms[] = { @@ -9552,22 +9677,15 @@ debug_scene_view_print_paint_node(FILE *fp, struct weston_paint_node *pnode; pnode = weston_view_find_paint_node(view, output); - if (!pnode) - fprintf(fp, "\t\t\tpaint node [pending repaint]:\n"); - else - fprintf(fp, "\t\t\tpaint node %p:\n", pnode); - - fprintf(fp, "\t\t\t\toutput: %d (%s)%s\n", - output->id, output->name, - (view->output == output) ? " (primary)" : ""); + fputs(weston_cached_str_get(&pnode->scene_graph_record, pnode), fp); if (!pnode) return; fprintf(fp, "\t\t\t\tBuffer to output transform: "); - if (!pnode->valid_transform) + if (!pnode->valid_transform) { fprintf(fp, "Free form\n"); - else { + } else { const char *tform; tform = weston_transform_to_string(pnode->transform); @@ -9575,11 +9693,10 @@ debug_scene_view_print_paint_node(FILE *fp, } if (pnode->try_view_on_plane_failure_reasons) { - char *fr_str = bits_to_str(pnode->try_view_on_plane_failure_reasons, - weston_plane_failure_reasons_to_str); - fprintf(fp, "\t\t\t\tPlane failure reasons: %s\n", fr_str); - - free(fr_str); + fprintf(fp, "\t\t\t\tPlane failure reasons: "); + bits_to_str_stream(pnode->try_view_on_plane_failure_reasons, + weston_plane_failure_reasons_to_str, fp); + fprintf(fp, "\n"); } } @@ -9588,53 +9705,12 @@ debug_scene_view_print(FILE *fp, struct weston_view *view, int view_idx) { struct weston_compositor *ec = view->surface->compositor; struct weston_output *output; - char desc[512]; - pixman_box32_t *box; - uint32_t surface_id = 0; - pid_t pid = 0; - if (view->surface->resource) { - struct wl_resource *resource = view->surface->resource; - wl_client_get_credentials(wl_resource_get_client(resource), - &pid, NULL, NULL); - surface_id = wl_resource_get_id(view->surface->resource); - } + fprintf(fp, "\tView %d (%s %p):\n", view_idx, + weston_cached_str_get(&view->surface->scene_graph_record, view->surface), + view); - if (!view->surface->get_label || - view->surface->get_label(view->surface, desc, sizeof(desc)) < 0) { - strcpy(desc, "[no description available]"); - } - fprintf(fp, "\tView %d (role %s, PID %d, surface ID %u, %s, %p):\n", - view_idx, view->surface->role_name, pid, surface_id, - desc, view); - - if (!weston_view_is_mapped(view)) - fprintf(fp, "\t[view is not mapped!]\n"); - if (!weston_surface_is_mapped(view->surface)) - fprintf(fp, "\t[surface is not mapped!]\n"); - if (wl_list_empty(&view->layer_link.link)) { - if (!get_view_layer(view)) - fprintf(fp, "\t[view is not part of any layer]\n"); - else - fprintf(fp, "\t[view is under parent view layer]\n"); - } - - box = pixman_region32_extents(&view->transform.boundingbox); - fprintf(fp, "\t\tposition: (%d, %d) -> (%d, %d)\n", - box->x1, box->y1, box->x2, box->y2); - box = pixman_region32_extents(&view->transform.opaque); - - if (weston_view_is_opaque(view, &view->transform.boundingbox)) { - fprintf(fp, "\t\t[fully opaque]\n"); - } else if (!pixman_region32_not_empty(&view->transform.opaque)) { - fprintf(fp, "\t\t[not opaque]\n"); - } else { - fprintf(fp, "\t\t[opaque: (%d, %d) -> (%d, %d)]\n", - box->x1, box->y1, box->x2, box->y2); - } - - if (view->alpha < 1.0) - fprintf(fp, "\t\talpha: %f\n", view->alpha); + fputs(weston_cached_str_get(&view->scene_graph_record, view), fp); if (view->output_mask != 0) { fprintf(fp, "\t\tpaint nodes:\n"); @@ -9708,6 +9784,8 @@ weston_compositor_print_scene_graph(struct weston_compositor *ec) size_t len; int err; + WESTON_TRACE_FUNC(); + fp = open_memstream(&ret, &len); assert(fp); @@ -10920,3 +10998,56 @@ weston_output_set_ready(struct weston_output *output) weston_output_schedule_repaint(output); } } + +/** + * Returns a cached string value held up by a pointer to a struct + * weston_cached_string or regenerates the string in case the cached value has + * been invalidated. This is useful for caching object's properties as a string + * value. Users should use weston_cached_str_invalidate in combination with + * this function to denote when it is time to regenerate the string cached + * value. + * + * Note that over-invalidation would actually hurt performance so this is ideal + * for (object) properties that do *not* change each frame. + * + * \param s a pointer to struct weston_cached_string + * \param data generic pointer to be passed to the regen function + * \sa weston_cached_str_invalidate + * + */ +WL_EXPORT const char * +weston_cached_str_get(struct weston_cached_string *s, void *data) +{ + if (!s->is_up_to_date) { + char *str = NULL; + size_t size = 0; + FILE *fp; + + free(s->cached_str); + + fp = open_memstream(&str, &size); + abort_oom_if_null(fp); + s->regen(fp, data); + if (fclose(fp) == 0) { + s->cached_str = str; + } else { + free(str); + s->cached_str = xstrdup("[error]"); + } + s->is_up_to_date = true; + } + return s->cached_str; +} + +/** Invalidates the cached string value and forces weston_cached_str_get() to + * call the regen callback associated with that particular weston_cached_string + * + * \param s a pointer to struct weston_cached_string + * + */ +WL_EXPORT void +weston_cached_str_invalidate(struct weston_cached_string *s) +{ + WESTON_TRACE_FUNC(); + s->is_up_to_date = false; +} diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 7ff673a91..777a8210f 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -750,6 +750,8 @@ struct weston_paint_node { */ bool need_hole; uint32_t psf_flags; /* presentation-feedback flags */ + + struct weston_cached_string scene_graph_record; }; struct weston_paint_node * diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 45a2a148e..943e39539 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3398,6 +3398,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, buffer->renderer_private = gb; gb->destroy_listener.notify = handle_buffer_destroy; + weston_cached_str_invalidate(&buffer->scene_graph_record); wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); return true; diff --git a/shared/string-helpers.h b/shared/string-helpers.h index d566f8c2f..e17348ca4 100644 --- a/shared/string-helpers.h +++ b/shared/string-helpers.h @@ -130,12 +130,42 @@ str_printf(char **str_out, const char *fmt, ...) } /** - * Utility to print combination of enum values as string + * Utility to print combination of enum values as string. Use an opened FILE + * stream to write data. * - * Only works for enum whose values are defined as power of two. Given a bitmask - * in which each bit represents an enum value and a function that maps each enum - * value to a string, this function returns a string (comma separated) with all - * the enum values that are present in the bitmask. + * \param bits The bitmask of enum values. + * \param map Function that maps enum values to string. + * \param fp an opened FILE stream + */ +static inline void +bits_to_str_stream(uint32_t bits, const char *(*map)(uint32_t), FILE *fp) +{ + unsigned i; + const char *sep = ""; + + if (!fp) + return; + + for (i = 0; bits; i++) { + uint32_t bitmask = 1u << i; + + if (bits & bitmask) { + fprintf(fp, "%s%s", sep, map(bitmask)); + sep = ", "; + } + + bits &= ~bitmask; + } +} + +/** + * Utility to print combination of enum values as string. + * Uses bits_to_str_stream() helper. + * + * Only works for enum whose values are defined as power of two. Given a + * bitmask in which each bit represents an enum value and a function that maps + * each enum value to a string, this function returns a string (comma + * separated) with all the enum values that are present in the bitmask. * * \param bits The bitmask of enum values. * \param map Function that maps enum values to string. @@ -148,23 +178,13 @@ bits_to_str(uint32_t bits, const char *(*map)(uint32_t)) FILE *fp; char *str = NULL; size_t size = 0; - unsigned i; - const char *sep = ""; fp = open_memstream(&str, &size); if (!fp) return NULL; - for (i = 0; bits; i++) { - uint32_t bitmask = 1u << i; + bits_to_str_stream(bits, map, fp); - if (bits & bitmask) { - fprintf(fp, "%s%s", sep, map(bitmask)); - sep = ", "; - } - - bits &= ~bitmask; - } fclose(fp); return str;