sna_gradient.c revision 03b705cf
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 37bool 38sna_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 50static int 51sna_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 101static bool 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 113struct kgem_bo * 114sna_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 227void 228sna_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 242static void 243sna_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)\n", 250 force, cache->cache_bo->domain, cache->cache_bo->rq != NULL, cache->dirty)); 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 289struct kgem_bo * 290sna_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 == 1024); 357 358 i = cache->size++; 359 cache->color[i] = color; 360 cache->dirty = 1; 361 DBG(("sna_render_get_solid(%d) = %x (new)\n", i, color)); 362 363create: 364 cache->bo[i] = kgem_create_proxy(&sna->kgem, cache->cache_bo, 365 i*sizeof(uint32_t), sizeof(uint32_t)); 366 cache->bo[i]->pitch = 4; 367 368done: 369 cache->last = i; 370 return kgem_bo_reference(cache->bo[i]); 371} 372 373static bool sna_alpha_cache_init(struct sna *sna) 374{ 375 struct sna_alpha_cache *cache = &sna->render.alpha_cache; 376 uint32_t color[256 + 7]; 377 int i; 378 379 DBG(("%s\n", __FUNCTION__)); 380 381 cache->cache_bo = kgem_create_linear(&sna->kgem, sizeof(color), 0); 382 if (!cache->cache_bo) 383 return false; 384 385 for (i = 0; i < 256; i++) { 386 color[i] = i << 24; 387 cache->bo[i] = kgem_create_proxy(&sna->kgem, 388 cache->cache_bo, 389 sizeof(uint32_t)*i, 390 sizeof(uint32_t)); 391 if (cache->bo[i] == NULL) 392 return false; 393 394 cache->bo[i]->pitch = 4; 395 } 396 397 /* primary */ 398 for (i = 1; i < 8; i++) { 399 int j = 255+i; 400 401 color[j] = 0xff << 24; 402 if (i & 1) 403 color[j] |= 0xff << 0; 404 if (i & 2) 405 color[j] |= 0xff << 8; 406 if (i & 4) 407 color[j] |= 0xff << 16; 408 cache->bo[j] = kgem_create_proxy(&sna->kgem, 409 cache->cache_bo, 410 sizeof(uint32_t)*j, 411 sizeof(uint32_t)); 412 if (cache->bo[j] == NULL) 413 return false; 414 415 cache->bo[j]->pitch = 4; 416 } 417 418 return kgem_bo_write(&sna->kgem, cache->cache_bo, color, sizeof(color)); 419} 420 421static bool sna_solid_cache_init(struct sna *sna) 422{ 423 struct sna_solid_cache *cache = &sna->render.solid_cache; 424 425 DBG(("%s\n", __FUNCTION__)); 426 427 cache->cache_bo = 428 kgem_create_linear(&sna->kgem, 4096, 0); 429 if (!cache->cache_bo) 430 return false; 431 432 cache->last = 1024; 433 cache->color[cache->last] = 0; 434 cache->dirty = 0; 435 cache->size = 0; 436 437 return true; 438} 439 440bool sna_gradients_create(struct sna *sna) 441{ 442 DBG(("%s\n", __FUNCTION__)); 443 444 if (unlikely(sna->kgem.wedged)) 445 return true; 446 447 if (!sna_alpha_cache_init(sna)) 448 return false; 449 450 if (!sna_solid_cache_init(sna)) 451 return false; 452 453 return true; 454} 455 456void sna_gradients_close(struct sna *sna) 457{ 458 int i; 459 460 DBG(("%s\n", __FUNCTION__)); 461 462 for (i = 0; i < 256; i++) { 463 if (sna->render.alpha_cache.bo[i]) { 464 kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.bo[i]); 465 sna->render.alpha_cache.bo[i] = NULL; 466 } 467 } 468 if (sna->render.alpha_cache.cache_bo) { 469 kgem_bo_destroy(&sna->kgem, sna->render.alpha_cache.cache_bo); 470 sna->render.alpha_cache.cache_bo = NULL; 471 } 472 473 if (sna->render.solid_cache.cache_bo) 474 kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.cache_bo); 475 for (i = 0; i < sna->render.solid_cache.size; i++) { 476 if (sna->render.solid_cache.bo[i]) 477 kgem_bo_destroy(&sna->kgem, sna->render.solid_cache.bo[i]); 478 } 479 sna->render.solid_cache.cache_bo = 0; 480 sna->render.solid_cache.size = 0; 481 sna->render.solid_cache.dirty = 0; 482 483 for (i = 0; i < sna->render.gradient_cache.size; i++) { 484 struct sna_gradient_cache *cache = 485 &sna->render.gradient_cache.cache[i]; 486 487 if (cache->bo) 488 kgem_bo_destroy(&sna->kgem, cache->bo); 489 490 free(cache->stops); 491 cache->stops = NULL; 492 cache->nstops = 0; 493 } 494 sna->render.gradient_cache.size = 0; 495} 496