1 /*
2 * Copyright © 2010 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "sna.h"
33 #include "sna_render.h"
34
35 #define xFixedToDouble(f) pixman_fixed_to_double(f)
36
37 bool
sna_gradient_is_opaque(const PictGradient * gradient)38 sna_gradient_is_opaque(const PictGradient *gradient)
39 {
40 int n;
41
42 for (n = 0; n < gradient->nstops; n++) {
43 if (gradient->stops[n].color.alpha < 0xff00)
44 return false;
45 }
46
47 return true;
48 }
49
50 static int
sna_gradient_sample_width(PictGradient * gradient)51 sna_gradient_sample_width(PictGradient *gradient)
52 {
53 int n, width;
54
55 width = 0;
56 for (n = 1; n < gradient->nstops; n++) {
57 xFixed dx = gradient->stops[n].x - gradient->stops[n-1].x;
58 int delta, max, ramp;
59
60 if (dx == 0)
61 return 1024;
62
63 max = gradient->stops[n].color.red -
64 gradient->stops[n-1].color.red;
65 if (max < 0)
66 max = -max;
67
68 delta = gradient->stops[n].color.green -
69 gradient->stops[n-1].color.green;
70 if (delta < 0)
71 delta = -delta;
72 if (delta > max)
73 max = delta;
74
75 delta = gradient->stops[n].color.blue -
76 gradient->stops[n-1].color.blue;
77 if (delta < 0)
78 delta = -delta;
79 if (delta > max)
80 max = delta;
81
82 delta = gradient->stops[n].color.alpha -
83 gradient->stops[n-1].color.alpha;
84 if (delta < 0)
85 delta = -delta;
86 if (delta > max)
87 max = delta;
88
89 ramp = 256 * max / dx;
90 if (ramp > width)
91 width = ramp;
92 }
93
94 if (width == 0)
95 return 1;
96
97 width = (width + 7) & -8;
98 return min(width, 1024);
99 }
100
101 static bool
_gradient_color_stops_equal(PictGradient * pattern,struct sna_gradient_cache * cache)102 _gradient_color_stops_equal(PictGradient *pattern,
103 struct sna_gradient_cache *cache)
104 {
105 if (cache->nstops != pattern->nstops)
106 return false;
107
108 return memcmp(cache->stops,
109 pattern->stops,
110 sizeof(PictGradientStop)*cache->nstops) == 0;
111 }
112
113 struct kgem_bo *
sna_render_get_gradient(struct sna * sna,PictGradient * pattern)114 sna_render_get_gradient(struct sna *sna,
115 PictGradient *pattern)
116 {
117 struct sna_render *render = &sna->render;
118 struct sna_gradient_cache *cache;
119 pixman_image_t *gradient, *image;
120 pixman_point_fixed_t p1, p2;
121 int i, width;
122 struct kgem_bo *bo;
123
124 DBG(("%s: %dx[%f:%x ... %f:%x ... %f:%x]\n", __FUNCTION__,
125 pattern->nstops,
126 pattern->stops[0].x / 65536.,
127 pattern->stops[0].color.alpha >> 8 << 24 |
128 pattern->stops[0].color.red >> 8 << 16 |
129 pattern->stops[0].color.green >> 8 << 8 |
130 pattern->stops[0].color.blue >> 8 << 0,
131 pattern->stops[pattern->nstops/2].x / 65536.,
132 pattern->stops[pattern->nstops/2].color.alpha >> 8 << 24 |
133 pattern->stops[pattern->nstops/2].color.red >> 8 << 16 |
134 pattern->stops[pattern->nstops/2].color.green >> 8 << 8 |
135 pattern->stops[pattern->nstops/2].color.blue >> 8 << 0,
136 pattern->stops[pattern->nstops-1].x / 65536.,
137 pattern->stops[pattern->nstops-1].color.alpha >> 8 << 24 |
138 pattern->stops[pattern->nstops-1].color.red >> 8 << 16 |
139 pattern->stops[pattern->nstops-1].color.green >> 8 << 8 |
140 pattern->stops[pattern->nstops-1].color.blue >> 8 << 0));
141
142 for (i = 0; i < render->gradient_cache.size; i++) {
143 cache = &render->gradient_cache.cache[i];
144 if (_gradient_color_stops_equal(pattern, cache)) {
145 DBG(("%s: old --> %d\n", __FUNCTION__, i));
146 return kgem_bo_reference(cache->bo);
147 }
148 }
149
150 width = sna_gradient_sample_width(pattern);
151 DBG(("%s: sample width = %d\n", __FUNCTION__, width));
152 if (width == 0)
153 return NULL;
154
155 p1.x = 0;
156 p1.y = 0;
157 p2.x = width << 16;
158 p2.y = 0;
159
160 gradient = pixman_image_create_linear_gradient(&p1, &p2,
161 (pixman_gradient_stop_t *)pattern->stops,
162 pattern->nstops);
163 if (gradient == NULL)
164 return NULL;
165
166 pixman_image_set_filter(gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
167 pixman_image_set_repeat(gradient, PIXMAN_REPEAT_PAD);
168
169 image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, 1, NULL, 0);
170 if (image == NULL) {
171 pixman_image_unref(gradient);
172 return NULL;
173 }
174
175 pixman_image_composite(PIXMAN_OP_SRC,
176 gradient, NULL, image,
177 0, 0,
178 0, 0,
179 0, 0,
180 width, 1);
181 pixman_image_unref(gradient);
182
183 DBG(("%s: [0]=%x, [%d]=%x [%d]=%x\n", __FUNCTION__,
184 pixman_image_get_data(image)[0],
185 width/2, pixman_image_get_data(image)[width/2],
186 width-1, pixman_image_get_data(image)[width-1]));
187
188 bo = kgem_create_linear(&sna->kgem, width*4, 0);
189 if (!bo) {
190 pixman_image_unref(image);
191 return NULL;
192 }
193
194 bo->pitch = 4*width;
195 kgem_bo_write(&sna->kgem, bo, pixman_image_get_data(image), 4*width);
196
197 pixman_image_unref(image);
198
199 if (render->gradient_cache.size < GRADIENT_CACHE_SIZE)
200 i = render->gradient_cache.size++;
201 else
202 i = rand () % GRADIENT_CACHE_SIZE;
203
204 cache = &render->gradient_cache.cache[i];
205 if (cache->nstops < pattern->nstops) {
206 PictGradientStop *newstops;
207
208 newstops = malloc(sizeof(PictGradientStop) * pattern->nstops);
209 if (newstops == NULL)
210 return bo;
211
212 free(cache->stops);
213 cache->stops = newstops;
214 }
215
216 memcpy(cache->stops, pattern->stops,
217 sizeof(PictGradientStop) * pattern->nstops);
218 cache->nstops = pattern->nstops;
219
220 if (cache->bo)
221 kgem_bo_destroy(&sna->kgem, cache->bo);
222 cache->bo = kgem_bo_reference(bo);
223
224 return bo;
225 }
226
227 void
sna_render_flush_solid(struct sna * sna)228 sna_render_flush_solid(struct sna *sna)
229 {
230 struct sna_solid_cache *cache = &sna->render.solid_cache;
231
232 DBG(("sna_render_flush_solid(size=%d)\n", cache->size));
233 assert(cache->dirty);
234 assert(cache->size);
235 assert(cache->size <= 1024);
236
237 kgem_bo_write(&sna->kgem, cache->cache_bo,
238 cache->color, cache->size*sizeof(uint32_t));
239 cache->dirty = 0;
240 }
241
242 static void
sna_render_finish_solid(struct sna * sna,bool force)243 sna_render_finish_solid(struct sna *sna, bool force)
244 {
245 struct sna_solid_cache *cache = &sna->render.solid_cache;
246 struct kgem_bo *old;
247 int i;
248
249 DBG(("sna_render_finish_solid(force=%d, domain=%d, busy=%d, dirty=%d, size=%d)\n",
250 force, cache->cache_bo->domain, cache->cache_bo->rq != NULL, cache->dirty, cache->size));
251
252 if (!force && cache->cache_bo->domain != DOMAIN_GPU)
253 return;
254
255 if (cache->dirty)
256 sna_render_flush_solid(sna);
257
258 for (i = 0; i < cache->size; i++) {
259 if (cache->bo[i] == NULL)
260 continue;
261
262 kgem_bo_destroy(&sna->kgem, cache->bo[i]);
263 cache->bo[i] = NULL;
264 }
265
266 DBG(("sna_render_finish_solid reset\n"));
267 old = cache->cache_bo;
268 cache->cache_bo = kgem_create_linear(&sna->kgem, sizeof(cache->color), 0);
269 if (cache->cache_bo == NULL) {
270 cache->cache_bo = old;
271 old = NULL;
272 }
273
274 if (force)
275 cache->size = 0;
276 if (cache->last < cache->size) {
277 cache->bo[cache->last] = kgem_create_proxy(&sna->kgem, cache->cache_bo,
278 cache->last*sizeof(uint32_t), sizeof(uint32_t));
279 if (cache->bo[cache->last])
280 cache->bo[cache->last]->pitch = 4;
281 else
282 cache->last = 1024;
283 }
284
285 if (old)
286 kgem_bo_destroy(&sna->kgem, old);
287 }
288
289 struct kgem_bo *
sna_render_get_solid(struct sna * sna,uint32_t color)290 sna_render_get_solid(struct sna *sna, uint32_t color)
291 {
292 struct sna_solid_cache *cache = &sna->render.solid_cache;
293 int i;
294
295 DBG(("%s: %08x\n", __FUNCTION__, color));
296
297 if ((color & 0xffffff) == 0) /* alpha only */
298 return kgem_bo_reference(sna->render.alpha_cache.bo[color>>24]);
299
300 if (color == 0xffffffff) {
301 DBG(("%s(white)\n", __FUNCTION__));
302 return kgem_bo_reference(sna->render.alpha_cache.bo[255+7]);
303 }
304
305 if ((color >> 24) == 0xff) {
306 int v = 0;
307
308 if (((color >> 16) & 0xff) == 0)
309 v |= 0;
310 else if (((color >> 16) & 0xff) == 0xff)
311 v |= 1 << 2;
312 else
313 v = -1;
314
315 if (((color >> 8) & 0xff) == 0)
316 v |= 0;
317 else if (((color >> 8) & 0xff) == 0xff)
318 v |= 1 << 1;
319 else
320 v = -1;
321
322 if (((color >> 0) & 0xff) == 0)
323 v |= 0;
324 else if (((color >> 0) & 0xff) == 0xff)
325 v |= 1 << 0;
326 else
327 v = -1;
328
329 if (v >= 0) {
330 DBG(("%s(primary (%d,%d,%d): %d)\n",
331 __FUNCTION__, v & 4, v & 2, v & 1, v));
332 return kgem_bo_reference(sna->render.alpha_cache.bo[255+v]);
333 }
334 }
335
336 if (cache->color[cache->last] == color) {
337 DBG(("sna_render_get_solid(%d) = %x (last)\n",
338 cache->last, color));
339 return kgem_bo_reference(cache->bo[cache->last]);
340 }
341
342 for (i = 0; i < cache->size; i++) {
343 if (cache->color[i] == color) {
344 if (cache->bo[i] == NULL) {
345 DBG(("sna_render_get_solid(%d) = %x (recreate)\n",
346 i, color));
347 goto create;
348 } else {
349 DBG(("sna_render_get_solid(%d) = %x (old)\n",
350 i, color));
351 goto done;
352 }
353 }
354 }
355
356 sna_render_finish_solid(sna, i == ARRAY_SIZE(cache->color));
357
358 i = cache->size++;
359 assert(i < ARRAY_SIZE(cache->color));
360 cache->color[i] = color;
361 cache->dirty = 1;
362 DBG(("sna_render_get_solid(%d) = %x (new)\n", i, color));
363
364 create:
365 cache->bo[i] = kgem_create_proxy(&sna->kgem, cache->cache_bo,
366 i*sizeof(uint32_t), sizeof(uint32_t));
367 cache->bo[i]->pitch = 4;
368
369 done:
370 cache->last = i;
371 return kgem_bo_reference(cache->bo[i]);
372 }
373
sna_alpha_cache_init(struct sna * sna)374 static bool sna_alpha_cache_init(struct sna *sna)
375 {
376 struct sna_alpha_cache *cache = &sna->render.alpha_cache;
377 uint32_t color[256 + 7];
378 int i;
379
380 DBG(("%s\n", __FUNCTION__));
381
382 cache->cache_bo = kgem_create_linear(&sna->kgem, sizeof(color), 0);
383 if (!cache->cache_bo)
384 return false;
385
386 for (i = 0; i < 256; i++) {
387 color[i] = i << 24;
388 cache->bo[i] = kgem_create_proxy(&sna->kgem,
389 cache->cache_bo,
390 sizeof(uint32_t)*i,
391 sizeof(uint32_t));
392 if (cache->bo[i] == NULL)
393 return false;
394
395 cache->bo[i]->pitch = 4;
396 }
397
398 /* primary */
399 for (i = 1; i < 8; i++) {
400 int j = 255+i;
401
402 color[j] = 0xff << 24;
403 if (i & 1)
404 color[j] |= 0xff << 0;
405 if (i & 2)
406 color[j] |= 0xff << 8;
407 if (i & 4)
408 color[j] |= 0xff << 16;
409 cache->bo[j] = kgem_create_proxy(&sna->kgem,
410 cache->cache_bo,
411 sizeof(uint32_t)*j,
412 sizeof(uint32_t));
413 if (cache->bo[j] == NULL)
414 return false;
415
416 cache->bo[j]->pitch = 4;
417 }
418
419 return kgem_bo_write(&sna->kgem, cache->cache_bo, color, sizeof(color));
420 }
421
sna_solid_cache_init(struct sna * sna)422 static bool sna_solid_cache_init(struct sna *sna)
423 {
424 struct sna_solid_cache *cache = &sna->render.solid_cache;
425
426 DBG(("%s\n", __FUNCTION__));
427
428 cache->cache_bo =
429 kgem_create_linear(&sna->kgem, 4096, 0);
430 if (!cache->cache_bo)
431 return false;
432
433 cache->last = 0;
434 cache->color[cache->last] = 0;
435 cache->dirty = 0;
436 cache->size = 0;
437
438 return true;
439 }
440
sna_gradients_create(struct sna * sna)441 bool sna_gradients_create(struct sna *sna)
442 {
443 DBG(("%s\n", __FUNCTION__));
444
445 if (unlikely(sna->kgem.wedged))
446 return true;
447
448 if (!sna_alpha_cache_init(sna))
449 return false;
450
451 if (!sna_solid_cache_init(sna))
452 return false;
453
454 return true;
455 }
456
sna_gradients_close(struct sna * sna)457 void sna_gradients_close(struct sna *sna)
458 {
459 int i;
460
461 DBG(("%s\n", __FUNCTION__));
462
463 for (i = 0; i < 256; i++) {
464 if (sna->render.alpha_cache.bo[i]) {
465 kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.bo[i]);
466 sna->render.alpha_cache.bo[i] = NULL;
467 }
468 }
469 if (sna->render.alpha_cache.cache_bo) {
470 kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.cache_bo);
471 sna->render.alpha_cache.cache_bo = NULL;
472 }
473
474 if (sna->render.solid_cache.cache_bo)
475 kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.cache_bo);
476 for (i = 0; i < sna->render.solid_cache.size; i++) {
477 if (sna->render.solid_cache.bo[i])
478 kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.bo[i]);
479 }
480 sna->render.solid_cache.cache_bo = 0;
481 sna->render.solid_cache.size = 0;
482 sna->render.solid_cache.dirty = 0;
483
484 for (i = 0; i < sna->render.gradient_cache.size; i++) {
485 struct sna_gradient_cache *cache =
486 &sna->render.gradient_cache.cache[i];
487
488 if (cache->bo)
489 kgem_bo_destroy(&sna->kgem, cache->bo);
490
491 free(cache->stops);
492 cache->stops = NULL;
493 cache->nstops = 0;
494 }
495 sna->render.gradient_cache.size = 0;
496 }
497