Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  sampler.c   Sprache: C

 

/*****************************************************************************
 * sampler.c: built-in OpenGL sampler module
 *****************************************************************************
 * Copyright (C) 2020 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "sampler.h"

#include <vlc_common.h>
#include <vlc_memstream.h>
#include <vlc_opengl.h>
#include <vlc_fs.h>

#ifdef HAVE_LIBPLACEBO_GL
#include <libplacebo/opengl.h>
#include <libplacebo/shaders.h>
#include <libplacebo/shaders/colorspace.h>
#include "../libplacebo/utils.h"
#endif

#include "gl_api.h"
#include "gl_common.h"
#include "gl_util.h"

struct sampler_priv {
    struct vlc_gl_t *gl;
    struct vlc_gl_api api;
    const opengl_vtable_t *vt; /* for convenience, same as &api.vt */

    struct vlc_gl_picture pic;

    struct {
        GLint Textures[PICTURE_PLANE_MAX];
        GLint TexSizes[PICTURE_PLANE_MAX]; /* for GL_TEXTURE_RECTANGLE */
        GLint ConvMatrix;
        GLint *pl_vars, *pl_descs; /* for pl_sh_res */
    } uloc;

    bool yuv_color;
    bool unsigned_sampler;
    GLfloat conv_matrix[4*4];

#ifdef HAVE_LIBPLACEBO_GL
    /* libplacebo context */
    pl_log pl_log;
    pl_opengl pl_opengl;
    pl_shader pl_sh;
    pl_shader_obj dither_state, tone_map_state, lut_state;
    const struct pl_shader_res *pl_sh_res;
#endif

    /* If set, vlc_texture() exposes a single plane (without chroma
     * conversion), selected by vlc_gl_sampler_SetCurrentPlane(). */

    bool expose_planes;
    unsigned plane;

    int glsl_version;

    struct vlc_gl_extension_vt extension_vt;
};

static inline struct sampler_priv *
PRIV(struct vlc_gl_sampler *sampler)
{
    return sampler->sys;
}

static int
sampler_update(struct vlc_gl_sampler *sampler,
               const struct vlc_gl_picture *picture)
{
    struct sampler_priv *priv = PRIV(sampler);
    priv->pic = *picture;

    return VLC_SUCCESS;
}

static void
sampler_select_plane(struct vlc_gl_sampler *sampler, unsigned plane)
{
    struct sampler_priv *priv = PRIV(sampler);
    priv->plane = plane;
}

static void
sampler_close(struct vlc_gl_sampler *sampler)
{
#ifdef HAVE_LIBPLACEBO_GL
    struct sampler_priv *priv = PRIV(sampler);
    FREENULL(priv->uloc.pl_vars);
    FREENULL(priv->uloc.pl_descs);
    pl_shader_free(&priv->pl_sh);
    pl_shader_obj_destroy(&priv->lut_state);
    pl_shader_obj_destroy(&priv->tone_map_state);
    pl_shader_obj_destroy(&priv->dither_state);
    pl_opengl_destroy(&priv->pl_opengl);
    pl_log_destroy(&priv->pl_log);
#endif
    free(sampler->sys);
}

static const float MATRIX_COLOR_RANGE_LIMITED[4*3] = {
    255.0/219,         0,         0, -255.0/219 *  16.0/255,
            0, 255.0/224,         0, -255.0/224 * 128.0/255,
            0,         0, 255.0/224, -255.0/224 * 128.0/255,
};

static const float MATRIX_COLOR_RANGE_FULL[4*3] = {
    1, 0, 0,          0,
    0, 1, 0, -128.0/255,
    0, 0, 1, -128.0/255,
};

/*
 * Construct the transformation matrix from the luma weight of the red and blue
 * component (the green component is deduced).
 */

#define MATRIX_YUV_TO_RGB(KR, KB) \
    MATRIX_YUV_TO_RGB_(KR, (1-(KR)-(KB)), KB)

/*
 * Construct the transformation matrix from the luma weight of the RGB
 * components.
 *
 * KR: luma weight of the red component
 * KG: luma weight of the green component
 * KB: luma weight of the blue component
 *
 * By definition, KR + KG + KB == 1.
 *
 * Ref: <https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion>
 * Ref: libplacebo: src/colorspace.c:luma_coeffs()
 * */

#define MATRIX_YUV_TO_RGB_(KR, KG, KB) { \
    1,                         0,              2*(1.0-(KR)), \
    1, -2*(1.0-(KB))*((KB)/(KG)), -2*(1.0-(KR))*((KR)/(KG)), \
    1,              2*(1.0-(KB)),                         0, \
}

static const float MATRIX_BT601[3*3] = MATRIX_YUV_TO_RGB(0.299, 0.114);
static const float MATRIX_BT709[3*3] = MATRIX_YUV_TO_RGB(0.2126, 0.0722);
static const float MATRIX_BT2020[3*3] = MATRIX_YUV_TO_RGB(0.2627, 0.0593);

static void
init_conv_matrix(float conv_matrix_out[],
                 video_color_space_t color_space,
                 video_color_range_t color_range)
{
    const float *space_matrix;
    switch (color_space) {
        case COLOR_SPACE_BT601:
            space_matrix = MATRIX_BT601;
            break;
        case COLOR_SPACE_BT2020:
            space_matrix = MATRIX_BT2020;
            break;
        default:
            space_matrix = MATRIX_BT709;
    }

    /* Init the conversion matrix in column-major order (OpenGL expects
     * column-major order by default, and OpenGL ES does not support row-major
     * order at all). */


    const float *range_matrix = color_range == COLOR_RANGE_FULL
                              ? MATRIX_COLOR_RANGE_FULL
                              : MATRIX_COLOR_RANGE_LIMITED;
    /* Multiply the matrices on CPU once for all */
    for (int x = 0; x < 4; ++x)
    {
        for (int y = 0; y < 3; ++y)
        {
            /* Perform intermediate computation in double precision even if the
             * result is in single-precision, to avoid unnecessary errors. */

            double sum = 0;
            for (int k = 0; k < 3; ++k)
                sum += space_matrix[y * 3 + k] * range_matrix[k * 4 + x];
            /* Notice the reversed indices: x is now the row, y is the
             * column. */

            conv_matrix_out[x * 4 + y] = sum;
        }
    }

    /* Add a row to fill a 4x4 matrix (remember it's in column-major order).
     * (non-square matrices are not supported on old OpenGL ES versions) */

    conv_matrix_out[3] = 0;
    conv_matrix_out[7] = 0;
    conv_matrix_out[11] = 0;
    conv_matrix_out[15] = 1;
}

static int
sampler_yuv_base_init(struct vlc_gl_sampler *sampler,
                      const vlc_chroma_description_t *desc,
                      video_color_space_t yuv_space)
{
    struct sampler_priv *priv = PRIV(sampler);

    /* The current implementation always converts from limited to full range. */
    const video_color_range_t range = COLOR_RANGE_LIMITED;
    float *matrix = priv->conv_matrix;
    init_conv_matrix(matrix, yuv_space, range);

    /* If using half_float, we multiply the conversion matrix by 255.  Reason:
     * half-float YUV planes store values in [0..1]. Multiplying by 255
     * compensates for that.
     */

    if (sampler->half_float)
        for (int i = 0; i < 4*3; ++i)
            matrix[i] *= 255;

    if (desc->pixel_size == 2)
    {
        if (desc->fcc != VLC_CODEC_P010 && desc->fcc != VLC_CODEC_P012
         && desc->fcc != VLC_CODEC_P016) {
            /* Do a bit shift if samples are stored on LSB. */
            float yuv_range_correction = (float)((1 << 16) - 1)
                                         / ((1 << desc->pixel_bits) - 1);
            /* We want to transform the input color (y, u, v, 1) to
             * (r*y, r*u, r*v, 1), where r = yuv_range_correction.
             *
             * This can be done by left-multiplying the color vector by a
             * matrix R:
             *
             *                 R
             *  / r*y \   / r 0 0 0 \   / y \
             *  | r*u | = | 0 r 0 0 | * | u |
             *  | r*v |   | 0 0 r 0 |   | v |
             *  \  1  /   \ 0 0 0 1 /   \ 1 /
             *
             * Combine this transformation with the color conversion matrix:
             *
             *     matrix := matrix * R
             *
             * This is equivalent to multipying the 3 first rows by r
             * (yuv_range_conversion).
             */

            for (int i = 0; i < 4*3; ++i)
                matrix[i] *= yuv_range_correction;
        }
    }

    priv->yuv_color = true;

    /* Some formats require to swap the U and V components.
     *
     * This can be done by left-multiplying the color vector by a matrix S:
     *
     *               S
     *  / y \   / 1 0 0 0 \   / y \
     *  | v | = | 0 0 1 0 | * | u |
     *  | u |   | 0 1 0 0 |   | v |
     *  \ 1 /   \ 0 0 0 1 /   \ 1 /
     *
     * Combine this transformation with the color conversion matrix:
     *
     *     matrix := matrix * S
     *
     * This is equivalent to swap columns 1 and 2.
     */

    bool swap_uv = desc->fcc == VLC_CODEC_YV12 ||
                   desc->fcc == VLC_CODEC_NV21;
    if (swap_uv)
    {
        /* Remember, the matrix in column-major order */
        float tmp[4];
        /* tmp <- column1 */
        memcpy(tmp, matrix + 4, sizeof(tmp));
        /* column1 <- column2 */
        memcpy(matrix + 4, matrix + 8, sizeof(tmp));
        /* column2 <- tmp */
        memcpy(matrix + 8, tmp, sizeof(tmp));
    }
    return VLC_SUCCESS;
}

static void
sampler_base_fetch_locations(struct vlc_gl_sampler *sampler, uint32_t program)
{
    struct sampler_priv *priv = PRIV(sampler);

    const opengl_vtable_t *vt = priv->vt;

    if (priv->yuv_color)
    {
        priv->uloc.ConvMatrix = vt->GetUniformLocation(program, "ConvMatrix");
        assert(priv->uloc.ConvMatrix != -1);
    }

    const unsigned tex_count = sampler->tex_count;
    /* To guarantee variable names length, we need to fix the number
     * of texture from now on.
     * tex_count > PICTURE_PLANE_MAX (5) would overflow uloc.Textures
     * tex_count > 9 would overflow the char name[] string during
     * snprintf operations. */

    if (tex_count >= PICTURE_PLANE_MAX)
        vlc_assert_unreachable();

    for (unsigned i = 0; i < tex_count; ++i)
    {
        char name[sizeof("Textures[X]")];

        snprintf(name, sizeof(name), "Textures[%1u]", i);
        priv->uloc.Textures[i] = vt->GetUniformLocation(program, name);
        assert(priv->uloc.Textures[i] != -1);

        if (sampler->tex_target == GL_TEXTURE_RECTANGLE)
        {
            snprintf(name, sizeof(name), "TexSizes[%1u]", i);
            priv->uloc.TexSizes[i] = vt->GetUniformLocation(program, name);
            assert(priv->uloc.TexSizes[i] != -1);
        }
    }

#ifdef HAVE_LIBPLACEBO_GL
    const struct pl_shader_res *res = priv->pl_sh_res;
    for (int i = 0; res && i < res->num_variables; i++) {
        struct pl_shader_var sv = res->variables[i];
        priv->uloc.pl_vars[i] = vt->GetUniformLocation(program, sv.var.name);
    }

    for (int i = 0; res && i < res->num_descriptors; i++) {
        struct pl_shader_desc sd = res->descriptors[i];
        priv->uloc.pl_descs[i] = vt->GetUniformLocation(program, sd.desc.name);
    }
#endif
}

static void
sampler_base_load(struct vlc_gl_sampler *sampler)
{
    struct sampler_priv *priv = PRIV(sampler);

    const opengl_vtable_t *vt = priv->vt;
    struct vlc_gl_picture *pic = &priv->pic;

    if (priv->yuv_color)
        vt->UniformMatrix4fv(priv->uloc.ConvMatrix, 1, GL_FALSE,
                             priv->conv_matrix);

    for (unsigned i = 0; i < sampler->tex_count; ++i)
    {
        vt->Uniform1i(priv->uloc.Textures[i], i);

        assert(pic->textures[i] != 0);
        vt->ActiveTexture(GL_TEXTURE0 + i);
        vt->BindTexture(sampler->tex_target, pic->textures[i]);

    }

    if (sampler->tex_target == GL_TEXTURE_RECTANGLE)
    {
        for (unsigned i = 0; i < sampler->tex_count; ++i)
            vt->Uniform2f(priv->uloc.TexSizes[i], sampler->tex_widths[i],
                          sampler->tex_heights[i]);
    }

#ifdef HAVE_LIBPLACEBO_GL
    const struct pl_shader_res *res = priv->pl_sh_res;
    for (int i = 0; res && i < res->num_variables; i++) {
        GLint loc = priv->uloc.pl_vars[i];
        if (loc == -1) // uniform optimized out
            continue;

        struct pl_shader_var sv = res->variables[i];
        struct pl_var var = sv.var;
        // libplacebo doesn't need anything else anyway
        assert(var.type == PL_VAR_FLOAT);
        assert(var.dim_m == 1 || var.dim_m == var.dim_v);

        const float *f = sv.data;
        switch (var.dim_m) {
        case 4: vt->UniformMatrix4fv(loc, var.dim_a, GL_FALSE, f); break;
        case 3: vt->UniformMatrix3fv(loc, var.dim_a, GL_FALSE, f); break;
        case 2: vt->UniformMatrix2fv(loc, var.dim_a, GL_FALSE, f); break;

        case 1:
            switch (var.dim_v) {
            case 1: vt->Uniform1fv(loc, var.dim_a, f); break;
            case 2: vt->Uniform2fv(loc, var.dim_a, f); break;
            case 3: vt->Uniform3fv(loc, var.dim_a, f); break;
            case 4: vt->Uniform4fv(loc, var.dim_a, f); break;
            }
            break;
        }
    }

    for (int i = 0; res && i < res->num_descriptors; i++) {
        GLint loc = priv->uloc.pl_descs[i];
        if (loc == -1)
            continue;
        struct pl_shader_desc sd = res->descriptors[i];
        assert(sd.desc.type == PL_DESC_SAMPLED_TEX);
        pl_tex tex = sd.binding.object;
        int texid = sampler->tex_count + i; // first free texture unit
        unsigned gltex, target;
        gltex = pl_opengl_unwrap(priv->pl_opengl->gpu, tex, &target, NULL, NULL);
        vt->Uniform1i(loc, texid);
        vt->ActiveTexture(GL_TEXTURE0 + texid);
        vt->BindTexture(target, gltex);

        static const GLint wraps[PL_TEX_ADDRESS_MODE_COUNT] = {
            [PL_TEX_ADDRESS_CLAMP]  = GL_CLAMP_TO_EDGE,
            [PL_TEX_ADDRESS_REPEAT] = GL_REPEAT,
            [PL_TEX_ADDRESS_MIRROR] = GL_MIRRORED_REPEAT,
        };

        static const GLint filters[PL_TEX_SAMPLE_MODE_COUNT] = {
            [PL_TEX_SAMPLE_NEAREST] = GL_NEAREST,
            [PL_TEX_SAMPLE_LINEAR]  = GL_LINEAR,
        };

        GLint filter = filters[sd.binding.sample_mode];
        GLint wrap = wraps[sd.binding.address_mode];
        vt->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
        vt->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
        switch (pl_tex_params_dimension(tex->params)) {
        case 3: vt->TexParameteri(target, GL_TEXTURE_WRAP_R, wrap); // fall through
        case 2: vt->TexParameteri(target, GL_TEXTURE_WRAP_T, wrap); // fall through
        case 1: vt->TexParameteri(target, GL_TEXTURE_WRAP_S, wrap); break;
        }
    }
#endif
}

static void
sampler_xyz12_fetch_locations(struct vlc_gl_sampler *sampler, uint32_t program)
{
    struct sampler_priv *priv = PRIV(sampler);
    const opengl_vtable_t *vt = priv->vt;

    priv->uloc.Textures[0] = vt->GetUniformLocation(program, "Textures[0]");
    assert(priv->uloc.Textures[0] != -1);
}

static void
sampler_xyz12_load(struct vlc_gl_sampler *sampler)
{
    struct sampler_priv *priv = PRIV(sampler);
    const opengl_vtable_t *vt = priv->vt;
    struct vlc_gl_picture *pic = &priv->pic;

    vt->Uniform1i(priv->uloc.Textures[0], 0);

    assert(pic->textures[0] != 0);
    vt->ActiveTexture(GL_TEXTURE0);
    vt->BindTexture(sampler->tex_target, pic->textures[0]);
}

static int
xyz12_shader_init(struct vlc_gl_sampler *sampler)
{
    static const struct vlc_gl_sampler_ops ops = {
        .fetch_locations = sampler_xyz12_fetch_locations,
        .load = sampler_xyz12_load,
        .update = sampler_update,
        .select_plane = sampler_select_plane,
        .close = sampler_close,
    };
    sampler->ops = &ops;

    /* Shader for XYZ to RGB correction
     * 3 steps :
     *  - XYZ gamma correction
     *  - XYZ to RGB matrix conversion
     *  - reverse RGB gamma correction
     */

    static const char *template =
        "uniform sampler2D Textures[1];"
        "uniform vec4 xyz_gamma = vec4(2.6);"
        "uniform vec4 rgb_gamma = vec4(1.0/2.2);"
        /* WARN: matrix Is filled column by column (not row !) */
        "uniform mat4 matrix_xyz_rgb = mat4("
        "    3.240454 , -0.9692660, 0.0556434, 0.0,"
        "   -1.5371385,  1.8760108, -0.2040259, 0.0,"
        "    -0.4985314, 0.0415560, 1.0572252,  0.0,"
        "    0.0,      0.0,         0.0,        1.0 "
        " );"

        "vec4 vlc_texture(vec2 tex_coords)\n"
        "{ "
        " vec4 v_in, v_out;"
        " v_in  = texture2D(Textures[0], tex_coords);\n"
        " v_in = pow(v_in, xyz_gamma);"
        " v_out = matrix_xyz_rgb * v_in ;"
        " v_out = pow(v_out, rgb_gamma) ;"
        " v_out = clamp(v_out, 0.0, 1.0) ;"
        " return v_out;"
        "}\n";

    sampler->shader.body = strdup(template);
    if (!sampler->shader.body)
        return VLC_ENOMEM;

    return VLC_SUCCESS;
}

static int
opengl_init_swizzle(struct vlc_gl_sampler *sampler,
                    const char *swizzle_per_tex[],
                    const vlc_chroma_description_t *desc)
{
    if (desc->plane_count == 4)
        swizzle_per_tex[0] = swizzle_per_tex[1] = swizzle_per_tex[2] = swizzle_per_tex[3] = "r";
    else if (desc->plane_count == 3)
        swizzle_per_tex[0] = swizzle_per_tex[1] = swizzle_per_tex[2] = "r";
    else if (desc->plane_count == 2)
    {
        swizzle_per_tex[0] = "r";
        if (sampler->formats[1] == GL_RG)
            swizzle_per_tex[1] = "rg";
        else
            swizzle_per_tex[1] = "ra";
    }
    else if (desc->plane_count == 1)
    {
        /*
         * One plane, but uploaded into two separate textures for Y and UV.
         *
         * Set swizzling in Y1 U V order
         * R  G  B  A
         * U  Y1 V  Y2 => GRB
         * Y1 U  Y2 V  => RGA
         * V  Y1 U  Y2 => GBR
         * Y1 V  Y2 U  => RAG
         */

        switch (desc->fcc)
        {
            case VLC_CODEC_UYVY:
                swizzle_per_tex[0] = "g";
                swizzle_per_tex[1] = "rb";
                break;
            case VLC_CODEC_YUYV:
                swizzle_per_tex[0] = "r";
                swizzle_per_tex[1] = "ga";
                break;
            case VLC_CODEC_VYUY:
                swizzle_per_tex[0] = "g";
                swizzle_per_tex[1] = "br";
                break;
            case VLC_CODEC_YVYU:
                swizzle_per_tex[0] = "r";
                swizzle_per_tex[1] = "ag";
                break;
            case VLC_CODEC_V308:
                swizzle_per_tex[0] = "rgb";
                assert(sampler->tex_count == 1);
                break;
            case VLC_CODEC_VUYX:
            case VLC_CODEC_VUYA:
                swizzle_per_tex[0] = "bgr";
                break;
            case VLC_CODEC_Y410:
            case VLC_CODEC_Y412:
                swizzle_per_tex[0] = "grb";
                break;
            default:
                assert(!"missing chroma");
                return VLC_EGENERIC;
        }
    }
    return VLC_SUCCESS;
}

static void
GetNames(struct vlc_gl_sampler *sampler, GLenum tex_target,
         const char **glsl_sampler, const char **texture)
{
    struct sampler_priv *priv = PRIV(sampler);

    bool has_texture_func =
        (priv->gl->api_type == VLC_OPENGL && priv->glsl_version >= 130) ||
        (priv->gl->api_type == VLC_OPENGL_ES2 && priv->glsl_version >= 300);

    //GLES has no texture rectangle
    bool has_texture_rect_func =
        (priv->gl->api_type == VLC_OPENGL && priv->glsl_version >= 140);

    const bool is_yuv = vlc_fourcc_IsYUV(sampler->fmt_in.i_chroma);

    switch (tex_target)
    {
        case GL_TEXTURE_EXTERNAL_OES:
            *glsl_sampler = is_yuv ? "__samplerExternal2DY2YEXT" : "samplerExternalOES";
            *texture = has_texture_func ? "texture" : "texture2D";
            break;
        case GL_TEXTURE_2D:
            *glsl_sampler = priv->unsigned_sampler ? "usampler2D" : "sampler2D";
            *texture = has_texture_func ? "texture" : "texture2D";
            break;
        case GL_TEXTURE_RECTANGLE:
            *glsl_sampler = "sampler2DRect";
            *texture = has_texture_rect_func ? "texture" : "texture2DRect";
            break;
        default:
            vlc_assert_unreachable();
    }
}

static int
InitShaderExtensions(struct vlc_gl_sampler *sampler, GLenum tex_target)
{
    struct sampler_priv *priv = PRIV(sampler);
    const bool is_yuv = vlc_fourcc_IsYUV(sampler->fmt_in.i_chroma);

    const char *image_external = priv->glsl_version >= 300
        ? "#extension GL_OES_EGL_image_external_essl3 : require\n"
        : "#extension GL_OES_EGL_image_external : require\n";

    const char *ext_yuv_target =
        "#extension GL_EXT_YUV_target : require\n";

    const char *image_ext = "";
    if (tex_target == GL_TEXTURE_EXTERNAL_OES)
    {
        if (is_yuv)
            image_ext = ext_yuv_target;
        else
            image_ext = image_external;
    }

    const char *texture3D_ext = priv->api.supports_sampler3D
        ? "#extension GL_OES_texture_3D : enable\n" : "";

    int ret = asprintf(&sampler->shader.extensions,"%s%s\n", image_ext, texture3D_ext);
    if (ret <= 0)
    {
        if (ret == 0)
            FREENULL(sampler->shader.extensions);
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

static void
sampler_planes_fetch_locations(struct vlc_gl_sampler *sampler, uint32_t program)
{
    struct sampler_priv *priv = PRIV(sampler);

    const opengl_vtable_t *vt = priv->vt;

    priv->uloc.Textures[0] = vt->GetUniformLocation(program, "Texture");
    assert(priv->uloc.Textures[0] != -1);

    if (sampler->tex_target == GL_TEXTURE_RECTANGLE)
    {
        priv->uloc.TexSizes[0] = vt->GetUniformLocation(program, "TexSize");
        assert(priv->uloc.TexSizes[0] != -1);
    }
}

static void
sampler_planes_load(struct vlc_gl_sampler *sampler)
{
    struct sampler_priv *priv = PRIV(sampler);
    unsigned plane = priv->plane;

    const opengl_vtable_t *vt = priv->vt;
    struct vlc_gl_picture *pic = &priv->pic;

    vt->Uniform1i(priv->uloc.Textures[0], 0);

    assert(pic->textures[plane] != 0);
    vt->ActiveTexture(GL_TEXTURE0);
    vt->BindTexture(sampler->tex_target, pic->textures[plane]);

    if (sampler->tex_target == GL_TEXTURE_RECTANGLE)
    {
        vt->Uniform2f(priv->uloc.TexSizes[0], sampler->tex_widths[plane],
                      sampler->tex_heights[plane]);
    }
}

static int
sampler_planes_init(struct vlc_gl_sampler *sampler)
{
    GLenum tex_target = sampler->tex_target;

    struct vlc_memstream ms;
    if (vlc_memstream_open(&ms))
        return VLC_EGENERIC;

#define ADD(x) vlc_memstream_puts(&ms, x)
#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)

    const char *sampler_type;
    const char *texture_fn;
    GetNames(sampler, tex_target, &sampler_type, &texture_fn);

    ADDF("uniform %s Texture;\n", sampler_type);

    if (tex_target == GL_TEXTURE_RECTANGLE)
        ADD("uniform vec2 TexSize;\n");

    ADD("vec4 vlc_texture(vec2 tex_coords) {\n");

    if (tex_target == GL_TEXTURE_RECTANGLE)
    {
        /* The coordinates are in texels values, not normalized */
        ADD(" tex_coords = TexSize * tex_coords;\n");
    }

    ADDF("  return %s(Texture, tex_coords);\n", texture_fn);
    ADD("}\n");

#undef ADD
#undef ADDF

    if (vlc_memstream_close(&ms) != 0)
        return VLC_EGENERIC;

    int ret = InitShaderExtensions(sampler, tex_target);
    if (ret != VLC_SUCCESS)
    {
        free(ms.ptr);
        return VLC_EGENERIC;
    }
    sampler->shader.body = ms.ptr;

    static const struct vlc_gl_sampler_ops ops = {
        .fetch_locations = sampler_planes_fetch_locations,
        .load = sampler_planes_load,
        .update = sampler_update,
        .select_plane = sampler_select_plane,
        .close = sampler_close,
    };
    sampler->ops = &ops;

    return VLC_SUCCESS;
}

#ifdef HAVE_LIBPLACEBO_GL
static struct pl_custom_lut *LoadCustomLUT(struct vlc_gl_sampler *sampler,
                                           const char *filepath)
{
    struct sampler_priv *priv = PRIV(sampler);
    if (!filepath || !filepath[0])
        return NULL;

    FILE *fs = vlc_fopen(filepath, "rb");
    struct pl_custom_lut *lut = NULL;
    char *lut_file = NULL;
    if (!fs)
        goto error;
    int ret = fseek(fs, 0, SEEK_END);
    if (ret == -1)
        goto error;
    long length = ftell(fs);
    if (length < 0)
        goto error;
    rewind(fs);

    lut_file = vlc_alloc(length, sizeof(*lut_file));
    if (!lut_file)
        goto error;
    ret = fread(lut_file, length, 1, fs);
    if (ret != 1)
        goto error;

    lut = pl_lut_parse_cube(priv->pl_log, lut_file, length);
    // fall through

error:
    if (fs)
        fclose(fs);
    free(lut_file);
    return lut;
}
#endif

static int
opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, bool expose_planes)
{
    struct sampler_priv *priv = PRIV(sampler);
    const video_format_t *fmt = &sampler->fmt_in;

    GLenum tex_target = sampler->tex_target;

    priv->expose_planes = expose_planes;
    priv->plane = 0;

    vlc_fourcc_t chroma = fmt->i_chroma;
    video_color_space_t yuv_space = fmt->space;

    const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL, };
    const bool is_yuv = vlc_fourcc_IsYUV(chroma);
    int ret;

    const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
    if (desc == NULL)
    {
        msg_Dbg(priv->gl, "opengl sampler: unknown chroma description %4.4s", (const char *)&chroma);
        return VLC_EGENERIC;
    }

    unsigned tex_count = sampler->tex_count;

    if (expose_planes)
        return sampler_planes_init(sampler);

    if (chroma == VLC_CODEC_XYZ_12L ||
        chroma == VLC_CODEC_XYZ_12B)
        return xyz12_shader_init(sampler);

    const bool is_y21x_packed = chroma == VLC_CODEC_Y210
                             || chroma == VLC_CODEC_Y212;

    if (is_yuv)
    {
        ret = sampler_yuv_base_init(sampler, desc, yuv_space);
        if (ret != VLC_SUCCESS)
        {
            msg_Dbg(priv->gl, "opengl sampler: could not initialize YUV shaders for %4.4s", (const char *)&chroma);
            return ret;
        }
        if (!is_y21x_packed)
        {
            ret = opengl_init_swizzle(sampler, swizzle_per_tex, desc);
            if (ret != VLC_SUCCESS)
            {
                msg_Dbg(priv->gl, "opengl sampler: could not determine swizzle for %4.4s", (const char *)&chroma);
                return ret;
            }
        }
    }

    const char *glsl_sampler, *lookup;
    GetNames(sampler, tex_target, &glsl_sampler, &lookup);

    struct vlc_memstream ms;
    if (vlc_memstream_open(&ms) != 0)
        return VLC_EGENERIC;

#define ADD(x) vlc_memstream_puts(&ms, x)
#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)

    ADDF("uniform %s Textures[%u];\n", glsl_sampler, tex_count);

#ifdef HAVE_LIBPLACEBO_GL
    if (priv->pl_sh) {
        pl_shader sh = priv->pl_sh;
        struct pl_color_map_params color_params;
        vlc_placebo_ColorMapParams(VLC_OBJECT(priv->gl), "gl", &color_params);

        struct pl_color_space src_space = vlc_placebo_ColorSpace(fmt);
        struct pl_color_space dst_space = pl_color_space_unknown;
        dst_space.primaries = var_InheritInteger(priv->gl, "target-prim");
        dst_space.transfer = var_InheritInteger(priv->gl, "target-trc");

        char *lut_file = var_InheritString(priv->gl, "gl-lut-file");
        struct pl_custom_lut *lut = LoadCustomLUT(sampler, lut_file);
        free(lut_file);
        if (lut) {
            // Transform from the video input to the LUT input color space,
            // defaulting to a no-op if LUT input color space info is unknown
            dst_space = lut->color_in;
            pl_color_space_merge(&dst_space, &src_space);
        }

        pl_shader_color_map(sh, &color_params, src_space, dst_space,
                            &priv->tone_map_state, false);

        if (lut) {
            pl_shader_custom_lut(sh, lut, &priv->lut_state);
            pl_lut_free(&lut);
        }

        int method = var_InheritInteger(priv->gl, "dither-algo");
        if (method >= 0) {

            unsigned out_bits = 0;
            int override = var_InheritInteger(priv->gl, "dither-depth");
            if (override > 0)
                out_bits = override;
            else
            {
                GLint fb_depth = 0;
#if !defined(USE_OPENGL_ES2)
                const opengl_vtable_t *vt = priv->vt;
                /* fetch framebuffer depth (we are already bound to the default one). */
                if (priv->gl->api_type == VLC_OPENGL &&
                    vt->GetFramebufferAttachmentParameteriv != NULL)
                {
                    vt->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT,
                                                            GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
                                                            &fb_depth);
                }
#endif
                if (fb_depth <= 0)
                    fb_depth = 8;
                out_bits = fb_depth;
            }

            pl_shader_dither(sh, out_bits, &priv->dither_state, &(struct pl_dither_params) {
                .method   = method,
            });
        }

        const struct pl_shader_res *res = priv->pl_sh_res = pl_shader_finalize(sh);
# if PL_API_VER >= 266
        msg_Dbg(priv->gl, "libplacebo shader: %s", res->info->description);
# else
        msg_Dbg(priv->gl, "libplacebo shader: %s", res->description);
# endif

        FREENULL(priv->uloc.pl_vars);
        priv->uloc.pl_vars = calloc(res->num_variables, sizeof(GLint));
        for (int i = 0; i < res->num_variables; i++) {
            struct pl_shader_var sv = res->variables[i];
            const char *glsl_type_name = pl_var_glsl_type_name(sv.var);
            ADDF("uniform %s %s", glsl_type_name, sv.var.name);
            if (sv.var.dim_a > 1) {
                ADDF("[%d];\n", sv.var.dim_a);
            } else {
                ADDF(";\n");
            }
        }

        FREENULL(priv->uloc.pl_descs);
        priv->uloc.pl_descs = calloc(res->num_descriptors, sizeof(GLint));
        for (int i = 0; i < res->num_descriptors; i++) {
            struct pl_shader_desc sd = res->descriptors[i];
            assert(sd.desc.type == PL_DESC_SAMPLED_TEX);
            pl_tex tex = sd.binding.object;
            assert(tex->sampler_type == PL_SAMPLER_NORMAL);
            int dims = pl_tex_params_dimension(tex->params);
            ADDF("uniform sampler%dD %s;\n", dims, sd.desc.name);
        }

        // We can't handle these yet, but nothing we use requires them, either
        assert(res->num_vertex_attribs == 0);

        ADD(res->glsl);
    }
#else
    if (fmt->transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
        fmt->primaries == COLOR_PRIMARIES_BT2020)
    {
        // no warning for HLG because it's more or less backwards-compatible
        msg_Warn(priv->gl, "VLC needs to be built with support for libplacebo "
                 "in order to display wide gamut or HDR signals correctly.");
    }
#endif

    if (tex_target == GL_TEXTURE_RECTANGLE)
        ADDF("uniform vec2 TexSizes[%u];\n", tex_count);

    if (is_yuv)
        ADD("uniform mat4 ConvMatrix;\n");

    ADD("vec4 vlc_texture(vec2 tex_coords) {\n");

    unsigned color_count;
    if (is_y21x_packed)
    {
        assert(is_yuv);
        assert(tex_count == 1);
        /* Y210/Y212: packed 4:2:2, each 8-byte texel holds 2 pixels:
         * [Y0:16][U:16][Y1:16][V:16] -> sampled as (R=Y0, G=U, B=Y1, A=V)
         * A simple swizzle can't select the correct luma, so bypass the
         * generic path and use texelFetch to pick Y0 or Y1 by pixel parity. */

        ADD(" ivec2 _ts = textureSize(Textures[0], 0);\n"
            " int _px = int(tex_coords.x * float(_ts.x) * 2.0);\n"
            " int _py = int(tex_coords.y * float(_ts.y));\n"
            " vec4 _t = texelFetch(Textures[0], ivec2(_px >> 1, _py), 0);\n"
            " float _Y = ((_px & 1) == 0) ? _t.r : _t.b;\n"
            " vec4 pixel = vec4(_Y, _t.g, _t.a, 1.0);\n"
            " vec4 result = ConvMatrix * pixel;\n");
        color_count = 3;
    }
    else if (is_yuv)
    {
        if (priv->unsigned_sampler)
        {
            /* One extra integer -> float step will be needed */
            for (unsigned i = 0; i < tex_count; ++i)
            {
                if (tex_target == GL_TEXTURE_RECTANGLE)
                    ADDF(" uvec4 t%u = %s(Textures[%u], TexSizes[%u] * tex_coords);\n",
                         i, lookup, i, i);
                else
                    ADDF(" uvec4 t%u = %s(Textures[%u], tex_coords);\n",
                         i, lookup, i);
            }
        }

        ADD(" vec4 pixel = vec4(\n");
        color_count = 0;
        for (unsigned i = 0; i < tex_count; ++i)
        {
            const char *swizzle = swizzle_per_tex[i];
            assert(swizzle);
            color_count += strlen(swizzle);
            assert(color_count < PICTURE_PLANE_MAX);
            if (i > 0)
                ADD("  ,");

            if (priv->unsigned_sampler)
            {
                /* Integer -> float */
                ADDF("  float(t%u.%s) / 65535.0\n", i, swizzle);
            }
            else if (tex_target == GL_TEXTURE_RECTANGLE)
            {
                /* The coordinates are in texels values, not normalized */
                ADDF("  %s(Textures[%u], TexSizes[%u] * tex_coords).%s\n", lookup, i, i, swizzle);
            }
            else
            {
                ADDF("  %s(Textures[%u], tex_coords).%s\n", lookup, i, swizzle);
            }
        }
        if (color_count == 3)
            ADD("  ,  1.0);\n");
        else
            ADD(");\n");
        ADD(" vec4 result = ConvMatrix * pixel;\n");
    }
    else
    {
        if (tex_target == GL_TEXTURE_RECTANGLE)
            ADD(" tex_coords *= TexSizes[0];\n");

        ADDF(" vec4 result = %s(Textures[0], tex_coords);\n", lookup);
        color_count = 1;
    }
    assert(yuv_space == COLOR_SPACE_UNDEF || color_count == 3 || color_count == 4);

#ifdef HAVE_LIBPLACEBO_GL
    if (priv->pl_sh_res) {
        const struct pl_shader_res *res = priv->pl_sh_res;
        if (res->input != PL_SHADER_SIG_NONE) {
            assert(res->input  == PL_SHADER_SIG_COLOR);
            assert(res->output == PL_SHADER_SIG_COLOR);
            ADDF(" result = %s(result);\n", res->name);
        }
    }
#endif

    ADD(" return result;\n"
        "}\n");

#undef ADD
#undef ADDF

    if (vlc_memstream_close(&ms) != 0)
        return VLC_EGENERIC;

    ret = InitShaderExtensions(sampler, tex_target);
    if (ret != VLC_SUCCESS)
    {
        free(ms.ptr);
        return VLC_EGENERIC;
    }
    sampler->shader.body = ms.ptr;

    static const struct vlc_gl_sampler_ops ops = {
        .fetch_locations = sampler_base_fetch_locations,
        .load = sampler_base_load,
        .update = sampler_update,
        .select_plane = sampler_select_plane,
        .close = sampler_close,
    };
    sampler->ops = &ops;

    return VLC_SUCCESS;
}

#if defined(HAVE_LIBPLACEBO_GL) && PL_API_VER >= 215
static pl_voidfunc_t PlaceboGetProcAddr(void *opaque, const char *name)
{
    vlc_gl_t *gl = opaque;
    return (pl_voidfunc_t)vlc_gl_GetProcAddress(gl, name);
}
#endif

static int
Open(struct vlc_gl_sampler *sampler,
     bool expose_planes)
{
    struct sampler_priv *priv = calloc(1, sizeof(*priv));
    if (!priv)
        return VLC_ENOMEM;

    sampler->sys = priv;

    priv->gl = sampler->gl;

    int ret = vlc_gl_api_Init(&priv->api, sampler->gl);
    if (ret != VLC_SUCCESS)
    {
        free(priv);
        return ret;
    }
    priv->vt = &priv->api.vt;

    vlc_gl_LoadExtensionFunctions(sampler->gl, &priv->extension_vt);

    memcpy(priv->pic.mtx, MATRIX2x3_IDENTITY, sizeof(MATRIX2x3_IDENTITY));
    priv->pic.mtx_has_changed = true;

    sampler->pic_to_tex_matrix = priv->pic.mtx;

    sampler->shader.extensions = NULL;
    sampler->shader.body = NULL;

    switch (sampler->formats[0])
    {
        case GL_RED_INTEGER:
        case GL_RG_INTEGER:
            priv->unsigned_sampler = true;
            break;
        default:
            priv->unsigned_sampler = false;
            break;
    }

    const struct vlc_gl_api *api = &priv->api;
    int glsl_version;
    if (api->is_gles) {
        if (asprintf(&sampler->shader.precision,
            "precision highp float;\n"
            "precision highp sampler2D;\n"
            "%s%s",
            api->supports_sampler3D ? "precision highp sampler3D;\n" : "",
            priv->unsigned_sampler ? "precision highp usampler2D;\n" : "" ) <= 0 )
        {
            free(priv);
            return VLC_ENOMEM;
        }

        if (api->glsl_version >= 300) {
            sampler->shader.version = strdup("#version 300 es\n");
            glsl_version = 300;
        } else {
            sampler->shader.version = strdup("#version 100\n");
            glsl_version = 100;
        }
        if (!sampler->shader.version)
        {
            free(sampler->shader.precision);
            sampler->shader.precision = NULL;
            free(priv);
            return VLC_ENOMEM;
        }
    } else {
        sampler->shader.precision = strdup("// no precision\n");
        if (!sampler->shader.precision)
        {
            free(priv);
            return VLC_ENOMEM;
        }
        /* GLSL version 420+ breaks backwards compatibility with pre-GLSL 130
         * syntax, which we use in our vertex/fragment shaders. */

        glsl_version = __MIN(api->glsl_version, 410);
        if (asprintf(&sampler->shader.version, "#version %d\n", glsl_version) < 0)
        {
            free(sampler->shader.precision);
            sampler->shader.precision = NULL;
            free(priv);
            return VLC_ENOMEM;
        }
    }
    priv->glsl_version = glsl_version;

#ifdef HAVE_LIBPLACEBO_GL
    priv->uloc.pl_vars = NULL;
    priv->uloc.pl_descs = NULL;
    priv->pl_sh_res = NULL;
    priv->pl_log = vlc_placebo_CreateLog(VLC_OBJECT(sampler->gl));

    const struct pl_opengl_params gl_params =
    {
        /* Libplacebo won't control whether the context is current or not.
         * This is the default but we also want to ensure that the struct is
         * properly initialized even if PL_API_VER < 215. */

        .make_current = NULL,
        .release_current = NULL,

#if PL_API_VER >= 215
        .get_proc_addr_ex = PlaceboGetProcAddr,
        .proc_ctx = sampler->gl,
#endif
    };

    const opengl_vtable_t *vt = priv->vt;

    /* Workaround libplacebo changing the current framebuffer when running
     * with OpenGL. */

    GLint out_fb;
    vt->GetIntegerv(GL_FRAMEBUFFER_BINDING, &out_fb);

    priv->pl_opengl = pl_opengl_create(priv->pl_log, &gl_params);
    if (!priv->pl_opengl)
    {
        msg_Dbg(sampler->gl, "opengl sampler: could not initialize libplacebo");
        pl_log_destroy(&priv->pl_log);
        free(sampler->shader.precision);
        sampler->shader.precision = NULL;
        free(sampler->shader.version);
        sampler->shader.version = NULL;
        free(priv);
        return VLC_EGENERIC;
    }

    vt->BindFramebuffer(GL_FRAMEBUFFER, out_fb);

    priv->pl_sh = pl_shader_alloc(priv->pl_log, &(struct pl_shader_params) {
        .gpu = priv->pl_opengl->gpu,
        .glsl = {
            .version = glsl_version,
            .gles = sampler->gl->api_type == VLC_OPENGL_ES2,
        },
    });
#endif

    ret = opengl_fragment_shader_init(sampler, expose_planes);
    if (ret != VLC_SUCCESS)
    {
        msg_Dbg(sampler->gl, "opengl sampler: could not initialize shaders");
        free(sampler->shader.precision);
        sampler->shader.precision = NULL;
        free(sampler->shader.version);
        sampler->shader.version = NULL;
        free(priv);
        return ret;
    }

    return VLC_SUCCESS;
}

#include <vlc_plugin.h>

vlc_module_begin()
    set_shortname("glsampler")
    set_description("OpenGL built-in sampler")
    set_callback_opengl_sampler(Open)
    set_capability("opengl sampler", 1)
    set_subcategory(SUBCAT_VIDEO_VOUT)
#ifdef HAVE_LIBPLACEBO_GL
    set_section(N_("Colorspace conversion"), NULL)
    add_placebo_color_map_opts("gl")
    add_integer("target-prim", PL_COLOR_PRIM_UNKNOWN,
                N_("Target primaries"), N_("Override the target color primaries"))
            change_integer_list(prim_values, prim_text)
    add_integer("target-trc", PL_COLOR_TRC_UNKNOWN,
                N_("Target transfer"), N_("Override the target transfer function"))
            change_integer_list(trc_values, trc_text)
    set_section(N_("Dithering"), NULL)
    add_integer("dither-algo", -1,
                N_("Dithering algorithm"), N_("Algorithm for dithering"))
            change_integer_list(dither_values, dither_text)
    add_integer_with_range("dither-depth", 0, 0, 16,
                           N_("Dither depth"), N_("Override the dither depth"))
    add_loadfile("gl-lut-file", NULL,
                 N_("Custom LUT"), N_("Path to a custom .cube LUT file"))
#endif
vlc_module_end()

Messung
CHZG
859610093

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417348