1 /******************************************************************************
2     Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17 
18 #include "effect.h"
19 #include "graphics-internal.h"
20 #include "vec2.h"
21 #include "vec3.h"
22 #include "vec4.h"
23 
gs_effect_actually_destroy(gs_effect_t * effect)24 void gs_effect_actually_destroy(gs_effect_t *effect)
25 {
26 	effect_free(effect);
27 	bfree(effect);
28 }
29 
gs_effect_destroy(gs_effect_t * effect)30 void gs_effect_destroy(gs_effect_t *effect)
31 {
32 	if (effect) {
33 		if (!effect->cached)
34 			gs_effect_actually_destroy(effect);
35 	}
36 }
37 
gs_effect_get_technique(const gs_effect_t * effect,const char * name)38 gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect,
39 					const char *name)
40 {
41 	if (!effect)
42 		return NULL;
43 
44 	for (size_t i = 0; i < effect->techniques.num; i++) {
45 		struct gs_effect_technique *tech = effect->techniques.array + i;
46 		if (strcmp(tech->name, name) == 0)
47 			return tech;
48 	}
49 
50 	return NULL;
51 }
52 
gs_effect_get_current_technique(const gs_effect_t * effect)53 gs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect)
54 {
55 	if (!effect)
56 		return NULL;
57 
58 	return effect->cur_technique;
59 }
60 
gs_effect_loop(gs_effect_t * effect,const char * name)61 bool gs_effect_loop(gs_effect_t *effect, const char *name)
62 {
63 	if (!effect) {
64 		return false;
65 	}
66 
67 	if (!effect->looping) {
68 		gs_technique_t *tech;
69 
70 		if (!!gs_get_effect()) {
71 			blog(LOG_WARNING, "gs_effect_loop: An effect is "
72 					  "already active");
73 			return false;
74 		}
75 
76 		tech = gs_effect_get_technique(effect, name);
77 		if (!tech) {
78 			blog(LOG_WARNING,
79 			     "gs_effect_loop: Technique '%s' "
80 			     "not found.",
81 			     name);
82 			return false;
83 		}
84 
85 		gs_technique_begin(tech);
86 
87 		effect->looping = true;
88 	} else {
89 		gs_technique_end_pass(effect->cur_technique);
90 	}
91 
92 	if (!gs_technique_begin_pass(effect->cur_technique,
93 				     effect->loop_pass++)) {
94 		gs_technique_end(effect->cur_technique);
95 		effect->looping = false;
96 		effect->loop_pass = 0;
97 		return false;
98 	}
99 
100 	return true;
101 }
102 
gs_technique_begin(gs_technique_t * tech)103 size_t gs_technique_begin(gs_technique_t *tech)
104 {
105 	if (!tech)
106 		return 0;
107 
108 	tech->effect->cur_technique = tech;
109 	tech->effect->graphics->cur_effect = tech->effect;
110 
111 	return tech->passes.num;
112 }
113 
gs_technique_end(gs_technique_t * tech)114 void gs_technique_end(gs_technique_t *tech)
115 {
116 	if (!tech)
117 		return;
118 
119 	struct gs_effect *effect = tech->effect;
120 	struct gs_effect_param *params = effect->params.array;
121 	size_t i;
122 
123 	gs_load_vertexshader(NULL);
124 	gs_load_pixelshader(NULL);
125 
126 	tech->effect->cur_technique = NULL;
127 	tech->effect->graphics->cur_effect = NULL;
128 
129 	for (i = 0; i < effect->params.num; i++) {
130 		struct gs_effect_param *param = params + i;
131 
132 		da_resize(param->cur_val, 0);
133 		param->changed = false;
134 		if (param->next_sampler)
135 			param->next_sampler = NULL;
136 	}
137 }
138 
reset_params(struct darray * shaderparams)139 static inline void reset_params(struct darray *shaderparams)
140 {
141 	struct pass_shaderparam *params = shaderparams->array;
142 	size_t i;
143 
144 	for (i = 0; i < shaderparams->num; i++)
145 		params[i].eparam->changed = false;
146 }
147 
upload_shader_params(struct darray * pass_params,bool changed_only)148 static void upload_shader_params(struct darray *pass_params, bool changed_only)
149 {
150 	struct pass_shaderparam *params = pass_params->array;
151 	size_t i;
152 
153 	for (i = 0; i < pass_params->num; i++) {
154 		struct pass_shaderparam *param = params + i;
155 		struct gs_effect_param *eparam = param->eparam;
156 		gs_sparam_t *sparam = param->sparam;
157 
158 		if (eparam->next_sampler)
159 			gs_shader_set_next_sampler(sparam,
160 						   eparam->next_sampler);
161 
162 		if (changed_only && !eparam->changed)
163 			continue;
164 
165 		if (!eparam->cur_val.num) {
166 			if (eparam->default_val.num)
167 				da_copy(eparam->cur_val, eparam->default_val);
168 			else
169 				continue;
170 		}
171 
172 		gs_shader_set_val(sparam, eparam->cur_val.array,
173 				  eparam->cur_val.num);
174 	}
175 }
176 
upload_parameters(struct gs_effect * effect,bool changed_only)177 static inline void upload_parameters(struct gs_effect *effect,
178 				     bool changed_only)
179 {
180 	struct darray *vshader_params, *pshader_params;
181 
182 	if (!effect->cur_pass)
183 		return;
184 
185 	vshader_params = &effect->cur_pass->vertshader_params.da;
186 	pshader_params = &effect->cur_pass->pixelshader_params.da;
187 
188 	upload_shader_params(vshader_params, changed_only);
189 	upload_shader_params(pshader_params, changed_only);
190 	reset_params(vshader_params);
191 	reset_params(pshader_params);
192 }
193 
gs_effect_update_params(gs_effect_t * effect)194 void gs_effect_update_params(gs_effect_t *effect)
195 {
196 	if (effect)
197 		upload_parameters(effect, true);
198 }
199 
gs_technique_begin_pass(gs_technique_t * tech,size_t idx)200 bool gs_technique_begin_pass(gs_technique_t *tech, size_t idx)
201 {
202 	struct gs_effect_pass *passes;
203 	struct gs_effect_pass *cur_pass;
204 
205 	if (!tech || idx >= tech->passes.num)
206 		return false;
207 
208 	passes = tech->passes.array;
209 	cur_pass = passes + idx;
210 
211 	tech->effect->cur_pass = cur_pass;
212 	gs_load_vertexshader(cur_pass->vertshader);
213 	gs_load_pixelshader(cur_pass->pixelshader);
214 	upload_parameters(tech->effect, false);
215 
216 	return true;
217 }
218 
gs_technique_begin_pass_by_name(gs_technique_t * tech,const char * name)219 bool gs_technique_begin_pass_by_name(gs_technique_t *tech, const char *name)
220 {
221 	if (!tech)
222 		return false;
223 
224 	for (size_t i = 0; i < tech->passes.num; i++) {
225 		struct gs_effect_pass *pass = tech->passes.array + i;
226 		if (strcmp(pass->name, name) == 0) {
227 			gs_technique_begin_pass(tech, i);
228 			return true;
229 		}
230 	}
231 
232 	return false;
233 }
234 
clear_tex_params(struct darray * in_params)235 static inline void clear_tex_params(struct darray *in_params)
236 {
237 	struct pass_shaderparam *params = in_params->array;
238 
239 	for (size_t i = 0; i < in_params->num; i++) {
240 		struct pass_shaderparam *param = params + i;
241 		struct gs_shader_param_info info;
242 
243 		gs_shader_get_param_info(param->sparam, &info);
244 		if (info.type == GS_SHADER_PARAM_TEXTURE)
245 			gs_shader_set_texture(param->sparam, NULL);
246 	}
247 }
248 
gs_technique_end_pass(gs_technique_t * tech)249 void gs_technique_end_pass(gs_technique_t *tech)
250 {
251 	if (!tech)
252 		return;
253 
254 	struct gs_effect_pass *pass = tech->effect->cur_pass;
255 	if (!pass)
256 		return;
257 
258 	clear_tex_params(&pass->vertshader_params.da);
259 	clear_tex_params(&pass->pixelshader_params.da);
260 	tech->effect->cur_pass = NULL;
261 }
262 
gs_effect_get_num_params(const gs_effect_t * effect)263 size_t gs_effect_get_num_params(const gs_effect_t *effect)
264 {
265 	return effect ? effect->params.num : 0;
266 }
267 
gs_effect_get_param_by_idx(const gs_effect_t * effect,size_t param)268 gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param)
269 {
270 	if (!effect)
271 		return NULL;
272 
273 	struct gs_effect_param *params = effect->params.array;
274 	if (param >= effect->params.num)
275 		return NULL;
276 
277 	return params + param;
278 }
279 
gs_effect_get_param_by_name(const gs_effect_t * effect,const char * name)280 gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
281 					 const char *name)
282 {
283 	if (!effect)
284 		return NULL;
285 
286 	struct gs_effect_param *params = effect->params.array;
287 
288 	for (size_t i = 0; i < effect->params.num; i++) {
289 		struct gs_effect_param *param = params + i;
290 
291 		if (strcmp(param->name, name) == 0)
292 			return param;
293 	}
294 
295 	return NULL;
296 }
297 
gs_param_get_num_annotations(const gs_eparam_t * param)298 size_t gs_param_get_num_annotations(const gs_eparam_t *param)
299 {
300 	return param ? param->annotations.num : 0;
301 }
302 
gs_param_get_annotation_by_idx(const gs_eparam_t * param,size_t annotation)303 gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param,
304 					    size_t annotation)
305 {
306 	if (!param)
307 		return NULL;
308 
309 	struct gs_effect_param *params = param->annotations.array;
310 	if (annotation > param->annotations.num)
311 		return NULL;
312 
313 	return params + annotation;
314 }
315 
gs_param_get_annotation_by_name(const gs_eparam_t * param,const char * name)316 gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param,
317 					     const char *name)
318 {
319 	if (!param)
320 		return NULL;
321 	struct gs_effect_param *params = param->annotations.array;
322 
323 	for (size_t i = 0; i < param->annotations.num; i++) {
324 		struct gs_effect_param *g_param = params + i;
325 		if (strcmp(g_param->name, name) == 0)
326 			return g_param;
327 	}
328 	return NULL;
329 }
330 
gs_technique_get_pass_by_idx(const gs_technique_t * technique,size_t pass)331 gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique,
332 					 size_t pass)
333 {
334 	if (!technique)
335 		return NULL;
336 	struct gs_effect_pass *passes = technique->passes.array;
337 
338 	if (pass > technique->passes.num)
339 		return NULL;
340 
341 	return passes + pass;
342 }
343 
gs_technique_get_pass_by_name(const gs_technique_t * technique,const char * name)344 gs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique,
345 					  const char *name)
346 {
347 	if (!technique)
348 		return NULL;
349 	struct gs_effect_pass *passes = technique->passes.array;
350 
351 	for (size_t i = 0; i < technique->passes.num; i++) {
352 		struct gs_effect_pass *g_pass = passes + i;
353 		if (strcmp(g_pass->name, name) == 0)
354 			return g_pass;
355 	}
356 	return NULL;
357 }
358 
gs_effect_get_viewproj_matrix(const gs_effect_t * effect)359 gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)
360 {
361 	return effect ? effect->view_proj : NULL;
362 }
363 
gs_effect_get_world_matrix(const gs_effect_t * effect)364 gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect)
365 {
366 	return effect ? effect->world : NULL;
367 }
368 
gs_effect_get_param_info(const gs_eparam_t * param,struct gs_effect_param_info * info)369 void gs_effect_get_param_info(const gs_eparam_t *param,
370 			      struct gs_effect_param_info *info)
371 {
372 	if (!param)
373 		return;
374 
375 	info->name = param->name;
376 	info->type = param->type;
377 }
378 
effect_setval_inline(gs_eparam_t * param,const void * data,size_t size)379 static inline void effect_setval_inline(gs_eparam_t *param, const void *data,
380 					size_t size)
381 {
382 	bool size_changed;
383 
384 	if (!param) {
385 		blog(LOG_ERROR, "effect_setval_inline: invalid param");
386 		return;
387 	}
388 
389 	if (!data) {
390 		blog(LOG_ERROR, "effect_setval_inline: invalid data");
391 		return;
392 	}
393 
394 	size_changed = param->cur_val.num != size;
395 
396 	if (size_changed)
397 		da_resize(param->cur_val, size);
398 
399 	if (size_changed || memcmp(param->cur_val.array, data, size) != 0) {
400 		memcpy(param->cur_val.array, data, size);
401 		param->changed = true;
402 	}
403 }
404 
405 #ifndef min
406 #define min(a, b) (((a) < (b)) ? (a) : (b))
407 #endif
effect_getval_inline(gs_eparam_t * param,void * data,size_t size)408 static inline void effect_getval_inline(gs_eparam_t *param, void *data,
409 					size_t size)
410 {
411 	if (!param) {
412 		blog(LOG_ERROR, "effect_getval_inline: invalid param");
413 		return;
414 	}
415 
416 	if (!data) {
417 		blog(LOG_ERROR, "effect_getval_inline: invalid data");
418 		return;
419 	}
420 
421 	size_t bytes = min(size, param->cur_val.num);
422 
423 	memcpy(data, param->cur_val.array, bytes);
424 }
425 
effect_getdefaultval_inline(gs_eparam_t * param,void * data,size_t size)426 static inline void effect_getdefaultval_inline(gs_eparam_t *param, void *data,
427 					       size_t size)
428 {
429 	if (!param) {
430 		blog(LOG_ERROR, "effect_getdefaultval_inline: invalid param");
431 		return;
432 	}
433 
434 	if (!data) {
435 		blog(LOG_ERROR, "effect_getdefaultval_inline: invalid data");
436 		return;
437 	}
438 
439 	size_t bytes = min(size, param->default_val.num);
440 
441 	memcpy(data, param->default_val.array, bytes);
442 }
443 
gs_effect_set_bool(gs_eparam_t * param,bool val)444 void gs_effect_set_bool(gs_eparam_t *param, bool val)
445 {
446 	int b_val = (int)val;
447 	effect_setval_inline(param, &b_val, sizeof(int));
448 }
449 
gs_effect_set_float(gs_eparam_t * param,float val)450 void gs_effect_set_float(gs_eparam_t *param, float val)
451 {
452 	effect_setval_inline(param, &val, sizeof(float));
453 }
454 
gs_effect_set_int(gs_eparam_t * param,int val)455 void gs_effect_set_int(gs_eparam_t *param, int val)
456 {
457 	effect_setval_inline(param, &val, sizeof(int));
458 }
459 
gs_effect_set_matrix4(gs_eparam_t * param,const struct matrix4 * val)460 void gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val)
461 {
462 	effect_setval_inline(param, val, sizeof(struct matrix4));
463 }
464 
gs_effect_set_vec2(gs_eparam_t * param,const struct vec2 * val)465 void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val)
466 {
467 	effect_setval_inline(param, val, sizeof(struct vec2));
468 }
469 
gs_effect_set_vec3(gs_eparam_t * param,const struct vec3 * val)470 void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val)
471 {
472 	effect_setval_inline(param, val, sizeof(float) * 3);
473 }
474 
gs_effect_set_vec4(gs_eparam_t * param,const struct vec4 * val)475 void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val)
476 {
477 	effect_setval_inline(param, val, sizeof(struct vec4));
478 }
479 
gs_effect_set_color(gs_eparam_t * param,uint32_t argb)480 void gs_effect_set_color(gs_eparam_t *param, uint32_t argb)
481 {
482 	struct vec4 v_color;
483 	vec4_from_bgra(&v_color, argb);
484 	effect_setval_inline(param, &v_color, sizeof(struct vec4));
485 }
486 
gs_effect_set_texture(gs_eparam_t * param,gs_texture_t * val)487 void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val)
488 {
489 	struct gs_shader_texture shader_tex;
490 	shader_tex.tex = val;
491 	shader_tex.srgb = false;
492 	effect_setval_inline(param, &shader_tex, sizeof(shader_tex));
493 }
494 
gs_effect_set_texture_srgb(gs_eparam_t * param,gs_texture_t * val)495 void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val)
496 {
497 	struct gs_shader_texture shader_tex;
498 	shader_tex.tex = val;
499 	shader_tex.srgb = true;
500 	effect_setval_inline(param, &shader_tex, sizeof(shader_tex));
501 }
502 
gs_effect_set_val(gs_eparam_t * param,const void * val,size_t size)503 void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)
504 {
505 	effect_setval_inline(param, val, size);
506 }
507 
gs_effect_get_val(gs_eparam_t * param)508 void *gs_effect_get_val(gs_eparam_t *param)
509 {
510 	if (!param) {
511 		blog(LOG_ERROR, "gs_effect_get_val: invalid param");
512 		return NULL;
513 	}
514 	size_t size = param->cur_val.num;
515 	void *data;
516 
517 	if (size)
518 		data = (void *)bzalloc(size);
519 	else
520 		return NULL;
521 
522 	effect_getval_inline(param, data, size);
523 
524 	return data;
525 }
526 
gs_effect_get_val_size(gs_eparam_t * param)527 size_t gs_effect_get_val_size(gs_eparam_t *param)
528 {
529 	return param ? param->cur_val.num : 0;
530 }
531 
gs_effect_get_default_val(gs_eparam_t * param)532 void *gs_effect_get_default_val(gs_eparam_t *param)
533 {
534 	if (!param) {
535 		blog(LOG_ERROR, "gs_effect_get_default_val: invalid param");
536 		return NULL;
537 	}
538 	size_t size = param->default_val.num;
539 	void *data;
540 
541 	if (size)
542 		data = (void *)bzalloc(size);
543 	else
544 		return NULL;
545 
546 	effect_getdefaultval_inline(param, data, size);
547 
548 	return data;
549 }
550 
gs_effect_get_default_val_size(gs_eparam_t * param)551 size_t gs_effect_get_default_val_size(gs_eparam_t *param)
552 {
553 	return param ? param->default_val.num : 0;
554 }
555 
gs_effect_set_default(gs_eparam_t * param)556 void gs_effect_set_default(gs_eparam_t *param)
557 {
558 	effect_setval_inline(param, param->default_val.array,
559 			     param->default_val.num);
560 }
561 
gs_effect_set_next_sampler(gs_eparam_t * param,gs_samplerstate_t * sampler)562 void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler)
563 {
564 	if (!param) {
565 		blog(LOG_ERROR, "gs_effect_set_next_sampler: invalid param");
566 		return;
567 	}
568 
569 	if (param->type == GS_SHADER_PARAM_TEXTURE)
570 		param->next_sampler = sampler;
571 }
572