diff options
author | Dave Airlie <airlied@gmail.com> | 2013-02-26 15:03:37 +1000 |
---|---|---|
committer | Dave Airlie <airlied@gmail.com> | 2013-03-06 13:22:42 +1000 |
commit | 9d45cc50c0f7302a649a5ae5b850f0bdbb9a2771 (patch) | |
tree | 6c645749b5be99e3770567ff2efc081d60d061b2 | |
parent | 8c7fada8f42cc4a741f7f1d210b50f754ef8805e (diff) |
qxl: separate surface ums code out into separate file
All the surface cache code is UMS specific, so separate most of it out.
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/qxl_surface.c | 856 | ||||
-rw-r--r-- | src/qxl_surface.h | 3 | ||||
-rw-r--r-- | src/qxl_surface_ums.c | 880 |
4 files changed, 890 insertions, 851 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 82ed0cc..f9557da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,6 +41,7 @@ qxl_drv_la_SOURCES = \ qxl_driver.c \ qxl_image.c \ qxl_surface.c \ + qxl_surface_ums.c \ qxl_surface.h \ qxl_ring.c \ qxl_mem.c \ @@ -86,6 +87,7 @@ spiceqxl_drv_la_SOURCES = \ qxl_driver.c \ qxl_image.c \ qxl_surface.c \ + qxl_surface_ums.c \ qxl_surface.h \ qxl_ring.c \ qxl_mem.c \ diff --git a/src/qxl_surface.c b/src/qxl_surface.c index b404873..d2fb267 100644 --- a/src/qxl_surface.c +++ b/src/qxl_surface.c @@ -20,399 +20,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* The life cycle of surfaces - * - * free => live => dead => destroyed => free - * - * A 'free' surface is one that is not allocated on the device. These - * are stored on the 'free_surfaces' list. - * - * A 'live' surface is one that the X server is using for something. It - * has an associated pixmap. It is allocated in the device. These are stored on - * the "live_surfaces" list. - * - * A 'dead' surface is one that the X server is no using any more, but - * is still allocated in the device. These surfaces may be stored in the - * cache, from where they can be resurrected. The cache holds a ref on the - * surfaces. - * - * A 'destroyed' surface is one whose ref count has reached 0. It is no - * longer being referenced by either the server or the device or the cache. - * When a surface enters this state, the associated pixman images are freed, and - * a destroy command is sent. This will eventually trigger a 'recycle' call, - * which puts the surface into the 'free' state. - * - */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" -#include "qxl_surface.h" -#ifdef DEBUG_SURFACE_LIFECYCLE -#include <stdio.h> - -static FILE* surface_log; -#endif - -typedef struct evacuated_surface_t evacuated_surface_t; - -struct evacuated_surface_t -{ - pixman_image_t *image; - PixmapPtr pixmap; - int bpp; - - evacuated_surface_t *prev; - evacuated_surface_t *next; -}; - -#define N_CACHED_SURFACES 64 - -/* - * Surface cache - */ -struct surface_cache_t -{ - qxl_screen_t *qxl; - - /* Array of surfaces (not a linked list). - * All surfaces, excluding the primary one, indexed by surface id. - */ - qxl_surface_t *all_surfaces; - - /* All surfaces that the driver is currently using (linked through next/prev) */ - qxl_surface_t *live_surfaces; - - /* All surfaces that need to be allocated (linked through next, but not prev) */ - qxl_surface_t *free_surfaces; - - /* Surfaces that are already allocated, but not in used by the driver, - * linked through next - */ - qxl_surface_t *cached_surfaces[N_CACHED_SURFACES]; -}; - -#ifdef DEBUG_SURFACE_LIFECYCLE -static void debug_surface_open(void) -{ - if (surface_log) - return; - surface_log = fopen("/tmp/xf86-video-qxl.surface.log", "w+"); - if (!surface_log) - { - fprintf(stderr, "error creating surface log file (DEBUG_SURFACE_LIFECYCLE)\n"); - exit(-1); - } -} - -static int surface_count(qxl_surface_t *surface) -{ - int i; - - for (i = 0; surface ;++i, surface = surface->next); - return i; -} - -static void debug_surface_log(surface_cache_t *cache) -{ - int live_n, free_n; - - debug_surface_open(); - live_n = surface_count(cache->live_surfaces); - free_n = surface_count(cache->free_surfaces); - fprintf(surface_log, "live,free,sum = %d, %d, %d\n", live_n, free_n, - live_n + free_n); - fflush(surface_log); -} - -#else -#define debug_surface_log(cache) -#endif - - -static Bool -surface_cache_init (surface_cache_t *cache, qxl_screen_t *qxl) -{ - int n_surfaces = qxl->rom->n_surfaces; - int i; - - if (!cache->all_surfaces) - { - /* all_surfaces is not freed when evacuating, since surfaces are still - * tied to pixmaps that may be destroyed after evacuation before - * recreation */ - cache->all_surfaces = calloc (n_surfaces, sizeof (qxl_surface_t)); - if (!cache->all_surfaces) - return FALSE; - } - - memset (cache->all_surfaces, 0, n_surfaces * sizeof (qxl_surface_t)); - memset (cache->cached_surfaces, 0, N_CACHED_SURFACES * sizeof (qxl_surface_t *)); - - cache->free_surfaces = NULL; - cache->live_surfaces = NULL; - - for (i = 0; i < n_surfaces; ++i) - { - cache->all_surfaces[i].id = i; - cache->all_surfaces[i].cache = cache; - cache->all_surfaces[i].qxl = qxl; - cache->all_surfaces[i].dev_image = NULL; - cache->all_surfaces[i].host_image = NULL; - cache->all_surfaces[i].evacuated = NULL; - - REGION_INIT ( - NULL, &(cache->all_surfaces[i].access_region), (BoxPtr)NULL, 0); - cache->all_surfaces[i].access_type = UXA_ACCESS_RO; - - if (i) /* surface 0 is the primary surface */ - { - cache->all_surfaces[i].next = cache->free_surfaces; - cache->free_surfaces = &(cache->all_surfaces[i]); - cache->all_surfaces[i].in_use = FALSE; - } - } - - return TRUE; -} - -surface_cache_t * -qxl_surface_cache_create (qxl_screen_t *qxl) -{ - surface_cache_t *cache = malloc (sizeof *cache); - - if (!cache) - return NULL; - - memset(cache, 0, sizeof(*cache)); - cache->qxl = qxl; - if (!surface_cache_init (cache, qxl)) - { - free (cache); - return NULL; - } - - return cache; -} - -void -qxl_surface_cache_sanity_check (surface_cache_t *qxl) -{ -#if 0 - qxl_surface_t *s; - - for (s = qxl->live_surfaces; s != NULL; s = s->next) - { - PixmapPtr pixmap = s->pixmap; - - if (! (get_surface (pixmap) == s) ) - { - ErrorF ("Surface %p has pixmap %p, but pixmap %p has surface %p\n", - s, pixmap, pixmap, get_surface (pixmap)); - - assert (0); - } - } -#endif -} - -static void -print_cache_info (surface_cache_t *cache) -{ - int i; - int n_surfaces = 0; - - ErrorF ("Cache contents: "); - for (i = 0; i < N_CACHED_SURFACES; ++i) - { - if (cache->cached_surfaces[i]) - { - ErrorF ("%4d ", cache->cached_surfaces[i]->id); - n_surfaces++; - } - else - ErrorF ("null "); - } - - ErrorF (" total: %d\n", n_surfaces); -} - -static void -get_formats (int bpp, SpiceBitmapFmt *format, pixman_format_code_t *pformat) -{ - switch (bpp) - { - case 8: - *format = SPICE_SURFACE_FMT_8_A; - *pformat = PIXMAN_a8; - break; - - case 16: - *format = SPICE_SURFACE_FMT_16_565; - *pformat = PIXMAN_r5g6b5; - break; - - case 24: - *format = SPICE_SURFACE_FMT_32_xRGB; - *pformat = PIXMAN_a8r8g8b8; - break; - - case 32: - *format = SPICE_SURFACE_FMT_32_ARGB; - *pformat = PIXMAN_a8r8g8b8; - break; - - default: - *format = *pformat = -1; - break; - } -} - -static qxl_surface_t * -surface_get_from_cache (surface_cache_t *cache, int width, int height, int bpp) -{ - int i; - - for (i = 0; i < N_CACHED_SURFACES; ++i) - { - qxl_surface_t *s = cache->cached_surfaces[i]; - - if (s && bpp == s->bpp) - { - int w = pixman_image_get_width (s->host_image); - int h = pixman_image_get_height (s->host_image); - - if (width <= w && width * 4 > w && height <= h && height * 4 > h) - { - cache->cached_surfaces[i] = NULL; - - return s; - } - } - } - - return NULL; -} - -static int n_live; - -void -qxl_surface_recycle (surface_cache_t *cache, uint32_t id) -{ - qxl_surface_t *surface = cache->all_surfaces + id; - - n_live--; - if (surface->bo) - cache->qxl->bo_funcs->bo_decref (cache->qxl, surface->bo); - surface->bo = NULL; - surface->next = cache->free_surfaces; - cache->free_surfaces = surface; -} - -/* - * mode is used for the whole virtual screen, not for a specific head. - * For a single head where virtual size is equal to the head size, they are - * equal. For multiple heads this mode will not match any existing heads and - * will be the containing virtual size. - */ -qxl_surface_t * -qxl_surface_cache_create_primary (qxl_screen_t *qxl, - struct QXLMode *mode) -{ - pixman_format_code_t format; - uint8_t *dev_addr; - pixman_image_t *dev_image, *host_image; - qxl_surface_t *surface; - surface_cache_t *cache = qxl->surface_cache; - struct qxl_bo *bo; - - if (mode->bits == 16) - { - format = PIXMAN_x1r5g5b5; - } - else if (mode->bits == 32) - { - format = PIXMAN_x8r8g8b8; - } - else - { - xf86DrvMsg (qxl->pScrn->scrnIndex, X_ERROR, - "Unknown bit depth %d\n", mode->bits); - return NULL; - } - - bo = qxl->bo_funcs->create_primary(qxl, mode->x_res, mode->y_res, mode->stride, mode->bits); +#include "qxl_surface.h"/* send anything pending to the other side */ - dev_addr = qxl->bo_funcs->bo_map(bo); - dev_image = pixman_image_create_bits (format, mode->x_res, mode->y_res, - (uint32_t *)dev_addr, -mode->stride); - - host_image = pixman_image_create_bits (format, - qxl->virtual_x, qxl->virtual_y, - NULL, mode->stride); -#if 0 - xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, - "testing dev_image memory (%d x %d)\n", - mode->x_res, mode->y_res); - memset(qxl->ram, 0, mode->stride * mode->y_res); - xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, - "testing host_image memory\n"); - memset(qxl->fb, 0, mode->stride * mode->y_res); -#endif - - surface = malloc (sizeof *surface); - surface->id = 0; - surface->dev_image = dev_image; - surface->host_image = host_image; - surface->cache = cache; - surface->qxl = qxl; - surface->bpp = mode->bits; - surface->next = NULL; - surface->prev = NULL; - surface->evacuated = NULL; - surface->bo = bo; - - REGION_INIT (NULL, &(surface->access_region), (BoxPtr)NULL, 0); - surface->access_type = UXA_ACCESS_RO; - - return surface; -} - -void * -qxl_surface_get_host_bits(qxl_surface_t *surface) -{ - if (!surface) - return NULL; - return (void *) pixman_image_get_data(surface->host_image); -} - -static struct qxl_bo * -make_surface_cmd (surface_cache_t *cache, uint32_t id, QXLSurfaceCmdType type) -{ - struct qxl_bo *cmd_bo; - struct QXLSurfaceCmd *cmd; - qxl_screen_t *qxl = cache->qxl; - - cmd_bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof *cmd, "surface command"); - cmd = qxl->bo_funcs->bo_map(cmd_bo); - - cmd->release_info.id = pointer_to_u64 (cmd_bo) | 2; - cmd->type = type; - cmd->flags = 0; - cmd->surface_id = id; - - qxl->bo_funcs->bo_unmap(cmd_bo); - return cmd_bo; -} - -static void -push_surface_cmd (surface_cache_t *cache, struct qxl_bo *cmd_bo) -{ - qxl_screen_t *qxl = cache->qxl; - - qxl->bo_funcs->write_command (qxl, QXL_CMD_SURFACE, cmd_bo); -} enum ROPDescriptor { @@ -505,371 +119,6 @@ submit_fill (qxl_screen_t *qxl, qxl_surface_t *surf, push_drawable (qxl, drawable_bo); } -static qxl_surface_t * -surface_get_from_free_list (surface_cache_t *cache) -{ - qxl_surface_t *result = NULL; - - if (cache->free_surfaces) - { - qxl_surface_t *s; - - result = cache->free_surfaces; - cache->free_surfaces = cache->free_surfaces->next; - - result->next = NULL; - result->in_use = TRUE; - result->ref_count = 1; - result->pixmap = NULL; - - for (s = cache->free_surfaces; s; s = s->next) - { - if (s->id == result->id) - ErrorF ("huh: %d to be returned, but %d is in list\n", - s->id, result->id); - - assert (s->id != result->id); - } - } - - return result; -} - -static int -align (int x) -{ - return x; -} - -static qxl_surface_t * -surface_send_create (surface_cache_t *cache, - int width, - int height, - int bpp) -{ - SpiceBitmapFmt format; - pixman_format_code_t pformat; - struct QXLSurfaceCmd *cmd; - int stride; - uint32_t *dev_addr; - int n_attempts = 0; - qxl_screen_t *qxl = cache->qxl; - qxl_surface_t *surface; - struct qxl_bo *bo, *cmd_bo; - void *dev_ptr; - get_formats (bpp, &format, &pformat); - - width = align (width); - height = align (height); - - stride = width * PIXMAN_FORMAT_BPP (pformat) / 8; - stride = (stride + 3) & ~3; - - /* the final + stride is to work around a bug where the device apparently - * scribbles after the end of the image - */ - qxl_garbage_collect (qxl); -retry2: - bo = qxl_ums_surf_mem_alloc(qxl, stride * height + stride); - - if (!bo) - { - ErrorF ("- %dth attempt\n", n_attempts++); - - if (qxl_garbage_collect (qxl)) - goto retry2; - - ErrorF ("- OOM at %d %d %d (= %d bytes)\n", width, height, bpp, width * height * (bpp / 8)); - print_cache_info (cache); - - if (qxl_handle_oom (qxl)) - { - while (qxl_garbage_collect (qxl)) - ; - goto retry2; - } - - ErrorF ("Out of video memory: Could not allocate %d bytes\n", - stride * height + stride); - - return NULL; - } - -retry: - surface = surface_get_from_free_list (cache); - if (!surface) - { - if (!qxl_handle_oom (cache->qxl)) - { - ErrorF (" Out of surfaces\n"); - qxl->bo_funcs->bo_decref (qxl, bo); - return NULL; - } - else - goto retry; - } - - surface->bo = bo; - - cmd_bo = make_surface_cmd (cache, surface->id, QXL_SURFACE_CMD_CREATE); - - cmd = qxl->bo_funcs->bo_map(cmd_bo); - cmd->u.surface_create.format = format; - cmd->u.surface_create.width = width; - cmd->u.surface_create.height = height; - cmd->u.surface_create.stride = - stride; - qxl->bo_funcs->bo_unmap(cmd_bo); - - qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(struct QXLSurfaceCmd, u.surface_create.data), cmd_bo, surface->bo); - - push_surface_cmd (cache, cmd_bo); - - dev_ptr = qxl->bo_funcs->bo_map(surface->bo); - dev_addr - = (uint32_t *)((uint8_t *)dev_ptr + stride * (height - 1)); - - surface->dev_image = pixman_image_create_bits ( - pformat, width, height, dev_addr, - stride); - - surface->host_image = pixman_image_create_bits ( - pformat, width, height, NULL, -1); - - qxl->bo_funcs->bo_unmap(surface->bo); - surface->bpp = bpp; - - n_live++; - - return surface; -} - -qxl_surface_t * -qxl_surface_create (qxl_screen_t *qxl, - int width, - int height, - int bpp) -{ - qxl_surface_t *surface; - surface_cache_t *cache = qxl->surface_cache; - - if (!qxl->enable_surfaces) - return NULL; - - if ((bpp & 3) != 0) - { - ErrorF ("%s: Bad bpp: %d (%d)\n", __FUNCTION__, bpp, bpp & 7); - return NULL; - } - -#if 0 - if (bpp == 8) - { - static int warned; - if (!warned) - { - warned = 1; - ErrorF ("bpp == 8 triggers bugs in spice apparently\n"); - } - - return NULL; - } -#endif - - if (bpp != 8 && bpp != 16 && bpp != 32 && bpp != 24) - { - ErrorF ("%s: Unknown bpp\n", __FUNCTION__); - return NULL; - } - - if (width == 0 || height == 0) - { - ErrorF ("%s: Zero width or height\n", __FUNCTION__); - return NULL; - } - - if (!(surface = surface_get_from_cache (cache, width, height, bpp))) - if (!(surface = surface_send_create (cache, width, height, bpp))) - return NULL; - - surface->next = cache->live_surfaces; - surface->prev = NULL; - if (cache->live_surfaces) - cache->live_surfaces->prev = surface; - cache->live_surfaces = surface; - - return surface; -} - -void -qxl_surface_set_pixmap (qxl_surface_t *surface, PixmapPtr pixmap) -{ - surface->pixmap = pixmap; - - assert (get_surface (pixmap) == surface); -} - -static void -unlink_surface (qxl_surface_t *surface) -{ - if (surface->id != 0) - { - if (surface->prev) - surface->prev->next = surface->next; - else - surface->cache->live_surfaces = surface->next; - } - - debug_surface_log(surface->cache); - - if (surface->next) - surface->next->prev = surface->prev; - - surface->pixmap = NULL; - - surface->prev = NULL; - surface->next = NULL; -} - -static void -surface_destroy (qxl_surface_t *surface) -{ - struct qxl_bo *cmd_bo; - - if (surface->dev_image) - pixman_image_unref (surface->dev_image); - if (surface->host_image) - pixman_image_unref (surface->host_image); - -#if 0 - ErrorF("destroy %ld\n", (long int)surface->end - (long int)surface->address); -#endif - cmd_bo = make_surface_cmd (surface->cache, surface->id, QXL_SURFACE_CMD_DESTROY); - - push_surface_cmd (surface->cache, cmd_bo); - - surface->cache->qxl->bo_funcs->bo_decref(surface->cache->qxl, surface->bo); -} - -static void -surface_add_to_cache (qxl_surface_t *surface) -{ - surface_cache_t *cache = surface->cache; - int oldest = -1; - int n_surfaces = 0; - int i, delta; - int destroy_id = -1; - qxl_surface_t *destroy_surface = NULL; - - surface->ref_count++; - - for (i = 0; i < N_CACHED_SURFACES; ++i) - { - if (cache->cached_surfaces[i]) - { - oldest = i; - n_surfaces++; - } - } - - if (n_surfaces == N_CACHED_SURFACES) - { - destroy_id = cache->cached_surfaces[oldest]->id; - - destroy_surface = cache->cached_surfaces[oldest]; - - cache->cached_surfaces[oldest] = NULL; - - for (i = 0; i < N_CACHED_SURFACES; ++i) - assert (!cache->cached_surfaces[i] || - cache->cached_surfaces[i]->id != destroy_id); - } - - delta = 0; - for (i = N_CACHED_SURFACES - 1; i >= 0; i--) - { - if (cache->cached_surfaces[i]) - { - if (delta > 0) - { - cache->cached_surfaces[i + delta] = - cache->cached_surfaces[i]; - - assert (cache->cached_surfaces[i + delta]->id != destroy_id); - - cache->cached_surfaces[i] = NULL; - } - } - else - { - delta++; - } - } - - assert (delta > 0); - - cache->cached_surfaces[i + delta] = surface; - - for (i = 0; i < N_CACHED_SURFACES; ++i) - assert (!cache->cached_surfaces[i] || cache->cached_surfaces[i]->id != destroy_id); - - /* Note that sending a destroy command can trigger callbacks into - * this function (due to memory management), so we have to - * do this after updating the cache - */ - if (destroy_surface) - qxl_surface_unref (destroy_surface->cache, destroy_surface->id); -} - -void -qxl_surface_unref (surface_cache_t *cache, uint32_t id) -{ - if (id != 0) - { - qxl_surface_t *surface = cache->all_surfaces + id; - - if (--surface->ref_count == 0) - surface_destroy (surface); - } -} - -void -qxl_surface_kill (qxl_surface_t *surface) -{ - struct evacuated_surface_t *ev = surface->evacuated; - - if (ev) - { - /* server side surface is already destroyed (via reset), don't - * resend a destroy. Just mark surface as not to be recreated */ - ev->pixmap = NULL; - if (ev->image) - pixman_image_unref (ev->image); - if (ev->next) - ev->next->prev = ev->prev; - if (ev->prev) - ev->prev->next = ev->next; - free(ev); - surface->evacuated = NULL; - return; - } - - unlink_surface (surface); - - if (!surface->cache->all_surfaces) { - return; - } - - if (surface->id != 0 && - surface->host_image && - pixman_image_get_width (surface->host_image) >= 128 && - pixman_image_get_height (surface->host_image) >= 128) - { - surface_add_to_cache (surface); - } - - qxl_surface_unref (surface->cache, surface->id); -} - -/* send anything pending to the other side */ void qxl_surface_flush (qxl_surface_t *surface) { @@ -887,8 +136,8 @@ download_box_no_update (qxl_surface_t *surface, int x1, int y1, int x2, int y2) x1, y1, 0, 0, x1, y1, x2 - x1, y2 - y1); } -static void -download_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2) +void +qxl_download_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2) { surface->qxl->bo_funcs->update_area(surface, x1, y1, x2, y2); @@ -925,14 +174,14 @@ qxl_surface_prepare_access (qxl_surface_t *surface, { while (n_boxes--) { - download_box (surface, boxes->x1, boxes->y1, boxes->x2, boxes->y2); + qxl_download_box (surface, boxes->x1, boxes->y1, boxes->x2, boxes->y2); boxes++; } } else { - download_box ( + qxl_download_box ( surface, new.extents.x1, new.extents.y1, new.extents.x2, new.extents.y2); } @@ -1123,101 +372,6 @@ qxl_surface_finish_access (qxl_surface_t *surface, PixmapPtr pixmap) pScreen->ModifyPixmapHeader(pixmap, w, h, -1, -1, 0, NULL); } -void * -qxl_surface_cache_evacuate_all (surface_cache_t *cache) -{ - evacuated_surface_t *evacuated_surfaces = NULL; - qxl_surface_t *s; - int i; - - for (i = 0; i < N_CACHED_SURFACES; ++i) - { - if (cache->cached_surfaces[i]) - { - surface_destroy (cache->cached_surfaces[i]); - cache->cached_surfaces[i] = NULL; - } - } - - s = cache->live_surfaces; - while (s != NULL) - { - qxl_surface_t *next = s->next; - evacuated_surface_t *evacuated = malloc (sizeof (evacuated_surface_t)); - int width, height; - - width = pixman_image_get_width (s->host_image); - height = pixman_image_get_height (s->host_image); - - download_box (s, 0, 0, width, height); - - evacuated->image = s->host_image; - evacuated->pixmap = s->pixmap; - - assert (get_surface (evacuated->pixmap) == s); - - evacuated->bpp = s->bpp; - - s->host_image = NULL; - - unlink_surface (s); - - evacuated->next = evacuated_surfaces; - if (evacuated_surfaces) - evacuated_surfaces->prev = evacuated; - evacuated_surfaces = evacuated; - s->evacuated = evacuated; - - s = next; - } - - cache->live_surfaces = NULL; - cache->free_surfaces = NULL; - - return evacuated_surfaces; -} - -void -qxl_surface_cache_replace_all (surface_cache_t *cache, void *data) -{ - evacuated_surface_t *ev; - - if (!surface_cache_init (cache, cache->qxl)) - { - /* FIXME: report the error */ - return; - } - - ev = data; - while (ev != NULL) - { - evacuated_surface_t *next = ev->next; - int width = pixman_image_get_width (ev->image); - int height = pixman_image_get_height (ev->image); - qxl_surface_t *surface; - - surface = qxl_surface_create (cache->qxl, width, height, ev->bpp); - - assert (surface->host_image); - assert (surface->dev_image); - - pixman_image_unref (surface->host_image); - surface->host_image = ev->image; - - upload_box (surface, 0, 0, width, height); - - set_surface (ev->pixmap, surface); - - qxl_surface_set_pixmap (surface, ev->pixmap); - - free (ev); - - ev = next; - } - - qxl_surface_cache_sanity_check (cache); - -} #ifdef DEBUG_REGIONS static void diff --git a/src/qxl_surface.h b/src/qxl_surface.h index 89e08fd..c3e6b35 100644 --- a/src/qxl_surface.h +++ b/src/qxl_surface.h @@ -48,4 +48,7 @@ struct qxl_surface_t } u; }; +void +qxl_download_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2); + #endif diff --git a/src/qxl_surface_ums.c b/src/qxl_surface_ums.c new file mode 100644 index 0000000..bd8d310 --- /dev/null +++ b/src/qxl_surface_ums.c @@ -0,0 +1,880 @@ +/* + * Copyright 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* The life cycle of surfaces + * + * free => live => dead => destroyed => free + * + * A 'free' surface is one that is not allocated on the device. These + * are stored on the 'free_surfaces' list. + * + * A 'live' surface is one that the X server is using for something. It + * has an associated pixmap. It is allocated in the device. These are stored on + * the "live_surfaces" list. + * + * A 'dead' surface is one that the X server is no using any more, but + * is still allocated in the device. These surfaces may be stored in the + * cache, from where they can be resurrected. The cache holds a ref on the + * surfaces. + * + * A 'destroyed' surface is one whose ref count has reached 0. It is no + * longer being referenced by either the server or the device or the cache. + * When a surface enters this state, the associated pixman images are freed, and + * a destroy command is sent. This will eventually trigger a 'recycle' call, + * which puts the surface into the 'free' state. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "qxl.h" +#include "qxl_surface.h" +#ifdef DEBUG_SURFACE_LIFECYCLE +#include <stdio.h> + +static FILE* surface_log; +#endif + +typedef struct evacuated_surface_t evacuated_surface_t; + +struct evacuated_surface_t +{ + pixman_image_t *image; + PixmapPtr pixmap; + int bpp; + + evacuated_surface_t *prev; + evacuated_surface_t *next; +}; + +#define N_CACHED_SURFACES 64 + +/* + * Surface cache + */ +struct surface_cache_t +{ + qxl_screen_t *qxl; + + /* Array of surfaces (not a linked list). + * All surfaces, excluding the primary one, indexed by surface id. + */ + qxl_surface_t *all_surfaces; + + /* All surfaces that the driver is currently using (linked through next/prev) */ + qxl_surface_t *live_surfaces; + + /* All surfaces that need to be allocated (linked through next, but not prev) */ + qxl_surface_t *free_surfaces; + + /* Surfaces that are already allocated, but not in used by the driver, + * linked through next + */ + qxl_surface_t *cached_surfaces[N_CACHED_SURFACES]; +}; + +#ifdef DEBUG_SURFACE_LIFECYCLE +static void debug_surface_open(void) +{ + if (surface_log) + return; + surface_log = fopen("/tmp/xf86-video-qxl.surface.log", "w+"); + if (!surface_log) + { + fprintf(stderr, "error creating surface log file (DEBUG_SURFACE_LIFECYCLE)\n"); + exit(-1); + } +} + +static int surface_count(qxl_surface_t *surface) +{ + int i; + + for (i = 0; surface ;++i, surface = surface->next); + return i; +} + +static void debug_surface_log(surface_cache_t *cache) +{ + int live_n, free_n; + + debug_surface_open(); + live_n = surface_count(cache->live_surfaces); + free_n = surface_count(cache->free_surfaces); + fprintf(surface_log, "live,free,sum = %d, %d, %d\n", live_n, free_n, + live_n + free_n); + fflush(surface_log); +} + +#else +#define debug_surface_log(cache) +#endif + + +static Bool +surface_cache_init (surface_cache_t *cache, qxl_screen_t *qxl) +{ + int n_surfaces = qxl->rom->n_surfaces; + int i; + + if (!cache->all_surfaces) + { + /* all_surfaces is not freed when evacuating, since surfaces are still + * tied to pixmaps that may be destroyed after evacuation before + * recreation */ + cache->all_surfaces = calloc (n_surfaces, sizeof (qxl_surface_t)); + if (!cache->all_surfaces) + return FALSE; + } + + memset (cache->all_surfaces, 0, n_surfaces * sizeof (qxl_surface_t)); + memset (cache->cached_surfaces, 0, N_CACHED_SURFACES * sizeof (qxl_surface_t *)); + + cache->free_surfaces = NULL; + cache->live_surfaces = NULL; + + for (i = 0; i < n_surfaces; ++i) + { + cache->all_surfaces[i].id = i; + cache->all_surfaces[i].cache = cache; + cache->all_surfaces[i].qxl = qxl; + cache->all_surfaces[i].dev_image = NULL; + cache->all_surfaces[i].host_image = NULL; + cache->all_surfaces[i].evacuated = NULL; + + REGION_INIT ( + NULL, &(cache->all_surfaces[i].access_region), (BoxPtr)NULL, 0); + cache->all_surfaces[i].access_type = UXA_ACCESS_RO; + + if (i) /* surface 0 is the primary surface */ + { + cache->all_surfaces[i].next = cache->free_surfaces; + cache->free_surfaces = &(cache->all_surfaces[i]); + cache->all_surfaces[i].in_use = FALSE; + } + } + + return TRUE; +} + +surface_cache_t * +qxl_surface_cache_create (qxl_screen_t *qxl) +{ + surface_cache_t *cache = malloc (sizeof *cache); + + if (!cache) + return NULL; + + memset(cache, 0, sizeof(*cache)); + cache->qxl = qxl; + if (!surface_cache_init (cache, qxl)) + { + free (cache); + return NULL; + } + + return cache; +} + +void +qxl_surface_cache_sanity_check (surface_cache_t *qxl) +{ +#if 0 + qxl_surface_t *s; + + for (s = qxl->live_surfaces; s != NULL; s = s->next) + { + PixmapPtr pixmap = s->pixmap; + + if (! (get_surface (pixmap) == s) ) + { + ErrorF ("Surface %p has pixmap %p, but pixmap %p has surface %p\n", + s, pixmap, pixmap, get_surface (pixmap)); + + assert (0); + } + } +#endif +} + +static void +print_cache_info (surface_cache_t *cache) +{ + int i; + int n_surfaces = 0; + + ErrorF ("Cache contents: "); + for (i = 0; i < N_CACHED_SURFACES; ++i) + { + if (cache->cached_surfaces[i]) + { + ErrorF ("%4d ", cache->cached_surfaces[i]->id); + n_surfaces++; + } + else + ErrorF ("null "); + } + + ErrorF (" total: %d\n", n_surfaces); +} + +static void +get_formats (int bpp, SpiceBitmapFmt *format, pixman_format_code_t *pformat) +{ + switch (bpp) + { + case 8: + *format = SPICE_SURFACE_FMT_8_A; + *pformat = PIXMAN_a8; + break; + + case 16: + *format = SPICE_SURFACE_FMT_16_565; + *pformat = PIXMAN_r5g6b5; + break; + + case 24: + *format = SPICE_SURFACE_FMT_32_xRGB; + *pformat = PIXMAN_a8r8g8b8; + break; + + case 32: + *format = SPICE_SURFACE_FMT_32_ARGB; + *pformat = PIXMAN_a8r8g8b8; + break; + + default: + *format = *pformat = -1; + break; + } +} + +static qxl_surface_t * +surface_get_from_cache (surface_cache_t *cache, int width, int height, int bpp) +{ + int i; + + for (i = 0; i < N_CACHED_SURFACES; ++i) + { + qxl_surface_t *s = cache->cached_surfaces[i]; + + if (s && bpp == s->bpp) + { + int w = pixman_image_get_width (s->host_image); + int h = pixman_image_get_height (s->host_image); + + if (width <= w && width * 4 > w && height <= h && height * 4 > h) + { + cache->cached_surfaces[i] = NULL; + + return s; + } + } + } + + return NULL; +} + +static int n_live; + +void +qxl_surface_recycle (surface_cache_t *cache, uint32_t id) +{ + qxl_surface_t *surface = cache->all_surfaces + id; + + n_live--; + if (surface->bo) + cache->qxl->bo_funcs->bo_decref (cache->qxl, surface->bo); + surface->bo = NULL; + surface->next = cache->free_surfaces; + cache->free_surfaces = surface; +} + +/* + * mode is used for the whole virtual screen, not for a specific head. + * For a single head where virtual size is equal to the head size, they are + * equal. For multiple heads this mode will not match any existing heads and + * will be the containing virtual size. + */ +qxl_surface_t * +qxl_surface_cache_create_primary (qxl_screen_t *qxl, + struct QXLMode *mode) +{ + pixman_format_code_t format; + uint8_t *dev_addr; + pixman_image_t *dev_image, *host_image; + qxl_surface_t *surface; + surface_cache_t *cache = qxl->surface_cache; + struct qxl_bo *bo; + + if (mode->bits == 16) + { + format = PIXMAN_x1r5g5b5; + } + else if (mode->bits == 32) + { + format = PIXMAN_x8r8g8b8; + } + else + { + xf86DrvMsg (qxl->pScrn->scrnIndex, X_ERROR, + "Unknown bit depth %d\n", mode->bits); + return NULL; + } + + bo = qxl->bo_funcs->create_primary(qxl, mode->x_res, mode->y_res, mode->stride, mode->bits); + + dev_addr = qxl->bo_funcs->bo_map(bo); + dev_image = pixman_image_create_bits (format, mode->x_res, mode->y_res, + (uint32_t *)dev_addr, -mode->stride); + + host_image = pixman_image_create_bits (format, + qxl->virtual_x, qxl->virtual_y, + NULL, mode->stride); +#if 0 + xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, + "testing dev_image memory (%d x %d)\n", + mode->x_res, mode->y_res); + memset(qxl->ram, 0, mode->stride * mode->y_res); + xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, + "testing host_image memory\n"); + memset(qxl->fb, 0, mode->stride * mode->y_res); +#endif + + surface = malloc (sizeof *surface); + surface->id = 0; + surface->dev_image = dev_image; + surface->host_image = host_image; + surface->cache = cache; + surface->qxl = qxl; + surface->bpp = mode->bits; + surface->next = NULL; + surface->prev = NULL; + surface->evacuated = NULL; + surface->bo = bo; + + REGION_INIT (NULL, &(surface->access_region), (BoxPtr)NULL, 0); + surface->access_type = UXA_ACCESS_RO; + + return surface; +} + +void * +qxl_surface_get_host_bits(qxl_surface_t *surface) +{ + if (!surface) + return NULL; + return (void *) pixman_image_get_data(surface->host_image); +} + + + + +static struct qxl_bo * +make_surface_cmd (surface_cache_t *cache, uint32_t id, QXLSurfaceCmdType type) +{ + struct qxl_bo *cmd_bo; + struct QXLSurfaceCmd *cmd; + qxl_screen_t *qxl = cache->qxl; + + cmd_bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof *cmd, "surface command"); + cmd = qxl->bo_funcs->bo_map(cmd_bo); + + cmd->release_info.id = pointer_to_u64 (cmd_bo) | 2; + cmd->type = type; + cmd->flags = 0; + cmd->surface_id = id; + + qxl->bo_funcs->bo_unmap(cmd_bo); + return cmd_bo; +} + +static void +push_surface_cmd (surface_cache_t *cache, struct qxl_bo *cmd_bo) +{ + qxl_screen_t *qxl = cache->qxl; + + qxl->bo_funcs->write_command (qxl, QXL_CMD_SURFACE, cmd_bo); +} + + +static qxl_surface_t * +surface_get_from_free_list (surface_cache_t *cache) +{ + qxl_surface_t *result = NULL; + + if (cache->free_surfaces) + { + qxl_surface_t *s; + + result = cache->free_surfaces; + cache->free_surfaces = cache->free_surfaces->next; + + result->next = NULL; + result->in_use = TRUE; + result->ref_count = 1; + result->pixmap = NULL; + + for (s = cache->free_surfaces; s; s = s->next) + { + if (s->id == result->id) + ErrorF ("huh: %d to be returned, but %d is in list\n", + s->id, result->id); + + assert (s->id != result->id); + } + } + + return result; +} + +static int +align (int x) +{ + return x; +} + +static qxl_surface_t * +surface_send_create (surface_cache_t *cache, + int width, + int height, + int bpp) +{ + SpiceBitmapFmt format; + pixman_format_code_t pformat; + struct QXLSurfaceCmd *cmd; + int stride; + uint32_t *dev_addr; + int n_attempts = 0; + qxl_screen_t *qxl = cache->qxl; + qxl_surface_t *surface; + struct qxl_bo *bo, *cmd_bo; + void *dev_ptr; + get_formats (bpp, &format, &pformat); + + width = align (width); + height = align (height); + + stride = width * PIXMAN_FORMAT_BPP (pformat) / 8; + stride = (stride + 3) & ~3; + + /* the final + stride is to work around a bug where the device apparently + * scribbles after the end of the image + */ + qxl_garbage_collect (qxl); +retry2: + bo = qxl_ums_surf_mem_alloc(qxl, stride * height + stride); + + if (!bo) + { + ErrorF ("- %dth attempt\n", n_attempts++); + + if (qxl_garbage_collect (qxl)) + goto retry2; + + ErrorF ("- OOM at %d %d %d (= %d bytes)\n", width, height, bpp, width * height * (bpp / 8)); + print_cache_info (cache); + + if (qxl_handle_oom (qxl)) + { + while (qxl_garbage_collect (qxl)) + ; + goto retry2; + } + + ErrorF ("Out of video memory: Could not allocate %d bytes\n", + stride * height + stride); + + return NULL; + } + +retry: + surface = surface_get_from_free_list (cache); + if (!surface) + { + if (!qxl_handle_oom (cache->qxl)) + { + ErrorF (" Out of surfaces\n"); + qxl->bo_funcs->bo_decref (qxl, bo); + return NULL; + } + else + goto retry; + } + + surface->bo = bo; + + cmd_bo = make_surface_cmd (cache, surface->id, QXL_SURFACE_CMD_CREATE); + + cmd = qxl->bo_funcs->bo_map(cmd_bo); + cmd->u.surface_create.format = format; + cmd->u.surface_create.width = width; + cmd->u.surface_create.height = height; + cmd->u.surface_create.stride = - stride; + qxl->bo_funcs->bo_unmap(cmd_bo); + + qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(struct QXLSurfaceCmd, u.surface_create.data), cmd_bo, surface->bo); + + push_surface_cmd (cache, cmd_bo); + + dev_ptr = qxl->bo_funcs->bo_map(surface->bo); + dev_addr + = (uint32_t *)((uint8_t *)dev_ptr + stride * (height - 1)); + + surface->dev_image = pixman_image_create_bits ( + pformat, width, height, dev_addr, - stride); + + surface->host_image = pixman_image_create_bits ( + pformat, width, height, NULL, -1); + + qxl->bo_funcs->bo_unmap(surface->bo); + surface->bpp = bpp; + + n_live++; + + return surface; +} + +qxl_surface_t * +qxl_surface_create (qxl_screen_t *qxl, + int width, + int height, + int bpp) +{ + qxl_surface_t *surface; + surface_cache_t *cache = qxl->surface_cache; + + if (!qxl->enable_surfaces) + return NULL; + + if ((bpp & 3) != 0) + { + ErrorF ("%s: Bad bpp: %d (%d)\n", __FUNCTION__, bpp, bpp & 7); + return NULL; + } + +#if 0 + if (bpp == 8) + { + static int warned; + if (!warned) + { + warned = 1; + ErrorF ("bpp == 8 triggers bugs in spice apparently\n"); + } + + return NULL; + } +#endif + + if (bpp != 8 && bpp != 16 && bpp != 32 && bpp != 24) + { + ErrorF ("%s: Unknown bpp\n", __FUNCTION__); + return NULL; + } + + if (width == 0 || height == 0) + { + ErrorF ("%s: Zero width or height\n", __FUNCTION__); + return NULL; + } + + if (!(surface = surface_get_from_cache (cache, width, height, bpp))) + if (!(surface = surface_send_create (cache, width, height, bpp))) + return NULL; + + surface->next = cache->live_surfaces; + surface->prev = NULL; + if (cache->live_surfaces) + cache->live_surfaces->prev = surface; + cache->live_surfaces = surface; + + return surface; +} + +void +qxl_surface_set_pixmap (qxl_surface_t *surface, PixmapPtr pixmap) +{ + surface->pixmap = pixmap; + + assert (get_surface (pixmap) == surface); +} + +static void +unlink_surface (qxl_surface_t *surface) +{ + if (surface->id != 0) + { + if (surface->prev) + surface->prev->next = surface->next; + else + surface->cache->live_surfaces = surface->next; + } + + debug_surface_log(surface->cache); + + if (surface->next) + surface->next->prev = surface->prev; + + surface->pixmap = NULL; + + surface->prev = NULL; + surface->next = NULL; +} + +static void +surface_destroy (qxl_surface_t *surface) +{ + struct qxl_bo *cmd_bo; + + if (surface->dev_image) + pixman_image_unref (surface->dev_image); + if (surface->host_image) + pixman_image_unref (surface->host_image); + +#if 0 + ErrorF("destroy %ld\n", (long int)surface->end - (long int)surface->address); +#endif + cmd_bo = make_surface_cmd (surface->cache, surface->id, QXL_SURFACE_CMD_DESTROY); + + push_surface_cmd (surface->cache, cmd_bo); + + surface->cache->qxl->bo_funcs->bo_decref(surface->cache->qxl, surface->bo); +} + +static void +surface_add_to_cache (qxl_surface_t *surface) +{ + surface_cache_t *cache = surface->cache; + int oldest = -1; + int n_surfaces = 0; + int i, delta; + int destroy_id = -1; + qxl_surface_t *destroy_surface = NULL; + + surface->ref_count++; + + for (i = 0; i < N_CACHED_SURFACES; ++i) + { + if (cache->cached_surfaces[i]) + { + oldest = i; + n_surfaces++; + } + } + + if (n_surfaces == N_CACHED_SURFACES) + { + destroy_id = cache->cached_surfaces[oldest]->id; + + destroy_surface = cache->cached_surfaces[oldest]; + + cache->cached_surfaces[oldest] = NULL; + + for (i = 0; i < N_CACHED_SURFACES; ++i) + assert (!cache->cached_surfaces[i] || + cache->cached_surfaces[i]->id != destroy_id); + } + + delta = 0; + for (i = N_CACHED_SURFACES - 1; i >= 0; i--) + { + if (cache->cached_surfaces[i]) + { + if (delta > 0) + { + cache->cached_surfaces[i + delta] = + cache->cached_surfaces[i]; + + assert (cache->cached_surfaces[i + delta]->id != destroy_id); + + cache->cached_surfaces[i] = NULL; + } + } + else + { + delta++; + } + } + + assert (delta > 0); + + cache->cached_surfaces[i + delta] = surface; + + for (i = 0; i < N_CACHED_SURFACES; ++i) + assert (!cache->cached_surfaces[i] || cache->cached_surfaces[i]->id != destroy_id); + + /* Note that sending a destroy command can trigger callbacks into + * this function (due to memory management), so we have to + * do this after updating the cache + */ + if (destroy_surface) + qxl_surface_unref (destroy_surface->cache, destroy_surface->id); +} + +void +qxl_surface_unref (surface_cache_t *cache, uint32_t id) +{ + if (id != 0) + { + qxl_surface_t *surface = cache->all_surfaces + id; + + if (--surface->ref_count == 0) + surface_destroy (surface); + } +} + +void +qxl_surface_kill (qxl_surface_t *surface) +{ + struct evacuated_surface_t *ev = surface->evacuated; + + if (ev) + { + /* server side surface is already destroyed (via reset), don't + * resend a destroy. Just mark surface as not to be recreated */ + ev->pixmap = NULL; + if (ev->image) + pixman_image_unref (ev->image); + if (ev->next) + ev->next->prev = ev->prev; + if (ev->prev) + ev->prev->next = ev->next; + free(ev); + surface->evacuated = NULL; + return; + } + + unlink_surface (surface); + + if (!surface->cache->all_surfaces) { + return; + } + + if (surface->id != 0 && + surface->host_image && + pixman_image_get_width (surface->host_image) >= 128 && + pixman_image_get_height (surface->host_image) >= 128) + { + surface_add_to_cache (surface); + } + + qxl_surface_unref (surface->cache, surface->id); +} + + +void * +qxl_surface_cache_evacuate_all (surface_cache_t *cache) +{ + evacuated_surface_t *evacuated_surfaces = NULL; + qxl_surface_t *s; + int i; + + for (i = 0; i < N_CACHED_SURFACES; ++i) + { + if (cache->cached_surfaces[i]) + { + surface_destroy (cache->cached_surfaces[i]); + cache->cached_surfaces[i] = NULL; + } + } + + s = cache->live_surfaces; + while (s != NULL) + { + qxl_surface_t *next = s->next; + evacuated_surface_t *evacuated = malloc (sizeof (evacuated_surface_t)); + int width, height; + + width = pixman_image_get_width (s->host_image); + height = pixman_image_get_height (s->host_image); + + qxl_download_box (s, 0, 0, width, height); + + evacuated->image = s->host_image; + evacuated->pixmap = s->pixmap; + + assert (get_surface (evacuated->pixmap) == s); + + evacuated->bpp = s->bpp; + + s->host_image = NULL; + + unlink_surface (s); + + evacuated->next = evacuated_surfaces; + if (evacuated_surfaces) + evacuated_surfaces->prev = evacuated; + evacuated_surfaces = evacuated; + s->evacuated = evacuated; + + s = next; + } + + cache->live_surfaces = NULL; + cache->free_surfaces = NULL; + + return evacuated_surfaces; +} + +void +qxl_surface_cache_replace_all (surface_cache_t *cache, void *data) +{ + evacuated_surface_t *ev; + + if (!surface_cache_init (cache, cache->qxl)) + { + /* FIXME: report the error */ + return; + } + + ev = data; + while (ev != NULL) + { + evacuated_surface_t *next = ev->next; + int width = pixman_image_get_width (ev->image); + int height = pixman_image_get_height (ev->image); + qxl_surface_t *surface; + + surface = qxl_surface_create (cache->qxl, width, height, ev->bpp); + + assert (surface->host_image); + assert (surface->dev_image); + + pixman_image_unref (surface->host_image); + surface->host_image = ev->image; + + upload_box (surface, 0, 0, width, height); + + set_surface (ev->pixmap, surface); + + qxl_surface_set_pixmap (surface, ev->pixmap); + + free (ev); + + ev = next; + } + + qxl_surface_cache_sanity_check (cache); + +} |