diff options
Diffstat (limited to 'tests/prime_mmap_kms.c')
-rw-r--r-- | tests/prime_mmap_kms.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/tests/prime_mmap_kms.c b/tests/prime_mmap_kms.c new file mode 100644 index 00000000..8e14b250 --- /dev/null +++ b/tests/prime_mmap_kms.c @@ -0,0 +1,275 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Tiago Vignatti <tiago.vignatti at intel.com> + */ + +/* + * Testcase: show case dma-buf new API and processes restrictions. Most likely + * you want to run like ./prime_mmap_kms --interactive-debug=paint, to see the + * actual rectangle painted on the screen. + */ + +#include "igt.h" + +IGT_TEST_DESCRIPTION( + "Efficiently sharing CPU and GPU buffers"); + +/* + * render_process_t: + * + * Render is basically a user-space regular client. It's the unprivileged + * process with limited system accesses. + * + * Worth note the vendor-independent characteristic, meaning that the + * client doesn't need to perform any vendor specific calls for buffer + * handling. Mesa GBM library is a counter-example because, even though its API + * is vendor-independent, under-the-hood the library actually calls vendor + * specific ioctls, which is not really sandboxable and not the goal here. + */ +typedef struct { + int prime_fd; + size_t size; + int width; + int height; +} render_process_t; + +typedef struct { + int x; + int y; + int w; + int h; +} rect_t; + +/* set ptr in a linear view */ +static void set_pixel(void *_ptr, int index, uint32_t color, int bpp) +{ + if (bpp == 16) { + uint16_t *ptr = _ptr; + ptr[index] = color; + } else if (bpp == 32) { + uint32_t *ptr = _ptr; + ptr[index] = color; + } else { + igt_assert_f(false, "bpp: %d\n", bpp); + } +} + +static void paint(render_process_t *render) +{ + void *frame; + rect_t rect = { + .x = 200, + .y = 200, + .w = render->width / 4, + .h = render->height / 4, + }; + uint32_t color = 0xFF; + int stride, bpp; + int x, y, line_begin; + + frame = mmap(NULL, render->size, PROT_READ | PROT_WRITE, MAP_SHARED, + render->prime_fd, 0); + igt_assert(frame != MAP_FAILED); + + /* TODO: what's the mmap'ed buffer semantics on tiling, format etc. How + * does the client know whether that the BO was created X-tiled, + * Y-tiled and how it will map back? This is something we need to + * address in this API still. */ + stride = render->width * 4; + bpp = 32; + + /* ioctls to keep up the GPU <-> CPU coherency */ + prime_sync_start(render->prime_fd, true); + + /* the actual painting phase happens here */ + for (y = rect.y; y < rect.y + rect.h; y++) { + line_begin = y * stride / (bpp / 8); + for (x = rect.x; x < rect.x + rect.w; x++) + set_pixel(frame, line_begin + x, color, bpp); + } + + prime_sync_end(render->prime_fd, true); + munmap(frame, render->size); +} + +static void init_renderer(int prime_fd, int fb_size, int width, int height) +{ + render_process_t render; + + render.prime_fd = prime_fd; + render.size = fb_size; + render.width = width; + render.height = height; + paint(&render); +} + +/* + * gpu_process_t: + * + * GPU process is the privileged process and has access to the system graphics + * routines, like DRM, display management and driver accesses. + */ +typedef struct { + int drm_fd; + igt_display_t display; + struct igt_fb fb; + igt_output_t *output; + igt_plane_t *primary; + enum pipe pipe; +} gpu_process_t; + +static void cleanup_crtc(gpu_process_t *gpu) +{ + igt_display_t *display = &gpu->display; + igt_output_t *output = gpu->output; + + igt_plane_set_fb(gpu->primary, NULL); + + igt_output_set_pipe(output, PIPE_ANY); + igt_display_commit(display); + + igt_remove_fb(gpu->drm_fd, &gpu->fb); +} + +static bool prepare_crtc(gpu_process_t *gpu) +{ + igt_display_t *display = &gpu->display; + igt_output_t *output = gpu->output; + drmModeModeInfo *mode; + + /* select the pipe we want to use */ + igt_output_set_pipe(output, gpu->pipe); + igt_display_commit(display); + + if (!output->valid) { + igt_output_set_pipe(output, PIPE_ANY); + igt_display_commit(display); + return false; + } + + mode = igt_output_get_mode(output); + + /* create a white fb and flip to it */ + igt_create_color_fb(gpu->drm_fd, mode->hdisplay, mode->vdisplay, + DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, + 1.0, 1.0, 1.0, &gpu->fb); + + gpu->primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY); + + igt_plane_set_fb(gpu->primary, &gpu->fb); + igt_display_commit(display); + + return true; +} + +/* + * The idea is to create a BO (in this case the framebuffer's) in one process, + * export and pass its prime fd to another process, which in turn uses the fd + * to map and write. This is Chrome-like architectures, where the Web content + * (a "tab" or the "unprivileged process") maps and CPU-paints a buffer, which + * was previously allocated in the GPU process ("privileged process"). + */ +static void run_test(gpu_process_t *gpu) +{ + igt_display_t *display = &gpu->display; + igt_output_t *output; + enum pipe pipe; + int prime_fd; + + for_each_connected_output(display, output) { + gpu->output = output; + for_each_pipe(display, pipe) { + gpu->pipe = pipe; + + if (!prepare_crtc(gpu)) + continue; + + prime_fd = prime_handle_to_fd_for_mmap(gpu->drm_fd, + gpu->fb.gem_handle); + igt_skip_on(prime_fd == -1 && errno == EINVAL); + + /* Note that it only shares the dma-buf fd and some + * other basic info */ + igt_fork(renderer_no, 1) { + init_renderer(prime_fd, gpu->fb.size, gpu->fb.width, + gpu->fb.height); + } + igt_waitchildren(); + + igt_debug_wait_for_keypress("paint"); + cleanup_crtc(gpu); + + /* once is enough */ + return; + } + } + + igt_skip("no valid crtc/connector combinations found\n"); +} + +static int +check_for_dma_buf_mmap(int fd) +{ + int dma_buf_fd; + char *ptr; + uint32_t handle; + int ret = 1; + + handle = gem_create(fd, 4096); + dma_buf_fd = prime_handle_to_fd(fd, handle); + ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dma_buf_fd, 0); + if (ptr != MAP_FAILED) + ret = 0; + munmap(ptr, 4096); + gem_close(fd, handle); + close(dma_buf_fd); + return ret; +} + +igt_main +{ + gpu_process_t gpu; + + igt_skip_on_simulation(); + + igt_fixture { + gpu.drm_fd = drm_open_driver_master(DRIVER_INTEL); + igt_skip_on((check_for_dma_buf_mmap(gpu.drm_fd) != 0)); + kmstest_set_vt_graphics_mode(); + + igt_require_pipe_crc(); + + igt_display_init(&gpu.display, gpu.drm_fd); + } + + igt_subtest("buffer-sharing") + run_test(&gpu); + + igt_fixture { + igt_display_fini(&gpu.display); + close(gpu.drm_fd); + } + + igt_exit(); +} |