• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
_mesa_validate_pbo_access(GLuint dimensions,const struct gl_pixelstore_attrib * pack,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,GLsizei clientMemSize,const GLvoid * ptr)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 *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)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
_mesa_validate_pbo_source(struct gl_context * ctx,GLuint dimensions,const struct gl_pixelstore_attrib * unpack,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,GLsizei clientMemSize,const GLvoid * ptr,const char * where)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
_mesa_validate_pbo_source_compressed(struct gl_context * ctx,GLuint dimensions,const struct gl_pixelstore_attrib * unpack,GLsizei imageSize,const GLvoid * pixels,const char * where)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 *
_mesa_map_validate_pbo_source(struct gl_context * ctx,GLuint dimensions,const struct gl_pixelstore_attrib * unpack,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,GLsizei clientMemSize,const GLvoid * ptr,const char * where)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
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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 *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)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 *
_mesa_map_validate_pbo_dest(struct gl_context * ctx,GLuint dimensions,const struct gl_pixelstore_attrib * unpack,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,GLsizei clientMemSize,GLvoid * ptr,const char * where)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
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)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 *
_mesa_validate_pbo_teximage(struct gl_context * ctx,GLuint dimensions,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,const GLvoid * pixels,const struct gl_pixelstore_attrib * unpack,const char * funcName)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 *
_mesa_validate_pbo_compressed_teximage(struct gl_context * ctx,GLuint dimensions,GLsizei imageSize,const GLvoid * pixels,const struct gl_pixelstore_attrib * packing,const char * funcName)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
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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