<commit_before>/******************************************************************************
 * The MIT License (MIT)
 * 
 * Copyright (c) 2014 <NAME>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_shader_refl.h"

#include <algorithm>

GLuint MakeSeparableShaderProgram(const GLHookSet &gl, GLenum type, vector<string> sources)
{
	const string block = "\nout gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };";

	const char **strings = new const char*[sources.size()];
	for(size_t i=0; i < sources.size(); i++)
		strings[i] = sources[i].c_str();

	GLuint sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings);

	GLint status;
	gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status);

	if(status == 0 && type == eGL_VERTEX_SHADER)
	{
		gl.glDeleteProgram(sepProg);
		sepProg = 0;

		// try and patch up shader
		// naively insert gl_PerVertex block as soon as it's valid (after #version)
		// this will fail if e.g. a member of gl_PerVertex is declared at global scope
		// (this is probably most likely for clipdistance if it's redeclared with a size)

		string substituted;

		for(size_t i=0; i < sources.size(); i++)
		{
			string &src = sources[i];

			size_t len = src.length();
			size_t it = src.find("#version");
			if(it == string::npos)
				continue;

			// skip #version
			it += sizeof("#version")-1;

			// skip whitespace
			while(it < len && (src[it] == ' ' || src[it] == '\t'))
				++it;

			// skip number
			while(it < len && src[it] >= '0' && src[it] <= '9')
				++it;

			// skip whitespace
			while(it < len && (src[it] == ' ' || src[it] == '\t'))
				++it;

			substituted = src;

			if(!strncmp(&substituted[it], "core"         ,  4)) it += sizeof("core")-1;
			if(!strncmp(&substituted[it], "compatibility", 13)) it += sizeof("compatibility")-1;
			if(!strncmp(&substituted[it], "es"           ,  2)) it += sizeof("es")-1;

			substituted.insert(it, block);

			strings[i] = substituted.c_str();

			break;
		}

		sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings);

		gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status);
		if(status == 0)
		{
			gl.glDeleteProgram(sepProg);
			sepProg = 0;
		}
	}

	delete[] strings;

	return sepProg;
}

void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, ShaderReflection &refl)
{
	refl.DebugInfo.entryFunc = "main";
	refl.DebugInfo.compileFlags = 0;

	refl.Disassembly = "";

	vector<ShaderResource> resources;

	GLint numUniforms = 0;
	gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms);

	const size_t numProps = 7;

	GLenum resProps[numProps] = {
		eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, eGL_OFFSET, eGL_IS_ROW_MAJOR,
	};
	
	for(GLint u=0; u < numUniforms; u++)
	{
		GLint values[numProps];
		gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values);
		
		ShaderResource res;
		res.IsSampler = false; // no separate sampler objects in GL
		res.IsSRV = true;
		res.IsTexture = true;
		res.IsUAV = false;
		res.variableType.descriptor.rows = 1;
		res.variableType.descriptor.cols = 4;
		res.variableType.descriptor.elements = 1;
		res.variableType.descriptor.rowMajorStorage = false;
		res.bindPoint = (int32_t)resources.size();

		// float samplers
		if(values[0] == GL_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_SHADOW)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1DShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_ARRAY_SHADOW)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArrayShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_SHADOW)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_ARRAY_SHADOW)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArrayShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_RECT_SHADOW)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRectShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE_SHADOW)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCubeShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		// int samplers
		else if(values[0] == GL_INT_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		// unsigned int samplers
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else
		{
			// not a sampler
			continue;
		}

		create_array_uninit(res.name, values[1]);
		gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[1], NULL, res.name.elems);
		res.name.count--; // trim off trailing null

		resources.push_back(res);
	}

	refl.Resources = resources;

	vector<ShaderConstant> globalUniforms;
	
	GLint numUBOs = 0;
	vector<string> uboNames;
	vector<ShaderConstant> *ubos = NULL;
	
	{
		gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs);

		ubos = new vector<ShaderConstant>[numUBOs];
		uboNames.resize(numUBOs);

		for(GLint u=0; u < numUBOs; u++)
		{
			GLenum nameLen = eGL_NAME_LENGTH;
			GLint len;
			gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM_BLOCK, u, 1, &nameLen, 1, NULL, &len);

			char *nm = new char[len+1];
			gl.glGetProgramResourceName(sepProg, eGL_UNIFORM_BLOCK, u, len+1, NULL, nm);
			uboNames[u] = nm;
			delete[] nm;
		}
	}

	for(GLint u=0; u < numUniforms; u++)
	{
		GLint values[numProps];
		gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values);
		
		ShaderConstant var;
		
		var.type.descriptor.elements = RDCMAX(1, values[4]);

		// set type (or bail if it's not a variable - sampler or such)
		switch(values[0])
		{
			case GL_FLOAT_VEC4:
			case GL_FLOAT_VEC3:
			case GL_FLOAT_VEC2:
			case GL_FLOAT:
			case GL_FLOAT_MAT4:
			case GL_FLOAT_MAT3:
			case GL_FLOAT_MAT2:
			case GL_FLOAT_MAT4x2:
			case GL_FLOAT_MAT4x3:
			case GL_FLOAT_MAT3x4:
			case GL_FLOAT_MAT3x2:
			case GL_FLOAT_MAT2x4:
			case GL_FLOAT_MAT2x3:
				var.type.descriptor.type = eVar_Float;
				break;
			case GL_DOUBLE_VEC4:
			case GL_DOUBLE_VEC3:
			case GL_DOUBLE_VEC2:
			case GL_DOUBLE:
			case GL_DOUBLE_MAT4:
			case GL_DOUBLE_MAT3:
			case GL_DOUBLE_MAT2:
			case GL_DOUBLE_MAT4x2:
			case GL_DOUBLE_MAT4x3:
			case GL_DOUBLE_MAT3x4:
			case GL_DOUBLE_MAT3x2:
			case GL_DOUBLE_MAT2x4:
			case GL_DOUBLE_MAT2x3:
				var.type.descriptor.type = eVar_Double;
				break;
			case GL_UNSIGNED_INT_VEC4:
			case GL_UNSIGNED_INT_VEC3:
			case GL_UNSIGNED_INT_VEC2:
			case GL_UNSIGNED_INT:
			case GL_BOOL_VEC4:
			case GL_BOOL_VEC3:
			case GL_BOOL_VEC2:
			case GL_BOOL:
				var.type.descriptor.type = eVar_UInt;
				break;
			case GL_INT_VEC4:
			case GL_INT_VEC3:
			case GL_INT_VEC2:
			case GL_INT:
				var.type.descriptor.type = eVar_Int;
				break;
			default:
				// not a variable (sampler etc)
				continue;
		}
		
		// set # rows if it's a matrix
		var.type.descriptor.rows = 1;

		switch(values[0])
		{
			case GL_FLOAT_MAT4:
			case GL_DOUBLE_MAT4:
			case GL_FLOAT_MAT2x4:
			case GL_DOUBLE_MAT2x4:
			case GL_FLOAT_MAT3x4:
			case GL_DOUBLE_MAT3x4:
				var.type.descriptor.rows = 4;
				break;
			case GL_FLOAT_MAT3:
			case GL_DOUBLE_MAT3:
			case GL_FLOAT_MAT4x3:
			case GL_DOUBLE_MAT4x3:
			case GL_FLOAT_MAT2x3:
			case GL_DOUBLE_MAT2x3:
				var.type.descriptor.rows = 3;
				break;
			case GL_FLOAT_MAT2:
			case GL_DOUBLE_MAT2:
			case GL_FLOAT_MAT4x2:
			case GL_DOUBLE_MAT4x2:
			case GL_FLOAT_MAT3x2:
			case GL_DOUBLE_MAT3x2:
				var.type.descriptor.rows = 2;
				break;
			default:
				break;
		}

		// set # columns
		switch(values[0])
		{
			case GL_FLOAT_VEC4:
			case GL_FLOAT_MAT4:
			case GL_FLOAT_MAT4x2:
			case GL_FLOAT_MAT4x3:
			case GL_DOUBLE_VEC4:
			case GL_DOUBLE_MAT4:
			case GL_DOUBLE_MAT4x2:
			case GL_DOUBLE_MAT4x3:
			case GL_UNSIGNED_INT_VEC4:
			case GL_BOOL_VEC4:
			case GL_INT_VEC4:
				var.type.descriptor.cols = 4;
				break;
			case GL_FLOAT_VEC3:
			case GL_FLOAT_MAT3:
			case GL_FLOAT_MAT3x4:
			case GL_FLOAT_MAT3x2:
			case GL_DOUBLE_VEC3:
			case GL_DOUBLE_MAT3:
			case GL_DOUBLE_MAT3x4:
			case GL_DOUBLE_MAT3x2:
			case GL_UNSIGNED_INT_VEC3:
			case GL_BOOL_VEC3:
			case GL_INT_VEC3:
				var.type.descriptor.cols = 3;
				break;
			case GL_FLOAT_VEC2:
			case GL_FLOAT_MAT2:
			case GL_FLOAT_MAT2x4:
			case GL_FLOAT_MAT2x3:
			case GL_DOUBLE_VEC2:
			case GL_DOUBLE_MAT2:
			case GL_DOUBLE_MAT2x4:
			case GL_DOUBLE_MAT2x3:
			case GL_UNSIGNED_INT_VEC2:
			case GL_BOOL_VEC2:
			case GL_INT_VEC2:
				var.type.descriptor.cols = 2;
				break;
			case GL_FLOAT:
			case GL_DOUBLE:
			case GL_UNSIGNED_INT:
			case GL_INT:
			case GL_BOOL:
				var.type.descriptor.cols = 1;
				break;
			default:
				break;
		}

		// set name
		switch(values[0])
		{
			case GL_FLOAT_VEC4:          var.type.descriptor.name = "vec4"; break;
			case GL_FLOAT_VEC3:          var.type.descriptor.name = "vec3"; break;
			case GL_FLOAT_VEC2:          var.type.descriptor.name = "vec2"; break;
			case GL_FLOAT:               var.type.descriptor.name = "float"; break;
			case GL_FLOAT_MAT4:          var.type.descriptor.name = "mat4"; break;
			case GL_FLOAT_MAT3:          var.type.descriptor.name = "mat3"; break;
			case GL_FLOAT_MAT2:          var.type.descriptor.name = "mat2"; break;
			case GL_FLOAT_MAT4x2:        var.type.descriptor.name = "mat4x2"; break;
			case GL_FLOAT_MAT4x3:        var.type.descriptor.name = "mat4x3"; break;
			case GL_FLOAT_MAT3x4:        var.type.descriptor.name = "mat3x4"; break;
			case GL_FLOAT_MAT3x2:        var.type.descriptor.name = "mat3x2"; break;
			case GL_FLOAT_MAT2x4:        var.type.descriptor.name = "mat2x4"; break;
			case GL_FLOAT_MAT2x3:        var.type.descriptor.name = "mat2x3"; break;
			case GL_DOUBLE_VEC4:         var.type.descriptor.name = "dvec4"; break;
			case GL_DOUBLE_VEC3:         var.type.descriptor.name = "dvec3"; break;
			case GL_DOUBLE_VEC2:         var.type.descriptor.name = "dvec2"; break;
			case GL_DOUBLE:              var.type.descriptor.name = "double"; break;
			case GL_DOUBLE_MAT4:         var.type.descriptor.name = "dmat4"; break;
			case GL_DOUBLE_MAT3:         var.type.descriptor.name = "dmat3"; break;
			case GL_DOUBLE_MAT2:         var.type.descriptor.name = "dmat2"; break;
			case GL_DOUBLE_MAT4x2:       var.type.descriptor.name = "dmat4x2"; break;
			case GL_DOUBLE_MAT4x3:       var.type.descriptor.name = "dmat4x3"; break;
			case GL_DOUBLE_MAT3x4:       var.type.descriptor.name = "dmat3x4"; break;
			case GL_DOUBLE_MAT3x2:       var.type.descriptor.name = "dmat3x2"; break;
			case GL_DOUBLE_MAT2x4:       var.type.descriptor.name = "dmat2x4"; break;
			case GL_DOUBLE_MAT2x3:       var.type.descriptor.name = "dmat2x3"; break;
			case GL_UNSIGNED_INT_VEC4:   var.type.descriptor.name = "uvec4"; break;
			case GL_UNSIGNED_INT_VEC3:   var.type.descriptor.name = "uvec3"; break;
			case GL_UNSIGNED_INT_VEC2:   var.type.descriptor.name = "uvec2"; break;
			case GL_UNSIGNED_INT:        var.type.descriptor.name = "uint"; break;
			case GL_BOOL_VEC4:           var.type.descriptor.name = "bvec4"; break;
			case GL_BOOL_VEC3:           var.type.descriptor.name = "bvec3"; break;
			case GL_BOOL_VEC2:           var.type.descriptor.name = "bvec2"; break;
			case GL_BOOL:                var.type.descriptor.name = "bool"; break;
			case GL_INT_VEC4:            var.type.descriptor.name = "ivec4"; break;
			case GL_INT_VEC3:            var.type.descriptor.name = "ivec3"; break;
			case GL_INT_VEC2:            var.type.descriptor.name = "ivec2"; break;
			case GL_INT:                 var.type.descriptor.name = "int"; break;
			default:
				break;
		}

		if(values[5] == -1 && values[2] >= 0)
		{
			var.reg.vec = values[3];
			var.reg.comp = 0;
		}
		else if(values[5] >= 0)
		{
			var.reg.vec = values[5] / 16;
			var.reg.comp = (values[5] / 4) % 4;

			RDCASSERT((values[5] % 4) == 0);
		}
		else
		{
			var.reg.vec = var.reg.comp = ~0U;
		}

		var.type.descriptor.rowMajorStorage = (values[6] >= 0);

		create_array_uninit(var.name, values[1]);
		gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[0], NULL, var.name.elems);
		var.name.count--; // trim off trailing null

		int32_t c = var.name.count;

		// trim off trailing [0] if it's an array<commit_msg>Fix wrong value index being used<commit_after>/******************************************************************************
 * The MIT License (MIT)
 * 
 * Copyright (c) 2014 <NAME>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "gl_shader_refl.h"

#include <algorithm>

GLuint MakeSeparableShaderProgram(const GLHookSet &gl, GLenum type, vector<string> sources)
{
	const string block = "\nout gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };";

	const char **strings = new const char*[sources.size()];
	for(size_t i=0; i < sources.size(); i++)
		strings[i] = sources[i].c_str();

	GLuint sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings);

	GLint status;
	gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status);

	if(status == 0 && type == eGL_VERTEX_SHADER)
	{
		gl.glDeleteProgram(sepProg);
		sepProg = 0;

		// try and patch up shader
		// naively insert gl_PerVertex block as soon as it's valid (after #version)
		// this will fail if e.g. a member of gl_PerVertex is declared at global scope
		// (this is probably most likely for clipdistance if it's redeclared with a size)

		string substituted;

		for(size_t i=0; i < sources.size(); i++)
		{
			string &src = sources[i];

			size_t len = src.length();
			size_t it = src.find("#version");
			if(it == string::npos)
				continue;

			// skip #version
			it += sizeof("#version")-1;

			// skip whitespace
			while(it < len && (src[it] == ' ' || src[it] == '\t'))
				++it;

			// skip number
			while(it < len && src[it] >= '0' && src[it] <= '9')
				++it;

			// skip whitespace
			while(it < len && (src[it] == ' ' || src[it] == '\t'))
				++it;

			substituted = src;

			if(!strncmp(&substituted[it], "core"         ,  4)) it += sizeof("core")-1;
			if(!strncmp(&substituted[it], "compatibility", 13)) it += sizeof("compatibility")-1;
			if(!strncmp(&substituted[it], "es"           ,  2)) it += sizeof("es")-1;

			substituted.insert(it, block);

			strings[i] = substituted.c_str();

			break;
		}

		sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings);

		gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status);
		if(status == 0)
		{
			gl.glDeleteProgram(sepProg);
			sepProg = 0;
		}
	}

	delete[] strings;

	return sepProg;
}

void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, ShaderReflection &refl)
{
	refl.DebugInfo.entryFunc = "main";
	refl.DebugInfo.compileFlags = 0;

	refl.Disassembly = "";

	vector<ShaderResource> resources;

	GLint numUniforms = 0;
	gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms);

	const size_t numProps = 7;

	GLenum resProps[numProps] = {
		eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, eGL_OFFSET, eGL_IS_ROW_MAJOR,
	};
	
	for(GLint u=0; u < numUniforms; u++)
	{
		GLint values[numProps];
		gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values);
		
		ShaderResource res;
		res.IsSampler = false; // no separate sampler objects in GL
		res.IsSRV = true;
		res.IsTexture = true;
		res.IsUAV = false;
		res.variableType.descriptor.rows = 1;
		res.variableType.descriptor.cols = 4;
		res.variableType.descriptor.elements = 1;
		res.variableType.descriptor.rowMajorStorage = false;
		res.bindPoint = (int32_t)resources.size();

		// float samplers
		if(values[0] == GL_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_SHADOW)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1DShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_1D_ARRAY_SHADOW)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArrayShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_SHADOW)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_ARRAY_SHADOW)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArrayShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_RECT_SHADOW)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRectShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE_SHADOW)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCubeShadow";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_Float;
		}
		else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_Float;
		}
		// int samplers
		else if(values[0] == GL_INT_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_Int;
		}
		else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_Int;
		}
		// unsigned int samplers
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_BUFFER)
		{
			res.resType = eResType_Buffer;
			res.variableType.descriptor.name = "samplerBuffer";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D)
		{
			res.resType = eResType_Texture1D;
			res.variableType.descriptor.name = "sampler1D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY)
		{
			res.resType = eResType_Texture1DArray;
			res.variableType.descriptor.name = "sampler1DArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY)
		{
			res.resType = eResType_Texture2DArray;
			res.variableType.descriptor.name = "sampler2DArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_RECT)
		{
			res.resType = eResType_Texture2D;
			res.variableType.descriptor.name = "sampler2DRect";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_3D)
		{
			res.resType = eResType_Texture3D;
			res.variableType.descriptor.name = "sampler3D";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE)
		{
			res.resType = eResType_TextureCube;
			res.variableType.descriptor.name = "samplerCube";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY)
		{
			res.resType = eResType_TextureCubeArray;
			res.variableType.descriptor.name = "samplerCubeArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE)
		{
			res.resType = eResType_Texture2DMS;
			res.variableType.descriptor.name = "sampler2DMS";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
		{
			res.resType = eResType_Texture2DMSArray;
			res.variableType.descriptor.name = "sampler2DMSArray";
			res.variableType.descriptor.type = eVar_UInt;
		}
		else
		{
			// not a sampler
			continue;
		}

		create_array_uninit(res.name, values[1]);
		gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[1], NULL, res.name.elems);
		res.name.count--; // trim off trailing null

		resources.push_back(res);
	}

	refl.Resources = resources;

	vector<ShaderConstant> globalUniforms;
	
	GLint numUBOs = 0;
	vector<string> uboNames;
	vector<ShaderConstant> *ubos = NULL;
	
	{
		gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs);

		ubos = new vector<ShaderConstant>[numUBOs];
		uboNames.resize(numUBOs);

		for(GLint u=0; u < numUBOs; u++)
		{
			GLenum nameLen = eGL_NAME_LENGTH;
			GLint len;
			gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM_BLOCK, u, 1, &nameLen, 1, NULL, &len);

			char *nm = new char[len+1];
			gl.glGetProgramResourceName(sepProg, eGL_UNIFORM_BLOCK, u, len+1, NULL, nm);
			uboNames[u] = nm;
			delete[] nm;
		}
	}

	for(GLint u=0; u < numUniforms; u++)
	{
		GLint values[numProps];
		gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values);
		
		ShaderConstant var;
		
		var.type.descriptor.elements = RDCMAX(1, values[4]);

		// set type (or bail if it's not a variable - sampler or such)
		switch(values[0])
		{
			case GL_FLOAT_VEC4:
			case GL_FLOAT_VEC3:
			case GL_FLOAT_VEC2:
			case GL_FLOAT:
			case GL_FLOAT_MAT4:
			case GL_FLOAT_MAT3:
			case GL_FLOAT_MAT2:
			case GL_FLOAT_MAT4x2:
			case GL_FLOAT_MAT4x3:
			case GL_FLOAT_MAT3x4:
			case GL_FLOAT_MAT3x2:
			case GL_FLOAT_MAT2x4:
			case GL_FLOAT_MAT2x3:
				var.type.descriptor.type = eVar_Float;
				break;
			case GL_DOUBLE_VEC4:
			case GL_DOUBLE_VEC3:
			case GL_DOUBLE_VEC2:
			case GL_DOUBLE:
			case GL_DOUBLE_MAT4:
			case GL_DOUBLE_MAT3:
			case GL_DOUBLE_MAT2:
			case GL_DOUBLE_MAT4x2:
			case GL_DOUBLE_MAT4x3:
			case GL_DOUBLE_MAT3x4:
			case GL_DOUBLE_MAT3x2:
			case GL_DOUBLE_MAT2x4:
			case GL_DOUBLE_MAT2x3:
				var.type.descriptor.type = eVar_Double;
				break;
			case GL_UNSIGNED_INT_VEC4:
			case GL_UNSIGNED_INT_VEC3:
			case GL_UNSIGNED_INT_VEC2:
			case GL_UNSIGNED_INT:
			case GL_BOOL_VEC4:
			case GL_BOOL_VEC3:
			case GL_BOOL_VEC2:
			case GL_BOOL:
				var.type.descriptor.type = eVar_UInt;
				break;
			case GL_INT_VEC4:
			case GL_INT_VEC3:
			case GL_INT_VEC2:
			case GL_INT:
				var.type.descriptor.type = eVar_Int;
				break;
			default:
				// not a variable (sampler etc)
				continue;
		}
		
		// set # rows if it's a matrix
		var.type.descriptor.rows = 1;

		switch(values[0])
		{
			case GL_FLOAT_MAT4:
			case GL_DOUBLE_MAT4:
			case GL_FLOAT_MAT2x4:
			case GL_DOUBLE_MAT2x4:
			case GL_FLOAT_MAT3x4:
			case GL_DOUBLE_MAT3x4:
				var.type.descriptor.rows = 4;
				break;
			case GL_FLOAT_MAT3:
			case GL_DOUBLE_MAT3:
			case GL_FLOAT_MAT4x3:
			case GL_DOUBLE_MAT4x3:
			case GL_FLOAT_MAT2x3:
			case GL_DOUBLE_MAT2x3:
				var.type.descriptor.rows = 3;
				break;
			case GL_FLOAT_MAT2:
			case GL_DOUBLE_MAT2:
			case GL_FLOAT_MAT4x2:
			case GL_DOUBLE_MAT4x2:
			case GL_FLOAT_MAT3x2:
			case GL_DOUBLE_MAT3x2:
				var.type.descriptor.rows = 2;
				break;
			default:
				break;
		}

		// set # columns
		switch(values[0])
		{
			case GL_FLOAT_VEC4:
			case GL_FLOAT_MAT4:
			case GL_FLOAT_MAT4x2:
			case GL_FLOAT_MAT4x3:
			case GL_DOUBLE_VEC4:
			case GL_DOUBLE_MAT4:
			case GL_DOUBLE_MAT4x2:
			case GL_DOUBLE_MAT4x3:
			case GL_UNSIGNED_INT_VEC4:
			case GL_BOOL_VEC4:
			case GL_INT_VEC4:
				var.type.descriptor.cols = 4;
				break;
			case GL_FLOAT_VEC3:
			case GL_FLOAT_MAT3:
			case GL_FLOAT_MAT3x4:
			case GL_FLOAT_MAT3x2:
			case GL_DOUBLE_VEC3:
			case GL_DOUBLE_MAT3:
			case GL_DOUBLE_MAT3x4:
			case GL_DOUBLE_MAT3x2:
			case GL_UNSIGNED_INT_VEC3:
			case GL_BOOL_VEC3:
			case GL_INT_VEC3:
				var.type.descriptor.cols = 3;
				break;
			case GL_FLOAT_VEC2:
			case GL_FLOAT_MAT2:
			case GL_FLOAT_MAT2x4:
			case GL_FLOAT_MAT2x3:
			case GL_DOUBLE_VEC2:
			case GL_DOUBLE_MAT2:
			case GL_DOUBLE_MAT2x4:
			case GL_DOUBLE_MAT2x3:
			case GL_UNSIGNED_INT_VEC2:
			case GL_BOOL_VEC2:
			case GL_INT_VEC2:
				var.type.descriptor.cols = 2;
				break;
			case GL_FLOAT:
			case GL_DOUBLE:
			case GL_UNSIGNED_INT:
			case GL_INT:
			case GL_BOOL:
				var.type.descriptor.cols = 1;
				break;
			default:
				break;
		}

		// set name
		switch(values[0])
		{
			case GL_FLOAT_VEC4:          var.type.descriptor.name = "vec4"; break;
			case GL_FLOAT_VEC3:          var.type.descriptor.name = "vec3"; break;
			case GL_FLOAT_VEC2:          var.type.descriptor.name = "vec2"; break;
			case GL_FLOAT:               var.type.descriptor.name = "float"; break;
			case GL_FLOAT_MAT4:          var.type.descriptor.name = "mat4"; break;
			case GL_FLOAT_MAT3:          var.type.descriptor.name = "mat3"; break;
			case GL_FLOAT_MAT2:          var.type.descriptor.name = "mat2"; break;
			case GL_FLOAT_MAT4x2:        var.type.descriptor.name = "mat4x2"; break;
			case GL_FLOAT_MAT4x3:        var.type.descriptor.name = "mat4x3"; break;
			case GL_FLOAT_MAT3x4:        var.type.descriptor.name = "mat3x4"; break;
			case GL_FLOAT_MAT3x2:        var.type.descriptor.name = "mat3x2"; break;
			case GL_FLOAT_MAT2x4:        var.type.descriptor.name = "mat2x4"; break;
			case GL_FLOAT_MAT2x3:        var.type.descriptor.name = "mat2x3"; break;
			case GL_DOUBLE_VEC4:         var.type.descriptor.name = "dvec4"; break;
			case GL_DOUBLE_VEC3:         var.type.descriptor.name = "dvec3"; break;
			case GL_DOUBLE_VEC2:         var.type.descriptor.name = "dvec2"; break;
			case GL_DOUBLE:              var.type.descriptor.name = "double"; break;
			case GL_DOUBLE_MAT4:         var.type.descriptor.name = "dmat4"; break;
			case GL_DOUBLE_MAT3:         var.type.descriptor.name = "dmat3"; break;
			case GL_DOUBLE_MAT2:         var.type.descriptor.name = "dmat2"; break;
			case GL_DOUBLE_MAT4x2:       var.type.descriptor.name = "dmat4x2"; break;
			case GL_DOUBLE_MAT4x3:       var.type.descriptor.name = "dmat4x3"; break;
			case GL_DOUBLE_MAT3x4:       var.type.descriptor.name = "dmat3x4"; break;
			case GL_DOUBLE_MAT3x2:       var.type.descriptor.name = "dmat3x2"; break;
			case GL_DOUBLE_MAT2x4:       var.type.descriptor.name = "dmat2x4"; break;
			case GL_DOUBLE_MAT2x3:       var.type.descriptor.name = "dmat2x3"; break;
			case GL_UNSIGNED_INT_VEC4:   var.type.descriptor.name = "uvec4"; break;
			case GL_UNSIGNED_INT_VEC3:   var.type.descriptor.name = "uvec3"; break;
			case GL_UNSIGNED_INT_VEC2:   var.type.descriptor.name = "uvec2"; break;
			case GL_UNSIGNED_INT:        var.type.descriptor.name = "uint"; break;
			case GL_BOOL_VEC4:           var.type.descriptor.name = "bvec4"; break;
			case GL_BOOL_VEC3:           var.type.descriptor.name = "bvec3"; break;
			case GL_BOOL_VEC2:           var.type.descriptor.name = "bvec2"; break;
			case GL_BOOL:                var.type.descriptor.name = "bool"; break;
			case GL_INT_VEC4:            var.type.descriptor.name = "ivec4"; break;
			case GL_INT_VEC3:            var.type.descriptor.name = "ivec3"; break;
			case GL_INT_VEC2:            var.type.descriptor.name = "ivec2"; break;
			case GL_INT:                 var.type.descriptor.name = "int"; break;
			default:
				break;
		}

		if(values[5] == -1 && values[2] >= 0)
		{
			var.reg.vec = values[2];
			var.reg.comp = 0;
		}
		else if(values[5] >= 0)
		{
			var.reg.vec = values[5] / 16;
			var.reg.comp = (values[5] / 4) % 4;

			RDCASSERT((values[5] % 4) == 0);
		}
		else
		{
			var.reg.vec = var.reg.comp = ~0U;
		}

		var.type.descriptor.rowMajorStorage = (values[6] >= 0);

		create_array_uninit(var.name, values[1]);
		gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[0], NULL, var.name.elems);
		var.name.count--; // trim off trailing null

		int32_t c = var.name.count;

		// trim off trailing [0] if it's an array
{'fraction_non_alphanumeric': 0.12008425226118201, 'fraction_numerical': 0.020517903605501178, 'mean_word_length': 3.7421856639247943, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3938451', 'n_tokens_mistral': 17342, 'n_tokens_neox': 15339, 'n_words': 2770}