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.