Commits

Michael Ludwig committed a0e026b

Finish shader generator implementation for vertex shaders.

Comments (0)

Files changed (6)

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/Environment.java

         return p;
     }
 
-    public Environment declare(Type type, String name) {
-        Map<String, Type> newState = new HashMap<String, Type>();
-        newState.put(name, type);
-        return new Environment(this,
-                               inFragmentShader,
-                               inLoop,
-                               requiredReturnType,
-                               newState);
+    public boolean declare(Type type, String name) {
+        if (variables.get(name) != null) {
+            return false;
+        } else {
+            variables.put(name, type);
+            return true;
+        }
     }
 
     public Environment functionScope(Type returnType, Map<String, Type> variables) {

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/ShaderAccumulator.java

     public void accumulateFunction(Function f);
 
     public void accumulateStruct(Struct s);
+
+    public ShaderAccumulator getGlobalDeclarationAccumulator();
+
+    public ShaderAccumulator getMainAccumulator();
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/ShaderBuilder.java

 package com.ferox.resource.shader;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import com.ferox.resource.GlslShader;
+import com.ferox.resource.GlslShader.ShaderType;
+import com.ferox.resource.GlslShader.Version;
 import com.ferox.resource.shader.simple_grammar.Constant;
 import com.ferox.resource.shader.simple_grammar.DoWhileLoop;
 import com.ferox.resource.shader.simple_grammar.ForLoop;
 import com.ferox.resource.shader.simple_grammar.IfThenElse;
 import com.ferox.resource.shader.simple_grammar.Jump;
 import com.ferox.resource.shader.simple_grammar.Jump.JumpType;
+import com.ferox.resource.shader.simple_grammar.LocalDeclaration;
+import com.ferox.resource.shader.simple_grammar.StructDefinition;
 import com.ferox.resource.shader.simple_grammar.Variable;
+import com.ferox.resource.shader.simple_grammar.VertexShaderImpl;
 import com.ferox.resource.shader.simple_grammar.WhileLoop;
 
 public final class ShaderBuilder {
 
     private ShaderBuilder() {}
 
-    public static VertexShaderBuilder newVertexShader();
+    public static VertexShaderBuilder newVertexShader() {
+        return new VertexShaderImpl.Builder();
+    }
 
-    public static FragmentShaderBuilder newFragmentShader();
-
-    public static FragmentShaderBuilder newFragmentShader(VertexShader vertexStage);
+    //    public static FragmentShaderBuilder newFragmentShader();
+    //
+    //    public static FragmentShaderBuilder newFragmentShader(VertexShader vertexStage);
 
     public static GlslShader build(VertexShader vertexShader,
-                                   FragmentShader fragmentShader);
+                                   FragmentShader fragmentShader, Version target) {
+        vertexShader.validate(new Environment(false));
+
+        ShaderAccumulatorImpl topLevel = new ShaderAccumulatorImpl();
+        topLevel.main = new ShaderAccumulatorImpl();
+        topLevel.declare = new ShaderAccumulatorImpl();
+        topLevel.struct = new ShaderAccumulatorImpl();
+        topLevel.function = new ShaderAccumulatorImpl();
+
+        // wire others together
+        topLevel.main.connect(topLevel);
+        topLevel.declare.connect(topLevel);
+        topLevel.struct.connect(topLevel);
+        topLevel.function.connect(topLevel);
+
+        vertexShader.emit(topLevel);
+
+        // convert to single string
+        StringBuilder combined = new StringBuilder();
+        if (target != Version.V1_10) {
+            combined.append("#version " + target);
+        }
+
+        combined.append('\n');
+        combined.append(topLevel.struct.buffer);
+        combined.append('\n');
+        combined.append(topLevel.declare.buffer);
+        combined.append('\n');
+        combined.append(topLevel.function.buffer);
+        combined.append('\n');
+        combined.append(topLevel.main.buffer);
+
+        // FIXME also combine the toplevel's buffer?
+
+        GlslShader shader = new GlslShader();
+        shader.setShader(ShaderType.VERTEX, combined.toString());
+        return shader;
+    }
 
     public static Type arrayOf(Type type, int len) {
-
+        return new ArrayType(type, len);
     }
 
     public static Type arrayOf(Type type) {
     }
 
     public static StructBuilder struct(String structName) {
-
+        return new StructDefinition.Builder(structName);
     }
 
     public static Expression v(float v) {
     }
 
     public static LValue declare(Type type, String name) {
-
+        return new LocalDeclaration(type, name);
     }
 
     public static IfBuilder if_(Expression condition) {
     public static Statement discard() {
         return new Jump(JumpType.DISCARD);
     }
+
+    private static class ShaderAccumulatorImpl implements ShaderAccumulator {
+        private int indentLevel;
+
+        private final StringBuilder buffer;
+        private final Set<Function> accumulatedFunctions;
+        private final Set<Struct> accumulatedStructs;
+
+        private ShaderAccumulatorImpl main;
+        private ShaderAccumulatorImpl declare;
+        private ShaderAccumulatorImpl struct;
+        private ShaderAccumulatorImpl function;
+
+        public ShaderAccumulatorImpl() {
+            buffer = new StringBuilder();
+            accumulatedFunctions = new HashSet<Function>();
+            accumulatedStructs = new HashSet<Struct>();
+
+            indentLevel = 0;
+            main = null;
+            declare = null;
+            struct = null;
+            function = null;
+        }
+
+        @Override
+        public void addLine(String code) {
+            buffer.append('\n');
+            for (int i = 0; i < indentLevel; i++) {
+                buffer.append("   ");
+            }
+            buffer.append(code);
+        }
+
+        @Override
+        public void pushIndent() {
+            indentLevel++;
+        }
+
+        @Override
+        public void popIndent() {
+            indentLevel--;
+        }
+
+        @Override
+        public void accumulateFunction(Function f) {
+            if (!function.accumulatedFunctions.contains(f)) {
+                function.accumulatedFunctions.add(f);
+                f.emit(function);
+            }
+        }
+
+        @Override
+        public void accumulateStruct(Struct s) {
+            if (!struct.accumulatedStructs.contains(s)) {
+                struct.accumulatedStructs.add(s);
+                s.emit(struct);
+            }
+        }
+
+        @Override
+        public ShaderAccumulator getGlobalDeclarationAccumulator() {
+            return declare;
+        }
+
+        @Override
+        public ShaderAccumulator getMainAccumulator() {
+            return main;
+        }
+
+        public void connect(ShaderAccumulatorImpl parent) {
+            main = parent.main;
+            declare = parent.declare;
+            struct = parent.struct;
+            function = parent.function;
+        }
+    }
 }

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/FunctionDefinition.java

     public void emit(ShaderAccumulator accumulator) {
         StringBuilder header = new StringBuilder();
         header.append(returnType.getTypeIdentifier(accumulator, ""));
-        header.append(" ");
         header.append(name);
         header.append("(");
 

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/LocalDeclaration.java

 
     @Override
     public Environment validate(Environment environment) {
-        // declarations are always valid, we just need to create a new scope
-        return environment.declare(type, identifier);
+        if (!environment.declare(type, identifier)) {
+            throw new IllegalStateException("Variable already declared at this scope");
+        }
+        return environment;
     }
 
     @Override

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/shader/simple_grammar/VertexShaderImpl.java

+package com.ferox.resource.shader.simple_grammar;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.ferox.resource.shader.Environment;
+import com.ferox.resource.shader.PrimitiveType;
+import com.ferox.resource.shader.ShaderAccumulator;
+import com.ferox.resource.shader.Statement;
+import com.ferox.resource.shader.Type;
+import com.ferox.resource.shader.VertexShader;
+import com.ferox.resource.shader.VertexShaderBuilder;
+import com.ferox.resource.shader.simple_grammar.Parameter.ParameterQualifier;
+
+public class VertexShaderImpl implements VertexShader {
+    private final Parameter[] uniforms;
+    private final Parameter[] constants;
+    private final Parameter[] inputs;
+    private final Parameter[] outputs;
+
+    private final Statement[] mainBody;
+
+    public VertexShaderImpl(Parameter[] uniforms, Parameter[] constants,
+                            Parameter[] inputs, Parameter[] outputs, Statement[] mainBody) {
+        this.uniforms = uniforms;
+        this.constants = constants;
+        this.inputs = inputs;
+        this.outputs = outputs;
+        this.mainBody = mainBody;
+    }
+
+    @Override
+    public Environment validate(Environment environment) {
+        // uniforms
+        for (int i = 0; i < uniforms.length; i++) {
+            if (!environment.declare(uniforms[i].getType(), uniforms[i].getName())) {
+                throw new IllegalStateException("Global variable name already defined");
+            }
+        }
+        // constants
+        for (int i = 0; i < constants.length; i++) {
+            if (!environment.declare(constants[i].getType(), constants[i].getName())) {
+                throw new IllegalStateException("Global variable name already defined");
+            }
+        }
+        // inputs
+        for (int i = 0; i < inputs.length; i++) {
+            if (!environment.declare(inputs[i].getType(), inputs[i].getName())) {
+                throw new IllegalStateException("Global variable name already defined");
+            }
+        }
+        // outputs
+        for (int i = 0; i < outputs.length; i++) {
+            if (!environment.declare(outputs[i].getType(), outputs[i].getName())) {
+                throw new IllegalStateException("Global variable name already defined");
+            }
+        }
+
+        // validate main function body using a child scope
+        Environment mainScope = environment.functionScope(PrimitiveType.VOID,
+                                                          new HashMap<String, Type>());
+        for (int i = 0; i < mainBody.length; i++) {
+            mainScope = mainBody[i].validate(mainScope);
+        }
+
+        return environment;
+    }
+
+    @Override
+    public void emit(ShaderAccumulator accumulator) {
+        ShaderAccumulator globalVars = accumulator.getGlobalDeclarationAccumulator();
+
+        for (int i = 0; i < constants.length; i++) {
+            globalVars.addLine("const " + constants[i].getType()
+                                                      .getTypeIdentifier(globalVars,
+                                                                         constants[i].getName()) + ";");
+        }
+        globalVars.addLine("");
+
+        for (int i = 0; i < uniforms.length; i++) {
+            globalVars.addLine("uniform " + uniforms[i].getType()
+                                                       .getTypeIdentifier(globalVars,
+                                                                          uniforms[i].getName()) + ";");
+        }
+        globalVars.addLine("");
+
+        for (int i = 0; i < inputs.length; i++) {
+            globalVars.addLine("attribute " + inputs[i].getType()
+                                                       .getTypeIdentifier(globalVars,
+                                                                          inputs[i].getName()) + ";");
+        }
+        globalVars.addLine("");
+
+        for (int i = 0; i < outputs.length; i++) {
+            globalVars.addLine("varying " + outputs[i].getType()
+                                                      .getTypeIdentifier(globalVars,
+                                                                         outputs[i].getName()) + ";");
+        }
+        globalVars.addLine("");
+
+        // main body
+        ShaderAccumulator main = accumulator.getMainAccumulator();
+        main.addLine("void main() {");
+        main.pushIndent();
+
+        for (int i = 0; i < mainBody.length; i++) {
+            mainBody[i].emit(main);
+        }
+        main.popIndent();
+        main.addLine("}");
+        main.addLine("");
+    }
+
+    public static class Builder implements VertexShaderBuilder {
+        private final List<Parameter> uniforms;
+        private final List<Parameter> constants;
+        private final List<Parameter> inputs;
+        private final List<Parameter> outputs;
+
+        public Builder() {
+            uniforms = new ArrayList<Parameter>();
+            constants = new ArrayList<Parameter>();
+            inputs = new ArrayList<Parameter>();
+            outputs = new ArrayList<Parameter>();
+        }
+
+        @Override
+        public VertexShaderBuilder uniform(Type type, String name) {
+            uniforms.add(new Parameter(ParameterQualifier.NONE, type, name));
+            return this;
+        }
+
+        @Override
+        public VertexShaderBuilder constant(Type type, String name) {
+            constants.add(new Parameter(ParameterQualifier.NONE, type, name));
+            return this;
+        }
+
+        @Override
+        public VertexShaderBuilder in(Type type, String name) {
+            inputs.add(new Parameter(ParameterQualifier.IN, type, name));
+            return this;
+        }
+
+        @Override
+        public VertexShaderBuilder out(Type type, String name) {
+            outputs.add(new Parameter(ParameterQualifier.OUT, type, name));
+            return this;
+        }
+
+        @Override
+        public VertexShader main(Statement... body) {
+            return new VertexShaderImpl(uniforms.toArray(new Parameter[uniforms.size()]),
+                                        constants.toArray(new Parameter[constants.size()]),
+                                        inputs.toArray(new Parameter[inputs.size()]),
+                                        outputs.toArray(new Parameter[outputs.size()]),
+                                        body);
+        }
+    }
+}