OpenGL Vizserver Reference Page


NAME
vsCompressor - OpenGL Vizserver compressor interface

INHERITS FROM
vsLoadable

HEADER FILE
#include <vizserver/vsCompressor.h>

PUBLIC METHOD SUMMARY

   Creation and destruction
virtual ~vsCompressor (  );

   Querying
virtual const Info* getInfo (  ) const = 0;
virtual size_t getMaxCompressedSize ( const vsCmprParamArg&) = 0;

   Compression and decompression
virtual size_t compress ( const vsCmprInputQueryArg& input, vsCmprOutputQueryArg& output) = 0;
virtual void expand ( const vsCmprInputQueryArg& input, vsCmprOutputQueryArg& output) = 0;
virtual void receiveMessage ( const vsCmprParamArg& msg) = 0;

PROTECTED METHOD SUMMARY

   Creation and destruction
vsCompressor (  );

CLASS DESCRIPTION
The vsCompressor object class defines the interface for loadable compression objects. It is based upon the vsLoadable object class which supplies the generic, loadable object interface. There are three aspects of the vsCompressor interface: functions to query compressor module for information, another set for actually compressing or expanding imagery via the underlying compressor object, and a final set for API based messaging.

Compressor object definitions must be capable of compressing or expanding multiple fields (see vsFieldInfo) simultaneously (in separate threads). Any required interlocking is the responsibility of the implementation and should use the features afforded by the vsLock and vsSemaphore object classes.

Field sources are identified by their destination drawable identifier, specific frame identifier and associated field type (and optional composition id). These three keys uniquely identify a stream of field updates to the compressor. It is the responsibility of the compressor to handle the stream of data as well as the compression and decompression of the pixel bytes.

Compressors can choose to support any number of pixels formats or component types (see vsFieldInfo) but must support at least vsFieldPixelFormats::RGB RGB pixel format and vsFieldPixelTypes::UChar unsigned char component type. Since some hardware deals better with RGBA pixels (e.g., O2), compressors should make every effort to support that format as well.

The set of input and output pixel formats/component types is identified by an vsCompressor::Info struct. This object contains a bit mask of the set of formats and types supported by the compressor for both input (compression) and output (expansion). This structure is useful for VizServer determining whether a given compressor can support a graphic context's pixel data, or if it should be converted to one of the compressor's supported formats.

Compressors can also indicate that they support packing options like top-down orientation and pixel padding on every scan line (see vsFieldPacking), by OR'ing the vsCompressorTypes::FlipImage and vsCompressorTypes::ScanLinePadding in the compressor info structure.

   Query interface
The compressor query interface exists to provide potential optimization paths to the caller. There are three interfaces that fit into this class. These are: getInfo(), and getMaxCompressedSize().

The first of the three, getInfo(), provides the application framework the knowledge of compressor specifics. The format of the Info structure is as follows:
struct Info {
    char * description, Holds a string for the compressors decription.
    vsBitfield compressorTypes, a bit string of values for various
                                capabilities of the compressor.
    vsBitfield inputFormats, a bit string of concatenated input field
                             formats accepted by the compressor.
    vsBitfield inputTypes, a bit string of concatenated input field 
                           types accepted by the compressor.
    vsBitfield outputFormats, a bit string of concatenated output field
                              formats accepted by the compressor.
    vsBitfield outputTypes, a bit string of concatenated output field 
                            types accepted by the compressor.
};
This is the structure that should be implemented by a compressor module and is returned upon invoking getInfo(). The compressor module need not implement the getInfo() function, however the Info structure should be provided so that the Vizserver framework can determine the compressor capabilities.

The second method, getMaxCompressedSize(), is critical to the function of any compressor. With a query container object as its parameter, the compressor module should be implemented to return the largest possible size in bytes for a buffer needed to store the field data of that type. The container object should hold a pointer to a corresponding vsFieldInfo object, which is used to calculate the compressed image size. This size is highly dependant on the compressor's implementation. The fate of the size of the field buffers passed to compress() and expand() is determined by this query function and the compressor module should adhere to this size during its compression and decompression routines.

   Compression interface
The compression interface consists to two function. The compress() function to compress field tiles and the expand() function to expand them.

The compress() function takes as its input a query container filled with the frame data and its description. The compressor must handle any striding needed to extract the appropriate pixels from the input field when generating the compressed output. The output from the compressor is placed into a buffer that is allocated by the caller. This buffer must have at least getMaxCompressedSize() bytes to eliminate overflow problems.

The expand() function takes as its input another query container filled with the compressed data, and a description of the frame where the uncompressed data should be placed. The expanded results are placed into the caller provided buffer that corresponds to the field description. The expansion implementation must handle any striding needed to insert the appropriate pixels into the output field buffer.

It is critical for proper function that both compression and decompression adhere to the specified and requested field types and formats. The Vizserver implementation will expect the compressor to guarauntee the specified format to be interpreted correctly within the call to compress(). Upon transmission of the compressed data, the data will be expected to be retranslated to the requested field type and format within the call to expand(). However the compressed format is completely encapsulated within the compressor itself and can be determined by the compressor module programmer.

   API messaging
The API messaging interface consists of one function, recieveMessage(). This is the entry point for specific messages sent from the Vizserver framework to the compressor for added features. The parameter passed to this function is a query container with various parameters, such as vsStreamMessage and vsFieldInfo, contained inside. Extracting the messages and examining the message ID value will help identify the message and the appropriate actions needed. At the present time, the messages will pertain to initializing, destroying, and resizing of frame data streams (i.e. drawables), so that the compression module can handle any necessary resource allocations or preparations for the incoming imagery into compress() and expand().

   Implementing new compressors
The task of implementing new compressors is rather easy. All that is required is to define a new class from vsCompressor, use the postscribed macros to implement several of the core functions and define the compress(), expand(), and getMaxCompressedSize(), receiveMessage() functions.

The following is an example of a simple compressor implementation. It simply copies its input to output, but can serve as a basis for more complex compression implementations.

This example can be found in the vizserver_dev.sw.examples inst subsystem in the /usr/share/vizserver/src/compressor directory.

/usr/share/vizserver/src/compressor/nopCompressor.cxx

/usr/share/vizserver/src/compressor/nopIFCompressor.cxx

   Mutlithreading
Multithreading compressors is a valid way to increase performance. It is encouraged that a compressor use multithreading to its full potential when dealing with multiple field streams and also in using complex compression algorithms. vsLock and vsSemaphore are utility classes that provide POSIX thread style multithreading. Use these classes to implement mutltithreaded compressors.

Another factor to consider in the design of compressor modules is the use of vsCompressorTypes::ThreadSafe or vsCompressorTypes::ThreadUnsafe within the info structure. Vizserver will attempt to multi-thread the module itself, meaning running multiple threads through the same compressor object, if ThreadSafe is used. This can result in better performance, throughput and memory-utilization. Otherwise the compressor object will be deemed unsafe for multi-threading and will not be shared between threads. The compressor must be designed with thread-safe constructs such as vsLock to be well-behaved for use with external Vizserver threads of execution. This does not mean that multi-threading cannot occur within the module itself as described above.

   Interframe Compression and API Messaging
Interframe compression is supported under Vizserver 1.3. The shift from a normal compressor to an interframe compressor involves two steps: creating an algorithm that is progressive as frames pass through the compressor module and maintaining a persitant storage for each image stream. The interframe nature of the compressor must be hinted through the API using the vsCompressorTypes::InterFrame bit within the info structure.

Vizserver benefits from an interframe compression algorithm by reducing the amount of bandwidth needed to transmit image information. This is why the algorithm must be progressive; it will incrementally build the next image by only transmitting and receiving a minimal set updates. Compression ratios can increase using this strategy since less data is needed to transmit updates rather than a whole image.

A consequence of a progressive algorithm is persistant storage because the transmission stream of updates must be applied to stored data to achieve the final image. This persistant data can be viewed as compression contexts that are tied to each image stream. Therefore every unique drawable, which is an image stream, will have a context associated with it.

The API messaging system comes into play at this point because Vizserver will send messages to the active compression module regarding image stream changes. A vsStreamMessage object is the key object passed through the receiveMessage() function to alert the modules of any changes. If a new drawable is created and its image stream is passed through a compressor, a vsStreamMessage::InitializeStream message will be sent to the module. An interframe compressor can then decide what action to take for this new image stream (e.g. initialize a new context for persistant storage). Any other information will be imbedded inside the message object that pertains to the image stream in question. After this compress() or expand() will be called and it is the responsibility of the module to locate the context that is associated with the incoming image transmission. The key idea is that a call to receiveMessage() will occur before any call to compress() or expand() when any change occurs to the set of image streams, so that the module can do any pre or post-processing for the image data.

An example of an interframe compressor and the concepts above can be found at: /usr/share/vizserver/src/compressor/nopIFCompressor.cxx

LIMITATIONS
A compressor implementation should realize a solution to endian-dependant issues. When working between a client and server of different endian architechtures and word-sized data, a compressor will not function across calls to compress() and expand(). A simple solution is to store compressed data in network order (i.e. htonl) and upon decompression the data should be converted to host order (i.e. ntohl). Compressors that deal with byte-sized data should not need to deal with these issues.

METHOD DESCRIPTIONS

   vsCompressor()
vsCompressor (  );

Constructs a new compressor.

   ~vsCompressor()
virtual ~vsCompressor (  );

Destroy this compressor object.

   compress()
virtual size_t compress ( const vsCmprInputQueryArg& input, vsCmprOutputQueryArg& output) = 0;

Compresses the frame within input in the frame buffer within by output. The vsCmprInputQueryArg object is a query type that contains the following: vsFrameInfo, vsFramePacking and vsFrameData. These are used to extract the field information, packing options and pixel buffers that are to be compressed. The vsCmprOutputQueryArg object is a query type that contains the following: vsFrameInfo, vsFramePacking and vsFrameData. These contain the field information, packing options and data buffers where the compressed image will reside.

The return value size_t is an unsigned integer containing the size of the compressed image. Vizserver will only transmit this value of bytes over the network, even if the buffer size requested by getMaxCompressedSize() is larger.

   expand()
virtual void expand ( const vsCmprInputQueryArg& input, vsCmprOutputQueryArg& output) = 0;

Expands the compressed data within input into the buffers within output. The vsCmprInputQueryArg object is a query type that contains the following: vsFrameInfo, vsFramePacking and vsFrameData. These are used to extract the field information, data buffers, and data packing options, that are to be expanded. The vsCmprOutputQueryArg object is a query type that contains the following: vsFrameInfo, vsFramePacking and vsFrameData. These contain the field information, data packing options, and data buffers where the final decompressed image will reside.

The resultant output pixels are stored into output. The implementation must handle any striding needed to insert the tile into output.

   getInfo()
virtual const Info* getInfo (  ) const = 0;

Retrieve the compressor info. This function is automatically declared/defined for subclasses through the use of the vsCompressorDeclare and vsCompressorImplement macros.

   getMaxCompressedSize()
virtual size_t getMaxCompressedSize ( const vsCmprParamArg&) = 0;

Returns the maximum number of bytes into which the field data defined with vsCmprParamArg will compress to. The caller can use the returned value to allocate an appropriate sized buffer for storing the compressed results. The implementation must ensure that this size is not exceeded by the compress() function. vsCmprParamArg is a query type with the following objects within it: vsFieldInfo. After extraction, the field info struct can then be used to determine the maximum compressed size for this image.

   receiveMessage()
virtual void receiveMessage ( const vsCmprParamArg& msg) = 0;

Entry point for incoming compression messages. The msg is a query type containing the following: vsStreamMessage. This message struct will contain a message ID and the necessary data to execute an appropriate action.

Helper macros for class definitions and declarations
vsCompressorDeclare public: const vsCompressor::Info* getInfo() const;

This declaration is needed within the class definition of all compressors.

vsCompressorImplement(_class, _infoPtr, _desc) vsLoadableImplement(vsCompressor, _class, _desc) const vsCompressor::Info* _class::getInfo() const { return _infoPtr; }

This definition needs to be placed in global scope outside of the class definition of all compressors. It provides a means to return the static Info struct within each compressor.

SEE ALSO
vsFieldInfo, vsFieldPacking, vsFrameData, vsFrameInfo, vsFramePacking, vsLoadable, vsLock, vsSemaphore, vsStreamMessage