mirror of
https://github.com/mpv-player/mpv
synced 2024-12-29 02:22:19 +00:00
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 <assert.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;
|
||||
}
|
||||
|
||||
// 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
|
||||
bool parse_user_shader_pass(struct mp_log *log, struct bstr *body,
|
||||
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,
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// Returns whether successful. 'result' is left untouched on failure
|
||||
static bool eval_szexpr(struct gl_video *p, struct img_tex tex,
|
||||
struct szexp expr[MAX_SZEXP_SIZE],
|
||||
float *result)
|
||||
struct szexp_ctx {
|
||||
struct gl_video *p;
|
||||
struct img_tex tex;
|
||||
};
|
||||
|
||||
static bool szexp_lookup(void *priv, struct bstr var, float size[2])
|
||||
{
|
||||
float stack[MAX_SZEXP_SIZE] = {0};
|
||||
int idx = 0; // points to next element to push
|
||||
struct szexp_ctx *ctx = priv;
|
||||
struct gl_video *p = ctx->p;
|
||||
|
||||
for (int i = 0; i < MAX_SZEXP_SIZE; i++) {
|
||||
switch (expr[i].tag) {
|
||||
case SZEXP_END:
|
||||
goto done;
|
||||
// The size of OUTPUT is determined. It could be useful for certain
|
||||
// user shaders to skip passes.
|
||||
if (bstr_equals0(var, "OUTPUT")) {
|
||||
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:
|
||||
// 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;
|
||||
// HOOKED is a special case
|
||||
if (bstr_equals0(var, "HOOKED")) {
|
||||
size[0] = ctx->tex.w;
|
||||
size[1] = ctx->tex.h;
|
||||
return true;
|
||||
}
|
||||
|
||||
case SZEXP_OP1:
|
||||
if (idx < 1) {
|
||||
MP_WARN(p, "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(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;
|
||||
}
|
||||
for (int o = 0; o < p->saved_tex_num; o++) {
|
||||
if (bstr_equals0(var, p->saved_tex[o].name)) {
|
||||
size[0] = p->saved_tex[o].tex.w;
|
||||
size[1] = p->saved_tex[o].tex.h;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
// Return the single stack element
|
||||
if (idx != 1) {
|
||||
MP_WARN(p, "Malformed stack after RPN expression!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = stack[0];
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
float w = 1.0, h = 1.0;
|
||||
|
||||
eval_szexpr(p, tex, shader->width, &w);
|
||||
eval_szexpr(p, tex, shader->height, &h);
|
||||
eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->width, &w);
|
||||
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}}};
|
||||
gl_transform_trans(shader->offset, trans);
|
||||
|
Loading…
Reference in New Issue
Block a user