vo_rpi: use EGL to render subtitles

Slightly faster than using the dispmanx mess (perhaps to a large amount
due to the rather stupid C-only unoptimized ASS->RGBA blending code).

Do this by reusing vo_opengl's subtitle renderer, and vo_opengl's RPI
backend.
This commit is contained in:
wm4 2015-08-18 22:35:33 +02:00
parent c36c596b7b
commit 58ba2a9087
5 changed files with 92 additions and 138 deletions

View File

@ -494,7 +494,7 @@ extern const struct mpgl_driver mpgl_driver_x11;
extern const struct mpgl_driver mpgl_driver_x11egl;
static const struct backend backends[] = {
#if HAVE_RPI_GLES
#if HAVE_RPI
{"rpi", mpgl_set_backend_rpi},
#endif
#if HAVE_GL_COCOA

View File

@ -553,6 +553,8 @@ static void sc_flush_cache(struct gl_shader_cache *sc)
void gl_sc_destroy(struct gl_shader_cache *sc)
{
if (!sc)
return;
gl_sc_reset(sc);
sc_flush_cache(sc);
talloc_free(sc);

View File

@ -38,35 +38,25 @@
#include "vo.h"
#include "video/mp_image.h"
#include "sub/osd.h"
#include "sub/img_convert.h"
#include "gl_osd.h"
// In theory, the number of RGBA subbitmaps the OSD code could give us is
// unlimited; but in practice there will be rarely many elements.
#define MAX_OSD_ELEMS MP_SUB_BB_LIST_MAX
struct osd_elem {
DISPMANX_RESOURCE_HANDLE_T resource;
DISPMANX_ELEMENT_HANDLE_T element;
};
struct osd_part {
struct osd_elem elems[MAX_OSD_ELEMS];
int num_elems;
int change_id;
bool needed;
};
#include "gl_rpi.h"
struct priv {
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_ELEMENT_HANDLE_T window;
DISPMANX_ELEMENT_HANDLE_T osd_overlay;
DISPMANX_UPDATE_HANDLE_T update;
uint32_t w, h;
double display_fps;
struct osd_part osd_parts[MAX_OSD_PARTS];
double osd_pts;
struct mp_osd_res osd_res;
struct mp_egl_rpi egl;
struct gl_shader_cache *sc;
struct mpgl_osd *osd;
MMAL_COMPONENT_T *renderer;
bool renderer_enabled;
@ -118,108 +108,38 @@ static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
return size;
}
static void wipe_osd_part(struct vo *vo, struct osd_part *part)
{
struct priv *p = vo->priv;
for (int n = 0; n < part->num_elems; n++) {
vc_dispmanx_element_remove(p->update, part->elems[n].element);
vc_dispmanx_resource_delete(part->elems[n].resource);
}
part->num_elems = 0;
part->change_id = -1;
}
static void wipe_osd(struct vo *vo)
{
struct priv *p = vo->priv;
for (int x = 0; x < MAX_OSD_PARTS; x++)
wipe_osd_part(vo, &p->osd_parts[x]);
}
static int add_element(struct vo *vo, struct osd_part *part, int index,
struct sub_bitmap *sub)
{
struct priv *p = vo->priv;
VC_IMAGE_TYPE_T format = VC_IMAGE_ARGB8888; // assuming RPI is always LE
struct osd_elem *elem = &part->elems[index];
*elem = (struct osd_elem){0};
// I have no idea why stride must be passed in such a hacky way. It's not
// documented. Other software does it too. Other software claims aligning
// the width and "probably" the height is required too, but for me it works
// just fine without on rpi2. (See Weston's rpi renderer.)
elem->resource = vc_dispmanx_resource_create(format,
sub->w | (sub->stride << 16),
sub->h,
&(int32_t){0});
if (!elem->resource) {
MP_ERR(vo, "Could not create %dx%d sub-bitmap\n", sub->w, sub->h);
return -1;
}
VC_RECT_T rc = {.width = sub->w, .height = sub->h};
vc_dispmanx_resource_write_data(elem->resource, format,
sub->stride, sub->bitmap, &rc);
VC_RECT_T src = {.width = sub->w << 16, .height = sub->h << 16};
VC_RECT_T dst = {.x = sub->x, .y = sub->y, .width = sub->dw, .height = sub->dh};
VC_DISPMANX_ALPHA_T alpha = {
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_PREMULT,
.opacity = 0xFF,
};
elem->element = vc_dispmanx_element_add(p->update, p->display, p->osd_layer,
&dst, elem->resource, &src,
DISPMANX_PROTECTION_NONE,
&alpha, 0, 0);
if (!elem->element) {
MP_ERR(vo, "Could not create sub-bitmap element\n");
return -1;
}
return 0;
}
static void osd_draw_cb(void *ctx, struct sub_bitmaps *imgs)
{
struct vo *vo = ctx;
struct priv *p = vo->priv;
struct osd_part *part = &p->osd_parts[imgs->render_index];
part->needed = true;
if (imgs->change_id == part->change_id)
return;
wipe_osd_part(vo, part);
part->change_id = imgs->change_id;
for (int n = 0; n < imgs->num_parts; n++) {
if (part->num_elems == MAX_OSD_ELEMS) {
MP_ERR(vo, "Too many OSD elements.\n");
break;
}
int index = part->num_elems++;
if (add_element(vo, part, index, &imgs->parts[n]) < 0)
break;
}
}
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
static void update_osd(struct vo *vo)
{
struct priv *p = vo->priv;
for (int x = 0; x < MAX_OSD_PARTS; x++)
p->osd_parts[x].needed = false;
mpgl_osd_generate(p->osd, p->osd_res, p->osd_pts, 0, 0);
static const bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_RGBA] = true};
osd_draw(vo->osd, p->osd_res, p->osd_pts, 0, formats, osd_draw_cb, vo);
for (int x = 0; x < MAX_OSD_PARTS; x++) {
struct osd_part *part = &p->osd_parts[x];
if (!part->needed)
wipe_osd_part(vo, part);
for (int n = 0; n < MAX_OSD_PARTS; n++) {
enum sub_bitmap_format fmt = mpgl_osd_get_part_format(p->osd, n);
if (!fmt)
continue;
gl_sc_uniform_sampler(p->sc, "osdtex", GL_TEXTURE_2D, 0);
switch (fmt) {
case SUBBITMAP_RGBA: {
GLSLF("// OSD (RGBA)\n");
GLSL(vec4 color = texture(osdtex, texcoord).bgra;)
break;
}
case SUBBITMAP_LIBASS: {
GLSLF("// OSD (libass)\n");
GLSL(vec4 color =
vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
break;
}
default:
abort();
}
gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd));
gl_sc_gen_shader_and_reset(p->sc);
mpgl_osd_draw_part(p->osd, p->w, -p->h, n);
}
}
@ -249,6 +169,25 @@ static void resize(struct vo *vo)
MP_WARN(vo, "could not set video rectangle\n");
}
static void destroy_overlays(struct vo *vo)
{
struct priv *p = vo->priv;
if (p->window)
vc_dispmanx_element_remove(p->update, p->window);
p->window = 0;
mpgl_osd_destroy(p->osd);
p->osd = NULL;
gl_sc_destroy(p->sc);
p->sc = NULL;
mp_egl_rpi_destroy(&p->egl);
if (p->osd_overlay)
vc_dispmanx_element_remove(p->update, p->osd_overlay);
p->osd_overlay = 0;
}
static int update_display_size(struct vo *vo)
{
struct priv *p = vo->priv;
@ -267,9 +206,7 @@ static int update_display_size(struct vo *vo)
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
if (p->window)
vc_dispmanx_element_remove(p->update, p->window);
p->window = 0;
destroy_overlays(vo);
// Use the whole screen.
VC_RECT_T dst = {.width = p->w, .height = p->h};
@ -286,6 +223,27 @@ static int update_display_size(struct vo *vo)
return -1;
}
alpha = (VC_DISPMANX_ALPHA_T){
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE,
.opacity = 0xFF,
};
p->osd_overlay = vc_dispmanx_element_add(p->update, p->display,
p->background_layer + 2,
&dst, 0, &src,
DISPMANX_PROTECTION_NONE,
&alpha, 0, 0);
if (!p->osd_overlay) {
MP_FATAL(vo, "Could not add DISPMANX element.\n");
return -1;
}
if (mp_egl_rpi_init(&p->egl, p->osd_overlay, p->w, p->h) < 0) {
MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n");
return -1;
}
p->sc = gl_sc_create(p->egl.gl, vo->log, vo->global),
p->osd = mpgl_osd_init(p->egl.gl, vo->log, vo->osd);
p->display_fps = 0;
TV_GET_STATE_RESP_T tvstate;
TV_DISPLAY_STATE_T tvstate_disp;
@ -329,10 +287,8 @@ static void flip_page(struct vo *vo)
p->next_image = NULL;
// For OSD
if (!p->skip_osd) {
vc_dispmanx_update_submit_sync(p->update);
p->update = vc_dispmanx_update_start(10);
}
if (!p->skip_osd && p->egl.gl)
eglSwapBuffers(p->egl.egl_display, p->egl.egl_surface);
p->skip_osd = false;
if (mpi) {
@ -375,8 +331,11 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
// Redraw only if the OSD has meaningfully changed, which we assume it
// hasn't when a frame is merely repeated for display sync.
p->skip_osd = !frame->redraw && frame->repeat;
if (!p->skip_osd)
if (!p->skip_osd && p->egl.gl) {
p->egl.gl->ClearColor(0, 0, 0, 0);
p->egl.gl->Clear(GL_COLOR_BUFFER_BIT);
update_osd(vo);
}
p->display_synced = frame->display_synced;
@ -610,10 +569,7 @@ static void uninit(struct vo *vo)
talloc_free(p->next_image);
wipe_osd(vo);
if (p->window)
vc_dispmanx_element_remove(p->update, p->window);
destroy_overlays(vo);
if (p->update)
vc_dispmanx_update_submit_sync(p->update);
@ -642,6 +598,8 @@ static int preinit(struct vo *vo)
p->video_layer = p->layer + 1;
p->osd_layer = p->layer + 2;
p->egl.log = vo->log;
bcm_host_init();
if (mmal_vc_init()) {

16
wscript
View File

@ -676,7 +676,7 @@ video_output_features = [
# including them (compensate with -isystem and -fgnu89-inline).
'name': '--rpi',
'desc': 'Raspberry Pi support',
'func':
'func': compose_checks(
check_cc(cflags="-isystem/opt/vc/include/ "+
"-isystem/opt/vc/include/interface/vcos/pthreads " +
"-isystem/opt/vc/include/interface/vmcs_host/linux " +
@ -684,23 +684,17 @@ video_output_features = [
linkflags="-L/opt/vc/lib",
header_name="bcm_host.h",
lib=['mmal_core', 'mmal_util', 'mmal_vc_client', 'bcm_host']),
}, {
'name': '--rpi-gles',
'desc': 'GLES on Raspberry Pi',
'groups': [ 'gl' ],
'deps': ['rpi'],
# We still need all OpenGL symbols, because the vo_opengl code is
# generic and supports anything from GLES2/OpenGL 2.1 to OpenGL 4 core.
'func': compose_checks(
# We still need all OpenGL symbols, because the vo_opengl code is
# generic and supports anything from GLES2/OpenGL 2.1 to OpenGL 4 core.
check_cc(lib="EGL"),
check_cc(lib="GLESv2"),
check_statement('GL/gl.h', '(void)GL_RGB32F'), # arbitrary OpenGL 3.0 symbol
check_statement('GL/gl.h', '(void)GL_LUMINANCE16') # arbitrary OpenGL legacy-only symbol
),
),
} , {
'name': '--gl',
'desc': 'OpenGL video outputs',
'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland', 'rpi-gles' ],
'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland', 'rpi' ],
'func': check_true
}
]

View File

@ -333,7 +333,7 @@ def build(ctx):
( "video/out/filter_kernels.c" ),
( "video/out/gl_cocoa.c", "gl-cocoa" ),
( "video/out/gl_common.c", "gl" ),
( "video/out/gl_rpi.c", "rpi-gles" ),
( "video/out/gl_rpi.c", "rpi" ),
( "video/out/gl_hwdec.c", "gl" ),
( "video/out/gl_hwdec_dxva2.c", "gl-win32" ),
( "video/out/gl_hwdec_vaglx.c", "vaapi-glx" ),