mesa: Separate PBO validation checks from buffer mapping, to allow reuse
[mesa.git] / src / mesa / main / pbo.c
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 /* get the offset to the first pixel we'll read/write */
107 start = _mesa_image_offset(dimensions, pack, width, height,
108 format, type, 0, 0, 0);
109
110 /* get the offset to just past the last pixel we'll read/write */
111 end = _mesa_image_offset(dimensions, pack, width, height,
112 format, type, depth-1, height-1, width);
113
114 start += offset;
115 end += offset;
116
117 if (start > size) {
118 /* This will catch negative values / wrap-around */
119 return GL_FALSE;
120 }
121 if (end > size) {
122 /* Image read/write goes beyond end of buffer */
123 return GL_FALSE;
124 }
125
126 /* OK! */
127 return GL_TRUE;
128 }
129
130
131 /**
132 * For commands that read from a PBO (glDrawPixels, glTexImage,
133 * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
134 * and return the pointer into the PBO. If we're not reading from a
135 * PBO, return \p src as-is.
136 * If non-null return, must call _mesa_unmap_pbo_source() when done.
137 *
138 * \return NULL if error, else pointer to start of data
139 */
140 const GLvoid *
141 _mesa_map_pbo_source(struct gl_context *ctx,
142 const struct gl_pixelstore_attrib *unpack,
143 const GLvoid *src)
144 {
145 const GLubyte *buf;
146
147 if (_mesa_is_bufferobj(unpack->BufferObj)) {
148 /* unpack from PBO */
149 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
150 unpack->BufferObj->Size,
151 GL_MAP_READ_BIT,
152 unpack->BufferObj,
153 MAP_INTERNAL);
154 if (!buf)
155 return NULL;
156
157 buf = ADD_POINTERS(buf, src);
158 }
159 else {
160 /* unpack from normal memory */
161 buf = src;
162 }
163
164 return buf;
165 }
166
167 /**
168 * Perform PBO validation for read operations with uncompressed textures.
169 * If any GL errors are detected, false is returned, otherwise returns true.
170 * \sa _mesa_validate_pbo_access
171 */
172 bool
173 _mesa_validate_pbo_source(struct gl_context *ctx, GLuint dimensions,
174 const struct gl_pixelstore_attrib *unpack,
175 GLsizei width, GLsizei height, GLsizei depth,
176 GLenum format, GLenum type,
177 GLsizei clientMemSize,
178 const GLvoid *ptr, const char *where)
179 {
180 assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
181
182 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
183 format, type, clientMemSize, ptr)) {
184 if (_mesa_is_bufferobj(unpack->BufferObj)) {
185 _mesa_error(ctx, GL_INVALID_OPERATION,
186 "%s(out of bounds PBO access)",
187 where);
188 } else {
189 _mesa_error(ctx, GL_INVALID_OPERATION,
190 "%s(out of bounds access: bufSize (%d) is too small)",
191 where, clientMemSize);
192 }
193 return false;
194 }
195
196 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
197 /* non-PBO access: no further validation to be done */
198 return true;
199 }
200
201 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
202 /* buffer is already mapped - that's an error */
203 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
204 where);
205 return false;
206 }
207
208 return true;
209 }
210
211 /**
212 * Perform PBO validation for read operations with compressed textures.
213 * If any GL errors are detected, false is returned, otherwise returns true.
214 */
215 bool
216 _mesa_validate_pbo_source_compressed(struct gl_context *ctx, GLuint dimensions,
217 const struct gl_pixelstore_attrib *unpack,
218 GLsizei imageSize, const GLvoid *pixels,
219 const char *where)
220 {
221 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
222 /* not using a PBO */
223 return true;
224 }
225
226 if ((const GLubyte *) pixels + imageSize >
227 ((const GLubyte *) 0) + unpack->BufferObj->Size) {
228 /* out of bounds read! */
229 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid PBO access)",
230 where);
231 return false;
232 }
233
234 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
235 /* buffer is already mapped - that's an error */
236 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)",
237 where);
238 return false;
239 }
240
241 return true;
242 }
243
244 /**
245 * Perform PBO-read mapping.
246 * If any GL errors are detected, they'll be recorded and NULL returned.
247 * \sa _mesa_validate_pbo_source
248 * \sa _mesa_map_pbo_source
249 * A call to this function should have a matching call to
250 * _mesa_unmap_pbo_source().
251 */
252 const GLvoid *
253 _mesa_map_validate_pbo_source(struct gl_context *ctx,
254 GLuint dimensions,
255 const struct gl_pixelstore_attrib *unpack,
256 GLsizei width, GLsizei height, GLsizei depth,
257 GLenum format, GLenum type,
258 GLsizei clientMemSize,
259 const GLvoid *ptr, const char *where)
260 {
261 if (!_mesa_validate_pbo_source(ctx, dimensions, unpack,
262 width, height, depth, format, type,
263 clientMemSize, ptr, where)) {
264 return NULL;
265 }
266
267 ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
268 return ptr;
269 }
270
271
272 /**
273 * Counterpart to _mesa_map_pbo_source()
274 */
275 void
276 _mesa_unmap_pbo_source(struct gl_context *ctx,
277 const struct gl_pixelstore_attrib *unpack)
278 {
279 assert(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
280 if (_mesa_is_bufferobj(unpack->BufferObj)) {
281 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
282 }
283 }
284
285
286 /**
287 * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
288 * if we're writing to a PBO, map it write-only and return the pointer
289 * into the PBO. If we're not writing to a PBO, return \p dst as-is.
290 * If non-null return, must call _mesa_unmap_pbo_dest() when done.
291 *
292 * \return NULL if error, else pointer to start of data
293 */
294 void *
295 _mesa_map_pbo_dest(struct gl_context *ctx,
296 const struct gl_pixelstore_attrib *pack,
297 GLvoid *dest)
298 {
299 void *buf;
300
301 if (_mesa_is_bufferobj(pack->BufferObj)) {
302 /* pack into PBO */
303 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
304 pack->BufferObj->Size,
305 GL_MAP_WRITE_BIT,
306 pack->BufferObj,
307 MAP_INTERNAL);
308 if (!buf)
309 return NULL;
310
311 buf = ADD_POINTERS(buf, dest);
312 }
313 else {
314 /* pack to normal memory */
315 buf = dest;
316 }
317
318 return buf;
319 }
320
321
322 /**
323 * Combine PBO-write validation and mapping.
324 * If any GL errors are detected, they'll be recorded and NULL returned.
325 * \sa _mesa_validate_pbo_access
326 * \sa _mesa_map_pbo_dest
327 * A call to this function should have a matching call to
328 * _mesa_unmap_pbo_dest().
329 */
330 GLvoid *
331 _mesa_map_validate_pbo_dest(struct gl_context *ctx,
332 GLuint dimensions,
333 const struct gl_pixelstore_attrib *unpack,
334 GLsizei width, GLsizei height, GLsizei depth,
335 GLenum format, GLenum type, GLsizei clientMemSize,
336 GLvoid *ptr, const char *where)
337 {
338 assert(dimensions == 1 || dimensions == 2 || dimensions == 3);
339
340 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
341 format, type, clientMemSize, ptr)) {
342 if (_mesa_is_bufferobj(unpack->BufferObj)) {
343 _mesa_error(ctx, GL_INVALID_OPERATION,
344 "%s(out of bounds PBO access)", where);
345 } else {
346 _mesa_error(ctx, GL_INVALID_OPERATION,
347 "%s(out of bounds access: bufSize (%d) is too small)",
348 where, clientMemSize);
349 }
350 return NULL;
351 }
352
353 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
354 /* non-PBO access: no further validation to be done */
355 return ptr;
356 }
357
358 if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
359 /* buffer is already mapped - that's an error */
360 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
361 return NULL;
362 }
363
364 ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
365 return ptr;
366 }
367
368
369 /**
370 * Counterpart to _mesa_map_pbo_dest()
371 */
372 void
373 _mesa_unmap_pbo_dest(struct gl_context *ctx,
374 const struct gl_pixelstore_attrib *pack)
375 {
376 assert(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
377 if (_mesa_is_bufferobj(pack->BufferObj)) {
378 ctx->Driver.UnmapBuffer(ctx, pack->BufferObj, MAP_INTERNAL);
379 }
380 }
381
382
383 /**
384 * Check if an unpack PBO is active prior to fetching a texture image.
385 * If so, do bounds checking and map the buffer into main memory.
386 * Any errors detected will be recorded.
387 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
388 */
389 const GLvoid *
390 _mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
391 GLsizei width, GLsizei height, GLsizei depth,
392 GLenum format, GLenum type, const GLvoid *pixels,
393 const struct gl_pixelstore_attrib *unpack,
394 const char *funcName)
395 {
396 GLubyte *buf;
397
398 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
399 /* no PBO */
400 return pixels;
401 }
402 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
403 format, type, INT_MAX, pixels)) {
404 _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
405 funcName, dimensions);
406 return NULL;
407 }
408
409 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
410 unpack->BufferObj->Size,
411 GL_MAP_READ_BIT,
412 unpack->BufferObj,
413 MAP_INTERNAL);
414 if (!buf) {
415 _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
416 dimensions);
417 return NULL;
418 }
419
420 return ADD_POINTERS(buf, pixels);
421 }
422
423
424 /**
425 * Check if an unpack PBO is active prior to fetching a compressed texture
426 * image.
427 * If so, do bounds checking and map the buffer into main memory.
428 * Any errors detected will be recorded.
429 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
430 */
431 const GLvoid *
432 _mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
433 GLuint dimensions, GLsizei imageSize,
434 const GLvoid *pixels,
435 const struct gl_pixelstore_attrib *packing,
436 const char *funcName)
437 {
438 GLubyte *buf;
439
440 if (!_mesa_validate_pbo_source_compressed(ctx, dimensions, packing,
441 imageSize, pixels, funcName)) {
442 /* error is already set during validation */
443 return NULL;
444 }
445
446 if (!_mesa_is_bufferobj(packing->BufferObj)) {
447 /* not using a PBO - return pointer unchanged */
448 return pixels;
449 }
450
451 buf = (GLubyte*) ctx->Driver.MapBufferRange(ctx, 0,
452 packing->BufferObj->Size,
453 GL_MAP_READ_BIT,
454 packing->BufferObj,
455 MAP_INTERNAL);
456
457 /* Validation above already checked that PBO is not mapped, so buffer
458 * should not be null.
459 */
460 assert(buf);
461
462 return ADD_POINTERS(buf, pixels);
463 }
464
465
466 /**
467 * This function must be called after either of the validate_pbo_*_teximage()
468 * functions. It unmaps the PBO buffer if it was mapped earlier.
469 */
470 void
471 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
472 const struct gl_pixelstore_attrib *unpack)
473 {
474 if (_mesa_is_bufferobj(unpack->BufferObj)) {
475 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
476 }
477 }