st/vega: Delay blend texture creation until needed.
[mesa.git] / src / gallium / state_trackers / vega / mask.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 "mask.h"
28
29 #include "path.h"
30 #include "image.h"
31 #include "shaders_cache.h"
32 #include "renderer.h"
33 #include "asm_util.h"
34
35 #include "pipe/p_context.h"
36 #include "pipe/p_screen.h"
37 #include "util/u_inlines.h"
38 #include "util/u_format.h"
39 #include "util/u_memory.h"
40 #include "util/u_sampler.h"
41
42 struct vg_mask_layer {
43 struct vg_object base;
44
45 VGint width;
46 VGint height;
47
48 struct pipe_sampler_view *sampler_view;
49 };
50
51 static INLINE struct pipe_surface *
52 alpha_mask_surface(struct vg_context *ctx, int usage)
53 {
54 struct pipe_screen *screen = ctx->pipe->screen;
55 struct st_framebuffer *stfb = ctx->draw_buffer;
56 return screen->get_tex_surface(screen,
57 stfb->alpha_mask_view->texture,
58 0, 0, 0,
59 usage);
60 }
61
62 static INLINE VGboolean
63 intersect_rectangles(VGint dwidth, VGint dheight,
64 VGint swidth, VGint sheight,
65 VGint tx, VGint ty,
66 VGint twidth, VGint theight,
67 VGint *offsets,
68 VGint *location)
69 {
70 if (tx + twidth <= 0 || tx >= dwidth)
71 return VG_FALSE;
72 if (ty + theight <= 0 || ty >= dheight)
73 return VG_FALSE;
74
75 offsets[0] = 0;
76 offsets[1] = 0;
77 location[0] = tx;
78 location[1] = ty;
79
80 if (tx < 0) {
81 offsets[0] -= tx;
82 location[0] = 0;
83
84 location[2] = MIN2(tx + swidth, MIN2(dwidth, tx + twidth));
85 offsets[2] = location[2];
86 } else {
87 offsets[2] = MIN2(twidth, MIN2(dwidth - tx, swidth ));
88 location[2] = offsets[2];
89 }
90
91 if (ty < 0) {
92 offsets[1] -= ty;
93 location[1] = 0;
94
95 location[3] = MIN2(ty + sheight, MIN2(dheight, ty + theight));
96 offsets[3] = location[3];
97 } else {
98 offsets[3] = MIN2(theight, MIN2(dheight - ty, sheight));
99 location[3] = offsets[3];
100 }
101
102 return VG_TRUE;
103 }
104
105 #if DEBUG_MASKS
106 static void read_alpha_mask(void * data, VGint dataStride,
107 VGImageFormat dataFormat,
108 VGint sx, VGint sy,
109 VGint width, VGint height)
110 {
111 struct vg_context *ctx = vg_current_context();
112 struct pipe_context *pipe = ctx->pipe;
113 struct pipe_screen *screen = pipe->screen;
114
115 struct st_framebuffer *stfb = ctx->draw_buffer;
116 struct st_renderbuffer *strb = stfb->alpha_mask;
117
118 VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
119 VGfloat *df = (VGfloat*)temp;
120 VGint y = (stfb->height - sy) - 1, yStep = -1;
121 VGint i;
122 VGubyte *dst = (VGubyte *)data;
123 VGint xoffset = 0, yoffset = 0;
124
125 /* make sure rendering has completed */
126 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
127 if (sx < 0) {
128 xoffset = -sx;
129 xoffset *= _vega_size_for_format(dataFormat);
130 width += sx;
131 sx = 0;
132 }
133 if (sy < 0) {
134 yoffset = -sy;
135 height += sy;
136 sy = 0;
137 y = (stfb->height - sy) - 1;
138 yoffset *= dataStride;
139 }
140
141 {
142 struct pipe_surface *surf;
143
144 surf = screen->get_tex_surface(screen, strb->texture, 0, 0, 0,
145 PIPE_BIND_TRANSFER_READ);
146
147 /* Do a row at a time to flip image data vertically */
148 for (i = 0; i < height; i++) {
149 #if 0
150 debug_printf("%d-%d == %d\n", sy, height, y);
151 #endif
152 pipe_get_tile_rgba(surf, sx, y, width, 1, df);
153 y += yStep;
154 _vega_pack_rgba_span_float(ctx, width, temp, dataFormat,
155 dst + yoffset + xoffset);
156 dst += dataStride;
157 }
158
159 pipe_surface_reference(&surf, NULL);
160 }
161 }
162
163 void save_alpha_to_file(const char *filename)
164 {
165 struct vg_context *ctx = vg_current_context();
166 struct st_framebuffer *stfb = ctx->draw_buffer;
167 VGint *data;
168 int i, j;
169
170 data = malloc(sizeof(int) * stfb->width * stfb->height);
171 read_alpha_mask(data, stfb->width * sizeof(int),
172 VG_sRGBA_8888,
173 0, 0, stfb->width, stfb->height);
174 fprintf(stderr, "/*---------- start */\n");
175 fprintf(stderr, "const int image_width = %d;\n",
176 stfb->width);
177 fprintf(stderr, "const int image_height = %d;\n",
178 stfb->height);
179 fprintf(stderr, "const int image_data = {\n");
180 for (i = 0; i < stfb->height; ++i) {
181 for (j = 0; j < stfb->width; ++j) {
182 int rgba = data[i * stfb->height + j];
183 int argb = 0;
184 argb = (rgba >> 8);
185 argb |= ((rgba & 0xff) << 24);
186 fprintf(stderr, "0x%x, ", argb);
187 }
188 fprintf(stderr, "\n");
189 }
190 fprintf(stderr, "};\n");
191 fprintf(stderr, "/*---------- end */\n");
192 }
193 #endif
194
195 /* setup mask shader */
196 static void *setup_mask_operation(VGMaskOperation operation)
197 {
198 struct vg_context *ctx = vg_current_context();
199 void *shader = 0;
200
201 switch (operation) {
202 case VG_UNION_MASK: {
203 if (!ctx->mask.union_fs) {
204 ctx->mask.union_fs = shader_create_from_text(ctx->pipe,
205 union_mask_asm,
206 200,
207 PIPE_SHADER_FRAGMENT);
208 }
209 shader = ctx->mask.union_fs->driver;
210 }
211 break;
212 case VG_INTERSECT_MASK: {
213 if (!ctx->mask.intersect_fs) {
214 ctx->mask.intersect_fs = shader_create_from_text(ctx->pipe,
215 intersect_mask_asm,
216 200,
217 PIPE_SHADER_FRAGMENT);
218 }
219 shader = ctx->mask.intersect_fs->driver;
220 }
221 break;
222 case VG_SUBTRACT_MASK: {
223 if (!ctx->mask.subtract_fs) {
224 ctx->mask.subtract_fs = shader_create_from_text(ctx->pipe,
225 subtract_mask_asm,
226 200,
227 PIPE_SHADER_FRAGMENT);
228 }
229 shader = ctx->mask.subtract_fs->driver;
230 }
231 break;
232 case VG_SET_MASK: {
233 if (!ctx->mask.set_fs) {
234 ctx->mask.set_fs = shader_create_from_text(ctx->pipe,
235 set_mask_asm,
236 200,
237 PIPE_SHADER_FRAGMENT);
238 }
239 shader = ctx->mask.set_fs->driver;
240 }
241 break;
242 default:
243 assert(0);
244 break;
245 }
246
247 return shader;
248 }
249
250 static void mask_resource_fill(struct pipe_resource *dst,
251 int x, int y, int width, int height,
252 VGfloat coverage)
253 {
254 struct vg_context *ctx = vg_current_context();
255 VGfloat fs_consts[12] = {
256 0.0f, 0.0f, 0.0f, 0.0f, /* not used */
257 0.0f, 0.0f, 0.0f, 0.0f, /* not used */
258 0.0f, 0.0f, 0.0f, coverage /* color */
259 };
260 void *fs;
261
262 if (x < 0) {
263 width += x;
264 x = 0;
265 }
266 if (y < 0) {
267 height += y;
268 y = 0;
269 }
270
271 fs = shaders_cache_fill(ctx->sc, VEGA_SOLID_FILL_SHADER);
272
273 if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE, ~0,
274 NULL, NULL, 0, fs, (const void *) fs_consts, sizeof(fs_consts))) {
275 renderer_filter(ctx->renderer, x, y, width, height, 0, 0, 0, 0);
276 renderer_filter_end(ctx->renderer);
277 }
278
279 #if DEBUG_MASKS
280 save_alpha_to_file(0);
281 #endif
282 }
283
284
285 static void mask_using_texture(struct pipe_sampler_view *sampler_view,
286 VGboolean is_layer,
287 VGMaskOperation operation,
288 VGint x, VGint y,
289 VGint width, VGint height)
290 {
291 struct vg_context *ctx = vg_current_context();
292 struct pipe_resource *dst = ctx->draw_buffer->alpha_mask_view->texture;
293 struct pipe_resource *texture = sampler_view->texture;
294 const struct pipe_sampler_state *samplers[2];
295 struct pipe_sampler_view *views[2];
296 struct pipe_sampler_state sampler;
297 VGint offsets[4], loc[4];
298 const VGfloat ones[4] = {1.f, 1.f, 1.f, 1.f};
299 void *fs;
300
301 if (!intersect_rectangles(dst->width0, dst->height0,
302 texture->width0, texture->height0,
303 x, y, width, height,
304 offsets, loc))
305 return;
306 #if 0
307 debug_printf("Offset = [%d, %d, %d, %d]\n", offsets[0],
308 offsets[1], offsets[2], offsets[3]);
309 debug_printf("Locati = [%d, %d, %d, %d]\n", loc[0],
310 loc[1], loc[2], loc[3]);
311 #endif
312
313 sampler = ctx->mask.sampler;
314 sampler.normalized_coords = 1;
315 samplers[0] = &sampler;
316 views[0] = sampler_view;
317
318 /* prepare our blend surface */
319 samplers[1] = &ctx->mask.sampler;
320 views[1] = vg_prepare_blend_surface_from_mask(ctx);
321
322 fs = setup_mask_operation(operation);
323
324 if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE,
325 ~0, samplers, views, 2, fs, (const void *) ones, sizeof(ones))) {
326 /* layer should be flipped when used as a texture */
327 if (is_layer) {
328 offsets[1] += offsets[3];
329 offsets[3] = -offsets[3];
330 }
331 renderer_filter(ctx->renderer,
332 loc[0], loc[1], loc[2], loc[3],
333 offsets[0], offsets[1], offsets[2], offsets[3]);
334 renderer_filter_end(ctx->renderer);
335 }
336 }
337
338
339 #ifdef OPENVG_VERSION_1_1
340
341 struct vg_mask_layer * mask_layer_create(VGint width, VGint height)
342 {
343 struct vg_context *ctx = vg_current_context();
344 struct vg_mask_layer *mask = 0;
345
346 mask = CALLOC_STRUCT(vg_mask_layer);
347 vg_init_object(&mask->base, ctx, VG_OBJECT_MASK);
348 mask->width = width;
349 mask->height = height;
350
351 {
352 struct pipe_resource pt;
353 struct pipe_context *pipe = ctx->pipe;
354 struct pipe_screen *screen = ctx->pipe->screen;
355 struct pipe_sampler_view view_templ;
356 struct pipe_sampler_view *view = NULL;
357 struct pipe_resource *texture;
358
359 memset(&pt, 0, sizeof(pt));
360 pt.target = PIPE_TEXTURE_2D;
361 pt.format = PIPE_FORMAT_B8G8R8A8_UNORM;
362 pt.last_level = 0;
363 pt.width0 = width;
364 pt.height0 = height;
365 pt.depth0 = 1;
366 pt.bind = PIPE_BIND_SAMPLER_VIEW;
367
368 texture = screen->resource_create(screen, &pt);
369
370 if (texture) {
371 u_sampler_view_default_template(&view_templ, texture, texture->format);
372 view = pipe->create_sampler_view(pipe, texture, &view_templ);
373 }
374 pipe_resource_reference(&texture, NULL);
375 mask->sampler_view = view;
376 }
377
378 vg_context_add_object(ctx, VG_OBJECT_MASK, mask);
379
380 return mask;
381 }
382
383 void mask_layer_destroy(struct vg_mask_layer *layer)
384 {
385 struct vg_context *ctx = vg_current_context();
386
387 vg_context_remove_object(ctx, VG_OBJECT_MASK, layer);
388 pipe_sampler_view_reference(&layer->sampler_view, NULL);
389 FREE(layer);
390 }
391
392 void mask_layer_fill(struct vg_mask_layer *layer,
393 VGint x, VGint y,
394 VGint width, VGint height,
395 VGfloat value)
396 {
397 VGfloat alpha_color[4] = {0, 0, 0, 0};
398
399 alpha_color[3] = value;
400
401 mask_resource_fill(layer->sampler_view->texture,
402 x, y, width, height, value);
403 }
404
405 void mask_copy(struct vg_mask_layer *layer,
406 VGint sx, VGint sy,
407 VGint dx, VGint dy,
408 VGint width, VGint height)
409 {
410 struct vg_context *ctx = vg_current_context();
411 struct pipe_sampler_view *src = ctx->draw_buffer->alpha_mask_view;
412 struct pipe_surface *surf;
413
414 /* get the destination surface */
415 surf = ctx->pipe->screen->get_tex_surface(ctx->pipe->screen,
416 layer->sampler_view->texture, 0, 0, 0, PIPE_BIND_RENDER_TARGET);
417 if (surf && renderer_copy_begin(ctx->renderer, surf, VG_FALSE, src)) {
418 /* layer should be flipped when used as a texture */
419 sy += height;
420 height = -height;
421
422 renderer_copy(ctx->renderer,
423 dx, dy, width, height,
424 sx, sy, width, height);
425 renderer_copy_end(ctx->renderer);
426 }
427
428 pipe_surface_reference(&surf, NULL);
429 }
430
431 static void mask_layer_render_to(struct vg_mask_layer *layer,
432 struct path *path,
433 VGbitfield paint_modes)
434 {
435 struct vg_context *ctx = vg_current_context();
436 struct matrix *mat = &ctx->state.vg.path_user_to_surface_matrix;
437 struct pipe_surface *surf;
438
439 surf = alpha_mask_surface(ctx, PIPE_BIND_RENDER_TARGET);
440
441 renderer_validate_for_mask_rendering(ctx->renderer, surf);
442
443 if (paint_modes & VG_FILL_PATH) {
444 path_fill(path, mat);
445 }
446
447 if (paint_modes & VG_STROKE_PATH){
448 path_stroke(path, mat);
449 }
450 }
451
452 void mask_render_to(struct path *path,
453 VGbitfield paint_modes,
454 VGMaskOperation operation)
455 {
456 struct vg_context *ctx = vg_current_context();
457 struct st_framebuffer *fb_buffers = ctx->draw_buffer;
458 struct vg_mask_layer *temp_layer;
459 VGint width, height;
460
461 width = fb_buffers->alpha_mask_view->texture->width0;
462 height = fb_buffers->alpha_mask_view->texture->height0;
463
464 temp_layer = mask_layer_create(width, height);
465 mask_layer_fill(temp_layer, 0, 0, width, height, 0.0f);
466
467 mask_layer_render_to(temp_layer, path, paint_modes);
468
469 mask_using_layer(temp_layer, operation, 0, 0, width, height);
470
471 mask_layer_destroy(temp_layer);
472 }
473
474 void mask_using_layer(struct vg_mask_layer *layer,
475 VGMaskOperation operation,
476 VGint x, VGint y,
477 VGint width, VGint height)
478 {
479 mask_using_texture(layer->sampler_view, VG_TRUE, operation,
480 x, y, width, height);
481 }
482
483 VGint mask_layer_width(struct vg_mask_layer *layer)
484 {
485 return layer->width;
486 }
487
488 VGint mask_layer_height(struct vg_mask_layer *layer)
489 {
490 return layer->height;
491 }
492
493
494 #endif
495
496 void mask_using_image(struct vg_image *image,
497 VGMaskOperation operation,
498 VGint x, VGint y,
499 VGint width, VGint height)
500 {
501 mask_using_texture(image->sampler_view, VG_FALSE, operation,
502 x, y, width, height);
503 }
504
505 void mask_fill(VGint x, VGint y, VGint width, VGint height,
506 VGfloat value)
507 {
508 struct vg_context *ctx = vg_current_context();
509
510 #if DEBUG_MASKS
511 debug_printf("mask_fill(%d, %d, %d, %d) with rgba(%f, %f, %f, %f)\n",
512 x, y, width, height,
513 0.0f, 0.0f, 0.0f, value);
514 #endif
515
516 mask_resource_fill(ctx->draw_buffer->alpha_mask_view->texture,
517 x, y, width, height, value);
518 }
519
520 VGint mask_bind_samplers(struct pipe_sampler_state **samplers,
521 struct pipe_sampler_view **sampler_views)
522 {
523 struct vg_context *ctx = vg_current_context();
524
525 if (ctx->state.vg.masking) {
526 struct st_framebuffer *fb_buffers = ctx->draw_buffer;
527
528 samplers[1] = &ctx->mask.sampler;
529 sampler_views[1] = fb_buffers->alpha_mask_view;
530 return 1;
531 } else
532 return 0;
533 }