1706f2543Smrg/*
2706f2543Smrg * Copyright � 2009 Maarten Maathuis
3706f2543Smrg *
4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5706f2543Smrg * copy of this software and associated documentation files (the "Software"),
6706f2543Smrg * to deal in the Software without restriction, including without limitation
7706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
9706f2543Smrg * Software is furnished to do so, subject to the following conditions:
10706f2543Smrg *
11706f2543Smrg * The above copyright notice and this permission notice (including the next
12706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the
13706f2543Smrg * Software.
14706f2543Smrg *
15706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20706f2543Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21706f2543Smrg * SOFTWARE.
22706f2543Smrg *
23706f2543Smrg */
24706f2543Smrg
25706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
26706f2543Smrg#include <dix-config.h>
27706f2543Smrg#endif
28706f2543Smrg
29706f2543Smrg#include <string.h>
30706f2543Smrg
31706f2543Smrg#include "exa_priv.h"
32706f2543Smrg#include "exa.h"
33706f2543Smrg
34706f2543Smrgvoid
35706f2543SmrgexaCreateDriverPixmap_mixed(PixmapPtr pPixmap)
36706f2543Smrg{
37706f2543Smrg    ScreenPtr pScreen = pPixmap->drawable.pScreen;
38706f2543Smrg    ExaScreenPriv(pScreen);
39706f2543Smrg    ExaPixmapPriv(pPixmap);
40706f2543Smrg    int w = pPixmap->drawable.width, h = pPixmap->drawable.height;
41706f2543Smrg    int depth = pPixmap->drawable.depth, bpp = pPixmap->drawable.bitsPerPixel;
42706f2543Smrg    int usage_hint = pPixmap->usage_hint;
43706f2543Smrg    int paddedWidth = pExaPixmap->sys_pitch;
44706f2543Smrg
45706f2543Smrg    /* Already done. */
46706f2543Smrg    if (pExaPixmap->driverPriv)
47706f2543Smrg	return;
48706f2543Smrg
49706f2543Smrg    if (exaPixmapIsPinned(pPixmap))
50706f2543Smrg	return;
51706f2543Smrg
52706f2543Smrg    /* Can't accel 1/4 bpp. */
53706f2543Smrg    if (pExaPixmap->accel_blocked || bpp < 8)
54706f2543Smrg	return;
55706f2543Smrg
56706f2543Smrg    if (pExaScr->info->CreatePixmap2) {
57706f2543Smrg	int new_pitch = 0;
58706f2543Smrg        pExaPixmap->driverPriv = pExaScr->info->CreatePixmap2(pScreen, w, h, depth, usage_hint, bpp, &new_pitch);
59706f2543Smrg	paddedWidth = pExaPixmap->fb_pitch = new_pitch;
60706f2543Smrg    } else {
61706f2543Smrg	if (paddedWidth < pExaPixmap->fb_pitch)
62706f2543Smrg	    paddedWidth = pExaPixmap->fb_pitch;
63706f2543Smrg	pExaPixmap->driverPriv = pExaScr->info->CreatePixmap(pScreen, paddedWidth*h, 0);
64706f2543Smrg    }
65706f2543Smrg
66706f2543Smrg    if (!pExaPixmap->driverPriv)
67706f2543Smrg	return;
68706f2543Smrg
69706f2543Smrg    (*pScreen->ModifyPixmapHeader)(pPixmap, w, h, 0, 0,
70706f2543Smrg				paddedWidth, NULL);
71706f2543Smrg}
72706f2543Smrg
73706f2543Smrgvoid
74706f2543SmrgexaDoMigration_mixed(ExaMigrationPtr pixmaps, int npixmaps, Bool can_accel)
75706f2543Smrg{
76706f2543Smrg    int i;
77706f2543Smrg
78706f2543Smrg    /* If anything is pinned in system memory, we won't be able to
79706f2543Smrg     * accelerate.
80706f2543Smrg     */
81706f2543Smrg    for (i = 0; i < npixmaps; i++) {
82706f2543Smrg	if (exaPixmapIsPinned (pixmaps[i].pPix) &&
83706f2543Smrg	    !exaPixmapHasGpuCopy (pixmaps[i].pPix))
84706f2543Smrg	{
85706f2543Smrg	    can_accel = FALSE;
86706f2543Smrg	    break;
87706f2543Smrg	}
88706f2543Smrg    }
89706f2543Smrg
90706f2543Smrg    /* We can do nothing. */
91706f2543Smrg    if (!can_accel)
92706f2543Smrg	return;
93706f2543Smrg
94706f2543Smrg    for (i = 0; i < npixmaps; i++) {
95706f2543Smrg	PixmapPtr pPixmap = pixmaps[i].pPix;
96706f2543Smrg	ExaPixmapPriv(pPixmap);
97706f2543Smrg
98706f2543Smrg	if (!pExaPixmap->driverPriv)
99706f2543Smrg	    exaCreateDriverPixmap_mixed(pPixmap);
100706f2543Smrg
101706f2543Smrg	if (pExaPixmap->pDamage && exaPixmapHasGpuCopy(pPixmap)) {
102706f2543Smrg	    ExaScreenPriv(pPixmap->drawable.pScreen);
103706f2543Smrg
104706f2543Smrg	    /* This pitch is needed for proper acceleration. For some reason
105706f2543Smrg	     * there are pixmaps without pDamage and a bad fb_pitch value.
106706f2543Smrg	     * So setting devKind when only exaPixmapHasGpuCopy() is true
107706f2543Smrg	     * causes corruption. Pixmaps without pDamage are not migrated
108706f2543Smrg	     * and should have a valid devKind at all times, so that's why this
109706f2543Smrg	     * isn't causing problems. Pixmaps have their gpu pitch set the
110706f2543Smrg	     * first time in the MPH call from exaCreateDriverPixmap_mixed().
111706f2543Smrg	     */
112706f2543Smrg	    pPixmap->devKind = pExaPixmap->fb_pitch;
113706f2543Smrg	    exaCopyDirtyToFb(pixmaps + i);
114706f2543Smrg
115706f2543Smrg	    if (pExaScr->deferred_mixed_pixmap == pPixmap &&
116706f2543Smrg		!pixmaps[i].as_dst && !pixmaps[i].pReg)
117706f2543Smrg		pExaScr->deferred_mixed_pixmap = NULL;
118706f2543Smrg	}
119706f2543Smrg
120706f2543Smrg	pExaPixmap->use_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
121706f2543Smrg    }
122706f2543Smrg}
123706f2543Smrg
124706f2543Smrgvoid
125706f2543SmrgexaMoveInPixmap_mixed(PixmapPtr pPixmap)
126706f2543Smrg{
127706f2543Smrg    ExaMigrationRec pixmaps[1];
128706f2543Smrg
129706f2543Smrg    pixmaps[0].as_dst = FALSE;
130706f2543Smrg    pixmaps[0].as_src = TRUE;
131706f2543Smrg    pixmaps[0].pPix = pPixmap;
132706f2543Smrg    pixmaps[0].pReg = NULL;
133706f2543Smrg
134706f2543Smrg    exaDoMigration(pixmaps, 1, TRUE);
135706f2543Smrg}
136706f2543Smrg
137706f2543Smrgvoid
138706f2543SmrgexaDamageReport_mixed(DamagePtr pDamage, RegionPtr pRegion, void *closure)
139706f2543Smrg{
140706f2543Smrg    PixmapPtr pPixmap = closure;
141706f2543Smrg    ExaPixmapPriv(pPixmap);
142706f2543Smrg
143706f2543Smrg    /* Move back results of software rendering on system memory copy of mixed driver
144706f2543Smrg     * pixmap (see exaPrepareAccessReg_mixed).
145706f2543Smrg     *
146706f2543Smrg     * Defer moving the destination back into the driver pixmap, to try and save
147706f2543Smrg     * overhead on multiple subsequent software fallbacks.
148706f2543Smrg     */
149706f2543Smrg    if (!pExaPixmap->use_gpu_copy && exaPixmapHasGpuCopy(pPixmap)) {
150706f2543Smrg	ExaScreenPriv(pPixmap->drawable.pScreen);
151706f2543Smrg
152706f2543Smrg	if (pExaScr->deferred_mixed_pixmap &&
153706f2543Smrg	    pExaScr->deferred_mixed_pixmap != pPixmap)
154706f2543Smrg	    exaMoveInPixmap_mixed(pExaScr->deferred_mixed_pixmap);
155706f2543Smrg	pExaScr->deferred_mixed_pixmap = pPixmap;
156706f2543Smrg    }
157706f2543Smrg}
158706f2543Smrg
159706f2543Smrg/* With mixed pixmaps, if we fail to get direct access to the driver pixmap, we
160706f2543Smrg * use the DownloadFromScreen hook to retrieve contents to a copy in system
161706f2543Smrg * memory, perform software rendering on that and move back the results with the
162706f2543Smrg * UploadToScreen hook (see exaDamageReport_mixed).
163706f2543Smrg */
164706f2543Smrgvoid
165706f2543SmrgexaPrepareAccessReg_mixed(PixmapPtr pPixmap, int index, RegionPtr pReg)
166706f2543Smrg{
167706f2543Smrg    ExaPixmapPriv(pPixmap);
168706f2543Smrg    Bool has_gpu_copy = exaPixmapHasGpuCopy(pPixmap);
169706f2543Smrg    Bool success;
170706f2543Smrg
171706f2543Smrg    success = ExaDoPrepareAccess(pPixmap, index);
172706f2543Smrg
173706f2543Smrg    if (success && has_gpu_copy && pExaPixmap->pDamage) {
174706f2543Smrg	/* You cannot do accelerated operations while a buffer is mapped. */
175706f2543Smrg	exaFinishAccess(&pPixmap->drawable, index);
176706f2543Smrg	/* Update the gpu view of both deferred destination pixmaps and of
177706f2543Smrg	 * source pixmaps that were migrated with a bounding region.
178706f2543Smrg	 */
179706f2543Smrg	exaMoveInPixmap_mixed(pPixmap);
180706f2543Smrg	success = ExaDoPrepareAccess(pPixmap, index);
181706f2543Smrg
182706f2543Smrg	if (success) {
183706f2543Smrg	    /* We have a gpu pixmap that can be accessed, we don't need the cpu
184706f2543Smrg	     * copy anymore. Drivers that prefer DFS, should fail prepare
185706f2543Smrg	     * access.
186706f2543Smrg	     */
187706f2543Smrg	    DamageUnregister(&pPixmap->drawable, pExaPixmap->pDamage);
188706f2543Smrg	    DamageDestroy(pExaPixmap->pDamage);
189706f2543Smrg	    pExaPixmap->pDamage = NULL;
190706f2543Smrg
191706f2543Smrg	    free(pExaPixmap->sys_ptr);
192706f2543Smrg	    pExaPixmap->sys_ptr = NULL;
193706f2543Smrg
194706f2543Smrg	    return;
195706f2543Smrg	}
196706f2543Smrg    }
197706f2543Smrg
198706f2543Smrg    if (!success) {
199706f2543Smrg	ExaMigrationRec pixmaps[1];
200706f2543Smrg
201706f2543Smrg	/* Do we need to allocate our system buffer? */
202706f2543Smrg	if (!pExaPixmap->sys_ptr) {
203706f2543Smrg	    pExaPixmap->sys_ptr = malloc(pExaPixmap->sys_pitch *
204706f2543Smrg					 pPixmap->drawable.height);
205706f2543Smrg	    if (!pExaPixmap->sys_ptr)
206706f2543Smrg		FatalError("EXA: malloc failed for size %d bytes\n",
207706f2543Smrg			   pExaPixmap->sys_pitch * pPixmap->drawable.height);
208706f2543Smrg	}
209706f2543Smrg
210706f2543Smrg	if (index == EXA_PREPARE_DEST || index == EXA_PREPARE_AUX_DEST) {
211706f2543Smrg	    pixmaps[0].as_dst = TRUE;
212706f2543Smrg	    pixmaps[0].as_src = FALSE;
213706f2543Smrg	} else {
214706f2543Smrg	    pixmaps[0].as_dst = FALSE;
215706f2543Smrg	    pixmaps[0].as_src = TRUE;
216706f2543Smrg	}
217706f2543Smrg	pixmaps[0].pPix = pPixmap;
218706f2543Smrg	pixmaps[0].pReg = pReg;
219706f2543Smrg
220706f2543Smrg	if (!pExaPixmap->pDamage &&
221706f2543Smrg		(has_gpu_copy || !exaPixmapIsPinned(pPixmap))) {
222706f2543Smrg	    Bool as_dst = pixmaps[0].as_dst;
223706f2543Smrg
224706f2543Smrg	    /* Set up damage tracking */
225706f2543Smrg	    pExaPixmap->pDamage = DamageCreate(exaDamageReport_mixed, NULL,
226706f2543Smrg					       DamageReportNonEmpty, TRUE,
227706f2543Smrg					       pPixmap->drawable.pScreen,
228706f2543Smrg					       pPixmap);
229706f2543Smrg
230706f2543Smrg	    DamageRegister(&pPixmap->drawable, pExaPixmap->pDamage);
231706f2543Smrg	    /* This ensures that pending damage reflects the current operation. */
232706f2543Smrg	    /* This is used by exa to optimize migration. */
233706f2543Smrg	    DamageSetReportAfterOp(pExaPixmap->pDamage, TRUE);
234706f2543Smrg
235706f2543Smrg	    if (has_gpu_copy) {
236706f2543Smrg		exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
237706f2543Smrg			       pPixmap->drawable.height);
238706f2543Smrg
239706f2543Smrg		/* We don't know which region of the destination will be damaged,
240706f2543Smrg		 * have to assume all of it
241706f2543Smrg		 */
242706f2543Smrg		if (as_dst) {
243706f2543Smrg		    pixmaps[0].as_dst = FALSE;
244706f2543Smrg		    pixmaps[0].as_src = TRUE;
245706f2543Smrg		    pixmaps[0].pReg = NULL;
246706f2543Smrg		}
247706f2543Smrg		exaCopyDirtyToSys(pixmaps);
248706f2543Smrg	    }
249706f2543Smrg
250706f2543Smrg	    if (as_dst)
251706f2543Smrg		exaPixmapDirty(pPixmap, 0, 0, pPixmap->drawable.width,
252706f2543Smrg			       pPixmap->drawable.height);
253706f2543Smrg	} else if (has_gpu_copy)
254706f2543Smrg	    exaCopyDirtyToSys(pixmaps);
255706f2543Smrg
256706f2543Smrg	pPixmap->devPrivate.ptr = pExaPixmap->sys_ptr;
257706f2543Smrg	pPixmap->devKind = pExaPixmap->sys_pitch;
258706f2543Smrg	pExaPixmap->use_gpu_copy = FALSE;
259706f2543Smrg    }
260706f2543Smrg}
261706f2543Smrg
262