Tiago Vignatti | 122f34c | 2016-05-06 16:51:29 -0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2016 Intel Corporation |
| 3 | * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 5 | * copy of this software and associated documentation files (the "Software"), |
| 6 | * to deal in the Software without restriction, including without limitation |
| 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 8 | * and/or sell copies of the Software, and to permit persons to whom the |
| 9 | * Software is furnished to do so, subject to the following conditions: |
| 10 | * |
| 11 | * The above copyright notice and this permission notice (including the next |
| 12 | * paragraph) shall be included in all copies or substantial portions of the |
| 13 | * Software. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 | * IN THE SOFTWARE. |
| 22 | * |
| 23 | * Authors: |
| 24 | * Tiago Vignatti <tiago.vignatti at intel.com> |
| 25 | */ |
| 26 | |
| 27 | /* |
| 28 | * Testcase: show case dma-buf new API and processes restrictions. Most likely |
| 29 | * you want to run like ./prime_mmap_kms --interactive-debug=paint, to see the |
| 30 | * actual rectangle painted on the screen. |
| 31 | */ |
| 32 | |
| 33 | #include "igt.h" |
| 34 | |
| 35 | IGT_TEST_DESCRIPTION( |
| 36 | "Efficiently sharing CPU and GPU buffers"); |
| 37 | |
| 38 | /* |
| 39 | * render_process_t: |
| 40 | * |
| 41 | * Render is basically a user-space regular client. It's the unprivileged |
| 42 | * process with limited system accesses. |
| 43 | * |
| 44 | * Worth note the vendor-independent characteristic, meaning that the |
| 45 | * client doesn't need to perform any vendor specific calls for buffer |
| 46 | * handling. Mesa GBM library is a counter-example because, even though its API |
| 47 | * is vendor-independent, under-the-hood the library actually calls vendor |
| 48 | * specific ioctls, which is not really sandboxable and not the goal here. |
| 49 | */ |
| 50 | typedef struct { |
| 51 | int prime_fd; |
| 52 | size_t size; |
| 53 | int width; |
| 54 | int height; |
| 55 | } render_process_t; |
| 56 | |
| 57 | typedef struct { |
| 58 | int x; |
| 59 | int y; |
| 60 | int w; |
| 61 | int h; |
| 62 | } rect_t; |
| 63 | |
| 64 | /* set ptr in a linear view */ |
| 65 | static void set_pixel(void *_ptr, int index, uint32_t color, int bpp) |
| 66 | { |
| 67 | if (bpp == 16) { |
| 68 | uint16_t *ptr = _ptr; |
| 69 | ptr[index] = color; |
| 70 | } else if (bpp == 32) { |
| 71 | uint32_t *ptr = _ptr; |
| 72 | ptr[index] = color; |
| 73 | } else { |
| 74 | igt_assert_f(false, "bpp: %d\n", bpp); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | static void paint(render_process_t *render) |
| 79 | { |
| 80 | void *frame; |
| 81 | rect_t rect = { |
| 82 | .x = 200, |
| 83 | .y = 200, |
| 84 | .w = render->width / 4, |
| 85 | .h = render->height / 4, |
| 86 | }; |
| 87 | uint32_t color = 0xFF; |
| 88 | int stride, bpp; |
| 89 | int x, y, line_begin; |
| 90 | |
| 91 | frame = mmap(NULL, render->size, PROT_READ | PROT_WRITE, MAP_SHARED, |
| 92 | render->prime_fd, 0); |
| 93 | igt_assert(frame != MAP_FAILED); |
| 94 | |
| 95 | /* TODO: what's the mmap'ed buffer semantics on tiling, format etc. How |
| 96 | * does the client know whether that the BO was created X-tiled, |
| 97 | * Y-tiled and how it will map back? This is something we need to |
| 98 | * address in this API still. */ |
| 99 | stride = render->width * 4; |
| 100 | bpp = 32; |
| 101 | |
| 102 | /* ioctls to keep up the GPU <-> CPU coherency */ |
| 103 | prime_sync_start(render->prime_fd, true); |
| 104 | |
| 105 | /* the actual painting phase happens here */ |
| 106 | for (y = rect.y; y < rect.y + rect.h; y++) { |
| 107 | line_begin = y * stride / (bpp / 8); |
| 108 | for (x = rect.x; x < rect.x + rect.w; x++) |
| 109 | set_pixel(frame, line_begin + x, color, bpp); |
| 110 | } |
| 111 | |
| 112 | prime_sync_end(render->prime_fd, true); |
| 113 | munmap(frame, render->size); |
| 114 | } |
| 115 | |
| 116 | static void init_renderer(int prime_fd, int fb_size, int width, int height) |
| 117 | { |
| 118 | render_process_t render; |
| 119 | |
| 120 | render.prime_fd = prime_fd; |
| 121 | render.size = fb_size; |
| 122 | render.width = width; |
| 123 | render.height = height; |
| 124 | paint(&render); |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * gpu_process_t: |
| 129 | * |
| 130 | * GPU process is the privileged process and has access to the system graphics |
| 131 | * routines, like DRM, display management and driver accesses. |
| 132 | */ |
| 133 | typedef struct { |
| 134 | int drm_fd; |
| 135 | igt_display_t display; |
| 136 | struct igt_fb fb; |
| 137 | igt_output_t *output; |
| 138 | igt_plane_t *primary; |
| 139 | enum pipe pipe; |
| 140 | } gpu_process_t; |
| 141 | |
| 142 | static void cleanup_crtc(gpu_process_t *gpu) |
| 143 | { |
| 144 | igt_display_t *display = &gpu->display; |
| 145 | igt_output_t *output = gpu->output; |
| 146 | |
| 147 | igt_plane_set_fb(gpu->primary, NULL); |
| 148 | |
| 149 | igt_output_set_pipe(output, PIPE_ANY); |
| 150 | igt_display_commit(display); |
| 151 | |
| 152 | igt_remove_fb(gpu->drm_fd, &gpu->fb); |
| 153 | } |
| 154 | |
| 155 | static bool prepare_crtc(gpu_process_t *gpu) |
| 156 | { |
| 157 | igt_display_t *display = &gpu->display; |
| 158 | igt_output_t *output = gpu->output; |
| 159 | drmModeModeInfo *mode; |
| 160 | |
| 161 | /* select the pipe we want to use */ |
| 162 | igt_output_set_pipe(output, gpu->pipe); |
| 163 | igt_display_commit(display); |
| 164 | |
| 165 | if (!output->valid) { |
| 166 | igt_output_set_pipe(output, PIPE_ANY); |
| 167 | igt_display_commit(display); |
| 168 | return false; |
| 169 | } |
| 170 | |
| 171 | mode = igt_output_get_mode(output); |
| 172 | |
| 173 | /* create a white fb and flip to it */ |
| 174 | igt_create_color_fb(gpu->drm_fd, mode->hdisplay, mode->vdisplay, |
| 175 | DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, |
| 176 | 1.0, 1.0, 1.0, &gpu->fb); |
| 177 | |
| 178 | gpu->primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY); |
| 179 | |
| 180 | igt_plane_set_fb(gpu->primary, &gpu->fb); |
| 181 | igt_display_commit(display); |
| 182 | |
| 183 | return true; |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | * The idea is to create a BO (in this case the framebuffer's) in one process, |
| 188 | * export and pass its prime fd to another process, which in turn uses the fd |
| 189 | * to map and write. This is Chrome-like architectures, where the Web content |
| 190 | * (a "tab" or the "unprivileged process") maps and CPU-paints a buffer, which |
| 191 | * was previously allocated in the GPU process ("privileged process"). |
| 192 | */ |
| 193 | static void run_test(gpu_process_t *gpu) |
| 194 | { |
| 195 | igt_display_t *display = &gpu->display; |
| 196 | igt_output_t *output; |
| 197 | enum pipe pipe; |
| 198 | int prime_fd; |
| 199 | |
| 200 | for_each_connected_output(display, output) { |
| 201 | gpu->output = output; |
| 202 | for_each_pipe(display, pipe) { |
| 203 | gpu->pipe = pipe; |
| 204 | |
| 205 | if (!prepare_crtc(gpu)) |
| 206 | continue; |
| 207 | |
| 208 | prime_fd = prime_handle_to_fd_for_mmap(gpu->drm_fd, |
Marius Vlad | ef8fc99 | 2016-05-09 14:43:20 +0300 | [diff] [blame] | 209 | gpu->fb.gem_handle); |
Tiago Vignatti | 122f34c | 2016-05-06 16:51:29 -0300 | [diff] [blame] | 210 | igt_skip_on(prime_fd == -1 && errno == EINVAL); |
| 211 | |
| 212 | /* Note that it only shares the dma-buf fd and some |
| 213 | * other basic info */ |
| 214 | igt_fork(renderer_no, 1) { |
| 215 | init_renderer(prime_fd, gpu->fb.size, gpu->fb.width, |
| 216 | gpu->fb.height); |
| 217 | } |
| 218 | igt_waitchildren(); |
| 219 | |
| 220 | igt_debug_wait_for_keypress("paint"); |
| 221 | cleanup_crtc(gpu); |
| 222 | |
| 223 | /* once is enough */ |
| 224 | return; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | igt_skip("no valid crtc/connector combinations found\n"); |
| 229 | } |
| 230 | |
| 231 | static int |
| 232 | check_for_dma_buf_mmap(int fd) |
| 233 | { |
| 234 | int dma_buf_fd; |
| 235 | char *ptr; |
| 236 | uint32_t handle; |
| 237 | int ret = 1; |
| 238 | |
| 239 | handle = gem_create(fd, 4096); |
| 240 | dma_buf_fd = prime_handle_to_fd(fd, handle); |
| 241 | ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dma_buf_fd, 0); |
| 242 | if (ptr != MAP_FAILED) |
| 243 | ret = 0; |
| 244 | munmap(ptr, 4096); |
| 245 | gem_close(fd, handle); |
| 246 | close(dma_buf_fd); |
| 247 | return ret; |
| 248 | } |
| 249 | |
| 250 | igt_main |
| 251 | { |
| 252 | gpu_process_t gpu; |
| 253 | |
| 254 | igt_skip_on_simulation(); |
| 255 | |
| 256 | igt_fixture { |
| 257 | gpu.drm_fd = drm_open_driver_master(DRIVER_INTEL); |
| 258 | igt_skip_on((check_for_dma_buf_mmap(gpu.drm_fd) != 0)); |
| 259 | kmstest_set_vt_graphics_mode(); |
| 260 | |
| 261 | igt_require_pipe_crc(); |
| 262 | |
| 263 | igt_display_init(&gpu.display, gpu.drm_fd); |
| 264 | } |
| 265 | |
| 266 | igt_subtest("buffer-sharing") |
| 267 | run_test(&gpu); |
| 268 | |
| 269 | igt_fixture { |
| 270 | igt_display_fini(&gpu.display); |
| 271 | close(gpu.drm_fd); |
| 272 | } |
| 273 | |
| 274 | igt_exit(); |
| 275 | } |