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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /**
27 * \file pbo.c
28 * \brief Functions related to Pixel Buffer Objects.
29 */
30
31
32
33 #include "glheader.h"
34 #include "bufferobj.h"
35 #include "glformats.h"
36 #include "image.h"
37 #include "imports.h"
38 #include "mtypes.h"
39 #include "pbo.h"
40
41
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 (!_mesa_is_bufferobj(pack->BufferObj)) {
81 offset = 0;
82 size = 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 /* get the offset to the first pixel we'll read/write */
106 start = _mesa_image_offset(dimensions, pack, width, height,
107 format, type, 0, 0, 0);
108
109 /* get the offset to just past the last pixel we'll read/write */
110 end = _mesa_image_offset(dimensions, pack, width, height,
111 format, type, depth-1, height-1, width);
112
113 start += offset;
114 end += offset;
115
116 if (start > size) {
117 /* This will catch negative values / wrap-around */
118 return GL_FALSE;
119 }
120 if (end > size) {
121 /* Image read/write goes beyond end of buffer */
122 return GL_FALSE;
123 }
124
125 /* OK! */
126 return GL_TRUE;
127 }
128
129
130 /**
131 * For commands that read from a PBO (glDrawPixels, glTexImage,
132 * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
133 * and return the pointer into the PBO. If we're not reading from a
134 * PBO, return \p src as-is.
135 * If non-null return, must call _mesa_unmap_pbo_source() when done.
136 *
137 * \return NULL if error, else pointer to start of data
138 */
139 const GLvoid *
_mesa_map_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack,const GLvoid * src)140 _mesa_map_pbo_source(struct gl_context *ctx,
141 const struct gl_pixelstore_attrib *unpack,
142 const GLvoid *src)
143 {
144 const GLubyte *buf;
145
146 if (_mesa_is_bufferobj(unpack->BufferObj)) {
147 /* unpack from PBO */
148 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
149 unpack->BufferObj->Size,
150 GL_MAP_READ_BIT,
151 unpack->BufferObj);
152 if (!buf)
153 return NULL;
154
155 buf = ADD_POINTERS(buf, src);
156 }
157 else {
158 /* unpack from normal memory */
159 buf = src;
160 }
161
162 return buf;
163 }
164
165
166 /**
167 * Combine PBO-read validation and mapping.
168 * If any GL errors are detected, they'll be recorded and NULL returned.
169 * \sa _mesa_validate_pbo_access
170 * \sa _mesa_map_pbo_source
171 * A call to this function should have a matching call to
172 * _mesa_unmap_pbo_source().
173 */
174 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)175 _mesa_map_validate_pbo_source(struct gl_context *ctx,
176 GLuint dimensions,
177 const struct gl_pixelstore_attrib *unpack,
178 GLsizei width, GLsizei height, GLsizei depth,
179 GLenum format, GLenum type,
180 GLsizei clientMemSize,
181 const GLvoid *ptr, const char *where)
182 {
183 ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
184
185 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
186 format, type, clientMemSize, ptr)) {
187 if (_mesa_is_bufferobj(unpack->BufferObj)) {
188 _mesa_error(ctx, GL_INVALID_OPERATION,
189 "%s(out of bounds PBO access)", where);
190 } else {
191 _mesa_error(ctx, GL_INVALID_OPERATION,
192 "%s(out of bounds access: bufSize (%d) is too small)",
193 where, clientMemSize);
194 }
195 return NULL;
196 }
197
198 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
199 /* non-PBO access: no further validation to be done */
200 return ptr;
201 }
202
203 if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
204 /* buffer is already mapped - that's an error */
205 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
206 return NULL;
207 }
208
209 ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
210 return ptr;
211 }
212
213
214 /**
215 * Counterpart to _mesa_map_pbo_source()
216 */
217 void
_mesa_unmap_pbo_source(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)218 _mesa_unmap_pbo_source(struct gl_context *ctx,
219 const struct gl_pixelstore_attrib *unpack)
220 {
221 ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
222 if (_mesa_is_bufferobj(unpack->BufferObj)) {
223 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj);
224 }
225 }
226
227
228 /**
229 * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
230 * if we're writing to a PBO, map it write-only and return the pointer
231 * into the PBO. If we're not writing to a PBO, return \p dst as-is.
232 * If non-null return, must call _mesa_unmap_pbo_dest() when done.
233 *
234 * \return NULL if error, else pointer to start of data
235 */
236 void *
_mesa_map_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack,GLvoid * dest)237 _mesa_map_pbo_dest(struct gl_context *ctx,
238 const struct gl_pixelstore_attrib *pack,
239 GLvoid *dest)
240 {
241 void *buf;
242
243 if (_mesa_is_bufferobj(pack->BufferObj)) {
244 /* pack into PBO */
245 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
246 pack->BufferObj->Size,
247 GL_MAP_WRITE_BIT,
248 pack->BufferObj);
249 if (!buf)
250 return NULL;
251
252 buf = ADD_POINTERS(buf, dest);
253 }
254 else {
255 /* pack to normal memory */
256 buf = dest;
257 }
258
259 return buf;
260 }
261
262
263 /**
264 * Combine PBO-write validation and mapping.
265 * If any GL errors are detected, they'll be recorded and NULL returned.
266 * \sa _mesa_validate_pbo_access
267 * \sa _mesa_map_pbo_dest
268 * A call to this function should have a matching call to
269 * _mesa_unmap_pbo_dest().
270 */
271 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)272 _mesa_map_validate_pbo_dest(struct gl_context *ctx,
273 GLuint dimensions,
274 const struct gl_pixelstore_attrib *unpack,
275 GLsizei width, GLsizei height, GLsizei depth,
276 GLenum format, GLenum type, GLsizei clientMemSize,
277 GLvoid *ptr, const char *where)
278 {
279 ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
280
281 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
282 format, type, clientMemSize, ptr)) {
283 if (_mesa_is_bufferobj(unpack->BufferObj)) {
284 _mesa_error(ctx, GL_INVALID_OPERATION,
285 "%s(out of bounds PBO access)", where);
286 } else {
287 _mesa_error(ctx, GL_INVALID_OPERATION,
288 "%s(out of bounds access: bufSize (%d) is too small)",
289 where, clientMemSize);
290 }
291 return NULL;
292 }
293
294 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
295 /* non-PBO access: no further validation to be done */
296 return ptr;
297 }
298
299 if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
300 /* buffer is already mapped - that's an error */
301 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
302 return NULL;
303 }
304
305 ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
306 return ptr;
307 }
308
309
310 /**
311 * Counterpart to _mesa_map_pbo_dest()
312 */
313 void
_mesa_unmap_pbo_dest(struct gl_context * ctx,const struct gl_pixelstore_attrib * pack)314 _mesa_unmap_pbo_dest(struct gl_context *ctx,
315 const struct gl_pixelstore_attrib *pack)
316 {
317 ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
318 if (_mesa_is_bufferobj(pack->BufferObj)) {
319 ctx->Driver.UnmapBuffer(ctx, pack->BufferObj);
320 }
321 }
322
323
324 /**
325 * Check if an unpack PBO is active prior to fetching a texture image.
326 * If so, do bounds checking and map the buffer into main memory.
327 * Any errors detected will be recorded.
328 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
329 */
330 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)331 _mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
332 GLsizei width, GLsizei height, GLsizei depth,
333 GLenum format, GLenum type, const GLvoid *pixels,
334 const struct gl_pixelstore_attrib *unpack,
335 const char *funcName)
336 {
337 GLubyte *buf;
338
339 if (!_mesa_is_bufferobj(unpack->BufferObj)) {
340 /* no PBO */
341 return pixels;
342 }
343 if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
344 format, type, INT_MAX, pixels)) {
345 _mesa_error(ctx, GL_INVALID_OPERATION, funcName, "(invalid PBO access)");
346 return NULL;
347 }
348
349 buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
350 unpack->BufferObj->Size,
351 GL_MAP_READ_BIT,
352 unpack->BufferObj);
353 if (!buf) {
354 _mesa_error(ctx, GL_INVALID_OPERATION, funcName, "(PBO is mapped)");
355 return NULL;
356 }
357
358 return ADD_POINTERS(buf, pixels);
359 }
360
361
362 /**
363 * Check if an unpack PBO is active prior to fetching a compressed texture
364 * image.
365 * If so, do bounds checking and map the buffer into main memory.
366 * Any errors detected will be recorded.
367 * The caller _must_ call _mesa_unmap_teximage_pbo() too!
368 */
369 const GLvoid *
_mesa_validate_pbo_compressed_teximage(struct gl_context * ctx,GLsizei imageSize,const GLvoid * pixels,const struct gl_pixelstore_attrib * packing,const char * funcName)370 _mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
371 GLsizei imageSize, const GLvoid *pixels,
372 const struct gl_pixelstore_attrib *packing,
373 const char *funcName)
374 {
375 GLubyte *buf;
376
377 if (!_mesa_is_bufferobj(packing->BufferObj)) {
378 /* not using a PBO - return pointer unchanged */
379 return pixels;
380 }
381 if ((const GLubyte *) pixels + imageSize >
382 ((const GLubyte *) 0) + packing->BufferObj->Size) {
383 /* out of bounds read! */
384 _mesa_error(ctx, GL_INVALID_OPERATION, funcName, "(invalid PBO access)");
385 return NULL;
386 }
387
388 buf = (GLubyte*) ctx->Driver.MapBufferRange(ctx, 0,
389 packing->BufferObj->Size,
390 GL_MAP_READ_BIT,
391 packing->BufferObj);
392 if (!buf) {
393 _mesa_error(ctx, GL_INVALID_OPERATION, funcName, "(PBO is mapped");
394 return NULL;
395 }
396
397 return ADD_POINTERS(buf, pixels);
398 }
399
400
401 /**
402 * This function must be called after either of the validate_pbo_*_teximage()
403 * functions. It unmaps the PBO buffer if it was mapped earlier.
404 */
405 void
_mesa_unmap_teximage_pbo(struct gl_context * ctx,const struct gl_pixelstore_attrib * unpack)406 _mesa_unmap_teximage_pbo(struct gl_context *ctx,
407 const struct gl_pixelstore_attrib *unpack)
408 {
409 if (_mesa_is_bufferobj(unpack->BufferObj)) {
410 ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj);
411 }
412 }
413