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
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 */
67 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)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 (!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 */
147 const GLvoid *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)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 (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 */
179 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)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 (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 (!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 */
222 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)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 (!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 */
259 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)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 */
282 void
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)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 (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 */
301 void *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)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 (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 */
337 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)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 (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 (!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 */
379 void
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)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 (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 */
396 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)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 (!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 */
438 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)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 (!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 */
477 void
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)478 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
479 const struct gl_pixelstore_attrib *unpack)
480 {
481 if (unpack->BufferObj) {
482 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
483 }
484 }
485