Skip to content

Commit

Permalink
(core/lua/shmif) wire up image_metadata
Browse files Browse the repository at this point in the history
This should provide most of the missing pieces for ensuring both a
path from a shmif consumer signalling HDR capability, annotating
frame with metadata and having that persist across the stack.

What is mainly missing for testing this for real is accessing the
metadata from Lua space (only really needed if you manually need
to composite / tone-map different metadata- sourced images in the
same pipeline.

The rest would be to simply add the properties to the atomic
modeset when a vstore with metadata set is mapped to an output.
  • Loading branch information
letoram committed Sep 16, 2023
1 parent b3364d0 commit 23f6675
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 57 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@
* open\_nonblock can now adopt an existing iostream into a target vid
* input\_remap\_translation overloaded form for serializing backing keymap
* clockreq no longer forwarded for frameserver event handler
* image\_metadata added for annotating a vid used when streaming/sharing/scanout HDR contents

## Core
* respect border attribute in text rasteriser
Expand Down
33 changes: 33 additions & 0 deletions doc/image_metadata.lua
@@ -0,0 +1,33 @@
-- image_metadata
-- @short: Set / update image contents metadata
-- @inargs: vid:tgt, string:model=drmv1, numtbl[8]:coords, number:mmin, number:mmax, number:cll, number:fll, string:eotf
-- @outargs: bool:ok
-- @longdescr:
-- HDR rendering, computer vision, compression and similar processes all can
-- benefit about added information about what the pixels represents or other
-- kind of contextual detail such as motion vectors.
--
-- This function is used to attach/update/define a metadata model for the
-- target vid. The currently support model is "drmv1" used for HDR scanout
-- if the *tgt* is mapped to a display supporting it, the metadata will be
-- be forwarded to that screen accordingly.
--
-- The arguments for *model=drmv1* are 8 coordinates for red, green, blue
-- chromacities and whitepoint. These coordinates will be clamped to range from
-- 0 to 1.3107.
--
-- *mmin* sets the master minimum luminance, in cd/m2 from 1 to 65535.
-- *mmax* sets the master max luminance, in cd/m2 from 1 to 65535.
-- *cll* sets the max content light level and *fll* the frame average light
-- level, both in cd/m2 from 1 to 65535.
--
-- The *eotf* (electro-optical transfer function) should be one out of 'sdr'
-- (sRGB), 'hdr' (linear), 'pq', 'pq-inv' or 'bt709'.
--
-- @group: image
-- @cfunction: imagemetadata
-- @flags: debugbuild
function main()
#ifdef MAIN
#endif
end
39 changes: 28 additions & 11 deletions doc/rendertarget_reconfigure.lua
@@ -1,17 +1,34 @@
-- rendertarget_reconfigure
-- @short: Change the output density on a rendertarget
-- @short: Change the output density or colour properties of a rendertarget
-- @inargs: vid:rtgt, float:hppcm, float:vppcm
-- @inargs: vid:rtgt, tbl:metadata
-- @outargs:
-- @longdescr: Vectorized assets that are sized in physical units
-- e.g. pt (1/72th of an inch) like with render_text, the engine needs
-- needs knowledge of the target output before drawing. By default, this is
-- some display-dependent initial size, accessible through the global
-- constants HPPCM and VPPCM, or 38.4 when those cannot be found. When
-- targeting a display, locally or remote, that has a different density
-- it is typically advised to update the rendertarget pipeline that gets
-- mapped to that output using this function. Whenever an asset gets created
-- or attached to a rendertarget, it will rerastered to match the density
-- of the rendertarget.
-- @longdescr:
-- Two major properties of a rendertarget are the intended output density
-- and the targetted colour space.
--
-- For vectorized assets that are sized in physical units e.g. pt (1/72th of an
-- inch) like with render_text, the engine needs knowledge of the target output
-- before drawing. By default, this is some display-dependent initial size,
-- accessible through the global constants HPPCM and VPPCM, or 38.4 when those
-- cannot be found. When targeting a display, locally or remote, that has a
-- different density it is typically advised to update the rendertarget
-- pipeline that gets mapped to that output using this function. Whenever an
-- asset gets created or attached to a rendertarget, it will be rerastered to
-- match the density of the rendertarget.
--
-- For HDR composition and rendering, you typically need to provide further
-- metadata about light levels and so on. This can be set through the metadata
-- table which accepts the following fields: {encoding, whitepoint, levels,
-- lumarange, primaries}.
--
-- @tblent: string:encoding, "itu-r", "bt.601", "bt.709", "bt.2020", "YCBCr"
-- @tblent: table:whitepoint[2], x/y coordinates ranging from 0..1, 0..1
-- @tblent: table:primaries[6], x/y coordinates of red, green, blue primaries
-- @tblent: number:max_frame_average, in nits, caps at 65535.
-- @tblent: number:max_content_level, in nits, caps at 65535.
-- @tblent: table:mastering_levels[2], in nits, caps at 65535.
--
-- @group: targetcontrol
-- @cfunction: renderreconf
-- @external: yes
Expand Down
21 changes: 21 additions & 0 deletions src/engine/arcan_frameserver.c
Expand Up @@ -269,6 +269,27 @@ static bool push_buffer(arcan_frameserver* src,
vready = (vready <= 0 || vready > src->vbuf_cnt) ? 0 : vready - 1;
shmif_pixel* buf = src->vbufs[vready];

/* If the HDR subprotocol is enabled, verify and translate into store metadata
* - explicitly map the metadata format. There is only the one to chose from
* right now, but it wouldn't be surprising if that changes. */
if (src->desc.aext.hdr){
struct arcan_shmif_hdr fc = *src->desc.aext.hdr;
store->hdr.model = 1;
store->hdr.drm = (struct drm_hdr_meta){
.eotf = fc.drm.eotf,
.rx = fc.drm.rx,
.ry = fc.drm.ry,
.gx = fc.drm.gx,
.gy = fc.drm.gy,
.bx = fc.drm.bx,
.by = fc.drm.by,
.wpx = fc.drm.wpx,
.wpy = fc.drm.wpy,
.cll = fc.drm.cll_max,
.fll = fc.drm.fll_max
};
}

/* Need to do this check here as-well as in the regular frameserver tick
* control because the backing store might have changed somehwere else. */
if (src->desc.width != store->w || src->desc.height != store->h ||
Expand Down
63 changes: 61 additions & 2 deletions src/engine/arcan_lua.c
Expand Up @@ -4553,9 +4553,8 @@ bool arcan_lua_pushevent(lua_State* ctx, arcan_event* ev)
}
else if (ev->ext.netstate.state == 0)
tblbool(ctx, "lost", true, top);
else {
else
tblbool(ctx, "bad", true, top);
}

if (ev->ext.netstate.type & 1)
tblbool(ctx, "source", true, top);
Expand Down Expand Up @@ -9739,6 +9738,65 @@ enum arcan_ffunc_rv arcan_lua_proctarget FFUNC_HEAD
return 0;
}

static int imagemetadata(lua_State* ctx)
{
LUA_TRACE("image_metadata");
arcan_vobject* vobj;
luaL_checkvid(ctx, 1, &vobj);
if (vobj->vstore->txmapped != TXSTATE_TEX2D){
lua_pushboolean(ctx, false);
LUA_ETRACE("image_metadata", "storage_type mismatch", 1);
}

const char* model = luaL_checkstring(ctx, 2);
if (strcmp(model, "drmv1") != 0){
lua_pushboolean(ctx, false);
LUA_ETRACE("image_metadata", "metadata model missing", 1);
}

if (lua_type(ctx, 3) != LUA_TTABLE){
lua_pushboolean(ctx, false);
arcan_fatal("image_metadata(, , >tbl< ) expected table");
LUA_ETRACE("image_metadata", "expected table for coordinates", 1);
}

int ncords = lua_rawlen(ctx, 3);
if (ncords < 8){
arcan_fatal("image_metadata(), wrong coordinate set");
lua_pushboolean(ctx, false);
LUA_ETRACE("image_metadata", "expected 10 coordinates (rgb,w)", 1);
}

float coords[8];
for (size_t i = 0; i < 8; i++){
lua_rawgeti(ctx, 3, i+1);
coords[i] = lua_tonumber(ctx, -1);
lua_pop(ctx, 1);
}

struct drm_hdr_meta meta = {
.rx = coords[0],
.ry = coords[1],
.gx = coords[2],
.gy = coords[3],
.bx = coords[4],
.by = coords[5],
.wpx = coords[6],
.wpy = coords[7]
};

meta.master_min = luaL_checknumber(ctx, 4);
meta.master_max = luaL_checknumber(ctx, 5);
meta.cll = luaL_checknumber(ctx, 6);
meta.fll = luaL_checknumber(ctx, 7);

vobj->vstore->hdr.model = 1;
vobj->vstore->hdr.drm = meta;

lua_pushboolean(ctx, true);
LUA_ETRACE("image_metadata", NULL, 1);
}

static int imagestorage(lua_State* ctx)
{
LUA_TRACE("image_access_storage");
Expand Down Expand Up @@ -12024,6 +12082,7 @@ static const luaL_Reg imgfuns[] = {
{"image_state", imagestate },
{"image_access_storage", imagestorage },
{"image_resize_storage", imageresizestorage },
{"image_metadata", imagemetadata },
{"image_sharestorage", sharestorage },
{"image_matchstorage", matchstorage },
{"cursor_setstorage", cursorstorage },
Expand Down
25 changes: 14 additions & 11 deletions src/platform/egl-dri/video.c
Expand Up @@ -830,7 +830,7 @@ static int setup_buffers_gbm(struct dispout* d)

if (!got_config){
debug_print("no matching gbm-format <-> visual "
"<-> egl config for fmt: %d", (int)gbm_formats[i]);
"<-> egl config for fmt: %s", fmt_lbls[i]);
continue;
}

Expand Down Expand Up @@ -1100,14 +1100,11 @@ bool platform_video_set_mode(platform_display_id disp,
}
*/

/*
* setup / allocate a new set of buffers that match the new mode
*/
if (!realloc_buffers(d))
if (!realloc_buffers(d)){
return false;
}

d->state = DISP_MAPPED;

return true;
}

Expand Down Expand Up @@ -3917,8 +3914,10 @@ ssize_t platform_video_map_display_layer(arcan_vobj_id id,
arcan_video_display.default_txcos), sizeof(float) * 8
);

debug_print("map_display(%d->%d) ok @%zu*%zu+%zu,%zu, hint: %d",
(int) id, (int) disp, (size_t) d->dispw, (size_t) d->disph,
debug_print("map_display(%d:%s->%d) ok @%zu*%zu+%zu,%zu, hint: %d",
(int) id,
vobj->tracetag ? vobj->tracetag : "(notag)",
(int) disp, (size_t) d->dispw, (size_t) d->disph,
(size_t) d->dispx, (size_t) d->dispy, (int) hint);

d->frame_cookie = 0;
Expand Down Expand Up @@ -4075,7 +4074,9 @@ static enum display_update_state draw_display(struct dispout* d)
struct rendertarget* newtgt = arcan_vint_findrt(vobj);
if (newtgt){
size_t nd = agp_rendertarget_dirty(newtgt->art, NULL);
verbose_print("(%d) draw display, dirty regions: %zu", nd);
verbose_print(
"(%d:%s) draw display, dirty regions: %zu",
(int) d->id, vobj->tracetag ? vobj->tracetag : "(untagged)", nd);
if (nd || newtgt->frame_cookie != d->frame_cookie){
agp_rendertarget_dirty_reset(newtgt->art, NULL);
}
Expand Down Expand Up @@ -4124,8 +4125,10 @@ static enum display_update_state draw_display(struct dispout* d)
agp_rendertarget_clear();
agp_blendstate(BLEND_NONE);
agp_draw_vobj(0, 0, d->dispw, d->disph, d->txcos, NULL);
verbose_print("(%d) draw, shader: %d, %zu*%zu",
(int)d->id, (int)shid, (size_t)d->dispw, (size_t)d->disph);
verbose_print("(%d:%s) draw, shader: %d, %zu*%zu",
(int)d->id,
vobj->tracetag ? vobj->tracetag : "(notag)",
(int)shid, (size_t)d->dispw, (size_t)d->disph);
}
/*
* another rough corner case, if we have a store that is not world ID but
Expand Down
14 changes: 14 additions & 0 deletions src/platform/platform_types.h
Expand Up @@ -232,6 +232,15 @@ struct agp_region {
size_t x1, y1, x2, y2;
};

struct drm_hdr_meta {
int eotf;
uint16_t rx, ry, gx, gy, bx, by;
uint16_t wpx, wpy;
uint16_t master_min, master_max;
uint16_t cll;
uint16_t fll;
};

struct agp_vstore;
struct agp_vstore {
size_t refcount;
Expand Down Expand Up @@ -294,6 +303,11 @@ struct agp_vstore {
size_t w, h;
uint8_t bpp, txmapped,
txu, txv, scale, imageproc, filtermode;

struct {
int model;
struct drm_hdr_meta drm;
} hdr;
};

/* Built in Shader Vertex Attributes */
Expand Down
6 changes: 3 additions & 3 deletions src/shmif/arcan_shmif_sub.c
Expand Up @@ -26,7 +26,7 @@ union shmif_ext_substruct arcan_shmif_substruct(
sub.vr = (struct arcan_shmif_vr*)(base + aofs->ofs_vr);

if (aofs->sz_hdr)
sub.hdr = (struct arcan_shmif_hdr16f*)(base + aofs->ofs_hdr);
sub.hdr = (struct arcan_shmif_hdr*)(base + aofs->ofs_hdr);

if (aofs->sz_vector)
sub.vector = (struct arcan_shmif_vector*)(base + aofs->ofs_vector);
Expand All @@ -40,8 +40,8 @@ union shmif_ext_substruct arcan_shmif_substruct(
bool arcan_shmifsub_getramp(
struct arcan_shmif_cont* cont, size_t ind, struct ramp_block* out)
{
struct arcan_shmif_ramp* hdr = arcan_shmif_substruct(
cont, SHMIF_META_CM).cramp;
struct arcan_shmif_ramp* hdr =
arcan_shmif_substruct(cont, SHMIF_META_CM).cramp;

if (!hdr || hdr->magic != ARCAN_SHMIF_RAMPMAGIC || ind > (hdr->n_blocks >> 1))
return false;
Expand Down
41 changes: 11 additions & 30 deletions src/shmif/arcan_shmif_sub.h
Expand Up @@ -67,14 +67,14 @@ static inline uint16_t subp_checksum(const uint8_t* const buf, size_t len)
*/
struct arcan_shmif_vr;
struct arcan_shmif_ramp;
struct arcan_shmif_hdr16f;
struct arcan_shmif_hdr;
struct arcan_shmif_vector;
struct arcan_shmif_venc;

union shmif_ext_substruct {
struct arcan_shmif_vr* vr;
struct arcan_shmif_ramp* cramp;
struct arcan_shmif_hdr16f* hdr;
struct arcan_shmif_hdr* hdr;
struct arcan_shmif_vector* vector;
struct arcan_shmif_venc* venc;
};
Expand Down Expand Up @@ -107,28 +107,6 @@ struct arcan_shmif_ofstbl {
};
};

/*
* Practically speaking these will only be used witin libdrm like settings,
* thus the fields practically match libdrm expected metadata. For other
* applications
*/
struct arcan_shmif_hdr_metadata {
bool valid;
int eotf;

struct {
float white[2];
float red[2];
float green[2];
float blue[2];
} primaries;

uint16_t max_disp_luma;
uint16_t min_disp_luma;
uint16_t max_cll;
uint16_t max_fall;
};

/* HDR is something of a misnomer here, it can also refer to SDR contents with
* higher precision (e.g. 10-bit). In that case the SDR eotf mode is specified.
* The values here match what libdrm metadata takes. */
Expand All @@ -140,13 +118,16 @@ enum shmif_hdr_eotf {
};

struct arcan_shmif_hdr {
int format; /* 0 = fp32 rgba, 1 = fp16 rgba, 2 = 10-bit rgba in 1010102 */
uint8_t model; /* match eotf */

/* PRODUCER SET */
struct arcan_shmif_hdr_metadata source;

/* CONSUMER SET - updated on DISPLAYHINT, if known / applicable */
struct arcan_shmif_hdr_metadata sink;
struct {
int eotf;
uint16_t rx, ry, gx, gy, bx, by;
uint16_t wpx, wpy;
uint16_t master_min, master_max;
uint16_t cll_max;
uint16_t fll_max;
} drm;
};

/* verified during _signal, framesize <= w * h * sizeof(shmif_pixel) */
Expand Down

0 comments on commit 23f6675

Please sign in to comment.