blob: 15e310fc87f4a8283078de6a8886d0d3fa52cdab [file] [log] [blame]
Tiago Vignatti122f34c2016-05-06 16:51:29 -03001/*
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
35IGT_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 */
50typedef struct {
51 int prime_fd;
52 size_t size;
53 int width;
54 int height;
55} render_process_t;
56
57typedef struct {
58 int x;
59 int y;
60 int w;
61 int h;
62} rect_t;
63
64/* set ptr in a linear view */
65static 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
78static 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
116static 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 */
133typedef 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
142static 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
155static 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 */
193static 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 Vladef8fc992016-05-09 14:43:20 +0300209 gpu->fb.gem_handle);
Tiago Vignatti122f34c2016-05-06 16:51:29 -0300210 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
231static int
232check_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
250igt_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}