• 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 "errors.h"
35 #include "glheader.h"
36 #include "bufferobj.h"
37 #include "glformats.h"
38 #include "image.h"
39 #include "mtypes.h"
40 #include "macros.h"
41 #include "pbo.h"
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
_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)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 (!pack->BufferObj) {
81       offset = 0;
82       size = (clientMemSize == INT_MAX) ? UINTPTR_MAX : 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    /* If the size of the image is zero then no pixels are accessed so we
106     * don't need to check anything else.
107     */
108    if (width == 0 || height == 0 || depth == 0)
109       return GL_TRUE;
110 
111    /* get the offset to the first pixel we'll read/write */
112    start = _mesa_image_offset(dimensions, pack, width, height,
113                               format, type, 0, 0, 0);
114 
115    /* get the offset to just past the last pixel we'll read/write */
116    end =  _mesa_image_offset(dimensions, pack, width, height,
117                              format, type, depth-1, height-1, width);
118 
119    start += offset;
120    end += offset;
121 
122    if (start > size) {
123       /* This will catch negative values / wrap-around */
124       return GL_FALSE;
125    }
126    if (end > size) {
127       /* Image read/write goes beyond end of buffer */
128       return GL_FALSE;
129    }
130 
131    /* OK! */
132    return GL_TRUE;
133 }
134 
135 
136 /**
137  * For commands that read from a PBO (glDrawPixels, glTexImage,
138  * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
139  * and return the pointer into the PBO.  If we're not reading from a
140  * PBO, return \p src as-is.
141  * If non-null return, must call _mesa_unmap_pbo_source() when done.
142  *
143  * \return NULL if error, else pointer to start of data
144  */
145 const GLvoid *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)146 _mesa_map_pbo_source(struct gl_context *ctx,
147                      const struct gl_pixelstore_attrib *unpack,
148                      const GLvoid *src)
149 {
150    const GLubyte *buf;
151 
152    if (unpack->BufferObj) {
153       /* unpack from PBO */
154       buf = (GLubyte *) _mesa_bufferobj_map_range(ctx, 0,
155                                                   unpack->BufferObj->Size,
156                                                   GL_MAP_READ_BIT,
157                                                   unpack->BufferObj,
158                                                   MAP_INTERNAL);
159       if (!buf)
160          return NULL;
161 
162       buf = ADD_POINTERS(buf, src);
163    }
164    else {
165       /* unpack from normal memory */
166       buf = src;
167    }
168 
169    return buf;
170 }
171 
172 /**
173  * Perform PBO validation for read operations with uncompressed textures.
174  * If any GL errors are detected, false is returned, otherwise returns true.
175  * \sa _mesa_validate_pbo_access
176  */
177 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)178 _mesa_validate_pbo_source(struct gl_context *ctx, GLuint dimensions,
179                           const struct gl_pixelstore_attrib *unpack,
180                           GLsizei width, GLsizei height, GLsizei depth,
181                           GLenum format, GLenum type,
182                           GLsizei clientMemSize,
183                           const GLvoid *ptr, const char *where)
184 {
185    assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
186 
187    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
188                                   format, type, clientMemSize, ptr)) {
189       if (unpack->BufferObj) {
190          _mesa_error(ctx, GL_INVALID_OPERATION,
191                      "%s(out of bounds PBO access)",
192                      where);
193       } else {
194          _mesa_error(ctx, GL_INVALID_OPERATION,
195                      "%s(out of bounds access: bufSize (%d) is too small)",
196                      where, clientMemSize);
197       }
198       return false;
199    }
200 
201    if (!unpack->BufferObj) {
202       /* non-PBO access: no further validation to be done */
203       return true;
204    }
205 
206    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
207       /* buffer is already mapped - that's an error */
208       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
209                   where);
210       return false;
211    }
212 
213    return true;
214 }
215 
216 /**
217  * Perform PBO validation for read operations with compressed textures.
218  * If any GL errors are detected, false is returned, otherwise returns true.
219  */
220 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)221 _mesa_validate_pbo_source_compressed(struct gl_context *ctx, GLuint dimensions,
222                                      const struct gl_pixelstore_attrib *unpack,
223                                      GLsizei imageSize, const GLvoid *pixels,
224                                      const char *where)
225 {
226    if (!unpack->BufferObj) {
227       /* not using a PBO */
228       return true;
229    }
230 
231    if ((const GLubyte *) pixels + imageSize >
232        ((const GLubyte *) 0) + unpack->BufferObj->Size) {
233       /* out of bounds read! */
234       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid PBO access)",
235                   where);
236       return false;
237    }
238 
239    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
240       /* buffer is already mapped - that's an error */
241       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
242                   where);
243       return false;
244    }
245 
246    return true;
247 }
248 
249 /**
250  * Perform PBO-read mapping.
251  * If any GL errors are detected, they'll be recorded and NULL returned.
252  * \sa _mesa_validate_pbo_source
253  * \sa _mesa_map_pbo_source
254  * A call to this function should have a matching call to
255  * _mesa_unmap_pbo_source().
256  */
257 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)258 _mesa_map_validate_pbo_source(struct gl_context *ctx,
259                               GLuint dimensions,
260                               const struct gl_pixelstore_attrib *unpack,
261                               GLsizei width, GLsizei height, GLsizei depth,
262                               GLenum format, GLenum type,
263                               GLsizei clientMemSize,
264                               const GLvoid *ptr, const char *where)
265 {
266    if (!_mesa_validate_pbo_source(ctx, dimensions, unpack,
267                                   width, height, depth, format, type,
268                                   clientMemSize, ptr, where)) {
269      return NULL;
270    }
271 
272    ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
273    return ptr;
274 }
275 
276 
277 /**
278  * Counterpart to _mesa_map_pbo_source()
279  */
280 void
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)281 _mesa_unmap_pbo_source(struct gl_context *ctx,
282                        const struct gl_pixelstore_attrib *unpack)
283 {
284    assert(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
285    if (unpack->BufferObj) {
286       _mesa_bufferobj_unmap(ctx, unpack->BufferObj, MAP_INTERNAL);
287    }
288 }
289 
290 
291 /**
292  * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
293  * if we're writing to a PBO, map it write-only and return the pointer
294  * into the PBO.  If we're not writing to a PBO, return \p dst as-is.
295  * If non-null return, must call _mesa_unmap_pbo_dest() when done.
296  *
297  * \return NULL if error, else pointer to start of data
298  */
299 void *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)300 _mesa_map_pbo_dest(struct gl_context *ctx,
301                    const struct gl_pixelstore_attrib *pack,
302                    GLvoid *dest)
303 {
304    void *buf;
305 
306    if (pack->BufferObj) {
307       /* pack into PBO */
308       buf = (GLubyte *) _mesa_bufferobj_map_range(ctx, 0,
309                                                   pack->BufferObj->Size,
310                                                   GL_MAP_WRITE_BIT,
311                                                   pack->BufferObj,
312                                                   MAP_INTERNAL);
313       if (!buf)
314          return NULL;
315 
316       buf = ADD_POINTERS(buf, dest);
317    }
318    else {
319       /* pack to normal memory */
320       buf = dest;
321    }
322 
323    return buf;
324 }
325 
326 
327 /**
328  * Combine PBO-write validation and mapping.
329  * If any GL errors are detected, they'll be recorded and NULL returned.
330  * \sa _mesa_validate_pbo_access
331  * \sa _mesa_map_pbo_dest
332  * A call to this function should have a matching call to
333  * _mesa_unmap_pbo_dest().
334  */
335 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)336 _mesa_map_validate_pbo_dest(struct gl_context *ctx,
337                             GLuint dimensions,
338                             const struct gl_pixelstore_attrib *unpack,
339                             GLsizei width, GLsizei height, GLsizei depth,
340                             GLenum format, GLenum type, GLsizei clientMemSize,
341                             GLvoid *ptr, const char *where)
342 {
343    assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
344 
345    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
346                                   format, type, clientMemSize, ptr)) {
347       if (unpack->BufferObj) {
348          _mesa_error(ctx, GL_INVALID_OPERATION,
349                      "%s(out of bounds PBO access)", where);
350       } else {
351          _mesa_error(ctx, GL_INVALID_OPERATION,
352                      "%s(out of bounds access: bufSize (%d) is too small)",
353                      where, clientMemSize);
354       }
355       return NULL;
356    }
357 
358    if (!unpack->BufferObj) {
359       /* non-PBO access: no further validation to be done */
360       return ptr;
361    }
362 
363    if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
364       /* buffer is already mapped - that's an error */
365       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
366       return NULL;
367    }
368 
369    ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
370    return ptr;
371 }
372 
373 
374 /**
375  * Counterpart to _mesa_map_pbo_dest()
376  */
377 void
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)378 _mesa_unmap_pbo_dest(struct gl_context *ctx,
379                      const struct gl_pixelstore_attrib *pack)
380 {
381    assert(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
382    if (pack->BufferObj) {
383       _mesa_bufferobj_unmap(ctx, pack->BufferObj, MAP_INTERNAL);
384    }
385 }
386 
387 
388 /**
389  * Check if an unpack PBO is active prior to fetching a texture image.
390  * If so, do bounds checking and map the buffer into main memory.
391  * Any errors detected will be recorded.
392  * The caller _must_ call _mesa_unmap_teximage_pbo() too!
393  */
394 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)395 _mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
396 			    GLsizei width, GLsizei height, GLsizei depth,
397 			    GLenum format, GLenum type, const GLvoid *pixels,
398 			    const struct gl_pixelstore_attrib *unpack,
399 			    const char *funcName)
400 {
401    GLubyte *buf;
402 
403    if (!unpack->BufferObj) {
404       /* no PBO */
405       return pixels;
406    }
407    if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
408                                   format, type, INT_MAX, pixels)) {
409       _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
410                   funcName, dimensions);
411       return NULL;
412    }
413 
414    buf = (GLubyte *) _mesa_bufferobj_map_range(ctx, 0,
415                                                unpack->BufferObj->Size,
416                                                GL_MAP_READ_BIT,
417                                                unpack->BufferObj,
418                                                MAP_INTERNAL);
419    if (!buf) {
420       _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
421                   dimensions);
422       return NULL;
423    }
424 
425    return ADD_POINTERS(buf, pixels);
426 }
427 
428 
429 /**
430  * Check if an unpack PBO is active prior to fetching a compressed texture
431  * image.
432  * If so, do bounds checking and map the buffer into main memory.
433  * Any errors detected will be recorded.
434  * The caller _must_ call _mesa_unmap_teximage_pbo() too!
435  */
436 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)437 _mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
438                                  GLuint dimensions, GLsizei imageSize,
439                                  const GLvoid *pixels,
440                                  const struct gl_pixelstore_attrib *packing,
441                                  const char *funcName)
442 {
443    GLubyte *buf;
444 
445    if (!_mesa_validate_pbo_source_compressed(ctx, dimensions, packing,
446                                              imageSize, pixels, funcName)) {
447      /* error is already set during validation */
448       return NULL;
449    }
450 
451    if (!packing->BufferObj) {
452       /* not using a PBO - return pointer unchanged */
453       return pixels;
454    }
455 
456    buf = (GLubyte*) _mesa_bufferobj_map_range(ctx, 0,
457                                               packing->BufferObj->Size,
458                                               GL_MAP_READ_BIT,
459                                               packing->BufferObj,
460                                               MAP_INTERNAL);
461 
462    /* Validation above already checked that PBO is not mapped, so buffer
463     * should not be null.
464     */
465    assert(buf);
466 
467    return ADD_POINTERS(buf, pixels);
468 }
469 
470 
471 /**
472  * This function must be called after either of the validate_pbo_*_teximage()
473  * functions.  It unmaps the PBO buffer if it was mapped earlier.
474  */
475 void
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)476 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
477                          const struct gl_pixelstore_attrib *unpack)
478 {
479    if (unpack->BufferObj) {
480       _mesa_bufferobj_unmap(ctx, unpack->BufferObj, MAP_INTERNAL);
481    }
482 }
483