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 */
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 (!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 */
145 const GLvoid *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)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 */
177 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)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 */
220 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)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 */
257 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)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 */
280 void
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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 */
299 void *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)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 */
335 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)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 */
377 void
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)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 */
394 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)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 */
436 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)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 */
475 void
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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