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