Implementation Notes for OpenGL Programmers,
July, 1994


Kurt Akeley
Notes for OpenGL implementors

SGI specific
------------

    *	SGI may choose to add support for more pixel component orders.  For
	example, we may choose to support BGR and ABGR.

    *	All SGI OpenGL implementations will add a small epsilon (about 1/100
	of a pixel) to the X and Y viewport offsets.  This will cause
	points and lines (as well as polygons) rendered at integer locations
	with an integer Ortho2 mapping to rasterize predictably.  (e.g.
	a point rendered to 0,0 will fill the lower-left pixel in the
	window, etc.)

    *	If the implementation uses some form of scissoring instead of clipping
	for the frustum sides, this must be defeated if the viewport is
	smaller than the window, and the OpenGL scissor extends outside the
	viewport.  (See the Scissor note below.)

    *	The OpenGL enumerants all have different values (with very few
	exceptions) so that it is always a recognizable error to pass an
	unaccepted enumerant to a function.  As we "overload" some of the
	enumerants with dynamic name spaces (such as texture names passed
	through the target enumerant, and ibuffer names passed through
	the DrawBuffers and ReadBuffer enumerants) we should keep these
	name spaces distinct so that the error detection property is
	sustained.  (e.g. it should be detected as an error when an
	Ibuffer name is used as a texture name, or vice versa.)

Generic
-------

    *	Frustum clipping is defined in clip coordinates, and user-specified
	clipping is defined in eye coordinates.  Since the Projection
	matrix may be singular, it is not always possible to satisfy the
	spec by transforming user-specified clipping planes into clip
	coordinates and doing all actual clipping there.  Because there
	is no invariance rule regarding the contents of the matrix stacks,
	however, it is possible to switch to a software renderer when
	the Projection matrix is found to be poorly behaved and user-
	defined clipping planes are enabled.

    *	Viewport clamping must not be a function of window position, since
	it is a constant value that can be returned at any time.

    *	Buffers (depth, accum, etc) do not go out of existence when
	their use is disabled; they hang around for as long as the window
	does.

    *	Buffers are window state, not renderer state.  This means that
	direct renderers may have to keep buffers in shared memory.

    *	Multiple error flags can be maintained by parallel implementations.
	For example, parameter errors can be detected and stored by the
	host processor, and matrix stack overflows can be detected and stored
	by the graphics accelerator.

    *	All rendering must be done in the order specified.

    *	PushAttrib and PopAttrib performance is more important than
	query performance.

    *	The invariance rules constrain when an implementation can switch
	between renderers that do not produce identical results (e.g.
	typical software and hardware).

    *	Swapbuffers can be tricky to implement when multiple renderers
	are connected to a single window.  The aliases of each renderer must
	be swapped when any renderer calls SwapBuffers.  Remember that
	which buffer is front and which is back is window state, not renderer
	state!  (So there shouldn't be any state in the renderer -- especially
	a host-based renderer -- that "knows" about absolute buffer
	assignments.)

    *	The accumulation buffer accepts a wide range of signed values.  A
	hardware implementation may choose to handle only a subset of these
	values, faulting to software in difficult cases.  A reasonable
	compromise, for example, is to handle in hardware only positive
	values in the scaled range [0,1].

    *	Fast path pixel rendering for signed color components should assume
	a scale of 1/2, followed by a bias of 1/2.  This simply maps the
	signed [-1,1] range to the unsigned [0,1] range.  Note that this
	mapping can be implemented in hardware by simply inverting the MSB
	of signed bytes.

    *	Be careful about sharing a floating point environment with the
	application when running a direct, software OpenGL implementation.
	For example, the application may change the rounding mode, and
	this should not affect the operation of the OpenGL.  Also, the
	application may specify floating point exception handling that is
	not desired by OpenGL.

    *	Care should be taken to handle "unusual" floating point values,
	such as IEEE denorms and negative zero, correctly.

    *	Pixel update semantics are not defined for pixels that are "obscured"
	by other windows.  This allows software renderers with some buffers
	that are not "obscured" to ignore window overlap.

    *	Usually state values should be stored as presented, rather than
	truncated or rounded or whatever.  If a truncated or rounded version
	is needed by the implementation, it should be stored as a separate
	value.  The application-specified value should be returned when
	queried.  The exception is for state values that are clamped or
	masked when received, such as clear colors.

    *	It is OK to support only pointsize and linewidth of 1.0, for both
	aliased and antialiased points and lines.  Implementors are
	strongly encouraged to support very large aliased points and lines,
	and at least a small range of antialiased sizes and widths.

    *	When enabled, ColorMaterial overrides attempts to directly change
	the target material property.

    *	The OpenGL scissor is not required to be within the viewport area.
	If an application moves the viewport area inside the drawable area
	(typically a rectangular window) AND extends the scissor region
	beyond the viewport, non-geometric primitives that extend beyond
	the viewport must be rendered (e.g. wide lines, large points, images,
	and bitmaps).

    *	OpenGL allows up to four color buffers to be rendered to
	simultaneously.  (glDrawBuffers with argument GL_FRONT_AND_BACK
	while using a stereo visual, resulting in rendering to front-left,
	front-right, back-left, and back-right.)

    *	Drawing to multiple buffers has different semantics than Iris GL.
	In OpenGL, each buffer is drawn individually, with its own blending
	and logicop computations, based on its own contents.  Iris GL
	multiple buffer drawing computed a single value and jammed it into
	all of the enabled buffers.

    *	If a blending operation multiplies a component value by the
	maximum component value (e.g. 0xff in an 8-bit system) the
	result should be the original component value, exactly.  It is
	more important that color values multiplied by a maximum-value
	blend factor be unmodified, but it is also desirable that blend
	factors multiplied by a maximum-value color component be
	unmodified.  A simple implementation that accomplishes this in
	fixed-point arithmetic is to do the NxN bit multiplication to
	get a N.N bit result, then increment the integer part of the N.N
	result if the MSB of the integer part is 1, or if the MSB of the
	fractional part is 1.

    *	Be sure to divide Xe, Ye, and Ze by We if We is not 1.0.  Lighting
	and clipping calculations in OpenGL are corrected for homogeneous
	coordinates.

    *	The semantics for GL storage mode GL_*PACK_ROW_LENGTH can be
	problematic when the specified row length is greater than zero,
	but is less than the  specified by the DrawPixels or
	ReadPixels command.  In this case, OpenGL requires that pixels
	be retransferred (or copied over) as per the address arithmetic
	of the spec.  For example, if GL_UNPACK_ROW_LENGTH is 10 and
	the width specified to DrawPixels is 20, pixels 0-19 are rendered
	to the first row, pixels 10-29 are rendered to the second row,
	pixels 20-39 are rendered to the third row, etc.  Likewise, in the
	case of ReadPixels the pixels would be overwritten in memory.

    *	OpenGL textures clamp with borders, Iris GL textures clamp
	without borders.

    *	The pixel storage mode GL_*_ALIGNMENT is poorly named, it really
	implies something about the number of bytes per image row,
	rather than the alignment of the pointer to the first pixel
	with memory boundaries.  For example, a GL_*_ALIGNMENT value of
	4 does *not* mean that the pointer to the first pixel component
	is aligned to a multiple-of-4 memory boundary, it means that
	each row of the image has a multiple of 4 bytes.  If the pixel
	components are of type GLint, the pointer will point to a
	multiple-of-4 boundary, but if it is of type GLbyte or GLshort,
	it might not be.

	The only thing that determines the memory alignment of a pointer
	to an image is the data type of the components of that image.
	There are no other constraints.