Direct3D10 以降、Pixel フォーマットと頂点の各 Element のフォーマット形式は
統一されています。DXGI_FORMAT はまた Direct3D からも独立しており、
バージョンに依存せず同じものが使えるようになりました。
つまり Direct3D10 と Direct3D11 のフォーマットは同一です。
●Direct3D9
・ピクセル、インデックス用フォーマット D3DFORMAT
D3DFMT_A8R8G8B8
D3DFMT_R5G6B5
D3DFMT_A16B16G16R16F
D3DFMT_INDEX16 (IndexBuffer 用)
等
・頂点フォーマット D3DDECLTYPE
D3DDECLTYPE_FLOAT3
D3DDECLTYPE_UBYTE4
他
●Direct3D10/Direct3D11
・ピクセル、頂点、インデックス兼用 DXGI_FORMAT
DXGI_FORMAT_R8G8B8A8_UNORM
DXGI_FORMAT_B5G6R5_UNORM
DXGI_FORMAT_R32G32B32_FLOAT (従来の D3DDECLTYPE_FLOAT3 も兼ねる)
DXGI_FORMAT_R16_UINT (従来の D3DFMT_INDEX16 も兼ねる)
~
Direct3D11 ではライブラリ独自の頂点形式から ID3D11InputLayout を作るために
下記の関数を用意していました。
これをそのまま OpenGLES.20 用に置き換えたのが、先日の GLInputLayout の関数です。
// DXInputLayout.cpp
static struct _FormatType {
DXGI_FORMAT dxgi_format;
} formatTable[]= {
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R32_FLOAT, },
{ DXGI_FORMAT_R32G32_FLOAT, },
{ DXGI_FORMAT_R32G32B32_FLOAT, },
{ DXGI_FORMAT_R32G32B32A32_FLOAT, },
{ DXGI_FORMAT_R16_FLOAT, },
{ DXGI_FORMAT_R16G16_FLOAT, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R16G16B16A16_FLOAT, },
{ DXGI_FORMAT_R32_SINT, },
{ DXGI_FORMAT_R32G32_SINT, },
{ DXGI_FORMAT_R32G32B32_SINT, },
{ DXGI_FORMAT_R32G32B32A32_SINT, },
{ DXGI_FORMAT_R16_SINT, },
{ DXGI_FORMAT_R16G16_SINT, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R16G16B16A16_SINT, },
{ DXGI_FORMAT_R8_SINT, },
{ DXGI_FORMAT_R8G8_SINT, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R8G8B8A8_SINT, },
{ DXGI_FORMAT_R32_UINT, },
{ DXGI_FORMAT_R32G32_UINT, },
{ DXGI_FORMAT_R32G32B32_UINT, },
{ DXGI_FORMAT_R32G32B32A32_UINT, },
{ DXGI_FORMAT_R16_UINT, },
{ DXGI_FORMAT_R16G16_UINT, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R16G16B16A16_UINT, },
{ DXGI_FORMAT_R8_UINT, },
{ DXGI_FORMAT_R8G8_UINT, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R8G8B8A8_UINT, },
{ DXGI_FORMAT_R16_SNORM, },
{ DXGI_FORMAT_R16G16_SNORM, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R16G16B16A16_SNORM, },
{ DXGI_FORMAT_R8_SNORM, },
{ DXGI_FORMAT_R8G8_SNORM, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R8G8B8A8_SNORM, },
{ DXGI_FORMAT_R16_UNORM, },
{ DXGI_FORMAT_R16G16_UNORM, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R16G16B16A16_UNORM, },
{ DXGI_FORMAT_R8_UNORM, },
{ DXGI_FORMAT_R8G8_UNORM, },
{ DXGI_FORMAT_UNKNOWN, },
{ DXGI_FORMAT_R8G8B8A8_UNORM, },
};
DXGI_FORMAT _flFASTCALL
PixelToFormat( unsigned int func )
{
assert( func < Image::FUNC_END );
return formatTable[ func ].dxgi_format;
}
void _flFASTCALL
SetElementDesc(
D3D11_INPUT_ELEMENT_DESC* desc,
unsigned int desccount,
const a5::InputLayout& layout
)
{
unsigned int ecount= layout.GetElementCount();
assert( ecount < desccount );
for( unsigned int ei= 0 ; ei< ecount ; ei++, desc++ ){
const a5::InputElement* ep= layout.GetElement( ei );
desc->SemanticName= ep->SemanticName;
desc->SemanticIndex= ep->SemanticIndex;
desc->Format= PixelToFormat( ep->pixf.Func );
desc->InputSlot= 0;
desc->AlignedByteOffset= ep->ByteOffset;
desc->InputSlotClass= D3D11_INPUT_PER_VERTEX_DATA;
desc->InstanceDataStepRate= 0;
}
}
このように、独自の頂点形式をテーブルで DXGI_FORMAT に変換しています。
DXGI そのままでないのが幸いして、OpenGLES2.0 への変換も比較的うまく
いっています。OpenGLES2.0 で用意したのが下記のコードです。
このテーブルはそのまま上の DXGI_FORMAT に対応しています。
static struct _FormatType {
GLenum dxgi_format;
unsigned int component;
unsigned int normalize;
} formatTable[]= {
{ 0, 0, FALSE, },
{ 0, 0, FALSE, },
{ 0, 0, FALSE, },
{ 0, 0, FALSE, },
{ GL_FLOAT, 1, FALSE, },
{ GL_FLOAT, 2, FALSE, },
{ GL_FLOAT, 3, FALSE, },
{ GL_FLOAT, 4, FALSE, },
{ 0, 0, FALSE, }, // half float
{ 0, 0, FALSE, }, // half float
{ 0, 0, FALSE, }, // half float
{ 0, 0, FALSE, }, // half float
{ 0, 0, FALSE, }, // signed int 32
{ 0, 0, FALSE, }, // signed int 32
{ 0, 0, FALSE, }, // signed int 32
{ 0, 0, FALSE, }, // signed int 32
{ GL_SHORT, 1, FALSE, }, // signed int 16
{ GL_SHORT, 2, FALSE, }, // signed int 16
{ GL_SHORT, 3, FALSE, }, // signed int 16
{ GL_SHORT, 4, FALSE, }, // signed int 16
{ GL_BYTE, 1, FALSE, }, // signed byte 8
{ GL_BYTE, 2, FALSE, }, // signed byte 8
{ GL_BYTE, 3, FALSE, }, // signed byte 8
{ GL_BYTE, 4, FALSE, }, // signed byte 8
{ 0, 0, FALSE, }, // unsigned int 32
{ 0, 0, FALSE, }, // unsigned int 32
{ 0, 0, FALSE, }, // unsigned int 32
{ 0, 0, FALSE, }, // unsigned int 32
{ GL_UNSIGNED_SHORT, 1, FALSE, },
{ GL_UNSIGNED_SHORT, 2, FALSE, },
{ GL_UNSIGNED_SHORT, 3, FALSE, },
{ GL_UNSIGNED_SHORT, 4, FALSE, },
{ GL_UNSIGNED_BYTE, 1, FALSE, },
{ GL_UNSIGNED_BYTE, 2, FALSE, },
{ GL_UNSIGNED_BYTE, 3, FALSE, },
{ GL_UNSIGNED_BYTE, 4, FALSE, },
{ GL_SHORT, 1, TRUE, },
{ GL_SHORT, 2, TRUE, },
{ GL_SHORT, 3, TRUE, },
{ GL_SHORT, 4, TRUE, },
{ GL_BYTE, 1, TRUE, },
{ GL_BYTE, 2, TRUE, },
{ GL_BYTE, 3, TRUE, },
{ GL_BYTE, 4, TRUE, },
{ GL_UNSIGNED_SHORT, 1, TRUE, },
{ GL_UNSIGNED_SHORT, 2, TRUE, },
{ GL_UNSIGNED_SHORT, 3, TRUE, },
{ GL_UNSIGNED_SHORT, 4, TRUE, },
{ GL_UNSIGNED_BYTE, 1, TRUE, },
{ GL_UNSIGNED_BYTE, 2, TRUE, },
{ GL_UNSIGNED_BYTE, 3, TRUE, },
{ GL_UNSIGNED_BYTE, 4, TRUE, },
};
GLenum _flFASTCALL
PixelToFormat( unsigned int func, unsigned int* component, unsigned int* normal )
{
assert( func < Image::FUNC_END );
const _FormatType* fp= &formatTable[ func ];
if( component ){
*component= fp->component;
}
if( normal ){
*normal= fp->normalize;
}
return fp->dxgi_format;
}
前回 GLInputLayout の CreateInputLayout() の中で呼び出していた
PixelToFormat() がこれです。
まだ FLOAT しか使っていないので本当に互換性があるかどうかは検証してません。
BYTE の並びは D3D9 の D3DDECLTYPE_UBYTE4 と D3DDECLTYPE_D3DCOLOR
のように順番が異なっている可能性があります。
同じく前回「ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。」と書いた
のはコンパイラの最適化で三項演算子が完全に消えることを期待していたからです。
(ep->Flag & ELEMENTFLAG_NORMALIZE) ? GL_TRUE : GL_FALSE
GL_TRUE / GL_FALSE の定義は 1 と 0 なので、最終的には
「ep->Flag & ELEMENTFLAG_NORMALIZE」だけが残ります。
予想通り分岐は消えますが、シンボルが 1 以外でも shr が一つ追加されるだけでした。
; ELEMENTFLAG_NORMALIZE == 4 の場合 (x64 + VC) 000000013F4D6697 shr ebx,2 000000013F4D66A1 and ebx,1 000000013F4D66A4 mov edx,ebx
シフトを命令に埋め込める ARM だったら全く意識する必要ないのかもしれません。
関連エントリ
・OpenGLES 2.0 頂点フォーマットの管理
・OpenGLES2.0 の頂点
・OpenGLES2.0 D3D座標系
・OpenGLES2.0 シェーダー管理