From 0d868515a525df8c08167d80f6e88bb3058ff875 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 15 Sep 2023 13:44:26 +0200 Subject: [PATCH] libyuv: add support to overwrite default allocator Internally libyuv uses malloc & free for short time dynamic memory allocation during image transformation. The converted images are such large, that the Libc allocator will create and destroy new Genode dataspace per image. In time sensitive code paths, the overhead can be noticeable by the caller of the image transformation. The patch adds the option to register callbacks in the libyuv library to implement the image allocation by users of the library. They may implement caching strategies to avoid the overhead, e.g. as seen with qemu-usb and the webcam model. --- repos/libports/include/libyuv/libyuv.h | 26 ++++ repos/libports/lib/mk/libyuv.inc | 9 +- repos/libports/lib/symbols/libyuv | 1 + repos/libports/ports/libyuv.hash | 2 +- repos/libports/ports/libyuv.port | 2 +- repos/libports/recipes/api/libyuv/content.mk | 1 + repos/libports/recipes/src/libyuv/content.mk | 4 +- repos/libports/recipes/src/libyuv/used_apis | 2 +- repos/libports/src/lib/libyuv/memory.cc | 57 +++++++++ repos/libports/src/lib/libyuv/memory.patch | 128 +++++++++++++++++++ 10 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 repos/libports/include/libyuv/libyuv.h create mode 100644 repos/libports/src/lib/libyuv/memory.cc create mode 100644 repos/libports/src/lib/libyuv/memory.patch diff --git a/repos/libports/include/libyuv/libyuv.h b/repos/libports/include/libyuv/libyuv.h new file mode 100644 index 0000000000..bd9cebfc96 --- /dev/null +++ b/repos/libports/include/libyuv/libyuv.h @@ -0,0 +1,26 @@ +/* + * \brief Libyuv initialization to overwrite default libc memory/free allocator + * \author Alexander Boettcher + * \date 2023-09-15 + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INCLUDE__LIBYUV__LIBYUV_H_ +#define _INCLUDE__LIBYUV__LIBYUV_H_ + +/* use same namespace as used in contrib libyuv sources */ +namespace libyuv +{ + typedef void * (*type_malloc)(unsigned long); + typedef void (*type_free )(void *); + + extern "C" void libyuv_init(type_malloc os_malloc, type_free os_free); +} + +#endif /* _INCLUDE__LIBYUV_LIBYUV_H_ */ diff --git a/repos/libports/lib/mk/libyuv.inc b/repos/libports/lib/mk/libyuv.inc index 813769a194..8788884fb5 100644 --- a/repos/libports/lib/mk/libyuv.inc +++ b/repos/libports/lib/mk/libyuv.inc @@ -3,13 +3,18 @@ SHARED_LIB = yes YUV_PORT := $(call select_from_ports,libyuv) YUV_DIR = $(YUV_PORT)/src/lib/libyuv +LIB_DIR = $(REP_DIR)/src/lib/libyuv + LIBS = libc stdcxx jpeg +INC_DIR += $(REP_DIR)/include INC_DIR += $(YUV_PORT)/include -SRC_CC = $(notdir $(wildcard $(YUV_DIR)/source/*.cc)) +SRC_CC = $(notdir $(wildcard $(YUV_DIR)/source/*.cc)) +SRC_CC += memory.cc CC_CXX_WARN_STRICT = -Wextra -Werror CC_OPT += -Wno-unused-parameter -DHAVE_JPEG -vpath %.cc $(YUV_DIR)/source +vpath memory.cc $(LIB_DIR) +vpath %.cc $(YUV_DIR)/source diff --git a/repos/libports/lib/symbols/libyuv b/repos/libports/lib/symbols/libyuv index e9b31dc5e3..e31c345f14 100644 --- a/repos/libports/lib/symbols/libyuv +++ b/repos/libports/lib/symbols/libyuv @@ -351,3 +351,4 @@ YUY2ToI420 T YUY2ToI422 T YUY2ToNV12 T YUY2ToY T +libyuv_init T diff --git a/repos/libports/ports/libyuv.hash b/repos/libports/ports/libyuv.hash index ff1ba76773..b00eb92182 100644 --- a/repos/libports/ports/libyuv.hash +++ b/repos/libports/ports/libyuv.hash @@ -1 +1 @@ -01cdd9be97364d27b8d6aa7aafc18ba0f2eb4c3f +d53528820b90c03e8b8d23ef6bae7ad4337a3cf3 diff --git a/repos/libports/ports/libyuv.port b/repos/libports/ports/libyuv.port index ba7e8fdc05..c987c9633b 100644 --- a/repos/libports/ports/libyuv.port +++ b/repos/libports/ports/libyuv.port @@ -10,6 +10,6 @@ DIRS := include include/libyuv DIR_CONTENT(include) := src/lib/libyuv/include/libyuv.h DIR_CONTENT(include/libyuv) := src/lib/libyuv/include/libyuv/*.h -PATCHES := src/lib/libyuv/constraints.patch +PATCHES := src/lib/libyuv/constraints.patch src/lib/libyuv/memory.patch PATCH_OPT := -d src/lib/libyuv -p1 diff --git a/repos/libports/recipes/api/libyuv/content.mk b/repos/libports/recipes/api/libyuv/content.mk index 22c66df785..9b0803da5d 100644 --- a/repos/libports/recipes/api/libyuv/content.mk +++ b/repos/libports/recipes/api/libyuv/content.mk @@ -5,6 +5,7 @@ PORT_DIR := $(call port_dir,$(REP_DIR)/ports/libyuv) include: mkdir $@ cp -r $(PORT_DIR)/include/* $@/ + cp -r $(REP_DIR)/include/libyuv $@/ lib/symbols/libyuv: $(mirror_from_rep_dir) diff --git a/repos/libports/recipes/src/libyuv/content.mk b/repos/libports/recipes/src/libyuv/content.mk index 9b83f440e6..f0830f3937 100644 --- a/repos/libports/recipes/src/libyuv/content.mk +++ b/repos/libports/recipes/src/libyuv/content.mk @@ -2,7 +2,8 @@ MIRROR_FROM_REP_DIR := lib/mk/libyuv.inc \ lib/mk/spec/arm_v8/libyuv.mk \ lib/mk/spec/x86_32/libyuv.mk \ lib/mk/spec/x86_64/libyuv.mk \ - lib/import/import-libyuv.mk + lib/import/import-libyuv.mk \ + include/libyuv content: src/lib/libyuv include LICENSE $(MIRROR_FROM_REP_DIR) @@ -11,6 +12,7 @@ PORT_DIR := $(call port_dir,$(REP_DIR)/ports/libyuv) src/lib/libyuv: mkdir -p $@ cp -r $(PORT_DIR)/src/lib/libyuv/source $@ + cp -r $(REP_DIR)/src/lib/libyuv/* $@ include: mkdir -p $@ diff --git a/repos/libports/recipes/src/libyuv/used_apis b/repos/libports/recipes/src/libyuv/used_apis index 8935f6c215..ba836f18bc 100644 --- a/repos/libports/recipes/src/libyuv/used_apis +++ b/repos/libports/recipes/src/libyuv/used_apis @@ -1,4 +1,4 @@ +base libc stdcxx jpeg - diff --git a/repos/libports/src/lib/libyuv/memory.cc b/repos/libports/src/lib/libyuv/memory.cc new file mode 100644 index 0000000000..bde842159c --- /dev/null +++ b/repos/libports/src/lib/libyuv/memory.cc @@ -0,0 +1,57 @@ +/* + * \brief Support to overwrite default memory allocator of libyuv + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 2. + */ + +#include + +#include + + +typedef void * (*type_malloc)(unsigned long); +typedef void (*type_free )(void *); + + +static type_malloc use_malloc(type_malloc m = malloc) +{ + static auto defined_malloc = m; + return defined_malloc; +} + + +static type_free use_free(type_free f = free) +{ + static auto defined_free = f; + return defined_free; +} + + +extern "C" void libyuv_init(type_malloc os_malloc, type_free os_free) +{ + if (!os_malloc || !os_free) { + Genode::error("invalid libyuv allocator specified"); + return; + } + + use_malloc(os_malloc); + use_free (os_free); +} + + +extern "C" void *libyuv_malloc(unsigned long size) +{ + return use_malloc()(size); +} + + +extern "C" void libyuv_free(void *ptr) +{ + use_free()(ptr); +} diff --git a/repos/libports/src/lib/libyuv/memory.patch b/repos/libports/src/lib/libyuv/memory.patch new file mode 100644 index 0000000000..eb9171a183 --- /dev/null +++ b/repos/libports/src/lib/libyuv/memory.patch @@ -0,0 +1,128 @@ +--- libyuv/include/libyuv/row.h ++++ libyuv/include/libyuv/row.h +@@ -20,6 +20,9 @@ + extern "C" { + #endif + ++extern void *libyuv_malloc(unsigned long size); ++extern void libyuv_free(void *ptr); ++ + // TODO: Fix Win32 build + // https://bugs.chromium.org/p/libyuv/issues/detail?id=900 + #if defined(__pnacl__) || defined(__CLR_VER) || defined(_M_IX86) || \ +@@ -785,11 +788,11 @@ + #define IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a)-1))) + + #define align_buffer_64(var, size) \ +- uint8_t* var##_mem = (uint8_t*)(malloc((size) + 63)); /* NOLINT */ \ ++ uint8_t* var##_mem = (uint8_t*)(libyuv_malloc((size) + 63)); /* NOLINT */ \ + uint8_t* var = (uint8_t*)(((intptr_t)(var##_mem) + 63) & ~63) /* NOLINT */ + + #define free_aligned_buffer_64(var) \ +- free(var##_mem); \ ++ libyuv_free(var##_mem); \ + var = 0 + + #if defined(__APPLE__) || defined(__x86_64__) || defined(__llvm__) +--- libyuv/source/convert_jpeg.cc ++++ libyuv/source/convert_jpeg.cc +@@ -32,6 +32,10 @@ + int h; + }; + ++extern void *libyuv_malloc(unsigned long size); ++extern void libyuv_free(void *ptr); ++ ++ + static void JpegCopyI420(void* opaque, + const uint8_t* const* data, + const int* strides, +--- libyuv/source/convert_to_argb.cc ++++ libyuv/source/convert_to_argb.cc +@@ -23,6 +23,9 @@ + extern "C" { + #endif + ++extern void *libyuv_malloc(unsigned long size); ++extern void libyuv_free(void *ptr); ++ + // Convert camera sample to ARGB with cropping, rotation and vertical flip. + // src_width is used for source stride computation + // src_height is used to compute location of planes, and indicate inversion +@@ -76,7 +79,7 @@ + + if (need_buf) { + int argb_size = crop_width * 4 * abs_crop_height; +- rotate_buffer = (uint8_t*)malloc(argb_size); /* NOLINT */ ++ rotate_buffer = (uint8_t*)libyuv_malloc(argb_size); /* NOLINT */ + if (!rotate_buffer) { + return 1; // Out of memory runtime error. + } +@@ -366,7 +369,7 @@ + r = ARGBRotate(dst_argb, dst_stride_argb, dest_argb, dest_dst_stride_argb, + crop_width, abs_crop_height, rotation); + } +- free(rotate_buffer); ++ libyuv_free(rotate_buffer); + } else if (rotation) { + src = sample + (src_width * crop_y + crop_x) * 4; + r = ARGBRotate(src, src_width * 4, dst_argb, dst_stride_argb, crop_width, +--- libyuv/source/convert_to_i420.cc ++++ libyuv/source/convert_to_i420.cc +@@ -19,6 +19,9 @@ + extern "C" { + #endif + ++extern void *libyuv_malloc(unsigned long size); ++extern void libyuv_free(void *ptr); ++ + // Convert camera sample to I420 with cropping, rotation and vertical flip. + // src_width is used for source stride computation + // src_height is used to compute location of planes, and indicate inversion +@@ -76,7 +79,7 @@ + if (need_buf) { + int y_size = crop_width * abs_crop_height; + int uv_size = ((crop_width + 1) / 2) * ((abs_crop_height + 1) / 2); +- rotate_buffer = (uint8_t*)malloc(y_size + uv_size * 2); /* NOLINT */ ++ rotate_buffer = (uint8_t*)libyuv_malloc(y_size + uv_size * 2); /* NOLINT */ + if (!rotate_buffer) { + return 1; // Out of memory runtime error. + } +@@ -260,7 +263,7 @@ + tmp_v, tmp_v_stride, crop_width, abs_crop_height, + rotation); + } +- free(rotate_buffer); ++ libyuv_free(rotate_buffer); + } + + return r; +--- libyuv/source/scale_argb.cc ++++ libyuv/source/scale_argb.cc +@@ -23,6 +23,9 @@ + extern "C" { + #endif + ++extern void *libyuv_malloc(unsigned long size); ++extern void libyuv_free(void *ptr); ++ + static __inline int Abs(int v) { + return v >= 0 ? v : -v; + } +@@ -1071,7 +1074,7 @@ + int clip_width, + int clip_height, + enum FilterMode filtering) { +- uint8_t* argb_buffer = (uint8_t*)malloc(src_width * src_height * 4); ++ uint8_t* argb_buffer = (uint8_t*)libyuv_malloc(src_width * src_height * 4); + int r; + (void)src_fourcc; // TODO(fbarchard): implement and/or assert. + (void)dst_fourcc; +@@ -1081,7 +1084,7 @@ + r = ARGBScaleClip(argb_buffer, src_width * 4, src_width, src_height, dst_argb, + dst_stride_argb, dst_width, dst_height, clip_x, clip_y, + clip_width, clip_height, filtering); +- free(argb_buffer); ++ libyuv_free(argb_buffer); + return r; + }