From ad01d369153d690fe96e9df071119cbe5b89ff63 Mon Sep 17 00:00:00 2001 From: "Sha..rd" Date: Sat, 14 Apr 2012 20:22:13 +0000 Subject: [PATCH] * Added shader validation system. Requires either NVIDIA Cg or GPU Shader Analyser to be in the path. Run the ShaderCheck class to see which jME3 shaders fail validation. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9316 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../jme3tools/shadercheck/CgcValidator.java | 112 ++++++++++++++++ .../shadercheck/GpuAnalyzerValidator.java | 122 ++++++++++++++++++ .../jme3tools/shadercheck/ShaderCheck.java | 97 ++++++++++++++ .../jme3tools/shadercheck/Validator.java | 37 ++++++ 4 files changed, 368 insertions(+) create mode 100644 engine/src/tools/jme3tools/shadercheck/CgcValidator.java create mode 100644 engine/src/tools/jme3tools/shadercheck/GpuAnalyzerValidator.java create mode 100644 engine/src/tools/jme3tools/shadercheck/ShaderCheck.java create mode 100644 engine/src/tools/jme3tools/shadercheck/Validator.java diff --git a/engine/src/tools/jme3tools/shadercheck/CgcValidator.java b/engine/src/tools/jme3tools/shadercheck/CgcValidator.java new file mode 100644 index 000000000..28dbf21f3 --- /dev/null +++ b/engine/src/tools/jme3tools/shadercheck/CgcValidator.java @@ -0,0 +1,112 @@ +package jme3tools.shadercheck; + +import com.jme3.shader.Shader; +import com.jme3.shader.Shader.ShaderSource; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class CgcValidator implements Validator { + + private static final Logger logger = Logger.getLogger(CgcValidator.class.getName()); + private static String version; + + private static String checkCgCompilerVersion(){ + try { + ProcessBuilder pb = new ProcessBuilder("cgc", "--version"); + Process p = pb.start(); + + Scanner scan = new Scanner(p.getErrorStream()); + String ln = scan.nextLine(); + scan.close(); + + p.waitFor(); + + String versionNumber = ln.split("\\s")[2]; + return versionNumber.substring(0, versionNumber.length()-1); + } catch (IOException ex) { + logger.log(Level.SEVERE, "IOEx", ex); + } catch (InterruptedException ex){ + } + return null; + } + + public String getName() { + return "NVIDIA Cg Toolkit"; + } + + public boolean isInstalled() { + return getInstalledVersion() != null; + } + + public String getInstalledVersion() { + if (version == null){ + version = checkCgCompilerVersion(); + } + return version; + } + + private static void executeCg(String sourceCode, String language, String defines, String profile, StringBuilder output){ + try { + ProcessBuilder pb = new ProcessBuilder("cgc", "-oglsl", + "-nocode", + "-strict", + "-glslWerror", + "-profile", profile, + //"-po", "NumMathInstructionSlots=64",// math instruction slots + //"-po", "MaxTexIndirections=4", // texture indirections + "-po", "NumTemps=32", // temporary variables + //"-po", "NumInstructionSlots=1", // total instruction slots + //"-po", "NumTexInstructionSlots=32",// texture instruction slots + "-po", "MaxLocalParams=32"); // local parameters + + + Process p = pb.start(); + + String glslVer = language.substring(4); + + OutputStreamWriter writer = new OutputStreamWriter(p.getOutputStream()); + writer.append("#version ").append(glslVer).append('\n'); + writer.append("#extension all : warn").append('\n'); + writer.append(defines).append('\n'); + writer.write(sourceCode); + writer.close(); + + Scanner scan = new Scanner(p.getErrorStream()); + String ln = scan.nextLine(); + if (ln.contains("0 errors")){ + output.append(" - Success!").append('\n'); + }else{ + output.append(" - Failure!").append('\n'); + output.append(ln).append('\n'); + while (scan.hasNextLine()){ + output.append(scan.nextLine()).append('\n'); + } + } + scan.close(); + + p.waitFor(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "IOEx", ex); + } catch (InterruptedException ex){ + } + } + + public void validate(Shader shader, StringBuilder results) { + String language = shader.getLanguage(); + for (ShaderSource source : shader.getSources()){ + results.append("Checking: ").append(source.getName()); + switch (source.getType()){ + case Fragment: + executeCg(source.getSource(), language, source.getDefines(), "arbfp1", results); + break; + case Vertex: + executeCg(source.getSource(), language, source.getDefines(), "arbvp1", results); + break; + } + } + } + +} diff --git a/engine/src/tools/jme3tools/shadercheck/GpuAnalyzerValidator.java b/engine/src/tools/jme3tools/shadercheck/GpuAnalyzerValidator.java new file mode 100644 index 000000000..c2b8973e1 --- /dev/null +++ b/engine/src/tools/jme3tools/shadercheck/GpuAnalyzerValidator.java @@ -0,0 +1,122 @@ +package jme3tools.shadercheck; + +import com.jme3.shader.Shader; +import com.jme3.shader.Shader.ShaderSource; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Shader validator implementation for AMD's GPUShaderAnalyser. + * + * @author Kirill Vainer + */ +public class GpuAnalyzerValidator implements Validator { + + private static final Logger logger = Logger.getLogger(CgcValidator.class.getName()); + private static String version; + + private static String checkGpuAnalyzerVersion(){ + try { + ProcessBuilder pb = new ProcessBuilder("GPUShaderAnalyzer", "-ListModules"); + Process p = pb.start(); + + Scanner scan = new Scanner(p.getInputStream()); + String ln = scan.nextLine(); + scan.close(); + + p.destroy(); + + return ln; + } catch (IOException ex) { + logger.log(Level.SEVERE, "IOEx", ex); + } + return null; + } + + public String getName() { + return "AMD GPU Shader Analyzer"; + } + + public boolean isInstalled() { + return getInstalledVersion() != null; + } + + public String getInstalledVersion() { + if (version == null){ + version = checkGpuAnalyzerVersion(); + } + return version; + } + private static void executeAnalyzer(String sourceCode, String language, String defines, String asic, StringBuilder results){ + try { + // Export sourcecode to temporary file + File tempFile = File.createTempFile("test_shader", ".glsl"); + FileWriter writer = new FileWriter(tempFile); + + String glslVer = language.substring(4); + writer.append("#version ").append(glslVer).append('\n'); + writer.append("#extension all : warn").append('\n'); + writer.append(defines).append('\n'); + writer.write(sourceCode); + writer.close(); + + ProcessBuilder pb = new ProcessBuilder("GPUShaderAnalyzer", + tempFile.getAbsolutePath(), + "-I", + "-ASIC", asic); + + Process p = pb.start(); + + Scanner scan = new Scanner(p.getInputStream()); + + if (!scan.hasNextLine()){ + String x = scan.next(); + System.out.println(x); + } + + String ln = scan.nextLine(); + + if (ln.startsWith(";")){ + results.append(" - Success!").append('\n'); + }else{ + results.append(" - Failure!").append('\n'); + results.append(ln).append('\n'); + while (scan.hasNextLine()){ + results.append(scan.nextLine()).append('\n'); + } + } + + scan.close(); + p.getOutputStream().close(); + p.getErrorStream().close(); + + p.waitFor(); + p.destroy(); + + tempFile.delete(); + } catch (InterruptedException ex) { + } catch (IOException ex) { + logger.log(Level.SEVERE, "IOEx", ex); + } + } + + public void validate(Shader shader, StringBuilder results) { + String language = shader.getLanguage(); + for (ShaderSource source : shader.getSources()){ + results.append("Checking: ").append(source.getName()); + switch (source.getType()){ + case Fragment: + executeAnalyzer(source.getSource(), language, source.getDefines(), "HD5770", results); + break; + case Vertex: + executeAnalyzer(source.getSource(), language, source.getDefines(), "HD5770", results); + break; + } + } + } + +} diff --git a/engine/src/tools/jme3tools/shadercheck/ShaderCheck.java b/engine/src/tools/jme3tools/shadercheck/ShaderCheck.java new file mode 100644 index 000000000..a0b73e10f --- /dev/null +++ b/engine/src/tools/jme3tools/shadercheck/ShaderCheck.java @@ -0,0 +1,97 @@ +package jme3tools.shadercheck; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.plugins.ClasspathLocator; +import com.jme3.asset.plugins.FileLocator; +import com.jme3.material.MaterialDef; +import com.jme3.material.TechniqueDef; +import com.jme3.material.plugins.J3MLoader; +import com.jme3.shader.DefineList; +import com.jme3.shader.Shader; +import com.jme3.shader.ShaderKey; +import com.jme3.shader.plugins.GLSLLoader; +import com.jme3.system.JmeSystem; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ShaderCheck { + + private static final Logger logger = Logger.getLogger(ShaderCheck.class.getName()); + private static AssetManager assetManager; + + private static Validator[] validators = new Validator[]{ + new CgcValidator(), +// new GpuAnalyzerValidator() + }; + + private static void initAssetManager(){ + assetManager = JmeSystem.newAssetManager(); + assetManager.registerLocator(".", FileLocator.class); + assetManager.registerLocator("/", ClasspathLocator.class); + assetManager.registerLoader(J3MLoader.class, "j3m"); + assetManager.registerLoader(J3MLoader.class, "j3md"); + assetManager.registerLoader(GLSLLoader.class, "vert", "frag", "glsllib"); + } + + private static void checkMatDef(String matdefName){ + MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName); + for (TechniqueDef techDef : def.getDefaultTechniques()){ + if (techDef.isUsingShaders()){ + DefineList dl = new DefineList(); + dl.addFrom(techDef.getShaderPresetDefines()); + ShaderKey shaderKey = new ShaderKey(techDef.getVertexShaderName(), + techDef.getFragmentShaderName(), + dl, + techDef.getShaderLanguage()); + Shader shader = assetManager.loadShader(shaderKey); + + for (Validator validator : validators){ + StringBuilder sb = new StringBuilder(); + validator.validate(shader, sb); + System.out.println("==== Validator: " + validator.getName() + " " + + validator.getInstalledVersion() + " ===="); + System.out.println(sb.toString()); + } + } + } + } + + public static void main(String[] args){ + Logger.getLogger(MaterialDef.class.getName()).setLevel(Level.OFF); + initAssetManager(); + checkMatDef("Common/MatDefs/Blur/HGaussianBlur.j3md"); + checkMatDef("Common/MatDefs/Blur/RadialBlur.j3md"); + checkMatDef("Common/MatDefs/Blur/VGaussianBlur.j3md"); + checkMatDef("Common/MatDefs/Gui/Gui.j3md"); + checkMatDef("Common/MatDefs/Hdr/LogLum.j3md"); + checkMatDef("Common/MatDefs/Hdr/ToneMap.j3md"); + checkMatDef("Common/MatDefs/Light/Lighting.j3md"); + checkMatDef("Common/MatDefs/Misc/ColoredTextured.j3md"); + checkMatDef("Common/MatDefs/Misc/Particle.j3md"); + checkMatDef("Common/MatDefs/Misc/ShowNormals.j3md"); + checkMatDef("Common/MatDefs/Misc/Sky.j3md"); + checkMatDef("Common/MatDefs/Misc/Unshaded.j3md"); + + checkMatDef("Common/MatDefs/Post/BloomExtract.j3md"); + checkMatDef("Common/MatDefs/Post/BloomFinal.j3md"); + checkMatDef("Common/MatDefs/Post/CartoonEdge.j3md"); + checkMatDef("Common/MatDefs/Post/CrossHatch.j3md"); + checkMatDef("Common/MatDefs/Post/DepthOfField.j3md"); + checkMatDef("Common/MatDefs/Post/FXAA.j3md"); + checkMatDef("Common/MatDefs/Post/Fade.j3md"); + checkMatDef("Common/MatDefs/Post/Fog.j3md"); + checkMatDef("Common/MatDefs/Post/GammaCorrection.j3md"); + checkMatDef("Common/MatDefs/Post/LightScattering.j3md"); + checkMatDef("Common/MatDefs/Post/Overlay.j3md"); + checkMatDef("Common/MatDefs/Post/Posterization.j3md"); + + checkMatDef("Common/MatDefs/SSAO/ssao.j3md"); + checkMatDef("Common/MatDefs/SSAO/ssaoBlur.j3md"); + checkMatDef("Common/MatDefs/Shadow/PostShadow.j3md"); + checkMatDef("Common/MatDefs/Shadow/PostShadowPSSM.j3md"); + checkMatDef("Common/MatDefs/Shadow/PreShadow.j3md"); + + checkMatDef("Common/MatDefs/Water/SimpleWater.j3md"); + checkMatDef("Common/MatDefs/Water/Water.j3md"); + } +} diff --git a/engine/src/tools/jme3tools/shadercheck/Validator.java b/engine/src/tools/jme3tools/shadercheck/Validator.java new file mode 100644 index 000000000..cbfc339da --- /dev/null +++ b/engine/src/tools/jme3tools/shadercheck/Validator.java @@ -0,0 +1,37 @@ +package jme3tools.shadercheck; + +import com.jme3.shader.Shader; + +/** + * Interface for shader validator tools. + */ +public interface Validator { + + /** + * Returns the name of the validation tool + */ + public String getName(); + + /** + * Returns true if the tool is installed on the system, false otherwise. + */ + public boolean isInstalled(); + + /** + * Returns the tool version as a string, must return null if the tool + * is not installed. + */ + public String getInstalledVersion(); + + /** + * Validates the given shader to make sure it follows all requirements + * of the shader language specified as {@link Shader#getLanguage() }. + * The results of the validation will be written into the + * results argument. + * + * @param shader The shader to validate + * @param results The storage for the validation results + */ + public void validate(Shader shader, StringBuilder results); + +}