Name EXT_pixel_buffer_object Name Strings GL_EXT_pixel_buffer_object Status Implemented by NVIDIA drivers (Release 55). Contributors Ralf Biermann Derek Cornish Matt Craighead Bill Licea-Kane Brian Paul Contact Ralf Biermann, NVIDIA Corporation (rbiermann 'at' nvidia.com) Derek Cornish, NVIDIA Corporation (dcornish 'at' nvidia.com) IP Status Unknown. Version NVIDIA Date: March 29, 2004 (version 1.0) Number 302 Status NVIDIA Release 55 (early 2004) drivers support this extension. Dependencies Written based on the wording of the OpenGL 1.5 specification. GL_NV_pixel_data_range affects the definition of this extension. Overview This extension expands on the interface provided by buffer objects. It is intended to permit buffer objects to be used not only with vertex array data, but also with pixel data. Buffer objects were promoted from the ARB_vertex_buffer_object extension in OpenGL 1.5. Recall that buffer objects conceptually are nothing more than arrays of bytes, just like any chunk of memory. Buffer objects allow GL commands to source data from a buffer object by binding the buffer object to a given target and then overloading a certain set of GL commands' pointer arguments to refer to offsets inside the buffer, rather than pointers to user memory. An offset is encoded in a pointer by adding the offset to a null pointer. This extension does not add any new functionality to buffer objects themselves. It simply adds two new targets to which buffer objects can be bound: PIXEL_PACK_BUFFER and PIXEL_UNPACK_BUFFER. When a buffer object is bound to the PIXEL_PACK_BUFFER target, commands such as ReadPixels write their data into a buffer object. When a buffer object is bound to the PIXEL_UNPACK_BUFFER target, commands such as DrawPixels read their data from a buffer object. There are a wide variety of applications for such functionality. Some of the most interesting ones are: - "Render to vertex array." The application can use a fragment program to render some image into one of its buffers, then read this image out into a buffer object via ReadPixels. Then, it can use this buffer object as a source of vertex data. - Streaming textures. If the application uses MapBuffer/UnmapBuffer to write its data for TexSubImage into a buffer object, at least one of the data copies usually required to download a texture can be eliminated, significantly increasing texture download performance. - Asynchronous ReadPixels. If an application needs to read back a number of images and process them with the CPU, the existing GL interface makes it nearly impossible to pipeline this operation. The driver will typically send the hardware a readback command when ReadPixels is called, and then wait for all of the data to be available before returning control to the application. Then, the application can either process the data immediately or call ReadPixels again; in neither case will the readback overlap with the processing. If the application issues several readbacks into several buffer objects, however, and then maps each one to process its data, then the readbacks can proceed in parallel with the data processing. Issues How does this extension relate to ARB_vertex_buffer_object? It builds on the ARB_vertex_buffer_object framework by adding two new targets that buffers can be bound to. How does this extension relate to NV_pixel_data_range? This extension relates to NV_pixel_data_range in the same way that ARB_vertex_buffer_object relates to NV_vertex_array_range. To paraphrase the ARB_vertex_buffer_object spec, here are the main differences: - Applications are no longer responsible for memory management and synchronization. - Applications may still access high-performance memory directly, but this is optional, and such access is more restricted. - Buffer changes (BindBuffer) are generally expected to be very lightweight, rather than extremely heavyweight (PixelDataRangeNV). - A platform-specific allocator such as wgl/glXAllocateMemoryNV is no longer required. Can a given buffer be used for both vertex and pixel data? RESOLVED: YES. All buffers can be used with all buffer bindings, in whatever combinations the application finds useful. Consider yourself warned, however, by the following issue. May implementations make use of the target as a hint to select an appropriate memory space for the buffer? RESOLVED: YES, as long as such behavior is transparent to the application. Some implementations may choose, for example, that they would rather stream vertex data from write-combined system memory, element (or index) data from video memory, and pixel data from video memory. In fact, one can imagine arbitrarily complicated heuristics for selecting the memory space, based on factors such as the target, the "usage" argument, and the application's observed behavior. While it is entirely legal to create a buffer object by binding it to ARRAY_BUFFER and loading it with data, then using it with the PIXEL_UNPACK_BUFFER_EXT or PIXEL_PACK_BUFFER_EXT binding, such behavior is liable to confuse the driver and may hurt performance. If the driver implemented the hypothetical heuristic described earlier, such a buffer might have already been located in write-combined system memory, and so the driver would have to choose between two bad options: relocate the buffer into video memory, or accept lower performance caused by streaming pixel data from slower system memory. Should all pixel path commands be supported, or just a subset of them? RESOLVED: ALL. While there is little reason to believe that, say, ConvolutionFilter2D would benefit from this extension, there is no reason _not_ to support it. The full list of commands affected by this extension is listed in the spec. Should PixelMap and GetPixelMap be supported? RESOLVED: YES. They're not really pixel path operations, but, again, there is no good reason to omit operations, and they _are_ operations that pass around big chunks of pixel-related data. If we support PolygonStipple, surely we should support this. How does the buffer binding state push/pop? RESOLVED: As part of the pixel store client state. This is analogous to how the vertex buffer object bindings pushed/popped as part of the vertex array client state. Should NV_pixel_data_range (PDR) be used concurrently with pixel buffer objects ? RESOLVED: NO. While it would be possible to allocate a memory range for PDR, using a pointer into this memory range with one of the commands affected by EXT_pixel_buffer_object will not work if a pixel buffer object other than zero is bound to the buffer binding point affecting the command. Pixel buffer objects always have higher precedence than PDR. Do the null pointer rules for glTexImage1D, glTexImage2D and glTexImage3D for allocating textures with undefined content also apply when a non-zero buffer object is bound to PIXEL_UNPACK_BUFFER_BINDING_EXT ? RESOLVED: NO. The null pointer is interpreted as a non-zero pointer to the data storage whose contents may be still undefined. This data will be used to create the texture array. If the null pointer rule is required, no non-zero buffer object should be bound to PIXEL_UNPACK_BUFFER_BINDING_EXT. New Procedures and Functions None. New Tokens Accepted by the parameters of BindBuffer, BufferData, BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData, GetBufferParameteriv, and GetBufferPointerv: PIXEL_PACK_BUFFER_EXT 0x88EB PIXEL_UNPACK_BUFFER_EXT 0x88EC Accepted by the parameter of GetBooleanv, GetIntegerv, GetFloatv, and GetDoublev: PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF Additions to Chapter 2 of the GL Specification (OpenGL Operation) None Additions to Chapter 3 of the 1.2.1 Specification (Rasterization) Additions to subsection 3.8.1 of the 1.2.1 Specification (Texture Image Specification) The extension EXT_pixel_buffer_object makes an exception to this rule of passing a null pointer to glTexImage1D, glTexImage2D and glTexImage3D. If PIXEL_UNPACK_BUFFER_BINDING_EXT is non-zero and a null pointer is passed to these functions, the texture array is created and the image contents are sourced from the data store of the bound buffer object. Additions to Chapter 4 of the 1.2.1 Specification (Per-Fragment Operations and the Frame Buffer) Added a subsection 4.3.5 (Pixel Buffer Object unpack operation) in section 4.3 (Drawing, Reading and copying Pixels) The extension EXT_pixel_buffer_object affects the operation of several OpenGL commands described in section 3.6 (Pixel Rectangles), section 3.7 (Bitmaps), and section 3.8 (Texturing). In unextended OpenGL 1.3 with ARB_imaging support, the commands glBitmap, glColorSubTable, glColorTable, glCompressedTexImage1D, glCompressedTexImage2D, glCompressedTexImage3D, glCompressedTexSubImage1D, glCompressedTexSubImage2D, glCompressedTexSubImage3D, glConvolutionFilter1D, glConvolutionFilter2D, glDrawPixels, glPixelMapfv, glPixelMapuiv, glPixelMapusv, glPolygonStipple, glSeparableFilter2D, glTexImage1D, glTexImage2D, glTexImage3D, glTexSubImage1D, glTexSubImage2D and glTexSubImage3D operate as previously defined, except that pixel data is sourced from a buffer object's data store if PIXEL_UNPACK_BUFFER_BINDING_EXT is non-zero. When the data is sourced from a buffer object, the pointer value passed in as an argument to the command is used to compute an offset, in basic machine units, into the data store of the buffer object. This offset is computed by subtracting a null pointer from the pointer value, where both pointers are treated as pointers to basic machine units. Additions to Chapter 5 of the 1.2.1 Specification (Special Functions) None Additions to Chapter 6 of the 1.2.1 Specification (State and State Requests) Additions to subsection 6.1.13 (Buffer Object Queries) in chapter 6 In unextended OpenGL 1.5 with ARB_imaging support, the commands glGetColorTable, glGetCompressedTexImage, glGetConvolutionFilter, glGetHistogram, glGetMinmax, glGetPixelMapfv, glGetPixelMapuiv, glGetPixelMapusv, glGetPolygonStipple, glGetSeparableFilter, glGetTexImage and glReadPixels operate as previously defined, except that pixel data is stored in a buffer object's data store if PIXEL_PACK_BUFFER_BINDING_EXT is non-zero. When a buffer object is the target of the pixel data, the target pointer value passed in as an argument to the command is used to compute an offset, in basic machine units, into the data store of the buffer object. This offset is computed by subtracting a null pointer from the pointer value, where both pointers are treated as pointers to basic machine units. Errors None New State (table 6.20, Pixels, p. 235) Get Value Type Get Command Initial Value Sec Attribute --------- ---- ----------- ------------- --- --------- PIXEL_PACK_BUFFER_BINDING_EXT Z+ GetIntegerv 0 4.3.5 pixel-store PIXEL_UNPACK_BUFFER_BINDING_EXT Z+ GetIntegerv 0 6.1.13 pixel-store New Implementation Dependent State (none) Usage Examples Convenient macro definition for specifying buffer offsets: #define BUFFER_OFFSET(i) ((char *)NULL + (i)) Example 1: Render to vertex array // create a buffer object for a number of vertices consisting of // 4 float values per vertex GenBuffers(1, vertexBuffer); BindBuffer(PIXEL_PACK_BUFFER_EXT, vertexBuffer); BufferData(PIXEL_PACK_BUFFER_EXT, numberVertices*4, NULL, DYNAMIC_DRAW); // render vertex data into framebuffer using a fragment program BindProgramARB(FRAGMENT_PROGRAM_ARB, fragmentProgram); DrawBuffer(GL_BACK); renderVertexData(); BindProgramARB(FRAGMENT_PROGRAM_ARB, 0); // read the vertex data back from framebuffer ReadBuffer(GL_BACK); ReadPixels(0, 0, numberVertices*4, height/2, GL_BGRA, GL_FLOAT, BUFFER_OFFSET(0)); // change the binding point of the buffer object to // the vertex array binding point BindBuffer(GL_ARRAY_BUFFER, vertexBuffer); EnableClientState(VERTEX_ARRAY); VertexPointer(4, FLOAT, 0, BUFFER_OFFSET(0)); DrawArrays(TRIANGLE_STRIP, 0, numberVertices); Example 2: Streaming textures streaming textures using NV_pixel_data_range void *pdrMemory, *texData; pdrMemory = AllocateMemoryNV(texsize, 0.0, 1.0, 1.0); PixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV, texsize, pdrMemory); EnableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV); // setup texture environment ... texData = getNextImage(); while (texData) { memcpy(pdrMemory, texData, texsize); FlushPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV); TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texWidth, texHeight, GL_BGRA, GL_UNSIGNED_BYTE, pdrMemory); // draw textured geometry Begin(GL_QUADS); ... End(); texData = getNextImage(); } DisableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV); FreeMemoryNV(pdrMemory); streaming textures using EXT_pixel_buffer_object: void *pboMemory, *texData; // create and bind texture image buffer object GenBuffers(1, &texBuffer); BindBuffer(PIXEL_UNPACK_BUFFER_EXT, texBuffer); BufferData(PIXEL_UNPACK_BUFFER_EXT, texSize, NULL, STREAM_DRAW); texData = getNextImage(); while (texData) { // map the texture image buffer pboMemory = MapBuffer(PIXEL_UNPACK_BUFFER_EXT, WRITE_ONLY); // modify (sub-)buffer data memcpy(pboMemory, texData, texsize); // unmap the texture image buffer if (!UnmapBuffer(PIXEL_UNPACK_BUFFER_EXT)) { // Handle error case } // update (sub-)teximage from texture image buffer TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texWidth, texHeight, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); // draw textured geometry Begin(GL_QUADS); ... End(); texData = getNextImage(); } BindBuffer(PIXEL_UNPACK_BUFFER_EXT, 0); Example 3: Asynchronous ReadPixels traditional ReadPixels unsigned int readBuffer[imagewidth*imageheight*4]; // render to framebuffer DrawBuffer(GL_BACK); renderScene() // read image from framebuffer ReadBuffer(GL_BACK); ReadPixels(); // process image when ReadPixels returns after reading the whole buffer processImage(readBuffer); asynchronous ReadPixels GenBuffers(2, imageBuffers); BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[0]); BufferData(PIXEL_PACK_BUFFER_EXT, imageSize / 2, NULL, STATIC_READ); BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[1]); BufferData(PIXEL_PACK_BUFFER_EXT, imageSize / 2, NULL, STATIC_READ); // render to framebuffer DrawBuffer(GL_BACK); renderScene(); // Bind two different buffer objects and start the ReadPixels // asynchronously. Each call will return directly after starting the // DMA transfer. BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[0]); ReadPixels(0, 0, width, height/2, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[1]); ReadPixels(0, height/2, width, height/2, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); // process partial images pboMemory1 = MapBuffer(PIXEL_PACK_BUFFER_EXT, READ_ONLY); processImage(pboMemory1); pboMemory2 = MapBuffer(PIXEL_PACK_BUFFER_EXT, READ_ONLY); processImage(pboMemory2); // unmap the image buffers BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[0]); if (!UnmapBuffer(PIXEL_PACK_BUFFER_EXT)) { // Handle error case } BindBuffer(PIXEL_PACK_BUFFER_EXT, imageBuffers[1]); if (!UnmapBuffer(PIXEL_PACK_BUFFER_EXT)) { // Handle error case }