<!DOCUMENT HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">

<html>

<head>
<meta http-equiv="Content-Language" content="ja">
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<link rel="stylesheet" type="text/css" href="../../default.css">
<title>GDI/GDI+のビットマップ転送能力</title>
</head>

<body>
<!--#include virtual="../../menu2.html"-->
<div class="contents">
<h4>contents</h4>
<ol>
	<li><a href="#intro">はじめに</a></li>
	<li><a href="#exam">実験</a></li>
	<li><a href="#consider">考察</a></li>
	<li><a href="#appendix">付録：GDI/GDI+の描画能力</a></li>
</ol>
</div>

<h1>GDI/GDI+のビットマップ転送能力</h1>
<div>
	<p>
		GDI+はGDIに比べて10倍ほど遅いという噂がありますが、実際のところを計測しました。
		速度に最も影響するのは、DDB（デバイス依存ビットマップ）の存在でした。
		GDIのDDBは非常に高速ですが、制限も多いようです。
	</p>
	<p style="font-weight:bold">※前回とは実験条件が変わっています。以前の結果は<a href="gdiplus_blt_old.html">こちら</a>。</p>
</div>

<h2 id="intro">はじめに</h2>
<div>
	<p>
		先行研究として、やねうらおさんの<a href="http://www.sun-inet.or.jp/~yaneurao/intensive/diw1.html">超高速描画の謎</a>があります。
		DDBとDirectDrawとの比較をされています。</p>
	<p>
		GDIでは明示的にDIB, DDBを区別して作成できるのに対し、GDI+では基本的にはDIBのみを扱います。
		Gdiplus::CachedBitmapは存在するものの、オフスクリーン描画の対象にすることはできません（CachedBitmapを対象とするGraphicsは作成できない）。
		一方、GDIでは、CreateCompatibleBitmap()で作成したオフスクリーンDDBに、DDBのイメージソースを描画できます。
		こちらは、ビデオメモリ間のみでの転送になるので高速であると考えられます。
	</p>
</div>

<h2 id="exam">実験</h2>
<div>
	<p>
		以下の条件で速度を計測しました。
		転送は、Graphcis::DrawImage()もしくはBitBlt()を使用しました。
		【<a href="GDItest.zip">計測用プログラム</a>】
	</p>
	<div>
	<table border>
		<tr><th>番号</th><th>転送先</th><th>転送元</th></tr>
		<tr><th>1</th><td>ON</td><td>GDI::DIB</td></tr>
		<tr><th>2</th><td>ON</td><td>GDI::DDB</td></tr>
		<tr><th>3</th><td>ON</td><td>Gdiplus::Bitmap</td></tr>
		<tr><th>4</th><td>ON</td><td>Gdiplus::CachedBitmap</td></tr>
		<tr><th>5</th><td>OFF</td><td>GDI::DIB</td></tr>
		<tr><th>6</th><td>OFF</td><td>GDI::DDB</td></tr>
		<tr><th>7</th><td>OFF</td><td>Gdiplus::Bitmap</td></tr>
		<tr><th>8</th><td>OFF</td><td>Gdiplus::CachedBitmap</td></tr>
	</table>
	</div>
	<div style="border:medium double; padding:0.5em;">
		ここでの用語
		<dl>
			<dt>GDI::DIB</dt>
			<dd>GDIのDIBセクション。</dd>
			<dt>GDI::DDB</dt>
			<dd>GDIのDDB（デバイス依存ビットマップ）。</dd>
			<dt>転送先：ON</dt>
			<dd>
				GDIの場合は、GetDC(hWnd)で取得できるデバイスコンテキスト。<br>
				GDI+の場合は、HWND を引数にとるコンストラクタで作成したGdiplus::Graphics。
			</dd>
			<dt>転送先：OFF</dt>
			<dd>
				GDI::DIBの場合は、CreateDIBSection()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。<br>
				GDI::DDBの場合は、CreateCompatibleBitmap()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。<br>
				GDI+の場合は、Gdiplus::Bitmap を引数にとるコンストラクタで作成したGdiplus::Graphics。<br>
				<br>
				※オフスクリーンのフォーマットを転送元のフォーマットに一致させた理由は、異なる場合には必ず遅くなり、実用上意味がないことが<a href="gdiplus_blt_old.html">以前の実験</a>から判明しているためです。
			</dd>
		</dl>
	</div>

	<h3>結果</h3>
	<table border align="left">
		<tr><th>条件</th></tr>
		<tr><td>1 DIB-ON</td></tr>
		<tr><td>2 DDB-ON</td></tr>
		<tr><td>3 Bmp-ON</td></tr>
		<tr><td>4 Cache-ON</td></tr>
		<tr><td>5 DIB-OFF</td></tr>
		<tr><td>6 DDB-OFF</td></tr>
		<tr><td>7 Bmp-OFF</td></tr>
		<tr><td>8 Cache-OFF</td></tr>
	</table>

	<table border align="left">
		<caption align="bottom">
			<em>A</em>
			画像:640x480x24, 解像度:1600x1200x32bpp, 500回平均, AthronXP 1800+, nVidia GeForce2MX, DDR266
		</caption>
		<thead>
			<tr><th>番号</th><th>１回あたり [msec]</th></tr>
		</thead>
		<tbody align="right">
			<tr><th>1</th><td>6.64</td></tr>
			<tr><th>2</th><td>0.00890</td></tr>
			<tr><th>3</th><td>9.23</td></tr>
			<tr><th>4</th><td>6.87</td></tr>
			<tr><th>5</th><td>3.07</td></tr>
			<tr><th>6</th><td>0.00685</td></tr>
			<tr><th>7</th><td>4.97</td></tr>
			<tr><th>8</th><td>4.03</td></tr>
		</tbody>
	</table>
	<table border align="left">
		<caption align="bottom">
			<em>B</em>
			最初にソース用デバイスコンテキストを作成しておき、BitBlt()ごとに再作成しない。
			他はAと同じ。
		</caption>
		<thead>
			<tr><th>番号</th><th>１回あたり [msec]</th></tr>
		</thead>
		<tbody align="right">
			<tr><th>1</th><td>6.61</td></tr>
			<tr><th>2</th><td>0.00131</td></tr>
			<tr><th>3</th><td>9.23</td></tr>
			<tr><th>4</th><td>6.85</td></tr>
			<tr><th>5</th><td>3.03</td></tr>
			<tr><th>6</th><td>0.00121</td></tr>
			<tr><th>7</th><td>4.97</td></tr>
			<tr><th>8</th><td>4.03</td></tr>
		</tbody>
	</table>
	<table border>
		<caption align="bottom">
			<em>C</em>
			画像：800x600x24
			他はAと同じ。
		</caption>
		<thead>
			<tr><th>番号</th><th>１回あたり [msec]</th></tr>
		</thead>
		<tbody align="right">
			<tr><th>1</th><td>10.0</td></tr>
			<tr><th>2</th><td>0.00697</td></tr>
			<tr><th>3</th><td>13.9</td></tr>
			<tr><th>4</th><td>10.1</td></tr>
			<tr><th>5</th><td>4.53</td></tr>
			<tr><th>6</th><td>0.00686</td></tr>
			<tr><th>7</th><td>7.37</td></tr>
			<tr><th>8</th><td>5.97</td></tr>
		</tbody>
	</table>

	<div style="clear:both">
	<ul>
		<li>Bitmap, DIBの転送速度はほぼ同等か、Bitmapが劣る。(1, 3, 4, 7)</li>
		<li>CachedBitmapはon/offともに高速化に貢献する。(3, 4, 7, 8)</li>
		<li>ON/OFFともに、DDBはCachedBitmapよりも圧倒的に高速である。(2, 4, 6, 8)</li>
		<li>デバイスコンテキストの再作成はそれほど大きなコストがかからない。(A, B)</li>
		<li>DDBはサイズが大きくなっても速度が遅くならない。(A, C)</li>
	</ul>
	</div>
</div>

<h2 id="consider">考察</h2>
<div>
	<p>
		GDI+とGDI::DDBを比較するならば、10倍どころではなく遅いようです。
		ちょっと古いマシンでは、GDI+で30fps出すのは不可能です。
		CachedBitmapをオフスクリーンで使用した場合でもDDBの速度にはかないません。
		高速性が求められるならば、GDI+ではなくGDI（のDDB）を使用するべきでしょう。
	</p>
	<p>
		<a href="#appendix">98/2000以降に追加されたGDI関数</a>を使えば、GDI+の多彩な描画ルーチンの一部はカバーできますが、
		これらの関数をサポートしているハードウェアは少ないようです。
		その場合、例えばMaskBlt()をソフトウェア的に行うよりも、BitBlt()を2回使ったほうが遥かに高速です。
		つまり、GDIを使う場合、BitBlt()しか信用できず、その結果「単純な転送」「単色カラーキー付き転送」くらいしかハードウェアアクセラレーションを期待できません（ただし、とんでもなく高速ですが）。
		そのような制限を受けるくらいならば、初めから全部ソフトウェアで処理するGDI+を使ったほうがいいかもしれません。
	</p>
	<p>
		実験データが不足していますので、計測結果を報告していただければ幸いです。
		計測用プログラムは<a href="GDItest.zip">こちら</a>。
		Windows2000以降ならば、メッセージボックス上でCtrl-Cをすることでテキストがクリップボードにコピーされます。
	</p>
	<p>
		宿題：
		DDBに対するGraphicsは作成できるのか？
		BltBlt()以外の転送関数の性能比較。
	</p>
</div>

<h2 id="appendix">付録：GDI/GDI+の描画能力</h2>
<div>
	<h3>98/2000 以降で使用できるGDI関数</h3>
	<dl>
		<dt>GradientFill()</dt>
		<dd>グラデーションのかかった三角形もしくは矩形を描画する。<br>
			GDI+ : LinearGradientBrush, PathGradientBrush に上位機能。</dd>
		<dt>AlphaBlend()</dt>
		<dd>ソース全体もしくはピクセルごとのα値に対応したStretchBlt()。<br>
			GDI+ : ImageAttributes::SetColorMatrix(), Graphics::DrawImage() に上位機能。</dd>
		<dt>MaskBlt()</dt>
		<dd>マスクビットマップを使用して2種類のラスタオペレーションを行う。
			いわゆるカラーキー付き転送が可能。<br>
			GDI+ : 互換機能なし。</dd>
		<dt>PlgBlt()</dt>
		<dd>平行四辺形に変形描画する。マスク指定も可能。<br>
			GDI+ : Graphics::DrawImage() に上位機能。</dd>
		<dt>TransparentBlt()</dt>
		<dd>カラーキーを指定できるStretchBlt()。
			Windows98ではリソースリークが発生するので注意！<br>
			GDI+ : ImageAttributes::SetColorKey() に上位機能。</dd>
	</dl>

	<h3>GDI+独自のイメージ描画機能</h3>
	<dl>
		<dt>Graphics::SetTransform()</dt>
		<dd>2次元同次変換行列を用いた描画。</dd>
		<dt>ImageAttributes::SetRemapTable()</dt>
		<dd>指定した色を、もうひとつの指定した色に置き換えながら描画する。</dd>
		<dt>ImageAttributes::SetColorKey()</dt>
		<dd>透過色を範囲で指定し、範囲内の色を透過して描画する。</dd>
		<dt>ImageAttributes::SetColorMatrix()</dt>
		<dd>同次変換行列を使用してARGB値を変換する。</dd>
		<dt>ImageAttributes::SetGamma()</dt>
		<dd>ガンマ値を指定する。</dd>
		<dt>ImageAttributes::SetOutputChannel()</dt>
		<dd>CMYK (cyan, magenta, yellow, black) 変換する。</dd>
		<dt>ImageAttributes::SetThreshold()</dt>
		<dd>色が指定した値を超えたか否かで 0 または 255 に変換する。</dd>
		<dt>ImageAttributes::SetWrapMode()</dt>
		<dd>描画先サイズがイメージサイズより大きな場合のラップ（回り込み）モードを指定する。</dd>
	</dl>
</div>


<hr>
<!--#config timefmt="%Y/%m/%d"-->
<p align="right"><!--#echo var='LAST_MODIFIED'--></p>

</body>
</html>
