diff --git a/extensions/gdx-tools/.classpath b/extensions/gdx-tools/.classpath
index 65048f9d5d3..617da55d5e2 100644
--- a/extensions/gdx-tools/.classpath
+++ b/extensions/gdx-tools/.classpath
@@ -1,14 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/gdx-tools/libs/glslang.jar b/extensions/gdx-tools/libs/glslang.jar
new file mode 100644
index 00000000000..fd26f7efd97
Binary files /dev/null and b/extensions/gdx-tools/libs/glslang.jar differ
diff --git a/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderTester.java b/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderTester.java
new file mode 100644
index 00000000000..545d978b6cb
--- /dev/null
+++ b/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderTester.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright 2011 See AUTHORS file.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+package com.badlogic.gdx.tools.shadertester;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.UUID;
+
+import com.badlogic.gdx.math.MathUtils;
+import com.badlogic.gdx.tools.FileProcessor;
+import com.badlogic.gdx.tools.FileProcessor.Entry;
+import com.badlogic.gdx.utils.SharedLibraryLoader;
+import com.badlogic.gdx.utils.StreamUtils;
+
+/** A thin wrapper around the Khronos reference shader compiler. It can check whole directories and shaders as strings.
+ * @author Simon Gerst */
+public class ShaderTester {
+
+ /** Returns a path to a file that can be written. Tries multiple locations and verifies writing succeeds. */
+ private static File getWritableFile (String dirName, String fileName) {
+ // Temp directory with username in path.
+ File idealFile = new File(System.getProperty("java.io.tmpdir") + "/libgdx" + System.getProperty("user.name") + "/"
+ + dirName, fileName);
+ if (canWrite(idealFile)) return idealFile;
+
+ // System provided temp directory.
+ try {
+ File file = File.createTempFile(dirName, null);
+ if (file.delete()) {
+ file = new File(file, fileName);
+ if (canWrite(file)) return file;
+ }
+ } catch (IOException ignored) {
+ }
+
+ // User home.
+ File file = new File(System.getProperty("user.home") + "/.libgdx/" + dirName, fileName);
+ if (canWrite(file)) return file;
+
+ // Relative directory.
+ file = new File(".temp/" + dirName, fileName);
+ if (canWrite(file)) return file;
+
+ return idealFile; // Will likely fail, but we did our best.
+ }
+
+ /** Returns true if the parent directories of the file can be created and the file can be written. */
+ private static boolean canWrite (File file) {
+ File parent = file.getParentFile();
+ File testFile;
+ if (file.exists()) {
+ if (!file.canWrite() || !canExecute(file)) return false;
+ // Don't overwrite existing file just to check if we can write to directory.
+ testFile = new File(parent, UUID.randomUUID().toString());
+ } else {
+ parent.mkdirs();
+ if (!parent.isDirectory()) return false;
+ testFile = file;
+ }
+ try {
+ new FileOutputStream(testFile).close();
+ if (!canExecute(testFile)) return false;
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ private static boolean canExecute (File file) {
+ try {
+ Method canExecute = File.class.getMethod("canExecute");
+ if ((Boolean)canExecute.invoke(file)) return true;
+
+ Method setExecutable = File.class.getMethod("setExecutable", boolean.class, boolean.class);
+ setExecutable.invoke(file, true, false);
+
+ return (Boolean)canExecute.invoke(file);
+ } catch (Exception ignored) {
+ }
+ return false;
+ }
+
+ /** Tests a shader.
+ * Note: This method creates a temp file, due to some limitations
+ * @param shaderSource the source of the shader
+ * @param type the type of the shader, see {@link ShaderType} */
+ static public void processFragmentShader (String shaderSource, ShaderType type) {
+ ArrayList list = new ArrayList();
+ File file = getUseableShaderFile(shaderSource, type.getFileExtension());
+ writeSourceToFile(shaderSource, file);
+ list.add(file);
+ process(new Settings() {
+ {
+ recursive = false;
+ }
+ }, list);
+ }
+
+ static private File getUseableShaderFile (String source, String extension) {
+ return getWritableFile("shaderTester", new Date().getTime() + MathUtils.random(Integer.MAX_VALUE) + extension);
+ }
+
+ static private void writeSourceToFile (String source, File fileToWrite) {
+ FileWriter w = null;
+ try {
+ w = new FileWriter(fileToWrite);
+ w.write(source);
+ } catch (IOException e) {
+ System.err.println("Failed to write shader to file, internal error!");
+ e.printStackTrace();
+ } finally {
+ StreamUtils.closeQuietly(w);
+ }
+ }
+
+ /** Tests shaders using defaults settings.
+ * Fragment shaders must have .frag as extension
+ * Vertex shaders must have .vert as extension
+ * Geometry shaders must have .geom as extension
+ * Tessellation control shaders must have .tesc as extension
+ * Tessellation evaluation shaders must have .tese as extension
+ * Use ShaderTester#process(ArrayList, ArrayList) to circumvent those restrictions
+ * @param inputDir the directory to process
+ * @see ShaderTester#process(Settings, String) */
+ static public void process (String inputDir) {
+ process(new Settings(), inputDir);
+ }
+
+ /** Tests shaders using the given settings.
+ * Fragment shaders must have .frag as extension
+ * Vertex shaders must have .vert as extension
+ * Geometry shaders must have .geom as extension
+ * Tessellation control shaders must have .tesc as extension
+ * Tessellation evaluation shaders must have .tese as extension
+ * Use ShaderTester#process(String, ShaderType) to circumvent those restrictions
+ * @param inputDir the directory to process
+ * @param settings the {@link Settings} used for processing */
+ static void process (Settings settings, String inputDir) {
+ File inputDirectory = new File(inputDir);
+ FileProcessor fileProcessor = new FileProcessor() {
+ @Override
+ protected void processDir (Entry entryDir, ArrayList files) throws Exception {
+ for (Entry entry : files) {
+ addProcessedFile(entry);
+ }
+ }
+ };
+ fileProcessor.setRecursive(settings.recursive);
+
+ fileProcessor.setInputFilter(new FilenameFilter() {
+ @Override
+ public boolean accept (File dir, String name) {
+ if (name.endsWith(".vert") || name.endsWith(".frag") || name.endsWith(".tese") || name.endsWith(".geom")
+ || name.endsWith(".comp") || name.endsWith(".tesc")) return true;
+ return false;
+ }
+ });
+
+ ArrayList shaderEntries = null;
+ try {
+ shaderEntries = fileProcessor.process(inputDirectory, null);
+ } catch (Exception e1) {
+ throw new RuntimeException("Faild to process inputDir:" + inputDirectory, e1);
+ }
+ ArrayList shaderFiles = new ArrayList();
+ for (Entry entry : shaderEntries) {
+ if (entry.inputFile != null) {
+ shaderFiles.add(entry.inputFile);
+ }
+ }
+ process(settings, shaderFiles);
+ }
+
+ /** Tests shaders using the given settings.
+ * Fragment shaders must have .frag as extension
+ * Vertex shaders must have .vert as extension
+ * Geometry shaders must have .geom as extension
+ * Tessellation control shaders must have .tesc as extension
+ * Tessellation evaluation shaders must have .tese as extension
+ * Use ShaderTester#process(String, ShaderType) to circumvent those restrictions
+ * @param filePaths array of files to test
+ * @param settings the {@link Settings} used for processing */
+ static public void process (Settings settings, ArrayList filePaths) {
+
+ SharedLibraryLoader loader = new SharedLibraryLoader();
+ File glslangFileExecutable = null;
+ try {
+ if (SharedLibraryLoader.isWindows) {
+ glslangFileExecutable = loader.extractFile("glslangValidator.exe", null);
+ } else if (SharedLibraryLoader.isLinux) {
+ glslangFileExecutable = loader.extractFile("glslangValidator", null);
+ } else if (SharedLibraryLoader.isMac) {
+ System.err.println("Mac is currently not supported!");
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("Faild to extract needed natives.", ex);
+ }
+
+ ArrayList command = new ArrayList();
+ command.add(glslangFileExecutable.getAbsolutePath());
+ for (File file : filePaths) {
+ command.add(file.getAbsolutePath());
+ }
+ try {
+ ProcessBuilder b = new ProcessBuilder();
+ b.command(command);
+ Process proc = b.start();
+
+ // Read input from process, else the process could hang
+ try {
+ InputStreamReader isr = new InputStreamReader(proc.getInputStream());
+ BufferedReader br = new BufferedReader(isr);
+ String line = null;
+ while ((line = br.readLine()) != null)
+ System.out.println(line);
+ } catch (IOException ioe) {
+ // Ignore
+ ioe.printStackTrace();
+ }
+ int exitVal = proc.waitFor();
+ System.out.println("Exit value:" + exitVal);
+
+ } catch (IOException ioe) {
+ // Ignore
+ ioe.printStackTrace();
+ } catch (InterruptedException ie) {
+ // Ignore
+ ie.printStackTrace();
+ }
+
+ }
+
+ static class Settings {
+ /** Whether to recursively check folders */
+ public boolean recursive = false;
+ }
+
+}
diff --git a/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderType.java b/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderType.java
new file mode 100644
index 00000000000..954e5d0a39d
--- /dev/null
+++ b/extensions/gdx-tools/src/com/badlogic/gdx/tools/shadertester/ShaderType.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright 2011 See AUTHORS file.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+package com.badlogic.gdx.tools.shadertester;
+
+public enum ShaderType {
+ Geometry(".geom"), Vertex(".vert"), Fragment(".frag"), Compute(".comp"), TessellationControl(".tesc"), TessellationEvaluation(
+ ".tese");
+ private final String extension;
+
+ private ShaderType (String extension) {
+ this.extension = extension;
+ }
+
+ protected String getFileExtension () {
+ return extension;
+ }
+}