/*==========================================================================;
 *
 *  Copyright (C) 1997 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       dphal.c
 *  Content:    DrawPrimitive implementation for DrawPrimitive HALs
 *
 ***************************************************************************/

#include "pch.cpp"
#pragma hdrstop
#include "drawprim.hpp"
#include "clipfunc.h"
#include "d3dfei.h"

extern const DWORD LOWVERTICESNUMBER = 20;

#if DBG
extern DWORD FaceCount;
DWORD FaceCount = 0;
DWORD StartFace = 0;
DWORD EndFace = 10000;
#endif

extern void SetDebugRenderState(DWORD value);

#define ALIGN32(x) x = ((DWORD)(x + 31)) & (~31);
//---------------------------------------------------------------------
// Array to map D3DVERTEXTYPE to FVF vertex type
//
DWORD d3dVertexToFVF[4] =
{
    0,
    D3DFVF_VERTEX,
    D3DFVF_LVERTEX,
    D3DFVF_TLVERTEX
};
//---------------------------------------------------------------------
// Handles strides and FVF
//
#undef DPF_MODNAME
#define DPF_MODNAME "D3DFE_updateExtents"

void D3DFE_updateExtents(LPDIRECT3DDEVICEI lpDevI)
{
    int i;
    D3DVECTOR *v = (D3DVECTOR*)lpDevI->position.lpvData;
    DWORD stride = lpDevI->position.dwStride;
    for (i = lpDevI->dwNumVertices; i; i--)
    {
        if (v->x < lpDevI->rExtents.x1)
            lpDevI->rExtents.x1 = v->x;
        if (v->x > lpDevI->rExtents.x2)
            lpDevI->rExtents.x2 = v->x;
        if (v->y < lpDevI->rExtents.y1)
            lpDevI->rExtents.y1 = v->y;
        if (v->y > lpDevI->rExtents.y2)
            lpDevI->rExtents.y2 = v->y;
        v = (D3DVECTOR*)((char*)v + stride);
    }
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "MapFVFtoTLVertex1"

inline void MapFVFtoTLVertex1(LPDIRECT3DDEVICEI lpDevI, D3DTLVERTEX *pOut,
                              DWORD *pIn)
{
// Copy position
    pOut->sx  = *(D3DVALUE*)pIn++;
    pOut->sy  = *(D3DVALUE*)pIn++;
    pOut->sz  = *(D3DVALUE*)pIn++;
    pOut->rhw = *(D3DVALUE*)pIn++;
// Other fields: diffuse, specular, texture
    if (lpDevI->dwVIDOut & D3DFVF_DIFFUSE)
        pOut->color = *pIn++;
    else
    {
        pOut->color = __DEFAULT_DIFFUSE;
    }
    if (lpDevI->dwVIDOut & D3DFVF_SPECULAR)
        pOut->specular = *pIn++;
    else
    {
        pOut->specular= __DEFAULT_SPECULAR;
    }
    if (lpDevI->nTexCoord)
    {
        pIn = &pIn[lpDevI->dwTextureIndexToCopy << 1];
        pOut->tu = *(D3DVALUE*)&pIn[0];
        pOut->tv = *(D3DVALUE*)&pIn[1];
    }
    else
    {
        pOut->tu = 0;
        pOut->tv = 0;
    }
}
//---------------------------------------------------------------------
// All vertices from lpDevI->lpVout are copied to the output buffer, expanding
// to D3DTLVERTEX.
// The output buffer is lpAddress if it is not NULL, otherwise it is TLVbuf
//
#undef DPF_MODNAME
#define DPF_MODNAME "MapFVFtoTLVertex"

HRESULT MapFVFtoTLVertex(LPDIRECT3DDEVICEI lpDevI, LPVOID lpAddress)
{
    int i;
    DWORD size = lpDevI->dwNumVertices * sizeof(D3DTLVERTEX);
    D3DTLVERTEX *pOut;
    if (lpAddress)
        pOut = (D3DTLVERTEX*)lpAddress;
    else
    {
    // See if TL buffer has sufficient space
        if (size > lpDevI->TLVbuf.GetSize())
        {
            if (lpDevI->TLVbuf.Grow(lpDevI, size) != D3D_OK)
            {
                D3D_ERR( "Could not grow TL vertex buffer" );
                return DDERR_OUTOFMEMORY;
            }
        }
        pOut = (D3DTLVERTEX*)lpDevI->TLVbuf.GetAddress();
    }
// Map vertices
    DWORD *pIn = (DWORD*)lpDevI->lpvOut;
    for (i=lpDevI->dwNumVertices; i; i--)
    {
        MapFVFtoTLVertex1(lpDevI, pOut, pIn);
        pOut++;
        pIn = (DWORD*)((char*)pIn + lpDevI->dwOutputSize);
    }
    return D3D_OK;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CheckDrawPrimitive"

HRESULT CheckDrawPrimitive(LPDIRECT3DDEVICEI lpDevI)
{
    D3DFE_PROCESSVERTICES* data = lpDevI;
    HRESULT ret = CheckDeviceSettings(lpDevI);
    if (ret != D3D_OK)
        return ret;
    if (!data->dwNumVertices)
    {
        D3D_ERR( "Invalid dwNumVertices in DrawPrimitive" );
        return DDERR_INVALIDPARAMS;
    }

    if(data->position.lpvData==NULL) {
        D3D_ERR( "Invalid lpvVertices param in DrawPrimitive" );
        return DDERR_INVALIDPARAMS;
    }

    switch (data->primType)
    {
    case D3DPT_POINTLIST:
                break;
    case D3DPT_LINELIST:
        if (data->dwNumVertices & 1)
        {
            D3D_ERR( "DrawPrimitive: bad vertex count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_LINESTRIP:
        if (data->dwNumVertices == 1)
        {
            D3D_ERR( "DrawPrimitive: bad vertex count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_TRIANGLEFAN:
    case D3DPT_TRIANGLESTRIP:
        if (data->dwNumVertices < 3)
        {
            D3D_ERR( "DrawPrimitive: bad vertex count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_TRIANGLELIST:
        if ( (data->dwNumVertices % 3) != 0 )
        {
            D3D_ERR( "DrawPrimitive: bad vertex count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    default:
        D3D_ERR( "Unknown or unsupported primitive type requested of DrawPrimitive" );
        return D3DERR_INVALIDPRIMITIVETYPE;
    }

    return D3D_OK;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CheckDrawIndexedPrimitive"

HRESULT
CheckDrawIndexedPrimitive(LPDIRECT3DDEVICEI lpDevI)
{
    D3DFE_PROCESSVERTICES *data = lpDevI;
    DWORD i;

    HRESULT ret = CheckDeviceSettings(lpDevI);
    if (ret != D3D_OK)
        return ret;
    if (data->dwNumVertices <= 0 || data->dwNumIndices <= 0)
    {
        D3D_ERR( "Invalid dwNumVertices or dwNumIndices in DrawIndexedPrimitive" );
        return DDERR_INVALIDPARAMS;
    }

    if (data->dwNumVertices > 65535ul )
    {
        D3D_ERR( "DrawIndexedPrimitive vertex array > 64K" );
        return DDERR_INVALIDPARAMS;
    }

    if((data->lpwIndices==NULL) || IsBadReadPtr(data->lpwIndices,data->dwNumIndices*sizeof(WORD))) {
        D3D_ERR( "Invalid lpwIndices param in DrawIndexedPrimitive" );
        return DDERR_INVALIDPARAMS;
    }

    if(data->position.lpvData==NULL) {
        D3D_ERR( "Invalid lpvVertices param in DrawIndexedPrimitive" );
        return DDERR_INVALIDPARAMS;
    }

    switch (data->primType)
    {
    case D3DPT_LINELIST:
        if (data->dwNumIndices & 1)
        {
            D3D_ERR( "DrawIndexedPrimitive: bad index count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_LINESTRIP:
        if (data->dwNumIndices == 1)
        {
            D3D_ERR( "DrawIndexedPrimitive: bad index count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_TRIANGLEFAN:
    case D3DPT_TRIANGLESTRIP:
        if (data->dwNumIndices < 3)
        {
            D3D_ERR( "DrawIndexedPrimitive: bad index count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    case D3DPT_TRIANGLELIST:
        if ( (data->dwNumIndices % 3) != 0 )
        {
            D3D_ERR( "DrawIndexedPrimitive: bad index count" );
            return DDERR_INVALIDPARAMS;
        }
        break;
    default:
        D3D_ERR( "Unknown or unsupported primitive type requested of DrawIndexedPrimitive" );
        return D3DERR_INVALIDPRIMITIVETYPE;
    }
    for (i=0; i < data->dwNumIndices; i++)
    {
        if (data->lpwIndices[i] >= data->dwNumVertices)
        {
            D3D_ERR( "Invalid index value in DrawIndexedPrimitive" );
            return DDERR_INVALIDPARAMS;
        }
    }
    return D3D_OK;
}
//---------------------------------------------------------------------
// Draws non-indexed primitives which do not require clipping
//
#undef DPF_MODNAME
#define DPF_MODNAME "DrawPrim"

#define __DRAWPRIMFUNC
#include "dpgen.h"
//---------------------------------------------------------------------
// Draws indexed primitives which do not require clipping
//
#undef DPF_MODNAME
#define DPF_MODNAME "DrawIndexedPrim"

#define __DRAWPRIMFUNC
#define __DRAWPRIMINDEX
#include "dpgen.h"
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "FlushStatesDP"

HRESULT
CDirect3DDeviceIDP::FlushStates()
{
    HRESULT dwRet=D3D_OK;
    FlushTextureFromDevice( this ); // delink all texture surfaces
    if (this->dwDPOffset>sizeof(D3DHAL_DRAWPRIMCOUNTS))
    {
        if ((dwRet=CheckSurfaces()) != D3D_OK)
        {
            this->dwDPOffset = sizeof(D3DHAL_DRAWPRIMCOUNTS);
            this->lpDPPrimCounts = (LPD3DHAL_DRAWPRIMCOUNTS)this->lpwDPBuffer;
            memset( (char *)this->lpwDPBuffer,0,sizeof(D3DHAL_DRAWPRIMCOUNTS)); //Clear header also
            if (dwRet == DDERR_SURFACELOST)
            {
                this->dwFEFlags |= D3DFE_LOSTSURFACES;
                return D3D_OK;
            }
            return dwRet;
        }

        D3DHAL_DRAWPRIMITIVESDATA dpData;
        DWORD   dwDPOffset;
        if (this->lpDPPrimCounts->wNumVertices)    //this->lpDPPrimCounts->wNumVertices==0 means the end
        {                      //force it if not
            memset(((LPBYTE)this->lpwDPBuffer+this->dwDPOffset),0,sizeof(D3DHAL_DRAWPRIMCOUNTS));
        }
        dpData.dwhContext = this->dwhContext;
        dpData.dwFlags =  0;
        dpData.lpvData = this->lpwDPBuffer;
        if (FVF_DRIVERSUPPORTED(this))
            dpData.dwFVFControl = this->dwCurrentBatchVID;
        else
        {
            if (this->dwDebugFlags & D3DDEBUG_DISABLEFVF)
                dpData.dwFVFControl = D3DFVF_TLVERTEX;
            else
                dpData.dwFVFControl = 0;    //always zero for non-FVF drivers
        }
        dpData.ddrval = 0;
        dwDPOffset=this->dwDPOffset;  //save it in case Flush returns prematurely
#if 0
        if (D3DRENDERSTATE_TEXTUREHANDLE==*((DWORD*)this->lpwDPBuffer+2))
        DPF(0,"Flushing dwDPOffset=%08lx ddihandle=%08lx",dwDPOffset,*((DWORD*)this->lpwDPBuffer+3));
#endif  //0
        //we clear this to break re-entering as SW rasterizer needs to lock DDRAWSURFACE
        this->dwDPOffset = sizeof(D3DHAL_DRAWPRIMCOUNTS);

        // Spin waiting on the driver if wait requested
#if _D3D_FORCEDOUBLE
        CD3DForceFPUDouble  ForceFPUDouble(this);
#endif  //_D3D_FORCEDOUBLE
        do {
#ifndef WIN95
            if((dwRet = CheckContextSurface(this)) != D3D_OK)
            {
                this->dwDPOffset = dwDPOffset;
                return (dwRet);
            }
#endif //WIN95
            CALL_HAL2ONLY(dwRet, this, DrawPrimitives, &dpData);
            if (dwRet != DDHAL_DRIVER_HANDLED)
            {
                D3D_ERR ( "Driver call for DrawOnePrimitive failed" );
                // Need sensible return value in this case,
                // currently we return whatever the driver stuck in here.
            }
        } while (dpData.ddrval == DDERR_WASSTILLDRAWING);
        this->lpDPPrimCounts = (LPD3DHAL_DRAWPRIMCOUNTS)this->lpwDPBuffer;
        memset( (char *)this->lpwDPBuffer,0,sizeof(D3DHAL_DRAWPRIMCOUNTS));   //Clear header also
        dwRet= dpData.ddrval;
        this->dwCurrentBatchVID = this->dwVIDOut;
    }
    return dwRet;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DoDrawPrimitive"

HRESULT DoDrawPrimitive(LPD3DFE_PROCESSVERTICES pv)
{
    HRESULT ret;

    if (!CheckIfNeedClipping(pv))
        return pv->DrawPrim();

    // Preserve primitive type for large begin-end primitives
    // Primitive type could be changed by the clipper
    D3DPRIMITIVETYPE oldPrimType = pv->primType;
    switch (pv->primType)
    {
    case D3DPT_POINTLIST:
        ret = ProcessClippedPoints(pv);
        break;
    case D3DPT_LINELIST:
        ret = ProcessClippedLine(pv);
        break;
    case D3DPT_LINESTRIP:
        ret = ProcessClippedLine(pv);
        break;
    case D3DPT_TRIANGLELIST:
        ret = ProcessClippedTriangleList(pv);
        break;
    case D3DPT_TRIANGLESTRIP:
        ret = ProcessClippedTriangleStrip(pv);
        break;
    case D3DPT_TRIANGLEFAN:
        ret = ProcessClippedTriangleFan(pv);
        break;
    default:
        ret = DDERR_GENERIC;
        break;
    }
    ClampExtents(pv);
    pv->primType = oldPrimType;
    return ret;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DoDrawIndexedPrimitive"

HRESULT DoDrawIndexedPrimitive(LPD3DFE_PROCESSVERTICES pv)
{
    HRESULT ret;

    if (!CheckIfNeedClipping(pv))
        return pv->DrawIndexPrim();

    // Preserve primitive type for large begin-end primitives
    // Primitive type could be changed by the clipper
    D3DPRIMITIVETYPE oldPrimType = pv->primType;
    switch (pv->primType)
    {
    case D3DPT_LINELIST:
        ret = ProcessClippedIndexedLine(pv);
        break;
    case D3DPT_LINESTRIP:
        ret = ProcessClippedIndexedLine(pv);
        break;
    case D3DPT_TRIANGLELIST:
        ret = ProcessClippedIndexedTriangleList(pv);
            break;
    case D3DPT_TRIANGLEFAN:
        ret = ProcessClippedIndexedTriangleFan(pv);
        break;
    case D3DPT_TRIANGLESTRIP:
        ret = ProcessClippedIndexedTriangleStrip(pv);
        break;
    default:
        break;
    }
    ClampExtents(pv);
    pv->primType = oldPrimType;
    return ret;
}
//---------------------------------------------------------------------
// ProcessPrimitive processes indexed, non-indexed primitives or
// vertices only as defined by "op"
//
// op = __PROCPRIMOP_NONINDEXEDPRIM by default
//
HRESULT DIRECT3DDEVICEI::ProcessPrimitive(__PROCPRIMOP op)
{
    HRESULT ret=D3D_OK;
    DWORD vertexPoolSize;
    // Update vertex stats
    this->D3DStats.dwVerticesProcessed += this->dwNumVertices;
        DWORD dwCurrPrimVertices = this->dwNumVertices;

    // Need to call UpdateTextures()
    this->dwFEFlags |= D3DFE_NEED_TEXTURE_UPDATE;

    // Viewport ID could be different from Device->v_id, because during Execute call
    // Device->v_id is changed to whatever viewport is used as a parameter.
    // So we have to make sure that we use the right viewport.
    //
    LPDIRECT3DVIEWPORTI lpView = this->lpCurrentViewport;
    if (this->v_id != lpView->v_id)
    {
        HRESULT ret = downloadView(lpView);
        if (ret != D3D_OK)
            return ret;
    }

// We need to grow TL vertex buffer if
// 1. We have to transform vertices
// 2. We do not have to transform vertices and
//    2.1 Ramp mode is used, because we have to change vertex colors
//    2.2 DP2HAL is used and we have small number of vertices, so we need to
//        copy vertices into TL buffer
//
    vertexPoolSize = this->dwNumVertices * this->dwOutputSize;
    if ((!FVF_TRANSFORMED(this->dwVIDIn)) ||
        (this->dwDeviceFlags & D3DDEV_RAMP))
    {
        if (vertexPoolSize > this->TLVbuf.GetSize())
        {
            if (this->TLVbuf.Grow(this, vertexPoolSize) != D3D_OK)
            {
                D3D_ERR( "Could not grow TL vertex buffer" );
                ret = DDERR_OUTOFMEMORY;
                return ret;
            }
        }
        if (IS_DP2HAL_DEVICE(this))
        {
            CDirect3DDeviceIDP2 *dev = static_cast<CDirect3DDeviceIDP2*>(this);
            ret = dev->StartPrimVB(this->TLVbuf.GetVBI(), 0);
            if (ret != D3D_OK)
                return ret;
        }
        this->lpvOut = this->TLVbuf.GetAddress();
    }

// Grow clip flags buffer if we need clipping
//
    if (!(this->dwFlags & D3DDP_DONOTCLIP))
    {
        DWORD size = this->dwNumVertices * sizeof(D3DFE_CLIPCODE);
        if (size > this->HVbuf.GetSize())
        {
            if (this->HVbuf.Grow(size) != D3D_OK)
            {
                D3D_ERR( "Could not grow clip buffer" );
                ret = DDERR_OUTOFMEMORY;
                return ret;
            }
        }
        this->lpClipFlags = (D3DFE_CLIPCODE*)this->HVbuf.GetAddress();
    }

    if (FVF_TRANSFORMED(this->dwVIDIn))
    {
        if (this->dwDeviceFlags & D3DDEV_RAMP)
        {
            ConvertColorsToRamp(this,
                                (D3DTLVERTEX*)this->position.lpvData,
                                (D3DTLVERTEX*)(this->lpvOut),
                                this->dwNumVertices);
        }
        else
        {
            // Pass vertices directly from the user memory
            this->dwVIDOut = this->dwVIDIn;
            this->dwOutputSize = this->position.dwStride;
            this->lpvOut = this->position.lpvData;
            vertexPoolSize = this->dwNumVertices * this->dwOutputSize;

            if (IS_DP2HAL_DEVICE(this))
            {
                CDirect3DDeviceIDP2 *dev = static_cast<CDirect3DDeviceIDP2*>(this);
                dev->StartPrimUserMem(this->position.lpvData);
                if (ret != D3D_OK)
                    return ret;
            }
        }
        if (this->dwFlags & D3DDP_DONOTCLIP)
        {
            if (!(this->dwFlags & D3DDP_DONOTUPDATEEXTENTS))
                D3DFE_updateExtents(this);

            if (op == __PROCPRIMOP_INDEXEDPRIM)
            {
                ret = this->DrawIndexPrim();
            }
            else if (op == __PROCPRIMOP_NONINDEXEDPRIM)
            {
                ret = this->DrawPrim();
            }
            goto l_exit;
        }
        else
        {
            // Clear clip union and intersection flags
            this->dwClipIntersection = 0;
            this->dwClipUnion = 0;
            DWORD clip_intersect;
            clip_intersect = this->pGeometryFuncs->GenClipFlags(this);
            D3DFE_UpdateClipStatus(this);
            if (!clip_intersect)
            {
                this->dwFlags |= D3DPV_TLVCLIP;
                if (op == __PROCPRIMOP_INDEXEDPRIM)
                {
                    ret = DoDrawIndexedPrimitive(this);
                }
                else if (op == __PROCPRIMOP_NONINDEXEDPRIM)
                {
                    ret = DoDrawPrimitive(this);
                }
                goto l_exit;
            }
        }
    }
    else
    {
        // Clear clip union and intersection flags
        this->dwClipIntersection = 0;
        this->dwClipUnion = 0;

        // Update Lighting and related flags
        if ((ret = DoUpdateState(this)) != D3D_OK)
            return ret;

        // Call PSGP or our implementation
        if (op == __PROCPRIMOP_INDEXEDPRIM)
            ret = this->pGeometryFuncs->ProcessIndexedPrimitive(this);
        else if (op == __PROCPRIMOP_NONINDEXEDPRIM)
            ret = this->pGeometryFuncs->ProcessPrimitive(this);
        else
            ret = this->pGeometryFuncs->ProcessVertices(this);

        D3DFE_UpdateClipStatus(this);
    }
l_exit:
    if (IS_DP2HAL_DEVICE(this))
    {
        CDirect3DDeviceIDP2 *dev = static_cast<CDirect3DDeviceIDP2*>(this);
        if (op != __PROCPRIMOP_PROCVERONLY)
            ret = dev->EndPrim(vertexPoolSize);
    }
    return ret;
}
//---------------------------------------------------------------------
// This function is called when a number of indices in an indexed primitive
// is much less than a number of vertices. In this case we build non-indexed
// primitives.
HRESULT DereferenceIndexedPrim(LPDIRECT3DDEVICEI lpDevI)
{
    HRESULT ret = CheckVertexBatch(lpDevI);
    if (ret != D3D_OK)
        return ret;

    // Save original vertice and number of primitives
    D3DVERTEX *lpvVertices = (D3DVERTEX*)lpDevI->position.lpvData;
    DWORD dwNumPrimitivesOrg = lpDevI->dwNumPrimitives;
    WORD *lpwIndices = lpDevI->lpwIndices;
    // We will dereference here
        D3DVERTEX *lpVertex = (D3DVERTEX*)lpDevI->lpvVertexBatch;
    lpDevI->position.lpvData = lpVertex;
    lpDevI->lpwIndices = NULL;

    switch (lpDevI->primType)
    {
        case D3DPT_LINELIST:
        {
            for (DWORD j=0; j < dwNumPrimitivesOrg; j += BEGIN_DATA_BLOCK_SIZE/2)
            {
                lpDevI->dwNumPrimitives = min(dwNumPrimitivesOrg - j,
                                              (BEGIN_DATA_BLOCK_SIZE/2));
                lpDevI->dwNumVertices = lpDevI->dwNumPrimitives << 1;
                D3DVERTEX *lpTmp = lpVertex;
                for (DWORD i=lpDevI->dwNumVertices; i; i--)
                {
                    *lpTmp++ = lpvVertices[*lpwIndices++];
                }
                ret = lpDevI->ProcessPrimitive();
                if (ret != D3D_OK)
                    return ret;
            }
            break;
        }
        case D3DPT_LINESTRIP:
        {
                        for (DWORD j=0; j < dwNumPrimitivesOrg; j+= BEGIN_DATA_BLOCK_SIZE-1)
            {
                                lpDevI->dwNumPrimitives = min(dwNumPrimitivesOrg-j,
                                              (BEGIN_DATA_BLOCK_SIZE-1));
                lpDevI->dwNumVertices = lpDevI->dwNumPrimitives + 1;
                D3DVERTEX *lpTmp = lpVertex;
                                for (DWORD i=lpDevI->dwNumVertices; i; i--)
                {
                                        *lpTmp++ = lpvVertices[*lpwIndices++];
                                }
                                lpwIndices--;   // back off one so the next batch is connected
                                ret = lpDevI->ProcessPrimitive();
                                if (ret != D3D_OK)
                                        return ret;
                        }
                        break;
        }
        case D3DPT_TRIANGLEFAN:
        {
                lpVertex[0] = lpvVertices[*lpwIndices++];
                        for (DWORD j=0; j < dwNumPrimitivesOrg; j+= BEGIN_DATA_BLOCK_SIZE-2)
            {
                                lpDevI->dwNumPrimitives = min(dwNumPrimitivesOrg - j,
                                              (BEGIN_DATA_BLOCK_SIZE-2));
                lpDevI->dwNumVertices = lpDevI->dwNumPrimitives + 2;
                D3DVERTEX *lpTmp = &lpVertex[1];
                                for (DWORD i=lpDevI->dwNumVertices-1; i; i--)
                {
                                        *lpTmp++ = lpvVertices[*lpwIndices++];
                                }
                                lpwIndices--;   // back off one so the next batch is connected
                                ret = lpDevI->ProcessPrimitive();
                                if (ret != D3D_OK)
                                        return ret;
                        }
                        break;
        }
        case D3DPT_TRIANGLESTRIP:
        {
            for (DWORD j=0; j < dwNumPrimitivesOrg; j+= BEGIN_DATA_BLOCK_SIZE-2)
            {
                lpDevI->dwNumPrimitives = min(dwNumPrimitivesOrg-j,
                                              (BEGIN_DATA_BLOCK_SIZE-2));
                lpDevI->dwNumVertices = lpDevI->dwNumPrimitives + 2;
                D3DVERTEX *lpTmp = lpVertex;
                for (DWORD i=lpDevI->dwNumVertices; i; i--)
                {
                    *lpTmp++ = lpvVertices[*lpwIndices++];
                }
                lpwIndices-= 2; // back off so the next batch is connected
                ret = lpDevI->ProcessPrimitive();
                if (ret != D3D_OK)
                    return ret;
            }
            break;
        }
        case D3DPT_TRIANGLELIST:
        {
            for (DWORD j=0; j < dwNumPrimitivesOrg; j+= BEGIN_DATA_BLOCK_SIZE/3)
            {
                lpDevI->dwNumPrimitives = min(dwNumPrimitivesOrg-j,
                                              (BEGIN_DATA_BLOCK_SIZE/3));
                lpDevI->dwNumVertices = lpDevI->dwNumPrimitives * 3;
                D3DVERTEX *lpTmp = lpVertex;
                for (DWORD i=lpDevI->dwNumVertices; i; i--)
                {
                        *lpTmp++ = lpvVertices[*lpwIndices++];
                }
                ret = lpDevI->ProcessPrimitive();
                if (ret != D3D_OK)
                        return ret;
            }
            break;
        }
        }
        return D3D_OK;
}
//---------------------------------------------------------------------
//                              API calls
//---------------------------------------------------------------------

//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DrawPrimitiveStrided"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawPrimitiveStrided(
                             D3DPRIMITIVETYPE PrimitiveType,
                             DWORD dwVertexType,
                             LPD3DDRAWPRIMITIVESTRIDEDDATA lpDrawData,
                             DWORD dwNumVertices,
                             DWORD dwFlags)
{
    HRESULT        ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    if (ValidateFVF(dwVertexType) != D3D_OK || !IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;
    Profile(PROF_DRAWPRIMITIVESTRIDED,PrimitiveType,dwVertexType);
#endif
    //note: this check should be done in retail as well as dbg build
    if((dwVertexType & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
        return D3DERR_INVALIDVERTEXTYPE;
    this->primType = PrimitiveType;
    this->dwVIDIn  = dwVertexType;
    this->position = lpDrawData->position;
    this->normal = lpDrawData->normal;
    this->diffuse = lpDrawData->diffuse;
    this->specular = lpDrawData->specular;
    ComputeOutputFVF(this);
    for (DWORD i=0; i < this->nTexCoord; i++)
        this->textures[i] = lpDrawData->textureCoords[i];
    this->dwNumVertices = dwNumVertices;
    this->lpwIndices = NULL;
    this->dwNumIndices = 0;
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    this->dwFlags = dwFlags | D3DPV_STRIDE;
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;

    GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives and update stats

#if DBG
    if (this->dwNumVertices > MAX_DX6_VERTICES)
    {
        D3D_ERR("D3D for DX6 cannot handle greater than 64K vertices");
        return D3DERR_TOOMANYVERTICES;
    }
    ret = CheckDrawPrimitive(this);

    if (ret != D3D_OK)
    {
        return ret;
    }
#endif
    return this->ProcessPrimitive();
}   // end of DrawPrimitiveStrided()
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DrawIndexedPrimitiveStrided"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawIndexedPrimitiveStrided(
                                 D3DPRIMITIVETYPE PrimitiveType,
                                 DWORD dwVertexType,
                                 LPD3DDRAWPRIMITIVESTRIDEDDATA lpDrawData,
                                 DWORD dwNumVertices,
                                 LPWORD lpwIndices,
                                 DWORD dwNumIndices,
                                 DWORD dwFlags)
{
    HRESULT        ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    if (ValidateFVF(dwVertexType) != D3D_OK || !IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;
    Profile(PROF_DRAWINDEXEDPRIMITIVESTRIDED,PrimitiveType,dwVertexType);
#endif
    //note: this check should be done in retail as well as dbg build
    if((dwVertexType & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
        return D3DERR_INVALIDVERTEXTYPE;
    this->primType = PrimitiveType;
    this->dwVIDIn  = dwVertexType;
    this->position = lpDrawData->position;
    this->normal = lpDrawData->normal;
    this->diffuse = lpDrawData->diffuse;
    this->specular = lpDrawData->specular;
    ComputeOutputFVF(this);
    for (DWORD i=0; i < this->nTexCoord; i++)
        this->textures[i] = lpDrawData->textureCoords[i];
    this->dwNumVertices = dwNumVertices;
    this->lpwIndices = lpwIndices;
    this->dwNumIndices = dwNumIndices;
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    this->dwFlags = dwFlags | D3DPV_STRIDE;
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;
    GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives and update stats

#if DBG
    if (this->dwNumPrimitives > MAX_DX6_PRIMCOUNT)
    {
        D3D_ERR("D3D for DX6 cannot handle greater than 64K sized primitives");
        return D3DERR_TOOMANYPRIMITIVES;
    }
    ret = CheckDrawIndexedPrimitive(this);
    if (ret != D3D_OK)
    {
        return ret;
    }
    if (this->dwNumIndices * INDEX_BATCH_SCALE < this->dwNumVertices &&
        !FVF_TRANSFORMED(this->dwVIDIn))
    {
        D3D_WARN(1, "The number of indices is much less than the number of vertices.");
        D3D_WARN(1, "This will likely be inefficient. Consider using vertex buffers.");
    }
#endif
    return this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
}   // end of DrawIndexedPrimitiveStrided()
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DrawPrimitive3"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,
                               DWORD dwVertexType,
                               LPVOID lpvVertices,
                               DWORD dwNumVertices,
                               DWORD dwFlags)
{
    HRESULT        ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    if (ValidateFVF(dwVertexType) != D3D_OK || !IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;
    Profile(PROF_DRAWPRIMITIVEDEVICE3,PrimitiveType,dwVertexType);
#endif

    /* This stuff does not change from call to call */
    this->lpwIndices = NULL;
    this->dwNumIndices = 0;
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    /* This stuff is mandatory for the call */
    this->primType = PrimitiveType;
    this->dwVIDIn  = dwVertexType;
    this->position.lpvData = lpvVertices;
    this->dwNumVertices = dwNumVertices;
    this->dwFlags = dwFlags;
    /* This stuff depends upon the vertex type */
    this->position.dwStride = GetVertexSizeFVF(this->dwVIDIn);
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;
    ComputeOutputFVF(this);
    GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives and update stats

#if DBG
    if (this->dwNumVertices > MAX_DX6_VERTICES)
    {
        D3D_ERR("D3D for DX6 cannot handle greater than 64K vertices");
        return D3DERR_TOOMANYVERTICES;
    }
    ret = CheckDrawPrimitive(this);
    if (ret != D3D_OK)
    {
        return ret;
    }
#endif
    return this->ProcessPrimitive();
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DDevice::DrawIndexedPrimitive"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType,
                                      DWORD  dwVertexType,
                                      LPVOID lpvVertices, DWORD dwNumVertices,
                                      LPWORD lpwIndices, DWORD dwNumIndices,
                                      DWORD dwFlags)
{
    HRESULT ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    if (ValidateFVF(dwVertexType) != D3D_OK || !IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;
    Profile(PROF_DRAWINDEXEDPRIMITIVEDEVICE3,PrimitiveType,dwVertexType);
#endif
    // Static part
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    // Mandatory part
    this->primType = PrimitiveType;
    this->dwVIDIn = dwVertexType;
    this->dwNumVertices = dwNumVertices;
    this->lpwIndices = lpwIndices;
    this->dwNumIndices = dwNumIndices;
    this->dwFlags = dwFlags;
    this->position.lpvData = lpvVertices;
    // Stuff that depends upon dwVIDIn
    this->position.dwStride = GetVertexSizeFVF(this->dwVIDIn);
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;
    ComputeOutputFVF(this);
    GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives and update stats

#if DBG
    if (this->dwNumPrimitives > MAX_DX6_PRIMCOUNT)
    {
        D3D_ERR("D3D for DX6 cannot handle greater than 64K sized primitives");
        return D3DERR_TOOMANYPRIMITIVES;
    }
    ret = CheckDrawIndexedPrimitive(this);
    if (ret != D3D_OK)
    {
        return ret;
    }
    if (this->dwNumIndices * INDEX_BATCH_SCALE < this->dwNumVertices &&
        !FVF_TRANSFORMED(this->dwVIDIn))
    {
        D3D_WARN(1, "The number of indices is much less than the number of vertices.");
        D3D_WARN(1, "This will likely be inefficient. Consider using vertex buffers.");
    }
#endif
    return this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DrawPrimitive"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,
                               D3DVERTEXTYPE VertexType,
                               LPVOID lpvVertices, DWORD dwNumVertices, DWORD dwFlags)
{
    HRESULT        ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.
#if DBG
    if (!IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;

    if((VertexType<=0) || (VertexType>D3DVT_TLVERTEX)) {
        D3D_ERR("Invalid VertexType");
        return D3DERR_INVALIDVERTEXTYPE;
    }
    Profile(PROF_DRAWPRIMITIVEDEVICE2,PrimitiveType,VertexType);
#endif
    /* Static assignments */
    this->position.dwStride = sizeof(D3DVERTEX);
    this->lpwIndices = NULL;
    this->dwNumIndices = 0;
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    this->nTexCoord = 1;
    /* Necessary work */
    this->primType = PrimitiveType;
    this->dwNumVertices = dwNumVertices;
    this->dwFlags = dwFlags;
    this->position.lpvData = lpvVertices;
    this->dwVIDIn = d3dVertexToFVF[VertexType];
    DWORD dwVertexSize = sizeof(D3DVERTEX);
    ComputeOutputFVF(this);
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;

    // AnanKan (6/22/98)
    // !! This is a hack for the crippled DP2 driver model which cannot accept
    // !! more that 64K vertices. In DX6 interfaces we fail rendering if we
    // !! encounter such large primitives for all DDI.
    // !! In the legacy (DX5) interfaces,
    // !! If the primitive size is greater than MAX_DX6_PRIMCOUNT and
    // !! the DDI cannot handle it, then we to break up the primitive into
    // !! manageable chunks.

    if ((this->dwNumVertices > MAX_DX6_VERTICES) &&
        (IS_DP2HAL_DEVICE(this)))
    {
        DWORD dwOrigNumVerts = this->dwNumVertices;
        WORD wChunkSize = MAX_DX6_VERTICES - 3; // Even and a multiple of 3
        BYTE TmpVertex[32], FirstVertex[32];
        DWORD dwStepPerChunk;

        switch(this->primType)
        {
        case D3DPT_POINTLIST:
        case D3DPT_LINELIST:
        case D3DPT_TRIANGLELIST:
            dwStepPerChunk = dwVertexSize*wChunkSize;
            break;
        case D3DPT_LINESTRIP:
            dwStepPerChunk = dwVertexSize*(wChunkSize - 1);
            break;
        case D3DPT_TRIANGLEFAN:
            // Save the first index
            memcpy(FirstVertex, this->position.lpvData, dwVertexSize);
            // Fall through
        case D3DPT_TRIANGLESTRIP:
            dwStepPerChunk = dwVertexSize*(wChunkSize - 2);
            break;
        }

        int numChunks = (int)(dwOrigNumVerts/(DWORD)wChunkSize);
        WORD wRemainingVerts = (WORD)(dwOrigNumVerts - wChunkSize*numChunks);
        this->dwNumVertices = wChunkSize;
        // Calculate dwNumPrimitives and update stats
        GetNumPrim(this, this->dwNumVertices);

        // 0th iteration
#if DBG
        ret = CheckDrawPrimitive(this);
        if (ret != D3D_OK)
        {
            return ret;
        }
#endif
        ret = this->ProcessPrimitive();
        if (ret != D3D_OK)
        {
            return ret;
        }

        for (int i=1; i<numChunks; i++)
        {
            this->position.lpvData = (LPVOID)((LPBYTE)this->position.lpvData +
                                              dwStepPerChunk);
            if (this->primType == D3DPT_TRIANGLEFAN)
            {
                // Save the vertex
                memcpy(TmpVertex, this->position.lpvData, dwVertexSize);
                // Copy in the first vertex
                memcpy(this->position.lpvData, FirstVertex, dwVertexSize);
            }

#if DBG
            ret = CheckDrawPrimitive(this);
            if (ret != D3D_OK)
            {
                return ret;
            }
#endif
            ret = this->ProcessPrimitive();

            // Write back the proper vertex in case something has been
            // switched
            if(this->primType == D3DPT_TRIANGLEFAN)
                memcpy(this->position.lpvData, TmpVertex, dwVertexSize);

            if (ret != D3D_OK)
            {
                return ret;
            }
        }

        // The last time around
        if (wRemainingVerts)
        {
            this->dwNumVertices = wRemainingVerts;
            // Calculate dwNumPrimitives and update stats
            GetNumPrim(this, this->dwNumVertices);
            this->position.lpvData = (LPVOID)((LPBYTE)this->position.lpvData +
                                              dwStepPerChunk);
            if (this->primType == D3DPT_TRIANGLEFAN)
            {
                memcpy(TmpVertex, this->position.lpvData, dwVertexSize);
                memcpy(this->position.lpvData, FirstVertex, dwVertexSize);
            }
#if DBG
            ret = CheckDrawPrimitive(this);
            if (ret != D3D_OK)
            {
                return ret;
            }
#endif
            ret = this->ProcessPrimitive();

            // Write back the proper vertex in case something has been
            // switched
            if(this->primType == D3DPT_TRIANGLEFAN)
                memcpy(this->position.lpvData, TmpVertex, dwVertexSize);

            if (ret != D3D_OK)
            {
                return ret;
            }
        }
    }
    else
    {
        // Calculate dwNumPrimitives and update stats
        GetNumPrim(this, dwNumVertices);
#if DBG
        ret = CheckDrawPrimitive(this);
        if (ret != D3D_OK)
        {
            return ret;
        }
#endif
        return this->ProcessPrimitive();
    }

    return D3D_OK;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DrawIndexedPrimitive"

HRESULT D3DAPI
DIRECT3DDEVICEI::DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType,
                                      D3DVERTEXTYPE VertexType,
                                      LPVOID lpvVertices, DWORD dwNumVertices,
                                      LPWORD lpwIndices, DWORD dwNumIndices,
                                      DWORD dwFlags)
{
    HRESULT ret = D3D_OK;

    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    if (!IsDPFlagsValid(dwFlags))
        return DDERR_INVALIDPARAMS;
    Profile(PROF_DRAWINDEXEDPRIMITIVEDEVICE2,PrimitiveType,VertexType);
#endif
    this->primType = PrimitiveType;
    this->dwVIDIn = d3dVertexToFVF[VertexType];
    this->position.dwStride =  sizeof(D3DVERTEX);
    this->dwNumVertices = dwNumVertices;
    this->lpwIndices = lpwIndices;
    this->dwNumIndices = dwNumIndices;
    this->lpClipFlags = (D3DFE_CLIPCODE*)HVbuf.GetAddress();
    this->dwFlags = dwFlags;
    this->position.lpvData = lpvVertices;
    this->nTexCoord = 1;
    ComputeOutputFVF(this);
    if (this->dwVIDIn & D3DFVF_NORMAL)
        this->dwFlags |= D3DPV_LIGHTING;
    GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives and update stats

    // AnanKan (6/22/98)
    // !! This is a hack for the crippled DP2 driver model which cannot accept
    // !! more that 64K primitives (primcount is a WORD). In DX6 interfaces
    // !! we fail rendering if we encounter such large primitives.
    // !! We do this for all DDI.
    // !! In the legacy (DX5) interfaces,
    // !! If the primitive size is greater than MAX_DX6_PRIMCOUNT and
    // !! the DDI cannot handle it, then we to break up the primitive into
    // !! manageable chunks.

    if ((this->dwNumPrimitives > MAX_DX6_PRIMCOUNT) &&
        (IS_DP2HAL_DEVICE(this)))
    {
        WORD wFirstIndex, wTmpIndex;
        WORD wChunkSize = MAX_DX6_PRIMCOUNT;
        int numPrimChunks;
        DWORD dwResidualPrim;
        DWORD dwResidualIndices;
        DWORD dwStepPerChunk;
        DWORD dwOrigNumPrim = this->dwNumPrimitives;
        this->dwNumPrimitives = wChunkSize;
        numPrimChunks = (int)(dwOrigNumPrim/(DWORD)wChunkSize);
        dwResidualPrim = dwOrigNumPrim - wChunkSize*numPrimChunks;

        switch(this->primType)
        {
        case D3DPT_POINTLIST:
            this->dwNumIndices = this->dwNumPrimitives;
            dwStepPerChunk = this->dwNumIndices;
            dwResidualIndices = dwResidualPrim;
            break;
        case D3DPT_LINELIST:
            this->dwNumIndices = this->dwNumPrimitives<<1;
            dwStepPerChunk = this->dwNumIndices;
            dwResidualIndices = dwResidualPrim << 1;
            break;
        case D3DPT_LINESTRIP:
            this->dwNumIndices = this->dwNumPrimitives + 1;
            dwStepPerChunk = this->dwNumIndices - 1;
            dwResidualIndices = dwResidualPrim + 1;
            break;
        case D3DPT_TRIANGLEFAN:
            this->dwNumIndices = this->dwNumPrimitives + 2;
            dwStepPerChunk = this->dwNumIndices - 2;
            dwResidualIndices = dwResidualPrim + 2;
            // Save the first index
            wTmpIndex = wFirstIndex = this->lpwIndices[0];
            break;
        case D3DPT_TRIANGLESTRIP:
            wChunkSize = (MAX_DX6_PRIMCOUNT-1);
            this->dwNumPrimitives = wChunkSize;
            this->dwNumIndices = this->dwNumPrimitives + 2;
            dwStepPerChunk = this->dwNumIndices - 2;
            numPrimChunks = (int)(dwOrigNumPrim/(DWORD)wChunkSize);
            dwResidualPrim = dwOrigNumPrim - wChunkSize*numPrimChunks;
            dwResidualIndices = dwResidualPrim + 2;
            break;
        case D3DPT_TRIANGLELIST:
            this->dwNumIndices = this->dwNumPrimitives * 3;
            dwStepPerChunk = this->dwNumIndices;
            dwResidualIndices = dwResidualPrim * 3;
            break;
        }

        // 0th iteration
#if DBG
        ret = CheckDrawIndexedPrimitive(this);
        if (ret != D3D_OK)
        {
            return ret;
        }
#endif
        ret = this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
        if (ret != D3D_OK)
        {
            return ret;
        }

        // Remaining chunks
        for (int i=1; i<numPrimChunks; i++)
        {
            this->lpwIndices += dwStepPerChunk;

            if (this->primType == D3DPT_TRIANGLEFAN)
            {
                // Save the index
                wTmpIndex = this->lpwIndices[0];
                // Copy in the first vertex
                this->lpwIndices[0] = wFirstIndex;
            }

#if DBG
            ret = CheckDrawIndexedPrimitive(this);
            if (ret != D3D_OK)
            {
                return ret;
            }
#endif
            ret = this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);

            // Write back the proper index in case something has been
            // switched
            if(this->primType == D3DPT_TRIANGLEFAN)
                this->lpwIndices[0] = wTmpIndex;

            if (ret != D3D_OK)
            {
                return ret;
            }
        }

        // The last time around
        if (dwResidualPrim)
        {
            this->dwNumPrimitives = dwResidualPrim;
            this->dwNumIndices = dwResidualIndices;
            this->lpwIndices += dwStepPerChunk;
            if (this->primType == D3DPT_TRIANGLEFAN)
            {
                wTmpIndex = this->lpwIndices[0];
                this->lpwIndices[0] = wFirstIndex;
            }
#if DBG
            ret = CheckDrawIndexedPrimitive(this);
            if (ret != D3D_OK)
            {
                return ret;
            }
#endif
            ret = this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
            // Write back the proper index in case something has been
            // switched
            if(this->primType == D3DPT_TRIANGLEFAN)
                this->lpwIndices[0] = wTmpIndex;
            if (ret != D3D_OK)
            {
                return ret;
            }
        }
    }
    else
    {

#if DBG
        ret = CheckDrawIndexedPrimitive(this);
        if (ret != D3D_OK)
        {
            return ret;
        }
#endif
        // For an untransformed primitive, if a number of indices is much less
        // than number of vertices we rebuild the primitive to reduce number
        // of vertices to process.
        if (this->dwNumIndices * INDEX_BATCH_SCALE < this->dwNumVertices &&
            !FVF_TRANSFORMED(this->dwVIDIn))
            return DereferenceIndexedPrim(this);
        else
            return this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
    }
    return D3D_OK;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CDirect3DDeviceIDP::SetRenderStateI"

HRESULT D3DAPI
CDirect3DDeviceIDP::SetRenderStateI(D3DRENDERSTATETYPE dwStateType,
                                    DWORD value)
{
    LPD3DHAL_DRAWPRIMCOUNTS lpPC;
    LPDWORD lpStateChange;
    HRESULT ret;
    if (dwStateType > D3DRENDERSTATE_STIPPLEPATTERN31)
    {
        D3D_WARN(4,"Trying to send invalid state %d to legacy driver",dwStateType);
        return D3D_OK;
    }
    if (dwStateType > D3DRENDERSTATE_FLUSHBATCH && dwStateType < D3DRENDERSTATE_STIPPLEPATTERN00)
    {
        D3D_WARN(4,"Trying to send invalid state %d to legacy driver",dwStateType);
        return D3D_OK;
    }

    if (D3DRENDERSTATE_FLUSHBATCH == dwStateType)
    {
        CLockD3DST lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock (ST only).
        ret = FlushStates();
        if (ret != D3D_OK)
        {
            D3D_ERR("Error trying to render batched commands in SetRenderStateI");
        }
        return ret;
    }

    lpPC = this->lpDPPrimCounts;
    if (lpPC->wNumVertices) //Do we already have Vertices filled in for this count ?
    {               //Yes, then Increment count
        lpPC=this->lpDPPrimCounts=(LPD3DHAL_DRAWPRIMCOUNTS)((LPBYTE)this->lpwDPBuffer+this->dwDPOffset);
        memset( (char *)lpPC,0,sizeof(D3DHAL_DRAWPRIMCOUNTS));
        this->dwDPOffset += sizeof(D3DHAL_DRAWPRIMCOUNTS);
    }
    if (this->dwDPOffset + 2*sizeof(DWORD)  > this->dwDPMaxOffset )
    {
        CLockD3DST lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock (ST only).
        ret = FlushStates();
        if (ret != D3D_OK)
        {
            D3D_ERR("Error trying to render batched commands in SetRenderStateI");
            return ret;
        }
    }
    lpStateChange=(LPDWORD)((char *)this->lpwDPBuffer + this->dwDPOffset);
    *lpStateChange=dwStateType;
    lpStateChange ++;
    *lpStateChange=value;
    this->lpDPPrimCounts->wNumStateChanges ++;
    this->dwDPOffset += 2*sizeof(DWORD);
#if 0
    if (dwStateType == D3DRENDERSTATE_TEXTUREHANDLE && this->dwDPOffset== 0x10){
    DPF(0,"SRdwDPOffset=%08lx, dwStateType=%08lx value=%08lx ddihandle=%08lx lpStateChange=%08lx lpDPPrimCounts=%08lx",
    this->dwDPOffset,dwStateType,value,*lpStateChange,lpStateChange,this->lpDPPrimCounts);
        _asm int 3
    }
#endif //0

    return D3D_OK;
}
//---------------------------------------------------------------------
DWORD visResults[6][2] =
{
    D3DVIS_INTERSECT_LEFT   ,
    D3DVIS_OUTSIDE_LEFT     ,
    D3DVIS_INTERSECT_RIGHT  ,
    D3DVIS_OUTSIDE_RIGHT    ,
    D3DVIS_INTERSECT_TOP    ,
    D3DVIS_OUTSIDE_TOP      ,
    D3DVIS_INTERSECT_BOTTOM ,
    D3DVIS_OUTSIDE_BOTTOM   ,
    D3DVIS_INTERSECT_NEAR   ,
    D3DVIS_OUTSIDE_NEAR     ,
    D3DVIS_INTERSECT_FAR    ,
    D3DVIS_OUTSIDE_FAR
};
//---------------------------------------------------------------------
DWORD CheckSphere(LPDIRECT3DDEVICEI lpDevI, LPD3DVECTOR center, D3DVALUE radius)
{
    DWORD result = 0;
    for (int i=0; i < 6; i++)
    {
        // Compute a distance from the center to the plane
        D3DVALUE d = lpDevI->transform.frustum[i].x*center->x +
                     lpDevI->transform.frustum[i].y*center->y +
                     lpDevI->transform.frustum[i].z*center->z +
                     lpDevI->transform.frustum[i].w;
        if (d + radius < 0)
            result |= visResults[i][1];  // Outside
        else
        if (d - radius < 0.5f)  // 0.5 is chosen to offset precision errors
            result |= visResults[i][0];  // Intersect
    }
    if (result & (D3DVIS_OUTSIDE_LEFT   |
                  D3DVIS_OUTSIDE_RIGHT  |
                  D3DVIS_OUTSIDE_TOP    |
                  D3DVIS_OUTSIDE_BOTTOM |
                  D3DVIS_OUTSIDE_NEAR   |
                  D3DVIS_OUTSIDE_FAR))
    {
        result |= D3DVIS_OUTSIDE_FRUSTUM;
    }
    else
    if (result)
        result |= D3DVIS_INTERSECT_FRUSTUM;

    return result;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DIRECT3DDEVICEI::ComputeSphereVisibility"

HRESULT D3DAPI
DIRECT3DDEVICEI::ComputeSphereVisibility(LPD3DVECTOR lpCenters,
                                         LPD3DVALUE lpRadii,
                                         DWORD dwNumSpheres,
                                         DWORD dwFlags,
                                         LPDWORD lpdwReturnValues)
{
    CLockD3DMT lockObject(this, DPF_MODNAME, REMIND(""));   // Takes D3D lock.

#if DBG
    TRY
    {
        if (dwFlags != 0 || dwNumSpheres == 0 ||
            IsBadWritePtr(lpdwReturnValues, dwNumSpheres * sizeof(DWORD)) ||
            IsBadWritePtr(lpRadii, dwNumSpheres * sizeof(D3DVALUE)) ||
            IsBadWritePtr(lpCenters, dwNumSpheres * sizeof(LPD3DVECTOR)))
        {
            return DDERR_INVALIDPARAMS;
        }
    }
    EXCEPT( EXCEPTION_EXECUTE_HANDLER )
    {
        D3D_ERR( "Exception encountered validating parameters" );
        return DDERR_INVALIDPARAMS;
    }
#endif

    this->dwFlags = 0;
    if (dwFEFlags & D3DFE_TRANSFORM_DIRTY)
    {
        updateTransform(this);
    }
    if (this->dwFEFlags & D3DFE_FRUSTUMPLANES_DIRTY)
    {
        this->dwFlags |= D3DPV_FRUSTUMPLANES_DIRTY;
        this->dwFEFlags &= ~D3DFE_FRUSTUMPLANES_DIRTY;
    }

    return this->pGeometryFuncs->ComputeSphereVisibility(this,
                                                         lpCenters,
                                                         lpRadii,
                                                         dwNumSpheres,
                                                         dwFlags,
                                                         lpdwReturnValues);
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "ID3DFE_PVFUNCS::ComputeSphereVisibility"

HRESULT
D3DFE_PVFUNCS::ComputeSphereVisibility(LPD3DFE_PROCESSVERTICES pv,
                                       LPD3DVECTOR lpCenters,
                                       LPD3DVALUE lpRadii,
                                       DWORD dwNumSpheres,
                                       DWORD dwFlags,
                                       LPDWORD lpdwReturnValues)
{
    LPDIRECT3DDEVICEI lpDevI = static_cast<LPDIRECT3DDEVICEI>(pv);
    HRESULT ret = D3DERR_INVALIDMATRIX;

#define transform lpDevI->transform

    if (pv->dwFlags & D3DPV_FRUSTUMPLANES_DIRTY)
    {
        transform.dwFlags &= ~D3DTRANS_VALIDFRUSTUM;
        if (Inverse4x4((D3DMATRIX*)&lpDevI->mCTM,
                       (D3DMATRIX*)&transform.mCTMI))
        {
            D3D_ERR("Cannot invert current (World X View) matrix.");
            return ret;
        }
        // Transform the following clipping volume points to the model space by
        // multiplying by inverse CTM
        //
        // v1 = { 0, 0, 0, 1};
        // v2 = { 1, 0, 0, 1};
        // v3 = { 1, 1, 0, 1};
        // v4 = { 0, 1, 0, 1};
        // v5 = { 0, 0, 1, 1};
        // v6 = { 1, 0, 1, 1};
        // v7 = { 0, 1, 1, 1};
        //
        // We do it manually to speed up
        //
        D3DVECTORH v1 = {transform.mCTMI._41,
                         transform.mCTMI._42,
                         transform.mCTMI._43,
                         transform.mCTMI._44};
        D3DVECTORH v2 = {transform.mCTMI._11 + transform.mCTMI._41,
                         transform.mCTMI._12 + transform.mCTMI._42,
                         transform.mCTMI._13 + transform.mCTMI._43,
                         transform.mCTMI._14 + transform.mCTMI._44};
        D3DVECTORH v3 = {transform.mCTMI._11 + transform.mCTMI._21 + transform.mCTMI._41,
                         transform.mCTMI._12 + transform.mCTMI._22 + transform.mCTMI._42,
                         transform.mCTMI._13 + transform.mCTMI._23 + transform.mCTMI._43,
                         transform.mCTMI._14 + transform.mCTMI._24 + transform.mCTMI._44};
        D3DVECTORH v4 = {transform.mCTMI._21 + transform.mCTMI._41,
                         transform.mCTMI._22 + transform.mCTMI._42,
                         transform.mCTMI._23 + transform.mCTMI._43,
                         transform.mCTMI._24 + transform.mCTMI._44};
        D3DVECTORH v5 = {transform.mCTMI._31 + transform.mCTMI._41,
                         transform.mCTMI._32 + transform.mCTMI._42,
                         transform.mCTMI._33 + transform.mCTMI._43,
                         transform.mCTMI._34 + transform.mCTMI._44};
        D3DVECTORH v6 = {transform.mCTMI._11 + transform.mCTMI._31 + transform.mCTMI._41,
                         transform.mCTMI._12 + transform.mCTMI._32 + transform.mCTMI._42,
                         transform.mCTMI._13 + transform.mCTMI._33 + transform.mCTMI._43,
                         transform.mCTMI._14 + transform.mCTMI._34 + transform.mCTMI._44};
        D3DVECTORH v7 = {transform.mCTMI._21 + transform.mCTMI._31 + transform.mCTMI._41,
                         transform.mCTMI._22 + transform.mCTMI._32 + transform.mCTMI._42,
                         transform.mCTMI._23 + transform.mCTMI._33 + transform.mCTMI._43,
                         transform.mCTMI._24 + transform.mCTMI._34 + transform.mCTMI._44};

        // Convert vectors from homogeneous to 3D
        if (Vector4to3D(&v1))
            goto exit;
        if (Vector4to3D(&v2))
            goto exit;
        if (Vector4to3D(&v3))
            goto exit;
        if (Vector4to3D(&v4))
            goto exit;
        if (Vector4to3D(&v5))
            goto exit;
        if (Vector4to3D(&v6))
            goto exit;
        if (Vector4to3D(&v7))
            goto exit;
        // Build frustum planes
        // Left
        if (MakePlane((D3DVECTOR*)&v1, (D3DVECTOR*)&v4, (D3DVECTOR*)&v5, &transform.frustum[0]))
            goto exit;
        // Right
        if (MakePlane((D3DVECTOR*)&v2, (D3DVECTOR*)&v6, (D3DVECTOR*)&v3, &transform.frustum[1]))
            goto exit;
        // Top
        if (MakePlane((D3DVECTOR*)&v4, (D3DVECTOR*)&v3, (D3DVECTOR*)&v7, &transform.frustum[2]))
            goto exit;
        // Bottom
        if (MakePlane((D3DVECTOR*)&v1, (D3DVECTOR*)&v5, (D3DVECTOR*)&v2, &transform.frustum[3]))
            goto exit;
        // Near
        if (MakePlane((D3DVECTOR*)&v1, (D3DVECTOR*)&v2, (D3DVECTOR*)&v3, &transform.frustum[4]))
            goto exit;
        // Far
        if (MakePlane((D3DVECTOR*)&v6, (D3DVECTOR*)&v5, (D3DVECTOR*)&v7, &transform.frustum[5]))
            goto exit;

        transform.dwFlags |= D3DTRANS_VALIDFRUSTUM;
    }

    if (transform.dwFlags & D3DTRANS_VALIDFRUSTUM)
    {
        // Now we can check the spheres against the clipping planes

        for (DWORD i=0; i < dwNumSpheres; i++)
        {
            lpdwReturnValues[i] = CheckSphere(lpDevI, &lpCenters[i], lpRadii[i]);
        }
        return D3D_OK;
    }

exit:
    D3D_ERR("Non-orthogonal (world X view) matrix");
    return ret;
#undef transform
}
//---------------------------------------------------------------------
DWORD
D3DFE_PVFUNCS::GenClipFlags(D3DFE_PROCESSVERTICES *pv)
{
    return D3DFE_GenClipFlags(pv);
}
//---------------------------------------------------------------------
DWORD
D3DFE_PVFUNCS::TransformVertices(D3DFE_PROCESSVERTICES *pv, 
                                 DWORD vertexCount, 
                                 LPD3DTRANSFORMDATAI data)
{

    if (pv->dwFlags & D3DDP_DONOTCLIP) 
        return D3DFE_TransformUnclippedVp(pv, vertexCount, data);
    else 
        return D3DFE_TransformClippedVp(pv, vertexCount, data);
}
//---------------------------------------------------------------------
DWORD ID3DFE_PVFUNCS::GenClipFlags(D3DFE_PROCESSVERTICES *pv)
{   
    return GeometryFuncsGuaranteed.GenClipFlags(pv);
}
//---------------------------------------------------------------------
// Used to implement viewport->TransformVertices
// Returns clip intersection code
DWORD ID3DFE_PVFUNCS::TransformVertices(D3DFE_PROCESSVERTICES *pv, 
                                DWORD vertexCount, 
                                D3DTRANSFORMDATAI* data)
{
    return GeometryFuncsGuaranteed.TransformVertices(pv, vertexCount, data);
}