GlslEmit.cpp

00001 // Sh: A GPU metaprogramming language.
00002 //
00003 // Copyright 2003-2005 Serious Hack Inc.
00004 // 
00005 // This library is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 //
00010 // This library is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with this library; if not, write to the Free Software
00017 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
00018 // MA  02110-1301, USA
00020 #include "GlslCode.hpp"
00021 #include <iostream>
00022 #include <cmath>
00023 
00024 namespace shgl {
00025 
00026 using namespace SH;
00027 using namespace std;
00028 
00029 static GlslMapping opCodeTable[] = {
00030   {SH_OP_ABS,   "abs($0)"},
00031   {SH_OP_ACOS,  "acos($0)"},
00032   {SH_OP_ADD,   "$0 + $1"},
00033   {SH_OP_ASIN,  "asin($0)"},
00034   {SH_OP_ASN,   "$0"},
00035   {SH_OP_ATAN,  "atan($0)"},
00036   {SH_OP_CEIL,  "ceil($0)"},
00037   {SH_OP_COS,   "cos($0)"},
00038   {SH_OP_DOT,   "dot($0, $1)"},
00039   {SH_OP_DIV,   "$0 / $1"},
00040   {SH_OP_DX,    "dFdx($0)"},
00041   {SH_OP_DY,    "dFdy($0)"},
00042   {SH_OP_EXP,   "exp($0)"},
00043   {SH_OP_EXP2,  "exp2($0)"},
00044   {SH_OP_FLR,   "floor($0)"},
00045   {SH_OP_FRAC,  "fract($0)"},
00046   {SH_OP_LOG,   "log($0)"},
00047   {SH_OP_LOG2,  "log2($0)"},
00048   {SH_OP_LRP,   "mix($2, $1, $0)"},
00049   {SH_OP_MAD,   "$0 * $1 + $2"},
00050   {SH_OP_MAX,   "max($0, $1)"},
00051   {SH_OP_MIN,   "min($0, $1)"},
00052   {SH_OP_MOD,   "mod($0, $1)"},
00053   {SH_OP_MUL,   "$0 * $1"},
00054   {SH_OP_NEG,   "-($0)"},
00055   {SH_OP_NORM,  "normalize($0)"},
00056   {SH_OP_POW,   "pow($0, $1)"},
00057   {SH_OP_RCP,   "1.0 / $0"},
00058   {SH_OP_RND,   "floor($0 + 0.5)"},
00059   {SH_OP_RSQ,   "inversesqrt($0)"},
00060   {SH_OP_SGN,   "sign($0)"},
00061   {SH_OP_SIN,   "sin($0)"},
00062   {SH_OP_SQRT,  "sqrt($0)"},
00063   {SH_OP_TAN,   "tan($0)"},
00064   {SH_OP_XPD,   "cross($0, $1)"},
00065 
00066   {SH_OPERATION_END,  0} 
00067 };
00068 
00069 typedef map<SH::ShOperation, GlslOpCodeVecs> GlslOpCodeMap;
00070 
00071 GlslOpCodeVecs::GlslOpCodeVecs(const GlslMapping& mapping)
00072 {
00073   string code(mapping.code);
00074 
00075   // Dices up the code string into references #i or $i to 
00076   // src variables and the code fragments between references. 
00077   string::size_type i, j;
00078   i = j = 0;
00079   for (; (j = code.find_first_of("$", i)) != string::npos;) {
00080     frag.push_back(code.substr(i, j - i));
00081     i = j + 1;
00082     j = code.find_first_not_of("012345689", i);
00083     index.push_back(atoi(code.substr(i, j - i).c_str()));
00084     i = j;
00085   }
00086   if (i == string::npos) {
00087     frag.push_back("");
00088   } else {
00089     frag.push_back(code.substr(i));
00090   }
00091 }
00092 
00093 void GlslCode::table_substitution(const ShStatement& stmt, GlslOpCodeVecs codeVecs)
00094 {
00095   unsigned i=0;
00096 
00097   // find the size of the biggest parameter
00098   int param_size=0;
00099   for (i=0; i < codeVecs.index.size(); i++) {
00100     const ShVariable& src = stmt.src[codeVecs.index[i]];
00101     if (src.size() > param_size) {
00102       param_size = src.size();
00103     }
00104   }
00105   
00106   // print each parameter
00107   stringstream operation;
00108   for (i=0; i < codeVecs.index.size(); i++) { 
00109     const ShVariable& src = stmt.src[codeVecs.index[i]];
00110     operation << codeVecs.frag[i] << resolve(src, -1, param_size);
00111   }
00112   operation << codeVecs.frag[i]; // code fragment after the last variable
00113 
00114   // create the full line of code
00115   stringstream line;
00116   line << resolve(stmt.dest) << " = ";
00117 
00118   if (param_size != stmt.dest.size()) {
00119     // cast for the destination size  
00120     line << glsl_typename(stmt.dest.valueType(), stmt.dest.size()); 
00121     line << "(" << operation.str() << ")";
00122   } else {
00123     line << operation.str();
00124   }
00125 
00126   append_line(line.str());
00127 }
00128 
00129 void GlslCode::emit(const ShStatement &stmt)
00130 {
00131   static GlslOpCodeMap opCodeMap;
00132 
00133   // Lazily fill-in opCodeMap from the above table
00134   if (opCodeMap.empty()) {
00135     for (int i=0; opCodeTable[i].op != SH_OPERATION_END; i++) {
00136       opCodeMap[opCodeTable[i].op] = GlslOpCodeVecs(opCodeTable[i]);
00137     }
00138   }
00139 
00140   if(opCodeMap.find(stmt.op) != opCodeMap.end()) {
00141     // Handle ops in the table first  
00142     table_substitution(stmt, opCodeMap[stmt.op]);
00143   } 
00144   else {
00145     // Handle the rest of the operations
00146     switch(stmt.op) {
00147     case SH_OP_CBRT:
00148       emit_cbrt(stmt);
00149       break;
00150     case SH_OP_CMUL:
00151       emit_prod(stmt);
00152       break;
00153     case SH_OP_CSUM:
00154       emit_sum(stmt);
00155       break;
00156     case SH_OP_COND:
00157       emit_cond(stmt);
00158       break;
00159     case SH_OP_EXP10:
00160       emit_exp10(stmt);
00161       break;
00162     case SH_OP_KIL:
00163       emit_discard(stmt, "discard");
00164       break;
00165     case SH_OP_LIT:
00166       emit_lit(stmt);
00167       break;
00168     case SH_OP_LOG10:
00169       emit_log10(stmt);
00170       break;
00171     case SH_OP_NOISE:
00172       emit_noise(stmt);
00173       break;
00174     case SH_OP_PAL:
00175       emit_pal(stmt);
00176       break;
00177     case SH_OP_RET:
00178       emit_discard(stmt, "return");
00179       break;
00180     case SH_OP_SEQ:
00181     case SH_OP_SGE:
00182     case SH_OP_SGT:
00183     case SH_OP_SLE:
00184     case SH_OP_SLT:
00185     case SH_OP_SNE:
00186       emit_logic(stmt);
00187       break;
00188     case SH_OP_TEX:
00189     case SH_OP_TEXI:
00190       emit_texture(stmt);
00191       break;
00192     case SH_OP_COSH:
00193     case SH_OP_SINH:
00194     case SH_OP_TANH:
00195       emit_hyperbolic(stmt);
00196       break;
00197     case SH_OP_COMMENT:
00198       emit_comment(stmt);
00199       break;
00200     default:
00201       shError(ShException(string("Glsl Code: Unknown operation ") + opInfo[stmt.op].name));
00202       break;
00203     }
00204   }
00205 }
00206 
00207 ShVariableNodePtr GlslCode::allocate_constant(const ShStatement& stmt, double constant, int size) const
00208 {
00209   if (size <= 0) size = stmt.dest.size(); // default size is size of destination variable
00210 
00211   const ShVariableNodePtr& dest_node = stmt.dest.node();
00212   ShVariableNode* node = new ShVariableNode(SH_CONST, size, dest_node->valueType(), dest_node->specialType());
00213   
00214   ShDataVariant<double>* variant = new ShDataVariant<double>(size, constant);
00215   node->setVariant(variant);
00216 
00217   ShVariableNodePtr node_ptr = ShPointer<ShVariableNode>(node);
00218   return node_ptr;
00219 }
00220 
00221 ShVariableNodePtr GlslCode::allocate_temp(const ShStatement& stmt, int size) const
00222 {
00223   if (size <= 0) size = stmt.dest.size(); // default size is size of destination variable
00224   
00225   const ShVariableNodePtr& dest_node = stmt.dest.node();
00226   ShVariableNode* node = new ShVariableNode(SH_TEMP, size, dest_node->valueType(), dest_node->specialType());
00227   ShVariableNodePtr node_ptr = ShPointer<ShVariableNode>(node);
00228   return node_ptr;
00229 }
00230 
00231 string GlslCode::resolve_constant(double constant, const ShVariable& var, int size) const
00232 {
00233   if (size <= 0) size = var.size(); // default size is size of variable
00234 
00235   stringstream s;
00236   s.precision(16);
00237   s << glsl_typename(var.valueType(), size);
00238 
00239   s << "(";
00240   for (int i=0; i < size; i++) {
00241     if (i > 0) s << ", ";
00242     s << constant;
00243   }
00244   s << ")";
00245 
00246   return s.str();
00247 }
00248 
00249 void GlslCode::emit_cbrt(const ShStatement& stmt)
00250 {
00251   SH_DEBUG_ASSERT(SH_OP_CBRT == stmt.op);
00252 
00253   ShVariable temp(allocate_constant(stmt, 1.0 / 3.0));
00254   emit_pow(stmt.dest, stmt.src[0], temp);
00255 }
00256 
00257 void GlslCode::emit_comment(const ShStatement& stmt)
00258 {
00259   append_line("// " + stmt.get_info<ShInfoComment>()->comment, false);
00260 }
00261 
00262 void GlslCode::emit_cond(const ShStatement& stmt)
00263 {
00264   SH_DEBUG_ASSERT(SH_OP_COND == stmt.op);
00265   static string vendor((char*)glGetString(GL_VENDOR));
00266 
00267   const int size = stmt.src[0].size();
00268   if (("NVIDIA Corporation" == vendor) || (1 == size)) {
00269     // CG has a component-wise COND operator
00270     append_line(resolve(stmt.dest) + " = (" + resolve(stmt.src[0]) + " > " + 
00271                 resolve_constant(0, stmt.src[0]) + ") ? " + resolve(stmt.src[1]) +
00272                 " : " + resolve(stmt.src[2]));
00273   } else {
00274     // The Glsl spec requires the first argument to COND to be a scalar
00275     for (int i=0; i < size; i++) {
00276       append_line(resolve(stmt.dest, i) + " = (" + resolve(stmt.src[0], i) + " > " + 
00277                   resolve_constant(0, stmt.src[0], 1) + ") ? " + resolve(stmt.src[1], i) +
00278                   " : " + resolve(stmt.src[2], i));
00279     }
00280   }
00281 }
00282 
00283 void GlslCode::emit_discard(const ShStatement& stmt, const string& function)
00284 {
00285   SH_DEBUG_ASSERT((SH_OP_KIL == stmt.op) || (SH_OP_RET == stmt.op));
00286 
00287   append_line(string("if (any(") + resolve(stmt.src[0]) + ")) " + function);
00288 }
00289 
00290 void GlslCode::emit_pow(const ShVariable& dest, const ShVariable& a, const ShVariable& b)
00291 {
00292   // cast each argument's size if necessary
00293   const int size = std::max(a.size(), b.size());
00294   string pow_call = string("pow(") + resolve(a, -1, size) + ", " + resolve(b, -1, size) + ")";
00295   
00296   // cast to the destination size if necessary
00297   if (dest.size() != size) {
00298     pow_call = glsl_typename(dest.valueType(), dest.size()) + "(" + pow_call + ")";
00299   }
00300 
00301   append_line(resolve(dest) + " = " + pow_call);
00302 }
00303 
00304 void GlslCode::emit_exp10(const ShStatement& stmt)
00305 {
00306   SH_DEBUG_ASSERT(SH_OP_EXP10 == stmt.op);
00307   ShVariable c(allocate_constant(stmt, 1.0/std::log10(2.0)));
00308   append_line(resolve(stmt.dest) + " = exp2(" + resolve(c) + " * " + resolve(stmt.src[0]) + ")");
00309 }
00310 
00311 void GlslCode::emit_hyperbolic(const ShStatement& stmt)
00312 {
00313   ShVariable two(allocate_constant(stmt, 2));
00314   ShVariable e(allocate_constant(stmt, M_E));
00315 
00316   ShVariable e_plusX(allocate_temp(stmt));
00317   ShVariable e_minusX(allocate_temp(stmt));
00318   emit_pow(e_plusX, e, stmt.src[0]);
00319   append_line(resolve(e_minusX) + " = pow(" + resolve(e) + ", -(" + resolve(stmt.src[0]) + "))");
00320 
00321   switch (stmt.op) {
00322   case SH_OP_COSH:
00323     // cosh x = [e^x + e^-x] / 2
00324     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " + " + resolve(e_minusX) + ") / " + resolve(two));
00325     break;
00326   case SH_OP_SINH:
00327     // sinh x = [e^x - e^-x] / 2
00328     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " - " + resolve(e_minusX) + ") / " + resolve(two));
00329     break;
00330   case SH_OP_TANH:
00331     // tanh x = sinh x / cosh x = [e^x - e^-x] / [e^x + e^-x]
00332     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " - " + resolve(e_minusX) + ") / " +
00333                                         "(" + resolve(e_plusX) + " + " + resolve(e_minusX) + ")");
00334     break;
00335   default:
00336     SH_DEBUG_ASSERT(0);
00337   }
00338 }
00339 
00340 void GlslCode::emit_lit(const ShStatement& stmt)
00341 {
00342   SH_DEBUG_ASSERT(SH_OP_LIT == stmt.op);
00343   
00344   // Result according to OpenGL spec
00345   append_line(resolve(stmt.dest, 0) + " = " + resolve_constant(1, stmt.dest, 1));
00346 
00347   append_line(resolve(stmt.dest, 1) + " = max(" + resolve_constant(0, stmt.dest, 1) +
00348               ", " + resolve(stmt.src[0], 0) + ")");
00349 
00350   append_line(resolve(stmt.dest, 2) + " = " + resolve(stmt.src[0], 0) + 
00351               " > " + resolve_constant(0, stmt.src[0], 1) + " ? pow(max(" + 
00352               resolve_constant(0, stmt.dest, 1) + ", " + resolve(stmt.src[0], 1) + 
00353               "), clamp(" + resolve(stmt.src[0], 2) + ", " + 
00354               resolve_constant(-128, stmt.dest, 1) + ", " + 
00355               resolve_constant(128, stmt.dest, 1) + ")) : " + resolve_constant(0, stmt.dest, 1));
00356 
00357   append_line(resolve(stmt.dest, 3) + " = " + resolve_constant(1, stmt.dest, 1));
00358 }
00359 
00360 void GlslCode::emit_log10(const ShStatement& stmt)
00361 {
00362   SH_DEBUG_ASSERT(SH_OP_LOG10 == stmt.op);
00363   ShVariable c(allocate_constant(stmt, std::log10(2.0))); 
00364   append_line(resolve(stmt.dest) + " = log2(" + resolve(stmt.src[0]) + ") * " + resolve(c) + "");
00365 }
00366 
00367 void GlslCode::emit_logic(const ShStatement& stmt)
00368 {
00369   GlslMapping mapping;
00370   mapping.op = stmt.op;
00371 
00372   if ((stmt.src[0].size() > 1) || (stmt.src[1].size() > 1) ) {
00373     switch (stmt.op) {
00374     case SH_OP_SEQ:
00375       mapping.code = "equal($0, $1)";
00376       break;
00377     case SH_OP_SGT:
00378       mapping.code = "greaterThan($0, $1)";
00379       break;
00380     case SH_OP_SGE:
00381       mapping.code = "greaterThanEqual($0, $1)";
00382       break;
00383     case SH_OP_SNE:
00384       mapping.code = "notEqual($0, $1)";
00385       break;
00386     case SH_OP_SLT:
00387       mapping.code = "lessThan($0, $1)";
00388       break;
00389     case SH_OP_SLE:
00390       mapping.code = "lessThanEqual($0, $1)";
00391       break;
00392     default:
00393       SH_DEBUG_ASSERT(0);
00394     }
00395   } else {
00396     switch (stmt.op) {
00397     case SH_OP_SEQ:
00398       mapping.code = "$0 == $1";
00399       break;
00400     case SH_OP_SGT:
00401       mapping.code = "$0 > $1";
00402       break;
00403     case SH_OP_SGE:
00404       mapping.code = "$0 >= $1";
00405       break;
00406     case SH_OP_SNE:
00407       mapping.code = "$0 != $1";
00408       break;
00409     case SH_OP_SLT:
00410       mapping.code = "$0 < $1";
00411       break;
00412     case SH_OP_SLE:
00413       mapping.code = "$0 <= $1";
00414       break;
00415     default:
00416       SH_DEBUG_ASSERT(0);
00417     }
00418   }
00419 
00420   // TODO: cache these mappings?
00421   
00422   table_substitution(stmt, mapping);
00423 }
00424 
00425 void GlslCode::emit_noise(const ShStatement& stmt)
00426 {
00427   SH_DEBUG_ASSERT(SH_OP_NOISE == stmt.op);
00428 
00429   int output_size = stmt.dest.size();
00430   SH_DEBUG_ASSERT((output_size <= 4) && (output_size >= 1));
00431   
00432   stringstream line;
00433   line << resolve(stmt.dest) << " = " << "noise" << output_size << "("
00434        << resolve(stmt.src[0]) << ")";
00435   append_line(line.str());
00436 }
00437 
00438 void GlslCode::emit_pal(const ShStatement& stmt)
00439 {
00440   SH_DEBUG_ASSERT(SH_OP_PAL == stmt.op);
00441 
00442   append_line(resolve(stmt.dest) + " = " + resolve(stmt.src[0], stmt.src[1]));
00443 }
00444 
00445 void GlslCode::emit_prod(const ShStatement& stmt)
00446 {
00447   SH_DEBUG_ASSERT(SH_OP_CMUL == stmt.op);
00448 
00449   ShVariable temp(allocate_temp(stmt, 1));
00450   append_line(resolve(temp) + " = " + resolve(stmt.src[0], 0));
00451 
00452   int size = stmt.src[0].size();
00453   for (int i=1; i < size; i++) {
00454     append_line(resolve(temp) + " *= " + resolve(stmt.src[0], i));
00455   }
00456 
00457   append_line(resolve(stmt.dest) + " = " + resolve(temp));
00458 }
00459 
00460 void GlslCode::emit_sum(const ShStatement& stmt)
00461 {
00462   SH_DEBUG_ASSERT(SH_OP_CSUM == stmt.op);
00463 
00464   ShVariable temp(allocate_temp(stmt, 1));
00465   append_line(resolve(temp) + " = " + resolve(stmt.src[0], 0));
00466 
00467   int size = stmt.src[0].size();
00468   for (int i=1; i < size; i++) {
00469     append_line(resolve(temp) + " += " + resolve(stmt.src[0], i));
00470   }
00471 
00472   append_line(resolve(stmt.dest) + " = " + resolve(temp));
00473 }
00474 
00475 void GlslCode::emit_texture(const ShStatement& stmt)
00476 {
00477   SH_DEBUG_ASSERT((SH_OP_TEX == stmt.op) || (SH_OP_TEXI == stmt.op));
00478 
00479   stringstream line;
00480   line << resolve(stmt.dest) << " = texture";
00481 
00482   ShTextureNodePtr texture = shref_dynamic_cast<ShTextureNode>(stmt.src[0].node());
00483   switch (texture->dims()) {
00484   case SH_TEXTURE_1D:
00485     line << "1D";
00486     break;
00487   case SH_TEXTURE_2D:
00488     line << "2D";
00489     break;
00490   case SH_TEXTURE_3D:
00491     line << "3D";
00492     break;
00493   case SH_TEXTURE_CUBE:
00494     line << "Cube";
00495     break;
00496   case SH_TEXTURE_RECT:
00497     line << "2DRect"; // Not supported on ATI, but there is no equivalent
00498     break;
00499   }
00500 
00501   line << "(" << resolve(stmt.src[0]) << ", " << resolve(stmt.src[1]) << ")";
00502   if (2 == texture->size()) {
00503     line << ".xw"; // 2-component inputs are uploaded as GL_LUMINANCE_ALPHA
00504   } else if (texture->size() != 4) {
00505     line << ".";
00506     for (int i = 0; i < texture->size(); i++) {
00507       line << "xyzw"[i];
00508     }
00509   }
00510 
00511   append_line(line.str());
00512 }
00513 
00514 }

Generated on Wed Nov 9 15:29:24 2005 for Sh by  doxygen 1.4.5