glamor_transfer.c revision 1b5d61b8
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_transfer.h"
25
26/* XXX a kludge for now */
27void
28glamor_format_for_pixmap(PixmapPtr pixmap, GLenum *format, GLenum *type)
29{
30    switch (pixmap->drawable.depth) {
31    case 24:
32    case 32:
33        *format = GL_BGRA;
34        *type = GL_UNSIGNED_INT_8_8_8_8_REV;
35        break;
36    case 30:
37        *format = GL_BGRA;
38        *type = GL_UNSIGNED_INT_2_10_10_10_REV;
39        break;
40    case 16:
41        *format = GL_RGB;
42        *type = GL_UNSIGNED_SHORT_5_6_5;
43        break;
44    case 15:
45        *format = GL_BGRA;
46        *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
47        break;
48    case 8:
49        *format = glamor_get_screen_private(pixmap->drawable.pScreen)->one_channel_format;
50        *type = GL_UNSIGNED_BYTE;
51        break;
52    default:
53        FatalError("Invalid pixmap depth %d\n", pixmap->drawable.depth);
54        break;
55    }
56}
57
58/*
59 * Write a region of bits into a pixmap
60 */
61void
62glamor_upload_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
63                    int dx_src, int dy_src,
64                    int dx_dst, int dy_dst,
65                    uint8_t *bits, uint32_t byte_stride)
66{
67    ScreenPtr                   screen = pixmap->drawable.pScreen;
68    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
69    glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
70    int                         box_index;
71    int                         bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
72    GLenum                      type;
73    GLenum                      format;
74
75    glamor_format_for_pixmap(pixmap, &format, &type);
76
77    glamor_make_current(glamor_priv);
78
79    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
80
81    if (glamor_priv->has_unpack_subimage)
82        glPixelStorei(GL_UNPACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
83
84    glamor_pixmap_loop(priv, box_index) {
85        BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
86        glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
87        BoxPtr                  boxes = in_boxes;
88        int                     nbox = in_nbox;
89
90        glamor_bind_texture(glamor_priv, GL_TEXTURE0, fbo, TRUE);
91
92        while (nbox--) {
93
94            /* compute drawable coordinates */
95            int x1 = MAX(boxes->x1 + dx_dst, box->x1);
96            int x2 = MIN(boxes->x2 + dx_dst, box->x2);
97            int y1 = MAX(boxes->y1 + dy_dst, box->y1);
98            int y2 = MIN(boxes->y2 + dy_dst, box->y2);
99
100            size_t ofs = (y1 - dy_dst + dy_src) * byte_stride;
101            ofs += (x1 - dx_dst + dx_src) * bytes_per_pixel;
102
103            boxes++;
104
105            if (x2 <= x1 || y2 <= y1)
106                continue;
107
108            if (glamor_priv->has_unpack_subimage ||
109                x2 - x1 == byte_stride / bytes_per_pixel) {
110                glTexSubImage2D(GL_TEXTURE_2D, 0,
111                                x1 - box->x1, y1 - box->y1,
112                                x2 - x1, y2 - y1,
113                                format, type,
114                                bits + ofs);
115            } else {
116                for (; y1 < y2; y1++, ofs += byte_stride)
117                    glTexSubImage2D(GL_TEXTURE_2D, 0,
118                                    x1 - box->x1, y1 - box->y1,
119                                    x2 - x1, 1,
120                                    format, type,
121                                    bits + ofs);
122            }
123        }
124    }
125
126    if (glamor_priv->has_unpack_subimage)
127        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
128}
129
130/*
131 * Upload a region of data
132 */
133
134void
135glamor_upload_region(PixmapPtr pixmap, RegionPtr region,
136                     int region_x, int region_y,
137                     uint8_t *bits, uint32_t byte_stride)
138{
139    glamor_upload_boxes(pixmap, RegionRects(region), RegionNumRects(region),
140                        -region_x, -region_y,
141                        0, 0,
142                        bits, byte_stride);
143}
144
145/*
146 * Take the data in the pixmap and stuff it back into the FBO
147 */
148void
149glamor_upload_pixmap(PixmapPtr pixmap)
150{
151    BoxRec box;
152
153    box.x1 = 0;
154    box.x2 = pixmap->drawable.width;
155    box.y1 = 0;
156    box.y2 = pixmap->drawable.height;
157    glamor_upload_boxes(pixmap, &box, 1, 0, 0, 0, 0,
158                        pixmap->devPrivate.ptr, pixmap->devKind);
159}
160
161/*
162 * Read stuff from the pixmap FBOs and write to memory
163 */
164void
165glamor_download_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
166                      int dx_src, int dy_src,
167                      int dx_dst, int dy_dst,
168                      uint8_t *bits, uint32_t byte_stride)
169{
170    ScreenPtr screen = pixmap->drawable.pScreen;
171    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
172    glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
173    int box_index;
174    int bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
175    GLenum type;
176    GLenum format;
177
178    glamor_format_for_pixmap(pixmap, &format, &type);
179
180    glamor_make_current(glamor_priv);
181
182    glPixelStorei(GL_PACK_ALIGNMENT, 4);
183    if (glamor_priv->has_pack_subimage)
184        glPixelStorei(GL_PACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
185
186    glamor_pixmap_loop(priv, box_index) {
187        BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
188        glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
189        BoxPtr                  boxes = in_boxes;
190        int                     nbox = in_nbox;
191
192        /* This should not be called on GLAMOR_FBO_NO_FBO-allocated pixmaps. */
193        assert(fbo->fb);
194        glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
195
196        while (nbox--) {
197
198            /* compute drawable coordinates */
199            int                     x1 = MAX(boxes->x1 + dx_src, box->x1);
200            int                     x2 = MIN(boxes->x2 + dx_src, box->x2);
201            int                     y1 = MAX(boxes->y1 + dy_src, box->y1);
202            int                     y2 = MIN(boxes->y2 + dy_src, box->y2);
203            size_t ofs = (y1 - dy_src + dy_dst) * byte_stride;
204            ofs += (x1 - dx_src + dx_dst) * bytes_per_pixel;
205
206            boxes++;
207
208            if (x2 <= x1 || y2 <= y1)
209                continue;
210
211            if (glamor_priv->has_pack_subimage ||
212                x2 - x1 == byte_stride / bytes_per_pixel) {
213                glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, y2 - y1, format, type, bits + ofs);
214            } else {
215                for (; y1 < y2; y1++, ofs += byte_stride)
216                    glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, 1, format, type, bits + ofs);
217            }
218        }
219    }
220    if (glamor_priv->has_pack_subimage)
221        glPixelStorei(GL_PACK_ROW_LENGTH, 0);
222}
223
224/*
225 * Read data from the pixmap FBO
226 */
227void
228glamor_download_rect(PixmapPtr pixmap, int x, int y, int w, int h, uint8_t *bits)
229{
230    BoxRec      box;
231
232    box.x1 = x;
233    box.x2 = x + w;
234    box.y1 = y;
235    box.y2 = y + h;
236
237    glamor_download_boxes(pixmap, &box, 1, 0, 0, -x, -y,
238                          bits, PixmapBytePad(w, pixmap->drawable.depth));
239}
240
241/*
242 * Pull the data from the FBO down to the pixmap
243 */
244void
245glamor_download_pixmap(PixmapPtr pixmap)
246{
247    BoxRec      box;
248
249    box.x1 = 0;
250    box.x2 = pixmap->drawable.width;
251    box.y1 = 0;
252    box.y2 = pixmap->drawable.height;
253
254    glamor_download_boxes(pixmap, &box, 1, 0, 0, 0, 0,
255                          pixmap->devPrivate.ptr, pixmap->devKind);
256}
257