glamor_fbo.c revision 1b5d61b8
1/*
2 * Copyright © 2009 Intel Corporation
3 * Copyright © 1998 Keith Packard
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Zhigang Gong <zhigang.gong@gmail.com>
26 *
27 */
28
29#include <stdlib.h>
30
31#include "glamor_priv.h"
32
33void
34glamor_destroy_fbo(glamor_screen_private *glamor_priv,
35                   glamor_pixmap_fbo *fbo)
36{
37    glamor_make_current(glamor_priv);
38
39    if (fbo->fb)
40        glDeleteFramebuffers(1, &fbo->fb);
41    if (fbo->tex)
42        glDeleteTextures(1, &fbo->tex);
43
44    free(fbo);
45}
46
47static int
48glamor_pixmap_ensure_fb(glamor_screen_private *glamor_priv,
49                        glamor_pixmap_fbo *fbo)
50{
51    int status, err = 0;
52
53    glamor_make_current(glamor_priv);
54
55    if (fbo->fb == 0)
56        glGenFramebuffers(1, &fbo->fb);
57    assert(fbo->tex != 0);
58    glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
59    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
60                           GL_TEXTURE_2D, fbo->tex, 0);
61    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
62    if (status != GL_FRAMEBUFFER_COMPLETE) {
63        const char *str;
64
65        switch (status) {
66        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
67            str = "incomplete attachment";
68            break;
69        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
70            str = "incomplete/missing attachment";
71            break;
72        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
73            str = "incomplete draw buffer";
74            break;
75        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
76            str = "incomplete read buffer";
77            break;
78        case GL_FRAMEBUFFER_UNSUPPORTED:
79            str = "unsupported";
80            break;
81        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
82            str = "incomplete multiple";
83            break;
84        default:
85            str = "unknown error";
86            break;
87        }
88
89        glamor_fallback("glamor: Failed to create fbo, %s\n", str);
90        err = -1;
91    }
92
93    return err;
94}
95
96glamor_pixmap_fbo *
97glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv,
98                           int w, int h, GLenum format, GLint tex, int flag)
99{
100    glamor_pixmap_fbo *fbo;
101
102    fbo = calloc(1, sizeof(*fbo));
103    if (fbo == NULL)
104        return NULL;
105
106    fbo->tex = tex;
107    fbo->width = w;
108    fbo->height = h;
109    fbo->format = format;
110
111    if (flag != GLAMOR_CREATE_FBO_NO_FBO) {
112        if (glamor_pixmap_ensure_fb(glamor_priv, fbo) != 0) {
113            glamor_destroy_fbo(glamor_priv, fbo);
114            fbo = NULL;
115        }
116    }
117
118    return fbo;
119}
120
121static int
122_glamor_create_tex(glamor_screen_private *glamor_priv,
123                   int w, int h, GLenum format)
124{
125    unsigned int tex;
126    GLenum iformat = format;
127
128    if (format == GL_RGB10_A2)
129        format = GL_RGBA;
130    glamor_make_current(glamor_priv);
131    glGenTextures(1, &tex);
132    glBindTexture(GL_TEXTURE_2D, tex);
133    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
134    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
135    if (format == glamor_priv->one_channel_format && format == GL_RED)
136        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
137    glamor_priv->suppress_gl_out_of_memory_logging = true;
138    glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0,
139                 format, GL_UNSIGNED_BYTE, NULL);
140    glamor_priv->suppress_gl_out_of_memory_logging = false;
141
142    if (glGetError() == GL_OUT_OF_MEMORY) {
143        if (!glamor_priv->logged_any_fbo_allocation_failure) {
144            LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %dx%d "
145                           "FBO due to GL_OUT_OF_MEMORY.\n", w, h);
146            LogMessageVerb(X_WARNING, 0,
147                           "glamor: Expect reduced performance.\n");
148            glamor_priv->logged_any_fbo_allocation_failure = true;
149        }
150        glDeleteTextures(1, &tex);
151        return 0;
152    }
153
154    return tex;
155}
156
157glamor_pixmap_fbo *
158glamor_create_fbo(glamor_screen_private *glamor_priv,
159                  int w, int h, GLenum format, int flag)
160{
161    GLint tex = _glamor_create_tex(glamor_priv, w, h, format);
162
163    if (!tex) /* Texture creation failed due to GL_OUT_OF_MEMORY */
164        return NULL;
165
166    return glamor_create_fbo_from_tex(glamor_priv, w, h, format, tex, flag);
167}
168
169/**
170 * Create storage for the w * h region, using FBOs of the GL's maximum
171 * supported size.
172 */
173glamor_pixmap_fbo *
174glamor_create_fbo_array(glamor_screen_private *glamor_priv,
175                         int w, int h, GLenum format, int flag,
176                         int block_w, int block_h,
177                         glamor_pixmap_private *priv)
178{
179    int block_wcnt;
180    int block_hcnt;
181    glamor_pixmap_fbo **fbo_array;
182    BoxPtr box_array;
183    int i, j;
184
185    priv->block_w = block_w;
186    priv->block_h = block_h;
187
188    block_wcnt = (w + block_w - 1) / block_w;
189    block_hcnt = (h + block_h - 1) / block_h;
190
191    box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0]));
192    if (box_array == NULL)
193        return NULL;
194
195    fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo *));
196    if (fbo_array == NULL) {
197        free(box_array);
198        return FALSE;
199    }
200    for (i = 0; i < block_hcnt; i++) {
201        int block_y1, block_y2;
202        int fbo_w, fbo_h;
203
204        block_y1 = i * block_h;
205        block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h);
206        fbo_h = block_y2 - block_y1;
207
208        for (j = 0; j < block_wcnt; j++) {
209            box_array[i * block_wcnt + j].x1 = j * block_w;
210            box_array[i * block_wcnt + j].y1 = block_y1;
211            box_array[i * block_wcnt + j].x2 =
212                (j + 1) * block_w > w ? w : (j + 1) * block_w;
213            box_array[i * block_wcnt + j].y2 = block_y2;
214            fbo_w =
215                box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt +
216                                                             j].x1;
217            fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv,
218                                                              fbo_w, fbo_h,
219                                                              format,
220                                                              GLAMOR_CREATE_PIXMAP_FIXUP);
221            if (fbo_array[i * block_wcnt + j] == NULL)
222                goto cleanup;
223        }
224    }
225
226    priv->box = box_array[0];
227    priv->box_array = box_array;
228    priv->fbo_array = fbo_array;
229    priv->block_wcnt = block_wcnt;
230    priv->block_hcnt = block_hcnt;
231    return fbo_array[0];
232
233 cleanup:
234    for (i = 0; i < block_wcnt * block_hcnt; i++)
235        if (fbo_array[i])
236            glamor_destroy_fbo(glamor_priv, fbo_array[i]);
237    free(box_array);
238    free(fbo_array);
239    return NULL;
240}
241
242glamor_pixmap_fbo *
243glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv)
244{
245    glamor_pixmap_fbo *fbo;
246
247    if (pixmap_priv == NULL)
248        return NULL;
249
250    fbo = pixmap_priv->fbo;
251    if (fbo == NULL)
252        return NULL;
253
254    pixmap_priv->fbo = NULL;
255    return fbo;
256}
257
258/* The pixmap must not be attached to another fbo. */
259void
260glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo)
261{
262    glamor_pixmap_private *pixmap_priv;
263
264    pixmap_priv = glamor_get_pixmap_private(pixmap);
265
266    if (pixmap_priv->fbo)
267        return;
268
269    pixmap_priv->fbo = fbo;
270
271    switch (pixmap_priv->type) {
272    case GLAMOR_TEXTURE_ONLY:
273    case GLAMOR_TEXTURE_DRM:
274        pixmap_priv->gl_fbo = GLAMOR_FBO_NORMAL;
275        pixmap->devPrivate.ptr = NULL;
276    default:
277        break;
278    }
279}
280
281void
282glamor_pixmap_destroy_fbo(PixmapPtr pixmap)
283{
284    ScreenPtr screen = pixmap->drawable.pScreen;
285    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
286    glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
287    glamor_pixmap_fbo *fbo;
288
289    if (glamor_pixmap_priv_is_large(priv)) {
290        int i;
291
292        for (i = 0; i < priv->block_wcnt * priv->block_hcnt; i++)
293            glamor_destroy_fbo(glamor_priv, priv->fbo_array[i]);
294        free(priv->fbo_array);
295        priv->fbo_array = NULL;
296    }
297    else {
298        fbo = glamor_pixmap_detach_fbo(priv);
299        if (fbo)
300            glamor_destroy_fbo(glamor_priv, fbo);
301    }
302}
303
304Bool
305glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag)
306{
307    glamor_screen_private *glamor_priv;
308    glamor_pixmap_private *pixmap_priv;
309    glamor_pixmap_fbo *fbo;
310
311    glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
312    pixmap_priv = glamor_get_pixmap_private(pixmap);
313    if (pixmap_priv->fbo == NULL) {
314
315        fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width,
316                                pixmap->drawable.height, format, flag);
317        if (fbo == NULL)
318            return FALSE;
319
320        glamor_pixmap_attach_fbo(pixmap, fbo);
321    }
322    else {
323        /* We do have a fbo, but it may lack of fb or tex. */
324        if (!pixmap_priv->fbo->tex)
325            pixmap_priv->fbo->tex =
326                _glamor_create_tex(glamor_priv, pixmap->drawable.width,
327                                   pixmap->drawable.height, format);
328
329        if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->fbo->fb == 0)
330            if (glamor_pixmap_ensure_fb(glamor_priv, pixmap_priv->fbo) != 0)
331                return FALSE;
332    }
333
334    return TRUE;
335}
336
337_X_EXPORT void
338glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back)
339{
340    glamor_pixmap_private *front_priv, *back_priv;
341    glamor_pixmap_fbo *temp_fbo;
342
343    front_priv = glamor_get_pixmap_private(front);
344    back_priv = glamor_get_pixmap_private(back);
345    temp_fbo = front_priv->fbo;
346    front_priv->fbo = back_priv->fbo;
347    back_priv->fbo = temp_fbo;
348}
349