frontend: intro glsl_validation to validate shader code in Nit programs
authorAlexis Laferrière <alexis.laf@xymus.net>
Mon, 1 Dec 2014 20:38:56 +0000 (15:38 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Tue, 2 Dec 2014 21:36:42 +0000 (16:36 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

src/frontend/frontend.nit
src/frontend/glsl_validation.nit [new file with mode: 0644]

index cfdf85b..53c1a77 100644 (file)
@@ -23,6 +23,7 @@ import div_by_zero
 import cached
 import serialization_phase
 import check_annotation
+import glsl_validation
 
 redef class ToolContext
        # FIXME: there is conflict in linex in nitc, so use this trick to force invocation
diff --git a/src/frontend/glsl_validation.nit b/src/frontend/glsl_validation.nit
new file mode 100644 (file)
index 0000000..5dd6b79
--- /dev/null
@@ -0,0 +1,122 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Check shader code within Nit modules using the tool _glslangValidator_
+#
+# For this phase to work, _glslangValidator_ must be in PATH. It can be
+# downloaded from https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
+module glsl_validation
+
+import literal
+
+redef class ToolContext
+       # Shader code validation phase
+       var glsl_validation_phase: Phase = new GLSLValidationPhase(self, [literal_phase])
+end
+
+private class GLSLValidationPhase
+       super Phase
+
+       # Annotation names
+
+       fun annot_name_vertex: String do return "glsl_vertex_shader"
+       fun annot_name_fragment: String do return "glsl_fragment_shader"
+
+       # TODO support more shader types as needed
+
+       # Is the tool _glsllangValidator_ in path?
+       var tool_is_in_path: nullable Bool = null
+
+       redef fun process_annotated_node(nstring, nat)
+       do
+               var annot_name = nat.n_atid.n_id.text
+               var is_vertex = annot_name == annot_name_vertex
+               var is_fragment = annot_name == annot_name_fragment
+
+               # Skip if we are not interested
+               if not is_vertex and not is_fragment then return
+
+               # Only applicable on strings
+               if not nstring isa AStringFormExpr then
+                       toolcontext.error(nstring.location,
+                               "Syntax error: only a string literal can be annotated as \"{annot_name}\".")
+                       return
+               end
+
+               # Do not double check if tool is in path
+               var in_path = tool_is_in_path
+               if in_path != null then
+                       if not in_path then return
+               else
+                       # Is _glslangValidator_ installed?
+                       var proc_which = new IProcess("which", "glslangValidator")
+                       proc_which.wait
+                       proc_which.close
+                       var status = proc_which.status
+                       in_path = status == 0
+                       tool_is_in_path = in_path
+                       if not in_path then
+                               toolcontext.warning(nat.location, "glslvalidator",
+                                       "Warning: program \"glslangValidator\" not in PATH, cannot validate this shader.")
+                               return
+                       end
+               end
+
+               # Get the shader source
+               var shader = nstring.value
+               assert shader != null
+
+               # Copy the shader to a file
+               # TODO make it more portable
+               var tmp = "/tmp/"
+               var ext
+               if is_vertex then
+                       ext = "vert"
+               else ext = "frag"
+               var path = tmp / "nit_shader." + ext
+
+               shader.write_to_file path
+
+               # Execute the validator
+               var proc_validator = new IProcess("glslangValidator", path)
+               proc_validator.wait
+               var lines = proc_validator.read_all.split('\n')
+               proc_validator.close
+
+               # Parse errors
+               var regex = "[A-Z]+: ([0-9]+):([0-9]+): (.*)".to_re
+               for line in lines do
+                       var match = line.search(regex)
+
+                       # Does it match an error?
+                       # If not, then it should be the summary
+                       if match != null then
+                               var shader_line_no = match.subs[1].to_s.to_i
+                               var msg = match.subs[2].to_s
+
+                               var line_start = nstring.location.line_start + shader_line_no
+                               var char_start = 0
+                               var char_end = 0
+                               var loc = new Location(nat.location.file,
+                                       line_start, line_start,
+                                       char_start, char_end)
+
+                               toolcontext.warning(loc, "glslvalidator",
+                                       "Shader error on {msg}")
+                       end
+               end
+       end
+end