| /* |
| * Copyright 2014 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #define _GNU_SOURCE |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/dma-buf.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <xf86drm.h> |
| #include <xf86drmMode.h> |
| |
| #include <gbm.h> |
| |
| #define CHECK(cond) do {\ |
| if (!(cond)) {\ |
| printf("CHECK failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);\ |
| return 0;\ |
| }\ |
| } while(0) |
| |
| #define HANDLE_EINTR(x) \ |
| ({ \ |
| int eintr_wrapper_counter = 0; \ |
| int eintr_wrapper_result; \ |
| do { \ |
| eintr_wrapper_result = (x); \ |
| } while (eintr_wrapper_result == -1 && errno == EINTR && \ |
| eintr_wrapper_counter++ < 100); \ |
| eintr_wrapper_result; \ |
| }) |
| |
| #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A))) |
| |
| #define ENODRM -1 |
| |
| static int fd; |
| static struct gbm_device *gbm; |
| |
| static const uint32_t format_list[] = { |
| GBM_FORMAT_C8, |
| GBM_FORMAT_RGB332, |
| GBM_FORMAT_BGR233, |
| GBM_FORMAT_XRGB4444, |
| GBM_FORMAT_XBGR4444, |
| GBM_FORMAT_RGBX4444, |
| GBM_FORMAT_BGRX4444, |
| GBM_FORMAT_ARGB4444, |
| GBM_FORMAT_ABGR4444, |
| GBM_FORMAT_RGBA4444, |
| GBM_FORMAT_BGRA4444, |
| GBM_FORMAT_XRGB1555, |
| GBM_FORMAT_XBGR1555, |
| GBM_FORMAT_RGBX5551, |
| GBM_FORMAT_BGRX5551, |
| GBM_FORMAT_ARGB1555, |
| GBM_FORMAT_ABGR1555, |
| GBM_FORMAT_RGBA5551, |
| GBM_FORMAT_BGRA5551, |
| GBM_FORMAT_RGB565, |
| GBM_FORMAT_BGR565, |
| GBM_FORMAT_RGB888, |
| GBM_FORMAT_BGR888, |
| GBM_FORMAT_XRGB8888, |
| GBM_FORMAT_XBGR8888, |
| GBM_FORMAT_RGBX8888, |
| GBM_FORMAT_BGRX8888, |
| GBM_FORMAT_ARGB8888, |
| GBM_FORMAT_ABGR8888, |
| GBM_FORMAT_RGBA8888, |
| GBM_FORMAT_BGRA8888, |
| GBM_FORMAT_XRGB2101010, |
| GBM_FORMAT_XBGR2101010, |
| GBM_FORMAT_RGBX1010102, |
| GBM_FORMAT_BGRX1010102, |
| GBM_FORMAT_ARGB2101010, |
| GBM_FORMAT_ABGR2101010, |
| GBM_FORMAT_RGBA1010102, |
| GBM_FORMAT_BGRA1010102, |
| GBM_FORMAT_YUYV, |
| GBM_FORMAT_YVYU, |
| GBM_FORMAT_UYVY, |
| GBM_FORMAT_VYUY, |
| GBM_FORMAT_AYUV, |
| GBM_FORMAT_NV12, |
| GBM_FORMAT_YVU420, |
| }; |
| |
| struct plane_info { |
| uint32_t bits_per_pixel; |
| uint32_t subsample_rate; |
| uint32_t data_mask; |
| }; |
| |
| #define MAX_PLANES 3 |
| struct format_info { |
| uint32_t pixel_format; |
| int num_planes; |
| struct plane_info planes[MAX_PLANES]; |
| }; |
| |
| /* Bits per pixel for each. */ |
| static const struct format_info format_info_list[] = { |
| {GBM_FORMAT_C8, 1, {{8, 1, 0xFF}}}, |
| {GBM_FORMAT_RGB332, 1, {{8, 1, 0xFF}}}, |
| {GBM_FORMAT_BGR233, 1, {{8, 1, 0xFF}}}, |
| {GBM_FORMAT_XRGB4444, 1, {{16, 1, 0x0FFF}}}, |
| {GBM_FORMAT_XBGR4444, 1, {{16, 1, 0x0FFF}}}, |
| {GBM_FORMAT_RGBX4444, 1, {{16, 1, 0xFFF0}}}, |
| {GBM_FORMAT_BGRX4444, 1, {{16, 1, 0xFFF0}}}, |
| {GBM_FORMAT_ARGB4444, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_ABGR4444, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_RGBA4444, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_BGRA4444, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_XRGB1555, 1, {{16, 1, 0x7FFF}}}, |
| {GBM_FORMAT_XBGR1555, 1, {{16, 1, 0x7FFF}}}, |
| {GBM_FORMAT_RGBX5551, 1, {{16, 1, 0xFFFE}}}, |
| {GBM_FORMAT_BGRX5551, 1, {{16, 1, 0xFFFE}}}, |
| {GBM_FORMAT_ARGB1555, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_ABGR1555, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_RGBA5551, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_BGRA5551, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_RGB565, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_BGR565, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_RGB888, 1, {{24, 1, 0xFFFFFF}}}, |
| {GBM_FORMAT_BGR888, 1, {{24, 1, 0xFFFFFF}}}, |
| {GBM_FORMAT_XRGB8888, 1, {{32, 1, 0x00FFFFFF}}}, |
| {GBM_FORMAT_XBGR8888, 1, {{32, 1, 0x00FFFFFF}}}, |
| {GBM_FORMAT_RGBX8888, 1, {{32, 1, 0xFFFFFF00}}}, |
| {GBM_FORMAT_BGRX8888, 1, {{32, 1, 0xFFFFFF00}}}, |
| {GBM_FORMAT_ARGB8888, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_ABGR8888, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_RGBA8888, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_BGRA8888, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_XRGB2101010, 1, {{32, 1, 0x3FFFFFFF}}}, |
| {GBM_FORMAT_XBGR2101010, 1, {{32, 1, 0x3FFFFFFF}}}, |
| {GBM_FORMAT_RGBX1010102, 1, {{32, 1, 0xFFFFFFFC}}}, |
| {GBM_FORMAT_BGRX1010102, 1, {{32, 1, 0xFFFFFFFC}}}, |
| {GBM_FORMAT_ARGB2101010, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_ABGR2101010, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_RGBA1010102, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_BGRA1010102, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_YUYV, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_YVYU, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_UYVY, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_VYUY, 1, {{16, 1, 0xFFFF}}}, |
| {GBM_FORMAT_AYUV, 1, {{32, 1, 0xFFFFFFFF}}}, |
| {GBM_FORMAT_NV12, 2, {{8, 1, 0xFF}, {16, 2, 0xFFFF}}}, |
| {GBM_FORMAT_YVU420, 3, {{8, 1, 0xFF}, {8, 2, 0xFF}, {8,2, 0xFF}}}, |
| }; |
| |
| static const uint32_t usage_list[] = { |
| GBM_BO_USE_SCANOUT, |
| GBM_BO_USE_CURSOR_64X64, |
| GBM_BO_USE_RENDERING, |
| GBM_BO_USE_LINEAR, |
| GBM_BO_USE_SW_READ_OFTEN, |
| GBM_BO_USE_SW_READ_RARELY, |
| GBM_BO_USE_SW_WRITE_OFTEN, |
| GBM_BO_USE_SW_WRITE_RARELY, |
| }; |
| |
| static const uint32_t buffer_list[] = { |
| GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY | |
| GBM_BO_USE_TEXTURING, |
| |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | |
| GBM_BO_USE_SW_WRITE_RARELY, |
| |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | |
| GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| }; |
| |
| static int check_bo(struct gbm_bo *bo) |
| { |
| uint32_t format; |
| size_t num_planes, plane; |
| int fd; |
| int i; |
| |
| CHECK(bo); |
| CHECK(gbm_bo_get_width(bo) >= 0); |
| CHECK(gbm_bo_get_height(bo) >= 0); |
| CHECK(gbm_bo_get_stride(bo) >= gbm_bo_get_width(bo)); |
| |
| format = gbm_bo_get_format(bo); |
| for (i = 0; i < ARRAY_SIZE(format_list); i++) |
| if (format_list[i] == format) |
| break; |
| CHECK(i < ARRAY_SIZE(format_list)); |
| |
| num_planes = gbm_bo_get_plane_count(bo); |
| if (format == GBM_FORMAT_NV12) |
| CHECK(num_planes == 2); |
| else if (format == GBM_FORMAT_YVU420) |
| CHECK(num_planes == 3); |
| else |
| CHECK(num_planes == 1); |
| |
| CHECK(gbm_bo_get_handle_for_plane(bo, 0).u32 == gbm_bo_get_handle(bo).u32); |
| |
| CHECK(gbm_bo_get_offset(bo, 0) == 0); |
| CHECK(gbm_bo_get_plane_size(bo, 0) >= |
| gbm_bo_get_width(bo) * gbm_bo_get_height(bo)); |
| CHECK(gbm_bo_get_stride_for_plane(bo, 0) == gbm_bo_get_stride(bo)); |
| |
| for (plane = 0; plane < num_planes; plane++) { |
| CHECK(gbm_bo_get_handle_for_plane(bo, plane).u32); |
| |
| fd = gbm_bo_get_plane_fd(bo, plane); |
| CHECK(fd > 0); |
| close(fd); |
| |
| gbm_bo_get_offset(bo, plane); |
| CHECK(gbm_bo_get_plane_size(bo, plane)); |
| CHECK(gbm_bo_get_stride_for_plane(bo, plane)); |
| } |
| |
| return 1; |
| } |
| |
| static drmModeConnector *find_first_connected_connector(int fd, |
| drmModeRes *resources) |
| { |
| int i; |
| for (i = 0; i < resources->count_connectors; i++) { |
| drmModeConnector *connector; |
| |
| connector = drmModeGetConnector(fd, resources->connectors[i]); |
| if (connector) { |
| if ((connector->count_modes > 0) && |
| (connector->connection == DRM_MODE_CONNECTED)) |
| return connector; |
| |
| drmModeFreeConnector(connector); |
| } |
| } |
| return NULL; |
| } |
| |
| static int drm_open() |
| { |
| int fd; |
| unsigned i; |
| |
| /* Find the first drm device with a connected display. */ |
| for (i = 0; i < DRM_MAX_MINOR; i++) { |
| char* dev_name; |
| drmModeRes *res = NULL; |
| int ret; |
| |
| ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); |
| if (ret < 0) |
| continue; |
| |
| fd = open(dev_name, O_RDWR, 0); |
| free(dev_name); |
| if (fd < 0) |
| continue; |
| |
| res = drmModeGetResources(fd); |
| if (!res) { |
| drmClose(fd); |
| continue; |
| } |
| |
| if (res->count_crtcs > 0 && res->count_connectors > 0) { |
| if (find_first_connected_connector(fd, res)) { |
| drmModeFreeResources(res); |
| return fd; |
| } |
| } |
| |
| drmClose(fd); |
| drmModeFreeResources(res); |
| } |
| |
| /* |
| * If no drm device has a connected display, fall back to the first |
| * drm device. |
| */ |
| for (i = 0; i < DRM_MAX_MINOR; i++) { |
| char* dev_name; |
| int ret; |
| |
| ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); |
| if (ret < 0) |
| continue; |
| |
| fd = open(dev_name, O_RDWR, 0); |
| free(dev_name); |
| if (fd < 0) |
| continue; |
| |
| return fd; |
| } |
| |
| return ENODRM; |
| } |
| |
| static int drm_open_vgem() |
| { |
| const char g_sys_card_path_format[] = |
| "/sys/bus/platform/devices/vgem/drm/card%d"; |
| const char g_dev_card_path_format[] = |
| "/dev/dri/card%d"; |
| char *name; |
| int i, fd; |
| |
| for (i = 0; i < 16; i++) { |
| struct stat _stat; |
| int ret; |
| ret = asprintf(&name, g_sys_card_path_format, i); |
| assert(ret != -1); |
| |
| if (stat(name, &_stat) == -1) { |
| free(name); |
| continue; |
| } |
| |
| free(name); |
| ret = asprintf(&name, g_dev_card_path_format, i); |
| assert(ret != -1); |
| |
| fd = open(name, O_RDWR); |
| free(name); |
| if (fd == -1) { |
| return -1; |
| } |
| return fd; |
| } |
| return -1; |
| } |
| |
| static int create_vgem_bo(int fd, size_t size, uint32_t * handle) |
| { |
| struct drm_mode_create_dumb create; |
| int ret; |
| |
| memset(&create, 0, sizeof(create)); |
| create.height = size; |
| create.width = 1; |
| create.bpp = 8; |
| |
| ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); |
| if (ret) |
| return ret; |
| |
| assert(create.size >= size); |
| |
| *handle = create.handle; |
| |
| return 0; |
| } |
| |
| /* |
| * Tests initialization. |
| */ |
| static int test_init() |
| { |
| fd = drm_open(); |
| |
| CHECK(fd >= 0); |
| |
| gbm = gbm_create_device(fd); |
| |
| CHECK(gbm_device_get_fd(gbm) == fd); |
| |
| const char* backend_name = gbm_device_get_backend_name(gbm); |
| |
| CHECK(backend_name); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests reinitialization. |
| */ |
| static int test_reinit() |
| { |
| gbm_device_destroy(gbm); |
| close(fd); |
| |
| fd = drm_open(); |
| CHECK(fd >= 0); |
| |
| gbm = gbm_create_device(fd); |
| |
| CHECK(gbm_device_get_fd(gbm) == fd); |
| |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests repeated alloc/free. |
| */ |
| static int test_alloc_free() |
| { |
| int i; |
| for(i = 0; i < 1000; i++) { |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| } |
| return 1; |
| } |
| |
| /* |
| * Tests that we can allocate different buffer dimensions. |
| */ |
| static int test_alloc_free_sizes() |
| { |
| int i; |
| for(i = 1; i < 1920; i++) { |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, i, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| } |
| |
| for(i = 1; i < 1920; i++) { |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, i, 1, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| } |
| |
| for(i = 1; i < 1920; i++) { |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, 1, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Tests that we can allocate different buffer formats. |
| */ |
| static int test_alloc_free_formats() |
| { |
| int i; |
| |
| for(i = 0; i < ARRAY_SIZE(format_list); i++) { |
| uint32_t format = format_list[i]; |
| if (gbm_device_is_format_supported(gbm, format, GBM_BO_USE_RENDERING)) { |
| struct gbm_bo *bo; |
| bo = gbm_bo_create(gbm, 1024, 1024, format, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| } |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Tests that we find at least one working format for each usage. |
| */ |
| static int test_alloc_free_usage() |
| { |
| int i, j; |
| |
| for(i = 0; i < ARRAY_SIZE(usage_list); i++) { |
| uint32_t usage = usage_list[i]; |
| int found = 0; |
| for(j = 0; j < ARRAY_SIZE(format_list); j++) { |
| uint32_t format = format_list[j]; |
| if (gbm_device_is_format_supported(gbm, format, usage)) { |
| struct gbm_bo *bo; |
| if (usage == GBM_BO_USE_CURSOR_64X64) |
| bo = gbm_bo_create(gbm, 64, 64, format, usage); |
| else |
| bo = gbm_bo_create(gbm, 1024, 1024, format, usage); |
| CHECK(check_bo(bo)); |
| found = 1; |
| gbm_bo_destroy(bo); |
| } |
| } |
| CHECK(found); |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Tests user data. |
| */ |
| static int been_there1; |
| static int been_there2; |
| |
| void destroy_data1(struct gbm_bo *bo, void *data) |
| { |
| been_there1 = 1; |
| } |
| |
| void destroy_data2(struct gbm_bo *bo, void *data) |
| { |
| been_there2 = 1; |
| } |
| |
| static int test_user_data() |
| { |
| struct gbm_bo *bo1, *bo2; |
| char *data1, *data2; |
| |
| been_there1 = 0; |
| been_there2 = 0; |
| |
| bo1 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| bo2 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| data1 = (char*)malloc(1); |
| data2 = (char*)malloc(1); |
| CHECK(data1); |
| CHECK(data2); |
| |
| gbm_bo_set_user_data(bo1, data1, destroy_data1); |
| gbm_bo_set_user_data(bo2, data2, destroy_data2); |
| |
| CHECK((char*)gbm_bo_get_user_data(bo1) == data1); |
| CHECK((char*)gbm_bo_get_user_data(bo2) == data2); |
| |
| gbm_bo_destroy(bo1); |
| CHECK(been_there1 == 1); |
| |
| gbm_bo_set_user_data(bo2, NULL, NULL); |
| gbm_bo_destroy(bo2); |
| CHECK(been_there2 == 0); |
| |
| free(data1); |
| free(data2); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests destruction. |
| */ |
| static int test_destroy() |
| { |
| gbm_device_destroy(gbm); |
| close(fd); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests prime export. |
| */ |
| static int test_export() |
| { |
| struct gbm_bo *bo; |
| int prime_fd; |
| |
| bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| |
| prime_fd = gbm_bo_get_fd(bo); |
| CHECK(prime_fd > 0); |
| close(prime_fd); |
| |
| gbm_bo_destroy(bo); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests prime import using VGEM sharing buffer. |
| */ |
| static int test_import_vgem() |
| { |
| struct gbm_import_fd_data fd_data; |
| int vgem_fd = drm_open_vgem(); |
| struct drm_prime_handle prime_handle; |
| struct gbm_bo *bo; |
| const int width = 123; |
| const int height = 456; |
| const int bytes_per_pixel = 4; |
| const int size = width * height * bytes_per_pixel; |
| |
| if (vgem_fd <= 0) |
| return 1; |
| |
| CHECK(create_vgem_bo(vgem_fd, size, &prime_handle.handle) == 0); |
| prime_handle.flags = DRM_CLOEXEC; |
| CHECK(drmIoctl(vgem_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle) == 0); |
| |
| fd_data.fd = prime_handle.fd; |
| fd_data.width = width; |
| fd_data.height = height; |
| fd_data.stride = width * bytes_per_pixel; |
| fd_data.format = GBM_FORMAT_XRGB8888; |
| |
| bo = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo)); |
| gbm_bo_destroy(bo); |
| close(prime_handle.fd); |
| |
| close(vgem_fd); |
| |
| return 1; |
| } |
| |
| /* |
| * Tests prime import using dma-buf API. |
| */ |
| static int test_import_dmabuf() |
| { |
| struct gbm_import_fd_data fd_data; |
| struct gbm_bo *bo1, *bo2; |
| const int width = 123; |
| const int height = 456; |
| int prime_fd; |
| |
| bo1 = gbm_bo_create(gbm, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo1)); |
| |
| prime_fd = gbm_bo_get_fd(bo1); |
| CHECK(prime_fd >= 0); |
| |
| fd_data.fd = prime_fd; |
| fd_data.width = width; |
| fd_data.height = height; |
| fd_data.stride = gbm_bo_get_stride(bo1); |
| fd_data.format = GBM_FORMAT_XRGB8888; |
| |
| gbm_bo_destroy(bo1); |
| |
| bo2 = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo2)); |
| CHECK(fd_data.width == gbm_bo_get_width(bo2)); |
| CHECK(fd_data.height == gbm_bo_get_height(bo2)); |
| CHECK(fd_data.stride == gbm_bo_get_stride(bo2)); |
| |
| gbm_bo_destroy(bo2); |
| close(prime_fd); |
| |
| return 1; |
| } |
| |
| |
| /* |
| * Tests GBM_BO_IMPORT_FD_MODIFIER entry point. |
| */ |
| static int test_import_modifier() |
| { |
| struct gbm_import_fd_modifier_data fd_data; |
| struct gbm_bo *bo1, *bo2; |
| const int width = 567; |
| const int height = 891; |
| size_t num_planes, p; |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(format_list); i++) { |
| uint32_t format = format_list[i]; |
| if (gbm_device_is_format_supported(gbm, format, GBM_BO_USE_RENDERING)) { |
| bo1 = gbm_bo_create(gbm, width, height, format, GBM_BO_USE_RENDERING); |
| CHECK(check_bo(bo1)); |
| |
| num_planes = gbm_bo_get_plane_count(bo1); |
| fd_data.num_fds = num_planes; |
| |
| for (p = 0; p < num_planes; p++) { |
| fd_data.fds[p] = gbm_bo_get_plane_fd(bo1, p); |
| CHECK(fd_data.fds[p] >= 0); |
| |
| fd_data.strides[p] = gbm_bo_get_stride_for_plane(bo1, p); |
| fd_data.offsets[p] = gbm_bo_get_offset(bo1, p); |
| } |
| |
| fd_data.modifier = gbm_bo_get_modifier(bo1); |
| fd_data.width = width; |
| fd_data.height = height; |
| fd_data.format = format; |
| |
| gbm_bo_destroy(bo1); |
| |
| bo2 = gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, |
| GBM_BO_USE_RENDERING); |
| |
| CHECK(check_bo(bo2)); |
| CHECK(fd_data.width == gbm_bo_get_width(bo2)); |
| CHECK(fd_data.height == gbm_bo_get_height(bo2)); |
| CHECK(fd_data.modifier == gbm_bo_get_modifier(bo2)); |
| |
| for (p = 0; p < num_planes; p++) { |
| CHECK(fd_data.strides[p] == gbm_bo_get_stride_for_plane(bo2, p)); |
| CHECK(fd_data.offsets[p] == gbm_bo_get_offset(bo2, p)); |
| } |
| |
| gbm_bo_destroy(bo2); |
| |
| for (p = 0; p < num_planes; p++) |
| close(fd_data.fds[p]); |
| } |
| } |
| |
| return 1; |
| } |
| |
| static int test_gem_map() |
| { |
| uint32_t *pixel, pixel_size; |
| struct gbm_bo *bo; |
| void *map_data, *addr; |
| |
| uint32_t stride = 0; |
| const int width = 666; |
| const int height = 777; |
| |
| addr = map_data = NULL; |
| |
| bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_ARGB8888, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY); |
| CHECK(check_bo(bo)); |
| |
| addr = gbm_bo_map2(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ_WRITE, &stride, |
| &map_data, 0); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint32_t *)addr; |
| pixel_size = sizeof(*pixel); |
| |
| pixel[(height / 2) * (stride / pixel_size) + width / 2] = 0xABBAABBA; |
| gbm_bo_unmap(bo, map_data); |
| |
| /* Re-map and verify written previously data. */ |
| stride = 0; |
| addr = map_data = NULL; |
| |
| addr = gbm_bo_map2(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ_WRITE, &stride, |
| &map_data, 0); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint32_t *)addr; |
| CHECK(pixel[(height / 2) * (stride / pixel_size) + width / 2] == 0xABBAABBA); |
| |
| gbm_bo_unmap(bo, map_data); |
| gbm_bo_destroy(bo); |
| |
| return 1; |
| } |
| |
| static int test_dmabuf_map() |
| { |
| uint32_t *pixel; |
| struct gbm_bo *bo; |
| void *addr, *map_data; |
| const int width = 666; |
| const int height = 777; |
| int x, y, ret, prime_fd; |
| struct dma_buf_sync sync_end = { 0 }; |
| struct dma_buf_sync sync_start = { 0 }; |
| uint32_t pixel_size, stride, stride_pixels, length; |
| |
| bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR); |
| CHECK(check_bo(bo)); |
| |
| prime_fd = gbm_bo_get_fd(bo); |
| CHECK(prime_fd > 0); |
| |
| stride = gbm_bo_get_stride(bo); |
| length = gbm_bo_get_plane_size(bo, 0); |
| CHECK(stride > 0); |
| CHECK(length > 0); |
| |
| addr = mmap(NULL, length, (PROT_READ | PROT_WRITE), MAP_SHARED, prime_fd, 0); |
| CHECK(addr != MAP_FAILED); |
| |
| pixel = (uint32_t *)addr; |
| pixel_size = sizeof(*pixel); |
| stride_pixels = stride / pixel_size; |
| |
| sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE; |
| ret = HANDLE_EINTR(ioctl(prime_fd, DMA_BUF_IOCTL_SYNC, &sync_start)); |
| CHECK(ret == 0); |
| |
| for (y = 0; y < height; ++y) |
| for (x = 0; x < width; ++x) |
| pixel[y * stride_pixels + x] = ((y << 16) | x); |
| |
| sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE; |
| ret = HANDLE_EINTR(ioctl(prime_fd, DMA_BUF_IOCTL_SYNC, &sync_end)); |
| CHECK(ret == 0); |
| |
| ret = munmap(addr, length); |
| CHECK(ret == 0); |
| |
| ret = close(prime_fd); |
| CHECK(ret == 0); |
| |
| prime_fd = gbm_bo_get_fd(bo); |
| CHECK(prime_fd > 0); |
| |
| addr = mmap(NULL, length, (PROT_READ | PROT_WRITE), MAP_SHARED, prime_fd, 0); |
| CHECK(addr != MAP_FAILED); |
| |
| pixel = (uint32_t *)addr; |
| |
| memset(&sync_start, 0, sizeof(sync_start)); |
| memset(&sync_end, 0, sizeof(sync_end)); |
| |
| sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ; |
| ret = HANDLE_EINTR(ioctl(prime_fd, DMA_BUF_IOCTL_SYNC, &sync_start)); |
| CHECK(ret == 0); |
| |
| for (y = 0; y < height; ++y) |
| for (x = 0; x < width; ++x) |
| CHECK(pixel[y * stride_pixels + x] == ((y << 16) | x)); |
| |
| sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ; |
| ret = HANDLE_EINTR(ioctl(prime_fd, DMA_BUF_IOCTL_SYNC, &sync_end)); |
| CHECK(ret == 0); |
| |
| ret = munmap(addr, length); |
| CHECK(ret == 0); |
| |
| ret = close(prime_fd); |
| CHECK(ret == 0); |
| |
| addr = gbm_bo_map2(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ, &stride, |
| &map_data, 0); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint32_t *)addr; |
| |
| for (y = 0; y < height; ++y) |
| for (x = 0; x < width; ++x) |
| CHECK(pixel[y * stride_pixels + x] == ((y << 16) | x)); |
| |
| gbm_bo_unmap(bo, map_data); |
| gbm_bo_destroy(bo); |
| |
| return 1; |
| } |
| |
| static int test_gem_map_tiling(enum gbm_bo_flags buffer_create_flag) |
| { |
| uint32_t *pixel, pixel_size; |
| struct gbm_bo *bo; |
| void *map_data, *addr; |
| |
| uint32_t stride = 0; |
| uint32_t stride_pixels = 0; |
| const int width = 666; |
| const int height = 777; |
| int x, y; |
| |
| addr = map_data = NULL; |
| |
| bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_ARGB8888, buffer_create_flag); |
| CHECK(check_bo(bo)); |
| |
| addr = gbm_bo_map2(bo, 0, 0, width, height, GBM_BO_TRANSFER_WRITE, &stride, |
| &map_data, 0); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint32_t *)addr; |
| pixel_size = sizeof(*pixel); |
| stride_pixels = stride / pixel_size; |
| |
| for (y = 0; y < height; ++y) |
| for (x = 0; x < width; ++x) |
| pixel[y * stride_pixels + x] = ((y << 16) | x); |
| |
| gbm_bo_unmap(bo, map_data); |
| |
| /* Re-map and verify written previously data. */ |
| stride = 0; |
| addr = map_data = NULL; |
| |
| addr = gbm_bo_map2(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ, &stride, |
| &map_data, 0); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint32_t *)addr; |
| pixel_size = sizeof(*pixel); |
| stride_pixels = stride / pixel_size; |
| |
| for (y = 0; y < height; ++y) |
| for (x = 0; x < width; ++x) |
| CHECK(pixel[y * stride_pixels + x] == ((y << 16) | x)); |
| |
| gbm_bo_unmap(bo, map_data); |
| gbm_bo_destroy(bo); |
| |
| return 1; |
| } |
| |
| |
| static int test_gem_map_format(int format_index, |
| enum gbm_bo_flags buffer_create_flag) |
| { |
| uint8_t *pixel; |
| struct gbm_bo *bo; |
| void *map_data, *addr; |
| uint32_t x, y, p, w, h, b, planes, bytes_per_pixel, pixel_data_mask, idx; |
| uint8_t byte_mask; |
| uint32_t stride = 0; |
| const int width = 333; |
| const int height = 444; |
| const uint32_t pixel_format = format_info_list[format_index].pixel_format; |
| |
| addr = map_data = NULL; |
| if (!gbm_device_is_format_supported(gbm, pixel_format, buffer_create_flag)) |
| return 1; |
| |
| bo = gbm_bo_create(gbm, width, height, pixel_format, buffer_create_flag); |
| CHECK(check_bo(bo)); |
| planes = gbm_bo_get_plane_count(bo); |
| CHECK(planes == format_info_list[format_index].num_planes); |
| |
| for (p = 0; p < planes; ++p) { |
| w = width / format_info_list[format_index].planes[p].subsample_rate; |
| h = height / format_info_list[format_index].planes[p].subsample_rate; |
| addr = gbm_bo_map2(bo, 0, 0, w, h, GBM_BO_TRANSFER_WRITE, &stride, |
| &map_data, p); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint8_t *)addr; |
| bytes_per_pixel = format_info_list[format_index].planes[p].bits_per_pixel / 8; |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| idx = y * stride + x * bytes_per_pixel; |
| for (b = 0; b < bytes_per_pixel; ++b) |
| pixel[idx + b] = y ^ x ^ b; |
| } |
| } |
| gbm_bo_unmap(bo, map_data); |
| stride = 0; |
| addr = map_data = NULL; |
| } |
| |
| /* Re-map and verify written previously data. */ |
| for (p = 0; p < planes; ++p) { |
| w = width / format_info_list[format_index].planes[p].subsample_rate; |
| h = height / format_info_list[format_index].planes[p].subsample_rate; |
| addr = gbm_bo_map2(bo, 0, 0, w, h, GBM_BO_TRANSFER_READ, &stride, |
| &map_data, p); |
| |
| CHECK(addr != MAP_FAILED); |
| CHECK(map_data); |
| CHECK(stride > 0); |
| |
| pixel = (uint8_t *)addr; |
| bytes_per_pixel = format_info_list[format_index].planes[p].bits_per_pixel / 8; |
| pixel_data_mask = format_info_list[format_index].planes[p].data_mask; |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| idx = y * stride + x * bytes_per_pixel; |
| for (b = 0; b < bytes_per_pixel; ++b) { |
| byte_mask = pixel_data_mask >> (8 * b); |
| CHECK((pixel[idx + b] & byte_mask) == ((uint8_t)(y ^ x ^ b) & byte_mask)); |
| } |
| } |
| } |
| gbm_bo_unmap(bo, map_data); |
| stride = 0; |
| addr = map_data = NULL; |
| } |
| |
| gbm_bo_destroy(bo); |
| return 1; |
| } |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| int result, i, j; |
| |
| result = test_init(); |
| if (result == ENODRM) { |
| printf("[ FAILED ] graphics_Gbm test initialization failed\n"); |
| return EXIT_FAILURE; |
| } |
| |
| result &= test_reinit(); |
| result &= test_alloc_free(); |
| result &= test_alloc_free_sizes(); |
| result &= test_alloc_free_formats(); |
| result &= test_alloc_free_usage(); |
| result &= test_user_data(); |
| result &= test_export(); |
| result &= test_import_vgem(); |
| result &= test_import_dmabuf(); |
| result &= test_import_modifier(); |
| result &= test_gem_map(); |
| |
| // TODO(crbug.com/752669) |
| if (strcmp(gbm_device_get_backend_name(gbm), "tegra")) { |
| for (i = 0; i < ARRAY_SIZE(buffer_list); ++i) { |
| result &= test_gem_map_tiling(buffer_list[i]); |
| for (j = 0; j < ARRAY_SIZE(format_info_list); ++j) |
| result &= test_gem_map_format(j, buffer_list[i]); |
| } |
| |
| result &= test_dmabuf_map(); |
| } |
| result &= test_destroy(); |
| |
| if (!result) { |
| printf("[ FAILED ] graphics_Gbm test failed\n"); |
| return EXIT_FAILURE; |
| } else { |
| printf("[ PASSED ] graphics_Gbm test success\n"); |
| return EXIT_SUCCESS; |
| } |
| } |