luboslenco 4 mesi fa
parent
commit
d5a53b3aae

+ 5 - 0
base/sources/libs/kong/analyzer.c

@@ -188,6 +188,10 @@ void find_used_builtins(function *f) {
 				f->used_builtins.group_id = true;
 			}
 
+			if (func == add_name("vertex_id")) {
+				f->used_builtins.vertex_id = true;
+			}
+
 			for (function_id i = 0; get_function(i) != NULL; ++i) {
 				function *called = get_function(i);
 				if (called->name == o->op_call.func) {
@@ -196,6 +200,7 @@ void find_used_builtins(function *f) {
 					f->used_builtins.dispatch_thread_id |= called->used_builtins.dispatch_thread_id;
 					f->used_builtins.group_thread_id |= called->used_builtins.group_thread_id;
 					f->used_builtins.group_id |= called->used_builtins.group_id;
+					f->used_builtins.vertex_id |= called->used_builtins.vertex_id;
 
 					break;
 				}

+ 851 - 32
base/sources/libs/kong/backends/spirv.c

@@ -191,12 +191,15 @@ typedef enum spirv_opcode {
 	SPIRV_OPCODE_COMPOSITE_EXTRACT         = 81,
 	SPIRV_OPCODE_SAMPLED_IMAGE             = 86,
 	SPIRV_OPCODE_IMAGE_SAMPLE_IMPLICIT_LOD = 87,
+	SPIRV_OPCODE_IMAGE_SAMPLE_EXPLICIT_LOD = 88,
 	SPIRV_OPCODE_IMAGE_WRITE               = 99,
 	SPIRV_OPCODE_CONVERT_F_TO_U            = 109,
 	SPIRV_OPCODE_CONVERT_F_TO_S            = 110,
 	SPIRV_OPCODE_CONVERT_S_TO_F            = 111,
 	SPIRV_OPCODE_CONVERT_U_TO_F            = 112,
 	SPIRV_OPCODE_BITCAST                   = 124,
+	SPIRV_OPCODE_S_NEGATE                  = 126,
+	SPIRV_OPCODE_F_NEGATE                  = 127,
 	SPIRV_OPCODE_I_ADD                     = 128,
 	SPIRV_OPCODE_F_ADD                     = 129,
 	SPIRV_OPCODE_I_SUB                     = 130,
@@ -206,12 +209,24 @@ typedef enum spirv_opcode {
 	SPIRV_OPCODE_VECTOR_TIMES_MATRIX       = 144,
 	SPIRV_OPCODE_MATRIX_TIMES_VECTOR       = 145,
 	SPIRV_OPCODE_MATRIX_TIMES_MATRIX       = 146,
+	SPIRV_OPCODE_F_MOD                     = 161,
+	SPIRV_OPCODE_LOGICAL_OR                = 166,
+	SPIRV_OPCODE_LOGICAL_AND               = 167,
+	SPIRV_OPCODE_LOGICAL_NOT               = 168,
+	SPIRV_OPCODE_I_EQUAL                   = 170,
+	SPIRV_OPCODE_I_NOT_EQUAL               = 171,
+	SPIRV_OPCODE_F_ORD_EQUAL               = 180,
+	SPIRV_OPCODE_F_ORD_NOT_EQUAL           = 182,
 	SPIRV_OPCODE_F_ORD_LESS_THAN           = 184,
+	SPIRV_OPCODE_F_ORD_GREATER_THAN        = 186,
+	SPIRV_OPCODE_F_ORD_LESS_THAN_EQUAL     = 188,
+	SPIRV_OPCODE_F_ORD_GREATER_THAN_EQUAL  = 190,
 	SPIRV_OPCODE_LOOP_MERGE                = 246,
 	SPIRV_OPCODE_SELECTION_MERGE           = 247,
 	SPIRV_OPCODE_LABEL                     = 248,
 	SPIRV_OPCODE_BRANCH                    = 249,
 	SPIRV_OPCODE_BRANCH_CONDITIONAL        = 250,
+	SPIRV_OPCODE_KILL                      = 252,
 	SPIRV_OPCODE_RETURN                    = 253,
 } spirv_opcode;
 
@@ -290,6 +305,7 @@ typedef enum decoration {
 
 typedef enum builtin {
 	BUILTIN_POSITION             = 0,
+	BUILTIN_VERTEX_ID            = 5,
 	BUILTIN_WORKGROUP_SIZE       = 25,
 	BUILTIN_WORKGROUP_ID         = 26,
 	BUILTIN_LOCAL_INVOCATION_ID  = 27,
@@ -596,6 +612,7 @@ static spirv_id dispatch_thread_id_variable;
 static spirv_id group_thread_id_variable;
 static spirv_id group_id_variable;
 static spirv_id work_group_size_variable;
+static spirv_id vertex_id_variable;
 
 typedef struct complex_type {
 	type_id  type;
@@ -902,6 +919,10 @@ static void write_op_return(instructions_buffer *instructions) {
 	write_simple_instruction(instructions, SPIRV_OPCODE_RETURN);
 }
 
+static void write_op_discard(instructions_buffer *instructions) {
+	write_simple_instruction(instructions, SPIRV_OPCODE_KILL);
+}
+
 static void write_op_function_end(instructions_buffer *instructions) {
 	write_simple_instruction(instructions, SPIRV_OPCODE_FUNCTION_END);
 }
@@ -1031,6 +1052,36 @@ static spirv_id write_op_f_ord_less_than(instructions_buffer *instructions, spir
 	return result;
 }
 
+static spirv_id write_op_f_ord_less_than_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_ORD_LESS_THAN_EQUAL, operands);
+
+	return result;
+}
+
+static spirv_id write_op_f_ord_greater_than(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_ORD_GREATER_THAN, operands);
+
+	return result;
+}
+
+static spirv_id write_op_f_ord_greater_than_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_ORD_GREATER_THAN_EQUAL, operands);
+
+	return result;
+}
+
 static spirv_id write_op_i_add(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
 	spirv_id result = allocate_index();
 
@@ -1091,6 +1142,16 @@ static spirv_id write_op_f_div(instructions_buffer *instructions, spirv_id type,
 	return result;
 }
 
+static spirv_id write_op_f_mod(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_MOD, operands);
+
+	return result;
+}
+
 static spirv_id write_op_matrix_times_vector(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
 	spirv_id result = allocate_index();
 
@@ -1201,6 +1262,17 @@ static spirv_id write_op_image_sample_implicit_lod(instructions_buffer *instruct
 	return result;
 }
 
+static spirv_id write_op_image_sample_explicit_lod(instructions_buffer *instructions, spirv_id result_type, spirv_id sampled_image, spirv_id coordinate, spirv_id lod) {
+	spirv_id result = allocate_index();
+
+	int lod_operands = 0x2;
+	uint32_t operands[] = {result_type.id, result.id, sampled_image.id, coordinate.id, lod_operands, lod.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_IMAGE_SAMPLE_EXPLICIT_LOD, operands);
+
+	return result;
+}
+
 static spirv_id write_op_ext_inst(instructions_buffer *instructions, spirv_id result_type, spirv_id set, uint32_t instruction, spirv_id operand) {
 	spirv_id result = allocate_index();
 
@@ -1238,6 +1310,90 @@ static void write_op_variable_preallocated(instructions_buffer *instructions, sp
 //	return result;
 // }
 
+static spirv_id write_op_f_ord_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_ORD_EQUAL, operands);
+
+	return result;
+}
+
+static spirv_id write_op_i_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_I_EQUAL, operands);
+
+	return result;
+}
+
+static spirv_id write_op_f_ord_not_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_ORD_NOT_EQUAL, operands);
+
+	return result;
+}
+
+static spirv_id write_op_i_not_equal(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_I_NOT_EQUAL, operands);
+
+	return result;
+}
+
+static spirv_id write_op_f_negate(instructions_buffer *instructions, spirv_id type, spirv_id operand) {
+    spirv_id result = allocate_index();
+
+    uint32_t operands[] = {type.id, result.id, operand.id};
+    write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_F_NEGATE, operands);
+    return result;
+}
+
+static spirv_id write_op_s_negate(instructions_buffer *instructions, spirv_id type, spirv_id operand) {
+    spirv_id result = allocate_index();
+
+    uint32_t operands[] = {type.id, result.id, operand.id};
+    write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_S_NEGATE, operands);
+    return result;
+}
+
+static spirv_id write_op_logical_and(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_LOGICAL_AND, operands);
+
+	return result;
+}
+
+static spirv_id write_op_logical_or(instructions_buffer *instructions, spirv_id type, spirv_id operand1, spirv_id operand2) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand1.id, operand2.id};
+
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_LOGICAL_OR, operands);
+
+	return result;
+}
+
+static spirv_id write_op_not(instructions_buffer *instructions, spirv_id type, spirv_id operand) {
+	spirv_id result = allocate_index();
+
+	uint32_t operands[] = {type.id, result.id, operand.id};
+	write_instruction(instructions, WORD_COUNT(operands), SPIRV_OPCODE_LOGICAL_NOT, operands);
+	return result;
+}
+
 static struct {
 	uint64_t key;
 	spirv_id value;
@@ -1507,7 +1663,29 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 				hmput(index_map, o->op_call.var.index, id);
 			}
 			else if (func == add_name("sample_lod")) {
-				assert(false);
+				variable image_var = o->op_call.parameters[0];
+
+				spirv_id image_type;
+				spirv_id sampled_image_type;
+
+				if (image_var.type.type == tex2d_type_id) {
+					image_type         = spirv_image_type;
+					sampled_image_type = spirv_sampled_image_type;
+				}
+				else if (image_var.type.type == tex2darray_type_id) {
+					image_type         = spirv_image2darray_type;
+					sampled_image_type = spirv_sampled_image2darray_type;
+				}
+
+				spirv_id image = write_op_load(instructions, image_type, convert_kong_index_to_spirv_id(image_var.index));
+
+				spirv_id sampler = write_op_load(instructions, spirv_sampler_type, convert_kong_index_to_spirv_id(o->op_call.parameters[1].index));
+
+				spirv_id sampled_image = write_op_sampled_image(instructions, sampled_image_type, image, sampler);
+				spirv_id id            = write_op_image_sample_explicit_lod(instructions, spirv_float4_type, sampled_image,
+				                                                            convert_kong_index_to_spirv_id(o->op_call.parameters[2].index),
+																		    convert_kong_index_to_spirv_id(o->op_call.parameters[3].index));
+				hmput(index_map, o->op_call.var.index, id);
 			}
 			else if (func == add_name("float")) {
 				if (o->op_call.parameters[0].type.type == int_id) {
@@ -1642,6 +1820,10 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 				spirv_id id = write_op_load(instructions, convert_type_to_spirv_id(uint3_id), group_id_variable);
 				hmput(index_map, o->op_call.var.index, id);
 			}
+			else if (func == add_name("vertex_id")) {
+				spirv_id id = write_op_load(instructions, convert_type_to_spirv_id(uint_id), vertex_id_variable);
+				hmput(index_map, o->op_call.var.index, id);
+			}
 			else if (func == add_name("length")) {
 				spirv_id id = write_op_ext_inst(instructions, spirv_float_type, glsl_import, SPIRV_GLSL_STD_LENGTH,
 				                                convert_kong_index_to_spirv_id(o->op_call.parameters[0].index));
@@ -1662,7 +1844,11 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 			}
 			break;
 		}
-		case OPCODE_STORE_ACCESS_LIST: {
+		case OPCODE_STORE_ACCESS_LIST:
+		case OPCODE_ADD_AND_STORE_ACCESS_LIST:
+		case OPCODE_SUB_AND_STORE_ACCESS_LIST:
+		case OPCODE_MULTIPLY_AND_STORE_ACCESS_LIST:
+		case OPCODE_DIVIDE_AND_STORE_ACCESS_LIST: {
 			spirv_id    indices[256];
 			int         plain_indices[256];
 			access_kind access_kinds[256];
@@ -1754,14 +1940,141 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 
 				spirv_id pointer =
 				    write_op_access_chain(instructions, access_type, convert_kong_index_to_spirv_id(o->op_store_access_list.to.index), indices, indices_size);
-				write_op_store(instructions, pointer, convert_kong_index_to_spirv_id(o->op_store_access_list.from.index));
+
+				spirv_id stored;
+
+				if (o->type == OPCODE_STORE_ACCESS_LIST) {
+					stored = convert_kong_index_to_spirv_id(o->op_store_access_list.from.index);
+				}
+				else {
+					spirv_id loaded_pointer = write_op_load(instructions, convert_type_to_spirv_id(access_kong_type), pointer);
+					spirv_id from = convert_kong_index_to_spirv_id(o->op_store_access_list.from.index);
+
+					if (o->type == OPCODE_ADD_AND_STORE_ACCESS_LIST) {
+						if (vector_base_type(access_kong_type) == float_id) {
+							stored = write_op_f_add(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+						}
+						else if (vector_base_type(access_kong_type) == int_id || vector_base_type(access_kong_type) == uint_id) {
+							stored = write_op_i_add(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+						}
+					}
+					else if (o->type == OPCODE_SUB_AND_STORE_ACCESS_LIST) {
+						if (vector_base_type(access_kong_type) == float_id) {
+							stored = write_op_f_sub(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+						}
+						else if (vector_base_type(access_kong_type) == int_id || vector_base_type(access_kong_type) == uint_id) {
+							stored = write_op_i_sub(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+						}
+					}
+					else if (o->type == OPCODE_MULTIPLY_AND_STORE_ACCESS_LIST) {
+						stored = write_op_f_mul(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+					}
+					else if (o->type == OPCODE_DIVIDE_AND_STORE_ACCESS_LIST) {
+						stored = write_op_f_div(instructions, convert_type_to_spirv_id(access_kong_type), loaded_pointer, from);
+					}
+				}
+
+				write_op_store(instructions, pointer, stored);
+			}
+			break;
+		}
+		case OPCODE_AND: {
+			spirv_id result = write_op_logical_and(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
+			                                       convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			hmput(index_map, o->op_binary.result.index, result);
+			break;
+		}
+		case OPCODE_OR: {
+			spirv_id result = write_op_logical_or(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
+			                                      convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			hmput(index_map, o->op_binary.result.index, result);
+			break;
+		}
+		case OPCODE_NOT: {
+			spirv_id operand;
+			if (o->op_not.from.kind != VARIABLE_INTERNAL) {
+				operand = write_op_load(instructions, convert_type_to_spirv_id(o->op_not.from.type.type),
+										convert_kong_index_to_spirv_id(o->op_not.from.index));
+			}
+			else {
+				operand = convert_kong_index_to_spirv_id(o->op_not.from.index);
+			}
+
+			spirv_id result = write_op_not(instructions, spirv_bool_type, operand);
+			hmput(index_map, o->op_not.to.index, result);
+			break;
+		}
+		case OPCODE_NEGATE: {
+			spirv_id from;
+			if (o->op_negate.from.kind != VARIABLE_INTERNAL) {
+				from = write_op_load(instructions, convert_type_to_spirv_id(o->op_negate.from.type.type),
+									 convert_kong_index_to_spirv_id(o->op_negate.from.index));
+			}
+			else {
+				from = convert_kong_index_to_spirv_id(o->op_negate.from.index);
+			}
+
+			if (vector_base_type(o->op_binary.result.type.type) == float_id) {
+				spirv_id result = write_op_f_negate(instructions, convert_type_to_spirv_id(o->op_negate.to.type.type), from);
+				hmput(index_map, o->op_negate.to.index, result);
+			}
+			else if (vector_base_type(o->op_binary.result.type.type) == int_id || vector_base_type(o->op_binary.result.type.type) == uint_id) {
+				spirv_id result = write_op_s_negate(instructions, convert_type_to_spirv_id(o->op_negate.to.type.type), from);
+				hmput(index_map, o->op_negate.to.index, result);
 			}
+
 			break;
 		}
 		case OPCODE_STORE_VARIABLE: {
 			write_op_store(instructions, convert_kong_index_to_spirv_id(o->op_store_var.to.index), convert_kong_index_to_spirv_id(o->op_store_var.from.index));
 			break;
 		}
+		case OPCODE_ADD_AND_STORE_VARIABLE:
+		case OPCODE_SUB_AND_STORE_VARIABLE:
+		case OPCODE_MULTIPLY_AND_STORE_VARIABLE:
+		case OPCODE_DIVIDE_AND_STORE_VARIABLE: {
+			spirv_id to = convert_kong_index_to_spirv_id(o->op_store_var.to.index);
+			spirv_id from = convert_kong_index_to_spirv_id(o->op_store_var.from.index);
+			spirv_id to_loaded = write_op_load(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to);
+			spirv_id from_loaded = write_op_load(instructions, convert_type_to_spirv_id(o->op_store_var.from.type.type), from);
+			spirv_id result;
+
+			switch (o->type) {
+			case OPCODE_ADD_AND_STORE_VARIABLE: {
+				if (vector_base_type(o->op_store_var.to.type.type) == float_id) {
+					result = write_op_f_add(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				}
+				else if (vector_base_type(o->op_store_var.to.type.type) == int_id || vector_base_type(o->op_store_var.to.type.type) == uint_id) {
+					result = write_op_i_add(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				}
+				break;
+			}
+			case OPCODE_SUB_AND_STORE_VARIABLE: {
+				if (vector_base_type(o->op_store_var.to.type.type) == float_id) {
+					result = write_op_f_sub(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				}
+				else if (vector_base_type(o->op_store_var.to.type.type) == int_id || vector_base_type(o->op_store_var.to.type.type) == uint_id) {
+					result = write_op_i_sub(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				}
+				break;
+			}
+			case OPCODE_MULTIPLY_AND_STORE_VARIABLE: {
+				result = write_op_f_mul(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				break;
+			}
+			case OPCODE_DIVIDE_AND_STORE_VARIABLE: {
+				result = write_op_f_div(instructions, convert_type_to_spirv_id(o->op_store_var.to.type.type), to_loaded, from_loaded);
+				break;
+			}
+			default:
+				assert(false);
+				break;
+			}
+
+			write_op_store(instructions, to, result);
+
+			break;
+		}
 		case OPCODE_RETURN: {
 			if (stage == SHADER_STAGE_VERTEX && main) {
 				type *output_type = get_type(output);
@@ -1830,12 +2143,34 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 			ends_with_return = true;
 			break;
 		}
+		case OPCODE_DISCARD: {
+			write_op_discard(instructions);
+			break;
+		}
 		case OPCODE_LESS: {
 			spirv_id result = write_op_f_ord_less_than(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
 			                                           convert_kong_index_to_spirv_id(o->op_binary.right.index));
 			hmput(index_map, o->op_binary.result.index, result);
 			break;
 		}
+		case OPCODE_LESS_EQUAL: {
+			spirv_id result = write_op_f_ord_less_than_equal(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
+			                                                 convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			hmput(index_map, o->op_binary.result.index, result);
+			break;
+		}
+		case OPCODE_GREATER: {
+			spirv_id result = write_op_f_ord_greater_than(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
+			                                              convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			hmput(index_map, o->op_binary.result.index, result);
+			break;
+		}
+		case OPCODE_GREATER_EQUAL: {
+			spirv_id result = write_op_f_ord_greater_than_equal(instructions, spirv_bool_type, convert_kong_index_to_spirv_id(o->op_binary.left.index),
+			                                                    convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			hmput(index_map, o->op_binary.result.index, result);
+			break;
+		}
 		case OPCODE_ADD: {
 			spirv_id left;
 			if (o->op_binary.left.kind != VARIABLE_INTERNAL) {
@@ -1968,44 +2303,133 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 
 			break;
 		}
-		case OPCODE_IF: {
-			write_op_selection_merge(instructions, convert_kong_index_to_spirv_id(o->op_if.end_id), SELECTION_CONTROL_NONE);
-
-			write_op_branch_conditional(instructions, convert_kong_index_to_spirv_id(o->op_if.condition.index),
-			                            convert_kong_index_to_spirv_id(o->op_if.start_id), convert_kong_index_to_spirv_id(o->op_if.end_id));
+		case OPCODE_MOD: {
+			spirv_id left;
+			if (o->op_binary.left.kind != VARIABLE_INTERNAL) {
+				left =
+				    write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.left.type.type), convert_kong_index_to_spirv_id(o->op_binary.left.index));
+			}
+			else {
+				left = convert_kong_index_to_spirv_id(o->op_binary.left.index);
+			}
 
-			break;
-		}
-		case OPCODE_WHILE_START: {
-			spirv_id while_start_label    = convert_kong_index_to_spirv_id(o->op_while_start.start_id);
-			spirv_id while_continue_label = convert_kong_index_to_spirv_id(o->op_while_start.continue_id);
-			spirv_id while_end_label      = convert_kong_index_to_spirv_id(o->op_while_start.end_id);
+			spirv_id right;
+			if (o->op_binary.right.kind != VARIABLE_INTERNAL) {
+				right = write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.right.type.type),
+				                      convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			}
+			else {
+				right = convert_kong_index_to_spirv_id(o->op_binary.right.index);
+			}
 
-			write_op_branch(instructions, while_start_label);
-			write_op_label_preallocated(instructions, while_start_label);
+			spirv_id result = write_op_f_mod(instructions, convert_type_to_spirv_id(o->op_binary.result.type.type), left, right);
 
-			write_op_loop_merge(instructions, while_end_label, while_continue_label, LOOP_CONTROL_NONE);
+			hmput(index_map, o->op_binary.result.index, result);
 
-			spirv_id loop_start_id = allocate_index();
-			write_op_branch(instructions, loop_start_id);
-			write_op_label_preallocated(instructions, loop_start_id);
 			break;
 		}
-		case OPCODE_WHILE_CONDITION: {
-			spirv_id while_end_label = convert_kong_index_to_spirv_id(o->op_while.end_id);
+		case OPCODE_EQUALS: {
+			spirv_id left;
+			if (o->op_binary.left.kind != VARIABLE_INTERNAL) {
+				left = write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.left.type.type),
+									 convert_kong_index_to_spirv_id(o->op_binary.left.index));
+			}
+			else {
+				left = convert_kong_index_to_spirv_id(o->op_binary.left.index);
+			}
 
-			spirv_id pass = allocate_index();
+			spirv_id right;
+			if (o->op_binary.right.kind != VARIABLE_INTERNAL) {
+				right = write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.right.type.type),
+									  convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			}
+			else {
+				right = convert_kong_index_to_spirv_id(o->op_binary.right.index);
+			}
 
-			write_op_branch_conditional(instructions, convert_kong_index_to_spirv_id(o->op_while.condition.index), pass, while_end_label);
+			type_id result_type = o->op_binary.result.type.type;
+
+			if (result_type == float_id) {
+				spirv_id result = write_op_f_ord_equal(instructions, convert_type_to_spirv_id(result_type), left, right);
+				hmput(index_map, o->op_binary.result.index, result);
+			}
+			else if (result_type == int_id) {
+				spirv_id result = write_op_i_equal(instructions, convert_type_to_spirv_id(result_type), left, right);
+				hmput(index_map, o->op_binary.result.index, result);
+			}
 
-			write_op_label_preallocated(instructions, pass);
 			break;
 		}
-		case OPCODE_WHILE_END: {
-			spirv_id while_start_label    = convert_kong_index_to_spirv_id(o->op_while_end.start_id);
-			spirv_id while_continue_label = convert_kong_index_to_spirv_id(o->op_while_end.continue_id);
-			spirv_id while_end_label      = convert_kong_index_to_spirv_id(o->op_while_end.end_id);
-
+		case OPCODE_NOT_EQUALS: {
+			spirv_id left;
+			if (o->op_binary.left.kind != VARIABLE_INTERNAL) {
+				left = write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.left.type.type),
+									 convert_kong_index_to_spirv_id(o->op_binary.left.index));
+			}
+			else {
+				left = convert_kong_index_to_spirv_id(o->op_binary.left.index);
+			}
+
+			spirv_id right;
+			if (o->op_binary.right.kind != VARIABLE_INTERNAL) {
+				right = write_op_load(instructions, convert_type_to_spirv_id(o->op_binary.right.type.type),
+									  convert_kong_index_to_spirv_id(o->op_binary.right.index));
+			}
+			else {
+				right = convert_kong_index_to_spirv_id(o->op_binary.right.index);
+			}
+
+			type_id result_type = o->op_binary.result.type.type;
+
+			if (result_type == float_id) {
+				spirv_id result = write_op_f_ord_not_equal(instructions, convert_type_to_spirv_id(result_type), left, right);
+				hmput(index_map, o->op_binary.result.index, result);
+			}
+			else if (result_type == int_id) {
+				spirv_id result = write_op_i_not_equal(instructions, convert_type_to_spirv_id(result_type), left, right);
+				hmput(index_map, o->op_binary.result.index, result);
+			}
+
+			break;
+		}
+		case OPCODE_IF: {
+			write_op_selection_merge(instructions, convert_kong_index_to_spirv_id(o->op_if.end_id), SELECTION_CONTROL_NONE);
+
+			write_op_branch_conditional(instructions, convert_kong_index_to_spirv_id(o->op_if.condition.index),
+			                            convert_kong_index_to_spirv_id(o->op_if.start_id), convert_kong_index_to_spirv_id(o->op_if.end_id));
+
+			break;
+		}
+		case OPCODE_WHILE_START: {
+			spirv_id while_start_label    = convert_kong_index_to_spirv_id(o->op_while_start.start_id);
+			spirv_id while_continue_label = convert_kong_index_to_spirv_id(o->op_while_start.continue_id);
+			spirv_id while_end_label      = convert_kong_index_to_spirv_id(o->op_while_start.end_id);
+
+			write_op_branch(instructions, while_start_label);
+			write_op_label_preallocated(instructions, while_start_label);
+
+			write_op_loop_merge(instructions, while_end_label, while_continue_label, LOOP_CONTROL_NONE);
+
+			spirv_id loop_start_id = allocate_index();
+			write_op_branch(instructions, loop_start_id);
+			write_op_label_preallocated(instructions, loop_start_id);
+			break;
+		}
+		case OPCODE_WHILE_CONDITION: {
+			spirv_id while_end_label = convert_kong_index_to_spirv_id(o->op_while.end_id);
+
+			spirv_id pass = allocate_index();
+
+			write_op_branch_conditional(instructions, convert_kong_index_to_spirv_id(o->op_while.condition.index), pass, while_end_label);
+
+			write_op_label_preallocated(instructions, pass);
+			break;
+		}
+		case OPCODE_WHILE_END: {
+			spirv_id while_start_label    = convert_kong_index_to_spirv_id(o->op_while_end.start_id);
+			spirv_id while_continue_label = convert_kong_index_to_spirv_id(o->op_while_end.continue_id);
+			spirv_id while_end_label      = convert_kong_index_to_spirv_id(o->op_while_end.end_id);
+
 			write_op_branch(instructions, while_continue_label);
 			write_op_label_preallocated(instructions, while_continue_label);
 
@@ -2020,7 +2444,7 @@ static void write_function(instructions_buffer *instructions, function *f, spirv
 		}
 		default: {
 			debug_context context = {0};
-			error(context, "Opcode not implemented for SPIR-V");
+			error(context, "Opcode %d not implemented for SPIR-V", o->type);
 			break;
 		}
 		}
@@ -2325,6 +2749,12 @@ static void write_globals(instructions_buffer *decorations, instructions_buffer
 		write_op_decorate_value(decorations, group_id_variable, DECORATION_BUILTIN, BUILTIN_WORKGROUP_ID);
 	}
 
+	if (main->used_builtins.vertex_id) {
+		write_op_variable_preallocated(global_vars_block, convert_pointer_type_to_spirv_id(uint_id, STORAGE_CLASS_INPUT), vertex_id_variable,
+		                               STORAGE_CLASS_INPUT);
+		write_op_decorate_value(decorations, vertex_id_variable, DECORATION_BUILTIN, BUILTIN_VERTEX_ID);
+	}
+
 	if (stage == SHADER_STAGE_COMPUTE) {
 		write_op_decorate_value(decorations, work_group_size_variable, DECORATION_BUILTIN, BUILTIN_WORKGROUP_SIZE);
 	}
@@ -2445,6 +2875,12 @@ static void spirv_export_vertex(char *directory, function *main, bool debug) {
 		interfaces_count += 1;
 	}
 
+	if (main->used_builtins.vertex_id) {
+		vertex_id_variable = allocate_index();
+		interfaces[interfaces_count] = vertex_id_variable;
+		interfaces_count += 1;
+	}
+
 	write_op_entry_point(&decorations, EXECUTION_MODEL_VERTEX, entry_point, "main", interfaces, (uint16_t)interfaces_count);
 
 	write_vertex_input_decorations(&decorations, input_vars, (uint32_t)input_vars_count);
@@ -2854,6 +3290,389 @@ void spirv_export(char *directory, bool debug) {
 }
 
 ////
-void spirv_export2(char **vs, char **fs, bool debug) {
+
+static char *write_bytecode2(int *size_out, instructions_buffer *header, instructions_buffer *decorations,
+	instructions_buffer *base_types, instructions_buffer *constants, instructions_buffer *aggregate_types,
+	instructions_buffer *global_vars, instructions_buffer *instructions, bool debug) {
+	uint8_t *output_header      = (uint8_t *)header->instructions;
+	size_t   output_header_size = header->offset * 4;
+
+	uint8_t *output_decorations      = (uint8_t *)decorations->instructions;
+	size_t   output_decorations_size = decorations->offset * 4;
+
+	uint8_t *output_base_types      = (uint8_t *)base_types->instructions;
+	size_t   output_base_types_size = base_types->offset * 4;
+
+	uint8_t *output_constants      = (uint8_t *)constants->instructions;
+	size_t   output_constants_size = constants->offset * 4;
+
+	uint8_t *output_aggregate_types      = (uint8_t *)aggregate_types->instructions;
+	size_t   output_aggregate_types_size = aggregate_types->offset * 4;
+
+	uint8_t *output_global_vars      = (uint8_t *)global_vars->instructions;
+	size_t   output_global_vars_size = global_vars->offset * 4;
+
+	uint8_t *output_instructions      = (uint8_t *)instructions->instructions;
+	size_t   output_instructions_size = instructions->offset * 4;
+
+
+	{
+		char *buffer = malloc(1024 * 128);
+		int pos = 0;
+		memcpy(buffer + pos, output_header, output_header_size);
+		pos += output_header_size;
+		memcpy(buffer + pos, output_decorations, output_decorations_size);
+		pos += output_decorations_size;
+		memcpy(buffer + pos, output_base_types, output_base_types_size);
+		pos += output_base_types_size;
+		memcpy(buffer + pos, output_constants, output_constants_size);
+		pos += output_constants_size;
+		memcpy(buffer + pos, output_aggregate_types, output_aggregate_types_size);
+		pos += output_aggregate_types_size;
+		memcpy(buffer + pos, output_global_vars, output_global_vars_size);
+		pos += output_global_vars_size;
+		memcpy(buffer + pos, output_instructions, output_instructions_size);
+		pos += output_instructions_size;
+
+		*size_out =
+			output_header_size + output_decorations_size + output_base_types_size + output_constants_size + output_aggregate_types_size +
+			output_global_vars_size + output_instructions_size;
+
+		return buffer;
+	}
+}
+
+static char *spirv_export_vertex2(function *main, bool debug, int *size_out) {
+	next_index = 1;
+	init_maps();
+
+	find_used_builtins(main);
+	find_used_capabilities(main);
+
+	instructions_buffer header = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer decorations = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer base_types = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer constants = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer aggregate_types = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer global_vars = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer instructions = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	assert(main->parameters_size > 0);
+	type_id vertex_input  = main->parameter_types[0].type;
+	type_id vertex_output = main->return_type.type;
+
+	debug_context context = {0};
+	check(vertex_input != NO_TYPE, context, "vertex input missing");
+	check(vertex_output != NO_TYPE, context, "vertex output missing");
+
+	write_capabilities(&decorations, &main->used_capabilities);
+	glsl_import = write_op_ext_inst_import(&decorations, "GLSL.std.450");
+	write_op_memory_model(&decorations, ADDRESSING_MODEL_LOGICAL, MEMORY_MODEL_GLSL450);
+
+	type *input  = get_type(vertex_input);
+	type *output = get_type(vertex_output);
+
+	spirv_id entry_point = allocate_index();
+
+	input_vars_count = input->members.size;
+	for (size_t input_var_index = 0; input_var_index < input_vars_count; ++input_var_index) {
+		input_vars[input_var_index] = allocate_index();
+	}
+
+	output_vars_count = output->members.size;
+	for (size_t output_var_index = 0; output_var_index < output_vars_count; ++output_var_index) {
+		output_vars[output_var_index] = allocate_index();
+	}
+	per_vertex_var = output_vars[0];
+
+	spirv_id interfaces[256];
+	size_t   interfaces_count = 0;
+
+	for (size_t input_var_index = 0; input_var_index < input_vars_count; ++input_var_index) {
+		interfaces[interfaces_count] = input_vars[input_var_index];
+		interfaces_count += 1;
+	}
+
+	for (size_t output_var_index = 0; output_var_index < output_vars_count; ++output_var_index) {
+		interfaces[interfaces_count] = output_vars[output_var_index];
+		interfaces_count += 1;
+	}
+
+	if (main->used_builtins.vertex_id) {
+		vertex_id_variable = allocate_index();
+		interfaces[interfaces_count] = vertex_id_variable;
+		interfaces_count += 1;
+	}
+
+	write_op_entry_point(&decorations, EXECUTION_MODEL_VERTEX, entry_point, "main", interfaces, (uint16_t)interfaces_count);
+
+	write_vertex_input_decorations(&decorations, input_vars, (uint32_t)input_vars_count);
+
+	write_base_types(&base_types);
+
+	write_globals(&decorations, &aggregate_types, &global_vars, main, SHADER_STAGE_VERTEX);
+
+	for (size_t i = 0; i < input_vars_count; ++i) {
+		member m       = input->members.m[i];
+		input_types[i] = m.type.type;
+
+		if (m.type.type == float2_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float2_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else if (m.type.type == float3_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float3_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else if (m.type.type == float4_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float4_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else {
+			debug_context context = {0};
+			error(context, "Type unsupported for input in SPIR-V");
+		}
+	}
+
+	spirv_id types[]       = {spirv_float4_type};
+	spirv_id output_struct = write_type_struct(&aggregate_types, types, 1);
+	write_vertex_output_decorations(&decorations, output_struct, output_vars, (uint32_t)output_vars_count);
+
+	output_struct_pointer_type = write_type_pointer(&aggregate_types, STORAGE_CLASS_OUTPUT, output_struct);
+	write_op_variable_preallocated(&instructions, output_struct_pointer_type, per_vertex_var, STORAGE_CLASS_OUTPUT);
+
+	// special handling for the first one (position) via per_vertex_var
+	for (size_t i = 1; i < output_vars_count; ++i) {
+		member m        = output->members.m[i];
+		output_types[i] = m.type.type;
+
+		if (m.type.type == float2_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float2_id, STORAGE_CLASS_OUTPUT), output_vars[i],
+			                               STORAGE_CLASS_OUTPUT);
+		}
+		else if (m.type.type == float3_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float3_id, STORAGE_CLASS_OUTPUT), output_vars[i],
+			                               STORAGE_CLASS_OUTPUT);
+		}
+		else if (m.type.type == float4_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float4_id, STORAGE_CLASS_OUTPUT), output_vars[i],
+			                               STORAGE_CLASS_OUTPUT);
+		}
+		else {
+			debug_context context = {0};
+			error(context, "Type unsupported for input in SPIR-V");
+		}
+	}
+
+	write_functions(&instructions, main, entry_point, SHADER_STAGE_VERTEX, vertex_input, vertex_output);
+
+	write_types(&aggregate_types, main);
+
+	// header
+	write_magic_number(&header);
+	write_version_number(&header);
+	write_generator_magic_number(&header);
+	write_bound(&header);
+	write_instruction_schema(&header);
+
+	write_constants(&constants);
+
+	return write_bytecode2(size_out, &header, &decorations, &base_types, &constants, &aggregate_types, &global_vars, &instructions, debug);
 }
+
+static char *spirv_export_fragment2(function *main, bool debug, int *size_out) {
+	next_index = 1;
+	init_maps();
+
+	find_used_builtins(main);
+	find_used_capabilities(main);
+
+	instructions_buffer header = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer decorations = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer base_types = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer constants = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer aggregate_types = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer global_vars = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	instructions_buffer instructions = {
+	    .instructions = (uint32_t *)calloc(1024 * 1024, 1),
+	};
+
+	assert(main->parameters_size > 0);
+	type_id pixel_input  = main->parameter_types[0].type;
+	type_id pixel_output = main->return_type.type;
+
+	debug_context context = {0};
+	check(pixel_input != NO_TYPE, context, "fragment input missing");
+	check(pixel_output != NO_TYPE, context, "fragment output missing");
+
+	write_capabilities(&decorations, &main->used_capabilities);
+	glsl_import = write_op_ext_inst_import(&decorations, "GLSL.std.450");
+	write_op_memory_model(&decorations, ADDRESSING_MODEL_LOGICAL, MEMORY_MODEL_GLSL450);
+
+	type *input  = get_type(pixel_input);
+	type *output = get_type(pixel_output);
+
+	spirv_id entry_point = allocate_index();
+
+	input_vars_count = input->members.size - 1; // jump over pos input
+	for (size_t input_var_index = 0; input_var_index < input_vars_count; ++input_var_index) {
+		input_vars[input_var_index] = allocate_index();
+	}
+
+	assert(output->built_in); // has to be a float4 or a float4[]
+
+	if (output->array_size > 0) {
+		output_vars_count = output->array_size;
+		for (size_t output_var_index = 0; output_var_index < output->array_size; ++output_var_index) {
+			output_vars[output_var_index] = allocate_index();
+		}
+	}
+	else {
+		output_vars_count = 1;
+		output_vars[0]    = allocate_index();
+	}
+
+	spirv_id interfaces[256];
+	size_t   interfaces_count = 0;
+
+	for (size_t input_var_index = 0; input_var_index < input_vars_count; ++input_var_index) {
+		interfaces[interfaces_count] = input_vars[input_var_index];
+		interfaces_count += 1;
+	}
+
+	for (size_t output_var_index = 0; output_var_index < output_vars_count; ++output_var_index) {
+		interfaces[interfaces_count] = output_vars[output_var_index];
+		interfaces_count += 1;
+	}
+
+	write_op_entry_point(&decorations, EXECUTION_MODEL_FRAGMENT, entry_point, "main", interfaces, (uint16_t)interfaces_count);
+
+	write_op_execution_mode(&decorations, entry_point, EXECUTION_MODE_ORIGIN_UPPER_LEFT);
+
+	write_fragment_input_decorations(&decorations, input_vars, (uint32_t)input_vars_count);
+
+	write_base_types(&base_types);
+
+	write_globals(&decorations, &aggregate_types, &global_vars, main, SHADER_STAGE_FRAGMENT);
+
+	for (size_t i = 0; i < input_vars_count; ++i) {
+		member m       = input->members.m[i + 1]; // jump over pos input
+		input_types[i] = m.type.type;
+
+		if (m.type.type == float2_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float2_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else if (m.type.type == float3_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float3_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else if (m.type.type == float4_id) {
+			write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float4_id, STORAGE_CLASS_INPUT), input_vars[i], STORAGE_CLASS_INPUT);
+		}
+		else {
+			debug_context context = {0};
+			error(context, "Type unsupported for input in SPIR-V");
+		}
+	}
+
+	write_fragment_output_decorations(&decorations, output_vars, (uint32_t)output_vars_count);
+
+	for (size_t i = 0; i < output_vars_count; ++i) {
+		write_op_variable_preallocated(&instructions, convert_pointer_type_to_spirv_id(float4_id, STORAGE_CLASS_OUTPUT), output_vars[i], STORAGE_CLASS_OUTPUT);
+	}
+
+	write_functions(&instructions, main, entry_point, SHADER_STAGE_FRAGMENT, pixel_input, NO_TYPE);
+
+	write_types(&aggregate_types, main);
+
+	// header
+	write_magic_number(&header);
+	write_version_number(&header);
+	write_generator_magic_number(&header);
+	write_bound(&header);
+	write_instruction_schema(&header);
+
+	write_constants(&constants);
+
+	return write_bytecode2(size_out, &header, &decorations, &base_types, &constants, &aggregate_types, &global_vars, &instructions, debug);
+}
+
+void spirv_export2(char **vs, char **fs, int *vs_size, int *fs_size, bool debug) {
+	function *vertex_shaders[256];
+	size_t    vertex_shaders_size = 0;
+
+	function *fragment_shaders[256];
+	size_t    fragment_shaders_size = 0;
+
+	for (type_id i = 0; get_type(i) != NULL; ++i) {
+		type *t = get_type(i);
+		if (!t->built_in && has_attribute(&t->attributes, add_name("pipe"))) {
+			name_id vertex_shader_name   = NO_NAME;
+			name_id fragment_shader_name = NO_NAME;
+
+			for (size_t j = 0; j < t->members.size; ++j) {
+				if (t->members.m[j].name == add_name("vertex")) {
+					vertex_shader_name = t->members.m[j].value.identifier;
+				}
+				else if (t->members.m[j].name == add_name("fragment")) {
+					fragment_shader_name = t->members.m[j].value.identifier;
+				}
+			}
+
+			debug_context context = {0};
+			check(vertex_shader_name != NO_NAME, context, "vertex shader missing");
+			check(fragment_shader_name != NO_NAME, context, "fragment shader missing");
+
+			for (function_id i = 0; get_function(i) != NULL; ++i) {
+				function *f = get_function(i);
+				if (f->name == vertex_shader_name) {
+					vertex_shaders[vertex_shaders_size] = f;
+					vertex_shaders_size += 1;
+				}
+				else if (f->name == fragment_shader_name) {
+					fragment_shaders[fragment_shaders_size] = f;
+					fragment_shaders_size += 1;
+				}
+			}
+		}
+	}
+
+	*vs = spirv_export_vertex2(vertex_shaders[0], debug, vs_size);
+	*fs = spirv_export_fragment2(fragment_shaders[0], debug, fs_size);
+}
+
 ////

+ 1 - 0
base/sources/libs/kong/functions.h

@@ -15,6 +15,7 @@ typedef struct builtins {
 	bool dispatch_thread_id;
 	bool group_thread_id;
 	bool group_id;
+	bool vertex_id;
 } builtins;
 
 typedef struct capabilities {