Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
      5  * Copyright (C) 2009-2011  VMware, Inc.  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  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     23  * OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 
     27 /**
     28  * \file pbo.c
     29  * \brief Functions related to Pixel Buffer Objects.
     30  */
     31 
     32 
     33 
     34 #include "glheader.h"
     35 #include "bufferobj.h"
     36 #include "glformats.h"
     37 #include "image.h"
     38 #include "imports.h"
     39 #include "mtypes.h"
     40 #include "pbo.h"
     41 
     42 
     43 
     44 /**
     45  * When we're about to read pixel data out of a PBO (via glDrawPixels,
     46  * glTexImage, etc) or write data into a PBO (via glReadPixels,
     47  * glGetTexImage, etc) we call this function to check that we're not
     48  * going to read/write out of bounds.
     49  *
     50  * XXX This would also be a convenient time to check that the PBO isn't
     51  * currently mapped.  Whoever calls this function should check for that.
     52  * Remember, we can't use a PBO when it's mapped!
     53  *
     54  * If we're not using a PBO, this is a no-op.
     55  *
     56  * \param width  width of image to read/write
     57  * \param height  height of image to read/write
     58  * \param depth  depth of image to read/write
     59  * \param format  format of image to read/write
     60  * \param type  datatype of image to read/write
     61  * \param clientMemSize  the maximum number of bytes to read/write
     62  * \param ptr  the user-provided pointer/offset
     63  * \return GL_TRUE if the buffer access is OK, GL_FALSE if the access would
     64  *         go out of bounds.
     65  */
     66 GLboolean
     67 _mesa_validate_pbo_access(GLuint dimensions,
     68                           const struct gl_pixelstore_attrib *pack,
     69                           GLsizei width, GLsizei height, GLsizei depth,
     70                           GLenum format, GLenum type, GLsizei clientMemSize,
     71                           const GLvoid *ptr)
     72 {
     73    /* unsigned, to detect overflow/wrap-around */
     74    uintptr_t start, end, offset, size;
     75 
     76    /* If no PBO is bound, 'ptr' is a pointer to client memory containing
     77       'clientMemSize' bytes.
     78       If a PBO is bound, 'ptr' is an offset into the bound PBO.
     79       In that case 'clientMemSize' is ignored: we just use the PBO's size.
     80     */
     81    if (!_mesa_is_bufferobj(pack->BufferObj)) {
     82       offset = 0;
     83       size = (clientMemSize == INT_MAX) ? UINTPTR_MAX : clientMemSize;
     84    } else {
     85       offset = (uintptr_t)ptr;
     86       size = pack->BufferObj->Size;
     87       /* The ARB_pixel_buffer_object spec says:
     88        *    "INVALID_OPERATION is generated by ColorTable, ColorSubTable,
     89        *    ConvolutionFilter2D, ConvolutionFilter1D, SeparableFilter2D,
     90        *    TexImage1D, TexImage2D, TexImage3D, TexSubImage1D,
     91        *    TexSubImage2D, TexSubImage3D, and DrawPixels if the current
     92        *    PIXEL_UNPACK_BUFFER_BINDING_ARB value is non-zero and the data
     93        *    parameter is not evenly divisible into the number of basic machine
     94        *    units needed to store in memory a datum indicated by the type
     95        *    parameter."
     96        */
     97       if (type != GL_BITMAP &&
     98           (offset % _mesa_sizeof_packed_type(type)))
     99          return GL_FALSE;
    100    }
    101 
    102    if (size == 0)
    103       /* no buffer! */
    104       return GL_FALSE;
    105 
    106    /* If the size of the image is zero then no pixels are accessed so we
    107     * don't need to check anything else.
    108     */
    109    if (width == 0 || height == 0 || depth == 0)
    110       return GL_TRUE;
    111 
    112    /* get the offset to the first pixel we'll read/write */
    113    start = _mesa_image_offset(dimensions, pack, width, height,
    114                               format, type, 0, 0, 0);
    115 
    116    /* get the offset to just past the last pixel we'll read/write */
    117    end =  _mesa_image_offset(dimensions, pack, width, height,
    118                              format, type, depth-1, height-1, width);
    119 
    120    start += offset;
    121    end += offset;
    122 
    123    if (start > size) {
    124       /* This will catch negative values / wrap-around */
    125       return GL_FALSE;
    126    }
    127    if (end > size) {
    128       /* Image read/write goes beyond end of buffer */
    129       return GL_FALSE;
    130    }
    131 
    132    /* OK! */
    133    return GL_TRUE;
    134 }
    135 
    136 
    137 /**
    138  * For commands that read from a PBO (glDrawPixels, glTexImage,
    139  * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
    140  * and return the pointer into the PBO.  If we're not reading from a
    141  * PBO, return \p src as-is.
    142  * If non-null return, must call _mesa_unmap_pbo_source() when done.
    143  *
    144  * \return NULL if error, else pointer to start of data
    145  */
    146 const GLvoid *
    147 _mesa_map_pbo_source(struct gl_context *ctx,
    148                      const struct gl_pixelstore_attrib *unpack,
    149                      const GLvoid *src)
    150 {
    151    const GLubyte *buf;
    152 
    153    if (_mesa_is_bufferobj(unpack->BufferObj)) {
    154       /* unpack from PBO */
    155       buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
    156 						   unpack->BufferObj->Size,
    157 						   GL_MAP_READ_BIT,
    158 						   unpack->BufferObj,
    159                                                    MAP_INTERNAL);
    160       if (!buf)
    161          return NULL;
    162 
    163       buf = ADD_POINTERS(buf, src);
    164    }
    165    else {
    166       /* unpack from normal memory */
    167       buf = src;
    168    }
    169 
    170    return buf;
    171 }
    172 
    173 /**
    174  * Perform PBO validation for read operations with uncompressed textures.
    175  * If any GL errors are detected, false is returned, otherwise returns true.
    176  * \sa _mesa_validate_pbo_access
    177  */
    178 bool
    179 _mesa_validate_pbo_source(struct gl_context *ctx, GLuint dimensions,
    180                           const struct gl_pixelstore_attrib *unpack,
    181                           GLsizei width, GLsizei height, GLsizei depth,
    182                           GLenum format, GLenum type,
    183                           GLsizei clientMemSize,
    184                           const GLvoid *ptr, const char *where)
    185 {
    186    assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
    187 
    188    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
    189                                   format, type, clientMemSize, ptr)) {
    190       if (_mesa_is_bufferobj(unpack->BufferObj)) {
    191          _mesa_error(ctx, GL_INVALID_OPERATION,
    192                      "%s(out of bounds PBO access)",
    193                      where);
    194       } else {
    195          _mesa_error(ctx, GL_INVALID_OPERATION,
    196                      "%s(out of bounds access: bufSize (%d) is too small)",
    197                      where, clientMemSize);
    198       }
    199       return false;
    200    }
    201 
    202    if (!_mesa_is_bufferobj(unpack->BufferObj)) {
    203       /* non-PBO access: no further validation to be done */
    204       return true;
    205    }
    206 
    207    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
    208       /* buffer is already mapped - that's an error */
    209       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
    210                   where);
    211       return false;
    212    }
    213 
    214    return true;
    215 }
    216 
    217 /**
    218  * Perform PBO validation for read operations with compressed textures.
    219  * If any GL errors are detected, false is returned, otherwise returns true.
    220  */
    221 bool
    222 _mesa_validate_pbo_source_compressed(struct gl_context *ctx, GLuint dimensions,
    223                                      const struct gl_pixelstore_attrib *unpack,
    224                                      GLsizei imageSize, const GLvoid *pixels,
    225                                      const char *where)
    226 {
    227    if (!_mesa_is_bufferobj(unpack->BufferObj)) {
    228       /* not using a PBO */
    229       return true;
    230    }
    231 
    232    if ((const GLubyte *) pixels + imageSize >
    233        ((const GLubyte *) 0) + unpack->BufferObj->Size) {
    234       /* out of bounds read! */
    235       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid PBO access)",
    236                   where);
    237       return false;
    238    }
    239 
    240    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
    241       /* buffer is already mapped - that's an error */
    242       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
    243                   where);
    244       return false;
    245    }
    246 
    247    return true;
    248 }
    249 
    250 /**
    251  * Perform PBO-read mapping.
    252  * If any GL errors are detected, they'll be recorded and NULL returned.
    253  * \sa _mesa_validate_pbo_source
    254  * \sa _mesa_map_pbo_source
    255  * A call to this function should have a matching call to
    256  * _mesa_unmap_pbo_source().
    257  */
    258 const GLvoid *
    259 _mesa_map_validate_pbo_source(struct gl_context *ctx,
    260                               GLuint dimensions,
    261                               const struct gl_pixelstore_attrib *unpack,
    262                               GLsizei width, GLsizei height, GLsizei depth,
    263                               GLenum format, GLenum type,
    264                               GLsizei clientMemSize,
    265                               const GLvoid *ptr, const char *where)
    266 {
    267    if (!_mesa_validate_pbo_source(ctx, dimensions, unpack,
    268                                   width, height, depth, format, type,
    269                                   clientMemSize, ptr, where)) {
    270      return NULL;
    271    }
    272 
    273    ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
    274    return ptr;
    275 }
    276 
    277 
    278 /**
    279  * Counterpart to _mesa_map_pbo_source()
    280  */
    281 void
    282 _mesa_unmap_pbo_source(struct gl_context *ctx,
    283                        const struct gl_pixelstore_attrib *unpack)
    284 {
    285    assert(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
    286    if (_mesa_is_bufferobj(unpack->BufferObj)) {
    287       ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
    288    }
    289 }
    290 
    291 
    292 /**
    293  * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
    294  * if we're writing to a PBO, map it write-only and return the pointer
    295  * into the PBO.  If we're not writing to a PBO, return \p dst as-is.
    296  * If non-null return, must call _mesa_unmap_pbo_dest() when done.
    297  *
    298  * \return NULL if error, else pointer to start of data
    299  */
    300 void *
    301 _mesa_map_pbo_dest(struct gl_context *ctx,
    302                    const struct gl_pixelstore_attrib *pack,
    303                    GLvoid *dest)
    304 {
    305    void *buf;
    306 
    307    if (_mesa_is_bufferobj(pack->BufferObj)) {
    308       /* pack into PBO */
    309       buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
    310 						   pack->BufferObj->Size,
    311 						   GL_MAP_WRITE_BIT,
    312 						   pack->BufferObj,
    313                                                    MAP_INTERNAL);
    314       if (!buf)
    315          return NULL;
    316 
    317       buf = ADD_POINTERS(buf, dest);
    318    }
    319    else {
    320       /* pack to normal memory */
    321       buf = dest;
    322    }
    323 
    324    return buf;
    325 }
    326 
    327 
    328 /**
    329  * Combine PBO-write validation and mapping.
    330  * If any GL errors are detected, they'll be recorded and NULL returned.
    331  * \sa _mesa_validate_pbo_access
    332  * \sa _mesa_map_pbo_dest
    333  * A call to this function should have a matching call to
    334  * _mesa_unmap_pbo_dest().
    335  */
    336 GLvoid *
    337 _mesa_map_validate_pbo_dest(struct gl_context *ctx,
    338                             GLuint dimensions,
    339                             const struct gl_pixelstore_attrib *unpack,
    340                             GLsizei width, GLsizei height, GLsizei depth,
    341                             GLenum format, GLenum type, GLsizei clientMemSize,
    342                             GLvoid *ptr, const char *where)
    343 {
    344    assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
    345 
    346    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
    347                                   format, type, clientMemSize, ptr)) {
    348       if (_mesa_is_bufferobj(unpack->BufferObj)) {
    349          _mesa_error(ctx, GL_INVALID_OPERATION,
    350                      "%s(out of bounds PBO access)", where);
    351       } else {
    352          _mesa_error(ctx, GL_INVALID_OPERATION,
    353                      "%s(out of bounds access: bufSize (%d) is too small)",
    354                      where, clientMemSize);
    355       }
    356       return NULL;
    357    }
    358 
    359    if (!_mesa_is_bufferobj(unpack->BufferObj)) {
    360       /* non-PBO access: no further validation to be done */
    361       return ptr;
    362    }
    363 
    364    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
    365       /* buffer is already mapped - that's an error */
    366       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
    367       return NULL;
    368    }
    369 
    370    ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
    371    return ptr;
    372 }
    373 
    374 
    375 /**
    376  * Counterpart to _mesa_map_pbo_dest()
    377  */
    378 void
    379 _mesa_unmap_pbo_dest(struct gl_context *ctx,
    380                      const struct gl_pixelstore_attrib *pack)
    381 {
    382    assert(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
    383    if (_mesa_is_bufferobj(pack->BufferObj)) {
    384       ctx->Driver.UnmapBuffer(ctx, pack->BufferObj, MAP_INTERNAL);
    385    }
    386 }
    387 
    388 
    389 /**
    390  * Check if an unpack PBO is active prior to fetching a texture image.
    391  * If so, do bounds checking and map the buffer into main memory.
    392  * Any errors detected will be recorded.
    393  * The caller _must_ call _mesa_unmap_teximage_pbo() too!
    394  */
    395 const GLvoid *
    396 _mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
    397 			    GLsizei width, GLsizei height, GLsizei depth,
    398 			    GLenum format, GLenum type, const GLvoid *pixels,
    399 			    const struct gl_pixelstore_attrib *unpack,
    400 			    const char *funcName)
    401 {
    402    GLubyte *buf;
    403 
    404    if (!_mesa_is_bufferobj(unpack->BufferObj)) {
    405       /* no PBO */
    406       return pixels;
    407    }
    408    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
    409                                   format, type, INT_MAX, pixels)) {
    410       _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
    411                   funcName, dimensions);
    412       return NULL;
    413    }
    414 
    415    buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
    416                                                 unpack->BufferObj->Size,
    417 						GL_MAP_READ_BIT,
    418 						unpack->BufferObj,
    419                                                 MAP_INTERNAL);
    420    if (!buf) {
    421       _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
    422                   dimensions);
    423       return NULL;
    424    }
    425 
    426    return ADD_POINTERS(buf, pixels);
    427 }
    428 
    429 
    430 /**
    431  * Check if an unpack PBO is active prior to fetching a compressed texture
    432  * image.
    433  * If so, do bounds checking and map the buffer into main memory.
    434  * Any errors detected will be recorded.
    435  * The caller _must_ call _mesa_unmap_teximage_pbo() too!
    436  */
    437 const GLvoid *
    438 _mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
    439                                  GLuint dimensions, GLsizei imageSize,
    440                                  const GLvoid *pixels,
    441                                  const struct gl_pixelstore_attrib *packing,
    442                                  const char *funcName)
    443 {
    444    GLubyte *buf;
    445 
    446    if (!_mesa_validate_pbo_source_compressed(ctx, dimensions, packing,
    447                                              imageSize, pixels, funcName)) {
    448      /* error is already set during validation */
    449       return NULL;
    450    }
    451 
    452    if (!_mesa_is_bufferobj(packing->BufferObj)) {
    453       /* not using a PBO - return pointer unchanged */
    454       return pixels;
    455    }
    456 
    457    buf = (GLubyte*) ctx->Driver.MapBufferRange(ctx, 0,
    458 					       packing->BufferObj->Size,
    459 					       GL_MAP_READ_BIT,
    460 					       packing->BufferObj,
    461                                                MAP_INTERNAL);
    462 
    463    /* Validation above already checked that PBO is not mapped, so buffer
    464     * should not be null.
    465     */
    466    assert(buf);
    467 
    468    return ADD_POINTERS(buf, pixels);
    469 }
    470 
    471 
    472 /**
    473  * This function must be called after either of the validate_pbo_*_teximage()
    474  * functions.  It unmaps the PBO buffer if it was mapped earlier.
    475  */
    476 void
    477 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
    478                          const struct gl_pixelstore_attrib *unpack)
    479 {
    480    if (_mesa_is_bufferobj(unpack->BufferObj)) {
    481       ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
    482    }
    483 }
    484