<commit_before>// render/gl1/shader - WebGL1 implementation of pipeline / shader / program
// Part of Stardazed
// (c) 2015-2017 by <NAME> - @zenmumbler
// https://github.com/stardazed/stardazed

namespace sd.render.gl1 {

	export interface ExtensionUsage {
		name: string;
		action: "enable" | "require";
	}

	export interface ShaderDefine {
		name: string;
		value?: string;
	}

	export interface ShaderConstValue {
		name: string;
		type: ShaderValueType;
		expr: string;
	}

	export interface GL1VertexFunction extends VertexFunction {
		extensions?: ExtensionUsage[];
		defines?: ShaderDefine[];
		constValues?: ShaderConstValue[];
		structs?: string[];
		code?: string;
		main: string;
	}

	export interface GL1FragmentFunction extends FragmentFunction {
		extensions?: ExtensionUsage[];
		defines?: ShaderDefine[];
		constValues?: ShaderConstValue[];
		structs?: string[];
		code?: string;
		main: string;
	}

	const valueTypeMap: { [k: string]: { [t: string]: string } } = {
		attribute: {
			int: "float",
			int2: "vec2",
			int3: "vec3",
			int4: "vec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		},
		varying: {
			int: "float",
			int2: "vec2",
			int3: "vec3",
			int4: "vec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		},
		uniform: {
			int: "int",
			int2: "ivec2",
			int3: "ivec3",
			int4: "ivec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		}
	};

	function generateDefinesBlock(defines: ShaderDefine[] | undefined) {
		return (defines || []).map(def => {
			return `#define ${def.name} ${def.value || ""}\n`;
		}).join("");
	}

	function generateConstValuesBlock(constVals: ShaderConstValue[] | undefined) {
		return (constVals || []).map(cv => {
			const mappedValueType = valueTypeMap.uniform[cv.type];
			return `const ${mappedValueType} ${cv.name} = ${cv.expr};\n`;
		}).join("");
	}

	function generateStructsBlock(structs: string[] | undefined) {
		return (structs || []).join("\n");
	}

	function generateValueBlock(keyword: string, vals: ShaderConstant[] | undefined) {
		return (vals || []).map(val => {
			const arrayPostfix = (val.length! > 0) ? `[${val.length}]` : "";
			const mappedValueType = valueTypeMap[keyword][val.type];
			return `${keyword} ${mappedValueType} ${val.name}${arrayPostfix};\n`;
		}).join("");
	}

	function generateConstantsBlock(blocks: ShaderConstantBlock[] | undefined) {
		return (blocks || []).map(block => generateValueBlock("uniform", block.constants)).join("");
	}

	function generateSamplerBlock(samplers: SamplerSlot[] | undefined) {
		return (samplers || []).map(tex => {
			const mappedTextureType = (tex.type === TextureClass.Normal) ? "sampler2D" : "samplerCube";
			return `uniform ${mappedTextureType} ${tex.name};\n`;
		}).join("");
	}

	function generateExtensionBlock(exts: ExtensionUsage[] | undefined) {
		return (exts || []).map(ext =>
			`#extension ${ext.name} : ${ext.action}\n`
		).join("");
	}

	function generateVertexSource(fn: GL1VertexFunction) {
		const extensions = generateExtensionBlock(fn.extensions);
		const defines = generateDefinesBlock(fn.defines);
		const attributes = generateValueBlock("attribute", fn.in);
		const varying = generateValueBlock("varying", fn.out);
		const constValues = generateConstValuesBlock(fn.constValues);
		const structs = generateStructsBlock(fn.structs);
		const uniforms = generateConstantsBlock(fn.constantBlocks);
		const samplers = generateSamplerBlock(fn.samplers);
		
		return `${extensions}${defines}
		${attributes}${varying}
		${constValues}${structs}
		${uniforms}${samplers}
		${fn.code || ""}
		void main() {
			${fn.main}
		}`;
	}

	function generateFragmentSource(fn: GL1FragmentFunction) {
		const extensions = generateExtensionBlock(fn.extensions);
		const defines = generateDefinesBlock(fn.defines);
		const varying = generateValueBlock("varying", fn.in);
		const constValues = generateConstValuesBlock(fn.constValues);
		const structs = generateStructsBlock(fn.structs);
		const uniforms = generateConstantsBlock(fn.constantBlocks);
		const samplers = generateSamplerBlock(fn.samplers);
		
		return `${extensions}${defines}
		precision highp float;
		${varying}
		${constValues}${structs}
		${uniforms}${samplers}
		${fn.code || ""}
		void main() {
			${fn.main}
		}`;
	}

	function compileFunction(rd: GL1RenderDevice, type: number, sourceText: string) {
		const gl = rd.gl;
		const shader = gl.createShader(type)!; // TODO: handle resource allocation failure
		gl.shaderSource(shader, sourceText);
		gl.compileShader(shader);

		if (! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
			const errorLog = gl.getShaderInfoLog(shader);
			console.error("Shader compilation failed:", errorLog);
			console.error("Source", sourceText);
			return undefined;
		}

		return shader;
	}

	// ----

	export function makeProgram(rd: GL1RenderDevice, shader: Shader) {
		const gl = rd.gl;

		const vertexShader = compileFunction(rd, gl.VERTEX_SHADER, generateVertexSource(shader.vertexFunction as GL1VertexFunction));
		const fragmentShader = compileFunction(rd, gl.FRAGMENT_SHADER, generateFragmentSource(shader.fragmentFunction as GL1FragmentFunction));<commit_msg>feat(shader): show line #s of glsl source on error<commit_after>// render/gl1/shader - WebGL1 implementation of pipeline / shader / program
// Part of Stardazed
// (c) 2015-2017 by <NAME> - @zenmumbler
// https://github.com/stardazed/stardazed

namespace sd.render.gl1 {

	export interface ExtensionUsage {
		name: string;
		action: "enable" | "require";
	}

	export interface ShaderDefine {
		name: string;
		value?: string;
	}

	export interface ShaderConstValue {
		name: string;
		type: ShaderValueType;
		expr: string;
	}

	export interface GL1VertexFunction extends VertexFunction {
		extensions?: ExtensionUsage[];
		defines?: ShaderDefine[];
		constValues?: ShaderConstValue[];
		structs?: string[];
		code?: string;
		main: string;
	}

	export interface GL1FragmentFunction extends FragmentFunction {
		extensions?: ExtensionUsage[];
		defines?: ShaderDefine[];
		constValues?: ShaderConstValue[];
		structs?: string[];
		code?: string;
		main: string;
	}

	const valueTypeMap: { [k: string]: { [t: string]: string } } = {
		attribute: {
			int: "float",
			int2: "vec2",
			int3: "vec3",
			int4: "vec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		},
		varying: {
			int: "float",
			int2: "vec2",
			int3: "vec3",
			int4: "vec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		},
		uniform: {
			int: "int",
			int2: "ivec2",
			int3: "ivec3",
			int4: "ivec4",
			float: "float",
			float2: "vec2",
			float3: "vec3",
			float4: "vec4",
			mat2: "mat2",
			mat3: "mat3",
			mat4: "mat4"
		}
	};

	function generateDefinesBlock(defines: ShaderDefine[] | undefined) {
		return (defines || []).map(def => {
			return `#define ${def.name} ${def.value || ""}\n`;
		}).join("");
	}

	function generateConstValuesBlock(constVals: ShaderConstValue[] | undefined) {
		return (constVals || []).map(cv => {
			const mappedValueType = valueTypeMap.uniform[cv.type];
			return `const ${mappedValueType} ${cv.name} = ${cv.expr};\n`;
		}).join("");
	}

	function generateStructsBlock(structs: string[] | undefined) {
		return (structs || []).join("\n");
	}

	function generateValueBlock(keyword: string, vals: ShaderConstant[] | undefined) {
		return (vals || []).map(val => {
			const arrayPostfix = (val.length! > 0) ? `[${val.length}]` : "";
			const mappedValueType = valueTypeMap[keyword][val.type];
			return `${keyword} ${mappedValueType} ${val.name}${arrayPostfix};\n`;
		}).join("");
	}

	function generateConstantsBlock(blocks: ShaderConstantBlock[] | undefined) {
		return (blocks || []).map(block => generateValueBlock("uniform", block.constants)).join("");
	}

	function generateSamplerBlock(samplers: SamplerSlot[] | undefined) {
		return (samplers || []).map(tex => {
			const mappedTextureType = (tex.type === TextureClass.Normal) ? "sampler2D" : "samplerCube";
			return `uniform ${mappedTextureType} ${tex.name};\n`;
		}).join("");
	}

	function generateExtensionBlock(exts: ExtensionUsage[] | undefined) {
		return (exts || []).map(ext =>
			`#extension ${ext.name} : ${ext.action}\n`
		).join("");
	}

	function generateVertexSource(fn: GL1VertexFunction) {
		const extensions = generateExtensionBlock(fn.extensions);
		const defines = generateDefinesBlock(fn.defines);
		const attributes = generateValueBlock("attribute", fn.in);
		const varying = generateValueBlock("varying", fn.out);
		const constValues = generateConstValuesBlock(fn.constValues);
		const structs = generateStructsBlock(fn.structs);
		const uniforms = generateConstantsBlock(fn.constantBlocks);
		const samplers = generateSamplerBlock(fn.samplers);
		
		return `${extensions}${defines}
		${attributes}${varying}
		${constValues}${structs}
		${uniforms}${samplers}
		${fn.code || ""}
		void main() {
			${fn.main}
		}`;
	}

	function generateFragmentSource(fn: GL1FragmentFunction) {
		const extensions = generateExtensionBlock(fn.extensions);
		const defines = generateDefinesBlock(fn.defines);
		const varying = generateValueBlock("varying", fn.in);
		const constValues = generateConstValuesBlock(fn.constValues);
		const structs = generateStructsBlock(fn.structs);
		const uniforms = generateConstantsBlock(fn.constantBlocks);
		const samplers = generateSamplerBlock(fn.samplers);
		
		return `${extensions}${defines}
		precision highp float;
		${varying}
		${constValues}${structs}
		${uniforms}${samplers}
		${fn.code || ""}
		void main() {
			${fn.main}
		}`;
	}

	function compileFunction(rd: GL1RenderDevice, type: number, sourceText: string) {
		const gl = rd.gl;
		const shader = gl.createShader(type)!; // TODO: handle resource allocation failure
		gl.shaderSource(shader, sourceText);
		gl.compileShader(shader);

		if (! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
			const errorLog = gl.getShaderInfoLog(shader);
			console.error("Shader compilation failed:", errorLog);
			console.error("Source", sourceText.split("\n").map((l, i) => `${i + 1}: ${l}`).join("\n"));
			return undefined;
		}

		return shader;
	}

	// ----

	export function makeProgram(rd: GL1RenderDevice, shader: Shader) {
		const gl = rd.gl;

		const vertexShader = compileFunction(rd, gl.VERTEX_SHADER, generateVertexSource(shader.vertexFunction as GL1VertexFunction));
		const fragmentShader = compileFunction(rd, gl.FRAGMENT_SHADER, generateFragmentSource(shader.fragmentFunction as GL1FragmentFunction));
{'fraction_non_alphanumeric': 0.16127242463473276, 'fraction_numerical': 0.013963380802663215, 'mean_word_length': 4.534800409416581, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 20, 'https://': 2, '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': '333975', 'n_tokens_mistral': 4019, 'n_tokens_neox': 3618, 'n_words': 779}