From 7261edfe363482e140162cc18534a8e46f01915c Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 29 Jan 2019 16:22:45 +0200 Subject: [PATCH] clients: Add a mandelbrot set shader to simple-dmabuf-egl Support drawing a mandelbrot set in the fragment shader, rendering it with separate draw calls, one for each cell in a virtual 4x4 grid. This more complex and heavy drawing will potentially help us to visually discover any present or future explicit synchronization issues. The mandelbrot set rendering is enabled with the -m/--mandelbrot command-line switch. Signed-off-by: Alexandros Frantzis --- clients/simple-dmabuf-egl.c | 126 +++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 8 deletions(-) diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c index 3d0cf7a10..e81f1629c 100644 --- a/clients/simple-dmabuf-egl.c +++ b/clients/simple-dmabuf-egl.c @@ -66,6 +66,7 @@ /* Possible options that affect the displayed image */ #define OPT_IMMEDIATE (1 << 0) /* create wl_buffer immediately */ #define OPT_IMPLICIT_SYNC (1 << 1) /* force implicit sync */ +#define OPT_MANDELBROT (1 << 2) /* render mandelbrot */ #define BUFFER_FORMAT DRM_FORMAT_XRGB8888 #define MAX_BUFFER_PLANES 4 @@ -147,6 +148,7 @@ struct window { GLuint color; GLuint offset_uniform; } gl; + bool render_mandelbrot; }; static sig_atomic_t running = 1; @@ -485,6 +487,40 @@ static const char *frag_shader_text = " gl_FragColor = v_color;\n" "}\n"; +static const char *vert_shader_mandelbrot_text = + "uniform float offset;\n" + "attribute vec4 pos;\n" + "varying vec2 v_pos;\n" + "void main() {\n" + " v_pos = pos.xy;\n" + " gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n" + "}\n"; + + +/* Mandelbrot set shader using the escape time algorithm. */ +static const char *frag_shader_mandelbrot_text = + "precision mediump float;\n" + "varying vec2 v_pos;\n" + "void main() {\n" + " const int max_iteration = 500;\n" + " // Scale and translate position to get a nice mandelbrot drawing for\n" + " // the used v_pos x and y range (-0.5 to 0.5).\n" + " float x0 = 3.0 * v_pos.x - 0.5;\n" + " float y0 = 3.0 * v_pos.y;\n" + " float x = 0.0;\n" + " float y = 0.0;\n" + " int iteration = 0;\n" + " while (x * x + y * y <= 4.0 && iteration < max_iteration) {\n" + " float xtemp = x * x - y * y + x0;\n" + " y = 2.0 * x * y + y0;\n" + " x = xtemp;\n" + " ++iteration;\n" + " }\n" + " float red = iteration == max_iteration ?\n" + " 0.0 : 1.0 - fract(float(iteration) / 20.0);\n" + " gl_FragColor = vec4(red, 0.0, 0.0, 1.0);\n" + "}\n"; + static GLuint create_shader(const char *source, GLenum shader_type) { @@ -536,8 +572,14 @@ create_and_link_program(GLuint vert, GLuint frag) static bool window_set_up_gl(struct window *window) { - GLuint vert = create_shader(vert_shader_text, GL_VERTEX_SHADER); - GLuint frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER); + GLuint vert = create_shader( + window->render_mandelbrot ? vert_shader_mandelbrot_text : + vert_shader_text, + GL_VERTEX_SHADER); + GLuint frag = create_shader( + window->render_mandelbrot ? frag_shader_mandelbrot_text : + frag_shader_text, + GL_FRAGMENT_SHADER); window->gl.program = create_and_link_program(vert, frag); @@ -582,7 +624,7 @@ destroy_window(struct window *window) } static struct window * -create_window(struct display *display, int width, int height) +create_window(struct display *display, int width, int height, int opts) { struct window *window; int i; @@ -651,6 +693,8 @@ create_window(struct display *display, int width, int height) goto error; } + window->render_mandelbrot = opts & OPT_MANDELBROT; + if (!window_set_up_gl(window)) goto error; @@ -755,6 +799,63 @@ render(struct window *window, struct buffer *buffer) glDisableVertexAttribArray(window->gl.color); } +static void +render_mandelbrot(struct window *window, struct buffer *buffer) +{ + /* Complete a movement iteration in 5000 ms. */ + static const uint64_t iteration_ms = 5000; + /* Split drawing in a square grid consisting of grid_side * grid_side + * cells. */ + static const int grid_side = 4; + GLfloat norm_cell_side = 1.0 / grid_side; + int num_cells = grid_side * grid_side; + GLfloat offset; + struct timeval tv; + uint64_t time_ms; + int i; + + gettimeofday(&tv, NULL); + time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + /* Split time_ms in repeating windows of [0, iteration_ms) and map them + * to offsets in the [-0.5, 0.5) range. */ + offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5; + + /* Direct all GL draws to the buffer through the FBO */ + glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo); + + glViewport(0, 0, window->width, window->height); + + glUniform1f(window->gl.offset_uniform, offset); + + glClearColor(0.6, 0.6, 0.6, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + for (i = 0; i < num_cells; ++i) { + /* Calculate the vertex coordinates of the current grid cell. */ + int row = i / grid_side; + int col = i % grid_side; + GLfloat left = -0.5 + norm_cell_side * col; + GLfloat right = left + norm_cell_side; + GLfloat top = 0.5 - norm_cell_side * row; + GLfloat bottom = top - norm_cell_side; + GLfloat verts[4][2] = { + { left, bottom }, + { left, top }, + { right, bottom }, + { right, top } + }; + + /* ... and draw it. */ + glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts); + glEnableVertexAttribArray(window->gl.pos); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(window->gl.pos); + } +} + static void buffer_fenced_release(void *data, struct zwp_linux_buffer_release_v1 *release, @@ -836,7 +937,10 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) if (buffer->release_fence_fd >= 0) wait_for_buffer_release_fence(buffer); - render(window, buffer); + if (window->render_mandelbrot) + render_mandelbrot(window, buffer); + else + render(window, buffer); if (window->display->use_explicit_sync) { int fence_fd = create_egl_fence_fd(window); @@ -1295,7 +1399,9 @@ print_usage_and_exit(void) "\n\t\tthe window size in pixels (default: 256)\n" "\t'-e,--explicit-sync=<>'" "\n\t\t0 to disable explicit sync, " - "\n\t\t1 to enable explicit sync (default: 1)\n"); + "\n\t\t1 to enable explicit sync (default: 1)\n" + "\t'-m,--mandelbrot'" + "\n\t\trender a mandelbrot set with multiple draw calls\n"); exit(0); } @@ -1328,12 +1434,13 @@ main(int argc, char **argv) {"drm-render-node", required_argument, 0, 'd' }, {"size", required_argument, 0, 's' }, {"explicit-sync", required_argument, 0, 'e' }, + {"mandelbrot", no_argument, 0, 'm' }, {"help", no_argument , 0, 'h' }, {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "hi:d:s:e:", - long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "hi:d:s:e:m", + long_options, &option_index)) != -1) { switch (c) { case 'i': if (is_true(optarg)) @@ -1349,6 +1456,9 @@ main(int argc, char **argv) if (!is_true(optarg)) opts |= OPT_IMPLICIT_SYNC; break; + case 'm': + opts |= OPT_MANDELBROT; + break; default: print_usage_and_exit(); } @@ -1357,7 +1467,7 @@ main(int argc, char **argv) display = create_display(drm_render_node, opts); if (!display) return 1; - window = create_window(display, window_size, window_size); + window = create_window(display, window_size, window_size, opts); if (!window) return 1;