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, 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 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 == 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 364create: 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 369done: 370 cache->last = i; 371 return kgem_bo_reference(cache->bo[i]); 372} 373 374static 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 422static 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 441bool 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 457void 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