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