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