1/*
2 * Copyright © 2009 Maarten Maathuis
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25#ifdef HAVE_DIX_CONFIG_H
26#include <dix-config.h>
27#endif
28
29#include <string.h>
30
31#include "exa_priv.h"
32#include "exa.h"
33
34/* This file holds the driver allocated pixmaps + better initial placement code.
35 */
36
37static _X_INLINE void *
38ExaGetPixmapAddress(PixmapPtr p)
39{
40    ExaPixmapPriv(p);
41
42    return pExaPixmap->sys_ptr;
43}
44
45/**
46 * exaCreatePixmap() creates a new pixmap.
47 */
48PixmapPtr
49exaCreatePixmap_mixed(ScreenPtr pScreen, int w, int h, int depth,
50                      unsigned usage_hint)
51{
52    PixmapPtr pPixmap;
53    ExaPixmapPrivPtr pExaPixmap;
54    int bpp;
55    size_t paddedWidth;
56
57    ExaScreenPriv(pScreen);
58
59    if (w > 32767 || h > 32767)
60        return NullPixmap;
61
62    swap(pExaScr, pScreen, CreatePixmap);
63    pPixmap = pScreen->CreatePixmap(pScreen, 0, 0, depth, usage_hint);
64    swap(pExaScr, pScreen, CreatePixmap);
65
66    if (!pPixmap)
67        return NULL;
68
69    pExaPixmap = ExaGetPixmapPriv(pPixmap);
70    pExaPixmap->driverPriv = NULL;
71
72    bpp = pPixmap->drawable.bitsPerPixel;
73
74    paddedWidth = ((w * bpp + FB_MASK) >> FB_SHIFT) * sizeof(FbBits);
75    if (paddedWidth / 4 > 32767 || h > 32767)
76        return NullPixmap;
77
78    /* We will allocate the system pixmap later if needed. */
79    pPixmap->devPrivate.ptr = NULL;
80    pExaPixmap->sys_ptr = NULL;
81    pExaPixmap->sys_pitch = paddedWidth;
82
83    pExaPixmap->area = NULL;
84    pExaPixmap->fb_ptr = NULL;
85    pExaPixmap->pDamage = NULL;
86
87    exaSetFbPitch(pExaScr, pExaPixmap, w, h, bpp);
88    exaSetAccelBlock(pExaScr, pExaPixmap, w, h, bpp);
89
90    (*pScreen->ModifyPixmapHeader) (pPixmap, w, h, 0, 0, paddedWidth, NULL);
91
92    /* A scratch pixmap will become a driver pixmap right away. */
93    if (!w || !h) {
94        exaCreateDriverPixmap_mixed(pPixmap);
95        pExaPixmap->use_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
96    }
97    else {
98        pExaPixmap->use_gpu_copy = FALSE;
99
100        if (w == 1 && h == 1) {
101            pExaPixmap->sys_ptr = malloc(paddedWidth);
102
103            /* Set up damage tracking */
104            pExaPixmap->pDamage = DamageCreate(exaDamageReport_mixed, NULL,
105                                               DamageReportNonEmpty, TRUE,
106                                               pPixmap->drawable.pScreen,
107                                               pPixmap);
108
109            if (pExaPixmap->pDamage) {
110                DamageRegister(&pPixmap->drawable, pExaPixmap->pDamage);
111                /* This ensures that pending damage reflects the current
112                 * operation. This is used by exa to optimize migration.
113                 */
114                DamageSetReportAfterOp(pExaPixmap->pDamage, TRUE);
115            }
116        }
117    }
118
119    /* During a fallback we must prepare access. */
120    if (pExaScr->fallback_counter)
121        exaPrepareAccess(&pPixmap->drawable, EXA_PREPARE_AUX_DEST);
122
123    return pPixmap;
124}
125
126Bool
127exaModifyPixmapHeader_mixed(PixmapPtr pPixmap, int width, int height, int depth,
128                            int bitsPerPixel, int devKind, void *pPixData)
129{
130    ScreenPtr pScreen;
131    ExaScreenPrivPtr pExaScr;
132    ExaPixmapPrivPtr pExaPixmap;
133    Bool ret, has_gpu_copy;
134
135    if (!pPixmap)
136        return FALSE;
137
138    pScreen = pPixmap->drawable.pScreen;
139    pExaScr = ExaGetScreenPriv(pScreen);
140    pExaPixmap = ExaGetPixmapPriv(pPixmap);
141
142    if (pPixData) {
143        if (pExaPixmap->driverPriv) {
144            if (pExaPixmap->pDamage) {
145                DamageDestroy(pExaPixmap->pDamage);
146                pExaPixmap->pDamage = NULL;
147            }
148
149            pExaScr->info->DestroyPixmap(pScreen, pExaPixmap->driverPriv);
150            pExaPixmap->driverPriv = NULL;
151        }
152
153        pExaPixmap->use_gpu_copy = FALSE;
154        pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED;
155    }
156
157    has_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
158
159    if (width <= 0)
160        width = pPixmap->drawable.width;
161
162    if (height <= 0)
163        height = pPixmap->drawable.height;
164
165    if (bitsPerPixel <= 0) {
166        if (depth <= 0)
167            bitsPerPixel = pPixmap->drawable.bitsPerPixel;
168        else
169            bitsPerPixel = BitsPerPixel(depth);
170    }
171
172    if (depth <= 0)
173        depth = pPixmap->drawable.depth;
174
175    if (width != pPixmap->drawable.width ||
176        height != pPixmap->drawable.height ||
177        depth != pPixmap->drawable.depth ||
178        bitsPerPixel != pPixmap->drawable.bitsPerPixel) {
179        if (pExaPixmap->driverPriv) {
180            if (devKind > 0)
181                pExaPixmap->fb_pitch = devKind;
182            else
183                exaSetFbPitch(pExaScr, pExaPixmap, width, height, bitsPerPixel);
184
185            exaSetAccelBlock(pExaScr, pExaPixmap, width, height, bitsPerPixel);
186            RegionEmpty(&pExaPixmap->validFB);
187        }
188
189        /* Need to re-create system copy if there's also a GPU copy */
190        if (has_gpu_copy) {
191            if (pExaPixmap->sys_ptr) {
192                free(pExaPixmap->sys_ptr);
193                pExaPixmap->sys_ptr = NULL;
194                DamageDestroy(pExaPixmap->pDamage);
195                pExaPixmap->pDamage = NULL;
196                RegionEmpty(&pExaPixmap->validSys);
197
198                if (pExaScr->deferred_mixed_pixmap == pPixmap)
199                    pExaScr->deferred_mixed_pixmap = NULL;
200            }
201
202            pExaPixmap->sys_pitch = PixmapBytePad(width, depth);
203        }
204    }
205
206    if (has_gpu_copy) {
207        pPixmap->devPrivate.ptr = pExaPixmap->fb_ptr;
208        pPixmap->devKind = pExaPixmap->fb_pitch;
209    }
210    else {
211        pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
212        pPixmap->devKind = pExaPixmap->sys_pitch;
213    }
214
215    /* Only pass driver pixmaps to the driver. */
216    if (pExaScr->info->ModifyPixmapHeader && pExaPixmap->driverPriv) {
217        ret = pExaScr->info->ModifyPixmapHeader(pPixmap, width, height, depth,
218                                                bitsPerPixel, devKind,
219                                                pPixData);
220        if (ret == TRUE)
221            goto out;
222    }
223
224    swap(pExaScr, pScreen, ModifyPixmapHeader);
225    ret = pScreen->ModifyPixmapHeader(pPixmap, width, height, depth,
226                                      bitsPerPixel, devKind, pPixData);
227    swap(pExaScr, pScreen, ModifyPixmapHeader);
228
229 out:
230    if (has_gpu_copy) {
231        pExaPixmap->fb_ptr = pPixmap->devPrivate.ptr;
232        pExaPixmap->fb_pitch = pPixmap->devKind;
233    }
234    else {
235        pExaPixmap->sys_ptr = pPixmap->devPrivate.ptr;
236        pExaPixmap->sys_pitch = pPixmap->devKind;
237    }
238    /* Always NULL this, we don't want lingering pointers. */
239    pPixmap->devPrivate.ptr = NULL;
240
241    return ret;
242}
243
244Bool
245exaDestroyPixmap_mixed(PixmapPtr pPixmap)
246{
247    ScreenPtr pScreen = pPixmap->drawable.pScreen;
248
249    ExaScreenPriv(pScreen);
250    Bool ret;
251
252    if (pPixmap->refcnt == 1) {
253        ExaPixmapPriv(pPixmap);
254
255        exaDestroyPixmap(pPixmap);
256
257        if (pExaScr->deferred_mixed_pixmap == pPixmap)
258            pExaScr->deferred_mixed_pixmap = NULL;
259
260        if (pExaPixmap->driverPriv)
261            pExaScr->info->DestroyPixmap(pScreen, pExaPixmap->driverPriv);
262        pExaPixmap->driverPriv = NULL;
263
264        if (pExaPixmap->pDamage) {
265            free(pExaPixmap->sys_ptr);
266            pExaPixmap->sys_ptr = NULL;
267            pExaPixmap->pDamage = NULL;
268        }
269    }
270
271    swap(pExaScr, pScreen, DestroyPixmap);
272    ret = pScreen->DestroyPixmap(pPixmap);
273    swap(pExaScr, pScreen, DestroyPixmap);
274
275    return ret;
276}
277
278Bool
279exaPixmapHasGpuCopy_mixed(PixmapPtr pPixmap)
280{
281    ScreenPtr pScreen = pPixmap->drawable.pScreen;
282
283    ExaScreenPriv(pScreen);
284    ExaPixmapPriv(pPixmap);
285    void *saved_ptr;
286    Bool ret;
287
288    if (!pExaPixmap->driverPriv)
289        return FALSE;
290
291    saved_ptr = pPixmap->devPrivate.ptr;
292    pPixmap->devPrivate.ptr = ExaGetPixmapAddress(pPixmap);
293    ret = pExaScr->info->PixmapIsOffscreen(pPixmap);
294    pPixmap->devPrivate.ptr = saved_ptr;
295
296    return ret;
297}
298
299Bool
300exaSharePixmapBacking_mixed(PixmapPtr pPixmap, ScreenPtr secondary, void **handle_p)
301{
302    ScreenPtr pScreen = pPixmap->drawable.pScreen;
303    ExaScreenPriv(pScreen);
304    Bool ret = FALSE;
305
306    exaMoveInPixmap(pPixmap);
307    /* get the driver to give us a handle */
308    if (pExaScr->info->SharePixmapBacking)
309        ret = pExaScr->info->SharePixmapBacking(pPixmap, secondary, handle_p);
310
311    return ret;
312}
313
314Bool
315exaSetSharedPixmapBacking_mixed(PixmapPtr pPixmap, void *handle)
316{
317    ScreenPtr pScreen = pPixmap->drawable.pScreen;
318    ExaScreenPriv(pScreen);
319    Bool ret = FALSE;
320
321    if (pExaScr->info->SetSharedPixmapBacking)
322        ret = pExaScr->info->SetSharedPixmapBacking(pPixmap, handle);
323
324    if (ret == TRUE)
325        exaMoveInPixmap(pPixmap);
326
327    return ret;
328}
329
330
331