diff options
Diffstat (limited to 'video')
43 files changed, 2385 insertions, 652 deletions
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h index 28844f5882..ad33d784f6 100644 --- a/video/decode/dec_video.h +++ b/video/decode/dec_video.h @@ -56,7 +56,7 @@ struct dec_video { int num_codec_dts_problems; // PTS sorting (needed for AVI-style timestamps) - double buffered_pts[64]; + double buffered_pts[128]; int num_buffered_pts; // PTS or DTS of packet first read diff --git a/video/decode/lavc.h b/video/decode/lavc.h index 90b2d6a886..76b7ac7883 100644 --- a/video/decode/lavc.h +++ b/video/decode/lavc.h @@ -18,6 +18,7 @@ typedef struct lavc_ctx { enum AVPixelFormat pix_fmt; int best_csp; enum AVDiscard skip_frame; + bool flushing; const char *software_fallback_decoder; bool hwdec_failed; bool hwdec_notified; @@ -34,6 +35,7 @@ typedef struct lavc_ctx { int hwdec_profile; bool hwdec_request_reinit; + int hwdec_fail_count; } vd_ffmpeg_ctx; struct vd_lavc_hwdec { @@ -55,7 +57,7 @@ struct vd_lavc_hwdec { void (*lock)(struct lavc_ctx *ctx); void (*unlock)(struct lavc_ctx *ctx); // Optional; if a special hardware decoder is needed (instead of "hwaccel"). - const char *(*get_codec)(struct lavc_ctx *ctx); + const char *(*get_codec)(struct lavc_ctx *ctx, const char *codec); }; enum { diff --git a/video/decode/rpi.c b/video/decode/rpi.c index 819369de0d..12816cdc86 100644 --- a/video/decode/rpi.c +++ b/video/decode/rpi.c @@ -18,6 +18,21 @@ #include "lavc.h" #include "common/common.h" +static const char *const codecs[][2] = { + {"h264", "h264_mmal"}, + {"mpeg2video", "mpeg2_mmal"}, + {0} +}; + +static const char *map_codec(const char *c) +{ + for (int n = 0; codecs[n][0]; n++) { + if (c && strcmp(codecs[n][0], c) == 0) + return codecs[n][1]; + } + return NULL; +} + static int init_decoder(struct lavc_ctx *ctx, int w, int h) { return 0; @@ -35,14 +50,12 @@ static int init(struct lavc_ctx *ctx) static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, const char *decoder) { - if (strcmp(decoder, "h264") != 0) - return HWDEC_ERR_NO_CODEC; - return 0; + return map_codec(decoder) ? 0 : HWDEC_ERR_NO_CODEC; } -static const char *get_codec(struct lavc_ctx *ctx) +static const char *get_codec(struct lavc_ctx *ctx, const char *codec) { - return "h264_mmal"; + return map_codec(codec); } const struct vd_lavc_hwdec mp_vd_lavc_rpi = { diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index 91569b99f3..bae0224e02 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -102,7 +102,8 @@ const struct m_sub_options vd_lavc_conf = { OPT_INT("threads", threads, M_OPT_MIN, .min = 0), OPT_FLAG("bitexact", bitexact, 0), OPT_FLAG("check-hw-profile", check_hw_profile, 0), - OPT_FLAG("software-fallback", software_fallback, 0), + OPT_CHOICE_OR_INT("software-fallback", software_fallback, 0, 1, INT_MAX, + ({"no", INT_MAX}, {"yes", 1})), OPT_KEYVALUELIST("o", avopts, 0), {0} }, @@ -110,7 +111,7 @@ const struct m_sub_options vd_lavc_conf = { .defaults = &(const struct vd_lavc_params){ .show_all = 0, .check_hw_profile = 1, - .software_fallback = 1, + .software_fallback = 3, .skip_loop_filter = AVDISCARD_DEFAULT, .skip_idct = AVDISCARD_DEFAULT, .skip_frame = AVDISCARD_DEFAULT, @@ -118,12 +119,12 @@ const struct m_sub_options vd_lavc_conf = { }, }; -const struct vd_lavc_hwdec mp_vd_lavc_vdpau; -const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox; -const struct vd_lavc_hwdec mp_vd_lavc_vaapi; -const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy; -const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy; -const struct vd_lavc_hwdec mp_vd_lavc_rpi; +extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau; +extern const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox; +extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi; +extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy; +extern const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy; +extern const struct vd_lavc_hwdec mp_vd_lavc_rpi; static const struct vd_lavc_hwdec *const hwdec_list[] = { #if HAVE_RPI @@ -326,7 +327,7 @@ static int init(struct dec_video *vd, const char *decoder) if (hwdec) { ctx->software_fallback_decoder = talloc_strdup(ctx, decoder); if (hwdec->get_codec) - decoder = hwdec->get_codec(ctx); + decoder = hwdec->get_codec(ctx, decoder); MP_VERBOSE(vd, "Trying hardware decoding.\n"); } else { MP_VERBOSE(vd, "Using software decoding.\n"); @@ -442,6 +443,15 @@ error: uninit_avctx(vd); } +static void reset_avctx(struct dec_video *vd) +{ + vd_ffmpeg_ctx *ctx = vd->priv; + + if (ctx->avctx) + avcodec_flush_buffers(ctx->avctx); + ctx->flushing = false; +} + static void uninit_avctx(struct dec_video *vd) { vd_ffmpeg_ctx *ctx = vd->priv; @@ -456,12 +466,15 @@ static void uninit_avctx(struct dec_video *vd) if (ctx->hwdec && ctx->hwdec->uninit) ctx->hwdec->uninit(ctx); + ctx->hwdec = NULL; av_freep(&ctx->avctx); av_frame_free(&ctx->pic); + ctx->flushing = false; ctx->hwdec_failed = false; + ctx->hwdec_fail_count = 0; } static void update_image_params(struct dec_video *vd, AVFrame *frame, @@ -608,30 +621,40 @@ static void decode(struct dec_video *vd, struct demux_packet *packet, int ret; vd_ffmpeg_ctx *ctx = vd->priv; AVCodecContext *avctx = ctx->avctx; - struct vd_lavc_params *lavc_param = ctx->opts->vd_lavc_params; + struct vd_lavc_params *opts = ctx->opts->vd_lavc_params; AVPacket pkt; - if (ctx->hwdec_request_reinit) - avcodec_flush_buffers(avctx); - if (flags) { // hr-seek framedrop vs. normal framedrop - avctx->skip_frame = flags == 2 ? AVDISCARD_NONREF : lavc_param->framedrop; + avctx->skip_frame = flags == 2 ? AVDISCARD_NONREF : opts->framedrop; } else { // normal playback avctx->skip_frame = ctx->skip_frame; } mp_set_av_packet(&pkt, packet, NULL); + ctx->flushing |= !pkt.data; + + // Reset decoder if hw state got reset, or new data comes during flushing. + if (ctx->hwdec_request_reinit || (pkt.data && ctx->flushing)) + reset_avctx(vd); hwdec_lock(ctx); ret = avcodec_decode_video2(avctx, ctx->pic, &got_picture, &pkt); hwdec_unlock(ctx); + // Reset decoder if it was fully flushed. Caller might send more flush + // packets, or even new actual packets. + if (ctx->flushing && (ret < 0 || !got_picture)) + reset_avctx(vd); + if (ret < 0) { MP_WARN(vd, "Error while decoding frame!\n"); - if (lavc_param->software_fallback) - ctx->hwdec_failed = true; + if (ctx->hwdec) { + ctx->hwdec_fail_count += 1; + if (ctx->hwdec_fail_count >= opts->software_fallback) + ctx->hwdec_failed = true; + } return; } @@ -644,6 +667,8 @@ static void decode(struct dec_video *vd, struct demux_packet *packet, if (!got_picture) return; + ctx->hwdec_fail_count = 0; + struct mp_image_params params; update_image_params(vd, ctx->pic, ¶ms); vd->codec_pts = mp_pts_from_av(ctx->pic->pkt_pts, NULL); @@ -693,10 +718,9 @@ static struct mp_image *decode_with_fallback(struct dec_video *vd, static int control(struct dec_video *vd, int cmd, void *arg) { vd_ffmpeg_ctx *ctx = vd->priv; - AVCodecContext *avctx = ctx->avctx; switch (cmd) { case VDCTRL_RESET: - avcodec_flush_buffers(avctx); + reset_avctx(vd); return CONTROL_TRUE; case VDCTRL_GET_HWDEC: { int hwdec = ctx->hwdec ? ctx->hwdec->type : 0; diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c index 9a3e7e27cf..f8375532fb 100644 --- a/video/decode/vdpau.c +++ b/video/decode/vdpau.c @@ -80,13 +80,9 @@ static int init(struct lavc_ctx *ctx) ctx->hwdec_priv = p; if (mp_vdpau_handle_preemption(p->mpvdp, &p->preemption_counter) < 1) - goto error; + return -1; return 0; - -error: - uninit(ctx); - return -1; } static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, diff --git a/video/filter/vf.c b/video/filter/vf.c index 6ad484c7ff..b16c0b3627 100644 --- a/video/filter/vf.c +++ b/video/filter/vf.c @@ -269,12 +269,15 @@ static vf_instance_t *vf_open_filter(struct vf_chain *c, const char *name, for (i = 0; args && args[2 * i]; i++) l += 1 + strlen(args[2 * i]) + 1 + strlen(args[2 * i + 1]); l += strlen(name); - char str[l + 1]; + char *str = malloc(l + 1); + if (!str) + return NULL; char *p = str; p += sprintf(str, "%s", name); for (i = 0; args && args[2 * i]; i++) p += sprintf(p, " %s=%s", args[2 * i], args[2 * i + 1]); MP_INFO(c, "Opening video filter: [%s]\n", str); + free(str); return vf_open(c, name, args); } diff --git a/video/img_format.c b/video/img_format.c index c2bbf6bb01..332bf3676f 100644 --- a/video/img_format.c +++ b/video/img_format.c @@ -232,7 +232,8 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt) desc.flags |= MP_IMGFLAG_PAL; if ((desc.flags & (MP_IMGFLAG_YUV | MP_IMGFLAG_RGB)) - && (desc.flags & MP_IMGFLAG_BYTE_ALIGNED)) + && (desc.flags & MP_IMGFLAG_BYTE_ALIGNED) + && !(pd->flags & AV_PIX_FMT_FLAG_PAL)) { bool same_depth = true; for (int p = 0; p < desc.num_planes; p++) { diff --git a/video/out/drm_common.c b/video/out/drm_common.c index 1ea0e45ae8..3462a5d4de 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -1,8 +1,6 @@ /* * This file is part of mpv. * - * by rr- <rr-@sakuya.pl> - * * mpv is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,6 +13,11 @@ * * You should have received a copy of the GNU General Public License along * with mpv. If not, see <http://www.gnu.org/licenses/>. + * + * You can alternatively redistribute this file 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. */ #include <errno.h> @@ -35,16 +38,216 @@ #define EVT_INTERRUPT 255 #define HANDLER_ACQUIRE 0 #define HANDLER_RELEASE 1 +#define RELEASE_SIGNAL SIGUSR1 +#define ACQUIRE_SIGNAL SIGUSR2 static int vt_switcher_pipe[2]; +// KMS ------------------------------------------------------------------------ + +static bool is_connector_valid(struct kms *kms, int connector_id, + drmModeConnector *connector, bool silent) +{ + if (!connector) { + if (!silent) { + MP_ERR(kms, "Cannot get connector %d: %s\n", connector_id, + mp_strerror(errno)); + } + return false; + } + + if (connector->connection != DRM_MODE_CONNECTED) { + if (!silent) { + MP_ERR(kms, "Connector %d is disconnected\n", connector_id); + } + return false; + } + + if (connector->count_modes == 0) { + if (!silent) { + MP_ERR(kms, "Connector %d has no valid modes\n", connector_id); + } + return false; + } + + return true; +} + +static bool setup_connector( + struct kms *kms, const drmModeRes *res, int connector_id) +{ + drmModeConnector *connector = NULL; + if (connector_id == -1) { + // get the first connected connector + for (int i = 0; i < res->count_connectors; i++) { + connector = drmModeGetConnector(kms->fd, res->connectors[i]); + if (is_connector_valid(kms, i, connector, true)) { + connector_id = i; + break; + } + if (connector) { + drmModeFreeConnector(connector); + connector = NULL; + } + } + if (connector_id == -1) { + MP_ERR(kms, "No connected connectors found\n"); + return false; + } + } + + if (connector_id < 0 || connector_id >= res->count_connectors) { + MP_ERR(kms, "Bad connector ID. Max valid connector ID = %u\n", + res->count_connectors); + return false; + } + + connector = drmModeGetConnector(kms->fd, res->connectors[connector_id]); + if (!is_connector_valid(kms, connector_id, connector, false)) + return false; + + kms->connector = connector; + return true; +} + +static bool setup_crtc(struct kms *kms, const drmModeRes *res) +{ + for (unsigned int i = 0; i < kms->connector->count_encoders; ++i) { + drmModeEncoder *encoder + = drmModeGetEncoder(kms->fd, kms->connector->encoders[i]); + if (!encoder) { + MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n", + i, kms->connector->encoders[i], mp_strerror(errno)); + continue; + } + + // iterate all global CRTCs + for (unsigned int j = 0; j < res->count_crtcs; ++j) { + // check whether this CRTC works with the encoder + if (!(encoder->possible_crtcs & (1 << j))) + continue; + + kms->encoder = encoder; + kms->crtc_id = encoder->crtc_id; + return true; + } + + drmModeFreeEncoder(encoder); + } + + MP_ERR(kms, + "Connector %u has no suitable CRTC\n", + kms->connector->connector_id); + return false; +} + +static bool setup_mode(struct kms *kms, int mode_id) +{ + if (mode_id < 0 || mode_id >= kms->connector->count_modes) { + MP_ERR( + kms, + "Bad mode ID (max = %d).\n", + kms->connector->count_modes - 1); + + MP_INFO(kms, "Available modes:\n"); + for (unsigned int i = 0; i < kms->connector->count_modes; i++) { + MP_INFO(kms, + "Mode %d: %s (%dx%d)\n", + i, + kms->connector->modes[i].name, + kms->connector->modes[i].hdisplay, + kms->connector->modes[i].vdisplay); + } + return false; + } + + kms->mode = kms->connector->modes[mode_id]; + return true; +} + + +struct kms *kms_create(struct mp_log *log) +{ + struct kms *ret = talloc(NULL, struct kms); + *ret = (struct kms) { + .log = mp_log_new(ret, log, "kms"), + .fd = -1, + .connector = NULL, + .encoder = NULL, + .mode = { 0 }, + .crtc_id = -1, + }; + return ret; +} + +bool kms_setup(struct kms *kms, const char *device_path, int connector_id, int mode_id) +{ + kms->fd = open(device_path, O_RDWR | O_CLOEXEC); + if (kms->fd < 0) { + MP_ERR(kms, "Cannot open \"%s\": %s.\n", device_path, mp_strerror(errno)); + return false; + } + + drmModeRes *res = drmModeGetResources(kms->fd); + if (!res) { + MP_ERR(kms, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno)); + return false; + } + + if (!setup_connector(kms, res, mode_id)) + return false; + if (!setup_crtc(kms, res)) + return false; + if (!setup_mode(kms, mode_id)) + return false; + + return true; +} + +void kms_destroy(struct kms *kms) +{ + if (!kms) + return; + if (kms->connector) { + drmModeFreeConnector(kms->connector); + kms->connector = NULL; + } + if (kms->encoder) { + drmModeFreeEncoder(kms->encoder); + kms->encoder = NULL; + } + close(kms->fd); + talloc_free(kms); +} + + + +// VT switcher ---------------------------------------------------------------- + static void vt_switcher_sighandler(int sig) { - unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE; + unsigned char event = sig == RELEASE_SIGNAL ? EVT_RELEASE : EVT_ACQUIRE; write(vt_switcher_pipe[1], &event, sizeof(event)); } -int vt_switcher_init(struct vt_switcher *s, struct mp_log *log) +static bool has_signal_installed(int signo) +{ + struct sigaction act = { 0 }; + sigaction(signo, 0, &act); + return act.sa_handler != 0; +} + +static int install_signal(int signo, void (*handler)(int)) +{ + struct sigaction act = { 0 }; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + return sigaction(signo, &act, NULL); +} + + +bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log) { s->log = log; s->tty_fd = -1; @@ -53,37 +256,48 @@ int vt_switcher_init(struct vt_switcher *s, struct mp_log *log) if (mp_make_cloexec_pipe(vt_switcher_pipe)) { MP_ERR(s, "Creating pipe failed: %s\n", mp_strerror(errno)); - return -1; + return false; } s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC); if (s->tty_fd < 0) { MP_ERR(s, "Can't open TTY for VT control: %s\n", mp_strerror(errno)); - return -1; + return false; } - struct sigaction act; - act.sa_handler = vt_switcher_sighandler; - act.sa_flags = SA_RESTART; - sigemptyset(&act.sa_mask); - sigaction(SIGUSR1, &act, 0); - sigaction(SIGUSR2, &act, 0); + if (has_signal_installed(RELEASE_SIGNAL)) { + MP_ERR(s, "Can't handle VT release - signal already used\n"); + return false; + } + if (has_signal_installed(ACQUIRE_SIGNAL)) { + MP_ERR(s, "Can't handle VT acquire - signal already used\n"); + return false; + } + + if (install_signal(RELEASE_SIGNAL, vt_switcher_sighandler)) { + MP_ERR(s, "Failed to install release signal: %s\n", mp_strerror(errno)); + return false; + } + if (install_signal(ACQUIRE_SIGNAL, vt_switcher_sighandler)) { + MP_ERR(s, "Failed to install acquire signal: %s\n", mp_strerror(errno)); + return false; + } struct vt_mode vt_mode; if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) { MP_ERR(s, "VT_GETMODE failed: %s\n", mp_strerror(errno)); - return -1; + return false; } vt_mode.mode = VT_PROCESS; - vt_mode.relsig = SIGUSR1; - vt_mode.acqsig = SIGUSR2; + vt_mode.relsig = RELEASE_SIGNAL; + vt_mode.acqsig = ACQUIRE_SIGNAL; if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) { MP_ERR(s, "VT_SETMODE failed: %s\n", mp_strerror(errno)); - return -1; + return false; } - return 0; + return true; } void vt_switcher_acquire(struct vt_switcher *s, @@ -108,6 +322,8 @@ void vt_switcher_interrupt_poll(struct vt_switcher *s) void vt_switcher_destroy(struct vt_switcher *s) { + install_signal(RELEASE_SIGNAL, SIG_DFL); + install_signal(ACQUIRE_SIGNAL, SIG_DFL); close(s->tty_fd); close(vt_switcher_pipe[0]); close(vt_switcher_pipe[1]); diff --git a/video/out/drm_common.h b/video/out/drm_common.h index 5e6c1915ba..98a4bad04d 100644 --- a/video/out/drm_common.h +++ b/video/out/drm_common.h @@ -18,6 +18,19 @@ #ifndef MP_VT_SWITCHER_H #define MP_VT_SWITCHER_H +#include <stdbool.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +struct kms { + struct mp_log *log; + int fd; + drmModeConnector *connector; + drmModeEncoder *encoder; + drmModeModeInfo mode; + uint32_t crtc_id; +}; + struct vt_switcher { int tty_fd; struct mp_log *log; @@ -25,7 +38,7 @@ struct vt_switcher { void *handler_data[2]; }; -int vt_switcher_init(struct vt_switcher *s, struct mp_log *log); +bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log); void vt_switcher_destroy(struct vt_switcher *s); void vt_switcher_poll(struct vt_switcher *s, int timeout_ms); void vt_switcher_interrupt_poll(struct vt_switcher *s); @@ -33,4 +46,8 @@ void vt_switcher_interrupt_poll(struct vt_switcher *s); void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*), void *user_data); void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data); +struct kms *kms_create(struct mp_log *log); +bool kms_setup(struct kms *kms, const char *device_path, int conn_id, int mode_id); +void kms_destroy(struct kms *kms); + #endif diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c index f045184373..54389e15cf 100644 --- a/video/out/opengl/common.c +++ b/video/out/opengl/common.c @@ -83,9 +83,7 @@ struct gl_functions { const char *extension; // introduced with this extension in any version int provides; // bitfield of MPGL_CAP_* constants int ver_core; // introduced as required function - int ver_removed; // removed as required function (no replacement) int ver_es_core; // introduced as required GL ES function - int ver_es_removed; // removed as required function (no replacement) const struct gl_function *functions; }; @@ -173,6 +171,7 @@ static const struct gl_functions gl_functions[] = { .ver_core = 300, .ver_es_core = 300, .functions = (const struct gl_function[]) { + DEF_FN(BindBufferBase), DEF_FN(GetStringi), // for ES 3.0 DEF_FN(GetTexLevelParameteriv), @@ -230,6 +229,16 @@ static const struct gl_functions gl_functions[] = { .extension = "GL_ARB_texture_rg", .provides = MPGL_CAP_TEX_RG, }, + { + .ver_core = 320, + .extension = "GL_ARB_sync", + .functions = (const struct gl_function[]) { + DEF_FN(FenceSync), + DEF_FN(ClientWaitSync), + DEF_FN(DeleteSync), + {0} + }, + }, // Swap control, always an OS specific extension // The OSX code loads this manually. { @@ -305,6 +314,16 @@ static const struct gl_functions gl_functions[] = { {0} }, }, + // uniform buffer object extensions, requires OpenGL 3.1. + { + .ver_core = 310, + .extension = "GL_ARB_uniform_buffer_object", + .functions = (const struct gl_function[]) { + DEF_FN(GetUniformBlockIndex), + DEF_FN(UniformBlockBinding), + {0} + }, + }, }; #undef FN_OFFS @@ -389,10 +408,6 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n), const struct gl_functions *section = &gl_functions[n]; int version = gl->es ? gl->es : gl->version; int ver_core = gl->es ? section->ver_es_core : section->ver_core; - int ver_removed = gl->es ? section->ver_es_removed : section->ver_removed; - - if (ver_removed && version >= ver_removed) - continue; // NOTE: Function entrypoints can exist, even if they do not work. // We must always check extension strings and versions. @@ -450,16 +465,12 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n), if (gl->es >= 300) gl->glsl_version = 300; } else { - if (gl->version >= 200) - gl->glsl_version = 110; - if (gl->version >= 210) - gl->glsl_version = 120; - if (gl->version >= 300) - gl->glsl_version = 130; - // Specifically needed for OSX (normally we request 3.0 contexts only, but - // OSX always creates 3.2 contexts when requesting a core context). - if (gl->version >= 320) - gl->glsl_version = 150; + gl->glsl_version = 110; + int glsl_major = 0, glsl_minor = 0; + if (sscanf(shader, "%d.%d", &glsl_major, &glsl_minor) == 2) + gl->glsl_version = glsl_major * 100 + glsl_minor; + // GLSL 400 defines "sample" as keyword - breaks custom shaders. + gl->glsl_ |