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 software is provided 'as-is', without any express or implied
00006 // warranty. In no event will the authors be held liable for any damages
00007 // arising from the use of this software.
00008 // 
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it
00011 // freely, subject to the following restrictions:
00012 // 
00013 // 1. The origin of this software must not be misrepresented; you must
00014 // not claim that you wrote the original software. If you use this
00015 // software in a product, an acknowledgment in the product documentation
00016 // would be appreciated but is not required.
00017 // 
00018 // 2. Altered source versions must be plainly marked as such, and must
00019 // not be misrepresented as being the original software.
00020 // 
00021 // 3. This notice may not be removed or altered from any source
00022 // distribution.
00024 #include "GlslCode.hpp"
00025 #include <iostream>
00026 #include <cmath>
00027 
00028 namespace shgl {
00029 
00030 using namespace SH;
00031 using namespace std;
00032 
00033 static GlslMapping opCodeTable[] = {
00034   {SH_OP_ABS,   "abs($0)"},
00035   {SH_OP_ACOS,  "acos($0)"},
00036   {SH_OP_ADD,   "$0 + $1"},
00037   {SH_OP_ASIN,  "asin($0)"},
00038   {SH_OP_ASN,   "$0"},
00039   {SH_OP_ATAN,  "atan($0)"},
00040   //{SH_OP_ATAN2, "atan($1 / $0)"}, // FIXME
00041   {SH_OP_CEIL,  "ceil($0)"},
00042   {SH_OP_COS,   "cos($0)"},
00043   {SH_OP_DOT,   "dot($0, $1)"},
00044   {SH_OP_DIV,   "$0 / $1"},
00045   {SH_OP_DX,    "dFdx($0)"},
00046   {SH_OP_DY,    "dFdy($0)"},
00047   {SH_OP_EXP2,  "exp2($0)"},
00048   {SH_OP_FLR,   "floor($0)"},
00049   {SH_OP_FRAC,  "fract($0)"},
00050   {SH_OP_LOG2,  "log2($0)"},
00051   {SH_OP_LRP,   "mix($2, $1, $0)"},
00052   {SH_OP_MAD,   "$0 * $1 + $2"},
00053   {SH_OP_MAX,   "max($0, $1)"},
00054   {SH_OP_MIN,   "min($0, $1)"},
00055   {SH_OP_MOD,   "mod($0, $1)"},
00056   {SH_OP_MUL,   "$0 * $1"},
00057   {SH_OP_NEG,   "-($0)"},
00058   {SH_OP_NORM,  "normalize($0)"},
00059   {SH_OP_POW,   "pow($0, $1)"},
00060   {SH_OP_RCP,   "1.0 / $0"},
00061   {SH_OP_RND,   "floor($0 + 0.5)"},
00062   {SH_OP_RSQ,   "inversesqrt($0)"},
00063   {SH_OP_SGN,   "sign($0)"},
00064   {SH_OP_SIN,   "sin($0)"},
00065   {SH_OP_SQRT,  "sqrt($0)"},
00066   {SH_OP_TAN,   "tan($0)"},
00067   {SH_OP_XPD,   "cross($0, $1)"},
00068 
00069   {SH_OPERATION_END,  0} 
00070 };
00071 
00072 typedef map<SH::ShOperation, GlslOpCodeVecs> GlslOpCodeMap;
00073 
00074 GlslOpCodeVecs::GlslOpCodeVecs(const GlslMapping& mapping)
00075 {
00076   string code(mapping.code);
00077 
00078   // Dices up the code string into references #i or $i to 
00079   // src variables and the code fragments between references. 
00080   unsigned i, j;
00081   i = j = 0;
00082   for (; (j = code.find_first_of("$", i)) != string::npos;) {
00083     frag.push_back(code.substr(i, j - i));
00084     i = j + 1;
00085     j = code.find_first_not_of("012345689", i);
00086     index.push_back(atoi(code.substr(i, j - i).c_str()));
00087     i = j;
00088   }
00089   if (i == string::npos) {
00090     frag.push_back("");
00091   } else {
00092     frag.push_back(code.substr(i));
00093   }
00094 }
00095 
00096 void GlslCode::table_substitution(const ShStatement& stmt, GlslOpCodeVecs codeVecs)
00097 {
00098   stringstream line;
00099   line << resolve(stmt.dest) << " = ";
00100   line << glsl_typename(stmt.dest.valueType(), stmt.dest.size()) << "("; // cast for the destination size
00101   
00102   unsigned i=0;
00103   if (SH_OP_ASN == stmt.op) {
00104     // assignments should not be cast twice
00105     const ShVariable& src = stmt.src[codeVecs.index[i]];
00106     line << codeVecs.frag[i] << resolve(src);
00107   } 
00108   else {
00109     // find the size of the biggest parameter
00110     int param_size=0;
00111     for (i=0; i < codeVecs.index.size(); i++) {
00112       const ShVariable& src = stmt.src[codeVecs.index[i]];
00113       if (src.size() > param_size) {
00114         param_size = src.size();
00115       }
00116     }
00117 
00118     // print each parameter
00119     for (i=0; i < codeVecs.index.size(); i++) { 
00120       const ShVariable& src = stmt.src[codeVecs.index[i]];
00121       line << codeVecs.frag[i] << glsl_typename(src.valueType(), param_size)
00122            << "(" << resolve(src) << ")";
00123     }
00124   }
00125 
00126   line << codeVecs.frag[i]; // code fragment after the last variable
00127 
00128   line << ")";
00129   append_line(line.str());
00130 }
00131 
00132 void GlslCode::emit(const ShStatement &stmt)
00133 {
00134   static GlslOpCodeMap opCodeMap;
00135 
00136   // Lazily fill-in opCodeMap from the above table
00137   if (opCodeMap.empty()) {
00138     for (int i=0; opCodeTable[i].op != SH_OPERATION_END; i++) {
00139       opCodeMap[opCodeTable[i].op] = GlslOpCodeVecs(opCodeTable[i]);
00140     }
00141   }
00142 
00143   if(opCodeMap.find(stmt.op) != opCodeMap.end()) {
00144     // Handle ops in the table first  
00145     table_substitution(stmt, opCodeMap[stmt.op]);
00146   } 
00147   else {
00148     // Handle the rest of the operations
00149     switch(stmt.op) {
00150     case SH_OP_CBRT:
00151       emit_cbrt(stmt);
00152       break;
00153     case SH_OP_CMUL:
00154       emit_prod(stmt);
00155       break;
00156     case SH_OP_CSUM:
00157       emit_sum(stmt);
00158       break;
00159     case SH_OP_COND:
00160       emit_cond(stmt);
00161       break;
00162     case SH_OP_EXP:
00163       emit_exp(stmt, M_E);
00164       break;
00165     case SH_OP_EXP10:
00166       emit_exp(stmt, 10);
00167       break;
00168     case SH_OP_KIL:
00169       emit_discard(stmt);
00170       break;
00171     case SH_OP_LIT:
00172       emit_lit(stmt);
00173       break;
00174     case SH_OP_LOG:
00175       emit_log(stmt, M_E);
00176       break;
00177     case SH_OP_LOG10:
00178       emit_log(stmt, 10);
00179       break;
00180     case SH_OP_PAL:
00181       emit_pal(stmt);
00182       break;
00183     case SH_OP_SEQ:
00184     case SH_OP_SGE:
00185     case SH_OP_SGT:
00186     case SH_OP_SLE:
00187     case SH_OP_SLT:
00188     case SH_OP_SNE:
00189       emit_logic(stmt);
00190       break;
00191     case SH_OP_TEX:
00192     case SH_OP_TEXI:
00193       emit_texture(stmt);
00194       break;
00195     case SH_OP_COSH:
00196     case SH_OP_SINH:
00197     case SH_OP_TANH:
00198       emit_hyperbolic(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   append_line(resolve(stmt.dest) + " = pow(" + resolve(stmt.src[0]) + ", " + resolve(temp) + ")");
00255 }
00256 
00257 void GlslCode::emit_cond(const ShStatement& stmt)
00258 {
00259   SH_DEBUG_ASSERT(SH_OP_COND == stmt.op);
00260   static string vendor((char*)glGetString(GL_VENDOR));
00261 
00262   const int size = stmt.src[0].size();
00263   if (("NVIDIA Corporation" == vendor) || (1 == size)) {
00264     // CG has a component-wise COND operator
00265     append_line(resolve(stmt.dest) + " = (" + resolve(stmt.src[0]) + " > " + 
00266                 resolve_constant(0, stmt.src[0]) + ") ? " + resolve(stmt.src[1]) +
00267                 " : " + resolve(stmt.src[2]));
00268   } else {
00269     // The Glsl spec requires the first argument to COND to be a scalar
00270     for (int i=0; i < size; i++) {
00271       append_line(resolve(stmt.dest, i) + " = (" + resolve(stmt.src[0], i) + " > " + 
00272                   resolve_constant(0, stmt.src[0], 1) + ") ? " + resolve(stmt.src[1], i) +
00273                   " : " + resolve(stmt.src[2], i));
00274     }
00275   }
00276 }
00277 
00278 void GlslCode::emit_discard(const ShStatement& stmt)
00279 {
00280   SH_DEBUG_ASSERT(SH_OP_KIL == stmt.op);
00281 
00282   append_line(string("if (") + resolve(stmt.src[0]) + " > " + 
00283               resolve_constant(0, stmt.src[0]) + ") discard");
00284 }
00285 
00286 void GlslCode::emit_exp(const ShStatement& stmt, double power)
00287 {
00288   SH_DEBUG_ASSERT((SH_OP_EXP == stmt.op) || (SH_OP_EXP10 == stmt.op));
00289 
00290   ShVariable temp(allocate_constant(stmt, power));
00291   append_line(resolve(stmt.dest) + " = pow(" + resolve(temp) + ", " + resolve(stmt.src[0]) + ")");
00292 }
00293 
00294 void GlslCode::emit_hyperbolic(const ShStatement& stmt)
00295 {
00296   ShVariable two(allocate_constant(stmt, 2));
00297   ShVariable e(allocate_constant(stmt, M_E));
00298 
00299   ShVariable e_plusX(allocate_temp(stmt));
00300   ShVariable e_minusX(allocate_temp(stmt));
00301   append_line(resolve(e_plusX) + " = pow(" + resolve(e) + ", " + resolve(stmt.src[0]) + ")");
00302   append_line(resolve(e_minusX) + " = pow(" + resolve(e) + ", -(" + resolve(stmt.src[0]) + "))");
00303 
00304   switch (stmt.op) {
00305   case SH_OP_COSH:
00306     // cosh x = [e^x + e^-x] / 2
00307     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " + " + resolve(e_minusX) + ") / " + resolve(two));
00308     break;
00309   case SH_OP_SINH:
00310     // sinh x = [e^x - e^-x] / 2
00311     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " - " + resolve(e_minusX) + ") / " + resolve(two));
00312     break;
00313   case SH_OP_TANH:
00314     // tanh x = sinh x / cosh x = [e^x - e^-x] / [e^x + e^-x]
00315     append_line(resolve(stmt.dest) + " = (" + resolve(e_plusX) + " - " + resolve(e_minusX) + ") / " +
00316                                         "(" + resolve(e_plusX) + " + " + resolve(e_minusX) + ")");
00317     break;
00318   default:
00319     SH_DEBUG_ASSERT(0);
00320   }
00321 }
00322 
00323 void GlslCode::emit_lit(const ShStatement& stmt)
00324 {
00325   SH_DEBUG_ASSERT(SH_OP_LIT == stmt.op);
00326   
00327   // Result according to OpenGL spec
00328   append_line(resolve(stmt.dest, 0) + " = " + resolve_constant(1, stmt.dest, 1));
00329 
00330   append_line(resolve(stmt.dest, 1) + " = max(" + resolve_constant(0, stmt.dest, 1) +
00331               ", " + resolve(stmt.src[0], 0) + ")");
00332 
00333   append_line(resolve(stmt.dest, 2) + " = " + resolve(stmt.src[0], 0) + 
00334               " > " + resolve_constant(0, stmt.src[0], 1) + " ? pow(max(" + 
00335               resolve_constant(0, stmt.dest, 1) + ", " + resolve(stmt.src[0], 1) + 
00336               "), clamp(" + resolve(stmt.src[0], 2) + ", " + 
00337               resolve_constant(-128, stmt.dest, 1) + ", " + 
00338               resolve_constant(128, stmt.dest, 1) + ")) : " + resolve_constant(0, stmt.dest, 1));
00339 
00340   append_line(resolve(stmt.dest, 3) + " = " + resolve_constant(1, stmt.dest, 1));
00341 }
00342 
00343 void GlslCode::emit_log(const ShStatement& stmt, double base)
00344 {
00345   SH_DEBUG_ASSERT((SH_OP_LOG == stmt.op) || (SH_OP_LOG10 == stmt.op));
00346 
00347   const double log2_base = log(base) / log(2.0);
00348 
00349   ShVariable temp(allocate_constant(stmt, log2_base)); 
00350   append_line(resolve(stmt.dest) + " = log2(" + resolve(stmt.src[0]) + ") / " + resolve(temp) + "");
00351 }
00352 
00353 void GlslCode::emit_logic(const ShStatement& stmt)
00354 {
00355   GlslMapping mapping;
00356   mapping.op = stmt.op;
00357 
00358   if ((stmt.src[0].size() > 1) || (stmt.src[1].size() > 1) ) {
00359     switch (stmt.op) {
00360     case SH_OP_SEQ:
00361       mapping.code = "equal($0, $1)";
00362       break;
00363     case SH_OP_SGT:
00364       mapping.code = "greaterThan($0, $1)";
00365       break;
00366     case SH_OP_SGE:
00367       mapping.code = "greaterThanEqual($0, $1)";
00368       break;
00369     case SH_OP_SNE:
00370       mapping.code = "notEqual($0, $1)";
00371       break;
00372     case SH_OP_SLT:
00373       mapping.code = "lessThan($0, $1)";
00374       break;
00375     case SH_OP_SLE:
00376       mapping.code = "lessThanEqual($0, $1)";
00377       break;
00378     default:
00379       SH_DEBUG_ASSERT(0);
00380     }
00381   } else {
00382     switch (stmt.op) {
00383     case SH_OP_SEQ:
00384       mapping.code = "$0 == $1";
00385       break;
00386     case SH_OP_SGT:
00387       mapping.code = "$0 > $1";
00388       break;
00389     case SH_OP_SGE:
00390       mapping.code = "$0 >= $1";
00391       break;
00392     case SH_OP_SNE:
00393       mapping.code = "$0 != $1";
00394       break;
00395     case SH_OP_SLT:
00396       mapping.code = "$0 < $1";
00397       break;
00398     case SH_OP_SLE:
00399       mapping.code = "$0 <= $1";
00400       break;
00401     default:
00402       SH_DEBUG_ASSERT(0);
00403     }
00404   }
00405 
00406   // TODO: cache these mappings?
00407   
00408   table_substitution(stmt, mapping);
00409 }
00410 
00411 void GlslCode::emit_pal(const ShStatement& stmt)
00412 {
00413   SH_DEBUG_ASSERT(SH_OP_PAL == stmt.op);
00414 
00415   append_line(resolve(stmt.dest) + " = " + resolve(stmt.src[0], stmt.src[1]));
00416 }
00417 
00418 void GlslCode::emit_prod(const ShStatement& stmt)
00419 {
00420   SH_DEBUG_ASSERT(SH_OP_CMUL == stmt.op);
00421 
00422   ShVariable temp(allocate_temp(stmt, 1));
00423   append_line(resolve(temp) + " = " + resolve(stmt.src[0], 0));
00424 
00425   int size = stmt.src[0].size();
00426   for (int i=1; i < size; i++) {
00427     append_line(resolve(temp) + " *= " + resolve(stmt.src[0], i));
00428   }
00429 
00430   append_line(resolve(stmt.dest) + " = " + resolve(temp));
00431 }
00432 
00433 void GlslCode::emit_sum(const ShStatement& stmt)
00434 {
00435   SH_DEBUG_ASSERT(SH_OP_CSUM == stmt.op);
00436 
00437   ShVariable temp(allocate_temp(stmt, 1));
00438   append_line(resolve(temp) + " = " + resolve(stmt.src[0], 0));
00439 
00440   int size = stmt.src[0].size();
00441   for (int i=1; i < size; i++) {
00442     append_line(resolve(temp) + " += " + resolve(stmt.src[0], i));
00443   }
00444 
00445   append_line(resolve(stmt.dest) + " = " + resolve(temp));
00446 }
00447 
00448 void GlslCode::emit_texture(const ShStatement& stmt)
00449 {
00450   SH_DEBUG_ASSERT((SH_OP_TEX == stmt.op) || (SH_OP_TEXI == stmt.op));
00451 
00452   stringstream line;
00453   line << resolve(stmt.dest) << " = texture";
00454 
00455   ShTextureNodePtr texture = shref_dynamic_cast<ShTextureNode>(stmt.src[0].node());
00456   switch (texture->dims()) {
00457   case SH_TEXTURE_1D:
00458     line << "1D";
00459     break;
00460   case SH_TEXTURE_2D:
00461     line << "2D";
00462     break;
00463   case SH_TEXTURE_3D:
00464     line << "3D";
00465     break;
00466   case SH_TEXTURE_CUBE:
00467     line << "Cube";
00468     break;
00469   case SH_TEXTURE_RECT:
00470     line << "2DRect"; // Not supported on ATI, but there is no equivalent
00471     break;
00472   }
00473 
00474   line << "(" << resolve(stmt.src[0]) << ", " << resolve(stmt.src[1]) << ")";
00475   if (2 == texture->size()) {
00476     line << ".xw"; // 2-component inputs are uploaded as GL_LUMINANCE_ALPHA
00477   } else if (texture->size() != 4) {
00478     line << ".";
00479     for (int i = 0; i < texture->size(); i++) {
00480       line << "xyzw"[i];
00481     }
00482   }
00483 
00484   append_line(line.str());
00485 }
00486 
00487 }

Generated on Wed Jun 15 18:12:37 2005 for Sh by  doxygen 1.4.3-20050530