1 /*
   2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.scenario.effect.compiler.backend.sw.sse;
  27 
  28 import java.io.InputStreamReader;
  29 import java.io.Reader;
  30 import java.util.Collection;
  31 import java.util.Comparator;
  32 import java.util.HashMap;
  33 import java.util.HashSet;
  34 import java.util.Map;
  35 import java.util.Set;
  36 import java.util.SortedSet;
  37 import java.util.TreeSet;
  38 import com.sun.scenario.effect.compiler.JSLParser;
  39 import com.sun.scenario.effect.compiler.model.BaseType;
  40 import com.sun.scenario.effect.compiler.model.Qualifier;
  41 import com.sun.scenario.effect.compiler.model.Type;
  42 import com.sun.scenario.effect.compiler.model.Variable;
  43 import com.sun.scenario.effect.compiler.tree.FuncDef;
  44 import com.sun.scenario.effect.compiler.tree.ProgramUnit;
  45 import com.sun.scenario.effect.compiler.tree.TreeScanner;
  46 import org.antlr.stringtemplate.StringTemplate;
  47 import org.antlr.stringtemplate.StringTemplateGroup;
  48 import org.antlr.stringtemplate.language.DefaultTemplateLexer;
  49 
  50 /**
  51  */
  52 public class SSEBackend extends TreeScanner {
  53 
  54     private final JSLParser parser;
  55     private final String body;
  56 
  57     public SSEBackend(JSLParser parser, ProgramUnit program) {
  58         // TODO: will be removed once we clean up static usage
  59         resetStatics();
  60 
  61         this.parser = parser;
  62         
  63         SSETreeScanner scanner = new SSETreeScanner();
  64         scanner.scan(program);
  65         this.body = scanner.getResult();
  66     }
  67     
  68     public static class GenCode {
  69         public String javaCode;
  70         public String nativeCode;
  71     }
  72 
  73     private static void appendGetRelease(StringBuilder get,
  74                                          StringBuilder rel,
  75                                          String ctype,
  76                                          String cbufName, String jarrayName)
  77     {
  78         get.append("j" + ctype + " *" + cbufName + " = (j" + ctype + " *)");
  79         get.append("env->GetPrimitiveArrayCritical(" + jarrayName + ", 0);\n");
  80         get.append("if (" + cbufName + " == NULL) return;\n");
  81         rel.append("env->ReleasePrimitiveArrayCritical(" + jarrayName + ", " + cbufName + ", JNI_ABORT);\n");
  82     }
  83 
  84     private static SortedSet<Variable> getSortedVars(Collection<Variable> unsortedVars) {
  85         Comparator<Variable> c = new Comparator<Variable>() {
  86             public int compare(Variable v0, Variable v1) {
  87                 return v0.getName().compareTo(v1.getName());
  88             }
  89         };
  90         SortedSet<Variable> sortedVars = new TreeSet<Variable>(c);
  91         sortedVars.addAll(unsortedVars);
  92         return sortedVars;
  93     }
  94 
  95     public final GenCode getGenCode(String effectName,
  96                                     String peerName,
  97                                     String genericsName,
  98                                     String interfaceName)
  99     {
 100         Map<String, Variable> vars = parser.getSymbolTable().getGlobalVariables();
 101         StringBuilder genericsDecl = new StringBuilder();
 102         StringBuilder interfaceDecl = new StringBuilder();
 103         StringBuilder constants = new StringBuilder();
 104         StringBuilder samplers = new StringBuilder();
 105         StringBuilder cleanup = new StringBuilder();
 106         StringBuilder srcRects = new StringBuilder();
 107         StringBuilder posDecls = new StringBuilder();
 108         StringBuilder pixInitY = new StringBuilder();
 109         StringBuilder pixInitX = new StringBuilder();
 110         StringBuilder posIncrY = new StringBuilder();
 111         StringBuilder posInitY = new StringBuilder();
 112         StringBuilder posIncrX = new StringBuilder();
 113         StringBuilder posInitX = new StringBuilder();
 114         StringBuilder jparams = new StringBuilder();
 115         StringBuilder jparamDecls = new StringBuilder();
 116         StringBuilder cparamDecls = new StringBuilder();
 117         StringBuilder arrayGet = new StringBuilder();
 118         StringBuilder arrayRelease = new StringBuilder();
 119 
 120         appendGetRelease(arrayGet, arrayRelease, "int", "dst", "dst_arr");
 121         
 122         // TODO: only need to declare these if pixcoord is referenced
 123         // somewhere in the program...
 124         pixInitY.append("float pixcoord_y = (float)dy;\n");
 125         pixInitX.append("float pixcoord_x = (float)dx;\n");
 126 
 127         // this step isn't strictly necessary but helps give some predictability
 128         // to the generated jar/nativelib so that the method signatures have
 129         // a consistent parameter ordering on all platforms for each build,
 130         // which may help debugging (see RT-4475)
 131         SortedSet<Variable> sortedVars = getSortedVars(vars.values());
 132         for (Variable v : sortedVars) {
 133             if (v.getQualifier() == Qualifier.CONST && v.getConstValue() == null) {
 134                 // this must be a special built-in variable (e.g. pos0);
 135                 // these are handled elsewhere, so just continue...
 136                 continue;
 137             }
 138             
 139             Type t = v.getType();
 140             BaseType bt = t.getBaseType();
 141             String vtype = bt.toString();
 142             String vname = v.getName();
 143             if (v.getQualifier() != null && bt != BaseType.SAMPLER) {
 144                 String accName = v.getAccessorName();
 145                 if (v.isArray()) {
 146                     // TODO: we currently assume that param arrays will be
 147                     // stored in NIO Int/FloatBuffers, but the inner loop
 148                     // expects to access them as Java arrays, so we convert
 149                     // here; this is obviously bad for performance, so we need
 150                     // to come up with a better system soon...
 151                     String bufType = (bt == BaseType.FLOAT) ?
 152                         "FloatBuffer" : "IntBuffer";
 153                     String bufName = vname + "_buf";
 154                     String arrayName = vname + "_arr";
 155                     constants.append(bufType + " " + bufName + " = " + accName + "();\n");
 156                     constants.append(vtype + "[] " + arrayName);
 157                     constants.append(" = new " + vtype + "[");
 158                     constants.append(bufName + ".capacity()];\n");
 159                     constants.append(bufName + ".get(" + arrayName + ");\n");
 160                     jparams.append(",\n");
 161                     jparams.append(arrayName);
 162                     jparamDecls.append(",\n");
 163                     jparamDecls.append(vtype + "[] " + vname);
 164                     cparamDecls.append(",\n");
 165                     cparamDecls.append("j" + vtype + "Array " + vname);
 166                     appendGetRelease(arrayGet, arrayRelease, vtype, arrayName, vname);
 167                 } else {
 168                     if (t.isVector()) {
 169                         String arrayName = vname + "_arr";
 170                         constants.append(vtype + "[] " + arrayName + " = " + accName + "();\n");
 171                         jparams.append(",\n");
 172                         jparamDecls.append(",\n");
 173                         cparamDecls.append(",\n");
 174                         for (int i = 0; i < t.getNumFields(); i++) {
 175                             if (i > 0) {
 176                                 jparams.append(", ");
 177                                 jparamDecls.append(", ");
 178                                 cparamDecls.append(", ");
 179                             }
 180                             String vn = vname + getSuffix(i);
 181                             jparams.append(arrayName + "[" + i + "]");
 182                             jparamDecls.append(vtype + " " + vn);
 183                             cparamDecls.append("j" + vtype + " " + vn);
 184                         }
 185                     } else {
 186                         constants.append(vtype + " " + vname);
 187                         if (v.getQualifier() == Qualifier.CONST) {
 188                             constants.append(" = " + v.getConstValue());
 189                         } else {
 190                             constants.append(" = " + accName + "()");
 191                         }
 192                         constants.append(";\n");
 193                         jparams.append(",\n");
 194                         jparams.append(vname);
 195                         jparamDecls.append(",\n");
 196                         jparamDecls.append(vtype + " " + vname);
 197                         cparamDecls.append(",\n");
 198                         cparamDecls.append("j" + vtype + " " + vname);
 199                     }
 200                 }
 201             } else if (v.getQualifier() == Qualifier.PARAM && bt == BaseType.SAMPLER) {
 202                 int i = v.getReg();
 203                 if (t == Type.FSAMPLER) {
 204                     samplers.append("FloatMap src" + i + " = (FloatMap)getSamplerData(" + i + ");\n");
 205                     samplers.append("int src" + i + "x = 0;\n");
 206                     samplers.append("int src" + i + "y = 0;\n");
 207                     samplers.append("int src" + i + "w = src" + i + ".getWidth();\n");
 208                     samplers.append("int src" + i + "h = src" + i + ".getHeight();\n");
 209                     samplers.append("int src" + i + "scan = src" + i + ".getWidth();\n");
 210                     samplers.append("float[] " + vname + " = src" + i + ".getData();\n");
 211 
 212                     arrayGet.append("float " + vname + "_vals[4];\n");
 213 
 214                     // TODO: for now, assume [0,0,1,1]
 215                     srcRects.append("float[] src" + i + "Rect = new float[] {0,0,1,1};\n");
 216                     
 217                     jparams.append(",\n");
 218                     jparams.append(vname);
 219                     
 220                     jparamDecls.append(",\n");
 221                     jparamDecls.append("float[] " + vname + "_arr");
 222                     
 223                     cparamDecls.append(",\n");
 224                     cparamDecls.append("jfloatArray " + vname + "_arr");
 225                     
 226                     appendGetRelease(arrayGet, arrayRelease, "float", vname, vname + "_arr");
 227                 } else {
 228                     if (t == Type.LSAMPLER) {
 229                         samplers.append("HeapImage src" + i + " = (HeapImage)inputs[" + i + "].getUntransformedImage();\n");
 230                     } else {
 231                         samplers.append("HeapImage src" + i + " = (HeapImage)inputs[" + i + "].getTransformedImage(dstBounds);\n");
 232                         cleanup.append("inputs[" + i + "].releaseTransformedImage(src" + i + ");\n");
 233                     }
 234                     samplers.append("int src" + i + "x = 0;\n");
 235                     samplers.append("int src" + i + "y = 0;\n");
 236                     samplers.append("int src" + i + "w = src" + i + ".getPhysicalWidth();\n");
 237                     samplers.append("int src" + i + "h = src" + i + ".getPhysicalHeight();\n");
 238                     samplers.append("int src" + i + "scan = src" + i + ".getScanlineStride();\n");
 239                     samplers.append("int[] " + vname + " =\n");
 240                     samplers.append("    src" + i + ".getPixelArray();\n");
 241 
 242                     samplers.append("Rectangle src" + i + "Bounds = new Rectangle(");
 243                     samplers.append("src" + i + "x, ");
 244                     samplers.append("src" + i + "y, ");
 245                     samplers.append("src" + i + "w, ");
 246                     samplers.append("src" + i + "h);\n");
 247                     if (t == Type.LSAMPLER) {
 248                         samplers.append("Rectangle src" + i + "InputBounds = inputs[" + i + "].getUntransformedBounds();\n");
 249                         samplers.append("BaseTransform src" + i + "Transform = inputs[" + i + "].getTransform();\n");
 250                     } else {
 251                         samplers.append("Rectangle src" + i + "InputBounds = inputs[" + i + "].getTransformedBounds(dstBounds);\n");
 252                         samplers.append("BaseTransform src" + i + "Transform = BaseTransform.IDENTITY_TRANSFORM;\n");
 253                     }
 254                     samplers.append("setInputBounds(" + i + ", src" + i + "InputBounds);\n");
 255                     samplers.append("setInputNativeBounds(" + i + ", src" + i + "Bounds);\n");
 256 
 257                     if (t == Type.LSAMPLER) {
 258                         arrayGet.append("float " + vname + "_vals[4];\n");
 259                     }
 260 
 261                     // the source rect decls need to come after all calls to
 262                     // setInput[Native]Bounds() for all inputs (since the
 263                     // getSourceRegion() impl may need to query the bounds of
 264                     // other inputs, as is the case in PhongLighting)...
 265                     srcRects.append("float[] src" + i + "Rect = new float[4];\n");
 266                     // Note that we only allocate 4 floats here because none
 267                     // of the loops can deal with fully mapped inputs.  Only
 268                     // shaders that declare LSAMPLERs would require mapped
 269                     // inputs and so far that is only Perspective and
 270                     // Displacement, both of which override getTC() and return
 271                     // only 4 values.
 272                     srcRects.append("getTextureCoordinates(" + i + ", src" + i + "Rect,\n");
 273                     srcRects.append("                      src" + i + "InputBounds.x, src" + i + "InputBounds.y,\n");
 274                     srcRects.append("                      src" + i + "w, src" + i + "h,\n");
 275                     srcRects.append("                      dstBounds, src" + i + "Transform);\n");
 276 
 277                     jparams.append(",\n");
 278                     jparams.append(vname);
 279                     
 280                     jparamDecls.append(",\n");
 281                     jparamDecls.append("int[] " + vname + "_arr");
 282                     
 283                     cparamDecls.append(",\n");
 284                     cparamDecls.append("jintArray " + vname + "_arr");
 285                     
 286                     appendGetRelease(arrayGet, arrayRelease, "int", vname, vname + "_arr");
 287                 }
 288 
 289                 posDecls.append("float inc" + i + "_x = (src" + i + "Rect_x2 - src" + i + "Rect_x1) / dstw;\n");
 290                 posDecls.append("float inc" + i + "_y = (src" + i + "Rect_y2 - src" + i + "Rect_y1) / dsth;\n");
 291 
 292                 posInitY.append("float pos" + i + "_y = src" + i + "Rect_y1 + inc" + i + "_y*0.5f;\n");
 293                 posInitX.append("float pos" + i + "_x = src" + i + "Rect_x1 + inc" + i + "_x*0.5f;\n");
 294                 posIncrX.append("pos" + i + "_x += inc" + i + "_x;\n");
 295                 posIncrY.append("pos" + i + "_y += inc" + i + "_y;\n");
 296 
 297                 jparams.append(",\n");
 298                 jparams.append("src" + i + "Rect[0], src" + i + "Rect[1],\n");
 299                 jparams.append("src" + i + "Rect[2], src" + i + "Rect[3],\n");
 300                 jparams.append("src" + i + "w, src" + i + "h, src" + i + "scan");
 301                 
 302                 jparamDecls.append(",\n");
 303                 jparamDecls.append("float src" + i + "Rect_x1, float src" + i + "Rect_y1,\n");
 304                 jparamDecls.append("float src" + i + "Rect_x2, float src" + i + "Rect_y2,\n");
 305                 jparamDecls.append("int src" + i + "w, int src" + i + "h, int src" + i + "scan");
 306 
 307                 cparamDecls.append(",\n");
 308                 cparamDecls.append("jfloat src" + i + "Rect_x1, jfloat src" + i + "Rect_y1,\n");
 309                 cparamDecls.append("jfloat src" + i + "Rect_x2, jfloat src" + i + "Rect_y2,\n");
 310                 cparamDecls.append("jint src" + i + "w, jint src" + i + "h, jint src" + i + "scan");
 311             }
 312         }
 313 
 314         if (genericsName != null) {
 315             genericsDecl.append("<"+genericsName+">");
 316         }
 317 
 318         if (interfaceName != null) {
 319             interfaceDecl.append("implements "+interfaceName);
 320         }
 321 
 322         Reader template = new InputStreamReader(getClass().getResourceAsStream("SSEJavaGlue.stg"));
 323         StringTemplateGroup group = new StringTemplateGroup(template, DefaultTemplateLexer.class);
 324         StringTemplate jglue = group.getInstanceOf("glue");
 325         jglue.setAttribute("effectName", effectName);
 326         jglue.setAttribute("peerName", peerName);
 327         jglue.setAttribute("genericsDecl", genericsDecl.toString());
 328         jglue.setAttribute("interfaceDecl", interfaceDecl.toString());
 329         jglue.setAttribute("usercode", usercode.toString());
 330         jglue.setAttribute("samplers", samplers.toString());
 331         jglue.setAttribute("cleanup", cleanup.toString());
 332         jglue.setAttribute("srcRects", srcRects.toString());
 333         jglue.setAttribute("constants", constants.toString());
 334         jglue.setAttribute("params", jparams.toString());
 335         jglue.setAttribute("paramDecls", jparamDecls.toString());
 336 
 337         template = new InputStreamReader(getClass().getResourceAsStream("SSENativeGlue.stg"));
 338         group = new StringTemplateGroup(template, DefaultTemplateLexer.class);
 339         StringTemplate cglue = group.getInstanceOf("glue");
 340         cglue.setAttribute("peerName", peerName);
 341         cglue.setAttribute("jniName", peerName.replace("_", "_1"));
 342         cglue.setAttribute("paramDecls", cparamDecls.toString());
 343         cglue.setAttribute("arrayGet", arrayGet.toString());
 344         cglue.setAttribute("arrayRelease", arrayRelease.toString());
 345         cglue.setAttribute("posDecls", posDecls.toString());
 346         cglue.setAttribute("pixInitY", pixInitY.toString());
 347         cglue.setAttribute("pixInitX", pixInitX.toString());
 348         cglue.setAttribute("posIncrY", posIncrY.toString());
 349         cglue.setAttribute("posInitY", posInitY.toString());
 350         cglue.setAttribute("posIncrX", posIncrX.toString());
 351         cglue.setAttribute("posInitX", posInitX.toString());
 352         cglue.setAttribute("body", body);
 353         
 354         GenCode gen = new GenCode();
 355         gen.javaCode = jglue.toString();
 356         gen.nativeCode = cglue.toString();
 357         return gen;
 358     }
 359     
 360     // TODO: need better mechanism for querying fields
 361     private static char[] fields = {'x', 'y', 'z', 'w'};
 362     public static String getSuffix(int i) {
 363         return "_" + fields[i];
 364     }
 365 
 366     static int getFieldIndex(char field) {
 367         switch (field) {
 368         case 'r':
 369         case 'x':
 370             return 0;
 371         case 'g':
 372         case 'y':
 373             return 1;
 374         case 'b':
 375         case 'z':
 376             return 2;
 377         case 'a':
 378         case 'w':
 379             return 3;
 380         default:
 381             throw new InternalError();
 382         }
 383     }
 384     
 385     // TODO: these shouldn't be implemented as a static method
 386     private static Map<String, FuncDef> funcDefs = new HashMap<String, FuncDef>();
 387     static void putFuncDef(FuncDef def) {
 388         funcDefs.put(def.getFunction().getName(), def);
 389     }
 390     static FuncDef getFuncDef(String name) {
 391         return funcDefs.get(name);
 392     }
 393     
 394     private static Set<String> resultVars = new HashSet<String>();
 395     static boolean isResultVarDeclared(String vname) {
 396         return resultVars.contains(vname);
 397     }
 398     static void declareResultVar(String vname) {
 399         resultVars.add(vname);
 400     }
 401     
 402     private static StringBuilder usercode = new StringBuilder();
 403     static void addGlueBlock(String block) {
 404         usercode.append(block);
 405     }
 406     
 407     private static void resetStatics() {
 408         funcDefs.clear();
 409         resultVars.clear();
 410         usercode = new StringBuilder();
 411     }
 412 }