glamor_prepare.c revision 5a7dfde8
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            glamor_priv->suppress_gl_out_of_memory_logging = true;
92
93            glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo);
94            glBufferData(GL_PIXEL_PACK_BUFFER,
95                         pixmap->devKind * pixmap->drawable.height, NULL,
96                         gl_usage);
97
98            glamor_priv->suppress_gl_out_of_memory_logging = false;
99
100            if (glGetError() == GL_OUT_OF_MEMORY) {
101                if (!glamor_priv->logged_any_pbo_allocation_failure) {
102                    LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %d "
103                                   "bytes PBO due to GL_OUT_OF_MEMORY.\n",
104                                   pixmap->devKind * pixmap->drawable.height);
105                    glamor_priv->logged_any_pbo_allocation_failure = true;
106                }
107                glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
108                glDeleteBuffers(1, &priv->pbo);
109                priv->pbo = 0;
110            }
111        }
112
113        if (!priv->pbo) {
114            pixmap->devPrivate.ptr = xallocarray(pixmap->devKind,
115                                                 pixmap->drawable.height);
116            if (!pixmap->devPrivate.ptr)
117                return FALSE;
118        }
119        priv->map_access = access;
120    }
121
122    glamor_download_boxes(pixmap, RegionRects(&region), RegionNumRects(&region),
123                          0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
124
125    RegionUninit(&region);
126
127    if (priv->pbo) {
128        if (priv->map_access == GLAMOR_ACCESS_RW)
129            gl_access = GL_READ_WRITE;
130        else
131            gl_access = GL_READ_ONLY;
132
133        pixmap->devPrivate.ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, gl_access);
134        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
135    }
136
137    priv->prepared = TRUE;
138    return TRUE;
139}
140
141/*
142 * When we're done with the drawable, unmap the PBO, reupload
143 * if we were writing to it and then unbind it to release the memory
144 */
145
146static void
147glamor_fini_pixmap(PixmapPtr pixmap)
148{
149    glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
150
151    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
152        return;
153
154    if (!priv->prepared)
155        return;
156
157    if (priv->pbo) {
158        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, priv->pbo);
159        glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
160        pixmap->devPrivate.ptr = NULL;
161    }
162
163    if (priv->map_access == GLAMOR_ACCESS_RW) {
164        glamor_upload_boxes(pixmap,
165                            RegionRects(&priv->prepare_region),
166                            RegionNumRects(&priv->prepare_region),
167                            0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
168    }
169
170    RegionUninit(&priv->prepare_region);
171
172    if (priv->pbo) {
173        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
174        glDeleteBuffers(1, &priv->pbo);
175        priv->pbo = 0;
176    } else {
177        free(pixmap->devPrivate.ptr);
178        pixmap->devPrivate.ptr = NULL;
179    }
180
181    priv->prepared = FALSE;
182}
183
184Bool
185glamor_prepare_access(DrawablePtr drawable, glamor_access_t access)
186{
187    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
188    BoxRec box;
189    int off_x, off_y;
190
191    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
192
193    box.x1 = drawable->x + off_x;
194    box.x2 = box.x1 + drawable->width;
195    box.y1 = drawable->y + off_y;
196    box.y2 = box.y1 + drawable->height;
197    return glamor_prep_pixmap_box(pixmap, access, &box);
198}
199
200Bool
201glamor_prepare_access_box(DrawablePtr drawable, glamor_access_t access,
202                         int x, int y, int w, int h)
203{
204    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
205    BoxRec box;
206    int off_x, off_y;
207
208    glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
209    box.x1 = drawable->x + x + off_x;
210    box.x2 = box.x1 + w;
211    box.y1 = drawable->y + y + off_y;
212    box.y2 = box.y1 + h;
213    return glamor_prep_pixmap_box(pixmap, access, &box);
214}
215
216void
217glamor_finish_access(DrawablePtr drawable)
218{
219    glamor_fini_pixmap(glamor_get_drawable_pixmap(drawable));
220}
221
222/*
223 * Make a picture ready to use with fb.
224 */
225
226Bool
227glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access)
228{
229    if (!picture || !picture->pDrawable)
230        return TRUE;
231
232    return glamor_prepare_access(picture->pDrawable, access);
233}
234
235Bool
236glamor_prepare_access_picture_box(PicturePtr picture, glamor_access_t access,
237                        int x, int y, int w, int h)
238{
239    if (!picture || !picture->pDrawable)
240        return TRUE;
241
242    /* If a transform is set, we don't know what the bounds is on the
243     * source, so just prepare the whole pixmap.  XXX: We could
244     * potentially work out where in the source would be sampled based
245     * on the transform, and we don't need do do this for destination
246     * pixmaps at all.
247     */
248    if (picture->transform) {
249        return glamor_prepare_access_box(picture->pDrawable, access,
250                                         0, 0,
251                                         picture->pDrawable->width,
252                                         picture->pDrawable->height);
253    } else {
254        return glamor_prepare_access_box(picture->pDrawable, access,
255                                         x, y, w, h);
256    }
257}
258
259void
260glamor_finish_access_picture(PicturePtr picture)
261{
262    if (!picture || !picture->pDrawable)
263        return;
264
265    glamor_finish_access(picture->pDrawable);
266}
267
268/*
269 * Make a GC ready to use with fb. This just
270 * means making sure the appropriate fill pixmap is
271 * in CPU memory again
272 */
273
274Bool
275glamor_prepare_access_gc(GCPtr gc)
276{
277    switch (gc->fillStyle) {
278    case FillTiled:
279        return glamor_prepare_access(&gc->tile.pixmap->drawable,
280                                     GLAMOR_ACCESS_RO);
281    case FillStippled:
282    case FillOpaqueStippled:
283        return glamor_prepare_access(&gc->stipple->drawable, GLAMOR_ACCESS_RO);
284    }
285    return TRUE;
286}
287
288/*
289 * Free any temporary CPU pixmaps for the GC
290 */
291void
292glamor_finish_access_gc(GCPtr gc)
293{
294    switch (gc->fillStyle) {
295    case FillTiled:
296        glamor_finish_access(&gc->tile.pixmap->drawable);
297        break;
298    case FillStippled:
299    case FillOpaqueStippled:
300        glamor_finish_access(&gc->stipple->drawable);
301        break;
302    }
303}
304