//----------------------------------------------------------------------------
//
// dprim2.cpp
//
// Implements DrawPrimitives2.
//
// Copyright (C) Microsoft Corporation, 1997.
//
//----------------------------------------------------------------------------
include(`m4hdr.mh')dnl
#include "pch.cpp"
#pragma hdrstop

inline D3DPRIMITIVETYPE ConvertDP2OPToPrimType(D3DHAL_DP2OPERATION Dp2Op)
{
    switch (Dp2Op)
    {
    case D3DDP2OP_POINTS              :
        return D3DPT_POINTLIST;
    case D3DDP2OP_INDEXEDLINELIST     :
    case D3DDP2OP_INDEXEDLINELIST2    :
    case D3DDP2OP_LINELIST_IMM        :
    case D3DDP2OP_LINELIST            :
        return D3DPT_LINELIST;
    case D3DDP2OP_TRIANGLELIST        :
    case D3DDP2OP_INDEXEDTRIANGLELIST :
    case D3DDP2OP_INDEXEDTRIANGLELIST2:
        return D3DPT_TRIANGLELIST;
    case D3DDP2OP_LINESTRIP           :
    case D3DDP2OP_INDEXEDLINESTRIP    :
        return D3DPT_LINESTRIP;
    case D3DDP2OP_TRIANGLESTRIP       :
    case D3DDP2OP_INDEXEDTRIANGLESTRIP:
        return D3DPT_TRIANGLESTRIP;
    case D3DDP2OP_TRIANGLEFAN         :
    case D3DDP2OP_INDEXEDTRIANGLEFAN  :
    case D3DDP2OP_TRIANGLEFAN_IMM     :
        return D3DPT_TRIANGLEFAN;
    case D3DDP2OP_RENDERSTATE         :
    case D3DDP2OP_TEXTURESTAGESTATE   :
    case D3DDP2OP_VIEWPORTINFO        :
    case D3DDP2OP_WINFO               :
    default:
        D3D_INFO(4, "(Rast)Non primitive operation operation in DrawPrimitives2");
        return (D3DPRIMITIVETYPE)0;
    }
}
define(`d_DrawPrim2', `
//----------------------------------------------------------------------------
//
// $1DrawPrimitives2
//
// This is called by D3DIM for API DrawPrimitives2 to draw a set of primitives
// using a vertex buffer.
//
//----------------------------------------------------------------------------
DWORD __stdcall
$1DrawPrimitives2(LPD3DHAL_DRAWPRIMITIVES2DATA pDPrim2Data)
{
    HRESULT hr;
    ifelse(`$1', `Rast',`
    D3DContext *pDCtx;', `
    ReferenceRasterizer *pRefRast;
    DWORD dwStride;')
    PUINT8 pVtData;
ifelse(`$1', `Rast',`
    VALIDATE_D3DCONTEXT("RastDrawPrimitives2", pDPrim2Data);', `
    VALIDATE_REFRAST_CONTEXT("RefRastDrawPrimitives", pDPrim2Data);')

    if (pDPrim2Data->dwFlags & D3DHALDP2_USERMEMVERTICES)
        pVtData = (PUINT8)pDPrim2Data->lpVertices + pDPrim2Data->dwVertexOffset;
    else
        pVtData = (PUINT8)pDPrim2Data->lpDDVertex->lpGbl->fpVidMem + pDPrim2Data->dwVertexOffset;
    LPD3DHAL_DP2COMMAND pCmd = (LPD3DHAL_DP2COMMAND)
                                ((PUINT8)pDPrim2Data->lpDDCommands->lpGbl->fpVidMem +
                                 pDPrim2Data->dwCommandOffset);
    UINT_PTR CmdBoundary = (UINT_PTR)pCmd +
                               pDPrim2Data->dwCommandLength;

ifelse(`$1', `Rast', `
    // Check for FVF vertex, and init FVF related fileds if necessary
    CHECK_FVF(pDPrim2Data->ddrval, pDCtx, (DWORD)pDPrim2Data->dwVertexType);', `
    // Unconditionally get the vertex stride, since it can not change
    if ((pDPrim2Data->ddrval = FVFCheckAndStride(
                        (DWORD)pDPrim2Data->dwVertexType, &dwStride)) != D3D_OK)
    {
        return DDHAL_DRIVER_HANDLED;
    }
    if ((pDPrim2Data->ddrval=RefRastLockTarget(pRefRast)) != D3D_OK)
    {
        return DDHAL_DRIVER_HANDLED;
    }')dnl

    // Skip state check and texture lock if the first thing is state change
    //
    // WINFO is excluded here because it currently does not affect RGB/MMX
    // and refrast does not care if it changes between begin/endrendering.
    //
    // VIEWPORTINFO is excluded here because it is OK to change the viewport
    // between begin/endrendering on both RGB/MMX and Ref.
    //
    if (pCmd->bCommand != D3DDP2OP_RENDERSTATE &&
        pCmd->bCommand != D3DDP2OP_TEXTURESTAGESTATE)
    {
ifelse(`$1', `Rast', `
        pDPrim2Data->ddrval = pDCtx->Begin();
        if (pDPrim2Data->ddrval != D3D_OK)
        {
            return DDHAL_DRIVER_HANDLED;
        }', `
        if ((pDPrim2Data->ddrval=RefRastLockTexture(pRefRast)) != D3D_OK)
        {
            return DDHAL_DRIVER_HANDLED;
        }
        if ((pDPrim2Data->ddrval =
            pRefRast->BeginRendering((DWORD)pDPrim2Data->dwVertexType)) != D3D_OK)
        {
            return DDHAL_DRIVER_HANDLED;
        }')
    }

    // Loop through the data, update render states
    // and then draw the primitive
    for (;;)
    {
        LPDWORD lpdwRStates;
        if (pDPrim2Data->dwFlags & D3DHALDP2_EXECUTEBUFFER)
            lpdwRStates = pDPrim2Data->lpdwRStates;
        else
            lpdwRStates = NULL;
ifelse(`$1', `Rast', `
        D3DPRIMITIVETYPE Primtype =
                ConvertDP2OPToPrimType((D3DHAL_DP2OPERATION)pCmd->bCommand);
        if (Primtype > 0)
        {
            pDCtx->BeginPrimSet(Primtype, pDCtx->GetFvfVertexType());
        }
        BOOL bWireframe =
                (pDCtx->GetRastCtx())->pdwRenderState[D3DRENDERSTATE_FILLMODE]
                    == D3DFILL_WIREFRAME;
        pDPrim2Data->ddrval = DoDrawPrimitives2((LPVOID)pDCtx,
                                                pDCtx->GetFunsTbl(),
                                                (UINT16)pDCtx->GetFvfStride(),
                                                (DWORD)pDPrim2Data->dwVertexType,
                                                pVtData,
                                                &pCmd,
                                                lpdwRStates,
                                                bWireframe
                                                );', `
        BOOL bWireframe = pRefRast->GetRenderState()[D3DRENDERSTATE_FILLMODE]
                        == D3DFILL_WIREFRAME;
        pDPrim2Data->ddrval = DoDrawPrimitives2((LPVOID)pRefRast,
                                                (PRIMITIVE_FUNTIONS *)
                (((PUINT8)pRefRast->GetUserPrivate())+sizeof(D3DFE_STATESET)),
                                                (UINT16)dwStride,
                                                (DWORD)pDPrim2Data->dwVertexType,
                                                pVtData,
                                                &pCmd,
                                                lpdwRStates,
                                                bWireframe
                                                );')
        if (pDPrim2Data->ddrval != D3D_OK)
        {
            if (pDPrim2Data->ddrval == D3DERR_COMMAND_UNPARSED)
            {
                pDPrim2Data->dwErrorOffset = (UINT32)((ULONG_PTR)pCmd -
                          (UINT_PTR)(pDPrim2Data->lpDDCommands->lpGbl->fpVidMem));
            }
            goto EH_Exit;
        }
        if ((UINT_PTR)pCmd >= CmdBoundary)
            break;
    }

 EH_Exit:
 ifelse(`$1', `Rast', `
    hr = pDCtx->End();', `
    hr = pRefRast->EndRendering();
    RefRastUnlockTexture(pRefRast);
    RefRastUnlockTarget(pRefRast);')
    if (pDPrim2Data->ddrval == D3D_OK)
    {
        pDPrim2Data->ddrval = hr;
    }

    return DDHAL_DRIVER_HANDLED;
}')

d_DrawPrim2(`Rast')
dnl
dnl Now the d3dref.dll is used for all RefRast rendering
dnl
dnl d_DrawPrim2(`RefRast')

HRESULT FASTCALL
DoDrawIndexedTriList2(LPVOID pCtx,
                  PRIMITIVE_FUNTIONS *pfnPrims,
                  DWORD dwStride,
                  PUINT8 pVtx,
                  WORD cPrims,
                  D3DHAL_DP2INDEXEDTRIANGLELIST *pTriList)
{
    INT i;
    D3DHAL_DP2INDEXEDTRIANGLELIST *pTri = pTriList;

    for (i = 0; i < cPrims; i ++)
    {
        HRESULT hr;

        PUINT8 pVtx0, pVtx1, pVtx2;
        pVtx0 = pVtx + dwStride * pTri->wV1;
        pVtx1 = pVtx + dwStride * pTri->wV2;
        pVtx2 = pVtx + dwStride * pTri->wV3;
        HR_RET(pfnPrims->pfnTri(pCtx,
                         pVtx0,
                         pVtx1,
                         pVtx2,
                         pTri->wFlags));
        pTri ++;
    }

    return D3D_OK;
}

//----------------------------------------------------------------------------
//
// DoDrawPrimitives2
//
// It's called by RastDrawPrimitives2. .
//
//----------------------------------------------------------------------------
HRESULT FASTCALL
DoDrawPrimitives2(LPVOID pCtx,
                  PRIMITIVE_FUNTIONS *pfnPrims,
                  UINT16 dwStride,
                  DWORD dwFvf,
                  PUINT8 pVtx,
                  LPD3DHAL_DP2COMMAND *ppCmd,
                  LPDWORD lpdwRStates,
                  BOOL bWireframe
                  )
{
    LPD3DHAL_DP2COMMAND pCmd = *ppCmd;
    HRESULT hr;

    D3D_INFO(7, "(Rast)Read Ins: %08lx", *(LPDWORD)pCmd);
    switch(pCmd->bCommand)
    {
    case D3DDP2OP_VIEWPORTINFO:
        {
            HR_RET(pfnPrims->pfnDp2SetViewport(pCtx, pCmd));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)
                     ((D3DHAL_DP2VIEWPORTINFO *)(pCmd + 1) + pCmd->wStateCount);
        }
        break;
    case D3DDP2OP_WINFO:
        {
            HR_RET(pfnPrims->pfnDp2SetWRange(pCtx, pCmd));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)
                     ((D3DHAL_DP2WINFO *)(pCmd + 1) + pCmd->wStateCount);
        }
        break;
    case D3DDP2OP_RENDERSTATE:
        {
            HR_RET(pfnPrims->pfnDp2SetRenderStates(pCtx, dwFvf, pCmd, lpdwRStates));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)
                     ((D3DHAL_DP2RENDERSTATE *)(pCmd + 1) + pCmd->wStateCount);
        }
        break;
    case D3DDP2OP_TEXTURESTAGESTATE:
        {
            HR_RET(pfnPrims->pfnDp2TextureStageState(pCtx, dwFvf, pCmd));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)
               ((LPD3DHAL_DP2TEXTURESTAGESTATE)(pCmd + 1) + pCmd->wStateCount);
        }
        break;
    // This is a special case because it has edge flags. Other D3DDP2OP
    // can actually make use of DoDrawOneIndexedPrimitive/DoDrawOnePrimitive.
    case D3DDP2OP_INDEXEDTRIANGLELIST:
        {
            WORD cPrims = pCmd->wPrimitiveCount;
            HR_RET(DoDrawIndexedTriList2(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx,
                                  cPrims,
                                  (D3DHAL_DP2INDEXEDTRIANGLELIST *)(pCmd + 1)));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(pCmd + 1) +
                            sizeof(D3DHAL_DP2INDEXEDTRIANGLELIST) * cPrims);
        }
        break;
    case D3DDP2OP_INDEXEDLINELIST:
        {
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx,
                                  (LPWORD)(pCmd + 1),
                                  D3DPT_LINELIST,
                                  pCmd->wPrimitiveCount * 2));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(pCmd + 1) +
                    pCmd->wPrimitiveCount * sizeof(D3DHAL_DP2INDEXEDLINELIST));
        }
        break;
    // Following ops All use DoDrawOneIndexedPrimitive/DoDrawOnePrimitive.
    // There are some extra overheads introduced because those two functions
    // need to switch over the PrimTypes while we already know it here.
    // Striping out the code to add inline functions for each PrimType means
    // adding about twenty functions(considering the types of prim times types
    // of vertex). So I have used DoDrawOneIndexedPrimitive/DoDrawOnePrimitive
    // here anyway. We can later change it if necessary.
    case D3DDP2OP_POINTS:
        {
            WORD cPrims = pCmd->wPrimitiveCount;
            WORD i;
            D3DHAL_DP2POINTS *pPt = (D3DHAL_DP2POINTS *)(pCmd + 1);
            for (i = 0; i < cPrims; i++)
            {
                HR_RET(DoDrawOnePrimitive(pCtx,
                                      pfnPrims,
                                      dwStride,
                                      (pVtx + pPt->wVStart * dwStride),
                                      D3DPT_POINTLIST,
                                      pPt->wCount));
                pPt ++;
            }
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)pPt;
        }
        break;
    case D3DDP2OP_LINELIST:
        {
            D3DHAL_DP2LINELIST *pLine = (D3DHAL_DP2LINELIST *)(pCmd + 1);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  (pVtx + pLine->wVStart * dwStride),
                                  D3DPT_LINELIST,
                                  pCmd->wPrimitiveCount * 2));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)(pLine + 1);
        }
        break;
    case D3DDP2OP_INDEXEDLINELIST2:
        {
            LPD3DHAL_DP2STARTVERTEX lpStartVertex;
            lpStartVertex = (LPD3DHAL_DP2STARTVERTEX)(pCmd + 1);
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx + lpStartVertex->wVStart*dwStride,
                                  (LPWORD)(lpStartVertex + 1),
                                  D3DPT_LINELIST,
                                  pCmd->wPrimitiveCount * 2));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(lpStartVertex + 1) +
                    pCmd->wPrimitiveCount * sizeof(D3DHAL_DP2INDEXEDLINELIST));
        }
        break;
    case D3DDP2OP_LINESTRIP:
        {
            D3DHAL_DP2LINESTRIP *pLine = (D3DHAL_DP2LINESTRIP *)(pCmd + 1);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  (pVtx + pLine->wVStart * dwStride),
                                  D3DPT_LINESTRIP,
                                  pCmd->wPrimitiveCount + 1));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)(pLine + 1);
        }
        break;
    case D3DDP2OP_INDEXEDLINESTRIP:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount + 1;
            LPD3DHAL_DP2STARTVERTEX lpStartVertex;
            lpStartVertex = (LPD3DHAL_DP2STARTVERTEX)(pCmd + 1);
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx + lpStartVertex->wVStart*dwStride,
                                  (LPWORD)(lpStartVertex + 1),
                                  D3DPT_LINESTRIP,
                                  vertexCount));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(lpStartVertex + 1) +
                                           vertexCount * sizeof(WORD));
        }
        break;
    case D3DDP2OP_TRIANGLELIST:
        {
            D3DHAL_DP2TRIANGLELIST *pTri = (D3DHAL_DP2TRIANGLELIST *)(pCmd + 1);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  (pVtx + pTri->wVStart * dwStride),
                                  D3DPT_TRIANGLELIST,
                                  pCmd->wPrimitiveCount * 3));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)(pTri + 1);
        }
        break;
    case D3DDP2OP_INDEXEDTRIANGLELIST2:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount*3;
            LPD3DHAL_DP2STARTVERTEX lpStartVertex;
            lpStartVertex = (LPD3DHAL_DP2STARTVERTEX)(pCmd + 1);
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx + lpStartVertex->wVStart*dwStride,
                                  (LPWORD)(lpStartVertex + 1),
                                  D3DPT_TRIANGLELIST,
                                  vertexCount));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(lpStartVertex + 1) +
                                           vertexCount * sizeof(WORD));
        }
        break;
    case D3DDP2OP_TRIANGLESTRIP:
        {
            D3DHAL_DP2TRIANGLESTRIP *pTri = (D3DHAL_DP2TRIANGLESTRIP *)(pCmd + 1);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  (pVtx + pTri->wVStart * dwStride),
                                  D3DPT_TRIANGLESTRIP,
                                  pCmd->wPrimitiveCount + 2));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)(pTri + 1);
        }
        break;
    case D3DDP2OP_INDEXEDTRIANGLESTRIP:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount+2;
            LPD3DHAL_DP2STARTVERTEX lpStartVertex;
            lpStartVertex = (LPD3DHAL_DP2STARTVERTEX)(pCmd + 1);
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx + lpStartVertex->wVStart*dwStride,
                                  (LPWORD)(lpStartVertex + 1),
                                  D3DPT_TRIANGLESTRIP,
                                  vertexCount));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(lpStartVertex + 1) +
                                           vertexCount * sizeof(WORD));
        }
        break;
    case D3DDP2OP_TRIANGLEFAN:
        {
            D3DHAL_DP2TRIANGLEFAN *pTri = (D3DHAL_DP2TRIANGLEFAN *)(pCmd + 1);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  (pVtx + pTri->wVStart * dwStride),
                                  D3DPT_TRIANGLEFAN,
                                  pCmd->wPrimitiveCount + 2));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)(pTri + 1);
        }
        break;
    case D3DDP2OP_INDEXEDTRIANGLEFAN:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount + 2;
            LPD3DHAL_DP2STARTVERTEX lpStartVertex;
            lpStartVertex = (LPD3DHAL_DP2STARTVERTEX)(pCmd + 1);
            HR_RET(DoDrawOneIndexedPrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pVtx + lpStartVertex->wVStart*dwStride,
                                  (LPWORD)(lpStartVertex + 1),
                                  D3DPT_TRIANGLEFAN,
                                  vertexCount));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)(lpStartVertex + 1) +
                                           vertexCount * sizeof(WORD));
        }
        break;
    case D3DDP2OP_TRIANGLEFAN_IMM:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount + 2;
            // Make sure the pFanVtx pointer is DWORD aligned: (pFanVtx +3) % 4
            PUINT8 pFanVtx = (PUINT8)(((ULONG_PTR)(pCmd + 1) +
                                         sizeof(D3DHAL_DP2TRIANGLEFAN_IMM) + 3) & ~3);
            if (bWireframe)
            {
                // Read edge flags
                UINT32 dwEdgeFlags =
                        ((LPD3DHAL_DP2TRIANGLEFAN_IMM)(pCmd + 1))->dwEdgeFlags;
                HR_RET(DoDrawOneEdgeFlagTriangleFan(pCtx,
                                      pfnPrims,
                                      dwStride,
                                      pFanVtx,
                                      vertexCount,
                                      dwEdgeFlags));
            }
            else
            {
                HR_RET(DoDrawOnePrimitive(pCtx,
                                      pfnPrims,
                                      dwStride,
                                      pFanVtx,
                                      D3DPT_TRIANGLEFAN,
                                      vertexCount));
            }
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)pFanVtx +
                                          vertexCount * dwStride);
        }
        break;
    case D3DDP2OP_LINELIST_IMM:
        {
            DWORD vertexCount = pCmd->wPrimitiveCount * 2;
            // Make sure the pLineVtx pointer is DWORD aligned: (pLineVtx +3) % 4
            PUINT8 pLineVtx = (PUINT8)(((ULONG_PTR)(pCmd + 1) + 3) & ~3);
            HR_RET(DoDrawOnePrimitive(pCtx,
                                  pfnPrims,
                                  dwStride,
                                  pLineVtx,
                                  D3DPT_LINELIST,
                                  vertexCount));
            // Update the command buffer pointer
            *ppCmd = (LPD3DHAL_DP2COMMAND)((PUINT8)pLineVtx +
                                          vertexCount * dwStride);
        }
        break;
    default :
        hr = D3DParseUnknownCommand((LPVOID)pCmd, (LPVOID*)ppCmd);
        break;
    }
    return hr;
}