text: /* Copyright (c) 2001, Stanford University
   * All rights reserved
   *
   * See the file LICENSE.txt for information on redistributing this software.
   */
  
  #include <stdio.h>
  #include "state.h"
  #include "state/cr_statetypes.h"
  #include "state_internals.h"
  #include "cr_error.h"
  #include "cr_mem.h"
  
  /**
   * \mainpage state_tracker 
   *
   * \section StateTrackerIntroduction Introduction
   *
   * Chromium consists of all the top-level files in the cr
   * directory.  The state_tracker module basically takes care of API dispatch,
   * and OpenGL state management.
   *
   *
   */
  void crStateAttribInit (CRAttribState *a) 
  {
      int i;
      a->attribStackDepth = 0;
      a->accumBufferStackDepth = 0;
      a->colorBufferStackDepth = 0;
      a->currentStackDepth = 0;
      a->depthBufferStackDepth = 0;
      a->enableStackDepth = 0;
      for ( i = 0 ; i < CR_MAX_ATTRIB_STACK_DEPTH ; i++)
      {
          a->enableStack[i].clip = NULL;
          a->enableStack[i].light = NULL;
          a->lightingStack[i].light = NULL;
          a->transformStack[i].clip = NULL;
          a->transformStack[i].clipPlane = NULL;
      }
      a->evalStackDepth = 0;
      a->fogStackDepth = 0;
      a->lightingStackDepth = 0;
      a->lineStackDepth = 0;
      a->listStackDepth = 0;
      a->pixelModeStackDepth = 0;
      a->pointStackDepth = 0;
      a->polygonStackDepth = 0;
      a->polygonStippleStackDepth = 0;
      a->scissorStackDepth = 0;
      a->stencilBufferStackDepth = 0;
      a->textureStackDepth = 0;
      a->transformStackDepth = 0;
      a->viewportStackDepth = 0;
  }
  
  /** @todo check if NV rect needed too*/
  static void
  copy_texunit(CRTextureUnit *dest, const CRTextureUnit *src)
  {
      dest->enabled1D = src->enabled1D;
      dest->enabled2D = src->enabled2D;
      dest->enabled3D = src->enabled3D;
      dest->enabledCubeMap = src->enabledCubeMap;
      dest->envMode = src->envMode;
      dest->envColor = src->envColor;
      dest->textureGen = src->textureGen;
      dest->objSCoeff = src->objSCoeff;
      dest->objTCoeff = src->objTCoeff;
      dest->objRCoeff = src->objRCoeff;
      dest->objQCoeff = src->objQCoeff;
      dest->eyeSCoeff = src->eyeSCoeff;
      dest->eyeTCoeff = src->eyeTCoeff;
      dest->eyeRCoeff = src->eyeRCoeff;
      dest->eyeQCoeff = src->eyeQCoeff;
      dest->gen = src->gen;
      dest->currentTexture1D = src->currentTexture1D;
      dest->currentTexture2D = src->currentTexture2D;
      dest->currentTexture3D = src->currentTexture3D;
      dest->currentTextureCubeMap = src->currentTextureCubeMap;
  }
  
  static void
  copy_texobj(PCRStateTracker pState, CRTextureObj *dest, CRTextureObj *src, GLboolean copyName)
  {
      if (copyName)
      {
          dest->id = src->id;
          dest->hwid = crStateGetTextureObjHWID(pState, src);
      }
  
      dest->borderColor = src->borderColor;
      dest->wrapS = src->wrapS;
      dest->wrapT = src->wrapT;
      dest->minFilter = src->minFilter;
      dest->magFilter = src->magFilter;
  #ifdef CR_OPENGL_VERSION_1_2
      dest->priority = src->priority;
      dest->wrapR = src->wrapR;
      dest->minLod = src->minLod;
      dest->maxLod = src->maxLod;
      dest->baseLevel = src->baseLevel;
      dest->maxLevel = src->maxLevel;
  #endif
  #ifdef CR_EXT_texture_filter_anisotropic
      dest->maxAnisotropy = src->maxAnisotropy;
  #endif
  }
  
  void STATE_APIENTRY crStatePushAttrib(PCRStateTracker pState, GLbitfield mask)
  {
      CRContext *g = GetCurrentContext(pState);
      CRAttribState *a = &(g->attrib);
      CRStateBits *sb = GetCurrentBits(pState);
      CRAttribBits *ab = &(sb->attrib);
      unsigned int i;
  
      if (g->current.inBeginEnd)
      {
          crStateError(pState, __LINE__, __FILE__, GL_INVALID_OPERATION, "glPushAttrib called in Begin/End");
          return;
      }
  
      if (a->attribStackDepth == CR_MAX_ATTRIB_STACK_DEPTH - 1)
      {
          crStateError(pState, __LINE__, __FILE__, GL_STACK_OVERFLOW, "glPushAttrib called with a full stack!" );
          return;
      }
  
      FLUSH();
  
      a->pushMaskStack[a->attribStackDepth++] = mask;
  
      if (mask & GL_ACCUM_BUFFER_BIT)
      {
          a->accumBufferStack[a->accumBufferStackDepth].accumClearValue = g->buffer.accumClearValue;
          a->accumBufferStackDepth++;
      }
      if (mask & GL_COLOR_BUFFER_BIT)
      {
          a->colorBufferStack[a->colorBufferStackDepth].alphaTest = g->buffer.alphaTest;
          a->colorBufferStack[a->colorBufferStackDepth].alphaTestFunc = g->buffer.alphaTestFunc;
          a->colorBufferStack[a->colorBufferStackDepth].alphaTestRef = g->buffer.alphaTestRef;
          a->colorBufferStack[a->colorBufferStackDepth].blend = g->buffer.blend;
          a->colorBufferStack[a->colorBufferStackDepth].blendSrcRGB = g->buffer.blendSrcRGB;
          a->colorBufferStack[a->colorBufferStackDepth].blendDstRGB = g->buffer.blendDstRGB;
  #if defined(CR_EXT_blend_func_separate)
          a->colorBufferStack[a->colorBufferStackDepth].blendSrcA = g->buffer.blendSrcA;
          a->colorBufferStack[a->colorBufferStackDepth].blendDstA = g->buffer.blendDstA;
  #endif
  #ifdef CR_EXT_blend_color
          a->colorBufferStack[a->colorBufferStackDepth].blendColor = g->buffer.blendColor;
  #endif
  #if defined(CR_EXT_blend_minmax) || defined(CR_EXT_blend_subtract) || defined(CR_EXT_blend_logic_op)
          a->colorBufferStack[a->colorBufferStackDepth].blendEquation = g->buffer.blendEquation;
  #endif
          a->colorBufferStack[a->colorBufferStackDepth].dither = g->buffer.dither;
          a->colorBufferStack[a->colorBufferStackDepth].drawBuffer = g->buffer.drawBuffer;
          a->colorBufferStack[a->colorBufferStackDepth].logicOp = g->buffer.logicOp;
          a->colorBufferStack[a->colorBufferStackDepth].indexLogicOp = g->buffer.indexLogicOp;
          a->colorBufferStack[a->colorBufferStackDepth].logicOpMode = g->buffer.logicOpMode;
          a->colorBufferStack[a->colorBufferStackDepth].colorClearValue = g->buffer.colorClearValue;
          a->colorBufferStack[a->colorBufferStackDepth].indexClearValue = g->buffer.indexClearValue;
          a->colorBufferStack[a->colorBufferStackDepth].colorWriteMask = g->buffer.colorWriteMask;
          a->colorBufferStack[a->colorBufferStackDepth].indexWriteMask = g->buffer.indexWriteMask;
          a->colorBufferStackDepth++;
      }
      if (mask & GL_CURRENT_BIT)
      {
          for (i = 0 ; i < CR_MAX_VERTEX_ATTRIBS ; i++)
          {
              COPY_4V(a->currentStack[a->currentStackDepth].attrib[i] , g->current.vertexAttrib[i]);
              COPY_4V(a->currentStack[a->currentStackDepth].rasterAttrib[i] , g->current.rasterAttrib[i]);
          }
          a->currentStack[a->currentStackDepth].rasterValid = g->current.rasterValid;
          a->currentStack[a->currentStackDepth].edgeFlag = g->current.edgeFlag;
          a->currentStack[a->currentStackDepth].colorIndex = g->current.colorIndex;
          a->currentStackDepth++;
      }
      if (mask & GL_DEPTH_BUFFER_BIT)
      {
          a->depthBufferStack[a->depthBufferStackDepth].depthTest = g->buffer.depthTest;
          a->depthBufferStack[a->depthBufferStackDepth].depthFunc = g->buffer.depthFunc;
          a->depthBufferStack[a->depthBufferStackDepth].depthClearValue = g->buffer.depthClearValue;
          a->depthBufferStack[a->depthBufferStackDepth].depthMask = g->buffer.depthMask;
          a->depthBufferStackDepth++;
      }
      if (mask & GL_ENABLE_BIT)
      {
          if (a->enableStack[a->enableStackDepth].clip == NULL)
          {
              a->enableStack[a->enableStackDepth].clip = (GLboolean *) crCalloc( g->limits.maxClipPlanes * sizeof( GLboolean ));
          }
          if (a->enableStack[a->enableStackDepth].light == NULL)
          {
              a->enableStack[a->enableStackDepth].light = (GLboolean *) crCalloc( g->limits.maxLights * sizeof( GLboolean ));
          }
          a->enableStack[a->enableStackDepth].alphaTest = g->buffer.alphaTest;
          a->enableStack[a->enableStackDepth].autoNormal = g->eval.autoNormal;
          a->enableStack[a->enableStackDepth].blend = g->buffer.blend;
          for (i = 0 ; i < g->limits.maxClipPlanes ; i++)
          {
              a->enableStack[a->enableStackDepth].clip[i] = g->transform.clip[i];
          }
          a->enableStack[a->enableStackDepth].colorMaterial = g->lighting.colorMaterial;
          a->enableStack[a->enableStackDepth].cullFace = g->polygon.cullFace;
          a->enableStack[a->enableStackDepth].depthTest = g->buffer.depthTest;
          a->enableStack[a->enableStackDepth].dither = g->buffer.dither;
          a->enableStack[a->enableStackDepth].fog = g->fog.enable;
          for (i = 0 ; i < g->limits.maxLights ; i++)
          {
              a->enableStack[a->enableStackDepth].light[i] = g->lighting.light[i].enable;
          }
          a->enableStack[a->enableStackDepth].lighting = g->lighting.lighting;
          a->enableStack[a->enableStackDepth].lineSmooth = g->line.lineSmooth;
          a->enableStack[a->enableStackDepth].lineStipple = g->line.lineStipple;
          a->enableStack[a->enableStackDepth].logicOp = g->buffer.logicOp;
          a->enableStack[a->enableStackDepth].indexLogicOp = g->buffer.indexLogicOp;
          for (i = 0 ; i < GLEVAL_TOT ; i++)
          {
              a->enableStack[a->enableStackDepth].map1[i] = g->eval.enable1D[i];
              a->enableStack[a->enableStackDepth].map2[i] = g->eval.enable2D[i];
          }
          a->enableStack[a->enableStackDepth].normalize = g->transform.normalize;
          a->enableStack[a->enableStackDepth].pointSmooth = g->point.pointSmooth;
  #if CR_ARB_point_sprite
          a->enableStack[a->enableStackDepth].pointSprite = g->point.pointSprite;
          for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++)
              a->enableStack[a->enableStackDepth].coordReplacement[i] = g->point.coordReplacement[i];
  #endif
          a->enableStack[a->enableStackDepth].polygonOffsetLine = g->polygon.polygonOffsetLine;
          a->enableStack[a->enableStackDepth].polygonOffsetFill = g->polygon.polygonOffsetFill;
          a->enableStack[a->enableStackDepth].polygonOffsetPoint = g->polygon.polygonOffsetPoint;
          a->enableStack[a->enableStackDepth].polygonSmooth = g->polygon.polygonSmooth;
          a->enableStack[a->enableStackDepth].polygonStipple = g->polygon.polygonStipple;
  #ifdef CR_OPENGL_VERSION_1_2
          a->enableStack[a->enableStackDepth].rescaleNormals = g->transform.rescaleNormals;
  #endif
          a->enableStack[a->enableStackDepth].scissorTest = g->viewport.scissorTest;
          a->enableStack[a->enableStackDepth].stencilTest = g->stencil.stencilTest;
          for (i = 0 ; i < g->limits.maxTextureUnits; i++)
          {
              a->enableStack[a->enableStackDepth].texture1D[i] = g->texture.unit[i].enabled1D;
              a->enableStack[a->enableStackDepth].texture2D[i] = g->texture.unit[i].enabled2D;
  #ifdef CR_OPENGL_VERSION_1_2
              a->enableStack[a->enableStackDepth].texture3D[i] = g->texture.unit[i].enabled3D;
  #endif
  #ifdef CR_ARB_texture_cube_map
              a->enableStack[a->enableStackDepth].textureCubeMap[i] = g->texture.unit[i].enabledCubeMap;
  #endif
  #ifdef CR_NV_texture_rectangle
              a->enableStack[a->enableStackDepth].textureRect[i] = g->texture.unit[i].enabledRect;
  #endif
              a->enableStack[a->enableStackDepth].textureGenS[i] = g->texture.unit[i].textureGen.s;
              a->enableStack[a->enableStackDepth].textureGenT[i] = g->texture.unit[i].textureGen.t;
              a->enableStack[a->enableStackDepth].textureGenR[i] = g->texture.unit[i].textureGen.r;
              a->enableStack[a->enableStackDepth].textureGenQ[i] = g->texture.unit[i].textureGen.q;
          }
          a->enableStackDepth++;
      }
      if (mask & GL_EVAL_BIT)
      {
          for (i = 0 ; i < GLEVAL_TOT ; i++)
          {
              int size1 = g->eval.eval1D[i].order * gleval_sizes[i] * 
                      sizeof (GLfloat);
              int size2 = g->eval.eval2D[i].uorder * g->eval.eval2D[i].vorder *
                      gleval_sizes[i] * sizeof (GLfloat);
              a->evalStack[a->evalStackDepth].enable1D[i] = g->eval.enable1D[i];
              a->evalStack[a->evalStackDepth].enable2D[i] = g->eval.enable2D[i];
              a->evalStack[a->evalStackDepth].eval1D[i].u1 = g->eval.eval1D[i].u1;
              a->evalStack[a->evalStackDepth].eval1D[i].u2 = g->eval.eval1D[i].u2;
              a->evalStack[a->evalStackDepth].eval1D[i].order = g->eval.eval1D[i].order;
              a->evalStack[a->evalStackDepth].eval1D[i].coeff = (GLfloat*)crCalloc(size1);
              crMemcpy(a->evalStack[a->evalStackDepth].eval1D[i].coeff, g->eval.eval1D[i].coeff, size1);
              a->evalStack[a->evalStackDepth].eval2D[i].u1 = g->eval.eval2D[i].u1;
              a->evalStack[a->evalStackDepth].eval2D[i].u2 = g->eval.eval2D[i].u2;
              a->evalStack[a->evalStackDepth].eval2D[i].v1 = g->eval.eval2D[i].v1;
              a->evalStack[a->evalStackDepth].eval2D[i].v2 = g->eval.eval2D[i].v2;
              a->evalStack[a->evalStackDepth].eval2D[i].uorder = g->eval.eval2D[i].uorder;
              a->evalStack[a->evalStackDepth].eval2D[i].vorder = g->eval.eval2D[i].vorder;
              a->evalStack[a->evalStackDepth].eval2D[i].coeff = (GLfloat*)crCalloc(size2);
              crMemcpy(a->evalStack[a->evalStackDepth].eval2D[i].coeff, g->eval.eval2D[i].coeff, size2);
          }
          a->evalStack[a->evalStackDepth].autoNormal = g->eval.autoNormal;
          a->evalStack[a->evalStackDepth].un1D = g->eval.un1D;
          a->evalStack[a->evalStackDepth].u11D = g->eval.u11D;
          a->evalStack[a->evalStackDepth].u21D = g->eval.u21D;
          a->evalStack[a->evalStackDepth].un2D = g->eval.un2D;
          a->evalStack[a->evalStackDepth].u12D = g->eval.u12D;
          a->evalStack[a->evalStackDepth].u22D = g->eval.u22D;
          a->evalStack[a->evalStackDepth].vn2D = g->eval.vn2D;
          a->evalStack[a->evalStackDepth].v12D = g->eval.v12D;
          a->evalStack[a->evalStackDepth].v22D = g->eval.v22D;
          a->evalStackDepth++;
      }
      if (mask & GL_FOG_BIT)
      {
          a->fogStack[a->fogStackDepth].enable = g->fog.enable;
          a->fogStack[a->fogStackDepth].color = g->fog.color;
          a->fogStack[a->fogStackDepth].density = g->fog.density;
          a->fogStack[a->fogStackDepth].start = g->fog.start;
          a->fogStack[a->fogStackDepth].end = g->fog.end;
          a->fogStack[a->fogStackDepth].index = g->fog.index;
          a->fogStack[a->fogStackDepth].mode = g->fog.mode;
          a->fogStackDepth++;
      }
      if (mask & GL_HINT_BIT)
      {
          a->hintStack[a->hintStackDepth].perspectiveCorrection = g->hint.perspectiveCorrection;
          a->hintStack[a->hintStackDepth].pointSmooth = g->hint.pointSmooth;
          a->hintStack[a->hintStackDepth].lineSmooth = g->hint.lineSmooth;
          a->hintStack[a->hintStackDepth].polygonSmooth = g->hint.polygonSmooth;
          a->hintStack[a->hintStackDepth].fog = g->hint.fog;
  #ifdef CR_EXT_clip_volume_hint
          a->hintStack[a->hintStackDepth].clipVolumeClipping = g->hint.clipVolumeClipping;
  #endif
  #ifdef CR_ARB_texture_compression
          a->hintStack[a->hintStackDepth].textureCompression = g->hint.textureCompression;
  #endif
  #ifdef CR_SGIS_generate_mipmap
          a->hintStack[a->hintStackDepth].generateMipmap = g->hint.generateMipmap;
  #endif
          a->hintStackDepth++;
      }
      if (mask & GL_LIGHTING_BIT)
      {
          if (a->lightingStack[a->lightingStackDepth].light == NULL)
          {
              a->lightingStack[a->lightingStackDepth].light = (CRLight *) crCalloc( g->limits.maxLights * sizeof( CRLight ));
          }
          a->lightingStack[a->lightingStackDepth].lightModelAmbient = g->lighting.lightModelAmbient;
          a->lightingStack[a->lightingStackDepth].lightModelLocalViewer = g->lighting.lightModelLocalViewer;
          a->lightingStack[a->lightingStackDepth].lightModelTwoSide = g->lighting.lightModelTwoSide;
  #if defined(CR_EXT_separate_specular_color) || defined(CR_OPENGL_VERSION_1_2)
          a->lightingStack[a->lightingStackDepth].lightModelColorControlEXT = g->lighting.lightModelColorControlEXT;
  #endif
          a->lightingStack[a->lightingStackDepth].lighting = g->lighting.lighting;
          a->lightingStack[a->lightingStackDepth].colorMaterial = g->lighting.colorMaterial;
          a->lightingStack[a->lightingStackDepth].colorMaterialMode = g->lighting.colorMaterialMode;
          a->lightingStack[a->lightingStackDepth].colorMaterialFace = g->lighting.colorMaterialFace;
          for (i = 0 ; i < g->limits.maxLights; i++)
          {
              a->lightingStack[a->lightingStackDepth].light[i].enable = g->lighting.light[i].enable;
              a->lightingStack[a->lightingStackDepth].light[i].ambient = g->lighting.light[i].ambient;
              a->lightingStack[a->lightingStackDepth].light[i].diffuse = g->lighting.light[i].diffuse;
              a->lightingStack[a->lightingStackDepth].light[i].specular = g->lighting.light[i].specular;
              a->lightingStack[a->lightingStackDepth].light[i].spotDirection = g->lighting.light[i].spotDirection;
              a->lightingStack[a->lightingStackDepth].light[i].position = g->lighting.light[i].position;
              a->lightingStack[a->lightingStackDepth].light[i].spotExponent = g->lighting.light[i].spotExponent;
              a->lightingStack[a->lightingStackDepth].light[i].spotCutoff = g->lighting.light[i].spotCutoff;
              a->lightingStack[a->lightingStackDepth].light[i].constantAttenuation = g->lighting.light[i].constantAttenuation;
              a->lightingStack[a->lightingStackDepth].light[i].linearAttenuation = g->lighting.light[i].linearAttenuation;
              a->lightingStack[a->lightingStackDepth].light[i].quadraticAttenuation = g->lighting.light[i].quadraticAttenuation;
          }
          for (i = 0 ; i < 2 ; i++)
          {
              a->lightingStack[a->lightingStackDepth].ambient[i] = g->lighting.ambient[i];
              a->lightingStack[a->lightingStackDepth].diffuse[i] = g->lighting.diffuse[i];
              a->lightingStack[a->lightingStackDepth].specular[i] = g->lighting.specular[i];
              a->lightingStack[a->lightingStackDepth].emission[i] = g->lighting.emission[i];
              a->lightingStack[a->lightingStackDepth].shininess[i] = g->lighting.shininess[i];
              a->lightingStack[a->lightingStackDepth].indexes[i][0] = g->lighting.indexes[i][0];
              a->lightingStack[a->lightingStackDepth].indexes[i][1] = g->lighting.indexes[i][1];
              a->lightingStack[a->lightingStackDepth].indexes[i][2] = g->lighting.indexes[i][2];
          }
          a->lightingStack[a->lightingStackDepth].shadeModel = g->lighting.shadeModel;
          a->lightingStackDepth++;
      }
      if (mask & GL_LINE_BIT)
      {
          a->lineStack[a->lineStackDepth].lineSmooth = g->line.lineSmooth;
          a->lineStack[a->lineStackDepth].lineStipple = g->line.lineStipple;
          a->lineStack[a->lineStackDepth].pattern = g->line.pattern;
          a->lineStack[a->lineStackDepth].repeat = g->line.repeat;
          a->lineStack[a->lineStackDepth].width = g->line.width;
          a->lineStackDepth++;
      }
      if (mask & GL_LIST_BIT)
      {
          a->listStack[a->listStackDepth].base = g->lists.base;
          a->listStackDepth++;
      }
      if (mask & GL_PIXEL_MODE_BIT)
      {
          a->pixelModeStack[a->pixelModeStackDepth].bias = g->pixel.bias;
          a->pixelModeStack[a->pixelModeStackDepth].scale = g->pixel.scale;
          a->pixelModeStack[a->pixelModeStackDepth].indexOffset = g->pixel.indexOffset;
          a->pixelModeStack[a->pixelModeStackDepth].indexShift = g->pixel.indexShift;
          a->pixelModeStack[a->pixelModeStackDepth].mapColor = g->pixel.mapColor;
          a->pixelModeStack[a->pixelModeStackDepth].mapStencil = g->pixel.mapStencil;
          a->pixelModeStack[a->pixelModeStackDepth].xZoom = g->pixel.xZoom;
          a->pixelModeStack[a->pixelModeStackDepth].yZoom = g->pixel.yZoom;
          a->pixelModeStack[a->pixelModeStackDepth].readBuffer = g->buffer.readBuffer;
          a->pixelModeStackDepth++;
      }
      if (mask & GL_POINT_BIT)
      {
          a->pointStack[a->pointStackDepth].pointSmooth = g->point.pointSmooth;
  #if CR_ARB_point_sprite
          a->pointStack[a->pointStackDepth].pointSprite = g->point.pointSprite;
          for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++)
              a->pointStack[a->enableStackDepth].coordReplacement[i] = g->point.coordReplacement[i];
  #endif
          a->pointStack[a->pointStackDepth].pointSize = g->point.pointSize;
          a->pointStackDepth++;
      }
      if (mask & GL_POLYGON_BIT)
      {
          a->polygonStack[a->polygonStackDepth].cullFace = g->polygon.cullFace;
          a->polygonStack[a->polygonStackDepth].cullFaceMode = g->polygon.cullFaceMode;
          a->polygonStack[a->polygonStackDepth].frontFace = g->polygon.frontFace;
          a->polygonStack[a->polygonStackDepth].frontMode = g->polygon.frontMode;
          a->polygonStack[a->polygonStackDepth].backMode = g->polygon.backMode;
          a->polygonStack[a->polygonStackDepth].polygonSmooth = g->polygon.polygonSmooth;
          a->polygonStack[a->polygonStackDepth].polygonStipple = g->polygon.polygonStipple;
          a->polygonStack[a->polygonStackDepth].polygonOffsetFill = g->polygon.polygonOffsetFill;
          a->polygonStack[a->polygonStackDepth].polygonOffsetLine = g->polygon.polygonOffsetLine;
          a->polygonStack[a->polygonStackDepth].polygonOffsetPoint = g->polygon.polygonOffsetPoint;
          a->polygonStack[a->polygonStackDepth].offsetFactor = g->polygon.offsetFactor;
          a->polygonStack[a->polygonStackDepth].offsetUnits = g->polygon.offsetUnits;
          a->polygonStackDepth++;
      }
      if (mask & GL_POLYGON_STIPPLE_BIT)
      {
          crMemcpy( a->polygonStippleStack[a->polygonStippleStackDepth].pattern, g->polygon.stipple, 32*sizeof(GLint) );
          a->polygonStippleStackDepth++;
      }
      if (mask & GL_SCISSOR_BIT)
      {
          a->scissorStack[a->scissorStackDepth].scissorTest = g->viewport.scissorTest;
          a->scissorStack[a->scissorStackDepth].scissorX = g->viewport.scissorX;
          a->scissorStack[a->scissorStackDepth].scissorY = g->viewport.scissorY;
          a->scissorStack[a->scissorStackDepth].scissorW = g->viewport.scissorW;
          a->scissorStack[a->scissorStackDepth].scissorH = g->viewport.scissorH;
          a->scissorStackDepth++;
      }
      if (mask & GL_STENCIL_BUFFER_BIT)
      {
          a->stencilBufferStack[a->stencilBufferStackDepth].stencilTest = g->stencil.stencilTest;
          a->stencilBufferStack[a->stencilBufferStackDepth].clearValue = g->stencil.clearValue;
          a->stencilBufferStack[a->stencilBufferStackDepth].writeMask = g->stencil.writeMask;
          for (i = 0; i < CRSTATE_STENCIL_BUFFER_COUNT; ++i)
          {
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].func = g->stencil.buffers[i].func;
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].mask = g->stencil.buffers[i].mask;
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].ref = g->stencil.buffers[i].ref;
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].fail = g->stencil.buffers[i].fail;
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].passDepthFail = g->stencil.buffers[i].passDepthFail;
              a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].passDepthPass = g->stencil.buffers[i].passDepthPass;
          }
          a->stencilBufferStackDepth++;
      }
      if (mask & GL_TEXTURE_BIT)
      {
          CRTextureStack *tState = a->textureStack + a->textureStackDepth;
          tState->curTextureUnit = g->texture.curTextureUnit;
          for (i = 0 ; i < g->limits.maxTextureUnits ; i++)
          {
              /* per-unit state */
              copy_texunit(&tState->unit[i], &g->texture.unit[i]);
              /* texture object state */
              copy_texobj(pState, &tState->unit[i].Saved1D, g->texture.unit[i].currentTexture1D, GL_TRUE);
              copy_texobj(pState, &tState->unit[i].Saved2D, g->texture.unit[i].currentTexture2D, GL_TRUE);
  #ifdef CR_OPENGL_VERSION_1_2
              copy_texobj(pState, &tState->unit[i].Saved3D, g->texture.unit[i].currentTexture3D, GL_TRUE);
  #endif
  #ifdef CR_ARB_texture_cube_map
              copy_texobj(pState, &tState->unit[i].SavedCubeMap, g->texture.unit[i].currentTextureCubeMap, GL_TRUE);
  #endif
  #ifdef CR_NV_texture_rectangle
              copy_texobj(pState, &tState->unit[i].SavedRect, g->texture.unit[i].currentTextureRect, GL_TRUE);
  #endif
          }
          a->textureStackDepth++;
      }
      if (mask & GL_TRANSFORM_BIT)
      {
          if (a->transformStack[a->transformStackDepth].clip == NULL)
          {
              a->transformStack[a->transformStackDepth].clip = (GLboolean *) crCalloc( g->limits.maxClipPlanes * sizeof( GLboolean ));
          }
          if (a->transformStack[a->transformStackDepth].clipPlane == NULL)
          {
              a->transformStack[a->transformStackDepth].clipPlane = (GLvectord *) crCalloc( g->limits.maxClipPlanes * sizeof( GLvectord ));
          }
          a->transformStack[a->transformStackDepth].matrixMode = g->transform.matrixMode;
          for (i = 0 ; i < g->limits.maxClipPlanes ; i++)
          {
              a->transformStack[a->transformStackDepth].clip[i] = g->transform.clip[i];
              a->transformStack[a->transformStackDepth].clipPlane[i] = g->transform.clipPlane[i];
          }
          a->transformStack[a->transformStackDepth].normalize = g->transform.normalize;
  #ifdef CR_OPENGL_VERSION_1_2
          a->transformStack[a->transformStackDepth].rescaleNormals = g->transform.rescaleNormals;
  #endif
          a->transformStackDepth++;
      }
      if (mask & GL_VIEWPORT_BIT)
      {
          a->viewportStack[a->viewportStackDepth].viewportX = g->viewport.viewportX;
          a->viewportStack[a->viewportStackDepth].viewportY = g->viewport.viewportY;
          a->viewportStack[a->viewportStackDepth].viewportW = g->viewport.viewportW;
          a->viewportStack[a->viewportStackDepth].viewportH = g->viewport.viewportH;
          a->viewportStack[a->viewportStackDepth].nearClip = g->viewport.nearClip;
          a->viewportStack[a->viewportStackDepth].farClip = g->viewport.farClip;
          a->viewportStackDepth++;
      }
  
      DIRTY(ab->dirty, g->neg_bitid);
  }
  
  void STATE_APIENTRY crStatePopAttrib(PCRStateTracker pState) 
  {
      CRContext *g = GetCurrentContext(pState);
      CRAttribState *a = &(g->attrib);
      CRStateBits *sb = GetCurrentBits(pState);
      CRAttribBits *ab = &(sb->attrib);
      CRbitvalue mask;
      unsigned int i;
  
      if (g->current.inBeginEnd)
      {
          crStateError(pState, __LINE__, __FILE__, GL_INVALID_OPERATION, "glPopAttrib called in Begin/End");
          return;
      }
  
      if (a->attribStackDepth == 0)
      {
          crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty stack!" );
          return;
      }
  
      FLUSH();
  
      mask = a->pushMaskStack[--a->attribStackDepth];
  
      if (mask & GL_ACCUM_BUFFER_BIT)
      {
          if (a->accumBufferStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty accum buffer stack!" );
              return;
          }
          a->accumBufferStackDepth--;
          g->buffer.accumClearValue = a->accumBufferStack[a->accumBufferStackDepth].accumClearValue;
          DIRTY(sb->buffer.dirty, g->neg_bitid);
          DIRTY(sb->buffer.clearAccum, g->neg_bitid);
      }
      if (mask & GL_COLOR_BUFFER_BIT)
      {
          if (a->colorBufferStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty color buffer stack!" );
              return;
          }
          a->colorBufferStackDepth--;
          g->buffer.alphaTest = a->colorBufferStack[a->colorBufferStackDepth].alphaTest;
          g->buffer.alphaTestFunc = a->colorBufferStack[a->colorBufferStackDepth].alphaTestFunc;
          g->buffer.alphaTestRef = a->colorBufferStack[a->colorBufferStackDepth].alphaTestRef;
          g->buffer.blend = a->colorBufferStack[a->colorBufferStackDepth].blend;
          g->buffer.blendSrcRGB = a->colorBufferStack[a->colorBufferStackDepth].blendSrcRGB;
          g->buffer.blendDstRGB = a->colorBufferStack[a->colorBufferStackDepth].blendDstRGB;
  #if defined(CR_EXT_blend_func_separate)
          g->buffer.blendSrcA = a->colorBufferStack[a->colorBufferStackDepth].blendSrcA;
          g->buffer.blendDstA = a->colorBufferStack[a->colorBufferStackDepth].blendDstA;
  #endif
  #ifdef CR_EXT_blend_color
          g->buffer.blendColor = a->colorBufferStack[a->colorBufferStackDepth].blendColor;
  #endif
  #if defined(CR_EXT_blend_minmax) || defined(CR_EXT_blend_subtract) || defined(CR_EXT_blend_logic_op)
          g->buffer.blendEquation = a->colorBufferStack[a->colorBufferStackDepth].blendEquation;
  #endif
          g->buffer.dither = a->colorBufferStack[a->colorBufferStackDepth].dither;
          g->buffer.drawBuffer = a->colorBufferStack[a->colorBufferStackDepth].drawBuffer;
          g->buffer.logicOp = a->colorBufferStack[a->colorBufferStackDepth].logicOp;
          g->buffer.indexLogicOp = a->colorBufferStack[a->colorBufferStackDepth].indexLogicOp;
          g->buffer.logicOpMode = a->colorBufferStack[a->colorBufferStackDepth].logicOpMode;
          g->buffer.colorClearValue = a->colorBufferStack[a->colorBufferStackDepth].colorClearValue;
          g->buffer.indexClearValue = a->colorBufferStack[a->colorBufferStackDepth].indexClearValue;
          g->buffer.colorWriteMask = a->colorBufferStack[a->colorBufferStackDepth].colorWriteMask;
          g->buffer.indexWriteMask = a->colorBufferStack[a->colorBufferStackDepth].indexWriteMask;
          DIRTY(sb->buffer.dirty, g->neg_bitid);
          DIRTY(sb->buffer.enable, g->neg_bitid);
          DIRTY(sb->buffer.alphaFunc, g->neg_bitid);
          DIRTY(sb->buffer.blendFunc, g->neg_bitid);
  #ifdef CR_EXT_blend_color
          DIRTY(sb->buffer.blendColor, g->neg_bitid);
  #endif
  #if defined(CR_EXT_blend_minmax) || defined(CR_EXT_blend_subtract) || defined(CR_EXT_blend_logic_op)
          DIRTY(sb->buffer.blendEquation, g->neg_bitid);
  #endif
          DIRTY(sb->buffer.drawBuffer, g->neg_bitid);
          DIRTY(sb->buffer.logicOp, g->neg_bitid);
          DIRTY(sb->buffer.indexLogicOp, g->neg_bitid);
          DIRTY(sb->buffer.clearColor, g->neg_bitid);
          DIRTY(sb->buffer.clearIndex, g->neg_bitid);
          DIRTY(sb->buffer.colorWriteMask, g->neg_bitid);
          DIRTY(sb->buffer.indexMask, g->neg_bitid);
      }
      if (mask & GL_CURRENT_BIT)
      {
          if (a->currentStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty current stack!" );
              return;
          }
          a->currentStackDepth--;
          for (i = 0 ; i < CR_MAX_VERTEX_ATTRIBS ; i++)
          {
              COPY_4V(g->current.vertexAttrib[i], a->currentStack[a->currentStackDepth].attrib[i]);
              COPY_4V(g->current.rasterAttrib[i],  a->currentStack[a->currentStackDepth].rasterAttrib[i]);
              DIRTY(sb->current.vertexAttrib[i], g->neg_bitid);
          }
          g->current.rasterValid = a->currentStack[a->currentStackDepth].rasterValid;
          g->current.edgeFlag = a->currentStack[a->currentStackDepth].edgeFlag;
          g->current.colorIndex = a->currentStack[a->currentStackDepth].colorIndex;
          DIRTY(sb->current.dirty, g->neg_bitid);
          DIRTY(sb->current.edgeFlag, g->neg_bitid);
          DIRTY(sb->current.colorIndex, g->neg_bitid);
          DIRTY(sb->current.rasterPos, g->neg_bitid);
      }
      if (mask & GL_DEPTH_BUFFER_BIT)
      {
          if (a->depthBufferStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty depth buffer stack!" );
              return;
          }
          a->depthBufferStackDepth--;
          g->buffer.depthTest = a->depthBufferStack[a->depthBufferStackDepth].depthTest;
          g->buffer.depthFunc = a->depthBufferStack[a->depthBufferStackDepth].depthFunc;
          g->buffer.depthClearValue = a->depthBufferStack[a->depthBufferStackDepth].depthClearValue;
          g->buffer.depthMask = a->depthBufferStack[a->depthBufferStackDepth].depthMask;
          DIRTY(sb->buffer.dirty, g->neg_bitid);
          DIRTY(sb->buffer.enable, g->neg_bitid);
          DIRTY(sb->buffer.depthFunc, g->neg_bitid);
          DIRTY(sb->buffer.clearDepth, g->neg_bitid);
          DIRTY(sb->buffer.depthMask, g->neg_bitid);
      }
      if (mask & GL_ENABLE_BIT)
      {
          if (a->enableStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty enable stack!" );
              return;
          }
          a->enableStackDepth--;
          g->buffer.alphaTest = a->enableStack[a->enableStackDepth].alphaTest;
          g->eval.autoNormal = a->enableStack[a->enableStackDepth].autoNormal;
          g->buffer.blend = a->enableStack[a->enableStackDepth].blend;
          for (i = 0 ; i < g->limits.maxClipPlanes ; i++)
          {
              g->transform.clip[i] = a->enableStack[a->enableStackDepth].clip[i];
          }
          g->lighting.colorMaterial = a->enableStack[a->enableStackDepth].colorMaterial;
          g->polygon.cullFace = a->enableStack[a->enableStackDepth].cullFace;
          g->buffer.depthTest = a->enableStack[a->enableStackDepth].depthTest;
          g->buffer.dither = a->enableStack[a->enableStackDepth].dither;
          g->fog.enable = a->enableStack[a->enableStackDepth].fog;
          for (i = 0 ; i < g->limits.maxLights ; i++)
          {
              g->lighting.light[i].enable = a->enableStack[a->enableStackDepth].light[i];
          }
          g->lighting.lighting = a->enableStack[a->enableStackDepth].lighting;
          g->line.lineSmooth = a->enableStack[a->enableStackDepth].lineSmooth;
          g->line.lineStipple = a->enableStack[a->enableStackDepth].lineStipple;
          g->buffer.logicOp = a->enableStack[a->enableStackDepth].logicOp;
          g->buffer.indexLogicOp = a->enableStack[a->enableStackDepth].indexLogicOp;
          for (i = 0 ; i < GLEVAL_TOT ; i++)
          {
              g->eval.enable1D[i] = a->enableStack[a->enableStackDepth].map1[i];
              g->eval.enable2D[i] = a->enableStack[a->enableStackDepth].map2[i];
          }
          g->transform.normalize = a->enableStack[a->enableStackDepth].normalize;
          g->point.pointSmooth = a->enableStack[a->enableStackDepth].pointSmooth;
  #ifdef CR_ARB_point_sprite
          g->point.pointSprite = a->enableStack[a->enableStackDepth].pointSprite;
          for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++)
              g->point.coordReplacement[i] = a->enableStack[a->enableStackDepth].coordReplacement[i];
  #endif
          g->polygon.polygonOffsetLine = a->enableStack[a->enableStackDepth].polygonOffsetLine;
          g->polygon.polygonOffsetFill = a->enableStack[a->enableStackDepth].polygonOffsetFill;
          g->polygon.polygonOffsetPoint = a->enableStack[a->enableStackDepth].polygonOffsetPoint;
          g->polygon.polygonSmooth = a->enableStack[a->enableStackDepth].polygonSmooth;
          g->polygon.polygonStipple = a->enableStack[a->enableStackDepth].polygonStipple;
  #ifdef CR_OPENGL_VERSION_1_2
          g->transform.rescaleNormals = a->enableStack[a->enableStackDepth].rescaleNormals;
  #endif
          g->viewport.scissorTest = a->enableStack[a->enableStackDepth].scissorTest;
          g->stencil.stencilTest = a->enableStack[a->enableStackDepth].stencilTest;
          for (i = 0 ; i < g->limits.maxTextureUnits; i++)
          {
              g->texture.unit[i].enabled1D = a->enableStack[a->enableStackDepth].texture1D[i];
              g->texture.unit[i].enabled2D = a->enableStack[a->enableStackDepth].texture2D[i];
  #ifdef CR_OPENGL_VERSION_1_2
              g->texture.unit[i].enabled3D = a->enableStack[a->enableStackDepth].texture3D[i];
  #endif
  #ifdef CR_ARB_texture_cube_map
              g->texture.unit[i].enabledCubeMap = a->enableStack[a->enableStackDepth].textureCubeMap[i];
  #endif
  #ifdef CR_NV_texture_rectangle
              g->texture.unit[i].enabledRect = a->enableStack[a->enableStackDepth].textureRect[i];
  #endif
              g->texture.unit[i].textureGen.s = a->enableStack[a->enableStackDepth].textureGenS[i];
              g->texture.unit[i].textureGen.t = a->enableStack[a->enableStackDepth].textureGenT[i];
              g->texture.unit[i].textureGen.r = a->enableStack[a->enableStackDepth].textureGenR[i];
              g->texture.unit[i].textureGen.q = a->enableStack[a->enableStackDepth].textureGenQ[i];
          }
          DIRTY(sb->buffer.dirty, g->neg_bitid);
          DIRTY(sb->eval.dirty, g->neg_bitid);
          DIRTY(sb->transform.dirty, g->neg_bitid);
          DIRTY(sb->lighting.dirty, g->neg_bitid);
          DIRTY(sb->fog.dirty, g->neg_bitid);
          DIRTY(sb->line.dirty, g->neg_bitid);
          DIRTY(sb->polygon.dirty, g->neg_bitid);
          DIRTY(sb->viewport.dirty, g->neg_bitid);
          DIRTY(sb->stencil.dirty, g->neg_bitid);
          DIRTY(sb->texture.dirty, g->neg_bitid);
  
          DIRTY(sb->buffer.enable, g->neg_bitid);
          DIRTY(sb->eval.enable, g->neg_bitid);
          DIRTY(sb->transform.enable, g->neg_bitid);
          DIRTY(sb->lighting.enable, g->neg_bitid);
          DIRTY(sb->fog.enable, g->neg_bitid);
          DIRTY(sb->line.enable, g->neg_bitid);
          DIRTY(sb->polygon.enable, g->neg_bitid);
          DIRTY(sb->viewport.enable, g->neg_bitid);
          DIRTY(sb->stencil.enable, g->neg_bitid);
          for (i = 0 ; i < g->limits.maxTextureUnits ; i++)
          {
              DIRTY(sb->texture.enable[i], g->neg_bitid);
          }
      }
      if (mask & GL_EVAL_BIT)
      {
          if (a->evalStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty eval stack!" );
              return;
          }
          a->evalStackDepth--;
          for (i = 0 ; i < GLEVAL_TOT ; i++)
          {
              int size1 = a->evalStack[a->evalStackDepth].eval1D[i].order * gleval_sizes[i] * sizeof(GLfloat);
              int size2 = a->evalStack[a->evalStackDepth].eval2D[i].uorder * a->evalStack[a->evalStackDepth].eval2D[i].vorder * gleval_sizes[i] * sizeof (GLfloat);
              g->eval.enable1D[i] = a->evalStack[a->evalStackDepth].enable1D[i];
              g->eval.enable2D[i] = a->evalStack[a->evalStackDepth].enable2D[i];
              g->eval.eval1D[i].u1 = a->evalStack[a->evalStackDepth].eval1D[i].u1;
              g->eval.eval1D[i].u2 = a->evalStack[a->evalStackDepth].eval1D[i].u2;
              g->eval.eval1D[i].order = a->evalStack[a->evalStackDepth].eval1D[i].order;
              crMemcpy((char*)g->eval.eval1D[i].coeff, a->evalStack[a->evalStackDepth].eval1D[i].coeff, size1);
              crFree(a->evalStack[a->evalStackDepth].eval1D[i].coeff);
              a->evalStack[a->evalStackDepth].eval1D[i].coeff = NULL;
              g->eval.eval2D[i].u1 = a->evalStack[a->evalStackDepth].eval2D[i].u1;
              g->eval.eval2D[i].u2 = a->evalStack[a->evalStackDepth].eval2D[i].u2;
              g->eval.eval2D[i].v1 = a->evalStack[a->evalStackDepth].eval2D[i].v1;
              g->eval.eval2D[i].v2 = a->evalStack[a->evalStackDepth].eval2D[i].v2;
              g->eval.eval2D[i].uorder = a->evalStack[a->evalStackDepth].eval2D[i].uorder;
              g->eval.eval2D[i].vorder = a->evalStack[a->evalStackDepth].eval2D[i].vorder;
              crMemcpy((char*)g->eval.eval2D[i].coeff, a->evalStack[a->evalStackDepth].eval2D[i].coeff, size2);
              crFree(a->evalStack[a->evalStackDepth].eval2D[i].coeff);
              a->evalStack[a->evalStackDepth].eval2D[i].coeff = NULL;
          }
          g->eval.autoNormal = a->evalStack[a->evalStackDepth].autoNormal;
          g->eval.un1D = a->evalStack[a->evalStackDepth].un1D;
          g->eval.u11D = a->evalStack[a->evalStackDepth].u11D;
          g->eval.u21D = a->evalStack[a->evalStackDepth].u21D;
          g->eval.un2D = a->evalStack[a->evalStackDepth].un2D;
          g->eval.u12D = a->evalStack[a->evalStackDepth].u12D;
          g->eval.u22D = a->evalStack[a->evalStackDepth].u22D;
          g->eval.vn2D = a->evalStack[a->evalStackDepth].vn2D;
          g->eval.v12D = a->evalStack[a->evalStackDepth].v12D;
          g->eval.v22D = a->evalStack[a->evalStackDepth].v22D;
          for (i = 0; i < GLEVAL_TOT; i++) {
              DIRTY(sb->eval.eval1D[i], g->neg_bitid);
              DIRTY(sb->eval.eval2D[i], g->neg_bitid);
          }
          DIRTY(sb->eval.dirty, g->neg_bitid);
          DIRTY(sb->eval.grid1D, g->neg_bitid);
          DIRTY(sb->eval.grid2D, g->neg_bitid);
          DIRTY(sb->eval.enable, g->neg_bitid);
      }
      if (mask & GL_FOG_BIT)
      {
          if (a->fogStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty fog stack!" );
              return;
          }
          a->fogStackDepth--;
          g->fog.enable = a->fogStack[a->fogStackDepth].enable;
          g->fog.color = a->fogStack[a->fogStackDepth].color;
          g->fog.density = a->fogStack[a->fogStackDepth].density;
          g->fog.start = a->fogStack[a->fogStackDepth].start;
          g->fog.end = a->fogStack[a->fogStackDepth].end;
          g->fog.index = a->fogStack[a->fogStackDepth].index;
          g->fog.mode = a->fogStack[a->fogStackDepth].mode;
          DIRTY(sb->fog.dirty, g->neg_bitid);
          DIRTY(sb->fog.color, g->neg_bitid);
          DIRTY(sb->fog.index, g->neg_bitid);
          DIRTY(sb->fog.density, g->neg_bitid);
          DIRTY(sb->fog.start, g->neg_bitid);
          DIRTY(sb->fog.end, g->neg_bitid);
          DIRTY(sb->fog.mode, g->neg_bitid);
          DIRTY(sb->fog.enable, g->neg_bitid);
      }
      if (mask & GL_HINT_BIT)
      {
          if (a->hintStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty hint stack!" );
              return;
          }
          a->hintStackDepth--;
          g->hint.perspectiveCorrection = a->hintStack[a->hintStackDepth].perspectiveCorrection;
          g->hint.pointSmooth = a->hintStack[a->hintStackDepth].pointSmooth;
          g->hint.lineSmooth = a->hintStack[a->hintStackDepth].lineSmooth;
          g->hint.polygonSmooth = a->hintStack[a->hintStackDepth].polygonSmooth;
          g->hint.fog = a->hintStack[a->hintStackDepth].fog;
          DIRTY(sb->hint.dirty, g->neg_bitid);
          DIRTY(sb->hint.perspectiveCorrection, g->neg_bitid);
          DIRTY(sb->hint.pointSmooth, g->neg_bitid);
          DIRTY(sb->hint.lineSmooth, g->neg_bitid);
          DIRTY(sb->hint.polygonSmooth, g->neg_bitid);
  #ifdef CR_EXT_clip_volume_hint
          g->hint.clipVolumeClipping = a->hintStack[a->hintStackDepth].clipVolumeClipping;
          DIRTY(sb->hint.clipVolumeClipping, g->neg_bitid);
  #endif
  #ifdef CR_ARB_texture_compression
          g->hint.textureCompression = a->hintStack[a->hintStackDepth].textureCompression;
          DIRTY(sb->hint.textureCompression, g->neg_bitid);
  #endif
  #ifdef CR_SGIS_generate_mipmap
          g->hint.generateMipmap = a->hintStack[a->hintStackDepth].generateMipmap;
          DIRTY(sb->hint.generateMipmap, g->neg_bitid);
  #endif
      }
      if (mask & GL_LIGHTING_BIT)
      {
          if (a->lightingStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty lighting stack!" );
              return;
          }
          a->lightingStackDepth--;
          g->lighting.lightModelAmbient = a->lightingStack[a->lightingStackDepth].lightModelAmbient;
          g->lighting.lightModelLocalViewer = a->lightingStack[a->lightingStackDepth].lightModelLocalViewer;
          g->lighting.lightModelTwoSide = a->lightingStack[a->lightingStackDepth].lightModelTwoSide;
  #if defined(CR_EXT_separate_specular_color) || defined(CR_OPENGL_VERSION_1_2)
          g->lighting.lightModelColorControlEXT = a->lightingStack[a->lightingStackDepth].lightModelColorControlEXT;
  #endif
          g->lighting.lighting = a->lightingStack[a->lightingStackDepth].lighting;
          g->lighting.colorMaterial = a->lightingStack[a->lightingStackDepth].colorMaterial;
          g->lighting.colorMaterialMode = a->lightingStack[a->lightingStackDepth].colorMaterialMode;
          g->lighting.colorMaterialFace = a->lightingStack[a->lightingStackDepth].colorMaterialFace;
          for (i = 0 ; i < g->limits.maxLights; i++)
          {
              g->lighting.light[i].enable = a->lightingStack[a->lightingStackDepth].light[i].enable;
              g->lighting.light[i].ambient = a->lightingStack[a->lightingStackDepth].light[i].ambient;
              g->lighting.light[i].diffuse = a->lightingStack[a->lightingStackDepth].light[i].diffuse;
              g->lighting.light[i].specular = a->lightingStack[a->lightingStackDepth].light[i].specular;
              g->lighting.light[i].spotDirection = a->lightingStack[a->lightingStackDepth].light[i].spotDirection;
              g->lighting.light[i].position = a->lightingStack[a->lightingStackDepth].light[i].position;
              g->lighting.light[i].spotExponent = a->lightingStack[a->lightingStackDepth].light[i].spotExponent;
              g->lighting.light[i].spotCutoff = a->lightingStack[a->lightingStackDepth].light[i].spotCutoff;
              g->lighting.light[i].constantAttenuation = a->lightingStack[a->lightingStackDepth].light[i].constantAttenuation;
              g->lighting.light[i].linearAttenuation = a->lightingStack[a->lightingStackDepth].light[i].linearAttenuation;
              g->lighting.light[i].quadraticAttenuation = a->lightingStack[a->lightingStackDepth].light[i].quadraticAttenuation;
          }
          for (i = 0 ; i < 2 ; i++)
          {
              g->lighting.ambient[i] = a->lightingStack[a->lightingStackDepth].ambient[i];
              g->lighting.diffuse[i] = a->lightingStack[a->lightingStackDepth].diffuse[i];
              g->lighting.specular[i] = a->lightingStack[a->lightingStackDepth].specular[i];
              g->lighting.emission[i] = a->lightingStack[a->lightingStackDepth].emission[i];
              g->lighting.shininess[i] = a->lightingStack[a->lightingStackDepth].shininess[i];
              g->lighting.indexes[i][0] = a->lightingStack[a->lightingStackDepth].indexes[i][0];
              g->lighting.indexes[i][1] = a->lightingStack[a->lightingStackDepth].indexes[i][1];
              g->lighting.indexes[i][2] = a->lightingStack[a->lightingStackDepth].indexes[i][2];
          }
          g->lighting.shadeModel = a->lightingStack[a->lightingStackDepth].shadeModel;
          DIRTY(sb->lighting.dirty, g->neg_bitid);
          DIRTY(sb->lighting.shadeModel, g->neg_bitid);
          DIRTY(sb->lighting.lightModel, g->neg_bitid);
          DIRTY(sb->lighting.material, g->neg_bitid);
          DIRTY(sb->lighting.enable, g->neg_bitid);
          for (i = 0 ; i < g->limits.maxLights; i++)
          {
              DIRTY(sb->lighting.light[i].dirty, g->neg_bitid);
              DIRTY(sb->lighting.light[i].enable, g->neg_bitid);
              DIRTY(sb->lighting.light[i].ambient, g->neg_bitid);
              DIRTY(sb->lighting.light[i].diffuse, g->neg_bitid);
              DIRTY(sb->lighting.light[i].specular, g->neg_bitid);
              DIRTY(sb->lighting.light[i].position, g->neg_bitid);
              DIRTY(sb->lighting.light[i].attenuation, g->neg_bitid);
              DIRTY(sb->lighting.light[i].spot, g->neg_bitid);
          }
      }
      if (mask & GL_LINE_BIT)
      {
          if (a->lineStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty line stack!" );
              return;
          }
          a->lineStackDepth--;
          g->line.lineSmooth = a->lineStack[a->lineStackDepth].lineSmooth;
          g->line.lineStipple = a->lineStack[a->lineStackDepth].lineStipple;
          g->line.pattern = a->lineStack[a->lineStackDepth].pattern;
          g->line.repeat = a->lineStack[a->lineStackDepth].repeat;
          g->line.width = a->lineStack[a->lineStackDepth].width;
          DIRTY(sb->line.dirty, g->neg_bitid);
          DIRTY(sb->line.enable, g->neg_bitid);
          DIRTY(sb->line.width, g->neg_bitid);
          DIRTY(sb->line.stipple, g->neg_bitid);
      }
      if (mask & GL_LIST_BIT)
      {
          if (a->listStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty list stack!" );
              return;
          }
          a->listStackDepth--;
          g->lists.base = a->listStack[a->listStackDepth].base;
          DIRTY(sb->lists.dirty, g->neg_bitid);
      }
      if (mask & GL_PIXEL_MODE_BIT)
      {
          if (a->pixelModeStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty pixel mode stack!" );
              return;
          }
          a->pixelModeStackDepth--;
          g->pixel.bias = a->pixelModeStack[a->pixelModeStackDepth].bias;
          g->pixel.scale = a->pixelModeStack[a->pixelModeStackDepth].scale;
          g->pixel.indexOffset = a->pixelModeStack[a->pixelModeStackDepth].indexOffset;
          g->pixel.indexShift = a->pixelModeStack[a->pixelModeStackDepth].indexShift;
          g->pixel.mapColor = a->pixelModeStack[a->pixelModeStackDepth].mapColor;
          g->pixel.mapStencil = a->pixelModeStack[a->pixelModeStackDepth].mapStencil;
          g->pixel.xZoom = a->pixelModeStack[a->pixelModeStackDepth].xZoom;
          g->pixel.yZoom = a->pixelModeStack[a->pixelModeStackDepth].yZoom;
          g->buffer.readBuffer = a->pixelModeStack[a->pixelModeStackDepth].readBuffer;
          DIRTY(sb->pixel.dirty, g->neg_bitid);
          DIRTY(sb->pixel.transfer, g->neg_bitid);
          DIRTY(sb->pixel.zoom, g->neg_bitid);
          DIRTY(sb->buffer.dirty, g->neg_bitid);
          DIRTY(sb->buffer.readBuffer, g->neg_bitid);
      }
      if (mask & GL_POINT_BIT)
      {
          if (a->pointStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty point stack!" );
              return;
          }
          a->pointStackDepth--;
          g->point.pointSmooth = a->pointStack[a->pointStackDepth].pointSmooth;
          g->point.pointSize = a->pointStack[a->pointStackDepth].pointSize;
  #if GL_ARB_point_sprite
          g->point.pointSprite = a->pointStack[a->pointStackDepth].pointSprite;
          DIRTY(sb->point.enableSprite, g->neg_bitid);
          for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++) {
              g->point.coordReplacement[i] = a->enableStack[a->enableStackDepth].coordReplacement[i];
              DIRTY(sb->point.coordReplacement[i], g->neg_bitid);
          }
  #endif
          DIRTY(sb->point.dirty, g->neg_bitid);
          DIRTY(sb->point.size, g->neg_bitid);
          DIRTY(sb->point.enableSmooth, g->neg_bitid);
      }
      if (mask & GL_POLYGON_BIT)
      {
          if (a->polygonStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty polygon stack!" );
              return;
          }
          a->polygonStackDepth--;
          g->polygon.cullFace = a->polygonStack[a->polygonStackDepth].cullFace;
          g->polygon.cullFaceMode = a->polygonStack[a->polygonStackDepth].cullFaceMode;
          g->polygon.frontFace = a->polygonStack[a->polygonStackDepth].frontFace;
          g->polygon.frontMode = a->polygonStack[a->polygonStackDepth].frontMode;
          g->polygon.backMode = a->polygonStack[a->polygonStackDepth].backMode;
          g->polygon.polygonSmooth = a->polygonStack[a->polygonStackDepth].polygonSmooth;
          g->polygon.polygonStipple = a->polygonStack[a->polygonStackDepth].polygonStipple;
          g->polygon.polygonOffsetFill = a->polygonStack[a->polygonStackDepth].polygonOffsetFill;
          g->polygon.polygonOffsetLine = a->polygonStack[a->polygonStackDepth].polygonOffsetLine;
          g->polygon.polygonOffsetPoint = a->polygonStack[a->polygonStackDepth].polygonOffsetPoint;
          g->polygon.offsetFactor = a->polygonStack[a->polygonStackDepth].offsetFactor;
          g->polygon.offsetUnits = a->polygonStack[a->polygonStackDepth].offsetUnits;
          DIRTY(sb->polygon.dirty, g->neg_bitid);
          DIRTY(sb->polygon.enable, g->neg_bitid);
          DIRTY(sb->polygon.offset, g->neg_bitid);
          DIRTY(sb->polygon.mode, g->neg_bitid);
          DIRTY(sb->polygon.stipple, g->neg_bitid);
      }
      if (mask & GL_POLYGON_STIPPLE_BIT)
      {
          if (a->polygonStippleStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty polygon stipple stack!" );
              return;
          }
          a->polygonStippleStackDepth--;
          crMemcpy( g->polygon.stipple, a->polygonStippleStack[a->polygonStippleStackDepth].pattern, 32*sizeof(GLint) );
          DIRTY(sb->polygon.dirty, g->neg_bitid);
          DIRTY(sb->polygon.stipple, g->neg_bitid);
      }
      if (mask & GL_SCISSOR_BIT)
      {
          if (a->scissorStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty scissor stack!" );
              return;
          }
          a->scissorStackDepth--;
          g->viewport.scissorTest = a->scissorStack[a->scissorStackDepth].scissorTest;
          g->viewport.scissorX = a->scissorStack[a->scissorStackDepth].scissorX;
          g->viewport.scissorY = a->scissorStack[a->scissorStackDepth].scissorY;
          g->viewport.scissorW = a->scissorStack[a->scissorStackDepth].scissorW;
          g->viewport.scissorH = a->scissorStack[a->scissorStackDepth].scissorH;
          DIRTY(sb->viewport.dirty, g->neg_bitid);
          DIRTY(sb->viewport.enable, g->neg_bitid);
          DIRTY(sb->viewport.s_dims, g->neg_bitid);
      }
      if (mask & GL_STENCIL_BUFFER_BIT)
      {
          if (a->stencilBufferStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty stencil stack!" );
              return;
          }
          a->stencilBufferStackDepth--;
          g->stencil.stencilTest = a->stencilBufferStack[a->stencilBufferStackDepth].stencilTest;
          g->stencil.clearValue = a->stencilBufferStack[a->stencilBufferStackDepth].clearValue;
          g->stencil.writeMask = a->stencilBufferStack[a->stencilBufferStackDepth].writeMask;
          for (i = 0; i < CRSTATE_STENCIL_BUFFER_COUNT; ++i)
          {
              g->stencil.buffers[i].func = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].func;
              g->stencil.buffers[i].mask = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].mask;
              g->stencil.buffers[i].ref = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].ref;
              g->stencil.buffers[i].fail = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].fail;
              g->stencil.buffers[i].passDepthFail = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].passDepthFail;
              g->stencil.buffers[i].passDepthPass = a->stencilBufferStack[a->stencilBufferStackDepth].buffers[i].passDepthPass;
          }
  
          DIRTY(sb->stencil.dirty, g->neg_bitid);
          DIRTY(sb->stencil.enable, g->neg_bitid);
          DIRTY(sb->stencil.clearValue, g->neg_bitid);
          DIRTY(sb->stencil.writeMask, g->neg_bitid);
  
          for (i = 0; i < CRSTATE_STENCIL_BUFFER_REF_COUNT; ++i)
          {
              DIRTY(sb->stencil.bufferRefs[i].func, g->neg_bitid);
              DIRTY(sb->stencil.bufferRefs[i].op, g->neg_bitid);
          }
      }
      if (mask & GL_TEXTURE_BIT)
      {
          CRTextureStack *tState;
          if (a->textureStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty texture stack!" );
              return;
          }
          a->textureStackDepth--;
          tState = a->textureStack + a->textureStackDepth;
  
          g->texture.curTextureUnit = tState->curTextureUnit;
          for (i = 0 ; i < g->limits.maxTextureUnits ; i++)
          {
              copy_texunit(&g->texture.unit[i], &tState->unit[i]);
              /* first, restore the bindings! */
              g->texture.unit[i].currentTexture1D = crStateTextureGet(pState, GL_TEXTURE_1D, tState->unit[i].Saved1D.id);
              copy_texobj(pState, g->texture.unit[i].currentTexture1D, &tState->unit[i].Saved1D, GL_FALSE);
              g->texture.unit[i].currentTexture2D = crStateTextureGet(pState, GL_TEXTURE_2D, tState->unit[i].Saved2D.id);
              copy_texobj(pState, g->texture.unit[i].currentTexture2D, &tState->unit[i].Saved2D, GL_FALSE);
  #ifdef CR_OPENGL_VERSION_1_2
              g->texture.unit[i].currentTexture3D = crStateTextureGet(pState, GL_TEXTURE_3D, tState->unit[i].Saved3D.id);
              copy_texobj(pState, g->texture.unit[i].currentTexture3D, &tState->unit[i].Saved3D, GL_FALSE);
  #endif
  #ifdef CR_ARB_texture_cube_map
              g->texture.unit[i].currentTextureCubeMap = crStateTextureGet(pState, GL_TEXTURE_CUBE_MAP_ARB, tState->unit[i].SavedCubeMap.id);
              copy_texobj(pState, g->texture.unit[i].currentTextureCubeMap, &tState->unit[i].SavedCubeMap, GL_FALSE);
  #endif
  #ifdef CR_NV_texture_rectangle
              g->texture.unit[i].currentTextureRect = crStateTextureGet(pState, GL_TEXTURE_CUBE_MAP_ARB, tState->unit[i].SavedRect.id);
              copy_texobj(pState, g->texture.unit[i].currentTextureRect, &tState->unit[i].SavedRect, GL_FALSE);
  #endif
          }
          DIRTY(sb->texture.dirty, g->neg_bitid);
          for (i = 0 ; i < g->limits.maxTextureUnits ; i++)
          {
              DIRTY(sb->texture.enable[i], g->neg_bitid);
              DIRTY(sb->texture.current[i], g->neg_bitid);
              DIRTY(sb->texture.objGen[i], g->neg_bitid);
              DIRTY(sb->texture.eyeGen[i], g->neg_bitid);
              DIRTY(sb->texture.envBit[i], g->neg_bitid);
              DIRTY(sb->texture.genMode[i], g->neg_bitid);
          }
  
          for (i = 0 ; i < g->limits.maxTextureUnits ; i++)
          {
              DIRTY(g->texture.unit[i].currentTexture1D->dirty, g->neg_bitid);
              DIRTY(g->texture.unit[i].currentTexture2D->dirty, g->neg_bitid);
              DIRTY(g->texture.unit[i].currentTexture3D->dirty, g->neg_bitid);
  #ifdef CR_ARB_texture_cube_map
              DIRTY(g->texture.unit[i].currentTextureCubeMap->dirty, g->neg_bitid);
  #endif
  #ifdef CR_NV_texture_rectangle
              DIRTY(g->texture.unit[i].currentTextureRect->dirty, g->neg_bitid);
  #endif
              DIRTY(g->texture.unit[i].currentTexture1D->paramsBit[i], g->neg_bitid);
              DIRTY(g->texture.unit[i].currentTexture2D->paramsBit[i], g->neg_bitid);
              DIRTY(g->texture.unit[i].currentTexture3D->paramsBit[i], g->neg_bitid);
  #ifdef CR_ARB_texture_cube_map
              DIRTY(g->texture.unit[i].currentTextureCubeMap->paramsBit[i], g->neg_bitid);
  #endif
  #ifdef CR_NV_texture_rectangle
              DIRTY(g->texture.unit[i].currentTextureRect->paramsBit[i], g->neg_bitid);
  #endif
          }
      }
      if (mask & GL_TRANSFORM_BIT)
      {
          if (a->transformStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty transform stack!" );
              return;
          }
          a->transformStackDepth--;
          g->transform.matrixMode = a->transformStack[a->transformStackDepth].matrixMode;
          crStateMatrixMode(pState, g->transform.matrixMode);
          for (i = 0 ; i < g->limits.maxClipPlanes ; i++)
          {
              g->transform.clip[i] = a->transformStack[a->transformStackDepth].clip[i];
              g->transform.clipPlane[i] = a->transformStack[a->transformStackDepth].clipPlane[i];
          }
          g->transform.normalize = a->transformStack[a->transformStackDepth].normalize;
  #ifdef CR_OPENGL_VERSION_1_2
          g->transform.rescaleNormals = a->transformStack[a->transformStackDepth].rescaleNormals;
  #endif
          DIRTY(sb->transform.dirty, g->neg_bitid);
          DIRTY(sb->transform.matrixMode, g->neg_bitid);
          DIRTY(sb->transform.clipPlane, g->neg_bitid);
          DIRTY(sb->transform.enable, g->neg_bitid);
      }
      if (mask & GL_VIEWPORT_BIT)
      {
          if (a->viewportStackDepth == 0)
          {
              crStateError(pState, __LINE__, __FILE__, GL_STACK_UNDERFLOW, "glPopAttrib called with an empty viewport stack!" );
              return;
          }
          a->viewportStackDepth--;
          g->viewport.viewportX = a->viewportStack[a->viewportStackDepth].viewportX;
          g->viewport.viewportY = a->viewportStack[a->viewportStackDepth].viewportY;
          g->viewport.viewportW = a->viewportStack[a->viewportStackDepth].viewportW;
          g->viewport.viewportH = a->viewportStack[a->viewportStackDepth].viewportH;
          g->viewport.nearClip = a->viewportStack[a->viewportStackDepth].nearClip;
          g->viewport.farClip = a->viewportStack[a->viewportStackDepth].farClip;
          DIRTY(sb->viewport.dirty, g->neg_bitid);
          DIRTY(sb->viewport.v_dims, g->neg_bitid);
          DIRTY(sb->viewport.depth, g->neg_bitid);
      }
      DIRTY(ab->dirty, g->neg_bitid);
  }
  
  void crStateAttribSwitch( CRAttribBits *bb, CRbitvalue *bitID,
                            CRContext *fromCtx, CRContext *toCtx )
  {
      CRAttribState *to = &(toCtx->attrib);
      CRAttribState *from = &(fromCtx->attrib);
      if (to->attribStackDepth != 0 || from->attribStackDepth != 0)
      {
          crWarning( "Trying to switch contexts when the attribute stacks "
                               "weren't empty.  Currently, this is not supported." );
      }
      (void) bb;
      (void) bitID;
  }
  