Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  * Version:  6.5
      4  *
      5  * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the "Software"),
      9  * to deal in the Software without restriction, including without limitation
     10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     11  * and/or sell copies of the Software, and to permit persons to whom the
     12  * Software is furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included
     15  * in all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     21  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 #include "glheader.h"
     26 #include "accum.h"
     27 #include "condrender.h"
     28 #include "context.h"
     29 #include "format_unpack.h"
     30 #include "format_pack.h"
     31 #include "imports.h"
     32 #include "macros.h"
     33 #include "mfeatures.h"
     34 #include "state.h"
     35 #include "mtypes.h"
     36 #include "main/dispatch.h"
     37 
     38 
     39 #if FEATURE_accum
     40 
     41 
     42 void GLAPIENTRY
     43 _mesa_ClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
     44 {
     45    GLfloat tmp[4];
     46    GET_CURRENT_CONTEXT(ctx);
     47    ASSERT_OUTSIDE_BEGIN_END(ctx);
     48 
     49    tmp[0] = CLAMP( red,   -1.0F, 1.0F );
     50    tmp[1] = CLAMP( green, -1.0F, 1.0F );
     51    tmp[2] = CLAMP( blue,  -1.0F, 1.0F );
     52    tmp[3] = CLAMP( alpha, -1.0F, 1.0F );
     53 
     54    if (TEST_EQ_4V(tmp, ctx->Accum.ClearColor))
     55       return;
     56 
     57    COPY_4FV( ctx->Accum.ClearColor, tmp );
     58 }
     59 
     60 
     61 static void GLAPIENTRY
     62 _mesa_Accum( GLenum op, GLfloat value )
     63 {
     64    GET_CURRENT_CONTEXT(ctx);
     65    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
     66 
     67    switch (op) {
     68    case GL_ADD:
     69    case GL_MULT:
     70    case GL_ACCUM:
     71    case GL_LOAD:
     72    case GL_RETURN:
     73       /* OK */
     74       break;
     75    default:
     76       _mesa_error(ctx, GL_INVALID_ENUM, "glAccum(op)");
     77       return;
     78    }
     79 
     80    if (ctx->DrawBuffer->Visual.haveAccumBuffer == 0) {
     81       _mesa_error(ctx, GL_INVALID_OPERATION, "glAccum(no accum buffer)");
     82       return;
     83    }
     84 
     85    if (ctx->DrawBuffer != ctx->ReadBuffer) {
     86       /* See GLX_SGI_make_current_read or WGL_ARB_make_current_read,
     87        * or GL_EXT_framebuffer_blit.
     88        */
     89       _mesa_error(ctx, GL_INVALID_OPERATION,
     90                   "glAccum(different read/draw buffers)");
     91       return;
     92    }
     93 
     94    if (ctx->NewState)
     95       _mesa_update_state(ctx);
     96 
     97    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
     98       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
     99                   "glAccum(incomplete framebuffer)");
    100       return;
    101    }
    102 
    103    if (ctx->RasterDiscard)
    104       return;
    105 
    106    if (ctx->RenderMode == GL_RENDER) {
    107       _mesa_accum(ctx, op, value);
    108    }
    109 }
    110 
    111 
    112 void
    113 _mesa_init_accum_dispatch(struct _glapi_table *disp)
    114 {
    115    SET_Accum(disp, _mesa_Accum);
    116    SET_ClearAccum(disp, _mesa_ClearAccum);
    117 }
    118 
    119 
    120 /**
    121  * Clear the accumulation buffer by mapping the renderbuffer and
    122  * writing the clear color to it.  Called by the driver's implementation
    123  * of the glClear function.
    124  */
    125 void
    126 _mesa_clear_accum_buffer(struct gl_context *ctx)
    127 {
    128    GLuint x, y, width, height;
    129    GLubyte *accMap;
    130    GLint accRowStride;
    131    struct gl_renderbuffer *accRb;
    132 
    133    if (!ctx->DrawBuffer)
    134       return;
    135 
    136    accRb = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
    137    if (!accRb)
    138       return;   /* missing accum buffer, not an error */
    139 
    140    /* bounds, with scissor */
    141    x = ctx->DrawBuffer->_Xmin;
    142    y = ctx->DrawBuffer->_Ymin;
    143    width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
    144    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
    145 
    146    ctx->Driver.MapRenderbuffer(ctx, accRb, x, y, width, height,
    147                                GL_MAP_WRITE_BIT, &accMap, &accRowStride);
    148 
    149    if (!accMap) {
    150       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    151       return;
    152    }
    153 
    154    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
    155       const GLshort clearR = FLOAT_TO_SHORT(ctx->Accum.ClearColor[0]);
    156       const GLshort clearG = FLOAT_TO_SHORT(ctx->Accum.ClearColor[1]);
    157       const GLshort clearB = FLOAT_TO_SHORT(ctx->Accum.ClearColor[2]);
    158       const GLshort clearA = FLOAT_TO_SHORT(ctx->Accum.ClearColor[3]);
    159       GLuint i, j;
    160 
    161       for (j = 0; j < height; j++) {
    162          GLshort *row = (GLshort *) accMap;
    163 
    164          for (i = 0; i < width; i++) {
    165             row[i * 4 + 0] = clearR;
    166             row[i * 4 + 1] = clearG;
    167             row[i * 4 + 2] = clearB;
    168             row[i * 4 + 3] = clearA;
    169          }
    170          accMap += accRowStride;
    171       }
    172    }
    173    else {
    174       /* other types someday? */
    175       _mesa_warning(ctx, "unexpected accum buffer type");
    176    }
    177 
    178    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
    179 }
    180 
    181 
    182 /**
    183  * if (bias)
    184  *    Accum += value
    185  * else
    186  *    Accum *= value
    187  */
    188 static void
    189 accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
    190                     GLint xpos, GLint ypos, GLint width, GLint height,
    191                     GLboolean bias)
    192 {
    193    struct gl_renderbuffer *accRb =
    194       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
    195    GLubyte *accMap;
    196    GLint accRowStride;
    197 
    198    assert(accRb);
    199 
    200    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
    201                                GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
    202                                &accMap, &accRowStride);
    203 
    204    if (!accMap) {
    205       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    206       return;
    207    }
    208 
    209    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
    210       const GLshort incr = (GLshort) (value * 32767.0f);
    211       GLuint i, j;
    212       if (bias) {
    213          for (j = 0; j < height; j++) {
    214             GLshort *acc = (GLshort *) accMap;
    215             for (i = 0; i < 4 * width; i++) {
    216                acc[i] += incr;
    217             }
    218             accMap += accRowStride;
    219          }
    220       }
    221       else {
    222          /* scale */
    223          for (j = 0; j < height; j++) {
    224             GLshort *acc = (GLshort *) accMap;
    225             for (i = 0; i < 4 * width; i++) {
    226                acc[i] = (GLshort) (acc[i] * value);
    227             }
    228             accMap += accRowStride;
    229          }
    230       }
    231    }
    232    else {
    233       /* other types someday? */
    234    }
    235 
    236    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
    237 }
    238 
    239 
    240 /**
    241  * if (load)
    242  *    Accum = ColorBuf * value
    243  * else
    244  *    Accum += ColorBuf * value
    245  */
    246 static void
    247 accum_or_load(struct gl_context *ctx, GLfloat value,
    248               GLint xpos, GLint ypos, GLint width, GLint height,
    249               GLboolean load)
    250 {
    251    struct gl_renderbuffer *accRb =
    252       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
    253    struct gl_renderbuffer *colorRb = ctx->ReadBuffer->_ColorReadBuffer;
    254    GLubyte *accMap, *colorMap;
    255    GLint accRowStride, colorRowStride;
    256    GLbitfield mappingFlags;
    257 
    258    if (!colorRb) {
    259       /* no read buffer - OK */
    260       return;
    261    }
    262 
    263    assert(accRb);
    264 
    265    mappingFlags = GL_MAP_WRITE_BIT;
    266    if (!load) /* if we're accumulating */
    267       mappingFlags |= GL_MAP_READ_BIT;
    268 
    269    /* Map accum buffer */
    270    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
    271                                mappingFlags, &accMap, &accRowStride);
    272    if (!accMap) {
    273       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    274       return;
    275    }
    276 
    277    /* Map color buffer */
    278    ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
    279                                GL_MAP_READ_BIT,
    280                                &colorMap, &colorRowStride);
    281    if (!colorMap) {
    282       ctx->Driver.UnmapRenderbuffer(ctx, accRb);
    283       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    284       return;
    285    }
    286 
    287    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
    288       const GLfloat scale = value * 32767.0f;
    289       GLuint i, j;
    290       GLfloat (*rgba)[4];
    291 
    292       rgba = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
    293       if (rgba) {
    294          for (j = 0; j < height; j++) {
    295             GLshort *acc = (GLshort *) accMap;
    296 
    297             /* read colors from source color buffer */
    298             _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, rgba);
    299 
    300             if (load) {
    301                for (i = 0; i < width; i++) {
    302                   acc[i * 4 + 0] = (GLshort) (rgba[i][RCOMP] * scale);
    303                   acc[i * 4 + 1] = (GLshort) (rgba[i][GCOMP] * scale);
    304                   acc[i * 4 + 2] = (GLshort) (rgba[i][BCOMP] * scale);
    305                   acc[i * 4 + 3] = (GLshort) (rgba[i][ACOMP] * scale);
    306                }
    307             }
    308             else {
    309                /* accumulate */
    310                for (i = 0; i < width; i++) {
    311                   acc[i * 4 + 0] += (GLshort) (rgba[i][RCOMP] * scale);
    312                   acc[i * 4 + 1] += (GLshort) (rgba[i][GCOMP] * scale);
    313                   acc[i * 4 + 2] += (GLshort) (rgba[i][BCOMP] * scale);
    314                   acc[i * 4 + 3] += (GLshort) (rgba[i][ACOMP] * scale);
    315                }
    316             }
    317 
    318             colorMap += colorRowStride;
    319             accMap += accRowStride;
    320          }
    321 
    322          free(rgba);
    323       }
    324       else {
    325          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    326       }
    327    }
    328    else {
    329       /* other types someday? */
    330    }
    331 
    332    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
    333    ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
    334 }
    335 
    336 
    337 /**
    338  * ColorBuffer = Accum * value
    339  */
    340 static void
    341 accum_return(struct gl_context *ctx, GLfloat value,
    342              GLint xpos, GLint ypos, GLint width, GLint height)
    343 {
    344    struct gl_framebuffer *fb = ctx->DrawBuffer;
    345    struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
    346    GLubyte *accMap, *colorMap;
    347    GLint accRowStride, colorRowStride;
    348    GLuint buffer;
    349 
    350    /* Map accum buffer */
    351    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
    352                                GL_MAP_READ_BIT,
    353                                &accMap, &accRowStride);
    354    if (!accMap) {
    355       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    356       return;
    357    }
    358 
    359    /* Loop over destination buffers */
    360    for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) {
    361       struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer];
    362       const GLboolean masking = (!ctx->Color.ColorMask[buffer][RCOMP] ||
    363                                  !ctx->Color.ColorMask[buffer][GCOMP] ||
    364                                  !ctx->Color.ColorMask[buffer][BCOMP] ||
    365                                  !ctx->Color.ColorMask[buffer][ACOMP]);
    366       GLbitfield mappingFlags = GL_MAP_WRITE_BIT;
    367 
    368       if (masking)
    369          mappingFlags |= GL_MAP_READ_BIT;
    370 
    371       /* Map color buffer */
    372       ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
    373                                   mappingFlags, &colorMap, &colorRowStride);
    374       if (!colorMap) {
    375          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    376          continue;
    377       }
    378 
    379       if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
    380          const GLfloat scale = value / 32767.0f;
    381          GLint i, j;
    382          GLfloat (*rgba)[4], (*dest)[4];
    383 
    384          rgba = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
    385          dest = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
    386 
    387          if (rgba && dest) {
    388             for (j = 0; j < height; j++) {
    389                GLshort *acc = (GLshort *) accMap;
    390 
    391                for (i = 0; i < width; i++) {
    392                   rgba[i][0] = acc[i * 4 + 0] * scale;
    393                   rgba[i][1] = acc[i * 4 + 1] * scale;
    394                   rgba[i][2] = acc[i * 4 + 2] * scale;
    395                   rgba[i][3] = acc[i * 4 + 3] * scale;
    396                }
    397 
    398                if (masking) {
    399 
    400                   /* get existing colors from dest buffer */
    401                   _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest);
    402 
    403                   /* use the dest colors where mask[channel] = 0 */
    404                   if (ctx->Color.ColorMask[buffer][RCOMP] == 0) {
    405                      for (i = 0; i < width; i++)
    406                         rgba[i][RCOMP] = dest[i][RCOMP];
    407                   }
    408                   if (ctx->Color.ColorMask[buffer][GCOMP] == 0) {
    409                      for (i = 0; i < width; i++)
    410                         rgba[i][GCOMP] = dest[i][GCOMP];
    411                   }
    412                   if (ctx->Color.ColorMask[buffer][BCOMP] == 0) {
    413                      for (i = 0; i < width; i++)
    414                         rgba[i][BCOMP] = dest[i][BCOMP];
    415                   }
    416                   if (ctx->Color.ColorMask[buffer][ACOMP] == 0) {
    417                      for (i = 0; i < width; i++)
    418                         rgba[i][ACOMP] = dest[i][ACOMP];
    419                   }
    420                }
    421 
    422                _mesa_pack_float_rgba_row(colorRb->Format, width,
    423                                          (const GLfloat (*)[4]) rgba, colorMap);
    424 
    425                accMap += accRowStride;
    426                colorMap += colorRowStride;
    427             }
    428          }
    429          else {
    430             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
    431          }
    432          free(rgba);
    433          free(dest);
    434       }
    435       else {
    436          /* other types someday? */
    437       }
    438 
    439       ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
    440    }
    441 
    442    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
    443 }
    444 
    445 
    446 
    447 /**
    448  * Software fallback for glAccum.  A hardware driver that supports
    449  * signed 16-bit color channels could implement hardware accumulation
    450  * operations, but no driver does so at this time.
    451  */
    452 void
    453 _mesa_accum(struct gl_context *ctx, GLenum op, GLfloat value)
    454 {
    455    GLint xpos, ypos, width, height;
    456 
    457    if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
    458       _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
    459       return;
    460    }
    461 
    462    if (!_mesa_check_conditional_render(ctx))
    463       return;
    464 
    465    xpos = ctx->DrawBuffer->_Xmin;
    466    ypos = ctx->DrawBuffer->_Ymin;
    467    width =  ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
    468    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
    469 
    470    switch (op) {
    471    case GL_ADD:
    472       if (value != 0.0F) {
    473          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_TRUE);
    474       }
    475       break;
    476    case GL_MULT:
    477       if (value != 1.0F) {
    478          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_FALSE);
    479       }
    480       break;
    481    case GL_ACCUM:
    482       if (value != 0.0F) {
    483          accum_or_load(ctx, value, xpos, ypos, width, height, GL_FALSE);
    484       }
    485       break;
    486    case GL_LOAD:
    487       accum_or_load(ctx, value, xpos, ypos, width, height, GL_TRUE);
    488       break;
    489    case GL_RETURN:
    490       accum_return(ctx, value, xpos, ypos, width, height);
    491       break;
    492    default:
    493       _mesa_problem(ctx, "invalid mode in _mesa_accum()");
    494       break;
    495    }
    496 }
    497 
    498 
    499 #endif /* FEATURE_accum */
    500 
    501 
    502 void
    503 _mesa_init_accum( struct gl_context *ctx )
    504 {
    505    /* Accumulate buffer group */
    506    ASSIGN_4V( ctx->Accum.ClearColor, 0.0, 0.0, 0.0, 0.0 );
    507 }
    508