Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/video_output/opengl/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 28 kB image not shown  

Quelle  renderer.c   Sprache: C

 

/*****************************************************************************
 * renderer.c
 *****************************************************************************
 * Copyright (C) 2004-2020 VLC authors and VideoLAN
 * Copyright (C) 2009, 2011 Laurent Aimar
 *
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
 *          Ilkka Ollakka <ileoo@videolan.org>
 *          Rémi Denis-Courmont
 *          Adrien Maglo <magsoft at videolan dot org>
 *          Felix Paul Kühne <fkuehne at videolan dot org>
 *          Pierre d'Herbemont <pdherbemont at videolan dot org>
 *          Alexandre Janniaux <ajanni@videolabs.io>
 *
 * 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 "renderer.h"

#include <assert.h>
#include <vlc_common.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_opengl_filter.h>
#include <vlc_configuration.h>

#include "gl_util.h"
#include "sampler.h"
#include "picture.h"

#define SPHERE_RADIUS 1.f

static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {

    const GLfloat m[] = {
        /* x   y     z     w */
        1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, zoom, 1.0f
    };

    memcpy(matrix, m, sizeof(m));
}

/* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {

    float zFar  = 1000;
    float zNear = 0.01;

    float f = 1.f / tanf(fovy / 2.f);

    const GLfloat m[] = {
        f / sar, 0.f,                   0.f,                0.f,
        0.f,     f,                     0.f,                0.f,
        0.f,     0.f,     (zNear + zFar) / (zNear - zFar), -1.f,
        0.f,     0.f, (2 * zNear * zFar) / (zNear - zFar),  0.f};

     memcpy(matrix, m, sizeof(m));
}

static void getViewpointMatrixes(struct vlc_gl_renderer *renderer,
                                 video_projection_mode_t projection_mode)
{
    if ((projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
        || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
        && renderer->multiview_mode == renderer->sampler->fmt_in.multiview_mode)
    {
        getProjectionMatrix(renderer->f_sar, renderer->f_fovy,
                            renderer->var.ProjectionMatrix);
        getZoomMatrix(renderer->f_z, renderer->var.ZoomMatrix);

        /* renderer->vp has been reversed and is a world transform */
        vlc_viewpoint_to_4x4(&renderer->vp, renderer->var.ViewMatrix);
    }
    else
    {
        memcpy(renderer->var.ProjectionMatrix, MATRIX4_IDENTITY,
                                               sizeof(MATRIX4_IDENTITY));
        memcpy(renderer->var.ZoomMatrix, MATRIX4_IDENTITY,
                                         sizeof(MATRIX4_IDENTITY));
        memcpy(renderer->var.ViewMatrix, MATRIX4_IDENTITY,
                                         sizeof(MATRIX4_IDENTITY));
    }

}

static void
InitStereoMatrix(GLfloat matrix_out[static 3*3],
                 vlc_stereoscopic_mode_t stereo_mode,
                 video_multiview_mode_t multiview_mode)
{
    /*
     * The stereo matrix transforms 2D pictures coordinates to crop the
     * content, in order to view only one eye.
     *
     * This 2D transformation is affine, so the matrix is 3x3 and applies to 3D
     * vectors in the form (x, y, 1).
     */


    memcpy(matrix_out, MATRIX3_IDENTITY, sizeof(MATRIX3_IDENTITY));

#define COL(x) (x*3)
#define ROW(x) (x)

    if (stereo_mode == VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE)
        return;
    assert(stereo_mode == VIDEO_STEREO_OUTPUT_LEFT_ONLY ||
           stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY);

    switch (multiview_mode)
    {
        case MULTIVIEW_STEREO_SBS:
            /*
             * +----------+----------+
             * |          .          |
             * |  LEFT    .   RIGHT  |
             * |  EYE     .     EYE  |
             * |          .          |
             * +----------+----------+
             *
             * To crop the coordinates to the left eye, divide the x
             * coordinates by 2:
             *
             *            / 0.5  0    0 \
             *  matrix =  | 0    1    0 |
             *            \ 0    0    1 /
             */

            matrix_out[COL(0) + ROW(0)] = 0.5;
            if (stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY)
                matrix_out[COL(2) + ROW(0)] = 0.5;
            break;
        case MULTIVIEW_STEREO_TB:
            /*
             * +----------+
             * |          |
             * |  LEFT    |
             * |  EYE     |
             * |          |
             * +..........+
             * |          |
             * |   RIGHT  |
             * |     EYE  |
             * |          |
             * +----------+
             *
             * To crop the coordinates to the left eye, divide the y
             * coordinates by 2:
             *
             *            / 1    0    0 \
             *  matrix =  | 0    0.5  0 |
             *            \ 0    0    1 /
             */

            matrix_out[COL(1) + ROW(1)] = 0.5;
            if (stereo_mode == VIDEO_STEREO_OUTPUT_RIGHT_ONLY)
                matrix_out[COL(2) + ROW(1)] = 0.5;
            break;
        default:
            break;
    }
#undef COL
#undef ROW
}

static int
opengl_link_program(struct vlc_gl_filter *filter)
{
    struct vlc_gl_renderer *renderer = filter->sys;
    struct vlc_gl_sampler *sampler = renderer->sampler;
    const opengl_vtable_t *vt = renderer->vt;

    static const char *const VERTEX_SHADER_BODY =
        "#if __VERSION__ < 300\n"
          "attribute vec2 PicCoordsIn;\n"
          "varying vec2 PicCoords;\n"
          "attribute vec3 VertexPosition;\n"
        "#else\n"
          "in vec2 PicCoordsIn;\n"
          "out vec2 PicCoords;\n"
          "in vec3 VertexPosition;\n"
        "#endif\n"
        "uniform mat3 StereoMatrix;\n"
        "uniform mat4 ProjectionMatrix;\n"
        "uniform mat4 ZoomMatrix;\n"
        "uniform mat4 ViewMatrix;\n"
        "uniform mat4 OrientationMatrix;\n"
        "void main() {\n"
        " PicCoords = (StereoMatrix * vec3(PicCoordsIn, 1.0)).st;\n"
        " gl_Position = OrientationMatrix * ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
        "               * vec4(VertexPosition, 1.0);\n"
        "}\n";

    static const char *const FRAGMENT_SHADER_BODY =
        "#if __VERSION__ < 300\n"
          "#define FragColor gl_FragColor\n"
          "varying vec2 PicCoords;\n"
        "#else\n"
          "in vec2 PicCoords;\n"
          "out vec4 FragColor;\n"
        "#endif\n"
        "void main() {\n"
        " FragColor = vlc_texture(PicCoords);\n"
        "}\n";

    const char *extensions = sampler->shader.extensions
                           ? sampler->shader.extensions : "";

    const char *vertex_shader[] = {
        sampler->shader.version,
        VERTEX_SHADER_BODY,
    };
    const char *fragment_shader[] = {
        sampler->shader.version,
        extensions,
        sampler->shader.precision,
        sampler->shader.body,
        FRAGMENT_SHADER_BODY,
    };

    if (renderer->dump_shaders)
    {
        video_format_t *fmt = &sampler->fmt_in;
        msg_Dbg(filter, "\n=== Vertex shader for fourcc: %4.4s ===\n",
                (const char *) &fmt->i_chroma);
        for (unsigned i = 0; i < ARRAY_SIZE(vertex_shader); ++i)
            msg_Dbg(filter, "[%u] %s", i, vertex_shader[i]);

        msg_Dbg(filter,
                "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n",
                (const char *) &fmt->i_chroma, fmt->space);
        for (unsigned i = 0; i < ARRAY_SIZE(fragment_shader); ++i)
            msg_Dbg(filter, "[%u] %s", i, fragment_shader[i]);
    }

    assert(sampler->ops &&
           sampler->ops->fetch_locations &&
           sampler->ops->load);

    GLuint program_id =
        vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
                            ARRAY_SIZE(vertex_shader), vertex_shader,
                            ARRAY_SIZE(fragment_shader), fragment_shader);
    if (!program_id)
        return VLC_EGENERIC;

    /* Fetch UniformLocations and AttribLocations */
#define GET_LOC(type, x, str) do { \
    x = vt->Get##type##Location(program_id, str); \
    assert(x != -1); \
    if (x == -1) { \
        msg_Err(filter, "Unable to Get"#type"Location(%s)", str); \
        goto error; \
    } \
} while (0)
#define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
#define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
    GET_ULOC(StereoMatrix, "StereoMatrix");
    GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
    GET_ULOC(ViewMatrix, "ViewMatrix");
    GET_ULOC(ZoomMatrix, "ZoomMatrix");
    GET_ULOC(OrientationMatrix, "OrientationMatrix");

    GET_ALOC(PicCoordsIn, "PicCoordsIn");
    GET_ALOC(VertexPosition, "VertexPosition");
#undef GET_LOC
#undef GET_ULOC
#undef GET_ALOC

    vlc_gl_sampler_FetchLocations(sampler, program_id);

    renderer->program_id = program_id;

    return VLC_SUCCESS;

error:
    vt->DeleteProgram(program_id);
    renderer->program_id = 0;
    return VLC_EGENERIC;
}

static void
Close(struct vlc_gl_filter *filter)
{
    struct vlc_gl_renderer *renderer = filter->sys;
    const opengl_vtable_t *vt = renderer->vt;

    vt->DeleteBuffers(1, &renderer->vertex_buffer_object);
    vt->DeleteBuffers(1, &renderer->index_buffer_object);
    vt->DeleteBuffers(1, &renderer->texture_buffer_object);
    vt->DeleteVertexArrays(1, &renderer->vertex_array_object);

    if (renderer->program_id != 0)
        vt->DeleteProgram(renderer->program_id);

    free(renderer);
}

static void UpdateZ(struct vlc_gl_renderer *renderer)
{
    /* Do trigonometry to calculate the minimal z value
     * that will allow us to zoom out without seeing the outside of the
     * sphere (black borders). */

    float tan_fovx_2 = tanf(renderer->f_fovx / 2);
    float tan_fovy_2 = tanf(renderer->f_fovy / 2);
    float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
                    tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));

    /* The FOV value above which z is dynamically calculated. */
    const float z_thresh = 90.f;

    if (renderer->f_fovx <= z_thresh * M_PI / 180)
        renderer->f_z = 0;
    else
    {
        float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
        renderer->f_z = f * renderer->f_fovx - f * z_thresh * M_PI / 180;
        if (renderer->f_z < z_min)
            renderer->f_z = z_min;
    }
}

static void UpdateFOVy(struct vlc_gl_renderer *renderer)
{
    renderer->f_fovy = 2 * atanf(tanf(renderer->f_fovx / 2) / renderer->f_sar);
}

int
vlc_gl_renderer_SetViewpoint(struct vlc_gl_renderer *renderer,
                             const vlc_viewpoint_t *p_vp)
{
    if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
            || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
        return VLC_EINVAL;

    // Convert degree into radian
    float f_fovx = p_vp->fov * (float)M_PI / 180.f;

    /* Copy the viewpoint for future pictures. */
    renderer->vp = *p_vp;

    if (fabsf(f_fovx - renderer->f_fovx) >= 0.001f)
    {
        /* FOVx has changed. */
        renderer->f_fovx = f_fovx;
        UpdateFOVy(renderer);
        UpdateZ(renderer);
    }
    getViewpointMatrixes(renderer, renderer->projection_mode);

    return VLC_SUCCESS;
}

static void
vlc_gl_renderer_SetOutputSize(struct vlc_gl_renderer *renderer, unsigned width,
                              unsigned height)
{
    float f_sar = (float) width / height;

    /* Each time the window size changes, we must recompute the minimum zoom
     * since the aspect ration changes.
     * We must also set the new current zoom value. */

    renderer->target_width = width;
    renderer->target_height = height;
    renderer->f_sar = f_sar;
    UpdateFOVy(renderer);
    UpdateZ(renderer);

    getViewpointMatrixes(renderer, renderer->projection_mode);
}

static int
RequestOutputSize(struct vlc_gl_filter *filter,
                  struct vlc_gl_tex_size *req,
                  struct vlc_gl_tex_size *optimal_in)
{
    struct vlc_gl_renderer *renderer = filter->sys;

    vlc_gl_renderer_SetOutputSize(renderer, req->width, req->height);

    /* The optimal input size is the size for which the renderer do not need to
     * scale */

    optimal_in->width = renderer->target_width;
    optimal_in->height = renderer->target_height;

    return VLC_SUCCESS;
}

static int BuildSphere(GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
                       GLushort **indices, unsigned *nbIndices)
{
    unsigned nbLatBands = 128;
    unsigned nbLonBands = 128;

    *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
    *nbIndices = nbLatBands * nbLonBands * 3 * 2;

    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
    if (*vertexCoord == NULL)
        return VLC_ENOMEM;
    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
    if (*textureCoord == NULL)
    {
        free(*vertexCoord);
        return VLC_ENOMEM;
    }
    *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
    if (*indices == NULL)
    {
        free(*textureCoord);
        free(*vertexCoord);
        return VLC_ENOMEM;
    }

    for (unsigned lat = 0; lat <= nbLatBands; lat++) {
        float theta = lat * (float) M_PI / nbLatBands;
        float sinTheta, cosTheta;

        sincosf(theta, &sinTheta, &cosTheta);

        for (unsigned int lon = 0; lon <= nbLonBands; lon++) {
            float phi =  2.f * (float)M_PI * (float)lon / nbLonBands;
            float sinPhi, cosPhi;

            sincosf(phi, &sinPhi, &cosPhi);

            /* The camera is centered on the Z axis when yaw = 0, while the
             * front part of the equirectangular texture is located at u=0.5.
             * To have the camera at the correct location, phi is
             * shifted +pi/2 to have u=0.5 fall at the correct location.
             *
             * Another way to interpret the shift is to interpret the shift
             * as a shift in texture coordinate. Considering the initial
             * orientation of the camera to Z which accounts as a pi/2
             * rotation, adding pi/2 maps the first and last coordinate to the
             * meridian, pi radians after the camera, so that u=0 amd u=1, ie.
             * the back face, is mapped to the back of the initial orientation
             * of the camera. */

            float x = -sinPhi * sinTheta;
            float y = cosTheta;
            float z = cosPhi * sinTheta;

            unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
            (*vertexCoord)[off1 + 0] = SPHERE_RADIUS * x;
            (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
            (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;

            unsigned off2 = (lat * (nbLonBands + 1) + lon) * 2;
            float u = (float)lon / nbLonBands;
            /* In OpenGL, the texture coordinates start at bottom left */
            float v = 1.0f - (float)lat / nbLatBands;
            (*textureCoord)[off2] = u;
            (*textureCoord)[off2 + 1] = v;
        }
    }

    for (unsigned lat = 0; lat < nbLatBands; lat++) {
        for (unsigned lon = 0; lon < nbLonBands; lon++) {
            unsigned first = (lat * (nbLonBands + 1)) + lon;
            unsigned second = first + nbLonBands + 1;

            unsigned off = (lat * nbLatBands + lon) * 3 * 2;

            (*indices)[off] = first;
            (*indices)[off + 1] = second;
            (*indices)[off + 2] = first + 1;

            (*indices)[off + 3] = second;
            (*indices)[off + 4] = second + 1;
            (*indices)[off + 5] = first + 1;
        }
    }

    return VLC_SUCCESS;
}


static int BuildCube(float padW, float padH,
                     GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
                     GLushort **indices, unsigned *nbIndices)
{
    *nbVertices = 4 * 6;
    *nbIndices = 6 * 6;

    *vertexCoord = NULL;
    *textureCoord = NULL;
    *indices = NULL;

    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
    if (*vertexCoord == NULL)
        goto error;

    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
    if (*textureCoord == NULL)
        goto error;

    *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
    if (*indices == NULL)
        goto error;

#define CUBEFACE(swap, value) \
    swap(value, -1.f,  1.f), \
    swap(value, -1.f, -1.f), \
    swap(value,  1.f,  1.f), \
    swap(value,  1.f, -1.f)

#define X_FACE(v, a, b) (v), (b), (a)
#define Y_FACE(v, a, b) (a), (v), (b)
#define Z_FACE(v, a, b) (a), (b), (v)

    static const GLfloat coord[] = {
        CUBEFACE(Z_FACE, -1.f), // FRONT
        CUBEFACE(Z_FACE, +1.f), // BACK
        CUBEFACE(X_FACE, -1.f), // LEFT
        CUBEFACE(X_FACE, +1.f), // RIGHT
        CUBEFACE(Y_FACE, -1.f), // BOTTOM
        CUBEFACE(Y_FACE, +1.f), // TOP
    };

#undef X_FACE
#undef Y_FACE
#undef Z_FACE
#undef CUBEFACE

    memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));

    float col[] = {0.f, 1.f/3, 2.f/3, 1.f};
    float row[] = {0.f, 1.f/2, 1.0};

    const GLfloat tex[] = {
        col[1] + padW, row[1] - padH, // front
        col[1] + padW, row[0] + padH,
        col[2] - padW, row[1] - padH,
        col[2] - padW, row[0] + padH,

        col[3] - padW, row[1] - padH, // back
        col[3] - padW, row[0] + padH,
        col[2] + padW, row[1] - padH,
        col[2] + padW, row[0] + padH,

        col[2] - padW, row[2] - padH, // left
        col[2] - padW, row[1] + padH,
        col[1] + padW, row[2] - padH,
        col[1] + padW, row[1] + padH,

        col[0] + padW, row[2] - padH, // right
        col[0] + padW, row[1] + padH,
        col[1] - padW, row[2] - padH,
        col[1] - padW, row[1] + padH,

        col[0] + padW, row[0] + padH, // bottom
        col[0] + padW, row[1] - padH,
        col[1] - padW, row[0] + padH,
        col[1] - padW, row[1] - padH,

        col[2] + padW, row[2] - padH, // top
        col[2] + padW, row[1] + padH,
        col[3] - padW, row[2] - padH,
        col[3] - padW, row[1] + padH,
    };

    memcpy(*textureCoord, tex,
           *nbVertices * 2 * sizeof(GLfloat));

    const GLushort ind[] = {
        0, 1, 2,       2, 1, 3, // front
        6, 7, 4,       4, 7, 5, // back
        10, 11, 8,     8, 11, 9, // left
        12, 13, 14,    14, 13, 15, // right
        18, 19, 16,    16, 19, 17, // bottom
        20, 21, 22,    22, 21, 23, // top
    };

    memcpy(*indices, ind, *nbIndices * sizeof(GLushort));

    return VLC_SUCCESS;

error:
    free(*vertexCoord);
    free(*textureCoord);
    return VLC_ENOMEM;
}

static int BuildRectangle(
        GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
        GLushort **indices, unsigned *nbIndices, video_orientation_t orientation)
{
    *nbVertices = 4;
    *nbIndices = 6;

    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
    if (*vertexCoord == NULL)
        return VLC_ENOMEM;
    *textureCoord = vlc_alloc(*nbVertices * 2, sizeof(GLfloat));
    if (*textureCoord == NULL)
    {
        free(*vertexCoord);
        return VLC_ENOMEM;
    }
    *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
    if (*indices == NULL)
    {
        free(*textureCoord);
        free(*vertexCoord);
        return VLC_ENOMEM;
    }

    static const GLfloat coord[] = {
       -1.0,    1.0,    -1.0f,
       -1.0,    -1.0,   -1.0f,
       1.0,     1.0,    -1.0f,
       1.0,     -1.0,   -1.0f
    };

    memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));

    static const GLfloat tex_normal[] = {
        0.0, 1.0,
        0.0, 0.0,
        1.0, 1.0,
        1.0, 0.0,
    };

    static const GLfloat tex_hflip[] = {
        1.0, 1.0,
        1.0, 0.0,
        0.0, 1.0,
        0.0, 0.0,
    };

    /* Since there is a bijection between symmetry and rotation,
     * applying the general orientation in each case is equivalent
     * to choosing the rotation we want to apply to the frame, and
     * then decide whether we apply a horizontal flip or not. */

    const GLfloat *tex;
    if (ORIENT_IS_MIRROR(orientation))
        tex = tex_hflip;
    else
        tex = tex_normal;

    memcpy(*textureCoord, tex, *nbVertices * 2 * sizeof(GLfloat));

    const GLushort ind[] = {
        0, 1, 2,
        2, 1, 3
    };

    memcpy(*indices, ind, *nbIndices * sizeof(GLushort));

    return VLC_SUCCESS;
}

static int SetupCoords(struct vlc_gl_renderer *renderer,
                       const struct vlc_gl_picture *pic,
                       video_orientation_t orientation)
{
    const opengl_vtable_t *vt = renderer->vt;
    struct vlc_gl_sampler *sampler = renderer->sampler;
    const video_format_t *fmt = &sampler->fmt_in;

    GLfloat *vertexCoord, *textureCoord;
    GLushort *indices;
    unsigned nbVertices, nbIndices;

    video_projection_mode_t projection_mode = renderer->projection_mode;
    if (renderer->multiview_mode != renderer->sampler->fmt_in.multiview_mode)
        projection_mode = PROJECTION_MODE_RECTANGULAR;

    int i_ret;
    switch (projection_mode)
    {
    case PROJECTION_MODE_RECTANGULAR:
        i_ret = BuildRectangle(&vertexCoord, &textureCoord, &nbVertices,
                               &indices, &nbIndices, orientation);
        break;
    case PROJECTION_MODE_EQUIRECTANGULAR:
        i_ret = BuildSphere(&vertexCoord, &textureCoord, &nbVertices,
                            &indices, &nbIndices);
        break;
    case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
        i_ret = BuildCube((float)fmt->i_cubemap_padding / fmt->i_width,
                          (float)fmt->i_cubemap_padding / fmt->i_height,
                          &vertexCoord, &textureCoord, &nbVertices,
                          &indices, &nbIndices);
        break;
    default:
        i_ret = VLC_EGENERIC;
        break;
    }

    if (i_ret != VLC_SUCCESS)
        return i_ret;

    /* Transform picture-to-texture coordinates in place */
    vlc_gl_picture_ToTexCoords(pic, nbVertices, textureCoord, textureCoord);

    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
    vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
                   textureCoord, GL_STATIC_DRAW);

    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
    vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
                   vertexCoord, GL_STATIC_DRAW);

    vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
    vt->BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
                   indices, GL_STATIC_DRAW);

    free(textureCoord);
    free(vertexCoord);
    free(indices);

    renderer->nb_indices = nbIndices;

    return VLC_SUCCESS;
}

static int
Draw(struct vlc_gl_filter *filter, const struct vlc_gl_picture *pic,
     const struct vlc_gl_input_meta *meta)
{
    (void) meta;

    struct vlc_gl_renderer *renderer = filter->sys;

    const opengl_vtable_t *vt = renderer->vt;

    vt->ClearColor(0.f,0.f,0.f,1.f);
    vt->Clear(GL_COLOR_BUFFER_BIT);
    GL_ASSERT_NOERROR(vt);

    vt->UseProgram(renderer->program_id);

    struct vlc_gl_sampler *sampler = renderer->sampler;
    vlc_gl_sampler_Update(sampler, pic);
    vlc_gl_sampler_Load(sampler);

    if (pic->mtx_has_changed)
        renderer->valid_coords = false;

    if (!renderer->valid_coords)
    {
        int ret = SetupCoords(renderer, pic, meta->orientation);
        if (ret != VLC_SUCCESS)
            return ret;

        renderer->valid_coords = true;
    }

    float orientation_matrix[ARRAY_SIZE(MATRIX4_IDENTITY)];
    if (meta->orientation != ORIENT_NORMAL)
    {
        vlc_viewpoint_t orient_vp;
        vlc_viewpoint_from_orientation(&orient_vp, meta->orientation);
        vlc_viewpoint_to_4x4(&orient_vp, orientation_matrix);
    }
    else memcpy(orientation_matrix, MATRIX4_IDENTITY, sizeof(MATRIX4_IDENTITY));

    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
    vt->BindVertexArray(renderer->vertex_array_object);
    assert(renderer->aloc.PicCoordsIn != -1);
    vt->EnableVertexAttribArray(renderer->aloc.PicCoordsIn);
    vt->VertexAttribPointer(renderer->aloc.PicCoordsIn, 2, GL_FLOAT, 0, 0, 0);

    vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
    vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
    vt->EnableVertexAttribArray(renderer->aloc.VertexPosition);
    vt->VertexAttribPointer(renderer->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);

    vt->UniformMatrix3fv(renderer->uloc.StereoMatrix, 1, GL_FALSE,
                         renderer->var.StereoMatrix);
    vt->UniformMatrix4fv(renderer->uloc.ProjectionMatrix, 1, GL_FALSE,
                         renderer->var.ProjectionMatrix);
    vt->UniformMatrix4fv(renderer->uloc.ViewMatrix, 1, GL_FALSE,
                         renderer->var.ViewMatrix);
    vt->UniformMatrix4fv(renderer->uloc.ZoomMatrix, 1, GL_FALSE,
                         renderer->var.ZoomMatrix);
    vt->UniformMatrix4fv(renderer->uloc.OrientationMatrix, 1, GL_FALSE,
                         orientation_matrix);

    vt->DrawElements(GL_TRIANGLES, renderer->nb_indices, GL_UNSIGNED_SHORT, 0);
    GL_ASSERT_NOERROR(vt);

    return VLC_SUCCESS;
}

int
vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
                     const config_chain_t *config,
                     struct vlc_gl_sampler *sampler,
                     struct vlc_gl_tex_size *size_out)
{
    (void) size_out;

    const char * const options[] = { "projection-mode", "video-stereo-mode", NULL };
    config_ChainParse(filter, "", options, config);

    struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
    if (!renderer)
        return VLC_ENOMEM;

    int ret = vlc_gl_api_Init(&renderer->api, filter->gl);
    if (ret != VLC_SUCCESS)
    {
        free(renderer);
        return VLC_EGENERIC;
    }

    static const struct vlc_gl_filter_ops filter_ops = {
        .draw = Draw,
        .close = Close,
        .request_output_size = RequestOutputSize,
    };
    filter->ops = &filter_ops;
    filter->sys = renderer;

    renderer->sampler = sampler;

    renderer->vt = &renderer->api.vt;
    renderer->dump_shaders = var_InheritInteger(filter, "verbose") >= 4;

    int projection_mode = var_InheritInteger(filter, "projection-mode");
    switch (projection_mode)
    {
        case PROJECTION_MODE_RECTANGULAR:
            renderer->projection_mode = projection_mode;
            break;
        default:
        case -1:
            renderer->projection_mode = sampler->fmt_in.projection_mode;
            break;
    }

    int stereo_mode = var_InheritInteger(filter, "video-stereo-mode");
    if (stereo_mode > VIDEO_STEREO_OUTPUT_AUTO && stereo_mode <= VIDEO_STEREO_OUTPUT_MAX)
        renderer->stereo_mode = stereo_mode;

    if (sampler->fmt_in.multiview_mode == MULTIVIEW_2D)
    {
        renderer->stereo_mode = VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE;
        renderer->multiview_mode = MULTIVIEW_2D;
    }
    else switch (stereo_mode)
    {
        case VIDEO_STEREO_OUTPUT_LEFT_ONLY:
        case VIDEO_STEREO_OUTPUT_RIGHT_ONLY:
        case VIDEO_STEREO_OUTPUT_AUTO:
            /* multiview_mode != MULTIVIEW_2D */
            renderer->multiview_mode = sampler->fmt_in.multiview_mode;
            if (stereo_mode == VIDEO_STEREO_OUTPUT_AUTO)
                renderer->stereo_mode = VIDEO_STEREO_OUTPUT_LEFT_ONLY;
            break;
        case VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE:
        case VIDEO_STEREO_OUTPUT_STEREO: /* stereo mode is not supported yet */
        default:
            renderer->multiview_mode = MULTIVIEW_2D; /* Enforce the planar view */
            renderer->stereo_mode = VIDEO_STEREO_OUTPUT_SIDE_BY_SIDE;
            break;
    }

    ret = opengl_link_program(filter);
    if (ret != VLC_SUCCESS)
    {
        msg_Dbg(&filter->obj, "Could not link shaders");
        free(renderer);
        return ret;
    }

    InitStereoMatrix(renderer->var.StereoMatrix, renderer->stereo_mode, renderer->multiview_mode);

    getViewpointMatrixes(renderer, renderer->projection_mode);

    const opengl_vtable_t *vt = renderer->vt;
    vt->GenVertexArrays(1, &renderer->vertex_array_object);

    vt->GenBuffers(1, &renderer->vertex_buffer_object);
    vt->GenBuffers(1, &renderer->index_buffer_object);
    vt->GenBuffers(1, &renderer->texture_buffer_object);

    /* The coords will be initialized on first draw */
    renderer->valid_coords = false;

    return VLC_SUCCESS;
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© 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.