<!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>
</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()を使用しました。</p>
	<ol>
		<li>on-screen に Gdiplus::Bitmap, Gdiplus::CachedBitmapを転送する。</li>
		<li>off-screen に Gdiplus::Bitmap, Gdiplus::CachedBitmapを転送する。</li>
		<li>on-screen に GDI::DIB, GDI::DDBを転送する。</li>
		<li>dib-off-screen に GDI::DIB, GDI::DDBを転送する。</li>
		<li>ddb-off-screen に GDI::DIB, GDI::DDBを転送する。</li>
	</ol>
	<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-screen</dt>
			<dd>GetDC(hWnd)で取得できるデバイスコンテキスト。もしくはそれから作成したGdiplus::Graphics。</dd>
			<dt>off-screen</dt>
			<dd>Gdiplus::Bitmapから作成したGdiplus::Graphics。</dd>
			<dt>dib-off-screen</dt>
			<dd>CreateDIBSection()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。</dd>
			<dt>ddb-off-screen</dt>
			<dd>CreateCompatibleBitmap()で作成したビットマップを選択したオフスクリーンのデバイスコンテキスト。</dd>
		</dl>
	</div>
	<h3>転送性能 例１</h3>
	<table border="yes">
		<caption>
			画像:640x480x24, ディスプレイ:1600x1200x32bpp, 400回平均, Celeron 400MHz, nVidiaTNT2, DIMM100
		</caption>
		<thead>
			<tr><th>実験 No.</th><td>Bitmap/DIB [ms]</td><td>CachedBitmap/DDB [ms]</td></tr>
		</thead>
		<tbody align="right">
			<tr><th align="left">1 : GDI+ on      </th><td>19.51	</td><td> 10.43</td></tr>
			<tr><th align="left">2 : GDI+ off     </th><td>12.70	</td><td> 11.55</td></tr>
			<tr><th align="left">3 : GDI on       </th><td>19.91	</td><td>  1.23</td></tr>
			<tr><th align="left">4a: GDI dib 24bpp</th><td>12.46	</td><td>168.08</td></tr>
			<tr><th align="left">4b: GDI dib 32bpp</th><td>19.55	</td><td>135.64</td></tr>
			<tr><th align="left">5 : GDI ddb      </th><td>19.79	</td><td>0.0725</td></tr>
		</tbody>
	</table>
	<h3>転送性能 例２</h3>
	<table border="yes">
		<caption>
			画像:640x480x24, ディスプレイ:1024x768x32bpp, 400回平均, Pentium4 2.0AGHz, RADEON8500LE, DDR266
		</caption>
		<thead>
			<tr><th>実験 No.</th><td>Bitmap/DIB [ms]</td><td>CachedBitmap/DDB [ms]</td></tr>
		</thead>
		<tbody align="right">
			<tr><th align="left">1 : GDI+ on      </th><td>7.256	</td><td>  6.139</td></tr>
			<tr><th align="left">2 : GDI+ off     </th><td>2.576	</td><td>  2.561</td></tr>
			<tr><th align="left">3 : GDI on       </th><td>5.753	</td><td>  0.013</td></tr>
			<tr><th align="left">4a: GDI dib 24bpp</th><td>2.061	</td><td>109.821</td></tr>
			<tr><th align="left">4b: GDI dib 32bpp</th><td>2.580	</td><td> 65.278</td></tr>
			<tr><th align="left">5 : GDI ddb      </th><td>5.744	</td><td> 0.0128</td></tr>
		</tbody>
	</table>
<!-- 
GDI+ ddb   	11.983	8.269
// こちらは描画に成功。ただし、激遅い。
HBITMAP hOffDDB = CreateCompatibleBtimap(w, h, hDC);
HDC hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hOffDDB);
Gdiplus::Graphics g(hMemDC);

// こちらは描画に失敗
HBITMAP hOffDDB = CreateCompatibleBtimap(w, h, hDC);
Gdiplus::Bitmap* bitmap = Bitmap::FromHBITMAP(hOffDDB);
Gdiplus::Graphics g(bitmap);
-->
	<ul>
		<li>Bitmap, DIBの転送速度は同等。転送元画像と転送先画像のフォーマットが一致している場合は高速になる(2, 4a)。</li>
		<li>CachedBitmapはon/offともに高速化に貢献する(1, 2)。</li>
		<li>DDBのon/ddb-offへの転送は、CachedBitmapよりも圧倒的に高速である(3, 5)。</li>
		<li>DDBのDIBセクションへの転送はとても遅い(4a, 4b)。</li>
		<li>最新のマシンはやっぱり速いｗ</li>
	</ul>
</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>
		宿題：
		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>
