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 "imports.h"
40#include "mtypes.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 */
67GLboolean
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 (!_mesa_is_bufferobj(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 */
147const GLvoid *
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 (_mesa_is_bufferobj(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 */
179bool
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 (_mesa_is_bufferobj(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 (!_mesa_is_bufferobj(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 */
222bool
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 (!_mesa_is_bufferobj(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 */
259const GLvoid *
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 */
282void
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 (_mesa_is_bufferobj(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 */
301void *
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 (_mesa_is_bufferobj(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 */
337GLvoid *
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 (_mesa_is_bufferobj(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 (!_mesa_is_bufferobj(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 */
379void
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 (_mesa_is_bufferobj(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 */
396const GLvoid *
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 (!_mesa_is_bufferobj(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 */
438const GLvoid *
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 (!_mesa_is_bufferobj(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 */
477void
478_mesa_unmap_teximage_pbo(struct gl_context *ctx,
479                         const struct gl_pixelstore_attrib *unpack)
480{
481   if (_mesa_is_bufferobj(unpack->BufferObj)) {
482      ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
483   }
484}
485