Merge branch 'mesa_7_5_branch'
[mesa.git] / src / gallium / state_trackers / vega / shaders_cache.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "shaders_cache.h"
28
29 #include "vg_context.h"
30
31 #include "pipe/p_context.h"
32 #include "pipe/p_defines.h"
33 #include "pipe/p_inlines.h"
34 #include "pipe/p_screen.h"
35 #include "pipe/p_shader_tokens.h"
36
37 #include "tgsi/tgsi_build.h"
38 #include "tgsi/tgsi_dump.h"
39 #include "tgsi/tgsi_parse.h"
40 #include "tgsi/tgsi_util.h"
41 #include "tgsi/tgsi_text.h"
42
43 #include "util/u_memory.h"
44 #include "util/u_math.h"
45 #include "util/u_debug.h"
46 #include "cso_cache/cso_hash.h"
47 #include "cso_cache/cso_context.h"
48
49 #include "VG/openvg.h"
50
51 #include "asm_fill.h"
52
53 /* Essentially we construct an ubber-shader based on the state
54 * of the pipeline. The stages are:
55 * 1) Fill (mandatory, solid color/gradient/pattern/image draw)
56 * 2) Image composition (image mode multiply and stencil)
57 * 3) Mask
58 * 4) Extended blend (multiply/screen/darken/lighten)
59 * 5) Premultiply/Unpremultiply
60 * 6) Color transform (to black and white)
61 */
62 #define SHADER_STAGES 6
63
64 struct cached_shader {
65 void *driver_shader;
66 struct pipe_shader_state state;
67 };
68
69 struct shaders_cache {
70 struct vg_context *pipe;
71
72 struct cso_hash *hash;
73 };
74
75
76 static INLINE struct tgsi_token *tokens_from_assembly(const char *txt, int num_tokens)
77 {
78 struct tgsi_token *tokens;
79
80 tokens = (struct tgsi_token *) MALLOC(num_tokens * sizeof(tokens[0]));
81
82 tgsi_text_translate(txt, tokens, num_tokens);
83
84 #if DEBUG_SHADERS
85 tgsi_dump(tokens, 0);
86 #endif
87
88 return tokens;
89 }
90
91 #define ALL_FILLS (VEGA_SOLID_FILL_SHADER | \
92 VEGA_LINEAR_GRADIENT_SHADER | \
93 VEGA_RADIAL_GRADIENT_SHADER | \
94 VEGA_PATTERN_SHADER | \
95 VEGA_IMAGE_NORMAL_SHADER)
96
97
98 /*
99 static const char max_shader_preamble[] =
100 "FRAG1.1\n"
101 "DCL IN[0], POSITION, LINEAR\n"
102 "DCL IN[1], GENERIC[0], PERSPECTIVE\n"
103 "DCL OUT[0], COLOR, CONSTANT\n"
104 "DCL CONST[0..9], CONSTANT\n"
105 "DCL TEMP[0..9], CONSTANT\n"
106 "DCL SAMP[0..9], CONSTANT\n";
107
108 max_shader_preamble strlen == 175
109 */
110 #define MAX_PREAMBLE 175
111
112 static INLINE VGint range_min(VGint min, VGint current)
113 {
114 if (min < 0)
115 min = current;
116 else
117 min = MIN2(min, current);
118 return min;
119 }
120
121 static INLINE VGint range_max(VGint max, VGint current)
122 {
123 return MAX2(max, current);
124 }
125
126 static void
127 create_preamble(char *txt,
128 const struct shader_asm_info *shaders[SHADER_STAGES],
129 int num_shaders)
130 {
131 VGboolean declare_input = VG_FALSE;
132 VGint start_const = -1, end_const = 0;
133 VGint start_temp = -1, end_temp = 0;
134 VGint start_sampler = -1, end_sampler = 0;
135 VGint i;
136 VGint num_consts, num_temps, num_samplers;
137
138 for (i = 0; i < num_shaders; ++i) {
139 if (shaders[i]->num_consts)
140 start_const = range_min(start_const, shaders[i]->start_const);
141 if (shaders[i]->num_temps)
142 start_temp = range_min(start_temp, shaders[i]->start_temp);
143 if (shaders[i]->num_samplers)
144 start_sampler = range_min(start_sampler, shaders[i]->start_sampler);
145
146 end_const = range_max(end_const, shaders[i]->start_const +
147 shaders[i]->num_consts);
148 end_temp = range_max(end_temp, shaders[i]->start_temp +
149 shaders[i]->num_temps);
150 end_sampler = range_max(end_sampler, shaders[i]->start_sampler +
151 shaders[i]->num_samplers);
152 if (shaders[i]->needs_position)
153 declare_input = VG_TRUE;
154 }
155 /* if they're still unitialized, initialize them */
156 if (start_const < 0)
157 start_const = 0;
158 if (start_temp < 0)
159 start_temp = 0;
160 if (start_sampler < 0)
161 start_sampler = 0;
162
163 num_consts = end_const - start_const;
164 num_temps = end_temp - start_temp;
165 num_samplers = end_sampler - start_sampler;
166 /* end exclusive */
167 --end_const;
168 --end_temp;
169 --end_sampler;
170
171 sprintf(txt, "FRAG1.1\n");
172
173 if (declare_input) {
174 sprintf(txt + strlen(txt), "DCL IN[0], POSITION, LINEAR\n");
175 sprintf(txt + strlen(txt), "DCL IN[1], GENERIC[0], PERSPECTIVE\n");
176 }
177
178 /* we always have a color output */
179 sprintf(txt + strlen(txt), "DCL OUT[0], COLOR, CONSTANT\n");
180
181 if (num_consts > 1)
182 sprintf(txt + strlen(txt), "DCL CONST[%d..%d], CONSTANT\n", start_const, end_const);
183 else if (num_consts == 1)
184 sprintf(txt + strlen(txt), "DCL CONST[%d], CONSTANT\n", start_const);
185
186 if (num_temps > 1)
187 sprintf(txt + strlen(txt), "DCL TEMP[%d..%d], CONSTANT\n", start_temp, end_temp);
188 else if (num_temps > 1)
189 sprintf(txt + strlen(txt), "DCL TEMP[%d], CONSTANT\n", start_temp);
190
191 if (num_samplers > 1)
192 sprintf(txt + strlen(txt), "DCL SAMP[%d..%d], CONSTANT\n", start_sampler, end_sampler);
193 else if (num_samplers == 1)
194 sprintf(txt + strlen(txt), "DCL SAMP[%d], CONSTANT\n", start_sampler);
195 }
196
197 static void *
198 combine_shaders(const struct shader_asm_info *shaders[SHADER_STAGES], int num_shaders,
199 struct pipe_context *pipe,
200 struct pipe_shader_state *shader)
201 {
202 char *combined_txt;
203 int combined_len = MAX_PREAMBLE;
204 int combined_tokens = 0;
205 int i = 0;
206 int current_shader = 0;
207 int current_len;
208
209 for (i = 0; i < num_shaders; ++i) {
210 combined_len += strlen(shaders[i]->txt);
211 combined_tokens += shaders[i]->num_tokens;
212 }
213 /* add for the %s->TEMP[0] substitutions */
214 combined_len += num_shaders * 7 /*TEMP[0]*/ + 4 /*"END\n"*/;
215
216 combined_txt = (char*)malloc(combined_len);
217 combined_txt[0] = '\0';
218
219 create_preamble(combined_txt, shaders, num_shaders);
220
221 while (current_shader < num_shaders) {
222 const char temp[] = "TEMP[0]";
223 const char out[] = "OUT[0]";
224 const char *subst = temp;
225
226 current_len = strlen(combined_txt);
227
228 /* if the last shader then output */
229 if (current_shader + 1 == num_shaders)
230 subst = out;
231
232 snprintf(combined_txt + current_len,
233 combined_len - current_len,
234 shaders[current_shader]->txt,
235 subst);
236 ++current_shader;
237 }
238
239
240 current_len = strlen(combined_txt);
241 snprintf(combined_txt + current_len,
242 combined_len - current_len,
243 "END\n");
244
245 debug_printf("Combined shader is : \n%s\n",
246 combined_txt);
247
248 shader->tokens = tokens_from_assembly(
249 combined_txt, combined_tokens);
250
251 free(combined_txt);
252
253 return pipe->create_fs_state(pipe, shader);
254 }
255
256 static void *
257 create_shader(struct pipe_context *pipe,
258 int id,
259 struct pipe_shader_state *shader)
260 {
261 int idx = 0;
262 const struct shader_asm_info * shaders[SHADER_STAGES];
263
264 /* the shader has to have a fill */
265 debug_assert(id & ALL_FILLS);
266
267 /* first stage */
268 if (id & VEGA_SOLID_FILL_SHADER) {
269 debug_assert(idx == 0);
270 shaders[idx] = &shaders_asm[0];
271 debug_assert(shaders_asm[0].id == VEGA_SOLID_FILL_SHADER);
272 ++idx;
273 }
274 if ((id & VEGA_LINEAR_GRADIENT_SHADER)) {
275 debug_assert(idx == 0);
276 shaders[idx] = &shaders_asm[1];
277 debug_assert(shaders_asm[1].id == VEGA_LINEAR_GRADIENT_SHADER);
278 ++idx;
279 }
280 if ((id & VEGA_RADIAL_GRADIENT_SHADER)) {
281 debug_assert(idx == 0);
282 shaders[idx] = &shaders_asm[2];
283 debug_assert(shaders_asm[2].id == VEGA_RADIAL_GRADIENT_SHADER);
284 ++idx;
285 }
286 if ((id & VEGA_PATTERN_SHADER)) {
287 debug_assert(idx == 0);
288 debug_assert(shaders_asm[3].id == VEGA_PATTERN_SHADER);
289 shaders[idx] = &shaders_asm[3];
290 ++idx;
291 }
292 if ((id & VEGA_IMAGE_NORMAL_SHADER)) {
293 debug_assert(idx == 0);
294 debug_assert(shaders_asm[4].id == VEGA_IMAGE_NORMAL_SHADER);
295 shaders[idx] = &shaders_asm[4];
296 ++idx;
297 }
298
299 /* second stage */
300 if ((id & VEGA_IMAGE_MULTIPLY_SHADER)) {
301 debug_assert(shaders_asm[5].id == VEGA_IMAGE_MULTIPLY_SHADER);
302 shaders[idx] = &shaders_asm[5];
303 ++idx;
304 } else if ((id & VEGA_IMAGE_STENCIL_SHADER)) {
305 debug_assert(shaders_asm[6].id == VEGA_IMAGE_STENCIL_SHADER);
306 shaders[idx] = &shaders_asm[6];
307 ++idx;
308 }
309
310 /* third stage */
311 if ((id & VEGA_MASK_SHADER)) {
312 debug_assert(idx == 1);
313 debug_assert(shaders_asm[7].id == VEGA_MASK_SHADER);
314 shaders[idx] = &shaders_asm[7];
315 ++idx;
316 }
317
318 /* fourth stage */
319 if ((id & VEGA_BLEND_MULTIPLY_SHADER)) {
320 debug_assert(shaders_asm[8].id == VEGA_BLEND_MULTIPLY_SHADER);
321 shaders[idx] = &shaders_asm[8];
322 ++idx;
323 } else if ((id & VEGA_BLEND_SCREEN_SHADER)) {
324 debug_assert(shaders_asm[9].id == VEGA_BLEND_SCREEN_SHADER);
325 shaders[idx] = &shaders_asm[9];
326 ++idx;
327 } else if ((id & VEGA_BLEND_DARKEN_SHADER)) {
328 debug_assert(shaders_asm[10].id == VEGA_BLEND_DARKEN_SHADER);
329 shaders[idx] = &shaders_asm[10];
330 ++idx;
331 } else if ((id & VEGA_BLEND_LIGHTEN_SHADER)) {
332 debug_assert(shaders_asm[11].id == VEGA_BLEND_LIGHTEN_SHADER);
333 shaders[idx] = &shaders_asm[11];
334 ++idx;
335 }
336
337 /* fifth stage */
338 if ((id & VEGA_PREMULTIPLY_SHADER)) {
339 debug_assert(shaders_asm[12].id == VEGA_PREMULTIPLY_SHADER);
340 shaders[idx] = &shaders_asm[12];
341 ++idx;
342 } else if ((id & VEGA_UNPREMULTIPLY_SHADER)) {
343 debug_assert(shaders_asm[13].id == VEGA_UNPREMULTIPLY_SHADER);
344 shaders[idx] = &shaders_asm[13];
345 ++idx;
346 }
347
348 /* sixth stage */
349 if ((id & VEGA_BW_SHADER)) {
350 debug_assert(shaders_asm[14].id == VEGA_BW_SHADER);
351 shaders[idx] = &shaders_asm[14];
352 ++idx;
353 }
354
355 return combine_shaders(shaders, idx, pipe, shader);
356 }
357
358 /*************************************************/
359
360 struct shaders_cache * shaders_cache_create(struct vg_context *vg)
361 {
362 struct shaders_cache *sc = CALLOC_STRUCT(shaders_cache);
363
364 sc->pipe = vg;
365 sc->hash = cso_hash_create();
366
367 return sc;
368 }
369
370 void shaders_cache_destroy(struct shaders_cache *sc)
371 {
372 struct cso_hash_iter iter = cso_hash_first_node(sc->hash);
373
374 while (!cso_hash_iter_is_null(iter)) {
375 struct cached_shader *cached =
376 (struct cached_shader *)cso_hash_iter_data(iter);
377 cso_delete_fragment_shader(sc->pipe->cso_context,
378 cached->driver_shader);
379 iter = cso_hash_erase(sc->hash, iter);
380 }
381
382 cso_hash_delete(sc->hash);
383 free(sc);
384 }
385
386 void * shaders_cache_fill(struct shaders_cache *sc,
387 int shader_key)
388 {
389 VGint key = shader_key;
390 struct cached_shader *cached;
391 struct cso_hash_iter iter = cso_hash_find(sc->hash, key);
392
393 if (cso_hash_iter_is_null(iter)) {
394 cached = CALLOC_STRUCT(cached_shader);
395 cached->driver_shader = create_shader(sc->pipe->pipe, key, &cached->state);
396
397 cso_hash_insert(sc->hash, key, cached);
398
399 return cached->driver_shader;
400 }
401
402 cached = (struct cached_shader *)cso_hash_iter_data(iter);
403
404 assert(cached->driver_shader);
405 return cached->driver_shader;
406 }
407
408 struct vg_shader * shader_create_from_text(struct pipe_context *pipe,
409 const char *txt, int num_tokens,
410 int type)
411 {
412 struct vg_shader *shader = (struct vg_shader *)malloc(
413 sizeof(struct vg_shader));
414 struct tgsi_token *tokens = tokens_from_assembly(txt, num_tokens);
415 struct pipe_shader_state state;
416
417 debug_assert(type == PIPE_SHADER_VERTEX ||
418 type == PIPE_SHADER_FRAGMENT);
419
420 state.tokens = tokens;
421 shader->type = type;
422 shader->tokens = tokens;
423
424 if (type == PIPE_SHADER_FRAGMENT)
425 shader->driver = pipe->create_fs_state(pipe, &state);
426 else
427 shader->driver = pipe->create_vs_state(pipe, &state);
428 return shader;
429 }
430
431 void vg_shader_destroy(struct vg_context *ctx, struct vg_shader *shader)
432 {
433 if (shader->type == PIPE_SHADER_FRAGMENT)
434 cso_delete_fragment_shader(ctx->cso_context, shader->driver);
435 else
436 cso_delete_vertex_shader(ctx->cso_context, shader->driver);
437 free(shader->tokens);
438 free(shader);
439 }