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.java;
  27 
  28 import java.io.InputStreamReader;
  29 import java.io.Reader;
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.Map;
  33 import java.util.Set;
  34 import com.sun.scenario.effect.compiler.JSLParser;
  35 import com.sun.scenario.effect.compiler.model.BaseType;
  36 import com.sun.scenario.effect.compiler.model.Qualifier;
  37 import com.sun.scenario.effect.compiler.model.Type;
  38 import com.sun.scenario.effect.compiler.model.Variable;
  39 import com.sun.scenario.effect.compiler.tree.FuncDef;
  40 import com.sun.scenario.effect.compiler.tree.ProgramUnit;
  41 import com.sun.scenario.effect.compiler.tree.TreeScanner;
  42 import org.antlr.stringtemplate.StringTemplate;
  43 import org.antlr.stringtemplate.StringTemplateGroup;
  44 import org.antlr.stringtemplate.language.DefaultTemplateLexer;
  45 
  46 /**
  47  */
  48 public class JSWBackend extends TreeScanner {
  49 
  50     private final JSLParser parser;
  51     private final String body;
  52 
  53     public JSWBackend(JSLParser parser, ProgramUnit program) {
  54         // TODO: will be removed once we clean up static usage
  55         resetStatics();
  56 
  57         this.parser = parser;
  58         
  59         JSWTreeScanner scanner = new JSWTreeScanner();
  60         scanner.scan(program);
  61         this.body = scanner.getResult();
  62     }
  63     
  64     public final String getGenCode(String effectName,
  65                                    String peerName,
  66                                    String genericsName,
  67                                    String interfaceName)
  68     {
  69         Map<String, Variable> vars = parser.getSymbolTable().getGlobalVariables();
  70         StringBuilder genericsDecl = new StringBuilder();
  71         StringBuilder interfaceDecl = new StringBuilder();
  72         StringBuilder constants = new StringBuilder();
  73         StringBuilder samplers = new StringBuilder();
  74         StringBuilder cleanup = new StringBuilder();
  75         StringBuilder srcRects = new StringBuilder();
  76         StringBuilder posDecls = new StringBuilder();
  77         StringBuilder pixInitY = new StringBuilder();
  78         StringBuilder pixInitX = new StringBuilder();
  79         StringBuilder posIncrY = new StringBuilder();
  80         StringBuilder posInitY = new StringBuilder();
  81         StringBuilder posIncrX = new StringBuilder();
  82         StringBuilder posInitX = new StringBuilder();
  83 
  84         // TODO: only need to declare these if pixcoord is referenced
  85         // somewhere in the program...
  86         pixInitY.append("float pixcoord_y = (float)dy;\n");
  87         pixInitX.append("float pixcoord_x = (float)dx;\n");
  88         
  89         for (Variable v : vars.values()) {
  90             if (v.getQualifier() == Qualifier.CONST && v.getConstValue() == null) {
  91                 // this must be a special built-in variable (e.g. pos0);
  92                 // these are handled elsewhere, so just continue...
  93                 continue;
  94             }
  95             
  96             Type t = v.getType();
  97             BaseType bt = t.getBaseType();
  98             if (v.getQualifier() != null && bt != BaseType.SAMPLER) {
  99                 String vtype = bt.toString();
 100                 String vname = v.getName();
 101                 String accName = v.getAccessorName();
 102                 if (v.isArray()) {
 103                     // TODO: we currently assume that param arrays will be
 104                     // stored in NIO Int/FloatBuffers, but the inner loop
 105                     // expects to access them as Java arrays, so we convert
 106                     // here; this is obviously bad for performance, so we need
 107                     // to come up with a better system soon...
 108                     String bufType = (bt == BaseType.FLOAT) ?
 109                         "FloatBuffer" : "IntBuffer";
 110                     String bufName = vname + "_buf";
 111                     String arrayName = vname + "_arr";
 112                     constants.append(bufType + " " + bufName + " = " + accName + "();\n");
 113                     constants.append(vtype + "[] " + arrayName);
 114                     constants.append(" = new " + vtype + "[");
 115                     constants.append(bufName + ".capacity()];\n");
 116                     constants.append(bufName + ".get(" + arrayName + ");\n");
 117                 } else {
 118                     if (t.isVector()) {
 119                         String arrayName = vname + "_arr";
 120                         constants.append(vtype + "[] " + arrayName + " = " + accName + "();\n");
 121                         constants.append(vtype + " ");
 122                         for (int i = 0; i < t.getNumFields(); i++) {
 123                             if (i > 0) {
 124                                 constants.append(", ");
 125                             }
 126                             constants.append(vname + getSuffix(i) + " = " + arrayName + "[" + i + "]");
 127                         }
 128                         constants.append(";\n");
 129                     } else {
 130                         constants.append(vtype + " " + vname);
 131                         if (v.getQualifier() == Qualifier.CONST) {
 132                             constants.append(" = " + v.getConstValue());
 133                         } else {
 134                             constants.append(" = " + accName + "()");
 135                         }
 136                         constants.append(";\n");
 137                     }
 138                 }
 139             } else if (v.getQualifier() == Qualifier.PARAM && bt == BaseType.SAMPLER) {
 140                 int i = v.getReg();
 141                 if (t == Type.FSAMPLER) {
 142                     samplers.append("FloatMap src" + i + " = (FloatMap)getSamplerData(" + i + ");\n");
 143                     samplers.append("int src" + i + "x = 0;\n");
 144                     samplers.append("int src" + i + "y = 0;\n");
 145                     samplers.append("int src" + i + "w = src" + i + ".getWidth();\n");
 146                     samplers.append("int src" + i + "h = src" + i + ".getHeight();\n");
 147                     samplers.append("int src" + i + "scan = src" + i + ".getWidth();\n");
 148                     samplers.append("float[] " + v.getName() + " = src" + i + ".getData();\n");
 149                     samplers.append("float " + v.getName() + "_vals[] = new float[4];\n");
 150 
 151                     // TODO: for now, assume [0,0,1,1]
 152                     srcRects.append("float[] src" + i + "Rect = new float[] {0,0,1,1};\n");
 153                 } else {
 154                     if (t == Type.LSAMPLER) {
 155                         samplers.append("HeapImage src" + i + " = (HeapImage)inputs[" + i + "].getUntransformedImage();\n");
 156                     } else {
 157                         samplers.append("HeapImage src" + i + " = (HeapImage)inputs[" + i + "].getTransformedImage(dstBounds);\n");
 158                         cleanup.append("inputs[" + i + "].releaseTransformedImage(src" + i + ");\n");
 159                     }
 160                     samplers.append("int src" + i + "x = 0;\n");
 161                     samplers.append("int src" + i + "y = 0;\n");
 162                     samplers.append("int src" + i + "w = src" + i + ".getPhysicalWidth();\n");
 163                     samplers.append("int src" + i + "h = src" + i + ".getPhysicalHeight();\n");
 164                     samplers.append("int src" + i + "scan = src" + i + ".getScanlineStride();\n");
 165                     samplers.append("int[] " + v.getName() + " =\n");
 166                     samplers.append("    src" + i + ".getPixelArray();\n");
 167 
 168                     samplers.append("Rectangle src" + i + "Bounds = new Rectangle(");
 169                     samplers.append("src" + i + "x, ");
 170                     samplers.append("src" + i + "y, ");
 171                     samplers.append("src" + i + "w, ");
 172                     samplers.append("src" + i + "h);\n");
 173                     if (t == Type.LSAMPLER) {
 174                         samplers.append("Rectangle src" + i + "InputBounds = inputs[" + i + "].getUntransformedBounds();\n");
 175                         samplers.append("BaseTransform src" + i + "Transform = inputs[" + i + "].getTransform();\n");
 176                     } else {
 177                         samplers.append("Rectangle src" + i + "InputBounds = inputs[" + i + "].getTransformedBounds(dstBounds);\n");
 178                         samplers.append("BaseTransform src" + i + "Transform = BaseTransform.IDENTITY_TRANSFORM;\n");
 179                     }
 180                     samplers.append("setInputBounds(" + i + ", src" + i + "InputBounds);\n");
 181                     samplers.append("setInputNativeBounds(" + i + ", src" + i + "Bounds);\n");
 182 
 183                     if (t == Type.LSAMPLER) {
 184                         samplers.append("float " + v.getName() + "_vals[] = new float[4];\n");
 185                     }
 186 
 187                     // the source rect decls need to come after all calls to
 188                     // setInput[Native]Bounds() for all inputs (since the
 189                     // getSourceRegion() impl may need to query the bounds of
 190                     // other inputs, as is the case in PhongLighting)...
 191                     srcRects.append("float[] src" + i + "Rect = new float[4];\n");
 192                     // Note that we only allocate 4 floats here because none
 193                     // of the loops can deal with fully mapped inputs.  Only
 194                     // shaders that declare LSAMPLERs would require mapped
 195                     // inputs and so far that is only Perspective and
 196                     // Displacement, both of which override getTC() and return
 197                     // only 4 values.
 198                     srcRects.append("getTextureCoordinates(" + i + ", src" + i + "Rect,\n");
 199                     srcRects.append("                      src" + i + "InputBounds.x, src" + i + "InputBounds.y,\n");
 200                     srcRects.append("                      src" + i + "w, src" + i + "h,\n");
 201                     srcRects.append("                      dstBounds, src" + i + "Transform);\n");
 202                 }
 203 
 204                 posDecls.append("float inc" + i + "_x = (src" + i + "Rect[2] - src" + i + "Rect[0]) / dstw;\n");
 205                 posDecls.append("float inc" + i + "_y = (src" + i + "Rect[3] - src" + i + "Rect[1]) / dsth;\n");
 206 
 207                 posInitY.append("float pos" + i + "_y = src" + i + "Rect[1] + inc" + i + "_y*0.5f;\n");
 208                 posInitX.append("float pos" + i + "_x = src" + i + "Rect[0] + inc" + i + "_x*0.5f;\n");
 209                 posIncrX.append("pos" + i + "_x += inc" + i + "_x;\n");
 210                 posIncrY.append("pos" + i + "_y += inc" + i + "_y;\n");
 211             }
 212         }
 213 
 214         if (genericsName != null) {
 215             genericsDecl.append("<"+genericsName+">");
 216         }
 217 
 218         if (interfaceName != null) {
 219             interfaceDecl.append("implements "+interfaceName);
 220         }
 221 
 222         Reader template = new InputStreamReader(getClass().getResourceAsStream("JSWGlue.stg"));
 223         StringTemplateGroup group = new StringTemplateGroup(template, DefaultTemplateLexer.class);
 224         StringTemplate glue = group.getInstanceOf("glue");
 225         glue.setAttribute("effectName", effectName);
 226         glue.setAttribute("peerName", peerName);
 227         glue.setAttribute("genericsDecl", genericsDecl.toString());
 228         glue.setAttribute("interfaceDecl", interfaceDecl.toString());
 229         glue.setAttribute("usercode", usercode.toString());
 230         glue.setAttribute("samplers", samplers.toString());
 231         glue.setAttribute("cleanup", cleanup.toString());
 232         glue.setAttribute("srcRects", srcRects.toString());
 233         glue.setAttribute("constants", constants.toString());
 234         glue.setAttribute("posDecls", posDecls.toString());
 235         glue.setAttribute("pixInitY", pixInitY.toString());
 236         glue.setAttribute("pixInitX", pixInitX.toString());
 237         glue.setAttribute("posIncrY", posIncrY.toString());
 238         glue.setAttribute("posInitY", posInitY.toString());
 239         glue.setAttribute("posIncrX", posIncrX.toString());
 240         glue.setAttribute("posInitX", posInitX.toString());
 241         glue.setAttribute("body", body);
 242         return glue.toString();
 243     }
 244     
 245     // TODO: need better mechanism for querying fields
 246     private static char[] fields = {'x', 'y', 'z', 'w'};
 247     public static String getSuffix(int i) {
 248         return "_" + fields[i];
 249     }
 250 
 251     static int getFieldIndex(char field) {
 252         switch (field) {
 253         case 'r':
 254         case 'x':
 255             return 0;
 256         case 'g':
 257         case 'y':
 258             return 1;
 259         case 'b':
 260         case 'z':
 261             return 2;
 262         case 'a':
 263         case 'w':
 264             return 3;
 265         default:
 266             throw new InternalError();
 267         }
 268     }
 269     
 270     // TODO: these shouldn't be implemented as a static method
 271     private static Map<String, FuncDef> funcDefs = new HashMap<String, FuncDef>();
 272     static void putFuncDef(FuncDef def) {
 273         funcDefs.put(def.getFunction().getName(), def);
 274     }
 275     static FuncDef getFuncDef(String name) {
 276         return funcDefs.get(name);
 277     }
 278     
 279     private static Set<String> resultVars = new HashSet<String>();
 280     static boolean isResultVarDeclared(String vname) {
 281         return resultVars.contains(vname);
 282     }
 283     static void declareResultVar(String vname) {
 284         resultVars.add(vname);
 285     }
 286     
 287     private static StringBuilder usercode = new StringBuilder();
 288     static void addGlueBlock(String block) {
 289         usercode.append(block);
 290     }
 291     
 292     private static void resetStatics() {
 293         funcDefs.clear();
 294         resultVars.clear();
 295         usercode = new StringBuilder();
 296     }
 297 }