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