/* * Copyright 2016 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. */ /* * Please run clang-format on this file after making changes: * * clang-format -style=file -i gralloctest.c * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #define ALIGN(A, B) (((A) + (B)-1) / (B) * (B)) #define ARRAY_SIZE(A) (sizeof(A) / sizeof(*(A))) #define CHECK(cond) \ do { \ if (!(cond)) { \ fprintf(stderr, "[ FAILED ] check in %s() %s:%d\n", __func__, __FILE__, \ __LINE__); \ return 0; \ } \ } while (0) #define CHECK_NO_MSG(cond) \ do { \ if (!(cond)) { \ return 0; \ } \ } while (0) /* Private API enumeration -- see */ enum { GRALLOC_DRM_GET_STRIDE, GRALLOC_DRM_GET_FORMAT, GRALLOC_DRM_GET_DIMENSIONS, GRALLOC_DRM_GET_BACKING_STORE, }; struct gralloctest_context { struct gralloc_module_t *module; struct alloc_device_t *device; int api; }; struct gralloc_testcase { const char *name; int (*run_test)(struct gralloctest_context *ctx); int required_api; }; struct combinations { int32_t format; int32_t usage; }; // clang-format off static struct combinations combos[] = { { HAL_PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_CURSOR }, { HAL_PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER }, { HAL_PIXEL_FORMAT_RGBX_8888, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN }, { HAL_PIXEL_FORMAT_YCbCr_420_888, GRALLOC_USAGE_EXTERNAL_DISP | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE }, { HAL_PIXEL_FORMAT_YCbCr_420_888, GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN }, { HAL_PIXEL_FORMAT_YV12, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP }, { HAL_PIXEL_FORMAT_RGB_565, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN }, { HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN }, { HAL_PIXEL_FORMAT_BLOB, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN }, }; // clang-format on struct grallocinfo { buffer_handle_t handle; /* handle to the buffer */ int w; /* width of buffer */ int h; /* height of buffer */ int format; /* format of the buffer */ int usage; /* bitfield indicating usage */ int fence_fd; /* fence file descriptor */ void *vaddr; /* buffer virtual memory address */ int stride; /* stride in pixels */ struct android_ycbcr ycbcr; /* sw access for yuv buffers */ }; /* This function is meant to initialize the test to commonly used defaults. */ void grallocinfo_init(struct grallocinfo *info, int w, int h, int format, int usage) { info->w = w; info->h = h; info->format = format; info->usage = usage; info->fence_fd = -1; info->vaddr = NULL; info->ycbcr.y = NULL; info->ycbcr.cb = NULL; info->ycbcr.cr = NULL; info->stride = 0; } static native_handle_t *duplicate_buffer_handle(buffer_handle_t handle) { native_handle_t *hnd = native_handle_create(handle->numFds, handle->numInts); if (hnd == NULL) return NULL; const int *old_data = handle->data; int *new_data = hnd->data; int i; for (i = 0; i < handle->numFds; i++) { *new_data = dup(*old_data); old_data++; new_data++; } memcpy(new_data, old_data, sizeof(int) * handle->numInts); return hnd; } /**************************************************************** * Wrappers around gralloc_module_t and alloc_device_t functions. * GraphicBufferMapper/GraphicBufferAllocator could replace this * in theory. ***************************************************************/ static int allocate(struct alloc_device_t *device, struct grallocinfo *info) { int ret; ret = device->alloc(device, info->w, info->h, info->format, info->usage, &info->handle, &info->stride); CHECK_NO_MSG(ret == 0); CHECK_NO_MSG(info->handle->version > 0); CHECK_NO_MSG(info->handle->numInts >= 0); CHECK_NO_MSG(info->handle->numFds >= 0); CHECK_NO_MSG(info->stride >= 0); return 1; } static int deallocate(struct alloc_device_t *device, struct grallocinfo *info) { int ret; ret = device->free(device, info->handle); CHECK(ret == 0); return 1; } static int register_buffer(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->registerBuffer(module, info->handle); return (ret == 0); } static int unregister_buffer(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->unregisterBuffer(module, info->handle); return (ret == 0); } static int lock(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->lock(module, info->handle, info->usage, 0, 0, (info->w) / 2, (info->h) / 2, &info->vaddr); return (ret == 0); } static int unlock(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->unlock(module, info->handle); return (ret == 0); } static int lock_ycbcr(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->lock_ycbcr(module, info->handle, info->usage, 0, 0, (info->w) / 2, (info->h) / 2, &info->ycbcr); return (ret == 0); } static int lock_async(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->lockAsync(module, info->handle, info->usage, 0, 0, (info->w) / 2, (info->h) / 2, &info->vaddr, info->fence_fd); return (ret == 0); } static int unlock_async(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->unlockAsync(module, info->handle, &info->fence_fd); return (ret == 0); } static int lock_async_ycbcr(struct gralloc_module_t *module, struct grallocinfo *info) { int ret; ret = module->lockAsync_ycbcr(module, info->handle, info->usage, 0, 0, (info->w) / 2, (info->h) / 2, &info->ycbcr, info->fence_fd); return (ret == 0); } /************************************************************** * END WRAPPERS * **************************************************************/ /* This function tests initialization of gralloc module and allocator. */ static struct gralloctest_context *test_init_gralloc() { int err; hw_module_t const *hw_module; struct gralloctest_context *ctx = calloc(1, sizeof(*ctx)); err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &hw_module); if (err) return NULL; gralloc_open(hw_module, &ctx->device); ctx->module = (gralloc_module_t *)hw_module; if (!ctx->module || !ctx->device) return NULL; switch (ctx->module->common.module_api_version) { case GRALLOC_MODULE_API_VERSION_0_3: ctx->api = 3; break; case GRALLOC_MODULE_API_VERSION_0_2: ctx->api = 2; break; default: ctx->api = 1; } return ctx; } static int test_close_gralloc(struct gralloctest_context *ctx) { CHECK(gralloc_close(ctx->device) == 0); return 1; } /* This function tests allocation with varying buffer dimensions. */ static int test_alloc_varying_sizes(struct gralloctest_context *ctx) { struct grallocinfo info; int i; grallocinfo_init(&info, 0, 0, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN); for (i = 1; i < 1920; i++) { info.w = i; info.h = i; CHECK(allocate(ctx->device, &info)); CHECK(deallocate(ctx->device, &info)); } info.w = 1; for (i = 1; i < 1920; i++) { info.h = i; CHECK(allocate(ctx->device, &info)); CHECK(deallocate(ctx->device, &info)); } info.h = 1; for (i = 1; i < 1920; i++) { info.w = i; CHECK(allocate(ctx->device, &info)); CHECK(deallocate(ctx->device, &info)); } return 1; } /* * This function tests that we find at least one working format for each * combos which we consider important. */ static int test_alloc_combinations(struct gralloctest_context *ctx) { int i; struct grallocinfo info; grallocinfo_init(&info, 512, 512, 0, 0); for (i = 0; i < ARRAY_SIZE(combos); i++) { info.format = combos[i].format; info.usage = combos[i].usage; CHECK(allocate(ctx->device, &info)); CHECK(deallocate(ctx->device, &info)); } return 1; } /* * This function tests the advertised API version. * Version_0_2 added (*lock_ycbcr)() method. * Version_0_3 added fence passing to/from lock/unlock. */ static int test_api(struct gralloctest_context *ctx) { CHECK(ctx->module->registerBuffer); CHECK(ctx->module->unregisterBuffer); CHECK(ctx->module->lock); CHECK(ctx->module->unlock); switch (ctx->module->common.module_api_version) { case GRALLOC_MODULE_API_VERSION_0_3: CHECK(ctx->module->lock_ycbcr); CHECK(ctx->module->lockAsync); CHECK(ctx->module->unlockAsync); CHECK(ctx->module->lockAsync_ycbcr); break; case GRALLOC_MODULE_API_VERSION_0_2: CHECK(ctx->module->lock_ycbcr); CHECK(ctx->module->lockAsync == NULL); CHECK(ctx->module->unlockAsync == NULL); CHECK(ctx->module->lockAsync_ycbcr == NULL); break; case GRALLOC_MODULE_API_VERSION_0_1: CHECK(ctx->module->lockAsync == NULL); CHECK(ctx->module->unlockAsync == NULL); CHECK(ctx->module->lockAsync_ycbcr == NULL); CHECK(ctx->module->lock_ycbcr == NULL); break; default: return 0; } return 1; } /* * This function registers, unregisters, locks and unlocks the buffer in * various orders. */ static int test_gralloc_order(struct gralloctest_context *ctx) { struct grallocinfo info, duplicate; grallocinfo_init(&info, 512, 512, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN); grallocinfo_init(&duplicate, 512, 512, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN); CHECK(allocate(ctx->device, &info)); /* * Duplicate the buffer handle to simulate an additional reference * in same process. */ native_handle_t *native_handle = duplicate_buffer_handle(info.handle); duplicate.handle = native_handle; CHECK(unregister_buffer(ctx->module, &duplicate) == 0); CHECK(register_buffer(ctx->module, &duplicate)); CHECK(unlock(ctx->module, &duplicate) == 0); CHECK(lock(ctx->module, &duplicate)); CHECK(duplicate.vaddr); CHECK(unlock(ctx->module, &duplicate)); CHECK(unregister_buffer(ctx->module, &duplicate)); CHECK(register_buffer(ctx->module, &duplicate)); CHECK(unregister_buffer(ctx->module, &duplicate)); CHECK(unregister_buffer(ctx->module, &duplicate) == 0); CHECK(register_buffer(ctx->module, &duplicate)); CHECK(deallocate(ctx->device, &info)); CHECK(lock(ctx->module, &duplicate)); CHECK(lock(ctx->module, &duplicate)); CHECK(unlock(ctx->module, &duplicate)); CHECK(unlock(ctx->module, &duplicate)); CHECK(unlock(ctx->module, &duplicate) == 0); CHECK(unregister_buffer(ctx->module, &duplicate)); CHECK(native_handle_close(duplicate.handle) == 0); CHECK(native_handle_delete(native_handle) == 0); return 1; } /* This function tests CPU reads and writes. */ static int test_mapping(struct gralloctest_context *ctx) { struct grallocinfo info; uint32_t *ptr = NULL; uint32_t magic_number = 0x000ABBA; grallocinfo_init(&info, 512, 512, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); CHECK(allocate(ctx->device, &info)); CHECK(lock(ctx->module, &info)); ptr = (uint32_t *)info.vaddr; CHECK(ptr); ptr[(info.w) / 2] = magic_number; CHECK(unlock(ctx->module, &info)); info.vaddr = NULL; ptr = NULL; CHECK(lock(ctx->module, &info)); ptr = (uint32_t *)info.vaddr; CHECK(ptr); CHECK(ptr[info.w / 2] == magic_number); CHECK(unlock(ctx->module, &info)); CHECK(deallocate(ctx->device, &info)); return 1; } /* This function tests the private API we use in ARC++ -- not part of official * gralloc. */ static int test_perform(struct gralloctest_context *ctx) { int32_t format; uint64_t id1, id2; uint32_t stride, width, height; struct grallocinfo info, duplicate; struct gralloc_module_t *mod = ctx->module; grallocinfo_init(&info, 650, 408, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN); CHECK(allocate(ctx->device, &info)); CHECK(mod->perform(mod, GRALLOC_DRM_GET_STRIDE, info.handle, &stride) == 0); CHECK(stride == info.stride); CHECK(mod->perform(mod, GRALLOC_DRM_GET_FORMAT, info.handle, &format) == 0); CHECK(format == info.format); CHECK(mod->perform(mod, GRALLOC_DRM_GET_DIMENSIONS, info.handle, &width, &height) == 0); CHECK(width == info.w); CHECK(height == info.h); native_handle_t *native_handle = duplicate_buffer_handle(info.handle); duplicate.handle = native_handle; CHECK(mod->perform(mod, GRALLOC_DRM_GET_BACKING_STORE, duplicate.handle, &id2)); CHECK(register_buffer(mod, &duplicate)); CHECK(mod->perform(mod, GRALLOC_DRM_GET_BACKING_STORE, info.handle, &id1) == 0); CHECK(mod->perform(mod, GRALLOC_DRM_GET_BACKING_STORE, duplicate.handle, &id2) == 0); CHECK(id1 == id2); CHECK(unregister_buffer(mod, &duplicate)); CHECK(deallocate(ctx->device, &info)); return 1; } /* This function tests that only YUV buffers work with *lock_ycbcr. */ static int test_ycbcr(struct gralloctest_context *ctx) { struct grallocinfo info; grallocinfo_init(&info, 512, 512, HAL_PIXEL_FORMAT_YCbCr_420_888, GRALLOC_USAGE_SW_READ_OFTEN); CHECK(allocate(ctx->device, &info)); CHECK(lock(ctx->module, &info) == 0); CHECK(lock_ycbcr(ctx->module, &info)); CHECK(info.ycbcr.y); CHECK(info.ycbcr.cb); CHECK(info.ycbcr.cr); CHECK(unlock(ctx->module, &info)); CHECK(deallocate(ctx->device, &info)); info.format = HAL_PIXEL_FORMAT_BGRA_8888; CHECK(allocate(ctx->device, &info)); CHECK(lock_ycbcr(ctx->module, &info) == 0); CHECK(lock(ctx->module, &info)); CHECK(unlock(ctx->module, &info)); CHECK(deallocate(ctx->device, &info)); return 1; } /* * This function tests a method ARC++ uses to query YUV buffer * info -- not part of official gralloc API. This is used in * Mali, Mesa, the ArcCodec and wayland_service. */ static int test_yuv_info(struct gralloctest_context *ctx) { struct grallocinfo info; uintptr_t y_size, c_stride, c_size, cr_offset, cb_offset; uint32_t width, height; width = height = 512; /* defines YV12 as having: * - an even width * - an even height * - a horizontal stride multiple of 16 pixels * - a vertical stride equal to the height * * y_size = stride * height. * c_stride = ALIGN(stride/2, 16). * c_size = c_stride * height/2. * size = y_size + c_size * 2. * cr_offset = y_size. * cb_offset = y_size + c_size. */ grallocinfo_init(&info, width, height, HAL_PIXEL_FORMAT_YV12, GRALLOC_USAGE_SW_READ_OFTEN); CHECK(allocate(ctx->device, &info)); y_size = info.stride * height; c_stride = ALIGN(info.stride / 2, 16); c_size = c_stride * height / 2; cr_offset = y_size; cb_offset = y_size + c_size; info.usage = 0; /* * Check if the (*lock_ycbcr) with usage of zero returns the * offsets and strides of the YV12 buffer. This is unofficial * behavior we are testing here. */ CHECK(lock_ycbcr(ctx->module, &info)); CHECK(info.stride == info.ycbcr.ystride); CHECK(c_stride == info.ycbcr.cstride); CHECK(cr_offset == (uintptr_t)info.ycbcr.cr); CHECK(cb_offset == (uintptr_t)info.ycbcr.cb); CHECK(unlock(ctx->module, &info)); CHECK(deallocate(ctx->device, &info)); return 1; } /* This function tests asynchronous locking and unlocking of buffers. */ static int test_async(struct gralloctest_context *ctx) { struct grallocinfo rgba_info, ycbcr_info; grallocinfo_init(&rgba_info, 512, 512, HAL_PIXEL_FORMAT_BGRA_8888, GRALLOC_USAGE_SW_READ_OFTEN); grallocinfo_init(&ycbcr_info, 512, 512, HAL_PIXEL_FORMAT_YCbCr_420_888, GRALLOC_USAGE_SW_READ_OFTEN); CHECK(allocate(ctx->device, &rgba_info)); CHECK(allocate(ctx->device, &ycbcr_info)); CHECK(lock_async(ctx->module, &rgba_info)); CHECK(lock_async_ycbcr(ctx->module, &ycbcr_info)); CHECK(rgba_info.vaddr); CHECK(ycbcr_info.ycbcr.y); CHECK(ycbcr_info.ycbcr.cb); CHECK(ycbcr_info.ycbcr.cr); /* * Wait on the fence returned from unlock_async and check it doesn't * return an error. */ CHECK(unlock_async(ctx->module, &rgba_info)); CHECK(unlock_async(ctx->module, &ycbcr_info)); if (rgba_info.fence_fd >= 0) { CHECK(sync_wait(rgba_info.fence_fd, 10000) >= 0); CHECK(close(rgba_info.fence_fd) == 0); } if (ycbcr_info.fence_fd >= 0) { CHECK(sync_wait(ycbcr_info.fence_fd, 10000) >= 0); CHECK(close(ycbcr_info.fence_fd) == 0); } CHECK(deallocate(ctx->device, &rgba_info)); CHECK(deallocate(ctx->device, &ycbcr_info)); return 1; } static const struct gralloc_testcase tests[] = { { "alloc_varying_sizes", test_alloc_varying_sizes, 1 }, { "alloc_combinations", test_alloc_combinations, 1 }, { "api", test_api, 1 }, { "gralloc_order", test_gralloc_order, 1 }, { "mapping", test_mapping, 1 }, { "perform", test_perform, 1 }, { "ycbcr", test_ycbcr, 2 }, { "yuv_info", test_yuv_info, 2 }, { "async", test_async, 3 }, }; static void print_help(const char *argv0) { uint32_t i; printf("usage: %s \n\n", argv0); printf("A valid name test is one the following:\n"); for (i = 0; i < ARRAY_SIZE(tests); i++) printf("%s\n", tests[i].name); } int main(int argc, char *argv[]) { int ret = 0; uint32_t num_run = 0; setbuf(stdout, NULL); if (argc == 2) { uint32_t i; char *name = argv[1]; struct gralloctest_context *ctx = test_init_gralloc(); if (!ctx) { fprintf(stderr, "[ FAILED ] to initialize gralloc.\n"); return 1; } for (i = 0; i < ARRAY_SIZE(tests); i++) { if (strcmp(tests[i].name, name) && strcmp("all", name)) continue; int success = 1; if (ctx->api >= tests[i].required_api) success = tests[i].run_test(ctx); printf("[ RUN ] gralloctest.%s\n", tests[i].name); if (!success) { fprintf(stderr, "[ FAILED ] gralloctest.%s\n", tests[i].name); ret |= 1; } else { printf("[ PASSED ] gralloctest.%s\n", tests[i].name); } num_run++; } if (!test_close_gralloc(ctx)) { fprintf(stderr, "[ FAILED ] to close gralloc.\n"); return 1; } if (!num_run) goto print_usage; return ret; } print_usage: print_help(argv[0]); return 0; }