2006年12月アーカイブ

概要: Opera 9もXSLTプロセッサが実装されたようですが、Mozilla Firefoxでも実装されているんですね。Netscapeの日本法人が撤退してバージョンアップが止まっているということを(今更...

Opera 9もXSLTプロセッサが実装されたようですが、Mozilla Firefoxでも実装されているんですね。Netscapeの日本法人が撤退してバージョンアップが止まっているということを(今更)知ったので、Firefox 2.0を入れてみましたが、…うん、これはなかなか。フォント周りも強くなってるし。

Firefox 2のXSLTプロセッサはXSLT 1.0のdocument関数が使えるのはいいんですが、結果ツリーフラグメント(Result Tree Fragment)をノードセットとして評価することができないようです。

そもそも結果ツリーフラグメントって何かというと、大雑把に言えばxsl:variable要素やxsl:param要素の内容としての値です。例えば、

<xsl:variable name="result">
  <level>3</level>
  <number>1-1</number>
</xsl:variable>

上記のような変数resultがあった場合、$resultはlevelとnumberの二つのノードを持つノードセットのように見えますが、これはノードセットにはなりません。結果ツリーフラグメントと呼ばれます。結果ツリーフラグメントに対しては文字列操作しか許されず、勿論/や//、[]などのノードセットに対してのみ使える演算子は使えません。ただ、xsl:copy-of要素のselect属性で結果ツリーフラグメントを値に持つ変数を指定すると(<xsl:copy-of select="$result"/>のようにすると)、あたかもそれがノードセットであるかのように振る舞います。

別の例を見てみましょう。対象とするXML文書の子孫にbodyという要素があって、body要素は幾つかのsection要素を子に持っているとします(うちのXML文書のスタイルです)。この時、次のXSLTコード(断片というべき?)は許されます:

<xsl:variable name="body" select="//body"/>
<xsl:for-each select="$body/section">
  <!-- 何らかの処理 -->
</xsl:for-each>

しかし、次のようにすると誤りです。

<xsl:variable name="body">
  <xsl:copy-of select="//body">
</xsl:variable>
<xsl:for-each select="$body/section">
  <!-- 何らかの処理 -->
</xsl:for-each>

先程も言った通り、xsl:variable要素などの内容にしてしまうと、それがなんであれ結果ツリーフラグメントになるのです。数値を書いても文字列を書いても何でもかんでも結果ツリーフラグメントに。一方で、select属性で値を与えると、式の型と同じになります。ノードセットならノードセットに、数値なら数値に、文字列なら文字列に、ブーリアンならブーリアンに。そんな訳で、前者の$bodyはノードセットで、後者の$bodyは結果ツリーフラグメントなのです。ああ無情。

しかしながら、xsl:variableの内容として書いてもノードセットとしてみてもらいたいことは少なくないわけで、──例えば変数の値にa要素を幾つか書いておいて、あとでそれをxsl:for-eachで処理するとか、テンプレートの戻り値(戻り結果ツリーフラグメント?)が複雑になる場合、さっきのlevelやnumberのように要素で区切って返すとか(Cで言うと構造体を返す感じ)。そんな時のために、IEやSablotron(PHPとかでも使える、何かの素粒子か加速器みたいな名前のXSLTプロセッサ)などでは次のような技巧が使えます:

<xsl:variable name="result">
  <level>3</level>
  <number>1-1</number>
</xsl:variable>
<xsl:variable name="result-node" select="$result"/>

これで、$resultは結果ツリーフラグメントですが、$result-nodeは(select属性で値を与えているので)ノードセットになります。……って、こんなんでいいなら最初っからxsl:variableの内容でもノードセットにしろッ! と叫びたくなりますが、まぁXSLT 1.0の仕様書に「内容だったら結果ツリーフラグメントと見なせ」って書いてあるので、なんとも……。ちなみに独自拡張のnode-set関数でもいいようです。

IE 6でもこの技巧が使えたので、数年前からずっとこの方法を愛用しているのですが、どーもFirefoxのXSLTプロセッサはこれが駄目らしい。仕様書に書いてないからバグではないんだけど、悲しい。Opera 9はこの方法が使えるけど、document関数がまだ実装されてないし…(近々実装されるという噂だけはあるけども)。うーん。結局IE6以降でなければサーバーサイドで変換するしかないようで。はぁ、がっくし……

概要: 些細なことでも定期的に書く習慣を付けようと思っていたようなので、些細なことを書きます。 .NET Framework 3.0の実体 CLRは2.0のまま、C#も2.0のまま、何が変わ...

些細なことでも定期的に書く習慣を付けようと思っていたようなので、些細なことを書きます。

.NET Framework 3.0の実体

CLRは2.0のまま、C#も2.0のまま、何が変わったかと言えば、4つのファンデーションが導入されただけみたいですね。.NET Framework 2.0やC# 2.0ほどの規模のアップデートではないそうな(参考:.NET Framework 3.0の紹介)。Windows Presentation Foundationってのだけは面白そう。今度少し調べてみようか……

Opera 9のXSLTプロセッサ

おお、いつの間にっ。って思ったもの第1位。ほぼXSLT 1.0が実装されているみたいですね……でも、document関数が使えない!! ……なんだよ、がっかりさせてくれるなぁ、もう……

documentが実装されるまではOperaに対してもサーバーサイドで変換してやらないと駄目ですね。

Devas

複数ファイル一括検索ソフト。正規表現が使えます。ああ、なんと素晴らしいその響き。いや、響きが良いだけじゃ駄目ですが、やっぱり一括正規表現置換が出来るって言うのは大きいですねぇ。UTF-8なんかのエンコーディングにも対応していますし、パーレンの中身を再利用するのも勿論出来ますし(\1とか\2とかで。正式名称忘れた)。

こいつを使って、長い間困っていたsectionとheadingの対応関係を楽に入れ替えることができました。それよりも、XSL書き直す方が大変でした。最初からXHTML 2.0の仕様書に載ってたサンプルよく見ておけば良かったですね。

概要: 最近XHTML 1.1 Referenceの方もForms Moduleのところで時間かかってるし、こっちも暫く放っておいたなぁと反省して、ちょこっと小ネタでも。以前やっていたDirect Sound...

最近XHTML 1.1 Referenceの方もForms Moduleのところで時間かかってるし、こっちも暫く放っておいたなぁと反省して、ちょこっと小ネタでも。以前やっていたDirect Sound周りが一段落したので(Ogg Vorbisに加えてFLACまであまり必要でもないのにやってしまった)、この前までDirect3D関係を実装していました。その辺りで身を以て思い知らされた事などを少々。

LoaderLock が検出されました

Visual Studio 2005とManaged DirectX 1.1を使っていると、デバッグ時に「ローダーロックが検出された」というエラーが出てきて、「DLL '...\Microsoft.DirectX.Direct3D.dll' は、OS ローダー ロック内でマネージ実行を試行しています。DllMain またはイメージ初期化関数内でマネージ コードを実行しないでください。この動作は、アプリケーションをハングさせる原因になる可能性があります。」と言われます。どうも.NET Framework 2.0とMDX 1.1は折りが合わないようで、そのままの設定ではうまくデバッグが出来ないようです。難しい内部事情は知りませんが、手っ取り早い解決策があります

  1. メインメニューで「デバッグ」「例外」を選び、「例外」ダイアログを開きます。
  2. 「Managed Debugging Assistants」の中の、「LoaderLock」のチェックを外します
  3. 「OK」ボタンで確定します。

Managed Debugging AddistantsのLoader Lockのチェックを外す

この設定はプロジェクト固有なので、新しいプロジェクトを作った場合には毎回設定しなければなりませんが、VS2003とCLR 1.1に戻るくらいならこっちの方がいいでしょう。

参考 : Why do I get a 'LoaderLock' Error when debugging my Managed DirectX application

Microsoft.DirectX.Direct3D.Spriteの有用性

下手な自前のコードよりは速く動作するでしょう。ただ、アルファブレンドが自由に行えないなどの機能的欠点や、バッファリングして描画した時のDrawIndexedPrimitivesには敵わないなどのパフォーマンス的欠点は持ちます。

しかし、デバッグ時にちょっとテクスチャを貼り付けて表示したいとか、普通のアルファブレンドしか使わず、スプライトの数も多くないといった場合には十分有用でしょう。

大量のプリミティブの高速描画

D3DXのSpriteは確かに数が増えてくるとシーン時間が長くなってきますが、DrawPrimitivesやDrawIndexedPrimitivesを使ってベタに組んでも、大した差は出ません。ベタに組む、というのは、スプライトを描画する度に、頂点4つの頂点バッファとインデックス4つのインデックスバッファを使ってTriangleStripを2つ、DrawIndexedPrimitivesで描画するというものです。DirectXでは、DrawUserPrimitivesなども含めて、Draw系の関数の呼び出しは出来るだけ数を減らす方がいいようです

これを実現するには、描画するスプライトの情報をバッファリングして、あとでまとめて頂点バッファに書き出して描画、ということが必要になります。シーンの中では、頂点バッファとインデックスバッファをセットして(ステートブロックを使うと良い)、TriangleListでDrawIndexedPrimitivesを呼び出すだけです。スプライト毎にワールド変換が異なるので、頂点変換はCPUで事前にやっておかなければなりませんが、GPUがせっせとレンダリングしてる裏で処理できるのでさほど問題ではないでしょう。

恐らく、一番のネックは頂点バッファへの書き込みとなります。C++ではポインタが何にでもキャストできる為気になりませんが、C#では、CustomVertex.PositionColoredTextured配列を書き込もうとするとキャストがうまく出来ない為につっかかります。かといって配列を返すバージョンのMicrosoft.DirectX.Direct3D.VertexBuffer.Lockではパフォーマンス的に不利です。GraphicsStreamを返すLockはbyte配列なら書き込めますが、頂点変換の時に一々頂点の構造体のデータの並びをbyte配列に手で直して代入するのも面倒です。

これには抜け道があります。アンセーフコードを書いてポインタにするのではありません、StructLayoutを使えば一発です。

using Microsoft.DirectX.Direct3D;
using System.InteropServices.Runtime;
 
[StructLayout( LayoutKind.Explicit )]
public struct VertexArray {
  public VertexArray( int length ) {
    this.VerticesAsByteArray = null;
    this.Vertices = new CustomVertex.PositionColoredTextured
      [ length ];
 
    return;
  }
  [FieldOffset( 0 )]
  public CustomVertex.PositionColoredTextured[] Vertices;
  [FieldOffset( 0 )]
  public byte[] VerticesAsByteArray;
}

何をやりたいか、見たら判ると思います。C#における共用体です(詳しくはUnions のサンプルを参照)。上のコードは、C++で書いたら次のようなものです。

struct PositionColoredTextured;
 
union VertexArray {
  PositionColoredTextured* Vertices;
  unsigned char* VerticesAsByteArray;
};

頂点変換の時はVerticesを、GraphicsStream.Writeに渡す時はVerticesAsByteArrayを使えばいい訳ですね。なんでこれくらいのことがC#では手軽に出来ないんでしょう。配列をbyte配列として見るかint配列としてみるかとかの変換くらいはキャストだけで出来てもいいような気がしますが……。僕が知らないだけですか? ちなみにオブジェクト配列は無理みたいですがbyte配列とプリミティブ間ならSystem.BitConverterとかいうものが、プリミティブ配列同士ならSystem.Bufferっていうのがあるみたいですね。どっちにしろ無駄なコピーが発生するみたいで残念ですけど。

2007-01-30追記: この時は配列の実体ばかり気にしていて、付随するプロパティに関心がありませんでしたが、問題になることがあるかも知れません。特にSystem.Array.Length。10000バイトのbyte配列を作り、上記の方法でushort配列として扱っても、その擬似ushort配列のLengthプロパティは10000を返します(Lengthプロパティのgetアクセサがプライベートメンバの値をただ返しているのでしょう)。勿論、インデクサはきちんと値を返してくれます(arrayContainer.UInt8Array[ 0 ]が0x34で、arrayContainer.UInt8Array[ 1 ]が0x12の時、arrayContainer.UInt16Array[ 0 ]は0x1234を返すという事)。プログラマは勿論実際の配列の長さを把握しているでしょうが、byte配列を要求する.NET Frameworkの関数(例えばSystem.IO.Stream.Readなど)に渡す時は、最初にbyte配列として作らないといけなくなるでしょう。

概要: Windows VistaがMSDN Subscriber向けにダウンロード開始されたそうですね(http://www.microsoft.com/japan/msdn/subscriptions/M...

Windows VistaがMSDN Subscriber向けにダウンロード開始されたそうですね(http://www.microsoft.com/japan/msdn/subscriptions/MSJVM/vista_2007office.aspx)。同時にOffice 2007とかWindows Internet Explorer 7とかも公開開始だそうで。IE7正式版はBeta 2の時のような不安定さが何とか取れたみたいですね。その内どこまで実装が進んだのか実験したいところです。

ま、そんなことは割とどうでも良くて、我らが(?).NET Frameworkもバージョン3.0になりました。まだ2.0もろくに使い込んでないのに。また新仕様調べに行かなくてはならないですね。いやまあ、それも割とどうでもいいんですけど。

.NET Framework 3.0の環境構築に必要らしいWindows SDKとやらが1GBもあるのはADSL回線サブスクライバに対する嫌味でしょうか。お陰でインストールする気が萎えてしまいました。これはVista環境に対応させる気がなければ要らないんでしょうか。それに英語版しかありません──、正直な所、英語のドキュメントはHTML 4.01とManaged DirectXのリファレンスだけでお腹いっぱいです。

はぁ。今回はタイトル変更(.NET Claimwork 2.0から.NET Claimwork 3.0)のついでに書いたんですけど、もうちょっと些細なことでも書くようにした方が良さそうでしょうか。XHTML 1.1 Referenceの方もFormsがそろそろ終わりそうとは言え次Tablesですし…

このアーカイブについて

このページには、2006年12月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2006年10月です。

次のアーカイブは2007年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。