glamor_prepare.c revision 35c4bbdf
1/*
2 * Copyright © 2014 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "glamor_priv.h"
24#include "glamor_prepare.h"
25#include "glamor_transfer.h"
26
27/*
28 * Make a pixmap ready to draw with fb by
29 * creating a PBO large enough for the whole object
30 * and downloading all of the FBOs into it.
31 */
32
33static Bool
34glamor_prep_pixmap_box(PixmapPtr pixmap, glamor_access_t access, BoxPtr box)
35{
36    ScreenPtr                   screen = pixmap->drawable.pScreen;
37    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
38    glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
39    int                         gl_access, gl_usage;
40    RegionRec                   region;
41
42    if (priv->type == GLAMOR_DRM_ONLY)
43        return FALSE;
44
45    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
46        return TRUE;
47
48    glamor_make_current(glamor_priv);
49
50    RegionInit(&region, box, 1);
51
52    /* See if it's already mapped */
53    if (pixmap->devPrivate.ptr) {
54        /*
55         * Someone else has mapped this pixmap;
56         * we'll assume that it's directly mapped
57         * by a lower level driver
58         */
59        if (!priv->prepared)
60            return TRUE;
61
62        /* In X, multiple Drawables can be stored in the same Pixmap (such as
63         * each individual window in a non-composited screen pixmap, or the
64         * reparented window contents inside the window-manager-decorated window
65         * pixmap on a composited screen).
66         *
67         * As a result, when doing a series of mappings for a fallback, we may
68         * need to add more boxes to the set of data we've downloaded, as we go.
69         */
70        RegionSubtract(&region, &region, &priv->prepare_region);
71        if (!RegionNotEmpty(&region))
72            return TRUE;
73
74        if (access == GLAMOR_ACCESS_RW)
75            FatalError("attempt to remap buffer as writable");
76
77        if (priv->pbo) {
78            glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo);
79            glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
80            pixmap->devPrivate.ptr = NULL;
81        }
82    } else {
83        RegionInit(&priv->prepare_region, box, 1);
84
85        if (glamor_priv->has_rw_pbo) {
86            if (priv->pbo == 0)
87                glGenBuffers(1, &priv->pbo);
88
89            gl_usage = GL_STREAM_READ;
90
91            glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo);
92            glBufferData(GL_PIXEL_PACK_BUFFER,
93                         pixmap->devKind * pixmap->drawable.height, NULL,
94                         gl_usage);
95        } else {
96            pixmap->devPrivate.ptr = xallocarray(pixmap->devKind,
97                                                 pixmap->drawable.height);
98            if (!pixmap->devPrivate.ptr)
99                return FALSE;
100        }
101        priv->map_access = access;
102    }
103
104    glamor_download_boxes(pixmap, RegionRects(&region), RegionNumRects(&region),
105                          0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
106
107    RegionUninit(&region);
108
109    if (glamor_priv->has_rw_pbo) {
110        if (priv->map_access == GLAMOR_ACCESS_RW)
111            gl_access = GL_READ_WRITE;
112        else
113            gl_access = GL_READ_ONLY;
114
115        pixmap->devPrivate.ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, gl_access);
116        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
117    }
118
119    priv->prepared = TRUE;
120    return TRUE;
121}
122
123/*
124 * When we're done with the drawable, unmap the PBO, reupload
125 * if we were writing to it and then unbind it to release the memory
126 */
127
128static void
129glamor_fini_pixmap(PixmapPtr pixmap)
130{
131    ScreenPtr                   screen = pixmap->drawable.pScreen;
132    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
133    glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
134
135    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
136        return;
137
138    if (!priv->prepared)
139        return;
140
141    if (glamor_priv->has_rw_pbo) {
142        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, priv->pbo);
143        glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
144        pixmap->devPrivate.ptr = NULL;
145    }
146
147    if (priv->map_access == GLAMOR_ACCESS_RW) {
148        glamor_upload_boxes(pixmap,
149                            RegionRects(&priv->prepare_region),
150                            RegionNumRects(&priv->prepare_region),
151                            0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
152    }
153
154    RegionUninit(&priv->prepare_region);
155
156    if (glamor_priv->has_rw_pbo) {
157        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
158        glDeleteBuffers(1, &priv->pbo);
159        priv->pbo = 0;
160    } else {
161        free(pixmap->devPrivate.ptr);
162        pixmap->devPrivate.ptr = NULL;
163    }
164
165    priv->prepared = FALSE;
166}
167
168Bool
169glamor_prepare_access(DrawablePtr drawable, glamor_access_t access)
170{
171    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
172    BoxRec box;
173    int off_x, off_y;
174
175    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
176
177    box.x1 = drawable->x + off_x;
178    box.x2 = box.x1 + drawable->width;
179    box.y1 = drawable->y + off_y;
180    box.y2 = box.y1 + drawable->height;
181    return glamor_prep_pixmap_box(pixmap, access, &box);
182}
183
184Bool
185glamor_prepare_access_box(DrawablePtr drawable, glamor_access_t access,
186                         int x, int y, int w, int h)
187{
188    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
189    BoxRec box;
190    int off_x, off_y;
191
192    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
193    box.x1 = drawable->x + x + off_x;
194    box.x2 = box.x1 + w;
195    box.y1 = drawable->y + y + off_y;
196    box.y2 = box.y1 + h;
197    return glamor_prep_pixmap_box(pixmap, access, &box);
198}
199
200void
201glamor_finish_access(DrawablePtr drawable)
202{
203    glamor_fini_pixmap(glamor_get_drawable_pixmap(drawable));
204}
205
206/*
207 * Make a picture ready to use with fb.
208 */
209
210Bool
211glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access)
212{
213    if (!picture || !picture->pDrawable)
214        return TRUE;
215
216    return glamor_prepare_access(picture->pDrawable, access);
217}
218
219Bool
220glamor_prepare_access_picture_box(PicturePtr picture, glamor_access_t access,
221                        int x, int y, int w, int h)
222{
223    if (!picture || !picture->pDrawable)
224        return TRUE;
225
226    /* If a transform is set, we don't know what the bounds is on the
227     * source, so just prepare the whole pixmap.  XXX: We could
228     * potentially work out where in the source would be sampled based
229     * on the transform, and we don't need do do this for destination
230     * pixmaps at all.
231     */
232    if (picture->transform) {
233        return glamor_prepare_access_box(picture->pDrawable, access,
234                                         0, 0,
235                                         picture->pDrawable->width,
236                                         picture->pDrawable->height);
237    } else {
238        return glamor_prepare_access_box(picture->pDrawable, access,
239                                         x, y, w, h);
240    }
241}
242
243void
244glamor_finish_access_picture(PicturePtr picture)
245{
246    if (!picture || !picture->pDrawable)
247        return;
248
249    glamor_finish_access(picture->pDrawable);
250}
251
252/*
253 * Make a GC ready to use with fb. This just
254 * means making sure the appropriate fill pixmap is
255 * in CPU memory again
256 */
257
258Bool
259glamor_prepare_access_gc(GCPtr gc)
260{
261    switch (gc->fillStyle) {
262    case FillTiled:
263        return glamor_prepare_access(&gc->tile.pixmap->drawable,
264                                     GLAMOR_ACCESS_RO);
265    case FillStippled:
266    case FillOpaqueStippled:
267        return glamor_prepare_access(&gc->stipple->drawable, GLAMOR_ACCESS_RO);
268    }
269    return TRUE;
270}
271
272/*
273 * Free any temporary CPU pixmaps for the GC
274 */
275void
276glamor_finish_access_gc(GCPtr gc)
277{
278    switch (gc->fillStyle) {
279    case FillTiled:
280        glamor_finish_access(&gc->tile.pixmap->drawable);
281        break;
282    case FillStippled:
283    case FillOpaqueStippled:
284        glamor_finish_access(&gc->stipple->drawable);
285        break;
286    }
287}
288