Jelajahi Sumber

libobs: Add HLSL annotation parsing

Gives the ability to retrieve param annotations. Blocks wrapped in <>
following a parameter.

For example:
float slider < float max_value = 10.0; float min_value = 0.0; >;

These blocks are not for shading purposes but to help describe the
shader's gui as in the example above.

Adds graphics api functions for retrieving annotations:
size_t gs_param_get_num_annotations(const gs_eparam_t *param);

gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param,
		size_t annotation);

gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param,
		const char *name);
Alex Anderson 7 tahun lalu
induk
melakukan
af67086912

+ 3 - 0
libobs-d3d11/d3d11-shader.cpp

@@ -137,8 +137,11 @@ void gs_shader::BuildConstantBuffer()
 		case GS_SHADER_PARAM_BOOL:
 		case GS_SHADER_PARAM_BOOL:
 		case GS_SHADER_PARAM_INT:
 		case GS_SHADER_PARAM_INT:
 		case GS_SHADER_PARAM_FLOAT:     size = sizeof(float);   break;
 		case GS_SHADER_PARAM_FLOAT:     size = sizeof(float);   break;
+		case GS_SHADER_PARAM_INT2:
 		case GS_SHADER_PARAM_VEC2:      size = sizeof(vec2);    break;
 		case GS_SHADER_PARAM_VEC2:      size = sizeof(vec2);    break;
+		case GS_SHADER_PARAM_INT3:
 		case GS_SHADER_PARAM_VEC3:      size = sizeof(float)*3; break;
 		case GS_SHADER_PARAM_VEC3:      size = sizeof(float)*3; break;
+		case GS_SHADER_PARAM_INT4:
 		case GS_SHADER_PARAM_VEC4:      size = sizeof(vec4);    break;
 		case GS_SHADER_PARAM_VEC4:      size = sizeof(vec4);    break;
 		case GS_SHADER_PARAM_MATRIX4X4:
 		case GS_SHADER_PARAM_MATRIX4X4:
 			size = sizeof(float)*4*4;
 			size = sizeof(float)*4*4;

+ 299 - 36
libobs/graphics/effect-parser.c

@@ -21,6 +21,39 @@
 #include "effect-parser.h"
 #include "effect-parser.h"
 #include "effect.h"
 #include "effect.h"
 
 
+static inline bool ep_parse_param_assign(struct effect_parser *ep,
+		struct ep_param *param);
+
+static enum gs_shader_param_type get_effect_param_type(const char *type)
+{
+	if (strcmp(type, "float") == 0)
+		return GS_SHADER_PARAM_FLOAT;
+	else if (strcmp(type, "float2") == 0)
+		return GS_SHADER_PARAM_VEC2;
+	else if (strcmp(type, "float3") == 0)
+		return GS_SHADER_PARAM_VEC3;
+	else if (strcmp(type, "float4") == 0)
+		return GS_SHADER_PARAM_VEC4;
+	else if (strcmp(type, "int2") == 0)
+		return GS_SHADER_PARAM_INT2;
+	else if (strcmp(type, "int3") == 0)
+		return GS_SHADER_PARAM_INT3;
+	else if (strcmp(type, "int4") == 0)
+		return GS_SHADER_PARAM_INT4;
+	else if (astrcmp_n(type, "texture", 7) == 0)
+		return GS_SHADER_PARAM_TEXTURE;
+	else if (strcmp(type, "float4x4") == 0)
+		return GS_SHADER_PARAM_MATRIX4X4;
+	else if (strcmp(type, "bool") == 0)
+		return GS_SHADER_PARAM_BOOL;
+	else if (strcmp(type, "int") == 0)
+		return GS_SHADER_PARAM_INT;
+	else if (strcmp(type, "string") == 0)
+		return GS_SHADER_PARAM_STRING;
+
+	return GS_SHADER_PARAM_UNKNOWN;
+}
+
 void ep_free(struct effect_parser *ep)
 void ep_free(struct effect_parser *ep)
 {
 {
 	size_t i;
 	size_t i;
@@ -92,6 +125,18 @@ static inline struct ep_param *ep_getparam(struct effect_parser *ep,
 	return NULL;
 	return NULL;
 }
 }
 
 
+static inline struct ep_param *ep_getannotation(struct ep_param *param,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < param->annotations.num; i++) {
+		if (strcmp(name, param->annotations.array[i].name) == 0)
+			return param->annotations.array+i;
+	}
+
+	return NULL;
+}
+
 static inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep,
 static inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep,
 		const struct strref *ref)
 		const struct strref *ref)
 {
 {
@@ -262,6 +307,145 @@ error:
 	ep_struct_free(&eps);
 	ep_struct_free(&eps);
 }
 }
 
 
+static inline int ep_parse_param_annotation_var(struct effect_parser *ep,
+		struct ep_param *var)
+{
+	int code;
+
+	/* -------------------------------------- */
+	/* variable type */
+
+	if (!cf_next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	if (cf_token_is(&ep->cfp, ";"))
+		return PARSE_CONTINUE;
+	if (cf_token_is(&ep->cfp, ">"))
+		return PARSE_BREAK;
+
+	code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "type name", ";");
+	if (code != PARSE_SUCCESS)
+		return code;
+
+	bfree(var->type);
+	cf_copy_token(&ep->cfp, &var->type);
+
+	/* -------------------------------------- */
+	/* variable name */
+
+	if (!cf_next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	if (cf_token_is(&ep->cfp, ";")) {
+		cf_adderror_expecting(&ep->cfp, "variable name");
+		return PARSE_UNEXPECTED_CONTINUE;
+	}
+	if (cf_token_is(&ep->cfp, ">")) {
+		cf_adderror_expecting(&ep->cfp, "variable name");
+		return PARSE_UNEXPECTED_BREAK;
+	}
+
+	code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "variable name", ";");
+	if (code != PARSE_SUCCESS)
+		return code;
+
+	bfree(var->name);
+	cf_copy_token(&ep->cfp, &var->name);
+
+	/* -------------------------------------- */
+	/* variable mapping if any (POSITION, TEXCOORD, etc) */
+
+	if (!cf_next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	if (cf_token_is(&ep->cfp, ":")) {
+		cf_adderror_expecting(&ep->cfp, "= or ;");
+		return PARSE_UNEXPECTED_BREAK;
+	} else if (cf_token_is(&ep->cfp, ">")) {
+		cf_adderror_expecting(&ep->cfp, "= or ;");
+		return PARSE_UNEXPECTED_BREAK;
+	} else if (cf_token_is(&ep->cfp, "=")) {
+		if (!ep_parse_param_assign(ep, var)) {
+			cf_adderror_expecting(&ep->cfp, "assignment value");
+			return PARSE_UNEXPECTED_BREAK;
+		}
+	}
+
+	/* -------------------------------------- */
+
+	if (!cf_token_is(&ep->cfp, ";")) {
+		if (!cf_go_to_valid_token(&ep->cfp, ";", ">")) {
+			cf_adderror_expecting(&ep->cfp, "; or >");
+			return PARSE_EOF;
+		}
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static int ep_parse_annotations(struct effect_parser *ep,
+		struct darray *annotations)
+{
+	if (!cf_token_is(&ep->cfp, "<")) {
+		cf_adderror_expecting(&ep->cfp, "<");
+		goto error;
+	}
+
+	/* get annotation variables */
+	while (true) {
+		bool do_break = false;
+		struct ep_param var;
+
+		ep_param_init(&var, bstrdup(""), bstrdup(""), false, false,
+				false);
+
+		switch (ep_parse_param_annotation_var(ep, &var)) {
+		case PARSE_UNEXPECTED_CONTINUE:
+			cf_adderror_syntax_error(&ep->cfp);
+			/* Falls through. */
+		case PARSE_CONTINUE:
+			ep_param_free(&var);
+			continue;
+
+		case PARSE_UNEXPECTED_BREAK:
+			cf_adderror_syntax_error(&ep->cfp);
+			/* Falls through. */
+		case PARSE_BREAK:
+			ep_param_free(&var);
+			do_break = true;
+			break;
+
+		case PARSE_EOF:
+			ep_param_free(&var);
+			goto error;
+		}
+
+		if (do_break)
+			break;
+
+		darray_push_back(sizeof(struct ep_param), annotations, &var);
+	}
+
+	if (!cf_token_is(&ep->cfp, ">")) {
+		cf_adderror_expecting(&ep->cfp, ">");
+		goto error;
+	}
+	if (!cf_next_valid_token(&ep->cfp))
+		goto error;
+
+	return true;
+
+error:
+	return false;
+}
+
+static int ep_parse_param_annotations(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	return ep_parse_annotations(ep, &param->annotations.da);
+}
+
 static inline int ep_parse_pass_command_call(struct effect_parser *ep,
 static inline int ep_parse_pass_command_call(struct effect_parser *ep,
 		struct darray *call)
 		struct darray *call)
 {
 {
@@ -328,7 +512,7 @@ static int ep_parse_pass(struct effect_parser *ep, struct ep_pass *pass)
 
 
 	if (!cf_token_is(&ep->cfp, "{")) {
 	if (!cf_token_is(&ep->cfp, "{")) {
 		pass->name = bstrdup_n(ep->cfp.cur_token->str.array,
 		pass->name = bstrdup_n(ep->cfp.cur_token->str.array,
-		                        ep->cfp.cur_token->str.len);
+					ep->cfp.cur_token->str.len);
 		if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF;
 		if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF;
 	}
 	}
 
 
@@ -356,9 +540,19 @@ static void ep_parse_technique(struct effect_parser *ep)
 
 
 	if (cf_next_name(&ep->cfp, &ept.name, "name", ";") != PARSE_SUCCESS)
 	if (cf_next_name(&ep->cfp, &ept.name, "name", ";") != PARSE_SUCCESS)
 		goto error;
 		goto error;
-	if (cf_next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS)
-		goto error;
 
 
+	if (!cf_next_valid_token(&ep->cfp))
+		return;
+
+	if (!cf_token_is(&ep->cfp, "{")) {
+		if (!cf_go_to_token(&ep->cfp, ";", NULL)) {
+			cf_adderror_expecting(&ep->cfp, ";");
+			return;
+		}
+
+		cf_adderror_expecting(&ep->cfp, "{");
+		goto error;
+	}
 	if (!cf_next_valid_token(&ep->cfp))
 	if (!cf_next_valid_token(&ep->cfp))
 		goto error;
 		goto error;
 
 
@@ -756,6 +950,30 @@ static inline int ep_parse_param_assign_texture(struct effect_parser *ep,
 	return PARSE_SUCCESS;
 	return PARSE_SUCCESS;
 }
 }
 
 
+static inline int ep_parse_param_assign_string(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	int code;
+	char *str = NULL;
+
+	if (!cf_next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	code = cf_token_is_type(&ep->cfp, CFTOKEN_STRING, "string", ";");
+	if (code != PARSE_SUCCESS)
+		return code;
+
+	str = cf_literal_to_str(ep->cfp.cur_token->str.array,
+			ep->cfp.cur_token->str.len);
+
+	if (str) {
+		da_copy_array(param->default_val, str, strlen(str) + 1);
+		bfree(str);
+	}
+
+	return PARSE_SUCCESS;
+}
+
 static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep,
 static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep,
 		struct ep_param *param, bool is_float)
 		struct ep_param *param, bool is_float)
 {
 {
@@ -789,30 +1007,51 @@ static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep,
 	return PARSE_SUCCESS;
 	return PARSE_SUCCESS;
 }
 }
 
 
-/*
- * parses assignment for float1, float2, float3, float4, and any combination
- * for float3x3, float4x4, etc
- */
-static inline int ep_parse_param_assign_float_array(struct effect_parser *ep,
+static inline int ep_parse_param_assign_bool(struct effect_parser *ep,
 		struct ep_param *param)
 		struct ep_param *param)
 {
 {
-	const char *float_type = param->type+5;
-	int float_count = 0, code, i;
+	if (!cf_next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	if (cf_token_is(&ep->cfp, "true")) {
+		long l = 1;
+		da_push_back_array(param->default_val, &l, sizeof(long));
+		return PARSE_SUCCESS;
+	} else if (cf_token_is(&ep->cfp, "false")) {
+		long l = 0;
+		da_push_back_array(param->default_val, &l, sizeof(long));
+		return PARSE_SUCCESS;
+	}
+
+	cf_adderror_expecting(&ep->cfp, "true or false");
+
+	return PARSE_EOF;
+}
+
+/*
+ * parses assignment for float1, float2, float3, float4, int1, int2, int3, int4,
+ * and any combination for float3x3, float4x4, int3x3, int4x4, etc
+*/
+static inline int ep_parse_param_assign_intfloat_array(struct effect_parser *ep,
+		struct ep_param *param, bool is_float)
+{
+	const char *intfloat_type = param->type + (is_float ? 5 : 3);
+	int intfloat_count = 0, code, i;
 
 
 	/* -------------------------------------------- */
 	/* -------------------------------------------- */
 
 
-	if (float_type[0] < '1' || float_type[0] > '4')
+	if (intfloat_type[0] < '1' || intfloat_type[0] > '4')
 		cf_adderror(&ep->cfp, "Invalid row count", LEX_ERROR,
 		cf_adderror(&ep->cfp, "Invalid row count", LEX_ERROR,
 				NULL, NULL, NULL);
 				NULL, NULL, NULL);
 
 
-	float_count = float_type[0]-'0';
+	intfloat_count = intfloat_type[0]-'0';
 
 
-	if (float_type[1] == 'x') {
-		if (float_type[2] < '1' || float_type[2] > '4')
+	if (intfloat_type[1] == 'x') {
+		if (intfloat_type[2] < '1' || intfloat_type[2] > '4')
 			cf_adderror(&ep->cfp, "Invalid column count",
 			cf_adderror(&ep->cfp, "Invalid column count",
 					LEX_ERROR, NULL, NULL, NULL);
 					LEX_ERROR, NULL, NULL, NULL);
 
 
-		float_count *= float_type[2]-'0';
+		intfloat_count *= intfloat_type[2]-'0';
 	}
 	}
 
 
 	/* -------------------------------------------- */
 	/* -------------------------------------------- */
@@ -820,10 +1059,10 @@ static inline int ep_parse_param_assign_float_array(struct effect_parser *ep,
 	code = cf_next_token_should_be(&ep->cfp, "{", ";", NULL);
 	code = cf_next_token_should_be(&ep->cfp, "{", ";", NULL);
 	if (code != PARSE_SUCCESS) return code;
 	if (code != PARSE_SUCCESS) return code;
 
 
-	for (i = 0; i < float_count; i++) {
-		char *next = ((i+1) < float_count) ? "," : "}";
+	for (i = 0; i < intfloat_count; i++) {
+		char *next = ((i+1) < intfloat_count) ? "," : "}";
 
 
-		code = ep_parse_param_assign_intfloat(ep, param, true);
+		code = ep_parse_param_assign_intfloat(ep, param, is_float);
 		if (code != PARSE_SUCCESS) return code;
 		if (code != PARSE_SUCCESS) return code;
 
 
 		code = cf_next_token_should_be(&ep->cfp, next, ";", NULL);
 		code = cf_next_token_should_be(&ep->cfp, next, ";", NULL);
@@ -842,8 +1081,14 @@ static int ep_parse_param_assignment_val(struct effect_parser *ep,
 		return ep_parse_param_assign_intfloat(ep, param, false);
 		return ep_parse_param_assign_intfloat(ep, param, false);
 	else if (strcmp(param->type, "float") == 0)
 	else if (strcmp(param->type, "float") == 0)
 		return ep_parse_param_assign_intfloat(ep, param, true);
 		return ep_parse_param_assign_intfloat(ep, param, true);
+	else if (astrcmp_n(param->type, "int", 3) == 0)
+		return ep_parse_param_assign_intfloat_array(ep, param, false);
 	else if (astrcmp_n(param->type, "float", 5) == 0)
 	else if (astrcmp_n(param->type, "float", 5) == 0)
-		return ep_parse_param_assign_float_array(ep, param);
+		return ep_parse_param_assign_intfloat_array(ep, param, true);
+	else if (astrcmp_n(param->type, "string", 6) == 0)
+		return ep_parse_param_assign_string(ep, param);
+	else if (strcmp(param->type, "bool") == 0)
+		return ep_parse_param_assign_bool(ep, param);
 
 
 	cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment",
 	cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment",
 			LEX_ERROR, param->type, NULL, NULL);
 			LEX_ERROR, param->type, NULL, NULL);
@@ -879,6 +1124,9 @@ static void ep_parse_param(struct effect_parser *ep,
 		goto complete;
 		goto complete;
 	if (cf_token_is(&ep->cfp, "[") && !ep_parse_param_array(ep, &param))
 	if (cf_token_is(&ep->cfp, "[") && !ep_parse_param_array(ep, &param))
 		goto error;
 		goto error;
+	if (cf_token_is(&ep->cfp, "<") && !ep_parse_param_annotations(ep,
+			&param))
+		goto error;
 	if (cf_token_is(&ep->cfp, "=") && !ep_parse_param_assign(ep, &param))
 	if (cf_token_is(&ep->cfp, "=") && !ep_parse_param_assign(ep, &param))
 		goto error;
 		goto error;
 	/*
 	/*
@@ -945,7 +1193,6 @@ static void ep_parse_other(struct effect_parser *ep)
 		goto error;
 		goto error;
 	if (cf_next_name(&ep->cfp, &name, "name", ";") != PARSE_SUCCESS)
 	if (cf_next_name(&ep->cfp, &name, "name", ";") != PARSE_SUCCESS)
 		goto error;
 		goto error;
-
 	if (!cf_next_valid_token(&ep->cfp))
 	if (!cf_next_valid_token(&ep->cfp))
 		goto error;
 		goto error;
 
 
@@ -1339,6 +1586,35 @@ static void ep_makeshaderstring(struct effect_parser *ep,
 	ep_reset_written(ep);
 	ep_reset_written(ep);
 }
 }
 
 
+static void ep_compile_annotations(struct darray *ep_annotations,
+	struct darray *gsp_annotations, struct effect_parser *ep)
+{
+	darray_resize(sizeof(struct gs_effect_param),
+		gsp_annotations, ep_annotations->num);
+
+	size_t i;
+	for (i = 0; i < ep_annotations->num; i++) {
+		struct gs_effect_param *param = ((struct gs_effect_param *)
+				gsp_annotations->array)+i;
+		struct ep_param *param_in = ((struct ep_param *)
+				ep_annotations->array)+i;
+
+		param->name = bstrdup(param_in->name);
+		param->section = EFFECT_ANNOTATION;
+		param->effect = ep->effect;
+		da_move(param->default_val, param_in->default_val);
+
+		param->type = get_effect_param_type(param_in->type);
+	}
+}
+
+static void ep_compile_param_annotations(struct ep_param *ep_param_input,
+	struct gs_effect_param *gs_effect_input, struct effect_parser *ep)
+{
+	ep_compile_annotations(&(ep_param_input->annotations.da),
+			&(gs_effect_input->annotations.da), ep);
+}
+
 static void ep_compile_param(struct effect_parser *ep, size_t idx)
 static void ep_compile_param(struct effect_parser *ep, size_t idx)
 {
 {
 	struct gs_effect_param *param;
 	struct gs_effect_param *param;
@@ -1353,27 +1629,14 @@ static void ep_compile_param(struct effect_parser *ep, size_t idx)
 	param->effect  = ep->effect;
 	param->effect  = ep->effect;
 	da_move(param->default_val, param_in->default_val);
 	da_move(param->default_val, param_in->default_val);
 
 
-	if (strcmp(param_in->type, "bool") == 0)
-		param->type = GS_SHADER_PARAM_BOOL;
-	else if (strcmp(param_in->type, "float") == 0)
-		param->type = GS_SHADER_PARAM_FLOAT;
-	else if (strcmp(param_in->type, "int") == 0)
-		param->type = GS_SHADER_PARAM_INT;
-	else if (strcmp(param_in->type, "float2") == 0)
-		param->type = GS_SHADER_PARAM_VEC2;
-	else if (strcmp(param_in->type, "float3") == 0)
-		param->type = GS_SHADER_PARAM_VEC3;
-	else if (strcmp(param_in->type, "float4") == 0)
-		param->type = GS_SHADER_PARAM_VEC4;
-	else if (strcmp(param_in->type, "float4x4") == 0)
-		param->type = GS_SHADER_PARAM_MATRIX4X4;
-	else if (param_in->is_texture)
-		param->type = GS_SHADER_PARAM_TEXTURE;
+	param->type = get_effect_param_type(param_in->type);
 
 
 	if (strcmp(param_in->name, "ViewProj") == 0)
 	if (strcmp(param_in->name, "ViewProj") == 0)
 		ep->effect->view_proj = param;
 		ep->effect->view_proj = param;
 	else if (strcmp(param_in->name, "World") == 0)
 	else if (strcmp(param_in->name, "World") == 0)
 		ep->effect->world = param;
 		ep->effect->world = param;
+
+	ep_compile_param_annotations(param_in, param, ep);
 }
 }
 
 
 static bool ep_compile_pass_shaderparams(struct effect_parser *ep,
 static bool ep_compile_pass_shaderparams(struct effect_parser *ep,

+ 7 - 0
libobs/graphics/effect-parser.h

@@ -20,6 +20,7 @@
 #include "../util/darray.h"
 #include "../util/darray.h"
 #include "../util/cf-parser.h"
 #include "../util/cf-parser.h"
 #include "graphics.h"
 #include "graphics.h"
+#include "shader-parser.h"
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
@@ -72,6 +73,7 @@ struct ep_param {
 	struct gs_effect_param *param;
 	struct gs_effect_param *param;
 	bool is_const, is_property, is_uniform, is_texture, written;
 	bool is_const, is_property, is_uniform, is_texture, written;
 	int writeorder, array_count;
 	int writeorder, array_count;
+	DARRAY(struct ep_param) annotations;
 };
 };
 
 
 extern void ep_param_writevar(struct dstr *dst, struct darray *use_params);
 extern void ep_param_writevar(struct dstr *dst, struct darray *use_params);
@@ -91,6 +93,7 @@ static inline void ep_param_init(struct ep_param *epp,
 	epp->array_count = 0;
 	epp->array_count = 0;
 	da_init(epp->default_val);
 	da_init(epp->default_val);
 	da_init(epp->properties);
 	da_init(epp->properties);
+	da_init(epp->annotations);
 }
 }
 
 
 static inline void ep_param_free(struct ep_param *epp)
 static inline void ep_param_free(struct ep_param *epp)
@@ -99,6 +102,10 @@ static inline void ep_param_free(struct ep_param *epp)
 	bfree(epp->name);
 	bfree(epp->name);
 	da_free(epp->default_val);
 	da_free(epp->default_val);
 	da_free(epp->properties);
 	da_free(epp->properties);
+
+	for (size_t i = 0; i < epp->annotations.num; i++)
+		ep_param_free(epp->annotations.array + i);
+	da_free(epp->annotations);
 }
 }
 
 
 /* ------------------------------------------------------------------------- */
 /* ------------------------------------------------------------------------- */

+ 57 - 0
libobs/graphics/effect.c

@@ -286,6 +286,63 @@ gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
 	return NULL;
 	return NULL;
 }
 }
 
 
+size_t gs_param_get_num_annotations(const gs_eparam_t *param)
+{
+	return param ? param->annotations.num : 0;
+}
+
+gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param,
+		size_t annotation)
+{
+	if (!param) return NULL;
+	
+	struct gs_effect_param *params = param->annotations.array;
+	if (annotation > param->annotations.num)
+		return NULL;
+
+	return params + annotation;
+}
+
+gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param,
+		const char *name)
+{
+	if (!param) return NULL;
+	struct gs_effect_param *params = param->annotations.array;
+
+	for (size_t i = 0; i < param->annotations.num; i++) {
+		struct gs_effect_param *g_param = params + i;
+		if (strcmp(g_param->name, name) == 0)
+			return g_param;
+	}
+	return NULL;
+}
+
+gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique,
+		size_t pass)
+{
+	if (!technique) return NULL;
+	struct gs_effect_pass *passes = technique->passes.array;
+
+	if (pass > technique->passes.num)
+		return NULL;
+
+	return passes + pass;
+}
+
+gs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique,
+		const char *name)
+{
+	if (!technique) return NULL;
+	struct gs_effect_pass *passes = technique->passes.array;
+
+	for (size_t i = 0; i < technique->passes.num; i++) {
+		struct gs_effect_pass *g_pass = passes + i;
+		if (strcmp(g_pass->name, name) == 0)
+			return g_pass;
+	}
+	return NULL;
+}
+
 gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)
 gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)
 {
 {
 	return effect ? effect->view_proj : NULL;
 	return effect ? effect->view_proj : NULL;

+ 12 - 1
libobs/graphics/effect.h

@@ -41,7 +41,8 @@ enum effect_section {
 	EFFECT_PARAM,
 	EFFECT_PARAM,
 	EFFECT_TECHNIQUE,
 	EFFECT_TECHNIQUE,
 	EFFECT_SAMPLER,
 	EFFECT_SAMPLER,
-	EFFECT_PASS
+	EFFECT_PASS,
+	EFFECT_ANNOTATION
 };
 };
 
 
 /* ------------------------------------------------------------------------- */
 /* ------------------------------------------------------------------------- */
@@ -61,11 +62,13 @@ struct gs_effect_param {
 
 
 	/*char *full_name;
 	/*char *full_name;
 	float scroller_min, scroller_max, scroller_inc, scroller_mul;*/
 	float scroller_min, scroller_max, scroller_inc, scroller_mul;*/
+	DARRAY(struct gs_effect_param) annotations;
 };
 };
 
 
 static inline void effect_param_init(struct gs_effect_param *param)
 static inline void effect_param_init(struct gs_effect_param *param)
 {
 {
 	memset(param, 0, sizeof(struct gs_effect_param));
 	memset(param, 0, sizeof(struct gs_effect_param));
+	da_init(param->annotations);
 }
 }
 
 
 static inline void effect_param_free(struct gs_effect_param *param)
 static inline void effect_param_free(struct gs_effect_param *param)
@@ -74,6 +77,12 @@ static inline void effect_param_free(struct gs_effect_param *param)
 	//bfree(param->full_name);
 	//bfree(param->full_name);
 	da_free(param->cur_val);
 	da_free(param->cur_val);
 	da_free(param->default_val);
 	da_free(param->default_val);
+
+	size_t i;
+	for (i = 0; i < param->annotations.num; i++)
+		effect_param_free(param->annotations.array + i);
+
+	da_free(param->annotations);
 }
 }
 
 
 EXPORT void effect_param_parse_property(gs_eparam_t *param,
 EXPORT void effect_param_parse_property(gs_eparam_t *param,
@@ -106,6 +115,7 @@ static inline void effect_pass_free(struct gs_effect_pass *pass)
 	bfree(pass->name);
 	bfree(pass->name);
 	da_free(pass->vertshader_params);
 	da_free(pass->vertshader_params);
 	da_free(pass->pixelshader_params);
 	da_free(pass->pixelshader_params);
+
 	gs_shader_destroy(pass->vertshader);
 	gs_shader_destroy(pass->vertshader);
 	gs_shader_destroy(pass->pixelshader);
 	gs_shader_destroy(pass->pixelshader);
 }
 }
@@ -130,6 +140,7 @@ static inline void effect_technique_free(struct gs_effect_technique *t)
 	size_t i;
 	size_t i;
 	for (i = 0; i < t->passes.num; i++)
 	for (i = 0; i < t->passes.num; i++)
 		effect_pass_free(t->passes.array+i);
 		effect_pass_free(t->passes.array+i);
+
 	da_free(t->passes);
 	da_free(t->passes);
 	bfree(t->name);
 	bfree(t->name);
 }
 }

+ 10 - 0
libobs/graphics/graphics.h

@@ -267,6 +267,7 @@ typedef struct gs_shader           gs_shader_t;
 typedef struct gs_shader_param     gs_sparam_t;
 typedef struct gs_shader_param     gs_sparam_t;
 typedef struct gs_effect           gs_effect_t;
 typedef struct gs_effect           gs_effect_t;
 typedef struct gs_effect_technique gs_technique_t;
 typedef struct gs_effect_technique gs_technique_t;
+typedef struct gs_effect_pass      gs_epass_t;
 typedef struct gs_effect_param     gs_eparam_t;
 typedef struct gs_effect_param     gs_eparam_t;
 typedef struct gs_device           gs_device_t;
 typedef struct gs_device           gs_device_t;
 typedef struct graphics_subsystem  graphics_t;
 typedef struct graphics_subsystem  graphics_t;
@@ -368,12 +369,21 @@ EXPORT bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass);
 EXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique,
 EXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique,
 		const char *name);
 		const char *name);
 EXPORT void gs_technique_end_pass(gs_technique_t *technique);
 EXPORT void gs_technique_end_pass(gs_technique_t *technique);
+EXPORT gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique,
+		size_t pass);
+EXPORT gs_epass_t *gs_technique_get_pass_by_name(
+		const gs_technique_t *technique, const char *name);
 
 
 EXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect);
 EXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect);
 EXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect,
 EXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect,
 		size_t param);
 		size_t param);
 EXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
 EXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
 		const char *name);
 		const char *name);
+EXPORT size_t gs_param_get_num_annotations(const gs_eparam_t *param);
+EXPORT gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param,
+		size_t annotation);
+EXPORT gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param,
+		const char *name);
 
 
 /** Helper function to simplify effect usage.  Use with a while loop that
 /** Helper function to simplify effect usage.  Use with a while loop that
  * contains drawing functions.  Automatically handles techniques, passes, and
  * contains drawing functions.  Automatically handles techniques, passes, and