]> Git Repo - linux.git/blob - drivers/gpu/drm/selftests/test-drm_framebuffer.c
Merge branch 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / selftests / test-drm_framebuffer.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Test cases for the drm_framebuffer functions
4  */
5
6 #include <drm/drmP.h>
7 #include "../drm_crtc_internal.h"
8
9 #include "test-drm_modeset_common.h"
10
11 #define MIN_WIDTH 4
12 #define MAX_WIDTH 4096
13 #define MIN_HEIGHT 4
14 #define MAX_HEIGHT 4096
15
16 struct drm_framebuffer_test {
17         int buffer_created;
18         struct drm_mode_fb_cmd2 cmd;
19         const char *name;
20 };
21
22 static struct drm_framebuffer_test createbuffer_tests[] = {
23 { .buffer_created = 1, .name = "ABGR8888 normal sizes",
24         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888,
25                  .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 },
26         }
27 },
28 { .buffer_created = 1, .name = "ABGR8888 max sizes",
29         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
30                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
31         }
32 },
33 { .buffer_created = 1, .name = "ABGR8888 pitch greater than min required",
34         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
35                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 },
36         }
37 },
38 { .buffer_created = 0, .name = "ABGR8888 pitch less than min required",
39         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
40                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 },
41         }
42 },
43 { .buffer_created = 0, .name = "ABGR8888 Invalid width",
44         .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
45                  .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 },
46         }
47 },
48 { .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle",
49         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
50                  .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
51         }
52 },
53 { .buffer_created = 0, .name = "No pixel format",
54         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0,
55                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
56         }
57 },
58 { .buffer_created = 0, .name = "ABGR8888 Width 0",
59         .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
60                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
61         }
62 },
63 { .buffer_created = 0, .name = "ABGR8888 Height 0",
64         .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888,
65                  .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
66         }
67 },
68 { .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination",
69         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
70                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
71         }
72 },
73 { .buffer_created = 1, .name = "ABGR8888 Large buffer offset",
74         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
75                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
76         }
77 },
78 { .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers",
79         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
80                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
81                  .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
82         }
83 },
84 { .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier",
85         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
86                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
87                  .flags = DRM_MODE_FB_MODIFIERS, .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 },
88         }
89 },
90 { .buffer_created = 0, .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)",
91         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
92                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
93                  .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
94                  .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
95         }
96 },
97 { .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS",
98         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
99                  .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
100                  .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
101         }
102 },
103 { .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS",
104         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
105                  .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
106                  .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
107         }
108 },
109 { .buffer_created = 1, .name = "NV12 Normal sizes",
110         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
111                  .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 },
112         }
113 },
114 { .buffer_created = 1, .name = "NV12 Max sizes",
115         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
116                  .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
117         }
118 },
119 { .buffer_created = 0, .name = "NV12 Invalid pitch",
120         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
121                  .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 },
122         }
123 },
124 { .buffer_created = 0, .name = "NV12 Invalid modifier/misssing DRM_MODE_FB_MODIFIERS flag",
125         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
126                  .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
127                  .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
128         }
129 },
130 { .buffer_created = 0, .name = "NV12 different  modifier per-plane",
131         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
132                  .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
133                  .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
134                  .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
135         }
136 },
137 { .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE",
138         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
139                  .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
140                  .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
141                  .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
142         }
143 },
144 { .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS",
145         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
146                  .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
147                                                        DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
148                  .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
149         }
150 },
151 { .buffer_created = 0, .name = "NV12 Modifier for inexistent plane",
152         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
153                  .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
154                  .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
155                                DRM_FORMAT_MOD_SAMSUNG_64_32_TILE },
156                  .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
157         }
158 },
159 { .buffer_created = 0, .name = "NV12 Handle for inexistent plane",
160         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
161                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
162         }
163 },
164 { .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS",
165         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
166                  .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 },
167         }
168 },
169 { .buffer_created = 1, .name = "YVU420 Normal sizes",
170         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
171                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
172                  .pitches = { 600, 300, 300 },
173         }
174 },
175 { .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier",
176         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
177                  .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 },
178         }
179 },
180 { .buffer_created = 1, .name = "YVU420 Max sizes",
181         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
182                  .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2),
183                                                       DIV_ROUND_UP(MAX_WIDTH, 2) },
184         }
185 },
186 { .buffer_created = 0, .name = "YVU420 Invalid pitch",
187         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
188                  .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1,
189                                                       DIV_ROUND_UP(MAX_WIDTH, 2) },
190         }
191 },
192 { .buffer_created = 1, .name = "YVU420 Different pitches",
193         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
194                  .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,
195                                                       DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
196         }
197 },
198 { .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches",
199         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
200                  .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH  + MAX_WIDTH * MAX_HEIGHT,
201                                                       MAX_WIDTH  + 2 * MAX_WIDTH * MAX_HEIGHT },
202                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
203         }
204 },
205 { .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS",
206         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
207                  .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
208                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
209         }
210 },
211 { .buffer_created = 0, .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS",
212         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
213                  .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
214                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
215         }
216 },
217 { .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS",
218         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
219                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
220                  .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
221                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
222         }
223 },
224 { .buffer_created = 1, .name = "YVU420 Valid modifier",
225         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
226                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
227                  .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE },
228                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
229         }
230 },
231 { .buffer_created = 0, .name = "YVU420 Different modifiers per plane",
232         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
233                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
234                  .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR,
235                                AFBC_FORMAT_MOD_SPARSE },
236                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
237         }
238 },
239 { .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane",
240         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
241                  .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
242                  .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,
243                                AFBC_FORMAT_MOD_SPARSE },
244                  .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
245         }
246 },
247 { .buffer_created = 1, .name = "X0L2 Normal sizes",
248         .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2,
249                  .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 }
250         }
251 },
252 { .buffer_created = 1, .name = "X0L2 Max sizes",
253         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
254                  .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 }
255         }
256 },
257 { .buffer_created = 0, .name = "X0L2 Invalid pitch",
258         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
259                  .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 }
260         }
261 },
262 { .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required",
263         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
264                  .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
265         }
266 },
267 { .buffer_created = 0, .name = "X0L2 Handle for inexistent plane",
268         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
269                  .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
270                  .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
271         }
272 },
273 { .buffer_created = 1, .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set",
274         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
275                  .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 },
276                  .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
277         }
278 },
279 { .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set",
280         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
281                  .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
282                  .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
283         }
284 },
285 { .buffer_created = 1, .name = "X0L2 Valid modifier",
286         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
287                  .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
288                  .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
289         }
290 },
291 { .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane",
292         .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT,
293                  .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 },
294                  .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
295                  .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
296                  .flags = DRM_MODE_FB_MODIFIERS,
297         }
298 },
299 };
300
301 static struct drm_framebuffer *fb_create_mock(struct drm_device *dev,
302                                               struct drm_file *file_priv,
303                                               const struct drm_mode_fb_cmd2 *mode_cmd)
304 {
305         int *buffer_created = dev->dev_private;
306         *buffer_created = 1;
307         return ERR_PTR(-EINVAL);
308 }
309
310 static struct drm_mode_config_funcs mock_config_funcs = {
311         .fb_create = fb_create_mock,
312 };
313
314 static struct drm_device mock_drm_device = {
315         .mode_config = {
316                 .min_width = MIN_WIDTH,
317                 .max_width = MAX_WIDTH,
318                 .min_height = MIN_HEIGHT,
319                 .max_height = MAX_HEIGHT,
320                 .allow_fb_modifiers = true,
321                 .funcs = &mock_config_funcs,
322         },
323 };
324
325 static int execute_drm_mode_fb_cmd2(struct drm_mode_fb_cmd2 *r)
326 {
327         int buffer_created = 0;
328         struct drm_framebuffer *fb;
329
330         mock_drm_device.dev_private = &buffer_created;
331         fb = drm_internal_framebuffer_create(&mock_drm_device, r, NULL);
332         return buffer_created;
333 }
334
335 int igt_check_drm_framebuffer_create(void *ignored)
336 {
337         int i = 0;
338
339         for (i = 0; i < ARRAY_SIZE(createbuffer_tests); i++) {
340                 FAIL(createbuffer_tests[i].buffer_created !=
341                                 execute_drm_mode_fb_cmd2(&createbuffer_tests[i].cmd),
342                      "Test %d: \"%s\" failed\n", i, createbuffer_tests[i].name);
343         }
344
345         return 0;
346 }
This page took 0.055548 seconds and 4 git commands to generate.