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 "shader.h"
28
29#include "vg_context.h"
30#include "shaders_cache.h"
31#include "paint.h"
32#include "mask.h"
33#include "image.h"
34#include "renderer.h"
35
36#include "pipe/p_context.h"
37#include "pipe/p_state.h"
38#include "util/u_memory.h"
39#include "util/u_math.h"
40#include "util/u_format.h"
41
42#define MAX_CONSTANTS 28
43
44struct shader {
45   struct vg_context *context;
46
47   VGboolean color_transform;
48   VGboolean masking;
49   struct vg_paint *paint;
50   struct vg_image *image;
51
52   struct matrix modelview;
53   struct matrix paint_matrix;
54
55   VGboolean drawing_image;
56   VGImageMode image_mode;
57
58   float constants[MAX_CONSTANTS];
59   struct pipe_resource *cbuf;
60   struct pipe_shader_state fs_state;
61   void *fs;
62};
63
64struct shader * shader_create(struct vg_context *ctx)
65{
66   struct shader *shader = 0;
67
68   shader = CALLOC_STRUCT(shader);
69   shader->context = ctx;
70
71   return shader;
72}
73
74void shader_destroy(struct shader *shader)
75{
76   FREE(shader);
77}
78
79void shader_set_color_transform(struct shader *shader, VGboolean set)
80{
81   shader->color_transform = set;
82}
83
84void shader_set_masking(struct shader *shader, VGboolean set)
85{
86   shader->masking = set;
87}
88
89VGboolean shader_is_masking(struct shader *shader)
90{
91   return shader->masking;
92}
93
94void shader_set_paint(struct shader *shader, struct vg_paint *paint)
95{
96   shader->paint = paint;
97}
98
99struct vg_paint * shader_paint(struct shader *shader)
100{
101   return shader->paint;
102}
103
104static VGint setup_constant_buffer(struct shader *shader)
105{
106   const struct vg_state *state = &shader->context->state.vg;
107   VGint param_bytes = paint_constant_buffer_size(shader->paint);
108   VGint i;
109
110   param_bytes += sizeof(VGfloat) * 8;
111   assert(param_bytes <= sizeof(shader->constants));
112
113   if (state->color_transform) {
114      for (i = 0; i < 8; i++) {
115         VGfloat val = (i < 4) ? 127.0f : 1.0f;
116         shader->constants[i] =
117            CLAMP(state->color_transform_values[i], -val, val);
118      }
119   }
120   else {
121      memset(shader->constants, 0, sizeof(VGfloat) * 8);
122   }
123
124   paint_fill_constant_buffer(shader->paint,
125         &shader->paint_matrix, shader->constants + 8);
126
127   return param_bytes;
128}
129
130static VGboolean blend_use_shader(struct shader *shader)
131{
132   struct vg_context *ctx = shader->context;
133   VGboolean advanced_blending;
134
135   switch (ctx->state.vg.blend_mode) {
136   case VG_BLEND_DST_OVER:
137   case VG_BLEND_MULTIPLY:
138   case VG_BLEND_SCREEN:
139   case VG_BLEND_DARKEN:
140   case VG_BLEND_LIGHTEN:
141   case VG_BLEND_ADDITIVE:
142      advanced_blending = VG_TRUE;
143      break;
144   case VG_BLEND_SRC_OVER:
145      if (util_format_has_alpha(ctx->draw_buffer->strb->format)) {
146         /* no blending is required if the paints and the image are opaque */
147         advanced_blending = !paint_is_opaque(ctx->state.vg.fill_paint) ||
148                             !paint_is_opaque(ctx->state.vg.stroke_paint);
149         if (!advanced_blending && shader->drawing_image) {
150            advanced_blending =
151               util_format_has_alpha(shader->image->sampler_view->format);
152         }
153         break;
154      }
155      /* fall through */
156   default:
157      advanced_blending = VG_FALSE;
158      break;
159   }
160
161   return advanced_blending;
162}
163
164static VGint blend_bind_samplers(struct shader *shader,
165                                 struct pipe_sampler_state **samplers,
166                                 struct pipe_sampler_view **sampler_views)
167{
168   if (blend_use_shader(shader)) {
169      struct vg_context *ctx = shader->context;
170
171      samplers[2] = &ctx->blend_sampler;
172      sampler_views[2] = vg_prepare_blend_surface(ctx);
173
174      if (!samplers[0] || !sampler_views[0]) {
175         samplers[0] = samplers[2];
176         sampler_views[0] = sampler_views[2];
177      }
178      if (!samplers[1] || !sampler_views[1]) {
179         samplers[1] = samplers[0];
180         sampler_views[1] = sampler_views[0];
181      }
182
183      return 1;
184   }
185   return 0;
186}
187
188static VGint setup_samplers(struct shader *shader,
189                            struct pipe_sampler_state **samplers,
190                            struct pipe_sampler_view **sampler_views)
191{
192   /* a little wonky: we use the num as a boolean that just says
193    * whether any sampler/textures have been set. the actual numbering
194    * for samplers is always the same:
195    * 0 - paint sampler/texture for gradient/pattern
196    * 1 - mask sampler/texture
197    * 2 - blend sampler/texture
198    * 3 - image sampler/texture
199    * */
200   VGint num = 0;
201
202   samplers[0] = NULL;
203   samplers[1] = NULL;
204   samplers[2] = NULL;
205   samplers[3] = NULL;
206   sampler_views[0] = NULL;
207   sampler_views[1] = NULL;
208   sampler_views[2] = NULL;
209   sampler_views[3] = NULL;
210
211   num += paint_bind_samplers(shader->paint, samplers, sampler_views);
212   num += mask_bind_samplers(samplers, sampler_views);
213   num += blend_bind_samplers(shader, samplers, sampler_views);
214   if (shader->drawing_image && shader->image)
215      num += image_bind_samplers(shader->image, samplers, sampler_views);
216
217   return (num) ? 4 : 0;
218}
219
220static INLINE VGboolean is_format_bw(struct shader *shader)
221{
222#if 0
223   struct vg_context *ctx = shader->context;
224   struct st_framebuffer *stfb = ctx->draw_buffer;
225#endif
226
227   if (shader->drawing_image && shader->image) {
228      if (shader->image->format == VG_BW_1)
229         return VG_TRUE;
230   }
231
232   return VG_FALSE;
233}
234
235static void setup_shader_program(struct shader *shader)
236{
237   struct vg_context *ctx = shader->context;
238   VGint shader_id = 0;
239   VGBlendMode blend_mode = ctx->state.vg.blend_mode;
240   VGboolean black_white = is_format_bw(shader);
241
242   /* 1st stage: fill */
243   if (!shader->drawing_image ||
244       (shader->image_mode == VG_DRAW_IMAGE_MULTIPLY || shader->image_mode == VG_DRAW_IMAGE_STENCIL)) {
245      switch(paint_type(shader->paint)) {
246      case VG_PAINT_TYPE_COLOR:
247         shader_id |= VEGA_SOLID_FILL_SHADER;
248         break;
249      case VG_PAINT_TYPE_LINEAR_GRADIENT:
250         shader_id |= VEGA_LINEAR_GRADIENT_SHADER;
251         break;
252      case VG_PAINT_TYPE_RADIAL_GRADIENT:
253         shader_id |= VEGA_RADIAL_GRADIENT_SHADER;
254         break;
255      case VG_PAINT_TYPE_PATTERN:
256         shader_id |= VEGA_PATTERN_SHADER;
257         break;
258
259      default:
260         abort();
261      }
262
263      if (paint_is_degenerate(shader->paint))
264         shader_id = VEGA_PAINT_DEGENERATE_SHADER;
265   }
266
267   /* second stage image */
268   if (shader->drawing_image) {
269      switch(shader->image_mode) {
270      case VG_DRAW_IMAGE_NORMAL:
271         shader_id |= VEGA_IMAGE_NORMAL_SHADER;
272         break;
273      case VG_DRAW_IMAGE_MULTIPLY:
274         shader_id |= VEGA_IMAGE_MULTIPLY_SHADER;
275         break;
276      case VG_DRAW_IMAGE_STENCIL:
277         shader_id |= VEGA_IMAGE_STENCIL_SHADER;
278         break;
279      default:
280         debug_printf("Unknown image mode!");
281      }
282   }
283
284   if (shader->color_transform)
285      shader_id |= VEGA_COLOR_TRANSFORM_SHADER;
286
287   if (blend_use_shader(shader)) {
288      if (shader->drawing_image && shader->image_mode == VG_DRAW_IMAGE_STENCIL)
289         shader_id |= VEGA_ALPHA_PER_CHANNEL_SHADER;
290      else
291         shader_id |= VEGA_ALPHA_NORMAL_SHADER;
292
293      switch(blend_mode) {
294      case VG_BLEND_SRC:
295         shader_id |= VEGA_BLEND_SRC_SHADER;
296         break;
297      case VG_BLEND_SRC_OVER:
298         shader_id |= VEGA_BLEND_SRC_OVER_SHADER;
299         break;
300      case VG_BLEND_DST_OVER:
301         shader_id |= VEGA_BLEND_DST_OVER_SHADER;
302         break;
303      case VG_BLEND_SRC_IN:
304         shader_id |= VEGA_BLEND_SRC_IN_SHADER;
305         break;
306      case VG_BLEND_DST_IN:
307         shader_id |= VEGA_BLEND_DST_IN_SHADER;
308         break;
309      case VG_BLEND_MULTIPLY:
310         shader_id |= VEGA_BLEND_MULTIPLY_SHADER;
311         break;
312      case VG_BLEND_SCREEN:
313         shader_id |= VEGA_BLEND_SCREEN_SHADER;
314         break;
315      case VG_BLEND_DARKEN:
316         shader_id |= VEGA_BLEND_DARKEN_SHADER;
317         break;
318      case VG_BLEND_LIGHTEN:
319         shader_id |= VEGA_BLEND_LIGHTEN_SHADER;
320         break;
321      case VG_BLEND_ADDITIVE:
322         shader_id |= VEGA_BLEND_ADDITIVE_SHADER;
323         break;
324      default:
325         assert(0);
326         break;
327      }
328   }
329   else {
330      /* update alpha of the source */
331      if (shader->drawing_image && shader->image_mode == VG_DRAW_IMAGE_STENCIL)
332         shader_id |= VEGA_ALPHA_PER_CHANNEL_SHADER;
333   }
334
335   if (shader->masking)
336      shader_id |= VEGA_MASK_SHADER;
337
338   if (black_white)
339      shader_id |= VEGA_BW_SHADER;
340
341   shader->fs = shaders_cache_fill(ctx->sc, shader_id);
342}
343
344
345void shader_bind(struct shader *shader)
346{
347   struct vg_context *ctx = shader->context;
348   struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS];
349   struct pipe_sampler_view *sampler_views[PIPE_MAX_SAMPLERS];
350   VGint num_samplers, param_bytes;
351
352   /* first resolve the real paint type */
353   paint_resolve_type(shader->paint);
354
355   num_samplers = setup_samplers(shader, samplers, sampler_views);
356   param_bytes = setup_constant_buffer(shader);
357   setup_shader_program(shader);
358
359   renderer_validate_for_shader(ctx->renderer,
360         (const struct pipe_sampler_state **) samplers,
361         sampler_views, num_samplers,
362         &shader->modelview,
363         shader->fs, (const void *) shader->constants, param_bytes);
364}
365
366void shader_set_image_mode(struct shader *shader, VGImageMode image_mode)
367{
368   shader->image_mode = image_mode;
369}
370
371VGImageMode shader_image_mode(struct shader *shader)
372{
373   return shader->image_mode;
374}
375
376void shader_set_drawing_image(struct shader *shader, VGboolean drawing_image)
377{
378   shader->drawing_image = drawing_image;
379}
380
381VGboolean shader_drawing_image(struct shader *shader)
382{
383   return shader->drawing_image;
384}
385
386void shader_set_image(struct shader *shader, struct vg_image *img)
387{
388   shader->image = img;
389}
390
391/**
392 * Set the transformation to map a vertex to the surface coordinates.
393 */
394void shader_set_surface_matrix(struct shader *shader,
395                               const struct matrix *mat)
396{
397   shader->modelview = *mat;
398}
399
400/**
401 * Set the transformation to map a pixel to the paint coordinates.
402 */
403void shader_set_paint_matrix(struct shader *shader, const struct matrix *mat)
404{
405   const struct st_framebuffer *stfb = shader->context->draw_buffer;
406   const VGfloat px_center_offset = 0.5f;
407
408   memcpy(&shader->paint_matrix, mat, sizeof(*mat));
409
410   /* make it window-to-paint for the shaders */
411   matrix_translate(&shader->paint_matrix, px_center_offset,
412         stfb->height - 1.0f + px_center_offset);
413   matrix_scale(&shader->paint_matrix, 1.0f, -1.0f);
414}
415