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