glamor_fbo.c revision 5a7dfde8
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
242void
243glamor_pixmap_clear_fbo(glamor_screen_private *glamor_priv, glamor_pixmap_fbo *fbo)
244{
245    glamor_make_current(glamor_priv);
246
247    assert(fbo->fb != 0 && fbo->tex != 0);
248
249    glamor_set_destination_pixmap_fbo(glamor_priv, fbo, 0, 0, fbo->width, fbo->height);
250    glClearColor(0.0, 0.0, 0.0, 0.0);
251    glClear(GL_COLOR_BUFFER_BIT);
252}
253
254glamor_pixmap_fbo *
255glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv)
256{
257    glamor_pixmap_fbo *fbo;
258
259    if (pixmap_priv == NULL)
260        return NULL;
261
262    fbo = pixmap_priv->fbo;
263    if (fbo == NULL)
264        return NULL;
265
266    pixmap_priv->fbo = NULL;
267    return fbo;
268}
269
270/* The pixmap must not be attached to another fbo. */
271void
272glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo)
273{
274    glamor_pixmap_private *pixmap_priv;
275
276    pixmap_priv = glamor_get_pixmap_private(pixmap);
277
278    if (pixmap_priv->fbo)
279        return;
280
281    pixmap_priv->fbo = fbo;
282
283    switch (pixmap_priv->type) {
284    case GLAMOR_TEXTURE_ONLY:
285    case GLAMOR_TEXTURE_DRM:
286        pixmap_priv->gl_fbo = GLAMOR_FBO_NORMAL;
287        pixmap->devPrivate.ptr = NULL;
288    default:
289        break;
290    }
291}
292
293void
294glamor_pixmap_destroy_fbo(PixmapPtr pixmap)
295{
296    ScreenPtr screen = pixmap->drawable.pScreen;
297    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
298    glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
299    glamor_pixmap_fbo *fbo;
300
301    if (glamor_pixmap_priv_is_large(priv)) {
302        int i;
303
304        for (i = 0; i < priv->block_wcnt * priv->block_hcnt; i++)
305            glamor_destroy_fbo(glamor_priv, priv->fbo_array[i]);
306        free(priv->fbo_array);
307        priv->fbo_array = NULL;
308    }
309    else {
310        fbo = glamor_pixmap_detach_fbo(priv);
311        if (fbo)
312            glamor_destroy_fbo(glamor_priv, fbo);
313    }
314}
315
316Bool
317glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag)
318{
319    glamor_screen_private *glamor_priv;
320    glamor_pixmap_private *pixmap_priv;
321    glamor_pixmap_fbo *fbo;
322
323    glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
324    pixmap_priv = glamor_get_pixmap_private(pixmap);
325    if (pixmap_priv->fbo == NULL) {
326
327        fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width,
328                                pixmap->drawable.height, format, flag);
329        if (fbo == NULL)
330            return FALSE;
331
332        glamor_pixmap_attach_fbo(pixmap, fbo);
333    }
334    else {
335        /* We do have a fbo, but it may lack of fb or tex. */
336        if (!pixmap_priv->fbo->tex)
337            pixmap_priv->fbo->tex =
338                _glamor_create_tex(glamor_priv, pixmap->drawable.width,
339                                   pixmap->drawable.height, format);
340
341        if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->fbo->fb == 0)
342            if (glamor_pixmap_ensure_fb(glamor_priv, pixmap_priv->fbo) != 0)
343                return FALSE;
344    }
345
346    return TRUE;
347}
348
349_X_EXPORT void
350glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back)
351{
352    glamor_pixmap_private *front_priv, *back_priv;
353    glamor_pixmap_fbo *temp_fbo;
354
355    front_priv = glamor_get_pixmap_private(front);
356    back_priv = glamor_get_pixmap_private(back);
357    temp_fbo = front_priv->fbo;
358    front_priv->fbo = back_priv->fbo;
359    back_priv->fbo = temp_fbo;
360}
361