1d514b0f3Smrg/* 2d514b0f3Smrg * Copyright 2010 Red Hat, Inc. 3d514b0f3Smrg * 4d514b0f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5d514b0f3Smrg * copy of this software and associated documentation files (the "Software"), 6d514b0f3Smrg * to deal in the Software without restriction, including without limitation 7d514b0f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 8d514b0f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom 9d514b0f3Smrg * the Software is furnished to do so, subject to the following conditions: 10d514b0f3Smrg * 11d514b0f3Smrg * The above copyright notice and this permission notice (including the next 12d514b0f3Smrg * paragraph) shall be included in all copies or substantial portions of the 13d514b0f3Smrg * Software. 14d514b0f3Smrg * 15d514b0f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16d514b0f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17d514b0f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18d514b0f3Smrg * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19d514b0f3Smrg * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20d514b0f3Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21d514b0f3Smrg */ 22d514b0f3Smrg 23d514b0f3Smrg/* The life cycle of surfaces 24d514b0f3Smrg * 25d514b0f3Smrg * free => live => dead => destroyed => free 26d514b0f3Smrg * 27d514b0f3Smrg * A 'free' surface is one that is not allocated on the device. These 28d514b0f3Smrg * are stored on the 'free_surfaces' list. 29d514b0f3Smrg * 30d514b0f3Smrg * A 'live' surface is one that the X server is using for something. It 31d514b0f3Smrg * has an associated pixmap. It is allocated in the device. These are stored on 32d514b0f3Smrg * the "live_surfaces" list. 33d514b0f3Smrg * 34d514b0f3Smrg * A 'dead' surface is one that the X server is no using any more, but 35d514b0f3Smrg * is still allocated in the device. These surfaces may be stored in the 36d514b0f3Smrg * cache, from where they can be resurrected. The cache holds a ref on the 37d514b0f3Smrg * surfaces. 38d514b0f3Smrg * 39d514b0f3Smrg * A 'destroyed' surface is one whose ref count has reached 0. It is no 40d514b0f3Smrg * longer being referenced by either the server or the device or the cache. 41d514b0f3Smrg * When a surface enters this state, the associated pixman images are freed, and 42d514b0f3Smrg * a destroy command is sent. This will eventually trigger a 'recycle' call, 43d514b0f3Smrg * which puts the surface into the 'free' state. 44d514b0f3Smrg * 45d514b0f3Smrg */ 46d514b0f3Smrg#ifdef HAVE_CONFIG_H 47d514b0f3Smrg#include "config.h" 48d514b0f3Smrg#endif 49d514b0f3Smrg 50d514b0f3Smrg#include "qxl.h" 51d514b0f3Smrg#include "qxl_surface.h" 52d514b0f3Smrg#ifdef DEBUG_SURFACE_LIFECYCLE 53d514b0f3Smrg#include <stdio.h> 54d514b0f3Smrg 55d514b0f3Smrgstatic FILE* surface_log; 56d514b0f3Smrg#endif 57d514b0f3Smrg 58d514b0f3Smrgtypedef struct evacuated_surface_t evacuated_surface_t; 59d514b0f3Smrg 60d514b0f3Smrgstruct evacuated_surface_t 61d514b0f3Smrg{ 62d514b0f3Smrg pixman_image_t *image; 63d514b0f3Smrg PixmapPtr pixmap; 64d514b0f3Smrg int bpp; 65d514b0f3Smrg 66d514b0f3Smrg evacuated_surface_t *prev; 67d514b0f3Smrg evacuated_surface_t *next; 68d514b0f3Smrg}; 69d514b0f3Smrg 70d514b0f3Smrg#define N_CACHED_SURFACES 64 71d514b0f3Smrg 72d514b0f3Smrg/* 73d514b0f3Smrg * Surface cache 74d514b0f3Smrg */ 75d514b0f3Smrgstruct surface_cache_t 76d514b0f3Smrg{ 77d514b0f3Smrg qxl_screen_t *qxl; 78d514b0f3Smrg 79d514b0f3Smrg /* Array of surfaces (not a linked list). 80d514b0f3Smrg * All surfaces, excluding the primary one, indexed by surface id. 81d514b0f3Smrg */ 82d514b0f3Smrg qxl_surface_t *all_surfaces; 83d514b0f3Smrg 84d514b0f3Smrg /* All surfaces that the driver is currently using (linked through next/prev) */ 85d514b0f3Smrg qxl_surface_t *live_surfaces; 86d514b0f3Smrg 87d514b0f3Smrg /* All surfaces that need to be allocated (linked through next, but not prev) */ 88d514b0f3Smrg qxl_surface_t *free_surfaces; 89d514b0f3Smrg 90d514b0f3Smrg /* Surfaces that are already allocated, but not in used by the driver, 91d514b0f3Smrg * linked through next 92d514b0f3Smrg */ 93d514b0f3Smrg qxl_surface_t *cached_surfaces[N_CACHED_SURFACES]; 94d514b0f3Smrg}; 95d514b0f3Smrg 96d514b0f3Smrg#ifdef DEBUG_SURFACE_LIFECYCLE 97d514b0f3Smrgstatic void debug_surface_open(void) 98d514b0f3Smrg{ 99d514b0f3Smrg if (surface_log) 100d514b0f3Smrg return; 101d514b0f3Smrg surface_log = fopen("/tmp/xf86-video-qxl.surface.log", "w+"); 102d514b0f3Smrg if (!surface_log) 103d514b0f3Smrg { 104d514b0f3Smrg fprintf(stderr, "error creating surface log file (DEBUG_SURFACE_LIFECYCLE)\n"); 105d514b0f3Smrg exit(-1); 106d514b0f3Smrg } 107d514b0f3Smrg} 108d514b0f3Smrg 109d514b0f3Smrgstatic int surface_count(qxl_surface_t *surface) 110d514b0f3Smrg{ 111d514b0f3Smrg int i; 112d514b0f3Smrg 113d514b0f3Smrg for (i = 0; surface ;++i, surface = surface->next); 114d514b0f3Smrg return i; 115d514b0f3Smrg} 116d514b0f3Smrg 117d514b0f3Smrgstatic void debug_surface_log(surface_cache_t *cache) 118d514b0f3Smrg{ 119d514b0f3Smrg int live_n, free_n; 120d514b0f3Smrg 121d514b0f3Smrg debug_surface_open(); 122d514b0f3Smrg live_n = surface_count(cache->live_surfaces); 123d514b0f3Smrg free_n = surface_count(cache->free_surfaces); 124d514b0f3Smrg fprintf(surface_log, "live,free,sum = %d, %d, %d\n", live_n, free_n, 125d514b0f3Smrg live_n + free_n); 126d514b0f3Smrg fflush(surface_log); 127d514b0f3Smrg} 128d514b0f3Smrg 129d514b0f3Smrg#else 130d514b0f3Smrg#define debug_surface_log(cache) 131d514b0f3Smrg#endif 132d514b0f3Smrg 133d514b0f3Smrg 134d514b0f3Smrgstatic Bool 135d514b0f3Smrgsurface_cache_init (surface_cache_t *cache, qxl_screen_t *qxl) 136d514b0f3Smrg{ 137d514b0f3Smrg int n_surfaces = qxl->rom->n_surfaces; 138d514b0f3Smrg int i; 139d514b0f3Smrg 140d514b0f3Smrg if (!cache->all_surfaces) 141d514b0f3Smrg { 142d514b0f3Smrg /* all_surfaces is not freed when evacuating, since surfaces are still 143d514b0f3Smrg * tied to pixmaps that may be destroyed after evacuation before 144d514b0f3Smrg * recreation */ 145d514b0f3Smrg cache->all_surfaces = calloc (n_surfaces, sizeof (qxl_surface_t)); 146d514b0f3Smrg if (!cache->all_surfaces) 147d514b0f3Smrg return FALSE; 148d514b0f3Smrg } 149d514b0f3Smrg 150d514b0f3Smrg memset (cache->all_surfaces, 0, n_surfaces * sizeof (qxl_surface_t)); 151d514b0f3Smrg memset (cache->cached_surfaces, 0, N_CACHED_SURFACES * sizeof (qxl_surface_t *)); 152d514b0f3Smrg 153d514b0f3Smrg cache->free_surfaces = NULL; 154d514b0f3Smrg cache->live_surfaces = NULL; 155d514b0f3Smrg 156d514b0f3Smrg for (i = 0; i < n_surfaces; ++i) 157d514b0f3Smrg { 158d514b0f3Smrg cache->all_surfaces[i].id = i; 159d514b0f3Smrg cache->all_surfaces[i].cache = cache; 160d514b0f3Smrg cache->all_surfaces[i].qxl = qxl; 161d514b0f3Smrg cache->all_surfaces[i].dev_image = NULL; 162d514b0f3Smrg cache->all_surfaces[i].host_image = NULL; 163d514b0f3Smrg cache->all_surfaces[i].evacuated = NULL; 164d514b0f3Smrg 165d514b0f3Smrg REGION_INIT ( 166d514b0f3Smrg NULL, &(cache->all_surfaces[i].access_region), (BoxPtr)NULL, 0); 167d514b0f3Smrg cache->all_surfaces[i].access_type = UXA_ACCESS_RO; 168d514b0f3Smrg 169d514b0f3Smrg if (i) /* surface 0 is the primary surface */ 170d514b0f3Smrg { 171d514b0f3Smrg cache->all_surfaces[i].next = cache->free_surfaces; 172d514b0f3Smrg cache->free_surfaces = &(cache->all_surfaces[i]); 173d514b0f3Smrg cache->all_surfaces[i].in_use = FALSE; 174d514b0f3Smrg } 175d514b0f3Smrg } 176d514b0f3Smrg 177d514b0f3Smrg return TRUE; 178d514b0f3Smrg} 179d514b0f3Smrg 180d514b0f3Smrgsurface_cache_t * 181d514b0f3Smrgqxl_surface_cache_create (qxl_screen_t *qxl) 182d514b0f3Smrg{ 183d514b0f3Smrg surface_cache_t *cache = malloc (sizeof *cache); 184d514b0f3Smrg 185d514b0f3Smrg if (!cache) 186d514b0f3Smrg return NULL; 187d514b0f3Smrg 188d514b0f3Smrg memset(cache, 0, sizeof(*cache)); 189d514b0f3Smrg cache->qxl = qxl; 190d514b0f3Smrg if (!surface_cache_init (cache, qxl)) 191d514b0f3Smrg { 192d514b0f3Smrg free (cache); 193d514b0f3Smrg return NULL; 194d514b0f3Smrg } 195d514b0f3Smrg 196d514b0f3Smrg return cache; 197d514b0f3Smrg} 198d514b0f3Smrg 199d514b0f3Smrgvoid 200d514b0f3Smrgqxl_surface_cache_sanity_check (surface_cache_t *qxl) 201d514b0f3Smrg{ 202d514b0f3Smrg#if 0 203d514b0f3Smrg qxl_surface_t *s; 204d514b0f3Smrg 205d514b0f3Smrg for (s = qxl->live_surfaces; s != NULL; s = s->next) 206d514b0f3Smrg { 207d514b0f3Smrg PixmapPtr pixmap = s->pixmap; 208d514b0f3Smrg 209d514b0f3Smrg if (! (get_surface (pixmap) == s) ) 210d514b0f3Smrg { 211d514b0f3Smrg ErrorF ("Surface %p has pixmap %p, but pixmap %p has surface %p\n", 212d514b0f3Smrg s, pixmap, pixmap, get_surface (pixmap)); 213d514b0f3Smrg 214d514b0f3Smrg assert (0); 215d514b0f3Smrg } 216d514b0f3Smrg } 217d514b0f3Smrg#endif 218d514b0f3Smrg} 219d514b0f3Smrg 220d514b0f3Smrgstatic void 221d514b0f3Smrgprint_cache_info (surface_cache_t *cache) 222d514b0f3Smrg{ 223d514b0f3Smrg int i; 224d514b0f3Smrg int n_surfaces = 0; 225d514b0f3Smrg 226d514b0f3Smrg ErrorF ("Cache contents: "); 227d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 228d514b0f3Smrg { 229d514b0f3Smrg if (cache->cached_surfaces[i]) 230d514b0f3Smrg { 231d514b0f3Smrg ErrorF ("%4d ", cache->cached_surfaces[i]->id); 232d514b0f3Smrg n_surfaces++; 233d514b0f3Smrg } 234d514b0f3Smrg else 235d514b0f3Smrg ErrorF ("null "); 236d514b0f3Smrg } 237d514b0f3Smrg 238d514b0f3Smrg ErrorF (" total: %d\n", n_surfaces); 239d514b0f3Smrg} 240d514b0f3Smrg 241d514b0f3Smrgstatic qxl_surface_t * 242d514b0f3Smrgsurface_get_from_cache (surface_cache_t *cache, int width, int height, int bpp) 243d514b0f3Smrg{ 244d514b0f3Smrg int i; 245d514b0f3Smrg 246d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 247d514b0f3Smrg { 248d514b0f3Smrg qxl_surface_t *s = cache->cached_surfaces[i]; 249d514b0f3Smrg 250d514b0f3Smrg if (s && bpp == s->bpp) 251d514b0f3Smrg { 252d514b0f3Smrg int w = pixman_image_get_width (s->host_image); 253d514b0f3Smrg int h = pixman_image_get_height (s->host_image); 254d514b0f3Smrg 255d514b0f3Smrg if (width <= w && width * 4 > w && height <= h && height * 4 > h) 256d514b0f3Smrg { 257d514b0f3Smrg cache->cached_surfaces[i] = NULL; 258d514b0f3Smrg 259d514b0f3Smrg return s; 260d514b0f3Smrg } 261d514b0f3Smrg } 262d514b0f3Smrg } 263d514b0f3Smrg 264d514b0f3Smrg return NULL; 265d514b0f3Smrg} 266d514b0f3Smrg 267d514b0f3Smrgstatic int n_live; 268d514b0f3Smrg 269d514b0f3Smrgvoid 270d514b0f3Smrgqxl_surface_recycle (surface_cache_t *cache, uint32_t id) 271d514b0f3Smrg{ 272d514b0f3Smrg qxl_surface_t *surface = cache->all_surfaces + id; 273d514b0f3Smrg 274d514b0f3Smrg n_live--; 275d514b0f3Smrg if (surface->bo) 276d514b0f3Smrg cache->qxl->bo_funcs->bo_decref (cache->qxl, surface->bo); 277d514b0f3Smrg surface->bo = NULL; 278d514b0f3Smrg surface->next = cache->free_surfaces; 279d514b0f3Smrg cache->free_surfaces = surface; 280d514b0f3Smrg} 281d514b0f3Smrg 282d514b0f3Smrg/* 283d514b0f3Smrg * mode is used for the whole virtual screen, not for a specific head. 284d514b0f3Smrg * For a single head where virtual size is equal to the head size, they are 285d514b0f3Smrg * equal. For multiple heads this mode will not match any existing heads and 286d514b0f3Smrg * will be the containing virtual size. 287d514b0f3Smrg */ 288d514b0f3Smrgqxl_surface_t * 289d514b0f3Smrgqxl_surface_cache_create_primary (qxl_screen_t *qxl, 290d514b0f3Smrg struct QXLMode *mode) 291d514b0f3Smrg{ 292d514b0f3Smrg pixman_format_code_t format; 293d514b0f3Smrg uint8_t *dev_addr; 294d514b0f3Smrg pixman_image_t *dev_image, *host_image; 295d514b0f3Smrg qxl_surface_t *surface; 296d514b0f3Smrg surface_cache_t *cache = qxl->surface_cache; 297d514b0f3Smrg struct qxl_bo *bo; 298d514b0f3Smrg 299d514b0f3Smrg if (mode->bits == 16) 300d514b0f3Smrg { 301d514b0f3Smrg format = PIXMAN_x1r5g5b5; 302d514b0f3Smrg } 303d514b0f3Smrg else if (mode->bits == 32) 304d514b0f3Smrg { 305d514b0f3Smrg format = PIXMAN_x8r8g8b8; 306d514b0f3Smrg } 307d514b0f3Smrg else 308d514b0f3Smrg { 309d514b0f3Smrg xf86DrvMsg (qxl->pScrn->scrnIndex, X_ERROR, 310d514b0f3Smrg "Unknown bit depth %d\n", mode->bits); 311d514b0f3Smrg return NULL; 312d514b0f3Smrg } 313d514b0f3Smrg 314d514b0f3Smrg bo = qxl->bo_funcs->create_primary(qxl, mode->x_res, mode->y_res, mode->stride, mode->bits); 315d514b0f3Smrg 316d514b0f3Smrg dev_addr = qxl->bo_funcs->bo_map(bo); 317d514b0f3Smrg dev_image = pixman_image_create_bits (format, mode->x_res, mode->y_res, 318d514b0f3Smrg (uint32_t *)dev_addr, (qxl->kms_enabled ? mode->stride : -mode->stride)); 319d514b0f3Smrg 320d514b0f3Smrg host_image = pixman_image_create_bits (format, 321d514b0f3Smrg qxl->virtual_x, qxl->virtual_y, 322d514b0f3Smrg NULL, mode->stride); 323d514b0f3Smrg#if 0 324d514b0f3Smrg xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, 325d514b0f3Smrg "testing dev_image memory (%d x %d)\n", 326d514b0f3Smrg mode->x_res, mode->y_res); 327d514b0f3Smrg memset(qxl->ram, 0, mode->stride * mode->y_res); 328d514b0f3Smrg xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, 329d514b0f3Smrg "testing host_image memory\n"); 330d514b0f3Smrg memset(qxl->fb, 0, mode->stride * mode->y_res); 331d514b0f3Smrg#endif 332d514b0f3Smrg 333d514b0f3Smrg surface = malloc (sizeof *surface); 334d514b0f3Smrg surface->id = 0; 335d514b0f3Smrg surface->dev_image = dev_image; 336d514b0f3Smrg surface->host_image = host_image; 337d514b0f3Smrg surface->cache = cache; 338d514b0f3Smrg surface->qxl = qxl; 339d514b0f3Smrg surface->bpp = mode->bits; 340d514b0f3Smrg surface->next = NULL; 341d514b0f3Smrg surface->prev = NULL; 342d514b0f3Smrg surface->evacuated = NULL; 343d514b0f3Smrg surface->bo = bo; 344d514b0f3Smrg surface->image_bo = NULL; 345d514b0f3Smrg 346d514b0f3Smrg REGION_INIT (NULL, &(surface->access_region), (BoxPtr)NULL, 0); 347d514b0f3Smrg surface->access_type = UXA_ACCESS_RO; 348d514b0f3Smrg 349d514b0f3Smrg return surface; 350d514b0f3Smrg} 351d514b0f3Smrg 352d514b0f3Smrgvoid * 353d514b0f3Smrgqxl_surface_get_host_bits(qxl_surface_t *surface) 354d514b0f3Smrg{ 355d514b0f3Smrg if (!surface) 356d514b0f3Smrg return NULL; 357d514b0f3Smrg return (void *) pixman_image_get_data(surface->host_image); 358d514b0f3Smrg} 359d514b0f3Smrg 360d514b0f3Smrg 361d514b0f3Smrg 362d514b0f3Smrg 363d514b0f3Smrgstatic struct qxl_bo * 364d514b0f3Smrgmake_surface_cmd (surface_cache_t *cache, uint32_t id, QXLSurfaceCmdType type) 365d514b0f3Smrg{ 366d514b0f3Smrg struct qxl_bo *cmd_bo; 367d514b0f3Smrg struct QXLSurfaceCmd *cmd; 368d514b0f3Smrg qxl_screen_t *qxl = cache->qxl; 369d514b0f3Smrg 370d514b0f3Smrg cmd_bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof *cmd, "surface command"); 371d514b0f3Smrg cmd = qxl->bo_funcs->bo_map(cmd_bo); 372d514b0f3Smrg 373d514b0f3Smrg cmd->release_info.id = pointer_to_u64 (cmd_bo) | 2; 374d514b0f3Smrg cmd->type = type; 375d514b0f3Smrg cmd->flags = 0; 376d514b0f3Smrg cmd->surface_id = id; 377d514b0f3Smrg 378d514b0f3Smrg qxl->bo_funcs->bo_unmap(cmd_bo); 379d514b0f3Smrg return cmd_bo; 380d514b0f3Smrg} 381d514b0f3Smrg 382d514b0f3Smrgstatic void 383d514b0f3Smrgpush_surface_cmd (surface_cache_t *cache, struct qxl_bo *cmd_bo) 384d514b0f3Smrg{ 385d514b0f3Smrg qxl_screen_t *qxl = cache->qxl; 386d514b0f3Smrg 387d514b0f3Smrg qxl->bo_funcs->write_command (qxl, QXL_CMD_SURFACE, cmd_bo); 388d514b0f3Smrg} 389d514b0f3Smrg 390d514b0f3Smrg 391d514b0f3Smrgstatic qxl_surface_t * 392d514b0f3Smrgsurface_get_from_free_list (surface_cache_t *cache) 393d514b0f3Smrg{ 394d514b0f3Smrg qxl_surface_t *result = NULL; 395d514b0f3Smrg 396d514b0f3Smrg if (cache->free_surfaces) 397d514b0f3Smrg { 398d514b0f3Smrg qxl_surface_t *s; 399d514b0f3Smrg 400d514b0f3Smrg result = cache->free_surfaces; 401d514b0f3Smrg cache->free_surfaces = cache->free_surfaces->next; 402d514b0f3Smrg 403d514b0f3Smrg result->next = NULL; 404d514b0f3Smrg result->in_use = TRUE; 405d514b0f3Smrg result->ref_count = 1; 406d514b0f3Smrg result->pixmap = NULL; 407d514b0f3Smrg 408d514b0f3Smrg for (s = cache->free_surfaces; s; s = s->next) 409d514b0f3Smrg { 410d514b0f3Smrg if (s->id == result->id) 411d514b0f3Smrg ErrorF ("huh: %d to be returned, but %d is in list\n", 412d514b0f3Smrg s->id, result->id); 413d514b0f3Smrg 414d514b0f3Smrg assert (s->id != result->id); 415d514b0f3Smrg } 416d514b0f3Smrg } 417d514b0f3Smrg 418d514b0f3Smrg return result; 419d514b0f3Smrg} 420d514b0f3Smrg 421d514b0f3Smrgstatic int 422d514b0f3Smrgalign (int x) 423d514b0f3Smrg{ 424d514b0f3Smrg return x; 425d514b0f3Smrg} 426d514b0f3Smrg 427d514b0f3Smrgstatic qxl_surface_t * 428d514b0f3Smrgsurface_send_create (surface_cache_t *cache, 429d514b0f3Smrg int width, 430d514b0f3Smrg int height, 431d514b0f3Smrg int bpp) 432d514b0f3Smrg{ 433d514b0f3Smrg SpiceSurfaceFmt format; 434d514b0f3Smrg pixman_format_code_t pformat; 435d514b0f3Smrg struct QXLSurfaceCmd *cmd; 436d514b0f3Smrg int stride; 437d514b0f3Smrg uint32_t *dev_addr; 438d514b0f3Smrg int n_attempts = 0; 439d514b0f3Smrg qxl_screen_t *qxl = cache->qxl; 440d514b0f3Smrg qxl_surface_t *surface; 441d514b0f3Smrg struct qxl_bo *bo, *cmd_bo; 442d514b0f3Smrg void *dev_ptr; 443d514b0f3Smrg qxl_get_formats (bpp, &format, &pformat); 444d514b0f3Smrg 445d514b0f3Smrg width = align (width); 446d514b0f3Smrg height = align (height); 447d514b0f3Smrg 448d514b0f3Smrg stride = width * PIXMAN_FORMAT_BPP (pformat) / 8; 449d514b0f3Smrg stride = (stride + 3) & ~3; 450d514b0f3Smrg 451d514b0f3Smrg /* the final + stride is to work around a bug where the device apparently 452d514b0f3Smrg * scribbles after the end of the image 453d514b0f3Smrg */ 454d514b0f3Smrg qxl_garbage_collect (qxl); 455d514b0f3Smrgretry2: 456d514b0f3Smrg bo = qxl_ums_surf_mem_alloc(qxl, stride * height + stride); 457d514b0f3Smrg 458d514b0f3Smrg if (!bo) 459d514b0f3Smrg { 460d514b0f3Smrg ErrorF ("- %dth attempt\n", n_attempts++); 461d514b0f3Smrg 462d514b0f3Smrg if (qxl_garbage_collect (qxl)) 463d514b0f3Smrg goto retry2; 464d514b0f3Smrg 465d514b0f3Smrg ErrorF ("- OOM at %d %d %d (= %d bytes)\n", width, height, bpp, width * height * (bpp / 8)); 466d514b0f3Smrg print_cache_info (cache); 467d514b0f3Smrg 468d514b0f3Smrg if (qxl_handle_oom (qxl)) 469d514b0f3Smrg { 470d514b0f3Smrg while (qxl_garbage_collect (qxl)) 471d514b0f3Smrg ; 472d514b0f3Smrg goto retry2; 473d514b0f3Smrg } 474d514b0f3Smrg 475d514b0f3Smrg ErrorF ("Out of video memory: Could not allocate %d bytes\n", 476d514b0f3Smrg stride * height + stride); 477d514b0f3Smrg 478d514b0f3Smrg return NULL; 479d514b0f3Smrg } 480d514b0f3Smrg 481d514b0f3Smrgretry: 482d514b0f3Smrg surface = surface_get_from_free_list (cache); 483d514b0f3Smrg if (!surface) 484d514b0f3Smrg { 485d514b0f3Smrg if (!qxl_handle_oom (cache->qxl)) 486d514b0f3Smrg { 487d514b0f3Smrg ErrorF (" Out of surfaces\n"); 488d514b0f3Smrg qxl->bo_funcs->bo_decref (qxl, bo); 489d514b0f3Smrg return NULL; 490d514b0f3Smrg } 491d514b0f3Smrg else 492d514b0f3Smrg goto retry; 493d514b0f3Smrg } 494d514b0f3Smrg 495d514b0f3Smrg surface->bo = bo; 496d514b0f3Smrg 497d514b0f3Smrg cmd_bo = make_surface_cmd (cache, surface->id, QXL_SURFACE_CMD_CREATE); 498d514b0f3Smrg 499d514b0f3Smrg cmd = qxl->bo_funcs->bo_map(cmd_bo); 500d514b0f3Smrg cmd->u.surface_create.format = format; 501d514b0f3Smrg cmd->u.surface_create.width = width; 502d514b0f3Smrg cmd->u.surface_create.height = height; 503d514b0f3Smrg cmd->u.surface_create.stride = - stride; 504d514b0f3Smrg qxl->bo_funcs->bo_unmap(cmd_bo); 505d514b0f3Smrg 506d514b0f3Smrg qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(struct QXLSurfaceCmd, u.surface_create.data), cmd_bo, surface->bo); 507d514b0f3Smrg 508d514b0f3Smrg push_surface_cmd (cache, cmd_bo); 509d514b0f3Smrg 510d514b0f3Smrg dev_ptr = qxl->bo_funcs->bo_map(surface->bo); 511d514b0f3Smrg dev_addr 512d514b0f3Smrg = (uint32_t *)((uint8_t *)dev_ptr + stride * (height - 1)); 513d514b0f3Smrg 514d514b0f3Smrg surface->dev_image = pixman_image_create_bits ( 515d514b0f3Smrg pformat, width, height, dev_addr, - stride); 516d514b0f3Smrg 517d514b0f3Smrg surface->host_image = pixman_image_create_bits ( 518d514b0f3Smrg pformat, width, height, NULL, -1); 519d514b0f3Smrg 520d514b0f3Smrg qxl->bo_funcs->bo_unmap(surface->bo); 521d514b0f3Smrg surface->bpp = bpp; 522d514b0f3Smrg 523d514b0f3Smrg n_live++; 524d514b0f3Smrg 525d514b0f3Smrg return surface; 526d514b0f3Smrg} 527d514b0f3Smrg 528d514b0f3Smrgqxl_surface_t * 529d514b0f3Smrgqxl_surface_create (qxl_screen_t *qxl, 530d514b0f3Smrg int width, 531d514b0f3Smrg int height, 532d514b0f3Smrg int bpp) 533d514b0f3Smrg{ 534d514b0f3Smrg qxl_surface_t *surface; 535d514b0f3Smrg surface_cache_t *cache = qxl->surface_cache; 536d514b0f3Smrg 537d514b0f3Smrg if (!qxl->enable_surfaces) 538d514b0f3Smrg return NULL; 539d514b0f3Smrg 540d514b0f3Smrg if ((bpp & 3) != 0) 541d514b0f3Smrg { 542d514b0f3Smrg ErrorF ("%s: Bad bpp: %d (%d)\n", __FUNCTION__, bpp, bpp & 7); 543d514b0f3Smrg return NULL; 544d514b0f3Smrg } 545d514b0f3Smrg 546d514b0f3Smrg#if 0 547d514b0f3Smrg if (bpp == 8) 548d514b0f3Smrg { 549d514b0f3Smrg static int warned; 550d514b0f3Smrg if (!warned) 551d514b0f3Smrg { 552d514b0f3Smrg warned = 1; 553d514b0f3Smrg ErrorF ("bpp == 8 triggers bugs in spice apparently\n"); 554d514b0f3Smrg } 555d514b0f3Smrg 556d514b0f3Smrg return NULL; 557d514b0f3Smrg } 558d514b0f3Smrg#endif 559d514b0f3Smrg 560d514b0f3Smrg if (bpp != 8 && bpp != 16 && bpp != 32 && bpp != 24) 561d514b0f3Smrg { 562d514b0f3Smrg ErrorF ("%s: Unknown bpp\n", __FUNCTION__); 563d514b0f3Smrg return NULL; 564d514b0f3Smrg } 565d514b0f3Smrg 566d514b0f3Smrg if (width == 0 || height == 0) 567d514b0f3Smrg { 568d514b0f3Smrg ErrorF ("%s: Zero width or height\n", __FUNCTION__); 569d514b0f3Smrg return NULL; 570d514b0f3Smrg } 571d514b0f3Smrg 572d514b0f3Smrg if (!(surface = surface_get_from_cache (cache, width, height, bpp))) 573d514b0f3Smrg if (!(surface = surface_send_create (cache, width, height, bpp))) 574d514b0f3Smrg return NULL; 575d514b0f3Smrg 576d514b0f3Smrg surface->next = cache->live_surfaces; 577d514b0f3Smrg surface->prev = NULL; 578d514b0f3Smrg if (cache->live_surfaces) 579d514b0f3Smrg cache->live_surfaces->prev = surface; 580d514b0f3Smrg cache->live_surfaces = surface; 581d514b0f3Smrg 582d514b0f3Smrg return surface; 583d514b0f3Smrg} 584d514b0f3Smrg 585d514b0f3Smrgvoid 586d514b0f3Smrgqxl_surface_set_pixmap (qxl_surface_t *surface, PixmapPtr pixmap) 587d514b0f3Smrg{ 588d514b0f3Smrg surface->pixmap = pixmap; 589d514b0f3Smrg 590d514b0f3Smrg assert (get_surface (pixmap) == surface); 591d514b0f3Smrg} 592d514b0f3Smrg 593d514b0f3Smrgstatic void 594d514b0f3Smrgunlink_surface (qxl_surface_t *surface) 595d514b0f3Smrg{ 596d514b0f3Smrg if (surface->id != 0) 597d514b0f3Smrg { 598d514b0f3Smrg if (surface->prev) 599d514b0f3Smrg surface->prev->next = surface->next; 600d514b0f3Smrg else 601d514b0f3Smrg surface->cache->live_surfaces = surface->next; 602d514b0f3Smrg } 603d514b0f3Smrg 604d514b0f3Smrg debug_surface_log(surface->cache); 605d514b0f3Smrg 606d514b0f3Smrg if (surface->next) 607d514b0f3Smrg surface->next->prev = surface->prev; 608d514b0f3Smrg 609d514b0f3Smrg surface->pixmap = NULL; 610d514b0f3Smrg 611d514b0f3Smrg surface->prev = NULL; 612d514b0f3Smrg surface->next = NULL; 613d514b0f3Smrg} 614d514b0f3Smrg 615d514b0f3Smrgstatic void 616d514b0f3Smrgsurface_destroy (qxl_surface_t *surface) 617d514b0f3Smrg{ 618d514b0f3Smrg struct qxl_bo *cmd_bo; 619d514b0f3Smrg 620d514b0f3Smrg if (surface->dev_image) 621d514b0f3Smrg pixman_image_unref (surface->dev_image); 622d514b0f3Smrg if (surface->host_image) 623d514b0f3Smrg pixman_image_unref (surface->host_image); 624d514b0f3Smrg 625d514b0f3Smrg#if 0 626d514b0f3Smrg ErrorF("destroy %ld\n", (long int)surface->end - (long int)surface->address); 627d514b0f3Smrg#endif 628d514b0f3Smrg cmd_bo = make_surface_cmd (surface->cache, surface->id, QXL_SURFACE_CMD_DESTROY); 629d514b0f3Smrg 630d514b0f3Smrg push_surface_cmd (surface->cache, cmd_bo); 631d514b0f3Smrg 632d514b0f3Smrg surface->cache->qxl->bo_funcs->bo_decref(surface->cache->qxl, surface->bo); 633d514b0f3Smrg} 634d514b0f3Smrg 635d514b0f3Smrgstatic void 636d514b0f3Smrgsurface_add_to_cache (qxl_surface_t *surface) 637d514b0f3Smrg{ 638d514b0f3Smrg surface_cache_t *cache = surface->cache; 639d514b0f3Smrg int oldest = -1; 640d514b0f3Smrg int n_surfaces = 0; 641d514b0f3Smrg int i, delta; 642d514b0f3Smrg int destroy_id = -1; 643d514b0f3Smrg qxl_surface_t *destroy_surface = NULL; 644d514b0f3Smrg 645d514b0f3Smrg surface->ref_count++; 646d514b0f3Smrg 647d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 648d514b0f3Smrg { 649d514b0f3Smrg if (cache->cached_surfaces[i]) 650d514b0f3Smrg { 651d514b0f3Smrg oldest = i; 652d514b0f3Smrg n_surfaces++; 653d514b0f3Smrg } 654d514b0f3Smrg } 655d514b0f3Smrg 656d514b0f3Smrg if (n_surfaces == N_CACHED_SURFACES) 657d514b0f3Smrg { 658d514b0f3Smrg destroy_id = cache->cached_surfaces[oldest]->id; 659d514b0f3Smrg 660d514b0f3Smrg destroy_surface = cache->cached_surfaces[oldest]; 661d514b0f3Smrg 662d514b0f3Smrg cache->cached_surfaces[oldest] = NULL; 663d514b0f3Smrg 664d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 665d514b0f3Smrg assert (!cache->cached_surfaces[i] || 666d514b0f3Smrg cache->cached_surfaces[i]->id != destroy_id); 667d514b0f3Smrg } 668d514b0f3Smrg 669d514b0f3Smrg delta = 0; 670d514b0f3Smrg for (i = N_CACHED_SURFACES - 1; i >= 0; i--) 671d514b0f3Smrg { 672d514b0f3Smrg if (cache->cached_surfaces[i]) 673d514b0f3Smrg { 674d514b0f3Smrg if (delta > 0) 675d514b0f3Smrg { 676d514b0f3Smrg cache->cached_surfaces[i + delta] = 677d514b0f3Smrg cache->cached_surfaces[i]; 678d514b0f3Smrg 679d514b0f3Smrg assert (cache->cached_surfaces[i + delta]->id != destroy_id); 680d514b0f3Smrg 681d514b0f3Smrg cache->cached_surfaces[i] = NULL; 682d514b0f3Smrg } 683d514b0f3Smrg } 684d514b0f3Smrg else 685d514b0f3Smrg { 686d514b0f3Smrg delta++; 687d514b0f3Smrg } 688d514b0f3Smrg } 689d514b0f3Smrg 690d514b0f3Smrg assert (delta > 0); 691d514b0f3Smrg 692d514b0f3Smrg cache->cached_surfaces[i + delta] = surface; 693d514b0f3Smrg 694d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 695d514b0f3Smrg assert (!cache->cached_surfaces[i] || cache->cached_surfaces[i]->id != destroy_id); 696d514b0f3Smrg 697d514b0f3Smrg /* Note that sending a destroy command can trigger callbacks into 698d514b0f3Smrg * this function (due to memory management), so we have to 699d514b0f3Smrg * do this after updating the cache 700d514b0f3Smrg */ 701d514b0f3Smrg if (destroy_surface) 702d514b0f3Smrg qxl_surface_unref (destroy_surface->cache, destroy_surface->id); 703d514b0f3Smrg} 704d514b0f3Smrg 705d514b0f3Smrgvoid 706d514b0f3Smrgqxl_surface_unref (surface_cache_t *cache, uint32_t id) 707d514b0f3Smrg{ 708d514b0f3Smrg if (id != 0) 709d514b0f3Smrg { 710d514b0f3Smrg qxl_surface_t *surface = cache->all_surfaces + id; 711d514b0f3Smrg 712d514b0f3Smrg if (--surface->ref_count == 0) 713d514b0f3Smrg surface_destroy (surface); 714d514b0f3Smrg } 715d514b0f3Smrg} 716d514b0f3Smrg 717d514b0f3Smrgvoid 718d514b0f3Smrgqxl_surface_kill (qxl_surface_t *surface) 719d514b0f3Smrg{ 720d514b0f3Smrg struct evacuated_surface_t *ev = surface->evacuated; 721d514b0f3Smrg 722d514b0f3Smrg if (ev) 723d514b0f3Smrg { 724d514b0f3Smrg /* server side surface is already destroyed (via reset), don't 725d514b0f3Smrg * resend a destroy. Just mark surface as not to be recreated */ 726d514b0f3Smrg ev->pixmap = NULL; 727d514b0f3Smrg if (ev->image) 728d514b0f3Smrg pixman_image_unref (ev->image); 729d514b0f3Smrg if (ev->next) 730d514b0f3Smrg ev->next->prev = ev->prev; 731d514b0f3Smrg if (ev->prev) 732d514b0f3Smrg ev->prev->next = ev->next; 733d514b0f3Smrg free(ev); 734d514b0f3Smrg surface->evacuated = NULL; 735d514b0f3Smrg return; 736d514b0f3Smrg } 737d514b0f3Smrg 738d514b0f3Smrg unlink_surface (surface); 739d514b0f3Smrg 740d514b0f3Smrg if (!surface->cache->all_surfaces) { 741d514b0f3Smrg return; 742d514b0f3Smrg } 743d514b0f3Smrg 744d514b0f3Smrg if (surface->id != 0 && 745d514b0f3Smrg surface->host_image && 746d514b0f3Smrg pixman_image_get_width (surface->host_image) >= 128 && 747d514b0f3Smrg pixman_image_get_height (surface->host_image) >= 128) 748d514b0f3Smrg { 749d514b0f3Smrg surface_add_to_cache (surface); 750d514b0f3Smrg } 751d514b0f3Smrg 752d514b0f3Smrg qxl_surface_unref (surface->cache, surface->id); 753d514b0f3Smrg} 754d514b0f3Smrg 755d514b0f3Smrg 756d514b0f3Smrgvoid * 757d514b0f3Smrgqxl_surface_cache_evacuate_all (surface_cache_t *cache) 758d514b0f3Smrg{ 759d514b0f3Smrg evacuated_surface_t *evacuated_surfaces = NULL; 760d514b0f3Smrg qxl_surface_t *s; 761d514b0f3Smrg int i; 762d514b0f3Smrg 763d514b0f3Smrg for (i = 0; i < N_CACHED_SURFACES; ++i) 764d514b0f3Smrg { 765d514b0f3Smrg if (cache->cached_surfaces[i]) 766d514b0f3Smrg { 767d514b0f3Smrg surface_destroy (cache->cached_surfaces[i]); 768d514b0f3Smrg cache->cached_surfaces[i] = NULL; 769d514b0f3Smrg } 770d514b0f3Smrg } 771d514b0f3Smrg 772d514b0f3Smrg s = cache->live_surfaces; 773d514b0f3Smrg while (s != NULL) 774d514b0f3Smrg { 775d514b0f3Smrg qxl_surface_t *next = s->next; 776d514b0f3Smrg evacuated_surface_t *evacuated = malloc (sizeof (evacuated_surface_t)); 777d514b0f3Smrg int width, height; 778d514b0f3Smrg 779d514b0f3Smrg width = pixman_image_get_width (s->host_image); 780d514b0f3Smrg height = pixman_image_get_height (s->host_image); 781d514b0f3Smrg 782d514b0f3Smrg qxl_download_box (s, 0, 0, width, height); 783d514b0f3Smrg 784d514b0f3Smrg evacuated->image = s->host_image; 785d514b0f3Smrg evacuated->pixmap = s->pixmap; 786d514b0f3Smrg 787d514b0f3Smrg assert (get_surface (evacuated->pixmap) == s); 788d514b0f3Smrg 789d514b0f3Smrg evacuated->bpp = s->bpp; 790d514b0f3Smrg 791d514b0f3Smrg s->host_image = NULL; 792d514b0f3Smrg 793d514b0f3Smrg unlink_surface (s); 794d514b0f3Smrg 795d514b0f3Smrg evacuated->prev = NULL; 796d514b0f3Smrg evacuated->next = evacuated_surfaces; 797d514b0f3Smrg if (evacuated_surfaces) 798d514b0f3Smrg evacuated_surfaces->prev = evacuated; 799d514b0f3Smrg evacuated_surfaces = evacuated; 800d514b0f3Smrg s->evacuated = evacuated; 801d514b0f3Smrg 802d514b0f3Smrg s = next; 803d514b0f3Smrg } 804d514b0f3Smrg 805d514b0f3Smrg cache->live_surfaces = NULL; 806d514b0f3Smrg cache->free_surfaces = NULL; 807d514b0f3Smrg 808d514b0f3Smrg return evacuated_surfaces; 809d514b0f3Smrg} 810d514b0f3Smrg 811d514b0f3Smrgvoid 812d514b0f3Smrgqxl_surface_cache_replace_all (surface_cache_t *cache, void *data) 813d514b0f3Smrg{ 814d514b0f3Smrg evacuated_surface_t *ev; 815d514b0f3Smrg 816d514b0f3Smrg if (!surface_cache_init (cache, cache->qxl)) 817d514b0f3Smrg { 818d514b0f3Smrg /* FIXME: report the error */ 819d514b0f3Smrg return; 820d514b0f3Smrg } 821d514b0f3Smrg 822d514b0f3Smrg ev = data; 823d514b0f3Smrg while (ev != NULL) 824d514b0f3Smrg { 825d514b0f3Smrg evacuated_surface_t *next = ev->next; 826d514b0f3Smrg int width = pixman_image_get_width (ev->image); 827d514b0f3Smrg int height = pixman_image_get_height (ev->image); 828d514b0f3Smrg qxl_surface_t *surface; 829d514b0f3Smrg 830d514b0f3Smrg surface = qxl_surface_create (cache->qxl, width, height, ev->bpp); 831d514b0f3Smrg 832d514b0f3Smrg assert (surface->host_image); 833d514b0f3Smrg assert (surface->dev_image); 834d514b0f3Smrg 835d514b0f3Smrg pixman_image_unref (surface->host_image); 836d514b0f3Smrg surface->host_image = ev->image; 837d514b0f3Smrg 838d514b0f3Smrg qxl_upload_box (surface, 0, 0, width, height); 839d514b0f3Smrg 840d514b0f3Smrg set_surface (ev->pixmap, surface); 841d514b0f3Smrg 842d514b0f3Smrg qxl_surface_set_pixmap (surface, ev->pixmap); 843d514b0f3Smrg 844d514b0f3Smrg free (ev); 845d514b0f3Smrg 846d514b0f3Smrg ev = next; 847d514b0f3Smrg } 848d514b0f3Smrg 849d514b0f3Smrg qxl_surface_cache_sanity_check (cache); 850d514b0f3Smrg 851d514b0f3Smrg} 852