1 | #include <gtk/gtk.h> |
2 | |
3 | static gboolean benchmark = FALSE; |
4 | static gboolean dump_variant = FALSE; |
5 | static gboolean fallback = FALSE; |
6 | static int runs = 1; |
7 | |
8 | static GOptionEntry options[] = { |
9 | { "benchmark" , 'b', 0, G_OPTION_ARG_NONE, &benchmark, "Time operations" , NULL }, |
10 | { "dump-variant" , 'd', 0, G_OPTION_ARG_NONE, &dump_variant, "Dump GVariant structure" , NULL }, |
11 | { "fallback" , '\0', 0, G_OPTION_ARG_NONE, &fallback, "Draw node without a renderer" , NULL }, |
12 | { "runs" , 'r', 0, G_OPTION_ARG_INT, &runs, "Render the test N times" , "N" }, |
13 | { NULL } |
14 | }; |
15 | |
16 | static void |
17 | deserialize_error_func (const GskParseLocation *start, |
18 | const GskParseLocation *end, |
19 | const GError *error, |
20 | gpointer user_data) |
21 | { |
22 | GString *string = g_string_new (init: "<data>" ); |
23 | |
24 | g_string_append_printf (string, format: ":%zu:%zu" , |
25 | start->lines + 1, start->line_chars + 1); |
26 | if (start->lines != end->lines || start->line_chars != end->line_chars) |
27 | { |
28 | g_string_append (string, val: "-" ); |
29 | if (start->lines != end->lines) |
30 | g_string_append_printf (string, format: "%zu:" , end->lines + 1); |
31 | g_string_append_printf (string, format: "%zu" , end->line_chars + 1); |
32 | } |
33 | |
34 | g_warning ("Error at %s: %s" , string->str, error->message); |
35 | |
36 | g_string_free (string, TRUE); |
37 | } |
38 | |
39 | |
40 | int |
41 | main(int argc, char **argv) |
42 | { |
43 | cairo_surface_t *surface; |
44 | GskRenderNode *node; |
45 | GError *error = NULL; |
46 | GBytes *bytes; |
47 | gint64 start, end; |
48 | char *contents; |
49 | gsize len; |
50 | int run; |
51 | GOptionContext *context; |
52 | GdkTexture *texture; |
53 | |
54 | context = g_option_context_new (parameter_string: "NODE-FILE PNG-FILE" ); |
55 | g_option_context_add_main_entries (context, entries: options, NULL); |
56 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
57 | { |
58 | g_printerr (format: "Option parsing failed: %s\n" , error->message); |
59 | return 1; |
60 | } |
61 | |
62 | gtk_init (); |
63 | |
64 | if (runs < 1) |
65 | { |
66 | g_printerr (format: "Number of runs given with -r/--runs must be at least 1 and not %d.\n" , runs); |
67 | return 1; |
68 | } |
69 | if (!(argc == 3 || (argc == 2 && (dump_variant || benchmark)))) |
70 | { |
71 | g_printerr (format: "Usage: %s [OPTIONS] NODE-FILE PNG-FILE\n" , argv[0]); |
72 | return 1; |
73 | } |
74 | |
75 | if (!g_file_get_contents (filename: argv[1], contents: &contents, length: &len, error: &error)) |
76 | { |
77 | g_printerr (format: "Could not open node file: %s\n" , error->message); |
78 | return 1; |
79 | } |
80 | |
81 | bytes = g_bytes_new_take (data: contents, size: len); |
82 | if (dump_variant) |
83 | { |
84 | GVariant *variant = g_variant_new_from_bytes (G_VARIANT_TYPE ("(suuv)" ), bytes, FALSE); |
85 | char *s; |
86 | |
87 | s = g_variant_print (value: variant, FALSE); |
88 | g_print (format: "%s\n" , s); |
89 | g_free (mem: s); |
90 | g_variant_unref (value: variant); |
91 | } |
92 | |
93 | start = g_get_monotonic_time (); |
94 | node = gsk_render_node_deserialize (bytes, error_func: deserialize_error_func, NULL); |
95 | end = g_get_monotonic_time (); |
96 | if (benchmark) |
97 | { |
98 | char *bytes_string = g_format_size (size: g_bytes_get_size (bytes)); |
99 | g_print (format: "Loaded %s in %.4gs\n" , bytes_string, (double) (end - start) / G_USEC_PER_SEC); |
100 | g_free (mem: bytes_string); |
101 | } |
102 | g_bytes_unref (bytes); |
103 | |
104 | if (node == NULL) |
105 | { |
106 | return 1; |
107 | } |
108 | |
109 | if (fallback) |
110 | { |
111 | graphene_rect_t bounds; |
112 | cairo_t *cr; |
113 | int width, height, stride; |
114 | guchar *pixels; |
115 | |
116 | gsk_render_node_get_bounds (node, bounds: &bounds); |
117 | width = ceil (x: bounds.size.width); |
118 | height = ceil (x: bounds.size.height); |
119 | stride = width * 4; |
120 | pixels = g_malloc0_n (n_blocks: stride, n_block_bytes: height); |
121 | |
122 | surface = cairo_image_surface_create_for_data (data: pixels, format: CAIRO_FORMAT_ARGB32, width, height, stride); |
123 | cr = cairo_create (target: surface); |
124 | |
125 | cairo_translate (cr, tx: - bounds.origin.x, ty: - bounds.origin.y); |
126 | for (run = 0; run < runs; run++) |
127 | { |
128 | if (run > 0) |
129 | { |
130 | cairo_save (cr); |
131 | cairo_set_operator (cr, op: CAIRO_OPERATOR_CLEAR); |
132 | cairo_paint (cr); |
133 | cairo_restore (cr); |
134 | } |
135 | start = g_get_monotonic_time (); |
136 | gsk_render_node_draw (node, cr); |
137 | end = g_get_monotonic_time (); |
138 | if (benchmark) |
139 | g_print (format: "Run %d: Rendered fallback in %.4gs\n" , run, (double) (end - start) / G_USEC_PER_SEC); |
140 | } |
141 | |
142 | cairo_destroy (cr); |
143 | cairo_surface_destroy (surface); |
144 | |
145 | bytes = g_bytes_new_take (data: pixels, size: stride * height); |
146 | texture = gdk_memory_texture_new (width, height, |
147 | GDK_MEMORY_DEFAULT, |
148 | bytes, |
149 | stride); |
150 | g_bytes_unref (bytes); |
151 | } |
152 | else |
153 | { |
154 | GskRenderer *renderer; |
155 | GdkSurface *window; |
156 | |
157 | window = gdk_surface_new_toplevel (display: gdk_display_get_default()); |
158 | renderer = gsk_renderer_new_for_surface (surface: window); |
159 | texture = NULL; /* poor gcc can't see that runs > 0 */ |
160 | |
161 | for (run = 0; run < runs; run++) |
162 | { |
163 | if (run > 0) |
164 | g_object_unref (object: texture); |
165 | start = g_get_monotonic_time (); |
166 | texture = gsk_renderer_render_texture (renderer, root: node, NULL); |
167 | end = g_get_monotonic_time (); |
168 | if (benchmark) |
169 | g_print (format: "Run %u: Rendered using %s in %.4gs\n" , run, G_OBJECT_TYPE_NAME (renderer), (double) (end - start) / G_USEC_PER_SEC); |
170 | } |
171 | |
172 | gsk_renderer_unrealize (renderer); |
173 | g_object_unref (object: window); |
174 | g_object_unref (object: renderer); |
175 | } |
176 | |
177 | gsk_render_node_unref (node); |
178 | |
179 | if (argc > 2) |
180 | { |
181 | if (!gdk_texture_save_to_png (texture, filename: argv[2])) |
182 | { |
183 | g_object_unref (object: texture); |
184 | g_print (format: "Failed to save PNG file\n" ); |
185 | return 1; |
186 | } |
187 | } |
188 | |
189 | g_object_unref (object: texture); |
190 | |
191 | return 0; |
192 | } |
193 | |