blob: 1c963e011758e07094c3c80f63f77f61196fb560 [file] [log] [blame]
/*
* 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 void 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);
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_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_plane_set_fb(gpu->primary, &gpu->fb);
igt_display_commit(display);
}
/*
* 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_pipe_with_valid_output(display, pipe, output) {
gpu->output = output;
gpu->pipe = pipe;
prepare_crtc(gpu);
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(gpu.drm_fd);
igt_display_require(&gpu.display, gpu.drm_fd);
}
igt_subtest("buffer-sharing")
run_test(&gpu);
igt_fixture {
igt_display_fini(&gpu.display);
close(gpu.drm_fd);
}
}