トップページ > 記事閲覧
文字列描画が遅い
名前:しいな 日時: 2017/10/17 02:07

.NET版のライブラリを使用しています。 CreateFontToHandleで作成したハンドルを使ってDrawStringToHandleや DrawExtendStringToHandleで文字列を描画する際に,非常に時間がかかっています。 2種類のフォントでそれぞれ10〜15文字を描画するだけなのですが, Core i7-4770のPCで50〜100ms,Windows7世代のPCだと1000〜2000msかかります。 (フォントサイズは60〜80) フォントはWindowsに標準で入っているものを使用しています。 同じ文字を2回目以降に描画する際には高速に描画される(キャッシュに入る?)のですが, 特定の文字だけを描画するわけではないので,困っています。 何か考えられることはありますでしょうか。 またはフォント全体をあらかじめキャッシュに入れるようなことができるのでしょうか。
メンテ

Page: 1 |

Re: 文字列描画が遅い ( No.1 )
名前:管理人 日時:2017/10/18 00:49

お察しの通りフォントの文字は 1回目の描画では低速な WindowsAPI の文字画像取得機能を使用して 文字の画像を取得しテクスチャに転送してから描画するので、同じ文字を2回以上描画する場合は 2回目以降は高速に描画できますが、1回目は遅くなります > Core i7-4770のPCで50〜100ms,Windows7世代のPCだと1000〜2000msかかります。 > (フォントサイズは60〜80) それは少し時間がかかり過ぎのような気がします… ( 1000〜2000ms とは、つまり 1〜2秒ということですよね? ) よろしければ以下の情報について教えていただけないでしょうか? m(_ _;m 1. CreateFontToHandle に渡した引数( フォント名、サイズ、太さ、フォントタイプ ) 2. 描画された 10〜15文字 の文字列 3. 2回目以降の描画にかかる時間 > またはフォント全体をあらかじめキャッシュに入れるようなことができるのでしょうか。 フォント全体をキャッシュに入れることはできません フォントサイズが 60〜80 となりますと、すべての文字をキャッシュしようとすると 漢字なども含まれるフォントの場合容量がかなり大きくなるので、あまり現実的ではありません 1回目の描画を2回目以降の描画ほど高速にすることはできませんが、DXライブラリのパッケージに付属している CreateDXFontData というツールを使用してフォントデータファイルを作成して、それを LoadFontDataToHandle で読み込んで使用すると、通常の文字描画よりは高速に1回目の文字描画を 行うことができます( ただし、この方法はフォントの画像情報をファイルで持つことになるので ソフトを配布する際にフォントのライセンスについて対応する必要が発生します… ) その他には実際に描画するタイミングの前( Now Loading 画面などのタイミング )に GetDrawStringWidthToHandle にキャッシュに入れたい文字を引数で渡して呼ぶことで文字をキャッシュに入れ、 実際に描画する際は『2回目以降の描画速度』で描画するという方法もあります
メンテ
Re: 文字列描画が遅い ( No.2 )
名前:しいな 日時:2017/10/18 21:17

ご返答ありがとうございます。 いろいろ検証してみました。 結果的には,文字にエッジを付けると描画が非常に遅くなるということでした。 アンチエイリアスは真っ先に外してみていたのですが,エッジの方はそれほど重い処理だと 思っていなかったので,盲点でした。 >1. CreateFontToHandle に渡した引数( フォント名、サイズ、太さ、フォントタイプ )  [Meiryo UI] サイズ:80 太さ:標準 タイプ:DX_FONTTYPE_ANTIALIASING_EDGE_4X4  [Times New Roman] サイズ:60 太さ:標準 タイプ:DX_FONTTYPE_ANTIALIASING_EDGE_4X4 >2. 描画された 10〜15文字 の文字列  「おはようございます」「Good morning」など >3. 2回目以降の描画にかかる時間  1ms未満 文字のエッジを外したところ,Windows7世代のPC(Direct3D 9EX/PS 3.0)でも20ms程度で 描画することができました。 >1回目の描画を2回目以降の描画ほど高速にすることはできませんが、DXライブラリのパッケージに付属している >CreateDXFontData というツールを使用してフォントデータファイルを作成して、それを >LoadFontDataToHandle で読み込んで使用すると、通常の文字描画よりは高速に1回目の文字描画を >行うことができます( ただし、この方法はフォントの画像情報をファイルで持つことになるので >ソフトを配布する際にフォントのライセンスについて対応する必要が発生します… ) > >その他には実際に描画するタイミングの前( Now Loading 画面などのタイミング )に >GetDrawStringWidthToHandle にキャッシュに入れたい文字を引数で渡して呼ぶことで文字をキャッシュに入れ、 >実際に描画する際は『2回目以降の描画速度』で描画するという方法もあります これも考えたのですが,表示する文字列の内容が事前にわからないのでちょっと難しいですね。 エッジを付けても高速に描画できる条件のようなものがあるのでしょうか。 背景の関係でエッジを付けた方が文字がきれいに見えるので,できればエッジ付きにしたいのですが。
メンテ
Re: 文字列描画が遅い ( No.3 )
名前:管理人 日時:2017/10/19 00:43

ご返答ありがとうございます なるほど、エッジ付きが重いのですね… エッジを付ける処理は若干の高速化の余地はありますが、どうしても重い処理なので DXライブラリ内部のエッジを付ける処理を改良することによる高速化には限界があります ( 特にアンチエイリアス付きフォントタイプの場合は CPU でアルファブレンドの処理も  行っているので尚更重くなります… ) 一番簡単な高速化の方法としてはエッジを DrawStringToHandle を何回も呼ぶことで実現してしまう方法です 例えば FontHandle = CreateFontToHandle( "Meiryo UI", 80, 3, DX_FONTTYPE_ANTIALIASING_EDGE_4X4 ) ; ↑こちらの処理で作成したフォントによる緑色のエッジ付きフォントの描画↓ですが DrawStringToHandle( 0, 0, "おはようございます", GetColor( 0,255,0 ), FontHandle, GetColor( 0,255,0 ) ) ; エッジなしのこちらのフォント↓でも FontHandle = CreateFontToHandle( "Meiryo UI", 80, 3, DX_FONTTYPE_ANTIALIASING_4X4 ) ; ↓こうすることでほぼ同じ見た目の緑のエッジつきの文字列が描画できます DrawStringToHandle( 1, 0, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( 0, 1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( -1, 0, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( 0, -1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( 1, 1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( -1, 1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( 1, -1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( -1, -1, "おはようございます", GetColor( 0,255,0 ), FontHandle ) ; DrawStringToHandle( 0, 0, "おはようございます", GetColor( 255,255,255 ), FontHandle ) ; エッジ付きフォントによる2回目以降の描画とエッジ無しフォントによる自前エッジ付き描画とでは当然後者の方が低速ですが、 1回目の描画についてはエッジ無しフォントによる自前エッジ付き描画の方が圧倒的に高速です よろしければお試しください m(_ _;m
メンテ
Re: 文字列描画が遅い ( No.4 )
名前:しいな 日時:2017/10/20 23:05

ご返答ありがとうございます。 >なるほど、エッジ付きが重いのですね… >エッジを付ける処理は若干の高速化の余地はありますが、どうしても重い処理なので >DXライブラリ内部のエッジを付ける処理を改良することによる高速化には限界があります >( 特にアンチエイリアス付きフォントタイプの場合は CPU でアルファブレンドの処理も > 行っているので尚更重くなります… ) 感覚的にはそんなに重い処理とは思えないのですが,そういうことであれば仕方ありませんね。 >一番簡単な高速化の方法としてはエッジを DrawStringToHandle を何回も呼ぶことで実現してしまう方法です これは代替手段としてやってみていました(私が実装したのは4方向だけでしたが)。 この方法であれば実用的な速度で動作することは確認できています。 通常のエッジ付き文字の描画が極端に遅くなるかどうかを,簡単に判断できないものでしょうか。 Direct3Dやピクセルシェーダのバージョンなどで判断できるとありがたいのですが。
メンテ
Re: 文字列描画が遅い ( No.5 )
名前:管理人 日時:2017/10/21 00:53

> 通常のエッジ付き文字の描画が極端に遅くなるかどうかを,簡単に判断できないものでしょうか。 > Direct3Dやピクセルシェーダのバージョンなどで判断できるとありがたいのですが。 処理が遅くなるかどうかは Direct3D やピクセルシェーダのバージョンは関係なく、 単純に CPU の性能次第となりますので、極端に遅い環境はそれだけ CPU の性能が低い( CPU の コア数は関係ないので、厳密にはシングルスレッドの処理性能が低い )ということになります 事前に確かめる方法ですが、GetDrawStringWidthToHandle で文字列の描画幅を取得する場合も キャッシュに文字画像を入れる処理が行われますので、DX_FONTTYPE_ANTIALIASING_EDGE_4X4 タイプで 作成したフォントハンドルを使用して適当な文字列で GetDrawStringWidthToHandle を実行して、 その処理に掛かった時間が長いか短いかで実際の描画時にも遅くなってしまうかどうかを判断することができます
メンテ
Re: 文字列描画が遅い ( No.6 )
名前:しいな(解決) 日時:2017/10/24 23:32

>処理が遅くなるかどうかは Direct3D やピクセルシェーダのバージョンは関係なく、 >単純に CPU の性能次第となりますので、極端に遅い環境はそれだけ CPU の性能が低い( CPU の >コア数は関係ないので、厳密にはシングルスレッドの処理性能が低い )ということになります なるほど CPU側の問題なのですね。了解しました。 検証してみましたが,結局どの環境でもエッジを自前で描いた方が(1回目は)速くなるようなので, ラッパーで対応することにしました。 いろいろとアドバイスありがとうございました。 ※文字列描画関連についてですが,GetDrawStringCharInfo系は.NET版では使えないのですね。  何か理由があるのでしょうか。できれば使用したいのですが。
メンテ
Re: 文字列描画が遅い ( No.7 )
名前:管理人 日時:2017/10/25 00:37

> ※文字列描画関連についてですが,GetDrawStringCharInfo系は.NET版では使えないのですね。 >  何か理由があるのでしょうか。できれば使用したいのですが。 .NET版は C++用のファイルである DxLib.h や DxDirectX.h などから DxLib.dll や DxLib.cs などの .NET版の作成に必要なファイルを自動生成するツールを使用して作成しているのですが、 GetDrawStringCharInfo のように結果を代入する構造体配列のアドレスを引数として渡す関数は .NET と相性が悪く、手作業で C#用のコードを書かなければならないので基本的に対応していません GetDrawStringCharInfo は機能的には1文字の情報を取得する GetFontCharInfo の文字列版といったものなので、 申し訳ありませんが文字列に含まれる文字の数だけ GetFontCharInfo を呼び出すことで代用してください m(_ _;m
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存