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
34void
35exaCreateDriverPixmap_mixed(PixmapPtr pPixmap)
36{
37    ScreenPtr pScreen = pPixmap->drawable.pScreen;
38    ExaScreenPriv(pScreen);
39    ExaPixmapPriv(pPixmap);
40    int w = pPixmap->drawable.width, h = pPixmap->drawable.height;
41    int depth = pPixmap->drawable.depth, bpp = pPixmap->drawable.bitsPerPixel;
42    int usage_hint = pPixmap->usage_hint;
43    int paddedWidth = pExaPixmap->sys_pitch;
44
45    /* Already done. */
46    if (pExaPixmap->driverPriv)
47	return;
48
49    if (exaPixmapIsPinned(pPixmap))
50	return;
51
52    /* Can't accel 1/4 bpp. */
53    if (pExaPixmap->accel_blocked || bpp < 8)
54	return;
55
56    if (pExaScr->info->CreatePixmap2) {
57	int new_pitch = 0;
58        pExaPixmap->driverPriv = pExaScr->info->CreatePixmap2(pScreen, w, h, depth, usage_hint, bpp, &new_pitch);
59	paddedWidth = pExaPixmap->fb_pitch = new_pitch;
60    } else {
61	if (paddedWidth < pExaPixmap->fb_pitch)
62	    paddedWidth = pExaPixmap->fb_pitch;
63	pExaPixmap->driverPriv = pExaScr->info->CreatePixmap(pScreen, paddedWidth*h, 0);
64    }
65
66    if (!pExaPixmap->driverPriv)
67	return;
68
69    (*pScreen->ModifyPixmapHeader)(pPixmap, w, h, 0, 0,
70				paddedWidth, NULL);
71}
72
73void
74exaDoMigration_mixed(ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
75{
76    int i;
77
78    /* If anything is pinned in system memory, we won't be able to
79     * accelerate.
80     */
81    for (i = 0; i < npixmaps; i++) {
82	if (exaPixmapIsPinned (pixmaps[i].pPix) &&
83	    !exaPixmapHasGpuCopy (pixmaps[i].pPix))
84	{
85	    can_accel = FALSE;
86	    break;
87	}
88    }
89
90    /* We can do nothing. */
91    if (!can_accel)
92	return;
93
94    for (i = 0; i < npixmaps; i++) {
95	PixmapPtr pPixmap = pixmaps[i].pPix;
96	ExaPixmapPriv(pPixmap);
97
98	if (!pExaPixmap->driverPriv)
99	    exaCreateDriverPixmap_mixed(pPixmap);
100
101	if (pExaPixmap->pDamage && exaPixmapHasGpuCopy(pPixmap)) {
102	    ExaScreenPriv(pPixmap->drawable.pScreen);
103
104	    /* This pitch is needed for proper acceleration. For some reason
105	     * there are pixmaps without pDamage and a bad fb_pitch value.
106	     * So setting devKind when only exaPixmapHasGpuCopy() is true
107	     * causes corruption. Pixmaps without pDamage are not migrated
108	     * and should have a valid devKind at all times, so that's why this
109	     * isn't causing problems. Pixmaps have their gpu pitch set the
110	     * first time in the MPH call from exaCreateDriverPixmap_mixed().
111	     */
112	    pPixmap->devKind = pExaPixmap->fb_pitch;
113	    exaCopyDirtyToFb(pixmaps + i);
114
115	    if (pExaScr->deferred_mixed_pixmap == pPixmap &&
116		!pixmaps[i].as_dst && !pixmaps[i].pReg)
117		pExaScr->deferred_mixed_pixmap = NULL;
118	}
119
120	pExaPixmap->use_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
121    }
122}
123
124void
125exaMoveInPixmap_mixed(PixmapPtr pPixmap)
126{
127    ExaMigrationRec pixmaps[1];
128
129    pixmaps[0].as_dst = FALSE;
130    pixmaps[0].as_src = TRUE;
131    pixmaps[0].pPix = pPixmap;
132    pixmaps[0].pReg = NULL;
133
134    exaDoMigration(pixmaps, 1, TRUE);
135}
136
137void
138exaDamageReport_mixed(DamagePtr pDamage, RegionPtr pRegion, void *closure)
139{
140    PixmapPtr pPixmap = closure;
141    ExaPixmapPriv(pPixmap);
142
143    /* Move back results of software rendering on system memory copy of mixed driver
144     * pixmap (see exaPrepareAccessReg_mixed).
145     *
146     * Defer moving the destination back into the driver pixmap, to try and save
147     * overhead on multiple subsequent software fallbacks.
148     */
149    if (!pExaPixmap->use_gpu_copy && exaPixmapHasGpuCopy(pPixmap)) {
150	ExaScreenPriv(pPixmap->drawable.pScreen);
151
152	if (pExaScr->deferred_mixed_pixmap &&
153	    pExaScr->deferred_mixed_pixmap != pPixmap)
154	    exaMoveInPixmap_mixed(pExaScr->deferred_mixed_pixmap);
155	pExaScr->deferred_mixed_pixmap = pPixmap;
156    }
157}
158
159/* With mixed pixmaps, if we fail to get direct access to the driver pixmap, we
160 * use the DownloadFromScreen hook to retrieve contents to a copy in system
161 * memory, perform software rendering on that and move back the results with the
162 * UploadToScreen hook (see exaDamageReport_mixed).
163 */
164void
165exaPrepareAccessReg_mixed(PixmapPtr pPixmap, int index, RegionPtr pReg)
166{
167    ExaPixmapPriv(pPixmap);
168    Bool has_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
169    Bool success;
170
171    success = ExaDoPrepareAccess(pPixmap, index);
172
173    if (success && has_gpu_copy && pExaPixmap->pDamage) {
174	/* You cannot do accelerated operations while a buffer is mapped. */
175	exaFinishAccess(&pPixmap->drawable, index);
176	/* Update the gpu view of both deferred destination pixmaps and of
177	 * source pixmaps that were migrated with a bounding region.
178	 */
179	exaMoveInPixmap_mixed(pPixmap);
180	success = ExaDoPrepareAccess(pPixmap, index);
181
182	if (success) {
183	    /* We have a gpu pixmap that can be accessed, we don't need the cpu
184	     * copy anymore. Drivers that prefer DFS, should fail prepare
185	     * access.
186	     */
187	    DamageUnregister(&pPixmap->drawable, pExaPixmap->pDamage);
188	    DamageDestroy(pExaPixmap->pDamage);
189	    pExaPixmap->pDamage = NULL;
190
191	    free(pExaPixmap->sys_ptr);
192	    pExaPixmap->sys_ptr = NULL;
193
194	    return;
195	}
196    }
197
198    if (!success) {
199	ExaMigrationRec pixmaps[1];
200
201	/* Do we need to allocate our system buffer? */
202	if (!pExaPixmap->sys_ptr) {
203	    pExaPixmap->sys_ptr = malloc(pExaPixmap->sys_pitch *
204					 pPixmap->drawable.height);
205	    if (!pExaPixmap->sys_ptr)
206		FatalError("EXA: malloc failed for size %d bytes\n",
207			   pExaPixmap->sys_pitch * pPixmap->drawable.height);
208	}
209
210	if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) {
211	    pixmaps[0].as_dst = TRUE;
212	    pixmaps[0].as_src = FALSE;
213	} else {
214	    pixmaps[0].as_dst = FALSE;
215	    pixmaps[0].as_src = TRUE;
216	}
217	pixmaps[0].pPix = pPixmap;
218	pixmaps[0].pReg = pReg;
219
220	if (!pExaPixmap->pDamage &&
221		(has_gpu_copy || !exaPixmapIsPinned(pPixmap))) {
222	    Bool as_dst = pixmaps[0].as_dst;
223
224	    /* Set up damage tracking */
225	    pExaPixmap->pDamage = DamageCreate(exaDamageReport_mixed, NULL,
226					       DamageReportNonEmpty, TRUE,
227					       pPixmap->drawable.pScreen,
228					       pPixmap);
229
230	    DamageRegister(&pPixmap->drawable, pExaPixmap->pDamage);
231	    /* This ensures that pending damage reflects the current operation. */
232	    /* This is used by exa to optimize migration. */
233	    DamageSetReportAfterOp(pExaPixmap->pDamage, TRUE);
234
235	    if (has_gpu_copy) {
236		exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
237			       pPixmap->drawable.height);
238
239		/* We don't know which region of the destination will be damaged,
240		 * have to assume all of it
241		 */
242		if (as_dst) {
243		    pixmaps[0].as_dst = FALSE;
244		    pixmaps[0].as_src = TRUE;
245		    pixmaps[0].pReg = NULL;
246		}
247		exaCopyDirtyToSys(pixmaps);
248	    }
249
250	    if (as_dst)
251		exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
252			       pPixmap->drawable.height);
253	} else if (has_gpu_copy)
254	    exaCopyDirtyToSys(pixmaps);
255
256	pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
257	pPixmap->devKind = pExaPixmap->sys_pitch;
258	pExaPixmap->use_gpu_copy = FALSE;
259    }
260}
261
262