mirror of https://github.com/mpv-player/mpv
vo_opengl: move eval_szexpr to user_shaders.c
This moves some of the bulky user-shader specific logic into the file dedicated to it. Rather than expose video.c state, variable lookup is now done via a simulated closure.
This commit is contained in:
parent
5b6cce2b73
commit
be230d16e5
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "user_shaders.h"
|
#include "user_shaders.h"
|
||||||
|
|
||||||
|
@ -69,6 +70,94 @@ static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether successful. 'result' is left untouched on failure
|
||||||
|
bool eval_szexpr(struct mp_log *log, void *priv,
|
||||||
|
bool (*lookup)(void *priv, struct bstr var, float size[2]),
|
||||||
|
struct szexp expr[MAX_SZEXP_SIZE], float *result)
|
||||||
|
{
|
||||||
|
float stack[MAX_SZEXP_SIZE] = {0};
|
||||||
|
int idx = 0; // points to next element to push
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SZEXP_SIZE; i++) {
|
||||||
|
switch (expr[i].tag) {
|
||||||
|
case SZEXP_END:
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
case SZEXP_CONST:
|
||||||
|
// Since our SZEXPs are bound by MAX_SZEXP_SIZE, it should be
|
||||||
|
// impossible to overflow the stack
|
||||||
|
assert(idx < MAX_SZEXP_SIZE);
|
||||||
|
stack[idx++] = expr[i].val.cval;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case SZEXP_OP1:
|
||||||
|
if (idx < 1) {
|
||||||
|
mp_warn(log, "Stack underflow in RPN expression!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (expr[i].val.op) {
|
||||||
|
case SZEXP_OP_NOT: stack[idx-1] = !stack[idx-1]; break;
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case SZEXP_OP2:
|
||||||
|
if (idx < 2) {
|
||||||
|
mp_warn(log, "Stack underflow in RPN expression!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop the operands in reverse order
|
||||||
|
float op2 = stack[--idx];
|
||||||
|
float op1 = stack[--idx];
|
||||||
|
float res = 0.0;
|
||||||
|
switch (expr[i].val.op) {
|
||||||
|
case SZEXP_OP_ADD: res = op1 + op2; break;
|
||||||
|
case SZEXP_OP_SUB: res = op1 - op2; break;
|
||||||
|
case SZEXP_OP_MUL: res = op1 * op2; break;
|
||||||
|
case SZEXP_OP_DIV: res = op1 / op2; break;
|
||||||
|
case SZEXP_OP_GT: res = op1 > op2; break;
|
||||||
|
case SZEXP_OP_LT: res = op1 < op2; break;
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isfinite(res)) {
|
||||||
|
mp_warn(log, "Illegal operation in RPN expression!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[idx++] = res;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case SZEXP_VAR_W:
|
||||||
|
case SZEXP_VAR_H: {
|
||||||
|
struct bstr name = expr[i].val.varname;
|
||||||
|
float size[2];
|
||||||
|
|
||||||
|
if (!lookup(priv, name, size)) {
|
||||||
|
mp_warn(log, "Variable %.*s not found in RPN expression!\n",
|
||||||
|
BSTR_P(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[idx++] = (expr[i].tag == SZEXP_VAR_W) ? size[0] : size[1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
// Return the single stack element
|
||||||
|
if (idx != 1) {
|
||||||
|
mp_warn(log, "Malformed stack after RPN expression!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = stack[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns false if no more shaders could be parsed
|
// Returns false if no more shaders could be parsed
|
||||||
bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
|
bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
|
||||||
struct gl_user_shader *out)
|
struct gl_user_shader *out)
|
||||||
|
|
|
@ -71,4 +71,9 @@ struct gl_user_shader {
|
||||||
bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
|
bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
|
||||||
struct gl_user_shader *out);
|
struct gl_user_shader *out);
|
||||||
|
|
||||||
|
// Evaluate a szexp, given a lookup function for named textures
|
||||||
|
bool eval_szexpr(struct mp_log *log, void *priv,
|
||||||
|
bool (*lookup)(void *priv, struct bstr var, float size[2]),
|
||||||
|
struct szexp expr[MAX_SZEXP_SIZE], float *result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1544,112 +1544,40 @@ static void user_hook_old(struct gl_video *p, struct img_tex tex,
|
||||||
GLSLF("color = %s(HOOKED_raw, HOOKED_pos, HOOKED_size);\n", fn_name);
|
GLSLF("color = %s(HOOKED_raw, HOOKED_pos, HOOKED_size);\n", fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether successful. 'result' is left untouched on failure
|
struct szexp_ctx {
|
||||||
static bool eval_szexpr(struct gl_video *p, struct img_tex tex,
|
struct gl_video *p;
|
||||||
struct szexp expr[MAX_SZEXP_SIZE],
|
struct img_tex tex;
|
||||||
float *result)
|
};
|
||||||
|
|
||||||
|
static bool szexp_lookup(void *priv, struct bstr var, float size[2])
|
||||||
{
|
{
|
||||||
float stack[MAX_SZEXP_SIZE] = {0};
|
struct szexp_ctx *ctx = priv;
|
||||||
int idx = 0; // points to next element to push
|
struct gl_video *p = ctx->p;
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SZEXP_SIZE; i++) {
|
// The size of OUTPUT is determined. It could be useful for certain
|
||||||
switch (expr[i].tag) {
|
// user shaders to skip passes.
|
||||||
case SZEXP_END:
|
if (bstr_equals0(var, "OUTPUT")) {
|
||||||
goto done;
|
size[0] = p->dst_rect.x1 - p->dst_rect.x0;
|
||||||
|
size[1] = p->dst_rect.y1 - p->dst_rect.y0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case SZEXP_CONST:
|
// HOOKED is a special case
|
||||||
// Since our SZEXPs are bound by MAX_SZEXP_SIZE, it should be
|
if (bstr_equals0(var, "HOOKED")) {
|
||||||
// impossible to overflow the stack
|
size[0] = ctx->tex.w;
|
||||||
assert(idx < MAX_SZEXP_SIZE);
|
size[1] = ctx->tex.h;
|
||||||
stack[idx++] = expr[i].val.cval;
|
return true;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
case SZEXP_OP1:
|
for (int o = 0; o < p->saved_tex_num; o++) {
|
||||||
if (idx < 1) {
|
if (bstr_equals0(var, p->saved_tex[o].name)) {
|
||||||
MP_WARN(p, "Stack underflow in RPN expression!\n");
|
size[0] = p->saved_tex[o].tex.w;
|
||||||
return false;
|
size[1] = p->saved_tex[o].tex.h;
|
||||||
}
|
return true;
|
||||||
|
|
||||||
switch (expr[i].val.op) {
|
|
||||||
case SZEXP_OP_NOT: stack[idx-1] = !stack[idx-1]; break;
|
|
||||||
default: abort();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case SZEXP_OP2:
|
|
||||||
if (idx < 2) {
|
|
||||||
MP_WARN(p, "Stack underflow in RPN expression!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the operands in reverse order
|
|
||||||
float op2 = stack[--idx];
|
|
||||||
float op1 = stack[--idx];
|
|
||||||
float res = 0.0;
|
|
||||||
switch (expr[i].val.op) {
|
|
||||||
case SZEXP_OP_ADD: res = op1 + op2; break;
|
|
||||||
case SZEXP_OP_SUB: res = op1 - op2; break;
|
|
||||||
case SZEXP_OP_MUL: res = op1 * op2; break;
|
|
||||||
case SZEXP_OP_DIV: res = op1 / op2; break;
|
|
||||||
case SZEXP_OP_GT: res = op1 > op2; break;
|
|
||||||
case SZEXP_OP_LT: res = op1 < op2; break;
|
|
||||||
default: abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isfinite(res)) {
|
|
||||||
MP_WARN(p, "Illegal operation in RPN expression!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack[idx++] = res;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case SZEXP_VAR_W:
|
|
||||||
case SZEXP_VAR_H: {
|
|
||||||
struct bstr name = expr[i].val.varname;
|
|
||||||
struct img_tex var_tex;
|
|
||||||
|
|
||||||
// The size of OUTPUT is determined. It could be useful for certain
|
|
||||||
// user shaders to skip passes.
|
|
||||||
if (bstr_equals0(name, "OUTPUT")) {
|
|
||||||
int vp_w = p->dst_rect.x1 - p->dst_rect.x0;
|
|
||||||
int vp_h = p->dst_rect.y1 - p->dst_rect.y0;
|
|
||||||
stack[idx++] = (expr[i].tag == SZEXP_VAR_W) ? vp_w : vp_h;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HOOKED is a special case
|
|
||||||
if (bstr_equals0(name, "HOOKED")) {
|
|
||||||
var_tex = tex;
|
|
||||||
goto found_tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int o = 0; o < p->saved_tex_num; o++) {
|
|
||||||
if (bstr_equals0(name, p->saved_tex[o].name)) {
|
|
||||||
var_tex = p->saved_tex[o].tex;
|
|
||||||
goto found_tex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MP_WARN(p, "Texture %.*s not found in RPN expression!\n", BSTR_P(name));
|
|
||||||
return false;
|
|
||||||
|
|
||||||
found_tex:
|
|
||||||
stack[idx++] = (expr[i].tag == SZEXP_VAR_W) ? var_tex.w : var_tex.h;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return false;
|
||||||
// Return the single stack element
|
|
||||||
if (idx != 1) {
|
|
||||||
MP_WARN(p, "Malformed stack after RPN expression!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = stack[0];
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv)
|
static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv)
|
||||||
|
@ -1658,7 +1586,7 @@ static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv)
|
||||||
assert(shader);
|
assert(shader);
|
||||||
|
|
||||||
float res = false;
|
float res = false;
|
||||||
eval_szexpr(p, tex, shader->cond, &res);
|
eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->cond, &res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1676,8 +1604,8 @@ static void user_hook(struct gl_video *p, struct img_tex tex,
|
||||||
// to do this and display an error message than just crash OpenGL
|
// to do this and display an error message than just crash OpenGL
|
||||||
float w = 1.0, h = 1.0;
|
float w = 1.0, h = 1.0;
|
||||||
|
|
||||||
eval_szexpr(p, tex, shader->width, &w);
|
eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->width, &w);
|
||||||
eval_szexpr(p, tex, shader->height, &h);
|
eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->height, &h);
|
||||||
|
|
||||||
*trans = (struct gl_transform){{{w / tex.w, 0}, {0, h / tex.h}}};
|
*trans = (struct gl_transform){{{w / tex.w, 0}, {0, h / tex.h}}};
|
||||||
gl_transform_trans(shader->offset, trans);
|
gl_transform_trans(shader->offset, trans);
|
||||||
|
|
Loading…
Reference in New Issue