glslang ======= An OpenGL and OpenGL ES shader front end and validator. There are two components: 1. A front-end library for programmatic parsing of GLSL/ESSL into an AST. 2. A standalone wrapper, `glslangValidator`, that can be used as a shader validation tool. How to add a feature protected by a version/extension/stage/profile: See the comment in `glslang/MachineIndependent/Versions.cpp`. Things left to do: See `Todo.txt` Execution of Standalone Wrapper ------------------------------- There are binaries in the `Install/Windows` and `Install/Linux` directories. To use the standalone binary form, execute `glslangValidator`, and it will print a usage statement. Basic operation is to give it a file containing a shader, and it will print out warnings/errors and optionally an AST. The applied stage-specific rules are based on the file extension: * `.vert` for a vertex shader * `.tesc` for a tessellation control shader * `.tese` for a tessellation evaluation shader * `.geom` for a geometry shader * `.frag` for a fragment shader * `.comp` for a compute shader There is also a non-shader extension * `.conf` for a configuration file of limits, see usage statement for example Source: Build and run on Linux ------------------------------- A simple bash script `BuildLinux.sh` is provided at the root directory to do the build and run a test cases. You will need a recent version of bison installed. Once the executable is generated, it needs to be dynamically linked with the shared object created in `lib` directory. To achieve that, `cd` to `StandAlone` directory to update the `LD_LIBRARY_PATH` as follows ```bash export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./../glslang/MachineIndependent/lib ``` You can also update `LD_LIBRARY_PATH` in the `.cshrc` or `.bashrc` file, depending on the shell you are using. You will need to give the complete path of `lib` directory in `.cshrc` or `.bashrc` files. Source: Build and run on Windows -------------------------------- Current development is with Visual Studio verion 11 (2012). The solution file is in the project's root directory `Standalone.sln`. Building the StandAlone project (the default) will create `glslangValidate.exe` and copy it into the `Test` directory and `Install` directory. This allows local test scripts to use either the debug or release version, whichever was built last. Windows execution and testing is generally done from within a cygwin shell. Note: Despite appearances, the use of a DLL is currently disabled; it simply makes a standalone executable from a statically linked library. Programmatic Interfaces ----------------------- Another piece of software can programmatically translate shaders to an AST using one of two different interfaces: * A new C++ class-oriented interface, or * The original C functional interface The `main()` in `StandAlone/StandAlone.cpp` shows examples using both styles. ### C++ Class Interface (new, preferred) This interface is in roughly the last 1/3 of `ShaderLang.h`. It is in the glslang namespace and contains the following. ```cxx const char* GetEsslVersionString(); const char* GetGlslVersionString(); bool InitializeProcess(); void FinalizeProcess(); class TShader bool parse(...); void setStrings(...); const char* getInfoLog(); class TProgram void addShader(...); bool link(...); const char* getInfoLog(); Reflection queries ``` See `ShaderLang.h` and the usage of it in `StandAlone/StandAlone.cpp` for more details. ### C Functional Interface (orginal) This interface is in roughly the first 2/3 of `ShaderLang.h`, and referred to as the `Sh*()` interface, as all the entry points start `Sh`. The `Sh*()` interface takes a "compiler" call-back object, which it calls after building call back that is passed the AST and can then execute a backend on it. The following is a simplified resulting run-time call stack: ```c ShCompile(shader, compiler) -> compiler(AST) -> <back end> ``` In practice, `ShCompile()` takes shader strings, default version, and warning/error and other options for controling compilation. Testing ------- `Test` is an active test directory that contains test input and a subdirectory `baseResults` that contains the expected results of the tests. Both the tests and `baseResults` are under source-code control. Executing the script `./runtests` will generate current results in the `localResults` directory and `diff` them against the `baseResults`. When you want to update the tracked test results, they need to be copied from `localResults` to `baseResults`. There are some tests borrowed from LunarGLASS. If LunarGLASS is missing, those tests just won't run. Basic Internal Operation ------------------------ * Initial lexical analysis is done by the preprocessor in `MachineIndependent/Preprocessor`, and then refined by a GLSL scanner in `MachineIndependent/Scan.cpp`. There is currently no use of flex. * Code is parsed using bison on `MachineIndependent/glslang.y` with the aid of a symbol table and an AST. The symbol table is not passed on to the back-end; the intermediate representation stands on its own. The tree is built by the grammar productions, many of which are offloaded into `ParseHelper.cpp`, and by `Intermediate.cpp`. * The intermediate representation is very high-level, and represented as an in-memory tree. This serves to lose no information from the original program, and to have efficient transfer of the result from parsing to the back-end. In the AST, constants are propogated and folded, and a very small amount of dead code is eliminated. To aid linking and reflection, the last top-level branch in the AST lists all global symbols. * The primary algorithm of the back-end compiler is to traverse the tree (high-level intermediate representation), and create an internal object code representation. There is an example of how to do this in `MachineIndependent/intermOut.cpp`. * Reduction of the tree to a linear byte-code style low-level intermediate representation is likely a good way to generate fully optimized code. * There is currently some dead old-style linker-type code still lying around. * Memory pool: parsing uses types derived from C++ `std` types, using a custom allocator that puts them in a memory pool. This makes allocation of individual container/contents just few cycles and deallocation free. This pool is popped after the AST is made and processed. The use is simple: if you are going to call `new`, there are three cases: - the object comes from the pool (its base class has the macro `POOL_ALLOCATOR_NEW_DELETE` in it) and you do not have to call `delete` - it is a `TString`, in which case call `NewPoolTString()`, which gets it from the pool, and there is no corresponding `delete` - the object does not come from the pool, and you have to do normal C++ memory management of what you `new`