/******************************Module*Header*******************************\ * Module Name: dl_opt.c * * Display list compilation error routines. * * Created: 12-24-1995 * Author: Hock San Lee [hockl] * * Copyright (c) 1995-96 Microsoft Corporation \**************************************************************************/ /* ** Copyright 1991, 1922, Silicon Graphics, Inc. ** All Rights Reserved. ** ** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; ** the contents of this file may not be disclosed to third parties, copied or ** duplicated in any form, in whole or in part, without the prior written ** permission of Silicon Graphics, Inc. ** ** RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data ** and Computer Software clause at DFARS 252.227-7013, and/or in similar or ** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - ** rights reserved under the Copyright Laws of the United States. ** */ #include "precomp.h" #pragma hdrstop #include "glclt.h" void FASTCALL VA_ArrayElementCompile(__GLcontext *gc, GLint i); /************************************************************************/ /* ** Optimized errors. Strange but true. These are called to save an error ** in the display list. */ void __gllc_InvalidValue() { void *data; __GL_SETUP(); data = __glDlistAddOpUnaligned(gc, DLIST_SIZE(0), DLIST_GENERIC_OP(InvalidValue)); if (data == NULL) return; __glDlistAppendOp(gc, data, __glle_InvalidValue); } void __gllc_InvalidEnum() { void *data; __GL_SETUP(); data = __glDlistAddOpUnaligned(gc, DLIST_SIZE(0), DLIST_GENERIC_OP(InvalidEnum)); if (data == NULL) return; __glDlistAppendOp(gc, data, __glle_InvalidEnum); } void __gllc_InvalidOperation() { void *data; __GL_SETUP(); data = __glDlistAddOpUnaligned(gc, DLIST_SIZE(0), DLIST_GENERIC_OP(InvalidOperation)); if (data == NULL) return; __glDlistAppendOp(gc, data, __glle_InvalidOperation); } /* ** These routines execute an error stored in a display list. */ const GLubyte * FASTCALL __glle_InvalidValue(__GLcontext *gc, const GLubyte *PC) { GLSETERROR(GL_INVALID_VALUE); return PC; } const GLubyte * FASTCALL __glle_InvalidEnum(__GLcontext *gc, const GLubyte *PC) { GLSETERROR(GL_INVALID_ENUM); return PC; } const GLubyte * FASTCALL __glle_InvalidOperation(__GLcontext *gc, const GLubyte *PC) { GLSETERROR(GL_INVALID_OPERATION); return PC; } /***************************************************************************/ // This function compiles a poly material structure. It does not // execute the record in COMPILE_AND_EXECUTE mode. The execution is done // when the poly array buffer is flushed. void APIENTRY __gllc_PolyMaterial(GLuint faceName, __GLmatChange *pdMat) { GLubyte *data, *data0; GLuint dirtyBits; GLuint size, newSize; __GL_SETUP(); ASSERTOPENGL(faceName == POLYDATA_MATERIAL_FRONT || faceName == POLYDATA_MATERIAL_BACK, "bad faceName\n"); // Allocate big enough record and resize it later size = sizeof(__GLmatChange) + sizeof(GLuint) + sizeof(GLuint); data = (GLubyte *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(size), DLIST_GENERIC_OP(PolyMaterial)); if (data == NULL) return; data0 = data; dirtyBits = pdMat->dirtyBits; // Skip size field to be filled in last ((GLuint *)data)++; // Record face name *((GLuint *) data)++ = faceName; *((GLuint *) data)++ = dirtyBits; if (dirtyBits & __GL_MATERIAL_AMBIENT) *((__GLcolor *) data)++ = pdMat->ambient; if (dirtyBits & __GL_MATERIAL_DIFFUSE) *((__GLcolor *) data)++ = pdMat->diffuse; if (dirtyBits & __GL_MATERIAL_SPECULAR) *((__GLcolor *) data)++ = pdMat->specular; if (dirtyBits & __GL_MATERIAL_EMISSIVE) *((__GLcolor *) data)++ = pdMat->emissive; if (dirtyBits & __GL_MATERIAL_SHININESS) *((__GLfloat *) data)++ = pdMat->shininess; if (dirtyBits & __GL_MATERIAL_COLORINDEXES) { *((__GLfloat *) data)++ = pdMat->cmapa; *((__GLfloat *) data)++ = pdMat->cmapd; *((__GLfloat *) data)++ = pdMat->cmaps; } // Now fill in the size field newSize = (GLuint) (data - data0); *((GLuint *) data0) = newSize; // Resize the record __glDlistResizeCurrentOp(gc, DLIST_SIZE(size), DLIST_SIZE(newSize)); } // Playback a PolyMaterial record in Begin. const GLubyte * FASTCALL __glle_PolyMaterial(__GLcontext *gc, const GLubyte *PC) { GLubyte *data; POLYARRAY *pa; POLYDATA *pd; GLuint size, faceName, dirtyBits; __GLmatChange *pdMat; POLYMATERIAL *pm; data = (GLubyte *) PC; size = *((GLuint *) data)++; faceName = *((GLuint *) data)++; dirtyBits = *((GLuint *) data)++; ASSERTOPENGL(faceName == POLYDATA_MATERIAL_FRONT || faceName == POLYDATA_MATERIAL_BACK, "bad faceName\n"); pa = gc->paTeb; if (pa->flags & POLYARRAY_IN_BEGIN) { // Update pa flags POLYARRAY_MATERIAL_FRONT and POLYARRAY_MATERIAL_BACK. pa->flags |= faceName; // Do front or back material for this vertex. // Overwrite the previous material changes for this vertex if they exist since // only the last material changes matter. pd = pa->pdNextVertex; // allocate __GLmatChange structure if this vertex hasn't got one if (!(pd->flags & faceName)) { if (!(pdMat = PAMatAlloc())) return PC + size; // Get POLYMATERIAL pointer after PAMatAlloc! pm = GLTEB_CLTPOLYMATERIAL(); if (faceName == POLYDATA_MATERIAL_FRONT) pm->pdMaterial0[pd - pa->pdBuffer0].front = pdMat; else pm->pdMaterial0[pd - pa->pdBuffer0].back = pdMat; pdMat->dirtyBits = dirtyBits; } else { pm = GLTEB_CLTPOLYMATERIAL(); if (faceName == POLYDATA_MATERIAL_FRONT) pdMat = pm->pdMaterial0[pd - pa->pdBuffer0].front; else pdMat = pm->pdMaterial0[pd - pa->pdBuffer0].back; pdMat->dirtyBits |= dirtyBits; } if (dirtyBits & __GL_MATERIAL_AMBIENT) pdMat->ambient = *((__GLcolor *) data)++; if (dirtyBits & __GL_MATERIAL_DIFFUSE) pdMat->diffuse = *((__GLcolor *) data)++; if (dirtyBits & __GL_MATERIAL_SPECULAR) pdMat->specular = *((__GLcolor *) data)++; if (dirtyBits & __GL_MATERIAL_EMISSIVE) pdMat->emissive = *((__GLcolor *) data)++; if (dirtyBits & __GL_MATERIAL_SHININESS) pdMat->shininess = *((__GLfloat *) data)++; if (dirtyBits & __GL_MATERIAL_COLORINDEXES) { pdMat->cmapa = *((__GLfloat *) data)++; pdMat->cmapd = *((__GLfloat *) data)++; pdMat->cmaps = *((__GLfloat *) data)++; } // Finally, update pd flags pd->flags |= faceName; } else { // Something went wrong at playback time! We can either try to playback // this record using the regular API or punt it altogether. I cannot think // of a situation when this can happen, so we will punt it for now. WARNING("Display list: playing back POLYMATERIAL outside BEGIN!\n"); } return PC + size; } // Compile a PolyData structure in Begin. If the poly data contains // material changes, it will call __gllc_PolyMaterial to compile the material // changes. This function does not execute the record in COMPILE_AND_EXECUTE // mode. The execution is done when the poly array buffer is flushed. void APIENTRY __glDlistCompilePolyData(__GLcontext *gc, GLboolean bPartial) { POLYARRAY *pa; POLYDATA *pd; GLubyte *data, *data0; GLuint pdflags; GLuint size, newSize; __GLlistExecFunc *fp; ASSERTOPENGL(gc->dlist.beginRec, "not in being!\n"); // If we have already recorded it in PolyArrayFlushPartialPrimitive, skip it. if (gc->dlist.skipPolyData) { gc->dlist.skipPolyData = GL_FALSE; return; } pa = gc->paTeb; if (bPartial) { // Record only current attribute changes pd = pa->pdNextVertex; if (!pd->flags) return; } else { pd = pa->pdNextVertex - 1; } // Record material changes first. if (pd->flags & (POLYDATA_MATERIAL_FRONT | POLYDATA_MATERIAL_BACK)) { POLYMATERIAL *pm; pm = GLTEB_CLTPOLYMATERIAL(); if (pd->flags & POLYDATA_MATERIAL_FRONT) __gllc_PolyMaterial(POLYDATA_MATERIAL_FRONT, pm->pdMaterial0[pd - pa->pdBuffer0].front); if (pd->flags & POLYDATA_MATERIAL_BACK) __gllc_PolyMaterial(POLYDATA_MATERIAL_BACK, pm->pdMaterial0[pd - pa->pdBuffer0].back); if (bPartial) { if (!(pd->flags & ~(POLYDATA_MATERIAL_FRONT | POLYDATA_MATERIAL_BACK))) return; } } // Record POLYARRAY_CLAMP_COLOR flag in the begin record. if (pa->flags & POLYARRAY_CLAMP_COLOR) gc->dlist.beginRec->flags |= DLIST_BEGIN_HAS_CLAMP_COLOR; // Make sure that we handle all the flags! ASSERTOPENGL( !(pd->flags & ~(POLYDATA_EDGEFLAG_BOUNDARY | POLYDATA_EDGEFLAG_VALID | POLYDATA_COLOR_VALID | POLYDATA_NORMAL_VALID | POLYDATA_TEXTURE_VALID | POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4 | POLYDATA_DLIST_COLOR_4 | POLYDATA_FOG_VALID | POLYDATA_DLIST_TEXTURE1 | POLYDATA_DLIST_TEXTURE2 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE4 | POLYDATA_MATERIAL_FRONT | POLYDATA_MATERIAL_BACK)), "Unknown POLYDATA flags!\n"); // Get the flags that we are interested. pdflags = pd->flags & (POLYDATA_EDGEFLAG_BOUNDARY | POLYDATA_EDGEFLAG_VALID | POLYDATA_COLOR_VALID | POLYDATA_NORMAL_VALID | POLYDATA_TEXTURE_VALID | POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4 | POLYDATA_DLIST_COLOR_4 | POLYDATA_DLIST_TEXTURE1 | POLYDATA_DLIST_TEXTURE2 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE4); // Find out if it matches one of the following packed data structure for // fast playback. // C3F_V3F // N3F_V3F // C3F_N3F_V3F (non 1.1 format) // C4F_N3F_V3F // T2F_V3F // T2F_C3F_V3F // T2F_N3F_V3F // T2F_C3F_N3F_V3F (non 1.1 format) // T2F_C4F_N3F_V3F #define VTYPE_V2F (POLYDATA_VERTEX2) #define VTYPE_V3F (POLYDATA_VERTEX3) #define VTYPE_V4F (POLYDATA_VERTEX4) #define VTYPE_C3F (POLYDATA_COLOR_VALID) #define VTYPE_C4F (POLYDATA_COLOR_VALID | POLYDATA_DLIST_COLOR_4) #define VTYPE_N3F (POLYDATA_NORMAL_VALID) #define VTYPE_T2F (POLYDATA_TEXTURE_VALID | POLYDATA_DLIST_TEXTURE2) #define VTYPE_C3F_V3F (VTYPE_C3F | VTYPE_V3F) #define VTYPE_N3F_V3F (VTYPE_N3F | VTYPE_V3F) #define VTYPE_C3F_N3F_V3F (VTYPE_C3F | VTYPE_N3F | VTYPE_V3F) #define VTYPE_C4F_N3F_V3F (VTYPE_C4F | VTYPE_N3F | VTYPE_V3F) #define VTYPE_T2F_V3F (VTYPE_T2F | VTYPE_V3F) #define VTYPE_T2F_C3F_V3F (VTYPE_T2F | VTYPE_C3F | VTYPE_V3F) #define VTYPE_T2F_N3F_V3F (VTYPE_T2F | VTYPE_N3F | VTYPE_V3F) #define VTYPE_T2F_C3F_N3F_V3F (VTYPE_T2F | VTYPE_C3F | VTYPE_N3F | VTYPE_V3F) #define VTYPE_T2F_C4F_N3F_V3F (VTYPE_T2F | VTYPE_C4F | VTYPE_N3F | VTYPE_V3F) // Default playback routine fp = __glle_PolyData; if (!gc->modes.colorIndexMode && !(pdflags & (POLYDATA_EDGEFLAG_BOUNDARY | POLYDATA_EDGEFLAG_VALID))) { switch (pdflags) { case VTYPE_V2F: case VTYPE_V3F: case VTYPE_V4F: ASSERTOPENGL(gc->dlist.mode != GL_COMPILE, "should have been recorded as a Vertex call\n"); break; case VTYPE_C3F_V3F: fp = __glle_PolyData_C3F_V3F; break; case VTYPE_N3F_V3F: fp = __glle_PolyData_N3F_V3F; break; case VTYPE_C3F_N3F_V3F: fp = __glle_PolyData_C3F_N3F_V3F; break; case VTYPE_C4F_N3F_V3F: fp = __glle_PolyData_C4F_N3F_V3F; break; case VTYPE_T2F_V3F: fp = __glle_PolyData_T2F_V3F; break; case VTYPE_T2F_C3F_V3F: fp = __glle_PolyData_T2F_C3F_V3F; break; case VTYPE_T2F_N3F_V3F: fp = __glle_PolyData_T2F_N3F_V3F; break; case VTYPE_T2F_C3F_N3F_V3F: fp = __glle_PolyData_T2F_C3F_N3F_V3F; break; case VTYPE_T2F_C4F_N3F_V3F: fp = __glle_PolyData_T2F_C4F_N3F_V3F; break; } } // Allocate the dlist record. Allocate big enough record and resize it later. size = sizeof(POLYDATA) + sizeof(GLuint); data = (GLubyte *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(size), fp); if (data == NULL) return; data0 = data; // Increment vertex count. if (!bPartial) gc->dlist.beginRec->nVertices++; // Compile the poly data record. // The fast poly data records do not include size and flags fields. if (fp == __glle_PolyData) { // Skip size field to be filled in last ((GLuint *) data)++; // flags and edge flag *((GLuint *) data)++ = pdflags; } // Texture coord if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE2 | POLYDATA_DLIST_TEXTURE1)) { *((__GLfloat *) data)++ = pd->texture.x; if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE2)) { *((__GLfloat *) data)++ = pd->texture.y; if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3)) { *((__GLfloat *) data)++ = pd->texture.z; if (pdflags & (POLYDATA_DLIST_TEXTURE4)) *((__GLfloat *) data)++ = pd->texture.w; } } } // Color if (pdflags & POLYDATA_COLOR_VALID) { *((__GLfloat *) data)++ = pd->colors[0].r; if (!gc->modes.colorIndexMode) { *((__GLfloat *) data)++ = pd->colors[0].g; *((__GLfloat *) data)++ = pd->colors[0].b; if (pdflags & POLYDATA_DLIST_COLOR_4) *((__GLfloat *) data)++ = pd->colors[0].a; } } // Normal if (pdflags & POLYDATA_NORMAL_VALID) { *((__GLfloat *) data)++ = pd->normal.x; *((__GLfloat *) data)++ = pd->normal.y; *((__GLfloat *) data)++ = pd->normal.z; } // Vertex, evalcoord1, evalcoord2, evapoint1, or evalpoint2 if (pdflags & (POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4)) { ASSERTOPENGL(!bPartial, "vertex unexpected\n"); *((__GLfloat *) data)++ = pd->obj.x; if (pdflags & (POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4)) { *((__GLfloat *) data)++ = pd->obj.y; if (pdflags & (POLYDATA_VERTEX3 | POLYDATA_VERTEX4)) { *((__GLfloat *) data)++ = pd->obj.z; if (pdflags & (POLYDATA_VERTEX4)) *((__GLfloat *) data)++ = pd->obj.w; } } } else { ASSERTOPENGL(bPartial, "vertex expected\n"); } // Now fill in the size field newSize = (GLuint) (data - data0); if (fp == __glle_PolyData) *((GLuint *) data0) = newSize; // Resize the record __glDlistResizeCurrentOp(gc, DLIST_SIZE(size), DLIST_SIZE(newSize)); } #ifndef __GL_ASM_FAST_DLIST_PLAYBACK // Define fast playback routines for PolyData records. #define __GLLE_POLYDATA_C3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_C3F_V3F #define __GLLE_POLYDATA_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_N3F_V3F #define __GLLE_POLYDATA_C3F_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_C3F_N3F_V3F #define __GLLE_POLYDATA_C4F_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_C4F_N3F_V3F #define __GLLE_POLYDATA_T2F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_T2F_V3F #define __GLLE_POLYDATA_T2F_C3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_T2F_C3F_V3F #define __GLLE_POLYDATA_T2F_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_T2F_N3F_V3F #define __GLLE_POLYDATA_T2F_C3F_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_T2F_C3F_N3F_V3F #define __GLLE_POLYDATA_T2F_C4F_N3F_V3F 1 #include "dl_pdata.h" #undef __GLLE_POLYDATA_T2F_C4F_N3F_V3F #endif // __GL_ASM_FAST_DLIST_PLAYBACK // Playback a PolyData record in Begin. const GLubyte * FASTCALL __glle_PolyData(__GLcontext *gc, const GLubyte *PC) { GLubyte *data; POLYARRAY *pa; POLYDATA *pd; GLuint size, pdflags; data = (GLubyte *) PC; size = *((GLuint *) data)++; pa = gc->paTeb; if (pa->flags & POLYARRAY_IN_BEGIN) { pdflags = *((GLuint *) data)++; // Make sure that we handle all the flags! ASSERTOPENGL( !(pdflags & ~(POLYDATA_EDGEFLAG_BOUNDARY | POLYDATA_EDGEFLAG_VALID | POLYDATA_COLOR_VALID | POLYDATA_NORMAL_VALID | POLYDATA_TEXTURE_VALID | POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4 | POLYDATA_DLIST_COLOR_4 | POLYDATA_DLIST_TEXTURE1 | POLYDATA_DLIST_TEXTURE2 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE4)), "Unknown POLYDATA flags!\n"); // Update pa flags. pa->flags |= pdflags & (POLYARRAY_VERTEX2 | POLYARRAY_VERTEX3 | POLYARRAY_VERTEX4 | POLYARRAY_TEXTURE1 | POLYARRAY_TEXTURE2 | POLYARRAY_TEXTURE3 | POLYARRAY_TEXTURE4); // Update pd attributes. pd = pa->pdNextVertex; pd->flags |= (pdflags & ~POLYDATA_EDGEFLAG_BOUNDARY); // Edge flag if (pdflags & POLYDATA_EDGEFLAG_VALID) { // Clear the edge flag here since they may be a previous edge flag pd->flags &= ~POLYDATA_EDGEFLAG_BOUNDARY; pd->flags |= pdflags; pa->pdCurEdgeFlag = pd; } // Texture coord // We need to be careful here if it has 2 TexCoord calls with // different sizes. if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE2 | POLYDATA_DLIST_TEXTURE1)) { pd->texture.x = *((__GLfloat *) data)++; pa->pdCurTexture = pd; if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3 | POLYDATA_DLIST_TEXTURE2)) pd->texture.y = *((__GLfloat *) data)++; else pd->texture.y = __glZero; if (pdflags & (POLYDATA_DLIST_TEXTURE4 | POLYDATA_DLIST_TEXTURE3)) pd->texture.z = *((__GLfloat *) data)++; else pd->texture.z = __glZero; if (pdflags & (POLYDATA_DLIST_TEXTURE4)) pd->texture.w = *((__GLfloat *) data)++; else pd->texture.w = __glOne; } // Color if (pdflags & POLYDATA_COLOR_VALID) { pd->color[0].r = *((__GLfloat *) data)++; if (!gc->modes.colorIndexMode) { pd->color[0].g = *((__GLfloat *) data)++; pd->color[0].b = *((__GLfloat *) data)++; if (pdflags & POLYDATA_DLIST_COLOR_4) pd->color[0].a = *((__GLfloat *) data)++; else pd->color[0].a = gc->alphaVertexScale; } pa->pdCurColor = pd; } // Normal if (pdflags & POLYDATA_NORMAL_VALID) { pd->normal.x = *((__GLfloat *) data)++; pd->normal.y = *((__GLfloat *) data)++; pd->normal.z = *((__GLfloat *) data)++; pa->pdCurNormal = pd; } // Vertex, evalcoord1, evalcoord2, evapoint1, or evalpoint2 if (pdflags & (POLYARRAY_VERTEX2 | POLYARRAY_VERTEX3 | POLYARRAY_VERTEX4)) { pd->obj.x = *((__GLfloat *) data)++; if (pdflags & (POLYDATA_VERTEX2 | POLYDATA_VERTEX3 | POLYDATA_VERTEX4)) pd->obj.y = *((__GLfloat *) data)++; if (pdflags & (POLYDATA_VERTEX3 | POLYDATA_VERTEX4)) pd->obj.z = *((__GLfloat *) data)++; else pd->obj.z = __glZero; if (pdflags & (POLYDATA_VERTEX4)) pd->obj.w = *((__GLfloat *) data)++; else pd->obj.w = __glOne; // Advance vertex pointer pa->pdNextVertex++; pd[1].flags = 0; if (pd >= pa->pdFlush) PolyArrayFlushPartialPrimitive(); } } else { // Something went wrong at playback time! We can either try to playback // this record using the regular API or punt it altogether. I cannot think // of a situation when this can happen, so we will punt it for now. WARNING("Display list: playing back POLYDATA outside BEGIN!\n"); } return PC + size; } void APIENTRY __gllc_ArrayElement(GLint i) { __GL_SETUP(); if (gc->vertexArray.flags & __GL_VERTEX_ARRAY_DIRTY) VA_ValidateArrayPointers(gc); VA_ArrayElementCompile(gc, i); } #define COMPILEARRAYPOINTER(ap, i) \ ((*(ap).pfnCompile)((ap).pointer + (i) * (ap).ibytes)) void FASTCALL VA_ArrayElementCompile(__GLcontext *gc, GLint i) { GLuint vaMask = gc->vertexArray.mask; // Call the individual compilation routines. They handle Begin mode, // color mode, and COMPILE_AND_EXECUTE mode correctly. if (vaMask & VAMASK_EDGEFLAG_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.edgeFlag, i); if (vaMask & VAMASK_TEXCOORD_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.texCoord, i); if (vaMask & VAMASK_COLOR_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.color, i); if (vaMask & VAMASK_INDEX_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.index, i); if (vaMask & VAMASK_NORMAL_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.normal, i); if (vaMask & VAMASK_VERTEX_ENABLE_MASK) COMPILEARRAYPOINTER(gc->vertexArray.vertex, i); } // Compile DrawArrays into Begin/End records. Since Begin/End records // contain optimized POLYDATA records, execution speed of these records // is optimal. However, it takes longer to compile this function using // this approach. But with this method, we don't have to deal with color // mode and COMPILE_AND_EXECUTE mode here. void APIENTRY __gllc_DrawArrays(GLenum mode, GLint first, GLsizei count) { int i; POLYARRAY *pa; __GL_SETUP(); pa = gc->paTeb; // Not allowed in begin/end. if (pa->flags & POLYARRAY_IN_BEGIN) { __gllc_InvalidOperation(); return; } if ((GLuint) mode > GL_POLYGON) { __gllc_InvalidEnum(); return; } if (count < 0) { __gllc_InvalidValue(); return; } else if (!count) return; // Find array element function to use. if (gc->vertexArray.flags & __GL_VERTEX_ARRAY_DIRTY) VA_ValidateArrayPointers(gc); // Draw the array elements. __gllc_Begin(mode); gc->dlist.beginRec->flags |= DLIST_BEGIN_DRAWARRAYS; for (i = 0; i < count; i++) VA_ArrayElementCompile(gc, first + i); __gllc_End(); } #define __GL_PAD8(x) (((x) + 7) & ~7) GLuint FASTCALL __glDrawElements_size(__GLcontext *gc, GLsizei nVertices, GLsizei nElements, struct __gllc_DrawElements_Rec *rec) { GLuint size; GLuint vaMask; // Compute the size of each of the six arrays. Always keep size and address // QWORD aligned since some arrays may use GLdouble. size = __GL_PAD8(sizeof(struct __gllc_DrawElements_Rec)); vaMask = gc->vertexArray.mask; if (vaMask & VAMASK_EDGEFLAG_ENABLE_MASK) { rec->edgeFlagOff = size; size += __GL_PAD8(nVertices * sizeof(GLboolean)); } else rec->edgeFlagOff = 0; if (vaMask & VAMASK_TEXCOORD_ENABLE_MASK) { rec->texCoordOff = size; size += __GL_PAD8(nVertices * gc->vertexArray.texCoord.size * __GLTYPESIZE(gc->vertexArray.texCoord.type)); } else rec->texCoordOff = 0; if (vaMask & VAMASK_COLOR_ENABLE_MASK) { rec->colorOff = size; size += __GL_PAD8(nVertices * gc->vertexArray.color.size * __GLTYPESIZE(gc->vertexArray.color.type)); } else rec->colorOff = 0; if (vaMask & VAMASK_INDEX_ENABLE_MASK) { rec->indexOff = size; size += __GL_PAD8(nVertices * __GLTYPESIZE(gc->vertexArray.index.type)); } else rec->indexOff = 0; if (vaMask & VAMASK_NORMAL_ENABLE_MASK) { rec->normalOff = size; size += __GL_PAD8(nVertices * 3 * __GLTYPESIZE(gc->vertexArray.normal.type)); } else rec->normalOff = 0; if (vaMask & VAMASK_VERTEX_ENABLE_MASK) { rec->vertexOff = size; size += __GL_PAD8(nVertices * gc->vertexArray.vertex.size * __GLTYPESIZE(gc->vertexArray.vertex.type)); } else rec->vertexOff = 0; rec->mapOff = size; size += __GL_PAD8(nElements * sizeof(GLubyte)); return(size); } void FASTCALL __gllc_ReducedElementsHandler(__GLcontext *gc, GLenum mode, GLsizei iVertexCount, GLsizei iVertexBase, VAMAP *pvmVertices, GLsizei iElementCount, GLubyte *pbElements, GLboolean fPartial) { GLuint vaMask; GLuint size; GLubyte *pv1, *pv2; GLsizei stride; GLsizei i; struct __gllc_DrawElements_Rec *data, drawElementsRec; ASSERTOPENGL(pvmVertices != NULL, "__gllc_ReducedElementsHandler requires mapped vertices\n"); // Allocate the record. size = __glDrawElements_size(gc, iVertexCount, iElementCount, &drawElementsRec); data = (struct __gllc_DrawElements_Rec *) __glDlistAddOpAligned(gc, DLIST_SIZE(size), DLIST_GENERIC_OP(DrawElements)); if (data == NULL) { return; } #ifndef _IA64_ ASSERTOPENGL((UINT_PTR) data == __GL_PAD8((UINT_PTR) data), "data not qword aligned\n"); #endif vaMask = gc->vertexArray.mask; data->mode = mode; data->iElementCount = iElementCount; data->iVertexCount = iVertexCount; data->vaMask = vaMask; data->partial = fPartial; data->recSize = size; data->edgeFlagOff = drawElementsRec.edgeFlagOff; data->texCoordOff = drawElementsRec.texCoordOff; data->indexOff = drawElementsRec.indexOff; data->colorOff = drawElementsRec.colorOff; data->normalOff = drawElementsRec.normalOff; data->vertexOff = drawElementsRec.vertexOff; data->mapOff = drawElementsRec.mapOff; // Record the vertex arrays. // Note that iVertexBase parameter is not used, since all accesses here are // 0-based. It is there for function ptr compatibility with glcltReducedElementHandler if (vaMask & VAMASK_EDGEFLAG_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->edgeFlagOff]; pv1 = (GLubyte *) gc->vertexArray.edgeFlag.pointer; stride = gc->vertexArray.edgeFlag.ibytes; for (i = 0; i < iVertexCount; i++) { *((GLboolean *) pv2) = *((GLboolean *) (pv1 + pvmVertices[i].iIn * stride)); pv2 += sizeof(GLboolean); } } if (vaMask & VAMASK_TEXCOORD_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->texCoordOff]; size = gc->vertexArray.texCoord.size * __GLTYPESIZE(gc->vertexArray.texCoord.type); pv1 = (GLubyte *) gc->vertexArray.texCoord.pointer; stride = gc->vertexArray.texCoord.ibytes; data->texCoordSize = gc->vertexArray.texCoord.size; data->texCoordType = gc->vertexArray.texCoord.type; for (i = 0; i < iVertexCount; i++) { memcpy(pv2, pv1 + pvmVertices[i].iIn * stride, size); pv2 += size; } } if (vaMask & VAMASK_COLOR_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->colorOff]; size = gc->vertexArray.color.size * __GLTYPESIZE(gc->vertexArray.color.type); pv1 = (GLubyte *) gc->vertexArray.color.pointer; stride = gc->vertexArray.color.ibytes; data->colorSize = gc->vertexArray.color.size; data->colorType = gc->vertexArray.color.type; for (i = 0; i < iVertexCount; i++) { memcpy(pv2, pv1 + pvmVertices[i].iIn * stride, size); pv2 += size; } } if (vaMask & VAMASK_INDEX_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->indexOff]; size = __GLTYPESIZE(gc->vertexArray.index.type); pv1 = (GLubyte *) gc->vertexArray.index.pointer; stride = gc->vertexArray.index.ibytes; data->indexType = gc->vertexArray.index.type; for (i = 0; i < iVertexCount; i++) { memcpy(pv2, pv1 + pvmVertices[i].iIn * stride, size); pv2 += size; } } if (vaMask & VAMASK_NORMAL_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->normalOff]; size = 3 * __GLTYPESIZE(gc->vertexArray.normal.type); pv1 = (GLubyte *) gc->vertexArray.normal.pointer; stride = gc->vertexArray.normal.ibytes; data->normalType = gc->vertexArray.normal.type; for (i = 0; i < iVertexCount; i++) { memcpy(pv2, pv1 + pvmVertices[i].iIn * stride, size); pv2 += size; } } if (vaMask & VAMASK_VERTEX_ENABLE_MASK) { pv2 = &((GLubyte *) data)[data->vertexOff]; size = gc->vertexArray.vertex.size * __GLTYPESIZE(gc->vertexArray.vertex.type); pv1 = (GLubyte *) gc->vertexArray.vertex.pointer; stride = gc->vertexArray.vertex.ibytes; data->vertexSize = gc->vertexArray.vertex.size; data->vertexType = gc->vertexArray.vertex.type; for (i = 0; i < iVertexCount; i++) { memcpy(pv2, pv1 + pvmVertices[i].iIn * stride, size); pv2 += size; } } // Record new index mapping array. pv2 = &((GLubyte *) data)[data->mapOff]; memcpy(pv2, pbElements, iElementCount*sizeof(GLubyte)); __glDlistAppendOp(gc, data, __glle_DrawElements); } void APIENTRY __gllc_DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *pIn) { POLYARRAY *pa; GLuint iIn; GLsizei iCount; struct __gllc_DrawElementsBegin_Rec *dataBegin; __GL_SETUP(); // Flush the cached memory pointers if we are in COMPILE_AND_EXECUTE mode. // See __glShrinkDlist for details. if (gc->dlist.mode == GL_COMPILE_AND_EXECUTE) glsbAttention(); pa = gc->paTeb; // If we are already in the begin/end bracket, return an error. if (pa->flags & POLYARRAY_IN_BEGIN) { __gllc_InvalidOperation(); return; } if ((GLuint) mode > GL_POLYGON) { __gllc_InvalidEnum(); return; } if (count < 0) { __gllc_InvalidValue(); return; } else if (!count) return; switch (type) { case GL_UNSIGNED_BYTE: case GL_UNSIGNED_SHORT: case GL_UNSIGNED_INT: break; default: __gllc_InvalidEnum(); return; } // Find array element function to use. if (gc->vertexArray.flags & __GL_VERTEX_ARRAY_DIRTY) VA_ValidateArrayPointers(gc); // Convert Points, Line Loop and Polygon to DrawArrays call. Points and Polygon // don't benefit from optimization in this function. Further, Polygon and // Line Loop are too tricky to deal with in this function. if (mode == GL_POINTS || mode == GL_LINE_LOOP || mode == GL_POLYGON) { __gllc_Begin(mode); gc->dlist.beginRec->flags |= DLIST_BEGIN_DRAWARRAYS; for (iCount = 0; iCount < count; iCount++) { // Get next input index. if (type == GL_UNSIGNED_BYTE) iIn = (GLuint) ((GLubyte *) pIn)[iCount]; else if (type == GL_UNSIGNED_SHORT) iIn = (GLuint) ((GLushort *) pIn)[iCount]; else iIn = (GLuint) ((GLuint *) pIn)[iCount]; VA_ArrayElementCompile(gc, iIn); } __gllc_End(); return; } // Allocate begin record dataBegin = (struct __gllc_DrawElementsBegin_Rec *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(sizeof(struct __gllc_DrawElementsBegin_Rec)), DLIST_GENERIC_OP(DrawElementsBegin)); if (dataBegin == NULL) { return; } dataBegin->mode = mode; dataBegin->count = min(count, VA_DRAWELEM_MAP_SIZE); dataBegin->vaMask = gc->vertexArray.mask; __glDlistAppendOp(gc, dataBegin, __glle_DrawElementsBegin); // Reduce input data into easily processed chunks ReduceDrawElements(gc, mode, count, type, pIn, __gllc_ReducedElementsHandler); } const GLubyte * FASTCALL __glle_DrawElementsBegin(__GLcontext *gc, const GLubyte *PC) { struct __gllc_DrawElementsBegin_Rec *data; // Not allowed in begin/end. // Must use the client side begin state if (gc->paTeb->flags & POLYARRAY_IN_BEGIN) { GLSETERROR(GL_INVALID_OPERATION); // Mark saved state as invalid gc->savedVertexArray.flags = 0xffffffff; goto __glle_DrawElementsBegin_exit; } data = (struct __gllc_DrawElementsBegin_Rec *) PC; // Save vertex array states. gc->savedVertexArray = gc->vertexArray; // Set up temporary vertex arrays. // By setting up the mask value in gc, we don't need to call EnableClientState // and DisableClientState. We still need to set up pointers for the enabled // arrays. gc->vertexArray.mask = data->vaMask; // Force validation since we just completely changed the vertex array // enable state VA_ValidateArrayPointers(gc); // Begin primitive VA_DrawElementsBegin(gc->paTeb, data->mode, data->count); __glle_DrawElementsBegin_exit: return PC + sizeof(struct __gllc_DrawElementsBegin_Rec); } void APIENTRY __gllc_DrawRangeElementsWIN(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *pIn) { // !!! Currently we call the DrawElements function here when in lc mode. // If compile time performance for DrawRangeElements becomes an issue, then // we can flesh out this function. __gllc_DrawElements( mode, count, type, pIn ); } const GLubyte * FASTCALL __glle_DrawElements(__GLcontext *gc, const GLubyte *PC) { GLuint vaMask; POLYARRAY *pa; struct __gllc_DrawElements_Rec *data; data = (struct __gllc_DrawElements_Rec *) PC; pa = gc->paTeb; // Must be in begin since DrawElementsBegin has started the primitive // Must use the client side begin state if ((pa->flags & POLYARRAY_IN_BEGIN) == 0 || gc->savedVertexArray.flags == 0xffffffff) { GLSETERROR(GL_INVALID_OPERATION); goto __glle_DrawElements_exit; } vaMask = data->vaMask; // Set up temporary vertex arrays. // We need to temporarily mask off the begin flag so that these // calls can succeed. We probably want to do something smarter // that avoids parameter validation but this is good enough for now // Note that in immediate mode, the array function pointers are set up // once in __glle_DrawElementsBegin and remain unchanged until all // sub-batches are processed. In COMPILE_AND_EXECUTE mode, the array // function pointers are also set up once in __glle_DrawElementsBegin. // Since these function pointers are the same for compilation and // execution, we don't need to re-validate them for each sub-batch here. pa->flags ^= POLYARRAY_IN_BEGIN; if (vaMask & VAMASK_EDGEFLAG_ENABLE_MASK) glcltEdgeFlagPointer(0, &((GLubyte *) data)[data->edgeFlagOff]); if (vaMask & VAMASK_TEXCOORD_ENABLE_MASK) glcltTexCoordPointer(data->texCoordSize, data->texCoordType, 0, &((GLubyte *) data)[data->texCoordOff]); if (vaMask & VAMASK_COLOR_ENABLE_MASK) glcltColorPointer(data->colorSize, data->colorType, 0, &((GLubyte *) data)[data->colorOff]); if (vaMask & VAMASK_INDEX_ENABLE_MASK) glcltIndexPointer(data->indexType, 0, &((GLubyte *) data)[data->indexOff]); if (vaMask & VAMASK_NORMAL_ENABLE_MASK) glcltNormalPointer(data->normalType, 0, &((GLubyte *) data)[data->normalOff]); if (vaMask & VAMASK_VERTEX_ENABLE_MASK) glcltVertexPointer(data->vertexSize, data->vertexType, 0, &((GLubyte *) data)[data->vertexOff]); pa->flags ^= POLYARRAY_IN_BEGIN; // Call immediate mode chunk handler glcltReducedElementsHandler(gc, data->mode, data->iVertexCount, 0, NULL, data->iElementCount, (GLubyte *)data+data->mapOff, data->partial); // Restore vertex array states in the following conditions: // 1. The DrawElements record is completed // 2. It is in COMPILE_AND_EXECUTE mode and it is not called as a result // of executing a CallList record. That is, the record is being // compile *and* executed at the same time. if ((!data->partial) || ((gc->dlist.mode == GL_COMPILE_AND_EXECUTE) && !gc->dlist.nesting)) { gc->vertexArray = gc->savedVertexArray; } __glle_DrawElements_exit: return PC + data->recSize; } void APIENTRY __gllc_Begin ( IN GLenum mode ) { POLYARRAY *pa; struct __gllc_Begin_Rec *data; __GL_SETUP(); // Flush the cached memory pointers if we are in COMPILE_AND_EXECUTE mode. // See __glShrinkDlist for details. if (gc->dlist.mode == GL_COMPILE_AND_EXECUTE) glsbAttention(); // If we are already in the begin/end bracket, return an error. pa = gc->paTeb; if (pa->flags & POLYARRAY_IN_BEGIN) { __gllc_InvalidOperation(); return; } if ((GLuint) mode > GL_POLYGON) { __gllc_InvalidEnum(); return; } // Add the Begin record. data = (struct __gllc_Begin_Rec *) __glDlistAddOpUnaligned(gc, DLIST_SIZE(sizeof(struct __gllc_Begin_Rec)), DLIST_GENERIC_OP(Begin)); if (data == NULL) return; data->mode = mode; data->flags = 0; data->nVertices = 0; gc->dlist.skipPolyData = GL_FALSE; // Use poly array code to compile the data structure for this primitive. (*gc->savedCltProcTable.glDispatchTable.glBegin)(mode); // Save the Begin record pointer. We are now compiling the poly array // primitive. It is set to NULL in End. gc->dlist.beginRec = data; } const GLubyte * FASTCALL __glle_Begin(__GLcontext *gc, const GLubyte *PC) { POLYARRAY *pa; struct __gllc_Begin_Rec *data; data = (struct __gllc_Begin_Rec *) PC; pa = gc->paTeb; // try not to break the poly data records into batches! The number 8 // is loosely chosen to allow for the poly array entry, the reserved // polygon entries, and the flush limit. At worst, it causes an // unnecessary attention! if (data->nVertices <= (GLint) gc->vertex.pdBufSize - 8 && data->nVertices >= (GLint) (pa->pdBufferMax - pa->pdBufferNext + 1 - 8)) glsbAttention(); // call glcltBegin first (*gc->savedCltProcTable.glDispatchTable.glBegin)(data->mode); if (data->flags & DLIST_BEGIN_DRAWARRAYS) pa->flags |= POLYARRAY_SAME_POLYDATA_TYPE; // Set POLYARRAY_CLAMP_COLOR flag. if (data->flags & DLIST_BEGIN_HAS_CLAMP_COLOR) pa->flags |= POLYARRAY_CLAMP_COLOR; // handle "otherColor" if (data->flags & DLIST_BEGIN_HAS_OTHER_COLOR) { if (gc->modes.colorIndexMode) (*gc->savedCltProcTable.glDispatchTable.glColor4fv)((GLfloat *) &data->otherColor); else (*gc->savedCltProcTable.glDispatchTable.glIndexf)(data->otherColor.r); } return PC + sizeof(struct __gllc_Begin_Rec); } void APIENTRY __gllc_End ( void ) { GLuint size; POLYARRAY *pa; void *data; __GL_SETUP(); pa = gc->paTeb; // If we are compiling poly array, finish the poly array processing. // Note that we may have aborted poly array compilation in CallList(s). // In that case, we need to compile an End record. if (gc->dlist.beginRec) { ASSERTOPENGL(pa->flags & POLYARRAY_IN_BEGIN, "not in begin!\n"); // Record the last POLYDATA since it may contain attribute changes. __glDlistCompilePolyData(gc, GL_TRUE); // Call glcltEnd to finish the primitive. (*gc->savedCltProcTable.glDispatchTable.glEnd)(); // Record the End call. __glDlistAddOpUnaligned(gc, DLIST_SIZE(0), DLIST_GENERIC_OP(End)); // If we are in COMPILE mode, we need to reset the command buffer, // the poly array buffer, and the poly material buffer. if (gc->dlist.mode == GL_COMPILE) { glsbResetBuffers(TRUE); // Clear begin flag too pa->flags &= ~POLYARRAY_IN_BEGIN; } // Terminate poly array compilation gc->dlist.beginRec = NULL; } else { // Record the call. data = __glDlistAddOpUnaligned(gc, DLIST_SIZE(0), DLIST_GENERIC_OP(End)); if (data == NULL) return; __glDlistAppendOp(gc, data, __glle_End); } } const GLubyte * FASTCALL __glle_End(__GLcontext *gc, const GLubyte *PC) { (*gc->savedCltProcTable.glDispatchTable.glEnd)(); return PC; }