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 "glheader.h"
35 #include "bufferobj.h"
36 #include "glformats.h"
37 #include "image.h"
38 #include "imports.h"
39 #include "mtypes.h"
40 #include "pbo.h"
41
42
43
44 /**
45 * When we're about to read pixel data out of a PBO (via glDrawPixels,
46 * glTexImage, etc) or write data into a PBO (via glReadPixels,
47 * glGetTexImage, etc) we call this function to check that we're not
48 * going to read/write out of bounds.
49 *
50 * XXX This would also be a convenient time to check that the PBO isn't
51 * currently mapped. Whoever calls this function should check for that.
52 * Remember, we can't use a PBO when it's mapped!
53 *
54 * If we're not using a PBO, this is a no-op.
55 *
56 * \param width width of image to read/write
57 * \param height height of image to read/write
58 * \param depth depth of image to read/write
59 * \param format format of image to read/write
60 * \param type datatype of image to read/write
61 * \param clientMemSize the maximum number of bytes to read/write
62 * \param ptr the user-provided pointer/offset
63 * \return GL_TRUE if the buffer access is OK, GL_FALSE if the access would
64 * go out of bounds.
65 */
66 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)67 _mesa_validate_pbo_access(GLuint dimensions,
68 const struct gl_pixelstore_attrib *pack,
69 GLsizei width, GLsizei height, GLsizei depth,
70 GLenum format, GLenum type, GLsizei clientMemSize,
71 const GLvoid *ptr)
72 {
73 /* unsigned, to detect overflow/wrap-around */
74 uintptr_t start, end, offset, size;
75
76 /* If no PBO is bound, 'ptr' is a pointer to client memory containing
77 'clientMemSize' bytes.
78 If a PBO is bound, 'ptr' is an offset into the bound PBO.
79 In that case 'clientMemSize' is ignored: we just use the PBO's size.
80 */
81 if (!_mesa_is_bufferobj(pack->BufferObj)) {
82 offset = 0;
83 size = (clientMemSize == INT_MAX) ? UINTPTR_MAX : clientMemSize;
84 } else {
85 offset = (uintptr_t)ptr;
86 size = pack->BufferObj->Size;
87 /* The ARB_pixel_buffer_object spec says:
88 * "INVALID_OPERATION is generated by ColorTable, ColorSubTable,
89 * ConvolutionFilter2D, ConvolutionFilter1D, SeparableFilter2D,
90 * TexImage1D, TexImage2D, TexImage3D, TexSubImage1D,
91 * TexSubImage2D, TexSubImage3D, and DrawPixels if the current
92 * PIXEL_UNPACK_BUFFER_BINDING_ARB value is non-zero and the data
93 * parameter is not evenly divisible into the number of basic machine
94 * units needed to store in memory a datum indicated by the type
95 * parameter."
96 */
97 if (type != GL_BITMAP &&
98 (offset % _mesa_sizeof_packed_type(type)))
99 return GL_FALSE;
100 }
101
102 if (size == 0)
103 /* no buffer! */
104 return GL_FALSE;
105
106 /* If the size of the image is zero then no pixels are accessed so we
107 * don't need to check anything else.
108 */
109 if (width == 0 || height == 0 || depth == 0)
110 return GL_TRUE;
111
112 /* get the offset to the first pixel we'll read/write */
113 start = _mesa_image_offset(dimensions, pack, width, height,
114 format, type, 0, 0, 0);
115
116 /* get the offset to just past the last pixel we'll read/write */
117 end = _mesa_image_offset(dimensions, pack, width, height,
118 format, type, depth-1, height-1, width);
119
120 start += offset;
121 end += offset;
122
123 if (start > size) {
124 /* This will catch negative values / wrap-around */
125 return GL_FALSE;
126 }
127 if (end > size) {
128 /* Image read/write goes beyond end of buffer */
129 return GL_FALSE;
130 }
131
132 /* OK! */
133 return GL_TRUE;
134 }
135
136
137 /**
138 * For commands that read from a PBO (glDrawPixels, glTexImage,
139 * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
140 * and return the pointer into the PBO. If we're not reading from a
141 * PBO, return \p src as-is.
142 * If non-null return, must call _mesa_unmap_pbo_source() when done.
143 *
144 * \return NULL if error, else pointer to start of data
145 */
146 const GLvoid *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)147 _mesa_map_pbo_source(struct gl_context *ctx,
148 const struct gl_pixelstore_attrib *unpack,
149 const GLvoid *src)
150 {
151 const GLubyte *buf;
152
153 if (_mesa_is_bufferobj(unpack->BufferObj)) {
154 /* unpack from PBO */
155 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
156 unpack->BufferObj->Size,
157 GL_MAP_READ_BIT,
158 unpack->BufferObj,
159 MAP_INTERNAL);
160 if (!buf)
161 return NULL;
162
163 buf = ADD_POINTERS(buf, src);
164 }
165 else {
166 /* unpack from normal memory */
167 buf = src;
168 }
169
170 return buf;
171 }
172
173 /**
174 * Perform PBO validation for read operations with uncompressed textures.
175 * If any GL errors are detected, false is returned, otherwise returns true.
176 * \sa _mesa_validate_pbo_access
177 */
178 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)179 _mesa_validate_pbo_source(struct gl_context *ctx, GLuint dimensions,
180 const struct gl_pixelstore_attrib *unpack,
181 GLsizei width, GLsizei height, GLsizei depth,
182 GLenum format, GLenum type,
183 GLsizei clientMemSize,
184 const GLvoid *ptr, const char *where)
185 {
186 assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
187
188 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
189 format, type, clientMemSize, ptr)) {
190 if (_mesa_is_bufferobj(unpack->BufferObj)) {
191 _mesa_error(ctx, GL_INVALID_OPERATION,
192 "%s(out of bounds PBO access)",
193 where);
194 } else {
195 _mesa_error(ctx, GL_INVALID_OPERATION,
196 "%s(out of bounds access: bufSize (%d) is too small)",
197 where, clientMemSize);
198 }
199 return false;
200 }
201
202 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
203 /* non-PBO access: no further validation to be done */
204 return true;
205 }
206
207 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
208 /* buffer is already mapped - that's an error */
209 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
210 where);
211 return false;
212 }
213
214 return true;
215 }
216
217 /**
218 * Perform PBO validation for read operations with compressed textures.
219 * If any GL errors are detected, false is returned, otherwise returns true.
220 */
221 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)222 _mesa_validate_pbo_source_compressed(struct gl_context *ctx, GLuint dimensions,
223 const struct gl_pixelstore_attrib *unpack,
224 GLsizei imageSize, const GLvoid *pixels,
225 const char *where)
226 {
227 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
228 /* not using a PBO */
229 return true;
230 }
231
232 if ((const GLubyte *) pixels + imageSize >
233 ((const GLubyte *) 0) + unpack->BufferObj->Size) {
234 /* out of bounds read! */
235 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid PBO access)",
236 where);
237 return false;
238 }
239
240 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
241 /* buffer is already mapped - that's an error */
242 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
243 where);
244 return false;
245 }
246
247 return true;
248 }
249
250 /**
251 * Perform PBO-read mapping.
252 * If any GL errors are detected, they'll be recorded and NULL returned.
253 * \sa _mesa_validate_pbo_source
254 * \sa _mesa_map_pbo_source
255 * A call to this function should have a matching call to
256 * _mesa_unmap_pbo_source().
257 */
258 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)259 _mesa_map_validate_pbo_source(struct gl_context *ctx,
260 GLuint dimensions,
261 const struct gl_pixelstore_attrib *unpack,
262 GLsizei width, GLsizei height, GLsizei depth,
263 GLenum format, GLenum type,
264 GLsizei clientMemSize,
265 const GLvoid *ptr, const char *where)
266 {
267 if (!_mesa_validate_pbo_source(ctx, dimensions, unpack,
268 width, height, depth, format, type,
269 clientMemSize, ptr, where)) {
270 return NULL;
271 }
272
273 ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
274 return ptr;
275 }
276
277
278 /**
279 * Counterpart to _mesa_map_pbo_source()
280 */
281 void
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)282 _mesa_unmap_pbo_source(struct gl_context *ctx,
283 const struct gl_pixelstore_attrib *unpack)
284 {
285 assert(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
286 if (_mesa_is_bufferobj(unpack->BufferObj)) {
287 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
288 }
289 }
290
291
292 /**
293 * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
294 * if we're writing to a PBO, map it write-only and return the pointer
295 * into the PBO. If we're not writing to a PBO, return \p dst as-is.
296 * If non-null return, must call _mesa_unmap_pbo_dest() when done.
297 *
298 * \return NULL if error, else pointer to start of data
299 */
300 void *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)301 _mesa_map_pbo_dest(struct gl_context *ctx,
302 const struct gl_pixelstore_attrib *pack,
303 GLvoid *dest)
304 {
305 void *buf;
306
307 if (_mesa_is_bufferobj(pack->BufferObj)) {
308 /* pack into PBO */
309 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
310 pack->BufferObj->Size,
311 GL_MAP_WRITE_BIT,
312 pack->BufferObj,
313 MAP_INTERNAL);
314 if (!buf)
315 return NULL;
316
317 buf = ADD_POINTERS(buf, dest);
318 }
319 else {
320 /* pack to normal memory */
321 buf = dest;
322 }
323
324 return buf;
325 }
326
327
328 /**
329 * Combine PBO-write validation and mapping.
330 * If any GL errors are detected, they'll be recorded and NULL returned.
331 * \sa _mesa_validate_pbo_access
332 * \sa _mesa_map_pbo_dest
333 * A call to this function should have a matching call to
334 * _mesa_unmap_pbo_dest().
335 */
336 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)337 _mesa_map_validate_pbo_dest(struct gl_context *ctx,
338 GLuint dimensions,
339 const struct gl_pixelstore_attrib *unpack,
340 GLsizei width, GLsizei height, GLsizei depth,
341 GLenum format, GLenum type, GLsizei clientMemSize,
342 GLvoid *ptr, const char *where)
343 {
344 assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
345
346 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
347 format, type, clientMemSize, ptr)) {
348 if (_mesa_is_bufferobj(unpack->BufferObj)) {
349 _mesa_error(ctx, GL_INVALID_OPERATION,
350 "%s(out of bounds PBO access)", where);
351 } else {
352 _mesa_error(ctx, GL_INVALID_OPERATION,
353 "%s(out of bounds access: bufSize (%d) is too small)",
354 where, clientMemSize);
355 }
356 return NULL;
357 }
358
359 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
360 /* non-PBO access: no further validation to be done */
361 return ptr;
362 }
363
364 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
365 /* buffer is already mapped - that's an error */
366 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
367 return NULL;
368 }
369
370 ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
371 return ptr;
372 }
373
374
375 /**
376 * Counterpart to _mesa_map_pbo_dest()
377 */
378 void
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)379 _mesa_unmap_pbo_dest(struct gl_context *ctx,
380 const struct gl_pixelstore_attrib *pack)
381 {
382 assert(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
383 if (_mesa_is_bufferobj(pack->BufferObj)) {
384 ctx->Driver.UnmapBuffer(ctx, pack->BufferObj, MAP_INTERNAL);
385 }
386 }
387
388
389 /**
390 * Check if an unpack PBO is active prior to fetching a texture image.
391 * If so, do bounds checking and map the buffer into main memory.
392 * Any errors detected will be recorded.
393 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
394 */
395 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)396 _mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
397 GLsizei width, GLsizei height, GLsizei depth,
398 GLenum format, GLenum type, const GLvoid *pixels,
399 const struct gl_pixelstore_attrib *unpack,
400 const char *funcName)
401 {
402 GLubyte *buf;
403
404 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
405 /* no PBO */
406 return pixels;
407 }
408 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
409 format, type, INT_MAX, pixels)) {
410 _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
411 funcName, dimensions);
412 return NULL;
413 }
414
415 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
416 unpack->BufferObj->Size,
417 GL_MAP_READ_BIT,
418 unpack->BufferObj,
419 MAP_INTERNAL);
420 if (!buf) {
421 _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
422 dimensions);
423 return NULL;
424 }
425
426 return ADD_POINTERS(buf, pixels);
427 }
428
429
430 /**
431 * Check if an unpack PBO is active prior to fetching a compressed texture
432 * image.
433 * If so, do bounds checking and map the buffer into main memory.
434 * Any errors detected will be recorded.
435 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
436 */
437 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)438 _mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
439 GLuint dimensions, GLsizei imageSize,
440 const GLvoid *pixels,
441 const struct gl_pixelstore_attrib *packing,
442 const char *funcName)
443 {
444 GLubyte *buf;
445
446 if (!_mesa_validate_pbo_source_compressed(ctx, dimensions, packing,
447 imageSize, pixels, funcName)) {
448 /* error is already set during validation */
449 return NULL;
450 }
451
452 if (!_mesa_is_bufferobj(packing->BufferObj)) {
453 /* not using a PBO - return pointer unchanged */
454 return pixels;
455 }
456
457 buf = (GLubyte*) ctx->Driver.MapBufferRange(ctx, 0,
458 packing->BufferObj->Size,
459 GL_MAP_READ_BIT,
460 packing->BufferObj,
461 MAP_INTERNAL);
462
463 /* Validation above already checked that PBO is not mapped, so buffer
464 * should not be null.
465 */
466 assert(buf);
467
468 return ADD_POINTERS(buf, pixels);
469 }
470
471
472 /**
473 * This function must be called after either of the validate_pbo_*_teximage()
474 * functions. It unmaps the PBO buffer if it was mapped earlier.
475 */
476 void
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)477 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
478 const struct gl_pixelstore_attrib *unpack)
479 {
480 if (_mesa_is_bufferobj(unpack->BufferObj)) {
481 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
482 }
483 }
484