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