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; 27 28 import com.sun.scenario.effect.compiler.backend.hw.ES2Backend; 29 import com.sun.scenario.effect.compiler.backend.hw.HLSLBackend; 30 import com.sun.scenario.effect.compiler.backend.prism.PrismBackend; 31 import com.sun.scenario.effect.compiler.backend.sw.java.JSWBackend; 32 import com.sun.scenario.effect.compiler.backend.sw.me.MEBackend; 33 import com.sun.scenario.effect.compiler.backend.sw.sse.SSEBackend; 34 import com.sun.scenario.effect.compiler.tree.ProgramUnit; 35 import org.antlr.runtime.ANTLRInputStream; 36 import org.antlr.runtime.CommonTokenStream; 37 import org.antlr.stringtemplate.CommonGroupLoader; 38 import org.antlr.stringtemplate.StringTemplateGroup; 39 40 import java.io.*; 41 import java.util.ArrayList; 42 import java.util.HashMap; 43 import java.util.List; 44 import java.util.Map; 45 46 /** 47 */ 48 public class JSLC { 49 50 public static final int OUT_NONE = (0 << 0); 51 public static final int OUT_D3D = (1 << 0); 52 public static final int OUT_ES2 = (1 << 1); 53 public static final int OUT_JAVA = (1 << 2); 54 public static final int OUT_PRISM = (1 << 3); 55 56 public static final int OUT_SSE_JAVA = (1 << 4); 57 public static final int OUT_SSE_NATIVE = (1 << 5); 58 public static final int OUT_ME_JAVA = (1 << 6); 59 public static final int OUT_ME_NATIVE = (1 << 7); 60 61 public static final int OUT_ME = OUT_ME_JAVA | OUT_ME_NATIVE; 62 public static final int OUT_SSE = OUT_SSE_JAVA | OUT_SSE_NATIVE; 63 64 public static final int OUT_SW_PEERS = OUT_JAVA | OUT_SSE; 65 public static final int OUT_HW_PEERS = OUT_PRISM; 66 public static final int OUT_HW_SHADERS = OUT_D3D | OUT_ES2; 67 public static final int OUT_ALL = OUT_SW_PEERS | OUT_HW_PEERS | OUT_HW_SHADERS; 68 69 private static final String rootPkg = "com/sun/scenario/effect"; 70 71 static { 72 CommonGroupLoader loader = new CommonGroupLoader(rootPkg + "/compiler/backend", null); 73 StringTemplateGroup.registerGroupLoader(loader); 74 } 75 76 public static class OutInfo { 77 public String basePath; 78 public String filePrefix; 79 public String fileSuffix; 80 } 81 82 public static class ParserInfo { 83 public JSLParser parser; 84 public ProgramUnit program; 85 86 public ParserInfo(JSLParser parser, ProgramUnit program) { 87 this.parser = parser; 88 this.program = program; 89 } 90 } 91 92 public static ParserInfo getParserInfo(String source) throws Exception { 93 return getParserInfo(new ByteArrayInputStream(source.getBytes())); 94 } 95 96 public static ParserInfo getParserInfo(InputStream stream) throws Exception { 97 JSLParser parser = parse(stream); 98 ProgramUnit program = parser.translation_unit(); 99 return new ParserInfo(parser, program); 100 } 101 102 /** 103 * If trimToOutDir is provided by the user, then we will output all files 104 * under the out directory, for example if outDir=/foo/bar: 105 * /foo/bar/ + rootPkg + /impl/sw/java 106 * /foo/bar/ + rootPkg + /impl/sw/sse 107 * /foo/bar/ + rootPkg + /impl/sw/me 108 * /foo/bar/ + rootPkg + /impl/hw/d3d/hlsl 109 * /foo/bar/ + rootPkg + /impl/es2/glsl 110 * /foo/bar/ + rootPkg + /impl/prism/ps 111 * 112 * Otherwise, we use the layout currently expected by decora-runtime 113 * for core effects: 114 * ../decora-jsw/build/gensrc/ + rootPkg + /impl/sw/java 115 * ../decora-sse/build/gensrc/ + rootPkg + /impl/sw/sse 116 * ../decora-me/build/gensrc/ + rootPkg + /impl/sw/me 117 * ../decora-d3d/build/gensrc/ + rootPkg + /impl/hw/d3d/hlsl 118 * ../decora-es2/build/gensrc/ + rootPkg + /impl/es2/glsl 119 * ../decora-prism-ps/build/gensrc/+ rootPkg + /impl/prism/ps 120 */ 121 private static Map<Integer, String> initDefaultInfoMap() { 122 Map<Integer, String> infoMap = new HashMap<Integer, String>(); 123 infoMap.put(OUT_D3D, "decora-d3d/build/gensrc/{pkg}/impl/hw/d3d/hlsl/{name}.hlsl"); 124 infoMap.put(OUT_ES2, "decora-es2/build/gensrc/{pkg}/impl/es2/glsl/{name}.frag"); 125 infoMap.put(OUT_JAVA, "decora-jsw/build/gensrc/{pkg}/impl/sw/java/JSW{name}Peer.java"); 126 infoMap.put(OUT_PRISM, "decora-prism-ps/build/gensrc/{pkg}/impl/prism/ps/PPS{name}Peer.java"); 127 infoMap.put(OUT_SSE_JAVA, "decora-sse/build/gensrc/{pkg}/impl/sw/sse/SSE{name}Peer.java"); 128 infoMap.put(OUT_ME_JAVA, "decora-me/build/gensrc/{pkg}/impl/sw/me/ME{name}Peer.java"); 129 infoMap.put(OUT_SSE_NATIVE, "decora-sse-native/build/gensrc/SSE{name}Peer.cc"); 130 infoMap.put(OUT_ME_NATIVE, "decora-me-native/build/gensrc/ME{name}Peer.cc"); 131 return infoMap; 132 } 133 134 public static ParserInfo compile(JSLCInfo jslcinfo, 135 String str, 136 long sourceTime) 137 throws Exception 138 { 139 return compile(jslcinfo, 140 new ByteArrayInputStream(str.getBytes()), sourceTime); 141 } 142 143 public static ParserInfo compile(JSLCInfo jslcinfo, File file) 144 throws Exception 145 { 146 return compile(jslcinfo, new FileInputStream(file), file.lastModified()); 147 } 148 149 public static JSLParser parse(String str) 150 throws Exception 151 { 152 return parse(new ByteArrayInputStream(str.getBytes())); 153 } 154 155 private static JSLParser parse(InputStream stream) 156 throws Exception 157 { 158 // Read input 159 ANTLRInputStream input = new ANTLRInputStream(stream); 160 161 // Lexer 162 JSLLexer lexer = new JSLLexer(input); 163 CommonTokenStream tokens = new CommonTokenStream(lexer); 164 165 // Parser and AST construction 166 return new JSLParser(tokens); 167 } 168 169 private static ParserInfo compile(JSLCInfo jslcinfo, 170 InputStream stream, 171 long sourceTime) 172 throws Exception 173 { 174 return compile(jslcinfo, stream, sourceTime, null); 175 } 176 177 private static ParserInfo compile(JSLCInfo jslcinfo, 178 InputStream stream, 179 long sourceTime, 180 ParserInfo pinfo) 181 throws Exception 182 { 183 int outTypes = jslcinfo.outTypes; 184 String interfaceName = jslcinfo.interfaceName; 185 String peerName = jslcinfo.peerName; 186 String shaderName = jslcinfo.shaderName; 187 if (peerName == null) peerName = shaderName; 188 189 // Compiler 190 if ((outTypes & OUT_D3D) != 0) { 191 File outFile = jslcinfo.getOutputFile(OUT_D3D); 192 if (jslcinfo.force || outOfDate(outFile, sourceTime)) { 193 if (pinfo == null) pinfo = getParserInfo(stream); 194 HLSLBackend hlslBackend = new HLSLBackend(pinfo.parser, pinfo.program); 195 write(hlslBackend.getShader(), outFile); 196 } 197 } 198 199 if ((outTypes & OUT_ES2) != 0) { 200 File outFile = jslcinfo.getOutputFile(OUT_ES2); 201 if (jslcinfo.force || outOfDate(outFile, sourceTime)) { 202 if (pinfo == null) pinfo = getParserInfo(stream); 203 ES2Backend es2Backend = new ES2Backend(pinfo.parser, pinfo.program); 204 write(es2Backend.getShader(), outFile); 205 } 206 } 207 208 if ((outTypes & OUT_JAVA) != 0) { 209 File outFile = jslcinfo.getOutputFile(OUT_JAVA); 210 if (jslcinfo.force || outOfDate(outFile, sourceTime)) { 211 if (pinfo == null) pinfo = getParserInfo(stream); 212 JSWBackend javaBackend = new JSWBackend(pinfo.parser, pinfo.program); 213 String genCode = javaBackend.getGenCode(shaderName, peerName, interfaceName); 214 write(genCode, outFile); 215 } 216 } 217 218 if ((outTypes & OUT_SSE) != 0) { 219 File outFile = jslcinfo.getOutputFile(OUT_SSE_JAVA); 220 // TODO: native code is always generated into the same 221 // destination directory for now; need to make this more flexible 222 File genCFile = jslcinfo.getOutputFile(OUT_SSE_NATIVE); 223 224 boolean outFileStale = outOfDate(outFile, sourceTime); 225 boolean genCFileStale = outOfDate(genCFile, sourceTime); 226 if (jslcinfo.force || outFileStale || genCFileStale) { 227 if (pinfo == null) pinfo = getParserInfo(stream); 228 SSEBackend sseBackend = new SSEBackend(pinfo.parser, pinfo.program); 229 SSEBackend.GenCode gen = 230 sseBackend.getGenCode(shaderName, peerName, interfaceName); 231 232 // write impl class 233 if (outFileStale) { 234 write(gen.javaCode, outFile); 235 } 236 237 // write impl native code 238 if (genCFileStale) { 239 write(gen.nativeCode, genCFile); 240 } 241 } 242 } 243 244 if ((outTypes & OUT_ME) != 0) { 245 File outFile = jslcinfo.getOutputFile(OUT_ME_JAVA); 246 // TODO: native code is always generated into the same 247 // destination directory for now; need to make this more flexible 248 File genCFile = jslcinfo.getOutputFile(OUT_ME_NATIVE); 249 250 boolean outFileStale = outOfDate(outFile, sourceTime); 251 boolean genCFileStale = outOfDate(genCFile, sourceTime); 252 if (jslcinfo.force || outFileStale || genCFileStale) { 253 if (pinfo == null) pinfo = getParserInfo(stream); 254 MEBackend sseBackend = new MEBackend(pinfo.parser, pinfo.program); 255 MEBackend.GenCode gen = 256 sseBackend.getGenCode(shaderName, peerName, interfaceName); 257 258 // write impl class 259 if (outFileStale) { 260 write(gen.javaCode, outFile); 261 } 262 263 // write impl native code 264 if (genCFileStale) { 265 write(gen.nativeCode, genCFile); 266 } 267 } 268 } 269 270 if ((outTypes & OUT_PRISM) != 0) { 271 File outFile = jslcinfo.getOutputFile(OUT_PRISM); 272 if (jslcinfo.force || outOfDate(outFile, sourceTime)) { 273 if (pinfo == null) pinfo = getParserInfo(stream); 274 PrismBackend prismBackend = new PrismBackend(pinfo.parser, pinfo.program); 275 String genCode = prismBackend.getGlueCode(shaderName, peerName, interfaceName); 276 write(genCode, outFile); 277 } 278 } 279 280 return pinfo; 281 } 282 283 public static boolean outOfDate(File outFile, long sourceTime) { 284 if (sourceTime < outFile.lastModified()) { 285 return false; 286 } 287 return true; 288 } 289 290 public static void write(String str, File outFile) throws Exception { 291 File outDir = outFile.getParentFile(); 292 if (!outDir.exists()) { 293 outDir.mkdirs(); 294 } 295 FileWriter fw = null; 296 try { 297 fw = new FileWriter(outFile); 298 fw.write(str); 299 fw.flush(); 300 } finally { 301 if (fw != null) { 302 fw.close(); 303 } 304 } 305 } 306 307 public static class JSLCInfo { 308 public int outTypes; 309 public boolean force; 310 public String outDir; 311 public boolean trimToOutDir; 312 public List<String> srcDirs = new ArrayList<String>(); 313 public String shaderName; 314 public String peerName; 315 public String interfaceName; 316 public String pkgName = rootPkg; 317 public Map<Integer, String> outNameMap = initDefaultInfoMap(); 318 319 private String extraOpts; 320 321 public JSLCInfo() { 322 } 323 324 public JSLCInfo(String extraOpts) { 325 this.extraOpts = extraOpts; 326 } 327 328 public void usage(PrintStream out) { 329 StackTraceElement callers[] = Thread.currentThread().getStackTrace(); 330 String prog = callers[callers.length - 1].getClassName(); 331 String prefix0 = "Usage: java "+prog+" "; 332 String prefix1 = ""; 333 for (int i = 0; i < prefix0.length(); i++) prefix1 += " "; 334 out.println(prefix0+"[-d3d | -es2 | -java | -sse | -me | -sw | -hw | -all]"); 335 out.println(prefix1+"[-o <outdir>] [-i <srcdir>] [-t]"); 336 out.println(prefix1+"[-name <name>] [-ifname <interface name>]"); 337 if (extraOpts != null) { 338 out.println(prefix1+extraOpts); 339 } 340 } 341 342 public void error(String error) { 343 System.err.println(error); 344 usage(System.err); 345 System.exit(1); 346 } 347 348 public void parseAllArgs(String args[]) { 349 int index = parseArgs(args); 350 if (index != args.length) { 351 error("unrecognized argument: "+args[index]); 352 } 353 } 354 355 public int parseArgs(String args[]) { 356 int i = 0; 357 while (i < args.length) { 358 int consumed = parseArg(args, i); 359 if (consumed < 0) { 360 usage(System.err); 361 System.exit(1); 362 } else if (consumed == 0) { 363 break; 364 } 365 i += consumed; 366 } 367 return i; 368 } 369 370 public int parseArg(String args[], int index) { 371 String arg = args[index++]; 372 if (arg.equals("-force")) { 373 force = true; 374 } else if (arg.equals("-d3d")) { 375 outTypes |= OUT_D3D; 376 } else if (arg.equals("-es2")) { 377 outTypes |= OUT_ES2; 378 } else if (arg.equals("-java")) { 379 outTypes |= OUT_JAVA; 380 } else if (arg.equals("-sse")) { 381 outTypes |= OUT_SSE; 382 } else if (arg.equals("-me")) { 383 outTypes |= OUT_ME; 384 } else if (arg.equals("-sw")) { 385 outTypes = OUT_SW_PEERS; 386 } else if (arg.equals("-hw")) { 387 outTypes = OUT_HW_PEERS | OUT_HW_SHADERS; 388 } else if (arg.equals("-all")) { 389 outTypes = OUT_ALL; 390 } else if (arg.equals("-help")) { 391 usage(System.out); 392 System.exit(0); 393 } else if (arg.equals("-t")) { 394 trimToOutDir = true; 395 } else { 396 try { 397 // options with 1 argument 398 if (arg.equals("-o")) { 399 outDir = args[index]; 400 } else if (arg.equals("-i")) { 401 srcDirs.add(args[index]); 402 } else if (arg.equals("-name")) { 403 shaderName = args[index]; 404 } else if (arg.equals("-ifname")) { 405 interfaceName = args[index]; 406 } else if (arg.equals("-pkg")) { 407 pkgName = args[index]; 408 } else { 409 return 0; 410 } 411 } catch (ArrayIndexOutOfBoundsException e) { 412 return -1; 413 } 414 return 2; 415 } 416 return 1; 417 } 418 419 public File getJSLFile() { 420 return getJSLFile(shaderName); 421 } 422 423 public File getJSLFile(String jslBaseName) { 424 return getInputFile(jslBaseName + ".jsl"); 425 } 426 427 public File getInputFile(String filename) { 428 if (srcDirs.isEmpty()) { 429 File f = new File(filename); 430 if (f.exists()) { 431 return f; 432 } 433 } else { 434 for (String dir : srcDirs) { 435 File f = new File(dir, filename); 436 if (f.exists()) { 437 return f; 438 } 439 } 440 } 441 442 error("Input file not found: "+filename); 443 // NOT REACHED 444 return null; 445 } 446 447 public File getOutputFile(String outName) { 448 if (trimToOutDir) { 449 outName = outName.substring(outName.indexOf("gensrc") + 7); 450 } 451 outName = outName.replace("{pkg}", pkgName); 452 outName = outName.replace("{name}", peerName == null ? shaderName : peerName); 453 return outDir == null ? new File(outName) : new File(outDir, outName); 454 } 455 456 public File getOutputFile(int outType) { 457 String fileName = outNameMap.get(outType); 458 return getOutputFile(fileName); 459 } 460 } 461 462 public static void main(String[] args) throws Exception { 463 JSLCInfo jslcinfo = new JSLCInfo("<inputfile>"); 464 int index = jslcinfo.parseArgs(args); 465 466 if (index != args.length - 1) { 467 jslcinfo.error("Must specify one input file"); 468 } 469 String arg = args[index]; 470 if (!arg.endsWith(".jsl") || arg.length() < 5) { 471 jslcinfo.error("Input file name must end with '.jsl'"); 472 } 473 File inFile = jslcinfo.getInputFile(arg); 474 if (jslcinfo.shaderName == null) { 475 jslcinfo.shaderName = arg.substring(0, arg.length()-4); 476 } 477 478 compile(jslcinfo, inFile); 479 } 480 }