1 /*
2  * Copyright © 2007 Novell, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * Novell, Inc. not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior permission.
11  * Novell, Inc. makes no representations about the suitability of this
12  * software for any purpose. It is provided "as is" without express or
13  * implied warranty.
14  *
15  * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17  * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: David Reveman <davidr@novell.com>
24  */
25 
26 #include <compiz-core.h>
27 
28 #include <string.h>
29 #include <stdlib.h>
30 
31 #define COMP_FUNCTION_TYPE_ARB 0
32 #define COMP_FUNCTION_TYPE_NUM 1
33 
34 #define COMP_FUNCTION_ARB_MASK (1 << 0)
35 #define COMP_FUNCTION_MASK     (COMP_FUNCTION_ARB_MASK)
36 
37 struct _CompProgram {
38     struct _CompProgram *next;
39 
40     int *signature;
41     int nSignature;
42 
43     Bool blending;
44 
45     GLuint name;
46     GLenum type;
47 };
48 
49 typedef enum {
50     CompOpTypeData,
51     CompOpTypeDataStore,
52     CompOpTypeDataOffset,
53     CompOpTypeDataBlend,
54     CompOpTypeHeaderTemp,
55     CompOpTypeHeaderParam,
56     CompOpTypeHeaderAttrib,
57     CompOpTypeColor,
58     CompOpTypeFetch,
59     CompOpTypeLoad
60 } CompOpType;
61 
62 typedef struct _CompDataOp {
63     CompOpType type;
64 
65     char *data;
66 } CompDataOp;
67 
68 typedef struct _CompHeaderOp {
69     CompOpType type;
70 
71     char *name;
72 } CompHeaderOp;
73 
74 typedef struct _CompFetchOp {
75     CompOpType type;
76 
77     char *dst;
78     char *offset;
79     int  target;
80 } CompFetchOp;
81 
82 typedef struct _CompLoadOp {
83     CompOpType type;
84 
85     char *noOffset[COMP_FETCH_TARGET_NUM];
86     char *offset[COMP_FETCH_TARGET_NUM];
87 } CompLoadOp;
88 
89 typedef struct _CompColorOp {
90     CompOpType type;
91 
92     char *dst;
93     char *src;
94 } CompColorOp;
95 
96 typedef union _CompBodyOp {
97     CompOpType	type;
98 
99     CompDataOp  data;
100     CompFetchOp fetch;
101     CompLoadOp  load;
102     CompColorOp color;
103 } CompBodyOp;
104 
105 struct _CompFunctionData {
106     CompHeaderOp *header;
107     int		 nHeader;
108 
109     CompBodyOp *body;
110     int	       nBody;
111 };
112 
113 struct _CompFunction {
114     struct _CompFunction *next;
115 
116     int		     id;
117     char	     *name;
118     CompFunctionData data[COMP_FUNCTION_TYPE_NUM];
119     int		     mask;
120 };
121 
122 typedef struct _FetchInfo {
123     int  indices[MAX_FRAGMENT_FUNCTIONS];
124     char *data;
125 } FetchInfo;
126 
127 typedef void (*DataOpCallBackProc) (CompDataOp *op,
128 				    int	       index,
129 				    void       *closure);
130 
131 static CompLoadOp loadArbFpData = {
132     CompOpTypeLoad,
133     {
134 	"TEX output, fragment.texcoord[0], texture[0], 2D;",
135 	"TEX output, fragment.texcoord[0], texture[0], RECT;"
136     }, {
137 	"TEX output, __tmp_texcoord0, texture[0], 2D;",
138 	"TEX output, __tmp_texcoord0, texture[0], RECT;"
139     }
140 };
141 
142 static CompFunction initialLoadFunction = {
143     NULL,
144     0,
145     "__core_load",
146     {
147 	{
148 	    NULL,
149 	    0,
150 	    (CompBodyOp *) &loadArbFpData,
151 	    1
152 	}
153     },
154     COMP_FUNCTION_MASK
155 };
156 
157 static CompFunction *
findFragmentFunction(CompScreen * s,int id)158 findFragmentFunction (CompScreen *s,
159 		      int	 id)
160 {
161     CompFunction *function;
162 
163     for (function = s->fragmentFunctions; function; function = function->next)
164     {
165 	if (function->id == id)
166 	    return function;
167     }
168 
169     return NULL;
170 }
171 
172 static CompFunction *
findFragmentFunctionWithName(CompScreen * s,const char * name)173 findFragmentFunctionWithName (CompScreen *s,
174 			      const char *name)
175 {
176     CompFunction *function;
177 
178     for (function = s->fragmentFunctions; function; function = function->next)
179     {
180 	if (strcmp (function->name, name) == 0)
181 	    return function;
182     }
183 
184     return NULL;
185 }
186 
187 static CompProgram *
findFragmentProgram(CompScreen * s,int * signature,int nSignature)188 findFragmentProgram (CompScreen *s,
189 		     int	*signature,
190 		     int	nSignature)
191 {
192     CompProgram *program;
193     int		i;
194 
195     for (program = s->fragmentPrograms; program; program = program->next)
196     {
197 	if (nSignature != program->nSignature)
198 	    continue;
199 
200 	for (i = 0; i < nSignature; i++)
201 	{
202 	    if (program->signature[i] != signature[i])
203 		break;
204 	}
205 
206 	if (i == nSignature)
207 	    return program;
208     }
209 
210     return NULL;
211 }
212 
213 static int
functionMaskToType(int mask)214 functionMaskToType (int mask)
215 {
216     static struct {
217 	int type;
218 	int mask;
219     } maskToType[] = {
220 	{ COMP_FUNCTION_TYPE_ARB, COMP_FUNCTION_ARB_MASK }
221     };
222     int i;
223 
224     for (i = 0; i < sizeof (maskToType) / sizeof (maskToType[0]); i++)
225 	if (mask & maskToType[i].mask)
226 	    return maskToType[i].type;
227 
228     return 0;
229 }
230 
231 static void
forEachDataOpInFunction(CompFunction ** list,int index,int type,int loadTarget,char * loadOffset,Bool * color,Bool * blend,DataOpCallBackProc callBack,void * closure)232 forEachDataOpInFunction (CompFunction	    **list,
233 			 int		    index,
234 			 int		    type,
235 			 int		    loadTarget,
236 			 char		    *loadOffset,
237 			 Bool		    *color,
238 			 Bool		    *blend,
239 			 DataOpCallBackProc callBack,
240 			 void		    *closure)
241 {
242     CompFunction *f = list[index];
243     CompDataOp   dataOp;
244     char	 data[256];
245     Bool	 colorDone = FALSE;
246     Bool	 blendDone = FALSE;
247     int		 i;
248 
249     *color = FALSE;
250     *blend = FALSE;
251 
252     for (i = 0; i < f->data[type].nBody; i++)
253     {
254 	switch (f->data[type].body[i].type) {
255 	case CompOpTypeFetch: {
256 	    char *offset = loadOffset;
257 
258 	    /* add offset */
259 	    if (f->data[type].body[i].fetch.offset)
260 	    {
261 		if (loadOffset)
262 		{
263 		    snprintf (data, 256,
264 			      "ADD __tmp_texcoord%d, %s, %s;",
265 			      index, loadOffset,
266 			      f->data[type].body[i].fetch.offset);
267 
268 		    dataOp.type = CompOpTypeDataOffset;
269 		    dataOp.data = data;
270 
271 		    (*callBack) (&dataOp, index, closure);
272 
273 		    snprintf (data, 256, "__tmp_texcoord%d", index);
274 
275 		    offset = data;
276 		}
277 		else
278 		{
279 		    offset = f->data[type].body[i].fetch.offset;
280 		}
281 	    }
282 
283 	    forEachDataOpInFunction (list, index - 1, type,
284 				     f->data[type].body[i].fetch.target,
285 				     offset, &colorDone, &blendDone,
286 				     callBack, closure);
287 
288 	    if (strcmp (f->data[type].body[i].fetch.dst, "output"))
289 	    {
290 		snprintf (data, 256,
291 			  "MOV %s, output;",
292 			  f->data[type].body[i].fetch.dst);
293 
294 		dataOp.type = CompOpTypeDataStore;
295 		dataOp.data = data;
296 
297 		/* move to destination */
298 		(*callBack) (&dataOp, index, closure);
299 	    }
300 	} break;
301 	case CompOpTypeLoad:
302 	    if (loadOffset)
303 	    {
304 		snprintf (data, 256,
305 			  "ADD __tmp_texcoord0, fragment.texcoord[0], %s;",
306 			  loadOffset);
307 
308 		dataOp.type = CompOpTypeDataOffset;
309 		dataOp.data = data;
310 
311 		(*callBack) (&dataOp, index, closure);
312 
313 		dataOp.data = f->data[type].body[i].load.offset[loadTarget];
314 	    }
315 	    else
316 	    {
317 		dataOp.data = f->data[type].body[i].load.noOffset[loadTarget];
318 	    }
319 
320 	    dataOp.type = CompOpTypeData;
321 
322 	    (*callBack) (&dataOp, index, closure);
323 
324 	    break;
325 	case CompOpTypeColor:
326 	    if (!colorDone)
327 	    {
328 		snprintf (data, 256,
329 			  "MUL %s, fragment.color, %s;",
330 			  f->data[type].body[i].color.dst,
331 			  f->data[type].body[i].color.src);
332 
333 		dataOp.type = CompOpTypeData;
334 		dataOp.data = data;
335 
336 		(*callBack) (&dataOp, index, closure);
337 	    }
338 	    else if (strcmp (f->data[type].body[i].color.dst,
339 			     f->data[type].body[i].color.src))
340 	    {
341 		snprintf (data, 256,
342 			  "MOV %s, %s;",
343 			  f->data[type].body[i].color.dst,
344 			  f->data[type].body[i].color.src);
345 
346 		dataOp.type = CompOpTypeData;
347 		dataOp.data = data;
348 
349 		(*callBack) (&dataOp, index, closure);
350 	    }
351 	    *color = TRUE;
352 	    break;
353 	case CompOpTypeDataBlend:
354 	    *blend = TRUE;
355 	    /* fall-through */
356 	case CompOpTypeData:
357 	    (*callBack) (&f->data[type].body[i].data, index, closure);
358 	    break;
359 	case CompOpTypeDataStore:
360 	case CompOpTypeDataOffset:
361 	case CompOpTypeHeaderTemp:
362 	case CompOpTypeHeaderParam:
363 	case CompOpTypeHeaderAttrib:
364 	    break;
365 	}
366     }
367 
368     if (colorDone)
369 	*color = TRUE;
370 
371     if (blendDone)
372 	*blend = TRUE;
373 }
374 
375 static int
forEachHeaderOpWithType(CompHeaderOp * header,int nHeader,int index,CompOpType type,char * prefix,char * functionPrefix,int count,DataOpCallBackProc callBack,void * closure)376 forEachHeaderOpWithType (CompHeaderOp	    *header,
377 			 int		    nHeader,
378 			 int	            index,
379 			 CompOpType	    type,
380 			 char		    *prefix,
381 			 char		    *functionPrefix,
382 			 int		    count,
383 			 DataOpCallBackProc callBack,
384 			 void	            *closure)
385 {
386     CompDataOp dataOp;
387     int	       i;
388 
389     dataOp.type = CompOpTypeData;
390 
391     for (i = 0; i < nHeader; i++)
392     {
393 	if (header[i].type == type)
394 	{
395 	    if (count)
396 	    {
397 		dataOp.data = ", ";
398 		(*callBack) (&dataOp, index, closure);
399 	    }
400 	    else
401 	    {
402 		dataOp.data = prefix;
403 		(*callBack) (&dataOp, index, closure);
404 	    }
405 
406 	    dataOp.data = functionPrefix;
407 	    (*callBack) (&dataOp, index, closure);
408 
409 	    dataOp.data = "_";
410 	    (*callBack) (&dataOp, index, closure);
411 
412 	    dataOp.data = header[i].name;
413 	    (*callBack) (&dataOp, index, closure);
414 
415 	    count++;
416 	}
417     }
418 
419     return count;
420 }
421 
422 static Bool
forEachDataOp(CompFunction ** list,int nList,int type,DataOpCallBackProc callBack,void * closure)423 forEachDataOp (CompFunction	  **list,
424 	       int		  nList,
425 	       int		  type,
426 	       DataOpCallBackProc callBack,
427 	       void	          *closure)
428 {
429     CompDataOp dataOp;
430     Bool       colorDone;
431     Bool       blendDone;
432     int	       i, count;
433 
434     dataOp.type = CompOpTypeData;
435 
436     count = 1;
437 
438     dataOp.data = "TEMP output";
439 
440     (*callBack) (&dataOp, nList, closure);
441 
442     for (i = 0; i < nList; i++)
443 	count = forEachHeaderOpWithType (list[i]->data[type].header,
444 					 list[i]->data[type].nHeader,
445 					 nList, CompOpTypeHeaderTemp,
446 					 NULL, list[i]->name, count,
447 					 callBack, closure);
448 
449     dataOp.data = ";";
450 
451     (*callBack) (&dataOp, nList, closure);
452 
453     count = 0;
454 
455     for (i = 0; i < nList; i++)
456 	count = forEachHeaderOpWithType (list[i]->data[type].header,
457 					 list[i]->data[type].nHeader,
458 					 nList, CompOpTypeHeaderParam,
459 					 "PARAM ", list[i]->name, count,
460 					 callBack, closure);
461 
462     if (count)
463     {
464 	dataOp.data = ";";
465 
466 	(*callBack) (&dataOp, nList, closure);
467     }
468 
469     count = 0;
470 
471     for (i = 0; i < nList; i++)
472 	count = forEachHeaderOpWithType (list[i]->data[type].header,
473 					 list[i]->data[type].nHeader,
474 					 nList, CompOpTypeHeaderAttrib,
475 					 "ATTRIB ", list[i]->name, count,
476 					 callBack, closure);
477 
478     if (count)
479     {
480 	dataOp.data = ";";
481 
482 	(*callBack) (&dataOp, nList, closure);
483     }
484 
485     forEachDataOpInFunction (list, nList - 1, type, 0, NULL,
486 			     &colorDone, &blendDone,
487 			     callBack, closure);
488 
489     if (colorDone)
490 	dataOp.data = "MOV result.color, output;END";
491     else
492 	dataOp.data = "MUL result.color, fragment.color, output;END";
493 
494     (*callBack) (&dataOp, nList, closure);
495 
496     return blendDone;
497 }
498 
499 static void
addFetchOffsetVariables(CompDataOp * op,int index,void * closure)500 addFetchOffsetVariables (CompDataOp *op,
501 			 int	    index,
502 			 void       *closure)
503 {
504     if (op->type == CompOpTypeDataOffset)
505     {
506 	FetchInfo *info = (FetchInfo *) closure;
507 
508 	if (!info->indices[index])
509 	{
510 	    char *str;
511 	    int	 oldSize = strlen (info->data);
512 	    int	 newSize;
513 	    char data[256];
514 
515 	    snprintf (data, 256, "TEMP __tmp_texcoord%d;", index);
516 
517 	    newSize = oldSize + strlen (data);
518 
519 	    str = realloc (info->data, newSize + 1);
520 	    if (str)
521 	    {
522 		strcpy (str + oldSize, data);
523 		info->data = str;
524 	    }
525 
526 	    info->indices[index] = TRUE;
527 	}
528     }
529 }
530 
531 static void
addData(CompDataOp * op,int index,void * closure)532 addData (CompDataOp *op,
533 	 int	    index,
534 	 void       *closure)
535 {
536     FetchInfo *info = (FetchInfo *) closure;
537     char      *str;
538     int	      oldSize = strlen (info->data);
539     int	      newSize = oldSize + strlen (op->data);
540 
541     str = realloc (info->data, newSize + 1);
542     if (str)
543     {
544 	strcpy (str + oldSize, op->data);
545 	info->data = str;
546     }
547 }
548 
549 static CompProgram *
buildFragmentProgram(CompScreen * s,FragmentAttrib * attrib)550 buildFragmentProgram (CompScreen     *s,
551 		      FragmentAttrib *attrib)
552 {
553     CompProgram	 *program;
554     CompFunction **functionList;
555     int		 nFunctionList;
556     int		 mask = COMP_FUNCTION_MASK;
557     int		 type;
558     GLint	 errorPos;
559     FetchInfo    info;
560     int		 i;
561 
562     program = malloc (sizeof (CompProgram));
563     if (!program)
564 	return NULL;
565 
566     functionList = malloc ((attrib->nFunction + 1) * sizeof (void *));
567     if (!functionList)
568     {
569 	free (program);
570 
571 	return NULL;
572     }
573 
574     functionList[0] = &initialLoadFunction;
575     nFunctionList   = 1;
576 
577     for (i = 0; i < attrib->nFunction; i++)
578     {
579 	functionList[nFunctionList] =
580 	    findFragmentFunction (s, attrib->function[i]);
581 	if (functionList[nFunctionList])
582 	    nFunctionList++;
583     }
584 
585     for (i = 0; i < nFunctionList; i++)
586 	mask &= functionList[i]->mask;
587 
588     if (!mask)
589     {
590 	compLogMessage ("core", CompLogLevelWarn,
591 			"fragment functions can't be linked together "
592 			"because a common type doesn't exist");
593     }
594 
595     if (!mask || nFunctionList == 1)
596     {
597 	free (program);
598 	free (functionList);
599 
600 	return NULL;
601     }
602 
603     program->signature = malloc (attrib->nFunction * sizeof (int));
604     if (!program->signature)
605     {
606 	free (program);
607 	free (functionList);
608 
609 	return NULL;
610     }
611 
612     for (i = 0; i < attrib->nFunction; i++)
613 	program->signature[i] = attrib->function[i];
614 
615     program->nSignature = attrib->nFunction;
616 
617     type = functionMaskToType (mask);
618 
619     info.data = strdup ("!!ARBfp1.0");
620 
621     memset (info.indices, 0, sizeof (info.indices));
622 
623     forEachDataOp (functionList, nFunctionList, type,
624 		   addFetchOffsetVariables, (void *) &info);
625 
626     program->blending = forEachDataOp (functionList, nFunctionList, type,
627 				       addData, (void *) &info);
628 
629     program->type = GL_FRAGMENT_PROGRAM_ARB;
630 
631     glGetError ();
632 
633     (*s->genPrograms) (1, &program->name);
634     (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, program->name);
635     (*s->programString) (GL_FRAGMENT_PROGRAM_ARB,
636 			 GL_PROGRAM_FORMAT_ASCII_ARB,
637 			 strlen (info.data), info.data);
638 
639     glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
640     if (glGetError () != GL_NO_ERROR || errorPos != -1)
641     {
642 	compLogMessage ("core", CompLogLevelError,
643 			"failed to load fragment program");
644 
645 	(*s->deletePrograms) (1, &program->name);
646 
647 	program->name = 0;
648 	program->type = 0;
649     }
650 
651     free (info.data);
652     free (functionList);
653 
654     return program;
655 }
656 
657 static GLuint
getFragmentProgram(CompScreen * s,FragmentAttrib * attrib,GLenum * type,Bool * blending)658 getFragmentProgram (CompScreen	   *s,
659 		    FragmentAttrib *attrib,
660 		    GLenum	   *type,
661 		    Bool	   *blending)
662 {
663     CompProgram	*program;
664 
665     if (!attrib->nFunction)
666 	return 0;
667 
668     program = findFragmentProgram (s, attrib->function, attrib->nFunction);
669     if (!program)
670     {
671 	program = buildFragmentProgram (s, attrib);
672 	if (program)
673 	{
674 	    program->next = s->fragmentPrograms;
675 	    s->fragmentPrograms = program;
676 	}
677     }
678 
679     if (program)
680     {
681 	*type     = program->type;
682 	*blending = program->blending;
683 
684 	return program->name;
685     }
686 
687     return 0;
688 }
689 
690 CompFunctionData *
createFunctionData(void)691 createFunctionData (void)
692 {
693     CompFunctionData *data;
694 
695     data = malloc (sizeof (CompFunctionData));
696     if (!data)
697 	return NULL;
698 
699     data->header  = NULL;
700     data->nHeader = 0;
701 
702     data->body  = NULL;
703     data->nBody = 0;
704 
705     return data;
706 }
707 
708 static void
finiFunctionData(CompFunctionData * data)709 finiFunctionData (CompFunctionData *data)
710 {
711     int i;
712 
713     for (i = 0; i < data->nHeader; i++)
714 	free (data->header[i].name);
715 
716     if (data->header)
717 	free (data->header);
718 
719     for (i = 0; i < data->nBody; i++)
720     {
721 	switch (data->body[i].type) {
722 	case CompOpTypeFetch:
723 	    free (data->body[i].fetch.dst);
724 	    if (data->body[i].fetch.offset)
725 		free (data->body[i].fetch.offset);
726 	    break;
727 	case CompOpTypeLoad:
728 	case CompOpTypeHeaderTemp:
729 	case CompOpTypeHeaderParam:
730 	case CompOpTypeHeaderAttrib:
731 	    break;
732 	case CompOpTypeData:
733 	case CompOpTypeDataBlend:
734 	case CompOpTypeDataStore:
735 	case CompOpTypeDataOffset:
736 	    free (data->body[i].data.data);
737 	    break;
738 	case CompOpTypeColor:
739 	    free (data->body[i].color.dst);
740 	    free (data->body[i].color.src);
741 	    break;
742 	}
743     }
744 
745     if (data->body)
746 	free (data->body);
747 }
748 
749 void
destroyFunctionData(CompFunctionData * data)750 destroyFunctionData (CompFunctionData *data)
751 {
752     finiFunctionData (data);
753     free (data);
754 }
755 
756 /* performs simple variable substitution */
757 static char *
copyData(CompHeaderOp * header,int nHeader,const char * prefix,char * data)758 copyData (CompHeaderOp *header,
759 	  int	       nHeader,
760 	  const char   *prefix,
761 	  char	       *data)
762 {
763     char *copy, *needle, *haystack, *dst, *first;
764     int  i, index, length, prefixLength, count = 0;
765 
766     prefixLength = strlen (prefix);
767 
768     for (i = 0; i < nHeader; i++)
769     {
770 	length   = strlen (header[i].name);
771 	haystack = data;
772 
773 	do {
774 	    needle = strstr (haystack, header[i].name);
775 	    if (needle)
776 	    {
777 		haystack = needle + length;
778 		count++;
779 	    }
780 	} while (needle);
781     }
782 
783     /* allocate new memory that will fit all substitutions */
784     copy = malloc (strlen (data) + count * (strlen (prefix) + 1) + 1);
785     if (!copy)
786 	return NULL;
787 
788     haystack = data;
789     dst      = copy;
790 
791     for (;;)
792     {
793 	first = NULL;
794 	index = 0;
795 
796 	for (i = 0; i < nHeader; i++)
797 	{
798 	    needle = strstr (haystack, header[i].name);
799 	    if (needle && (!first || needle < first))
800 	    {
801 		first = needle;
802 		index = i;
803 	    }
804 	}
805 
806 	if (first)
807 	{
808 	    int length;
809 
810 	    if (first > haystack)
811 	    {
812 		strncpy (dst, haystack, first - haystack);
813 		dst += first - haystack;
814 	    }
815 
816 	    length = strlen (header[index].name);
817 
818 	    strcpy (dst, prefix);
819 	    dst += prefixLength;
820 	    *dst++ = '_';
821 	    strcpy (dst, header[index].name);
822 	    dst += length;
823 
824 	    haystack = first + length;
825 	}
826 	else
827 	{
828 	    strcpy (dst, haystack);
829 	    break;
830 	}
831     }
832 
833     return copy;
834 }
835 
836 static Bool
copyFunctionData(CompFunctionData * dst,const CompFunctionData * src,const char * dstPrefix)837 copyFunctionData (CompFunctionData	 *dst,
838 		  const CompFunctionData *src,
839 		  const char		 *dstPrefix)
840 {
841     int i;
842 
843     dst->header = malloc (src->nHeader * sizeof (CompHeaderOp));
844     if (!dst->header)
845 	return FALSE;
846 
847     dst->body = malloc (src->nBody * sizeof (CompBodyOp));
848     if (!dst->body)
849     {
850 	free (dst->header);
851 	return FALSE;
852     }
853 
854     dst->nHeader = src->nHeader;
855 
856     for (i = 0; i < src->nHeader; i++)
857     {
858 	dst->header[i].type = src->header[i].type;
859 	dst->header[i].name = strdup (src->header[i].name);
860     }
861 
862     dst->nBody = src->nBody;
863 
864     for (i = 0; i < src->nBody; i++)
865     {
866 	dst->body[i].type = src->body[i].type;
867 
868 	switch (src->body[i].type) {
869 	case CompOpTypeFetch:
870 	    dst->body[i].fetch.dst = copyData (dst->header,
871 					       dst->nHeader,
872 					       dstPrefix,
873 					       src->body[i].fetch.dst);
874 	    if (src->body[i].fetch.offset)
875 		dst->body[i].fetch.offset =
876 		    copyData (dst->header,
877 			      dst->nHeader,
878 			      dstPrefix,
879 			      src->body[i].fetch.offset);
880 	    else
881 		dst->body[i].fetch.offset = NULL;
882 
883 	    dst->body[i].fetch.target = src->body[i].fetch.target;
884 	    break;
885 	case CompOpTypeLoad:
886 	case CompOpTypeHeaderTemp:
887 	case CompOpTypeHeaderParam:
888 	case CompOpTypeHeaderAttrib:
889 	    break;
890 	case CompOpTypeData:
891 	case CompOpTypeDataBlend:
892 	case CompOpTypeDataStore:
893 	case CompOpTypeDataOffset:
894 	    dst->body[i].data.data = copyData (dst->header,
895 					       dst->nHeader,
896 					       dstPrefix,
897 					       src->body[i].data.data);
898 	    break;
899 	case CompOpTypeColor:
900 	    dst->body[i].color.dst = copyData (dst->header,
901 					       dst->nHeader,
902 					       dstPrefix,
903 					       src->body[i].color.dst);
904 	    dst->body[i].color.src = copyData (dst->header,
905 					       dst->nHeader,
906 					       dstPrefix,
907 					       src->body[i].color.src);
908 	    break;
909 	}
910     }
911 
912     return TRUE;
913 }
914 
915 static Bool
addHeaderOpToFunctionData(CompFunctionData * data,const char * name,CompOpType type)916 addHeaderOpToFunctionData (CompFunctionData *data,
917 			   const char	    *name,
918 			   CompOpType	    type)
919 {
920     static char  *reserved[] = {
921 	"output",
922 	"__tmp_texcoord",
923 	"fragment",
924 	"program",
925 	"result",
926 	"state",
927 	"texture"
928     };
929     CompHeaderOp *header;
930     int		 i;
931 
932     for (i = 0; i < sizeof (reserved) / sizeof (reserved[0]); i++)
933     {
934 	if (strncmp (name, reserved[i], strlen (reserved[i])) == 0)
935 	{
936 	    compLogMessage ("core", CompLogLevelWarn,
937 			    "%s is a reserved word", name);
938 	    return FALSE;
939 	}
940     }
941 
942     header = realloc (data->header,
943 		      (data->nHeader + 1) * sizeof (CompHeaderOp));
944     if (!header)
945 	return FALSE;
946 
947     header[data->nHeader].type = type;
948     header[data->nHeader].name = strdup (name);
949 
950     data->header = header;
951     data->nHeader++;
952 
953     return TRUE;
954 }
955 
956 Bool
addTempHeaderOpToFunctionData(CompFunctionData * data,const char * name)957 addTempHeaderOpToFunctionData (CompFunctionData *data,
958 			       const char	*name)
959 {
960     return addHeaderOpToFunctionData (data, name, CompOpTypeHeaderTemp);
961 }
962 
963 Bool
addParamHeaderOpToFunctionData(CompFunctionData * data,const char * name)964 addParamHeaderOpToFunctionData (CompFunctionData *data,
965 				const char	 *name)
966 {
967     return addHeaderOpToFunctionData (data, name, CompOpTypeHeaderParam);
968 }
969 
970 Bool
addAttribHeaderOpToFunctionData(CompFunctionData * data,const char * name)971 addAttribHeaderOpToFunctionData (CompFunctionData *data,
972 				 const char	  *name)
973 {
974     return addHeaderOpToFunctionData (data, name, CompOpTypeHeaderAttrib);
975 }
976 
977 static Bool
allocBodyOpInFunctionData(CompFunctionData * data)978 allocBodyOpInFunctionData (CompFunctionData *data)
979 {
980     CompBodyOp *body;
981 
982     body = realloc (data->body, (data->nBody + 1) * sizeof (CompBodyOp));
983     if (!body)
984 	return FALSE;
985 
986     data->body = body;
987     data->nBody++;
988 
989     return TRUE;
990 }
991 
992 Bool
addFetchOpToFunctionData(CompFunctionData * data,const char * dst,const char * offset,int target)993 addFetchOpToFunctionData (CompFunctionData *data,
994 			  const char	   *dst,
995 			  const char	   *offset,
996 			  int		   target)
997 {
998     int index = data->nBody;
999 
1000     if (!allocBodyOpInFunctionData (data))
1001 	return FALSE;
1002 
1003     data->body[index].type	   = CompOpTypeFetch;
1004     data->body[index].fetch.dst    = strdup (dst);
1005     data->body[index].fetch.target = target;
1006 
1007     if (offset)
1008 	data->body[index].fetch.offset = strdup (offset);
1009     else
1010 	data->body[index].fetch.offset = NULL;
1011 
1012     return TRUE;
1013 }
1014 
1015 Bool
addColorOpToFunctionData(CompFunctionData * data,const char * dst,const char * src)1016 addColorOpToFunctionData (CompFunctionData *data,
1017 			  const char	   *dst,
1018 			  const char	   *src)
1019 {
1020     int index = data->nBody;
1021 
1022     if (!allocBodyOpInFunctionData (data))
1023 	return FALSE;
1024 
1025     data->body[index].type      = CompOpTypeColor;
1026     data->body[index].color.dst = strdup (dst);
1027     data->body[index].color.src = strdup (src);
1028 
1029     return TRUE;
1030 }
1031 
1032 Bool
addDataOpToFunctionData(CompFunctionData * data,const char * str,...)1033 addDataOpToFunctionData (CompFunctionData *data,
1034 			 const char	  *str,
1035 			 ...)
1036 {
1037     int     index = data->nBody;
1038     int     size  = strlen (str) + 1;
1039     int     n;
1040     char    *fStr;
1041     char    *tmp;
1042     va_list ap;
1043 
1044     if (!allocBodyOpInFunctionData (data))
1045 	return FALSE;
1046 
1047     fStr = malloc (size);
1048     if (!fStr)
1049 	return FALSE;
1050 
1051     while (1)
1052     {
1053 	/* Try to print in the allocated space. */
1054 	va_start (ap, str);
1055 	n = vsnprintf (fStr, size, str, ap);
1056 	va_end (ap);
1057 
1058 	/* If that worked, leave the loop. */
1059 	if (n > -1 && n < size)
1060 	    break;
1061 
1062 	/* Else try again with more space. */
1063 	if (n > -1)       /* glibc 2.1 */
1064 	    size = n + 1; /* precisely what is needed */
1065 	else              /* glibc 2.0 */
1066 	    size++;       /* one more than the old size */
1067 
1068 	tmp = realloc (fStr, size);
1069 	if (!tmp)
1070 	{
1071 	    free (fStr);
1072 	    return FALSE;
1073 	}
1074 	else
1075 	{
1076 	    fStr = tmp;
1077 	}
1078     }
1079 
1080     data->body[index].type	= CompOpTypeData;
1081     data->body[index].data.data = fStr;
1082 
1083     return TRUE;
1084 }
1085 
1086 Bool
addBlendOpToFunctionData(CompFunctionData * data,const char * str,...)1087 addBlendOpToFunctionData (CompFunctionData *data,
1088 			  const char	   *str,
1089 			  ...)
1090 {
1091     int     index = data->nBody;
1092     int     size  = strlen (str) + 1;
1093     int     n;
1094     char    *fStr;
1095     char    *tmp;
1096     va_list ap;
1097 
1098     if (!allocBodyOpInFunctionData (data))
1099 	return FALSE;
1100 
1101     fStr = malloc (size);
1102     if (!fStr)
1103 	return FALSE;
1104 
1105     while (1)
1106     {
1107 	/* Try to print in the allocated space. */
1108 	va_start (ap, str);
1109 	n = vsnprintf (fStr, size, str, ap);
1110 	va_end (ap);
1111 
1112 	/* If that worked, leave the loop. */
1113 	if (n > -1 && n < size)
1114 	    break;
1115 
1116 	/* Else try again with more space. */
1117 	if (n > -1)       /* glibc 2.1 */
1118 	    size = n + 1; /* precisely what is needed */
1119 	else              /* glibc 2.0 */
1120 	    size++;       /* one more than the old size */
1121 
1122 	tmp = realloc (fStr, size);
1123 	if (!tmp)
1124 	{
1125 	    free (fStr);
1126 	    return FALSE;
1127 	}
1128 	else
1129 	{
1130 	    fStr = tmp;
1131 	}
1132     }
1133 
1134     data->body[index].type	= CompOpTypeDataBlend;
1135     data->body[index].data.data = fStr;
1136 
1137     return TRUE;
1138 }
1139 
1140 static int
allocFunctionId(CompScreen * s)1141 allocFunctionId (CompScreen *s)
1142 {
1143     return ++s->lastFunctionId;
1144 }
1145 
1146 int
createFragmentFunction(CompScreen * s,const char * name,CompFunctionData * data)1147 createFragmentFunction (CompScreen	 *s,
1148 			const char	 *name,
1149 			CompFunctionData *data)
1150 {
1151     CompFunction *function;
1152     const char	 *validName = name;
1153     char	 *nameBuffer = NULL;
1154     int		 i = 0;
1155 
1156     while (findFragmentFunctionWithName (s, validName))
1157     {
1158 	if (!nameBuffer)
1159 	{
1160 	    nameBuffer = malloc (strlen (name) + 64);
1161 	    if (!nameBuffer)
1162 		return 0;
1163 
1164 	    validName = nameBuffer;
1165 	}
1166 
1167 	sprintf (nameBuffer, "%s%d", name, i++);
1168     }
1169 
1170     function = malloc (sizeof (CompFunction));
1171     if (!function)
1172     {
1173 	if (nameBuffer)
1174 	    free (nameBuffer);
1175 
1176 	return 0;
1177     }
1178 
1179     if (!copyFunctionData (&function->data[COMP_FUNCTION_TYPE_ARB], data,
1180 			   validName))
1181     {
1182 	free (function);
1183 	if (nameBuffer)
1184 	    free (nameBuffer);
1185 
1186 	return 0;
1187     }
1188 
1189     function->name = strdup (validName);
1190     function->mask = COMP_FUNCTION_ARB_MASK;
1191     function->id   = allocFunctionId (s);
1192 
1193     function->next = s->fragmentFunctions;
1194     s->fragmentFunctions = function;
1195 
1196     if (nameBuffer)
1197 	free (nameBuffer);
1198 
1199     return function->id;
1200 }
1201 
1202 void
destroyFragmentFunction(CompScreen * s,int id)1203 destroyFragmentFunction (CompScreen *s,
1204 			 int	    id)
1205 {
1206     CompFunction *function, *prevFunction = NULL;
1207     CompProgram  *program, *prevProgram = NULL;
1208     int		 i;
1209 
1210     for (function = s->fragmentFunctions; function; function = function->next)
1211     {
1212 	if (function->id == id)
1213 	    break;
1214 
1215 	prevFunction = function;
1216     }
1217 
1218     if (!function)
1219 	return;
1220 
1221     program = s->fragmentPrograms;
1222     while (program)
1223     {
1224 	for (i = 0; i < program->nSignature; i++)
1225 	{
1226 	    if (program->signature[i] == id)
1227 		break;
1228 	}
1229 
1230 	if (i < program->nSignature)
1231 	{
1232 	    CompProgram *tmp = program;
1233 
1234 	    if (prevProgram)
1235 		prevProgram->next = program->next;
1236 	    else
1237 		s->fragmentPrograms = program->next;
1238 
1239 	    program = program->next;
1240 
1241 	    (*s->deletePrograms) (1, &tmp->name);
1242 
1243 	    free (tmp->signature);
1244 	    free (tmp);
1245 	}
1246 	else
1247 	{
1248 	    prevProgram = program;
1249 	    program = program->next;
1250 	}
1251     }
1252 
1253     if (prevFunction)
1254 	prevFunction->next = function->next;
1255     else
1256 	s->fragmentFunctions = function->next;
1257 
1258     finiFunctionData (&function->data[COMP_FUNCTION_TYPE_ARB]);
1259     free (function->name);
1260     free (function);
1261 }
1262 
1263 int
getSaturateFragmentFunction(CompScreen * s,CompTexture * texture,int param)1264 getSaturateFragmentFunction (CompScreen  *s,
1265 			     CompTexture *texture,
1266 			     int	 param)
1267 {
1268     int target;
1269 
1270     if (param >= 64)
1271 	return 0;
1272 
1273     if (texture->target == GL_TEXTURE_2D)
1274 	target = COMP_FETCH_TARGET_2D;
1275     else
1276 	target = COMP_FETCH_TARGET_RECT;
1277 
1278     if (!s->saturateFunction[target][param])
1279     {
1280 	static const char *saturateData =
1281 	    "MUL temp, output, { 1.0, 1.0, 1.0, 0.0 };"
1282 	    "DP3 temp, temp, program.env[%d];"
1283 	    "LRP output.xyz, program.env[%d].w, output, temp;";
1284 	CompFunctionData  *data;
1285 
1286 	data = createFunctionData ();
1287 	if (data)
1288 	{
1289 	    char str[1024];
1290 
1291 	    if (!addTempHeaderOpToFunctionData (data, "temp"))
1292 	    {
1293 		destroyFunctionData (data);
1294 		return 0;
1295 	    }
1296 
1297 	    if (!addFetchOpToFunctionData (data, "output", NULL, target))
1298 	    {
1299 		destroyFunctionData (data);
1300 		return 0;
1301 	    }
1302 
1303 	    if (!addColorOpToFunctionData (data, "output", "output"))
1304 	    {
1305 		destroyFunctionData (data);
1306 		return 0;
1307 	    }
1308 
1309 	    snprintf (str, 1024, saturateData, param, param);
1310 
1311 	    if (!addDataOpToFunctionData (data, str))
1312 	    {
1313 		destroyFunctionData (data);
1314 		return 0;
1315 	    }
1316 
1317 	    s->saturateFunction[target][param] =
1318 		createFragmentFunction (s, "__core_saturate", data);
1319 
1320 	    destroyFunctionData (data);
1321 	}
1322     }
1323 
1324     return s->saturateFunction[target][param];
1325 }
1326 
1327 int
allocFragmentTextureUnits(FragmentAttrib * attrib,int nTexture)1328 allocFragmentTextureUnits (FragmentAttrib *attrib,
1329 			   int		  nTexture)
1330 {
1331     int first = attrib->nTexture;
1332 
1333     attrib->nTexture += nTexture;
1334 
1335     /* 0 is reserved for source texture */
1336     return 1 + first;
1337 }
1338 
1339 int
allocFragmentParameters(FragmentAttrib * attrib,int nParam)1340 allocFragmentParameters (FragmentAttrib *attrib,
1341 			 int		nParam)
1342 {
1343     int first = attrib->nParam;
1344 
1345     attrib->nParam += nParam;
1346 
1347     return first;
1348 }
1349 
1350 void
addFragmentFunction(FragmentAttrib * attrib,int function)1351 addFragmentFunction (FragmentAttrib *attrib,
1352 		     int	    function)
1353 {
1354     if (attrib->nFunction < MAX_FRAGMENT_FUNCTIONS)
1355 	attrib->function[attrib->nFunction++] = function;
1356 }
1357 
1358 void
initFragmentAttrib(FragmentAttrib * attrib,const WindowPaintAttrib * paint)1359 initFragmentAttrib (FragmentAttrib	    *attrib,
1360 		    const WindowPaintAttrib *paint)
1361 {
1362     attrib->opacity    = paint->opacity;
1363     attrib->brightness = paint->brightness;
1364     attrib->saturation = paint->saturation;
1365     attrib->nTexture   = 0;
1366     attrib->nFunction  = 0;
1367     attrib->nParam     = 0;
1368 
1369     memset (attrib->function, 0, sizeof (attrib->function));
1370 }
1371 
1372 Bool
enableFragmentAttrib(CompScreen * s,FragmentAttrib * attrib,Bool * blending)1373 enableFragmentAttrib (CompScreen     *s,
1374 		      FragmentAttrib *attrib,
1375 		      Bool	     *blending)
1376 {
1377     GLuint name;
1378     GLenum type;
1379     Bool   programBlending;
1380 
1381     if (!s->fragmentProgram)
1382 	return FALSE;
1383 
1384     name = getFragmentProgram (s, attrib, &type, &programBlending);
1385     if (!name)
1386 	return FALSE;
1387 
1388     *blending = !programBlending;
1389 
1390     glEnable (GL_FRAGMENT_PROGRAM_ARB);
1391 
1392     (*s->bindProgram) (type, name);
1393 
1394     return TRUE;
1395 }
1396 
1397 void
disableFragmentAttrib(CompScreen * s,FragmentAttrib * attrib)1398 disableFragmentAttrib (CompScreen     *s,
1399 		       FragmentAttrib *attrib)
1400 {
1401     glDisable (GL_FRAGMENT_PROGRAM_ARB);
1402 }
1403