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 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 * 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 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 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 * 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 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 * 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 * 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 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 * 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 * 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 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