xref: /third_party/mesa3d/src/mesa/main/pbo.c (revision bf215546)
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 */
65GLboolean
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 */
145const GLvoid *
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 */
177bool
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 */
220bool
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 */
257const GLvoid *
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 */
280void
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 */
299void *
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 */
335GLvoid *
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 */
377void
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 */
394const GLvoid *
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 */
436const GLvoid *
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 */
475void
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