135c4bbdfSmrg/*
235c4bbdfSmrg * Copyright © 2009 Intel Corporation
335c4bbdfSmrg * Copyright © 1998 Keith Packard
435c4bbdfSmrg *
535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person obtaining a
635c4bbdfSmrg * copy of this software and associated documentation files (the "Software"),
735c4bbdfSmrg * to deal in the Software without restriction, including without limitation
835c4bbdfSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
935c4bbdfSmrg * and/or sell copies of the Software, and to permit persons to whom the
1035c4bbdfSmrg * Software is furnished to do so, subject to the following conditions:
1135c4bbdfSmrg *
1235c4bbdfSmrg * The above copyright notice and this permission notice (including the next
1335c4bbdfSmrg * paragraph) shall be included in all copies or substantial portions of the
1435c4bbdfSmrg * Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1735c4bbdfSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1835c4bbdfSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1935c4bbdfSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2035c4bbdfSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2135c4bbdfSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2235c4bbdfSmrg * IN THE SOFTWARE.
2335c4bbdfSmrg *
2435c4bbdfSmrg * Authors:
2535c4bbdfSmrg *    Zhigang Gong <zhigang.gong@gmail.com>
2635c4bbdfSmrg *
2735c4bbdfSmrg */
2835c4bbdfSmrg
2935c4bbdfSmrg#include <stdlib.h>
3035c4bbdfSmrg
3135c4bbdfSmrg#include "glamor_priv.h"
3235c4bbdfSmrg
331b5d61b8Smrgvoid
341b5d61b8Smrgglamor_destroy_fbo(glamor_screen_private *glamor_priv,
351b5d61b8Smrg                   glamor_pixmap_fbo *fbo)
3635c4bbdfSmrg{
3735c4bbdfSmrg    glamor_make_current(glamor_priv);
3835c4bbdfSmrg
3935c4bbdfSmrg    if (fbo->fb)
4035c4bbdfSmrg        glDeleteFramebuffers(1, &fbo->fb);
4135c4bbdfSmrg    if (fbo->tex)
4235c4bbdfSmrg        glDeleteTextures(1, &fbo->tex);
4335c4bbdfSmrg
4435c4bbdfSmrg    free(fbo);
4535c4bbdfSmrg}
4635c4bbdfSmrg
4735c4bbdfSmrgstatic int
4835c4bbdfSmrgglamor_pixmap_ensure_fb(glamor_screen_private *glamor_priv,
4935c4bbdfSmrg                        glamor_pixmap_fbo *fbo)
5035c4bbdfSmrg{
5135c4bbdfSmrg    int status, err = 0;
5235c4bbdfSmrg
5335c4bbdfSmrg    glamor_make_current(glamor_priv);
5435c4bbdfSmrg
5535c4bbdfSmrg    if (fbo->fb == 0)
5635c4bbdfSmrg        glGenFramebuffers(1, &fbo->fb);
5735c4bbdfSmrg    assert(fbo->tex != 0);
5835c4bbdfSmrg    glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
5935c4bbdfSmrg    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
6035c4bbdfSmrg                           GL_TEXTURE_2D, fbo->tex, 0);
6135c4bbdfSmrg    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
6235c4bbdfSmrg    if (status != GL_FRAMEBUFFER_COMPLETE) {
6335c4bbdfSmrg        const char *str;
6435c4bbdfSmrg
6535c4bbdfSmrg        switch (status) {
6635c4bbdfSmrg        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
6735c4bbdfSmrg            str = "incomplete attachment";
6835c4bbdfSmrg            break;
6935c4bbdfSmrg        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
7035c4bbdfSmrg            str = "incomplete/missing attachment";
7135c4bbdfSmrg            break;
7235c4bbdfSmrg        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
7335c4bbdfSmrg            str = "incomplete draw buffer";
7435c4bbdfSmrg            break;
7535c4bbdfSmrg        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
7635c4bbdfSmrg            str = "incomplete read buffer";
7735c4bbdfSmrg            break;
7835c4bbdfSmrg        case GL_FRAMEBUFFER_UNSUPPORTED:
7935c4bbdfSmrg            str = "unsupported";
8035c4bbdfSmrg            break;
8135c4bbdfSmrg        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
8235c4bbdfSmrg            str = "incomplete multiple";
8335c4bbdfSmrg            break;
8435c4bbdfSmrg        default:
8535c4bbdfSmrg            str = "unknown error";
8635c4bbdfSmrg            break;
8735c4bbdfSmrg        }
8835c4bbdfSmrg
8935c4bbdfSmrg        glamor_fallback("glamor: Failed to create fbo, %s\n", str);
9035c4bbdfSmrg        err = -1;
9135c4bbdfSmrg    }
9235c4bbdfSmrg
9335c4bbdfSmrg    return err;
9435c4bbdfSmrg}
9535c4bbdfSmrg
9635c4bbdfSmrgglamor_pixmap_fbo *
9735c4bbdfSmrgglamor_create_fbo_from_tex(glamor_screen_private *glamor_priv,
98ed6184dfSmrg                           PixmapPtr pixmap, int w, int h, GLint tex, int flag)
9935c4bbdfSmrg{
100ed6184dfSmrg    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
10135c4bbdfSmrg    glamor_pixmap_fbo *fbo;
10235c4bbdfSmrg
10335c4bbdfSmrg    fbo = calloc(1, sizeof(*fbo));
10435c4bbdfSmrg    if (fbo == NULL)
10535c4bbdfSmrg        return NULL;
10635c4bbdfSmrg
10735c4bbdfSmrg    fbo->tex = tex;
10835c4bbdfSmrg    fbo->width = w;
10935c4bbdfSmrg    fbo->height = h;
110ed6184dfSmrg    fbo->is_red = f->format == GL_RED;
11135c4bbdfSmrg
11235c4bbdfSmrg    if (flag != GLAMOR_CREATE_FBO_NO_FBO) {
11335c4bbdfSmrg        if (glamor_pixmap_ensure_fb(glamor_priv, fbo) != 0) {
1141b5d61b8Smrg            glamor_destroy_fbo(glamor_priv, fbo);
11535c4bbdfSmrg            fbo = NULL;
11635c4bbdfSmrg        }
11735c4bbdfSmrg    }
11835c4bbdfSmrg
11935c4bbdfSmrg    return fbo;
12035c4bbdfSmrg}
12135c4bbdfSmrg
12235c4bbdfSmrgstatic int
12335c4bbdfSmrg_glamor_create_tex(glamor_screen_private *glamor_priv,
124ed6184dfSmrg                   PixmapPtr pixmap, int w, int h)
12535c4bbdfSmrg{
126ed6184dfSmrg    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
12735c4bbdfSmrg    unsigned int tex;
12835c4bbdfSmrg
12935c4bbdfSmrg    glamor_make_current(glamor_priv);
13035c4bbdfSmrg    glGenTextures(1, &tex);
13135c4bbdfSmrg    glBindTexture(GL_TEXTURE_2D, tex);
13235c4bbdfSmrg    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
13335c4bbdfSmrg    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
134ed6184dfSmrg    if (f->format == GL_RED)
13535c4bbdfSmrg        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
13635c4bbdfSmrg    glamor_priv->suppress_gl_out_of_memory_logging = true;
137ed6184dfSmrg    glTexImage2D(GL_TEXTURE_2D, 0, f->internalformat, w, h, 0,
138ed6184dfSmrg                 f->format, f->type, NULL);
13935c4bbdfSmrg    glamor_priv->suppress_gl_out_of_memory_logging = false;
14035c4bbdfSmrg
14135c4bbdfSmrg    if (glGetError() == GL_OUT_OF_MEMORY) {
14235c4bbdfSmrg        if (!glamor_priv->logged_any_fbo_allocation_failure) {
14335c4bbdfSmrg            LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %dx%d "
14435c4bbdfSmrg                           "FBO due to GL_OUT_OF_MEMORY.\n", w, h);
14535c4bbdfSmrg            LogMessageVerb(X_WARNING, 0,
14635c4bbdfSmrg                           "glamor: Expect reduced performance.\n");
14735c4bbdfSmrg            glamor_priv->logged_any_fbo_allocation_failure = true;
14835c4bbdfSmrg        }
14935c4bbdfSmrg        glDeleteTextures(1, &tex);
15035c4bbdfSmrg        return 0;
15135c4bbdfSmrg    }
15235c4bbdfSmrg
15335c4bbdfSmrg    return tex;
15435c4bbdfSmrg}
15535c4bbdfSmrg
15635c4bbdfSmrgglamor_pixmap_fbo *
15735c4bbdfSmrgglamor_create_fbo(glamor_screen_private *glamor_priv,
158ed6184dfSmrg                  PixmapPtr pixmap, int w, int h, int flag)
15935c4bbdfSmrg{
160ed6184dfSmrg    GLint tex = _glamor_create_tex(glamor_priv, pixmap, w, h);
16135c4bbdfSmrg
1621b5d61b8Smrg    if (!tex) /* Texture creation failed due to GL_OUT_OF_MEMORY */
16335c4bbdfSmrg        return NULL;
16435c4bbdfSmrg
165ed6184dfSmrg    return glamor_create_fbo_from_tex(glamor_priv, pixmap, w, h,
166ed6184dfSmrg                                      tex, flag);
16735c4bbdfSmrg}
16835c4bbdfSmrg
16935c4bbdfSmrg/**
17035c4bbdfSmrg * Create storage for the w * h region, using FBOs of the GL's maximum
17135c4bbdfSmrg * supported size.
17235c4bbdfSmrg */
17335c4bbdfSmrgglamor_pixmap_fbo *
17435c4bbdfSmrgglamor_create_fbo_array(glamor_screen_private *glamor_priv,
175ed6184dfSmrg                        PixmapPtr pixmap, int flag,
17635c4bbdfSmrg                         int block_w, int block_h,
17735c4bbdfSmrg                         glamor_pixmap_private *priv)
17835c4bbdfSmrg{
179ed6184dfSmrg    int w = pixmap->drawable.width;
180ed6184dfSmrg    int h = pixmap->drawable.height;
18135c4bbdfSmrg    int block_wcnt;
18235c4bbdfSmrg    int block_hcnt;
18335c4bbdfSmrg    glamor_pixmap_fbo **fbo_array;
18435c4bbdfSmrg    BoxPtr box_array;
18535c4bbdfSmrg    int i, j;
18635c4bbdfSmrg
18735c4bbdfSmrg    priv->block_w = block_w;
18835c4bbdfSmrg    priv->block_h = block_h;
18935c4bbdfSmrg
19035c4bbdfSmrg    block_wcnt = (w + block_w - 1) / block_w;
19135c4bbdfSmrg    block_hcnt = (h + block_h - 1) / block_h;
19235c4bbdfSmrg
19335c4bbdfSmrg    box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0]));
19435c4bbdfSmrg    if (box_array == NULL)
19535c4bbdfSmrg        return NULL;
19635c4bbdfSmrg
19735c4bbdfSmrg    fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo *));
19835c4bbdfSmrg    if (fbo_array == NULL) {
19935c4bbdfSmrg        free(box_array);
20035c4bbdfSmrg        return FALSE;
20135c4bbdfSmrg    }
20235c4bbdfSmrg    for (i = 0; i < block_hcnt; i++) {
20335c4bbdfSmrg        int block_y1, block_y2;
20435c4bbdfSmrg        int fbo_w, fbo_h;
20535c4bbdfSmrg
20635c4bbdfSmrg        block_y1 = i * block_h;
20735c4bbdfSmrg        block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h);
20835c4bbdfSmrg        fbo_h = block_y2 - block_y1;
20935c4bbdfSmrg
21035c4bbdfSmrg        for (j = 0; j < block_wcnt; j++) {
21135c4bbdfSmrg            box_array[i * block_wcnt + j].x1 = j * block_w;
21235c4bbdfSmrg            box_array[i * block_wcnt + j].y1 = block_y1;
21335c4bbdfSmrg            box_array[i * block_wcnt + j].x2 =
21435c4bbdfSmrg                (j + 1) * block_w > w ? w : (j + 1) * block_w;
21535c4bbdfSmrg            box_array[i * block_wcnt + j].y2 = block_y2;
21635c4bbdfSmrg            fbo_w =
21735c4bbdfSmrg                box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt +
21835c4bbdfSmrg                                                             j].x1;
21935c4bbdfSmrg            fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv,
220ed6184dfSmrg                                                              pixmap,
22135c4bbdfSmrg                                                              fbo_w, fbo_h,
22235c4bbdfSmrg                                                              GLAMOR_CREATE_PIXMAP_FIXUP);
22335c4bbdfSmrg            if (fbo_array[i * block_wcnt + j] == NULL)
22435c4bbdfSmrg                goto cleanup;
22535c4bbdfSmrg        }
22635c4bbdfSmrg    }
22735c4bbdfSmrg
22835c4bbdfSmrg    priv->box = box_array[0];
22935c4bbdfSmrg    priv->box_array = box_array;
23035c4bbdfSmrg    priv->fbo_array = fbo_array;
23135c4bbdfSmrg    priv->block_wcnt = block_wcnt;
23235c4bbdfSmrg    priv->block_hcnt = block_hcnt;
23335c4bbdfSmrg    return fbo_array[0];
23435c4bbdfSmrg
23535c4bbdfSmrg cleanup:
23635c4bbdfSmrg    for (i = 0; i < block_wcnt * block_hcnt; i++)
23735c4bbdfSmrg        if (fbo_array[i])
23835c4bbdfSmrg            glamor_destroy_fbo(glamor_priv, fbo_array[i]);
23935c4bbdfSmrg    free(box_array);
24035c4bbdfSmrg    free(fbo_array);
24135c4bbdfSmrg    return NULL;
24235c4bbdfSmrg}
24335c4bbdfSmrg
2445a7dfde8Smrgvoid
245ed6184dfSmrgglamor_pixmap_clear_fbo(glamor_screen_private *glamor_priv, glamor_pixmap_fbo *fbo,
246ed6184dfSmrg                        const struct glamor_format *pixmap_format)
2475a7dfde8Smrg{
2485a7dfde8Smrg    glamor_make_current(glamor_priv);
2495a7dfde8Smrg
2505a7dfde8Smrg    assert(fbo->fb != 0 && fbo->tex != 0);
2515a7dfde8Smrg
252ed6184dfSmrg    if (glamor_priv->has_clear_texture) {
253ed6184dfSmrg        glClearTexImage(fbo->tex, 0, pixmap_format->format, pixmap_format->type, NULL);
254ed6184dfSmrg    }
255ed6184dfSmrg    else {
256ed6184dfSmrg        glamor_set_destination_pixmap_fbo(glamor_priv, fbo, 0, 0, fbo->width, fbo->height);
257ed6184dfSmrg
258ed6184dfSmrg        glClearColor(0.0, 0.0, 0.0, 0.0);
259ed6184dfSmrg        glClear(GL_COLOR_BUFFER_BIT);
260ed6184dfSmrg    }
2615a7dfde8Smrg}
2625a7dfde8Smrg
26335c4bbdfSmrgglamor_pixmap_fbo *
26435c4bbdfSmrgglamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv)
26535c4bbdfSmrg{
26635c4bbdfSmrg    glamor_pixmap_fbo *fbo;
26735c4bbdfSmrg
26835c4bbdfSmrg    if (pixmap_priv == NULL)
26935c4bbdfSmrg        return NULL;
27035c4bbdfSmrg
27135c4bbdfSmrg    fbo = pixmap_priv->fbo;
27235c4bbdfSmrg    if (fbo == NULL)
27335c4bbdfSmrg        return NULL;
27435c4bbdfSmrg
27535c4bbdfSmrg    pixmap_priv->fbo = NULL;
27635c4bbdfSmrg    return fbo;
27735c4bbdfSmrg}
27835c4bbdfSmrg
27935c4bbdfSmrg/* The pixmap must not be attached to another fbo. */
28035c4bbdfSmrgvoid
28135c4bbdfSmrgglamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo)
28235c4bbdfSmrg{
28335c4bbdfSmrg    glamor_pixmap_private *pixmap_priv;
28435c4bbdfSmrg
28535c4bbdfSmrg    pixmap_priv = glamor_get_pixmap_private(pixmap);
28635c4bbdfSmrg
28735c4bbdfSmrg    if (pixmap_priv->fbo)
28835c4bbdfSmrg        return;
28935c4bbdfSmrg
29035c4bbdfSmrg    pixmap_priv->fbo = fbo;
29135c4bbdfSmrg
29235c4bbdfSmrg    switch (pixmap_priv->type) {
29335c4bbdfSmrg    case GLAMOR_TEXTURE_ONLY:
29435c4bbdfSmrg    case GLAMOR_TEXTURE_DRM:
29535c4bbdfSmrg        pixmap_priv->gl_fbo = GLAMOR_FBO_NORMAL;
29635c4bbdfSmrg        pixmap->devPrivate.ptr = NULL;
29735c4bbdfSmrg    default:
29835c4bbdfSmrg        break;
29935c4bbdfSmrg    }
30035c4bbdfSmrg}
30135c4bbdfSmrg
30235c4bbdfSmrgvoid
30335c4bbdfSmrgglamor_pixmap_destroy_fbo(PixmapPtr pixmap)
30435c4bbdfSmrg{
30535c4bbdfSmrg    ScreenPtr screen = pixmap->drawable.pScreen;
30635c4bbdfSmrg    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
30735c4bbdfSmrg    glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
30835c4bbdfSmrg    glamor_pixmap_fbo *fbo;
30935c4bbdfSmrg
31035c4bbdfSmrg    if (glamor_pixmap_priv_is_large(priv)) {
31135c4bbdfSmrg        int i;
31235c4bbdfSmrg
31335c4bbdfSmrg        for (i = 0; i < priv->block_wcnt * priv->block_hcnt; i++)
31435c4bbdfSmrg            glamor_destroy_fbo(glamor_priv, priv->fbo_array[i]);
31535c4bbdfSmrg        free(priv->fbo_array);
3161b5d61b8Smrg        priv->fbo_array = NULL;
31735c4bbdfSmrg    }
31835c4bbdfSmrg    else {
31935c4bbdfSmrg        fbo = glamor_pixmap_detach_fbo(priv);
32035c4bbdfSmrg        if (fbo)
32135c4bbdfSmrg            glamor_destroy_fbo(glamor_priv, fbo);
32235c4bbdfSmrg    }
32335c4bbdfSmrg}
32435c4bbdfSmrg
32535c4bbdfSmrgBool
326ed6184dfSmrgglamor_pixmap_ensure_fbo(PixmapPtr pixmap, int flag)
32735c4bbdfSmrg{
32835c4bbdfSmrg    glamor_screen_private *glamor_priv;
32935c4bbdfSmrg    glamor_pixmap_private *pixmap_priv;
33035c4bbdfSmrg    glamor_pixmap_fbo *fbo;
33135c4bbdfSmrg
33235c4bbdfSmrg    glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
33335c4bbdfSmrg    pixmap_priv = glamor_get_pixmap_private(pixmap);
33435c4bbdfSmrg    if (pixmap_priv->fbo == NULL) {
33535c4bbdfSmrg
336ed6184dfSmrg        fbo = glamor_create_fbo(glamor_priv, pixmap, pixmap->drawable.width,
337ed6184dfSmrg                                pixmap->drawable.height, flag);
33835c4bbdfSmrg        if (fbo == NULL)
33935c4bbdfSmrg            return FALSE;
34035c4bbdfSmrg
34135c4bbdfSmrg        glamor_pixmap_attach_fbo(pixmap, fbo);
34235c4bbdfSmrg    }
34335c4bbdfSmrg    else {
34435c4bbdfSmrg        /* We do have a fbo, but it may lack of fb or tex. */
34535c4bbdfSmrg        if (!pixmap_priv->fbo->tex)
34635c4bbdfSmrg            pixmap_priv->fbo->tex =
347ed6184dfSmrg                _glamor_create_tex(glamor_priv, pixmap, pixmap->drawable.width,
348ed6184dfSmrg                                   pixmap->drawable.height);
34935c4bbdfSmrg
35035c4bbdfSmrg        if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->fbo->fb == 0)
35135c4bbdfSmrg            if (glamor_pixmap_ensure_fb(glamor_priv, pixmap_priv->fbo) != 0)
35235c4bbdfSmrg                return FALSE;
35335c4bbdfSmrg    }
35435c4bbdfSmrg
35535c4bbdfSmrg    return TRUE;
35635c4bbdfSmrg}
35735c4bbdfSmrg
35835c4bbdfSmrg_X_EXPORT void
35935c4bbdfSmrgglamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back)
36035c4bbdfSmrg{
36135c4bbdfSmrg    glamor_pixmap_private *front_priv, *back_priv;
36235c4bbdfSmrg    glamor_pixmap_fbo *temp_fbo;
36335c4bbdfSmrg
36435c4bbdfSmrg    front_priv = glamor_get_pixmap_private(front);
36535c4bbdfSmrg    back_priv = glamor_get_pixmap_private(back);
36635c4bbdfSmrg    temp_fbo = front_priv->fbo;
36735c4bbdfSmrg    front_priv->fbo = back_priv->fbo;
36835c4bbdfSmrg    back_priv->fbo = temp_fbo;
36935c4bbdfSmrg}
370