diff options
-rw-r--r-- | video/out/opengl/formats.c | 102 | ||||
-rw-r--r-- | video/out/opengl/formats.h | 10 | ||||
-rw-r--r-- | video/out/opengl/ra.c | 199 | ||||
-rw-r--r-- | video/out/opengl/ra.h | 175 | ||||
-rw-r--r-- | video/out/opengl/ra_gl.c | 290 | ||||
-rw-r--r-- | video/out/opengl/ra_gl.h | 29 | ||||
-rw-r--r-- | video/out/opengl/video.c | 206 | ||||
-rw-r--r-- | wscript_build.py | 2 |
8 files changed, 838 insertions, 175 deletions
diff --git a/video/out/opengl/formats.c b/video/out/opengl/formats.c index 2cbc130170..41b7f459e3 100644 --- a/video/out/opengl/formats.c +++ b/video/out/opengl/formats.c @@ -10,56 +10,56 @@ enum { // List of allowed formats, and their usability for bilinear filtering and FBOs. // This is limited to combinations that are useful for our renderer. -static const struct gl_format gl_formats[] = { +const struct gl_format gl_formats[] = { // These are used for desktop GL 3+, and GLES 3+ with GL_EXT_texture_norm16. - {GL_R8, GL_RED, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, - {GL_RG8, GL_RG, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, - {GL_RGB8, GL_RGB, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, - {GL_RGBA8, GL_RGBA, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, - {GL_R16, GL_RED, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, - {GL_RG16, GL_RG, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, - {GL_RGB16, GL_RGB, T_U16, F_CF | F_GL3 | F_GL2F}, - {GL_RGBA16, GL_RGBA, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, + {"r8", GL_R8, GL_RED, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, + {"rg8", GL_RG8, GL_RG, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, + {"rgb8", GL_RGB8, GL_RGB, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, + {"rgba8", GL_RGBA8, GL_RGBA, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3}, + {"r16", GL_R16, GL_RED, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, + {"rg16", GL_RG16, GL_RG, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, + {"rgb16", GL_RGB16, GL_RGB, T_U16, F_CF | F_GL3 | F_GL2F}, + {"rgba16", GL_RGBA16, GL_RGBA, T_U16, F_CF | F_GL3 | F_GL2F | F_EXT16}, // Specifically not color-renderable. - {GL_RGB16, GL_RGB, T_U16, F_TF | F_EXT16}, + {"rgb16", GL_RGB16, GL_RGB, T_U16, F_TF | F_EXT16}, // GL2 legacy. Ignores possibly present FBO extensions (no CF flag set). - {GL_LUMINANCE8, GL_LUMINANCE, T_U8, F_TF | F_GL2}, - {GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, T_U8, F_TF | F_GL2}, - {GL_RGB8, GL_RGB, T_U8, F_TF | F_GL2}, - {GL_RGBA8, GL_RGBA, T_U8, F_TF | F_GL2}, - {GL_LUMINANCE16, GL_LUMINANCE, T_U16, F_TF | F_GL2}, - {GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, T_U16, F_TF | F_GL2}, - {GL_RGB16, GL_RGB, T_U16, F_TF | F_GL2}, - {GL_RGBA16, GL_RGBA, T_U16, F_TF | F_GL2}, + {"l8", GL_LUMINANCE8, GL_LUMINANCE, T_U8, F_TF | F_GL2}, + {"la8", GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, T_U8, F_TF | F_GL2}, + {"rgb8", GL_RGB8, GL_RGB, T_U8, F_TF | F_GL2}, + {"rgba8", GL_RGBA8, GL_RGBA, T_U8, F_TF | F_GL2}, + {"l16", GL_LUMINANCE16, GL_LUMINANCE, T_U16, F_TF | F_GL2}, + {"la16", GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, T_U16, F_TF | F_GL2}, + {"rgb16", GL_RGB16, GL_RGB, T_U16, F_TF | F_GL2}, + {"rgba16", GL_RGBA16, GL_RGBA, T_U16, F_TF | F_GL2}, // ES2 legacy - {GL_LUMINANCE, GL_LUMINANCE, T_U8, F_TF | F_ES2}, - {GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, T_U8, F_TF | F_ES2}, - {GL_RGB, GL_RGB, T_U8, F_TF | F_ES2}, - {GL_RGBA, GL_RGBA, T_U8, F_TF | F_ES2}, + {"l" , GL_LUMINANCE,GL_LUMINANCE, T_U8, F_TF | F_ES2}, + {"la",GL_LUMINANCE_ALPHA,GL_LUMINANCE_ALPHA, T_U8, F_TF | F_ES2}, + {"rgb", GL_RGB, GL_RGB, T_U8, F_TF | F_ES2}, + {"rgba", GL_RGBA, GL_RGBA, T_U8, F_TF | F_ES2}, // Non-normalized integer formats. // Follows ES 3.0 as to which are color-renderable. - {GL_R8UI, GL_RED_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, - {GL_RG8UI, GL_RG_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, - {GL_RGB8UI, GL_RGB_INTEGER, T_U8, F_GL3 | F_ES3}, - {GL_RGBA8UI, GL_RGBA_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, - {GL_R16UI, GL_RED_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, - {GL_RG16UI, GL_RG_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, - {GL_RGB16UI, GL_RGB_INTEGER, T_U16, F_GL3 | F_ES3}, - {GL_RGBA16UI, GL_RGBA_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, + {"r8ui", GL_R8UI, GL_RED_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, + {"rg8ui", GL_RG8UI, GL_RG_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, + {"rgb8ui", GL_RGB8UI, GL_RGB_INTEGER, T_U8, F_GL3 | F_ES3}, + {"rgba8ui", GL_RGBA8UI, GL_RGBA_INTEGER, T_U8, F_CR | F_GL3 | F_ES3}, + {"r16ui", GL_R16UI, GL_RED_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, + {"rg16ui", GL_RG16UI, GL_RG_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, + {"rgb16ui", GL_RGB16UI, GL_RGB_INTEGER, T_U16, F_GL3 | F_ES3}, + {"rgba16ui",GL_RGBA16UI, GL_RGBA_INTEGER, T_U16, F_CR | F_GL3 | F_ES3}, // On GL3+ or GL2.1 with GL_ARB_texture_float, floats work fully. - {GL_R16F, GL_RED, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, - {GL_RG16F, GL_RG, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, - {GL_RGB16F, GL_RGB, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, - {GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, - {GL_R32F, GL_RED, T_FL, F_CF | F_GL3 | F_GL2F}, - {GL_RG32F, GL_RG, T_FL, F_CF | F_GL3 | F_GL2F}, - {GL_RGB32F, GL_RGB, T_FL, F_CF | F_GL3 | F_GL2F}, - {GL_RGBA32F, GL_RGBA, T_FL, F_CF | F_GL3 | F_GL2F}, + {"r16f", GL_R16F, GL_RED, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, + {"rg16f", GL_RG16F, GL_RG, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, + {"rgb16f", GL_RGB16F, GL_RGB, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, + {"rgba16f", GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_CF | F_GL3 | F_GL2F}, + {"r32f", GL_R32F, GL_RED, T_FL, F_CF | F_GL3 | F_GL2F}, + {"rg32f", GL_RG32F, GL_RG, T_FL, F_CF | F_GL3 | F_GL2F}, + {"rgb32f", GL_RGB32F, GL_RGB, T_FL, F_CF | F_GL3 | F_GL2F}, + {"rgba32f", GL_RGBA32F, GL_RGBA, T_FL, F_CF | F_GL3 | F_GL2F}, // Note: we simply don't support float anything on ES2, despite extensions. // We also don't bother with non-filterable float formats, and we ignore @@ -67,28 +67,28 @@ static const struct gl_format gl_formats[] = { // On ES3.2+, both 16 bit floats work fully (except 3-component formats). // F_EXTF16 implies extensions that also enable 16 bit floats fully. - {GL_R16F, GL_RED, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, - {GL_RG16F, GL_RG, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, - {GL_RGB16F, GL_RGB, T_FL, F_F16 | F_TF | F_ES32 | F_EXTF16}, - {GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, + {"r16f", GL_R16F, GL_RED, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, + {"rg16f", GL_RG16F, GL_RG, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, + {"rgb16f", GL_RGB16F, GL_RGB, T_FL, F_F16 | F_TF | F_ES32 | F_EXTF16}, + {"rgba16f", GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_CF | F_ES32 | F_EXTF16}, // On ES3.0+, 16 bit floats are texture-filterable. // Don't bother with 32 bit floats; they exist but are neither CR nor TF. - {GL_R16F, GL_RED, T_FL, F_F16 | F_TF | F_ES3}, - {GL_RG16F, GL_RG, T_FL, F_F16 | F_TF | F_ES3}, - {GL_RGB16F, GL_RGB, T_FL, F_F16 | F_TF | F_ES3}, - {GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_TF | F_ES3}, + {"r16f", GL_R16F, GL_RED, T_FL, F_F16 | F_TF | F_ES3}, + {"rg16f", GL_RG16F, GL_RG, T_FL, F_F16 | F_TF | F_ES3}, + {"rgb16f", GL_RGB16F, GL_RGB, T_FL, F_F16 | F_TF | F_ES3}, + {"rgba16f", GL_RGBA16F, GL_RGBA, T_FL, F_F16 | F_TF | F_ES3}, // These might be useful as FBO formats. - {GL_RGB10_A2, GL_RGBA, + {"rgb10_a2",GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, F_CF | F_GL3 | F_ES3}, - {GL_RGBA12, GL_RGBA, T_U16, F_CF | F_GL2 | F_GL3}, - {GL_RGB10, GL_RGB, T_U16, F_CF | F_GL2 | F_GL3}, + {"rgba12", GL_RGBA12, GL_RGBA, T_U16, F_CF | F_GL2 | F_GL3}, + {"rgb10", GL_RGB10, GL_RGB, T_U16, F_CF | F_GL2 | F_GL3}, // Special formats. - {GL_RGB8, GL_RGB, + {"rgb565", GL_RGB8, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, F_TF | F_GL2 | F_GL3}, - {GL_RGB, GL_RGB_422_APPLE, + {"ashit", GL_RGB, GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, F_TF | F_APPL}, {0} diff --git a/video/out/opengl/formats.h b/video/out/opengl/formats.h index 5bb3bcb4a3..d4d38c1150 100644 --- a/video/out/opengl/formats.h +++ b/video/out/opengl/formats.h @@ -2,8 +2,10 @@ #define MPGL_FORMATS_H_ #include "common.h" +#include "ra.h" struct gl_format { + const char *name; // symbolic name for user interaction/debugging GLint internal_format; // glTexImage argument GLenum format; // glTexImage argument GLenum type; // e.g. GL_UNSIGNED_SHORT @@ -33,11 +35,13 @@ enum { // the format is still GL_FLOAT (32 bit) // --- Other constants. - MPGL_TYPE_UNORM = 1, // normalized integer (fixed point) formats - MPGL_TYPE_UINT = 2, // full integer formats - MPGL_TYPE_FLOAT = 3, // float formats (both full and half) + MPGL_TYPE_UNORM = RA_CTYPE_UNORM, // normalized integer (fixed point) formats + MPGL_TYPE_UINT = RA_CTYPE_UINT, // full integer formats + MPGL_TYPE_FLOAT = RA_CTYPE_FLOAT, // float formats (both full and half) }; +extern const struct gl_format gl_formats[]; + int gl_format_feature_flags(GL *gl); const struct gl_format *gl_find_internal_format(GL *gl, GLint internal_format); const struct gl_format *gl_find_format(GL *gl, int type, int flags, diff --git a/video/out/opengl/ra.c b/video/out/opengl/ra.c new file mode 100644 index 0000000000..12d944f29c --- /dev/null +++ b/video/out/opengl/ra.c @@ -0,0 +1,199 @@ +#include "common/common.h" +#include "common/msg.h" +#include "video/img_format.h" + +#include "ra.h" + +// Return whether this is a tightly packed format with no external padding and +// with the same bit size/depth in all components. +static bool ra_format_is_regular(const struct ra_format *fmt) +{ + if (!fmt->pixel_size || !fmt->num_components) + return false; + for (int n = 1; n < fmt->num_components; n++) { + if (fmt->component_size[n] != fmt->component_size[0] || + fmt->component_depth[n] != fmt->component_depth[0]) + return false; + } + if (fmt->component_size[0] * fmt->num_components != fmt->pixel_size * 8) + return false; + return true; +} + +// Return a regular format using RA_CTYPE_UNORM. +const struct ra_format *ra_find_unorm_format(struct ra *ra, + int bytes_per_component, + int n_components) +{ + for (int n = 0; n < ra->num_formats; n++) { + const struct ra_format *fmt = ra->formats[n]; + if (fmt->ctype == RA_CTYPE_UNORM && fmt->num_components == n_components && + fmt->pixel_size == bytes_per_component * n_components && + fmt->component_depth[0] == bytes_per_component * 8 && + ra_format_is_regular(fmt)) + return fmt; + } + return NULL; +} + +// Return a regular format using RA_CTYPE_UINT. +const struct ra_format *ra_find_uint_format(struct ra *ra, + int bytes_per_component, + int n_components) +{ + for (int n = 0; n < ra->num_formats; n++) { + const struct ra_format *fmt = ra->formats[n]; + if (fmt->ctype == RA_CTYPE_UINT && fmt->num_components == n_components && + fmt->pixel_size == bytes_per_component * n_components && + fmt->component_depth[0] == bytes_per_component * 8 && + ra_format_is_regular(fmt)) + return fmt; + } + return NULL; +} + +// Return a regular format that uses float16 internally, but does 32 bit +// transfer. (This is just so we don't need 32->16 bit conversion on CPU, +// which would be ok but messy.) +const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components) +{ + for (int n = 0; n < ra->num_formats; n++) { + const struct ra_format *fmt = ra->formats[n]; + if (fmt->ctype == RA_CTYPE_FLOAT && fmt->num_components == n_components && + fmt->pixel_size == sizeof(float) * n_components && + fmt->component_depth[0] == 16 && + ra_format_is_regular(fmt)) + return fmt; + } + return NULL; +} + + +// Like ra_find_unorm_format(), but takes bits (not bytes), and if no fixed +// point format is available, return an unsigned integer format. +static const struct ra_format *find_plane_format(struct ra *ra, int bytes, + int n_channels) +{ + const struct ra_format *f = ra_find_unorm_format(ra, bytes, n_channels); + if (f) + return f; + return ra_find_uint_format(ra, bytes, n_channels); +} + +// Put a mapping of imgfmt to texture formats into *out. Basically it selects +// the correct texture formats needed to represent an imgfmt in a shader, with +// textures using the same memory organization as on the CPU. +// Each plane is represented by a texture, and each texture has a RGBA +// component order. out->components describes the meaning of them. +// May return integer formats for >8 bit formats, if the driver has no +// normalized 16 bit formats. +// Returns false (and *out is not touched) if no format found. +bool ra_get_imgfmt_desc(struct ra *ra, int imgfmt, struct ra_imgfmt_desc *out) +{ + struct ra_imgfmt_desc res = {0}; + + struct mp_regular_imgfmt regfmt; + if (mp_get_regular_imgfmt(®fmt, imgfmt)) { + res.num_planes = regfmt.num_planes; + res.component_bits = regfmt.component_size * 8; + res.component_pad = regfmt.component_pad; + for (int n = 0; n < regfmt.num_planes; n++) { + struct mp_regular_imgfmt_plane *plane = ®fmt.planes[n]; + res.planes[n] = find_plane_format(ra, regfmt.component_size, + plane->num_components); + if (!res.planes[n]) + return false; + for (int i = 0; i < plane->num_components; i++) + res.components[n][i] = plane->components[i]; + } + res.chroma_w = regfmt.chroma_w; + res.chroma_h = regfmt.chroma_h; + goto supported; + } + + for (int n = 0; n < ra->num_formats; n++) { + if (ra->formats[n]->special_imgfmt == imgfmt) { + res = *ra->formats[n]->special_imgfmt_desc; + goto supported; + } + } + + // Unsupported format + return false; + +supported: + + *out = res; + return true; +} + +void ra_dump_tex_formats(struct ra *ra, int msgl) +{ + if (!mp_msg_test(ra->log, msgl)) + return; + MP_MSG(ra, msgl, "Texture formats:\n"); + for (int n = 0; n < ra->num_formats; n++) { + const struct ra_format *fmt = ra->formats[n]; + const char *ctype = "unknown"; + switch (fmt->ctype) { + case RA_CTYPE_UNORM: ctype = "unorm"; break; + case RA_CTYPE_UINT: ctype = "uint "; break; + case RA_CTYPE_FLOAT: ctype = "float"; break; + } + char cl[40] = ""; + for (int i = 0; i < fmt->num_components; i++) { + mp_snprintf_cat(cl, sizeof(cl), "%s%d", i ? " " : "", + fmt->component_size[i]); + if (fmt->component_size[i] != fmt->component_depth[i]) + mp_snprintf_cat(cl, sizeof(cl), "/%d", fmt->component_depth[i]); + } + MP_MSG(ra, msgl, " %-10s %d*%s %3dB %s %s %s {%s}\n", fmt->name, + fmt->num_components, ctype, fmt->pixel_size, + fmt->luminance_alpha ? "LA" : " ", + fmt->linear_filter ? "LF" : " ", + fmt->renderable ? "CR" : " ", cl); + } +} + +void ra_dump_imgfmt_desc(struct ra *ra, const struct ra_imgfmt_desc *desc, + int msgl) +{ + char pl[80] = ""; + char pf[80] = ""; + for (int n = 0; n < desc->num_planes; n++) { + if (n > 0) { + mp_snprintf_cat(pl, sizeof(pl), "/"); + mp_snprintf_cat(pf, sizeof(pf), "/"); + } + char t[5] = {0}; + for (int i = 0; i < 4; i++) + t[i] = "_rgba"[desc->components[n][i]]; + for (int i = 3; i > 0 && t[i] == '_'; i--) + t[i] = '\0'; + mp_snprintf_cat(pl, sizeof(pl), "%s", t); + mp_snprintf_cat(pf, sizeof(pf), "%s", desc->planes[n]->name); + } + MP_MSG(ra, msgl, "%d planes %dx%d %d/%d [%s] (%s)\n", + desc->num_planes, desc->chroma_w, desc->chroma_h, + desc->component_bits, desc->component_pad, pf, pl); +} + +void ra_dump_img_formats(struct ra *ra, int msgl) +{ + if (!mp_msg_test(ra->log, msgl)) + return; + MP_MSG(ra, msgl, "Image formats:\n"); + for (int imgfmt = IMGFMT_START; imgfmt < IMGFMT_END; imgfmt++) { + const char *name = mp_imgfmt_to_name(imgfmt); + if (strcmp(name, "unknown") == 0) + continue; + MP_MSG(ra, msgl, " %s", name); + struct ra_imgfmt_desc desc; + if (ra_get_imgfmt_desc(ra, imgfmt, &desc)) { + MP_MSG(ra, msgl, " => "); + ra_dump_imgfmt_desc(ra, &desc, msgl); + } else { + MP_MSG(ra, msgl, "\n"); + } + } +} diff --git a/video/out/opengl/ra.h b/video/out/opengl/ra.h new file mode 100644 index 0000000000..211f87077b --- /dev/null +++ b/video/out/opengl/ra.h @@ -0,0 +1,175 @@ +#pragma once + +#include "common/common.h" + +// Handle for a rendering API backend. +struct ra { + struct ra_fns *fns; + void *priv; + + struct mp_log *log; + + // RA_CAP_* bit field. The RA backend must set supported features at init + // time. + uint64_t caps; + + // Set of supported texture formats. Must be added by RA backend at init time. + struct ra_format **formats; + int num_formats; +}; + +enum { + RA_CAP_TEX_1D = 0 << 0, // supports 1D textures (as shader source textures) + RA_CAP_TEX_3D = 0 << 1, // supports 3D textures (as shader source textures) +}; + +enum ra_ctype { + RA_CTYPE_UNKNOWN = 0, // also used for inconsistent multi-component formats + RA_CTYPE_UNORM, // unsigned normalized integer (fixed point) formats + RA_CTYPE_UINT, // full integer formats + RA_CTYPE_FLOAT, // float formats (signed, any bit size) +}; + +// All formats must be useable as texture formats. All formats must be byte +// aligned (all pixels start and end on a byte boundary), at least as far CPU +// transfers are concerned. +struct ra_format { + // All fields are read-only after creation. + const char *name; // symbolic name for user interaction/debugging + void *priv; + enum ra_ctype ctype; // data type of each component + int num_components; // component count, 0 if not applicable, max. 4 + int component_size[4]; // in bits, all entries 0 if not applicable + int component_depth[4]; // bits in use for each component, 0 if not applicable + // (_must_ be set if component_size[] includes padding, + // and the real procession as seen by shader is lower) + int pixel_size; // in bytes, total pixel size (0 if opaque) + bool luminance_alpha; // pre-GL_ARB_texture_rg hack for 2 component textures + // if this is set, shader must use .ra instead of .rg + // only applies to 2-component textures + bool linear_filter; // linear filtering available from shader + bool renderable; // can be used for render targets + + // If not 0, the format represents some sort of packed fringe format, whose + // shader representation is given by the special_imgfmt_desc pointer. + int special_imgfmt; + const struct ra_imgfmt_desc *special_imgfmt_desc; +}; + +struct ra_tex_params { + int dimensions; // 1-3 for 1D-3D textures + // Size of the texture. 1D textures require h=d=1, 2D textures require d=1. + int w, h, d; + const struct ra_format *format; + bool render_src; // must be useable as source texture in a shader + bool render_dst; // must be useable as target texture in a shader + // this requires creation of a FBO + // When used as render source texture. + bool src_linear; // if false, use nearest sampling (whether this can + // be true depends on ra_format.linear_filter) + bool src_repeat; // if false, clamp texture coordinates to edge + // if true, repeat texture coordinates + bool non_normalized; // hack for GL_TEXTURE_RECTANGLE OSX idiocy + // always set to false, except in OSX code +}; + +struct ra_tex { + // All fields are read-only after creation. + struct ra_tex_params params; + void *priv; + // Set by user, GL only: attempt to accelerate upload with PBOs. + bool use_pbo; +}; + +// A persistent mapping, which can be used for texture upload. +struct ra_mapped_buffer { + // All fields are read-only after creation. The data is read/write, but + // requires explicit fence usage. + void *priv; + void *data; // pointer to first usable byte + size_t size; // total size of the mapping, starting at data + size_t preferred_align; // preferred stride/start alignment for optimal copy +}; + +// Rendering API entrypoints. (Note: there are some additional hidden features +// you need to take care of. For example, hwdec mapping will be provided +// separately from ra, but might need to call into ra private code.) +struct ra_fns { + void (*destroy)(struct ra *ra); + + // Create a texture (with undefined contents). Return NULL on failure. + // This is a rare operation, and normally textures and even FBOs for + // temporary rendering intermediate data are cached. + struct ra_tex *(*tex_create)(struct ra *ra, + const struct ra_tex_params *params); + + void (*tex_destroy)(struct ra *ra, struct ra_tex *tex); + + // Copy from CPU RAM to the texture. The image dimensions are as specified + // in tex->params. + // This is an extremely common operation. + // Unlike with OpenGL, the src data has to have exactly the same format as + // the texture, and no conversion is supported. + // tex->params.require_upload must be true. + // For 1D textures, stride is ignored. + // For 3D textures, stride is not supported. All data is fully packed with + // no padding, and stride is ignored. + // If buf is not NULL, then src must be within the provided buffer. The + // operation is implied to have dramatically better performance, but + // requires correct flushing and fencing operations by the caller to deal + // with asynchronous host/GPU behavior. If any of these conditions are not + // met, undefined behavior will result. + void (*tex_upload)(struct ra *ra, struct ra_tex *tex, + const void *src, ptrdiff_t stride, + struct ra_mapped_buffer *buf); + + // Create a persistently mapped buffer for tex_upload. + // Optional, can be NULL or return NULL if unavailable. + struct ra_mapped_buffer *(*create_mapped_buffer)(struct ra *ra, size_t size); + + void (*destroy_mapped_buffer)(struct ra *ra, struct ra_mapped_buffer *buf); + + // Essentially a fence: once the GPU uses the mapping for read-access (e.g. + // by starting a texture upload), the host must not write to the mapped + // data until an internal object has been signalled. This call returns + // whether it was signalled yet. If true, write accesses are allowed again. + // Optional, only available if flush_mapping is. + bool (*poll_mapped_buffer)(struct ra *ra, struct ra_mapped_buffer *buf); +}; + +const struct ra_format *ra_find_unorm_format(struct ra *ra, + int bytes_per_component, + int n_components); +const struct ra_format *ra_find_uint_format(struct ra *ra, + int bytes_per_component, + int n_components); +const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components); + +struct ra_imgfmt_desc { + int num_planes; + const struct ra_format *planes[4]; + // Chroma pixel size (1x1 is 4:4:4) + uint8_t chroma_w, chroma_h; + // Component storage size in bits (possibly padded). For formats with + // different sizes per component, this is arbitrary. For padded formats + // like P010 or YUV420P10, padding is included. + int component_bits; + // Like mp_regular_imgfmt.component_pad. + int component_pad; + // For each texture and each texture output (rgba order) describe what + // component it returns. + // The values are like the values in mp_regular_imgfmt_plane.components[]. + // Access as components[plane_nr][component_index]. Set unused items to 0. + // For ra_format.luminance_alpha, this returns 1/2 ("rg") instead of 1/4 + // ("ra"). the logic is that the texture format has 2 channels, thus the + // data must be returned in the first two components. The renderer fixes + // this later. + uint8_t components[4][4]; +}; + +bool ra_get_imgfmt_desc(struct ra *ra, int imgfmt, struct ra_imgfmt_desc *out); + +void ra_dump_tex_formats(struct ra *ra, int msgl); +void ra_dump_imgfmt_desc(struct ra *ra, const struct ra_imgfmt_desc *desc, + int msgl); +void ra_dump_img_formats(struct ra *ra, int msgl); diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c new file mode 100644 index 0000000000..bf926a197e --- /dev/null +++ b/video/out/opengl/ra_gl.c @@ -0,0 +1,290 @@ +#include "formats.h" + +#include "ra_gl.h" + +static struct ra_fns ra_fns_gl; + +int ra_init_gl(struct ra *ra, GL *gl) +{ + if (gl->version < 210 && gl->es < 200) { + MP_ERR(ra, "At least OpenGL 2.1 or OpenGL ES 2.0 required.\n"); + return -1; + } + + struct ra_gl *p = ra->priv = talloc_zero(NULL, struct ra_gl); + p->gl = gl; + + ra->fns = &ra_fns_gl; + ra->caps = 0; + if (gl->mpgl_caps & MPGL_CAP_1D_TEX) + ra->caps |= RA_CAP_TEX_1D; + if (gl->mpgl_caps & MPGL_CAP_3D_TEX) + ra->caps |= RA_CAP_TEX_3D; + + int gl_fmt_features = gl_format_feature_flags(gl); + int depth16 = gl_determine_16bit_tex_depth(gl); + + for (int n = 0; gl_formats[n].internal_format; n++) { + const struct gl_format *gl_fmt = &gl_formats[n]; + + if (!(gl_fmt->flags & gl_fmt_features)) + continue; + + struct ra_format *fmt = talloc_zero(ra, struct ra_format); + *fmt = (struct ra_format){ + .name = gl_fmt->name, + .priv = (void *)gl_fmt, + .ctype = gl_format_type(gl_fmt), + .num_components = gl_format_components(gl_fmt->format), + .pixel_size = gl_bytes_per_pixel(gl_fmt->format, gl_fmt->type), + .luminance_alpha = gl_fmt->format == GL_LUMINANCE_ALPHA, + .linear_filter = gl_fmt->flags & F_TF, + .renderable = gl_fmt->flags & F_CR, + }; + + int csize = gl_component_size(gl_fmt->type) * 8; + int depth = csize; + if (fmt->ctype == RA_CTYPE_UNORM) + depth = MPMIN(csize, depth16); // naive/approximate + if (gl_fmt->flags & F_F16) { + depth = 16; + csize = 32; // always upload as GL_FLOAT (simpler for us) + } + + for (int i = 0; i < fmt->num_components; i++) { + fmt->component_size[i] = csize; + fmt->component_depth[i] = depth; + } + + // Special formats for which OpenGL happens to have direct support. + if (strcmp(fmt->name, "rgb565") == 0) { + fmt->special_imgfmt = IMGFMT_RGB565; + struct ra_imgfmt_desc *desc = talloc_zero(fmt, struct ra_imgfmt_desc); + fmt->special_imgfmt_desc = desc; + desc->num_planes = 1; + desc->planes[0] = fmt; + for (int i = 0; i < 3; i++) + desc->components[0][i] = i + 1; + desc->chroma_w = desc->chroma_h = 1; + } + if (strcmp(fmt->name, "ashit") == 0) { + fmt->special_imgfmt = IMGFMT_UYVY; + struct ra_imgfmt_desc *desc = talloc_zero(fmt, struct ra_imgfmt_desc); + fmt->special_imgfmt_desc = desc; + desc->num_planes = 1; + desc->planes[0] = fmt; + desc->components[0][0] = 3; + desc->components[0][1] = 1; + desc->components[0][2] = 2; + desc->chroma_w = desc->chroma_h = 1; + } + + MP_TARRAY_APPEND(ra, ra->formats, ra->num_formats, fmt); + } + + gl->Disable(GL_DITHER); + + return 0; +} + +static void gl_destroy(struct ra *ra) +{ + talloc_free(ra->priv); +} + +static void gl_tex_destroy(struct ra *ra, struct ra_tex *tex) +{ + struct ra_gl *p = ra->priv; + struct ra_tex_gl *tex_gl = tex->priv; + + p->gl->DeleteTextures(1, &tex_gl->texture); + gl_pbo_upload_uninit(&tex_gl->pbo); + talloc_free(tex_gl); + talloc_free(tex); +} + +static struct ra_tex *gl_tex_create(struct ra *ra, + const struct ra_tex_params *params) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + struct ra_tex *tex = talloc_zero(NULL, struct ra_tex); + tex->params = *params; + struct ra_tex_gl *tex_gl = tex->priv = talloc_zero(NULL, struct ra_tex_gl); + + const struct gl_format *fmt = params->format->priv; + tex_gl->internal_format = fmt->internal_format; + tex_gl->format = fmt->format; + tex_gl->type = fmt->type; + switch (params->dimensions) { + case 1: tex_gl->target = GL_TEXTURE_1D; break; + case 2: tex_gl->target = GL_TEXTURE_2D; break; + case 3: tex_gl->target = GL_TEXTURE_3D; break; + default: abort(); + } + if (params->non_normalized) { + assert(params->dimensions == 2); + tex_gl->target = GL_TEXTURE_RECTANGLE; + } + + gl->GenTextures(1, &tex_gl->texture); + gl->BindTexture(tex_gl->target, tex_gl->texture); + + GLint filter = params->src_linear ? GL_LINEAR : GL_NEAREST; + GLint wrap = params->src_repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE; + gl->TexParameteri(tex_gl->target, GL_TEXTURE_MIN_FILTER, filter); + gl->TexParameteri(tex_gl->target, GL_TEXTURE_MAG_FILTER, filter); + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_S, wrap); + if (params->dimensions > 1) + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_T, wrap); + if (params->dimensions > 2) + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_R, wrap); + + switch (params->dimensions) { + case 1: + gl->TexImage1D(tex_gl->target, 0, tex_gl->internal_format, params->w, + 0, tex_gl->format, tex_gl->type, NULL); + break; + case 2: + gl->TexImage2D(tex_gl->target, 0, tex_gl->internal_format, params->w, + params->h, 0, tex_gl->format, tex_gl->type, NULL); + break; + case 3: + gl->TexImage3D(tex_gl->target, 0, tex_gl->internal_format, params->w, + params->h, params->d, 0, tex_gl->format, tex_gl->type, + NULL); + break; + } + + gl->BindTexture(tex_gl->target, 0); + + return tex; +} + +static void gl_tex_upload(struct ra *ra, struct ra_tex *tex, + const void *src, ptrdiff_t stride, + struct ra_mapped_buffer *buf) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + struct ra_tex_gl *tex_gl = tex->priv; + struct ra_mapped_buffer_gl *buf_gl = NULL; + + if (buf) { + buf_gl = buf->priv; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + src = (void *)((uintptr_t)src - (uintptr_t)buf->data); + } + + gl->BindTexture(tex_gl->target, tex_gl->texture); + + switch (tex->params.dimensions) { + case 1: + gl->TexImage1D(tex_gl->target, 0, tex_gl->internal_format, + tex->params.w, 0, tex_gl->format, tex_gl->type, src); + break; + case 2: + gl_pbo_upload_tex(&tex_gl->pbo, gl, tex->use_pbo && !buf, + tex_gl->target, tex_gl->format, tex_gl->type, + tex->params.w, tex->params.h, src, stride, + 0, 0, tex->params.w, tex->params.h); + break; + case 3: + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl->TexImage3D(GL_TEXTURE_3D, 0, tex_gl->internal_format, tex->params.w, + tex->params.h, tex->params.d, 0, tex_gl->format, + tex_gl->type, src); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); + break; + } + + gl->BindTexture(tex_gl->target, 0); + + if (buf) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + // Make sure the PBO is not reused until GL is done with it. If a + // previous operation is pending, "update" it by creating a new + // fence that will cover the previous operation as well. + gl->DeleteSync(buf_gl->fence); + buf_gl->fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } +} + +static void gl_destroy_mapped_buffer(struct ra *ra, struct ra_mapped_buffer *buf) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + struct ra_mapped_buffer_gl *buf_gl = buf->priv; + + gl->DeleteSync(buf_gl->fence); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + if (buf->data) + gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + gl->DeleteBuffers(1, &buf_gl->pbo); + + talloc_free(buf_gl); + talloc_free(buf); +} + +static struct ra_mapped_buffer *gl_create_mapped_buffer(struct ra *ra, + size_t size) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + if (gl->version < 440) + return NULL; + + struct ra_mapped_buffer *buf = talloc_zero(NULL, struct ra_mapped_buffer); + buf->size = size; + buf->preferred_align = 1; + + struct ra_mapped_buffer_gl *buf_gl = buf->priv = + talloc_zero(NULL, struct ra_mapped_buffer_gl); + + unsigned flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | + GL_MAP_COHERENT_BIT; + + gl->GenBuffers(1, &buf_gl->pbo); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + gl->BufferStorage(GL_PIXEL_UNPACK_BUFFER, size, NULL, flags); + buf->data = gl->MapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, buf->size, flags); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + if (!buf->data) { + gl_check_e |