Perf cleanup (#151)

* Param refactor
collected all the code that reached inside Param (InitCond, Per*Eqn, Expr, Parser) to read/write and stuffed it all back into Param.cpp
made Param extend Expr to avoid any perf penalty (I actually think eval() is a tiny bit faster now)

* presets/tests

* ALWAYS_MATRIX is used with PER_POINT

* use SSE2 to impove initialize_PerPixelMeshes() performance

* TestRunner
very, very simple test framework, but it's better than no framework
(consider investigating adopting something)

* ProgramExpr
This commit is contained in:
mbellew
2019-01-14 05:33:38 +00:00
committed by GitHub
parent 8cf26aac15
commit 42fee50d64
34 changed files with 1310 additions and 496 deletions

18
presets/tests/100-square.milk Executable file
View File

@ -0,0 +1,18 @@
[preset00]
per_frame_1000=// just a double box
per_frame_1001=//
fDecay=1
zoom=0
rot=0.0
warp=1
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=1.0
ib_g=0.0
ib_b=0.0
ib_a=1.0

View File

@ -0,0 +1,20 @@
[preset00]
per_frame_1000=// one per frame equation
per_frame_1001=// varies outer frame color - red only
fDecay=0.98
zoom=0
rot=0.0
warp=0.0
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=1.0
ib_g=0.0
ib_b=0.0
ib_a=1.0
per_frame_1=ib_r=0.7+0.4*sin(3*time);

View File

@ -0,0 +1,22 @@
[preset00]
per_frame_1000=// one per frame equation
per_frame_1001=// varies outer frame color - multi-color
fDecay=0.98
zoom=0
rot=0.0
warp=0.0
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=0.0
ib_g=0.0
ib_b=0.0
ib_a=1.0
per_frame_1=ib_r=0.7+0.4*sin(3*time);
per_frame_2=ib_g=0.7+0.4*sin(4*time);
per_frame_3=ib_b=0.7+0.4*sin(5*time);

View File

@ -0,0 +1,20 @@
[preset00]
per_frame_1000=// one per frame equation
per_frame_1001=// test multiple equations one line
fDecay=0.98
zoom=0
rot=0.0
warp=0.0
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=0.0
ib_g=0.0
ib_b=0.0
ib_a=1.0
per_frame_1=ib_r=0.7+0.4*sin(3*time); ib_g=0.7+0.4*sin(4*time); ib_b=0.7+0.4*sin(5*time);

View File

@ -0,0 +1,21 @@
[preset00]
per_frame_1000=// one per frame equation
per_frame_1001=// test equation spanning two lines
fDecay=0.98
zoom=0
rot=0.0
warp=0.0
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=0.0
ib_g=0.0
ib_b=0.0
ib_a=1.0
per_frame_1=ib_r=0.7+0.4*
per_frame_2= sin(3*time);

View File

@ -0,0 +1,20 @@
[preset00]
per_frame_1000=// one per frame equation
per_frame_1001=// initialize user variable
fDecay=0.98
zoom=0
rot=0.0
warp=0.0
ob_size=0.2
ob_r=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.1
ib_r=1.0
ib_g=0.0
ib_a=1.0
per_frame_init_1=SPEED=10;
per_frame_1=ib_r=0.7+0.4*sin(time*SPEED);

View File

@ -0,0 +1,23 @@
[preset00]
fDecay=0.980000
fWarpScale=2.853000
fZoomExponent=1.000000
warp=0.000000
sx=1.000000
sy=1.000000
ob_size=0.2
ob_r=0.0
ob_g=0.0
ob_b=0.0
ob_a=0.0
ib_size=0.01
ib_r=1.0
ib_g=0.0
ib_b=0.0
ib_a=1.0
per_frame_1=ib_r=0.7+0.4*sin(3*time);
per_frame_2=ib_g=0.7+0.4*sin(4*time);
per_frame_3=ib_b=0.7+0.4*sin(5*time);
per_pixel_1=zoom=0.9615-rad*0.1;

20
presets/tests/200-wave.milk Executable file
View File

@ -0,0 +1,20 @@
[preset00]
per_frame_1000=// simple wave to build
per_frame_1001=//
fDecay=0.980000
nWaveMode=0
bMaximizeWaveColor=1
fWaveAlpha=4.400000
fWaveScale=1.605447
fZoomExponent=1.000000
zoom=1.000000
rot=0.006000
warp=0.000000
sx=1.000000
sy=1.000000
wave_r=0.900000
wave_g=0.90000
wave_b=0.900000
wave_x=0.500000
wave_y=0.500000

36
presets/tests/201-wavecode.milk Executable file
View File

@ -0,0 +1,36 @@
[preset00]
per_frame_1000=// use wavecode
per_frame_1001=// red circle with blue line
fDecay=0.980000
nWaveMode=0
bMaximizeWaveColor=1
fWaveAlpha=4.400000
fWaveScale=1.5
fZoomExponent=1.000000
zoom=1.000000
rot=0.006000
warp=0.000000
sx=1.000000
sy=1.000000
wave_r=0.00000
wave_g=0.0000
wave_b=0.00000
wave_a=0.00000
wave_x=0.500000
wave_y=0.5000000
wavecode_0_enabled=1
wavecode_0_mode=0
wavecode_0_bDrawThick=0
wavecode_0_bAdditive=1
wavecode_0_scaling=1.000000
wavecode_0_smoothing=0.500000
wavecode_0_r=0.000000
wavecode_0_g=1.000000
wavecode_0_b=1.000000
wavecode_0_a=1.000000
wave_0_per_point41=x=x+value1;
wave_0_per_point42=y=y+value2;
per_frame_1=zoom=1

1
presets/tests/README.md Normal file
View File

@ -0,0 +1 @@
These presets are designed to test very specific functionality to help with regression testing when code changes are made.

View File

@ -24,6 +24,7 @@ libprojectM_la_LIBADD = \
libprojectM_la_SOURCES = ConfigFile.cpp Preset.cpp PresetLoader.cpp timer.cpp \
KeyHandler.cpp PresetChooser.cpp TimeKeeper.cpp PCM.cpp PresetFactory.cpp \
fftsg.cpp wipemalloc.cpp PipelineMerger.cpp PresetFactoryManager.cpp projectM.cpp \
TestRunner.cpp TestRunner.hpp \
Common.hpp PipelineMerger.hpp PresetLoader.hpp\
HungarianMethod.hpp Preset.hpp RandomNumberGenerators.hpp\
IdleTextures.hpp PresetChooser.hpp TimeKeeper.hpp\

View File

@ -52,7 +52,7 @@ int BuiltinParams::load_builtin_param_float(const std::string & name, void * eng
std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
if ((param = new Param(lowerName, P_TYPE_DOUBLE, flags, engine_val, matrix, iv, ub, lb)) == NULL)
if ((param = Param::create(lowerName, P_TYPE_DOUBLE, flags, engine_val, matrix, iv, ub, lb)) == NULL)
{
return PROJECTM_OUTOFMEM_ERROR;
}
@ -171,7 +171,7 @@ int BuiltinParams::load_builtin_param_int(const std::string & name, void * engin
std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
param = new Param(lowerName, P_TYPE_INT, flags, engine_val, NULL, iv, ub, lb);
param = Param::create(lowerName, P_TYPE_INT, flags, engine_val, NULL, iv, ub, lb);
if (param == NULL)
{
@ -224,7 +224,7 @@ int BuiltinParams::load_builtin_param_bool(const std:: string & name, void * eng
std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
param = new Param(lowerName, P_TYPE_BOOL, flags, engine_val, NULL, iv, ub, lb);
param = Param::create(lowerName, P_TYPE_BOOL, flags, engine_val, NULL, iv, ub, lb);
if (param == NULL)
{
@ -397,7 +397,7 @@ int BuiltinParams::load_all_builtin_param(const PresetInputs & presetInputs, Pre
for (unsigned int i = 0; i < NUM_Q_VARIABLES;i++) {
std::ostringstream os;
os << "q" << i;
load_builtin_param_float(os.str().c_str(), (void*)&presetOutputs.q[i], NULL, P_FLAG_PER_PIXEL |P_FLAG_QVAR, 0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
load_builtin_param_float(os.str().c_str(), (void*)&presetOutputs.q[i], NULL, P_FLAG_QVAR, 0, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, "");
}

View File

@ -49,7 +49,8 @@ CustomWave::CustomWave(int _id) : Waveform(512),
r(0),
g(0),
b(0),
a(0)
a(0),
per_point_program(nullptr)
{
Param * param;
@ -308,7 +309,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t1", P_FLAG_PER_POINT | P_FLAG_TVAR, &this->t1, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t1", P_FLAG_TVAR, &this->t1, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
;
abort();
@ -320,7 +321,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t2", P_FLAG_PER_POINT |P_FLAG_TVAR, &this->t2, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t2", P_FLAG_TVAR, &this->t2, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
;
abort();
@ -331,7 +332,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t3", P_FLAG_PER_POINT |P_FLAG_TVAR, &this->t3, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t3", P_FLAG_TVAR, &this->t3, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
abort();
}
@ -341,7 +342,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t4", P_FLAG_PER_POINT |P_FLAG_TVAR, &this->t4, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t4", P_FLAG_TVAR, &this->t4, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
abort();
@ -361,7 +362,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t6", P_FLAG_TVAR | P_FLAG_PER_POINT, &this->t6, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t6", P_FLAG_TVAR, &this->t6, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
abort();
}
@ -371,7 +372,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t7", P_FLAG_TVAR | P_FLAG_PER_POINT, &this->t7, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t7", P_FLAG_TVAR, &this->t7, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
abort();
@ -383,7 +384,7 @@ CustomWave::CustomWave(int _id) : Waveform(512),
abort();
}
if ((param = Param::new_param_float("t8", P_FLAG_TVAR | P_FLAG_PER_POINT, &this->t8, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
if ((param = Param::new_param_float("t8", P_FLAG_TVAR, &this->t8, NULL, MAX_DOUBLE_SIZE, -MAX_DOUBLE_SIZE, 0.0)) == NULL)
{
;
abort();
@ -471,10 +472,10 @@ int CustomWave::add_per_point_eqn(char * name, Expr * gen_expr)
index = per_point_eqn_tree.size();
/* Create the per point equation given the index, parameter, and general expression */
if ((per_point_eqn = new PerPointEqn(index, param, gen_expr, samples)) == NULL)
if ((per_point_eqn = new PerPointEqn(index, param, gen_expr)) == NULL)
return PROJECTM_FAILURE;
if (CUSTOM_WAVE_DEBUG)
printf("add_per_point_eqn: created new equation (index = %d) (name = \"%s\")\n", per_point_eqn->index, per_point_eqn->param->name.c_str());
printf("add_per_point_eqn: created new equation (index = %d) (name = \"%s\")\n", per_point_eqn->index, param->name.c_str());
/* Insert the per pixel equation into the preset per pixel database */
@ -498,18 +499,28 @@ void CustomWave::evalInitConds()
ColoredPoint CustomWave::PerPoint(ColoredPoint p, const WaveformContext context)
{
r_mesh[context.sample_int] = r;
g_mesh[context.sample_int] = g;
b_mesh[context.sample_int] = b;
a_mesh[context.sample_int] = a;
x_mesh[context.sample_int] = x;
y_mesh[context.sample_int] = y;
sample = context.sample;
v1 = context.left;
v2 = context.right;
if (nullptr == per_point_program)
{
// see comment in MilkdropPreset, collect a list of assignments into one ProgramExpr
// which (theoretically) could be compiled together.
std::vector<Expr *> steps;
for (auto pos = per_point_eqn_tree.begin(); pos != per_point_eqn_tree.end();++pos)
steps.push_back((*pos)->assign_expr);
per_point_program = new ProgramExpr(steps, false);
}
for (std::vector<PerPointEqn*>::iterator pos = per_point_eqn_tree.begin(); pos != per_point_eqn_tree.end();++pos)
(*pos)->evaluate(context.sample_int);
r_mesh[context.sample_int] = r;
g_mesh[context.sample_int] = g;
b_mesh[context.sample_int] = b;
a_mesh[context.sample_int] = a;
x_mesh[context.sample_int] = x;
y_mesh[context.sample_int] = y;
sample = context.sample;
v1 = context.left;
v2 = context.right;
per_point_program->eval(context.sample_int, -1);
p.a = a_mesh[context.sample_int];
p.r = r_mesh[context.sample_int];

View File

@ -105,6 +105,7 @@ public:
std::map<std::string,InitCond*> init_cond_tree;
std::vector<PerFrameEqn*> per_frame_eqn_tree;
std::vector<PerPointEqn*> per_point_eqn_tree;
Expr *per_point_program;
std::map<std::string,InitCond*> per_frame_init_eqn_tree;
/* Denotes the index of the last character for each string buffer */

View File

@ -47,18 +47,20 @@ InfixOp *Eval::infix_positive = NULL;
/* Initializes all infix operators */
int Eval::init_infix_ops() {
Eval::infix_add = new InfixOp(INFIX_ADD, 4);
Eval::infix_minus = new InfixOp(INFIX_MINUS, 3);
Eval::infix_div = new InfixOp(INFIX_DIV, 2);
Eval::infix_or = new InfixOp(INFIX_OR, 5);
Eval::infix_and = new InfixOp(INFIX_AND,4);
Eval::infix_mod = new InfixOp(INFIX_MOD, 1);
Eval::infix_mult = new InfixOp(INFIX_MULT, 2);
/* Prefix operators */
Eval::infix_positive = new InfixOp(INFIX_ADD, 0);
Eval::infix_negative = new InfixOp(INFIX_MINUS, 0);
if (nullptr == Eval::infix_add)
{
Eval::infix_add = new InfixOp(INFIX_ADD, 4);
Eval::infix_minus = new InfixOp(INFIX_MINUS, 3);
Eval::infix_div = new InfixOp(INFIX_DIV, 2);
Eval::infix_or = new InfixOp(INFIX_OR, 5);
Eval::infix_and = new InfixOp(INFIX_AND, 4);
Eval::infix_mod = new InfixOp(INFIX_MOD, 1);
Eval::infix_mult = new InfixOp(INFIX_MULT, 2);
/* Prefix operators */
Eval::infix_positive = new InfixOp(INFIX_ADD, 0);
Eval::infix_negative = new InfixOp(INFIX_MINUS, 0);
}
return PROJECTM_SUCCESS;
}

View File

@ -26,6 +26,7 @@
#include <iostream>
#include "Eval.hpp"
#include "BuiltinFuncs.hpp"
/* Evaluates functions in prefix form */
@ -77,6 +78,42 @@ class PrefunExprOne : public PrefunExpr
}
};
class SinExpr : public PrefunExpr
{
float eval ( int mesh_i, int mesh_j ) override
{
float val = expr_list[0]->eval ( mesh_i, mesh_j );
return sinf(val);
}
};
class CosExpr : public PrefunExpr
{
float eval ( int mesh_i, int mesh_j ) override
{
float val = expr_list[0]->eval ( mesh_i, mesh_j );
return cosf(val);
}
};
class LogExpr : public PrefunExpr
{
float eval ( int mesh_i, int mesh_j ) override
{
float val = expr_list[0]->eval ( mesh_i, mesh_j );
return logf(val);
}
};
class PowExpr : public PrefunExpr
{
float eval ( int mesh_i, int mesh_j ) override
{
float x = expr_list[0]->eval ( mesh_i, mesh_j );
float y = expr_list[1]->eval ( mesh_i, mesh_j );
return powf(x, y);
}
};
class ConstantExpr : public Expr
{
@ -98,79 +135,6 @@ public:
}
};
class ParameterExpr : public Expr
{
protected:
Term term;
public:
ParameterExpr( int _type, Term *_term ) : Expr(PARAMETER), term(*_term) {}
float eval(int mesh_i, int mesh_j ) = 0;
std::ostream& to_string(std::ostream& out)
{
out << term.param->name;
return out;
}
};
class BoolParameterExpr : public ParameterExpr
{
public:
BoolParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_BOOL );
return ( float ) ( * ( ( bool* ) ( term.param->engine_val ) ) );
}
};
class IntParameterExpr : public ParameterExpr
{
public:
IntParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_INT );
return ( float ) ( * ( ( int* ) ( term.param->engine_val ) ) );
}
};
class FloatParameterExpr : public ParameterExpr
{
public:
FloatParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j );
};
/* TODO optimize FloatParameterExpr::eval() further.
// - flag "never matrix" parameters
// - always pass in 2d matrix. instead of using (i, -1) for 1d matrix, we could just use (0, i) and avoid check
// - instead of using matrix_flag to give "copy on write" behavior, maybe pre-copy from engine_val to matrix[]
*/
float FloatParameterExpr::eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_DOUBLE );
if ( term.param->matrix_flag | ( term.param->flags & P_FLAG_ALWAYS_MATRIX ) )
{
/* Sanity check the matrix is there... */
assert ( term.param->matrix != NULL );
/// @slow boolean check could be expensive in this critical (and common) step of evaluation
if ( mesh_i >= 0 )
{
if ( mesh_j >= 0 )
{
return ( ( ( float** ) term.param->matrix ) [mesh_i][mesh_j] );
}
else
{
// issue 64, make sure we're not reading a P_FLAG_PER_PIXEL matrix variable
if (!(term.param->flags & P_FLAG_PER_PIXEL))
return ( ( ( float* ) term.param->matrix ) [mesh_i] );
}
}
//assert(mesh_i >=0);
}
//std::cout << term.param->name << ": " << (*((float*)term.param->engine_val)) << std::endl;
return * ( ( float* ) ( term.param->engine_val ) );
}
class MultAndAddExpr : public Expr
{
@ -180,25 +144,50 @@ public:
a(_a), b(_b), c(_c)
{
}
~MultAndAddExpr() {
delete a;
delete b;
delete c;
~MultAndAddExpr() override {
Expr::delete_expr(a);
Expr::delete_expr(b);
Expr::delete_expr(c);
}
float eval(int mesh_i, int mesh_j)
float eval(int mesh_i, int mesh_j) override
{
float a_value = a->eval(mesh_i,mesh_j);
float b_value = b->eval(mesh_i,mesh_j);
float c_value = c->eval(mesh_i,mesh_j);
return a_value * b_value + c_value;
}
std::ostream &to_string(std::ostream &out)
std::ostream &to_string(std::ostream &out) override
{
out << "(" << a << " * " << b << ") + " << c;
return out;
}
};
class MultConstExpr : public Expr
{
Expr *expr;
float c;
public:
MultConstExpr(Expr *_expr, float _c) : Expr(OTHER),
expr(_expr), c(_c)
{
}
~MultConstExpr() override
{
Expr::delete_expr(expr);
}
float eval(int mesh_i, int mesh_j) override
{
float value = expr->eval(mesh_i,mesh_j);
return value * c;
}
std::ostream &to_string(std::ostream &out) override
{
out << "(" << expr << " * " << c << ") + " << c;
return out;
}
};
std::ostream &TreeExpr::to_string(std::ostream &out)
{
if (NULL == infix_op)
@ -232,31 +221,31 @@ std::ostream &TreeExpr::to_string(std::ostream &out)
return out;
}
/* NOTE: Parser.cpp directly manipulates TreeExpr, so it is easier to optimizer AFTER parsing
/* NOTE: Parser.cpp directly manipulates TreeExpr, so it is easier to _optimizer AFTER parsing
* than while building up the tree initially
*/
Expr *TreeExpr::optimize()
Expr *TreeExpr::_optimize()
{
if (infix_op == NULL)
{
Expr *opt = gen_expr->optimize();
Expr *opt = gen_expr->_optimize();
if (opt != gen_expr)
delete gen_expr;
Expr::delete_expr(gen_expr);
gen_expr = NULL;
return opt;
}
if (left != NULL)
{
Expr *l = left->optimize();
Expr *l = left->_optimize();
if (l != left)
delete left;
Expr::delete_expr(left);
left = l;
}
if (right != NULL)
{
Expr *r = right->optimize();
Expr *r = right->_optimize();
if (r != right)
delete right;
Expr::delete_expr(right);
right = r;
}
if (left == NULL)
@ -281,25 +270,26 @@ Expr *TreeExpr::optimize()
{
Expr *a, *b, *c;
if (left->clazz == TREE && ((TreeExpr *)left)->infix_op->type == INFIX_MULT)
{
a = ((TreeExpr *)left)->left;
b = ((TreeExpr *)left)->right;
c = right;
((TreeExpr *)left)->left = NULL;
((TreeExpr *)left)->right = NULL;
right = NULL;
}
else
{
a = ((TreeExpr *)right)->left;
b = ((TreeExpr *)right)->right;
c = left;
((TreeExpr *)right)->left = NULL;
((TreeExpr *)right)->right = NULL;
left = NULL;
}
std::swap(left,right);
a = ((TreeExpr *)right)->left;
b = ((TreeExpr *)right)->right;
c = left;
((TreeExpr *)right)->left = NULL;
((TreeExpr *)right)->right = NULL;
left = NULL;
return new MultAndAddExpr(a,b,c);
}
if (infix_op->type == INFIX_MULT && (left->isConstant() || right->isConstant()))
{
if (right->isConstant())
std::swap(left, right);
float c = left->eval(-1,-1);
Expr *expr = right;
right = left = nullptr;
return new MultConstExpr(expr, c);
}
return this;
}
@ -308,7 +298,7 @@ float TreeExpr::eval ( int mesh_i, int mesh_j )
{
float left_arg, right_arg;
/* shouldn't be null if we've called optimize() */
/* shouldn't be null if we've called _optimize() */
assert(NULL != infix_op);
left_arg = left->eval ( mesh_i, mesh_j );
@ -356,46 +346,52 @@ Expr * Expr::const_to_expr ( float val )
/* Converts a regular parameter to an expression */
Expr * Expr::param_to_expr ( Param * param )
{
Term term;
if ( param == NULL )
return NULL;
/* This code is still a work in progress. We need
to figure out if the initial condition is used for
each per frame equation or not. I am guessing that
it isn't, and it is thusly implemented this way */
/* Current guess of true behavior (08/01/03) note from carm
First try to use the per_pixel_expr (with cloning)
If it is null however, use the engine variable instead. */
/* 08/20/03 : Presets are now objects, as well as per pixel equations. This ends up
making the parser handle the case where parameters are essentially per pixel equation
substitutions */
term.param = param;
switch ( param->type )
{
case P_TYPE_BOOL:
return new BoolParameterExpr( PARAM_TERM_T, &term );
return param->getExpr();
//return new BoolParameterExpr( PARAM_TERM_T, &term );
case P_TYPE_INT:
return new IntParameterExpr( PARAM_TERM_T, &term );
return param->getExpr();
//return new IntParameterExpr( PARAM_TERM_T, &term );
case P_TYPE_DOUBLE:
return new FloatParameterExpr( PARAM_TERM_T, &term );
return param->getExpr();
//return new FloatParameterExpr( PARAM_TERM_T, &term );
default:
return NULL;
}
return NULL;
}
/* Converts a prefix function to an expression */
Expr * Expr::prefun_to_expr ( float ( *func_ptr ) ( void * ), Expr ** expr_list, int num_args )
{
PrefunExpr * prefun_expr;
if (num_args == 1)
prefun_expr = new PrefunExprOne();
else
prefun_expr = new PrefunExpr();
PrefunExpr *prefun_expr;
if (num_args == 1)
{
if (func_ptr == (float (*)(void *)) FuncWrappers::sin_wrapper)
prefun_expr = new SinExpr();
else if (func_ptr == (float (*)(void *)) FuncWrappers::cos_wrapper)
prefun_expr = new CosExpr();
else if (func_ptr == (float (*)(void *)) FuncWrappers::log_wrapper)
prefun_expr = new LogExpr();
else
prefun_expr = new PrefunExprOne();
}
else if (num_args == 2)
{
if (func_ptr == (float (*)(void *)) FuncWrappers::pow_wrapper)
prefun_expr = new PowExpr();
else
prefun_expr = new PrefunExpr();
}
else
{
prefun_expr = new PrefunExpr();
}
prefun_expr->num_args = num_args;
prefun_expr->func_ptr = ( float ( * ) ( void* ) ) func_ptr;
prefun_expr->expr_list = expr_list;
@ -463,7 +459,7 @@ PrefunExpr::~PrefunExpr()
/* Free every element in expression list */
for ( i = 0 ; i < num_args; i++ )
{
delete expr_list[i];
Expr::delete_expr(expr_list[i]);
}
free ( expr_list );
}
@ -475,13 +471,13 @@ TreeExpr::~TreeExpr()
/* free left tree */
if ( left != NULL )
{
delete left;
Expr::delete_expr(left);
}
/* free general expression object */
if ( gen_expr != NULL )
{
delete gen_expr;
Expr::delete_expr(gen_expr);
}
/* Note that infix operators are always
@ -491,7 +487,7 @@ TreeExpr::~TreeExpr()
/* free right tree */
if ( right != NULL )
{
delete right;
Expr::delete_expr(right);
}
}
@ -506,18 +502,27 @@ PrefunExpr::PrefunExpr() : Expr(FUNCTION)
{
}
Expr *PrefunExpr::optimize()
bool isConstantFn(float (* fn)(void*))
{
return (float (*)(float *))fn != FuncWrappers::print_wrapper &&
(float (*)(float *))fn != FuncWrappers::rand_wrapper;
}
Expr *PrefunExpr::_optimize()
{
bool constant_args = true;
for (int i=0 ; i < num_args ; i++)
{
Expr *orig = expr_list[i];
expr_list[i] = orig->optimize();
expr_list[i] = orig->_optimize();
if (orig != expr_list[i])
delete orig;
constant_args &= expr_list[i]->isConstant();
Expr::delete_expr(orig);
constant_args = constant_args && expr_list[i]->isConstant();
}
// TODO most functions can be pre-evaluated if inputs are constant, but not all
if (constant_args && isConstantFn(func_ptr))
{
return Expr::const_to_expr(eval(-1, -1));
}
return this;
}
@ -534,3 +539,127 @@ std::ostream& PrefunExpr::to_string(std::ostream& out)
out << ")";
return out;
}
AssignExpr::AssignExpr(LValue *lhs_, Expr *rhs_) : Expr(ASSIGN), lhs(lhs_), rhs(rhs_) {}
AssignExpr::~AssignExpr()
{
Expr::delete_expr(lhs);
Expr::delete_expr(rhs);
}
Expr * AssignExpr::_optimize()
{
Expr *t = rhs->_optimize();
if (t != rhs)
Expr::delete_expr(rhs);
rhs = t;
return this;
}
float AssignExpr::eval(int mesh_i, int mesh_j)
{
float v = rhs->eval( mesh_i, mesh_j );
lhs->set( v );
return v;
}
std::ostream& AssignExpr::to_string(std::ostream &out)
{
out << lhs << " = " << rhs;
return out;
}
AssignMatrixExpr::AssignMatrixExpr(LValue *lhs_, Expr *rhs_) : AssignExpr(lhs_, rhs_) {}
float AssignMatrixExpr::eval(int mesh_i, int mesh_j)
{
float v = rhs->eval( mesh_i, mesh_j );
lhs->set_matrix( mesh_i, mesh_j, v );
return v;
}
std::ostream& AssignMatrixExpr::to_string(std::ostream &out)
{
out << lhs << "[i,j] = " << rhs;
return out;
}
// TESTS
#include <TestRunner.hpp>
#ifndef NDEBUG
#define TEST(cond) if (!verify(#cond,cond)) return false
struct ExprTest : public Test
{
ExprTest() : Test("ExprTest")
{}
public:
bool optimize_constant_expr()
{
TreeExpr *a = TreeExpr::create(nullptr, Expr::const_to_expr( 1.0 ), nullptr, nullptr);
TreeExpr *b = TreeExpr::create(nullptr, Expr::const_to_expr( 2.0 ), nullptr, nullptr);
Expr *c = TreeExpr::create(Eval::infix_add, nullptr, a, b);
//TEST(3.0f == c->eval(-1,-1));
Expr *x = Expr::optimize(c);
TEST(x != c);
Expr::delete_expr(c);
TEST(x->clazz == CONSTANT);
TEST(3.0f == x->eval(-1,-1));
Expr::delete_expr(x);
Expr **expr_array = (Expr **)malloc(sizeof(Expr *));
expr_array[0] = TreeExpr::create(nullptr, Expr::const_to_expr( (float)M_PI ), nullptr, nullptr);
Expr *sin = Expr::prefun_to_expr((float (*)(void *))FuncWrappers::sin_wrapper, expr_array, 1);
x = Expr::optimize(sin);
TEST(x != sin);
Expr::delete_expr( sin );
TEST(x->clazz == CONSTANT);
TEST(sinf( (float)M_PI ) == x->eval(-1,-10));
Expr::delete_expr(x);
// make sure rand() is not optimized away
expr_array = (Expr **)malloc(sizeof(Expr *));
expr_array[0] = TreeExpr::create(nullptr, Expr::const_to_expr( (float)M_PI ), nullptr, nullptr);
Expr *rand = Expr::prefun_to_expr((float (*)(void *))FuncWrappers::rand_wrapper, expr_array, 1);
x = Expr::optimize(rand);
TEST(x == rand);
TEST(x->clazz != CONSTANT);
Expr::delete_expr(x);
return true;
}
bool test() override
{
Eval::init_infix_ops();
bool result = true;
result &= optimize_constant_expr();
return result;
}
};
Test* Expr::test()
{
return new ExprTest();
}
#else
Test* Expr::test()
{
return null;
}
#endif

View File

@ -31,8 +31,10 @@
#include "dlldefs.h"
#include "CValue.hpp"
#include <iostream>
#include <iostream>
#include <vector>
class Test;
class Param;
#define CONST_STACK_ELEMENT 0
@ -63,7 +65,7 @@ public:
enum ExprClass
{
TREE, CONSTANT, PARAMETER, FUNCTION, OTHER
TREE, CONSTANT, PARAMETER, FUNCTION, ASSIGN, PROGRAM, OTHER
};
class Expr
@ -72,7 +74,7 @@ public:
ExprClass clazz;
Expr(ExprClass c) : clazz(c) {};
virtual ~Expr() {};
virtual Expr *optimize() { return this; };
virtual bool isConstant() { return false; };
virtual float eval(int mesh_i, int mesh_j) = 0;
virtual std::ostream& to_string(std::ostream &out)
@ -80,9 +82,23 @@ public:
std::cout << "nyi"; return out;
}
static Test *test();
static Expr *const_to_expr( float val );
static Expr *param_to_expr( Param *param );
static Expr *prefun_to_expr( float (*func_ptr)(void *), Expr **expr_list, int num_args );
static void delete_expr(Expr *expr) { if (nullptr != expr) expr->_delete_from_tree(); }
static Expr *optimize(Expr *root) { return root->_optimize(); };
public: // but don't call these from outside Expr.cpp
virtual Expr *_optimize() { return this; };
// override if this expr is not 'owned' by the containg expression tree
virtual void _delete_from_tree()
{
delete this;
}
};
inline std::ostream& operator<<(std::ostream& out, Expr *expr)
@ -111,11 +127,11 @@ public:
TreeExpr *leftTree() { return dynamic_cast<TreeExpr *>(left); }
TreeExpr *rightTree() { return dynamic_cast<TreeExpr *>(right); }
~TreeExpr();
~TreeExpr() override;
Expr *optimize();
float eval(int mesh_i, int mesh_j);
std::ostream& to_string(std::ostream &out);
Expr *_optimize() override;
float eval(int mesh_i, int mesh_j) override;
std::ostream& to_string(std::ostream &out) override;
};
/* A function expression in prefix form */
@ -126,12 +142,69 @@ public:
int num_args;
Expr **expr_list;
PrefunExpr();
~PrefunExpr();
~PrefunExpr() override;
/* Evaluates functions in prefix form */
Expr *optimize();
float eval(int mesh_i, int mesh_j);
std::ostream& to_string(std::ostream &out);
Expr *_optimize() override;
float eval(int mesh_i, int mesh_j) override;
std::ostream& to_string(std::ostream &out) override;
};
class LValue : public Expr
{
public:
explicit LValue(ExprClass c) : Expr(c) {};
virtual void set(float value) = 0;
virtual void set_matrix(int mesh_i, int mesh_j, float value) = 0;
};
class AssignExpr : public Expr
{
protected:
LValue *lhs;
Expr *rhs;
public:
AssignExpr(LValue *lhs, Expr *rhs);
~AssignExpr() override;
Expr *_optimize() override;
float eval(int mesh_i, int mesh_j) override;
std::ostream& to_string(std::ostream &out) override;
};
class AssignMatrixExpr : public AssignExpr
{
public:
AssignMatrixExpr(LValue *lhs, Expr *rhs);
float eval(int mesh_i, int mesh_j) override;
std::ostream& to_string(std::ostream &out) override;
};
class ProgramExpr : public Expr
{
protected:
std::vector<Expr *> steps;
bool own;
public:
ProgramExpr(std::vector<Expr*> &steps_, bool ownSteps) : Expr(PROGRAM), steps(steps_), own(ownSteps)
{
}
~ProgramExpr()
{
if (!own)
return;
for (auto it=steps.begin() ; it<steps.end() ; it++)
Expr::delete_expr(*it);
}
float eval(int mesh_i, int mesh_j) override
{
float f=0.0f;
for (auto it=steps.begin() ; it<steps.end() ; it++)
f = (*it)->eval(mesh_i,mesh_j);
return f;
}
};
#endif /** _EXPR_H */

View File

@ -40,13 +40,9 @@ char InitCond::init_cond_string_buffer[STRING_BUFFER_SIZE];
int InitCond::init_cond_string_buffer_index = 0;
/* Creates a new initial condition */
InitCond::InitCond( Param * _param, CValue _init_val ):param(_param), init_val(_init_val) {
// std::cerr << "InitCond::InitCond: " << this->param->name << std::endl;
InitCond::InitCond( Param * _param, CValue _init_val ):param(_param), init_val(_init_val)
{
assert(param);
assert(param->engine_val);
}
/* Frees initial condition structure */
@ -58,59 +54,23 @@ void InitCond::evaluate()
}
/* Evaluate an initial conditon */
void InitCond::evaluate(bool evalUser) {
void InitCond::evaluate(bool evalUser)
{
assert(this);
assert(param);
if (param->flags & P_FLAG_USERDEF && !evalUser)
return;
assert(this);
assert(param);
if (param->flags & P_FLAG_USERDEF && !evalUser)
return;
/* Set matrix flag to zero. This ensures
its constant value will be used rather than a matrix value
*/
param->matrix_flag = false;
/* Parameter is of boolean type, either true/false */
if (param->type == P_TYPE_BOOL) {
// printf( "init_cond: %s = %d (TYPE BOOL)\n", param->name.c_str(), init_val.bool_val);
//std::cerr << "[InitCond] param is a boolean of with name "
// << param->name << std::endl;
assert(param->engine_val);
*((bool*)param->engine_val) = init_val.bool_val;
return;
}
/* Parameter is an integer type, just like C */
if ( param->type == P_TYPE_INT) {
assert(param->engine_val);
*((int*)param->engine_val) = init_val.int_val;
return;
}
/* Parameter is of a float type, just like C */
if (param->type == P_TYPE_DOUBLE) {
assert(param->engine_val);
*((float*)param->engine_val) = init_val.float_val;
return;
}
/* Unknown type of parameter */
return;
/* Has side-effect of also setting matrix flag to zero. This ensures
its constant value will be used rather than a matrix value
*/
param->set_param(init_val);
}
/* WIP */
void InitCond::init_cond_to_string() {
void InitCond::init_cond_to_string()
{
int string_length;
char string[MAX_TOKEN_SIZE];

View File

@ -25,10 +25,6 @@ inline void LoadUnspecInitCond::operator() (Param * param) {
InitCond * init_cond = 0;
CValue init_val;
assert(param);
assert(param->engine_val);
/* Don't count these parameters as initial conditions */
if (param->flags & P_FLAG_READONLY)
return;
@ -48,15 +44,7 @@ inline void LoadUnspecInitCond::operator() (Param * param) {
if (m_perFrameInitEqnTree.find(param->name) != m_perFrameInitEqnTree.end())
return;
// Set an initial vialue via correct union member
if (param->type == P_TYPE_BOOL)
init_val.bool_val = param->default_init_val.bool_val;
else if (param->type == P_TYPE_INT)
init_val.int_val = param->default_init_val.int_val;
else if (param->type == P_TYPE_DOUBLE) {
init_val.float_val = param->default_init_val.float_val;
}
init_val = param->default_init_val;
//printf("%s\n", param->name);
/* Create new initial condition */

View File

@ -44,10 +44,15 @@
#include "PresetFactoryManager.hpp"
#include "MilkdropPresetFactory.hpp"
#ifdef __SSE2__
#include <immintrin.h>
#endif
MilkdropPreset::MilkdropPreset(MilkdropPresetFactory *factory, std::istream & in, const std::string & presetName, PresetOutputs & presetOutputs):
Preset(presetName),
builtinParams(_presetInputs, presetOutputs),
per_pixel_program(nullptr),
_factory(factory),
_presetOutputs(presetOutputs)
{
@ -58,6 +63,7 @@ MilkdropPreset::MilkdropPreset(MilkdropPresetFactory *factory, std::istream & in
MilkdropPreset::MilkdropPreset(MilkdropPresetFactory *factory, const std::string & absoluteFilePath, const std::string & presetName, PresetOutputs & presetOutputs):
Preset(presetName),
builtinParams(_presetInputs, presetOutputs),
per_pixel_program(nullptr),
_filename(parseFilename(absoluteFilePath)),
_absoluteFilePath(absoluteFilePath),
_factory(factory),
@ -76,6 +82,7 @@ MilkdropPreset::~MilkdropPreset()
traverse<TraverseFunctors::Delete<InitCond> >(per_frame_init_eqn_tree);
traverse<TraverseFunctors::Delete<PerPixelEqn> >(per_pixel_eqn_tree);
Expr::delete_expr(per_pixel_program);
traverseVector<TraverseFunctors::Delete<PerFrameEqn> >(per_frame_eqn_tree);
@ -387,94 +394,61 @@ void MilkdropPreset::evaluateFrame()
}
#ifdef __SSE2__
inline void init_mesh(float **mesh, const float value, const int gx, const int gy)
{
__m128 mvalue = _mm_set_ps1(value);
for (int x = 0; x < gx; x++)
for (int y = 0; y < gy; y += 4)
_mm_store_ps(&mesh[x][y], mvalue);
}
#else
inline void init_mesh(float **mesh, const float value, const int gx, const int gy)
{
for (x=0;x<gx;x++)
for(y=0;gy;y++)
mesh[x,y] = value;
}
#endif
void MilkdropPreset::initialize_PerPixelMeshes()
{
int gx = presetInputs().gx;
int gy = presetInputs().gy;
int x,y;
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.cx_mesh[x][y]=presetOutputs().cx;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.cy_mesh[x][y]=presetOutputs().cy;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.sx_mesh[x][y]=presetOutputs().sx;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.sy_mesh[x][y]=presetOutputs().sy;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.dx_mesh[x][y]=presetOutputs().dx;
}}
//std::cout<<presetOutputs().cx<<","<<presetOutputs().cy<<" "<<presetOutputs().dx<<","<<presetOutputs().dy<<std::endl;
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.dy_mesh[x][y]=presetOutputs().dy;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.zoom_mesh[x][y]=presetOutputs().zoom;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.zoomexp_mesh[x][y]=presetOutputs().zoomexp;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.rot_mesh[x][y]=presetOutputs().rot;
}}
for (x=0;x<presetInputs().gx;x++){
for(y=0;y<presetInputs().gy;y++){
_presetOutputs.warp_mesh[x][y]=presetOutputs().warp;
}}
init_mesh(_presetOutputs.cx_mesh, presetOutputs().cx, gx, gy);
init_mesh(_presetOutputs.cy_mesh, presetOutputs().cy, gx, gy);
init_mesh(_presetOutputs.sx_mesh, presetOutputs().sx, gx, gy);
init_mesh(_presetOutputs.sy_mesh, presetOutputs().sy, gx, gy);
init_mesh(_presetOutputs.dx_mesh, presetOutputs().dx, gx, gy);
init_mesh(_presetOutputs.dy_mesh, presetOutputs().dy, gx, gy);
init_mesh(_presetOutputs.zoom_mesh, presetOutputs().zoom, gx, gy);
init_mesh(_presetOutputs.zoomexp_mesh, presetOutputs().zoomexp, gx, gy);
init_mesh(_presetOutputs.rot_mesh, presetOutputs().rot, gx, gy);
init_mesh(_presetOutputs.warp_mesh, presetOutputs().warp, gx, gy);
}
// Evaluates all per-pixel equations
void MilkdropPreset::evalPerPixelEqns()
{
if (nullptr == per_pixel_program)
{
// This is a little forward looking, but if we want to JIT assignments expressions, we might
// as well JIT the batch all together rather than one at a time. At the moment ProgramExpr is
// just a different place to loop over the individual steps, but the idea is that this encapsulates
// an optimizable chunk of work.
// See also CustomWave which does the same for PerPointEqn
std::vector<Expr *> steps;
for (std::map<int, PerPixelEqn*>::iterator pos = per_pixel_eqn_tree.begin(); pos != per_pixel_eqn_tree.end(); ++pos)
steps.push_back(pos->second->assign_expr);
per_pixel_program = new ProgramExpr(steps,false);
}
/* Evaluate all per pixel equations in the tree datastructure */
for (int mesh_x = 0; mesh_x < presetInputs().gx; mesh_x++)
for (int mesh_y = 0; mesh_y < presetInputs().gy; mesh_y++)
for (std::map<int, PerPixelEqn*>::iterator pos = per_pixel_eqn_tree.begin();
pos != per_pixel_eqn_tree.end(); ++pos)
pos->second->evaluate(mesh_x, mesh_y);
for (int mesh_x = 0; mesh_x < presetInputs().gx; mesh_x++)
for (int mesh_y = 0; mesh_y < presetInputs().gy; mesh_y++)
per_pixel_program->eval( mesh_x, mesh_y );
}
int MilkdropPreset::readIn(std::istream & fs) {

View File

@ -130,6 +130,7 @@ public:
/* Data structures that contain equation and initial condition information */
std::vector<PerFrameEqn*> per_frame_eqn_tree; /* per frame equations */
std::map<int, PerPixelEqn*> per_pixel_eqn_tree; /* per pixel equation tree */
Expr *per_pixel_program;
std::map<std::string,InitCond*> per_frame_init_eqn_tree; /* per frame initial equations */
std::map<std::string,InitCond*> init_cond_tree; /* initial conditions */
std::map<std::string,Param*> user_param_tree; /* user parameter splay tree */

View File

@ -40,9 +40,10 @@
#include <cassert>
/** Constructor */
Param::Param( std::string _name, short int _type, short int _flags, void * _engine_val, void * _matrix,
Param::Param( const std::string &_name, short int _type, short int _flags, void * _engine_val, void * _matrix,
CValue _default_init_val, CValue _upper_bound, CValue _lower_bound):
name(_name),
LValue(PARAMETER),
name(_name),
type(_type),
flags (_flags),
matrix_flag (0),
@ -52,24 +53,22 @@ Param::Param( std::string _name, short int _type, short int _flags, void * _engi
upper_bound (_upper_bound),
lower_bound (_lower_bound),
local_value(0.0)
{
{
}
/* Creates a user defined parameter */
Param::Param(std::string _name) :
name(_name),
Param::Param(const std::string &_name) : LValue(PARAMETER),
name(_name),
type(P_TYPE_DOUBLE),
flags(P_FLAG_USERDEF),
flags(P_FLAG_USERDEF),
matrix_flag(0),
matrix(0),
local_value(0.0)
{
local_value(0.0)
{
engine_val = (float *)&local_value;
engine_val = (float *)&local_value;
default_init_val.float_val = DEFAULT_DOUBLE_IV;
default_init_val.float_val = DEFAULT_DOUBLE_IV;
upper_bound.float_val = DEFAULT_DOUBLE_UB;
lower_bound.float_val = DEFAULT_DOUBLE_LB;
@ -123,7 +122,7 @@ Param * Param::new_param_float(const char * name, short int flags, void * engine
ub.float_val = upper_bound;
lb.float_val = lower_bound;
if ((param = new Param(name, P_TYPE_DOUBLE, flags, engine_val, matrix,iv, ub, lb)) == NULL)
if ((param = Param::create(name, P_TYPE_DOUBLE, flags, engine_val, matrix,iv, ub, lb)) == NULL)
return NULL;
@ -132,7 +131,7 @@ Param * Param::new_param_float(const char * name, short int flags, void * engine
}
/* Creates a new parameter of type int */
Param * Param::new_param_int(const char * name, short int flags, void * engine_val,
Param * Param::new_param_int(const char * name, short int flags, void * engine_val,
int upper_bound, int lower_bound, int init_val) {
Param * param;
@ -143,7 +142,7 @@ Param * Param::new_param_int(const char * name, short int flags, void * engine_v
ub.int_val = upper_bound;
lb.int_val = lower_bound;
if ((param = new Param(name, P_TYPE_INT, flags, engine_val, NULL, iv, ub, lb)) == NULL)
if ((param = Param::create(name, P_TYPE_INT, flags, engine_val, NULL, iv, ub, lb)) == NULL)
return NULL;
@ -163,7 +162,7 @@ Param * Param::new_param_bool(const char * name, short int flags, void * engine_
ub.bool_val = upper_bound;
lb.bool_val = lower_bound;
if ((param = new Param(name, P_TYPE_BOOL, flags, engine_val, NULL, iv, ub, lb)) == NULL)
if ((param = Param::create(name, P_TYPE_BOOL, flags, engine_val, NULL, iv, ub, lb)) == NULL)
return NULL;
@ -182,7 +181,7 @@ Param * Param::new_param_string(const char * name, short int flags, void * engin
ub.bool_val = 0;
lb.bool_val = 0;
if ((param = new Param(name, P_TYPE_STRING, flags, engine_val, NULL, iv, ub, lb)) == NULL)
if ((param = Param::create(name, P_TYPE_STRING, flags, engine_val, NULL, iv, ub, lb)) == NULL)
return NULL;
@ -191,3 +190,267 @@ Param * Param::new_param_string(const char * name, short int flags, void * engin
}
/*
* I made Param extend Expr just to avoid inheritence of multiple virtual classes,
* however, this is really the subclass that knows how to interact with Expr
*/
struct _Param : public Param
{
_Param( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_)
{
if (flags & P_FLAG_ALWAYS_MATRIX)
matrix_flag = true;
}
explicit _Param( const std::string &name_) : Param(name_) {}
LValue *getExpr() override
{
return (LValue *)this;
}
void _delete_from_tree() override
{
/* do nothing, as the param isn't owned by the expresion tree */
}
void set(float value) override
{
set_param(value);
}
void set_matrix(int mesh_i, int mesh_j, float value) override
{
set_param(value);
}
};
class _BoolParam : public _Param
{
public:
_BoolParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
float eval(int mesh_i, int mesh_j) override
{
return *(bool *)engine_val ? 1 : 0;
}
};
class _IntParam : public _Param
{
public:
_IntParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
float eval(int mesh_i, int mesh_j) override
{
return *(int *)engine_val;
}
};
class _StringParam : public _Param
{
public:
_StringParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
float eval(int mesh_i, int mesh_j) override
{
return 0;
}
};
class _FloatParam : public _Param
{
public:
_FloatParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_Param(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
explicit _FloatParam( const std::string &name_) : _Param(name_) {}
float eval(int mesh_i, int mesh_j) override
{
return *(float *)engine_val;
}
};
/*This isn't that useful yet. Maybe later
class _AlwaysMatrixParam : public _FloatParam
{
public:
_AlwaysMatrixParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_FloatParam(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_)
{
matrix_flag = true;
}
float eval(int mesh_i, int mesh_j) override
{
assert( mesh_i >=0 && mesh_j >= 0 );
return ((float **)matrix)[mesh_i][mesh_j];
}
void set_matrix(int mesh_i, int mesh_j, float value) override
{
assert( mesh_i >=0 && mesh_j >= 0);
// Yup, presets write to read-only ALWAYS_MATRIX parameters
// assert(!(flags & P_FLAG_READONLY));
((float **)matrix)[mesh_i][mesh_j] = value;
}
};*/
class _MeshParam : public _FloatParam
{
public:
_MeshParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_FloatParam(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
float eval(int mesh_i, int mesh_j) override
{
assert( type == P_TYPE_DOUBLE );
// see issue 64: There are presets with per_point equations that read from pre_frame/per_pixel parameters,
// e.g. per_point1=dx=dx*1.01
// any this means that we get called with (i>=0,j==-1)
if ( matrix_flag && mesh_i >= 0 && mesh_j >= 0)
return ( ( ( float** ) matrix ) [mesh_i][mesh_j] );
return * ( ( float* ) ( engine_val ) );
}
void set_matrix(int mesh_i, int mesh_j, float value) override
{
if (nullptr == matrix)
{
*(float *)engine_val = value;
}
else
{
((float **) matrix)[mesh_i][mesh_j] = value;
matrix_flag = true;
}
}
};
// TODO merge PointsParam/MeshParam
class _PointsParam : public _FloatParam
{
public:
_PointsParam( const std::string &name_, short int type_, short int flags_,
void * eqn_val_, void *matrix_,
CValue default_init_val_, CValue upper_bound_,
CValue lower_bound_) :
_FloatParam(name_, type_, flags_, eqn_val_, matrix_, default_init_val_, upper_bound_, lower_bound_) {}
float eval(int mesh_i, int mesh_j) override
{
assert( mesh_j == -1 );
assert( type == P_TYPE_DOUBLE );
if ( matrix_flag && mesh_i >= 0 )
return ( ( ( float* ) matrix ) [mesh_i] );
return * ( ( float* ) ( engine_val ) );
}
void set_matrix(int mesh_i, int mesh_j, float value) override
{
if (nullptr == matrix)
{
*(float *)engine_val = value;
}
else
{
((float *) matrix)[mesh_i] = value;
matrix_flag = true;
}
}
};
Param * Param::create( const std::string &name, short int type, short int flags,
void * eqn_val, void *matrix,
CValue default_init_val, CValue upper_bound,
CValue lower_bound)
{
if (type == P_TYPE_BOOL)
{
assert(nullptr == matrix);
assert(0 == (flags & (P_FLAG_PER_PIXEL|P_FLAG_PER_POINT)));
return new _BoolParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
if (type == P_TYPE_INT)
{
assert(nullptr == matrix);
assert(0 ==(flags & (P_FLAG_PER_PIXEL|P_FLAG_PER_POINT)));
return new _IntParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
if (type == P_TYPE_STRING)
{
assert(0 == (flags & (P_FLAG_PER_PIXEL|P_FLAG_PER_POINT)));
return new _StringParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
assert(type == P_TYPE_DOUBLE);
if (matrix == nullptr)
{
assert(0 == (flags & (P_FLAG_PER_PIXEL|P_FLAG_PER_POINT)));
return new _FloatParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
assert( flags & (P_FLAG_PER_PIXEL|P_FLAG_PER_POINT) );
if (flags & P_FLAG_PER_PIXEL)
{
return new _MeshParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
else
{
return new _PointsParam( name, type, flags, eqn_val, matrix, default_init_val, upper_bound, lower_bound );
}
}
Param * Param::createUser( const std::string &name )
{
return new _FloatParam( name );
}
// TESTS
#include <TestRunner.hpp>
#ifndef NDEBUG
struct ParamTest : public Test
{
ParamTest() : Test("ParamTest")
{}
public:
bool test() override
{
return true;
}
};
Test* Param::test()
{
return new ParamTest();
}
#else
Test* Param::test()
{
return null;
}
#endif

View File

@ -57,64 +57,86 @@
class InitCond;
class Param;
class Preset;
class Test;
#ifdef __SSE2__
#include <immintrin.h>
#endif
/* Parameter Type */
class Param {
class Param : public LValue
{
protected:
Param(const std::string &name, short int type, short int flags,
void * eqn_val, void *matrix,
CValue default_init_val, CValue upper_bound,
CValue lower_bound);
/// Create a user defined floating point parameter
explicit Param( const std::string &name );
public:
std::string name; /* name of the parameter, not necessary but useful neverthless */
short int type; /* parameter number type (int, bool, or float) */
short int flags; /* read, write, user defined, etc */
protected:
short int matrix_flag; /* for optimization purposes */
void * engine_val; /* pointer to the engine variable */
void * matrix; /* per pixel / per point matrix for this variable */
public:
CValue default_init_val; /* a default initial condition value */
protected:
CValue upper_bound; /* this parameter's upper bound */
CValue lower_bound; /* this parameter's lower bound */
// for a local variable, engine_val can point here
float local_value;
public:
/// Create a new parameter
Param(std::string name, short int type, short int flags,
static Param * create(const std::string &name, short int type, short int flags,
void * eqn_val, void *matrix,
CValue default_init_val, CValue upper_bound,
CValue lower_bound);
~Param();
static Param * createUser(const std::string &name);
/// Create a user defined floating point parameter
Param( std::string name );
static Test *test();
virtual ~Param();
static bool is_valid_param_string( const char *string );
void set_param( float val );
void set_param( CValue val );
void set_param( std::string &text) { *((std::string*)engine_val) = text; }
static Param *new_param_float( const char *name, short int flags, void *engine_val,
void *matrix, float upper_bound,
float lower_bound,
float init_val );
static Param *new_param_double(const char *name, short int flags, void *engine_val,
void *matrix, double upper_bound,
double lower_bound,
double init_val );
static Param * new_param_int(const char * name, short int flags, void * engine_val,
int upper_bound, int lower_bound, int init_val );
static Param * new_param_bool(const char * name, short int flags, void * engine_val,
bool upper_bound, bool lower_bound, bool init_val );
static Param * new_param_string(const char * name, short int flags, void * engine_val);
// return an Expr to inject directly into an Eqn
// this allows the parameter to stay encapsulated, but not add extra levels of virtual functions
// into the evaluation process
virtual LValue *getExpr() = 0;
};
/* Sets the parameter engine value to value val.
clipping occurs if necessary */
inline void Param::set_param( float val) {
switch (type) {
inline void Param::set_param( float val)
{
matrix_flag = false;
switch (type)
{
case P_TYPE_BOOL:
if (val < 0)
*((bool*)engine_val) = false;
@ -135,8 +157,6 @@ inline void Param::set_param( float val) {
break;
case P_TYPE_DOUBLE:
/* Make sure value is an integer */
if (val < lower_bound.float_val)
*((float*)engine_val) = lower_bound.float_val;
else if (val > upper_bound.float_val)
@ -147,10 +167,27 @@ inline void Param::set_param( float val) {
default:
//abort();
break;
}
}
return;
inline void Param::set_param( CValue val)
{
matrix_flag = false;
switch (type)
{
case P_TYPE_BOOL:
set_param( val.bool_val );
break;
case P_TYPE_INT:
set_param( val.int_val );
break;
case P_TYPE_DOUBLE:
set_param( val.float_val );
break;
default:
//abort();
break;
}
}
#endif /** !_PARAM_TYPES_H */

View File

@ -46,7 +46,7 @@ public:
return NULL;
/* Now, create the user defined parameter given the passed name */
if ((param = new Param(name)) == NULL)
if ((param = Param::createUser(name)) == NULL)
return NULL;
/* Finally, insert the new parameter into this preset's parameter tree */

View File

@ -46,6 +46,7 @@
#include <iostream>
#include <sstream>
#include "BuiltinFuncs.hpp"
#include "MilkdropPresetFactory.hpp"
/* Grabs the next token from the file. The second argument points
to the raw string */
@ -276,9 +277,8 @@ Expr **Parser::parse_prefix_args(std::istream & fs, int num_args, MilkdropPrese
{
//if (PARSE_DEBUG) printf("parse_prefix_args: failed to get parameter # %d for function (LINE %d)\n", i+1, line_count);
for (j = 0; j < i; j++)
delete expr_list[j];
Expr::delete_expr(expr_list[j]);
free(expr_list);
expr_list = NULL;
return NULL;
}
/* Assign entry in expression list */
@ -360,7 +360,7 @@ int Parser::parse_per_pixel_eqn(std::istream & fs, MilkdropPreset * preset, cha
{
}
delete gen_expr;
Expr::delete_expr(gen_expr);
return PROJECTM_PARSE_ERROR;
}
@ -751,7 +751,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
}
if ( tree_expr != NULL )
{
delete tree_expr;
Expr::delete_expr(tree_expr);
}
return NULL;
}
@ -762,11 +762,10 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
if (PARSE_DEBUG) printf("parse_prefix_args: failed to convert prefix function to general expression (LINE %d) \n",
line_count);
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
for (i = 0; i < func->getNumArgs();i++)
delete expr_list[i];
Expr::delete_expr(expr_list[i]);
free(expr_list);
expr_list = NULL;
return NULL;
}
@ -789,7 +788,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
std::cerr << "token prefix is " << *string << std::endl;
if (PARSE_DEBUG) printf("parse_gen_expr: implicit multiplication case unimplemented!\n");
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -800,7 +799,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
{
if (PARSE_DEBUG) printf("parse_gen_expr: found left parentice, but failed to create new expression tree \n");
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -854,7 +853,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
if (*string == 0)
{
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -864,7 +863,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
if ((gen_expr = Expr::const_to_expr(val)) == NULL)
{
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -883,7 +882,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(std::string(string), &current_shape->param_tree)) == NULL)
{
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
}
@ -898,7 +897,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
/* Convert parameter to an expression */
if ((gen_expr = Expr::param_to_expr(param)) == NULL)
{
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -917,7 +916,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(std::string(string), &current_wave->param_tree)) == NULL)
{
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
}
@ -932,7 +931,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
/* Convert parameter to an expression */
if ((gen_expr = Expr::param_to_expr(param)) == NULL)
{
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -955,7 +954,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
/* Convert parameter to an expression */
if ((gen_expr = Expr::param_to_expr(param)) == NULL)
{
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -973,7 +972,7 @@ Expr * Parser::_parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkd
}
if (tree_expr)
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
}
@ -985,10 +984,10 @@ Expr * Parser::parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, Milkdr
if (NULL == gen_expr)
return NULL;
//std::cout << gen_expr << std::endl;
Expr *opt = gen_expr->optimize();
Expr *opt = Expr::optimize(gen_expr);
if (opt != gen_expr) {
delete gen_expr;
Expr::delete_expr(gen_expr);
}
//std::cout << opt << std::endl << std::endl;
return opt;
@ -1193,31 +1192,31 @@ Expr * Parser::parse_infix_op(std::istream & fs, token_t token, TreeExpr * tree
if (PARSE_DEBUG) printf("parse_infix_op: found addition operator (LINE %d)\n", line_count);
if (PARSE_DEBUG) std::cerr << "WRAP AROUND IS " << tokenWrapAroundEnabled << std::endl;
return parse_gen_expr(fs, insert_infix_op(Eval::infix_add, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_add, &tree_expr), preset);
case tMinus:
if (PARSE_DEBUG) printf("parse_infix_op: found subtraction operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_minus, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_minus, &tree_expr), preset);
case tMult:
if (PARSE_DEBUG) printf("parse_infix_op: found multiplication operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_mult, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_mult, &tree_expr), preset);
case tDiv:
if (PARSE_DEBUG) printf("parse_infix_op: found division operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_div, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_div, &tree_expr), preset);
case tMod:
if (PARSE_DEBUG) printf("parse_infix_op: found modulo operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_mod, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_mod, &tree_expr), preset);
case tOr:
if (PARSE_DEBUG) printf("parse_infix_op: found bitwise or operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_or, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_or, &tree_expr), preset);
case tAnd:
if (PARSE_DEBUG) printf("parse_infix_op: found bitwise and operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_and, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_and, &tree_expr), preset);
case tPositive:
if (PARSE_DEBUG) printf("parse_infix_op: found positive operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_positive, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_positive, &tree_expr), preset);
case tNegative:
if (PARSE_DEBUG) printf("parse_infix_op: found negative operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_negative, &tree_expr), preset);
return _parse_gen_expr(fs, insert_infix_op(Eval::infix_negative, &tree_expr), preset);
case tEOL:
case tEOF:
@ -1230,7 +1229,7 @@ Expr * Parser::parse_infix_op(std::istream & fs, token_t token, TreeExpr * tree
return gen_expr;
default:
if (PARSE_DEBUG) printf("parse_infix_op: operator or terminal expected, but not found (LINE %d)\n", line_count);
delete tree_expr;
Expr::delete_expr(tree_expr);
return NULL;
}
@ -1406,7 +1405,7 @@ PerFrameEqn * Parser::parse_per_frame_eqn(std::istream & fs, int index, Milkdro
if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
Expr::delete_expr(gen_expr);
return NULL;
}
@ -1459,7 +1458,7 @@ PerFrameEqn * Parser::parse_implicit_per_frame_eqn(std::istream & fs, char * pa
if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
Expr::delete_expr(gen_expr);
return NULL;
}
@ -1616,7 +1615,7 @@ InitCond * Parser::parse_per_frame_init_eqn(std::istream & fs, MilkdropPreset *
val = gen_expr->eval(-1,-1);
/* Free the general expression now that we are done with it */
delete gen_expr;
Expr::delete_expr(gen_expr);
/* integer value (boolean is an integer in C) */
if (param->type == P_TYPE_BOOL)
@ -1918,7 +1917,7 @@ int Parser::parse_shapecode(char * token, std::istream & fs, MilkdropPreset * p
fs >> text;
*((std::string*)param->engine_val) = text;
param->set_param(text);
if (PARSE_DEBUG)
std::cerr << "parse_shapecode: found image url, text is \""
<< text << "\"" << std::endl;
@ -2278,7 +2277,7 @@ int Parser::parse_wave_helper(std::istream & fs, MilkdropPreset * preset, int
if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
Expr::delete_expr(gen_expr);
return PROJECTM_FAILURE;
}
@ -2328,7 +2327,7 @@ int Parser::parse_wave_helper(std::istream & fs, MilkdropPreset * preset, int
/* Add the per point equation */
if (custom_wave->add_per_point_eqn(string, gen_expr) < 0)
{
delete gen_expr;
Expr::delete_expr(gen_expr);
return PROJECTM_PARSE_ERROR;
}
@ -2511,7 +2510,7 @@ int Parser::parse_shape_per_frame_eqn(std::istream & fs, CustomShape * custom_sh
if ((per_frame_eqn = new PerFrameEqn(custom_shape->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
Expr::delete_expr(gen_expr);
return PROJECTM_FAILURE;
}
@ -2573,7 +2572,7 @@ int Parser::parse_wave_per_frame_eqn(std::istream & fs, CustomWave * custom_wav
if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
Expr::delete_expr(gen_expr);
return PROJECTM_FAILURE;
}
@ -2605,3 +2604,144 @@ else
return false;
}
// TESTS
#include <TestRunner.hpp>
#ifndef NDEBUG
#include <PresetLoader.hpp>
#define TEST(cond) if (!verify(#cond,cond)) return false
#define TEST2(str,cond) if (!verify(str,cond)) return false
struct ParserTest : public Test
{
ParserTest() : Test("ParserTest")
{}
MilkdropPreset *preset;
std::istringstream is;
std::istringstream &ss(const char *s) { return is = std::istringstream(s); }
bool eq(float a, float b)
{
return abs(a-b) < (abs(a)+abs(b))/1000.0f;
}
public:
bool test_float()
{
float f=-1.0f;
TEST(PROJECTM_SUCCESS == Parser::parse_float(ss("1.1"),&f));
TEST(1.1f == f);
TEST(PROJECTM_SUCCESS == Parser::parse_float(ss("+1.2"),&f));
TEST(PROJECTM_SUCCESS == Parser::parse_float(ss("-1.3"),&f));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_float(ss(""),&f));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_float(ss("\n"),&f));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_float(ss("+"),&f));
return true;
}
bool test_int()
{
int i=-1;
TEST(PROJECTM_SUCCESS == Parser::parse_int(ss("1"),&i));
TEST(1 == i);
TEST(PROJECTM_SUCCESS == Parser::parse_int(ss("+2"),&i));
TEST(PROJECTM_SUCCESS == Parser::parse_int(ss("-3"),&i));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_int(ss(""),&i));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_int(ss("\n"),&i));
TEST(PROJECTM_PARSE_ERROR == Parser::parse_int(ss("+"),&i));
return true;
}
bool eval_expr(float expected, const char *s)
{
float f1, f2;
Expr *expr = Parser::parse_gen_expr(ss(s),nullptr,preset);
TEST(expr != nullptr);
TEST(ParserTest::eq(expected, f1=expr->eval(-1,-1)));
// this is really a test for Expr.cpp, but since we're here
Expr *opt = Expr::optimize(expr);
if (opt != expr)
{
TEST(ParserTest::eq(expected, f2 = opt->eval(-1, -1)));
Expr::delete_expr(opt);
}
Expr::delete_expr(expr);
return true;
}
bool test_eqn()
{
TEST(eval_expr(1.0f, "1.0"));
TEST(eval_expr(-1.0f, "-(1.0)")); // unary`
TEST(eval_expr(0.5f, "5/10.000")); // binary
TEST(eval_expr(1.0f, "sin(3.14159/2)"));
preset->presetOutputs().rot = 0.99f;
TEST(eval_expr(0.99f, "rot"));
return true;
}
// test multi-line expression, and multi-expression line
bool test_lines()
{
// TODO
return true;
}
bool test_params()
{
// TODO
return true;
}
bool _test()
{
bool success = true;
success &= test_float();
success &= test_int();
success &= test_eqn();
success &= test_lines();
success &= test_params();
return success;
}
bool test() override
{
// load IdlePreset
PresetLoader *presetLoader = new PresetLoader ( 400, 400, "" );
std::unique_ptr<Preset> preset_ptr = presetLoader->loadPreset("idle://Geiss & Sperl - Feedback (projectM idle HDR mix).milk");
preset = (MilkdropPreset *)preset_ptr.get();
bool success = _test();
delete presetLoader;
return success;
}
};
Test* Parser::test()
{
return new ParserTest();
}
#else
Test* Param::test()
{
return null;
}
#endif

View File

@ -120,6 +120,7 @@ typedef enum {
tStringBufferFilled /* the string buffer for this line is maxed out */
} token_t;
class Test;
class CustomShape;
class CustomWave;
class Expr;
@ -145,6 +146,7 @@ public:
static int last_token_size;
static bool tokenWrapAroundEnabled;
static Test *test();
static PerFrameEqn *parse_per_frame_eqn( std::istream & fs, int index,
MilkdropPreset * preset);
static int parse_per_pixel_eqn( std::istream & fs, MilkdropPreset * preset,

View File

@ -36,8 +36,8 @@
#include <cassert>
/* Evaluate an equation */
void PerFrameEqn::evaluate() {
void PerFrameEqn::evaluate()
{
if (PER_FRAME_EQN_DEBUG) {
printf("per_frame_%d=%s= ", index, param->name.c_str());
fflush(stdout);
@ -46,20 +46,19 @@ void PerFrameEqn::evaluate() {
//*((float*)per_frame_eqn->param->engine_val) = eval(per_frame_eqn->gen_expr);
assert(gen_expr);
assert(param);
param->set_param(gen_expr->eval(-1,-1));
float v = gen_expr->eval(-1,-1);
param->set_param(v);
if (PER_FRAME_EQN_DEBUG) printf(" = %.4f\n", *((float*)param->engine_val));
if (PER_FRAME_EQN_DEBUG) printf(" = %.4f\n", v);
}
/* Frees perframe equation structure. Warning: assumes gen_expr pointer is not freed by anyone else! */
PerFrameEqn::~PerFrameEqn() {
delete gen_expr;
// param is freed in param_tree container of some other class
PerFrameEqn::~PerFrameEqn()
{
Expr::delete_expr(gen_expr);
// param is freed in param_tree container of some other class
}
/* Create a new per frame equation */

View File

@ -35,47 +35,23 @@
#include "wipemalloc.h"
#include <cassert>
/* Evaluates a per pixel equation */
void PerPixelEqn::evaluate(int mesh_i, int mesh_j) {
Expr * eqn_ptr = 0;
eqn_ptr = this->gen_expr;
float ** param_matrix = (float**)this->param->matrix;
if (param_matrix == 0) {
assert(param->engine_val);
(*(float*)param->engine_val) = eqn_ptr->eval(mesh_i, mesh_j);
} else {
assert(!(eqn_ptr == NULL || param_matrix == NULL));
param_matrix[mesh_i][mesh_j] = eqn_ptr->eval(mesh_i, mesh_j);
/* Now that this parameter has been referenced with a per
pixel equation, we let the evaluator know by setting
this flag */
/// @bug review and verify this behavior
param->matrix_flag = true;
param->flags |= P_FLAG_PER_PIXEL;
}
void PerPixelEqn::evaluate(int mesh_i, int mesh_j)
{
assign_expr->eval( mesh_i, mesh_j );
}
PerPixelEqn::PerPixelEqn(int _index, Param * _param, Expr * _gen_expr):index(_index), param(_param), gen_expr(_gen_expr) {
PerPixelEqn::PerPixelEqn(int _index, Param * param, Expr * gen_expr):index(_index)
{
assert(index >= 0);
assert(param != 0);
assert(gen_expr != 0);
assign_expr = new AssignMatrixExpr(param, gen_expr);
}
PerPixelEqn::~PerPixelEqn()
{
if (gen_expr)
delete (gen_expr);
Expr::delete_expr(assign_expr);
}

View File

@ -51,9 +51,6 @@ class Preset;
class PerPixelEqn {
public:
int index; /* used for splay tree ordering. */
int flags; /* primarily to specify if this variable is user-defined */
Param *param;
Expr *gen_expr;
void evalPerPixelEqns( Preset *preset );
void evaluate(int mesh_i, int mesh_j);
@ -61,6 +58,7 @@ public:
PerPixelEqn(int index, Param * param, Expr * gen_expr);
Expr *assign_expr;
};

View File

@ -40,51 +40,17 @@
/* Evaluates a per point equation for the current custom wave given by interface_wave ptr */
void PerPointEqn::evaluate(int i)
{
float * param_matrix;
Expr * eqn_ptr;
// samples = CustomWave::interface_wave->samples;
eqn_ptr = gen_expr;
if (param->matrix == NULL)
{
assert(param->matrix_flag == false);
(*(float*)param->engine_val) = eqn_ptr->eval(i,-1);
return;
}
else
{
param_matrix = (float*)param->matrix;
// -1 is because per points only use one dimension
param_matrix[i] = eqn_ptr->eval(i, -1);
/* Now that this parameter has been referenced with a per
point equation, we let the evaluator know by setting
this flag */
if (!param->matrix_flag)
param->matrix_flag = true;
}
assign_expr->eval( i, -1 );
}
PerPointEqn::PerPointEqn(int _index, Param * _param, Expr * _gen_expr, int _samples):
index(_index),
samples(_samples),
param(_param),
gen_expr(_gen_expr)
{}
PerPointEqn::PerPointEqn(int _index, Param * param, Expr * gen_expr):
index(_index)
{
assign_expr = new AssignExpr(param, gen_expr);
}
PerPointEqn::~PerPointEqn()
{
delete gen_expr;
Expr::delete_expr(assign_expr);
}

View File

@ -37,12 +37,10 @@ class PerPointEqn;
class PerPointEqn {
public:
int index;
int samples; // the number of samples to iterate over
Param *param;
Expr * gen_expr;
Expr * assign_expr;
~PerPointEqn();
void evaluate(int i);
PerPointEqn( int index, Param *param, Expr *gen_expr, int samples);
PerPointEqn( int index, Param *param, Expr *gen_expr );
};

View File

@ -0,0 +1,43 @@
//
// Created by matthew on 1/7/19.
//
#include <iostream>
#include <MilkdropPresetFactory/Parser.hpp>
#include <TestRunner.hpp>
#include <MilkdropPresetFactory/Param.hpp>
std::vector<Test *> TestRunner::tests;
bool TestRunner::run()
{
if (tests.empty())
{
// We still call register/run tests in NDEBUG (useful for performance testing)
// but tests may choose to comment out body to save space
tests.push_back(Param::test());
tests.push_back(Parser::test());
tests.push_back(Expr::test());
}
int count = 0;
bool successful = true;
for (auto it=tests.begin() ; it < tests.end() ; it++ )
{
if (nullptr == (*it))
continue;
count++;
std::cout << "TestRunner: " << (*it)->getName() << " started" << std::endl;
std::cout.flush();
bool result = (*it)->test();
successful &= result;
if (result)
std::cout << "TestRunner: " << (*it)->getName() << " passed" << std::endl;
else
std::cout << "TestRunner: " << (*it)->getName() << " FAILED" << std::endl;
}
if (0 == count)
std::cout << "TestRunner: no tests found to run" << std::endl;
return successful;
}

View File

@ -0,0 +1,48 @@
//
// Created by matthew on 1/7/19.
//
// This is a place to collect/run non-graphical unit tests
// CONSIDER using some framework like googletest etc, but for now didn't want new dependencies
//
#ifndef PROJECTM_LUA_TESTRUNNER_H
#define PROJECTM_LUA_TESTRUNNER_H
#include <string>
#include <vector>
class Test
{
public:
const std::string getName() { return name; };
#ifdef NDEBUG
virtual bool test() {return true;}
#else
virtual bool test() = 0;
#endif
virtual ~Test() = default;
protected:
explicit Test(std::string name_) : name(name_) {};
const std::string name;
bool verify(const char *test, bool success)
{
if (!success)
std::cout << "failed " << test << std::endl;
return success;
}
};
class TestRunner
{
public:
static bool run();
private:
static std::vector<Test *> tests;
};
#endif //PROJECTM_LUA_TESTRUNNER_H

View File

@ -74,6 +74,7 @@ std::string read_config();
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <src/libprojectM/TestRunner.hpp>
//#include <pulsecore/gccmacro.h>
@ -108,6 +109,8 @@ class ProjectMApplication : public QApplication {
int main ( int argc, char*argv[] )
{
// if (!TestRunner::run()) exit(1);
int i;
char projectM_data[1024];