Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

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

Generated on Thu Jul 28 17:33:00 2005 for Sh by  doxygen 1.4.3-20050530