From dafe48d556e7d6761500fec59bbdc2492a04b91b Mon Sep 17 00:00:00 2001 From: graysky Date: Wed, 19 Jun 2024 16:51:28 -0400 Subject: [PATCH] alarm/vlc-rpi to 3.0.21-1 Had to disable taglib support due to build breakage --- alarm/vlc-rpi/0002-libplacebo-5.patch | 108 - ...i_1.patch => 0002-test-3.0.21-rpi_2.patch} | 3864 ++++++++++++----- alarm/vlc-rpi/PKGBUILD | 161 +- 3 files changed, 2900 insertions(+), 1233 deletions(-) delete mode 100644 alarm/vlc-rpi/0002-libplacebo-5.patch rename alarm/vlc-rpi/{0003-test-3.0.20-rpi_1.patch => 0002-test-3.0.21-rpi_2.patch} (92%) diff --git a/alarm/vlc-rpi/0002-libplacebo-5.patch b/alarm/vlc-rpi/0002-libplacebo-5.patch deleted file mode 100644 index 006e900be..000000000 --- a/alarm/vlc-rpi/0002-libplacebo-5.patch +++ /dev/null @@ -1,108 +0,0 @@ -patch vaguely ported from: https://code.videolan.org/videolan/vlc/-/merge_requests/2233 - -diff --git a/modules/video_output/opengl/converter.h b/modules/video_output/opengl/converter.h -index 7000e1f..49fa667 100644 ---- a/modules/video_output/opengl/converter.h -+++ b/modules/video_output/opengl/converter.h -@@ -26,6 +26,9 @@ - #include - #include - -+#include -+#include -+ - /* if USE_OPENGL_ES2 is defined, OpenGL ES version 2 will be used, otherwise - * normal OpenGL will be used */ - #ifdef __APPLE__ -@@ -253,10 +256,6 @@ static inline bool HasExtension(const char *apis, const char *api) - return false; - } - --struct pl_context; --struct pl_shader; --struct pl_shader_res; -- - /* - * Structure that is filled by "glhw converter" module probe function - * The implementation should initialize every members of the struct that are -@@ -273,7 +272,7 @@ struct opengl_tex_converter_t - vlc_gl_t *gl; - - /* libplacebo context, created by the caller (optional) */ -- struct pl_context *pl_ctx; -+ pl_log pl_log; - - /* Function pointers to OpenGL functions, set by the caller */ - const opengl_vtable_t *vt; -@@ -337,7 +336,7 @@ struct opengl_tex_converter_t - bool yuv_color; - GLfloat yuv_coefficients[16]; - -- struct pl_shader *pl_sh; -+ pl_shader pl_sh; - const struct pl_shader_res *pl_sh_res; - - /* Private context */ -diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c -index ecf7226..29f4148 100644 ---- a/modules/video_output/opengl/fragment_shaders.c -+++ b/modules/video_output/opengl/fragment_shaders.c -@@ -611,7 +611,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target, - - #ifdef HAVE_LIBPLACEBO - if (tc->pl_sh) { -- struct pl_shader *sh = tc->pl_sh; -+ pl_shader sh = tc->pl_sh; - struct pl_color_map_params color_params = pl_color_map_default_params; - color_params.intent = var_InheritInteger(tc->gl, "rendering-intent"); - color_params.tone_mapping_algo = var_InheritInteger(tc->gl, "tone-mapping"); -@@ -634,7 +634,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target, - pl_color_space_from_video_format(&tc->fmt), - dst_space, NULL, false); - -- struct pl_shader_obj *dither_state = NULL; -+ pl_shader_obj dither_state = NULL; - int method = var_InheritInteger(tc->gl, "dither-algo"); - if (method >= 0) { - -diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c -index 13d65e0..1ee99af 100644 ---- a/modules/video_output/opengl/vout_helper.c -+++ b/modules/video_output/opengl/vout_helper.c -@@ -570,8 +570,7 @@ opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm) - - #ifdef HAVE_LIBPLACEBO - FREENULL(tc->uloc.pl_vars); -- if (tc->pl_ctx) -- pl_context_destroy(&tc->pl_ctx); -+ pl_log_destroy(&tc->pl_log); - #endif - - vlc_object_release(tc); -@@ -622,21 +621,21 @@ opengl_init_program(vout_display_opengl_t *vgl, struct prgm *prgm, - // create the main libplacebo context - if (!subpics) - { -- tc->pl_ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) { -+ tc->pl_log = pl_log_create(PL_API_VER, &(struct pl_log_params) { - .log_cb = log_cb, - .log_priv = tc, - .log_level = PL_LOG_INFO, - }); -- if (tc->pl_ctx) { -+ if (tc->pl_log) { - # if PL_API_VER >= 20 -- tc->pl_sh = pl_shader_alloc(tc->pl_ctx, &(struct pl_shader_params) { -+ tc->pl_sh = pl_shader_alloc(tc->pl_log, &(struct pl_shader_params) { - .glsl.version = tc->glsl_version, - .glsl.gles = tc->is_gles, - }); - # elif PL_API_VER >= 6 -- tc->pl_sh = pl_shader_alloc(tc->pl_ctx, NULL, 0); -+ tc->pl_sh = pl_shader_alloc(tc->pl_log, NULL, 0); - # else -- tc->pl_sh = pl_shader_alloc(tc->pl_ctx, NULL, 0, 0); -+ tc->pl_sh = pl_shader_alloc(tc->pl_log, NULL, 0, 0); - # endif - } - } diff --git a/alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch b/alarm/vlc-rpi/0002-test-3.0.21-rpi_2.patch similarity index 92% rename from alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch rename to alarm/vlc-rpi/0002-test-3.0.21-rpi_2.patch index 9339833fd..748472f0a 100644 --- a/alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch +++ b/alarm/vlc-rpi/0002-test-3.0.21-rpi_2.patch @@ -15,10 +15,10 @@ index 72e0eee428..6d92b95990 100644 /***************************************************************************** diff --git a/configure.ac b/configure.ac -index 69beb77ac0..4791877325 100644 +index 8fa2a87595..1f41d1ebcf 100644 --- a/configure.ac +++ b/configure.ac -@@ -3083,6 +3083,21 @@ AS_IF([test "${have_gl}" = "yes"], [ +@@ -3108,6 +3108,21 @@ AS_IF([test "${have_gl}" = "yes"], [ dnl OpenGL ES 2: depends on EGL 1.1 PKG_ENABLE_MODULES_VLC([GLES2], [], [glesv2], [OpenGL ES v2 support], [disabled]) @@ -40,7 +40,7 @@ index 69beb77ac0..4791877325 100644 dnl dnl Xlib dnl -@@ -3182,6 +3197,7 @@ AC_ARG_ENABLE(wayland, +@@ -3207,6 +3222,7 @@ AC_ARG_ENABLE(wayland, [AS_HELP_STRING([--enable-wayland], [Incomplete Wayland support (default disabled)])]) have_wayland="no" have_wayland_egl="no" @@ -48,7 +48,7 @@ index 69beb77ac0..4791877325 100644 AS_IF([test "${enable_wayland}" = "yes"], [ PKG_CHECK_MODULES([WAYLAND_CLIENT], [wayland-client >= 1.5.91], [ -@@ -3189,6 +3205,13 @@ AS_IF([test "${enable_wayland}" = "yes"], [ +@@ -3214,6 +3230,13 @@ AS_IF([test "${enable_wayland}" = "yes"], [ PKG_CHECK_EXISTS([wayland-protocols >= 1.4], [ WAYLAND_PROTOCOLS="$(${PKG_CONFIG} wayland-protocols --variable pkgdatadir)" AC_MSG_RESULT([${WAYLAND_PROTOCOLS}]) @@ -62,7 +62,7 @@ index 69beb77ac0..4791877325 100644 ], [ AC_MSG_RESULT([not found]) AC_MSG_ERROR([$(${PKG_CONFIG} --print-errors 'wayland-protocols >= 1.4')]) -@@ -3222,6 +3245,7 @@ AC_SUBST([WAYLAND_PROTOCOLS]) +@@ -3247,6 +3270,7 @@ AC_SUBST([WAYLAND_PROTOCOLS]) AC_SUBST([WAYLAND_SCANNER]) AM_CONDITIONAL([HAVE_WAYLAND], [test "${have_wayland}" = "yes"]) AM_CONDITIONAL([HAVE_WAYLAND_EGL], [test "${have_wayland_egl}" = "yes"]) @@ -70,7 +70,7 @@ index 69beb77ac0..4791877325 100644 dnl -@@ -3447,20 +3471,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have_kva}" = "yes"]) +@@ -3473,20 +3497,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have_kva}" = "yes"]) dnl dnl MMAL plugin dnl @@ -98,7 +98,7 @@ index 69beb77ac0..4791877325 100644 AS_IF([test "${enable_mmal}" = "yes"], [ AC_MSG_ERROR([Cannot find bcm library...]) ], [ AC_MSG_WARN([Cannot find bcm library...]) ]) -@@ -3472,6 +3500,7 @@ if test "${enable_mmal}" != "no"; then +@@ -3498,6 +3526,7 @@ if test "${enable_mmal}" != "no"; then VLC_RESTORE_FLAGS fi AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) @@ -663,10 +663,10 @@ index 6d9465fdae..5d46e5e95f 100644 packetizer/av1_obu.c packetizer/av1_obu.h packetizer/av1.h if ENABLE_SOUT diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c -index d0780e9a17..13bf0f921a 100644 +index d0780e9a17..d8d3ed3657 100644 --- a/modules/codec/avcodec/avcodec.c +++ b/modules/codec/avcodec/avcodec.c -@@ -252,28 +252,51 @@ vlc_module_begin () +@@ -252,17 +252,41 @@ vlc_module_begin () #endif vlc_module_end () @@ -698,33 +698,33 @@ index d0780e9a17..13bf0f921a 100644 + + // If named decoder do not attempt hw override - wait for non-hw pass + if( hw != 0 && psz_decoder != NULL ) -+ return NULL; ++ goto fail_free_psz_decoder; /* *** determine codec type *** */ if( !GetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, &i_codec_id, &psz_namecodec ) ) - return NULL; - -+ if( hw != 0 && (hw_dec_name = hw_v4l2m2m_dec_str(i_codec_id)) == NULL ) -+ return NULL; +- return NULL; ++ goto fail_free_psz_decoder; + ++ if( hw != 0 && (hw_dec_name = hw_v4l2m2m_dec_str(i_codec_id)) == NULL ) ++ goto fail_free_psz_decoder; + msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT ); - /* Initialization must be done before avcodec_find_decoder() */ +@@ -270,7 +294,6 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, vlc_init_avcodec(VLC_OBJECT(p_dec)); /* *** ask ffmpeg for a decoder *** */ - char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" ); if( psz_decoder != NULL ) { -- p_codec = avcodec_find_decoder_by_name( psz_decoder ); -+ p_codec = avcodec_find_decoder_by_name(psz_decoder); - if( !p_codec ) - msg_Err( p_dec, "Decoder `%s' not found", psz_decoder ); - else if( p_codec->id != i_codec_id ) -@@ -284,7 +307,9 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, + p_codec = avcodec_find_decoder_by_name( psz_decoder ); +@@ -282,9 +305,11 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, + psz_decoder, (char*)&p_dec->fmt_in.i_codec ); + p_codec = NULL; } - free( psz_decoder ); +- free( psz_decoder ); ++ free( (char*)psz_decoder ); } - if( !p_codec ) + if( hw_dec_name != NULL ) @@ -733,19 +733,23 @@ index d0780e9a17..13bf0f921a 100644 p_codec = avcodec_find_decoder( i_codec_id ); if( !p_codec ) { -@@ -304,6 +329,12 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, +@@ -302,6 +327,16 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, + avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" ); + avctx->opaque = p_dec; return avctx; - } - ++ ++fail_free_psz_decoder: ++ free((char*)psz_decoder); ++ return NULL; ++} ++ +AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, + const AVCodec **restrict codecp ) +{ + return ffmpeg_AllocContextHw(p_dec, codecp, 0); -+} -+ + } + /***************************************************************************** - * ffmpeg_OpenCodec: - *****************************************************************************/ diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h index c14170ebb8..7ee1463463 100644 --- a/modules/codec/avcodec/avcodec.h @@ -758,6 +762,21 @@ index c14170ebb8..7ee1463463 100644 AVCodecContext *ffmpeg_AllocContext( decoder_t *, const AVCodec ** ); int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *, const AVCodec * ); +diff --git a/modules/codec/avcodec/avcommon.h b/modules/codec/avcodec/avcommon.h +index ff5dba06c9..9b3f045c47 100644 +--- a/modules/codec/avcodec/avcommon.h ++++ b/modules/codec/avcodec/avcommon.h +@@ -84,7 +84,10 @@ static inline void vlc_init_avutil(vlc_object_t *obj) + break; + case VLC_MSG_DBG+1: + level = AV_LOG_DEBUG; ++ break; + default: ++ if (verbose + VLC_MSG_ERR >= VLC_MSG_DBG+2) ++ level = AV_LOG_TRACE; + break; + } + } diff --git a/modules/codec/avcodec/drm_pic.c b/modules/codec/avcodec/drm_pic.c new file mode 100644 index 0000000000..8319c1c210 @@ -908,7 +927,7 @@ index 0feb03b974..70956eb885 100644 #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) case AV_PIX_FMT_D3D11VA_VLD: diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c -index 8c892dd3f4..eb8a8956db 100644 +index 8c892dd3f4..88456f9e48 100644 --- a/modules/codec/avcodec/video.c +++ b/modules/codec/avcodec/video.c @@ -29,6 +29,8 @@ @@ -1078,7 +1097,17 @@ index 8c892dd3f4..eb8a8956db 100644 } static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, -@@ -686,6 +759,10 @@ static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, +@@ -652,7 +725,8 @@ static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, + return VLC_EGENERIC; + } + const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(hwfmt); +- msg_Dbg(p_dec, "trying format %s", dsc ? dsc->name : "unknown"); ++ const AVPixFmtDescriptor *dsc_sw = av_pix_fmt_desc_get(swfmt); ++ msg_Dbg(p_dec, "trying format %s:%s", dsc ? dsc->name : "unknown", dsc_sw ? dsc_sw->name : "unknown"); + if (lavc_UpdateVideoFormat(p_dec, p_context, hwfmt, swfmt)) + return VLC_EGENERIC; /* Unsupported brand of hardware acceleration */ + +@@ -686,6 +760,10 @@ static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, static const enum PixelFormat hwfmts[] = { @@ -1089,7 +1118,7 @@ index 8c892dd3f4..eb8a8956db 100644 #ifdef _WIN32 #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) AV_PIX_FMT_D3D11VA_VLD, -@@ -695,6 +772,7 @@ static const enum PixelFormat hwfmts[] = +@@ -695,6 +773,7 @@ static const enum PixelFormat hwfmts[] = AV_PIX_FMT_VAAPI, #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0)) AV_PIX_FMT_VDPAU, @@ -1097,7 +1126,7 @@ index 8c892dd3f4..eb8a8956db 100644 #endif AV_PIX_FMT_NONE, }; -@@ -813,11 +891,11 @@ failed: +@@ -813,11 +892,11 @@ failed: * the ffmpeg codec will be opened, some memory allocated. The vout is not yet * opened (done after the first decoded frame). *****************************************************************************/ @@ -1111,7 +1140,7 @@ index 8c892dd3f4..eb8a8956db 100644 if( p_context == NULL ) return VLC_EGENERIC; -@@ -839,6 +917,27 @@ int InitVideoDec( vlc_object_t *obj ) +@@ -839,6 +918,27 @@ int InitVideoDec( vlc_object_t *obj ) return InitVideoDecCommon( p_dec ); } @@ -1139,7 +1168,7 @@ index 8c892dd3f4..eb8a8956db 100644 /***************************************************************************** * Flush: *****************************************************************************/ -@@ -848,6 +947,8 @@ static void Flush( decoder_t *p_dec ) +@@ -848,6 +948,8 @@ static void Flush( decoder_t *p_dec ) AVCodecContext *p_context = p_sys->p_context; date_Set(&p_sys->pts, VLC_TICK_INVALID); /* To make sure we recover properly */ @@ -1148,7 +1177,7 @@ index 8c892dd3f4..eb8a8956db 100644 p_sys->i_late_frames = 0; p_sys->b_draining = false; cc_Flush( &p_sys->cc ); -@@ -875,6 +976,8 @@ static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) +@@ -875,6 +977,8 @@ static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { date_Set( &p_sys->pts, VLC_TICK_INVALID ); /* To make sure we recover properly */ @@ -1157,7 +1186,7 @@ index 8c892dd3f4..eb8a8956db 100644 cc_Flush( &p_sys->cc ); p_sys->i_late_frames = 0; -@@ -1215,6 +1318,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1215,6 +1319,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error } if( b_has_data ) { @@ -1168,7 +1197,7 @@ index 8c892dd3f4..eb8a8956db 100644 pkt->data = p_block->p_buffer; pkt->size = p_block->i_buffer; pkt->pts = p_block->i_pts > VLC_TICK_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; -@@ -1321,6 +1428,17 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1321,6 +1429,17 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error if( i_pts == AV_NOPTS_VALUE ) i_pts = date_Get( &p_sys->pts ); @@ -1186,7 +1215,7 @@ index 8c892dd3f4..eb8a8956db 100644 /* Interpolate the next PTS */ if( i_pts > VLC_TICK_INVALID ) date_Set( &p_sys->pts, i_pts ); -@@ -1373,9 +1491,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1373,9 +1492,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error { /* When direct rendering is not used, get_format() and get_buffer() * might not be called. The output video format must be set here * then picture buffer can be allocated. */ @@ -1199,7 +1228,7 @@ index 8c892dd3f4..eb8a8956db 100644 p_pic = decoder_NewPicture(p_dec); if( !p_pic ) -@@ -1768,6 +1887,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, +@@ -1768,6 +1888,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, } swfmt = defaultfmt; } @@ -1207,7 +1236,7 @@ index 8c892dd3f4..eb8a8956db 100644 if (p_sys->pix_fmt == AV_PIX_FMT_NONE) goto no_reuse; -@@ -1821,7 +1941,10 @@ no_reuse: +@@ -1821,7 +1942,10 @@ no_reuse: p_sys->level = p_context->level; if (!can_hwaccel) @@ -1218,8 +1247,48 @@ index 8c892dd3f4..eb8a8956db 100644 #if (LIBAVCODEC_VERSION_MICRO >= 100) \ && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101)) +@@ -1840,11 +1964,23 @@ no_reuse: + for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ ) + { + enum PixelFormat hwfmt = AV_PIX_FMT_NONE; ++ enum PixelFormat defsw = swfmt; ++ + for( size_t j = 0; hwfmt == AV_PIX_FMT_NONE && pi_fmt[j] != AV_PIX_FMT_NONE; j++ ) + if( hwfmts[i] == pi_fmt[j] ) + hwfmt = hwfmts[i]; + +- if (ffmpeg_OpenVa(p_dec, p_context, hwfmt, swfmt, src_desc, &p_sys->sem_mt) != VLC_SUCCESS) ++#if OPT_RPI ++ if (hwfmt == AV_PIX_FMT_DRM_PRIME && p_context->codec_id == AV_CODEC_ID_HEVC) ++ { ++ if (swfmt == AV_PIX_FMT_P010) ++ defsw = AV_PIX_FMT_RPI4_10; ++ if (swfmt == AV_PIX_FMT_YUV420P) ++ defsw = AV_PIX_FMT_RPI4_8; ++ } ++#endif ++ ++ if (ffmpeg_OpenVa(p_dec, p_context, hwfmt, defsw, src_desc, &p_sys->sem_mt) != VLC_SUCCESS) + continue; + + post_mt(p_sys); +diff --git a/modules/demux/adaptive/http/ConnectionParams.cpp b/modules/demux/adaptive/http/ConnectionParams.cpp +index 6e3f221b66..a60239d16d 100644 +--- a/modules/demux/adaptive/http/ConnectionParams.cpp ++++ b/modules/demux/adaptive/http/ConnectionParams.cpp +@@ -70,8 +70,8 @@ void ConnectionParams::setPath(const std::string &path_) + if(!hostname.empty()) + { + os << hostname; +- if( (port != 80 && scheme != "http") || +- (port != 443 && scheme != "https") ) ++ if( (port != 80 && scheme == "http") || ++ (port != 443 && scheme == "https") ) + os << ":" << port; + } + os << path; diff --git a/modules/gui/qt/components/controller.cpp b/modules/gui/qt/components/controller.cpp -index 3adfe67c0d..b93a83af6b 100644 +index 3adfe67c0d..96d18a7289 100644 --- a/modules/gui/qt/components/controller.cpp +++ b/modules/gui/qt/components/controller.cpp @@ -812,6 +812,17 @@ FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i, QWi @@ -1240,42 +1309,6 @@ index 3adfe67c0d..b93a83af6b 100644 setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint ); setAttribute( Qt::WA_ShowWithoutActivating ); setMinimumWidth( FSC_WIDTH ); -@@ -879,8 +890,11 @@ FullscreenControllerWidget::~FullscreenControllerWidget() - - void FullscreenControllerWidget::restoreFSC() - { -+ msg_Info(p_intf, "%s", __func__); - if( !isWideFSC ) - { -+ msg_Info(p_intf, "%s: 1", __func__); -+ - /* Restore half-bar and re-centre if needed */ - setMinimumWidth( FSC_WIDTH ); - adjustSize(); -@@ -903,6 +917,7 @@ void FullscreenControllerWidget::restoreFSC() - if( currentRes == screenRes && - currentRes.contains( previousPosition, true ) ) - { -+ msg_Info(p_intf, "%s: prev: %d, %d", __func__, previousPosition.x(), previousPosition.y()); - /* Restore to the last known position */ - move( previousPosition ); - } -@@ -917,6 +932,7 @@ void FullscreenControllerWidget::restoreFSC() - } - else - { -+ msg_Info(p_intf, "%s: 1", __func__); - /* Dock at the bottom of the screen */ - updateFullwidthGeometry( targetScreen() ); - } -@@ -929,6 +945,7 @@ void FullscreenControllerWidget::centerFSC( int number ) - /* screen has changed, calculate new position */ - QPoint pos = QPoint( currentRes.x() + (currentRes.width() / 2) - (width() / 2), - currentRes.y() + currentRes.height() - height()); -+ msg_Info(p_intf, "%s: move to %d, %d", __func__, pos.x(), pos.y()); - move( pos ); - } - diff --git a/modules/gui/qt/components/interface_widgets.cpp b/modules/gui/qt/components/interface_widgets.cpp index 4a0a0dae12..9ee73a3eaa 100644 --- a/modules/gui/qt/components/interface_widgets.cpp @@ -1392,7 +1425,7 @@ index 583f2d1f50..e949e65b1a 100644 signals: void sizeChanged( int, int ); diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp -index cefc75830f..3493db6958 100644 +index cefc75830f..297feb53bd 100644 --- a/modules/gui/qt/qt.cpp +++ b/modules/gui/qt/qt.cpp @@ -31,6 +31,7 @@ @@ -1424,8 +1457,8 @@ index cefc75830f..3493db6958 100644 + { + do_disable = true; + msg_Warn(p_intf, "devicePixelRatio (%g) > 2.1: assuming broken", dpr); -+ app.quit(); + } ++ app.quit(); + } + if( do_disable ) + { @@ -2052,10 +2085,10 @@ index 0000000000..b20eaf700e +vlc_module_end() diff --git a/modules/hw/drm/drm_gl_conv.c b/modules/hw/drm/drm_gl_conv.c new file mode 100644 -index 0000000000..b61b55f4e6 +index 0000000000..157d4a9c77 --- /dev/null +++ b/modules/hw/drm/drm_gl_conv.c -@@ -0,0 +1,367 @@ +@@ -0,0 +1,425 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif @@ -2180,6 +2213,64 @@ index 0000000000..b61b55f4e6 + *a++ = EGL_LINUX_DRM_FOURCC_EXT; + *a++ = desc->layers[0].format; + ++ if (vlc_fourcc_IsYUV(pic->format.i_chroma)) ++ { ++ *a++ = EGL_SAMPLE_RANGE_HINT_EXT; ++ *a++ = pic->format.b_color_range_full ? EGL_YUV_FULL_RANGE_EXT : EGL_YUV_NARROW_RANGE_EXT; ++ ++ switch (pic->format.chroma_location) ++ { ++ case CHROMA_LOCATION_LEFT: ++ *a++ = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_EXT; ++ *a++ = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_5_EXT; ++ break; ++ case CHROMA_LOCATION_CENTER: ++ *a++ = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_5_EXT; ++ *a++ = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_5_EXT; ++ break; ++ case CHROMA_LOCATION_TOP_LEFT: ++ *a++ = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_EXT; ++ *a++ = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_EXT; ++ break; ++ case CHROMA_LOCATION_TOP_CENTER: ++ *a++ = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_5_EXT; ++ *a++ = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT; ++ *a++ = EGL_YUV_CHROMA_SITING_0_EXT; ++ break; ++ case CHROMA_LOCATION_BOTTOM_LEFT: ++ case CHROMA_LOCATION_BOTTOM_CENTER: ++ case CHROMA_LOCATION_UNDEF: ++ default: ++ break; ++ } ++ ++ switch (pic->format.space) ++ { ++ case COLOR_SPACE_BT2020: ++ *a++ = EGL_YUV_COLOR_SPACE_HINT_EXT; ++ *a++ = EGL_ITU_REC2020_EXT; ++ break; ++ case COLOR_SPACE_BT601: ++ *a++ = EGL_YUV_COLOR_SPACE_HINT_EXT; ++ *a++ = EGL_ITU_REC601_EXT; ++ break; ++ case COLOR_SPACE_BT709: ++ *a++ = EGL_YUV_COLOR_SPACE_HINT_EXT; ++ *a++ = EGL_ITU_REC709_EXT; ++ break; ++ case COLOR_SPACE_UNDEF: ++ default: ++ break; ++ } ++ } ++ + const EGLint * ext = plane_exts; + + for (int i = 0; i < desc->nb_layers; ++i) @@ -2196,7 +2287,7 @@ index 0000000000..b61b55f4e6 + *a++ = plane->offset; + *a++ = *ext++; // PITCH + *a++ = plane->pitch; -+ if (!obj->format_modifier || obj->format_modifier == DRM_FORMAT_MOD_INVALID) ++ if (obj->format_modifier == DRM_FORMAT_MOD_INVALID) + { + ext += 2; + } @@ -15891,10 +15982,10 @@ index 76188a457c..b9fef71e3c 100644 + diff --git a/modules/hw/mmal/xsplitter.c b/modules/hw/mmal/xsplitter.c new file mode 100644 -index 0000000000..65fcc6b41e +index 0000000000..68771421ed --- /dev/null +++ b/modules/hw/mmal/xsplitter.c -@@ -0,0 +1,662 @@ +@@ -0,0 +1,669 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif @@ -16165,11 +16256,15 @@ index 0000000000..65fcc6b41e + * pictures. + * The vout display module keeps the ownership of the pool and can + * destroy it only when closing or on invalid pictures control. ++ * ++ * If the X pool doesn't have pictures invalid then it isn't safe ++ * to swap pools so always use that one (MMAL & DRM can cope with ++ * most stuff) + */ +static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t * const x_vd = sys->cur_desc->vout; ++ vout_display_t * const x_vd = vd->info.has_pictures_invalid ? sys->cur_desc->vout : sys->x_desc.vout; +#if TRACE_ALL + char buf0[5]; + char buf1[5]; @@ -16444,7 +16539,7 @@ index 0000000000..65fcc6b41e + .is_slow = false, + .has_double_click = false, + .needs_hide_mouse = false, -+ .has_pictures_invalid = true, ++ .has_pictures_invalid = false, + .subpicture_chromas = NULL + }; + @@ -16469,11 +16564,15 @@ index 0000000000..65fcc6b41e + { + msg_Dbg(vd, "Opengles2 output found"); + } -+ else ++ else if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0) + { + sys->x_desc.max_pels = MAX_MMAL_PELS; -+ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0) -+ msg_Dbg(vd, "X11 XCB output found"); ++ msg_Dbg(vd, "X11 XCB output found"); ++ } ++ else ++ { ++ msg_Dbg(vd, "No X output found"); ++ goto fail; + } + + if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0) @@ -16494,9 +16593,8 @@ index 0000000000..65fcc6b41e + goto fail; + } + -+ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) { ++ if (sys->mmal_desc.vout == NULL) { + vd->info = sys->cur_desc->vout->info; -+ vd->info.has_pictures_invalid = true; // Should make this unwanted + } + else { + // We have both - construct a combination @@ -16574,10 +16672,10 @@ index 2709425255..5ac384e633 100644 es_format_Clean( &fmt_mid ); if( i_ret == VLC_SUCCESS ) diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am -index ae48c8e062..a099f4d6b6 100644 +index 8f2cea4c67..210c595ab2 100644 --- a/modules/video_output/Makefile.am +++ b/modules/video_output/Makefile.am -@@ -187,6 +187,29 @@ vout_LTLIBRARIES += libglx_plugin.la +@@ -187,6 +187,33 @@ vout_LTLIBRARIES += libglx_plugin.la endif endif @@ -16592,7 +16690,11 @@ index ae48c8e062..a099f4d6b6 100644 + video_output/drmu/drmu_xlease.c video_output/drmu/drmu_atomic.c \ + video_output/drmu/drmu_util.c video_output/drmu/drmu_util.h \ + video_output/drmu/drmu_output.c video_output/drmu/drmu_output.h \ ++ video_output/drmu/drmu_scan.c video_output/drmu/drmu_scan.h \ + video_output/drmu/drmu.h\ ++ video_output/drmu/drmu_dmabuf.c video_output/drmu/drmu_dmabuf.h \ ++ video_output/drmu/drmu_pool.c video_output/drmu/drmu_pool.h \ ++ video_output/drmu/drmu_math.c video_output/drmu/drmu_math.h \ + video_output/drmu/pollqueue.c video_output/drmu/pollqueue.h +libdrm_vout_plugin_la_CFLAGS = $(AM_CFLAGS) -pthread -I/usr/include/libdrm +libdrm_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -pthread @@ -16607,7 +16709,7 @@ index ae48c8e062..a099f4d6b6 100644 ### Wayland ### libwl_shm_plugin_la_SOURCES = video_output/wayland/shm.c -@@ -205,7 +228,52 @@ video_output/wayland/viewporter-client-protocol.h: \ +@@ -205,7 +232,52 @@ video_output/wayland/viewporter-client-protocol.h: \ video_output/wayland/viewporter-protocol.c: \ $(WAYLAND_PROTOCOLS)/stable/viewporter/viewporter.xml @@ -16661,7 +16763,7 @@ index ae48c8e062..a099f4d6b6 100644 libwl_shell_plugin_la_SOURCES = video_output/wayland/shell.c libwl_shell_plugin_la_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -@@ -244,6 +312,8 @@ libegl_wl_plugin_la_LIBADD = $(EGL_LIBS) $(WAYLAND_EGL_LIBS) +@@ -244,6 +316,8 @@ libegl_wl_plugin_la_LIBADD = $(EGL_LIBS) $(WAYLAND_EGL_LIBS) if HAVE_WAYLAND BUILT_SOURCES += $(nodist_libwl_shm_plugin_la_SOURCES) vout_LTLIBRARIES += libwl_shm_plugin.la @@ -16689,10 +16791,10 @@ index 3440401082..9297823267 100644 #endif diff --git a/modules/video_output/drmu/drm_vout.c b/modules/video_output/drmu/drm_vout.c new file mode 100644 -index 0000000000..fc3395e4b4 +index 0000000000..bd81fe4850 --- /dev/null +++ b/modules/video_output/drmu/drm_vout.c -@@ -0,0 +1,1058 @@ +@@ -0,0 +1,1425 @@ +/***************************************************************************** + * drm_vout.c: DRM based output device + ***************************************************************************** @@ -16725,8 +16827,12 @@ index 0000000000..fc3395e4b4 +#include + +#include "drmu.h" ++#include "drmu_dmabuf.h" ++#include "drmu_fmts.h" +#include "drmu_log.h" +#include "drmu_output.h" ++#include "drmu_pool.h" ++#include "drmu_scan.h" +#include "drmu_util.h" +#include "drmu_vlc.h" + @@ -16742,6 +16848,7 @@ index 0000000000..fc3395e4b4 +#include + +#define TRACE_ALL 0 ++#define PIC_POOL_FB 1 + +#define SUBPICS_MAX 4 + @@ -16784,6 +16891,10 @@ index 0000000000..fc3395e4b4 +#define DRM_VOUT_MODULE_TEXT N_("DRM module to use") +#define DRM_VOUT_MODULE_LONGTEXT N_("DRM module for Rpi fullscreen") + ++#define DRM_VOUT_POOL_DMABUF_NAME "drm-vout-pool-dmabuf" ++#define DRM_VOUT_POOL_DMABUF_TEXT N_("Use dmabufs for pic pool") ++#define DRM_VOUT_POOL_DMABUF_LONGTEXT N_("Use dmabufs for pic pool. Saves a frame copy on output but may use up limited dmabuf resource.") ++ + +typedef struct subpic_ent_s { + drmu_fb_t * fb; @@ -16815,14 +16926,26 @@ index 0000000000..fc3395e4b4 + video_transform_t video_transform; + video_transform_t dest_transform; + ++ bool pool_try_fb; ++ bool pool_is_fb; ++ bool output_simple; + uint32_t con_id; + int mode_id; + + picture_pool_t * vlc_pic_pool; +} vout_display_sys_t; + ++#define PIC_SYS_SIG VLC_FOURCC('D', 'R', 'M', 'U') ++ ++ ++// pic->p_sys when we are allocating our own pics ++struct picture_sys_t { ++ uint32_t sig; ++ drmu_fb_t * fb; ++}; ++ +static drmu_fb_t * -+copy_pic_to_fb(vout_display_t *vd, drmu_pool_t * const pool, picture_t * const src) ++copy_pic_to_fb(vout_display_t *const vd, drmu_pool_t *const pool, picture_t *const src) +{ + uint64_t mod; + const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format, &mod); @@ -16834,17 +16957,108 @@ index 0000000000..fc3395e4b4 + return NULL; + } + -+ fb = drmu_pool_fb_new_dumb(pool, src->format.i_width, src->format.i_height, drm_fmt); ++ fb = drmu_pool_fb_new(pool, src->format.i_width, src->format.i_height, drm_fmt, mod); + if (fb == NULL) { + msg_Warn(vd, "Failed alloc for copy_pic: %dx%d", src->format.i_width, src->format.i_height); + return NULL; + } + ++ drmu_fb_write_start(fb); + for (i = 0; i != src->i_planes; ++i) { + plane_t dst_plane; + dst_plane = drmu_fb_vlc_plane(fb, i); + plane_CopyPixels(&dst_plane, src->p + i); + } ++ drmu_fb_write_end(fb); ++ ++ drmu_fb_vlc_pic_set_metadata(fb, src); ++ ++ return fb; ++} ++ ++static void ++create_box(drmu_fb_t * const fb, const unsigned int layer_no) ++{ ++ const drmu_fmt_info_t *const f = drmu_fb_format_info_get(fb); ++ unsigned int hdiv = drmu_fmt_info_hdiv(f, layer_no); ++ unsigned int wdiv = drmu_fmt_info_wdiv(f, layer_no); ++ const unsigned int pby = (drmu_fmt_info_pixel_bits(f) + 7) / 8; ++ const uint32_t pitch_n = drmu_fb_pitch(fb, layer_no); ++ const drmu_rect_t crop = drmu_rect_shr16_rnd(drmu_fb_crop_frac(fb)); ++ const drmu_rect_t active = drmu_fb_active(fb); ++ ++ uint8_t * const p0 = drmu_fb_data(fb, layer_no); ++ uint8_t * p1 = p0; ++ uint8_t * p2; ++ ++ // Assumes RGB as currently no better idea (and it is RGB in the case we expect) ++ const unsigned int c = 0; ++ ++ // Top ++ p2 = p1 + pitch_n * (crop.y / hdiv) + (crop.x / wdiv) * pby; ++ if (p1 != p2) ++ memset(p1, c, p2 - p1); ++ if (active.w == crop.w) { ++ p1 = p2 + (crop.h / hdiv) * pitch_n; // We expect crop.x == 0 ++ } ++ else { ++ unsigned int i; ++ const unsigned int vis_pitch = (crop.w / wdiv) * pby; ++ for (i = 1; i < (crop.h / hdiv); ++i) { ++ p1 = p2 + vis_pitch; ++ p2 = p2 + pitch_n; ++ memset(p1, c, p2 - p1); ++ } ++ p1 = p2 + vis_pitch; ++ } ++ p2 = p0 + pitch_n * (active.h / hdiv); ++ if (p1 != p2) ++ memset(p1, c, p2 - p1); ++} ++ ++static drmu_fb_t * ++copy_pic_to_fixed_fb(vout_display_t * const vd, vout_display_sys_t * const sys, ++ drmu_pool_t *const pool, picture_t *const src) ++{ ++ uint64_t mod; ++ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format, &mod); ++ drmu_fb_t * fb; ++ int i; ++ ++ if (drm_fmt == 0 || mod != DRM_FORMAT_MOD_LINEAR) { ++ msg_Warn(vd, "Failed vlc->drm format for copy_pic: %s", drmu_log_fourcc(src->format.i_chroma)); ++ return NULL; ++ } ++ ++ fb = drmu_pool_fb_new(pool, sys->display_rect.width, sys->display_rect.height, drm_fmt, mod); ++ if (fb == NULL) { ++ msg_Warn(vd, "Failed alloc for copy_pic_fixed: %dx%d", sys->display_rect.width, sys->display_rect.height); ++ return NULL; ++ } ++ ++ drmu_fb_crop_frac_set(fb, drmu_rect_shl16(drmu_rect_vlc_place(&sys->dest_rect))); ++ ++ { ++ const drmu_fmt_info_t *const f = drmu_fb_format_info_get(fb); ++ const drmu_rect_t crop = drmu_rect_shr16_rnd(drmu_fb_crop_frac(fb)); ++ const unsigned int bypp = (drmu_fmt_info_pixel_bits(f) + 7) / 8; ++ ++ drmu_fb_write_start(fb); ++ for (i = 0; i != src->i_planes; ++i) { ++ // It would seem more logical to use src->format than to use vd->fmt ++ // for the source rect but src->fmt doesn't have offset_x/y set (bug?) ++ drmu_memcpy_rect(drmu_fb_data(fb, i), drmu_fb_pitch(fb, i), ++ drmu_rect_div_xy(crop, drmu_fmt_info_wdiv(f, i), drmu_fmt_info_hdiv(f, i)), ++ src->p[i].p_pixels, src->p[i].i_pitch, ++ drmu_rect_vlc_format_crop(&vd->fmt), ++ bypp); ++ create_box(fb, i); ++ } ++ drmu_fb_write_end(fb); ++ } ++ ++ // Reset crop for display after we've used it for copy ++ drmu_fb_crop_frac_set(fb, drmu_rect_shl16(drmu_fb_active(fb))); + + drmu_fb_vlc_pic_set_metadata(fb, src); + @@ -17010,21 +17224,19 @@ index 0000000000..fc3395e4b4 +} + +static void -+place_dest_rect(vout_display_t * const vd, ++place_dest_rect(vout_display_sys_t * const sys, + const vout_display_cfg_t * const cfg, + const video_format_t * fmt) +{ -+ vout_display_sys_t * const sys = vd->sys; + sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), + sys->display_rect, sys->dest_transform); +} + +static void -+place_spu_rect(vout_display_t * const vd, -+ const vout_display_cfg_t * const cfg, -+ const video_format_t * fmt) ++place_spu_rect(vout_display_sys_t * const sys, ++ const vout_display_cfg_t * const cfg, ++ const video_format_t * fmt) +{ -+ vout_display_sys_t * const sys = vd->sys; + static const vout_display_place_t r0 = {0}; + + sys->spu_rect = place_out(cfg, fmt, r0); @@ -17044,19 +17256,17 @@ index 0000000000..fc3395e4b4 +} + +static void -+place_rects(vout_display_t * const vd, -+ const vout_display_cfg_t * const cfg, -+ const video_format_t * fmt) ++place_rects(vout_display_sys_t * const sys, ++ const vout_display_cfg_t * const cfg, ++ const video_format_t * fmt) +{ -+ place_dest_rect(vd, cfg, fmt); -+ place_spu_rect(vd, cfg, fmt); ++ place_dest_rect(sys, cfg, fmt); ++ place_spu_rect(sys, cfg, fmt); +} + -+static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, -+ const video_format_t *fmt) ++static int configure_display(const vout_display_t *vd, vout_display_sys_t *const sys, ++ const vout_display_cfg_t *cfg, const video_format_t *fmt) +{ -+ vout_display_sys_t * const sys = vd->sys; -+ + if (!cfg && !fmt) + { + msg_Err(vd, "%s: Missing cfg & fmt", __func__); @@ -17071,7 +17281,7 @@ index 0000000000..fc3395e4b4 + + sys->video_transform = combine_vxf((video_transform_t)fmt->orientation, sys->display_transform); + -+ place_rects(vd, cfg, fmt); ++ place_rects(sys, cfg, fmt); + return 0; +} + @@ -17169,7 +17379,7 @@ index 0000000000..fc3395e4b4 + drmu_fb_unref(&dst->fb); + } + -+ r = drmu_rect_vlc_place(&sys->dest_rect); ++ r = sys->output_simple ? drmu_rect_vlc_place(&sys->display_rect): drmu_rect_vlc_place(&sys->dest_rect); + +#if 0 + { @@ -17208,7 +17418,14 @@ index 0000000000..fc3395e4b4 + } + else +#endif -+ { ++ ++ if (sys->pool_is_fb && pic->p_sys != NULL && pic->p_sys->sig == PIC_SYS_SIG) { ++ dfb = drmu_fb_ref(pic->p_sys->fb); ++ } ++ else if (sys->output_simple) { ++ dfb = copy_pic_to_fixed_fb(vd, sys, sys->pic_pool, pic); ++ } ++ else { + dfb = copy_pic_to_fb(vd, sys->pic_pool, pic); + } + @@ -17227,7 +17444,8 @@ index 0000000000..fc3395e4b4 + drmu_rect_shl16(drmu_rect_wh(vd->fmt.i_width, vd->fmt.i_height)), + drmu_rect_wh(vd->source.i_width, vd->source.i_height))); +#else -+ drmu_fb_crop_frac_set(dfb, drmu_rect_shl16(drmu_rect_vlc_format_crop(&vd->source))); ++ if (!sys->output_simple) ++ drmu_fb_crop_frac_set(dfb, drmu_rect_shl16(drmu_rect_vlc_format_crop(&vd->source))); +#endif + drmu_output_fb_info_set(sys->dout, dfb); + @@ -17288,6 +17506,67 @@ index 0000000000..fc3395e4b4 + return; +} + ++static void ++destroy_drmu_pic(picture_t * pic) ++{ ++ drmu_fb_unref(&pic->p_sys->fb); ++ free(pic->p_sys); ++ free(pic); ++} ++ ++static picture_t * ++alloc_drmu_pic(vout_display_t * const vd, drmu_pool_t *const pool) ++{ ++ const video_format_t * const fmt = &vd->fmt; ++ uint64_t mod; ++ const uint32_t drm_fmt = drmu_format_vlc_to_drm(fmt, &mod); ++ const drmu_fmt_info_t * fmti; ++ drmu_fb_t * fb; ++ unsigned int layers; ++ unsigned int i; ++ picture_t * pic; ++ picture_resource_t res = { ++ .p_sys = NULL, ++ .pf_destroy = destroy_drmu_pic, ++ }; ++ ++ if (drm_fmt == 0 || mod != DRM_FORMAT_MOD_LINEAR) { ++ msg_Warn(vd, "Failed vlc->drm format for copy_pic: %s", drmu_log_fourcc(fmt->i_chroma)); ++ return NULL; ++ } ++ ++ fb = drmu_pool_fb_new(pool, fmt->i_width, fmt->i_height, drm_fmt, mod); ++ if (fb == NULL) { ++ msg_Warn(vd, "Failed alloc for copy_pic: %dx%d", fmt->i_width, fmt->i_height); ++ return NULL; ++ } ++ ++ if ((res.p_sys = calloc(1, sizeof(*res.p_sys))) == NULL) ++ goto fail; ++ ++ res.p_sys->sig = PIC_SYS_SIG; ++ res.p_sys->fb = fb; ++ ++ fmti = drmu_fb_format_info_get(fb); ++ layers = drmu_fmt_info_plane_count(fmti); ++ ++ for (i = 0; i != layers; ++i) { ++ res.p[i].p_pixels = drmu_fb_data(fb, i); ++ res.p[i].i_lines = drmu_fb_height(fb) / drmu_fmt_info_hdiv(fmti, i); ++ res.p[i].i_pitch = drmu_fb_pitch(fb, i); ++ } ++ ++ if ((pic = picture_NewFromResource(fmt, &res)) == NULL) ++ goto fail; ++ ++ return pic; ++ ++fail: ++ drmu_fb_unref(&fb); ++ free(res.p_sys); ++ return NULL; ++} ++ +static void subpic_cache_flush(vout_display_sys_t * const sys) +{ + for (unsigned int i = 0; i != SUBPICS_MAX; ++i) { @@ -17310,28 +17589,89 @@ index 0000000000..fc3395e4b4 + } +} + ++static picture_pool_t * ++make_fb_pool(vout_display_t * const vd, vout_display_sys_t * const sys, const unsigned int count) ++{ ++ picture_t * pics[40]; ++ unsigned int pics_alloc; ++ picture_pool_t * pool; ++ ++ if (count > ARRAY_SIZE(pics)) ++ return NULL; ++ ++ for (pics_alloc = 0; pics_alloc != count; ++pics_alloc) { ++ if ((pics[pics_alloc] = alloc_drmu_pic(vd, sys->pic_pool)) == NULL) { ++ msg_Err(vd, "Failed to alloc pic pool entry %u", pics_alloc); ++ goto fail; ++ } ++ } ++ ++ if ((pool = picture_pool_New(pics_alloc, pics)) == NULL) { ++ msg_Err(vd, "Failed to alloc picture pool"); ++ goto fail; ++ } ++ ++ return pool; ++ ++fail: ++ while (pics_alloc != 0) ++ picture_Release(pics[--pics_alloc]); ++ return NULL; ++} ++ +// Actual picture pool for MMAL opaques is just a set of trivial containers +static picture_pool_t *vd_drm_pool(vout_display_t *vd, unsigned count) +{ + vout_display_sys_t * const sys = vd->sys; + -+#if TRACE_ALL -+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, -+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); -+#endif ++ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d, count=%d", __func__, ++ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, ++ vd->source.i_width, vd->source.i_height, count); + -+ if (sys->vlc_pic_pool == NULL) { -+ sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); ++ if (sys->vlc_pic_pool != NULL) { ++ msg_Dbg(vd, "Pool exists"); ++ return sys->vlc_pic_pool; + } ++ ++ if (sys->pool_try_fb && drmu_format_vlc_to_drm_prime(&vd->fmt, NULL) == 0) { ++ if ((sys->vlc_pic_pool = make_fb_pool(vd, sys, count)) != NULL) { ++ msg_Dbg(vd, "Pool allocated using dmabufs"); ++ return sys->vlc_pic_pool; ++ } ++ msg_Warn(vd, "Pool failed dmabuf allocation"); ++ } ++ ++ msg_Dbg(vd, "Pool allocation from main memory"); ++ sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); + return sys->vlc_pic_pool; +} + -+// Copy format from *fmtp into vd->fmt and make any necessary adjustments to -+// ensure display (tweak chroma) -+static void -+set_format(vout_display_t * const vd, vout_display_sys_t * const sys, const video_format_t *const fmtp) ++static const drmu_vlc_fmt_info_t * ++find_fmt_fallback(const vout_display_t * const vd, const vout_display_sys_t * const sys, const vlc_fourcc_t * fallback) +{ -+ const drmu_vlc_fmt_info_t * const fi = drmu_vlc_fmt_info_find_vlc(fmtp); ++ VLC_UNUSED(vd); ++ ++ for (; *fallback; ++fallback) { ++ const video_frame_format_t vf = {.i_chroma = *fallback}; ++ const drmu_vlc_fmt_info_t * fi; ++ ++ for (fi = drmu_vlc_fmt_info_find_vlc(&vf); ++ fi != NULL; ++ fi = drmu_vlc_fmt_info_find_vlc_next(&vf, fi)) ++ { ++ if (drmu_plane_format_check(sys->dp, drmu_vlc_fmt_info_drm_pixelformat(fi), drmu_vlc_fmt_info_drm_modifier(fi))) ++ return fi; ++ } ++ } ++ return NULL; ++} ++ ++ ++// Adjust *fmtp to fix format for display (tweak chroma) ++static int ++set_format(const vout_display_t * const vd, vout_display_sys_t * const sys, video_format_t *const fmtp) ++{ ++ const drmu_vlc_fmt_info_t * fi = drmu_vlc_fmt_info_find_vlc(fmtp); + const uint64_t drm_mod = drmu_vlc_fmt_info_drm_modifier(fi); + const uint32_t drm_fmt = drmu_vlc_fmt_info_drm_pixelformat(fi); + @@ -17339,12 +17679,10 @@ index 0000000000..fc3395e4b4 + drmu_log_fourcc(fmtp->i_chroma), drmu_log_fourcc(drm_fmt), drm_mod, + drmu_vlc_fmt_info_is_drmprime(fi)); + -+ vd->fmt = *fmtp; -+ +#if HAS_ZC_CMA + if (fmtp->i_chroma == VLC_CODEC_MMAL_OPAQUE) { + // Can't deal directly with opaque - but we can always convert it to ZC I420 -+ vd->fmt.i_chroma = VLC_CODEC_MMAL_ZC_I420; ++ fmtp->i_chroma = VLC_CODEC_MMAL_ZC_I420; + } + else +#endif @@ -17355,47 +17693,112 @@ index 0000000000..fc3395e4b4 + const vlc_fourcc_t *fallback = vlc_fourcc_IsYUV(fmtp->i_chroma) ? + vlc_fourcc_GetYUVFallback(fmtp->i_chroma) : + vlc_fourcc_GetRGBFallback(fmtp->i_chroma); ++ static const vlc_fourcc_t fallback2[] = { ++ VLC_CODEC_I420, ++ VLC_CODEC_RGB32, ++ 0 ++ }; + -+ // *** How should we check RGB fallbacks given we need masks too? -+ for (; *fallback; ++fallback) { -+ if (drmu_plane_format_check(sys->dp, drmu_format_vlc_chroma_to_drm(*fallback), 0)) -+ break; -+ } ++ if ((fi = find_fmt_fallback(vd, sys, fallback)) == NULL && ++ (fi = find_fmt_fallback(vd, sys, fallback2)) == NULL) ++ return VLC_EGENERIC; + -+ // no conversion - ask for something we know we can deal with -+ vd->fmt.i_chroma = *fallback ? *fallback : VLC_CODEC_I420; ++ fmtp->i_chroma = drmu_vlc_fmt_info_vlc_chroma(fi); ++ drmu_vlc_fmt_info_vlc_rgb_masks(fi, &fmtp->i_rmask, &fmtp->i_gmask, &fmtp->i_bmask); ++ ++ msg_Dbg(vd, "%s: Fallback %s/%x/%x/%x -> %s %"PRIx64, __func__, ++ drmu_log_fourcc(fmtp->i_chroma), ++ fmtp->i_rmask, fmtp->i_gmask, fmtp->i_bmask, ++ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(fi)), ++ drmu_vlc_fmt_info_drm_modifier(fi)); + } ++ return 0; +} + ++static void ++set_simple_format_size(video_format_t * const dst_fmt, const video_format_t * const src_fmt, const drmu_rect_t dst_rect) ++{ ++#if 0 ++ // Create a full pic with a centre cropping region ++ const drmu_rect_t fmt_crop = drmu_rect_vlc_format_crop(src_fmt); ++ const drmu_rect_t src_rect = drmu_rect_resize(drmu_rect_wh(src_fmt->i_width, src_fmt->i_height), dst_rect, fmt_crop); ++ const drmu_rect_t crop_rect = drmu_rect_resize(fmt_crop, dst_rect, fmt_crop); ++ ++ dst_fmt->i_width = src_rect.w; ++ dst_fmt->i_height = src_rect.h; ++ dst_fmt->i_visible_width = crop_rect.w; ++ dst_fmt->i_visible_height = crop_rect.h; ++ dst_fmt->i_x_offset = crop_rect.x; ++ dst_fmt->i_y_offset = crop_rect.y; ++#else ++ // Just give us the cropped bit ++ VLC_UNUSED(src_fmt); ++ dst_fmt->i_width = dst_rect.w; ++ dst_fmt->i_height = dst_rect.h; ++ dst_fmt->i_visible_width = dst_rect.w; ++ dst_fmt->i_visible_height = dst_rect.h; ++ dst_fmt->i_x_offset = 0; ++ dst_fmt->i_y_offset = 0; ++#endif ++} ++ ++// Updates sys but shouldn't touch vd ++// Sets fmt ++static int ++reconfigure_display(const vout_display_t * const vd, vout_display_sys_t * const sys, ++ const vout_display_cfg_t * const cfg, video_format_t * const fmt) ++{ ++ int ret; ++ *fmt = vd->source; ++ if ((ret = set_format(vd, sys, fmt)) != 0) ++ return ret; ++ configure_display(vd, sys, cfg, fmt); ++ if (sys->output_simple) ++ set_simple_format_size(fmt, &vd->source, drmu_rect_vlc_place(&sys->dest_rect)); ++ return 0; ++} + +static int vd_drm_control(vout_display_t *vd, int query, va_list args) +{ + vout_display_sys_t * const sys = vd->sys; ++ video_format_t fmt; + int ret = VLC_EGENERIC; ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: query=%d", __func__, query); ++#endif + + switch (query) { + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: -+ if (configure_display(vd, vd->cfg, &vd->source) >= 0) -+ ret = VLC_SUCCESS; ++ if ((ret = reconfigure_display(vd, sys, NULL, &fmt)) != 0) ++ break; ++ if (!video_format_IsSimilar(&vd->fmt, &fmt)) { ++ if (vd->info.has_pictures_invalid) ++ vout_display_SendEventPicturesInvalid(vd); ++ else ++ msg_Err(vd, "Wanted Pic Invalid but not allowed"); ++ } + break; + + case VOUT_DISPLAY_CHANGE_ZOOM: + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: + case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: -+ { -+ const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *); -+ -+ if (configure_display(vd, cfg, &vd->source) >= 0) -+ ret = VLC_SUCCESS; ++ if ((ret = reconfigure_display(vd, sys, va_arg(args, const vout_display_cfg_t *), &fmt)) != 0) ++ break; ++ if (!video_format_IsSimilar(&vd->fmt, &fmt)) { ++ if (vd->info.has_pictures_invalid) ++ vout_display_SendEventPicturesInvalid(vd); ++ else ++ msg_Err(vd, "Wanted Pic Invalid but not allowed"); ++ } + break; -+ } + + case VOUT_DISPLAY_RESET_PICTURES: -+ msg_Warn(vd, "Reset Pictures"); + kill_pool(sys); -+ set_format(vd, sys, &vd->source); -+ ret = VLC_SUCCESS; ++ ++ if ((ret = reconfigure_display(vd, sys, NULL, &fmt)) != 0) ++ break; ++ vd->fmt = fmt; + break; + + default: @@ -17413,8 +17816,8 @@ index 0000000000..fc3395e4b4 + + msg_Dbg(vd, "<<< %s", __func__); + -+ drmu_pool_delete(&sys->sub_fb_pool); -+ drmu_pool_delete(&sys->pic_pool); ++ drmu_pool_kill(&sys->sub_fb_pool); ++ drmu_pool_kill(&sys->pic_pool); + + for (i = 0; i != SUBPICS_MAX; ++i) + drmu_plane_unref(sys->subplanes + i); @@ -17423,7 +17826,7 @@ index 0000000000..fc3395e4b4 + + drmu_plane_unref(&sys->dp); + drmu_output_unref(&sys->dout); -+ drmu_env_unref(&sys->du); ++ drmu_env_kill(&sys->du); + + free(sys->subpic_chromas); + vd->info.subpicture_chromas = NULL; @@ -17493,23 +17896,34 @@ index 0000000000..fc3395e4b4 +} + +static int -+test_simple_plane_set(vout_display_t * const vd, vout_display_sys_t * const sys) ++test_simple_plane_set(vout_display_t * const vd, vout_display_sys_t * const sys, ++ const video_format_t * const fmt, ++ unsigned int w, unsigned int h, ++ const drmu_rect_t dst_rect) +{ + drmu_atomic_t *da = drmu_atomic_new(sys->du); + drmu_fb_t *fb; + int rv = -ENOMEM; ++ const drmu_vlc_fmt_info_t * const fi = drmu_vlc_fmt_info_find_vlc(fmt); ++ ++ if (fi == NULL) { ++ msg_Err(vd, "Can't find chroma format"); ++ goto fail; ++ } + + if (da == NULL) { + msg_Warn(vd, "Failed to alloc test atomic"); + goto fail; + } + -+ if ((fb = drmu_pool_fb_new_dumb(sys->sub_fb_pool, 128, 128, drmu_format_vlc_chroma_to_drm(sys->subpic_chromas[0]))) == NULL) { ++ if ((fb = drmu_pool_fb_new(sys->sub_fb_pool, w, h, ++ drmu_vlc_fmt_info_drm_pixelformat(fi), ++ drmu_vlc_fmt_info_drm_modifier(fi))) == NULL) { + msg_Warn(vd, "Failed to alloc test FB"); + goto fail; + } + -+ if ((rv = drmu_atomic_plane_add_fb(da, sys->subplanes[0], fb, drmu_rect_wh(128, 128))) != 0) { ++ if ((rv = drmu_atomic_plane_add_fb(da, sys->dp, fb, dst_rect)) != 0) { + msg_Warn(vd, "Failed to add test FB to atomic"); + goto fail; + } @@ -17529,12 +17943,14 @@ index 0000000000..fc3395e4b4 +{ + vout_display_t * const vd = (vout_display_t *)object; +// video_format_t * const fmtp = &vd->fmt; -+ const video_format_t *const fmtp = &vd->source; -+ const uint32_t src_chroma = fmtp->i_chroma; ++ video_format_t out_fmt = vd->source; ++ const video_format_t *const src_fmt = &vd->source; ++ const uint32_t src_chroma = src_fmt->i_chroma; + vout_display_sys_t *sys; ++ char * display_name = NULL; + int ret = VLC_EGENERIC; + int rv; -+ msg_Info(vd, "<<< %s: Fmt=%4.4s", __func__, (const char *)&fmtp->i_chroma); ++ msg_Info(vd, "<<< %s: Fmt=%4.4s", __func__, (const char *)&src_fmt->i_chroma); + +// if (!var_InheritBool(vd, "fullscreen")) { +// msg_Dbg(vd, ">>> %s: Not fullscreen", __func__); @@ -17548,39 +17964,18 @@ index 0000000000..fc3395e4b4 + + sys->mode_id = -1; + ++ display_name = var_InheritString(vd, DRM_VOUT_DISPLAY_NAME); ++ + { ++ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber"); ++ const char * conn_name = qt_num == 0 ? "HDMI-A-1" : qt_num == 1 ? "HDMI-A-2" : NULL; ++ const char * dname; + const drmu_log_env_t log = { + .fn = drmu_log_vlc_cb, + .v = vd, + .max_level = DRMU_LOG_LEVEL_ALL + }; + -+ sys->du = drmu_env_new_xlease(&log); -+ if (sys->du == NULL) { -+ char * module_name = var_InheritString(vd, DRM_VOUT_MODULE_NAME); -+ sys->du = drmu_env_new_open(module_name, &log); -+ free(module_name); -+ if (sys->du == NULL) -+ goto fail; -+ } -+ } -+ -+ drmu_env_restore_enable(sys->du); -+ -+ if ((sys->dout = drmu_output_new(sys->du)) == NULL) { -+ msg_Err(vd, "Failed to allocate new drmu output"); -+ goto fail; -+ } -+ -+ drmu_output_modeset_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MODESET_NAME)); -+ drmu_output_max_bpc_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MAX_BPC)); -+ -+ { -+ char * const display_name = var_InheritString(vd, DRM_VOUT_DISPLAY_NAME); -+ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber"); -+ const char * conn_name = qt_num == 0 ? "HDMI-A-1" : qt_num == 1 ? "HDMI-A-2" : NULL; -+ const char * dname; -+ + if (display_name && strcasecmp(display_name, "auto") != 0) { + if (strcasecmp(display_name, "hdmi-1") == 0) + conn_name = "HDMI-A-1"; @@ -17592,20 +17987,51 @@ index 0000000000..fc3395e4b4 + + dname = conn_name != NULL ? conn_name : ""; + -+ if ((rv = drmu_output_add_output(sys->dout, conn_name)) != 0) -+ msg_Err(vd, "Failed to find output %s: %s", dname, strerror(-rv)); -+ else -+ msg_Dbg(vd, "Using conn %s", dname); ++ sys->du = drmu_env_new_xlease(&log); + -+ free(display_name); ++ if (sys->du == NULL) { ++ if (drmu_scan_output(conn_name, &log, &sys->du, &sys->dout) == 0) ++ msg_Dbg(vd, "Using conn %s", dname); ++ } + -+ if (rv != 0) -+ goto fail; ++ if (sys->du == NULL) { ++ char * module_name = var_InheritString(vd, DRM_VOUT_MODULE_NAME); ++ if (module_name != NULL) { ++ sys->du = drmu_env_new_open(module_name, &log); ++ free(module_name); ++ if (sys->du == NULL) ++ goto fail; ++ } ++ } ++ ++ if (sys->dout == NULL) { ++ if ((sys->dout = drmu_output_new(sys->du)) == NULL) { ++ msg_Err(vd, "Failed to allocate new drmu output"); ++ goto fail; ++ } ++ ++ if ((rv = drmu_output_add_output(sys->dout, conn_name)) != 0) ++ msg_Err(vd, "Failed to find output %s: %s", dname, strerror(-rv)); ++ else ++ msg_Dbg(vd, "Using conn %s", dname); ++ ++ if (rv != 0) ++ goto fail; ++ } + } + -+ if ((sys->sub_fb_pool = drmu_pool_new(sys->du, 10)) == NULL) ++ drmu_env_restore_enable(sys->du); ++ ++ drmu_output_modeset_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MODESET_NAME)); ++ drmu_output_max_bpc_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MAX_BPC)); ++ ++ // Get frame buffer pools - try generic dmabufs first for cached ++ // buffers that are faster than uncached BOs ++ if ((sys->sub_fb_pool = drmu_pool_new_dmabuf_video(sys->du, 10)) == NULL && ++ (sys->sub_fb_pool = drmu_pool_new_dumb(sys->du, 10)) == NULL) + goto fail; -+ if ((sys->pic_pool = drmu_pool_new(sys->du, 5)) == NULL) ++ if ((sys->pic_pool = drmu_pool_new_dmabuf_video(sys->du, 40)) == NULL && ++ (sys->pic_pool = drmu_pool_new_dumb(sys->du, 40)) == NULL) + goto fail; + + // This wants to be the primary @@ -17624,24 +18050,11 @@ index 0000000000..fc3395e4b4 + } + } + -+ if (test_simple_plane_set(vd, sys) != 0) { -+ msg_Warn(vd, "Failed simple pic test"); ++ if (set_format(vd, sys, &out_fmt)) { ++ msg_Warn(vd, "Failed to find compatible output format"); + goto fail; + } + -+ vd->info = (vout_display_info_t){ -+ .is_slow = false, -+ .has_double_click = false, -+ .needs_hide_mouse = false, -+ .has_pictures_invalid = true, -+ .subpicture_chromas = sys->subpic_chromas -+ }; -+ -+ vd->pool = vd_drm_pool; -+ vd->prepare = vd_drm_prepare; -+ vd->display = vd_drm_display; -+ vd->control = vd_drm_control; -+ + sys->mode_id = -1; + + char * mode_name = NULL; @@ -17656,10 +18069,10 @@ index 0000000000..fc3395e4b4 + + if (modestr != NULL && strcmp(modestr, "none") != 0) { + drmu_mode_simple_params_t pick = { -+ .width = fmtp->i_visible_width, -+ .height = fmtp->i_visible_height, -+ .hz_x_1000 = fmtp->i_frame_rate_base == 0 ? 0 : -+ (unsigned int)(((uint64_t)fmtp->i_frame_rate * 1000) / fmtp->i_frame_rate_base), ++ .width = src_fmt->i_visible_width, ++ .height = src_fmt->i_visible_height, ++ .hz_x_1000 = src_fmt->i_frame_rate_base == 0 ? 0 : ++ (unsigned int)(((uint64_t)src_fmt->i_frame_rate * 1000) / src_fmt->i_frame_rate_base), + }; + + if (strcmp(modestr, "source") != 0) { @@ -17695,11 +18108,6 @@ index 0000000000..fc3395e4b4 + } + free(mode_name); + -+ set_format(vd, sys, fmtp); -+ -+// vout_display_SetSizeAndSar(vd, drmu_crtc_width(sys->dc), drmu_crtc_height(sys->dc), -+// drmu_ufrac_vlc_to_rational(drmu_crtc_sar(sys->dc))); -+ + { + char * const window_str = var_InheritString(vd, DRM_VOUT_WINDOW_NAME); + if (strcmp(window_str, "fullscreen") == 0) { @@ -17718,24 +18126,84 @@ index 0000000000..fc3395e4b4 + free(window_str); + } + -+ if (src_chroma != vd->fmt.i_chroma) -+ msg_Warn(vd, "Cannot display %s directly trying %s", drmu_log_fourcc(src_chroma), drmu_log_fourcc(vd->fmt.i_chroma)); ++ if (src_chroma != out_fmt.i_chroma) ++ msg_Warn(vd, "Cannot display %s directly trying %s", drmu_log_fourcc(src_chroma), drmu_log_fourcc(out_fmt.i_chroma)); + + set_display_windows(vd, sys); + -+ configure_display(vd, vd->cfg, &vd->source); ++ { ++ const unsigned int w = sys->display_rect.width; ++ const unsigned int h = sys->display_rect.height; ++ const drmu_rect_t sr = {.x = w / 5, .y = h / 5, .w = w / 3, .h = h / 3}; ++ if (test_simple_plane_set(vd, sys, &out_fmt, w, h, drmu_rect_wh(w, h)) != 0) { ++ msg_Warn(vd, "Failed simple pic test for mode %dx%d", w, h); ++ goto fail; ++ } ++ else { ++ msg_Dbg(vd, "OK simple pic test for mode %dx%d", w, h); ++ } + ++ // Test for full scale & position capability - the incoming stream ++ // might not need it but if anything changes it is better to be sure we ++ // can cope with it ++ if (test_simple_plane_set(vd, sys, &out_fmt, vd->source.i_visible_width, vd->source.i_visible_height, sr) != 0) { ++ msg_Warn(vd, "Failed scale pic test for %dx%d->%dx%d", vd->source.i_visible_width, vd->source.i_visible_height, sr.w, sr.h); ++ sys->output_simple = true; ++ } ++ else { ++ msg_Dbg(vd, "OK source pic test for %dx%d->%dx%d", vd->source.i_visible_width, vd->source.i_visible_height, sr.w, sr.h); ++ } ++ } ++ ++ configure_display(vd, sys, vd->cfg, &vd->source); ++ ++ if (sys->output_simple) ++ set_simple_format_size(&out_fmt, src_fmt, drmu_rect_vlc_place(&sys->dest_rect)); ++ ++ // Simple does not work usefully with dmabuf input ++ sys->pool_try_fb = !sys->output_simple && var_InheritBool(vd, DRM_VOUT_POOL_DMABUF_NAME); ++ ++ // All setup done - no possibility of error from here on ++ // Do final config setup & cleanup ++ ++ free(display_name); ++ ++ vd->fmt = out_fmt; ++ ++ vd->info = (vout_display_info_t){ ++ .is_slow = false, ++ .has_double_click = false, ++ .needs_hide_mouse = false, ++ .has_pictures_invalid = sys->output_simple, ++ .subpicture_chromas = sys->subpic_chromas ++ }; ++ ++ vd->pool = vd_drm_pool; ++ vd->prepare = vd_drm_prepare; ++ vd->display = vd_drm_display; ++ vd->control = vd_drm_control; ++ ++ { ++ const drmu_mode_simple_params_t * const mode = drmu_output_mode_simple_params(sys->dout); ++ if (vd->cfg->display.width != mode->width || vd->cfg->display.height != mode->height) { ++ msg_Dbg(vd, "Set display size to %ux%u", mode->width, mode->height); ++ vout_display_SendEventDisplaySize(vd, mode->width, mode->height); ++ } ++ } ++ ++ msg_Dbg(vd, ">>> %s", __func__); + return VLC_SUCCESS; + +fail: + CloseDrmVout(vd); ++ free(display_name); + return ret; +} + +vlc_module_begin() + set_shortname(N_("DRM vout")) + set_description(N_("DRM vout plugin")) -+ set_capability("vout display", 16) // 1 point better than ASCII art ++ set_capability("vout display", 32) // 2 points better than fb(30), ascii(15) + add_shortcut("drm-vout") + set_category(CAT_VIDEO) + set_subcategory(SUBCAT_VIDEO_VOUT) @@ -17743,6 +18211,7 @@ index 0000000000..fc3395e4b4 + add_bool(DRM_VOUT_SOURCE_MODESET_NAME, false, DRM_VOUT_SOURCE_MODESET_TEXT, DRM_VOUT_SOURCE_MODESET_LONGTEXT, false) + add_bool(DRM_VOUT_NO_MODESET_NAME, false, DRM_VOUT_NO_MODESET_TEXT, DRM_VOUT_NO_MODESET_LONGTEXT, false) + add_bool(DRM_VOUT_NO_MAX_BPC, false, DRM_VOUT_NO_MAX_BPC_TEXT, DRM_VOUT_NO_MAX_BPC_LONGTEXT, false) ++ add_bool(DRM_VOUT_POOL_DMABUF_NAME, false, DRM_VOUT_POOL_DMABUF_TEXT, DRM_VOUT_POOL_DMABUF_LONGTEXT, false) + add_string(DRM_VOUT_MODE_NAME, "none", DRM_VOUT_MODE_TEXT, DRM_VOUT_MODE_LONGTEXT, false) + add_string(DRM_VOUT_WINDOW_NAME, "fullscreen", DRM_VOUT_WINDOW_TEXT, DRM_VOUT_WINDOW_LONGTEXT, false) + add_string(DRM_VOUT_DISPLAY_NAME, "auto", DRM_VOUT_DISPLAY_TEXT, DRM_VOUT_DISPLAY_LONGTEXT, false) @@ -17753,10 +18222,10 @@ index 0000000000..fc3395e4b4 + diff --git a/modules/video_output/drmu/drmu.c b/modules/video_output/drmu/drmu.c new file mode 100644 -index 0000000000..e090feec5b +index 0000000000..55ec75f8dd --- /dev/null +++ b/modules/video_output/drmu/drmu.c -@@ -0,0 +1,3924 @@ +@@ -0,0 +1,3888 @@ +// Needed to ensure we get a 64-bit offset to mmap when mapping BOs +#undef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 @@ -17771,8 +18240,8 @@ index 0000000000..e090feec5b + +#include +#include ++#include +#include -+#include +#include +#include +#include @@ -17786,6 +18255,8 @@ index 0000000000..e090feec5b +#include +#include + ++#include ++ +#define TRACE_PROP_NEW 0 + +#ifndef OPT_IO_CALLOC @@ -17835,46 +18306,6 @@ index 0000000000..e090feec5b + return 1; +} + -+drmu_ufrac_t -+drmu_ufrac_reduce(drmu_ufrac_t x) -+{ -+ static const unsigned int primes[] = {2,3,5,7,11,13,17,19,23,29,31,UINT_MAX}; -+ const unsigned int * p; -+ -+ // Deal with specials -+ if (x.den == 0) { -+ x.num = 0; -+ return x; -+ } -+ if (x.num == 0) { -+ x.den = 1; -+ return x; -+ } -+ -+ // Shortcut the 1:1 common case - also ensures the default loop terminates -+ if (x.num == x.den) { -+ x.num = 1; -+ x.den = 1; -+ return x; -+ } -+ -+ // As num != den, (num/UINT_MAX == 0 || den/UINT_MAX == 0) must be true -+ // so loop will terminate -+ for (p = primes;; ++p) { -+ const unsigned int n = *p; -+ for (;;) { -+ const unsigned int xd = x.den / n; -+ const unsigned int xn = x.num / n; -+ if (xn == 0 || xd == 0) -+ return x; -+ if (xn * n != x.num || xd * n != x.den) -+ break; -+ x.num = xn; -+ x.den = xd; -+ } -+ } -+} -+ +//---------------------------------------------------------------------------- +// +// propinfo @@ -18525,11 +18956,16 @@ index 0000000000..e090feec5b +//---------------------------------------------------------------------------- +// +// BO fns ++// ++// Beware that when importing from FD we need to check that we don't already ++// have the BO as multiple FDs can map to the same BO and a single close will ++// close it irrespective of how many imports have occured. + +enum drmu_bo_type_e { + BO_TYPE_NONE = 0, -+ BO_TYPE_FD, -+ BO_TYPE_DUMB ++ BO_TYPE_FD, // Created from FD import ++ BO_TYPE_DUMB, // Locally allocated ++ BO_TYPE_EXTERNAL, // Externally allocated and closed +}; + +// BO handles come in 2 very distinct types: DUMB and FD @@ -18623,6 +19059,11 @@ index 0000000000..e090feec5b + if (atomic_fetch_sub(&bo->ref_count, 1) == 0) + bo_free_dumb(bo); + break; ++ case BO_TYPE_EXTERNAL: ++ // Simple imported BO - close dealt with elsewhere ++ if (atomic_fetch_sub(&bo->ref_count, 1) == 0) ++ free(bo); ++ break; + case BO_TYPE_NONE: + default: + free(bo); @@ -18639,6 +19080,21 @@ index 0000000000..e090feec5b + return bo; +} + ++int ++drmu_bo_export_fd(drmu_bo_t * bo, uint32_t flags) ++{ ++ struct drm_prime_handle prime_handle = { ++ .handle = bo->handle, ++ .flags = flags == 0 ? DRM_RDWR | DRM_CLOEXEC : flags, ++ .fd = 0 ++ }; ++ ++ if (drmu_ioctl(bo->du, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle) != 0) ++ return -1; ++ ++ return prime_handle.fd; ++} ++ +static drmu_bo_t * +bo_alloc(drmu_env_t *const du, enum drmu_bo_type_e bo_type) +{ @@ -18655,6 +19111,20 @@ index 0000000000..e090feec5b +} + +drmu_bo_t * ++drmu_bo_new_external(drmu_env_t *const du, const uint32_t bo_handle) ++{ ++ drmu_bo_t *const bo = bo_alloc(du, BO_TYPE_EXTERNAL); ++ ++ if (bo == NULL) { ++ drmu_err(du, "%s: Failed to alloc BO", __func__); ++ return NULL; ++ } ++ ++ bo->handle = bo_handle; ++ return bo; ++} ++ ++drmu_bo_t * +drmu_bo_new_fd(drmu_env_t *const du, const int fd) +{ + drmu_bo_env_t * const boe = env_boe(du); @@ -18738,8 +19208,6 @@ index 0000000000..e090feec5b + +typedef struct drmu_fb_s { + atomic_int ref_count; // 0 == 1 ref for ease of init -+ struct drmu_fb_s * prev; -+ struct drmu_fb_s * next; + + struct drmu_env_s * du; + @@ -18750,6 +19218,8 @@ index 0000000000..e090feec5b + drmu_rect_t active; // Area that was asked for inside the buffer; pixels + drmu_rect_t crop; // Cropping inside that; fractional pels (16.16, 16.16) + ++ int buf_fd; ++ + void * map_ptr; + size_t map_size; + size_t map_pitch; @@ -18832,6 +19302,9 @@ index 0000000000..e090feec5b + for (i = 0; i != 4; ++i) + drmu_bo_unref(dfb->bo_list + i); + ++ if (dfb->buf_fd != -1) ++ close(dfb->buf_fd); ++ + // Call on_delete last so we have stopped using anything that might be + // freed by it + { @@ -18930,6 +19403,12 @@ index 0000000000..e090feec5b + return (layer >= 4 || dfb->map_ptr == NULL) ? NULL : (uint8_t * )dfb->map_ptr + dfb->fb.offsets[layer]; +} + ++drmu_bo_t * ++drmu_fb_bo(const drmu_fb_t * const dfb, const unsigned int layer) ++{ ++ return (layer >= 4) ? NULL : dfb->bo_list[layer]; ++} ++ +uint32_t +drmu_fb_width(const drmu_fb_t *const dfb) +{ @@ -18987,7 +19466,7 @@ index 0000000000..e090feec5b +} + +void -+drmu_fb_int_color_set(drmu_fb_t *const dfb, const drmu_color_encoding_t enc, const drmu_color_range_t range, const drmu_colorspace_t space) ++drmu_fb_color_set(drmu_fb_t *const dfb, const drmu_color_encoding_t enc, const drmu_color_range_t range, const drmu_colorspace_t space) +{ + dfb->color_encoding = enc; + dfb->color_range = range; @@ -18995,7 +19474,7 @@ index 0000000000..e090feec5b +} + +void -+drmu_fb_int_chroma_siting_set(drmu_fb_t *const dfb, const drmu_chroma_siting_t siting) ++drmu_fb_chroma_siting_set(drmu_fb_t *const dfb, const drmu_chroma_siting_t siting) +{ + dfb->chroma_siting = siting; +} @@ -19014,6 +19493,20 @@ index 0000000000..e090feec5b +} + +void ++drmu_fb_int_fd_set(drmu_fb_t *const dfb, const int fd) ++{ ++ dfb->buf_fd = fd; ++} ++ ++void ++drmu_fb_int_mmap_set(drmu_fb_t *const dfb, void * const buf, const size_t size, const size_t pitch) ++{ ++ dfb->map_ptr = buf; ++ dfb->map_size = size; ++ dfb->map_pitch = pitch; ++} ++ ++void +drmu_fb_int_layer_mod_set(drmu_fb_t *const dfb, unsigned int i, unsigned int obj_idx, uint32_t pitch, uint32_t offset, uint64_t modifier) +{ + dfb->fb.handles[i] = dfb->bo_list[obj_idx]->handle; @@ -19036,7 +19529,8 @@ index 0000000000..e090feec5b + drmu_env_t * du = dfb->du; + int rv; + -+ dfb->fb.flags = (dfb->fb.modifier[0] == DRM_FORMAT_MOD_INVALID || dfb->fb.modifier[0] == 0) ? 0 : DRM_MODE_FB_MODIFIERS; ++ dfb->fb.flags = (dfb->fb.modifier[0] == DRM_FORMAT_MOD_INVALID || ++ dfb->fb.modifier[0] == DRM_FORMAT_MOD_LINEAR) ? 0 : DRM_MODE_FB_MODIFIERS; + + if ((rv = drmu_ioctl(du, DRM_IOCTL_MODE_ADDFB2, &dfb->fb)) != 0) + drmu_err(du, "AddFB2 failed: %s", strerror(-rv)); @@ -19106,6 +19600,7 @@ index 0000000000..e090feec5b + + dfb->du = du; + dfb->chroma_siting = DRMU_CHROMA_SITING_UNSPECIFIED; ++ dfb->buf_fd = -1; + dfb->fence_fd = -1; + return dfb; +} @@ -19129,6 +19624,43 @@ index 0000000000..e090feec5b + return plane >= 4 ? DRM_FORMAT_MOD_INVALID : dfb->fb.modifier[plane]; +} + ++static int ++fb_sync(drmu_fb_t * const dfb, unsigned int flags) ++{ ++ struct dma_buf_sync sync = { ++ .flags = flags ++ }; ++ if (dfb->buf_fd == -1 || dfb->map_ptr == NULL) ++ return 0; ++ while (ioctl(dfb->buf_fd, DMA_BUF_IOCTL_SYNC, &sync) == -1) { ++ const int err = errno; ++ if (errno == EINTR) ++ continue; ++ drmu_debug(dfb->du, "%s: ioctl failed: flags=%#x\n", __func__, flags); ++ return -err; ++ } ++ return 0; ++} ++ ++int drmu_fb_write_start(drmu_fb_t * const dfb) ++{ ++ return fb_sync(dfb, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE); ++} ++ ++int drmu_fb_write_end(drmu_fb_t * const dfb) ++{ ++ return fb_sync(dfb, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE); ++} ++ ++int drmu_fb_read_start(drmu_fb_t * const dfb) ++{ ++ return fb_sync(dfb, DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ); ++} ++ ++int drmu_fb_read_end(drmu_fb_t * const dfb) ++{ ++ return fb_sync(dfb, DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ); ++} + +// Writeback fence +// Must be unset before set again @@ -19248,19 +19780,24 @@ index 0000000000..e090feec5b + struct drm_mode_map_dumb map_dumb = { + .handle = dfb->bo_list[0]->handle + }; ++ void * map_ptr; ++ + if ((rv = drmu_ioctl(du, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb)) != 0) + { + drmu_err(du, "%s: map dumb failed: %s", __func__, strerror(-rv)); + goto fail; + } + -+ if ((dfb->map_ptr = mmap(NULL, dfb->map_size, -+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, -+ drmu_fd(du), map_dumb.offset)) == MAP_FAILED) { ++ // Avoid having to test for MAP_FAILED when testing for mapped/unmapped ++ if ((map_ptr = mmap(NULL, dfb->map_size, ++ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, ++ drmu_fd(du), map_dumb.offset)) == MAP_FAILED) { + drmu_err(du, "%s: mmap failed (size=%zd, fd=%d, off=%#"PRIx64"): %s", __func__, + dfb->map_size, drmu_fd(du), map_dumb.offset, strerror(errno)); + goto fail; + } ++ ++ dfb->map_ptr = map_ptr; + } + + fb_pitches_set_mod(dfb, mod); @@ -19280,31 +19817,37 @@ index 0000000000..e090feec5b +drmu_fb_t * +drmu_fb_new_dumb(drmu_env_t * const du, uint32_t w, uint32_t h, const uint32_t format) +{ -+ return drmu_fb_new_dumb_mod(du, w, h, format, DRM_FORMAT_MOD_INVALID); ++ return drmu_fb_new_dumb_mod(du, w, h, format, DRM_FORMAT_MOD_LINEAR); +} + -+static int -+fb_try_reuse(drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format) ++bool ++drmu_fb_try_reuse(drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod) +{ -+ if (w > dfb->fb.width || h > dfb->fb.height || format != dfb->fb.pixel_format) -+ return 0; ++ if (w > dfb->fb.width || h > dfb->fb.height || format != dfb->fb.pixel_format || mod != dfb->fb.modifier[0]) ++ return false; + + dfb->active = drmu_rect_wh(w, h); + dfb->crop = drmu_rect_shl16(dfb->active); -+ return 1; ++ return true; ++} ++ ++drmu_fb_t * ++drmu_fb_realloc_dumb_mod(drmu_env_t * const du, drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod) ++{ ++ if (dfb == NULL) ++ return drmu_fb_new_dumb_mod(du, w, h, format, mod); ++ ++ if (drmu_fb_try_reuse(dfb, w, h, format, mod)) ++ return dfb; ++ ++ drmu_fb_unref(&dfb); ++ return drmu_fb_new_dumb_mod(du, w, h, format, mod); +} + +drmu_fb_t * +drmu_fb_realloc_dumb(drmu_env_t * const du, drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format) +{ -+ if (dfb == NULL) -+ return drmu_fb_new_dumb(du, w, h, format); -+ -+ if (fb_try_reuse(dfb, w, h, format)) -+ return dfb; -+ -+ drmu_fb_unref(&dfb); -+ return drmu_fb_new_dumb(du, w, h, format); ++ return drmu_fb_realloc_dumb_mod(du, dfb, w, h, format, DRM_FORMAT_MOD_LINEAR); +} + +static void @@ -19945,6 +20488,17 @@ index 0000000000..e090feec5b + return dc; +} + ++static int ++crtc_state_save(drmu_env_t * const du, drmu_crtc_t * const dc) ++{ ++ int rv = 0; ++ // 1st time through save state ++ if (!dc->saved && ++ (rv = env_object_state_save(du, dc->crtc.crtc_id, DRM_MODE_OBJECT_CRTC)) == 0) ++ dc->saved = true; ++ return rv; ++} ++ +// A Conn should be claimed before any op that might change its state +int +drmu_crtc_claim_ref(drmu_crtc_t * const dc) @@ -19955,8 +20509,7 @@ index 0000000000..e090feec5b + return -EBUSY; + + // 1st time through save state -+ if (!dc->saved && env_object_state_save(du, dc->crtc.crtc_id, DRM_MODE_OBJECT_CRTC) == 0) -+ dc->saved = true; ++ crtc_state_save(du, dc); + + return 0; +} @@ -20298,6 +20851,17 @@ index 0000000000..e090feec5b + return dn; +} + ++static int ++conn_state_save(drmu_env_t * const du, drmu_conn_t * const dn) ++{ ++ int rv = 0; ++ // 1st time through save state ++ if (!dn->saved && ++ (rv = env_object_state_save(du, dn->conn.connector_id, DRM_MODE_OBJECT_CONNECTOR)) == 0) ++ dn->saved = true; ++ return rv; ++} ++ +// A Conn should be claimed before any op that might change its state +int +drmu_conn_claim_ref(drmu_conn_t * const dn) @@ -20308,8 +20872,7 @@ index 0000000000..e090feec5b + return -EBUSY; + + // 1st time through save state -+ if (!dn->saved && env_object_state_save(du, dn->conn.connector_id, DRM_MODE_OBJECT_CONNECTOR) == 0) -+ dn->saved = true; ++ conn_state_save(du, dn); + + return 0; +} @@ -20319,6 +20882,7 @@ index 0000000000..e090feec5b +// Atomic Q fns (internal) + +typedef struct drmu_atomic_q_s { ++ bool kill; + pthread_mutex_t lock; + pthread_cond_t cond; + drmu_atomic_t * next_flip; @@ -20407,9 +20971,9 @@ index 0000000000..e090feec5b + drmu_err(du, "%s: User data el (%p) != cur (%p)", __func__, da, aq->cur_flip); + } + -+ drmu_atomic_unref(&aq->last_flip); -+ aq->last_flip = aq->cur_flip; -+ aq->cur_flip = NULL; ++ // Must merge cur into last rather than just replace last as there may ++ // still be things on screen not updated by the current commit ++ drmu_atomic_move_merge(&aq->last_flip, &aq->cur_flip); + + if (aq->next_flip != NULL) + atomic_q_attempt_commit_next(aq); @@ -20419,17 +20983,21 @@ index 0000000000..e090feec5b +} + +static int -+atomic_q_flush(drmu_atomic_q_t * const aq) ++atomic_q_kill(drmu_atomic_q_t * const aq) +{ + struct timespec ts; + int rv = 0; + + pthread_mutex_lock(&aq->lock); ++ aq->kill = true; ++ + clock_gettime(CLOCK_MONOTONIC, &ts); + ts.tv_sec += 1; // We should never timeout if all is well - 1 sec is plenty + -+ // Can flush next safely ++ // Can flush next safely - but call commit cbs ++ drmu_atomic_run_commit_callbacks(aq->next_flip); + drmu_atomic_unref(&aq->next_flip); ++ polltask_delete(&aq->retry_task); // If we've got here then retry would not succeed + + // Wait for cur to finish - seems to confuse the world otherwise + while (aq->cur_flip != NULL) { @@ -20441,23 +21009,36 @@ index 0000000000..e090feec5b + return rv; +} + -+// 'consumes' da -+static int -+atomic_q_queue(drmu_atomic_q_t * const aq, drmu_atomic_t * da) ++static void ++atomic_q_clear_flips(drmu_atomic_q_t * const aq) +{ -+ int rv = 0; ++ drmu_atomic_unref(&aq->next_flip); ++ drmu_atomic_unref(&aq->cur_flip); ++ drmu_atomic_unref(&aq->last_flip); ++} ++ ++int ++drmu_atomic_queue(drmu_atomic_t ** ppda) ++{ ++ int rv; ++ drmu_atomic_q_t * aq; ++ ++ if (*ppda == NULL) ++ return 0; ++ ++ aq = env_atomic_q(drmu_atomic_env(*ppda)); + + pthread_mutex_lock(&aq->lock); + -+ if (aq->next_flip != NULL) { -+ // We already have something pending or retrying - merge the new with it -+ rv = drmu_atomic_merge(aq->next_flip, &da); ++ if (aq->kill) { ++ drmu_atomic_unref(ppda); ++ rv = -EBUSY; + } + else { -+ aq->next_flip = da; ++ rv = drmu_atomic_move_merge(&aq->next_flip, ppda); + + // No pending commit? -+ if (aq->cur_flip == NULL) ++ if (rv == 0 && aq->cur_flip == NULL) + rv = atomic_q_attempt_commit_next(aq); + } + @@ -20465,20 +21046,6 @@ index 0000000000..e090feec5b + return rv; +} + -+// Consumes the passed atomic structure as it isn't copied -+// * arguably should copy & unref if ref count != 0 -+int -+drmu_atomic_queue(drmu_atomic_t ** ppda) -+{ -+ drmu_atomic_t * da = *ppda; -+ -+ if (da == NULL) -+ return 0; -+ *ppda = NULL; -+ -+ return atomic_q_queue(env_atomic_q(drmu_atomic_env(da)), da); -+} -+ +int +drmu_env_queue_wait(drmu_env_t * const du) +{ @@ -20504,10 +21071,9 @@ index 0000000000..e090feec5b +static void +atomic_q_uninit(drmu_atomic_q_t * const aq) +{ ++ aq->kill = true; + polltask_delete(&aq->retry_task); -+ drmu_atomic_unref(&aq->next_flip); -+ drmu_atomic_unref(&aq->cur_flip); -+ drmu_atomic_unref(&aq->last_flip); ++ atomic_q_clear_flips(aq); + pthread_cond_destroy(&aq->cond); + pthread_mutex_destroy(&aq->lock); +} @@ -20517,7 +21083,10 @@ index 0000000000..e090feec5b +{ + pthread_condattr_t condattr; + ++ aq->kill = false; + aq->next_flip = NULL; ++ aq->cur_flip = NULL; ++ aq->last_flip = NULL; + pthread_mutex_init(&aq->lock, NULL); + + pthread_condattr_init(&condattr); @@ -20528,226 +21097,6 @@ index 0000000000..e090feec5b + +//---------------------------------------------------------------------------- +// -+// Pool fns -+ -+typedef struct drmu_fb_list_s { -+ drmu_fb_t * head; -+ drmu_fb_t * tail; -+} drmu_fb_list_t; -+ -+typedef struct drmu_pool_s { -+ atomic_int ref_count; // 0 == 1 ref for ease of init -+ -+ struct drmu_env_s * du; -+ -+ pthread_mutex_t lock; -+ bool dead; -+ -+ unsigned int seq; // debug -+ -+ unsigned int fb_count; -+ unsigned int fb_max; -+ -+ drmu_fb_list_t free_fbs; -+} drmu_pool_t; -+ -+static void -+fb_list_add_tail(drmu_fb_list_t * const fbl, drmu_fb_t * const dfb) -+{ -+ assert(dfb->prev == NULL && dfb->next == NULL); -+ -+ if (fbl->tail == NULL) -+ fbl->head = dfb; -+ else -+ fbl->tail->next = dfb; -+ dfb->prev = fbl->tail; -+ fbl->tail = dfb; -+} -+ -+static drmu_fb_t * -+fb_list_extract(drmu_fb_list_t * const fbl, drmu_fb_t * const dfb) -+{ -+ if (dfb == NULL) -+ return NULL; -+ -+ if (dfb->prev == NULL) -+ fbl->head = dfb->next; -+ else -+ dfb->prev->next = dfb->next; -+ -+ if (dfb->next == NULL) -+ fbl->tail = dfb->prev; -+ else -+ dfb->next->prev = dfb->prev; -+ -+ dfb->next = NULL; -+ dfb->prev = NULL; -+ return dfb; -+} -+ -+static drmu_fb_t * -+fb_list_extract_head(drmu_fb_list_t * const fbl) -+{ -+ return fb_list_extract(fbl, fbl->head); -+} -+ -+static drmu_fb_t * -+fb_list_peek_head(drmu_fb_list_t * const fbl) -+{ -+ return fbl->head; -+} -+ -+static bool -+fb_list_is_empty(drmu_fb_list_t * const fbl) -+{ -+ return fbl->head == NULL; -+} -+ -+static void -+pool_free_pool(drmu_pool_t * const pool) -+{ -+ drmu_fb_t * dfb; -+ while ((dfb = fb_list_extract_head(&pool->free_fbs)) != NULL) -+ drmu_fb_unref(&dfb); -+} -+ -+static void -+pool_free(drmu_pool_t * const pool) -+{ -+ pool_free_pool(pool); -+ pthread_mutex_destroy(&pool->lock); -+ free(pool); -+} -+ -+void -+drmu_pool_unref(drmu_pool_t ** const pppool) -+{ -+ drmu_pool_t * const pool = *pppool; -+ -+ if (pool == NULL) -+ return; -+ *pppool = NULL; -+ -+ if (atomic_fetch_sub(&pool->ref_count, 1) != 0) -+ return; -+ -+ pool_free(pool); -+} -+ -+drmu_pool_t * -+drmu_pool_ref(drmu_pool_t * const pool) -+{ -+ atomic_fetch_add(&pool->ref_count, 1); -+ return pool; -+} -+ -+drmu_pool_t * -+drmu_pool_new(drmu_env_t * const du, unsigned int total_fbs_max) -+{ -+ drmu_pool_t * const pool = calloc(1, sizeof(*pool)); -+ -+ if (pool == NULL) { -+ drmu_err(du, "Failed pool env alloc"); -+ return NULL; -+ } -+ -+ pool->du = du; -+ pool->fb_max = total_fbs_max; -+ pthread_mutex_init(&pool->lock, NULL); -+ -+ return pool; -+} -+ -+static int -+pool_fb_pre_delete_cb(drmu_fb_t * dfb, void * v) -+{ -+ drmu_pool_t * pool = v; -+ -+ // Ensure we cannot end up in a delete loop -+ drmu_fb_pre_delete_unset(dfb); -+ -+ // If dead set then might as well delete now -+ // It should all work without this shortcut but this reclaims -+ // storage quicker -+ if (pool->dead) { -+ drmu_pool_unref(&pool); -+ return 0; -+ } -+ -+ drmu_fb_ref(dfb); // Restore ref -+ -+ pthread_mutex_lock(&pool->lock); -+ fb_list_add_tail(&pool->free_fbs, dfb); -+ pthread_mutex_unlock(&pool->lock); -+ -+ // May cause suicide & recursion on fb delete, but that should be OK as -+ // the 1 we return here should cause simple exit of fb delete -+ drmu_pool_unref(&pool); -+ return 1; // Stop delete -+} -+ -+drmu_fb_t * -+drmu_pool_fb_new_dumb(drmu_pool_t * const pool, uint32_t w, uint32_t h, const uint32_t format) -+{ -+ drmu_env_t * const du = pool->du; -+ drmu_fb_t * dfb; -+ -+ pthread_mutex_lock(&pool->lock); -+ -+ dfb = fb_list_peek_head(&pool->free_fbs); -+ while (dfb != NULL) { -+ if (fb_try_reuse(dfb, w, h, format)) { -+ fb_list_extract(&pool->free_fbs, dfb); -+ break; -+ } -+ dfb = dfb->next; -+ } -+ -+ if (dfb == NULL) { -+ if (pool->fb_count >= pool->fb_max && !fb_list_is_empty(&pool->free_fbs)) { -+ --pool->fb_count; -+ dfb = fb_list_extract_head(&pool->free_fbs); -+ } -+ ++pool->fb_count; -+ pthread_mutex_unlock(&pool->lock); -+ -+ drmu_fb_unref(&dfb); // Will free the dfb as pre-delete CB will be unset -+ if ((dfb = drmu_fb_realloc_dumb(du, NULL, w, h, format)) == NULL) { -+ --pool->fb_count; // ??? lock -+ return NULL; -+ } -+ } -+ else { -+ pthread_mutex_unlock(&pool->lock); -+ } -+ -+ drmu_fb_pre_delete_set(dfb, pool_fb_pre_delete_cb, pool); -+ drmu_pool_ref(pool); -+ return dfb; -+} -+ -+// Mark pool as dead (i.e. no new allocs) and unref it -+// Simple unref will also work but this reclaims storage faster -+// Actual pool structure will persist until all referencing fbs are deleted too -+void -+drmu_pool_delete(drmu_pool_t ** const pppool) -+{ -+ drmu_pool_t * pool = *pppool; -+ -+ if (pool == NULL) -+ return; -+ *pppool = NULL; -+ -+ pool->dead = true; -+ pthread_mutex_lock(&pool->lock); -+ pool_free_pool(pool); -+ pthread_mutex_unlock(&pool->lock); -+ -+ drmu_pool_unref(&pool); -+} -+ -+//---------------------------------------------------------------------------- -+// +// Plane fns + +typedef struct drmu_plane_s { @@ -20769,12 +21118,12 @@ index 0000000000..e090feec5b + struct { + uint32_t crtc_id; + uint32_t fb_id; -+ uint32_t crtc_h; -+ uint32_t crtc_w; ++ drmu_prop_range_t * crtc_h; ++ drmu_prop_range_t * crtc_w; + uint32_t crtc_x; + uint32_t crtc_y; -+ uint32_t src_h; -+ uint32_t src_w; ++ drmu_prop_range_t * src_h; ++ drmu_prop_range_t * src_w; + uint32_t src_x; + uint32_t src_y; + drmu_prop_range_t * alpha; @@ -20804,12 +21153,12 @@ index 0000000000..e090feec5b + drmu_atomic_add_prop_fb(da, plid, dp->pid.fb_id, dfb); + drmu_atomic_add_prop_value(da, plid, dp->pid.crtc_x, crtc_x); + drmu_atomic_add_prop_value(da, plid, dp->pid.crtc_y, crtc_y); -+ drmu_atomic_add_prop_value(da, plid, dp->pid.crtc_w, crtc_w); -+ drmu_atomic_add_prop_value(da, plid, dp->pid.crtc_h, crtc_h); ++ drmu_atomic_add_prop_range(da, plid, dp->pid.crtc_w, crtc_w); ++ drmu_atomic_add_prop_range(da, plid, dp->pid.crtc_h, crtc_h); + drmu_atomic_add_prop_value(da, plid, dp->pid.src_x, src_x); + drmu_atomic_add_prop_value(da, plid, dp->pid.src_y, src_y); -+ drmu_atomic_add_prop_value(da, plid, dp->pid.src_w, src_w); -+ drmu_atomic_add_prop_value(da, plid, dp->pid.src_h, src_h); ++ drmu_atomic_add_prop_range(da, plid, dp->pid.src_w, src_w); ++ drmu_atomic_add_prop_range(da, plid, dp->pid.src_h, src_h); + return 0; +} + @@ -20854,32 +21203,35 @@ index 0000000000..e090feec5b +} + +int ++drmu_atomic_plane_clear_add(drmu_atomic_t * const da, drmu_plane_t * const dp) ++{ ++ return plane_set_atomic(da, dp, NULL, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0); ++} ++ ++int +drmu_atomic_plane_add_fb(drmu_atomic_t * const da, drmu_plane_t * const dp, + drmu_fb_t * const dfb, const drmu_rect_t pos) +{ + int rv; + const uint32_t plid = dp->plane.plane_id; + -+ if (dfb == NULL) { -+ rv = plane_set_atomic(da, dp, NULL, -+ 0, 0, 0, 0, -+ 0, 0, 0, 0); -+ } -+ else { -+ rv = plane_set_atomic(da, dp, dfb, ++ if (dfb == NULL) ++ return drmu_atomic_plane_clear_add(da, dp); ++ ++ if ((rv = plane_set_atomic(da, dp, dfb, + pos.x, pos.y, + pos.w, pos.h, + dfb->crop.x + (dfb->active.x << 16), dfb->crop.y + (dfb->active.y << 16), -+ dfb->crop.w, dfb->crop.h); -+ } -+ if (rv != 0 || dfb == NULL) ++ dfb->crop.w, dfb->crop.h)) != 0) + return rv; + + drmu_atomic_add_prop_enum(da, plid, dp->pid.pixel_blend_mode, dfb->pixel_blend_mode); + drmu_atomic_add_prop_enum(da, plid, dp->pid.color_encoding, dfb->color_encoding); + drmu_atomic_add_prop_enum(da, plid, dp->pid.color_range, dfb->color_range); + drmu_atomic_plane_add_chroma_siting(da, dp, dfb->chroma_siting); -+ return rv != 0 ? -errno : 0; ++ return 0; +} + +uint32_t @@ -20934,6 +21286,12 @@ index 0000000000..e090feec5b + return false; +} + ++bool ++drmu_plane_is_claimed(drmu_plane_t * const dp) ++{ ++ return atomic_load(&dp->ref_count) != 0; ++} ++ +void +drmu_plane_unref(drmu_plane_t ** const ppdp) +{ @@ -20957,6 +21315,18 @@ index 0000000000..e090feec5b + return dp; +} + ++static int ++plane_state_save(drmu_env_t * const du, drmu_plane_t * const dp) ++{ ++ int rv = 0; ++ ++ // 1st time through save state ++ if (!dp->saved && ++ (rv = env_object_state_save(du, drmu_plane_id(dp), DRM_MODE_OBJECT_PLANE)) == 0) ++ dp->saved = true; ++ return rv; ++} ++ +// Associate a plane with a crtc and ref it +// Returns -EBUSY if plane already associated +int @@ -20969,9 +21339,8 @@ index 0000000000..e090feec5b + return -EBUSY; + dp->dc = dc; + -+ // 1st time through save state -+ if (!dp->saved && env_object_state_save(du, drmu_plane_id(dp), DRM_MODE_OBJECT_PLANE) == 0) -+ dp->saved = true; ++ // 1st time through save state if required - ignore fail ++ plane_state_save(du, dp); + + return 0; +} @@ -21021,6 +21390,10 @@ index 0000000000..e090feec5b +static void +plane_uninit(drmu_plane_t * const dp) +{ ++ drmu_prop_range_delete(&dp->pid.crtc_h); ++ drmu_prop_range_delete(&dp->pid.crtc_w); ++ drmu_prop_range_delete(&dp->pid.src_h); ++ drmu_prop_range_delete(&dp->pid.src_w); + drmu_prop_range_delete(&dp->pid.alpha); + drmu_prop_range_delete(&dp->pid.chroma_siting_h); + drmu_prop_range_delete(&dp->pid.chroma_siting_v); @@ -21059,12 +21432,12 @@ index 0000000000..e090feec5b + + if ((dp->pid.crtc_id = props_name_to_id(props, "CRTC_ID")) == 0 || + (dp->pid.fb_id = props_name_to_id(props, "FB_ID")) == 0 || -+ (dp->pid.crtc_h = props_name_to_id(props, "CRTC_H")) == 0 || -+ (dp->pid.crtc_w = props_name_to_id(props, "CRTC_W")) == 0 || ++ (dp->pid.crtc_h = drmu_prop_range_new(du, props_name_to_id(props, "CRTC_H"))) == NULL || ++ (dp->pid.crtc_w = drmu_prop_range_new(du, props_name_to_id(props, "CRTC_W"))) == NULL || + (dp->pid.crtc_x = props_name_to_id(props, "CRTC_X")) == 0 || + (dp->pid.crtc_y = props_name_to_id(props, "CRTC_Y")) == 0 || -+ (dp->pid.src_h = props_name_to_id(props, "SRC_H")) == 0 || -+ (dp->pid.src_w = props_name_to_id(props, "SRC_W")) == 0 || ++ (dp->pid.src_h = drmu_prop_range_new(du, props_name_to_id(props, "SRC_H"))) == NULL || ++ (dp->pid.src_w = drmu_prop_range_new(du, props_name_to_id(props, "SRC_W"))) == NULL || + (dp->pid.src_x = props_name_to_id(props, "SRC_X")) == 0 || + (dp->pid.src_y = props_name_to_id(props, "SRC_Y")) == 0 || + props_name_get_blob(props, "IN_FORMATS", &dp->formats_in, &dp->formats_in_len) != 0) @@ -21323,6 +21696,24 @@ index 0000000000..e090feec5b + return &du->aq; +} + ++static void ++env_restore(drmu_env_t * const du) ++{ ++ int rv; ++ drmu_atomic_t * bad = drmu_atomic_new(du); ++ if ((rv = drmu_atomic_commit_test(du->da_restore, DRM_MODE_ATOMIC_ALLOW_MODESET, bad)) != 0) { ++ drmu_atomic_sub(du->da_restore, bad); ++ if ((rv = drmu_atomic_commit(du->da_restore, DRM_MODE_ATOMIC_ALLOW_MODESET)) != 0) ++ drmu_err(du, "Failed to restore old mode on exit: %s", strerror(-rv)); ++ else ++ drmu_err(du, "Failed to completely restore old mode on exit"); ++ ++ if (drmu_env_log(du)->max_level >= DRMU_LOG_LEVEL_DEBUG) ++ drmu_atomic_dump(bad); ++ } ++ drmu_atomic_unref(&bad); ++ drmu_atomic_unref(&du->da_restore); ++} + +static void +env_free(drmu_env_t * const du) @@ -21330,31 +21721,22 @@ index 0000000000..e090feec5b + if (!du) + return; + -+ atomic_q_flush(&du->aq); ++ atomic_q_kill(env_atomic_q(du)); + -+ pollqueue_unref(&du->pq); + polltask_delete(&du->pt); ++ pollqueue_finish(&du->pq); ++ ++ // Restore previous values after shutting down the polltask but ++ // before uniniting the Q commits ++ // ++ // The Q uninit stands a plausible chance of causing BOs or dmabufs that ++ // are in use to be reclaimed so restoring the old FBs before that is a ++ // good idea (prevents visible artifacts in VLC). ++ if (du->da_restore) ++ env_restore(du); + + atomic_q_uninit(&du->aq); + -+ if (du->da_restore) { -+ int rv; -+ if ((rv = drmu_atomic_commit(du->da_restore, DRM_MODE_ATOMIC_ALLOW_MODESET)) != 0) { -+ drmu_atomic_t * bad = drmu_atomic_new(du); -+ drmu_atomic_commit_test(du->da_restore, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, bad); -+ drmu_atomic_sub(du->da_restore, bad); -+ if ((rv = drmu_atomic_commit(du->da_restore, DRM_MODE_ATOMIC_ALLOW_MODESET)) != 0) -+ drmu_err(du, "Failed to restore old mode on exit: %s", strerror(-rv)); -+ else -+ drmu_err(du, "Failed to completely restore old mode on exit"); -+ -+ if (drmu_env_log(du)->max_level >= DRMU_LOG_LEVEL_DEBUG) -+ drmu_atomic_dump(bad); -+ drmu_atomic_unref(&bad); -+ } -+ drmu_atomic_unref(&du->da_restore); -+ } -+ + env_free_planes(du); + env_free_conns(du); + env_free_crtcs(du); @@ -21368,14 +21750,49 @@ index 0000000000..e090feec5b +drmu_env_unref(drmu_env_t ** const ppdu) +{ + drmu_env_t * const du = *ppdu; ++ int n; ++ + if (!du) + return; + *ppdu = NULL; + -+ if (atomic_fetch_sub(&du->ref_count, 1) != 0) -+ return; ++ n = atomic_fetch_sub(&du->ref_count, 1); ++ assert(n >= 0); ++ if (n == 0) ++ env_free(du); ++} + -+ env_free(du); ++// Kill the Q ++// Ordering goes: ++// Shutdown Q events ++// Restore old state ++// Clear previous atomics ++// Must be done in that order or we end up destroying buffers that are ++// still in use ++void ++drmu_env_kill(drmu_env_t ** const ppdu) ++{ ++ drmu_env_t * du = *ppdu; ++ ++ if (!du) ++ return; ++ *ppdu = NULL; ++ ++ atomic_q_kill(&du->aq); ++ ++ polltask_delete(&du->pt); ++ pollqueue_finish(&du->pq); ++ ++ if (du->da_restore) ++ env_restore(du); ++ ++ atomic_q_clear_flips(&du->aq); ++ ++ // Q is now mostly shutdown. The mutex and cond are still valid so ++ // it is still possible to abtain teh lock and then note that it is ++ // dead. Remaining environment will be freed when drmu_env is freed. ++ ++ drmu_env_unref(&du); +} + +drmu_env_t * @@ -21419,10 +21836,26 @@ index 0000000000..e090feec5b +int +drmu_env_restore_enable(drmu_env_t * const du) +{ ++ uint32_t i; ++ + if (du->da_restore) + return 0; + if ((du->da_restore = drmu_atomic_new(du)) == NULL) + return -ENOMEM; ++ ++ // Save state of anything already claimed ++ // Cannot rewind time but this allows us to be a bit lax with the ++ // precise ordering of calls on setup (which is handy for scan) ++ for (i = 0; i != du->conn_count; ++i) ++ if (drmu_conn_is_claimed(du->conns + i)) ++ conn_state_save(du, du->conns + i); ++ for (i = 0; i != du->crtc_count; ++i) ++ if (drmu_crtc_is_claimed(du->crtcs + i)) ++ crtc_state_save(du, du->crtcs + i); ++ for (i = 0; i != du->plane_count; ++i) ++ if (drmu_plane_is_claimed(du->planes + i)) ++ plane_state_save(du, du->planes + i); ++ + return 0; +} + @@ -21459,7 +21892,7 @@ index 0000000000..e090feec5b +static int +evt_read(drmu_env_t * const du) +{ -+ uint8_t buf[128]; ++ uint8_t buf[256] __attribute__((aligned(__BIGGEST_ALIGNMENT__))); + const ssize_t rlen = read(drmu_fd(du), buf, sizeof(buf)); + size_t i; + @@ -21542,23 +21975,14 @@ index 0000000000..e090feec5b + drmu_bo_env_init(&du->boe); + atomic_q_init(&du->aq); + -+ if ((du->pq = pollqueue_new()) == NULL) { -+ drmu_err(du, "Failed to create pollqueue"); -+ goto fail1; -+ } -+ if ((du->pt = polltask_new(du->pq, du->fd, POLLIN | POLLPRI, drmu_env_polltask_cb, du)) == NULL) { -+ drmu_err(du, "Failed to create polltask"); -+ goto fail1; -+ } -+ -+ // We want the primary plane for video -+ if ((rv = env_set_client_cap(du, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) != 0) -+ drmu_debug(du, "Failed to set universal planes cap"); + // We need atomic for almost everything we do + if ((rv = env_set_client_cap(du, DRM_CLIENT_CAP_ATOMIC, 1)) != 0) { + drmu_err(du, "Failed to set atomic cap"); + goto fail1; + } ++ // We want the primary plane for video ++ if ((rv = env_set_client_cap(du, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) != 0) ++ drmu_debug(du, "Failed to set universal planes cap"); + // We can understand AR info + if ((rv = env_set_client_cap(du, DRM_CLIENT_CAP_ASPECT_RATIO, 1)) != 0) + drmu_debug(du, "Failed to set AR cap"); @@ -21626,6 +22050,15 @@ index 0000000000..e090feec5b + crtc_ids = NULL; + } + ++ if ((du->pq = pollqueue_new()) == NULL) { ++ drmu_err(du, "Failed to create pollqueue"); ++ goto fail1; ++ } ++ if ((du->pt = polltask_new(du->pq, du->fd, POLLIN | POLLPRI, drmu_env_polltask_cb, du)) == NULL) { ++ drmu_err(du, "Failed to create polltask"); ++ goto fail1; ++ } ++ + pollqueue_add_task(du->pt, 1000); + + free(plane_ids); @@ -21683,10 +22116,10 @@ index 0000000000..e090feec5b + diff --git a/modules/video_output/drmu/drmu.h b/modules/video_output/drmu/drmu.h new file mode 100644 -index 0000000000..ab0796ada9 +index 0000000000..c2f404926e --- /dev/null +++ b/modules/video_output/drmu/drmu.h -@@ -0,0 +1,576 @@ +@@ -0,0 +1,565 @@ +#ifndef _DRMU_DRMU_H +#define _DRMU_DRMU_H + @@ -21696,6 +22129,7 @@ index 0000000000..ab0796ada9 +#include + +#include "drmu_chroma.h" ++#include "drmu_math.h" + +#ifdef __cplusplus +extern "C" { @@ -21721,9 +22155,6 @@ index 0000000000..ab0796ada9 +struct drmu_prop_object_s; +typedef struct drmu_prop_object_s drmu_prop_object_t; + -+struct drmu_pool_s; -+typedef struct drmu_pool_s drmu_pool_t; -+ +struct drmu_crtc_s; +typedef struct drmu_crtc_s drmu_crtc_t; + @@ -21741,16 +22172,6 @@ index 0000000000..ab0796ada9 +struct drm_log_env_s; +typedef struct drmu_log_env_s drmu_log_env_t; + -+typedef struct drmu_rect_s { -+ int32_t x, y; -+ uint32_t w, h; -+} drmu_rect_t; -+ -+typedef struct drmu_ufrac_s { -+ unsigned int num; -+ unsigned int den; -+} drmu_ufrac_t; -+ +// HDR enums is copied from linux include/linux/hdmi.h (strangely not part of uapi) +enum hdmi_metadata_type +{ @@ -21770,67 +22191,6 @@ index 0000000000..ab0796ada9 + DRMU_ISSET_SET, // Thing has valid data +} drmu_isset_t; + -+drmu_ufrac_t drmu_ufrac_reduce(drmu_ufrac_t x); -+ -+static inline int_fast32_t -+drmu_rect_rescale_1s(int_fast32_t x, uint_fast32_t mul, uint_fast32_t div) -+{ -+ const int_fast64_t m = x * (int_fast64_t)mul; -+ const uint_fast32_t d2 = div/2; -+ return div == 0 ? (int_fast32_t)m : -+ m >= 0 ? (int_fast32_t)(((uint_fast64_t)m + d2) / div) : -+ -(int_fast32_t)(((uint_fast64_t)(-m) + d2) / div); -+} -+ -+static inline uint_fast32_t -+drmu_rect_rescale_1u(uint_fast32_t x, uint_fast32_t mul, uint_fast32_t div) -+{ -+ const uint_fast64_t m = x * (uint_fast64_t)mul; -+ return (uint_fast32_t)(div == 0 ? m : (m + div/2) / div); -+} -+ -+static inline drmu_rect_t -+drmu_rect_rescale(const drmu_rect_t s, const drmu_rect_t mul, const drmu_rect_t div) -+{ -+ return (drmu_rect_t){ -+ .x = drmu_rect_rescale_1s(s.x - div.x, mul.w, div.w) + mul.x, -+ .y = drmu_rect_rescale_1s(s.y - div.y, mul.h, div.h) + mul.y, -+ .w = drmu_rect_rescale_1u(s.w, mul.w, div.w), -+ .h = drmu_rect_rescale_1u(s.h, mul.h, div.h) -+ }; -+} -+ -+static inline drmu_rect_t -+drmu_rect_add_xy(const drmu_rect_t a, const drmu_rect_t b) -+{ -+ return (drmu_rect_t){ -+ .x = a.x + b.x, -+ .y = a.y + b.y, -+ .w = a.w, -+ .h = a.h -+ }; -+} -+ -+static inline drmu_rect_t -+drmu_rect_wh(const unsigned int w, const unsigned int h) -+{ -+ return (drmu_rect_t){ -+ .w = w, -+ .h = h -+ }; -+} -+ -+static inline drmu_rect_t -+drmu_rect_shl16(const drmu_rect_t a) -+{ -+ return (drmu_rect_t){ -+ .x = a.x << 16, -+ .y = a.y << 16, -+ .w = a.w << 16, -+ .h = a.h << 16 -+ }; -+} -+ +// Blob + +void drmu_blob_unref(drmu_blob_t ** const ppBlob); @@ -21883,10 +22243,16 @@ index 0000000000..ab0796ada9 + +struct drm_mode_create_dumb; + ++// Create an fd from a bo ++// fd not tracked by the bo so it is the callers reponsibility to free it ++// if flags are 0 then RDWR | CLOEXEC will be used ++int drmu_bo_export_fd(drmu_bo_t * bo, uint32_t flags); ++ +void drmu_bo_unref(drmu_bo_t ** const ppbo); +drmu_bo_t * drmu_bo_ref(drmu_bo_t * const bo); +drmu_bo_t * drmu_bo_new_fd(drmu_env_t *const du, const int fd); +drmu_bo_t * drmu_bo_new_dumb(drmu_env_t *const du, struct drm_mode_create_dumb * const d); ++drmu_bo_t * drmu_bo_new_external(drmu_env_t *const du, const uint32_t bo_handle); +void drmu_bo_env_uninit(drmu_bo_env_t * const boe); +void drmu_bo_env_init(drmu_bo_env_t * boe); + @@ -21911,6 +22277,10 @@ index 0000000000..ab0796ada9 +drmu_fb_t * drmu_fb_new_dumb(drmu_env_t * const du, uint32_t w, uint32_t h, const uint32_t format); +drmu_fb_t * drmu_fb_new_dumb_mod(drmu_env_t * const du, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod); +drmu_fb_t * drmu_fb_realloc_dumb(drmu_env_t * const du, drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format); ++drmu_fb_t * drmu_fb_realloc_dumb_mod(drmu_env_t * const du, drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod); ++// Try to reset geometry to these values ++// True if done, false if not ++bool drmu_fb_try_reuse(drmu_fb_t * dfb, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod); +void drmu_fb_unref(drmu_fb_t ** const ppdfb); +drmu_fb_t * drmu_fb_ref(drmu_fb_t * const dfb); + @@ -21924,6 +22294,8 @@ index 0000000000..ab0796ada9 +// Pitch2 is only a sand thing +uint32_t drmu_fb_pitch2(const drmu_fb_t *const dfb, const unsigned int layer); +void * drmu_fb_data(const drmu_fb_t *const dfb, const unsigned int layer); ++drmu_bo_t * drmu_fb_bo(const drmu_fb_t * const dfb, const unsigned int layer); ++// Allocated width height - may be rounded up from requested w/h +uint32_t drmu_fb_width(const drmu_fb_t *const dfb); +uint32_t drmu_fb_height(const drmu_fb_t *const dfb); +// Set cropping (fractional) - x, y, relative to active x, y (and must be +ve) @@ -21976,12 +22348,14 @@ index 0000000000..ab0796ada9 +#define DRMU_BROADCAST_RGB_FULL "Full" +#define DRMU_BROADCAST_RGB_LIMITED_16_235 "Limited 16:235" +static inline bool drmu_broadcast_rgb_is_set(const drmu_broadcast_rgb_t x) {return x != NULL;} -+void drmu_fb_int_color_set(drmu_fb_t *const dfb, const drmu_color_encoding_t enc, const drmu_color_range_t range, const drmu_colorspace_t space); -+void drmu_fb_int_chroma_siting_set(drmu_fb_t *const dfb, const drmu_chroma_siting_t siting); ++void drmu_fb_color_set(drmu_fb_t *const dfb, const drmu_color_encoding_t enc, const drmu_color_range_t range, const drmu_colorspace_t space); ++void drmu_fb_chroma_siting_set(drmu_fb_t *const dfb, const drmu_chroma_siting_t siting); +void drmu_fb_int_on_delete_set(drmu_fb_t *const dfb, drmu_fb_on_delete_fn fn, void * v); +void drmu_fb_int_bo_set(drmu_fb_t *const dfb, unsigned int i, drmu_bo_t * const bo); +void drmu_fb_int_layer_set(drmu_fb_t *const dfb, unsigned int i, unsigned int obj_idx, uint32_t pitch, uint32_t offset); +void drmu_fb_int_layer_mod_set(drmu_fb_t *const dfb, unsigned int i, unsigned int obj_idx, uint32_t pitch, uint32_t offset, uint64_t modifier); ++void drmu_fb_int_fd_set(drmu_fb_t *const dfb, const int fd); ++void drmu_fb_int_mmap_set(drmu_fb_t *const dfb, void * const buf, const size_t size, const size_t pitch); +drmu_isset_t drmu_fb_hdr_metadata_isset(const drmu_fb_t *const dfb); +const struct hdr_output_metadata * drmu_fb_hdr_metadata_get(const drmu_fb_t *const dfb); +drmu_broadcast_rgb_t drmu_color_range_to_broadcast_rgb(const drmu_color_range_t range); @@ -21991,6 +22365,12 @@ index 0000000000..ab0796ada9 +void drmu_fb_hdr_metadata_set(drmu_fb_t *const dfb, const struct hdr_output_metadata * meta); +int drmu_fb_int_make(drmu_fb_t *const dfb); + ++// Cached fb sync ops ++int drmu_fb_write_start(drmu_fb_t * const dfb); ++int drmu_fb_write_end(drmu_fb_t * const dfb); ++int drmu_fb_read_start(drmu_fb_t * const dfb); ++int drmu_fb_read_end(drmu_fb_t * const dfb); ++ +// Wait for data to become ready when fb used as destination of writeback +// Returns: +// -ve error @@ -21998,14 +22378,6 @@ index 0000000000..ab0796ada9 +// 1 ready +int drmu_fb_out_fence_wait(drmu_fb_t * const fb, const int timeout_ms); + -+// fb pool -+ -+void drmu_pool_unref(drmu_pool_t ** const pppool); -+drmu_pool_t * drmu_pool_ref(drmu_pool_t * const pool); -+drmu_pool_t * drmu_pool_new(drmu_env_t * const du, unsigned int total_fbs_max); -+drmu_fb_t * drmu_pool_fb_new_dumb(drmu_pool_t * const pool, uint32_t w, uint32_t h, const uint32_t format); -+void drmu_pool_delete(drmu_pool_t ** const pppool); -+ +// Object Id + +struct drmu_propinfo_s; @@ -22139,10 +22511,17 @@ index 0000000000..ab0796ada9 + +int drmu_atomic_plane_add_chroma_siting(struct drmu_atomic_s * const da, const drmu_plane_t * const dp, const drmu_chroma_siting_t siting); + ++// Set FB to 0 (i.e. clear the plane) ++int drmu_atomic_plane_clear_add(struct drmu_atomic_s * const da, drmu_plane_t * const dp); ++ +// Adds the fb to the plane along with all fb properties that apply to a plane ++// If fb == NULL is equivalent to _plane_clear_add +// pos is dest rect on the plane in full pixels (not frac) +int drmu_atomic_plane_add_fb(struct drmu_atomic_s * const da, drmu_plane_t * const dp, drmu_fb_t * const dfb, const drmu_rect_t pos); + ++// Is this plane reffed? ++bool drmu_plane_is_claimed(drmu_plane_t * const dp); ++ +// Unref a plane +void drmu_plane_unref(drmu_plane_t ** const ppdp); + @@ -22174,7 +22553,11 @@ index 0000000000..ab0796ada9 +// pending = Commit Qed to be done when the in-progress commit has +// completed +// -+// If there is a pending commit this atomic wiill be merged with it ++// If no in-progress commit then this will be committed immediately ++// otherwise it becomes the pending commit ++// If there is a pending commit this atomic will be merged with it ++// Commits are done with the PAGE_FLIP flag set so we expect the ack ++// on the next page flip. +int drmu_atomic_queue(struct drmu_atomic_s ** ppda); +// Wait for there to be no pending commit (there may be a commit in +// progress) @@ -22187,6 +22570,11 @@ index 0000000000..ab0796ada9 +const struct drmu_log_env_s * drmu_env_log(const drmu_env_t * const du); +void drmu_env_unref(drmu_env_t ** const ppdu); +drmu_env_t * drmu_env_ref(drmu_env_t * const du); ++// Disable queue, restore saved state and unref ++// Doesn't guarantee that the env will be freed by exit as there may still be ++// buffers that hold a ref for logging or DRM fd but it should resolve circular ++// reference problems where buffers on the screen hold refs to the env. ++void drmu_env_kill(drmu_env_t ** const ppdu); +// Restore state on env close +int drmu_env_restore_enable(drmu_env_t * const du); +bool drmu_env_restore_is_enabled(const drmu_env_t * const du); @@ -22195,7 +22583,9 @@ index 0000000000..ab0796ada9 +int drmu_atomic_env_restore_add_snapshot(struct drmu_atomic_s ** const ppda); + +// Open a drmu environment with the drm fd -+// Takes a logging structure so early errors can be reported. ++// Takes a logging structure so early errors can be reported. The logging ++// environment is copied so does not have to be valid for greater than the ++// duration of the call. +// If log = NULL logging is disabled (set to drmu_log_env_none). +drmu_env_t * drmu_env_new_fd(const int fd, const struct drmu_log_env_s * const log); +drmu_env_t * drmu_env_new_open(const char * name, const struct drmu_log_env_s * const log); @@ -22214,8 +22604,29 @@ index 0000000000..ab0796ada9 +void drmu_atomic_unref(drmu_atomic_t ** const ppda); +drmu_atomic_t * drmu_atomic_ref(drmu_atomic_t * const da); +drmu_atomic_t * drmu_atomic_new(drmu_env_t * const du); ++ ++// Copy (rather than just ref) b ++drmu_atomic_t * drmu_atomic_copy(drmu_atomic_t * const b); ++ ++// 'Move' b to the return value ++// If b has a single ref then rv is simply b otherwise it is a copy of b ++drmu_atomic_t * drmu_atomic_move(drmu_atomic_t ** const ppb); ++ ++// Merge b into a ++// This reference to b is unrefed (inc. on error); if this was the only ++// reference to b this allows the code to simply move properites from b ++// to a rather than having to copy. If there is >1 ref then the merge ++// will copy safely without breaking the other refs to b. +int drmu_atomic_merge(drmu_atomic_t * const a, drmu_atomic_t ** const ppb); + ++static inline int drmu_atomic_move_merge(drmu_atomic_t ** const ppa, drmu_atomic_t ** const ppb) ++{ ++ if (*ppa) ++ return drmu_atomic_merge(*ppa, ppb); ++ *ppa = drmu_atomic_move(ppb); ++ return 0; ++} ++ +// Remove all els in a that are also in b +// b may be sorted (if not already) but is otherwise unchanged +void drmu_atomic_sub(drmu_atomic_t * const a, drmu_atomic_t * const b); @@ -22226,6 +22637,17 @@ index 0000000000..ab0796ada9 +// This does NOT remove failing props from da. If da_fail == NULL then same as _commit +int drmu_atomic_commit_test(const drmu_atomic_t * const da, uint32_t flags, drmu_atomic_t * const da_fail); + ++// Add a callback that occurs when the atomic has been committed ++// This will occur on flip if atomic queued via _atomic_queue - if multiple ++// atomics are queued before flip then all fill occur on the same flip ++// If cb is 0 then NOP ++typedef void drmu_atomic_commit_fn(void * v); ++int drmu_atomic_add_commit_callback(drmu_atomic_t * const da, drmu_atomic_commit_fn * const cb, void * const v); ++// Clear all commit callbacks from this atomic ++void drmu_atomic_clear_commit_callbacks(drmu_atomic_t * const da); ++// Run all commit callbacks on this atomic. Callbacks are not cleared. ++void drmu_atomic_run_commit_callbacks(const drmu_atomic_t * const da); ++ +typedef void drmu_prop_unref_fn(void * v); +typedef void drmu_prop_ref_fn(void * v); +typedef void drmu_prop_commit_fn(void * v, uint64_t value); @@ -22265,10 +22687,10 @@ index 0000000000..ab0796ada9 + diff --git a/modules/video_output/drmu/drmu_atomic.c b/modules/video_output/drmu/drmu_atomic.c new file mode 100644 -index 0000000000..fcf48f2d1f +index 0000000000..79d772dedc --- /dev/null +++ b/modules/video_output/drmu/drmu_atomic.c -@@ -0,0 +1,831 @@ +@@ -0,0 +1,929 @@ +#include "drmu.h" +#include "drmu_log.h" + @@ -22304,12 +22726,21 @@ index 0000000000..fcf48f2d1f + aprop_obj_t * objs; +} aprop_hdr_t; + ++typedef struct atomic_cb_s { ++ struct atomic_cb_s * next; ++ void * v; ++ drmu_atomic_commit_fn * cb; ++} atomic_cb_t; ++ +typedef struct drmu_atomic_s { + atomic_int ref_count; // 0 == 1 ref for ease of init + + struct drmu_env_s * du; + + aprop_hdr_t props; ++ ++ atomic_cb_t * commit_cb_q; ++ atomic_cb_t ** commit_cb_last_ptr; +} drmu_atomic_t; + +static inline unsigned int @@ -22318,6 +22749,21 @@ index 0000000000..fcf48f2d1f + return a < b ? b : a; +} + ++static atomic_cb_t * ++atomic_cb_new(drmu_atomic_commit_fn * cb, void * v) ++{ ++ atomic_cb_t * acb = malloc(sizeof(*acb)); ++ if (acb == NULL) ++ return NULL; ++ ++ *acb = (atomic_cb_t){ ++ .next = NULL, ++ .cb = cb, ++ .v = v ++ }; ++ return acb; ++} ++ +static void +aprop_prop_unref(aprop_prop_t * const pp) +{ @@ -22823,6 +23269,46 @@ index 0000000000..fcf48f2d1f +} + +int ++drmu_atomic_add_commit_callback(drmu_atomic_t * const da, drmu_atomic_commit_fn * const cb, void * const v) ++{ ++ if (cb) { ++ atomic_cb_t *acb = atomic_cb_new(cb, v); ++ if (acb == NULL) ++ return -ENOMEM; ++ ++ *da->commit_cb_last_ptr = acb; ++ da->commit_cb_last_ptr = &acb->next; ++ } ++ ++ return 0; ++} ++ ++void ++drmu_atomic_clear_commit_callbacks(drmu_atomic_t * const da) ++{ ++ atomic_cb_t *p = da->commit_cb_q; ++ ++ da->commit_cb_q = NULL; ++ da->commit_cb_last_ptr = &da->commit_cb_q; ++ ++ while (p != NULL) { ++ atomic_cb_t * const next = p->next; ++ free(p); ++ p = next; ++ } ++} ++ ++void ++drmu_atomic_run_commit_callbacks(const drmu_atomic_t * const da) ++{ ++ if (da == NULL) ++ return; ++ ++ for (const atomic_cb_t *p = da->commit_cb_q; p != NULL; p = p->next) ++ p->cb(p->v); ++} ++ ++int +drmu_atomic_add_prop_generic(drmu_atomic_t * const da, + const uint32_t obj_id, const uint32_t prop_id, const uint64_t value, + const drmu_atomic_prop_fns_t * const fns, void * const v) @@ -22875,6 +23361,7 @@ index 0000000000..fcf48f2d1f +static void +drmu_atomic_free(drmu_atomic_t * const da) +{ ++ drmu_atomic_clear_commit_callbacks(da); + aprop_hdr_uninit(&da->props); + free(da); +} @@ -22909,44 +23396,73 @@ index 0000000000..fcf48f2d1f + return NULL; + } + da->du = du; ++ da->commit_cb_last_ptr = &da->commit_cb_q; + + return da; +} + ++drmu_atomic_t * ++drmu_atomic_copy(drmu_atomic_t * const b) ++{ ++ drmu_atomic_t * a; ++ ++ if (b == NULL || (a = drmu_atomic_new(b->du)) == NULL) ++ return NULL; ++ ++ if (aprop_hdr_copy(&a->props, &b->props) != 0) ++ goto fail; ++ for (atomic_cb_t * p = b->commit_cb_q; p != NULL; p = p->next) ++ if (drmu_atomic_add_commit_callback(a, p->cb, p->v) != 0) ++ goto fail; ++ return a; ++ ++fail: ++ drmu_atomic_free(a); ++ return NULL; ++} ++ ++drmu_atomic_t * ++drmu_atomic_move(drmu_atomic_t ** const ppb) ++{ ++ drmu_atomic_t * a; ++ drmu_atomic_t * b = *ppb; ++ *ppb = NULL; ++ ++ if (b == NULL || atomic_load(&b->ref_count) == 0) ++ return b; ++ ++ a = drmu_atomic_copy(b); ++ drmu_atomic_unref(&b); ++ return a; ++} ++ +// Merge b into a. b is unrefed (inc on error) ++// Commit cbs are added +int +drmu_atomic_merge(drmu_atomic_t * const a, drmu_atomic_t ** const ppb) +{ -+ drmu_atomic_t * b = *ppb; -+ aprop_hdr_t bh = {0}; ++ drmu_atomic_t * b; + int rv = -EINVAL; + -+ if (b == NULL) ++ if (*ppb == NULL) + return 0; -+ *ppb = NULL; + + if (a == NULL) { -+ drmu_atomic_unref(&b); ++ drmu_atomic_unref(ppb); + return -EINVAL; + } -+ // We expect this to be the sole ref to a -+ assert(atomic_load(&a->ref_count) == 0); + -+ // If this is the only copy of b then use it directly otherwise -+ // copy before (probably) making it unusable -+ if (atomic_load(&b->ref_count) == 0) -+ rv = aprop_hdr_move(&bh, &b->props); -+ else -+ rv = aprop_hdr_copy(&bh, &b->props); -+ drmu_atomic_unref(&b); ++ if ((b = drmu_atomic_move(ppb)) == NULL) ++ return -ENOMEM; + -+ if (rv != 0) { -+ drmu_err(a->du, "%s: Copy Failed", __func__); -+ return rv; ++ if (b->commit_cb_q != NULL) { ++ *a->commit_cb_last_ptr = b->commit_cb_q; ++ a->commit_cb_last_ptr = b->commit_cb_last_ptr; ++ b->commit_cb_q = NULL; + } + -+ rv = aprop_hdr_merge(&a->props, &bh); -+ aprop_hdr_uninit(&bh); ++ rv = aprop_hdr_merge(&a->props, &b->props); ++ drmu_atomic_unref(&b); + + if (rv != 0) { + drmu_err(a->du, "%s: Merge Failed", __func__); @@ -23070,7 +23586,11 @@ index 0000000000..fcf48f2d1f + + aprop_hdr_atomic_fill(&da->props, obj_ids, prop_counts, prop_ids, prop_values); + -+ if ((rv = drmu_ioctl(du, DRM_IOCTL_MODE_ATOMIC, &atomic)) == 0 || !da_fail) ++ rv = drmu_ioctl(du, DRM_IOCTL_MODE_ATOMIC, &atomic); ++ ++ drmu_atomic_run_commit_callbacks(da); ++ ++ if (rv == 0 || !da_fail) + return rv; + + for (;;) { @@ -23153,12 +23673,277 @@ index 0000000000..3e3f7e9735 + +#endif + +diff --git a/modules/video_output/drmu/drmu_dmabuf.c b/modules/video_output/drmu/drmu_dmabuf.c +new file mode 100644 +index 0000000000..240f01c472 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_dmabuf.c +@@ -0,0 +1,206 @@ ++#include "drmu_dmabuf.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "drmu.h" ++#include "drmu_fmts.h" ++#include "drmu_log.h" ++#include "drmu_pool.h" ++ ++struct drmu_dmabuf_env_s { ++ atomic_int ref_count; ++ drmu_env_t * du; ++ int fd; ++ size_t page_size; ++}; ++ ++drmu_fb_t * ++drmu_fb_new_dmabuf_mod(drmu_dmabuf_env_t * const dde, const uint32_t w, const uint32_t h, const uint32_t format, const uint64_t mod) ++{ ++ const drmu_fmt_info_t * const fmti = drmu_fmt_info_find_fmt(format); ++ drmu_env_t * const du = dde->du; ++ unsigned int i; ++ unsigned int layers; ++ unsigned int bypp; ++ uint32_t w2 = (w + 15) & ~15; ++ uint32_t h2 = (h + 15) & ~15; ++ drmu_fb_t * fb; ++ uint32_t offset = 0; ++ ++ if (fmti == NULL) { ++ drmu_err(du, "%s: Format not found: %s", __func__, drmu_log_fourcc(format)); ++ return NULL; ++ } ++ ++ if ((fb = drmu_fb_int_alloc(du)) == NULL) ++ return NULL; ++ ++ drmu_fb_int_fmt_size_set(fb, format, w2, h2, drmu_rect_wh(w, h)); ++ ++ layers = drmu_fmt_info_plane_count(fmti); ++ bypp = (drmu_fmt_info_pixel_bits(fmti) + 7) / 8; ++ ++ for (offset = 0, i = 0; i != layers; ++i) { ++ const uint32_t stride = w2 * bypp / drmu_fmt_info_wdiv(fmti, i); ++ const uint32_t size = stride * h2 / drmu_fmt_info_hdiv(fmti, i); ++ offset += size; ++ } ++ ++ ++ { ++ struct dma_heap_allocation_data data = { ++ .len = (offset + dde->page_size - 1) & ~(dde->page_size - 1), ++ .fd = 0, ++ .fd_flags = O_RDWR | O_CLOEXEC, ++ .heap_flags = 0 ++ }; ++ void * map_ptr; ++ drmu_bo_t * bo; ++ ++ while (ioctl(dde->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { ++ const int err = errno; ++ if (err == EINTR) ++ continue; ++ drmu_err(dde->du, "Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)", ++ (uint64_t)data.len, dde->fd, err, strerror(err)); ++ goto fail; ++ } ++ ++ drmu_fb_int_fd_set(fb, data.fd); ++ ++ if ((bo = drmu_bo_new_fd(du, data.fd)) == NULL) { ++ drmu_err(du, "%s: Failed to allocate BO", __func__); ++ goto fail; ++ } ++ ++ drmu_fb_int_bo_set(fb, 0, bo); ++ ++ if ((map_ptr = mmap(NULL, (size_t)data.len, ++ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, ++ data.fd, 0)) == MAP_FAILED) { ++ drmu_err(du, "%s: mmap failed (size=%zd, fd=%d): %s", __func__, ++ (size_t)data.len, data.fd, strerror(errno)); ++ goto fail; ++ } ++ ++ drmu_fb_int_mmap_set(fb, map_ptr, (size_t)data.len, w2 * bypp); ++ } ++ ++ for (offset = 0, i = 0; i != layers; ++i) { ++ const uint32_t stride = w2 * bypp / drmu_fmt_info_wdiv(fmti, i); ++ const uint32_t size = stride * h2 / drmu_fmt_info_hdiv(fmti, i); ++ drmu_fb_int_layer_mod_set(fb, i, 0, stride, offset, mod); ++ offset += size; ++ } ++ ++ if (drmu_fb_int_make(fb)) ++ goto fail; ++ ++ return fb; ++ ++fail: ++ drmu_fb_int_free(fb); ++ return NULL; ++} ++ ++drmu_dmabuf_env_t * ++drmu_dmabuf_env_ref(drmu_dmabuf_env_t * const dde) ++{ ++ atomic_fetch_add(&dde->ref_count, 1); ++ return dde; ++} ++ ++void drmu_dmabuf_env_unref(drmu_dmabuf_env_t ** const ppdde) ++{ ++ drmu_dmabuf_env_t * const dde = *ppdde; ++ if (dde == NULL) ++ return; ++ *ppdde = NULL; ++ if (atomic_fetch_sub(&dde->ref_count, 1) != 0) ++ return; ++ ++ drmu_env_unref(&dde->du); ++ if (dde->fd != -1) ++ close(dde->fd); ++ free(dde); ++} ++ ++drmu_dmabuf_env_t * ++drmu_dmabuf_env_new_fd(struct drmu_env_s * const du, const int fd) ++{ ++ if (fd == -1) { ++ return NULL; ++ } ++ else { ++ drmu_dmabuf_env_t *const dde = calloc(1, sizeof(*dde)); ++ if (dde == NULL) { ++ close(fd); ++ return NULL; ++ } ++ dde->du = drmu_env_ref(du); ++ dde->fd = fd; ++ dde->page_size = (size_t)sysconf(_SC_PAGE_SIZE); ++ ++ return dde; ++ } ++} ++ ++drmu_dmabuf_env_t * ++drmu_dmabuf_env_new_video(struct drmu_env_s * const du) ++{ ++ static const char * const names[] = { ++ "/dev/dma_heap/vidbuf_cached", ++ "/dev/dma_heap/linux,cma", ++ "/dev/dma_heap/reserved", ++ NULL ++ }; ++ const char * const * pfname; ++ ++ for (pfname = names; pfname != NULL; ++pfname) { ++ const int fd = open(*pfname, O_RDWR | O_CLOEXEC); ++ drmu_dmabuf_env_t * const dde = drmu_dmabuf_env_new_fd(du, fd); ++ if (dde != NULL) ++ return dde; ++ } ++ return NULL; ++} ++ ++static drmu_fb_t * ++pool_dmabuf_alloc_cb(void * const v, const uint32_t w, const uint32_t h, const uint32_t format, const uint64_t mod) ++{ ++ return drmu_fb_new_dmabuf_mod(v, w, h, format, mod); ++} ++ ++static void ++pool_dmabuf_on_delete_cb(void * const v) ++{ ++ drmu_dmabuf_env_t * dde = v; ++ drmu_dmabuf_env_unref(&dde); ++} ++ ++drmu_pool_t * ++drmu_pool_new_dmabuf(drmu_dmabuf_env_t * dde, unsigned int total_fbs_max) ++{ ++ static const drmu_pool_callback_fns_t fns = { ++ .alloc_fn = pool_dmabuf_alloc_cb, ++ .on_delete_fn = pool_dmabuf_on_delete_cb, ++ .try_reuse_fn = drmu_fb_try_reuse, ++ }; ++ if (dde == NULL) ++ return NULL; ++ return drmu_pool_new_alloc(dde->du, total_fbs_max, ++ &fns, drmu_dmabuf_env_ref(dde)); ++} ++ +diff --git a/modules/video_output/drmu/drmu_dmabuf.h b/modules/video_output/drmu/drmu_dmabuf.h +new file mode 100644 +index 0000000000..880f4e355d +--- /dev/null ++++ b/modules/video_output/drmu/drmu_dmabuf.h +@@ -0,0 +1,47 @@ ++#ifndef _DRMU_DRMU_DMABUF_H ++#define _DRMU_DRMU_DMABUF_H ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct drmu_env_s; ++struct drmu_fb_s; ++struct drmu_pool_s; ++ ++struct drmu_dmabuf_env_s; ++typedef struct drmu_dmabuf_env_s drmu_dmabuf_env_t; ++ ++struct drmu_fb_s * drmu_fb_new_dmabuf_mod(drmu_dmabuf_env_t * const dde, const uint32_t w, const uint32_t h, const uint32_t format, const uint64_t mod); ++ ++drmu_dmabuf_env_t * drmu_dmabuf_env_ref(drmu_dmabuf_env_t * const dde); ++void drmu_dmabuf_env_unref(drmu_dmabuf_env_t ** const ppdde); ++// Takes control of fd and will close it when the env is deleted ++// or on creation error so dup if it is needed to survive the pool ++drmu_dmabuf_env_t * drmu_dmabuf_env_new_fd(struct drmu_env_s * const du, int fd); ++ ++drmu_dmabuf_env_t * drmu_dmabuf_env_new_video(struct drmu_env_s * const du); ++ ++// Construct an fb pool from dmabufs ++// A reference to dde is held by the pool so it is safe to unref immediately ++// after this call ++// dde = NULL returns NULL safely ++struct drmu_pool_s * drmu_pool_new_dmabuf(drmu_dmabuf_env_t * dde, unsigned int total_fbs_max); ++ ++// Convienience fn. ++static inline struct drmu_pool_s * ++drmu_pool_new_dmabuf_video(struct drmu_env_s * const du, unsigned int total_fbs_max) ++{ ++ drmu_dmabuf_env_t * dde = drmu_dmabuf_env_new_video(du); ++ struct drmu_pool_s * const pool = drmu_pool_new_dmabuf(dde, total_fbs_max); ++ drmu_dmabuf_env_unref(&dde); ++ return pool; ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif diff --git a/modules/video_output/drmu/drmu_fmts.c b/modules/video_output/drmu/drmu_fmts.c new file mode 100644 -index 0000000000..c5769a2ad5 +index 0000000000..41abcf5bbc --- /dev/null +++ b/modules/video_output/drmu/drmu_fmts.c -@@ -0,0 +1,249 @@ +@@ -0,0 +1,253 @@ +#include "drmu_fmts.h" + +#include @@ -23200,8 +23985,12 @@ index 0000000000..c5769a2ad5 +#define P_YUV422 {{.wdiv = 1, .hdiv = 1}, {.wdiv = 2, .hdiv = 1}, {.wdiv = 2, .hdiv = 1}} +#define P_YUV444 {{.wdiv = 1, .hdiv = 1}, {.wdiv = 1, .hdiv = 1}, {.wdiv = 1, .hdiv = 1}} + -+// Not const 'cos we sort in place when creating the sorted version -+static drmu_fmt_info_t format_info[] = { ++static ++// Not const when creating the sorted version 'cos we sort in place ++#if !BUILD_MK_SORTED_FMTS_H ++const ++#endif ++drmu_fmt_info_t format_info[] = { + { .fourcc = DRM_FORMAT_XRGB1555, .bpp = 16, .bit_depth = 5, .plane_count = 1, .planes = P_ONE}, + { .fourcc = DRM_FORMAT_XBGR1555, .bpp = 16, .bit_depth = 5, .plane_count = 1, .planes = P_ONE}, + { .fourcc = DRM_FORMAT_RGBX5551, .bpp = 16, .bit_depth = 5, .plane_count = 1, .planes = P_ONE}, @@ -23522,18 +24311,201 @@ index 0000000000..cc6ba5cc91 + +#endif + +diff --git a/modules/video_output/drmu/drmu_math.c b/modules/video_output/drmu/drmu_math.c +new file mode 100644 +index 0000000000..800da1b9ac +--- /dev/null ++++ b/modules/video_output/drmu/drmu_math.c +@@ -0,0 +1,43 @@ ++#include "drmu_math.h" ++ ++#include ++ ++drmu_ufrac_t ++drmu_ufrac_reduce(drmu_ufrac_t x) ++{ ++ static const unsigned int primes[] = {2,3,5,7,11,13,17,19,23,29,31,UINT_MAX}; ++ const unsigned int * p; ++ ++ // Deal with specials ++ if (x.den == 0) { ++ x.num = 0; ++ return x; ++ } ++ if (x.num == 0) { ++ x.den = 1; ++ return x; ++ } ++ ++ // Shortcut the 1:1 common case - also ensures the default loop terminates ++ if (x.num == x.den) { ++ x.num = 1; ++ x.den = 1; ++ return x; ++ } ++ ++ // As num != den, (num/UINT_MAX == 0 || den/UINT_MAX == 0) must be true ++ // so loop will terminate ++ for (p = primes;; ++p) { ++ const unsigned int n = *p; ++ for (;;) { ++ const unsigned int xd = x.den / n; ++ const unsigned int xn = x.num / n; ++ if (xn == 0 || xd == 0) ++ return x; ++ if (xn * n != x.num || xd * n != x.den) ++ break; ++ x.num = xn; ++ x.den = xd; ++ } ++ } ++} +diff --git a/modules/video_output/drmu/drmu_math.h b/modules/video_output/drmu/drmu_math.h +new file mode 100644 +index 0000000000..4d1e27dc50 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_math.h +@@ -0,0 +1,127 @@ ++#ifndef _DRMU_DRMU_MATH_H ++#define _DRMU_DRMU_MATH_H ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef struct drmu_rect_s { ++ int32_t x, y; ++ uint32_t w, h; ++} drmu_rect_t; ++ ++typedef struct drmu_ufrac_s { ++ unsigned int num; ++ unsigned int den; ++} drmu_ufrac_t; ++ ++drmu_ufrac_t drmu_ufrac_reduce(drmu_ufrac_t x); ++ ++static inline int_fast32_t ++drmu_rect_rescale_1s(int_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const int_fast64_t m = x * (int_fast64_t)mul; ++ const uint_fast32_t d2 = div/2; ++ return div == 0 ? (int_fast32_t)m : ++ m >= 0 ? (int_fast32_t)(((uint_fast64_t)m + d2) / div) : ++ -(int_fast32_t)(((uint_fast64_t)(-m) + d2) / div); ++} ++ ++static inline uint_fast32_t ++drmu_rect_rescale_1u(uint_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const uint_fast64_t m = x * (uint_fast64_t)mul; ++ return (uint_fast32_t)(div == 0 ? m : (m + div/2) / div); ++} ++ ++static inline drmu_rect_t ++drmu_rect_rescale(const drmu_rect_t s, const drmu_rect_t mul, const drmu_rect_t div) ++{ ++ return (drmu_rect_t){ ++ .x = drmu_rect_rescale_1s(s.x - div.x, mul.w, div.w) + mul.x, ++ .y = drmu_rect_rescale_1s(s.y - div.y, mul.h, div.h) + mul.y, ++ .w = drmu_rect_rescale_1u(s.w, mul.w, div.w), ++ .h = drmu_rect_rescale_1u(s.h, mul.h, div.h) ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_add_xy(const drmu_rect_t a, const drmu_rect_t b) ++{ ++ return (drmu_rect_t){ ++ .x = a.x + b.x, ++ .y = a.y + b.y, ++ .w = a.w, ++ .h = a.h ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_wh(const unsigned int w, const unsigned int h) ++{ ++ return (drmu_rect_t){ ++ .w = w, ++ .h = h ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_shl16(const drmu_rect_t a) ++{ ++ return (drmu_rect_t){ ++ .x = a.x << 16, ++ .y = a.y << 16, ++ .w = a.w << 16, ++ .h = a.h << 16 ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_shr16(const drmu_rect_t a) ++{ ++ return (drmu_rect_t){ ++ .x = a.x >> 16, ++ .y = a.y >> 16, ++ .w = a.w >> 16, ++ .h = a.h >> 16 ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_shr_rnd(const drmu_rect_t a, unsigned int n) ++{ ++ if (n == 0) ++ return a; ++ --n; ++ return (drmu_rect_t) { ++ .x = ((a.x >> n) + 1) >> 1, ++ .y = ((a.y >> n) + 1) >> 1, ++ .w = ((a.w >> n) + 1) >> 1, ++ .h = ((a.h >> n) + 1) >> 1 ++ }; ++} ++ ++static inline drmu_rect_t ++drmu_rect_shr16_rnd(const drmu_rect_t a) ++{ ++ return drmu_rect_shr_rnd(a, 16); ++} ++ ++static inline drmu_rect_t ++drmu_rect_div_xy(const drmu_rect_t a, const unsigned int dx, const unsigned int dy) ++{ ++ return (drmu_rect_t) { ++ .x = a.x / (int)dx, ++ .y = a.y / (int)dy, ++ .w = a.w / dx, ++ .h = a.h / dy ++ }; ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif diff --git a/modules/video_output/drmu/drmu_output.c b/modules/video_output/drmu/drmu_output.c new file mode 100644 -index 0000000000..b4709aeb1e +index 0000000000..c6a9eafb78 --- /dev/null +++ b/modules/video_output/drmu/drmu_output.c -@@ -0,0 +1,616 @@ +@@ -0,0 +1,635 @@ +#include "drmu_output.h" + +#include "drmu_fmts.h" +#include "drmu_log.h" + +#include ++#include +#include + +#include @@ -23546,6 +24518,8 @@ index 0000000000..b4709aeb1e +} + +struct drmu_output_s { ++ atomic_int ref_count; ++ + drmu_env_t * du; + drmu_crtc_t * dc; + unsigned int conn_n; @@ -24107,6 +25081,12 @@ index 0000000000..b4709aeb1e + return !dout || n >= dout->conn_n ? NULL : dout->dns[n]; +} + ++drmu_env_t * ++drmu_output_env(const drmu_output_t * const dout) ++{ ++ return dout->du; ++} ++ +static void +output_free(drmu_output_t * const dout) +{ @@ -24115,6 +25095,7 @@ index 0000000000..b4709aeb1e + drmu_conn_unref(dout->dns + i); + free(dout->dns); + drmu_crtc_unref(&dout->dc); ++ drmu_env_unref(&dout->du); + free(dout); +} + @@ -24126,7 +25107,16 @@ index 0000000000..b4709aeb1e + return; + *ppdout = NULL; + -+ output_free(dout); ++ if (atomic_fetch_sub(&dout->ref_count, 1) == 0) ++ output_free(dout); ++} ++ ++drmu_output_t * ++drmu_output_ref(drmu_output_t * const dout) ++{ ++ if (dout != NULL) ++ atomic_fetch_add(&dout->ref_count, 1); ++ return dout; +} + +drmu_output_t * @@ -24139,17 +25129,17 @@ index 0000000000..b4709aeb1e + return NULL; + } + -+ dout->du = du; ++ dout->du = drmu_env_ref(du); + dout->mode_id = -1; + return dout; +} + diff --git a/modules/video_output/drmu/drmu_output.h b/modules/video_output/drmu/drmu_output.h new file mode 100644 -index 0000000000..c977b24721 +index 0000000000..5ec2d71840 --- /dev/null +++ b/modules/video_output/drmu/drmu_output.h -@@ -0,0 +1,81 @@ +@@ -0,0 +1,87 @@ +#ifndef _DRMU_DRMU_OUTPUT_H +#define _DRMU_DRMU_OUTPUT_H + @@ -24218,10 +25208,16 @@ index 0000000000..c977b24721 +drmu_crtc_t * drmu_output_crtc(const drmu_output_t * const dout); +drmu_conn_t * drmu_output_conn(const drmu_output_t * const dout, const unsigned int n); + ++// Return the in-use drmu environment ++drmu_env_t * drmu_output_env(const drmu_output_t * const dout); ++ +// Create a new empty output - has no crtc or conn ++// Takes a ref on the env (released when the output is deleted) +drmu_output_t * drmu_output_new(drmu_env_t * const du); + -+// Unref an output ++// Increment ref count on an output - cannot fail ++drmu_output_t * drmu_output_ref(drmu_output_t * const dout); ++// Unref an output - delete if ref count now zero +void drmu_output_unref(drmu_output_t ** const ppdout); + +#ifdef __cplusplus @@ -24231,15 +25227,484 @@ index 0000000000..c977b24721 +#endif + + +diff --git a/modules/video_output/drmu/drmu_pool.c b/modules/video_output/drmu/drmu_pool.c +new file mode 100644 +index 0000000000..b047154366 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_pool.c +@@ -0,0 +1,306 @@ ++#include "drmu_pool.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "drmu.h" ++#include "drmu_log.h" ++ ++//---------------------------------------------------------------------------- ++// ++// Pool fns ++ ++typedef struct drmu_fb_slot_s { ++ struct drmu_fb_s * fb; ++ struct drmu_fb_slot_s * next; ++ struct drmu_fb_slot_s * prev; ++} drmu_fb_slot_t; ++ ++typedef struct drmu_fb_list_s { ++ drmu_fb_slot_t * head; // Double linked list of free FBs; LRU @ head ++ drmu_fb_slot_t * tail; ++ drmu_fb_slot_t * unused; // Single linked list of unused slots ++} drmu_fb_list_t; ++ ++struct drmu_pool_s { ++ atomic_int ref_count; // 0 == 1 ref for ease of init ++ bool dead; // Pool killed - never alloc again ++ ++ unsigned int fb_count; // FBs allocated (not free count) ++ unsigned int fb_max; // Max FBs to allocate ++ ++ struct drmu_env_s * du; // Logging only - not reffed ++ ++ drmu_pool_callback_fns_t callback_fns; ++ void * callback_v; ++ ++ pthread_mutex_t lock; ++ ++ drmu_fb_list_t free_fbs; // Free FB list header ++ drmu_fb_slot_t * slots; // [fb_max] ++}; ++ ++static void ++fb_list_add_tail(drmu_fb_list_t * const fbl, drmu_fb_t * const dfb) ++{ ++ drmu_fb_slot_t * const slot = fbl->unused; ++ ++ assert(slot != NULL); ++ fbl->unused = slot->next; ++ slot->fb = dfb; ++ slot->next = NULL; ++ ++ if (fbl->tail == NULL) ++ fbl->head = slot; ++ else ++ fbl->tail->next = slot; ++ slot->prev = fbl->tail; ++ fbl->tail = slot; ++} ++ ++static drmu_fb_t * ++fb_list_extract(drmu_fb_list_t * const fbl, drmu_fb_slot_t * const slot) ++{ ++ drmu_fb_t * dfb; ++ ++ if (slot == NULL) ++ return NULL; ++ ++ if (slot->prev == NULL) ++ fbl->head = slot->next; ++ else ++ slot->prev->next = slot->next; ++ ++ if (slot->next == NULL) ++ fbl->tail = slot->prev; ++ else ++ slot->next->prev = slot->prev; ++ ++ dfb = slot->fb; ++ slot->fb = NULL; ++ slot->next = fbl->unused; ++ slot->prev = NULL; ++ fbl->unused = slot; ++ return dfb; ++} ++ ++static drmu_fb_t * ++fb_list_extract_head(drmu_fb_list_t * const fbl) ++{ ++ return fb_list_extract(fbl, fbl->head); ++} ++ ++static void ++pool_free_pool(drmu_pool_t * const pool) ++{ ++ drmu_fb_t * dfb; ++ pthread_mutex_lock(&pool->lock); ++ while ((dfb = fb_list_extract_head(&pool->free_fbs)) != NULL) { ++ --pool->fb_count; ++ pthread_mutex_unlock(&pool->lock); ++ drmu_fb_unref(&dfb); ++ pthread_mutex_lock(&pool->lock); ++ } ++ pthread_mutex_unlock(&pool->lock); ++} ++ ++static void ++pool_free(drmu_pool_t * const pool) ++{ ++ void *const v = pool->callback_v; ++ const drmu_pool_on_delete_fn on_delete_fn = pool->callback_fns.on_delete_fn; ++ pool_free_pool(pool); ++ free(pool->slots); ++ pthread_mutex_destroy(&pool->lock); ++ free(pool); ++ ++ on_delete_fn(v); ++} ++ ++void ++drmu_pool_unref(drmu_pool_t ** const pppool) ++{ ++ drmu_pool_t * const pool = *pppool; ++ int n; ++ ++ if (pool == NULL) ++ return; ++ *pppool = NULL; ++ ++ n = atomic_fetch_sub(&pool->ref_count, 1); ++ assert(n >= 0); ++ if (n == 0) ++ pool_free(pool); ++} ++ ++drmu_pool_t * ++drmu_pool_ref(drmu_pool_t * const pool) ++{ ++ atomic_fetch_add(&pool->ref_count, 1); ++ return pool; ++} ++ ++drmu_pool_t * ++drmu_pool_new_alloc(drmu_env_t * const du, const unsigned int total_fbs_max, ++ const drmu_pool_callback_fns_t * const cb_fns, ++ void * const v) ++{ ++ drmu_pool_t * const pool = calloc(1, sizeof(*pool)); ++ unsigned int i; ++ ++ if (pool == NULL) ++ goto fail0; ++ if ((pool->slots = calloc(total_fbs_max, sizeof(*pool->slots))) == NULL) ++ goto fail1; ++ ++ pool->du = du; ++ pool->fb_max = total_fbs_max; ++ pool->callback_fns = *cb_fns; ++ pool->callback_v = v; ++ ++ for (i = 1; i != total_fbs_max; ++i) ++ pool->slots[i - 1].next = pool->slots + i; ++ pool->free_fbs.unused = pool->slots + 0; ++ ++ pthread_mutex_init(&pool->lock, NULL); ++ ++ return pool; ++ ++fail1: ++ free(pool); ++fail0: ++ cb_fns->on_delete_fn(v); ++ drmu_err(du, "Failed pool env alloc"); ++ return NULL; ++} ++ ++static int ++pool_fb_pre_delete_cb(drmu_fb_t * dfb, void * v) ++{ ++ drmu_pool_t * pool = v; ++ ++ // Ensure we cannot end up in a delete loop ++ drmu_fb_pre_delete_unset(dfb); ++ ++ // If dead set then might as well delete now ++ // It should all work without this shortcut but this reclaims ++ // storage quicker ++ if (pool->dead) { ++ drmu_pool_unref(&pool); ++ return 0; ++ } ++ ++ drmu_fb_ref(dfb); // Restore ref ++ ++ pthread_mutex_lock(&pool->lock); ++ fb_list_add_tail(&pool->free_fbs, dfb); ++ pthread_mutex_unlock(&pool->lock); ++ ++ // May cause suicide & recursion on fb delete, but that should be OK as ++ // the 1 we return here should cause simple exit of fb delete ++ drmu_pool_unref(&pool); ++ return 1; // Stop delete ++} ++ ++drmu_fb_t * ++drmu_pool_fb_new(drmu_pool_t * const pool, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod) ++{ ++ drmu_fb_t * dfb; ++ drmu_fb_slot_t * slot; ++ ++ pthread_mutex_lock(&pool->lock); ++ ++ // If pool killed then _fb_new must fail ++ if (pool->dead) ++ goto fail_unlock; ++ ++ slot = pool->free_fbs.head; ++ while (slot != NULL) { ++ dfb = slot->fb; ++ if (pool->callback_fns.try_reuse_fn(dfb, w, h, format, mod)) { ++ fb_list_extract(&pool->free_fbs, slot); ++ pthread_mutex_unlock(&pool->lock); ++ goto found; ++ } ++ slot = slot->next; ++ } ++ // Nothing reusable ++ dfb = NULL; ++ ++ // Simply allocate new buffers until we hit fb_max then free LRU ++ // first. If nothing to free then fail. ++ if (pool->fb_count++ >= pool->fb_max) { ++ --pool->fb_count; ++ if ((dfb = fb_list_extract_head(&pool->free_fbs)) == NULL) ++ goto fail_unlock; ++ } ++ pthread_mutex_unlock(&pool->lock); ++ ++ drmu_fb_unref(&dfb); // Will free the dfb as pre-delete CB will be unset ++ ++ if ((dfb = pool->callback_fns.alloc_fn(pool->callback_v, w, h, format, mod)) == NULL) { ++ pthread_mutex_lock(&pool->lock); ++ --pool->fb_count; ++ goto fail_unlock; ++ } ++ ++found: ++ drmu_fb_pre_delete_set(dfb, pool_fb_pre_delete_cb, drmu_pool_ref(pool)); ++ return dfb; ++ ++fail_unlock: ++ pthread_mutex_unlock(&pool->lock); ++ return NULL; ++} ++ ++// Mark pool as dead (i.e. no new allocs) and unref it ++// Simple unref will also work but this reclaims storage faster ++// Actual pool structure will persist until all referencing fbs are deleted too ++void ++drmu_pool_kill(drmu_pool_t ** const pppool) ++{ ++ drmu_pool_t * pool = *pppool; ++ ++ if (pool == NULL) ++ return; ++ *pppool = NULL; ++ ++ pool->dead = true; ++ pool_free_pool(pool); ++ ++ drmu_pool_unref(&pool); ++} ++ ++//---------------------------------------------------------------------------- ++// ++// Dumb pool setup ++ ++static drmu_fb_t * ++pool_dumb_alloc_cb(void * const v, const uint32_t w, const uint32_t h, const uint32_t format, const uint64_t mod) ++{ ++ return drmu_fb_new_dumb_mod(v, w, h, format, mod); ++} ++ ++static void ++pool_dumb_on_delete_cb(void * const v) ++{ ++ drmu_env_t * du = v; ++ drmu_env_unref(&du); ++} ++ ++drmu_pool_t * ++drmu_pool_new_dumb(drmu_env_t * const du, unsigned int total_fbs_max) ++{ ++ static const drmu_pool_callback_fns_t fns = { ++ .alloc_fn = pool_dumb_alloc_cb, ++ .on_delete_fn = pool_dumb_on_delete_cb, ++ .try_reuse_fn = drmu_fb_try_reuse, ++ }; ++ return drmu_pool_new_alloc(du, total_fbs_max, &fns, drmu_env_ref(du)); ++} ++ ++ +diff --git a/modules/video_output/drmu/drmu_pool.h b/modules/video_output/drmu/drmu_pool.h +new file mode 100644 +index 0000000000..181d3389a5 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_pool.h +@@ -0,0 +1,58 @@ ++#ifndef _DRMU_DRMU_POOL_H ++#define _DRMU_DRMU_POOL_H ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct drmu_pool_s; ++typedef struct drmu_pool_s drmu_pool_t; ++ ++struct drmu_env_s; ++struct drmu_fb_s; ++ ++// fb pool ++ ++void drmu_pool_unref(drmu_pool_t ** const pppool); ++drmu_pool_t * drmu_pool_ref(drmu_pool_t * const pool); ++ ++// cb to allocate a new pool fb ++typedef struct drmu_fb_s * (* drmu_pool_alloc_fn)(void * const v, const uint32_t w, const uint32_t h, const uint32_t format, const uint64_t mod); ++// cb called when pool deleted or on new_pool failure - takes the same v as alloc ++typedef void (* drmu_pool_on_delete_fn)(void * const v); ++typedef bool (* drmu_pool_try_reuse_fn)(struct drmu_fb_s * dfb, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod); ++ ++typedef struct drmu_pool_callback_fns_s { ++ drmu_pool_alloc_fn alloc_fn; ++ drmu_pool_on_delete_fn on_delete_fn; ++ drmu_pool_try_reuse_fn try_reuse_fn; ++} drmu_pool_callback_fns_t; ++ ++// Create a new pool with custom alloc & pool delete ++// If pool creation fails then on_delete_fn(v) called and NULL returned ++// Pool entries are not pre-allocated. ++drmu_pool_t * drmu_pool_new_alloc(struct drmu_env_s * const du, const unsigned int total_fbs_max, ++ const drmu_pool_callback_fns_t * const cb_fns, ++ void * const v); ++// Create a new pool of fb allocated from dumb objects ++// N.B. BOs are alloced from uncached memory so may be slow to do anything other ++// than copy into. (See drmu_dmabuf_ if you want cached data) ++struct drmu_fb_s * drmu_pool_fb_new(drmu_pool_t * const pool, uint32_t w, uint32_t h, const uint32_t format, const uint64_t mod); ++// Marks the pool as dead & unrefs this reference ++// No allocs will succeed after this ++// All free fbs are unrefed ++void drmu_pool_kill(drmu_pool_t ** const pppool); ++ ++drmu_pool_t * drmu_pool_new_dumb(struct drmu_env_s * const du, unsigned int total_fbs_max); ++// Allocate a fb from the pool ++// Allocations need not be all of the same size but no guarantees are made about ++// efficient memory use if this is the case ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/modules/video_output/drmu/drmu_scan.c b/modules/video_output/drmu/drmu_scan.c +new file mode 100644 +index 0000000000..9cb0d20c90 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_scan.c +@@ -0,0 +1,60 @@ ++#include "drmu_scan.h" ++ ++#include ++#include ++#include ++ ++#include "drmu.h" ++#include "drmu_log.h" ++#include "drmu_output.h" ++ ++#define CARD_MAX 16 ++ ++ ++int ++drmu_scan_output(const char * const cname, const drmu_log_env_t * const dlog, ++ drmu_env_t ** const pDu, drmu_output_t ** const pDoutput) ++{ ++ static const char * card_prefix = "/dev/dri/card"; ++ unsigned int i; ++ ++ *pDu = NULL; ++ *pDoutput = NULL; ++ ++ for (i = 0; i != CARD_MAX; ++i) { ++ drmu_env_t * du; ++ drmu_output_t * dout = NULL; ++ char fname[32]; ++ int fd; ++ ++ drmu_debug_log(dlog, "Try card %d", i); ++ ++ sprintf(fname, "%s%d", card_prefix, i); ++ while ((fd = open(fname, O_RDWR | O_CLOEXEC)) == -1 && errno == EINTR) ++ /* Loop */; ++ if (fd == -1) { ++ if (errno == ENOENT) ++ break; ++ continue; ++ } ++ ++ // Have FD ++ if ((du = drmu_env_new_fd(fd, dlog)) == NULL) ++ continue; ++ ++ if ((dout = drmu_output_new(du)) == NULL) ++ goto loop1; ++ ++ if (drmu_output_add_output(dout, cname) == 0) { ++ *pDu = du; ++ *pDoutput = dout; ++ return 0; ++ } ++ ++ drmu_output_unref(&dout); ++loop1: ++ drmu_env_unref(&du); ++ } ++ return -ENOENT; ++} ++ +diff --git a/modules/video_output/drmu/drmu_scan.h b/modules/video_output/drmu/drmu_scan.h +new file mode 100644 +index 0000000000..4351deec08 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_scan.h +@@ -0,0 +1,20 @@ ++#ifndef _DRMU_DRMU_SCAN_H ++#define _DRMU_DRMU_SCAN_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct drmu_log_env_s; ++struct drmu_env_s; ++struct drmu_output_s; ++ ++int ++drmu_scan_output(const char * const cname, const struct drmu_log_env_s * const dlog, ++ struct drmu_env_s ** const pDu, struct drmu_output_s ** const pDoutput); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ diff --git a/modules/video_output/drmu/drmu_util.c b/modules/video_output/drmu/drmu_util.c new file mode 100644 -index 0000000000..12a088216d +index 0000000000..20d24bfc09 --- /dev/null +++ b/modules/video_output/drmu/drmu_util.c -@@ -0,0 +1,125 @@ -+#include "drmu.h" +@@ -0,0 +1,163 @@ +#include "drmu_util.h" + ++#include "drmu.h" ++ +#include +#include +#include @@ -24361,16 +25826,57 @@ index 0000000000..12a088216d + return r; +} + ++drmu_ufrac_t ++drmu_util_guess_par(const unsigned int w, const unsigned int h) ++{ ++ if (((w == 720 || w == 704) && (h == 480 || h == 576)) || ++ ((w == 360 || w == 352) && (h == 240 || h == 288))) ++ { ++ return (drmu_ufrac_t){.num = 4, .den = 3}; ++ } ++ return drmu_ufrac_reduce((drmu_ufrac_t){.num = w, .den = h}); ++} + ++drmu_ufrac_t ++drmu_util_guess_simple_mode_par(const drmu_mode_simple_params_t * const p) ++{ ++ if (p->par.den != 0 && p->par.num != 0) ++ return p->par; ++ return drmu_util_guess_par(p->width, p->height); ++} ++ ++void ++drmu_memcpy_2d(void * const dst_p, const size_t dst_stride, ++ const void * const src_p, const size_t src_stride, ++ const size_t width, const size_t height) ++{ ++ if (dst_stride == src_stride && dst_stride == width) { ++ memcpy(dst_p, src_p, width * height); ++ } ++ else { ++ size_t i; ++ char * d = dst_p; ++ const char * s = src_p; ++ for (i = 0; i != height; ++i) { ++ memcpy(d, s, width); ++ d += dst_stride; ++ s += src_stride; ++ } ++ } ++} diff --git a/modules/video_output/drmu/drmu_util.h b/modules/video_output/drmu/drmu_util.h new file mode 100644 -index 0000000000..9c4ea46e4c +index 0000000000..3d6cdd6103 --- /dev/null +++ b/modules/video_output/drmu/drmu_util.h -@@ -0,0 +1,30 @@ +@@ -0,0 +1,58 @@ +#ifndef _DRMU_DRMU_UTIL_H +#define _DRMU_DRMU_UTIL_H + ++#include ++ ++#include "drmu_math.h" ++ +#ifdef __cplusplus +extern "C" { +#endif @@ -24388,10 +25894,34 @@ index 0000000000..9c4ea46e4c +char * drmu_util_parse_mode_simple_params(const char * s, struct drmu_mode_simple_params_s * const p); + +// Simple params to mode string -+char * drmu_util_simple_param_to_mode_str(char * buf, size_t buflen, const drmu_mode_simple_params_t * const p); ++char * drmu_util_simple_param_to_mode_str(char * buf, size_t buflen, const struct drmu_mode_simple_params_s * const p); + +#define drmu_util_simple_mode(p) drmu_util_simple_param_to_mode_str((char[64]){0}, 64, (p)) + ++// Given width & height guess par. Spots Likely SD and returns 4:3 otherwise reduced w:h ++drmu_ufrac_t drmu_util_guess_par(const unsigned int w, const unsigned int h); ++// Get a par from simple_params. par can be zero & if so then guess ++drmu_ufrac_t drmu_util_guess_simple_mode_par(const struct drmu_mode_simple_params_s * const p); ++ ++// Misc memcpy util ++ ++// Simple 2d memcpy ++void drmu_memcpy_2d(void * const dst_p, const size_t dst_stride, ++ const void * const src_p, const size_t src_stride, ++ const size_t width, const size_t height); ++// 'FB' copy ++static inline void ++drmu_memcpy_rect(void * const dst_p, const size_t dst_stride, const drmu_rect_t dst_rect, ++ const void * const src_p, const size_t src_stride, const drmu_rect_t src_rect, ++ const unsigned int pixel_stride) ++{ ++ drmu_memcpy_2d((char *)dst_p + dst_rect.x * pixel_stride + dst_rect.y * dst_stride, dst_stride, ++ (char *)src_p + src_rect.x * pixel_stride + src_rect.y * src_stride, src_stride, ++ (src_rect.w < dst_rect.w ? src_rect.w : dst_rect.w) * pixel_stride, ++ src_rect.h < dst_rect.h ? src_rect.h : dst_rect.h); ++} ++ ++ +#ifdef __cplusplus +} +#endif @@ -24400,10 +25930,10 @@ index 0000000000..9c4ea46e4c + diff --git a/modules/video_output/drmu/drmu_vlc.c b/modules/video_output/drmu/drmu_vlc.c new file mode 100644 -index 0000000000..e5930f59fc +index 0000000000..506de90fbb --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc.c -@@ -0,0 +1,352 @@ +@@ -0,0 +1,353 @@ +#include "drmu_vlc.h" +#include "drmu_fmts.h" +#include "drmu_log.h" @@ -24556,12 +26086,12 @@ index 0000000000..e5930f59fc +{ + struct hdr_output_metadata meta; + -+ drmu_fb_int_color_set(dfb, ++ drmu_fb_color_set(dfb, + fb_vlc_color_encoding(&pic->format), + fb_vlc_color_range(&pic->format), + fb_vlc_colorspace(&pic->format)); + -+ drmu_fb_int_chroma_siting_set(dfb, fb_vlc_chroma_siting(&pic->format)); ++ drmu_fb_chroma_siting_set(dfb, fb_vlc_chroma_siting(&pic->format)); + + drmu_fb_hdr_metadata_set(dfb, pic_hdr_metadata(&meta, &pic->format) == 0 ? &meta : NULL); +} @@ -24718,21 +26248,22 @@ index 0000000000..e5930f59fc + const drmu_fmt_info_t *const f = drmu_fb_format_info_get(dfb); + unsigned int hdiv = drmu_fmt_info_hdiv(f, plane_n); + unsigned int wdiv = drmu_fmt_info_wdiv(f, plane_n); -+ const unsigned int bpp = drmu_fmt_info_pixel_bits(f); ++ const unsigned int bypp = (drmu_fmt_info_pixel_bits(f) + 7) / 8; + const uint32_t pitch_n = drmu_fb_pitch(dfb, plane_n); -+ const drmu_rect_t crop = drmu_fb_crop_frac(dfb); ++ const drmu_rect_t crop = drmu_rect_shr16_rnd(drmu_fb_crop_frac(dfb)); + + if (pitch_n == 0) { + return (plane_t) {.p_pixels = NULL }; + } + + return (plane_t){ -+ .p_pixels = drmu_fb_data(dfb, plane_n), ++ .p_pixels = (uint8_t *)drmu_fb_data(dfb, plane_n) + ++ pitch_n * (crop.y / hdiv) + (crop.x / wdiv) * bypp, + .i_lines = drmu_fb_height(dfb) / hdiv, + .i_pitch = pitch_n, -+ .i_pixel_pitch = bpp / 8, -+ .i_visible_lines = (crop.h >> 16) / hdiv, -+ .i_visible_pitch = ((crop.w >> 16) * bpp / 8) / wdiv ++ .i_pixel_pitch = bypp, ++ .i_visible_lines = crop.h / hdiv, ++ .i_visible_pitch = (crop.w / wdiv) * bypp + }; +} + @@ -24845,10 +26376,10 @@ index 0000000000..5c19141930 + diff --git a/modules/video_output/drmu/drmu_vlc_fmts.c b/modules/video_output/drmu/drmu_vlc_fmts.c new file mode 100644 -index 0000000000..862b89eef5 +index 0000000000..3e12095b62 --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc_fmts.c -@@ -0,0 +1,237 @@ +@@ -0,0 +1,246 @@ +#include "drmu_vlc_fmts.h" + +#include @@ -24905,25 +26436,26 @@ index 0000000000..862b89eef5 + I2(VLC_CODEC_I444, DRM_FORMAT_YUV444), + { VLC_CODEC_J444, DRM_FORMAT_YUV444, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_FULL_RANGE }, +#if HAS_DRMPRIME -+ { VLC_CODEC_DRM_PRIME_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, -+ { VLC_CODEC_DRM_PRIME_NV12, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, -+ { VLC_CODEC_DRM_PRIME_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_DRMP }, -+ { VLC_CODEC_DRM_PRIME_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_DRMP }, -+ { VLC_CODEC_DRM_PRIME_RGB32, DRM_FORMAT_XRGB8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_NV12, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_RGB32, DRM_FORMAT_XRGB8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, +#endif +#if HAS_ZC_CMA -+ { VLC_CODEC_MMAL_ZC_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, -+ { VLC_CODEC_MMAL_ZC_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_ZC }, -+ { VLC_CODEC_MMAL_ZC_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_ZC }, -+ { VLC_CODEC_MMAL_ZC_RGB32, DRM_FORMAT_RGBX8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128, DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128, DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_RGB32, DRM_FORMAT_RGBX8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, +#endif + + RM(VLC_CODEC_RGB32, DRM_FORMAT_XRGB8888, 0xff0000, 0xff00, 0xff), + RM(VLC_CODEC_RGB32, DRM_FORMAT_XBGR8888, 0xff, 0xff00, 0xff0000), + RM(VLC_CODEC_RGB32, DRM_FORMAT_RGBX8888, 0xff000000, 0xff0000, 0xff00), + RM(VLC_CODEC_RGB32, DRM_FORMAT_BGRX8888, 0xff00, 0xff0000, 0xff000000), -+ RM(VLC_CODEC_RGB24, DRM_FORMAT_RGB888, 0xff0000, 0xff00, 0xff), -+ RM(VLC_CODEC_RGB24, DRM_FORMAT_BGR888, 0xff, 0xff00, 0xff0000), ++ // The 24-bit versions seem to have BE masks?! ++ RM(VLC_CODEC_RGB24, DRM_FORMAT_BGR888, 0xff0000, 0xff00, 0xff), ++ RM(VLC_CODEC_RGB24, DRM_FORMAT_RGB888, 0xff, 0xff00, 0xff0000), + RM(VLC_CODEC_RGB16, DRM_FORMAT_RGB565, 0xf800, 0x7e0, 0x1f), + RM(VLC_CODEC_RGB16, DRM_FORMAT_BGR565, 0x1f, 0x7e0, 0xf800), + @@ -24958,14 +26490,22 @@ index 0000000000..862b89eef5 + return drmu_vlc_fmt_info_find_vlc_next(vf_vlc, NULL); +} + ++// Remove any params from a modifier ++static inline uint64_t canon_mod(const uint64_t m) ++{ ++ return fourcc_mod_is_vendor(m, BROADCOM) ? fourcc_mod_broadcom_mod(m) : m; ++} ++ +const drmu_vlc_fmt_info_t * +drmu_vlc_fmt_info_find_drm_next(const uint32_t pixelformat, const uint64_t modifier, const drmu_vlc_fmt_info_t * f) +{ ++ const uint64_t cmod = canon_mod(modifier); ++ + f = (f == NULL) ? fmt_table : f + 1; + + for (; f->vlc_chroma != 0; ++f) + { -+ if (f->drm_pixelformat != pixelformat || f->drm_modifier != modifier) ++ if (f->drm_pixelformat != pixelformat || f->drm_modifier != cmod) + continue; + // Only return the "base" version + if (f->flags != 0) @@ -25300,10 +26840,10 @@ index 0000000000..9bf2d7b25b + diff --git a/modules/video_output/drmu/pollqueue.c b/modules/video_output/drmu/pollqueue.c new file mode 100644 -index 0000000000..6dc76bd431 +index 0000000000..6cf81ba0fb --- /dev/null +++ b/modules/video_output/drmu/pollqueue.c -@@ -0,0 +1,502 @@ +@@ -0,0 +1,545 @@ +#include "pollqueue.h" + +#include @@ -25366,7 +26906,11 @@ index 0000000000..6dc76bd431 + void *v; + } prepost; + ++ void (* exit_fn)(void * v); ++ void * exit_v; ++ + bool kill; ++ bool join_req; // On thread exit do not detach + bool no_prod; + + bool sig_seq; // Signal cond when seq incremented @@ -25685,11 +27229,21 @@ index 0000000000..6dc76bd431 + pthread_mutex_unlock(&pq->lock); +fail_unlocked: + -+ polltask_free(pq->prod_pt); -+ pthread_cond_destroy(&pq->cond); -+ pthread_mutex_destroy(&pq->lock); -+ close(pq->prod_fd); -+ free(pq); ++ { ++ void (*const exit_fn)(void *v) = pq->exit_fn; ++ void * const exit_v = pq->exit_v; ++ ++ polltask_free(pq->prod_pt); ++ pthread_cond_destroy(&pq->cond); ++ pthread_mutex_destroy(&pq->lock); ++ close(pq->prod_fd); ++ if (!pq->join_req) ++ pthread_detach(pthread_self()); ++ free(pq); ++ ++ if (exit_fn) ++ exit_fn(exit_v); ++ } + + return NULL; +} @@ -25720,7 +27274,7 @@ index 0000000000..6dc76bd431 + }; + + pq->prod_fd = eventfd(0, EFD_NONBLOCK); -+ if (pq->prod_fd == 1) ++ if (pq->prod_fd == -1) + goto fail1; + pq->prod_pt = polltask_new(pq, pq->prod_fd, POLLIN, prod_fn, pq); + if (!pq->prod_pt) @@ -25747,16 +27301,18 @@ index 0000000000..6dc76bd431 + + if (pthread_equal(worker, pthread_self())) { + pq->kill = true; -+ pollqueue_prod(pq); -+ pthread_detach(worker); ++ if (!pq->no_prod) ++ pollqueue_prod(pq); + } + else + { + pthread_mutex_lock(&pq->lock); + pq->kill = true; -+ pollqueue_prod(pq); ++ // Must prod inside lock here as otherwise there is a potential race ++ // where the worker terminates and pq is freed before the prod ++ if (!pq->no_prod) ++ pollqueue_prod(pq); + pthread_mutex_unlock(&pq->lock); -+ pthread_join(worker, NULL); + } +} + @@ -25780,6 +27336,26 @@ index 0000000000..6dc76bd431 + pollqueue_free(pq); +} + ++void pollqueue_finish(struct pollqueue **const ppq) ++{ ++ struct pollqueue * pq = *ppq; ++ pthread_t worker; ++ ++ if (!pq) ++ return; ++ ++ pq->join_req = true; ++ worker = pq->worker; ++ ++ pollqueue_unref(&pq); ++ ++ pthread_join(worker, NULL); ++ ++ // Delay zapping the ref until after the join as it is legit for the ++ // remaining active polltasks to use it. ++ *ppq = NULL; ++} ++ +void pollqueue_set_pre_post(struct pollqueue *const pq, + void (*fn_pre)(void *v, struct pollfd *pfd), + void (*fn_post)(void *v, short revents), @@ -25806,12 +27382,19 @@ index 0000000000..6dc76bd431 + pthread_mutex_unlock(&pq->lock); +} + ++void pollqueue_set_exit(struct pollqueue *const pq, ++ void (* const exit_fn)(void * v), void * v) ++{ ++ pq->exit_fn = exit_fn; ++ pq->exit_v = v; ++} ++ diff --git a/modules/video_output/drmu/pollqueue.h b/modules/video_output/drmu/pollqueue.h new file mode 100644 -index 0000000000..b4f9348986 +index 0000000000..dd9c22fe5e --- /dev/null +++ b/modules/video_output/drmu/pollqueue.h -@@ -0,0 +1,78 @@ +@@ -0,0 +1,87 @@ +#ifndef POLLQUEUE_H_ +#define POLLQUEUE_H_ + @@ -25869,11 +27452,16 @@ index 0000000000..b4f9348986 + +// Unref a pollqueue +// Will be deleted once all polltasks (Qed or otherwise) are deleted too -+// If called from outside the polltask thread and this causes the pollqueue -+// to be deleted then it will wait for the polltask thread to terminate -+// before returning. ++// Will not wait for polltask termination whether or not this is the last ++// ref. +void pollqueue_unref(struct pollqueue **const ppq); + ++// Unrefs a pollqueue and then waits for the polltask thread to terminate ++// before returning. *ppq is not set to NULL until after the polltask thread ++// has terminated so it is safe for use by any remaining polltasks (e.g. for ++// creating other tasks that need to complete before finishing.) ++void pollqueue_finish(struct pollqueue **const ppq); ++ +// Add a reference to a pollqueue +struct pollqueue * pollqueue_ref(struct pollqueue *const pq); + @@ -25889,6 +27477,10 @@ index 0000000000..b4f9348986 + void (*fn_post)(void *v, short revents), + void *v); + ++// Set callback to execture on the poll thread when it exits ++void pollqueue_set_exit(struct pollqueue *const pq, ++ void (* const exit_fn)(void * v), void * v); ++ +#endif /* POLLQUEUE_H_ */ diff --git a/modules/video_output/opengl/display.c b/modules/video_output/opengl/display.c index ec671109bc..46f693a13a 100644 @@ -27445,10 +29037,10 @@ index 08ad5022f9..b1feb622d8 100644 vlc_module_end() diff --git a/modules/video_output/wayland/wl_dmabuf.c b/modules/video_output/wayland/wl_dmabuf.c new file mode 100644 -index 0000000000..14d06d147f +index 0000000000..c7fb902936 --- /dev/null +++ b/modules/video_output/wayland/wl_dmabuf.c -@@ -0,0 +1,2330 @@ +@@ -0,0 +1,2420 @@ +/** + * @file shm.c + * @brief Wayland shared memory video output module for VLC media player @@ -27533,6 +29125,11 @@ index 0000000000..14d06d147f +#define WL_DMABUF_CHEQUERBOARD_TEXT N_("Chequerboard background") +#define WL_DMABUF_CHEQUERBOARD_LONGTEXT N_("Fill unused window area with chequerboard rather than black") + ++#define WL_DMABUF_STATS_NAME "wl-dmabuf-stats" ++#define WL_DMABUF_STATS_TEXT N_("Display some display stats") ++#define WL_DMABUF_STATS_LONGTEXT N_("When display is closed report frames displayed/discarded and avg fps. "\ ++ "N.B. Unfortunately current implementation cannot track frames discarded by Wayland before display") ++ +typedef struct fmt_ent_s { + uint32_t fmt; + int32_t pri; @@ -27577,6 +29174,7 @@ index 0000000000..14d06d147f + enum wl_output_transform trans; + vout_display_place_t src_rect; + vout_display_place_t dst_rect; ++ vout_display_place_t orig_rect; + + atomic_int ready; + @@ -27590,6 +29188,10 @@ index 0000000000..14d06d147f + struct wl_subsurface * subsurface; + struct wp_viewport * viewport; + ++ bool buffer_attached; ++ bool commit_req; ++ int commit_parent; ++ + enum wl_output_transform trans; + vout_display_place_t src_rect; + vout_display_place_t dst_rect; @@ -27610,9 +29212,9 @@ index 0000000000..14d06d147f +#endif +} w_bound_t; + -+#define COMMIT_BKG 0 -+#define COMMIT_VID 1 -+#define COMMIT_SUB 2 ++#define PLANE_BKG 0 ++#define PLANE_VID 1 ++#define PLANE_SUB 2 + +struct vout_display_sys_t +{ @@ -27627,9 +29229,9 @@ index 0000000000..14d06d147f + + int x; + int y; -+ bool video_attached; + bool use_shm; + bool chequerboard; ++ bool want_stats; + + struct wp_viewport * bkg_viewport; + // Current size of background viewport if we have one @@ -27637,6 +29239,10 @@ index 0000000000..14d06d147f + unsigned int bkg_w; + unsigned int bkg_h; + ++ enum wl_output_transform video_trans; ++ vout_display_place_t video_src_rect; ++ vout_display_place_t video_dst_rect; ++ +#if CHECK_VDRE_COUNTS + atomic_int vdre_check_bkg; + atomic_int vdre_check_fg; @@ -27648,10 +29254,9 @@ index 0000000000..14d06d147f + struct pollqueue * speq; + + picpool_ctl_t * subpic_pool; -+ subplane_t video_plane[1]; -+ subplane_t subplanes[MAX_SUBPICS]; -+ bool commit_req[MAX_SUBPICS + 2]; -+ subpic_ent_t video_spe; ++ subplane_t planes[MAX_SUBPICS + 2]; ++ subpic_ent_t * video_spe_prep; ++ struct wl_callback * video_frame_callback; + vlc_fourcc_t * subpic_chromas; + + struct wl_region * region_none; @@ -27659,12 +29264,32 @@ index 0000000000..14d06d147f + + fmt_list_t dmabuf_fmts; + fmt_list_t shm_fmts; ++ ++ struct display_stats_s { ++ unsigned int frame_n; ++ unsigned int frame_frame; ++ unsigned int frame_display; ++ unsigned int frame_discard; ++ vlc_tick_t time_frame0; ++ vlc_tick_t time_frameN; ++ } stats; +}; + + +static struct wl_surface * bkg_surface_get_lock(vout_display_t * const vd, vout_display_sys_t * const sys); +static void bkg_surface_unlock(vout_display_t * const vd, vout_display_sys_t * const sys); + ++static void ++msg_stats(vout_display_t * const vd, const struct display_stats_s * const s) ++{ ++ unsigned int tframes = (s->frame_n + s->frame_discard); ++ unsigned int frx1000 = tframes < 2 ? 0 : ++ (unsigned int)((uint64_t)(tframes - 1) * 1000000000ULL / (s->time_frameN - s->time_frame0)); ++ msg_Info(vd, "Frames: Total: %d, Discarded %d, Display %d FpS(total):%d.%03d", ++ tframes, s->frame_discard, s->frame_display, ++ frx1000 / 1000, frx1000 % 1000); ++} ++ + +static inline struct wl_display * +video_display(const vout_display_sys_t * const sys) @@ -27675,7 +29300,7 @@ index 0000000000..14d06d147f +static inline struct wl_surface * +video_surface(const vout_display_sys_t * const sys) +{ -+ return sys->video_plane->surface; ++ return sys->planes[PLANE_VID].surface; +} + +static inline struct wl_compositor * @@ -27758,6 +29383,17 @@ index 0000000000..14d06d147f + }; +} + ++static inline vout_display_place_t ++place_zoffset(const vout_display_place_t s) ++{ ++ return (vout_display_place_t){ ++ .x = 0, ++ .y = 0, ++ .width = s.width, ++ .height = s.height ++ }; ++} ++ +static inline bool +place_xy_eq(const vout_display_place_t a, const vout_display_place_t b) +{ @@ -27860,30 +29496,6 @@ index 0000000000..14d06d147f + }; +} + -+#if 0 -+static vout_display_place_t -+rect_transform(vout_display_place_t s, const vout_display_place_t c, const video_transform_t t) -+{ -+ if (is_vxf_transpose(t)) -+ s = vplace_transpose(s); -+ if (is_vxf_hflip(t)) -+ s = vplace_hflip(s, c); -+ if (is_vxf_vflip(t) != 0) -+ s = vplace_vflip(s, c); -+ return s; -+} -+ -+static void -+place_dest_rect(vout_display_t * const vd, -+ const vout_display_cfg_t * const cfg, -+ const video_format_t * fmt) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), -+ sys->display_rect, sys->dest_transform); -+} -+#endif -+ +static int +fmt_list_add(fmt_list_t * const fl, uint32_t fmt, uint64_t mod, int32_t pri) +{ @@ -28397,7 +30009,6 @@ index 0000000000..14d06d147f +{ + unsigned int w = src->format.i_width; + unsigned int h = src->format.i_height; -+ struct zwp_linux_buffer_params_v1 *params = NULL; + uint64_t mod; + const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format, &mod); + struct dmabuf_h * dh = NULL; @@ -28468,8 +30079,6 @@ index 0000000000..14d06d147f + return VLC_SUCCESS; + +error: -+ if (params) -+ zwp_linux_buffer_params_v1_destroy(params); + vdre_delete(pVdre); + return VLC_EGENERIC; +} @@ -28488,10 +30097,9 @@ index 0000000000..14d06d147f +{ + vout_display_sys_t * const sys = vd->sys; + -+#if TRACE_ALL -+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, -+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); -+#endif ++ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d, count=%u", __func__, ++ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, ++ vd->source.i_width, vd->source.i_height, count); + + if (sys->vlc_pic_pool == NULL) + sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); @@ -28552,12 +30160,19 @@ index 0000000000..14d06d147f +} + +static void -+subpic_ent_attach(struct wl_surface * const surface, subpic_ent_t * const spe, eq_env_t * eq) ++subpic_ent_attach(subplane_t * const plane, subpic_ent_t * const spe, eq_env_t * const eq) +{ ++ struct wl_surface *const surface = plane->surface; ++ + if (spe->wb == NULL) + { + vdre_delete(&spe->vdre); -+ wl_surface_attach(surface, NULL, 0, 0); ++ if (plane->buffer_attached) ++ { ++ wl_surface_attach(surface, NULL, 0, 0); ++ plane->buffer_attached = false; ++ plane->commit_req = true; ++ } + } + else + { @@ -28567,6 +30182,8 @@ index 0000000000..14d06d147f + spe->vdre = NULL; + spe->wb = NULL; + wl_surface_damage(surface, 0, 0, INT32_MAX, INT32_MAX); ++ plane->buffer_attached = true; ++ plane->commit_req = true; + } +} + @@ -28596,7 +30213,7 @@ index 0000000000..14d06d147f +} + +static void -+spe_update_rect(subpic_ent_t * const spe, vout_display_sys_t * const sys, ++spe_update_rect(subpic_ent_t * const spe, + const subpicture_t * const spic, + const subpicture_region_t * const sreg) +{ @@ -28606,31 +30223,23 @@ index 0000000000..14d06d147f + .width = sreg->fmt.i_visible_width, + .height = sreg->fmt.i_visible_height, + }; -+ spe->dst_rect = place_rescale( -+ (vout_display_place_t) { -+ .x = sreg->i_x, -+ .y = sreg->i_y, -+ .width = sreg->fmt.i_visible_width, -+ .height = sreg->fmt.i_visible_height, -+ }, -+ (vout_display_place_t) { -+ .x = 0, -+ .y = 0, -+ .width = sys->video_spe.dst_rect.width, -+ .height = sys->video_spe.dst_rect.height, -+ }, -+ (vout_display_place_t) { -+ .x = 0, -+ .y = 0, -+ .width = spic->i_original_picture_width, -+ .height = spic->i_original_picture_height, -+ }); ++ spe->dst_rect = (vout_display_place_t) { ++ .x = sreg->i_x, ++ .y = sreg->i_y, ++ .width = sreg->fmt.i_visible_width, ++ .height = sreg->fmt.i_visible_height, ++ }; ++ spe->orig_rect = (vout_display_place_t) { ++ .x = 0, ++ .y = 0, ++ .width = spic->i_original_picture_width, ++ .height = spic->i_original_picture_height, ++ }; +} + +static subpic_ent_t * -+spe_new(vout_display_t * const vd, vout_display_sys_t * const sys, -+ const subpicture_t * const spic, -+ const subpicture_region_t * const sreg) ++spe_new_pic(vout_display_t * const vd, vout_display_sys_t * const sys, ++ picture_t * const pic) +{ + subpic_ent_t * const spe = calloc(1, sizeof(*spe)); + @@ -28641,16 +30250,32 @@ index 0000000000..14d06d147f + spe->vd = vd; + spe->sys = sys; + -+ if (sreg == NULL || sreg->i_alpha == 0) ++ if (pic == NULL) + { + atomic_init(&spe->ready, 1); + return spe; + } + -+ spe->pic = picture_Hold(sreg->p_picture); ++ spe->pic = picture_Hold(pic); ++ spe->alpha = 0xff; ++ return spe; ++} ++ ++ ++static subpic_ent_t * ++spe_new(vout_display_t * const vd, vout_display_sys_t * const sys, ++ const subpicture_t * const spic, ++ const subpicture_region_t * const sreg) ++{ ++ subpic_ent_t * const spe = spe_new_pic(vd, sys, ++ (sreg == NULL || sreg->i_alpha == 0) ? NULL : sreg->p_picture); ++ ++ if (spe_no_pic(spe)) ++ return spe; ++ + spe->alpha = sreg->i_alpha; + -+ spe_update_rect(spe, sys, spic, sreg); ++ spe_update_rect(spe, spic, sreg); + + spe->pt = polltask_new_timer(sys->speq, spe_convert_cb, spe); + @@ -28682,7 +30307,7 @@ index 0000000000..14d06d147f +static void +commit_req(vout_display_sys_t * const sys, const unsigned int layer) +{ -+ sys->commit_req[layer] = true; ++ sys->planes[layer].commit_req = true; +} + +static void @@ -28691,32 +30316,29 @@ index 0000000000..14d06d147f + int i; + bool flush_req = false; + -+ for (i = MAX_SUBPICS - 1; i >= 0; --i) ++ for (i = MAX_SUBPICS + PLANE_SUB - 1; i >= PLANE_VID; --i) + { -+ if (sys->commit_req[i + COMMIT_SUB]) ++ if (sys->planes[i].commit_req) + { -+ sys->commit_req[i + COMMIT_SUB] = false; -+ wl_surface_commit(sys->subplanes[i].surface); ++ sys->planes[i].commit_req = false; ++ wl_surface_commit(sys->planes[i].surface); + flush_req = true; + } + } -+ if (sys->commit_req[COMMIT_VID]) -+ { -+ sys->commit_req[COMMIT_VID] = false; -+ wl_surface_commit(video_surface(sys)); -+ flush_req = true; -+ } -+ if (sys->commit_req[COMMIT_BKG]) ++ if (sys->planes[PLANE_BKG].commit_req) + { + struct wl_surface * const bkg_surface = bkg_surface_get_lock(vd, sys); + if (bkg_surface != NULL) + { -+ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); -+ wl_surface_commit(bkg_surface); ++ if (sys->bkg_viewport) ++ { ++ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); ++ wl_surface_commit(bkg_surface); ++ } + bkg_surface_unlock(vd, sys); + flush_req = true; + } -+ sys->commit_req[COMMIT_BKG] = false; ++ sys->planes[PLANE_BKG].commit_req = false; + } + if (flush_req) + wl_display_flush(video_display(sys)); @@ -28732,23 +30354,23 @@ index 0000000000..14d06d147f +} + +static void ++plane_clear(subplane_t * const plane) ++{ ++ spe_delete(&plane->spe_next); ++ spe_delete(&plane->spe_cur); ++ plane->buffer_attached = false; ++ clear_surface_buffer(plane->surface); ++} ++ ++static void +clear_all_buffers(vout_display_sys_t * const sys, const bool bkg_valid) +{ -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) -+ { -+ subplane_t *const plane = sys->subplanes + i; -+ spe_delete(&plane->spe_next); -+ spe_delete(&plane->spe_cur); -+ clear_surface_buffer(plane->surface); -+ } -+ -+ clear_surface_buffer(video_surface(sys)); -+ sys->video_attached = false; ++ for (unsigned int i = MAX_SUBPICS + PLANE_SUB - 1; i >= PLANE_VID; --i) ++ plane_clear(sys->planes + i); ++ spe_delete(&sys->video_spe_prep); + + if (bkg_valid) + clear_surface_buffer(sys->last_embed_surface); -+ -+ subpic_ent_flush(&sys->video_spe); +} + +static void @@ -28758,6 +30380,7 @@ index 0000000000..14d06d147f + subsurface_destroy(&spl->subsurface); + surface_destroy(&spl->surface); + // Zap all tracking vars ++ spl->buffer_attached = false; + spl->trans = 0; + memset(&spl->src_rect, 0, sizeof(spl->src_rect)); + memset(&spl->dst_rect, 0, sizeof(spl->dst_rect)); @@ -28766,9 +30389,11 @@ index 0000000000..14d06d147f +static int +plane_create(vout_display_sys_t * const sys, subplane_t * const plane, + struct wl_surface * const parent, ++ const int commit_parent, + struct wl_surface * const above, + const bool sync) +{ ++ plane->commit_parent = commit_parent; + if ((plane->surface = wl_compositor_create_surface(video_compositor(sys))) == NULL || + (plane->subsurface = wl_subcompositor_get_subsurface(sys->bound.subcompositor, plane->surface, parent)) == NULL || + (plane->viewport = wp_viewporter_get_viewport(sys->bound.viewporter, plane->surface)) == NULL) @@ -28788,10 +30413,8 @@ index 0000000000..14d06d147f + clear_all_buffers(sys, bkg_valid); + + // Free subpic resources -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) -+ plane_destroy(sys->subplanes + i); -+ -+ plane_destroy(sys->video_plane); ++ for (unsigned int i = MAX_SUBPICS + PLANE_SUB - 1; i >= PLANE_VID; --i) ++ plane_destroy(sys->planes + i); + + viewport_destroy(&sys->bkg_viewport); +} @@ -28834,17 +30457,18 @@ index 0000000000..14d06d147f +make_subpic_surfaces(vout_display_t * const vd, vout_display_sys_t * const sys) +{ + unsigned int i; ++ subplane_t * const subplanes = sys->planes + PLANE_SUB; + struct wl_surface * const surface = video_surface(sys); + struct wl_surface * below = surface; + int rv; + -+ if (sys->subplanes[0].surface) ++ if (subplanes[0].surface) + return VLC_SUCCESS; + + for (i = 0; i != MAX_SUBPICS; ++i) + { -+ subplane_t * const plane = sys->subplanes + i; -+ if ((rv = plane_create(sys, plane, surface, below, true)) != 0) ++ subplane_t * const plane = subplanes + i; ++ if ((rv = plane_create(sys, plane, surface, PLANE_VID, below, true)) != 0) + { + msg_Err(vd, "%s: Failed to create subpic plane %d", __func__, i); + return rv; @@ -28943,15 +30567,15 @@ index 0000000000..14d06d147f + + wl_surface_damage(bkg_surface, 0, 0, INT32_MAX, INT32_MAX); + -+ if (plane_create(sys, sys->video_plane, bkg_surface, bkg_surface, false) != 0) ++ if (plane_create(sys, sys->planes + PLANE_VID, bkg_surface, PLANE_BKG, bkg_surface, false) != 0) + { + msg_Err(vd, "Failed to create video plane"); + goto err_unlock; + } + -+ wl_surface_set_opaque_region(sys->video_plane->surface, sys->region_all); ++ wl_surface_set_opaque_region(video_surface(sys), sys->region_all); + -+ commit_req(sys, COMMIT_BKG); ++ commit_req(sys, PLANE_BKG); + + bkg_surface_unlock(vd, sys); + @@ -29047,49 +30671,132 @@ index 0000000000..14d06d147f +{ + vout_display_sys_t * const sys = vd->sys; + -+ vout_display_PlacePicture(&sys->video_spe.dst_rect, &vd->source, cfg, true); -+ sys->video_spe.trans = transform_from_fmt(&vd->fmt, &sys->video_spe.src_rect); ++ vout_display_PlacePicture(&sys->video_dst_rect, &vd->source, cfg, true); ++ sys->video_trans = transform_from_fmt(&vd->source, &sys->video_src_rect); ++} ++ ++static const drmu_vlc_fmt_info_t * ++find_fmt_fallback(vout_display_t * const vd, const fmt_list_t * const flist, const vlc_fourcc_t * fallback) ++{ ++ const drmu_vlc_fmt_info_t * fmti_best = NULL; ++ int pri_best = INT_MAX; ++ ++ for (; *fallback != 0; ++fallback) ++ { ++ const video_frame_format_t vf = {.i_chroma = *fallback}; ++ const drmu_vlc_fmt_info_t * fmti = NULL; ++ ++ msg_Dbg(vd, "Try %s", drmu_log_fourcc(*fallback)); ++ ++ for (fmti = drmu_vlc_fmt_info_find_vlc(&vf); ++ fmti != NULL; ++ fmti = drmu_vlc_fmt_info_find_vlc_next(&vf, fmti)) ++ { ++ const int pri = fmt_list_find(flist, fmti); ++ msg_Dbg(vd, "Try %s -> %s %"PRIx64": %d", drmu_log_fourcc(*fallback), ++ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(fmti)), ++ drmu_vlc_fmt_info_drm_modifier(fmti), pri); ++ if (pri >= 0 && pri < pri_best) ++ { ++ fmti_best = fmti; ++ pri_best = pri; ++ ++ // If we've got pri 0 then might as well stop now ++ if (pri == 0) ++ return fmti_best; ++ } ++ } ++ } ++ ++ return fmti_best; ++} ++ ++static const drmu_vlc_fmt_info_t * ++get_usable_format(vout_display_t * const vd, ++ const fmt_list_t * const flist, ++ const video_format_t * const fmt) ++{ ++ const drmu_vlc_fmt_info_t * pic_fmti; ++ ++ // Check PIC DRM format here ++ if ((pic_fmti = drmu_vlc_fmt_info_find_vlc(fmt)) == NULL || ++ fmt_list_find(flist, pic_fmti) < 0) ++ { ++ static const vlc_fourcc_t fallback2[] = { ++ VLC_CODEC_I420, ++ VLC_CODEC_RGB32, ++ 0 ++ }; ++ ++ msg_Warn(vd, "Could not find %s -> %s mod %#"PRIx64" in supported formats", ++ drmu_log_fourcc(fmt->i_chroma), ++ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(pic_fmti)), ++ drmu_vlc_fmt_info_drm_modifier(pic_fmti)); ++ ++ if ((pic_fmti = find_fmt_fallback(vd, flist, ++ vlc_fourcc_IsYUV(fmt->i_chroma) ? ++ vlc_fourcc_GetYUVFallback(fmt->i_chroma) : ++ vlc_fourcc_GetRGBFallback(fmt->i_chroma))) == NULL && ++ (pic_fmti = find_fmt_fallback(vd, flist, fallback2)) == NULL) { ++ msg_Warn(vd, "Failed to find any usable fallback format"); ++ } ++ } ++ return pic_fmti; ++} ++ ++static int ++set_req_format(vout_display_t * const vd, const vout_display_sys_t * const sys, video_format_t * const fmt) ++{ ++ const video_format_t * const src_fmt = &vd->source; ++ const drmu_vlc_fmt_info_t * const fmti = get_usable_format(vd, sys->use_shm ? &sys->shm_fmts : &sys->dmabuf_fmts, src_fmt); ++ ++ if (fmti == NULL) ++ return VLC_EGENERIC; ++ ++ *fmt = *src_fmt; ++ fmt->i_chroma = drmu_vlc_fmt_info_vlc_chroma(fmti); ++ drmu_vlc_fmt_info_vlc_rgb_masks(fmti, &fmt->i_rmask, &fmt->i_gmask, &fmt->i_bmask); ++ ++ return VLC_SUCCESS; +} + +static void -+plane_set_rect(vout_display_sys_t * const sys, subplane_t * const plane, const subpic_ent_t * const spe, -+ const unsigned int commit_this, const unsigned int commit_parent) ++plane_set_rect(vout_display_sys_t * const sys, subplane_t * const plane, const subpic_ent_t * const spe) +{ ++ // Always called after the attach ++ if (!plane->buffer_attached || spe == NULL) ++ return; ++ ++ const vout_display_place_t dst_rect = place_rescale(spe->dst_rect, ++ plane->commit_parent == PLANE_VID ? place_zoffset(sys->video_dst_rect) : sys->video_dst_rect, ++ spe->orig_rect); ++ + if (spe->trans != plane->trans) + { + wl_surface_set_buffer_transform(plane->surface, spe->trans); -+ commit_req(sys, commit_this); ++ plane->commit_req = true; + } + if (!place_eq(spe->src_rect, plane->src_rect)) + { + wp_viewport_set_source(plane->viewport, + wl_fixed_from_int(spe->src_rect.x), wl_fixed_from_int(spe->src_rect.y), + wl_fixed_from_int(spe->src_rect.width), wl_fixed_from_int(spe->src_rect.height)); -+ commit_req(sys, commit_this); ++ plane->commit_req = true; + } -+ if (!place_xy_eq(spe->dst_rect, plane->dst_rect)) ++ if (!place_xy_eq(dst_rect, plane->dst_rect)) + { -+ wl_subsurface_set_position(plane->subsurface, spe->dst_rect.x, spe->dst_rect.y); -+ commit_req(sys, commit_this); ++ wl_subsurface_set_position(plane->subsurface, dst_rect.x, dst_rect.y); ++ plane->commit_req = true; + } -+ if (!place_wh_eq(spe->dst_rect, plane->dst_rect)) ++ if (!place_wh_eq(dst_rect, plane->dst_rect)) + { -+ wp_viewport_set_destination(plane->viewport, spe->dst_rect.width, spe->dst_rect.height); -+ commit_req(sys, commit_parent); // Subsurface pos needs parent commit (video) ++ wp_viewport_set_destination(plane->viewport, dst_rect.width, dst_rect.height); ++ commit_req(sys, plane->commit_parent); // Subsurface pos needs parent commit (video) + } + + plane->trans = spe->trans; + plane->src_rect = spe->src_rect; -+ plane->dst_rect = spe->dst_rect; -+} -+ -+static void -+set_video_viewport(vout_display_sys_t * const sys) -+{ -+ if (!sys->video_attached) -+ return; -+ -+ plane_set_rect(sys, sys->video_plane, &sys->video_spe, COMMIT_VID, COMMIT_BKG); ++ plane->dst_rect = dst_rect; +} + +static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) @@ -29101,30 +30808,51 @@ index 0000000000..14d06d147f + msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); +#endif + -+ subpic_ent_flush(&sys->video_spe); // If somehow we have a buffer here - avoid leaking -+ if (drmu_format_vlc_to_drm_prime(&pic->format, NULL) == 0) -+ copy_subpic_to_w_buffer(vd, sys, pic, 0xff, &sys->video_spe.vdre, &sys->video_spe.wb); -+ else -+ do_display_dmabuf(vd, sys, pic, &sys->video_spe.vdre, &sys->video_spe.wb); -+ wl_display_flush(video_display(sys)); // Kick off any work required by Wayland ++ { ++ subpic_ent_t * const spe = spe_new_pic(vd, sys, pic); ++ if (spe == NULL) ++ { ++ msg_Err(vd, "Failed to create new video spe"); ++ return; ++ } ++ spe->trans = sys->video_trans; ++ spe->src_rect = sys->video_src_rect; ++ spe->dst_rect = sys->video_dst_rect; ++ spe->orig_rect = spe->dst_rect; ++ ++ if (sys->video_spe_prep) ++ { ++ msg_Err(vd, "Spe prep != NULL"); ++ spe_delete(&sys->video_spe_prep); ++ ++sys->stats.frame_discard; ++ } ++ sys->video_spe_prep = spe; ++ ++ if (drmu_format_vlc_to_drm_prime(&pic->format, NULL) == 0) ++ copy_subpic_to_w_buffer(vd, sys, pic, 0xff, &spe->vdre, &spe->wb); ++ else ++ do_display_dmabuf(vd, sys, pic, &spe->vdre, &spe->wb); ++ atomic_store(&spe->ready, 1); ++ wl_display_flush(video_display(sys)); // Kick off any work required by Wayland ++ } + + // Attempt to import the subpics + for (const subpicture_t * spic = subpic; spic != NULL; spic = spic->p_next) + { + for (const subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) + { -+ subplane_t * const plane = sys->subplanes + n; ++ subplane_t * const plane = sys->planes + n + PLANE_SUB; + + if (plane->spe_next != NULL) + { + if (!spe_changed(plane->spe_next, sreg)) -+ spe_update_rect(plane->spe_next, sys, spic, sreg); ++ spe_update_rect(plane->spe_next, spic, sreg); + // else if changed ignore as we are already doing stuff + } + else + { + if (!spe_changed(plane->spe_cur, sreg)) -+ spe_update_rect(plane->spe_cur, sys, spic, sreg); ++ spe_update_rect(plane->spe_cur, spic, sreg); + else + { + plane->spe_next = spe_new(vd, sys, spic, sreg); @@ -29140,7 +30868,7 @@ index 0000000000..14d06d147f + + // Clear any other entries + for (; n != MAX_SUBPICS; ++n) { -+ subplane_t * const plane = sys->subplanes + n; ++ subplane_t * const plane = sys->planes + n + PLANE_SUB; + if (plane->spe_next == NULL && spe_changed(plane->spe_cur, NULL)) + plane->spe_next = spe_new(vd, sys, NULL, NULL); + } @@ -29150,11 +30878,77 @@ index 0000000000..14d06d147f +#endif +} + ++static void ++do_resize(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ if (!sys->bkg_viewport) ++ return; ++ ++ for (unsigned int i = PLANE_VID; i != PLANE_SUB + MAX_SUBPICS; ++i) ++ { ++ subplane_t * const plane = sys->planes + i; ++ subpic_ent_t * spe = plane->spe_cur; ++ ++ plane_set_rect(sys, plane, spe); ++ } ++ ++ if (sys->bkg_viewport != NULL && (vd->cfg->display.width != sys->bkg_w || vd->cfg->display.height != sys->bkg_h)) ++ { ++ msg_Dbg(vd, "Resize background: %dx%d", vd->cfg->display.width, vd->cfg->display.height); ++ commit_req(sys, PLANE_BKG); ++ } ++ sys->bkg_w = vd->cfg->display.width; ++ sys->bkg_h = vd->cfg->display.height; ++} ++ ++static void ++do_display(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++// msg_Info(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); ++ ++ sys->stats.time_frameN = mdate(); ++ if (!sys->stats.time_frame0) ++ sys->stats.time_frame0 = sys->stats.time_frameN; ++ ++sys->stats.frame_n; ++ ++ if (spe_no_pic(sys->planes[PLANE_VID].spe_next)) ++ { ++ msg_Warn(vd, "%s: No current pic", __func__); ++ return; ++ } ++ ++ if (make_background_and_video(vd, sys) != 0) ++ { ++ msg_Warn(vd, "%s: Make background fail", __func__); ++ return; ++ } ++ make_subpic_surfaces(vd, sys); ++ ++ for (unsigned int i = PLANE_VID; i != PLANE_SUB + MAX_SUBPICS; ++i) ++ { ++ subplane_t * const plane = sys->planes + i; ++ subpic_ent_t * spe = plane->spe_cur; ++ ++ if (plane->spe_next && atomic_load(&plane->spe_next->ready)) ++ { ++ spe_delete(&plane->spe_cur); ++ spe = plane->spe_cur = plane->spe_next; ++ plane->spe_next = NULL; ++ subpic_ent_attach(plane, spe, sys->eq); ++ } ++ } ++ ++ do_resize(vd, sys); ++ ++ commit_do(vd, sys); ++ return; ++} ++ +static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) +{ + vout_display_sys_t * const sys = vd->sys; + -+#if TRACE_ALL ++#if TRACE_ALL || 1 + msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); +#endif + @@ -29166,45 +30960,23 @@ index 0000000000..14d06d147f + } + bkg_surface_unlock(vd, sys); + -+ if (make_background_and_video(vd, sys) != 0) ++ if (!sys->video_spe_prep) + { -+ msg_Warn(vd, "%s: Make background fail", __func__); ++ msg_Warn(vd, "No prepared video spe"); + goto done; + } -+ make_subpic_surfaces(vd, sys); + -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ if (sys->planes[PLANE_VID].spe_next) + { -+ subplane_t * const plane = sys->subplanes + i; -+ subpic_ent_t * spe = plane->spe_cur; -+ -+ if (plane->spe_next && atomic_load(&plane->spe_next->ready)) -+ { -+ spe_delete(&plane->spe_cur); -+ spe = plane->spe_cur = plane->spe_next; -+ plane->spe_next = NULL; -+ subpic_ent_attach(plane->surface, spe, sys->eq); -+ commit_req(sys, COMMIT_SUB + i); -+ } -+ -+ if (!spe_no_pic(spe)) -+ plane_set_rect(sys, plane, spe, COMMIT_SUB + i, COMMIT_VID); ++ msg_Warn(vd, "Current video spe discarded"); ++ spe_delete(&sys->planes[PLANE_VID].spe_next); ++ ++sys->stats.frame_discard; + } ++ sys->planes[PLANE_VID].spe_next = sys->video_spe_prep; ++ sys->video_spe_prep = NULL; + -+ if (!sys->video_spe.wb) -+ { -+ msg_Warn(vd, "Display called but no prepared pic buffer"); -+ } -+ else -+ { -+ subpic_ent_attach(video_surface(sys), &sys->video_spe, sys->eq); -+ sys->video_attached = true; -+ commit_req(sys, COMMIT_VID); -+ } -+ -+ set_video_viewport(sys); -+ -+ commit_do(vd, sys); ++ ++sys->stats.frame_display; ++ do_display(vd, sys); + +done: + if (subpic) @@ -29216,21 +30988,6 @@ index 0000000000..14d06d147f +#endif +} + -+static void ResetPictures(vout_display_t *vd) -+{ -+ vout_display_sys_t *sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s", __func__); -+#endif -+ -+ kill_pool(sys); -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s", __func__); -+#endif -+} -+ +static int Control(vout_display_t *vd, int query, va_list ap) +{ + vout_display_sys_t * const sys = vd->sys; @@ -29241,65 +30998,32 @@ index 0000000000..14d06d147f + + switch (query) + { -+ case VOUT_DISPLAY_RESET_PICTURES: -+ { -+ vout_display_place_t place; -+ video_format_t src; -+ -+ assert(sys->video_plane->viewport == NULL); -+ -+ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); -+ video_format_ApplyRotation(&src, &vd->source); -+ -+ vd->fmt.i_width = src.i_width * place.width -+ / src.i_visible_width; -+ vd->fmt.i_height = src.i_height * place.height -+ / src.i_visible_height; -+ vd->fmt.i_visible_width = place.width; -+ vd->fmt.i_visible_height = place.height; -+ vd->fmt.i_x_offset = src.i_x_offset * place.width -+ / src.i_visible_width; -+ vd->fmt.i_y_offset = src.i_y_offset * place.height -+ / src.i_visible_height; -+ ResetPictures(vd); ++ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: ++ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: ++ place_rects(vd, vd->cfg); ++ do_resize(vd, sys); ++ commit_do(vd, sys); + break; -+ } + + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: + case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: + case VOUT_DISPLAY_CHANGE_ZOOM: -+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: -+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: + { -+ const vout_display_cfg_t *cfg; -+ -+ if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT -+ || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) -+ { -+ cfg = vd->cfg; -+ } -+ else -+ { -+ cfg = va_arg(ap, const vout_display_cfg_t *); -+ } ++ const vout_display_cfg_t * const cfg = va_arg(ap, const vout_display_cfg_t *); + + place_rects(vd, cfg); -+ -+ set_video_viewport(sys); -+ -+ if (sys->bkg_viewport != NULL && (cfg->display.width != sys->bkg_w || cfg->display.height != sys->bkg_h)) -+ { -+ msg_Dbg(vd, "Resize background: %dx%d", cfg->display.width, cfg->display.height); -+ commit_req(sys, COMMIT_BKG); -+ } -+ sys->bkg_w = cfg->display.width; -+ sys->bkg_h = cfg->display.height; ++ do_resize(vd, sys); + commit_do(vd, sys); + break; + } ++ ++ case VOUT_DISPLAY_RESET_PICTURES: ++ msg_Err(vd, "Unexpected reset pictures"); ++ return VLC_EGENERIC; ++ + default: -+ msg_Err(vd, "unknown request %d", query); -+ return VLC_EGENERIC; ++ msg_Err(vd, "unknown request %d", query); ++ return VLC_EGENERIC; + } + +#if TRACE_ALL @@ -29496,42 +31220,6 @@ index 0000000000..14d06d147f + return 0; +} + -+static const drmu_vlc_fmt_info_t * -+find_fmt_fallback(vout_display_t * const vd, const fmt_list_t * const flist, const vlc_fourcc_t * fallback) -+{ -+ const drmu_vlc_fmt_info_t * fmti_best = NULL; -+ int pri_best = INT_MAX; -+ -+ for (; *fallback != 0; ++fallback) -+ { -+ const video_frame_format_t vf = {.i_chroma = *fallback}; -+ const drmu_vlc_fmt_info_t * fmti = NULL; -+ -+ msg_Dbg(vd, "Try %s", drmu_log_fourcc(*fallback)); -+ -+ for (fmti = drmu_vlc_fmt_info_find_vlc(&vf); -+ fmti != NULL; -+ fmti = drmu_vlc_fmt_info_find_vlc_next(&vf, fmti)) -+ { -+ const int pri = fmt_list_find(flist, fmti); -+ msg_Dbg(vd, "Try %s -> %s %"PRIx64": %d", drmu_log_fourcc(*fallback), -+ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(fmti)), -+ drmu_vlc_fmt_info_drm_modifier(fmti), pri); -+ if (pri >= 0 && pri < pri_best) -+ { -+ fmti_best = fmti; -+ pri_best = pri; -+ -+ // If we've got pri 0 then might as well stop now -+ if (pri == 0) -+ return fmti_best; -+ } -+ } -+ } -+ -+ return fmti_best; -+} -+ +static void Close(vlc_object_t *obj) +{ + vout_display_t * const vd = (vout_display_t *)obj; @@ -29545,6 +31233,9 @@ index 0000000000..14d06d147f + if (sys->embed == NULL) + goto no_window; + ++ if (sys->want_stats) ++ msg_stats(vd, &sys->stats); ++ + if (bkg_surface_get_lock(vd, sys) != NULL) + { + unmap_all(sys, true); @@ -29554,7 +31245,7 @@ index 0000000000..14d06d147f + region_destroy(&sys->region_all); + region_destroy(&sys->region_none); + -+ pollqueue_unref(&sys->speq); ++ pollqueue_finish(&sys->speq); + + w_bound_destroy(&sys->bound); + @@ -29563,6 +31254,9 @@ index 0000000000..14d06d147f + if (eq_finish(&sys->eq) != 0) + msg_Err(vd, "Failed to reclaim all buffers on close"); + ++ // There is a risk of deadlock here if we wait for the pq to die as some ++ // wl buffers may only be relased after close returns so just unref and the ++ // pq will clean up after itself once the last buffer has been released. + pollqueue_unref(&sys->pollq); + + vout_display_DeleteWindow(vd, sys->embed); @@ -29591,8 +31285,8 @@ index 0000000000..14d06d147f +{ + vout_display_t * const vd = (vout_display_t *)obj; + vout_display_sys_t *sys; -+ const drmu_vlc_fmt_info_t * pic_fmti; + fmt_list_t * flist = NULL; ++ video_format_t req_fmt; + + if (var_InheritBool(vd, WL_DMABUF_DISABLE_NAME)) + return VLC_EGENERIC; @@ -29672,30 +31366,10 @@ index 0000000000..14d06d147f + flist = sys->use_shm ? &sys->shm_fmts : &sys->dmabuf_fmts; + + // Check PIC DRM format here -+ if ((pic_fmti = drmu_vlc_fmt_info_find_vlc(&vd->fmt)) == NULL || -+ fmt_list_find(flist, pic_fmti) < 0) -+ { -+ static const vlc_fourcc_t fallback2[] = { -+ VLC_CODEC_I420, -+ VLC_CODEC_RGB32, -+ 0 -+ }; -+ -+ msg_Warn(vd, "Could not find %s mod %#"PRIx64" in supported formats", -+ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(pic_fmti)), -+ drmu_vlc_fmt_info_drm_modifier(pic_fmti)); -+ -+ if ((pic_fmti = find_fmt_fallback(vd, flist, -+ vlc_fourcc_IsYUV(vd->fmt.i_chroma) ? -+ vlc_fourcc_GetYUVFallback(vd->fmt.i_chroma) : -+ vlc_fourcc_GetRGBFallback(vd->fmt.i_chroma))) == NULL && -+ (pic_fmti = find_fmt_fallback(vd, flist, fallback2)) == NULL) -+ { -+ msg_Warn(vd, "Failed to find any usable fallback format"); -+ goto error; -+ } -+ } ++ if (set_req_format(vd, sys, &req_fmt) != VLC_SUCCESS) ++ goto error; + ++ // Get subpic format(s) - it is a list but VLC only looks at list[0] + { + static vlc_fourcc_t const tryfmts[] = { + VLC_CODEC_RGBA, @@ -29743,13 +31417,20 @@ index 0000000000..14d06d147f + sys->region_none = wl_compositor_create_region(video_compositor(sys)); + wl_region_add(sys->region_all, 0, 0, 0, 0); + -+ vd->fmt.i_chroma = drmu_vlc_fmt_info_vlc_chroma(pic_fmti); -+ drmu_vlc_fmt_info_vlc_rgb_masks(pic_fmti, &vd->fmt.i_rmask, &vd->fmt.i_gmask, &vd->fmt.i_bmask); ++ vd->fmt = req_fmt; + + place_rects(vd, vd->cfg); + -+ vd->info.has_pictures_invalid = false; -+ vd->info.subpicture_chromas = sys->subpic_chromas; ++ sys->want_stats = var_InheritBool(vd, WL_DMABUF_STATS_NAME); ++ ++ // If we can invalidate the pic pool then DRI is disabled - we want DRI ++ vd->info = (vout_display_info_t){ ++ .is_slow = false, ++ .has_double_click = false, ++ .needs_hide_mouse = false, ++ .has_pictures_invalid = false, ++ .subpicture_chromas = sys->subpic_chromas, ++ }; + + vd->pool = vd_dmabuf_pool; + vd->prepare = Prepare; @@ -29778,6 +31459,7 @@ index 0000000000..14d06d147f + add_bool(WL_DMABUF_DISABLE_NAME, false, WL_DMABUF_DISABLE_TEXT, WL_DMABUF_DISABLE_LONGTEXT, false) + add_bool(WL_DMABUF_USE_SHM_NAME, false, WL_DMABUF_USE_SHM_TEXT, WL_DMABUF_USE_SHM_LONGTEXT, false) + add_bool(WL_DMABUF_CHEQUERBOARD_NAME, false, WL_DMABUF_CHEQUERBOARD_TEXT, WL_DMABUF_CHEQUERBOARD_LONGTEXT, false) ++ add_bool(WL_DMABUF_STATS_NAME, false, WL_DMABUF_STATS_TEXT, WL_DMABUF_STATS_LONGTEXT, false) +vlc_module_end() diff --git a/pi-util/README.txt b/pi-util/README.txt new file mode 100644 @@ -29941,12 +31623,13 @@ index 0000000000..f3b52b81d2 + diff --git a/pi-util/conf_native.sh b/pi-util/conf_native.sh new file mode 100755 -index 0000000000..54674fa8d0 +index 0000000000..66032c3b92 --- /dev/null +++ b/pi-util/conf_native.sh -@@ -0,0 +1,96 @@ +@@ -0,0 +1,97 @@ +set -e +BASE=`pwd` ++OUT_BASE=$BASE/out + +DO_BOOTSTRAP= +DO_MAKE= @@ -30001,7 +31684,7 @@ index 0000000000..54674fa8d0 + echo "Unknown machine name: $MC" + exit 1 +fi -+OUT=$BASE/out/$ARM-`lsb_release -sc`-rel ++OUT=$OUT_BASE/$ARM-`lsb_release -sc`-rel + +if [ $DO_BOOTSTRAP ]; then + echo "==== Bootstrapping & cleaning $OUT" @@ -30020,7 +31703,7 @@ index 0000000000..54674fa8d0 +echo "==== Configuring in $OUT" +mkdir -p $OUT +# Nothing under here need worry git - including this .gitignore! -+echo "**" > $BASE/out/.gitignore ++echo "**" > $OUT_BASE/.gitignore + +cd $OUT +if [ $DO_CONFIGURE ]; then @@ -30043,10 +31726,10 @@ index 0000000000..54674fa8d0 +fi diff --git a/pi-util/genpatch.sh b/pi-util/genpatch.sh new file mode 100755 -index 0000000000..f7c17bc4e7 +index 0000000000..d24ce46814 --- /dev/null +++ b/pi-util/genpatch.sh -@@ -0,0 +1,70 @@ +@@ -0,0 +1,71 @@ +set -e + +NOTAG= @@ -30112,6 +31795,7 @@ index 0000000000..f7c17bc4e7 +git diff $REFNAME -- modules/video_output/caca.c > $DIFFBASE-004-caca.patch +git diff $REFNAME -- modules/gui/qt/components/interface_widgets.* > $DIFFBASE-005-qt-wayland.patch +git diff $REFNAME -- modules/gui/qt/components/controller.cpp > $DIFFBASE-006-qt-fullscreen.patch ++git diff $REFNAME -- modules/demux/adaptive/http/ConnectionParams.cpp > $DIFFBASE-007-http-port-fix.patch +cd $DSTDIR +zip -m $ZIPNAME $PATCHNAME-*.patch + @@ -30369,25 +32053,21 @@ index 7eca0de2fd..67aad79855 100644 static void aout_DecSynchronize (audio_output_t *aout, vlc_tick_t dec_pts, diff --git a/src/input/decoder.c b/src/input/decoder.c -index 69488a681c..57858a8020 100644 +index 69488a681c..cb88c163d0 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c -@@ -2000,6 +2000,7 @@ void input_DecoderDelete( decoder_t *p_dec ) - vlc_mutex_lock( &p_owner->lock ); +@@ -2001,6 +2001,11 @@ void input_DecoderDelete( decoder_t *p_dec ) p_owner->b_waiting = false; vlc_cond_signal( &p_owner->wait_request ); -+ vlc_mutex_unlock( &p_owner->lock ); ++ /* RPI: V4L2 stateful (H265) can deadlock on buffer starvation if output ++ * buffers not returned */ ++ if( p_owner->p_vout != NULL ) ++ vout_Flush(p_owner->p_vout, 0); ++ /* If the video output is paused or slow, or if the picture pool size was * under-estimated (e.g. greedy video filter, buggy decoder...), the -@@ -2010,7 +2011,6 @@ void input_DecoderDelete( decoder_t *p_dec ) - * worker threads (if any) and the decoder thread to terminate. */ - if( p_owner->p_vout != NULL ) - vout_Cancel( p_owner->p_vout, true ); -- vlc_mutex_unlock( &p_owner->lock ); - - vlc_join( p_owner->thread, NULL ); - + * the picture pool may be empty, and the decoder thread or any decoder diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c index ebb7707a34..0bec4e75da 100644 --- a/src/misc/fourcc.c diff --git a/alarm/vlc-rpi/PKGBUILD b/alarm/vlc-rpi/PKGBUILD index bc1c2fb33..9ffe85080 100644 --- a/alarm/vlc-rpi/PKGBUILD +++ b/alarm/vlc-rpi/PKGBUILD @@ -6,34 +6,130 @@ pkgname=vlc-rpi _pkgname=vlc -_vlcver=3.0.20 +_vlcver=3.0.21 # optional fixup version including hyphen _vlcfixupver= pkgver=${_vlcver}${_vlcfixupver//-/.r} -pkgrel=5 +pkgrel=1 pkgdesc='Multi-platform MPEG, VCD/DVD, and DivX player with hw accel for RPi 3 and above' url='https://www.videolan.org/vlc/' arch=(aarch64) -license=('LGPL2.1' 'GPL2') +license=( + 'GPL-2.0-or-later' + 'LGPL-2.1-or-later' +) # NOTE: switch to ffmpeg-rpi which is basically debian's stuff patched with hw accel -depends=('a52dec' 'libdvbpsi' 'libxpm' 'libdca' 'libproxy' 'lua52' 'libidn' - 'libmatroska' 'taglib' 'libmpcdec' 'ffmpeg-rpi' 'faad2' 'libmad' - 'libmpeg2' 'xcb-util-keysyms' 'libtar' 'libxinerama' 'libsecret' - 'libupnp' 'libixml.so' 'libupnp.so' 'libarchive' 'qt5-base' - 'qt5-x11extras' 'qt5-svg' 'freetype2' 'fribidi' 'harfbuzz' - 'fontconfig' 'libxml2' 'gnutls' 'libplacebo' 'aribb24' - 'linux-rpi' 'libomxil-bellagio') -makedepends=('gst-plugins-base-libs' 'live-media' 'libnotify' 'libbluray' - 'flac' 'libdc1394' 'libavc1394' 'libcaca' 'gtk3' - 'librsvg' 'libgme' 'xosd' 'twolame' 'aalib' 'avahi' 'systemd-libs' - 'libmtp' 'libmicrodns' 'libdvdcss' 'smbclient' - 'vcdimager' 'libssh2' 'mesa' 'protobuf' 'libnfs' 'mpg123' - 'libdvdread' 'libdvdnav' 'libogg' 'libshout' 'libmodplug' 'libvpx' - 'libvorbis' 'speex' 'opus' 'libtheora' 'libpng' 'libjpeg-turbo' - 'libx265.so' 'libx264.so' 'zvbi' 'libass' 'libkate' 'libtiger' - 'sdl_image' 'libpulse' 'alsa-lib' 'jack' 'libsamplerate' 'libsoxr' - 'lirc' 'libgoom2' 'projectm' 'aom' 'srt' 'dav1d' 'libomxil-bellagio' - 'aribb25' 'pcsclite' 'wayland-protocols') +depends=( + 'a52dec' + 'abseil-cpp' + 'aribb24' + 'bash' + 'cairo' + 'dbus' + 'faad2' + 'ffmpeg-rpi' + 'fontconfig' + 'freetype2' + 'fribidi' + 'gcc-libs' + 'gdk-pixbuf2' + 'glib2' + 'glibc' + 'gnutls' + 'harfbuzz' + 'hicolor-icon-theme' + 'libarchive' + 'libdca' + 'libdvbpsi' + 'libglvnd' + 'libidn' + 'libmad' + 'libomxil-bellagio' + 'libmatroska' + 'libmpcdec' + 'libmpeg2' + 'libproxy' + 'libsecret' + 'libtar' + 'libupnp' 'libixml.so' 'libupnp.so' + #'libva' + #'libx11' + #'libxcb' + 'libxinerama' + 'libxml2' + 'libxpm' + 'lua' + 'qt5-base' + 'qt5-svg' + 'qt5-x11extras' + #'taglib' + #'wayland' + 'xcb-util-keysyms' + 'zlib' +) +makedepends=( + 'aalib' + 'alsa-lib' + 'aom' + 'aribb25' + 'avahi' + 'dav1d' + 'flac' + 'fluidsynth' + 'gst-plugins-base-libs' + 'gtk3' + 'jack' + 'libass' + 'libavc1394' + 'libbluray' + 'libcaca' + 'libdc1394' + 'libdvdcss' + 'libdvdnav' + 'libdvdread' + 'libgme' + 'libgoom2' + 'libjpeg-turbo' + 'libkate' + 'libmicrodns' + 'libmodplug' + 'libmtp' + 'libnotify' + 'libnfs' + 'libogg' + 'libomxil-bellagio' + 'libpng' + 'libpulse' + 'librsvg' + 'libsamplerate' + 'libshout' + 'libsoxr' + 'libssh2' + 'libtheora' + 'libtiger' + 'libvorbis' + 'libvpx' + 'lirc' + 'live-media' + 'mesa' + 'mpg123' + 'opus' + 'pcsclite' + 'projectm' + 'protobuf' + 'sdl_image' + 'smbclient' + 'speex' + 'srt' + 'systemd-libs' + 'twolame' + 'vcdimager' + 'wayland-protocols' + 'x264' 'libx264.so' + 'x265' 'libx265.so' + 'xosd' + 'zvbi' +) # 'chromaprint: Chromaprint audio fingerprinter' optdepends=('avahi: service discovery using bonjour protocol' @@ -62,7 +158,7 @@ optdepends=('avahi: service discovery using bonjour protocol' 'mpg123: mpg123 codec' 'protobuf: chromecast streaming' 'libmicrodns: mDNS services discovery (chromecast etc)' - 'lua52-socket: http interface' + 'lua-socket: http interface' 'libdvdread: DVD input module' 'libdvdnav: DVD with navigation input module' 'libogg: Ogg and OggSpots codec' @@ -109,19 +205,17 @@ source=(https://download.videolan.org/${_pkgname}/${_vlcver}/${_pkgname}-${_vlcv 99-vlc.rules vlc.config.txt 0001-vlc-live-media-2021.patch - 0002-libplacebo-5.patch # credit to jc and RPi-Distro maintainers for this work, see: # https://github.com/jc-kynesim/vlc.git # https://github.com/RPi-Distro/vlc/tree/bullseye-rpt/debian/patches - 0003-test-3.0.20-rpi_1.patch + 0002-test-3.0.21-rpi_2.patch update-vlc-plugin-cache.hook) -sha256sums=('adc7285b4d2721cddf40eb5270cada2aaa10a334cb546fd55a06353447ba29b5' +sha256sums=('24dbbe1d7dfaeea0994d5def0bbde200177347136dbfe573f5b6a4cee25afbb0' 'SKIP' '61125ab0da600d813f1aebd8445fcf03e176389cfb8aa28591f8225a13043089' 'f7d129441b5f1f1ce03f7056ebe2707d8c431a69a11afe153a9990dd61ce75ec' '753517a8b88c5950d516f0fe57a3ef169e0665ba7817d4b8d9976c666829a291' - 'c47ecb0e8e8c03f8c5451aa12fc2e38e380364c38c411a13aa38b7b41def6989' - '833e29aa4e1c17ef95dfaafc64bf37fbf8fa54f38211605f3394275919b6b2f2' + '616833e99372317d9a1e1a73254ee48f71a803eb13e8dd04db158e7c3db0eb5c' 'b98043683dd90d3f5a3f501212dfc629839b661100de5ac79fd30cb7b4a06f13') validpgpkeys=('65F7C6B4206BD057A7EB73787180713BE58D1ADC') # VideoLAN Release Signing Key @@ -145,13 +239,12 @@ prepare() { build() { cd ${_pkgname}-${_vlcver} - export CFLAGS+=" -I/usr/include/samba-4.0 -ffat-lto-objects -I/usr/include/ffmpeg-rpi" + export CFLAGS+=" -I/usr/include/samba-4.0 -ffat-lto-objects -I/usr/include/ffmpeg-rpi -Wno-incompatible-pointer-types" export CPPFLAGS+=" -I/usr/include/samba-4.0" - # building with lua ends in errors whereas lua5.2 builds and runs export CXXFLAGS+=" -std=c++17 -I/usr/include/ffmpeg-rpi" export PKG_CONFIG_PATH="/usr/lib/ffmpeg-rpi/pkgconfig" - export LUAC=/usr/bin/luac5.2 - export LUA_LIBS="$(pkg-config --libs lua5.2)" + export LUAC=/usr/bin/luac + export LUA_LIBS="$(pkg-config --libs lua)" export RCC=/usr/bin/rcc-qt5 export PKG_CONFIG_PATH="/usr/lib/ffmpeg-rpi/pkgconfig/:$PKG_CONFIG_PATH" @@ -198,6 +291,7 @@ build() { --enable-a52 \ --enable-dca \ --enable-flac \ + --enable-fluidsynth \ --enable-libmpeg2 \ --enable-vorbis \ --enable-speex \ @@ -244,14 +338,15 @@ build() { --enable-libxml2 \ --disable-libgcrypt \ --enable-gnutls \ - --enable-taglib \ + --disable-taglib \ --enable-secret \ --enable-kwallet \ --disable-update-check \ --enable-notify \ - --enable-libplacebo \ + --disable-libplacebo \ --enable-vlc \ --enable-aribsub \ + --enable-aribcam \ --enable-aom \ --enable-srt \ --enable-dav1d