Back to the OpenGL extension cross reference
GL_ARB_fragment_program
    ARB_fragment_program
Name Strings
    GL_ARB_fragment_program
Contributors
    Bob Beretta
    Pat Brown
    Matt Craighead
    Cass Everitt
    Evan Hart
    Jon Leech
    Bill Licea-Kane
    Bimal Poddar
    Jeremy Sandmel
    Jon Paul Schelter
    Avinash Seetharamaiah
    Nick Triantos
    and contributors to the ARB_vertex_program working group,
    the product of which provided the basis for this spec
Contact
    Benj Lipchak, ATI Research (blipchak 'at' ati.com)
IP Status
    Microsoft claims to own intellectual property related to this 
    extension.
Status
    Complete.  Approved by ARB on September 18, 2002
Version
    Last Modified Date: August 22, 2003
    Revision: 26
Number
    ARB Extension #27
Dependencies
    The extension is written against the OpenGL 1.3 Specification.
    OpenGL 1.3 is required.
    EXT_texture_lod_bias or OpenGL 1.4 is required.
    OpenGL 1.4 affects the definition of this extension.
    ARB_vertex_blend and EXT_vertex_weighting affect the definition of 
    this extension.
    ARB_matrix_palette affects the definition of this extension.
    ARB_transpose_matrix affects the definition of this extension.
    EXT_fog_coord affects the definition of this extension.
    EXT_texture_rectangle affects the definition of this extension.
    ARB_shadow interacts with this extension.
    ARB_vertex_program interacts with this extension.
    ATI_fragment_shader interacts with this extension.
    NV_fragment_program interacts with this extension.
Overview
    Unextended OpenGL mandates a certain set of configurable per-
    fragment computations defining texture application, texture 
    environment, color sum, and fog operations.  Several extensions have 
    added further per-fragment computations to OpenGL.  For example, 
    extensions have defined new texture environment capabilities 
    (ARB_texture_env_add, ARB_texture_env_combine, ARB_texture_env_dot3,
    ARB_texture_env_crossbar), per-fragment depth comparisons 
    (ARB_depth_texture, ARB_shadow, ARB_shadow_ambient, 
    EXT_shadow_funcs), per-fragment lighting (EXT_fragment_lighting, 
    EXT_light_texture), and environment mapped bump mapping 
    (ATI_envmap_bumpmap).  
    Each such extension adds a small set of relatively inflexible per-
    fragment computations.
    This inflexibility is in contrast to the typical flexibility 
    provided by the underlying programmable floating point engines 
    (whether micro-coded fragment engines, DSPs, or CPUs) that are 
    traditionally used to implement OpenGL's texturing computations.  
    The purpose of this extension is to expose to the OpenGL application 
    writer a significant degree of per-fragment programmability for 
    computing fragment parameters.
    For the purposes of discussing this extension, a fragment program is 
    a sequence of floating-point 4-component vector operations that 
    determines how a set of program parameters (not specific to an
    individual fragment) and an input set of per-fragment parameters are 
    transformed to a set of per-fragment result parameters.
    The per-fragment computations for standard OpenGL given a particular 
    set of texture and fog application modes (along with any state for 
    extensions defining per-fragment computations) is, in essence, a 
    fragment program.  However, the sequence of operations is defined 
    implicitly by the current OpenGL state settings rather than defined 
    explicitly as a sequence of instructions.
    This extension provides an explicit mechanism for defining fragment 
    program instruction sequences for application-defined fragment 
    programs.  In order to define such fragment programs, this extension 
    defines a fragment programming model including a floating-point
    4-component vector instruction set and a relatively large set of 
    floating-point 4-component registers.
    The extension's fragment programming model is designed for efficient
    hardware implementation and to support a wide variety of fragment 
    programs.  By design, the entire set of existing fragment programs 
    defined by existing OpenGL per-fragment computation extensions can 
    be implemented using the extension's fragment programming model.
Issues
    This extension is closely related to ARB_vertex_program, and is in 
    sync with revision 36 of that spec.  ARB_fragment_program will 
    continue to track changes made to ARB_vertex_program.
    (1) Should we provide precision queries?
      RESOLVED: We've decided not to include precision queries.
      Implementations are expected to meet or exceed the precision 
      guidelines set forth in the core GL spec, section 2.1.1, p. 6,
      as ammended by this extension.
      To summarize section 2.1.1, the maximum representable magnitude of 
      colors must be at least 2^10, while the maximum representable 
      magnitude of other floating-point values must be at least 2^32.  
      The individual results of floating-point operations must be 
      accurate to about 1 part in 10^5.
      Here are the reasons why precision queries were not included:
        1. It is unclear what the queries should be:
           a) min, max, [0,1) granularity
           b) min +, max +, min -, max -, [0,1) granularity
           c) IEEE mantissa bits, IEEE exponent bits
        2. Due to instruction emulation, there is no way to query the 
           actual precision that can be expected.  Should the query 
           return the best-case or worst-case precision?
        3. Implementations may support multiple precisions, on a per-
           instruction basis or across the board.  How would this be 
           exposed?
        4. Current implementations are able to meet the minimum 
           requirements specified in the core GL, thanks to its
           sufficiently loose wording "... so that the individual 
           results of floating-point operations are accurate to ABOUT 
           1 part in 10^5."  (Emphasis added.)
        5. A conformance test can act as watchdog to ensure 
           implementations are not cutting corners on precision.
        6. Adding precision queries would require a new entrypoint.
      See issue 22 regarding reduced-precision modes.
    (2) Should the LOD biased texture sample be optional?
      RESOLVED: TXB support is mandatory.  This exposes useful 
      functionality which enables blurring and sharpening effects.  It 
      will be more useful to entirely override derivatives (scale 
      factor) rather than just biasing the level-of-detail.  This would 
      be a future extension to fragment programs.
      It should be noted here that the bias introduced per-fragment by 
      TXB is added to any per-object or per-stage LOD bias.  If per-
      fragment LOD bias is not necessary, using the per-object and/or 
      per-stage LOD biases may perform better.
    (3) Should we include the ability to bind to the color matrix?  How
    about others?  Program matrices?
      RESOLVED: We will not specifically add anything that depends on 
      the ARB_imaging subset.  So we have not included matrix bindings 
      to the color matrix (or parameter bindings to the color biases, 
      etc.).  However, we have included matrix binding support and 
      support for all of the matrices present in ARB_vertex_program.
    (4) Should we include the ability to bind to just a texcoord 
    attribute's S,T components?  (Or just S, or S,T,P for that matter?) 
      RESOLVED: No.  Issue #15 below obviates this issue by making the
      texture coordinate usage within a program explicit, thereby making 
      optimizations to reduce the number of interpolated texture 
      coordinates something an implementation can do at compile time 
      instead of having to do it during every texture target change.
    (5) What other instructions should be added?  Should any be removed?
      RESOLVED: The differences between the ARB_vertex_program 
      instruction set and the ARB_fragment_program instruction set are
      minimal.  ARB_fragment_program removes the LOG and EXP rough
      approximation instructions and the ARL address register load
      instruction.  ARB_fragment_program adds the SIN/COS/SCS 
      trigonometric instructions, the LRP linear interpolation 
      instruction, the CMP compare instruction, and the TEX/TXP/TXB/KIL
      texture instructions.
    (6) Should depth output be a program option or a mandatory feature?
 
      RESOLVED: Depth output capability should be mandatory.
    (6a) How should per-vertex geometric depth clipping be handled when 
      replacing depth in a fragment program?
      RESOLVED: Per-vertex geometric depth clipping should be performed 
      by the GL as usual, so no spec change is required.  The ideal
      behavior would be to disable near and far clipping planes when
      replacing depth, but not all implementations can natively support
      disabling individual clip planes.
    (6b) How should depth output from the fragment program be further 
    processed before being handed to the per-fragment operations?
      RESOLVED: Depth gets clamped by GL to [0,1].  App has access to 
      depth range as a bindable parameter if it wants to either scale 
      and bias its depth to fall within the depth range, or to kill 
      fragments outside the depth range.
    (7) If a fragment program does not write a color value, what should
    be the final color of the fragment?
      RESOLVED: The final fragment color is undefined.  Note that it may
      be perfectly reasonable to have a program that computes depth 
      values but not colors.  Fragment colors are often irrelevant if
      color writes are disabled (via ColorMask).
    (7a) If a fragment program does not write a depth value, what should 
    be the final depth value of the fragment?
      RESOLVED: "Depth fly-over" (using the conventional depth produced
      by rasterization) should happen whenever a depth-replacing program 
      is not in use.  A depth-replacing program is defined as a program
      that writes to result.depth in at least one instruction.  The
      presence of a depth declaration alone DOES NOT designate a depth-
      replacing program.  The intention is that a future extension
      introducing conditional execution will still consider a program to
      be depth-replacing even if the instruction(s) writing to 
      result.depth do(es) not execute.
      Other considered definitions of depth-replacing program:
        1. The presence of a depth declaration -OR- the use of 
           result.depth as an instruction destination anywhere in the 
           program designates a depth-replacing program.
        2. Every program is a depth-replacing program, but the GL 
           initializes the depth output to be the depth produced by 
           rasterization.  The app may then overwrite the depth output.
        3. Every program is a depth-replacing program, and the app is 
           solely responsible for copying the depth input to depth 
           output if desired.
    (8) Should relative addressing, like that defined in 
    ARB_vertex_program, be supported in this spec?
      RESOLVED: No, relative addressing won't be included in this spec.
    (9) Should full-featured operand component swizzling, like that 
    defined in ARB_vertex_program, be supported in this spec?
      RESOLVED: Yes, full swizzling is mandatory.
    (10) Should texture instructions contain specific limitations on
    operations that can be performed?  For example, should write masks
    or operand component swizzling be disallowed?
      RESOLVED: Texture instructions are specified to be very similar to 
      ALU instructions.  They have been given 3-letter names, they allow 
      writemasking and saturation (which would be useful for floating-
      point texture formats), source swizzles and negates, and the 
      ability to use parameters as sources.
    (11) Should we standardize options for stencil or aux data buffer
    outputs?
      RESOLVED: Stencil and aux data buffers will be saved for a 
      possible future extension to fragment programs.
    (12) Should depth output be pulled from the 3rd or 4th component?
      RESOLVED: 3rd component, as the 3rd component is also used for
      depth input from the "fragment.position" attribute.
    (13) Which stages are subsumed by fragment programs?
      RESOLVED: Texturing, color sum, and fog.
    (14) What should the minimum resource limits be?
      RESOLVED: 10 attributes, 24 parameters, 4 texture indirections,
      48 ALU instructions, 24 texture instructions, and 16 temporaries.
    (15) OpenGL provides a hierarchy of texture enables (cube map, 3D, 
    2D, 1D).  Should the texture sampling instructions here override 
    that hierarchy and select specific texture targets?
      RESOLVED: Yes.  This removes a potential pitfall for developers: 
      leaving the hierarchy of enables in an undesired state.  It makes 
      programs more readable as the intent of the sample is more 
      obvious.  Finally, it allows compilers to be more aggressive as 
      to which texcoord components are "don't cares" without having to 
      recompile programs when fixed-function texenables change.  One 
      drawback is that programs cannot be reused for both 2D and 3D 
      texturing, for example, by simply changing the texture enables. 
      Texture sampling can be specified by instructions like 
      
        TEX myTexel, fragment.texcoord[1], texture[2], 3D;
      which would indicate to use texture coordinate set number 1 to
      sample from the texture object bound to the TEXTURE_3D target on 
      texture image unit 2.
      Each texture unit can have only one "active" target.  Programs are 
      not allowed to reference different texture targets in the same 
      texture image unit.  In the example above, any other texture 
      instructions using texture image unit 2 must specify the 3D 
      texture target.
      Note that every texture image unit always has a texture bound to 
      every texture target, whether it is a named texture object or a 
      default texture.  However, the texture may not be complete as
      defined in section 3.8.9 of the core GL spec.  See issue 23.
    (16) Should aux texture units be additional units on top of existing 
    full-featured texture units, or should this spec fully deprecate 
    "legacy" texture units and only expose texture coordinate sets and
    texture image units?
      Background: Some implementations are able to expose more 
      "texture image units" (texture maps and associated parameters) 
      than "texture coordinate sets" (current texcoords, texgen, and 
      texture matrices).  A conventional GL "texture unit" encompasses 
      both a texture image unit and a texture coordinate set as well as 
      texture environment state.
      RESOLVED: Yes, deprecate "legacy" texture units.  This is a more 
      flexible model.
    (17) Should fragment programs affect all fragments, or just those
    produced by the rasterization of points, lines, and triangles?
      RESOLVED: Every fragment generated by the GL is subject to 
      fragment program mode.  This includes point, line, and polygon 
      primitives as well as pixel rectangles and bitmaps.
    (18) Should per-fragment position and fogcoord be bindable as 
    fragment attributes?
      RESOLVED: Yes, interpolated fogcoord will make per-fragment 
      fog application possible, in addition to full fog stage 
      subsummation.  Interpolated window position, especially depth,
      enables interesting depth-replacing algorithms.
    (19) What characters should be used to identify individual 
    components in swizzle selectors and write masks?
      RESOLVED: ARB_vertex_program provides "xyzw".  This extension 
      supports "xyzw" and also provides "rgba" for better readability 
      when dealing with RGBA color values.  Adding support for special 
      identifiers for dealing with texture coordinates was considered 
      and rejected.  "strq" could be used to identify texture coordinate 
      components, but the "r" would conflict with the "r" from "rgba".
      "stpq" would be another possibility, but could be a source of 
      confusion.
    (20) Should implementations be required to support all programs that 
    fit within the exported limits on the number of resources (e.g.,
    instructions, temporaries) that can be present in a program, even if 
    it means falling back to software?  Should implementations be 
    required to reject programs that could never be accelerated?
      RESOLVED: No and no.  An implementation is allowed to fail 
      ProgramStringARB due to the program exceeding native resources.
      Note that this failure must be invariant with respect to all other
      OpenGL state.  In other words, a program cannot succeed to load
      with default state, but then fail to load when certain GL state
      is altered.  However, an implementation is not required to fail
      when a program would exceed native resources, and is in fact
      encouraged to fallback to a software path.  See issue 21 for a way
      of determining if this has happened.
      This notable departure from ARB_vertex_program was made as an
      accommodation to vendors who could not justify implementing a
      software fallback path which would be relatively slow even 
      compared to an ARB_vertex_program software fallback path.
      Two issues with this decision:
        1.  The API limits become hints, and one can no longer tell by
            visual inspection whether or not a program will load on
            every implementation.
        2.  Program loading will now depend on the optimizer, which may 
            vary from release to release of an implementation.  A 
            program that succeeded to load when an ISV first wrote it 
            may fail to load in a future driver version, and vice versa.
    (21) How can applications determine if their programs are too large
    to run on the native (likely hardware) implementation, and therefore may
    run with reduced performance?
      RESOLVED: The following code snippet uses a native resource
      query to guarantee a program is loaded natively (or not at all):
      GLboolean ProgramStringIsNative(GLenum target, GLenum format, 
                                     GLsizei len, const GLvoid *string)
      {
          GLint errorPos, isNative;
          glProgramStringARB(target, format, len, string);
          glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
          glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, 
              GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative);
          if ((errorPos == -1) && (isNative == 1))
              return GL_TRUE;
          else
              return GL_FALSE;
      }
      Note that a program that successfully loads, and falls under the
      native limits, is still not guaranteed to execute in hardware.
      Lack of other resources (e.g., texture memory) or the use of other 
      OpenGL features not natively supported by the implementation 
      (e.g., textures with borders) may also prevent the program from
      executing in hardware.
    (22) Should we provide applications with a method to control the 
    level of precision used to carry out fragment program computations?
      RESOLVED:  Yes.  The GL implementation ultimately has control over 
      the level of precision used for fragment program computations.  
      However, the "ARB_precision_hint_fastest" and 
      "ARB_precision_hint_nicest" program options allow applications to 
      guide the GL implementation in its precision selection.  The 
      "fastest" option encourages the GL to minimize execution time, 
      with possibly reduced precision.  The "nicest" option encourages 
      the GL to maximize precision, with possibly increased execution 
      time.
      If the precision hint is not "fastest", GL implementations should
      perform low-precision operations only if they could not 
      appreciably affect the final results of the program.  Regardless 
      of the precision hint, GL implementations are discouraged from 
      reducing the precision of computations so aggressively that final 
      rendering results could be seriously compromised due to overflow
      of intermediate values or insufficient number of mantissa bits.
      Some implementations may provide only a single level of precision, 
      in which case these hints may have no effect.  However, all
      implementations will accept these options, even if they are 
      silently ignored.
      More explicit control of precision, such as provided in "C" with 
      data types such as "short", "int", "float", "double", may also be 
      a desirable feature, but this level of detail is left to a 
      separate extension.
    (23) What is the result of a sample from an incomplete texture? 
    The definition of texture completeness can be found in section 3.8.9 
    of the core GL spec. 
      RESOLVED: The result of a sample from an incomplete texture is the 
      constant vector (0,0,0,1).  The benefit of defining the result to 
      be a constant is that broken apps are guaranteed to generate 
      unexpected (black) results from their bad samples.  If we were to 
      leave the result undefined, some implementations may generate 
      expected results some of the time, for example when magfiltering, 
      giving app developers a false sense of correctness in their apps. 
    (24) What is a texture indirection, and how is it counted?
       RESOLVED: On some implementations, fragment programs that have
       complex texture dependency chains may not be supported, even if 
       the instruction counts fit within the exported limits.  A texture
       dependency occurs when a texture instruction depends on the 
       result of a previous instruction (ALU or texture) for use as its 
       texture coordinate.
       A texture indirection can be considered a node in the texture 
       dependency chain.  Each node contains a set of texture 
       instructions which execute in parallel, followed by a sequence of 
       ALU instructions.  A dependent texture instruction is one that 
       uses a temporary as an input coordinate rather than an attribute 
       or a parameter.  A program with no dependent texture instructions 
       (or no texture instructions at all) will have a single node in 
       its texture dependency chain, and thus a single indirection.
       API-level texture indirections are counted by keeping track of
       which temporaries are read and written within the current node in 
       the texture dependency chain.  When a texture instruction is 
       encountered, an indirection may be added and a new node started 
       if either of the following two conditions is true:
         1. the source coordinate of the texture instruction is a
            temporary that has already been written in the current node, 
            either by a previous texture instruction or ALU instruction;
         2. the result of the texture instruction is a temporary that 
            has already been read or written in the current node by an 
            ALU instruction.
       The texture instruction provoking a new indirection and all
       subsequent instructions are added to the new node.  This process
       is repeated until the end of the program is encountered.  Below 
       is some pseudo-code to describe this:
         indirections = 1;
         tempsOutput = 0;
         aluTemps = 0;
         while (i = getInst()) 
         {
           if (i.type == TEX) 
           {
             if (((i.input.type == TEMP) && 
                   (tempsOutput & (1 << i.input.index))) ||
                 ((i.op != KILL) && (i.output.type == TEMP) && 
                   (aluTemps & (1 << i.output.index)))) 
             {
               indirections++;
               tempsOutput = 0;
               aluTemps = 0;
             }
           } else {
             if (i.input1.type == TEMP)
               aluTemps |= (1 << i.input1.index);
             if (i.input2 && i.input2.type == TEMP)
               aluTemps |= (1 << i.input2.index);
             if (i.input3 && i.input3.type == TEMP)
               aluTemps |= (1 << i.input3.index);
             if (i.output.type == TEMP)
               aluTemps |= (1 << i.output.index);
           }
           if ((i.op != KILL) && (i.output.type == TEMP))
             tempsOutput |= (1 << i.output.index);
         }
       For example, the following programs would have 1, 2, and 3
       texture indirections, respectively:
         !!ARBfp1.0
         # No texture instructions, but always 1 indirection
         MOV result.color, fragment.color;
         END
         !!ARBfp1.0
         # A simple dependent texture instruction, 2 indirections
         TEMP myColor;
         MUL myColor, fragment.texcoord[0], fragment.texcoord[1];
         TEX result.color, myColor, texture[0], 2D;
         END
         !!ARBfp1.0
         # A more complex example with 3 indirections
         TEMP myColor1, myColor2;
         TEX myColor1, fragment.texcoord[0], texture[0], 2D;
         MUL myColor1, myColor1, myColor1;
         TEX myColor2, fragment.texcoord[1], texture[1], 2D;
         # so far we still only have 1 indirection
         TEX myColor2, myColor1, texture[2], 2D;      # This is #2
         TEX result.color, myColor2, texture[3], 2D;  # And #3
         END
       Note that writemasks for the temporaries written and swizzles 
       for the temporaries read are not taken into consideration when
       counting indirections.  This makes hand-counting of indirections
       by a developer an easier task.
       Native texture indirections may be counted differently by an
       implementation to reflect its exact restrictions, to reflect the 
       true dependencies taking into account writemasks and swizzles, 
       and to reflect optimizations such as instruction reordering.  
       For implementations with no restrictions on the number of
       indirections, the maximum indirection count will equal the
       maximum texture instruction count.
    (25) How can a program reduce SCS's scalar operand to the 
    fundamental period [-PI,PI]?
       RESOLVED: Unlike the individual SIN and COS instructions, SCS 
       requires that its argument be reduced ahead of time to the 
       fundamental period.  The reason SCS doesn't perform this 
       operation automatically is that it may make unnecessary redundant 
       work for programs that already have their operand in the correct 
       range.  Other programs that do need to reduce their operand 
       simply need to add a block of code before the SCS instruction:
         PARAM myParams = { 0.5, -3.14159, 6.28319, 0.15915 };
         MAD myOperand.x, myOperand.x, myParams.w, myParams.x; # a = (a/(2*PI))+0.5
         FRC myOperand.x, myOperand.x;                         # a = frac(a)
         MAD myOperand.x, myOperand.x, myParams.z, myParams.y  # a = (a*2*PI)-PI
         ...
         SCS myResult, myOperand.x;
    (26) Is depth output from a fragment program guaranteed to be
    invariant with respect to depth produced via conventional 
    rasterization?
      RESOLVED:  No.  The floating-point representation of depth values 
      output from a fragment program may lead to the output of depth 
      with less precision than the depth output by convention GL 
      rasterization.  For example, a floating-point representation with 
      16 bits of mantissa will certainly produce depth with lesser 
      precision than that of conventional rasterization used in 
      conjunction with a 24-bit depth buffer, where all values are
      maintained as integers.  Be aware of this when mixing conventional 
      GL rendering with fragment program rendering.
    (27) How can conventional GL fog application be achieved within a 
    fragment program?
      RESOLVED: Program options have been introduced that allow a 
      program to request fog to be applied to the final clamped fragment 
      color before being passed along to the antialiasing application 
      stage.  This makes it easy for:
        1. developers to request conventional fog behavior
        2. implementations with dedicated fog hardware to use it
        3. implementations without dedicated fog hardware, so they need 
           not track fog state after compilation, and constantly 
           recompile when fog state changes.
      The three mandatory options are ARB_fog_exp, ARB_fog_exp2, and 
      ARB_fog_linear.  As these options are mutually exclusive by
      nature, specifying more than one is not useful.  If more than one
      is specified, the last one encountered in the <optionSequence> 
      will be the one to actually modify the execution environment.
    (28) Why have all of the enums, entrypoints, GLX protocol, and spec 
    language shared with ARB_vertex_program been reproduced here?
      RESOLVED: The two extensions are independent of one another, in
      so far as an implementation need not support both of them in order 
      to support one of them.  Everything needed to implement or make 
      use of ARB_fragment_program is present in this spec without the
      need to refer to the ARB_vertex_program spec.  When and if these
      two extensions are incorporated into the core OpenGL, the
      significant overlap of the two will be collapsed into a single
      instance of the shared parts.
    (29) How might an implementation implement the fog options?  To What
    does the extra resource consumption described in 3.11.4.5.1 
    correspond?
      RESOLVED: The following code snippets reflect possible
      implementations of the fog options.  While an implementation may
      use other instruction sequences to achieve the same result, or may 
      use external fog hardware if available, all implementations must 
      enforce the API-level resource consumption as described: 2 params,
      1 temp, 1 attribute, and 3, 4, or 2 instructions.  "finalColor" in
      the examples below is the color that would otherwise be 
      "result.color", with components clamped to the range [0,1].
      "result.color.a" is assumed to have already been written, as fog
      blending does not affect the alpha component.
      EXP:
        # Exponential fog
        # f = exp(-d*z)
        #
        PARAM p = {DENSITY/LN(2), NOT USED, NOT USED, NOT USED};
        PARAM fogColor = state.fog.color;
        TEMP fogFactor;
        ATTRIB fogCoord = fragment.fogcoord.x;
        MUL fogFactor.x, p.x, fogCoord.x;
        EX2_SAT fogFactor.x, -fogFactor.x;
        LRP result.color.rgb, fogFactor.x, finalColor, fogColor;
      EXP2:
        #
        # 2nd-order Exponential fog
        # f = exp(-(d*z)^2)
        #
        PARAM p = {DENSITY/SQRT(LN(2)), NOT USED, NOT USED, NOT USED};
        PARAM fogColor = state.fog.color;
        TEMP fogFactor;
        ATTRIB fogCoord = fragment.fogcoord.x;
        MUL fogFactor.x, p.x, fogCoord.x;
        MUL fogFactor.x, fogFactor.x, fogFactor.x;
        EX2_SAT fogFactor.x, -fogFactor.x;
        LRP result.color.rgb, fogFactor.x, finalColor, fogColor;
      LINEAR:
        #
        # Linear fog
        # f = (end-z)/(end-start)
        #
        PARAM p = {-1/(END-START), END/(END-START), NOT USED, NOT USED};
        PARAM fogColor = state.fog.color;
        TEMP fogFactor;
        ATTRIB fogCoord = fragment.fogcoord.x;
        MAD_SAT fogFactor.x, p.x, fogCoord.x, p.y;
        LRP result.color.rgb, fogFactor.x, finalColor, fogColor;
    (30) Why is the order of operands for the CMP instruction different
    than the order used by another popular graphics API?
      RESOLVED: No other graphics API was used as a basis for the
      design of ARB_fragment_program except ARB_vertex_program, which
      did not have a CMP instruction.  This independent evolution
      naturally led to differences in minor details such as order of
      operands.  This discrepancy is noted here to help developers 
      familiar with the other API to avoid this potential pitfall.
    (31) Is depth offset applied to the window z value before it enters
    the fragment program?
      RESOLVED: As in the base OpenGL specification, the depth offset
      generated by polygon offset is added during polygon rasterization.
      The depth value provided to shaders in the fragment.position.z
      attribute already includes polygon offset, if enabled.  If the
      depth value is replaced by a fragment program, the polygon offset
      value will NOT be recomputed and added back after fragment program
      execution.
      NOTE: This is probably not desirable for fragment programs that
      modify depth values since the partials used to generate the offset
      may not match the partials of the computed depth value.
    void ProgramStringARB(enum target, enum format, sizei len, 
                          const void *string); 
    void BindProgramARB(enum target, uint program);
    void DeleteProgramsARB(sizei n, const uint *programs);
    void GenProgramsARB(sizei n, uint *programs);
    void ProgramEnvParameter4dARB(enum target, uint index,
                                  double x, double y, double z, double w);
    void ProgramEnvParameter4dvARB(enum target, uint index,
                                   const double *params);
    void ProgramEnvParameter4fARB(enum target, uint index,
                                  float x, float y, float z, float w);
    void ProgramEnvParameter4fvARB(enum target, uint index,
                                   const float *params);
    void ProgramLocalParameter4dARB(enum target, uint index,
                                    double x, double y, double z, double w);
    void ProgramLocalParameter4dvARB(enum target, uint index,
                                     const double *params);
    void ProgramLocalParameter4fARB(enum target, uint index,
                                    float x, float y, float z, float w);
    void ProgramLocalParameter4fvARB(enum target, uint index,
                                     const float *params);
    void GetProgramEnvParameterdvARB(enum target, uint index,
                                     double *params);
    void GetProgramEnvParameterfvARB(enum target, uint index, 
                                     float *params);
    void GetProgramLocalParameterdvARB(enum target, uint index,
                                       double *params);
    void GetProgramLocalParameterfvARB(enum target, uint index, 
                                       float *params);
    void GetProgramivARB(enum target, enum pname, int *params);
    void GetProgramStringARB(enum target, enum pname, void *string);
    boolean IsProgramARB(uint program);
    Accepted by the <cap> parameter of Disable, Enable, and IsEnabled, 
    by the <pname> parameter of GetBooleanv, GetIntegerv, GetFloatv, 
    and GetDoublev, and by the <target> parameter of ProgramStringARB,
    BindProgramARB, ProgramEnvParameter4[df][v]ARB,
    ProgramLocalParameter4[df][v]ARB, GetProgramEnvParameter[df]vARB, 
    GetProgramLocalParameter[df]vARB, GetProgramivARB and
    GetProgramStringARB.
        FRAGMENT_PROGRAM_ARB                            0x8804
    Accepted by the <format> parameter of ProgramStringARB:
        PROGRAM_FORMAT_ASCII_ARB                        0x8875
    Accepted by the <pname> parameter of GetProgramivARB:
        PROGRAM_LENGTH_ARB                              0x8627
        PROGRAM_FORMAT_ARB                              0x8876
        PROGRAM_BINDING_ARB                             0x8677
        PROGRAM_INSTRUCTIONS_ARB                        0x88A0
        MAX_PROGRAM_INSTRUCTIONS_ARB                    0x88A1
        PROGRAM_NATIVE_INSTRUCTIONS_ARB                 0x88A2
        MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB             0x88A3
        PROGRAM_TEMPORARIES_ARB                         0x88A4
        MAX_PROGRAM_TEMPORARIES_ARB                     0x88A5
        PROGRAM_NATIVE_TEMPORARIES_ARB                  0x88A6
        MAX_PROGRAM_NATIVE_TEMPORARIES_ARB              0x88A7
        PROGRAM_PARAMETERS_ARB                          0x88A8
        MAX_PROGRAM_PARAMETERS_ARB                      0x88A9
        PROGRAM_NATIVE_PARAMETERS_ARB                   0x88AA
        MAX_PROGRAM_NATIVE_PARAMETERS_ARB               0x88AB
        PROGRAM_ATTRIBS_ARB                             0x88AC
        MAX_PROGRAM_ATTRIBS_ARB                         0x88AD
        PROGRAM_NATIVE_ATTRIBS_ARB                      0x88AE
        MAX_PROGRAM_NATIVE_ATTRIBS_ARB                  0x88AF
        MAX_PROGRAM_LOCAL_PARAMETERS_ARB                0x88B4
        MAX_PROGRAM_ENV_PARAMETERS_ARB                  0x88B5
        PROGRAM_UNDER_NATIVE_LIMITS_ARB                 0x88B6
        PROGRAM_ALU_INSTRUCTIONS_ARB                    0x8805
        PROGRAM_TEX_INSTRUCTIONS_ARB                    0x8806
        PROGRAM_TEX_INDIRECTIONS_ARB                    0x8807
        PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB             0x8808
        PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB             0x8809
        PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB             0x880A
        MAX_PROGRAM_ALU_INSTRUCTIONS_ARB                0x880B
        MAX_PROGRAM_TEX_INSTRUCTIONS_ARB                0x880C
        MAX_PROGRAM_TEX_INDIRECTIONS_ARB                0x880D
        MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB         0x880E
        MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB         0x880F
        MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB         0x8810
    Accepted by the <pname> parameter of GetProgramStringARB:
        PROGRAM_STRING_ARB                              0x8628
    Accepted by the <pname> parameter of GetBooleanv, GetIntegerv,
    GetFloatv, and GetDoublev:
        PROGRAM_ERROR_POSITION_ARB                      0x864B
        CURRENT_MATRIX_ARB                              0x8641
        TRANSPOSE_CURRENT_MATRIX_ARB                    0x88B7
        CURRENT_MATRIX_STACK_DEPTH_ARB                  0x8640
        MAX_PROGRAM_MATRICES_ARB                        0x862F
        MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB              0x862E
        MAX_TEXTURE_COORDS_ARB                          0x8871
        MAX_TEXTURE_IMAGE_UNITS_ARB                     0x8872
    Accepted by the <name> parameter of GetString:
        PROGRAM_ERROR_STRING_ARB                        0x8874
    Accepted by the <mode> parameter of MatrixMode:
    
        MATRIX0_ARB                                     0x88C0
        MATRIX1_ARB                                     0x88C1
        MATRIX2_ARB                                     0x88C2
        MATRIX3_ARB                                     0x88C3
        MATRIX4_ARB                                     0x88C4
        MATRIX5_ARB                                     0x88C5
        MATRIX6_ARB                                     0x88C6
        MATRIX7_ARB                                     0x88C7
        MATRIX8_ARB                                     0x88C8
        MATRIX9_ARB                                     0x88C9
        MATRIX10_ARB                                    0x88CA
        MATRIX11_ARB                                    0x88CB
        MATRIX12_ARB                                    0x88CC
        MATRIX13_ARB                                    0x88CD
        MATRIX14_ARB                                    0x88CE
        MATRIX15_ARB                                    0x88CF
        MATRIX16_ARB                                    0x88D0
        MATRIX17_ARB                                    0x88D1
        MATRIX18_ARB                                    0x88D2
        MATRIX19_ARB                                    0x88D3
        MATRIX20_ARB                                    0x88D4
        MATRIX21_ARB                                    0x88D5
        MATRIX22_ARB                                    0x88D6
        MATRIX23_ARB                                    0x88D7
        MATRIX24_ARB                                    0x88D8
        MATRIX25_ARB                                    0x88D9
        MATRIX26_ARB                                    0x88DA
        MATRIX27_ARB                                    0x88DB
        MATRIX28_ARB                                    0x88DC
        MATRIX29_ARB                                    0x88DD
        MATRIX30_ARB                                    0x88DE
        MATRIX31_ARB                                    0x88DF
Additions to Chapter 2 of the OpenGL 1.3 Specification (OpenGL 
Operation)
    Modify Section 2.1.1, Floating-Point Computation (p. 6)
    (modify first paragraph, p. 6) ... The maximum representable 
    magnitude of a floating-point number used to represent position,
    normal, or texture coordinates must be at least 2^32; the maximum
    representable magnitude for colors must be at least 2^10.  ...
    Modify Section 2.7, Vertex Specification (p. 19)
    (modify second paragraph, p. 20) Implementations support more than
    one set of texture coordinates.  The commands
      void MultiTexCoord{1234}{sifd}(enum texture, T coords);
      void MultiTexCoord{1234}{sifd}v(enum texture, T coords);
    take the coordinate set to be modified as the <texture> parameter.
    <texture> is a symbolic constant of the form TEXTUREi, indicating
    that texture coordinate set i is to be modified.  The constants obey
    TEXTUREi = TEXTURE0 + i (i is in the range 0 to k-1, where k is the
    implementation-dependent number of texture units defined by
    MAX_TEXTURE_COORDS_ARB).
    Modify Section 2.8, Vertex Arrays (p. 21)
    (modify first paragraph, p. 21) ... The client may specify up to 5
    plus the value of MAX_TEXTURE_COORDS_ARB arrays: one each to store
    vertex coordinates...
    (modify first paragraph, p. 23) The command
      void ClientActiveTexture(enum texture);
    is used to select the vertex array client state parameters to be
    modified by the TexCoordPointer command and the array affected by
    EnableClientState and DisableClientState with parameter
    TEXTURE_COORD_ARRAY.  This command sets the client state variable
    CLIENT_ACTIVE_TEXTURE.  Each texture coordinate set has a client 
    state vector which is selected when this command is invoked.  This
    state vector includes the vertex array state.  This call also 
    selects the texture coordinate set state used for queries of client
    state.
    (modify first paragraph, p. 28) If the number of supported texture
    coordinate sets (the value of MAX_TEXTURE_COORDS_ARB) is k, ...
    Modify Section 2.10.2, Matrices (p. 31)
    (modify first paragraph, p. 31) The projection matrix and model-view 
    matrix are set and modified with a variety of commands.  The 
    affected matrix is determined by the current matrix mode.  The 
    current matrix mode is set with
      void MatrixMode(enum mode);
    which takes one of the pre-defined constants TEXTURE, MODELVIEW, 
    COLOR, PROJECTION, or MATRIX<i>_ARB as the argument.  In the case of
    MATRIX<i>_ARB, <i> is an integer between 0 and <n>-1 indicating one 
    of <n> program matrices where <n> is the value of the implementation 
    defined constant MAX_PROGRAM_MATRICES_ARB.  Such program matrices 
    are described in section 3.11.7.  TEXTURE is described later in 
    section 2.10.2, and COLOR is described in section 3.6.3.  If the 
    current matrix mode is MODELVIEW, then matrix operations apply to 
    the model-view matrix; if PROJECTION, then they apply to the 
    projection matrix.
    (modify first paragraph, p. 34) For each texture coordinate set, a
    4x4 matrix is applied to the corresponding texture coordinates...
    (modify first and second paragraphs, p. 35) The command
      void ActiveTexture(enum texture);
    specifies the active texture unit selector, ACTIVE_TEXTURE.  Each
    texture unit contains up to two distinct sub-units: a texture 
    coordinate processing unit (consisting of a texture matrix stack and
    texture coordinate generation state) and a texture image unit
    (consisting of all the texture state defined in Section 3.8).  In
    implementations with a different number of supported texture 
    coordinate sets and texture image units, some texture units may
    consist of only one of the two sub-units.
    The active texture unit selector specifies the texture coordinate 
    set accessed by commands involving texture coordinate processing.  
    Such commands include those accessing the current matrix stack (if 
    MATRIX_MODE is TEXTURE), TexGen (section 2.10.4), Enable/Disable (if 
    any texture coordinate generation enum is selected), as well as 
    queries of the current texture coordinates and current raster 
    texture coordinates.  If the texture coordinate set number 
    corresponding to the current value of ACTIVE_TEXTURE is greater than 
    or equal to the implementation-dependent constant 
    MAX_TEXTURE_COORDS_ARB, the error INVALID_OPERATION is generated by 
    any such command.
    The active texture unit selector also selects the texture image unit
    accessed by commands involving texture image processing (section 
    3.8).  Such commands include all variants of TexEnv, TexParameter,
    and TexImage commands, BindTexture, Enable/Disable for any texture
    target (e.g., TEXTURE_2D), and queries of all such state.  If the
    texture image unit number corresponding to the current value of 
    ACTIVE_TEXTURE is greater than or equal to the implementation-
    dependent constant MAX_TEXTURE_IMAGE_UNITS_ARB, the error
    INVALID_OPERATION is generated by any such command.
    ActiveTexture generates the error INVALID_ENUM if an invalid 
    <texture> is specified.  <texture> is a symbolic constant of the 
    form TEXTUREi, indicating that texture unit i is to be modified.  
    The constants obey TEXTUREi = TEXTURE0 + i (i is in the range 0 to
    k-1, where k is the larger of the MAX_TEXTURE_COORDS_ARB and
    MAX_TEXTURE_IMAGE_UNITS_ARB).  For compatibility with old OpenGL
    specifications, the implementation-dependent constant 
    MAX_TEXTURE_UNITS specifies the number of conventional texture units 
    supported by the implementation.  Its value must be no larger than 
    the minimum of MAX_TEXTURE_COORDS_ARB and 
    MAX_TEXTURE_IMAGE_UNITS_ARB.
    
    (modify last paragraph, p. 35) The state required to implement
    transformations consists of a <n>-value integer indicating the 
    current matrix mode (where <n> is 4 + the number of supported 
    texture and program matrices), a stack of at least two 4x4 matrices 
    for each of COLOR, PROJECTION, and TEXTURE with associated stack 
    pointers, <n> stacks (where <n> is at least 8) of at least one 4x4 
    matrix for each MATRIX<i>_ARB with associated stack pointers, and a 
    stack of at least 32 4x4 matrices with an associated stack pointer 
    for MODELVIEW.  Initially, there is only one matrix on each stack, 
    and all matrices are set to the identity.  The initial matrix mode 
    is MODELVIEW.  The initial value of ACTIVE_TEXTURE is TEXTURE0.
Additions to Chapter 3 of the OpenGL 1.3 Specification (Rasterization)
    Modify Chapter 3, Introduction (p. 58)
    (modify first paragraph, p. 58) ... Figure 3.1 diagrams the 
    rasterization process.  The color value assigned to a fragment is
    initially determined by the rasterization operations (sections 3.3
    through 3.7) and modified by either the execution of the texturing,
    color sum, and fog operations as defined in sections 3.8, 3.9, and
    3.10, or of a fragment program defined in section 3.11.  The final
    depth value is initially determined by the rasterization operations
    and may be modified or replaced by a fragment program.
    (modify Figure 3.1)
                 _ +---------------+   FRAGMENT_PROGRAM_ARB
                 /||    Point      |          enable
                /  | Rasterization |\           |
               /   +---------------+ \          V  o-------------+
       From   /    +---------------+  \                          |
    Primitive ---> |     Line      |---+++--->o    o             |
     Assembly \    | Rasterization |  / ||         |             |
               \   +---------------+ /  ||         |             |
                \  +---------------+/   ||   +-----+-----+  +----+-----+
                 \||    Polygon    |    ||   | Texturing |  | Fragment |
                 - | Rasterization |   / |   +-----+-----+  | Program  |
                   +---------------+  /  |         |        +----+-----+
                   +---------------+ /   |   +-----+-----+       |
                   |     Pixel     |/    |   | Color Sum |       |
    DrawPixels --> |   Rectangle   |    /    +-----+-----+       |
                   | Rasterization |   /           |             V
                   +---------------+  /      +-----+-----+
                   +---------------+ /       |    Fog    |---> Fragments
      Bitmap ----> |    Bitmap     |/        +-----------+
                   | Rasterization |
                   +---------------+
    Modify Section 3.3, Points (p. 63)
    (modify first and second paragraphs, p. 64) All fragments produced
    in rasterizing a non-antialiased point are assigned the same
    associated data, which are those of the vertex corresponding to the
    point.  (delete reference to divide by q)
    If antialiasing is enabled, then ...  The data associated with each 
    fragment are otherwise the data associated with the point being 
    rasterized.  (delete reference to divide by q)
    
    Modify Section 3.4.1, Basic Line Segment Rasterization (p. 66)
    (modify first paragraph, p. 68) ... (Note that t=0 at p_a and t=1 at
    p_b).  The value of an associated datum f from the fragment center,
    whether it be R, G, B, or A (in RGBA mode) or a color index (in 
    color index mode) or the s, t, r, or q texture coordinate or the 
    clip w coordinate (the depth value, window z, must be found using
    equation 3.3, below), is found as
      f = (1-t)*(f_a/w_a) + t*(f_b/w_b)                     (3.2)
          -----------------------------
            (1-t)*(1/w_a) + t*(1/w_b)
    where f_a and f_b are the data associated with the starting and 
    ending endpoints of the segment, respectively; w_a and w_b are the 
    clip w coordinates of the starting and ending endpoints of the 
    segments, respectively.  Note that linear interpolation would use
      f = (1-t)*f_a + t*f_b.                                (3.3)
    ... A GL implementation may choose to approximate equation 3.2 with
    3.3, but this will normally lead to inacceptable distortion effects
    when interpolating texture coordinates or clip w coordinates.
    Modify Section 3.5.1, Basic Polygon Rasterization (p. 73)
    (modify third and fourth paragraphs, p. 74) Denote a datum at p_a, 
    p_b, or p_c as f_a, f_b, or f_c, respectively.  Then the value f of 
    a datum at a fragment produced by rasterizing a triangle is given by
      f = a*(f_a/w_a) + b*(f_b/w_b) + c*(f_c/w_c)           (3.4)
          ---------------------------------------
             a*(1/w_a) + b*(1/w_b) + c*(1/w_c)
    where w_a, w_b, and w_c are the clip w coordinates of p_a, p_b, and
    p_c, respectively.  a, b, and c are the barycentric coordinates of
    the fragment for which the data are produced.  a, b, and c must
    correspond precisely to the ... at the fragment's center.
    Just as with line segment rasterization, equation 3.4 may be
    approximated by
      f = a*f_a + b*f_b + c*f_c;
    this may yield ... for texture coordinates or clip w coordinates.
    Modify Section 3.6.4, Rasterization of Pixel Rectangles (p. 91)
    (modify third paragraph, p. 103) A fragment arising from a group ...
    the color and texture coordinates are given by those associated with 
    the current raster position.  (delete reference to divide by q)
    Groups arising from DrawPixels...  
    Modify Section 3.7, Bitmaps (p. 113)
    (modify third paragraph, p. 114) Otherwise, a rectangular array ...
    The associated data for each fragment are those associated with the
    current raster position.  (delete reference to divide by q)  Once
    the fragments have been produced ...
    Modify Section 3.8, Texturing (p. 115)
    (add new paragraphs before first paragraph, p. 115) Texture 
    coordinate sets are mapped to RGBA colors for application to 
    primitives in one of two modes.  The first mode, described in this 
    and subsequent sections, is GL's conventional multitexture pipeline, 
    describing texture environment and texture application.  The second 
    mode, referred to as fragment program mode and described in section 
    3.11, applies textures, color sum, and fog as specified in an 
    application-supplied fragment program.
    The fragment program mode is enabled and disabled using the generic 
    Enable and Disable commands, respectively, with the symbolic 
    constant FRAGMENT_PROGRAM_ARB.  The required state is one bit 
    indicating whether the fragment program mode is enabled or disabled.
    In the initial state, the fragment program mode is disabled.  When
    fragment program mode is enabled, texturing, color sum, and fog
    application stages are ignored and a general purpose program is
    executed instead.
    (modify first and second paragraph, p. 115) Conventional texturing 
    is employed when fragment program mode is disabled.  Texturing maps 
    ... color of an image at the location indicated by a fragment's 
    texture coordinates to modify the fragment's primary RGBA color.  
    Texturing does not affect the secondary color.
    An implementation may support texturing using more than one image at
    a time.  In this case the fragment carries multiple sets of texture
    coordinates which are used to index ... 
    (add paragraph before 1st paragraph, p. 116) Except when in fragment
    program mode (section 3.11), the (s,t,r) texture coordinates used
    for texturing are the values s/q, t/q, and r/q, respectively, where
    s, t, r, and q are the texture coordinates associated with the
    fragment.  When in fragment program mode, the (s,t,r) texture
    coordinates are specified by the program.  If q is less than or
    equal to zero, the results of texturing are undefined.
    Modify Section 3.8.7, Texture Minification (p. 135)
    (add new paragraph after first paragraph, p. 137) When fragment 
    program mode is enabled, the derivatives of the coordinates may be 
    ill-defined or non-existent.  As a result, the implementation is 
    free to approximate these derivatives with such techniques as 
    differencing.  The only requirement is that texture samples be 
    equivalent across the two modes.  In other words, the texture sample 
    chosen for a fragment of a primitive must be invariant between 
    fragment program mode and conventional mode subject to the rules
    set forth in Appendix A, Invariance.
    Modify Section 3.8.13, Texture Application (p. 149)
    (modify fourth paragraph, p. 152) Texturing is enabled and disabled
    individually for each texture unit.  If texturing is disabled for
    one of the units, then the fragment resulting from the previous unit
    is passed unaltered to the following unit.  Individual texture units
    beyond those specified by MAX_TEXTURE_UNITS may be incomplete and 
    are always treated as disabled.
    Insert a new Section 3.11, (p. 154), between existing sections 3.10 
    and 3.11.  Renumber 3.11, Antialiasing Application, to 3.12.
    3.11  Fragment Programs
    The conventional GL texturing model described in section 3.8 is a
    configurable but essentially hard-wired sequence of per-fragment
    computations based on a canonical set of per-fragment parameters
    and texturing-related state such as texture images, texture 
    parameters, and texture environment parameters.  The general success 
    and utility of the conventional GL texturing model reflects its 
    basic correspondence to the typical texturing requirements of 3D 
    applications.
    However when the conventional GL texturing model is not sufficient, 
    the fragment program mode provides a substantially more flexible
    model for generating fragment colors.  The fragment program mode 
    permits applications to define their own fragment programs.
    A fragment program is a character string that specifies a sequence 
    of operations to perform.  Fragment program instructions are 
    typically 4-component vector operations that operate on per-fragment 
    attributes and program parameters.  Fragment programs execute on a 
    per-fragment basis and operate on each fragment completely 
    independently from any other fragments.  Fragment programs execute a 
    finite fixed sequence of instructions with no branching or looping.  
    Fragment programs execute without data hazards so results computed 
    in one instruction can be used immediately afterwards.  The result 
    of a fragment program is a set of fragment result registers that 
    becomes the color used by antialiasing application and/or a depth 
    value used in place of the interpolated depth value generated by 
    conventional rasterization.
    In fragment program mode, the color sum is subsumed by the fragment
    program.  An application desiring the primary and secondary colors
    to be summed must explicitly include this operation in its program.
    Fragment programs are defined to operate only in RGBA mode.  The 
    results of fragment program execution are undefined if the GL is in 
    color index mode.
    3.11.1  Program Objects
    The GL provides one or more program targets, each identifying a 
    portion of the GL that can be controlled through application-
    specified programs.  The program target for fragment programs is 
    FRAGMENT_PROGRAM_ARB.  Each program target has an associated program 
    object, called the current program object.  Each program target also 
    has a default program object, which is initially the current program 
    object.
    Each program object has an associated program string.  The command
      ProgramStringARB(enum target, enum format, sizei len, 
                       const void *string);
    updates the program string for the current program object for 
    <target>.  <format> describes the format of the program string, 
    which must currently be PROGRAM_FORMAT_ASCII_ARB.  <string> is a 
    pointer to the array of bytes representing the program string being 
    loaded, which need not be null-terminated.  The length of the array 
    is given by <len>.  If <string> is null-terminated, <len> should not 
    include the terminator.
    When a program string is loaded, it is interpreted according to 
    syntactic and semantic rules corresponding to the program target 
    specified by <target>.  If a program violates the syntactic or 
    semantic restrictions of the program target, ProgramStringARB 
    generates the error INVALID_OPERATION.  An implementation may also
    generate the error INVALID_OPERATION if the program would exceed
    the native resource limits defined in section 6.1.12.  A program
    which fails to load due to exceeding native resource limits must
    always fail, regardless of any other GL state.
    Additionally, ProgramString will update the program error position
    (PROGRAM_ERROR_POSITION_ARB) and error string 
    (PROGRAM_ERROR_STRING_ARB).  If a program fails to load, the value 
    of the program error position is set to the ubyte offset into the 
    specified program string indicating where the first program error 
    was detected.  If the program fails to load because of a semantic 
    restriction that is not detected until the program is fully 
    scanned, the error position is set to the value of <len>.  If a
    program loads successfully, the error position is set to the value
    negative one.  The implementation-dependent program error string 
    contains one or more error or warning messages.  If a program loads 
    succesfully, the error string may either contain warning messages or 
    be empty.
    Each program object has an associated array of program local 
    parameters.  The number and type of program local parameters is 
    target- and implementation-dependent.  For fragment programs, 
    program local parameters are four-component floating-point vectors.  
    The number of vectors is given by the implementation-dependent 
    constant MAX_PROGRAM_LOCAL_PARAMETERS_ARB, which must be at least 
    24.  The commands
      void ProgramLocalParameter4fARB(enum target, uint index,
                                      float x, float y, float z, float w);
      void ProgramLocalParameter4fvARB(enum target, uint index, 
                                       const float *params);
      void ProgramLocalParameter4dARB(enum target, uint index,
                                      double x, double y, double z, double w);
      void ProgramLocalParameter4dvARB(enum target, uint index, 
                                       const double *params);
    update the values of the program local parameter numbered <index>
    belonging to the program object currently bound to <target>.  For
    ProgramLocalParameter4fARB and ProgramLocalParameter4dARB, the four
    components of the parameter are updated with the values of <x>, <y>, 
    <z>, and <w>, respectively.  For ProgramLocalParameter4fvARB and
    ProgramLocalParameter4dvARB, the four components of the parameter 
    are updated with the array of four values pointed to by <params>.  
    The error INVALID_VALUE is generated if <index> is greater than or 
    equal to the number of program local parameters supported by 
    <target>.
    Additionally, each program target has an associated array of program 
    environment parameters.  Unlike program local parameters, program
    environment parameters are shared by all program objects of a given 
    target.  The number and type of program environment parameters is 
    target- and implementation-dependent.  For fragment programs, 
    program environment parameters are four-component floating-point 
    vectors.  The number of vectors is given by the implementation-
    dependent constant MAX_PROGRAM_ENV_PARAMETERS_ARB, which must be at 
    least 24.  The commands
      void ProgramEnvParameter4fARB(enum target, uint index,
                                    float x, float y, float z, float w);
      void ProgramEnvParameter4fvARB(enum target, uint index,
                                     const float *params);
      void ProgramEnvParameter4dARB(enum target, uint index,
                                    double x, double y, double z, double w);
      void ProgramEnvParameter4dvARB(enum target, uint index,
                                     const double *params);
    update the values of the program environment parameter numbered 
    <index> for the given program target <target>.  For 
    ProgramEnvParameter4fARB and ProgramEnvParameter4dARB, the four 
    components of the parameter are updated with the values of <x>, <y>, 
    <z>, and <w>, respectively.  For ProgramEnvParameter4fvARB and 
    ProgramEnvParameter4dvARB, the four components of the parameter are 
    updated with the array of four values pointed to by <params>.  The 
    error INVALID_VALUE is generated if <index> is greater than or equal 
    to the number of program environment parameters supported by 
    <target>.
    Each program target has a default program object.  Additionally, 
    named program objects can be created and operated upon.  The name 
    space for program objects is the positive integers and is shared by 
    programs of all targets.  The name zero is reserved by the GL.
    A named program object is created by binding an unused program 
    object name to a valid program target.  The binding is effected by 
    calling 
      BindProgramARB(enum target, uint program);
    with <target> set to the desired program target and <program> set to 
    the unused program name.  The resulting program object has a program 
    target given by <target> and is assigned target-specific default 
    values (see section 3.11.8 for fragment programs).  BindProgramARB 
    may also be used to bind an existing program object to a program 
    target.  If <program> is zero, the default program object for 
    <target> is bound.  If <program> is the name of an existing program
    object whose associated program target is <target>, the named 
    program object is bound.  The error INVALID_OPERATION is generated 
    if <program> names an existing program object whose associated 
    program target is anything other than <target>.
    Programs objects are deleted by calling
      void DeleteProgramsARB(sizei n, const uint *programs);
    <programs> contains <n> names of programs to be deleted.  After a 
    program object is deleted, its name is again unused.  If a program 
    object that is bound to any target is deleted, it is as though 
    BindProgramARB is first executed with same target and a <program> of 
    zero.  Unused names in <programs> are silently ignored, as is the 
    value zero.
    The command
      void GenProgramsARB(sizei n, uint *programs);
    returns <n> currently unused program names in <programs>.  These 
    names are marked as used, for the purposes of GenProgramsARB only, 
    but objects are created only when they are first bound using 
    BindProgramARB.
    3.11.2  Fragment Program Grammar and Semantic Restrictions
    Fragment program strings are specified as an array of ASCII 
    characters containing the program text.  When a fragment program is 
    loaded by a call to ProgramStringARB, the program string is parsed 
    into a set of tokens possibly separated by whitespace.  Spaces, 
    tabs, newlines, carriage returns, and comments are considered 
    whitespace.  Comments begin with the character "#" and are 
    terminated by a newline, a carriage return, or the end of the 
    program array.
    The Backus-Naur Form (BNF) grammar below specifies the syntactically 
    valid sequences for fragment programs.  The set of valid tokens can 
    be inferred from the grammar.  The token "" represents an empty 
    string and is used to indicate optional rules.  A program is invalid 
    if it contains any undefined tokens or characters.
    A fragment program is required to begin with the header string 
    "!!ARBfp1.0", without any preceding whitespace.  This string 
    identifies the subsequent program text as a fragment program 
    (version 1.0) that should be parsed according to the following 
    grammar and semantic rules.  Program string parsing begins with the 
    character immediately following the header string.
    <program>              ::= <optionSequence> <statementSequence> "END"
    <optionSequence>       ::= <optionSequence> <option>
                             | ""
    <option>               ::= "OPTION" <identifier> ";"
    <statementSequence>    ::= <statementSequence> <statement>
                             | ""
    <statement>            ::= <instruction> ";"
                             | <namingStatement> ";"
    <instruction>          ::= <ALUInstruction>
                             | <TexInstruction>
    <ALUInstruction>       ::= <VECTORop_instruction>
                             | <SCALARop_instruction>
                             | <BINSCop_instruction>
                             | <BINop_instruction>
                             | <TRIop_instruction>
                             | <SWZ_instruction>
    <TexInstruction>       ::= <SAMPLE_instruction>
                             | <KIL_instruction>
    <VECTORop_instruction> ::= <VECTORop> <maskedDstReg> "," 
                               <vectorSrcReg>
    <VECTORop>             ::= "ABS" | "ABS_SAT"
                             | "FLR" | "FLR_SAT"
                             | "FRC" | "FRC_SAT"
                             | "LIT" | "LIT_SAT"
                             | "MOV" | "MOV_SAT"
    <SCALARop_instruction> ::= <SCALARop> <maskedDstReg> ","  
                               <scalarSrcReg> 
 
    <SCALARop>             ::= "COS" | "COS_SAT"
                             | "EX2" | "EX2_SAT"
                             | "LG2" | "LG2_SAT"
                             | "RCP" | "RCP_SAT"
                             | "RSQ" | "RSQ_SAT"
                             | "SIN" | "SIN_SAT"
                             | "SCS" | "SCS_SAT"
 
    <BINSCop_instruction>  ::= <BINSCop> <maskedDstReg> "," 
                               <scalarSrcReg> "," <scalarSrcReg> 
    <BINSCop>              ::= "POW" | "POW_SAT"
 
    <BINop_instruction>    ::= <BINop> <maskedDstReg> ","
                               <vectorSrcReg> "," <vectorSrcReg>
    <BINop>                ::= "ADD" | "ADD_SAT"
                             | "DP3" | "DP3_SAT"
                             | "DP4" | "DP4_SAT"
                             | "DPH" | "DPH_SAT"
                             | "DST" | "DST_SAT"
                             | "MAX" | "MAX_SAT"
                             | "MIN" | "MIN_SAT"
                             | "MUL" | "MUL_SAT"
                             | "SGE" | "SGE_SAT"
                             | "SLT" | "SLT_SAT"
                             | "SUB" | "SUB_SAT"
                             | "XPD" | "XPD_SAT"
    <TRIop_instruction>    ::= <TRIop> <maskedDstReg> ","
                               <vectorSrcReg> "," <vectorSrcReg> ","
                               <vectorSrcReg>
    <TRIop>                ::= "CMP" | "CMP_SAT"
                             | "LRP" | "LRP_SAT"
                             | "MAD" | "MAD_SAT"
    <SWZ_instruction>      ::= <SWZop> <maskedDstReg> "," 
                               <srcReg> "," <extendedSwizzle>
    <SWZop>                ::= "SWZ" | "SWZ_SAT"
    <SAMPLE_instruction>   ::= <SAMPLEop> <maskedDstReg> ","
                               <vectorSrcReg> "," <texImageUnit> "," 
                               <texTarget>
    <SAMPLEop>             ::= "TEX" | "TEX_SAT"
                             | "TXP" | "TXP_SAT"
                             | "TXB" | "TXB_SAT"
    <KIL_instruction>      ::= "KIL" <vectorSrcReg>
    <texImageUnit>         ::= "texture" <optTexImageUnitNum>
    <texTarget>            ::= "1D"
                             | "2D"
                             | "3D"
                             | "CUBE"
                             | "RECT"
    <optTexImageUnitNum>   ::= ""
                             | "[" <texImageUnitNum> "]"
    <texImageUnitNum>      ::= <integer> from 0 to 
                               MAX_TEXTURE_IMAGE_UNITS_ARB-1
    <scalarSrcReg>         ::= <optionalSign> <srcReg> <scalarSuffix>
    <vectorSrcReg>         ::= <optionalSign> <srcReg> <optionalSuffix> 
 
    <maskedDstReg>         ::= <dstReg> <optionalMask>
    <extendedSwizzle>      ::= <xyzwExtendedSwizzle>
                             | <rgbaExtendedSwizzle>
    <xyzwExtendedSwizzle>  ::= <xyzwExtSwizComp> "," <xyzwExtSwizComp> "," 
                               <xyzwExtSwizComp> "," <xyzwExtSwizComp>
    <rgbaExtendedSwizzle>  ::= <rgbaExtSwizComp> "," <rgbaExtSwizComp> "," 
                               <rgbaExtSwizComp> "," <rgbaExtSwizComp>
    <xyzwExtSwizComp>      ::= <optionalSign> <xyzwExtSwizSel>
    <rgbaExtSwizComp>      ::= <optionalSign> <rgbaExtSwizSel>
    <xyzwExtSwizSel>       ::= "0" 
                             | "1" 
                             | <xyzwComponent>
    <rgbaExtSwizSel>       ::= "0" 
                             | "1" 
                             | <rgbaComponent>
    <srcReg>               ::= <fragmentAttribReg>
                             | <temporaryReg>
                             | <progParamReg>
    <dstReg>               ::= <temporaryReg>
                             | <fragmentResultReg>
    <fragmentAttribReg>    ::= <establishedName>
                             | <fragAttribBinding>
    <temporaryReg>         ::= <establishedName>
    <progParamReg>         ::= <progParamSingle>
                             | <progParamArray> "[" <progParamArrayAbs> "]"
                             | <paramSingleItemUse>
    <progParamSingle>      ::= <establishedName>
    <progParamArray>       ::= <establishedName>
    <progParamArrayAbs>    ::= <integer>
    <fragmentResultReg>    ::= <establishedName>
                             | <resultBinding>
    <scalarSuffix>         ::= "." <component>
    <optionalSuffix>       ::= "" 
                             | "." <component> 
                             | "." <xyzwComponent> <xyzwComponent>
                                   <xyzwComponent> <xyzwComponent>
                             | "." <rgbaComponent> <rgbaComponent>
                                   <rgbaComponent> <rgbaComponent>
 
    <component>            ::= <xyzwComponent>
                             | <rgbaComponent>
    <xyzwComponent>        ::= "x" | "y" | "z" | "w"
    <rgbaComponent>        ::= "r" | "g" | "b" | "a"
    <optionalMask>         ::= ""
                             | <xyzwMask>
                             | <rgbaMask>
    <xyzwMask>             ::= "." "x"
                             | "." "y"
                             | "." "xy"
                             | "." "z"
                             | "." "xz"
                             | "." "yz"
                             | "." "xyz"
                             | "." "w"
                             | "." "xw"
                             | "." "yw"
                             | "." "xyw"
                             | "." "zw"
                             | "." "xzw"
                             | "." "yzw"
                             | "." "xyzw"
    <rgbaMask>             ::= "." "r"
                             | "." "g"
                             | "." "rg"
                             | "." "b"
                             | "." "rb"
                             | "." "gb"
                             | "." "rgb"
                             | "." "a"
                             | "." "ra"
                             | "." "ga"
                             | "." "rga"
                             | "." "ba"
                             | "." "rba"
                             | "." "gba"
                             | "." "rgba"
    <namingStatement>      ::= <ATTRIB_statement>
                             | <PARAM_statement>
                             | <TEMP_statement>
                             | <OUTPUT_statement>
                             | <ALIAS_statement>
    <ATTRIB_statement>     ::= "ATTRIB" <establishName> "="
                                 <fragAttribBinding>
    <fragAttribBinding>    ::= "fragment" "." <fragAttribItem>
    <fragAttribItem>       ::= "color" <optColorType>
                             | "texcoord" <optTexCoordNum>
                             | "fogcoord"
                             | "position"
    <PARAM_statement>      ::= <PARAM_singleStmt>
                             | <PARAM_multipleStmt>
    <PARAM_singleStmt>     ::= "PARAM" <establishName> <paramSingleInit>
    <PARAM_multipleStmt>   ::= "PARAM" <establishName> "[" <optArraySize> "]"
                                   <paramMultipleInit>
    <optArraySize>         ::= ""
                             | <integer> from 1 to MAX_PROGRAM_PARAMETERS_ARB
                                 (maximum number of allowed program 
                                  parameter bindings)
    <paramSingleInit>      ::= "=" <paramSingleItemDecl>
    <paramMultipleInit>    ::= "=" "{" <paramMultInitList> "}"
    <paramMultInitList>    ::= <paramMultipleItem>
                             | <paramMultipleItem> "," <paramMultInitList>
    <paramSingleItemDecl>  ::= <stateSingleItem>
                             | <programSingleItem>
                             | <paramConstDecl>
    <paramSingleItemUse>   ::= <stateSingleItem>
                             | <programSingleItem>
                             | <paramConstUse>
    <paramMultipleItem>    ::= <stateMultipleItem>
                             | <programMultipleItem>
                             | <paramConstDecl>
    <stateMultipleItem>    ::= <stateSingleItem>
                             | "state" "." <stateMatrixRows>
    <stateSingleItem>      ::= "state" "." <stateMaterialItem>
                             | "state" "." <stateLightItem>
                             | "state" "." <stateLightModelItem>
                             | "state" "." <stateLightProdItem>
                             | "state" "." <stateTexEnvItem>
                             | "state" "." <stateFogItem>
                             | "state" "." <stateDepthItem>
                             | "state" "." <stateMatrixRow>
    <stateMaterialItem>    ::= "material" <optFaceType> "." <stateMatProperty>
    <stateMatProperty>     ::= "ambient"
                             | "diffuse"
                             | "specular"
                             | "emission"
                             | "shininess"
    <stateLightItem>       ::= "light" "[" <stateLightNumber> "]" "." 
                                 <stateLightProperty>
    <stateLightProperty>   ::= "ambient"
                             | "diffuse" 
                             | "specular"
                             | "position"
                             | "attenuation"
                             | "spot" "." <stateSpotProperty>
                             | "half"
    <stateSpotProperty>    ::= "direction" 
    <stateLightModelItem>  ::= "lightmodel" <stateLModProperty>
    <stateLModProperty>    ::= "." "ambient"
                             | <optFaceType> "." "scenecolor"
    <stateLightProdItem>   ::= "lightprod" "[" <stateLightNumber> "]"
                                 <optFaceType> "." <stateLProdProperty>
    <stateLProdProperty>   ::= "ambient"
                             | "diffuse"
                             | "specular"
    <stateLightNumber>     ::= <integer> from 0 to MAX_LIGHTS-1
    <stateTexEnvItem>      ::= "texenv" <optLegacyTexUnitNum> "." 
                                 <stateTexEnvProperty>
    <stateTexEnvProperty>  ::= "color"
    <optLegacyTexUnitNum>  ::= ""
                             | "[" <legacyTexUnitNum> "]"
    <legacyTexUnitNum>     ::= <integer> from 0 to MAX_TEXTURE_UNITS-1
    <stateFogItem>         ::= "fog" "." <stateFogProperty>
    <stateFogProperty>     ::= "color" 
                             | "params" 
    <stateDepthItem>       ::= "depth" "." <stateDepthProperty>
    <stateDepthProperty>   ::= "range" 
    <stateMatrixRow>       ::= <stateMatrixItem> "." "row" "[" 
                                  <stateMatrixRowNum> "]"
    <stateMatrixRows>      ::= <stateMatrixItem> <optMatrixRows>
    <optMatrixRows>        ::= ""
                             | "." "row" "[" <stateMatrixRowNum> ".." 
                                  <stateMatrixRowNum> "]"
    <stateMatrixItem>      ::= "matrix" "." <stateMatrixName> 
                               <stateOptMatModifier>
    <stateOptMatModifier>  ::= ""
                             | "." <stateMatModifier>
    <stateMatModifier>     ::= "inverse" 
                             | "transpose" 
                             | "invtrans"
    <stateMatrixRowNum>    ::= <integer> from 0 to 3
    <stateMatrixName>      ::= "modelview" <stateOptModMatNum>
                             | "projection"
                             | "mvp"
                             | "texture" <optTexCoordNum>
                             | "palette" "[" <statePaletteMatNum> "]"
                             | "program" "[" <stateProgramMatNum> "]"
                             
    <stateOptModMatNum>    ::= ""
                             | "[" <stateModMatNum> "]"
    <stateModMatNum>       ::= <integer> from 0 to MAX_VERTEX_UNITS_ARB-1
    <optTexCoordNum>       ::= ""
                             | "[" <texCoordNum> "]"
    <texCoordNum>          ::= <integer> from 0 to MAX_TEXTURE_COORDS_ARB-1
    <statePaletteMatNum>   ::= <integer> from 0 to MAX_PALETTE_MATRICES_ARB-1
    <stateProgramMatNum>   ::= <integer> from 0 to MAX_PROGRAM_MATRICES_ARB-1
    <programSingleItem>    ::= <progEnvParam>
                             | <progLocalParam>
    <programMultipleItem>  ::= <progEnvParams>
                             | <progLocalParams>
    <progEnvParams>        ::= "program" "." "env" 
                                 "[" <progEnvParamNums> "]"
    <progEnvParamNums>     ::= <progEnvParamNum>
                             | <progEnvParamNum> ".." <progEnvParamNum>
    <progEnvParam>         ::= "program" "." "env" 
                                 "[" <progEnvParamNum> "]"
    <progLocalParams>      ::= "program" "." "local" 
                                 "[" <progLocalParamNums> "]"
    <progLocalParamNums>   ::= <progLocalParamNum>
                             | <progLocalParamNum> ".." <progLocalParamNum>
    <progLocalParam>       ::= "program" "." "local" 
                                 "[" <progLocalParamNum> "]"
    <progEnvParamNum>      ::= <integer> from 0 to
                               MAX_PROGRAM_ENV_PARAMETERS_ARB - 1 
    <progLocalParamNum>    ::= <integer> from 0 to
                               MAX_PROGRAM_LOCAL_PARAMETERS_ARB - 1 
    <paramConstDecl>       ::= <paramConstScalarDecl>
                             | <paramConstVector>
    <paramConstUse>        ::= <paramConstScalarUse>
                             | <paramConstVector>
    <paramConstScalarDecl> ::= <signedFloatConstant>
    <paramConstScalarUse>  ::= <floatConstant>
    <paramConstVector>     ::= "{" <signedFloatConstant> "}"
                             | "{" <signedFloatConstant> "," 
                                   <signedFloatConstant> "}"
                             | "{" <signedFloatConstant> "," 
                                   <signedFloatConstant> ","
                                   <signedFloatConstant> "}"
                             | "{" <signedFloatConstant> "," 
                                   <signedFloatConstant> ","
                                   <signedFloatConstant> "," 
                                   <signedFloatConstant> "}"
    <signedFloatConstant>  ::= <optionalSign> <floatConstant>
    <floatConstant>        ::= see text
    <optionalSign>         ::= ""
                             | "-"
                             | "+"
    <TEMP_statement>       ::= "TEMP" <varNameList>
    <varNameList>          ::= <establishName>
                             | <establishName> "," <varNameList>
    <OUTPUT_statement>     ::= "OUTPUT" <establishName> "="
                                 <resultBinding>
    <resultBinding>        ::= "result" "." "color"
                             | "result" "." "depth"
    <optFaceType>          ::= ""
                             | "." "front"
                             | "." "back"
    <optColorType>         ::= ""
                             | "." "primary"
                             | "." "secondary"
    <ALIAS_statement>      ::= "ALIAS" <establishName> "="
                                 <establishedName>
    <establishName>        ::= <identifier>
    <establishedName>      ::= <identifier>
    <identifier>           ::= see text
    The <integer> rule matches an integer constant.  The integer 
    consists of a sequence of one or more digits ("0" through "9").
    The <floatConstant> rule matches a floating-point constant 
    consisting of an integer part, a decimal point, a fraction part, an 
    "e" or "E", and an optionally signed integer exponent.  The integer 
    and fraction parts both consist of a sequence of one or more digits 
    ("0" through "9").  Either the integer part or the fraction parts 
    (not both) may be missing; either the decimal point or the "e" (or 
    "E") and the exponent (not both) may be missing.
    The <identifier> rule matches a sequence of one or more letters ("A"
    through "Z", "a" through "z"), digits ("0" through "9), underscores 
    ("_"), or dollar signs ("$"); the first character must not be a 
    number.  Upper and lower case letters are considered different 
    (names are case-sensitive).  The following strings are reserved 
    keywords and may not be used as identifiers:
        ABS, ABS_SAT, ADD, ADD_SAT, ALIAS, ATTRIB, CMP, CMP_SAT, COS,
        COS_SAT, DP3, DP3_SAT, DP4, DP4_SAT, DPH, DPH_SAT, DST, DST_SAT, 
        END, EX2, EX2_SAT, FLR, FLR_SAT, FRC, FRC_SAT, KIL, LG2, 
        LG2_SAT, LIT, LIT_SAT, LRP, LRP_SAT, MAD, MAD_SAT, MAX, MAX_SAT, 
        MIN, MIN_SAT, MOV, MOV_SAT, MUL, MUL_SAT, OPTION, OUTPUT, PARAM, 
        POW, POW_SAT, RCP, RCP_SAT, RSQ, RSQ_SAT, SIN, SIN_SAT, SCS, 
        SCS_SAT, SGE, SGE_SAT, SLT, SLT_SAT, SUB, SUB_SAT, SWZ, SWZ_SAT, 
        TEMP, TEX, TEX_SAT, TXB, TXB_SAT, TXP, TXP_SAT, XPD, XPD_SAT, 
        fragment, program, result, state, and texture.
    The error INVALID_OPERATION is generated if a fragment program fails 
    to load because it is not syntactically correct or for one of the 
    semantic restrictions described in the following sections.
    A successfully loaded fragment program is parsed into a sequence of
    instructions.  Each instruction is identified by its tokenized name.  
    The operation of these instructions when executed is defined in 
    section 3.11.5.
    A successfully loaded program string replaces the program string
    previously loaded into the specified program object.  If the 
    OUT_OF_MEMORY error is generated by ProgramStringARB, no change is 
    made to the previous contents of the current program object.
    3.11.3  Fragment Program Variables
    Fragment programs may access a number of different variables during 
    their execution.  The following sections define the variables that 
    can be declared and used by a fragment program.
    Explicit variable declarations allow a fragment program to establish 
    a variable name that can be used to refer to a specified resource in 
    subsequent instructions.  A fragment program will fail to load if it 
    declares the same variable name more than once or if it refers to a
    variable name that has not been previously declared in the program 
    string.
    Implicit variable declarations allow a fragment program to use the 
    name of certain available resources by name.
    3.11.3.1  Fragment Attributes
    Fragment program attribute variables are a set of four-component
    floating-point vectors holding the attributes of the fragment being
    processed.  Fragment attribute variables are read-only during 
    fragment program execution.
    Fragment attribute variables can be declared explicitly using the
    <ATTRIB_statement> grammar rule, or implicitly using the
    <fragAttribBinding> grammar rule in an executable instruction.
    Each fragment attribute variable is bound to a single item of 
    fragment state according to the <fragAttrBinding> grammar rule.  The 
    set of GL state that can be bound to a fragment attribute variable 
    is given in Table X.1.  Fragment attribute variables are initialized 
    at each fragment program invocation with the current values of the 
    bound state.
      Fragment Attribute Binding  Components  Underlying State
      --------------------------  ----------  ----------------------------
      fragment.color              (r,g,b,a)   primary color
      fragment.color.primary      (r,g,b,a)   primary color
      fragment.color.secondary    (r,g,b,a)   secondary color
      fragment.texcoord           (s,t,r,q)   texture coordinate, unit 0
      fragment.texcoord[n]        (s,t,r,q)   texture coordinate, unit n
      fragment.fogcoord           (f,0,0,1)   fog distance/coordinate
      fragment.position           (x,y,z,1/w) window position
      Table X.1:  Fragment Attribute Bindings.  The "Components" column
      indicates the mapping of the state in the "Underlying State" 
      column.  Bindings containing "[n]" require an integer value of <n> 
      to select an individual item.
    If a fragment attribute binding matches "fragment.color" or
    "fragment.color.primary", the "x", "y", "z", and "w" components of 
    the fragment attribute variable are filled with the "r", "g", "b", 
    and "a" components, respectively, of the fragment color.  Each 
    fixed-point color component undergoes an implied conversion to
    floating point.  This conversion must leave the values 0 and 1
    invariant.
    If a fragment attribute binding matches "fragment.color.secondary", 
    the "x", "y", "z", and "w" components of the fragment attribute 
    variable are filled with the "r", "g", "b", and "a" components, 
    respectively, of the fragment secondary color.  Each fixed-point 
    color component undergoes an implied conversion to floating point.  
    This conversion must leave the values 0 and 1 invariant.
    If a fragment attribute binding matches "fragment.texcoord" or
    "fragment.texcoord[n]", the "x", "y", "z", and "w" components of the 
    fragment attribute variable are filled with the "s", "t", "r", and 
    "q" components, respectively, of the fragment texture coordinates 
    for texture unit <n>.  If "[n]" is omitted, texture unit zero is 
    used.
    If a fragment attribute binding matches "fragment.fogcoord", the "x" 
    component of the fragment attribute variable is filled with either
    the fragment eye distance or the fog coordinate, depending on 
    whether the fog source is set to FRAGMENT_DEPTH_EXT or 
    FOG_COORDINATE_EXT, respectively.  The "y", "z", and "w" coordinates 
    are filled with 0, 0, and 1, respectively.
    If a fragment attribute binding matches "fragment.position", the "x"
    and "y" components of the fragment attribute variable are filled 
    with the (x,y) window coordinates of the fragment center, relative 
    to the lower left corner of the window.  The "z" component is filled 
    with the fragment's z window coordinate.  This z window coordinate 
    undergoes an implied conversion to floating point.  This conversion 
    must leave the values 0 and 1 invariant.  The "w" component is 
    filled with the reciprocal of the fragment's clip w coordinate.
    On some implementations, the components of fragment.position may be
    generated by interpolating per-vertex position values.  This may
    produce x and y window coordinates that don't exactly match those of
    the fragment center and z window coordinates that do not exactly
    match those generated by fixed-function rasterization.  Therefore,
    there is no guaranteed invariance between the final z window
    coordinates of fragments processed by fragment programs that write
    depth values and fragments processed by any other means, even if the
    fragment programs in question simply copy the z value from the
    fragment.position binding.
    3.11.3.2  Fragment Program Parameters
    Fragment program parameter variables are a set of four-component
    floating-point vectors used as constants during fragment program 
    execution.  Fragment program parameters retain their values across 
    fragment program invocations, although their values can change 
    between invocations due to GL state changes.
    Single program parameter variables and arrays of program parameter
    variables can be declared explicitly using the <PARAM_statement> 
    grammar rule.  Single program parameter variables can also be 
    declared implicitly using the <paramSingleItemUse> grammar rule in 
    an executable instruction.
    Each single program parameter variable is bound to a constant vector 
    or to a GL state vector according to the <paramSingleInit> grammar 
    rule.  Individual items of a program parameter array are bound to 
    constant vectors or GL state vectors according to the 
    <programMultipleInit> grammar rule.  The set of GL state that can be 
    bound to program parameter variables are given in Tables X.2.1 
    through X.2.4.
    Constant Bindings
    A program parameter variable can be bound to a scalar or vector 
    constant using the <paramConstDecl> grammar rule (explicit 
    declarations) or the <paramConstUse> grammar rule (implicit 
    declarations).
    If a program parameter binding matches the <paramConstScalarDecl> or
    <paramConstScalarUse> grammar rules, the corresponding program 
    parameter variable is bound to the vector (X,X,X,X), where X is the 
    value of the specified constant.  Note that the 
    <paramConstScalarUse> grammar rule, used only in implicit 
    declarations, allows only non-negative constants.  This 
    disambiguates cases like "-2", which could conceivably be taken to 
    mean either the vector "(2,2,2,2)" with all components negated or 
    "(-2,-2,-2,-2)" without negation.  Only the former interpretation is 
    allowed by the grammar.
    If a program parameter binding matches <paramConstVector>, the
    corresponding program parameter variable is bound to the vector
    (X,Y,Z,W), where X, Y, Z, and W are the values corresponding to the 
    first, second, third, and fourth match of <signedFloatConstant>.  If 
    fewer than four constants are specified, Y, Z, and W assume the 
    values 0.0, 0.0, and 1.0, if their respective constants are not 
    specified.
    Program parameter variables initialized to constant values can never 
    be modified.
    Program Environment/Local Parameter Bindings
      Binding                        Components  Underlying State
      -----------------------------  ----------  ----------------------------
      program.env[a]                 (x,y,z,w)   program environment 
                                                 parameter a
      program.local[a]               (x,y,z,w)   program local parameter a
      program.env[a..b]              (x,y,z,w)   program environment
                                                 parameters a through b
      program.local[a..b]            (x,y,z,w)   program local parameters
                                                 a through b
      Table X.2.1:  Program Environment/Local Parameter Bindings.  <a> 
      and <b> indicate parameter numbers, where <a> must be less than or 
      equal to <b>.
    If a program parameter binding matches "program.env[a]" or
    "program.local[a]", the four components of the program parameter 
    variable are filled with the four components of program environment 
    parameter <a> or program local parameter <a>, respectively.
    Additionally, for program parameter array bindings, 
    "program.env[a..b]" and "program.local[a..b]" are equivalent to 
    specifying program environment parameters <a> through <b> in order 
    or program local parameters <a> through <b> in order, respectively.  
    In either case, a program will fail to load if <a> is greater than 
    <b>.
    Material Property Bindings
      Binding                        Components  Underlying State
      -----------------------------  ----------  ----------------------------
      state.material.ambient         (r,g,b,a)   front ambient material color
      state.material.diffuse         (r,g,b,a)   front diffuse material color
      state.material.specular        (r,g,b,a)   front specular material color
      state.material.emission        (r,g,b,a)   front emissive material color
      state.material.shininess       (s,0,0,1)   front material shininess
      state.material.front.ambient   (r,g,b,a)   front ambient material color
      state.material.front.diffuse   (r,g,b,a)   front diffuse material color
      state.material.front.specular  (r,g,b,a)   front specular material color
      state.material.front.emission  (r,g,b,a)   front emissive material color
      state.material.front.shininess (s,0,0,1)   front material shininess
      state.material.back.ambient    (r,g,b,a)   back ambient material color
      state.material.back.diffuse    (r,g,b,a)   back diffuse material color
      state.material.back.specular   (r,g,b,a)   back specular material color
      state.material.back.emission   (r,g,b,a)   back emissive material color
      state.material.back.shininess  (s,0,0,1)   back material shininess
      Table X.2.2:  Material Property Bindings.  If a material face is 
      not specified in the binding, the front property is used.
    If a program parameter binding matches any of the material 
    properties listed in Table X.2.2, the program parameter variable is 
    filled according to the table.  For ambient, diffuse, specular, or 
    emissive colors, the "x", "y", "z", and "w" components are filled 
    with the "r", "g", "b", and "a" components, respectively, of the 
    corresponding material color.  For material shininess, the "x" 
    component is filled with the material's specular exponent, and the 
    "y", "z", and "w" components are filled with 0, 0, and 1, 
    respectively.  Bindings containing ".back" refer to the back 
    material; all other bindings refer to the front material.
    Material properties can be changed inside a Begin/End pair, either
    directly by calling Material, or indirectly through color material.
    However, such property changes are not guaranteed to update program
    parameter bindings until the following End command.  Program 
    parameter variables bound to material properties changed inside a 
    Begin/End pair are undefined until the following End command.
    Light Property Bindings
      Binding                        Components  Underlying State
      -----------------------------  ----------  ----------------------------
      state.light[n].ambient         (r,g,b,a)   light n ambient color
      state.light[n].diffuse         (r,g,b,a)   light n diffuse color
      state.light[n].specular        (r,g,b,a)   light n specular color
      state.light[n].position        (x,y,z,w)   light n position
      state.light[n].attenuation     (a,b,c,e)   light n attenuation constants
                                                 and spot light exponent
      state.light[n].spot.direction  (x,y,z,c)   light n spot direction and
                                                 cutoff angle cosine
      state.light[n].half            (x,y,z,1)   light n infinite half-angle
      state.lightmodel.ambient       (r,g,b,a)   light model ambient color
      state.lightmodel.scenecolor    (r,g,b,a)   light model front scene color
      state.lightmodel      .        (r,g,b,a)   light model front scene color
               front.scenecolor
      state.lightmodel      .        (r,g,b,a)   light model back scene color
               back.scenecolor
      state.lightprod[n].ambient     (r,g,b,a)   light n / front material
                                                 ambient color product
      state.lightprod[n].diffuse     (r,g,b,a)   light n / front material
                                                 diffuse color product
      state.lightprod[n].specular    (r,g,b,a)   light n / front material
                                                 specular color product
      state.lightprod[n].            (r,g,b,a)   light n / front material
              front.ambient                      ambient color product
      state.lightprod[n].            (r,g,b,a)   light n / front material
              front.diffuse                      diffuse color product
      state.lightprod[n].            (r,g,b,a)   light n / front material
              front.specular                     specular color product
      state.lightprod[n].            (r,g,b,a)   light n / back material
              back.ambient                       ambient color product
      state.lightprod[n].            (r,g,b,a)   light n / back material
              back.diffuse                       diffuse color product
      state.lightprod[n].            (r,g,b,a)   light n / back material
              back.specular                      specular color product
      Table X.2.3: Light Property Bindings.  <n> indicates a light 
      number.
    If a program parameter binding matches "state.light[n].ambient",
    "state.light[n].diffuse", or "state.light[n].specular", the "x", 
    "y", "z", and "w" components of the program parameter variable are 
    filled with the "r", "g", "b", and "a" components, respectively, of 
    the corresponding light color.
    If a program parameter binding matches "state.light[n].position", 
    the "x", "y", "z", and "w" components of the program parameter 
    variable are filled with the "x", "y", "z", and "w" components, 
    respectively, of the light position.
    
    If a program parameter binding matches "state.light[n].attenuation", 
    the "x", "y", and "z" components of the program parameter variable 
    are filled with the constant, linear, and quadratic attenuation 
    parameters of the specified light, respectively (section 2.13.1).  
    The "w" component of the program parameter variable is filled with 
    the spot light exponent of the specified light.
    If a program parameter binding matches 
    "state.light[n].spot.direction", the "x", "y", and "z" components of 
    the program parameter variable are filled with the "x", "y", and "z" 
    components of the spot light direction of the specified light, 
    respectively (section 2.13.1).  The "w" component of the program 
    parameter variable is filled with the cosine of the spot light 
    cutoff angle of the specified light.
    If a program parameter binding matches "state.light[n].half", the 
    "x", "y", and "z" components of the program parameter variable are 
    filled with the x, y, and z components, respectively, of the 
    normalized infinite half-angle vector
      h_inf = || P + (0, 0, 1) ||.
    The "w" component is filled with 1.  In the computation of h_inf, P
    consists of the x, y, and z coordinates of the normalized vector 
    from the eye position P_e to the eye-space light position P_pli 
    (section 2.13.1).  h_inf is defined to correspond to the normalized 
    half-angle vector when using an infinite light (w coordinate of the 
    position is zero) and an infinite viewer (v_bs is FALSE).  For local 
    lights or a local viewer, h_inf is well-defined but does not match 
    the normalized half-angle vector, which will vary depending on the 
    vertex position.
    If a program parameter binding matches "state.lightmodel.ambient", 
    the "x", "y", "z", and "w" components of the program parameter 
    variable are filled with the "r", "g", "b", and "a" components of 
    the light model ambient color, respectively.
    If a program parameter binding matches "state.lightmodel.scenecolor" 
    or "state.lightmodel.front.scenecolor", the "x", "y", and "z" 
    components of the program parameter variable are filled with the 
    "r", "g", and "b" components respectively of the "front scene color"
      c_scene = a_cs * a_cm + e_cm,
    where a_cs is the light model ambient color, a_cm is the front 
    ambient material color, and e_cm is the front emissive material 
    color.  The "w" component of the program parameter variable is 
    filled with the alpha component of the front diffuse material color.  
    If a program parameter binding matches 
    "state.lightmodel.back.scenecolor", a similar back scene color, 
    computed using back-facing material properties, is used.  The front
    and back scene colors match the values that would be assigned to 
    vertices using conventional lighting if all lights were disabled.
    If a program parameter binding matches anything beginning with
    "state.lightprod[n]", the "x", "y", and "z" components of the 
    program parameter variable are filled with the "r", "g", and "b" 
    components, respectively, of the corresponding light product.  The 
    three light product components are the products of the corresponding 
    color components of the specified material property and the light 
    color of the specified light (see Table X.2.3).  The "w" component 
    of the program parameter variable is filled with the alpha component 
    of the specified material property.
    Light products depend on material properties, which can be changed 
    inside a Begin/End pair.  Such property changes are not guaranteed 
    to take effect until the following End command.  Program parameter 
    variables bound to light products whose corresponding material 
    property changes inside a Begin/End pair are undefined until the 
    following End command.
    Texture Environment Property Bindings
      Binding                    Components  Underlying State
      -------------------------  ----------  ----------------------------
      state.texenv[n].color      (r,g,b,a)   texture environment n color
      Table X.2.4:  Texture Environment Property Bindings.  "[n]" is 
      optional -- texture unit <n> is used if specified; texture unit 0 
      is used otherwise.
    If a program parameter binding matches "state.texenv[n].color", the 
    "x", "y", "z", and "w" components of the program parameter variable 
    are filled with the "r", "g", "b", and "a" components, respectively, 
    of the corresponding texture environment color.  Note that only
    "legacy" texture units, as queried by MAX_TEXTURE_UNITS, include 
    texture environment state.  Texture image units and texture 
    coordinate sets do not have associated texture environment state.
    Fog Property Bindings
      Binding                      Components  Underlying State
      ---------------------------  ----------  ----------------------------
      state.fog.color              (r,g,b,a)   RGB fog color (section 3.11)
      state.fog.params             (d,s,e,r)   fog density, linear start
                                               and end, and 1/(end-start)
                                               (section 3.11) 
      Table X.2.5:  Fog Property Bindings
    If a program parameter binding matches "state.fog.color", the "x", 
    "y", "z", and "w" components of the program parameter variable are 
    filled with the "r", "g", "b", and "a" components, respectively, of 
    the fog color (section 3.11).
    If a program parameter binding matches "state.fog.params", the "x", 
    "y", and "z" components of the program parameter variable are filled 
    with the fog density, linear fog start, and linear fog end 
    parameters (section 3.11), respectively.  The "w" component is 
    filled with 1/(end-start), where end and start are the linear fog 
    end and start parameters, respectively.
    Depth Property Bindings
      Binding                      Components  Underlying State
      ---------------------------  ----------  ----------------------------
      state.depth.range            (n,f,d,1)   Depth range near, far, and
                                               (far-near) (section 2.10.1)
      Table X.2.6:  Depth Property Bindings
    If a program parameter binding matches "state.depth.range", the "x" 
    and "y" components of the program parameter variable are filled with 
    the mappings of near and far clipping planes to window coordinates,
    respectively.  The "z" component is filled with the difference of
    the mappings of near and far clipping planes, far minus near.  The 
    "w" component is filled with 1.
    Matrix Property Bindings
      Binding                               Underlying State
      ------------------------------------  ---------------------------
      * state.matrix.modelview[n]           modelview matrix n
        state.matrix.projection             projection matrix
        state.matrix.mvp                    modelview-projection matrix
      * state.matrix.texture[n]             texture matrix n
        state.matrix.palette[n]             modelview palette matrix n
        state.matrix.program[n]             program matrix n
      Table X.2.7:  Base Matrix Property Bindings.  The "[n]" syntax 
      indicates a specific matrix number.  For modelview and texture 
      matrices, a matrix number is optional, and matrix zero will be 
      used if the matrix number is omitted.  These base bindings may 
      further be modified by a inverse/transpose selector and a row 
      selector.
    If the beginning of a program parameter binding matches any of the 
    matrix binding names listed in Table X.2.7, the binding corresponds 
    to a 4x4 matrix.  If the parameter binding is followed by 
    ".inverse", ".transpose", or ".invtrans" (<stateMatModifier> grammar 
    rule), the inverse, transpose, or transpose of the inverse, 
    respectively, of the matrix specified in Table X.2.7 is selected.  
    Otherwise, the matrix specified in Table X.2.7 is selected.  If the 
    specified matrix is poorly-conditioned (singular or nearly so), its 
    inverse matrix is undefined.  The binding name "state.matrix.mvp" 
    refers to the product of modelview matrix zero and the projection 
    matrix, defined as
       MVP = P * M0,
    where P is the projection matrix and M0 is modelview matrix zero.
    If the selected matrix is followed by ".row[<a>]" (matching the
    <stateMatrixRow> grammar rule), the "x", "y", "z", and "w" 
    components of the program parameter variable are filled with the 
    four entries of row <a> of the selected matrix.  In the example,
      PARAM m0 = state.matrix.modelview[1].row[0];
      PARAM m1 = state.matrix.projection.transpose.row[3];
    the variable "m0" is set to the first row (row 0) of modelview 
    matrix 1 and "m1" is set to the last row (row 3) of the transpose of 
    the projection matrix.
    For program parameter array bindings, multiple rows of the selected 
    matrix can be bound via the <stateMatrixRows> grammar rule.  If the 
    selected matrix binding is followed by ".row[<a>..<b>]", the result 
    is equivalent to specifying matrix rows <a> through <b>, in order.  
    A program will fail to load if <a> is greater than <b>.  If no row 
    selection is specified (<optMatrixRows> matches ""), matrix rows 0 
    through 3 are bound in order.  In the example,
      PARAM m2[] = { state.matrix.program[0].row[1..2] };
      PARAM m3[] = { state.matrix.program[0].transpose };
    the array "m2" has two entries, containing rows 1 and 2 of program 
    matrix zero, and "m3" has four entries, containing all four rows of 
    the transpose of program matrix zero.
    Program Parameter Arrays
    A program parameter array variable can be declared explicitly by 
    matching the <PARAM_multipleStmt> grammar rule.  Programs can 
    optionally specify the number of individual program parameters in 
    the array, using the <optArraySize> grammar rule.  Program parameter 
    arrays may not be declared implicity.
    Individual parameter variables in a program parameter array are 
    bound to GL state vectors or constant vectors as specified by the 
    grammar rule <paramMultInitList>.  Each individual parameter in the 
    array is bound in turn as described above.  
    The total number of entries in the array is equal to the number of
    parameters bound in the initializer list.  A fragment program that 
    specifies an array size (<optArraySize> matches <integer>) that does 
    not match the number of parameter bindings in the initialization 
    list will fail to load.
    Program parameter array variables may only be accessed using 
    absolute addressing by matching the <progParamArrayAbs> grammar 
    rule.  Array accesses are checked against the limits of the array.  
    If any fragment program instruction accesses a program parameter
    array with an out-of-range index (greater than or equal to the size 
    of the array), the fragment program will fail to load.
    Individual state vectors can have no more than one unique binding in 
    any given program.  The GL will automatically combine multiple 
    bindings of the same state vector into a single unique binding.
    3.11.3.3  Fragment Program Temporaries
    Fragment program temporary variables are a set of four-component
    floating-point vectors used to hold temporary results during 
    fragment program execution.  Temporaries do not persist between 
    program invocations, and are undefined at the beginning of each 
    fragment program invocation.
    Fragment program temporary variables can be declared explicitly 
    using the <TEMP_statement> grammar rule.  Each such statement can 
    declare one or more temporaries.  Fragment program temporary 
    variables can not be declared implicitly.
    3.11.3.4  Fragment Program Results
    Fragment program result variables are a set of four component 
    floating-point vectors used to hold the final results of a fragment 
    program.  Fragment program result variables are write-only during 
    fragment program execution.
    Fragment program result variables can be declared explicitly using 
    the <OUTPUT_statement> grammar rule, or implicitly using the
    <resultBinding> grammar rule in an executable instruction.  Each 
    fragment program result variable is bound to a fragment attribute 
    used in subsequent back-end processing.  The set of fragment program
    result variable bindings is given in Table X.3.
      Binding                        Components  Description
      -----------------------------  ----------  ----------------------------
      result.color                   (r,g,b,a)   color
      result.depth                   (*,*,d,*)   depth coordinate
      Table X.3:  Fragment Result Variable Bindings.  Components labeled 
      "*" are unused.
    If a result variable binding matches "result.color", updates to the 
    "x", "y", "z", and "w" components of the result variable modify the
    "r", "g", "b", and "a" components, respectively, of the fragment's
    output color.  If "result.color" is not both bound by the fragment 
    program and written by some instruction of the program, the output 
    color of the fragment program is undefined.
    If a result variable binding matches "result.depth", updates to the 
    "z" component of the result variable modify the fragment's output
    depth value.  If "result.depth" is not both bound by the fragment 
    program and written by some instruction of the program, the 
    interpolated depth value produced by rasterization is used as if 
    fragment program mode is not enabled.  Writes to any component of 
    depth other than the "z" component have no effect.
    3.11.3.5  Fragment Program Aliases
    Fragment programs can create aliases by matching the 
    <ALIAS_statement> grammar rule.  Aliases allow programs to use 
    multiple variable names to refer to a single underlying variable.  
    For example, the statement
      ALIAS var1 = var0
    establishes a variable name named "var1".  Subsequent references to 
    "var1" in the program text are treated as references to "var0".  The 
    left hand side of an ALIAS statement must be a new variable name, 
    and the right hand side must be an established variable name.
    Aliases are not considered variable declarations, so do not count 
    against the limits on the number of variable declarations allowed in 
    the program text.
    3.11.3.6  Fragment Program Resource Limits
    The fragment program execution environment provides implementation-
    dependent resource limits on the number of ALU instructions, texture
    instructions, total instructions (ALU or texture), temporary 
    variable declarations, program parameter bindings, or texture
    indirections.  A program that exceeds any of these resource limits 
    will fail to load.  The resource limits for fragment programs can be 
    queried by calling GetProgramiv (section 6.1.12) with a target of 
    FRAGMENT_PROGRAM_ARB.
    
    The limit on fragment program ALU instructions can be queried with 
    a <pname> of MAX_PROGRAM_ALU_INSTRUCTIONS_ARB, and must be at least 
    48.  Each ALU instruction in the program (matches of the 
    <ALUInstruction> grammar rule) counts against this limit.
    The limit on fragment program texture instructions can be queried 
    with a <pname> of MAX_PROGRAM_TEX_INSTRUCTIONS_ARB, and must be at 
    least 24.  Each texture instruction in the program (matches of the 
    <TexInstruction> grammar rule) counts against this limit.
    The limit on fragment program total instructions can be queried with 
    a <pname> of MAX_PROGRAM_INSTRUCTIONS_ARB, and must be at least 72.  
    Each instruction in the program (matching the <instruction> grammar 
    rule) counts against this limit.  Note that the limit on total
    instructions is not necessarily equal to the sum of the limits on
    ALU instructions and texture instructions.
    The limit on fragment program texture indirections can be queried 
    with a <pname> of MAX_PROGRAM_TEX_INDIRECTIONS_ARB, and must be at 
    least 4.  Texture indirections are described in 3.11.6.  If an
    implementation has no limit on texture indirections, the limit will
    be equal to the limit on texture instructions.
    The limit on fragment program temporary variable declarations can be 
    queried with a <pname> of MAX_PROGRAM_TEMPORARIES_ARB, and must be at 
    least 16.  Each temporary declared in the program, using the 
    <TEMP_statement> grammar rule, counts against this limit.  Aliases 
    of declared temporaries do not.
    
    The limit on fragment program attribute bindings can be queried with 
    a <pname> of MAX_PROGRAM_ATTRIBS_ARB and must be at least 10.  Each 
    distinct vertex attribute bound explicitly or implicitly in the 
    program counts against this limit; vertex attributes bound multiple 
    times count only once.
    The limit on fragment program parameter bindings can be queried with 
    a <pname> of MAX_PROGRAM_PARAMETERS_ARB, and must be at least 24.  
    Each distinct GL state vector bound explicitly or implicitly in the 
    program counts against this limit; GL state vectors bound multiple 
    times count only once.  Every other constant vector bound in the 
    program is counted if and only if an identical constant vector has 
    not already been counted.  Two constant vectors are considered 
    identical if the four component values are numerically equivalent.
    Recall that scalar constants bound in a program are treated as 
    vector constants with the scalar value replicated.
    In addition to the limits described above, the GL provides a similar 
    set of implementation-dependent native resource limits.  These 
    limits, specified in Section 6.1.12, provide guidance as to whether 
    the program is small enough to use a "native" mode where fragment 
    programs may be executed with higher performance.  The native 
    resource limits and usage counts are implementation-dependent and 
    may not exactly correspond to limits and counts described above.   
    A program's native resource consumption may be reduced by program 
    optimizations performed by the GL.  Native resource consumption may
    be increased due to emulation of instructions or any other program
    features not natively supported by an implementation.  Notably, an
    additional texture indirection may be consumed due to an 
    implementation's lack of native support for texture instructions 
    with source coordinate swizzles or parameter source coordinates, 
    which may require emulation by prepending ALU instructions.  An 
    implementation may also fail to natively support all combinations of 
    attributes described in Table X.1, even if the total number of
    bound attributes is fewer than the native attribute limit.  In this
    case the program is still considered to exceed the native resource
    limits, as queried by PROGRAM_UNDER_NATIVE_LIMITS_ARB (section 
    6.1.12).
    To assist in resource counting, the GL additionally provides 
    GetProgram queries to determine the resource usage and native 
    resource usage of the currently bound program, and to determine 
    whether the bound program exceeds any native resource limit.
    Programs that exceed any native resource limit may or may not load 
    depending on the implementation.
    3.11.4  Fragment Program Execution Environment
    If fragment program mode is enabled, the currently bound fragment 
    program is executed when any fragment is produced by rasterization.
    If fragment program mode is enabled and the currently bound program 
    object does not contain a valid fragment program, the error 
    INVALID_OPERATION will be generated by Begin, RasterPos, and any 
    command that implicitly calls Begin (e.g., DrawArrays).
    Fragment programs execute a sequence of instructions without
    branching.  Fragment programs begin by executing the first 
    instruction in the program, and execute instructions in the order 
    specified in the program until the last instruction is completed.
    
    There are 33 fragment program instructions.  The instructions and 
    their respective input and output parameters are summarized in 
    Table X.5.
      Instruction    Inputs  Output   Description
      -----------    ------  ------   --------------------------------
      ABS            v       v        absolute value
      ADD            v,v     v        add
      CMP            v,v,v   v        compare
      COS            s       ssss     cosine with reduction to [-PI,PI]
      DP3            v,v     ssss     3-component dot product
      DP4            v,v     ssss     4-component dot product
      DPH            v,v     ssss     homogeneous dot product
      DST            v,v     v        distance vector
      EX2            s       ssss     exponential base 2
      FLR            v       v        floor
      FRC            v       v        fraction
      KIL            v       v        kill fragment
      LG2            s       ssss     logarithm base 2
      LIT            v       v        compute light coefficients
      LRP            v,v,v   v        linear interpolation
      MAD            v,v,v   v        multiply and add
      MAX            v,v     v        maximum
      MIN            v,v     v        minimum
      MOV            v       v        move
      MUL            v,v     v        multiply
      POW            s,s     ssss     exponentiate
      RCP            s       ssss     reciprocal
      RSQ            s       ssss     reciprocal square root
      SCS            s       ss--     sine/cosine without reduction
      SGE            v,v     v        set on greater than or equal
      SIN            s       ssss     sine with reduction to [-PI,PI]
      SLT            v,v     v        set on less than
      SUB            v,v     v        subtract
      SWZ            v       v        extended swizzle
      TEX            v,u,t   v        texture sample
      TXB            v,u,t   v        texture sample with bias
      TXP            v,u,t   v        texture sample with projection
      XPD            v,v     v        cross product
      Table X.5:  Summary of fragment program instructions.  "v" 
      indicates a floating-point vector input or output, "s" indicates a 
      floating-point scalar input, "ssss" indicates a scalar output 
      replicated across a 4-component result vector, "ss--" indicates
      two scalar outputs in the first two components, "u" indicates a 
      texture image unit identifier, and "t" indicates a texture target.
    3.11.4.1  Fragment Program Operands
    Most fragment program instructions operate on floating-point vectors 
    or scalars, as indicated by the grammar rules <vectorSrcReg> and
    <scalarSrcReg>, respectively.
    Vector and scalar operands can be obtained from fragment attribute, 
    program parameter, or temporary registers, as indicated by the 
    <srcReg> rule.  For scalar operands, a single vector component is 
    selected by the <scalarSuffix> rule, where the characters "x", "y", 
    "z", and "w", or "r", "g", "b", and "a" select the first, second, 
    third, and fourth components, respectively, of the vector.
    Vector operands can be swizzled according to the <optionalSuffix> 
    rule.  In its most general form, the <optionalSuffix> rule matches
    the pattern ".????" where each question mark is replaced with one of
    "x", "y", "z", "w", "r", "g", "b", or "a".  For such patterns, the 
    first, second, third, and fourth components of the operand are taken 
    from the vector components named by the first, second, third, and 
    fourth character of the pattern, respectively.  For example, if the 
    swizzle suffix is ".yzzx" or ".gbbr" and the specified source 
    contains {2,8,9,0}, the swizzled operand used by the instruction is 
    {8,9,9,2}.
    If the <optionalSuffix> rule matches "", it is treated as though it 
    were ".xyzw".  If the <optionalSuffix> rule matches (ignoring 
    whitespace) ".x", ".y", ".z", or ".w", these are treated the same as 
    ".xxxx", ".yyyy", ".zzzz", and ".wwww" respectively.  Likewise, if
    the <optionalSuffix> rule matches ".r", ".g", ".b", or ".a", these 
    are treated the same as ".rrrr", ".gggg", ".bbbb", and ".aaaa" 
    respectively.
    Floating-point scalar or vector operands can optionally be negated
    according to the <optionalSign> rule in <scalarSrcReg> and
    <vectorSrcReg>.  If the <optionalSign> matches "-", each operand or
    operand component is negated.
    The following pseudo-code spells out the operand generation process.  
    In the example, "float" is a floating-point scalar type, while 
    "floatVec" is a four-component vector.  "source" refers to the 
    register used for the operand, matching the <srcReg> rule.  "negate" 
    is TRUE if the <optionalSign> rule in <scalarSrcReg> or 
    <vectorSrcReg> matches "-" and FALSE otherwise.  The ".c***", 
    ".*c**", ".**c*", ".***c" modifiers refer to the x, y, z, and w 
    components obtained by the swizzle operation; the ".c" modifier
    refers to the single component selected for a scalar load.
      floatVec VectorLoad(floatVec source)
      {
          floatVec operand;
          operand.x = source.c***;
          operand.y = source.*c**;
          operand.z = source.**c*;
          operand.w = source.***c;
          if (negate) {
             operand.x = -operand.x;
             operand.y = -operand.y;
             operand.z = -operand.z;
             operand.w = -operand.w;
          }
          return operand;
      }
      float ScalarLoad(floatVec source) 
      {
          float operand;
          operand = source.c;
          if (negate) {
            operand = -operand;
          }
          return operand;
      }
    3.11.4.2  Fragment Program Parameter Arrays
    A fragment program can load a single element of a program parameter 
    array using only absolute addressing.  Program parameter arrays are 
    accessed when the <progParamArrayAbs> rule is matched.  The offset 
    of the selected entry in the array is given by the number matching 
    <progParamRegNum>.  If the offset exceeds the size of the 
    array, the results of the access are undefined, but may not lead to
    program or GL termination.
    3.11.4.3  Fragment Program Destination Register Update
    Fragment program instructions write a 4-component result vector to a
    single temporary or fragment result register.  Writes to individual 
    components of the destination register are controlled by individual 
    component write masks specified as part of the instruction.  
    Optional clamping of each component of the destination register to 
    the range [0,1] is controlled by an opcode modifier.
    The component write mask is specified by the <optionalMask> rule 
    found in the <maskedDstReg> rule.  If the optional mask is "", all 
    components are enabled.  Otherwise, the optional mask names the 
    individual components to enable.  The characters "x", "y", "z", and 
    "w", or "r", "g", "b", and "a" match the first, second, third, and 
    fourth components, respectively.  For example, an optional mask of 
    ".xzw" indicates that the x, z, and w components should be enabled 
    for writing but the y component should not.  The grammar requires 
    that the destination register mask components must be listed in 
    "xyzw", or "rgba" order.  Component names from one set (xyzw or 
    rgba) cannot be mixed with component names from another set.  For 
    example, ".rgw" is not a valid writemask.
    Each component of the destination register is updated with the 
    result of the fragment program instruction if and only if the 
    component is enabled for writes by the component write mask.  
    Otherwise, the component of the destination register remains 
    unchanged.
    If the instruction opcode has the "_SAT" suffix, requesting 
    saturated result vectors, each component of the result vector 
    enabled in the writemask is clamped to the range [0,1] before being 
    updated in the destination register.
    The following pseudocode illustrates the process of writing a result
    vector to the destination register.  In the pseudocode, "instrmask" 
    refers to the component write mask given by the <optionalMask> rule.  
    "clamp" is TRUE if the instruction specifies that the result should
    be clamped.  "result" and "destination" refer to the result vector 
    and the register selected by <dstReg>, respectively.
      void UpdateDestination(floatVec destination, floatVec result)
      {
          floatVec merged;
          // Clamp the result vector components to [0,1], if requested.
          if (instrClamp) {
              if (result.x < 0)      result.x = 0;
              else if (result.x > 1) result.x = 1;
              if (result.y < 0)      result.y = 0;
              else if (result.y > 1) result.y = 1;
              if (result.z < 0)      result.z = 0;
              else if (result.z > 1) result.z = 1;
              if (result.w < 0)      result.w = 0;
              else if (result.w > 1) result.w = 1;
          }
          // Merge the converted result into the destination register,
          // under control of the compile-time write mask.
          merged = destination;
          if (instrMask.x) {
              merged.x = result.x;
          }
          if (instrMask.y) {
              merged.y = result.y;
          }
          if (instrMask.z) {
              merged.z = result.z;
          }
          if (instrMask.w) {
              merged.w = result.w;
          }
          // Write out the new destination register.
          destination = merged;
      }
    3.11.4.4  Fragment Program Result Processing
    As a fragment program executes, it will write to either one or two 
    result registers that are mapped to the fragment's color and depth.
    The fragment's color components are first clamped to the range [0,1] 
    then converted to fixed point as in section 2.13.9.  If the fragment
    program does not write result.color, the color will be undefined in 
    subsequent stages.
    If the fragment program contains an instruction to write to
    result.depth, the fragment's depth is replaced by the value of the
    "z" component of result.depth.  This z value is first clamped to the
    range [0,1] then converted to fixed-point as if it were a window z
    value (section 2.10.1).  If the fragment program does not write 
    result.depth, the fragment's original depth is unmodified.
    3.11.4.5  Fragment Program Options
    The <optionSequence> grammar rule provides a mechanism for programs 
    to indicate that one or more extended language features are used by 
    the program.  All program options used by the program must be 
    declared at the beginning of the program string.  Each program 
    option specified in a program string will modify the syntactic or 
    semantic rules used to interpet the program and the execution 
    environment used to execute the program.  Program options not 
    present in the program string are ignored, even if they are 
    supported by the GL.
    The <identifier> token in the <option> rule must match the name of a
    program option supported by the implementation.  To avoid option 
    name conflicts, option identifiers are required to begin with a 
    vendor prefix.  A program will fail to load if it specifies a 
    program option not supported by the GL.
    Fragment program options should confine their semantic changes to 
    the domain of fragment programs.  Support for a fragment program 
    option should not change the specification and behavior of fragment 
    programs not requesting use of that option.
    3.11.4.5.1  Fog Application Fragment Program Options
    If a fragment program specifies one of the options "ARB_fog_exp", 
    "ARB_fog_exp2", or "ARB_fog_linear", the program will apply fog to 
    the program's final clamped color using a fog mode of EXP, EXP2, or 
    LINEAR, respectively, as described in section 3.10.
    When a fog option is specified in a fragment program, semantic 
    restrictions are added to indicate that a fragment program 
    will fail to load if the number of temporaries it contains exceeds 
    the implementation-dependent limit minus 1, if the number of 
    attributes it contains exceeds the implementation-dependent limit
    minus 1, or if the number of parameters it contains exceeds the 
    implementation-dependent limit minus 2.
    Additionally, when the ARB_fog_exp option is specified in a fragment
    program, a semantic restriction is added to indicate that a fragment
    program will fail to load if the number of instructions or ALU 
    instructions it contains exceeds the implementation-dependent limit 
    minus 3.  When the ARB_fog_exp2 option is specified in a fragment
    program, a semantic restriction is added to indicate that a fragment
    program will fail to load if the number of instructions or ALU 
    instructions it contains exceeds the implementation-dependent limit 
    minus 4.  When the ARB_fog_linear option is specified in a fragment
    program, a semantic restriction is added to indicate that a fragment
    program will fail to load if the number of instructions or ALU 
    instructions it contains exceeds the implementation-dependent limit 
    minus 2.
    Only one fog application option may be specified by any given 
    fragment program.  A fragment program that specifies more than one 
    of the program options "ARB_fog_exp", "ARB_fog_exp2", and 
    "ARB_fog_linear", will fail to load.
    3.11.4.5.2  Precision Hint Options
    Fragment program computations are carried out at an implementation-
    dependent precision.  However, some implementations may be able to
    perform fragment program computations at more than one precision, 
    and may be able to trade off computation precision for performance.  
    If a fragment program specifies the "ARB_precision_hint_fastest" 
    program option, implementations should select precision to minimize 
    program execution time, with possibly reduced precision.  If a 
    fragment program specifies the "ARB_precision_hint_nicest" program 
    option, implementations should maximize the precision, with possibly 
    increased execution time.
    Only one precision control option may be specified by any given 
    fragment program.  A fragment program that specifies both the
    "ARB_precision_hint_fastest" and "ARB_precision_hint_nicest" program
    options will fail to load.
    3.11.5  Fragment Program ALU Instruction Set
    The following sections describe the set of supported fragment 
    program instructions.  Each section contains pseudocode describing
    the instruction.  Instructions will have up to three operands,
    referred to as "op0", "op1", and "op2".  The operands are loaded 
    using the mechanisms specified in section 3.11.4.1.  The variables 
    "tmp", "tmp0", "tmp1", and "tmp2" describe scalars or vectors used 
    to hold intermediate results in the instruction.  Instructions will 
    generate a result vector called "result".  The result vector is then 
    written to the destination register specified in the instruction as 
    described in section 3.11.4.3.
    
    3.11.5.1  ABS:  Absolute Value
    The ABS instruction performs a component-wise absolute value 
    operation on the single operand to yield a result vector.
      tmp = VectorLoad(op0); 
      result.x = fabs(tmp.x);
      result.y = fabs(tmp.y);
      result.z = fabs(tmp.z);
      result.w = fabs(tmp.w);
    3.11.5.2  ADD:  Add
    The ADD instruction performs a component-wise add of the two 
    operands to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = tmp0.x + tmp1.x;
      result.y = tmp0.y + tmp1.y;
      result.z = tmp0.z + tmp1.z;
      result.w = tmp0.w + tmp1.w;
    The following rules apply to addition:
      1. <x> + <y> == <y> + <x>, for all <x> and <y>.
      2. <x> + 0.0 == <x>, for all <x>.
    3.11.5.3  CMP: Compare
    The CMP instructions performs a component-wise comparison of the 
    first operand against zero, and copies the values of the second or
    third operands based on the results of the compare.
    
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      tmp2 = VectorLoad(op2);
      result.x = (tmp0.x < 0.0) ? tmp1.x : tmp2.x;
      result.y = (tmp0.y < 0.0) ? tmp1.y : tmp2.y;
      result.z = (tmp0.z < 0.0) ? tmp1.z : tmp2.z;
      result.w = (tmp0.w < 0.0) ? tmp1.w : tmp2.w;
 
   
    3.11.5.4  COS:  Cosine
    The COS instruction approximates the trigonometric cosine of the 
    angle specified by the scalar operand and replicates it to all four 
    components of the result vector.  The angle is specified in radians
    and does not have to be in the range [-PI,PI].
      tmp = ScalarLoad(op0);
      result.x = ApproxCosine(tmp);
      result.y = ApproxCosine(tmp);
      result.z = ApproxCosine(tmp);
      result.w = ApproxCosine(tmp);
    3.11.5.5  DP3:  Three-Component Dot Product
    The DP3 instruction computes a three-component dot product of the 
    two operands (using the first three components) and replicates the 
    dot product to all four components of the result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      dot = (tmp0.x * tmp1.x) + (tmp0.y * tmp1.y) + (tmp0.z * tmp1.z);
      result.x = dot;
      result.y = dot;
      result.z = dot;
      result.w = dot;
    3.11.5.6  DP4:  Four-Component Dot Product
    The DP4 instruction computes a four-component dot product of the two
    operands and replicates the dot product to all four components of 
    the result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1):
      dot = (tmp0.x * tmp1.x) + (tmp0.y * tmp1.y) + 
            (tmp0.z * tmp1.z) + (tmp0.w * tmp1.w);
      result.x = dot;
      result.y = dot;
      result.z = dot;
      result.w = dot;
    3.11.5.7  DPH:  Homogeneous Dot Product
    The DPH instruction computes a three-component dot product of the 
    two operands (using the x, y, and z components), adds the w 
    component of the second operand, and replicates the sum to all four 
    components of the result vector.  This is equivalent to a four-
    component dot product where the w component of the first operand is 
    forced to 1.0.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1):
      dot = (tmp0.x * tmp1.x) + (tmp0.y * tmp1.y) + 
            (tmp0.z * tmp1.z) + tmp1.w;
      result.x = dot;
      result.y = dot;
      result.z = dot;
      result.w = dot;
    3.11.5.8  DST:  Distance Vector
    The DST instruction computes a distance vector from two specially-
    formatted operands.  The first operand should be of the form [NA, 
    d^2, d^2, NA] and the second operand should be of the form [NA, 1/d, 
    NA, 1/d], where NA values are not relevant to the calculation and d 
    is a vector length.  If both vectors satisfy these conditions, the 
    result vector will be of the form [1.0, d, d^2, 1/d].
    The exact behavior is specified in the following pseudo-code:
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = 1.0;
      result.y = tmp0.y * tmp1.y;
      result.z = tmp0.z;
      result.w = tmp1.w;
    Given an arbitrary vector, d^2 can be obtained using the DP3 
    instruction (using the same vector for both operands) and 1/d can be 
    obtained from d^2 using the RSQ instruction.
    This distance vector is useful for per-fragment light attenuation
    calculations:  a DP3 operation using the distance vector and an
    attenuation constants vector as operands will yield the attenuation
    factor.
    3.11.5.9  EX2:  Exponential Base 2
    The EX2 instruction approximates 2 raised to the power of the scalar
    operand and replicates the approximation to all four components of 
    the result vector.
      tmp = ScalarLoad(op0);
      result.x = Approx2ToX(tmp);
      result.y = Approx2ToX(tmp);
      result.z = Approx2ToX(tmp);
      result.w = Approx2ToX(tmp);
    3.11.5.10  FLR:  Floor
    The FLR instruction performs a component-wise floor operation on the
    operand to generate a result vector.  The floor of a value is 
    defined as the largest integer less than or equal to the value.  The 
    floor of 2.3 is 2.0; the floor of -3.6 is -4.0.
      tmp = VectorLoad(op0);
      result.x = floor(tmp.x);
      result.y = floor(tmp.y);
      result.z = floor(tmp.z);
      result.w = floor(tmp.w);
    3.11.5.11  FRC:  Fraction
    The FRC instruction extracts the fractional portion of each 
    component of the operand to generate a result vector.  The 
    fractional portion of a component is defined as the result after 
    subtracting off the floor of the component (see FLR), and is always 
    in the range [0.0, 1.0).
    For negative values, the fractional portion is NOT the number 
    written to the right of the decimal point -- the fractional portion 
    of -1.7 is not 0.7 -- it is 0.3.  0.3 is produced by subtracting the 
    floor of -1.7 (-2.0) from -1.7.
      tmp = VectorLoad(op0);
      result.x = fraction(tmp.x);
      result.y = fraction(tmp.y);
      result.z = fraction(tmp.z);
      result.w = fraction(tmp.w);
    3.11.5.12  LG2:  Logarithm Base 2
    The LG2 instruction approximates the base 2 logarithm of the scalar
    operand and replicates it to all four components of the result 
    vector.
      tmp = ScalarLoad(op0);
      result.x = ApproxLog2(tmp);
      result.y = ApproxLog2(tmp);
      result.z = ApproxLog2(tmp);
      result.w = ApproxLog2(tmp);
    If the scalar operand is zero or negative, the result is undefined.
    3.11.5.13  LIT:  Light Coefficients
    The LIT instruction accelerates per-fragment lighting by computing 
    lighting coefficients for ambient, diffuse, and specular light 
    contributions.  The "x" component of the single operand is assumed 
    to hold a diffuse dot product (n dot VP_pli, as in the vertex 
    lighting equations in Section 2.13.1).  The "y" component of the 
    operand is assumed to hold a specular dot product (n dot h_i).  The 
    "w" component of the operand is assumed to hold the specular 
    exponent of the material (s_rm), and is clamped to the range (-128, 
    +128) exclusive.
    The "x" component of the result vector receives the value that 
    should be multiplied by the ambient light/material product (always 
    1.0).  The "y" component of the result vector receives the value 
    that should be multiplied by the diffuse light/material product 
    (n dot VP_pli).  The "z" component of the result vector receives the 
    value that should be multiplied by the specular light/material 
    product (f_i * (n dot h_i) ^ s_rm).  The "w" component of the result 
    is the constant 1.0.
    Negative diffuse and specular dot products are clamped to 0.0, as is 
    done in the standard per-vertex lighting operations.  In addition, 
    if the diffuse dot product is zero or negative, the specular 
    coefficient is forced to zero.
      tmp = VectorLoad(op0);
      if (tmp.x < 0) tmp.x = 0;
      if (tmp.y < 0) tmp.y = 0;
      if (tmp.w < -(128.0-epsilon)) tmp.w = -(128.0-epsilon);
      else if (tmp.w > 128-epsilon) tmp.w = 128-epsilon;
      result.x = 1.0;
      result.y = tmp.x;
      result.z = (tmp.x > 0) ? RoughApproxPower(tmp.y, tmp.w) : 0.0;
      result.w = 1.0;
    The exponentiation approximation function may be defined in terms of 
    the base 2 exponentiation and logarithm approximation operations in 
    the EX2 and LG2 instructions, where
      ApproxPower(a,b) = ApproxExp2(b * ApproxLog2(a)).
    In particular, the approximation may not be any more accurate than 
    the underlying EX2 and LG2 operations.
    Also, since 0^0 is defined to be 1, RoughApproxPower(0.0, 0.0) will
    produce 1.0.
    3.11.5.14  LRP: Linear Interpolation
    The LRP instruction performs a component-wise linear interpolation 
    between the second and third operands using the first operand as the
    blend factor.
    
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      tmp2 = VectorLoad(op2);
      result.x = tmp0.x * tmp1.x + (1 - tmp0.x) * tmp2.x;
      result.y = tmp0.y * tmp1.y + (1 - tmp0.y) * tmp2.y;
      result.z = tmp0.z * tmp1.z + (1 - tmp0.z) * tmp2.z;
      result.w = tmp0.w * tmp1.w + (1 - tmp0.w) * tmp2.w;
    3.11.5.15  MAD:  Multiply and Add
    The MAD instruction performs a component-wise multiply of the first two
    operands, and then does a component-wise add of the product to the 
    third operand to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      tmp2 = VectorLoad(op2);
      result.x = tmp0.x * tmp1.x + tmp2.x;
      result.y = tmp0.y * tmp1.y + tmp2.y;
      result.z = tmp0.z * tmp1.z + tmp2.z;
      result.w = tmp0.w * tmp1.w + tmp2.w;
    The multiplication and addition operations in this instruction are 
    subject to the same rules as described for the MUL and ADD 
    instructions.
    3.11.5.16  MAX:  Maximum
    The MAX instruction computes component-wise maximums of the values 
    in the two operands to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = (tmp0.x > tmp1.x) ? tmp0.x : tmp1.x;
      result.y = (tmp0.y > tmp1.y) ? tmp0.y : tmp1.y;
      result.z = (tmp0.z > tmp1.z) ? tmp0.z : tmp1.z;
      result.w = (tmp0.w > tmp1.w) ? tmp0.w : tmp1.w;
    3.11.5.17  MIN:  Minimum
    The MIN instruction computes component-wise minimums of the values 
    in the two operands to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = (tmp0.x > tmp1.x) ? tmp1.x : tmp0.x;
      result.y = (tmp0.y > tmp1.y) ? tmp1.y : tmp0.y;
      result.z = (tmp0.z > tmp1.z) ? tmp1.z : tmp0.z;
      result.w = (tmp0.w > tmp1.w) ? tmp1.w : tmp0.w;
    3.11.5.18  MOV:  Move
    The MOV instruction copies the value of the operand to yield a 
    result vector.
      result = VectorLoad(op0);
    3.11.5.19  MUL:  Multiply
    The MUL instruction performs a component-wise multiply of the two 
    operands to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = tmp0.x * tmp1.x;
      result.y = tmp0.y * tmp1.y;
      result.z = tmp0.z * tmp1.z;
      result.w = tmp0.w * tmp1.w;
    The following rules apply to multiplication:
      1. <x> * <y> == <y> * <x>, for all <x> and <y>.
      2. +/-0.0 * <x> = +/-0.0, at least for all <x> that correspond to
         representable numbers (IEEE "not a number" and "infinity" 
         encodings may be exceptions).
      3. +1.0 * <x> = <x>, for all <x>.
    Multiplication by zero and one should be invariant, as it may be 
    used to evaluate conditional expressions without branching.
    3.11.5.20  POW:  Exponentiate
    The POW instruction approximates the value of the first scalar 
    operand raised to the power of the second scalar operand and 
    replicates it to all four components of the result vector.
      tmp0 = ScalarLoad(op0);
      tmp1 = ScalarLoad(op1);
      result.x = ApproxPower(tmp0, tmp1);
      result.y = ApproxPower(tmp0, tmp1);
      result.z = ApproxPower(tmp0, tmp1);
      result.w = ApproxPower(tmp0, tmp1);
    The exponentiation approximation function may be implemented using 
    the base 2 exponentiation and logarithm approximation operations in 
    the EX2 and LG2 instructions.  In particular,
      ApproxPower(a,b) = ApproxExp2(b * ApproxLog2(a)).
    Note that a logarithm may be involved even for cases where the 
    exponent is an integer.  This means that it may not be possible to 
    exponentiate correctly with a negative base.  In constrast, it is 
    possible in a "normal" mathematical formulation to raise negative 
    numbers to integral powers (e.g., (-3)^2== 9, and (-0.5)^-2==4).
    3.11.5.21  RCP:  Reciprocal
    The RCP instruction approximates the reciprocal of the scalar 
    operand and replicates it to all four components of the result 
    vector.
      tmp = ScalarLoad(op0);
      result.x = ApproxReciprocal(tmp);
      result.y = ApproxReciprocal(tmp);
      result.z = ApproxReciprocal(tmp);
      result.w = ApproxReciprocal(tmp);
    The following rule applies to reciprocation:
      1. ApproxReciprocal(+1.0) = +1.0.
    3.11.5.22  RSQ:  Reciprocal Square Root
    The RSQ instruction approximates the reciprocal of the square root 
    of the absolute value of the scalar operand and replicates it to all 
    four components of the result vector.
      tmp = fabs(ScalarLoad(op0));
      result.x = ApproxRSQRT(tmp);
      result.y = ApproxRSQRT(tmp);
      result.z = ApproxRSQRT(tmp);
      result.w = ApproxRSQRT(tmp);
    3.11.5.23  SCS:  Sine/Cosine
    The SCS instruction approximates the trigonometric sine and cosine
    of the angle specified by the scalar operand and places the cosine 
    in the x component and the sine in the y component of the result 
    vector.  The z and w components of the result vector are undefined.
    The angle is specified in radians and must be in the range [-PI,PI].
      tmp = ScalarLoad(op0);
      result.x = ApproxCosine(tmp);
      result.y = ApproxSine(tmp);
    If the scalar operand is not in the range [-PI,PI], the result
    vector is undefined.
    3.11.5.24  SGE:  Set On Greater or Equal Than
    The SGE instruction performs a component-wise comparison of the two
    operands.  Each component of the result vector is 1.0 if the 
    corresponding component of the first operands is greater than or 
    equal that of the second, and 0.0 otherwise.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = (tmp0.x >= tmp1.x) ? 1.0 : 0.0;
      result.y = (tmp0.y >= tmp1.y) ? 1.0 : 0.0;
      result.z = (tmp0.z >= tmp1.z) ? 1.0 : 0.0;
      result.w = (tmp0.w >= tmp1.w) ? 1.0 : 0.0;
    3.11.5.25  SIN:  Sine
    The SIN instruction approximates the trigonometric sine of the angle
    specified by the scalar operand and replicates it to all four 
    components of the result vector.  The angle is specified in radians
    and does not have to be in the range [-PI,PI].
      tmp = ScalarLoad(op0);
      result.x = ApproxSine(tmp);
      result.y = ApproxSine(tmp);
      result.z = ApproxSine(tmp);
      result.w = ApproxSine(tmp);
    3.11.5.26  SLT:  Set On Less Than
    The SLT instruction performs a component-wise comparison of the two
    operands.  Each component of the result vector is 1.0 if the 
    corresponding component of the first operand is less than that of 
    the second, and 0.0 otherwise.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = (tmp0.x < tmp1.x) ? 1.0 : 0.0;
      result.y = (tmp0.y < tmp1.y) ? 1.0 : 0.0;
      result.z = (tmp0.z < tmp1.z) ? 1.0 : 0.0;
      result.w = (tmp0.w < tmp1.w) ? 1.0 : 0.0;
    3.11.5.27  SUB:  Subtract
    The SUB instruction performs a component-wise subtraction of the 
    second operand from the first to yield a result vector.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = tmp0.x - tmp1.x;
      result.y = tmp0.y - tmp1.y;
      result.z = tmp0.z - tmp1.z;
      result.w = tmp0.w - tmp1.w;
    3.11.5.28  SWZ:  Extended Swizzle
    The SWZ instruction loads the single vector operand, and performs a
    swizzle operation more powerful than that provided for loading 
    normal vector operands to yield an instruction vector.
    After the operand is loaded, the "x", "y", "z", and "w" components 
    of the result vector are selected by the first, second, third, and 
    fourth matches of the <xyzwExtSwizComp> or <rgbaExtSwizComp> pattern 
    in the <extendedSwizzle> rule.
    A result component can be selected from any of the four components 
    of the operand or the constants 0.0 and 1.0.  The result component 
    can also be optionally negated.  The following pseudocode describes 
    the component selection method.  "operand" refers to the vector 
    operand.  "select" is an enumerant where the values ZERO, ONE, X, Y,
    Z, and W correspond to the <xyzwExtSwizSel> rule matching "0", "1", "x", 
    "y", "z", and "w", respectively, or the <rgbaExtSwizSel> rule
    matching "0", 1", "r", "g", "b", and "a", respectively.  "negate" is 
    TRUE if and only if the <optionalSign> rule in <xyzwExtSwizComp> 
    or <rgbaExtSwizComp> matches "-".
      float ExtSwizComponent(floatVec operand, enum select, boolean negate)
      {
          float result;
          switch (select) {
            case ZERO:  result = 0.0; break;
            case ONE:   result = 1.0; break;
            case X:     result = operand.x; break;
            case Y:     result = operand.y; break;
            case Z:     result = operand.z; break;
            case W:     result = operand.w; break;
          }
          if (negate) {
            result = -result;
          }
          return result;
      }
    The entire extended swizzle operation is then defined using the 
    following pseudocode:
      tmp = VectorLoad(op0);
      result.x = ExtSwizComponent(tmp, xSelect, xNegate);
      result.y = ExtSwizComponent(tmp, ySelect, yNegate);
      result.z = ExtSwizComponent(tmp, zSelect, zNegate);
      result.w = ExtSwizComponent(tmp, wSelect, wNegate);
    "xSelect", "xNegate", "ySelect", "yNegate", "zSelect", "zNegate",
    "wSelect", and "wNegate" correspond to the "select" and "negate" 
    values above for the four <xyzwExtSwizComp> or <rgbaExtSwizComp>
    matches.  
    Since this instruction allows for component selection and negation 
    for each individual component, the grammar does not allow the use of 
    the normal swizzle and negation operations allowed for vector 
    operands in other instructions.
    3.11.5.29  XPD:  Cross Product
    The XPD instruction computes the cross product using the first three
    components of its two vector operands to generate the x, y, and z
    components of the result vector.  The w component of the result 
    vector is undefined.
      tmp0 = VectorLoad(op0);
      tmp1 = VectorLoad(op1);
      result.x = tmp0.y * tmp1.z - tmp0.z * tmp1.y;
      result.y = tmp0.z * tmp1.x - tmp0.x * tmp1.z;
      result.z = tmp0.x * tmp1.y - tmp0.y * tmp1.x;
    3.11.6  Fragment Program Texture Instruction Set
    The first three texture instructions described below specify the 
    mapping of 4-tuple vectors to colors of an image.  The sampling of 
    the texture works as described in section 3.8, except that texture 
    environments and texture functions are not applicable, and the
    texture enables hierarchy is replaced by explicit references to
    the desired texture target (i.e., 1D, 2D, 3D, cube map, rectangle).  
    These texture instructions specify how the 4-tuple is mapped into 
    the coordinates used for sampling.  The following function is used 
    to describe the texture sampling in the descriptions below:
      vec4 TextureSample(float s, float t, float r, float lodBias,
                         int texImageUnit, enum texTarget);
    Note that not all three texture coordinates, s, t, and r, are
    used by all texture targets.  In particular, 1D texture targets only
    use the s component, and 2D and rectangle (non-power-of-two) texture
    targets only use the s and t components.  The descriptions of the
    texture instructions below supply all three components, as would be
    the case with 3D or cube map targets.
    If a fragment program samples from a texture target on a texture
    image unit where the bound texture object is not complete, as 
    defined in section 3.8.9, the result will be the vector 
    (R, G, B, A) = (0, 0, 0, 1).
    A fragment program will fail to load if it attempts to sample from
    multiple texture targets on the same texture image unit.  For 
    example, the following program would fail to load:
      !!ARBfp1.0
      TEX result.color, fragment.texcoord[0], texture[0], 2D;
      TEX result.depth, fragment.texcoord[1], texture[0], 3D;
      END
    The fourth texture instruction described below, KIL, does not sample
    from a texture, but rather prevents further processing of the 
    current fragment if any component of its 4-tuple vector is less than
    zero.
    A dependent texture instruction is one that samples using a texture
    coordinate residing in a temporary, rather than in an attribute or
    a parameter.  A program may have a chain of dependent texture
    instructions, where the result of the first texture instruction is 
    used as the coordinate for a second texture instruction, which is in 
    turn used as the coordinate for a third texture instruction, and so 
    on.  Each node in this chain is termed an indirection, and can be 
    thought of as a set of texture samples that execute in parallel
    followed by a sequence of ALU instructions.
    Some implementations may have limitations on how long the dependency 
    chain may be, and so indirections are counted as a resource just
    like instructions or temporaries are counted.  All programs have at 
    least one indirection, or one node in this chain, even if the 
    program performs no texture operation.  Each instruction encountered
    is included in this node until a texture instruction is encountered
      - whose texture coordinate is a temporary that has been previously 
        written in the current node; or
      - whose result vector is a temporary that is also the operand or 
        result vector of a previous ALU instruction in the current node.
    A new node is then started, including the texture instruction and 
    all subsequent instructions, and the process repeats for all 
    instructions in the program.  Note that for simplicity in counting, 
    result writemasks and operand suffixes are not taken into 
    consideration when counting indirections.
    3.11.6.1  TEX: Map coordinate to color
    The TEX instruction takes the first three components of 
    its source vector, and maps them to s, t, and r.  These coordinates 
    are used to sample from the specified texture target on the 
    specified texture image unit in a manner consistent with its 
    parameters.  The resulting sample is mapped to RGBA as described in 
    table 3.21 and written to the result vector.
      tmp = VectorLoad(op0);
      result = TextureSample(tmp.x, tmp.y, tmp.z, 0.0, op1, op2);
   
    3.11.6.2  TXP: Project coordinate and map to color
    The TXP instruction divides the first three components of its source 
    vector by the fourth component and maps the results to s, t, and r.  
    These coordinates are used to sample from the specified texture
    target on the specified texture image unit in a manner consistent 
    with its parameters.  The resulting sample is mapped to RGBA as 
    described in table 3.21 and written to the result vector.  If the 
    value of the fourth component of the source vector is less than or 
    equal to zero, the result vector is undefined.
      tmp = VectorLoad(op0);
      tmp.x = tmp.x / tmp.w;
      tmp.y = tmp.y / tmp.w;
      tmp.z = tmp.z / tmp.w;
      result = TextureSample(tmp.x, tmp.y, tmp.z, 0.0, op1, op2);
    3.11.6.3  TXB: Map coordinate to color while biasing its LOD
    The TXB instruction takes the first three components of its source 
    vector and maps them to s, t, and r.  These coordinates are used to 
    sample from the specified texture target on the specified texture
    image unit in a manner consistent with its parameters.  
    Additionally, the fourth component of the source vector is applied 
    to equation 3.14 as fragment_bias below to further bias the level of 
    detail.
    
     lambda'(x,y) = log2[p(x,y)] + 
                    clamp(texobj_bias + texunit_bias + fragment_bias)
    
    The resulting sample is mapped to RGBA as described in table 3.21 
    and written to the result vector.
      tmp = VectorLoad(op0);
      result = TextureSample(tmp.x, tmp.y, tmp.z, tmp.w, op1, op2);
    3.11.6.4  KIL: Kill fragment
    Rather than mapping a coordinate set to a color, this function
    prevents a fragment from receiving any future processing.  If any
    component of its source vector is negative, the processing of this 
    fragment will be discontinued and no further outputs to this 
    fragment will occur.  Subsequent stages of the GL pipeline will be 
    skipped for this fragment.
      tmp = VectorLoad(op0);
      if ((tmp.x < 0) || (tmp.y < 0) || 
          (tmp.z < 0) || (tmp.w < 0))
      {
          exit;
      }
    3.11.7  Program Matrices
    In addition to GL's conventional matrices, several additional 
    program matrices are available for use as program parameters.  These 
    matrices have names of the form MATRIX<i>_ARB where <i> is between 
    zero and <n>-1 where <n> is the value of the implementation-
    dependent constant MAX_PROGRAM_MATRICES_ARB.  The MATRIX<i>_ARB 
    constants obey MATRIX<i>_ARB = MATRIX0_ARB + <i>.  The value of 
    MAX_PROGRAM_MATRICES_ARB must be at least eight.  The maximum stack 
    depth for program matrices is defined by the 
    MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB and must be at least 1.
    3.11.8  Required Fragment Program State 
    The state required to support program objects of all targets 
    consists of:
      an integer for the program error position, initially -1;
      an array of ubytes for the program error string, initially empty;
      and the state that must be maintained to indicate which integers 
      are currently in use as program object names.
    The state required to support the fragment program target consists 
    of:
      a bit indicating whether or not fragment program mode is enabled, 
      initially disabled;
      a set of MAX_PROGRAM_ENV_PARAMETERS_ARB four-component floating-
      point program environment parameters, initially set to (0,0,0,0);
      an unsigned integer naming the currently bound fragment program, 
      initially zero;
    The state required for each fragment program object consists of:
      an unsigned integer indicating the program object name;
      an array of type ubyte containing the program string, initially 
      empty;
      an unsigned integer holding the length of the program string, 
      initially zero;
      an enum indicating the program string format, initially
      PROGRAM_FORMAT_ASCII_ARB;
      a bit indicating whether or not the program exceeds the native 
      limits;
      six unsigned integers holding the number of instruction (ALU,
      texture, and total), texture indirection, temporary variable, and 
      program parameter binding resources used by the program, initially 
      all zero;
      six unsigned integers holding the number of native instruction 
      (ALU, texture, and total), texture indirection, temporary 
      variable, and program parameter binding resources used by the 
      program, initially all zero;
      and a set of MAX_PROGRAM_LOCAL_PARAMETERS_ARB four-component 
      floating-point program local parameters, initially set to 
      (0,0,0,0).
    Initially, no fragment program objects exist.
Additions to Chapter 4 of the OpenGL 1.3 Specification (Per-Fragment
Operations and the Frame Buffer)
    None
Additions to Chapter 5 of the OpenGL 1.3 Specification (Special 
Functions)
    Modify Section 5.4, Display Lists (p. 191)
    (modify third paragraph, p. 195) ... These are IsList, GenLists, 
    ..., IsProgramARB, GenProgramsARB, and DeleteProgramsARB, as well as 
    IsEnabled and all the Get commands (chapter 6).
Additions to Chapter 6 of the OpenGL 1.3 Specification (State and
State Requests)
    Modify Section 6.1.2, Data Conversions (p. 198)
    (add before last paragraph, p. 198) The matrix selected by the 
    current matrix mode can be queried by calling GetBooleanv, 
    GetIntegerv, GetFloatv, and GetDoublev with <pname> set to 
    CURRENT_MATRIX_ARB; the matrix will be returned in transposed form 
    with <pname> set to TRANSPOSE_CURRENT_MATRIX_ARB.  The depth of the 
    selected matrix stack can be queried with <pname> set to 
    CURRENT_MATRIX_STACK_DEPTH_ARB.  Querying CURRENT_MATRIX_ARB and 
    CURRENT_MATRIX_STACK_DEPTH_ARB is the only means for querying the 
    matrix and matrix stack depth of the program matrices described in 
    section 3.11.7.
    (add to end of last paragraph, p. 199) Queries of texture state
    variables corresponding to texture coordinate processing unit
    (namely, TexGen state and enables, and matrices) will produce an
    INVALID_OPERATION error if the value of ACTIVE_TEXTURE is greater 
    than or equal to MAX_TEXTURE_COORDS_ARB.  All other texture state 
    queries will result in an INVALID_OPERATION error if the value of 
    ACTIVE_TEXTURE is greater than or equal to 
    MAX_TEXTURE_IMAGE_UNITS_ARB.
    Modify Section 6.1.11, Pointer and String Queries (p. 206)
    (modify last paragraph, p. 206) ... The possible values for <name> 
    are VENDOR, RENDERER, VERSION, EXTENSIONS, and 
    PROGRAM_ERROR_STRING_ARB.
    (add after last paragraph of section, p. 207) Queries of
    PROGRAM_ERROR_STRING_ARB return a pointer to an implementation-
    dependent program load error string.  If the last call to 
    ProgramStringARB failed to load a program, the returned string 
    describes at least one reason why the program failed to load.  If 
    the last call to ProgramStringARB successfully loaded a program, the 
    returned string may be empty (containing only a zero terminator) or 
    may contain one or more implementation-dependent warning messages.  
    The contents of the error string are guaranteed to remain constant 
    only until the next ProgramStringARB command, which may overwrite 
    the error string.
    Insert a new Section 6.1.12, Program Queries (p. 207), between 
    existing sections 6.1.11 and 6.1.12.
    6.1.12  Program Queries
    The commands
      void GetProgramEnvParameterdvARB(enum target, uint index,
                                       double *params);
      void GetProgramEnvParameterfvARB(enum target, uint index,
                                       float *params);
    obtain the current value for the program environment parameter 
    numbered <index> for the given program target <target>, and places 
    the information in the array <params>.  The error INVALID_ENUM is 
    generated if <target> specifies a nonexistent program target or a 
    program target that does not support program environment parameters.  
    The error INVALID_VALUE is generated if <index> is greater than or 
    equal to the implementation-dependent number of supported program 
    environment parameters for the program target.
    When <target> is FRAGMENT_PROGRAM_ARB, each program parameter 
    returned is an array of four values.
    The commands
      void GetProgramLocalParameterdvARB(enum target, uint index,
                                         double *params);
      void GetProgramLocalParameterfvARB(enum target, uint index,
                                         float *params);
    obtain the current value for the program local parameter numbered 
    <index> belonging to the program object currently bound to <target>, 
    and places the information in the array <params>.  The error 
    INVALID_ENUM is generated if <target> specifies a nonexistent 
    program target or a program target that does not support program 
    local parameters.  The error INVALID_VALUE is generated if <index> 
    is greater than or equal to the implementation-dependent number of 
    supported program local parameters for the program target.
    When the program target type is FRAGMENT_PROGRAM_ARB, each program
    local parameter returned is an array of four values.
    The command
      void GetProgramivARB(enum target, enum pname, int *params);
    obtains program state for the program target <target>, writing the 
    state into the array given by <params>.  GetProgramivARB can be used 
    to determine the properties of the currently bound program object or
    implementation limits for <target>.
    If <pname> is PROGRAM_LENGTH_ARB, PROGRAM_FORMAT_ARB, or
    PROGRAM_BINDING_ARB, GetProgramivARB returns one integer holding the
    program string length (in bytes), program string format, and program 
    name, respectively, for the program object currently bound to 
    <target>.
    If <pname> is MAX_PROGRAM_LOCAL_PARAMETERS_ARB or
    MAX_PROGRAM_ENV_PARAMETERS_ARB, GetProgramivARB returns one integer
    holding the maximum number of program local parameters or program
    environment parameters, respectively, supported for the program 
    target <target>.
    If <pname> is MAX_PROGRAM_INSTRUCTIONS_ARB, 
    MAX_PROGRAM_ALU_INSTRUCTIONS_ARB, MAX_PROGRAM_TEX_INSTRUCTIONS_ARB,
    MAX_PROGRAM_TEX_INDIRECTIONS_ARB, MAX_PROGRAM_TEMPORARIES_ARB,
    MAX_PROGRAM_PARAMETERS_ARB, or MAX_PROGRAM_ATTRIBS_ARB, 
    GetProgramivARB returns a single integer giving the maximum number 
    of total instructions, ALU instructions, texture instructions, 
    texture indirections, temporaries, parameters, and attributes that 
    can be used by a program of type <target>.  If <pname> is 
    PROGRAM_INSTRUCTIONS_ARB, PROGRAM_ALU_INSTRUCTIONS_ARB, 
    PROGRAM_TEX_INSTRUCTIONS_ARB, PROGRAM_TEX_INDIRECTIONS_ARB,
    PROGRAM_TEMPORARIES_ARB, PROGRAM_PARAMETERS_ARB, or
    PROGRAM_ATTRIBS_ARB, GetProgramivARB returns a single integer giving 
    the number of total instructions, ALU instructions, texture 
    instructions, texture indirections, temporaries, parameters, and
    attributes used by the current program for <target>.    
    If <pname> is MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, 
    MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, 
    MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, 
    MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, 
    MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, 
    MAX_PROGRAM_NATIVE_PARAMETERS_ARB, or 
    MAX_PROGRAM_NATIVE_ATTRIBS_ARB, GetProgramivARB returns a single 
    integer giving the maximum number of native instruction, ALU 
    instruction, texture instruction, texture indirection, temporary, 
    parameter, and attribute resources available to a program of type 
    <target>.  If <pname> is PROGRAM_NATIVE_INSTRUCTIONS_ARB, 
    PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, 
    PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, 
    PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, 
    PROGRAM_NATIVE_TEMPORARIES_ARB, PROGRAM_NATIVE_PARAMETERS_ARB, or
    PROGRAM_NATIVE_ATTRIBS_ARB, GetProgramivARB returns a single integer 
    giving the number of native instruction, ALU instruction, texture 
    instruction, texture indirection, temporary, parameter, and
    attribute resources consumed by the program currently bound to 
    <target>.  Native resource counts will reflect the results of 
    implementation-dependent scheduling and optimization algorithms 
    applied by the GL, as well as emulation of non-native features.  If 
    <pname> is PROGRAM_UNDER_NATIVE_LIMITS_ARB, GetProgramivARB returns 
    0 if the native resource consumption of the program currently bound 
    to <target> exceeds the number of available resources for any 
    resource type, and 1 otherwise.
    The command
      void GetProgramStringARB(enum target, enum pname, void *string);
    obtains the program string for the program object bound to <target> 
    and places the information in the array <string>.  <pname> must be
    PROGRAM_STRING_ARB.  <n> ubytes are returned into the array program 
    where <n> is the length of the program in ubytes, as returned by 
    GetProgramivARB when <pname> is PROGRAM_LENGTH_ARB.  The program 
    string is always returned using the format given when the program 
    string was specified.
    The command
      boolean IsProgramARB(uint program);
    returns TRUE if <program> is the name of a program object.  If 
    <program> is zero or is a non-zero value that is not the name of a 
    program object, or if an error condition occurs, IsProgramARB 
    returns FALSE.  A name returned by GenProgramsARB, but not yet 
    bound, is not the name of a program object.
    Modify Section 6.2, State Tables (p. 216)
    (add to caption of Table 6.5) When accessing the current texture
    coordinates (CURRENT_TEXTURE_COORDS) or the texture coordinates 
    associated with raster position (CURRENT_RASTER_TEXTURE_COORDS), the 
    active texture unit selector (ACTIVE_TEXTURE) must be less than the 
    implementation dependent maximum number of texture coordinate sets 
    (MAX_TEXTURE_COORDS_ARB).
    (add to caption of Table 6.8) When accessing the texture matrix 
    stack (TEXTURE_MATRIX, TRANSPOSE_TEXTURE_MATRIX) or the texture 
    matrix stack pointer (TEXTURE_STACK_DEPTH), the active texture unit 
    selector (ACTIVE_TEXTURE) must be less than the implementation 
    dependent maximum number of texture coordinate sets 
    (MAX_TEXTURE_COORDS_ARB).
    (split Table 6.17 into two tables, Texture Environment and Texture 
    Coordinate Generation; move active texture unit selector and texture 
    coordinate generation state to table 6.18; renumber subsequent 
    tables)
    
    (add to captions of Tables 6.14, 6.15, 6.16) The active texture unit 
    selector (ACTIVE_TEXTURE) identifies which texture object is 
    accessed, and must be less than the implementation dependent maximum 
    number of texture image units (MAX_TEXTURE_IMAGE_UNITS_ARB).
    (add to caption of Table 6.18) With the exception of ACTIVE_TEXTURE,
    the active texture unit selector (ACTIVE_TEXTURE) identifies which 
    texture coordinate set is accessed, and must be less than the 
    implementation dependent maximum number of texture coordinate sets 
    (MAX_TEXTURE_COORDS_ARB).
Additions to Appendix A of the OpenGL 1.3 Specification (Invariance)
    Add to end of Section A.3 (p. 242):
      Rule 4.  Fragment program instructions not relevant to the 
      calculation of any result must have no effect on that result.
      Rule 5.  Fragment program instructions relevant to the calculation 
      of any result must always produce the identical result.
    Instructions relevant to the calculation of a result are any 
    instructions in a sequence of instructions that eventually determine 
    the source values for the calculation under consideration.
    There is no guaranteed invariance between fragment colors generated 
    by conventional GL texturing mode and fragment colors generated by 
    fragment program mode.  Multi-pass rendering algorithms that require 
    rendering invariances to operate correctly should not mix 
    conventional GL fragment texturing mode with fragment program mode 
    for different rendering passes.  However, such algorithms will 
    operate correctly if the algorithms limit themselves to a single 
    mode of fragment color generation.
    There is no guaranteed invariance between the final z window
    coordinates of fragments processed by fragment programs that write
    depth values and fragments processed by any other means, even if the
    fragment programs in question simply copy the z value from the
    "fragment.position" binding.  Multi-pass rendering algorithms that
    use depth-replacing fragment programs should use depth-replacing
    fragment programs on each pass to guarantee identical z values.
    The texture sample chosen for a fragment of a primitive must be
    invariant between fragment program mode and conventional texture
    application mode subject to these conditions:
      1. All state with the exception of fragment program state is
         identical
      2. The primitives generating the fragments are identical
      3. The sample in the fragment program mode is the result of a
         'TEX' instruction (or a 'TXP' instruction with a unity q)
      4. The texture coordinate operand for the texture instruction uses 
         the same texture coordinate set as the conventional mode sample
      5. The texture coordinate operand for the texture instruction has 
         not been the result of any other operations in the fragment 
         program
Additions to the AGL/GLX/WGL Specifications
    Program objects are shared between AGL/GLX/WGL rendering contexts if
    and only if the rendering contexts share display lists.  No change
    is made to the AGL/GLX/WGL API.
    Changes to program objects shared between multiple rendering 
    contexts will be serialized (i.e., the changes will occur in a 
    specific order).  
    Changes to a program object made by one rendering context are not
    guaranteed to take effect in another rendering context until the 
    other calls BindProgram to bind the program object.  
    When a program object is deleted by one rendering context, the 
    object itself is not destroyed until it is no longer the current 
    program object in any context.  However, the name of the deleted 
    object is removed from the program object name space, so the next 
    attempt to bind a program using the same name will create a new 
    program object.  Recall that destroying a program object bound in 
    the current rendering context effectively unbinds the object being 
    destroyed.
Dependencies on OpenGL 1.4
    If OpenGL 1.4 is not supported, the modified equation for the 
    calculation of level of detail by the TXB instruction in 3.11.6.3 
    should read
      lambda'(x,y) = log2[p(x,y)] + 
                     clamp(texunit_bias + fragment_bias)
Dependencies on EXT_vertex_weighting and ARB_vertex_blend
    If EXT_vertex_weighting and ARB_vertex_blend are both not supported,
    all discussions of multiple modelview matrices should be removed.  
    In particular, the line in the grammar
      <stateMatrixName>      ::= "modelview" <stateOptModMatNum>
    should be changed to
      <stateMatrixName>      ::= "modelview"
    and the rules <stateOptModMatNum> and <stateModMatNum> should be 
    deleted.  The first line of Table X.2.7 should be modified to read:
      Binding                               Underlying State
      ------------------------------------  ---------------------------
        state.matrix.modelview              modelview matrix
    The caption for Table X.2.7 should be modified to exclude optional
    modelview matrix number.  Subsequent references to "modelview matrix 
    zero" and "modelview matrix 1" should be changed to "modelview 
    matrix" and the example "state.matrix.modelview[1].row[0]" should be 
    changed to "state.matrix.modelview.row[0]".
Dependencies on ARB_matrix_palette:
    If ARB_matrix_palette is not supported, all discussions of the 
    matrix palette should be removed.
    In particular, the line
      "palette" "[" <statePaletteMatNum> "]"
    should be removed from the <stateMatrixName> grammar rule, and the
    <statePaletteMatNum> grammar rule should be removed entirely.
    "state.matrix.palette[n]" should be removed from Table X.2.7.
Dependencies on ARB_transpose_matrix
    If ARB_transpose_matrix is not supported, the discussion of
    TRANSPOSE_CURRENT_MATRIX_ARB in the edits to section 6.1.2 should be
    removed.
Dependencies on EXT_fog_coord
    If EXT_fog_coord is not supported, references to "fog coordinate"
    in the definition of the "fragment.fogcoord" attribute should be 
    removed.
Dependencies on EXT_texture_rectangle
    If NV_texture_rectangle is not supported, the discussion of the
    rectangle (non-power-of-two) texture target in section 3.11.6 should
    be removed, and the line
      "RECT"
    should be removed from the <texTarget> grammar rule.
Interactions with ARB_shadow
    The texture comparison introduced by ARB_shadow can be expressed in 
    terms of a fragment program, and in fact use the same internal 
    resources on some implementations.  Therefore, if fragment program 
    mode is enabled, the GL behaves as if TEXTURE_COMPARE_MODE_ARB is 
    NONE.
Interactions with ARB_vertex_program
    The program object management entrypoints described in sections
    2.14.1 (for vertex programs) and 3.11.1 (for fragment programs)
    are shared by both program targets.  The PROGRAM_ERROR_STRING_ARB
    and program queries in sections 6.1.11 and 6.1.12 are also shared,
    as are all common tokens.
    The Errors section should be modified to generate INVALID_OPERATION
    from the Get command with argument CURRENT_MATRIX_ARB, 
    TRANSPOSE_CURRENT_MATRIX_ARB, and CURRENT_MATRIX_STACK_DEPTH_ARB
    when the current matrix mode is TEXTURE.
    In the presence of ARB_vertex_program, ARB_fragment_program must 
    recognize and return appropriate values for the GetProgram <pname> 
    tokens introduced in that spec but not otherwise shared by 
    ARB_fragment_program:
        PROGRAM_ADDRESS_REGISTERS_ARB                   0x88B0
        MAX_PROGRAM_ADDRESS_REGISTERS_ARB               0x88B1
        PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB            0x88B2
        MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB        0x88B3
    The following tables list new program object state and 
    implementation-dependent state:
    Get Value                        Type   Get Command          Initial Value    Description             Sec       Attrib
    --------------------             -----  -------------------  ---------------  ----------------------  --------  ------
    PROGRAM_ADDRESS_REGISTERS_ARB    Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                  address registers
    PROGRAM_NATIVE_ADDRESS_          Z+     GetProgramivARB      0                bound program native    6.1.12    -
        REGISTERS_ARB                                                             address registers
    Table X.7.  Program Object State.  Program object queries return attributes of
    the program object currently bound to the program target <target>.
                                                                Minimum
    Get Value                            Type  Get Command      Value    Description             Sec.       Attrib
    ---------                            ----  -----------      -------  -----------             ----       ------
    MAX_PROGRAM_ADDRESS_REGISTERS_ARB    Z+    GetProgramivARB  0        maximum program         6.1.12     -
                                                                         address registers
    MAX_PROGRAM_NATIVE_ADDRESS_          Z+    GetProgramivARB  0        maximum program native  6.1.12     -
        REGISTERS_ARB                                                    address registers
    Table X.10.  New Implementation-Dependent Values Introduced by
    ARB_vertex_program.
    In the presence of ARB_fragment_program, ARB_vertex_program must
    recognize and return appropriate values for the GetProgram <pname> 
    tokens introduced in this spec.  The following tables list new 
    program object state and implementation-dependent state:
    Get Value                            Type   Get Command          Initial Value    Description             Sec       Attrib
    --------------------                 -----  -------------------  ---------------  ----------------------  --------  ------
    PROGRAM_ALU_INSTRUCTIONS_ARB         Z+     GetProgramivARB      0                maximum program         6.1.12    -
                                                                                      ALU instructions
    PROGRAM_TEX_INSTRUCTIONS_ARB         Z+     GetProgramivARB      0                maximum program         6.1.12    -
                                                                                      texture instructions
    PROGRAM_TEX_INDIRECTIONS_ARB         Z+     GetProgramivARB      0                maximum program         6.1.12    -
                                                                                      texture indirections
    PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB  Z+     GetProgramivARB      0                maximum program native  6.1.12    -
                                                                                      ALU instructions
    PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB  Z+     GetProgramivARB      0                maximum program native  6.1.12    -
                                                                                      texture instructions
    PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB  Z+     GetProgramivARB      0                maximum program native  6.1.12    -
                                                                                      texture indirections
    Table X.7.  Program Object State.  Program object queries return attributes of
    the program object currently bound to the program target <target>.
                                                                    Minimum
    Get Value                                Type  Get Command      Value    Description             Sec.       Attrib
    ---------                                ----  -----------      -------  -----------             ----       ------
    MAX_PROGRAM_ALU_INSTRUCTIONS_ARB         Z+    GetProgramivARB  0        Number of frag. prg.    6.1.12     -
                                                                             ALU instructions
    MAX_PROGRAM_TEX_INSTRUCTIONS_ARB         Z+    GetProgramivARB  0        Number of frag. prg.    6.1.12     -
                                                                             texture instructions
    MAX_PROGRAM_TEX_INDIRECTIONS_ARB         Z+    GetProgramivARB  0        Number of frag. prg.    6.1.12     -
                                                                             texture indirections       
    MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB  Z+    GetProgramivARB  0        maximum program native  6.1.12     -
                                                                             ALU instructions
    MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB  Z+    GetProgramivARB  0        maximum program native  6.1.12     -
                                                                             texture instructions
    MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB  Z+    GetProgramivARB  0        maximum program native  6.1.12     -
                                                                             texture indirections
    Table X.10.  New Implementation-Dependent Values Introduced by
    ARB_fragment_program.
Interactions with ATI_fragment_shader
    The existing ATI_fragment_shader extension, if supported, also
    provides a similar fragment programming model.  Mixing the two
    models in a single application is possible but not recommended.
    FRAGMENT_PROGRAM_ARB has priority over FRAGMENT_SHADER_ATI if
    both are enabled.
Interactions with NV_fragment_program
    The NV_fragment_program extension, if supported, also provides a 
    similar programming model.  This extension is incompatible with
    NV_fragment_program in a number of different ways.  Mixing the two 
    models in a single application is possible but not recommended.  The 
    interactions between the extensions are defined below.
    Functions, enumerants, and programs defined in NV_fragment_program 
    are called "NV functions", "NV enumerants", and "NV programs," 
    respectively.  Functions, enumerants, and programs defined in 
    ARB_fragment_program are called "ARB functions", "ARB enumerants", 
    and "ARB programs," respectively.
    The following GL state is identical in the two extensions:
      - Fragment program mode enable.  The NV and ARB enumerants have
        different values, but the same effect.
      - Program error position.
      - Program error string.
      - NV_fragment_program and ARB_fragment_program "program local
        parameters."
      - Fragment program names, targets, formats, program string, 
        program string lengths, and residency information.  The ARB and 
        NV query functions operate differently.  The ARB query function 
        does not allow queries of target (passed in to the query) and 
        residency information.  The NV query function does not allow 
        queries of program name (passed in to the query) or format.  The 
        format of NV programs is always PROGRAM_FORMAT_ASCII_ARB.
      - Program object name space.  Program objects are created 
        differently in the NV and ARB specs.  Under the NV spec, program 
        objects are created by calling LoadProgramNV.  Under the ARB 
        spec, program objects are created by calling BindProgramARB with 
        an unused program name.
    The following state is provided only by ARB_fragment_program:
      - Program environment parameters.
      - Implementation-dependent limits on the number of instructions, 
        ALU instructions, texture instructions, texture indirections, 
        program parameters, fragment attributes, resource counts, and 
        native resource counts.  The instruction limit is baked into the 
        NV spec.  Implementations supporting NV_fragment_program have no 
        specific restrictions on the number of ALU instructions, texture 
        instructions, texture indirections, or fragment attributes used.  
        Such implementations also have no limit on program parameters 
        used, except that no more than one may be used by any single 
        program instruction.
    The following state is provided only by NV_fragment_program:
      - Named program parameters (variables defined in the program text 
        and updated by name).
    The following are additional functional differences between
    ARB_fragment_program and NV_fragment_program:
      - NV programs use a set of register names, with no support for
        user-defined variables (other than parameters in the program).  
        ARB programs provide no support for fixed variable names; all 
        variables must be declared, explicitly or implicitly, in the 
        program.
      - ARB programs support parameter variables that can be bound to 
        selected GL state variables, and are updated automatically when 
        the underlying state changes.  NV programs provide no such 
        support; applications must set program parameters themselves.
      - ARB_fragment_program doesn't provide explicit support for 
        multiple data types (fx12, fp16, fp32) described in 
        NV_fragment_program, and provides no mechanism for controlling 
        the precision used to carry out arithmetic operations.
      - ARB_fragment_program doesn't support condition codes, 
        conditional writemasks, or the "C" instruction suffix that 
        specifies a condition code update.
      - ARB_fragment_program doesn't support an absolute value operator 
        that can be applied to a source vector as it is loaded.
      - ARB_fragment_program doesn't define behavior for many floating-
        point special cases.  On platforms where NV_fragment_program is 
        supported, ARB programs will have the same special-case 
        behavior.
      - Language to declare program parameters is slightly different
        (NV_fragment_program has "DECLARE" and "DEFINE"; 
        ARB_fragment_program has "PARAM").
      - NV_fragment_program provides a number of instructions not found 
        in ARB_fragment_program:
          * DDX, DDY:  partial derivatives relative to x and  y.
          * "PK*" and "UP*":  packing and unpacking instructions.
          * RFL:  reflection vector.
          * SEQ, SFL, SGT, SLE, SNE, STR:  set on equal, false, greater 
            than, less than or equal, not equal, and true, respectively.
          * TXD:  texture lookup w/partials.
          * X2D:  2D coordinate transformation.
      - ARB_fragment_program provides several instructions not found in
        NV_fragment_program, and there are a few instruction set 
        differences:
          * ABS:  absolute value.  ABS instructions are unnecessary in 
              NV_fragment_program because of the free absolute value on 
              input operator.  Equivalent to:
           
                 MOV dst, |src|;
          * CMP:  compare.  Roughly equivalent to the following 
              sequence, but may be optimized further:
                 SLT tmp, src0; 
                 LRP dst, tmp, src1, src2;
          * DPH:  homogenous dot product.  Equivalent to:
                 DP3 tmp, src0, src1;
                 ADD dst, tmp, src0.w;
          * KIL:  kill fragment.  Both extensions support this 
              instruction, but the ARB instruction takes a vector 
              operand rather than a condition code.
          * SCS:  sine/cosine.  Emulated using the separate SIN and COS
              instructions in NV_fragment_program, which also have no
              restriction on the input values.
          * SWZ:  extended swizzle.  On NV_fragment_program platforms, 
              this instruction will be emulated using a single MAD 
              instruction and a program parameter constant.
          * TXB:  texture sample with bias.  Not exposed in the
              NV_fragment_program API.
          * XPD:  cross product.  Emulated using a MUL and a MAD 
              instruction.
GLX Protocol
     The following rendering commands are sent to the server as part of
     a glXRender request:
        BindProgramARB
            2           12              rendering command length
            2           4180            rendering command opcode
            4           ENUM            target
            4           CARD32          program
        ProgramEnvParameter4fvARB
            2           32              rendering command length
            2           4184            rendering command opcode
            4           ENUM            target
            4           CARD32          index
            4           FLOAT32         params[0]
            4           FLOAT32         params[1]
            4           FLOAT32         params[2]
            4           FLOAT32         params[3]
        ProgramEnvParameter4dvARB
            2           44              rendering command length
            2           4185            rendering command opcode
            4           ENUM            target
            4           CARD32          index
            8           FLOAT64         params[0]
            8           FLOAT64         params[1]
            8           FLOAT64         params[2]
            8           FLOAT64         params[3]
        ProgramLocalParameter4fvARB
            2           32              rendering command length
            2           4215            rendering command opcode
            4           ENUM            target
            4           CARD32          index
            4           FLOAT32         params[0]
            4           FLOAT32         params[1]
            4           FLOAT32         params[2]
            4           FLOAT32         params[3]
        ProgramLocalParameter4dvARB
            2           44              rendering command length
            2           4216            rendering command opcode
            4           ENUM            target
            4           CARD32          index
            8           FLOAT64         params[0]
            8           FLOAT64         params[1]
            8           FLOAT64         params[2]
            8           FLOAT64         params[3]
    The ProgramStringARB is potentially large, and hence can be sent in 
    a glXRender or glXRenderLarge request.
        ProgramStringARB
            2           16+len+p        rendering command length
            2           4217            rendering command opcode
            4           ENUM            target
            4           ENUM            format
            4           sizei           len
            len         LISTofBYTE      program
            p                           unused, p=pad(len)
         If the command is encoded in a glxRenderLarge request, the 
         command opcode and command length fields above are expanded to 
         4 bytes each:
            4           16+len+p        rendering command length
            4           4217            rendering command opcode
    The remaining commands are non-rendering commands.  These commands 
    are sent separately (i.e., not as part of a glXRender or 
    glXRenderLarge request), using the glXVendorPrivateWithReply 
    request:
        DeleteProgramsARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           4+n             request length
            4           1294            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           INT32           n
            n*4         LISTofCARD32    programs
        GenProgramsARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           4               request length
            4           1295            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           INT32           n
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           n               reply length
            24                          unused
            n*4         LISTofCARD322   programs
        GetProgramEnvParameterfvARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           6               request length
            4           1296            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           CARD32          index
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m=(n==1?0:n)
            4                           unused
            4           CARD32          n (number of parameter components)
            if (n=1) this follows:
            4           FLOAT32         params
            12                          unused
            otherwise this follows:
            16                          unused
            n*4         LISTofFLOAT32   params
        GetProgramEnvParameterdvARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           6               request length
            4           1297            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           CARD32          index
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m=(n==1?0:n*2)
            4                           unused
            4           CARD32          n (number of parameter components)
            if (n=1) this follows:
            8           FLOAT64         params
            8                           unused
            otherwise this follows:
            16                          unused
            n*8         LISTofFLOAT64   params
        GetProgramLocalParameterfvARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           6               request length
            4           1305            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           CARD32          index
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m=(n==1?0:n)
            4                           unused
            4           CARD32          n (number of parameter components)
            if (n=1) this follows:
            4           FLOAT32         params
            12                          unused
            otherwise this follows:
            16                          unused
            n*4         LISTofFLOAT32   params
        GetProgramLocalParameterdvARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           6               request length
            4           1306            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           CARD32          index
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m=(n==1?0:n*2)
            4                           unused
            4           CARD32          n (number of parameter components)
            if (n=1) this follows:
            8           FLOAT64         params
            8                           unused
            otherwise this follows:
            16                          unused
            n*8         LISTofFLOAT64   params
        GetProgramivARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           5               request length
            4           1307            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           m               reply length, m=(n==1?0:n)
            4                           unused
            4           CARD32          n
            if (n=1) this follows:
            4           INT32           params
            12                          unused
            otherwise this follows:
            16                          unused
            n*4         LISTofINT32     params
        GetProgramStringARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           5               request length
            4           1308            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           ENUM            target
            4           ENUM            pname
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           (n+p)/4         reply length
            4                           unused
            4           CARD32          n
            16                          unused
            n           STRING          program
            p                           unused, p=pad(n)
        IsProgramARB
            1           CARD8           opcode (X assigned)
            1           17              GLX opcode (glXVendorPrivateWithReply)
            2           4               request length
            4           1304            vendor specific opcode
            4           GLX_CONTEXT_TAG context tag
            4           INT32           n
          =>
            1           1               reply
            1                           unused
            2           CARD16          sequence number
            4           0               reply length
            4           BOOL32          return value
            20                          unused
Errors
  
    The error INVALID_OPERATION is generated by ProgramStringARB if the
    program string <string> is syntactically incorrect or violates any
    semantic restriction of the execution environment of the specified 
    program target <target>.  The error INVALID_OPERATION may also be
    generated by ProgramStringARB if the specified program would exceed 
    native resource limits of the implementation.
    The error INVALID_OPERATION is generated by BindProgramARB if 
    <program> is the name of a program whose target does not match 
    <target>.
    The error INVALID_VALUE is generated by commands 
    ProgramEnvParameter{fd}ARB, ProgramEnvParameter{fd}vARB, and 
    GetProgramEnvParameter{fd}vARB if <index> is greater than or equal 
    to the value of MAX_PROGRAM_ENV_PARAMETERS_ARB corresponding to the 
    program target <target>.
    The error INVALID_VALUE is generated by commands 
    ProgramLocalParameter4{fd}ARB, ProgramLocalParameter4{fd}vARB, and
    GetProgramLocalParameter{fd}vARB if <index> is greater than or equal 
    to the value of MAX_PROGRAM_LOCAL_PARAMETERS_ARB corresponding to 
    the program target <target>.
    The error INVALID_OPERATION is generated if Begin, RasterPos, or any
    command that performs an explicit Begin is called when fragment 
    program mode is enabled and the currently bound fragment program 
    object does not contain a valid fragment program.
    The error INVALID_OPERATION is generated by any command accessing
    texture coordinate processing state if the texture unit number
    corresponding to the current value of ACTIVE_TEXTURE is greater than
    or equal to the implementation-dependent constant 
    MAX_TEXTURE_COORDS_ARB.  Such commands include: GetTexGen{if}v; 
    TexGen{ifd}, TexGen{ifd}v; Disable, Enable, IsEnabled with argument 
    TEXTURE_GEN_{STRQ}; Get with argument CURRENT_TEXTURE_COORDS, 
    CURRENT_RASTER_TEXTURE_COORDS, TEXTURE_STACK_DEPTH, TEXTURE_MATRIX, 
    TRANSPOSE_TEXTURE_MATRIX; when the current matrix mode is TEXTURE, 
    Frustum, LoadIdentity, LoadMatrix{fd}, LoadTransposeMatrix{fd}, 
    MultMatrix{fd}, MultTransposeMatrix{fd}, Ortho, PopMatrix, 
    PushMatrix, Rotate{fd}, Scale{fd}, Translate{fd}.
    The error INVALID_OPERATION is generated by any command accessing
    texture image processing state if the texture unit number 
    corresponding to the current value of ACTIVE_TEXTURE is greater than
    or equal to the implementation-dependent constant 
    MAX_TEXTURE_IMAGE_UNITS_ARB.  Such commands include: BindTexture;
    GetCompressedTexImage, GetTexEnv{if}v, GetTexImage, 
    GetTexLevelParameter{if}v, GetTexParameter{if}v; TexEnv{if}, 
    TexEnv{if}v, TexParameter{if}, TexParameter{if}v; Disable, Enable, 
    IsEnabled with argument TEXTURE_{123}D, TEXTURE_CUBE_MAP; Get with 
    argument TEXTURE_BINDING_{123}D, TEXTURE_BINDING_CUBE_MAP; 
    CompressedTexImage{123}D, CompressedTexSubImage{123}D, 
    CopyTexImage{12}D, CopyTexSubImage{123}D, TexImage{123}D, 
    TexSubImage{123}D.
New State
    Get Value                        Type    Get Command    Initial Value Description         Section       Attribute
    -------------------------------  ------  -------------  ------------- ------------------  ------------  ------------
    FRAGMENT_PROGRAM_ARB             B       IsEnabled      False         fragment program    3.8           enable
                                                                          enable
    -                                24+xR4  GetProgramEnv- (0,0,0,0)     program environment 3.11.1        -
                                             ParameterARB                 parameters
    PROGRAM_ERROR_POSITION_ARB       Z       GetIntegerv    -1            last program error  3.11.1        -
                                                                          position
    PROGRAM_ERROR_STRING_ARB         0+xub   GetString      ""            last program error  3.11.1        -
                                                                          string
    Table X.6.  New Accessible State Introduced by ARB_fragment_program.
    Get Value                            Type   Get Command          Initial Value    Description             Sec       Attrib
    --------------------                 -----  -------------------  ---------------  ----------------------  --------  ------
    PROGRAM_BINDING_ARB                  Z+     GetProgramivARB      object-specific  bound program name      6.1.12    -
    PROGRAM_LENGTH_ARB                   Z+     GetProgramivARB      0                bound program length    6.1.12    -
    PROGRAM_FORMAT_ARB                   Z1     GetProgramivARB      PROGRAM_FORMAT_  bound program format    6.1.12    -
                                                                     ASCII_ARB
    PROGRAM_STRING_ARB                   ubxn   GetProgramStringARB  (empty)          bound program string    6.1.12    -
    PROGRAM_INSTRUCTIONS_ARB             Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      total instructions
    PROGRAM_ALU_INSTRUCTIONS_ARB         Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      ALU instructions
    PROGRAM_TEX_INSTRUCTIONS_ARB         Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      texture instructions
    PROGRAM_TEX_INDIRECTIONS_ARB         Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      texture indirections
    PROGRAM_TEMPORARIES_ARB              Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      temporaries
    PROGRAM_PARAMETERS_ARB               Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      parameter bindings
    PROGRAM_ATTRIBS_ARB                  Z+     GetProgramivARB      0                bound program           6.1.12    -
                                                                                      attribute bindings
    PROGRAM_NATIVE_INSTRUCTIONS_ARB      Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      instructions
    PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB  Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      ALU instructions
    PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB  Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      texture instructions
    PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB  Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      texture indirections
    PROGRAM_NATIVE_TEMPORARIES_ARB       Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      temporaries
    PROGRAM_NATIVE_PARAMETERS_ARB        Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      parameter bindings
    PROGRAM_NATIVE_ATTRIBS_ARB           Z+     GetProgramivARB      0                bound program native    6.1.12    -
                                                                                      attribute bindings
    PROGRAM_UNDER_NATIVE_LIMITS_ARB      B      GetProgramivARB      0                bound program under     6.1.12    -
                                                                                      native resource limits
    -                                    24+xR4 GetProgramLocal-     (0,0,0,0)        bound program local     3.11.1    -
                                                ParameterARB                          parameter value
    Table X.7.  Program Object State.  Program object queries return attributes of
    the program object currently bound to the program target <target>.
    Get Value    Type    Get Command   Initial Value  Description                Sec       Attribute
    ---------    ------  -----------   -------------  -------------------------  --------  ---------
    -            16+xR4  -             undefined      temporary registers        3.11.3.3  -
    -            2xR4    -             undefined      fragment result registers  3.11.3.4  -
    Table X.8.  Fragment Program Per-fragment Execution State.  All per-fragment
    execution state registers are uninitialized at the beginning of program
    execution.
    Get Value                          Type      Get Command      Initial Value  Description          Sec      Attribute
    ------------------------------     --------  --------------   -------------  -------------------  -------  ---------
    CURRENT_MATRIX_ARB                 m*n*xM^4  GetFloatv        Identity       current matrix       6.1.2    -
    CURRENT_MATRIX_STACK_DEPTH_ARB     m*Z+      GetIntegerv      1              current stack depth  6.1.2    -
    Table X.9.  Current matrix state where m is the total number of matrices
    including texture matrices and program matrices and n is the number of
    matrices on each particular matrix stack.  Note that this state is aliased
    with existing matrix state.
New Implementation Dependent State
                                                                    Minimum
    Get Value                                Type  Get Command      Value      Description             Sec.       Attrib
    ---------                                ----  -----------      -------    -----------             ----       ------
    MAX_TEXTURE_COORDS_ARB                   Z+    GetIntegerv      2          number of texture       2.7        -
                                                                               coordinate sets
    MAX_TEXTURE_IMAGE_UNITS_ARB              Z+    GetIntegerv      2          number of texture       2.10.2     -
                                                                               image units
    MAX_PROGRAM_ENV_PARAMETERS_ARB           Z+    GetProgramivARB  24         maximum program         3.11.1     -
                                                                               env parameters
    MAX_PROGRAM_LOCAL_PARAMETERS_ARB         Z+    GetProgramivARB  24         maximum program         3.11.1     -
                                                                               local parameters
    MAX_PROGRAM_MATRICES_ARB                 Z+    GetIntegerv      8 (not to  maximum number of       3.11.7     -
                                                                    exceed 32) program matrices
    MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB       Z+    GetIntegerv      1          maximum program         3.11.7     -
                                                                               matrix stack depth
    MAX_PROGRAM_INSTRUCTIONS_ARB             Z+    GetProgramivARB  72         maximum program         6.1.12     -
                                                                               total instructions
    MAX_PROGRAM_ALU_INSTRUCTIONS_ARB         Z+    GetProgramivARB  48         number of frag. prg.    6.1.12     -
                                                                               ALU instructions
    MAX_PROGRAM_TEX_INSTRUCTIONS_ARB         Z+    GetProgramivARB  24         number of frag. prg.    6.1.12     -
                                                                               texture instructions
    MAX_PROGRAM_TEX_INDIRECTIONS_ARB         Z+    GetProgramivARB  4          number of frag. prg.    6.1.12     -
                                                                               texture indirections       
    MAX_PROGRAM_TEMPORARIES_ARB              Z+    GetProgramivARB  16         maximum program         6.1.12     -
                                                                               temporaries
    MAX_PROGRAM_PARAMETERS_ARB               Z+    GetProgramivARB  24         maximum program         6.1.12     -
                                                                               parameter bindings
    MAX_PROGRAM_ATTRIBS_ARB                  Z+    GetProgramivARB  10         maximum program         6.1.12     -
                                                                               attribute bindings
    MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB      Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               total instructions
    MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB  Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               ALU instructions
    MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB  Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               texture instructions
    MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB  Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               texture indirections
    MAX_PROGRAM_NATIVE_TEMPORARIES_ARB       Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               temporaries
    MAX_PROGRAM_NATIVE_PARAMETERS_ARB        Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               parameter bindings
    MAX_PROGRAM_NATIVE_ATTRIBS_ARB           Z+    GetProgramivARB  -          maximum program native  6.1.12     -
                                                                               attribute bindings
    Table X.10.  New Implementation-Dependent Values Introduced by
    ARB_fragment_program.  Values queried by GetProgram require a <pname> of
    FRAGMENT_PROGRAM_ARB.
Sample Usage
    The following program shows how to perform a simple modulation 
    between the interpolated color and a single texture:
      !!ARBfp1.0
      # Simple program to show how to code up the default texture environment
      ATTRIB tex = fragment.texcoord;      #first set of texture coordinates
      ATTRIB col = fragment.color.primary; #diffuse interpolated color
      OUTPUT outColor = result.color;
      TEMP tmp;
      TXP tmp, tex, texture, 2D;           #sample the texture
    
      MUL outColor, tmp, col;              #perform the modulation
    
      END 
    The following is an example the simulates a chrome surface:
      !!ARBfp1.0
      ########################
      # Input Textures:
      #-----------------------
      # Texture 0 contains the default 2D texture used for general mapping
      # Texture 2 contains a 1D pointlight falloff map
      # Texture 3 contains a 2D map for calculating specular lighting
      # Texture 4 contains normalizer cube map
      #
      # Input Texture Coordinates:
      #-----------------------
      # TexCoord1 contains the calculated normal
      # TexCoord2 contains the light to vertex vector
      # TexCoord3 contains the half-vector in tangent space
      # TexCoord4 contains the light vector in tangent space
      # TexCoord5 contains the eye vector in tangent space
      ########################
      TEMP     NdotH, lV, L;
      ALIAS    diffuse  = L;
      PARAM       half  = { 0.5, 0.5, 0.5, 0.5 };
      ATTRIB   norm_tc  = fragment.texcoord[1];
      ATTRIB     lv_tc  = fragment.texcoord[2];
      ATTRIB   half_tc  = fragment.texcoord[3];
      ATTRIB  light_tc  = fragment.texcoord[4];
      ATTRIB    eye_tc  = fragment.texcoord[5];
      OUTPUT      oCol  = result.color;
      TEX     L, light_tc, texture[4], CUBE; # Sample cube map normalizer
      # Calculate diffuse lighting (N.L)
      SUB     L, L, half;              # Bias L and then multiply by 2
      ADD     L, L, L;
      DP3     diffuse, norm_tc, L;     # N.L
      # Calculate specular lighting component { (N.H), |H|^2 }
      DP3     NdotH.x, norm_tc, half_tc;
      DP3     NdotH.y, half_tc, half_tc;
      DP3     lV.x, lv_tc, lv_tc;     # lV = (|light to vertex|)^2
      #############
      # Pass 2
      #############
      TEMP     base, specular;
      ALIAS    atten  = lV;
      TEX      base,     eye_tc, texture[0], 2D; # sample enviroment map using eye vector
      TEX      atten,    lV,     texture[2], 1D; # Sample attenuation map
      TEX      specular, NdotH,  texture[3], 2D; # Sample specular NHHH map= (N.H)^256
      # specular = (N.H)^256 * (N.L) 
      # this ensures a pixel is only lit if facing the light (since the specular
      # exponent makes negative N.H positive we must do this)
      MUL      specular, specular, diffuse;
      # specular = specular * environment map
      MUL      specular, base, specular;
      # diffuse = diffuse * environment map
      MUL      diffuse, base, diffuse;
      # outColor = (specular * environment map) + (diffuse * environment map)
      ADD      base, specular, diffuse;
      # Apply point light attenutaion
      MUL      oCol, base, atten.r;
      END
Revision History
   Date: 8/22/2003
   Revision: 26
      - Added list of commands generating errors when active texture 
        unit selector is out of range.
      - Fixed typo in <stateMatrixItem> rule.
      - Clarified behavior of fragment.position.z with respect to depth 
        offset.
   Date: 2/26/2003
   Revision: 25
      - Fixed description of KIL instruction to reflect less than zero
        test, not less than or equal to zero.
      - Clarified the processing of incoming and outgoing depths and
        colors to reflect the conversion to floating-point on input and
        the conversion to fixed-point on output.
   Date: 1/10/2003
   Revision: 24
      - Fixed bug where "state.matrix.mvp" was specified incorrectly.
        It should be P*M0 rather than M0*P.
      - Added issue warning about CMP opcode's order of operands.
   Date: 10/22/2002
   Revision: 23
      - Fixed reference to <extSwizComp> rule in 3.11.5.28.  Instead
        reference both <xyzwExtSwizComp> and <rgbaExtSwizComp> rules.
   Date: 10/02/2002
   Revision: 22
      - Fixed typo in section 3.11.1, where 8 program environment and
        8 program local parameters are listed as the minimums instead
        of 24 of each.  Table X.10 had the correct values.
      - Fixed <stateTexEnvItem> to refer to legacy texture units.
      - Fixed typos in issue 29 pseudo-code, added some clarification.
   Date: 9/19/2002
   Revision: 21
      - Added clarifying paragraph for native texture indirection 
        counting, offering examples of possible cases where texture
        indirections may be increased.
      - Fixed typos in issues 25 and 29.
   Date: 9/16/2002
   Revision: 20
      - Added precision hint program options.
      - Fixed various typos, reworded some parts for consistency.
      - Updated issues list.
   Date: 9/13/2002
   Revision: 19
      - Promoted minimum precision of texture coordinates in 2.1.1.
      - Added ARB_fog_* program options.
      - Removed modification to 3.9, put clamps in 3.11.4.4.
      - Made 'texture' a reserved keyword in the grammar.
      - Fixed various typos.
      - Updated section 3.11.6.
      - Updated issues list.
   Date: 9/11/2002
   Revision: 18
      - Updated for consistency with ARB_vertex_program revision 36.
      - Depth output moved to 3rd component of result.depth.
      - Fixed various typos, reworded things in many places.
      - Added NV_fragment_program interactions.
      - Updated issues list.
   Date: 9/09/2002
   Revision: 17
      - Added fogcoord and position attributes.
      - Moved fragment program section to 3.11, after fog.
      - Changed MAX_TEXTURE_UNITS/MAX_AUX_TEXTURE_UNITS to 
        MAX_TEXTURE_COORDS/MAX_TEXTURE_IMAGE_UNITS.
      - Removed TRC and MOD instructions.
      - Added SIN and COS instructions.
      - Added more clarity to resource consumption wording.
      - Added invariance wording concerning depth-replacement.
      - Added rule that a program that fails to load must always fail to
        load, regardless of GL state.
      - Updated issues list.
   Date: 8/30/2002
   Revision: 16
      - Improved texture indirection description.
      - Defined result of sample from incomplete texture as (0,0,0,1).
      - Removed PROGRAMS_LOAD_OVER_NATIVE_LIMITS_ARB per-target query.
      - Allowed ProgramStringARB to fail on non-native programs.
      - Updated issues list.
   Date: 8/28/2002
   Revision: 15
      - Updated for consistency with ARB_vertex_program revision 35.
      - Added PROGRAMS_LOAD_OVER_NATIVE_LIMITS_ARB per-target query.
      - Changed MAX_AUX_TEXTURE_UNITS_ARB enum value.
      - Updated issues list.
   Date: 8/22/2002
   Revision: 14
      - Added sine/cosine instruction (SCS).
      - Updated texture sample grammar, replaced texenables hierarchy.
      - Added EXT_vertex_weighting and ARB_vertex_blend dependency.
      - Updated issues list.
   Date: 8/14/2002
   Revision: 13
      - Fixed <paramConstant> grammar rule.
      - Updated issues list.
   Date: 8/06/2002
   Revision: 12
      - Fixed various typos.
      - Updated issues list.
      - Added wording to 3.10.3.6 to reflect that native resource
        consumption may increase due to emulated instructions.
   Date: 7/29/2002
   Revision: 11
      - Updated for consistency with ARB_vertex_program revision 34.
      - Added support for matrix binding.
      - Removed precision queries.
      - Updated issues list.
   Date: 7/16/2002
   Revision: 10
      - Updated for consistency with ARB_vertex_program revision 31.
      - Added fog params and depth range bindings to grammar.
      - Removed stpq writemasks and swizzles from grammar.
      - Required swizzle components to come from same set, xyzw or rgba.
   Date: 7/10/2002
   Revision: 9
      - Made fog params and depth range bindable.
      - Changed texture instruction names to match 3-letter format.
      - Made texture instructions more consistent with ALU instructions.
      - Increased minimums for implementation-dependent values.
      - Re-introduced 4-components swizzles and the SWZ instruction.
      - Updated issues list.
   Date: 7/03/2002
   Revision: 8
      - Fixed typos.
      - Added DST, LIT, SGE, SLT instructions.
      - Changed FRC definition to match ARB_vertex_program, added MOD
        instruction to expose fmod(arg, 1.0) behavior.
   Date: 6/25/2002
   Revision: 7
      - Updated for consistency with ARB_vertex_program revision 29.
   Date: 6/19/2002
   Revision: 6
      - Updated for consistency with ARB_vertex_program revision 28.
      - Changed from ATI to ARB prefix/suffix.
      - Started using single integer revision number.
      - Added a few more issues to the list.
   Date: 6/14/2002
   Revision: 1.4
      - Updated for consistency with ARB_vertex_program revision 27.
      - Added a few more issues to the list.
   Date: 6/05/2002
   Revision: 1.3
      - Updated for consistency with ARB_vertex_program revision 26.
      - Incorporated program object management, removing dependency on
        ARB_vertex_program.
      - Added interaction with ARB_shadow.
   Date: 6/03/2002
   Revision: 1.2
      - Updated for consistency with ARB_vertex_program revision 25.
      - Fixed TexInstructions to use <texSrcReg>, i.e. no parameters.
      - Added TRC, POW, DPH instructions, updated FRC and LRP.
      - Added fog color parameter binding.
   Date: 5/23/2002
   Revision: 1.1
      - Updated for consistency with ARB_vertex_program revision 24.
      - Added GetProgramfvATI entrypoint for querying precision values.
   Date: 5/10/2002
   Revision: 1.0
      - First draft for circulation.
Implementation Support
   List of OpenGL implementations supporting the GL_ARB_fragment_program extension
Original File
   Original text file for the GL_ARB_fragment_program extension
Page generated on Sun Nov 20 18:36:34 2005