1/*
2 * Copyright © 2016 Broadcom
3 * Copyright © 2009 Intel Corporation
4 * Copyright © 1998 Keith Packard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 * IN THE SOFTWARE.
24 */
25
26/**
27 * @file glamor_picture.c
28 *
29 * Implements temporary uploads of GL_MEMORY Pixmaps to a texture that
30 * is swizzled appropriately for a given Render picture format.
31 * laid *
32 *
33 * This is important because GTK likes to use SHM Pixmaps for Render
34 * blending operations, and we don't want a blend operation to fall
35 * back to software (readback is more expensive than the upload we do
36 * here, and you'd have to re-upload the fallback output anyway).
37 */
38
39#include <stdlib.h>
40
41#include "glamor_priv.h"
42#include "mipict.h"
43
44static void byte_swap_swizzle(GLenum *swizzle)
45{
46    GLenum temp;
47
48    temp = swizzle[0];
49    swizzle[0] = swizzle[3];
50    swizzle[3] = temp;
51
52    temp = swizzle[1];
53    swizzle[1] = swizzle[2];
54    swizzle[2] = temp;
55}
56
57/**
58 * Returns the GL format and type for uploading our bits to a given PictFormat.
59 *
60 * We may need to tell the caller to translate the bits to another
61 * format, as in PICT_a1 (which GL doesn't support).  We may also need
62 * to tell the GL to swizzle the texture on sampling, because GLES3
63 * doesn't support the GL_UNSIGNED_INT_8_8_8_8{,_REV} types, so we
64 * don't have enough channel reordering options at upload time without
65 * it.
66 */
67static Bool
68glamor_get_tex_format_type_from_pictformat(ScreenPtr pScreen,
69                                           PictFormatShort format,
70                                           PictFormatShort *temp_format,
71                                           GLenum *tex_format,
72                                           GLenum *tex_type,
73                                           GLenum *swizzle)
74{
75    glamor_screen_private *glamor_priv = glamor_get_screen_private(pScreen);
76    Bool is_little_endian = IMAGE_BYTE_ORDER == LSBFirst;
77
78    *temp_format = format;
79    swizzle[0] = GL_RED;
80    swizzle[1] = GL_GREEN;
81    swizzle[2] = GL_BLUE;
82    swizzle[3] = GL_ALPHA;
83
84    switch (format) {
85    case PICT_a1:
86        *tex_format = glamor_priv->formats[1].format;
87        *tex_type = GL_UNSIGNED_BYTE;
88        *temp_format = PICT_a8;
89        break;
90
91    case PICT_b8g8r8x8:
92    case PICT_b8g8r8a8:
93        if (!glamor_priv->is_gles) {
94            *tex_format = GL_BGRA;
95            *tex_type = GL_UNSIGNED_INT_8_8_8_8;
96        } else {
97            *tex_format = GL_BGRA;
98            *tex_type = GL_UNSIGNED_BYTE;
99
100            swizzle[0] = GL_GREEN;
101            swizzle[1] = GL_BLUE;
102            swizzle[2] = GL_ALPHA;
103            swizzle[3] = GL_RED;
104
105            if (!is_little_endian)
106                byte_swap_swizzle(swizzle);
107        }
108        break;
109
110    case PICT_x8r8g8b8:
111    case PICT_a8r8g8b8:
112        if (!glamor_priv->is_gles) {
113            *tex_format = GL_BGRA;
114            *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV;
115        } else {
116            *tex_format = GL_BGRA;
117            *tex_type = GL_UNSIGNED_BYTE;
118
119            if (!is_little_endian)
120                byte_swap_swizzle(swizzle);
121            break;
122        }
123        break;
124
125    case PICT_x8b8g8r8:
126    case PICT_a8b8g8r8:
127        *tex_format = GL_RGBA;
128        if (!glamor_priv->is_gles) {
129            *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV;
130        } else {
131            *tex_format = GL_RGBA;
132            *tex_type = GL_UNSIGNED_BYTE;
133
134            if (!is_little_endian)
135                byte_swap_swizzle(swizzle);
136        }
137        break;
138
139    case PICT_x2r10g10b10:
140    case PICT_a2r10g10b10:
141        if (!glamor_priv->is_gles) {
142            *tex_format = GL_BGRA;
143            *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV;
144        } else {
145            return FALSE;
146        }
147        break;
148
149    case PICT_x2b10g10r10:
150    case PICT_a2b10g10r10:
151        if (!glamor_priv->is_gles) {
152            *tex_format = GL_RGBA;
153            *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV;
154        } else {
155            return FALSE;
156        }
157        break;
158
159    case PICT_r5g6b5:
160        *tex_format = GL_RGB;
161        *tex_type = GL_UNSIGNED_SHORT_5_6_5;
162        break;
163    case PICT_b5g6r5:
164        *tex_format = GL_RGB;
165        if (!glamor_priv->is_gles) {
166            *tex_type = GL_UNSIGNED_SHORT_5_6_5_REV;
167        } else {
168            *tex_type = GL_UNSIGNED_SHORT_5_6_5;
169            swizzle[0] = GL_BLUE;
170            swizzle[2] = GL_RED;
171        }
172        break;
173
174    case PICT_x1b5g5r5:
175    case PICT_a1b5g5r5:
176        *tex_format = GL_RGBA;
177        if (!glamor_priv->is_gles) {
178            *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
179        } else {
180            return FALSE;
181        }
182        break;
183
184    case PICT_x1r5g5b5:
185    case PICT_a1r5g5b5:
186        if (!glamor_priv->is_gles) {
187            *tex_format = GL_BGRA;
188            *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
189        } else {
190            return FALSE;
191        }
192        break;
193
194    case PICT_a8:
195        *tex_format = glamor_priv->formats[8].format;
196        *tex_type = GL_UNSIGNED_BYTE;
197        break;
198
199    case PICT_x4r4g4b4:
200    case PICT_a4r4g4b4:
201        if (!glamor_priv->is_gles) {
202            *tex_format = GL_BGRA;
203            *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
204        } else {
205            /* XXX */
206            *tex_format = GL_RGBA;
207            *tex_type = GL_UNSIGNED_SHORT_4_4_4_4;
208        }
209        break;
210
211    case PICT_x4b4g4r4:
212    case PICT_a4b4g4r4:
213        if (!glamor_priv->is_gles) {
214            *tex_format = GL_RGBA;
215            *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
216        } else {
217            /* XXX */
218            *tex_format = GL_RGBA;
219            *tex_type = GL_UNSIGNED_SHORT_4_4_4_4;
220        }
221        break;
222
223    default:
224        return FALSE;
225    }
226
227    if (!PICT_FORMAT_A(format))
228        swizzle[3] = GL_ONE;
229
230    return TRUE;
231}
232
233/**
234 * Takes a set of source bits with a given format and returns an
235 * in-memory pixman image of those bits in a destination format.
236 */
237static pixman_image_t *
238glamor_get_converted_image(PictFormatShort dst_format,
239                           PictFormatShort src_format,
240                           void *src_bits,
241                           int src_stride,
242                           int w, int h)
243{
244    pixman_image_t *dst_image;
245    pixman_image_t *src_image;
246
247    dst_image = pixman_image_create_bits(dst_format, w, h, NULL, 0);
248    if (dst_image == NULL) {
249        return NULL;
250    }
251
252    src_image = pixman_image_create_bits(src_format, w, h, src_bits, src_stride);
253
254    if (src_image == NULL) {
255        pixman_image_unref(dst_image);
256        return NULL;
257    }
258
259    pixman_image_composite(PictOpSrc, src_image, NULL, dst_image,
260                           0, 0, 0, 0, 0, 0, w, h);
261
262    pixman_image_unref(src_image);
263    return dst_image;
264}
265
266/**
267 * Uploads a picture based on a GLAMOR_MEMORY pixmap to a texture in a
268 * temporary FBO.
269 */
270Bool
271glamor_upload_picture_to_texture(PicturePtr picture)
272{
273    PixmapPtr pixmap = glamor_get_drawable_pixmap(picture->pDrawable);
274    ScreenPtr screen = pixmap->drawable.pScreen;
275    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
276    glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
277    PictFormatShort converted_format;
278    void *bits = pixmap->devPrivate.ptr;
279    int stride = pixmap->devKind;
280    GLenum format, type;
281    GLenum swizzle[4];
282    GLenum iformat;
283    Bool ret = TRUE;
284    Bool needs_swizzle;
285    pixman_image_t *converted_image = NULL;
286    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
287
288    assert(glamor_pixmap_is_memory(pixmap));
289    assert(!pixmap_priv->fbo);
290
291    glamor_make_current(glamor_priv);
292
293    /* No handling of large pixmap pictures here (would need to make
294     * an FBO array and split the uploads across it).
295     */
296    if (!glamor_check_fbo_size(glamor_priv,
297                               pixmap->drawable.width,
298                               pixmap->drawable.height)) {
299        return FALSE;
300    }
301
302    if (!glamor_get_tex_format_type_from_pictformat(screen,
303                                                    picture->format,
304                                                    &converted_format,
305                                                    &format,
306                                                    &type,
307                                                    swizzle)) {
308        glamor_fallback("Unknown pixmap depth %d.\n", pixmap->drawable.depth);
309        return FALSE;
310    }
311
312    needs_swizzle = (swizzle[0] != GL_RED ||
313                     swizzle[1] != GL_GREEN ||
314                     swizzle[2] != GL_BLUE ||
315                     swizzle[3] != GL_ALPHA);
316
317    if (!glamor_priv->has_texture_swizzle && needs_swizzle) {
318        glamor_fallback("Couldn't upload temporary picture due to missing "
319                        "GL_ARB_texture_swizzle.\n");
320        return FALSE;
321    }
322
323    if (converted_format != picture->format) {
324        converted_image = glamor_get_converted_image(converted_format,
325                                                     picture->format,
326                                                     bits, stride,
327                                                     pixmap->drawable.width,
328                                                     pixmap->drawable.height);
329        if (!converted_image)
330            return FALSE;
331
332        bits = pixman_image_get_data(converted_image);
333        stride = pixman_image_get_stride(converted_image);
334    }
335
336    if (!glamor_priv->is_gles)
337        iformat = f->internalformat;
338    else
339        iformat = format;
340
341    if (!glamor_pixmap_ensure_fbo(pixmap, GLAMOR_CREATE_FBO_NO_FBO)) {
342        ret = FALSE;
343        goto fail;
344    }
345
346    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
347
348    glamor_priv->suppress_gl_out_of_memory_logging = true;
349
350    /* We can't use glamor_pixmap_loop() because GLAMOR_MEMORY pixmaps
351     * don't have initialized boxes.
352     */
353    glBindTexture(GL_TEXTURE_2D, pixmap_priv->fbo->tex);
354    glTexImage2D(GL_TEXTURE_2D, 0, iformat,
355                 pixmap->drawable.width, pixmap->drawable.height, 0,
356                 format, type, bits);
357
358    if (needs_swizzle) {
359        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swizzle[0]);
360        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swizzle[1]);
361        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swizzle[2]);
362        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, swizzle[3]);
363    }
364
365    glamor_priv->suppress_gl_out_of_memory_logging = false;
366    if (glGetError() == GL_OUT_OF_MEMORY) {
367        ret = FALSE;
368    }
369
370fail:
371    if (converted_image)
372        pixman_image_unref(converted_image);
373
374    return ret;
375}
376