SeExpr
seop.cpp
Go to the documentation of this file.
1 /*
2  Copyright Disney Enterprises, Inc. All rights reserved.
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License
6  and the following modification to it: Section 6 Trademarks.
7  deleted and replaced with:
8 
9  6. Trademarks. This License does not grant permission to use the
10  trade names, trademarks, service marks, or product names of the
11  Licensor and its affiliates, except as required for reproducing
12  the content of the NOTICE file.
13 
14  You may obtain a copy of the License at
15  http://www.apache.org/licenses/LICENSE-2.0
16 */
17 #include <RslPlugin.h>
18 #include <rx.h>
19 #include <map>
20 #include <list>
21 #include <pthread.h>
22 #include <SeVec.h>
23 #include <SeExpression.h>
24 #include <SeExprFunc.h>
25 #include <RixInterfaces.h>
26 #include <cstdlib>
27 #include <cstring>
28 #include "immutable_hash_map.h"
29 
30 namespace {
31 RixTokenStorage* tokenizer;
32 inline const char* tokenize(const char* str) {
33  const char* result = tokenizer->GetToken(str);
34  return result;
35 }
36 
37 int SeTokenize(RslContext* ctx, int argc, const RslArg* argv[]) {
38  RslStringIter result(argv[0]);
39  const char* str = *RslStringIter(argv[1]);
40  *result = (RtString)tokenize(str);
41  return 0;
42 }
43 
44 struct SeRmanVarMap {
45  immutable_hash_map<const char*, int> indexMap;
46  std::vector<int> groupStarts;
47  std::vector<int> groupCounts;
48  std::vector<int> groupIndices;
49 
50  SeRmanVarMap(int nvars, const char** varnames, int* varindices, int* groupstarts)
51  : indexMap(varnames, varindices, nvars), groupStarts(groupstarts, groupstarts + nvars), groupCounts(nvars),
52  groupIndices(nvars) {
53  int groupIndex = 0, i, j;
54  for (i = 0; i < nvars;) {
55  // determine size of current group
56  for (j = i + 1; j < nvars; j++) {
57  if (groupStarts[j] != groupStarts[i]) break;
58  }
59  int groupCount = j - i;
60  // assign group size and index to group members
61  for (; i < j; i++) {
62  groupCounts[i] = groupCount;
63  groupIndices[i] = groupIndex;
64  }
65  groupIndex++;
66  }
67 
68  // for(size_t i=0;i<indexMap._keys.size();i++){
69  // const char* name=indexMap._keys[i]?indexMap._keys[i]:"";
70  // }
71  }
72 };
73 typedef std::map<const char*, SeRmanVarMap*> SeRmanVarMapMap;
74 typedef std::map<const char*, int> SeRmanExprMap;
75 typedef std::vector<int> SeVarBinding;
76 
77 class SeRmanExpr;
78 
80 struct ThreadData {
81 
82  // rix message interface
83  RixMessages* msgs;
84 
85  // all variable maps accessed by this thread
86  SeRmanVarMapMap varmaps;
87 
88  SeRmanVarMap& getVarMap(const char* varMapHandle) {
89  SeRmanVarMap*& varmap = varmaps[varMapHandle];
90  if (!varmap) {
91  // parse var list and make a new varmap
92  char* varlist = strdup(varMapHandle);
93  std::vector<const char*> varnames;
94  std::vector<int> groupStarts;
95 
96  // parse each var group (separated by spaces)
97  char* varlist_end = 0;
98  char* vargroup = strtok_r(varlist, " ", &varlist_end);
99  do {
100  // parse vars within var group (separated by commas)
101  int groupStart = varnames.size();
102  char* vargroup_end = 0;
103  char* var = strtok_r(vargroup, ",", &vargroup_end);
104  do {
105  varnames.push_back(tokenize(var));
106  groupStarts.push_back(groupStart);
107  } while ((var = strtok_r(0, ",", &vargroup_end)));
108  } while ((vargroup = strtok_r(0, " ", &varlist_end)));
109 
110  // build new varmap
111  int nvars = varnames.size();
112  int* varindices = (int*)alloca(sizeof(int) * nvars);
113  for (int i = 0; i < nvars; i++) {
114  varindices[i] = i;
115  }
116  varmap = new SeRmanVarMap(nvars, &varnames[0], &varindices[0], &groupStarts[0]);
117  free(varlist);
118  }
119  return *varmap;
120  }
121 
122  void ptError(char* fmt, ...) {
123  static char strbuf[1024];
124  va_list ap;
125  va_start(ap, fmt);
126  vsprintf(strbuf, fmt, ap); // TODO: this should use vsnprintf
127  msgs->Error("%s", strbuf);
128  va_end(ap);
129  }
130 
131  void ptWarn(char* fmt, ...) {
132  static char strbuf[1024];
133  va_list ap;
134  va_start(ap, fmt);
135  vsprintf(strbuf, fmt, ap); // TODO: this should use vsnprintf
136  msgs->Warning("%s", strbuf);
137  va_end(ap);
138  }
139 
140  SeRmanExprMap exprmap;
141  std::vector<SeRmanExpr*> exprs;
142 
143  RtColor* varValues; // value of every var at current grid point
144  float* Ci; // current value of Ci
145 
146  ThreadData() {
147  msgs = (RixMessages*)RxGetRixContext()->GetRixInterface(k_RixMessages);
148  exprs.push_back(0); // dummy entry; index 0 means uninitialized
149  }
150 };
151 
153 ThreadData& getThreadData(RslContext* ctx) {
154  ThreadData* td = (ThreadData*)ctx->GetThreadData();
155  if (!td) {
156  td = new ThreadData;
157  ctx->SetThreadData(td);
158  }
159  return *td;
160 }
161 
163 class SeRmanVar : public SeExprVarRef {
164  public:
165  SeRmanVar(ThreadData& td) : SeExprVarRef(SeExprType().FP(3).Varying()), td(td), index(0) {}
166  // SeRmanVar(ThreadData& td) : td(td), index(0) {}
167  virtual bool isVec() { return 1; } // treat all vars as vectors
168  void setIndex(int i) { index = i; }
169  virtual void eval(const SeExprVarNode* node, SeVec3d& result) {
170  RtColor& c = td.varValues[index];
171  result = c;
172  }
173 
174  private:
175  ThreadData& td;
176  int index;
177 };
178 
180 //$user::foo
181 class AttrVar : public SeExprVectorVarRef {
182  std::string name;
183  SeVec3d value;
184 
185  public:
186  AttrVar() : name(""), value(0.) {}
187 
188  AttrVar(const std::string& nameIn) : value(0.) {
189  // change "::" to ":" (needed :: for parsing SE).
190  size_t pos = nameIn.find("::");
191  name = nameIn.substr(0, pos) + nameIn.substr(pos + 1, nameIn.size() - (pos - 1));
192  }
193 
194  std::string getName() { return name; }
195 
196  void doLookup() {
197  // make sure have enough space to hold result
198  float fbuf16[16];
199 
200  RxInfoType_t rxType; // = RxInfoColor;
201  int count;
202  int statusOpt, statusAttr;
203 
204  // try option first then attribute
205  statusOpt = RxOption(name.c_str(), fbuf16, sizeof(fbuf16), &rxType, &count);
206  statusAttr = RxAttribute(name.c_str(), fbuf16, sizeof(fbuf16), &rxType, &count);
207 
208  if (statusAttr != 0 && statusOpt != 0) {
209  // no matches, go with default of 0
210  value.setValue(0.0, 0.0, 0.0);
211  return;
212  }
213 
214  // found something
215  switch (rxType) {
216  case RxInfoFloat:
217  // promote float to color grey scale
218  value.setValue(fbuf16[0], fbuf16[0], fbuf16[0]);
219  break;
220  case RxInfoColor:
221  case RxInfoNormal:
222  case RxInfoVector:
223  case RxInfoPoint:
224  // any of the tuples will do for color
225  value.setValue(fbuf16[0], fbuf16[1], fbuf16[2]);
226  break;
227  default:
228  // not an expected match
229  // stderr << "SeRmanExpr: Unexpected type for Option/Attribute" << name.c_str() << rxType <<". Only
230  // Float or Color allowed.";
231  break;
232  }
233  }
234 
235  // Implement the interface of SeExprVarRef
236  virtual void eval(const SeExprVarNode* node, SeVec3d& result) { result = value; }
237 };
238 
240 class SeRmanExpr : public SeExpression {
241  public:
242  SeRmanExpr(const std::string& expr, ThreadData& td) : SeExpression(expr), _td(td), _boundVarMap(-1) {}
243 
244  virtual SeExprVarRef* resolveVar(const std::string& name) const {
245  if (name.find("::") != std::string::npos) {
246  int i, size;
247  for (i = 0, size = _attrrefs.size(); i < size; i++)
248  // AttrVar attr = _attrrefs[i];
249  if (name == _attrrefs[i]->getName()) return _attrrefs[i];
250 
251  // didn't match so make new
252  AttrVar* attrVar = new AttrVar(/*td,*/ name);
253  _attrrefs.push_back(attrVar);
254  return attrVar;
255  }
256 
257  const char* token = tokenize(name.c_str());
258  for (int i = 0, size = _varrefs.size(); i < size; i++)
259  if (_varnames[i] == token) return _varrefs[i];
260  SeRmanVar* var = new SeRmanVar(_td);
261  _varnames.push_back(token);
262  _varrefs.push_back(var);
263  return var;
264  }
265 
266  SeVarBinding* bindVars(const char* varMapHandle) {
267  SeVarBinding*& binding = _bindings[varMapHandle];
268  if (!binding) {
269  binding = new SeVarBinding;
270 
271  // find varmap
272  SeRmanVarMap& varmap = _td.getVarMap(varMapHandle);
273  // bind varmap to expression
274  int nvars = _varnames.size();
275  binding->resize(nvars);
276  for (int i = 0; i < nvars; i++) {
277  const char* name = _varnames[i];
278  int index = varmap.indexMap[name];
279  if (!index) {
280  // for(int i=0;i<varmap.indexMap._keys.size();i++){
281  // std::cerr<<"key "<<i<<std::endl;
282  // const char* name=indexMap._keys[i]?indexMap._keys[i]:"";
283  // std::cerr<<" we have key "<<name<<" and val "<<indexMap._values[i]<<std::endl;
284  //}
285  char msg[] = "SeRmanExpr error: undefined variable \"$%s\"";
286  _td.ptError(msg, name);
287  }
288  (*binding)[i] = index;
289  }
290  }
291  _bindstack.push_back(binding);
292  return binding;
293  }
294 
295  void lookupAttrs() {
296  int nattrs = _attrrefs.size();
297 
298  // fill in attrs
299  for (int i = 0; i < nattrs; i++) {
300  _attrrefs[i]->doLookup();
301  }
302  }
303 
304  void setVarIndices() {
305  // set the varref indices to the currently bound varmap
306  // note: we can't do this during bind because expression evals may be nested
307  SeVarBinding* binding = _bindstack.back();
308  if (binding) {
309  for (int i = 0, size = binding->size(); i < size; i++) _varrefs[i]->setIndex((*binding)[i]);
310  }
311  }
312 
313  void unbindVars() { _bindstack.pop_back(); }
314 
315  private:
316  mutable std::vector<const char*> _varnames; // ordered, unique list of var names
317  mutable std::vector<SeRmanVar*> _varrefs; // var refs corresponding to _varnames
318  mutable std::vector<AttrVar*> _attrrefs;
319  mutable std::map<const char*, SeVarBinding*> _bindings; // bindings for each varmap
320  mutable std::vector<SeVarBinding*> _bindstack; // stack of active bindings
321  ThreadData& _td;
322  int _boundVarMap;
323 };
324 
325 void init(RixContext* ctx) {
326  tokenizer = (RixTokenStorage*)ctx->GetRixInterface(k_RixGlobalTokenData);
327 
328  // temporarily unset the expr plugins path
329  char* plugins_ptr = getenv("SE_EXPR_PLUGINS");
330  std::string plugins;
331  if (plugins_ptr) {
332  plugins = plugins_ptr;
333  unsetenv("SE_EXPR_PLUGINS");
334  }
335 
336  // and init the plugins explicitly (they're statically linked)
337  SeExprFunc::init();
338  // DefineSeExprPlugin (SeExprFunc::define);
339 
340  // restore environment
341  if (plugins_ptr) setenv("SE_EXPR_PLUGINS", plugins.c_str(), 1);
342 }
343 
344 int SeExprBind(RslContext* ctx, int argc, const RslArg* argv[]) {
345  RslFloatIter result(argv[0]);
346  const char* exprstr = *RslStringIter(argv[1]);
347  const char* varmapHandle = *RslStringIter(argv[2]);
348 
349  if (!exprstr[0]) {
350  // expression is blank - just return null handle
351  *result = 0;
352  argv[3]->GetResizer()->Resize(0);
353  return 0;
354  }
355 
356  // see if we have this expr already
357  ThreadData& td = getThreadData(ctx);
358  int& index = td.exprmap[exprstr];
359  if (!index) {
360  // parse expr (parser is not reentrant - use mutex)
361  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
362  pthread_mutex_lock(&mutex);
363  SeRmanExpr* expr = new SeRmanExpr(exprstr, td);
364  bool valid = expr->isValid(); // triggers parse
365  pthread_mutex_unlock(&mutex);
366  if (!valid) {
367  char msg[] = "SeRmanExpr error: %s";
368  td.ptError(msg, expr->parseError().c_str());
369  index = 0;
370  } else
371  index = td.exprs.size();
372  // store expr object whether parse succeeded or not (so we don't parse again)
373  td.exprs.push_back(expr);
374  }
375 
376  *result = index;
377  if (index) {
378  // bind vars only if we have a valid expr
379  SeRmanExpr* expr = td.exprs[index];
380  expr->lookupAttrs();
381  SeVarBinding& binding = *expr->bindVars(varmapHandle);
382  int nvars = binding.size();
383  argv[3]->GetResizer()->Resize(nvars);
384  float* varIndices = *RslFloatArrayIter(argv[3]);
385  for (int i = 0; i < nvars; i++) {
386  varIndices[i] = binding[i];
387  }
388  } else {
389  argv[3]->GetResizer()->Resize(0);
390  }
391  return 0;
392 }
393 
394 int SeExprEval(RslContext* ctx, int argc, const RslArg* argv[]) {
395  int index = int(*RslFloatIter(argv[1]));
396  RslColorArrayIter varValuesIter = argv[2];
397  RslColorIter CiIter = argv[3];
398 
399  int numVals = argv[3]->NumValues();
400 
401  if (!index) {
402  for (int i = 0; i < numVals; i++, CiIter++, varValuesIter++) {
403  float* Ci = *CiIter;
404  Ci[0] = Ci[1] = Ci[2] = 0;
405  }
406  return 0;
407  }
408 
409  ThreadData& td = getThreadData(ctx);
410 
411  SeRmanExpr& expr = *td.exprs[index];
412  expr.setVarIndices();
413 
414  bool isThreadSafe = expr.isThreadSafe();
415 
416  for (int i = 0; i < numVals; i++, CiIter++, varValuesIter++) {
417  td.varValues = &varValuesIter[0];
418  float* Ci = td.Ci = *CiIter;
419 
420  // expression evaluator is reentrant but functions may not be
421  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
422  if (!isThreadSafe) pthread_mutex_lock(&mutex);
423 
424  SeVec3d v = expr.evaluate();
425 
426  if (!isThreadSafe) pthread_mutex_unlock(&mutex);
427 
428  if (!isfinite(v[0]) || !isfinite(v[1]) || !isfinite(v[2])) {
429  char msg[] = "Shader Expression: %s: resulted in NAN. Setting val to 1";
430  td.ptWarn(msg, expr.getExpr().c_str());
431  v[0] = v[1] = v[2] = 1;
432  }
433 
434  Ci[0] = v[0];
435  Ci[1] = v[1];
436  Ci[2] = v[2];
437  }
438 
439  expr.unbindVars();
440  return 0;
441 }
442 }
443 
444 extern "C" {
445 static RslFunction funcs[] = {
446  // SeTokenize(inputStr) -> tokenized string
447  {"string SeTokenize(string)", SeTokenize, NULL, NULL},
448 
449  // SeExprBind(exprStr, varmap, varIndices[]) -> exprHandle
450  {"float SeExprBind(string, string, output uniform float[])", SeExprBind, NULL, NULL},
451 
452  // SeExprEval(exprHandle, varValues, CsVal)
453  {"void SeExprEval(uniform float, color[], output color)", SeExprEval, NULL, NULL},
454  {NULL}};
455 
456 RslFunctionTable RslPublicFunctions(funcs, init);
457 }
static SeExprInternal2::Mutex mutex
Definition: ExprFunc.cpp:108
</pre >< h2 > Evaluating expressions</h2 > Evaluating an expression is pretty easy But before we can do that we need to make an instance< pre > GrapherExpr expr("x+x^2")
virtual void eval(ArgHandle args)
For any rgb or hsl value(except for negative s values)
The result is computed int int< br >< divstyle="margin-left:40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed).&nbsp
RslFunctionTable RslPublicFunctions(funcs, init)
</pre > Once we have this we need an instance to store our variable and provide a reference to that We make it because resolveVar() is const .One does not need to store a variable reference in a given expression.In fact
static RslFunction funcs[]
Definition: seop.cpp:445