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