/*============================================================================
 *
 *  Copyright (C) 1999 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       pshader.cpp
 *  Content:    pixel shader runtime object init, including basic parsing
 *              of pixel shader instructions
 *
 ****************************************************************************/

#include "pch.cpp"
#pragma hdrstop

#include "fe.h"
#include "ddibase.h"
#include "vvm.h"

//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CreatePixelShader"

HRESULT
CPShader::Initialize(CONST DWORD* pCode, D3DDEVTYPE DevType)
{
    // get shader code sizes
    DWORD dwCodeAndCommentSize;
    DWORD dwCodeOnlySize;
    HRESULT hr = ComputeShaderCodeSize(pCode, &dwCodeOnlySize, &dwCodeAndCommentSize,
                                        &m_dwNumConstDefs);
    if (hr != S_OK)
        return hr;

    // copy original code
    m_dwCodeSizeOrig = dwCodeAndCommentSize;
    m_pCodeOrig = new DWORD[m_dwCodeSizeOrig >> 2];
    if (NULL == m_pCodeOrig)
    {
        D3D_ERR("Cannot allocate memory for shader");
        return E_OUTOFMEMORY;
    }
    memcpy( m_pCodeOrig, pCode, m_dwCodeSizeOrig );

    if( m_dwNumConstDefs )
    {
        m_pConstDefs = new CONST_DEF[m_dwNumConstDefs];
        if (NULL == m_pConstDefs)
        {
            D3D_ERR("Cannot allocate memory for shader");
            return E_OUTOFMEMORY;
        }
    }

    // strip comments before sending on if (not CHECKED) or HAL.
    // Also, store def declarations in CPShader, and strip them.
    BOOL bIsCheckedBuild =
#if DBG
        TRUE;
#else
        FALSE;
#endif
    BOOL  bStripComments = (!bIsCheckedBuild) || (DevType == D3DDEVTYPE_HAL);
    if ( bStripComments )
    {
        // strip comments from version to pass on to DDI
        m_dwCodeSize = dwCodeOnlySize;
    }
    else
    {
        // pass comments through
        m_dwCodeSize = m_dwCodeSizeOrig;
    }
    m_pCode = new DWORD[m_dwCodeSize >> 2];
    if (NULL == m_pCode)
    {
        D3D_ERR("Cannot allocate memory for shader");
        return E_OUTOFMEMORY;
    }
    DWORD* pDst = m_pCode;
    CONST DWORD* pSrc = pCode;
    DWORD dwCurrConstDef = 0;
    *pDst++ = *pSrc++; // copy version
    while (*pSrc != 0x0000FFFF)
    {
        if(IsInstructionToken(*pSrc))
        {
            DWORD opCode = (*pSrc) & D3DSI_OPCODE_MASK;
            if (opCode == D3DSIO_COMMENT )
            {
                UINT DWordSize = ((*pSrc)&D3DSI_COMMENTSIZE_MASK)>>D3DSI_COMMENTSIZE_SHIFT;
                // strip comments from version to pass on to DDI
                if( !bStripComments )
                {
                    memcpy( pDst, pSrc, (DWordSize + 1)*sizeof(DWORD) );
                    pDst += (DWordSize+1);
                }
                pSrc += (DWordSize+1);  // comment + instruction token
            }
            else if (opCode == D3DSIO_DEF)
            {
                *pDst++ = *pSrc++;
                DXGASSERT(m_pConstDefs && dwCurrConstDef < m_dwNumConstDefs);

                // Store reg. number
                m_pConstDefs[dwCurrConstDef].RegNum = (*pSrc & D3DSP_REGNUM_MASK);
                *pDst++ = *pSrc++;

                // Store the const vector
                memcpy( m_pConstDefs[dwCurrConstDef].f,pSrc,4*sizeof(DWORD) );
                memcpy( pDst,pSrc,4*sizeof(DWORD) );
                pSrc += 4;
                pDst += 4;
                dwCurrConstDef++;
            }
            else
            {
                *pDst++ = *pSrc++;
            }
        }
        else
        {
             *pDst++ = *pSrc++;
        }
    }
    *pDst++ = *pSrc++; // copy END

    DXGASSERT(dwCurrConstDef == m_dwNumConstDefs);

    return S_OK;
}

//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::GetPixelShaderConstant"

HRESULT D3DAPI
CD3DHal::GetPixelShaderConstant(DWORD dwRegisterAddress,
                                LPVOID lpvConstantData,
                                DWORD dwConstantCount)
{
    API_ENTER(this);
#if DBG
    // Validate Parameters
    if (!VALID_WRITEPTR(lpvConstantData, 4*sizeof(D3DVALUE)*dwConstantCount))
    {
        D3D_ERR("Invalid constant data pointer. GetPixelShaderConstant failed.");
        return D3DERR_INVALIDCALL;
    }
#endif

    GetPixelShaderConstantI( dwRegisterAddress, dwConstantCount,
                             lpvConstantData );
    return S_OK;
}

//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetPixelShaderFast"

HRESULT D3DAPI
CD3DHal::SetPixelShaderFast(DWORD dwHandle)
{
    try
    {
#if DBG
        CheckPixelShaderHandle(dwHandle);
#endif
        // Update constants (if any were defined in the shader code)
        if( dwHandle )
        {
            CPShader* pShader = (CPShader*)m_pPShaderArray->GetObject(dwHandle);
            for(UINT i = 0; i < pShader->m_dwNumConstDefs; i++ )
            {
                CONST_DEF* pConstDef = &pShader->m_pConstDefs[i];
                memcpy(&(m_PShaderConstReg[pConstDef->RegNum]), pConstDef->f, 4*sizeof(D3DVALUE));
            }
        }

        // No redundant handle check because shader may have embedded constant definitions which
        // must always be applied.
        if (!(m_dwRuntimeFlags & D3DRT_EXECUTESTATEMODE))
            m_pDDI->SetPixelShader(dwHandle);

        m_dwCurrentPixelShaderHandle = dwHandle;
    }
    catch(HRESULT hr)
    {
        D3D_ERR("SetPixelShader failed.");
        m_dwCurrentPixelShaderHandle = 0;
        return hr;
    }
   return S_OK;
}

//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetPixelShaderConstantFast"

HRESULT D3DAPI
CD3DHal::SetPixelShaderConstantFast(DWORD Register, CONST VOID* pData,
                                    DWORD count)
{

#if DBG
    // Validate Parameters
    if (!VALID_PTR(pData, sizeof(DWORD) * count))
    {
        D3D_ERR("Invalid constant data pointer. SetPixelShader failed.");
        return D3DERR_INVALIDCALL;
    }
    if(Register >= D3DPS_CONSTREG_MAX_DX8)
    {
        D3D_ERR("Invalid Constant Register number. SetPixelShader failed.");
        return D3DERR_INVALIDCALL;
    }
    if( (Register + count) > D3DPS_CONSTREG_MAX_DX8 )
    {
        D3D_ERR("Not that many constant registers in the pixel machine. SetPixelShader failed.");
        return D3DERR_INVALIDCALL;
    }
#endif

    // Cache the constants in the CPShader structure.
    memcpy(&(m_PShaderConstReg[Register]),  pData, count*4*sizeof(D3DVALUE));
    if (!(m_dwRuntimeFlags & D3DRT_EXECUTESTATEMODE))
    {
        try
        {
            m_pDDI->SetPixelShaderConstant(Register, pData, count);
        }
        catch(HRESULT hr)
        {
            D3D_ERR("SetPixelShaderConstant failed.");
            return hr;
        }
    }

    return S_OK;
}

//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::GetPixelShaderConstantI"

void
CD3DHal::GetPixelShaderConstantI(DWORD Register, DWORD count, LPVOID pData )
{
    // Cache the constants in the CPShader structure.
    memcpy( pData, &(m_PShaderConstReg[Register]), count*4*sizeof(D3DVALUE) );
}
//-----------------------------------------------------------------------------
// end