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 */
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 (!_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 */
139const GLvoid *
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 */
174const GLvoid *
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 */
217void
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 */
236void *
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 */
271GLvoid *
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 */
313void
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 */
330const GLvoid *
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 */
369const GLvoid *
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 */
405void
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