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
_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 (!_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 *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)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 *
_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)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
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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 *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)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 *
_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)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
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)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 *
_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)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 *
_mesa_validate_pbo_compressed_teximage(struct gl_context * ctx,GLsizei imageSize,const GLvoid * pixels,const struct gl_pixelstore_attrib * packing,const char * funcName)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
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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