axsoft
版主
發表:681 回覆:1056 積分:969 註冊:2002-03-13
發送簡訊給我
|
C template類TFVFProperties及其在DX8中的應用 說明:介紹了C template類TFVFProperties及類TFVFDeclGen的實現 by: 方泓 資料來源:http://www.cpp3d.com/articles/show.asp?aid=104
參考網頁:http://www.cpp3d.com/ 介紹 DX8中的FVF(Flexible Vertex Format Flags)是用來描述在單個數據流里交錯存儲的頂點內容, 它是用一個整形數(DWORD)來表示,更多關于FVF的說明,可以在DX8 SDK文檔[1]中找到。 從FVF的值我們就可以知道此FVF所表示的一些基本屬性。如果我們在編譯時間就知道了這個值,那麼我們甚至在編譯時間就可以知道並利用這些屬性,由此我們可以做一些有趣的應用。但是如何才能在編譯時間就得到這些屬性呢 ?
實現 C 中的template機制就能實現上面所說的目標,這種稱為C Meta Programming的方法是C template的一種有趣而且也有一定實用價值的應用,對此有興趣的人可以讀讀Todd Veldhuizen的著名文章[2]。 在這里我引進一個名為TFVFProperties template類(或結構),它的template參數就是FVF值,這個類非常的單純,就是根據此FVF值在編譯時間算出一些基本屬性,並將這些基本屬性信息保存在里面待用,這樣我們在編譯時就能得到指定FVF中所包含的屬性信息,我們可以利用這些屬性來做一些有趣的應用。 這個類的實現不算很複雜,下面我就直接將實現列在下面: template
struct TFVFProperties
{
BOOST_STATIC_CONSTANT(DWORD, sc_dwFVF = t_dwFVF);
private:
BOOST_STATIC_CONSTANT(int, _sc_nDummy = 1);
public:
BOOST_STATIC_CONSTANT(bool, sc_bHasPosition = ((t_dwFVF & D3DFVF_POSITION_MASK) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasRHW = ((t_dwFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW));
BOOST_STATIC_CONSTANT(bool, sc_bHasWeights = ((t_dwFVF & D3DFVF_POSITION_MASK) >= D3DFVF_XYZB1));
BOOST_STATIC_CONSTANT(bool, sc_bHasMatrixIndices = ((t_dwFVF & D3DFVF_LASTBETA_UBYTE4) != 0));
BOOST_STATIC_CONSTANT(int, sc_nWeights = sc_bHasWeights
? (sc_bHasMatrixIndices
? (t_dwFVF - D3DFVF_XYZB1 & D3DFVF_POSITION_MASK) >> 1
: ((t_dwFVF - D3DFVF_XYZB1 & D3DFVF_POSITION_MASK) >> 1) 1)
: _sc_nDummy);
BOOST_STATIC_CONSTANT(bool, sc_bHasNormal = ((t_dwFVF & D3DFVF_NORMAL) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasPointSize = ((t_dwFVF & D3DFVF_PSIZE) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasDiffuse = ((t_dwFVF & D3DFVF_DIFFUSE) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasSpecular = ((t_dwFVF & D3DFVF_SPECULAR) != 0));
BOOST_STATIC_CONSTANT(bool, sc_bHasTexCoords = ((t_dwFVF & D3DFVF_TEXCOUNT_MASK) != 0));
BOOST_STATIC_CONSTANT(int, sc_nTexCoords = ((t_dwFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT)); // D3DFVF_TEXCOORDSIZEx
#define _M_DIM_TEX(mCoordIndex)\
(0x1432 >> (((t_dwFVF >> ((mCoordIndex - 1) * 2 16)) & 0x03) * 4)) & 0x0F
BOOST_STATIC_CONSTANT(int, sc_nDimTex1 = _M_DIM_TEX(1) ? _M_DIM_TEX(1) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex2 = _M_DIM_TEX(2) ? _M_DIM_TEX(2) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex3 = _M_DIM_TEX(3) ? _M_DIM_TEX(3) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex4 = _M_DIM_TEX(4) ? _M_DIM_TEX(4) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex5 = _M_DIM_TEX(5) ? _M_DIM_TEX(5) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex6 = _M_DIM_TEX(6) ? _M_DIM_TEX(6) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex7 = _M_DIM_TEX(7) ? _M_DIM_TEX(7) : _sc_nDummy);
BOOST_STATIC_CONSTANT(int, sc_nDimTex8 = _M_DIM_TEX(8) ? _M_DIM_TEX(8) : _sc_nDummy);
#undef _M_DIM_TEX private:
BOOST_STATIC_CONSTANT(bool, _sc_bIsValid = sc_bHasPosition
&& (sc_bHasNormal || sc_bHasDiffuse || sc_bHasSpecular || sc_bHasTexCoords)
&& ! (sc_bHasRHW && (sc_bHasNormal || sc_bHasWeights))); M_CAssert(sc_nTexCoords <= 8);
M_CAssert(_sc_bIsValid);
}; 由此類的實現可以看出,象sc_bHasPosition這樣的bool值屬性的獲取是非常簡單的,只要將FVF值與D3DFVF_POSITION_MASK相與看是否為0就可以得到了。一些屬性數值的取得較為複雜一些,如sc_nWeights, sc_nDimTex* 等,不過相信只要仔細看DX8SDK的文檔[1]可以得到解答,在此我不再說明。
應用 下面我們來看TFVFProperties的一個簡單應用:根據FVF自動生成Vertex shader的declaration token數組,我們可以用下面這個template類來做這項工作: template
struct TFVFDeclGen
{
BOOST_STATIC_CONSTANT(DWORD, e_StreamNumber = t_dwStreamNumber);
public:
static const DWORD sm_adwDecl[D3DVSDE_NORMAL2 2 1];
}; template
const DWORD TFVFDeclGen::sm_adwDecl[] =
{
D3DVSD_STREAM(e_StreamNumber),
TFVFProperties::sc_bHasPosition ? D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3) : D3DVSD_NOP(),
TFVFProperties::sc_bHasWeights
? D3DVSD_REG(D3DVSDE_BLENDWEIGHT, (TFVFProperties::sc_nWeights == 1)
? D3DVSDT_FLOAT1
: (TFVFProperties::sc_nWeights == 2)
? D3DVSDT_FLOAT2
: (TFVFProperties::sc_nWeights == 3)
? D3DVSDT_FLOAT3
: (TFVFProperties::sc_nWeights == 4)
? D3DVSDT_FLOAT4
: D3DVSDT_FLOAT4)
: D3DVSD_NOP(),
TFVFProperties::sc_bHasMatrixIndices
? D3DVSD_REG(D3DVSDE_BLENDINDICES, D3DVSDT_UBYTE4) : D3DVSD_NOP(),
TFVFProperties::sc_bHasNormal ? D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3) : D3DVSD_NOP(),
TFVFProperties::sc_bHasPointSize ? D3DVSD_REG(D3DVSDE_PSIZE, D3DVSDT_FLOAT1) : D3DVSD_NOP(),
TFVFProperties::sc_bHasDiffuse ? D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR) : D3DVSD_NOP(),
TFVFProperties::sc_bHasSpecular ? D3DVSD_REG(D3DVSDE_SPECULAR, D3DVSDT_D3DCOLOR) : D3DVSD_NOP(), #define _M_GenTexureDecl(mN, mN_1)\
TFVFProperties::sc_nTexCoords >= ##mN\
? D3DVSD_REG(D3DVSDE_TEXCOORD##mN_1,\
(D3DVSDT_FLOAT1 TFVFProperties::sc_nDimTex##mN - 1))\
: D3DVSD_NOP()
_M_GenTexureDecl(1, 0),
_M_GenTexureDecl(2, 1),
_M_GenTexureDecl(3, 2),
_M_GenTexureDecl(4, 3),
_M_GenTexureDecl(5, 4),
_M_GenTexureDecl(6, 5),
_M_GenTexureDecl(7, 6),
_M_GenTexureDecl(8, 7),
#undef _M_GenTexureDecl
D3DVSD_NOP(), // D3DVSDE_POSITION2
D3DVSD_NOP(), // D3DVSDE_NORMAL2
D3DVSD_END()
}; 代碼很容易理解,在這我不加解說了。這樣的話我們在編譯時就能求出數組的值,省了一點點運行開銷,這個方法我去年底曾經在cpp3d的論壇上簡單說過,也有朋友認為這數組可能是運行時才算出的,所以我還寫了一個最簡單的試驗程序在VC6 SP5下驗証了一下: {
...
const DWORD dwFVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
printf("%X\n%X-%X-%X-%X\n%X-%X-%X-%X\n%X-%X-%X-%X\n%X-%X-%X-%X-%X\n%X\n"
, TFVFDeclGen::m_adwDecl[0], ...);
...
} 用IDA反匯編出來的代碼可以清楚的看到生成的declaration 數組: 可見在這些值並非在運行時才計算出的。 ; Segment type: Pure data
_rdata segment para public 'DATA' use32
assume cs:_rdata
;org 4070DCh
db 0 ;
db 0 ;
db 0 ;
db 0 ;
dword_4070E0 dd 20000000h ; DATA XREF: _main 76r
dword_4070E4 dd 40020000h ; DATA XREF: _main 6Fr
dword_4070E8 dd 0 ; DATA XREF: _main 68r
dword_4070EC dd 0 ; DATA XREF: _main 62r
dword_4070F0 dd 40020003h ; DATA XREF: _main 5Br
dword_4070F4 dd 0 ; DATA XREF: _main 54r
dword_4070F8 dd 0 ; DATA XREF: _main 4Er
dword_4070FC dd 0 ; DATA XREF: _main 47r
dword_407100 dd 40010007h ; DATA XREF: _main 40r
dword_407104 dd 0 ; DATA XREF: _main 3Ar
dword_407108 dd 0 ; DATA XREF: _main 33r
dword_40710C dd 0 ; DATA XREF: _main 2Cr
dword_407110 dd 0 ; DATA XREF: _main 26r
dword_407114 dd 0 ; DATA XREF: _main 1Fr
dword_407118 dd 0 ; DATA XREF: _main 18r
dword_40711C dd 0 ; DATA XREF: _main 12r
dword_407120 dd 0 ; DATA XREF: _main Br
dword_407124 dd 0 ; DATA XREF: _main 5r
dword_407128 dd 0FFFFFFFFh ; DATA XREF: _mainr 下面我們來看看如何使用這個類的代碼示例: class CD3DVertexShader
{
.... public:
#define M_FVF2Decl(mFVF) meta::TType2Type >()
template
inline bool Create(TWrapDecl, const DWORD* pVSCode)
{
return Create(TWrapDecl::TOriginalType::sm_adwDecl, pVSCode);
} inline bool Create(const DWORD* pdwDecl, const DWORD* pVSCode)
{
DWORD dwUsage = 0L;
if (CD3DRenderDevice::Instance()->IsSoftwareVertexProcessing())
dwUsage |= D3DUSAGE_SOFTWAREPROCESSING; // Create the vertex shader
return SUCCEEDED(CD3DRenderDevice::Instance()->CreateVertexShader(...);
}
...
}; const DWORD c_dwFVF0 = D3DFVF_XYZ | D3DFVF_DIFFUSE;
const DWORD c_dwFVF1 = D3DFVF_XYZ | D3DFVF_DIFFUSE;
const DWORD c_dwFVF2 = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1,
const DWORD c_dwFVF3 = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1;
...
{
...
// Create the vertex shader
m_VS0.Create(M_FVF2Decl(c_dwFVF0), dwSimple0VertexShader);
m_VS1.Create(M_FVF2Decl(c_dwFVF1), dwSimple1VertexShader);
m_VS2.Create(M_FVF2Decl(c_dwFVF2), dwSimple2VertexShader);
m_VS3.Create(M_FVF2Decl(c_dwFVF3), dwSimple3VertexShader);
...
} 這里需要提醒的是,declaration是提供給shader使用的,用來表達輸入數值的秩序,所以記得你的shader代碼得到輸入數據要和它一致。 對比D3DX中的D3DXDeclaratorFromFVF()方法,這種方法在編譯時生成數組,沒什麼運行開銷。但是需要FVF是一個編譯時知道的常量。而D3DXDeclaratorFromFVF()則是運行期間生成declaration數組的,有一點點運行開銷,但是FVF不必是常量。對比實際中更常用的直接定義declaration數組,好處是比較方便,不需要定義,但不能用于一些特殊情況,而且有多個Stream時就用不上了,而直接定義則有很大的靈活性,所以在實用中可以幾種方法一起使用。
總結 本文介紹了一個C template類TFVFProperties及其實現,並介紹它的一個具體應用,生成靜態Vertex Shader的declaration數組,可以在某些情況下簡化編程,但要注意在使用上有一些限制。
下一篇我將介紹應用TFVFProperties的一個更為複雜的應用。
注釋 [1] Microsoft Corp., Microsoft DirectX 8.1 SDK. Microsoft Visual C Documentation Homepage, Microsoft Corp., 2001. [2] Todd Veldhuizen. "Template Metaprograms.", C Report, 7, May, pp 36-43, 1995.
時間就是金錢---[ 發問前請先找找舊文章]
|