24.1. Device, context and memory abstraction

The buffer object classes provide a powerful and convenient abstraction for managing data objects stored in various kinds of memory on various computing (or rendering) devices. The main behavior of the buffer objects is to abstract access to the memory using a generic API. This API manages the calls to the hardware driver and provides useful methods to manage the allocated memory. All the buffer object classes must provide methods to copy data to a CPU buffer and from a CPU buffer. This allows basic data interchange with custom buffer object types. If a custom SoBufferObject( C++ | Java | .NET ) is defined it should always be possible to read and write data in this type of buffer.

Open Inventor 8.0 provides three kinds of Buffer Objects:

  • SoCpuBufferObject( C++ | Java | .NET ): This type of buffer object manages memory allocated in system memory.

  • SoGLBufferObject( C++ | Java | .NET ): This type of buffer object manages memory through the OpenGL API. The memory may be physically located in video card memory or in system memory, depending on the video card driver. OpenGL buffers can be used for textures, geometry (see the section about SoBufferedShape( C++ | Java | .NET )), etc.

The OpenGL buffer objects are new OpenGL extensions mainly used for high performance and easier modification of data located in the GPU’s “texture” memory. They are nothing but arrays of bytes located in the graphic card main memory. Each SoGLBufferObject( C++ | Java | .NET ) can be one of the following OpenGL buffer object types:

  • VBO: Vertex Buffer Object: This buffer can contain vertices, normals and texture coordinates.

  • PBO: Pixel Buffer Object: This buffer improves performance for texture data upload or download between the CPU and the GPU. It provides useful mechanisms to speed up the download and the upload by removing one data copy during the TexImage and the ReadPixels methods calls. It can also be used to do asynchronous ReadPixels to avoid the lock usually introduced by the call to ReadPixels.

  • TBO: Texture Buffer Object: This buffer provides data to shader programs. It can be regular texture data or any vertex coordinates, normal coordinates, vertex attributes, etc.

The main difference with the base SoBufferObject( C++ | Java | .NET ) class is the addition of the methods bind() and unbind() which are useful for some OpenGL functions which require the binding of those buffers in the OpenGL state.

OpenGL buffers allow the transfer of data, during rendering, between each step of the rendering pipeline without transferring the data back to system memory.

The SoBufferObject( C++ | Java | .NET ) classes provide methods for data input and output. All the buffer classes must provide a method to copy data from or to an SoCpuBufferObject( C++ | Java | .NET ). Those methods are named memcpy() and copyToCpuBuffer().

This mechanism provides a reliable way to copy data to any type of buffer, because memcpy and copyToCpuBuffer are mandatory.

Follow these general steps to put data in a buffer of type X:

  • Create a SoCpuBufferObject( C++ | Java | .NET ) (assume it is called cpuBuffer)

  • Set the size of the buffer: cpuBuffer.setSize( sizeInBytes )

  • Map the buffer to a valid pointer using the method cpuBuffer.map(). It returns a void* pointer usable like any C pointer.

  • Unmap the buffer: cpuBuffer.unmap()

  • Bind the destination buffer context: destBuffer->getContext()->bind();

  • Do the actual copy of the data: destBuffer->memcpy( &cpuBuffer );

  • Unbind the context: destBuffer->getContext()->unbind();

Some standard Open Inventor classes have been enhanced to allow direct use of buffer objects to improve performance and/or integrate with computation:

  • Images interface

    The field classes SoSFImage and SoSFImage3 have been modified to support SoBufferObject( C++ | Java | .NET ). They use SoBufferObject internally and use the mapping mechanism for the pointer based methods. It’s also possible to read and write the content of those fields through the read and write actions.

  • Textures interface

    The nodes SoTexture2 and SoTexture3 support buffer objects directly in their internal mechanism in order to provide them directly to OpenGL. For this feature the texture element was also modified to support SoBufferObject objects. The support for buffer objects in these nodes is through the modification of SoSFImage and SoSFImage3.

  • Shapes interface

    The new node SoBufferedShape( C++ | Java | .NET ) supports buffer objects directly for providing vertices, normals, colors, etc. Accordingly the shape data may be stored either on the CPU or GPU. The input buffer objects are mapped to the right kind of buffers directly in the method which uploads the texture to OpenGL.

Each device type supported by Open Inventor provides a class that allows you to get information for each such device present in the system. All these classes are derived from the class SoDevice( C++ | Java | .NET ).

The supported devices are CPU, OpenGL, CUDA and OpenCL. Each of them inherits from the SoDevice( C++ | Java | .NET ) class and implement the following common methods:

getTotalMemory()
getAvailableMemory()
getLogicalUnits()
getDriverVersion()
getDeviceName()
getDevicesCount()
getDevice()

Some device type dependent information can also be retrieved, like SSE support level for a CPU and number of SPU for a CUDA or OpenCL device. See the reference manual for a complete list.


A complete example illustrating this functionality is provided in

$OIHOME/src/Inventor/examples/Features/DevicesProperties/main.cxx

Limitations:

  • SoCpuDevice( C++ | Java | .NET ) is not able to retrieve instruction support set (MMX, SSE, ...) from the CPU on Sun OS Solaris.

  • SoGLDevice( C++ | Java | .NET ) is currently not able to return correct values for getAvailableMemory(), so this method returns the same value as getTotalMemory().

  • SoGLDevice( C++ | Java | .NET ) will return predefined values on UNIX platforms using ATI/AMD GPU and on SOLARIS platforms.

Currently CPU devices don’t require a context. The whole memory is accessible from the program in all the threads. The SoCpuContext( C++ | Java | .NET ) class is provided in order to keep a generic API. During the first allocation of an SoCpuBufferObject( C++ | Java | .NET ) a CPU device context is created for convenience.

The OpenGL contexts store all the information about the OpenGL state, buffer objects, textures, etc. In most cases they can be shared in order to use one single copy of the data in two threads. The class SoGLContext( C++ | Java | .NET ) provides methods to create, manage and destroy OpenGL contexts.

Starting with Open Inventor 8.1 the SoGLContext( C++ | Java | .NET ) class is used to manage all the OpenGL objects. Any OpenGL object created in Open Inventor is linked to an SoGLContext( C++ | Java | .NET ). This means that if an SoGLContext( C++ | Java | .NET ) is released, all the OpenGL objects associated with this context will be released. It also means that the viewer classes use SoGLContext( C++ | Java | .NET ) to setup rendering and to perform the actual rendering. The viewer classes now provide methods to bind and unbind the SoGLContext( C++ | Java | .NET ) associated with the viewer. In order to call OpenGL functions the viewer must bind and unbind the viewer context. Basically it means that there is no active context outside the rendering traversal and if the application needs a context it must explicitly bind it.

SoGLContext( C++ | Java | .NET ) objects are created according to specific formats that describe the configuration of the OpenGL rendering format like size of the depth buffer, color format, anti-alisaing configuration, etc.

The new SoGLFormat( C++ | Java | .NET ) class manages this configuration. It is possible to change the current configuration of a viewer by setting a new SoGLFormat( C++ | Java | .NET ).