pbo.c revision 848b8605
1848b8605Smrg/*
2848b8605Smrg * Mesa 3-D graphics library
3848b8605Smrg *
4848b8605Smrg * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
5848b8605Smrg * Copyright (C) 2009-2011  VMware, Inc.  All Rights Reserved.
6848b8605Smrg *
7848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
8848b8605Smrg * copy of this software and associated documentation files (the "Software"),
9848b8605Smrg * to deal in the Software without restriction, including without limitation
10848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the
12848b8605Smrg * Software is furnished to do so, subject to the following conditions:
13848b8605Smrg *
14848b8605Smrg * The above copyright notice and this permission notice shall be included
15848b8605Smrg * in all copies or substantial portions of the Software.
16848b8605Smrg *
17848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23848b8605Smrg * OTHER DEALINGS IN THE SOFTWARE.
24848b8605Smrg */
25848b8605Smrg
26848b8605Smrg
27848b8605Smrg/**
28848b8605Smrg * \file pbo.c
29848b8605Smrg * \brief Functions related to Pixel Buffer Objects.
30848b8605Smrg */
31848b8605Smrg
32848b8605Smrg
33848b8605Smrg
34848b8605Smrg#include "glheader.h"
35848b8605Smrg#include "bufferobj.h"
36848b8605Smrg#include "glformats.h"
37848b8605Smrg#include "image.h"
38848b8605Smrg#include "imports.h"
39848b8605Smrg#include "mtypes.h"
40848b8605Smrg#include "pbo.h"
41848b8605Smrg
42848b8605Smrg
43848b8605Smrg
44848b8605Smrg/**
45848b8605Smrg * When we're about to read pixel data out of a PBO (via glDrawPixels,
46848b8605Smrg * glTexImage, etc) or write data into a PBO (via glReadPixels,
47848b8605Smrg * glGetTexImage, etc) we call this function to check that we're not
48848b8605Smrg * going to read/write out of bounds.
49848b8605Smrg *
50848b8605Smrg * XXX This would also be a convenient time to check that the PBO isn't
51848b8605Smrg * currently mapped.  Whoever calls this function should check for that.
52848b8605Smrg * Remember, we can't use a PBO when it's mapped!
53848b8605Smrg *
54848b8605Smrg * If we're not using a PBO, this is a no-op.
55848b8605Smrg *
56848b8605Smrg * \param width  width of image to read/write
57848b8605Smrg * \param height  height of image to read/write
58848b8605Smrg * \param depth  depth of image to read/write
59848b8605Smrg * \param format  format of image to read/write
60848b8605Smrg * \param type  datatype of image to read/write
61848b8605Smrg * \param clientMemSize  the maximum number of bytes to read/write
62848b8605Smrg * \param ptr  the user-provided pointer/offset
63848b8605Smrg * \return GL_TRUE if the buffer access is OK, GL_FALSE if the access would
64848b8605Smrg *         go out of bounds.
65848b8605Smrg */
66848b8605SmrgGLboolean
67848b8605Smrg_mesa_validate_pbo_access(GLuint dimensions,
68848b8605Smrg                          const struct gl_pixelstore_attrib *pack,
69848b8605Smrg                          GLsizei width, GLsizei height, GLsizei depth,
70848b8605Smrg                          GLenum format, GLenum type, GLsizei clientMemSize,
71848b8605Smrg                          const GLvoid *ptr)
72848b8605Smrg{
73848b8605Smrg   /* unsigned, to detect overflow/wrap-around */
74848b8605Smrg   uintptr_t start, end, offset, size;
75848b8605Smrg
76848b8605Smrg   /* If no PBO is bound, 'ptr' is a pointer to client memory containing
77848b8605Smrg      'clientMemSize' bytes.
78848b8605Smrg      If a PBO is bound, 'ptr' is an offset into the bound PBO.
79848b8605Smrg      In that case 'clientMemSize' is ignored: we just use the PBO's size.
80848b8605Smrg    */
81848b8605Smrg   if (!_mesa_is_bufferobj(pack->BufferObj)) {
82848b8605Smrg      offset = 0;
83848b8605Smrg      size = clientMemSize;
84848b8605Smrg   } else {
85848b8605Smrg      offset = (uintptr_t)ptr;
86848b8605Smrg      size = pack->BufferObj->Size;
87848b8605Smrg      /* The ARB_pixel_buffer_object spec says:
88848b8605Smrg       *    "INVALID_OPERATION is generated by ColorTable, ColorSubTable,
89848b8605Smrg       *    ConvolutionFilter2D, ConvolutionFilter1D, SeparableFilter2D,
90848b8605Smrg       *    TexImage1D, TexImage2D, TexImage3D, TexSubImage1D,
91848b8605Smrg       *    TexSubImage2D, TexSubImage3D, and DrawPixels if the current
92848b8605Smrg       *    PIXEL_UNPACK_BUFFER_BINDING_ARB value is non-zero and the data
93848b8605Smrg       *    parameter is not evenly divisible into the number of basic machine
94848b8605Smrg       *    units needed to store in memory a datum indicated by the type
95848b8605Smrg       *    parameter."
96848b8605Smrg       */
97848b8605Smrg      if (type != GL_BITMAP &&
98848b8605Smrg          (offset % _mesa_sizeof_packed_type(type)))
99848b8605Smrg         return GL_FALSE;
100848b8605Smrg   }
101848b8605Smrg
102848b8605Smrg   if (size == 0)
103848b8605Smrg      /* no buffer! */
104848b8605Smrg      return GL_FALSE;
105848b8605Smrg
106848b8605Smrg   /* get the offset to the first pixel we'll read/write */
107848b8605Smrg   start = _mesa_image_offset(dimensions, pack, width, height,
108848b8605Smrg                              format, type, 0, 0, 0);
109848b8605Smrg
110848b8605Smrg   /* get the offset to just past the last pixel we'll read/write */
111848b8605Smrg   end =  _mesa_image_offset(dimensions, pack, width, height,
112848b8605Smrg                             format, type, depth-1, height-1, width);
113848b8605Smrg
114848b8605Smrg   start += offset;
115848b8605Smrg   end += offset;
116848b8605Smrg
117848b8605Smrg   if (start > size) {
118848b8605Smrg      /* This will catch negative values / wrap-around */
119848b8605Smrg      return GL_FALSE;
120848b8605Smrg   }
121848b8605Smrg   if (end > size) {
122848b8605Smrg      /* Image read/write goes beyond end of buffer */
123848b8605Smrg      return GL_FALSE;
124848b8605Smrg   }
125848b8605Smrg
126848b8605Smrg   /* OK! */
127848b8605Smrg   return GL_TRUE;
128848b8605Smrg}
129848b8605Smrg
130848b8605Smrg
131848b8605Smrg/**
132848b8605Smrg * For commands that read from a PBO (glDrawPixels, glTexImage,
133848b8605Smrg * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
134848b8605Smrg * and return the pointer into the PBO.  If we're not reading from a
135848b8605Smrg * PBO, return \p src as-is.
136848b8605Smrg * If non-null return, must call _mesa_unmap_pbo_source() when done.
137848b8605Smrg *
138848b8605Smrg * \return NULL if error, else pointer to start of data
139848b8605Smrg */
140848b8605Smrgconst GLvoid *
141848b8605Smrg_mesa_map_pbo_source(struct gl_context *ctx,
142848b8605Smrg                     const struct gl_pixelstore_attrib *unpack,
143848b8605Smrg                     const GLvoid *src)
144848b8605Smrg{
145848b8605Smrg   const GLubyte *buf;
146848b8605Smrg
147848b8605Smrg   if (_mesa_is_bufferobj(unpack->BufferObj)) {
148848b8605Smrg      /* unpack from PBO */
149848b8605Smrg      buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
150848b8605Smrg						   unpack->BufferObj->Size,
151848b8605Smrg						   GL_MAP_READ_BIT,
152848b8605Smrg						   unpack->BufferObj,
153848b8605Smrg                                                   MAP_INTERNAL);
154848b8605Smrg      if (!buf)
155848b8605Smrg         return NULL;
156848b8605Smrg
157848b8605Smrg      buf = ADD_POINTERS(buf, src);
158848b8605Smrg   }
159848b8605Smrg   else {
160848b8605Smrg      /* unpack from normal memory */
161848b8605Smrg      buf = src;
162848b8605Smrg   }
163848b8605Smrg
164848b8605Smrg   return buf;
165848b8605Smrg}
166848b8605Smrg
167848b8605Smrg
168848b8605Smrg/**
169848b8605Smrg * Combine PBO-read validation and mapping.
170848b8605Smrg * If any GL errors are detected, they'll be recorded and NULL returned.
171848b8605Smrg * \sa _mesa_validate_pbo_access
172848b8605Smrg * \sa _mesa_map_pbo_source
173848b8605Smrg * A call to this function should have a matching call to
174848b8605Smrg * _mesa_unmap_pbo_source().
175848b8605Smrg */
176848b8605Smrgconst GLvoid *
177848b8605Smrg_mesa_map_validate_pbo_source(struct gl_context *ctx,
178848b8605Smrg                              GLuint dimensions,
179848b8605Smrg                              const struct gl_pixelstore_attrib *unpack,
180848b8605Smrg                              GLsizei width, GLsizei height, GLsizei depth,
181848b8605Smrg                              GLenum format, GLenum type,
182848b8605Smrg                              GLsizei clientMemSize,
183848b8605Smrg                              const GLvoid *ptr, const char *where)
184848b8605Smrg{
185848b8605Smrg   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
186848b8605Smrg
187848b8605Smrg   if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
188848b8605Smrg                                  format, type, clientMemSize, ptr)) {
189848b8605Smrg      if (_mesa_is_bufferobj(unpack->BufferObj)) {
190848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
191848b8605Smrg                     "%s(out of bounds PBO access)", where);
192848b8605Smrg      } else {
193848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
194848b8605Smrg                     "%s(out of bounds access: bufSize (%d) is too small)",
195848b8605Smrg                     where, clientMemSize);
196848b8605Smrg      }
197848b8605Smrg      return NULL;
198848b8605Smrg   }
199848b8605Smrg
200848b8605Smrg   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
201848b8605Smrg      /* non-PBO access: no further validation to be done */
202848b8605Smrg      return ptr;
203848b8605Smrg   }
204848b8605Smrg
205848b8605Smrg   if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
206848b8605Smrg      /* buffer is already mapped - that's an error */
207848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
208848b8605Smrg      return NULL;
209848b8605Smrg   }
210848b8605Smrg
211848b8605Smrg   ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
212848b8605Smrg   return ptr;
213848b8605Smrg}
214848b8605Smrg
215848b8605Smrg
216848b8605Smrg/**
217848b8605Smrg * Counterpart to _mesa_map_pbo_source()
218848b8605Smrg */
219848b8605Smrgvoid
220848b8605Smrg_mesa_unmap_pbo_source(struct gl_context *ctx,
221848b8605Smrg                       const struct gl_pixelstore_attrib *unpack)
222848b8605Smrg{
223848b8605Smrg   ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
224848b8605Smrg   if (_mesa_is_bufferobj(unpack->BufferObj)) {
225848b8605Smrg      ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
226848b8605Smrg   }
227848b8605Smrg}
228848b8605Smrg
229848b8605Smrg
230848b8605Smrg/**
231848b8605Smrg * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
232848b8605Smrg * if we're writing to a PBO, map it write-only and return the pointer
233848b8605Smrg * into the PBO.  If we're not writing to a PBO, return \p dst as-is.
234848b8605Smrg * If non-null return, must call _mesa_unmap_pbo_dest() when done.
235848b8605Smrg *
236848b8605Smrg * \return NULL if error, else pointer to start of data
237848b8605Smrg */
238848b8605Smrgvoid *
239848b8605Smrg_mesa_map_pbo_dest(struct gl_context *ctx,
240848b8605Smrg                   const struct gl_pixelstore_attrib *pack,
241848b8605Smrg                   GLvoid *dest)
242848b8605Smrg{
243848b8605Smrg   void *buf;
244848b8605Smrg
245848b8605Smrg   if (_mesa_is_bufferobj(pack->BufferObj)) {
246848b8605Smrg      /* pack into PBO */
247848b8605Smrg      buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
248848b8605Smrg						   pack->BufferObj->Size,
249848b8605Smrg						   GL_MAP_WRITE_BIT,
250848b8605Smrg						   pack->BufferObj,
251848b8605Smrg                                                   MAP_INTERNAL);
252848b8605Smrg      if (!buf)
253848b8605Smrg         return NULL;
254848b8605Smrg
255848b8605Smrg      buf = ADD_POINTERS(buf, dest);
256848b8605Smrg   }
257848b8605Smrg   else {
258848b8605Smrg      /* pack to normal memory */
259848b8605Smrg      buf = dest;
260848b8605Smrg   }
261848b8605Smrg
262848b8605Smrg   return buf;
263848b8605Smrg}
264848b8605Smrg
265848b8605Smrg
266848b8605Smrg/**
267848b8605Smrg * Combine PBO-write validation and mapping.
268848b8605Smrg * If any GL errors are detected, they'll be recorded and NULL returned.
269848b8605Smrg * \sa _mesa_validate_pbo_access
270848b8605Smrg * \sa _mesa_map_pbo_dest
271848b8605Smrg * A call to this function should have a matching call to
272848b8605Smrg * _mesa_unmap_pbo_dest().
273848b8605Smrg */
274848b8605SmrgGLvoid *
275848b8605Smrg_mesa_map_validate_pbo_dest(struct gl_context *ctx,
276848b8605Smrg                            GLuint dimensions,
277848b8605Smrg                            const struct gl_pixelstore_attrib *unpack,
278848b8605Smrg                            GLsizei width, GLsizei height, GLsizei depth,
279848b8605Smrg                            GLenum format, GLenum type, GLsizei clientMemSize,
280848b8605Smrg                            GLvoid *ptr, const char *where)
281848b8605Smrg{
282848b8605Smrg   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
283848b8605Smrg
284848b8605Smrg   if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
285848b8605Smrg                                  format, type, clientMemSize, ptr)) {
286848b8605Smrg      if (_mesa_is_bufferobj(unpack->BufferObj)) {
287848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
288848b8605Smrg                     "%s(out of bounds PBO access)", where);
289848b8605Smrg      } else {
290848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
291848b8605Smrg                     "%s(out of bounds access: bufSize (%d) is too small)",
292848b8605Smrg                     where, clientMemSize);
293848b8605Smrg      }
294848b8605Smrg      return NULL;
295848b8605Smrg   }
296848b8605Smrg
297848b8605Smrg   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
298848b8605Smrg      /* non-PBO access: no further validation to be done */
299848b8605Smrg      return ptr;
300848b8605Smrg   }
301848b8605Smrg
302848b8605Smrg   if (_mesa_check_disallowed_mapping(unpack->BufferObj)) {
303848b8605Smrg      /* buffer is already mapped - that's an error */
304848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
305848b8605Smrg      return NULL;
306848b8605Smrg   }
307848b8605Smrg
308848b8605Smrg   ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
309848b8605Smrg   return ptr;
310848b8605Smrg}
311848b8605Smrg
312848b8605Smrg
313848b8605Smrg/**
314848b8605Smrg * Counterpart to _mesa_map_pbo_dest()
315848b8605Smrg */
316848b8605Smrgvoid
317848b8605Smrg_mesa_unmap_pbo_dest(struct gl_context *ctx,
318848b8605Smrg                     const struct gl_pixelstore_attrib *pack)
319848b8605Smrg{
320848b8605Smrg   ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
321848b8605Smrg   if (_mesa_is_bufferobj(pack->BufferObj)) {
322848b8605Smrg      ctx->Driver.UnmapBuffer(ctx, pack->BufferObj, MAP_INTERNAL);
323848b8605Smrg   }
324848b8605Smrg}
325848b8605Smrg
326848b8605Smrg
327848b8605Smrg/**
328848b8605Smrg * Check if an unpack PBO is active prior to fetching a texture image.
329848b8605Smrg * If so, do bounds checking and map the buffer into main memory.
330848b8605Smrg * Any errors detected will be recorded.
331848b8605Smrg * The caller _must_ call _mesa_unmap_teximage_pbo() too!
332848b8605Smrg */
333848b8605Smrgconst GLvoid *
334848b8605Smrg_mesa_validate_pbo_teximage(struct gl_context *ctx, GLuint dimensions,
335848b8605Smrg			    GLsizei width, GLsizei height, GLsizei depth,
336848b8605Smrg			    GLenum format, GLenum type, const GLvoid *pixels,
337848b8605Smrg			    const struct gl_pixelstore_attrib *unpack,
338848b8605Smrg			    const char *funcName)
339848b8605Smrg{
340848b8605Smrg   GLubyte *buf;
341848b8605Smrg
342848b8605Smrg   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
343848b8605Smrg      /* no PBO */
344848b8605Smrg      return pixels;
345848b8605Smrg   }
346848b8605Smrg   if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth,
347848b8605Smrg                                  format, type, INT_MAX, pixels)) {
348848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
349848b8605Smrg                  funcName, dimensions);
350848b8605Smrg      return NULL;
351848b8605Smrg   }
352848b8605Smrg
353848b8605Smrg   buf = (GLubyte *) ctx->Driver.MapBufferRange(ctx, 0,
354848b8605Smrg                                                unpack->BufferObj->Size,
355848b8605Smrg						GL_MAP_READ_BIT,
356848b8605Smrg						unpack->BufferObj,
357848b8605Smrg                                                MAP_INTERNAL);
358848b8605Smrg   if (!buf) {
359848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
360848b8605Smrg                  dimensions);
361848b8605Smrg      return NULL;
362848b8605Smrg   }
363848b8605Smrg
364848b8605Smrg   return ADD_POINTERS(buf, pixels);
365848b8605Smrg}
366848b8605Smrg
367848b8605Smrg
368848b8605Smrg/**
369848b8605Smrg * Check if an unpack PBO is active prior to fetching a compressed texture
370848b8605Smrg * image.
371848b8605Smrg * If so, do bounds checking and map the buffer into main memory.
372848b8605Smrg * Any errors detected will be recorded.
373848b8605Smrg * The caller _must_ call _mesa_unmap_teximage_pbo() too!
374848b8605Smrg */
375848b8605Smrgconst GLvoid *
376848b8605Smrg_mesa_validate_pbo_compressed_teximage(struct gl_context *ctx,
377848b8605Smrg                                 GLuint dimensions, GLsizei imageSize,
378848b8605Smrg                                 const GLvoid *pixels,
379848b8605Smrg                                 const struct gl_pixelstore_attrib *packing,
380848b8605Smrg                                 const char *funcName)
381848b8605Smrg{
382848b8605Smrg   GLubyte *buf;
383848b8605Smrg
384848b8605Smrg   if (!_mesa_is_bufferobj(packing->BufferObj)) {
385848b8605Smrg      /* not using a PBO - return pointer unchanged */
386848b8605Smrg      return pixels;
387848b8605Smrg   }
388848b8605Smrg   if ((const GLubyte *) pixels + imageSize >
389848b8605Smrg       ((const GLubyte *) 0) + packing->BufferObj->Size) {
390848b8605Smrg      /* out of bounds read! */
391848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(invalid PBO access)",
392848b8605Smrg                  funcName, dimensions);
393848b8605Smrg      return NULL;
394848b8605Smrg   }
395848b8605Smrg
396848b8605Smrg   buf = (GLubyte*) ctx->Driver.MapBufferRange(ctx, 0,
397848b8605Smrg					       packing->BufferObj->Size,
398848b8605Smrg					       GL_MAP_READ_BIT,
399848b8605Smrg					       packing->BufferObj,
400848b8605Smrg                                               MAP_INTERNAL);
401848b8605Smrg   if (!buf) {
402848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s%uD(PBO is mapped)", funcName,
403848b8605Smrg                  dimensions);
404848b8605Smrg      return NULL;
405848b8605Smrg   }
406848b8605Smrg
407848b8605Smrg   return ADD_POINTERS(buf, pixels);
408848b8605Smrg}
409848b8605Smrg
410848b8605Smrg
411848b8605Smrg/**
412848b8605Smrg * This function must be called after either of the validate_pbo_*_teximage()
413848b8605Smrg * functions.  It unmaps the PBO buffer if it was mapped earlier.
414848b8605Smrg */
415848b8605Smrgvoid
416848b8605Smrg_mesa_unmap_teximage_pbo(struct gl_context *ctx,
417848b8605Smrg                         const struct gl_pixelstore_attrib *unpack)
418848b8605Smrg{
419848b8605Smrg   if (_mesa_is_bufferobj(unpack->BufferObj)) {
420848b8605Smrg      ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
421848b8605Smrg   }
422848b8605Smrg}
423