Direct3D10 と DDS テクスチャフォーマット

D3D10 のテクスチャは汎用的なバッファの一種でピクセルフォーマットが
拡張されています。

特に整数型の直行性が高くなり、固定少数として扱うかどうか、符号付きと
みなすかどうかといったバリエーションが増えています。
また Texture Array が追加されたため、従来のテクスチャフォーマットよりも
次元が増えました。

DirectX SDK のマニュアルでは、Direct3D 10 用の DDS フォーマット拡張を
定義しています。
残念ながらこの解説は不完全で、このドキュメントを見ても厳密な DDS のヘッダ
構造がわかりませんでした。

DirectX SDK の texconv10 コマンドを使うと D3D10 用のテクスチャを生成する
ことができます。このコマンドでヘッダの違いを調べてみました。
以下 DirectX SDK Aug2008 x64 を使用しています。

● ヘッダサイズの疑問

DDS テクスチャのヘッダは先頭の dwMagic と構造体を合わせてちょうど 128byte です。
Programming Guide for DDS によるとヘッダブロックは次のように定義されています。

In Direct3D 10:
  DWORD             dwMagic;  // 4byte
  DDS_HEADER        header;   // 124byte  = 0x7c
  DDS_HEADER_DXT10  header10; // 20byte

In Direct3D 9:
  DWORD             dwMagic;  // 4byte
  DDSURFACEDESC2    header;   // 124byte  = 0x7c

sozeof(DDS_HEADER) == sizeof(DDSURFACEDESC2) == 124byte で内容は互換性が
あります。

Direct3D10 で追加された DDS_HEADER_DXT10 は 5要素の 32bit UINT でできており
20byte と少々中途半端な大きさです。ヘッダサイズのアライメント崩れてしまう
のでこのような構造にするとは考えられにくいのが正直なところです。
例えば R32G32B32A32_FLOAT 型のテクスチャを読み書きする場合でも、ヘッダを
含めると SSE の直接アクセスに制限が生じることになります。

DDS_HEADER(DDSURFACEDESC2) の最後、DDS_PIXELFORMAT の後ろがちょうど
20byte あるので、もしここに格納されるのならつじつまが合います。

ところが実際に texconv10 で D3D10 専用形式に変換してみると、
ぴったりファイルサイズが 20byte 増えています。
128byte 目以降に追加の DDS_HEADER_DXT10 が入っていました。

よって DDS_HEADER_DXT10 の追加ブロックはマニュアルのヘッダ構造が正解です。
Direct3D 10 用 DDS はヘッダが 20byte 増えて 148byte になります。

● ヘッダ区別の方法

DDS ファイルを開いたときに、Direct3D 10 の拡張情報が含まれているかどうか
判断する必要があります。先頭 128byte 部分はほぼ完全な互換性があり、
追加情報を読むべきかどうかの手段が明確にされていないようです。

基本的に D3D9 形式で表現できるテクスチャは D3D9 ヘッダのみ格納するようです。
例えば次のように dds 変換した場合

(1) texconv   -ft dds white.bmp -m 1 -sx _dx9   -f a8r8g8b8
(2) texconv10 -ft dds white.bmp -m 1 -sx _unorm -f r8g8b8a8_unorm
(3) texconv10 -ft dds white.bmp -m 1 -sx _sint  -f r8g8b8a8_sint

出力
(1) white_dx9.dds   262272byte
(2) white_unorm.DDS 262272byte
(3) white_sint.DDS  262292byte

同じ texconv10 を使っても、D3D9 形式と互換性のある (2) と D3D10 専用の (3)
ではファイルサイズに 20byte の差が生じます。

このとき (2) の DDS_PIXELFORMAT は DDPF_RGB|DDPF_ALPHAPIXELS となり
Mask にも一通り正しい値が入りました。

(3) の DDS_PIXELFORMAT は DDPF_FOURCC で、dwFourCC に 0x30315844 = ‘DX10’
が入りそれ以外はゼロです。

よって、D3D10 拡張 DDS の場合は dwFourCC を見て、’DX10′ の場合のみ追加
の D3D10 ヘッダを参照すればいいようです。

ここで悩んだのは、(2) のテクスチャにも 128byte 目以降に追加のヘッダ情報が
格納されているように見えること。
当初 dwFourCC != ‘DX10’ の場合でも DDS_HEADER_DXT10 が挿入される場合が
あるのではないかと疑ったのですが、ファイルサイズを見るとデータ領域と
判断して良さそうです。データが壊れているのかもしれません。

その後いろいろ試したけど、やはり texconv10 ではデータが壊れてしまう問題が
生じるようです。x86 bin でもだめ。
March 2008 の texconv10 だと正しい値が取れています。要調査。