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