summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/user_shaders.c
diff options
context:
space:
mode:
authorNiklas Haas <git@haasn.xyz>2017-07-11 01:59:21 +0200
committerNiklas Haas <git@haasn.xyz>2017-07-27 23:51:05 +0200
commit345bb193fe75ddbf2f21bd295869276b6fa87189 (patch)
tree319f31b8bb33be3532846916da32f93dd4b8152a /video/out/opengl/user_shaders.c
parentf1af6e53f0b043cac2d3f1024d7d91785072f237 (diff)
downloadmpv-345bb193fe75ddbf2f21bd295869276b6fa87189.tar.bz2
mpv-345bb193fe75ddbf2f21bd295869276b6fa87189.tar.xz
vo_opengl: support loading custom user textures
Parsing the texture data as raw strings makes the textures the most portable and self-contained. In order to facilitate different types of shaders, the parse_user_shader interaction has been changed to instead have it loop through blocks and call the passed functions for each valid block parsed. This is more modular and also cleaner, with better code separation. Closes #4586.
Diffstat (limited to 'video/out/opengl/user_shaders.c')
-rw-r--r--video/out/opengl/user_shaders.c177
1 files changed, 163 insertions, 14 deletions
diff --git a/video/out/opengl/user_shaders.c b/video/out/opengl/user_shaders.c
index 5cfd89b5ef..1b6fb42ab1 100644
--- a/video/out/opengl/user_shaders.c
+++ b/video/out/opengl/user_shaders.c
@@ -19,6 +19,7 @@
#include "misc/ctype.h"
#include "user_shaders.h"
+#include "formats.h"
static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
{
@@ -158,13 +159,10 @@ done:
return true;
}
-bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
- struct gl_user_shader *out)
+static bool parse_hook(struct mp_log *log, struct bstr *body,
+ struct gl_user_shader_hook *out)
{
- if (!body || !out || !body->start || body->len == 0)
- return false;
-
- *out = (struct gl_user_shader){
+ *out = (struct gl_user_shader_hook){
.pass_desc = bstr0("(unknown)"),
.offset = identity_trans,
.width = {{ SZEXP_VAR_W, { .varname = bstr0("HOOKED") }}},
@@ -175,14 +173,6 @@ bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
int hook_idx = 0;
int bind_idx = 0;
- // Skip all garbage (e.g. comments) before the first header
- int pos = bstr_find(*body, bstr0("//!"));
- if (pos < 0) {
- mp_warn(log, "Shader appears to contain no headers!\n");
- return false;
- }
- *body = bstr_cut(*body, pos);
-
// Parse all headers
while (true) {
struct bstr rest;
@@ -295,3 +285,162 @@ bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
return true;
}
+
+static bool parse_tex(struct mp_log *log, struct bstr *body,
+ struct gl_user_shader_tex *out)
+{
+ *out = (struct gl_user_shader_tex){
+ .name = bstr0("USER_TEX"),
+ .w = 1, .h = 1, .d = 1,
+ .components = 1,
+ .bytes = 1,
+ .mpgl_type = MPGL_TYPE_UINT,
+ .gl_filter = GL_LINEAR,
+ .gl_target = GL_TEXTURE_1D,
+ .gl_border = GL_CLAMP_TO_EDGE,
+ };
+
+ while (true) {
+ struct bstr rest;
+ struct bstr line = bstr_strip(bstr_getline(*body, &rest));
+
+ if (!bstr_eatstart0(&line, "//!"))
+ break;
+
+ *body = rest;
+
+ if (bstr_eatstart0(&line, "TEXTURE")) {
+ out->name = bstr_strip(line);
+ continue;
+ }
+
+ if (bstr_eatstart0(&line, "SIZE")) {
+ int num = bstr_sscanf(line, "%d %d %d", &out->w, &out->h, &out->d);
+ if (num < 1 || num > 3 || out->w < 1 || out->h < 1 || out->d < 1) {
+ mp_err(log, "Error while parsing SIZE!\n");
+ return false;
+ }
+ static GLenum tgt[] = {GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D};
+ out->gl_target = tgt[num - 1];
+ continue;
+ }
+
+ if (bstr_eatstart0(&line, "COMPONENTS")) {
+ if (bstr_sscanf(line, "%d", &out->components) != 1) {
+ mp_err(log, "Error while parsing COMPONENTS!\n");
+ return false;
+ }
+ continue;
+ }
+
+ if (bstr_eatstart0(&line, "FORMAT")) {
+ int bits;
+ char fmt;
+ if (bstr_sscanf(line, "%d%c", &bits, &fmt) != 2) {
+ mp_err(log, "Error while parsing FORMAT!\n");
+ return false;
+ }
+
+ out->bytes = bits / 8;
+ switch (fmt) {
+ case 'f': out->mpgl_type = MPGL_TYPE_FLOAT; break;
+ case 'i': out->mpgl_type = MPGL_TYPE_UINT; break;
+ case 'u': out->mpgl_type = MPGL_TYPE_UNORM; break;
+ default:
+ mp_err(log, "Unrecognized FORMAT description: '%c'!\n", fmt);
+ return false;
+ }
+ continue;
+ }
+
+ if (bstr_eatstart0(&line, "FILTER")) {
+ line = bstr_strip(line);
+ if (bstr_equals0(line, "LINEAR")) {
+ out->gl_filter = GL_LINEAR;
+ } else if (bstr_equals0(line, "NEAREST")) {
+ out->gl_filter = GL_NEAREST;
+ } else {
+ mp_err(log, "Unrecognized FILTER: '%.*s'!\n", BSTR_P(line));
+ return false;
+ }
+ continue;
+ }
+
+ if (bstr_eatstart0(&line, "BORDER")) {
+ line = bstr_strip(line);
+ if (bstr_equals0(line, "CLAMP")) {
+ out->gl_border = GL_CLAMP_TO_EDGE;
+ } else if (bstr_equals0(line, "REPEAT")) {
+ out->gl_border = GL_REPEAT;
+ } else if (bstr_equals0(line, "MIRROR")) {
+ out->gl_border = GL_MIRRORED_REPEAT;
+ } else {
+ mp_err(log, "Unrecognized BORDER: '%.*s'!\n", BSTR_P(line));
+ return false;
+ }
+ continue;
+ }
+
+ mp_err(log, "Unrecognized command '%.*s'!\n", BSTR_P(line));
+ return false;
+ }
+
+ // Decode the rest of the section (up to the next //! marker) as raw hex
+ // data for the texture
+ struct bstr hexdata;
+ if (bstr_split_tok(*body, "//!", &hexdata, body)) {
+ // Make sure the magic line is part of the rest
+ body->start -= 3;
+ body->len += 3;
+ }
+
+ struct bstr tex;
+ if (!bstr_decode_hex(NULL, bstr_strip(hexdata), &tex)) {
+ mp_err(log, "Error while parsing TEXTURE body: must be a valid "
+ "hexadecimal sequence, on a single line!\n");
+ return false;
+ }
+
+ int expected_len = out->w * out->h * out->d * out->components * out->bytes;
+ if (tex.len != expected_len) {
+ mp_err(log, "Shader TEXTURE size mismatch: got %zd bytes, expected %d!\n",
+ tex.len, expected_len);
+ talloc_free(tex.start);
+ return false;
+ }
+
+ out->texdata = tex.start;
+ return true;
+}
+
+void parse_user_shader(struct mp_log *log, struct bstr shader, void *priv,
+ bool (*dohook)(void *p, struct gl_user_shader_hook hook),
+ bool (*dotex)(void *p, struct gl_user_shader_tex tex))
+{
+ if (!dohook || !dotex || !shader.len)
+ return;
+
+ // Skip all garbage (e.g. comments) before the first header
+ int pos = bstr_find(shader, bstr0("//!"));
+ if (pos < 0) {
+ mp_warn(log, "Shader appears to contain no headers!\n");
+ return;
+ }
+ shader = bstr_cut(shader, pos);
+
+ // Loop over the file
+ while (shader.len > 0)
+ {
+ // Peek at the first header to dispatch the right type
+ if (bstr_startswith0(shader, "//!TEXTURE")) {
+ struct gl_user_shader_tex t;
+ if (!parse_tex(log, &shader, &t) || !dotex(priv, t))
+ return;
+ continue;
+ }
+
+ struct gl_user_shader_hook h;
+ if (!parse_hook(log, &shader, &h) || !dohook(priv, h))
+ return;
+ }
+}