Pārlūkot izejas kodu

* 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
Sha..rd 13 gadi atpakaļ
vecāks
revīzija
ad01d36915

+ 112 - 0
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;
+            }
+        }
+    }
+    
+}

+ 122 - 0
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;
+            }
+        }
+    }
+    
+}

+ 97 - 0
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");
+    }
+}

+ 37 - 0
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);
+    
+}