3.1 レンダリングパイプラインおよびそのプロパティーの変更
3.1.1 Solaris OS および Linux:デフォルト X11 パイプラインの使用
3.1.1.1 X11 ピックスマップの使用に関係するプロパティー
X11 パイプラインでの共有メモリーピックスマップの使用の無効化/強制
3.1.1.2 X11 パイプラインでの MIT 共有メモリー拡張の使用
X サーバーや Java 2D で使用可能な共有メモリーの増加
3.1.1.3 SPARC 上の Solaris OS:特定の構成での DGA の使用
3.1.2 SPARC 上の Solaris OS:Java 2D で使用されるデフォルトビジュアルの変更
3.1.3 Windows OS:デフォルトの DirectDraw/GDI パイプラインの使用
3.1.3.2 DirectDraw パイプラインの使用の強制
3.1.3.4 DirectDraw Blit 操作の無効化
3.1.4 Windows OS:Direct3D パイプラインの使用 (フルスクリーンモード)
3.1.4.3 Direct3D パイプラインのレンダリングの問題の診断
3.1.5 OpenGL パイプラインの使用 (SolarisOS、Linux、および Windows)
3.1.5.5 レンダリングおよびパフォーマンスの問題の診断
3.2.2 Java 2D プリミティブトレースを使用した非高速化レンダリングの検出と回避
3.2.3.1 高速化レンダリングと非高速化レンダリングの混在
3.2.3.3 ヒープベースのデスティネーション表面 (BufferedImage) の使用
getDataBuffer() によるピクセルへの直接アクセス
3.2.4 ソフトウェアのみのレンダリングのパフォーマンス改善
3.2.4.1 最適化されたサポートのあるイメージ型やイメージ操作の使用
この章では、Java 2D API で発生する可能性のあるもっとも一般的な問題のいくつかをトラブルシューティングするための情報と指針を提供します (次の各セクションを参照)。
Java 2D プロパティーのサマリーについては、「付録 A: Java 2D のプロパティー」を参照してください。
Java 2D FAQ も参照してください。
Java 2D では一連のパイプラインが使用されますが、これは大まかに、プリミティブをレンダリングするためのさまざまな方法として定義できます。これらのパイプラインは次のとおりです。
X11 パイプライン: Solaris OS および Linux でのデフォルトです
OpenGL パイプライン: Solaris OS および Linux、ならびに Windows での代替です
DirectDraw/GDI パイプライン: Windows でのデフォルトです
Direct3D パイプライン: Windows での代替です
別のパイプラインを選択したりパイプラインのプロパティーを操作したりすれば、問題の原因を特定できたり、しばしば回避方法を発見できたりする可能性があります。
Java 2D の問題のトラブルシューティングに役立つ可能性のある手順を次に示します。
構成で使用されているデフォルトパイプラインを確認します。
パイプラインを別のものに変更するか、デフォルトパイプラインのプロパティーを変更します。
問題が解消すれば、回避方法が見つかったことになります。
問題が継続する場合は、別のプロパティーを変更するか別のパイプラインに変更してみます。
このセクションでは、Java 2D のパイプラインやそのプロパティーを変更する方法を、次のオペレーティングシステムごとに説明します。UNIX (Solaris OS と Linux) および Windows。
Solaris OS および Linux 上のデフォルト X11 パイプラインについては、次の情報を提供します。
X11 ピックスマップの使用に関係するプロパティー
X11 パイプラインでのピックスマップの使用の無効化
X11 パイプラインでの共有メモリーピックスマップの使用の無効化/強制
X11 パイプラインでの共有メモリー拡張の使用
X サーバーや Java 2D で使用可能な共有メモリーの増量
X11 パイプラインでの共有メモリー拡張の使用の無効化
SPARC 上の Solaris OS:特定の構成での DGA の使用
画面へのレンダリングで DGA 拡張が使用されているかどうかの検出
DGA の使用に起因する典型的な問題
Java 2D での DGA の使用の制御
SPARC 上の Solaris OS:Java 2D で使用されるデフォルトビジュアルの変更
Windows 上のデフォルト DirectDraw/GDI パイプラインについては、次の情報を提供します。
DirectDraw の使用の無効化
DirectDraw パイプラインの使用の強制
組み込みパントメカニズムの無効化
DirectDraw Blit 操作の使用の無効化
Windows 上の代替 Direct3D パイプラインについては、次の情報を提供します。
Direct3D パイプラインの無効化
Direct3D パイプラインの使用の強制
Direct3D パイプラインのレンダリングの問題の診断
またこのセクションでは、Solaris OS、Linux、および Windows 上で OpenGL パイプラインを代替パイプラインとして使用する情報も提供します。
UNIX プラットフォームのデフォルトパイプラインは X11 パイプラインです。このパイプラインでは、画面へのレンダリングや、特定タイプのオフスクリーンイメージ (VolatileImage など) または「互換性のある」イメージ (GraphicsConfiguration.createCompatibleImage() メソッドで作成されたイメージ) へのレンダリングを行う際に、X プロトコルが使用されます。そのようなタイプのイメージは、X11 ピックスマップ内に格納すればパフォーマンスを改善できます (特にリモート X サーバーの場合)。
さらに、Java 2D では特定の場合に、X サーバー拡張 (MIT X 共有メモリー拡張、ダイレクトグラフィックスアクセス拡張、BufferStrategy API 使用時のダブルバッファリング用ダブルバッファー拡張など) が使用されます。
構成によっては、追加パイプラインの OpenGL パイプラインによってパフォーマンスが向上することもあります。
Java 2D ではデフォルトで、特定タイプのオフスクリーンイメージの格納やキャッシングに X11 ピックスマップが使用されます。ピックスマップに格納できるイメージのタイプは、次のものだけです。
不透明イメージ (この場合、ColorModel.getTransparency() から Transparency.OPAQUE が返される)
1 ビット透明イメージ (別名「スプライト」、Transparency.BITMASK)
イメージの格納にピックスマップを使用するメリットは、ドライバの判断でフレームバッファーのビデオメモリー内に配置できる点にあります (そうした場合、これらのピックスマップを画面や別のピックスマップにコピーする際の速度が改善される)。
ピックスマップを使用すれば、通常はパフォーマンスが向上します。ただし、場合によっては逆になることもあります。そのような場合は通常、X プロトコルを使って実行できない操作 (アンチエイリアス、アルファ合成、単純な移動変換よりも複雑な変換など) が使用されています。
これらの操作については、X11 パイプラインは組み込みソフトウェアレンダラを使ってレンダリングを行う必要があります。これにはほとんどの場合、ピックスマップの内容を読み取って (リモート X サーバーの場合はネットワーク経由で) システムメモリーに格納し、レンダリングを実行したあと、それらのピクセルを元のピックスマップに送信するという操作が含まれます。そのような操作ではパフォーマンスが大幅に低下する可能性があります (特に X サーバーがリモートの場合)。
Java2D でのピックスマップの使用を無効にするには、Java VM に次のプロパティーを渡します。-Dsun.java2d.pmoffscreen=false。
ピックスマップからのピクセルの読み取りを必要とするそのような操作の、全体のパフォーマンスに対する影響を最小限に抑えるため、X11 パイプラインは読み取り頻度の高いイメージの格納に共有メモリーピックスマップを使用します。共有メモリーピックスマップを使用できるのは、ローカル X サーバーの場合だけです。
共有メモリーピックスマップを使用するメリットは、X11 プロトコルをバイパスしてパイプライン内のピクセルにパイプラインから直接アクセスできる点にあり、その結果、パフォーマンスが向上します。
イメージはデフォルトでは通常の X サーバーピックスマップ内に格納されますが、あとでそのようなイメージからの過剰な読み取りがパイプラインによって検出された場合には、共有メモリーピックスマップにイメージを移動できます。イメージのコピー回数が十分な数に達したら、元のサーバーピックスマップにイメージを移動できます。
このパイプラインでは、共有メモリーピックスマップの使用を制御する方法が 2 つ用意されています。それらを無効にする方法と、すべてのイメージが常に共有メモリーピックスマップに強制的に格納されるようにする方法です。
共有メモリーピックスマップを無効にするには、J2D_PIXMAPS 環境変数を server に設定します。これが、リモート X サーバーの場合のデフォルトになります。
すべてのピックスマップが強制的に共有メモリー内に作成されるようにするには、J2D_PIXMAPS を shared に設定します。
まず共有メモリーピックスマップの強制を試してください。多くの場合でパフォーマンスが改善されます。ただし、特定のビデオボード/ドライバ構成では、レンダリングアーティファクトやクラッシュを回避するために、共有メモリーピックスマップの無効化が必要となることがあります。
Java 2D の X11 パイプラインではデフォルトで、MIT 共有メモリー拡張 (MIT SHM) が使用されます。この拡張を使えば、クライアント (Java アプリケーション) と X サーバー間のデータ交換の速度が向上するため、Java アプリケーションのパフォーマンスが大幅に改善される可能性があります。
Solaris OS リリース 8 以前ではときどき、システム (および特に X サーバー) で使用可能な共有メモリーの量を増やす必要がありましたが、これは、デフォルトが低すぎてレンダリングのパフォーマンスが低下することがあったからです。共有メモリーや共有メモリーセグメントの量を増やすと、パフォーマンスが向上する可能性があります。
Solaris OS でデフォルト設定を変更するには、/etc/system ファイルを編集し、shmsys:shminfo_* 設定を次の例のように変更します。これは、Solaris 9 OS 以降では不要です。
set shmsys:shminfo_shmmax=10000000 set shmsys:shminfo_shmni=200 set shmsys:shminfo_shminfo=150
Linux でこの設定を構成するには、/proc/sys/kernel/shm* ファイルを編集します。
古い X サーバーや共有メモリー拡張で問題 (クラッシュやレンダリングアーティファクトなど) が発生する場合、拡張を無効にできると便利です。MIT SHM の使用を無効にするには、J2D_USE_MITSHM 環境変数を false に設定します。
SPARC ハードウェアでは、フレームバッファーが Sun の DGA (ダイレクトグラフィックスアクセス) X サーバー拡張をサポートしていて、フレームバッファーにアクセスするための対応するモジュールが Java 2D に含まれている場合、画面へのレンダリングに DGA が使用されます。
オフスクリーンイメージはすべて Java ヒープメモリー内に存在しており、それらへのレンダリングには Java 2D のソフトウェア専用レンダリングパイプラインが使用されます。これは、オフスクリーンイメージに X11 ピックスマップが使用される通常の UNIX 構成とは異なっています。
画面へのレンダリング時に DGA 拡張が使用されているかどうかを検出するには、何らかのレンダリングを行うか GUI を表示する任意の Java アプリケーションを実行し、そのアプリケーション起動時に /tmp/wg* ファイルが作成されたかチェックします。アプリケーションを終了し、ファイルが削除されたことを確認します。その場合、このシステムでは Java 2D が DGA を使用しています。
DGA ではフレームバッファーのビデオメモリーに直接アクセスできるため、典型的な問題として、ウィンドウ境界の外側の破損、完全なシステム、X サーバーロックアップなどが挙げられます。
DGA が使用されていることが確認できた場合、最初に試すべきことは、その無効化です。これを行うには、NO_J2D_DGA 環境変数を true に設定します。するとデフォルトの UNIX パスで強制的に、画面へのレンダリングに X11 のみが使用され、オフスクリーンイメージの高速化にピックスマップが使用されるようになります。
場合によっては、ピックスマップの使用を有効にしつつ、DGA も画面へのレンダリングに使用したほうが有利なことがあります。オフスクリーンイメージの高速化にピックスマップの使用を強制するには、アプリケーションの起動時に次のプロパティーを設定します。-Dsun.java2d.pmoffscreen=true。
SPARC プラットフォームの特定のビデオボードでは、複数のビジュアルが X サーバーから使用できます。Java 2D はデフォルトで最適なビジュアルを選択しようとしますが、この「最適」というのは通常、ビットの深さがより深いビジュアルを指します。たとえば一部の Solaris OS リリースでは、デフォルト X11 ビジュアルは 8 ビット疑似カラーですが、24 ビットのビジュアルも使用できます。そのような場合、Java 2D は、Java ウィンドウのデフォルトとして 24 ビットトゥルーカラービジュアルを選択します。
別のビジュアルに対応する GraphicsConfiguration オブジェクトで Java トップレベルウィンドウを作成することも可能ですが、場合によっては、代わりに別のデフォルトビジュアルを Java に使用させる必要が生じることがあります。これを行うには、FORCEDEFVIS 環境変数を設定します。これは、true に設定してデフォルト X サーバービジュアルの使用を (たとえ最適でなくても) 強制することができる一方で、xdpyinfo などのツールで報告されたビジュアル ID に対応する 16 進数に設定することもできます。
X サーバーのデフォルトビジュアルを確認するには、xdpyinfo コマンドを実行し、default visual id のフィールドを確認します。
Windows プラットフォームのデフォルトパイプラインは、DirectDraw パイプラインと GDI パイプラインを組み合わせたものであり、DirectDraw パイプラインで実行される操作もあれば、GDI パイプラインで実行される操作もあります。高速化されたオフスクリーンやオンスクリーン表面へのレンダリングには、DirectDraw と GDI の API が使用されます。
Java SE 6 リリース以降では、ドライバが要件を満たしていれば、アプリケーションがフルスクリーンモードに入るときに新しい Direct3D パイプラインを使用できます。Direct3D パイプラインで発生する可能性のある問題として、レンダリングアーティファクト、クラッシュ、パフォーマンス関係の問題などが挙げられます。
構成によっては、追加パイプラインの OpenGL パイプラインによってパフォーマンスが向上することもあります。
DirectDraw が無効になると、GDI ですべての操作が実行されます。DirectDraw の使用を無効にするには、次のフラグを指定します。-Dsun.java2d.noddraw=true。この場合、オフスクリーンイメージはすべて Java ヒープ内に作成され、それらへのレンダリングはデフォルトソフトウェアパイプラインで行われます。オンスクリーンレンダリングや画面へのオフスクリーンイメージのコピーはすべて、GDI を使って実行されます。
何らかの理由でパイプラインがデフォルトで無効化された場合にそれを有効にするには、-Dsun.java2d.noddraw=false フラグを VM に指定します。
ただし通常は、そもそも無効にされた理由が存在するので、強制しないほうが得策です。
一般に、DirectDraw パイプラインはオフスクリーン表面をフレームバッファーのビデオメモリー内に配置しようとしますが、これにより、それらの表面を画面やほかの高速化された表面にコピーする操作が高速化されるほか、特定のグラフィックス操作のハードウェア高速化レンダリングが可能となります。
ただし、パイプラインが DirectDraw API を使って実行できない操作 (アルファ合成、変換、アンチエイリアスなどを使用する操作) については、ソフトウェアパイプラインを使ってレンダリングが実行されます。これは場合によっては、VRAM 内に存在するデスティネーション表面のピクセルを読み取ってシステムメモリーに格納する必要があることを意味しますが、それは非常にコストの高い操作です。
VRAM ベースの表面への非高速化レンダリングの影響を限定するために、パントメカニズムが存在します。このメカニズムは、読み取り頻度が高いことが検出された表面をシステムメモリーに移動します。表面のコピー回数が十分な数に達したと判断されると、表面が元のビデオメモリーに昇格される可能性があります。
特定のビデオボード/ドライバの組み合わせでは、システムメモリーベースの DirectDraw 表面がレンダリングアーティファクトやその他の問題の原因となることが知られています。DirectDraw パイプラインには、システムメモリー表面が使用されないようにパントメカニズムを無効にするための方法が用意されています。
組み込み表面パントメカニズムを無効にするには、次のフラグを Java VM に指定します。-Dsun.java2d.ddforcevram=true。この場合、ソフトウェアループが操作ごとに VRAM からピクセルを読み取る可能性があるため、パフォーマンスが低下する可能性があります。その場合は、DirectDraw パイプラインの無効化 (上を参照) を検討できます。
Blit 操作 (Bit Block Transfer) では 2 つのビットマップパターンが合成されます。この操作は基本的に、Graphics.drawImage() API の呼び出しに対応します。
場合によっては、DirectDraw Blit 操作を無効にすることでレンダリングの問題を回避できます。代わりに GDI Blit が使用されます。この場合、パフォーマンスが低下する可能性があります。代わりに DirectDraw パイプラインの無効化を検討してください。
DirectDraw Blit 操作の使用を無効にするには、パラメータ -Dsun.java2d.ddblit=false を Java VM に渡します。
Java SE 6 リリース以降の Direct3D パイプラインでは、レンダリングに Direct3D API が使用されます。フルスクリーンモードではこのパイプラインがデフォルトで有効にされます (ドライバが、必要な機能とそのレンダリング品質レベルをサポートしている場合)。
Java SE 5 とそれよりあとのリリースの両方で、Direct3D パイプラインを有効にしたりその使用を強制したりできます (下のサブセクションを参照)。
アルファ合成、アンチエイリアス、変換などのレンダリング操作を大量に使用するアプリケーションでは、Direct3D パイプラインを有効にすることを検討してください。
ただし、このパイプラインをアプリケーションで有効にすることを決定する際には注意してください。たとえば、一部の組み込みビデオチップセット (大部分のノートブックで使用されているもの) は、たとえ Java 2D パイプラインの品質要件を満たしていても、Direct3D 使用時には良好なパフォーマンスを示しません。
一部の古いビデオボード/ドライバの組み合わせでは、Direct3D パイプラインで問題 (レンダリングとパフォーマンスの両方) が発生することが知られています。そのような場合に Java SE 5 以降のリリースでパイプラインを無効にするには、パラメータ -Dsun.java2d.d3d=false を Java VM に渡すか、J2D_D3D 環境変数を false に設定します。
Java SE 5 以降のリリースで、ウィンドウモードとフルスクリーンモードの両方で Direct3D パイプラインを有効にするには、パラメータ -Dsun.java2d.d3d=true を使用するか、J2D_D3D 環境変数を true に設定します。パイプラインが有効にされるのは、ドライバが最小限必要な機能をサポートしている場合だけです。
Java SE 6 リリースでは、さまざまな Direct3D ラスタライザを強制することで、いくつかのレンダリングの問題 (欠落したピクセルや異常なレンダリングなど) を診断できます。J2D_D3D_RASTERIZER 環境変数を次のいずれかに設定します。ref、rgb、hal、tnl。
これらのラスタライザの説明については、Direct3D のドキュメントを参照してください。デフォルトでは、通知された機能に基づいて最適なラスタライザが選択されます。具体的には、ref ラスタライザでは、Microsoft 製のリファレンス Direct3D ラスタライザの使用が強制されます。このラスタライザでレンダリングの問題が再現できない場合、その問題はほぼ間違いなくビデオドライバのバグです。
rgb ラスタライザが使用可能なのは、Direct3D SDK がインストールされている場合だけです。この SDK は Microsoft Game Technologies Center から取得できます。
Direct3D パイプラインのテキストレンダリングでパフォーマンスや品質の問題が発生する場合、Direct3D パイプラインのグリフキャッシュで、デフォルトのアルファテクスチャーの代わりに ARGB テクスチャーを使用することを強制できます。これを行うには、J2D_D3D_NOALPHATEXTURE 環境変数を true に設定します。
OpenGL パイプラインは、J2SE 5.0 リリースではじめて Solaris OS、Linux、および Windows 上で使用可能になりました。この代替パイプラインでは、VolatileImage、BufferStrategy API で作成されたバックバッファー、および画面へのレンダリング時に、ハードウェア高速化されたクロスプラットフォームの OpenGL API が使用されます。
このパイプラインはデフォルト (X11 または GDI/DirectDraw) のパイプラインに比べ、特定のアプリケーションでパフォーマンス上の大きな利点を提供できます。アルファ合成、アンチエイリアス、変換などのレンダリング操作を大量に使用するアプリケーションでは、このパイプラインを有効にすることを検討してください。
OpenGL パイプラインで高速化される Java 2D 操作の完全な一覧については、記事「Behind the Graphics2D: The OpenGL-based Pipeline」を参照してください。
現時点では、OpenGL パイプラインはデフォルトで無効になっています。OpenGL パイプラインの有効化を試みるには、次のオプションを JVM に指定します。
-Dsun.java2d.opengl=true
OpenGL パイプラインが特定のスクリーンに対して正常に初期化されたかどうかに関する、冗長なコンソール出力を受け取るには、オプションを True に設定します (大文字「T」に注意)。
-Dsun.java2d.opengl=True
ハードウェアまたはドライバが最小要件を満たしていなければ、OpenGL パイプラインは有効にされません。何らかの理由で次の要件のいずれかが満たされない場合、Java 2D はフォールバックしてデフォルトパイプライン (Solaris/Linux では X11、Windows では GDI/DirectDraw) を使用するため、アプリケーションは正しく動作し続けますが、OpenGL の高速化は失われます。
Solaris OS および Linux の最小要件は、次のとおりです。
ハードウェア高速化の OpenGL/GLX ライブラリがインストールされ、適切に構成されている
OpenGL のバージョンが 1.2 以降
GLX のバージョンが 1.3 以降
使用可能な深度バッファー付きの、少なくとも 1 つのトゥルーカラービジュアル
Windows OS の最小要件は、次のとおりです。
拡張 WGL_ARB_pbuffer、WGL_ARB_render_texture、および WGL_ARB_pixel_format をサポートするハードウェア高速化ドライバ
OpenGL のバージョンが 1.2 以降
使用可能な深度バッファー付きの、少なくとも 1 つのピクセル形式
OpenGL パイプラインは OpenGL API およびベースとなるグラフィックスハードウェアやドライバに強く依存しているため、最新のグラフィックスドライバがマシンにインストールされているのを確認することが非常に重要です。ドライバは、グラフィックスカードの製造元の Web サイトからダウンロードできます (次の表を参照)。
|
前述したように、特定のマシン上でさまざまな理由で OpenGL パイプラインが有効にされない場合があります。たとえば、ドライバが正しくインストールされていない可能性や、報告されたバージョン番号が不十分である可能性があります。あるいは、マシンに搭載されている古いグラフィックスカードが OpenGL の適切なバージョンや拡張をサポートしていない可能性もあります。
Java SE 6 以降のリリースで、OpenGL ベースの Java 2D パイプラインの起動手順に関する詳細情報を取得するには、次のように J2D_TRACE_LEVEL 環境変数を使用します。
Windows の場合:
# set J2D_TRACE_LEVEL=4 # java -Dsun.java2d.opengl=True YourApp
Solaris OS および Linux の場合:
# export J2D_TRACE_LEVEL=4 # java -Dsun.java2d.opengl=True YourApp
その出力は、プラットフォームや取り付けられたグラフィックスハードウェアに応じて異なりますが、OpenGL パイプラインがユーザーの構成で正常に有効にされない理由に関する何らかの洞察を提供してくれます。この出力は特に、Sun の Java 2D チーム宛のバグレポートを提出する際に役立ちます (バグレポートについては後述)。
OpenGL パイプラインはベースとなるグラフィックスハードウェアやドライバに非常に強く依存しているため、レンダリングやパフォーマンスの問題の原因が Java 2D、OpenGL ドライバのどちらなのかを判定しかねる場合があります。
Java SE 6 リリースでの OpenGL パイプラインの新機能の 1 つは、VolatileImage 使用時のレンダリングパフォーマンスの改善と VRAM 消費量の低減を図る GL_EXT_framebuffer_object 拡張の使用です。この「FBO」コードパスは、OpenGL パイプラインが有効なときにデフォルトで有効にされますが、それは、グラフィックスハードウェアとドライバがこの OpenGL 拡張をサポートしている場合だけです。この拡張は一般に、Nvidia GeForce/Quadro FX シリーズ以降および ATI Radeon 9500 以降で使用できます。「FBO」コードパスがアプリケーションの問題の原因となっている疑いがある場合、次のシステムプロパティーを設定してそれを無効にすることができます。
-Dsun.java2d.opengl.fbobject=false
このプロパティーが設定されると、Java 2D は古い「pbuffer ベースの」コードパスにフォールバックします。
特定の Java 2D 操作で得られる視覚的な結果が、OpenGL パイプラインを有効にしたときとしなかったときで異なる場合、それはおそらく、グラフィックスドライバのバグを示しています。同様に、OpenGL パイプラインを有効にしたときに、しなかった場合よりも Java 2D レンダリングのパフォーマンスが大幅に悪化する場合、その原因はおそらくドライバまたはハードウェアの問題です。
いずれにしても、通常のバグ報告チャネルを通じて詳細なバグレポートを提出してください (第 8 章「バグレポートの提出」を参照)。バグレポートの提出時にはできるだけ詳しく記述し、必ず次の情報を含めてください。
オペレーティングシステム (Ubuntu Linux 6.06、Windows XP SP2 など)
グラフィックスハードウェアの製造元とデバイスの名前 (Nvidia GeForce? 2 MX 440 など)
ドライバの正確なバージョン (ATI Catalyst 6.8、Nvidia 91.33 など)
J2D_TRACE_LEVEL=4 をコマンド行に指定したときの出力 (前のセクションで説明)
Solaris OS または Linux の場合は、glxinfo コマンドの出力
このセクションには次のサブセクションが含まれます。
パフォーマンス問題の原因についての理解を深めるため、ハードウェア高速化の意味について考えてます。
一般に、ハードウェア高速化レンダリングは 2 つのカテゴリに分けられます。
「高速化」デスティネーションへのハードウェア高速化レンダリング。ハードウェア高速化可能なレンダリング先の例として、VolatileImage、画面、および BufferStrategy が挙げられます。あるデスティネーションが高速化対象である場合、そのような表面に対するレンダリングはビデオハードウェアによって実行される可能性があります。したがって、ユーザーが drawRect 呼び出しを発行すると、Java 2D はその呼び出しを、ベースとなるネイティブ API (GDI、DirectDraw、Direct3D、OpenGL、X11 など) にリダイレクトし、そこでハードウェアを使って操作が実行されます。
高速化メモリー (ビデオメモリーまたはピックスマップ) へのイメージのキャッシング。これにより、それらのイメージを別の高速化対象表面に非常に高速でコピーできるようになります。そのようなイメージは「管理対象イメージ」と呼ばれます。
理想的には、高速化対象表面に対して実行される操作はすべて、ハードウェアで高速化されます。この場合、アプリケーションはプラットフォームによって提供されるメリットを最大限に享受できます。
残念ながら多くの場合、デフォルトパイプラインはレンダリングにハードウェアを使用できません。その原因は、パイプラインの制限やベースとなるネイティブ API にあります。たとえば、ほとんどの X サーバーは、アンチエイリアスプリミティブのレンダリングやアルファ合成をサポートしません。
パフォーマンス問題の原因の 1 つは、実行される操作がハードウェア高速化されない場合にあります。デスティネーション表面が高速化される場合でも、その一部のプリミティブが高速化されない可能性があります。
ハードウェア高速化が使用されていない場合を検出する方法を知ることが重要です。それを知れば、パフォーマンスの改善が容易になる可能性があります。
高速化されないレンダリングを検出するには、Java 2D プリミティブトレースを使用できます。
Java 2D には組み込みのプリミティブトレースが含まれています。「Java 2D テクノロジのシステムプロパティー」の trace プロパティーの説明を参照してください。
アプリケーションの実行時に -Dsun.java2d.trace=count を指定します。アプリケーションが終了すると、プリミティブのリストとそれらのカウントがコンソールに出力されます。
MaskBlit プリミティブや任意の General* プリミティブが表示された場合、それは通常、レンダリングの一部がソフトウェアループによって処理されていることを意味します。Linux 上で、半透明の BufferedImage に対して drawImage を実行して VolatileImage に書き込む場合の出力を、次に示します。
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)
デフォルトパイプラインの一般的な非高速化プリミティブのいくつかと、それらのトレース出力でのシグニチャーを次に示します。このトレースの大部分は Linux 上で取得したものです。プラットフォームや構成によっては何らかの違いが生じる可能性があります。
半透明イメージ (ColorModel.getTranslucency() で Translucency.TRANSLUCENT が返されるイメージ)、または AlphaCompositing を使用したもの。サンプルのプリミティブトレース出力:
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb,SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)
アンチエイリアスの使用 (アンチエイリアスヒントの設定による)。サンプルのプリミティブトレース出力:
sun.java2d.loops.MaskFill::MaskFill(AnyColor, Src, IntBgr)
アンチエイリアステキストのレンダリング (テキストアンチエイリアスヒントの設定)。サンプル出力は次のいずれかになります。
sun.java2d.loops.DrawGlyphListAA::DrawGlyphListAA(OpaqueColor, SrcNoEa, AnyInt)
sun.java2d.loops.DrawGlyphListLCD::DrawGlyphListLCD(AnyColor, SrcNoEa, IntBgr)
アルファ合成 (半透明カラー (0xff 以外のアルファ値を含むカラー) でのレンダリング、Graphics2D.setComposite() によるデフォルト以外の AlphaCompositing モードの設定、のいずれかによる)。
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOver, IntRgb)sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
本格的な変換 (単なる移動ではない変換)。変換された不透明イメージの VolatileImage へのレンダリング。
sun.java2d.loops.TransformHelper::TransformHelper(IntBgr, SrcNoEa, IntArgbPre)
回転されるラインのレンダリング。
sun.java2d.loops.DrawPath::DrawPath(AnyColor, SrcNoEa, AnyInt)
アプリケーションをトレース付きで実行し、不要な非高速化プリミティブを使用していないことを確認してください。
次の各サブセクションでは、低いレンダリングパフォーマンスの原因となる可能性のあるものをいくつか説明します。
特定のパイプラインでの高速化表面へのレンダリング時に、アプリケーションによってレンダリングされるプリミティブの一部しか高速化できない状況では、スラッシングが発生する可能性があります。パイプラインで常にレンダリングパフォーマンスの改善に向けた調整が試行されても、成功する可能性がおそらくほとんどないからです。
レンダリングプリミティブの大部分が高速化されないことが事前にわかっている場合、BufferedImage にレンダリングしてからバックバッファーや画面にコピーするか、あるいは前述のフラグのいずれかを使って非ハードウェア高速化パイプラインに切り替えたほうが得策です。
ただしこのアプローチを採用した場合、Java 2D でのハードウェア高速化の使用状況が将来改善されても、そのメリットをアプリケーションで享受できなくなる可能性があります。
たとえば、リモート X サーバーケースでの使用頻度の高いアプリケーション内でアンチエイリアスやアルファ合成などが多用されると、パフォーマンスが極度に低下する可能性があります。これを避けるには、-Dsun.java2d.pmoffscreen=false プロパティーを Java ランタイムに渡すかプログラム内で System.setProperty() API を使って設定して、ピックスマップの使用を無効にします。このプロパティーは一度しか読み取られないため、すべての GUI 関連操作の前に設定する必要があります。
目的の視覚効果を実現する際に、可能なかぎりもっとも単純なプリミティブを使用することをお勧めします。
たとえば、Graphics.drawLine() を使用して new Line2D().draw() は使用しません。結果は同じようにみえます。ただし、2 番目の操作では、通常レンダリングコストの非常に高い汎用形状としてレンダリングされるため、計算量が格段に多くなります。形状は、プリミティブトレース内で、アンチエイリアス設定や特定のパイプラインに応じてさまざまな方法で表示されますが、おそらく多数の *FillSpans または DrawPath プリミティブとして表示されます。
複雑な属性のもう 1 つの例は、GradientPaint です。これは、デフォルト以外の一部のパイプライン (OpenGL など) ではハードウェア高速化される可能性がありますが、デフォルトパイプラインではハードウェア高速化されません。したがって、パフォーマンスの問題が発生する場合に GradientPaint の使用を制限できます。
BufferedImage へのレンダリングでは、ほとんど常にソフトウェアループが使用されます。
例外として、一部の SPARC システムで、特定のイメージング操作を高速化するために VIS 命令セットが使用される場合があります。VIS 命令セットの Web サイトを参照してください。
レンダリングが高速化される機会を残すには、BufferStrategy または VolatileImage オブジェクトをレンダリング先として選択してください。
Java 2D は特定タイプのイメージを高速化しようとします。VolatileImage などの高速化デスティネーションへのコピーを高速化するため、イメージの内容がビデオメモリーにキャッシュされる可能性があります。これらのメカニズムが知らないうちに (たとえば次のような場合に)、アプリケーションによって無効にされてしまう可能性があります。
アプリケーションが getRaster().getDataBuffer() API を使って BufferedImage ピクセルにアクセスした場合、Java 2D は、キャッシュ内のデータが最新であることを保証できないため、そのようなイメージの高速化の試みをすべて無効にします。
この問題を避ける方法は 2 つあります。
可能であれば、getDataBuffer() を呼び出さないでください。代わりに、BufferedImage.getRaster() メソッドで取得可能な WriteableRaster を操作します。
ピクセルを直接変更する必要がない場合には、イメージをビデオメモリー内に手動でキャッシュするためにイメージのキャッシュコピーを VolatileImage 内に保持し、元のイメージが更新された時点でそのキャッシュデータを更新します。
アプリケーションがイメージを高速化表面 (VolatileImage、BufferStrategy) にコピーする前にイメージに毎回レンダリングする場合、そのイメージは、高速化メモリーにキャッシュされるメリットを享受できません。その理由は、元のイメージが更新されるたびにキャッシュコピーを更新する必要があり、そのためにデフォルトのシステムメモリーベースの表面だけが使用されて、高速化されないからです。
アプリケーションが多数のイメージを使用すると、使用可能な高速化メモリーが使い果たされる可能性があります。これが本当にアプリケーションのパフォーマンス問題の原因となっている場合、リソースを管理しなければいけない可能性があります。
次の API を使えば、使用可能な高速化メモリーの量を要求できます。GraphicsDevice.getAvailableAcceleratedMemory()。
さらに次の API を使えば、イメージが高速化されているかどうかを判定できます。Image.getCapabilities()。
アプリケーションがリソースを使い果たしていることが判明した場合、次の方法で問題を処理できます。
不要になったイメージを保持しないようにします。たとえば、ゲームが次のレベルに進んだら、以前のレベルに含まれるイメージをすべて解放します。また、イメージに関連付けられた高速化リソースを Image.flush() API で解放することもできます。
高速化優先順位 API Image.getAccelerationPriority() および setAccelerationPriority() を使ってイメージの高速化優先順位を指定します。少なくともバックバッファーを高速化することは妥当なので、これを最初に作成し、高速化優先順位 1 (デフォルト) を指定します。また、必要であれば、高速化優先順位を 0.0 に設定して、特定のイメージが高速化されないようにすることもできます。
アプリケーションが (BufferedImage へのレンダリングのみを行うかデフォルトパイプラインを非高速化パイプラインに変更したために) ソフトウェアのみのレンダリングに依存している場合、あるいはアプリケーションが混在されたレンダリングを行う場合でも、特定のパフォーマンス改善アプローチが存在します。
プラットフォーム全体のサイズ制約のために、あるイメージ形式を別の形式に変換するための最適化されたルーチンは、Java 2D には限られた数しか含まれていません。最適化された直接ループが見つからない状況では、Java 2D は中間イメージ形式 (IntArgb) を介して変換します。この場合、パフォーマンスが低下します。
Java 2D プリミティブトレースを使用すればそのような状況を検出できます。
各 drawImage 呼び出しに対して 2 つのプリミティブが存在します。1 つ目はイメージをソース形式から中間 IntArgb 形式に変換し、2 つ目は中間 IntArgb から変換先の形式に変換します。
そのような状況を回避する 2 つの方法を次に示します。
可能であれば別のイメージ形式を使用します。
イメージを、よりサポートレベルの高い形式 (INT_RGB や INT_ARGB など) の中間イメージに変換します。そうすれば、カスタムイメージ形式からの変換が、コピーごとに発生する代わりに一度だけ発生するようになります。
できれば、完全な半透明 (INT_ARGB など) のイメージではなく、1 ビット透明 (BITMASK) のイメージをスプライトとして使用することを検討してください。
完全なアルファを持つイメージの処理では、CPU の負荷がより高くなります。
1 ビット透明イメージを取得するには、GraphicsConfiguration.createCompatibleImage(w,h, Transparency.BITMASK) の呼び出しを使用します。
このセクションでは、テキストレンダリングに関係する可能性のあるいくつかの問題について説明します (次の各サブセクションを参照)。
テキストレンダリング中にアプリケーションがクラッシュする場合は、まず致命的エラーログファイルをチェックしてください。このエラーログファイルの詳細については、「付録 B: 致命的エラーログ」を参照してください。fontmanager.dll 内でクラッシュが発生した場合やスタック内に fontmanager が存在している場合は、フォント処理コード内でクラッシュが発生したことになります。典型的なネイティブスタックフレームの例 (完全なログファイルからの抜粋) を次に示します。
Stack: [0x008a0000,0x008f0000), sp=0x008ef52c, free space=317k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [ntdll.dll+0x1888f] C [ntdll.dll+0x18238] C [ntdll.dll+0x11c76] C [MSVCR71.dll+0x16b3] C [MSVCR71.dll+0x16db] C [fontmanager.dll+0x21f9a] C [fontmanager.dll+0x22876] C [fontmanager.dll+0x1de40] C [fontmanager.dll+0x1da94] C [fontmanager.dll+0x48abb] j sun.font.FileFont.getGlyphImage(JI)J+0 j sun.font.FileFontStrike.getGlyphImagePtrs([I[JI)V+92 j sun.font.GlyphList.mapChars(Lsun/java2d/loops/FontInfo;I)Z+37 j sun.font.GlyphList.setFromString(Lsun/java2d/loops/FontInfo;Ljava/lang/String;FF)Z+71 j sun.java2d.pipe.GlyphListPipe.drawString(Lsun/java2d/SunGraphics2D;Ljava/lang/String;DD)V+148 j sun.java2d.SunGraphics2D.drawString(Ljava/lang/String;II)V+60 j FontCrasher.tryFont(Ljava/lang/String;)V+138 j FontCrasher.main([Ljava/lang/String;)V+20 v ~StubRoutines::call_stub
この場合、特定のフォントがおそらく問題です。その場合は、このフォントをシステムから削除すれば、おそらく問題が解決します。
フォントファイルを特定するには、アプリケーションの実行時に -Dsun.java2d.debugfonts=true を指定します。通常、最後に言及されたフォントが、問題の原因となっているフォントです。典型的な出力の例を次に示します。
INFO: Registered file C:\WINDOWS\Fonts\WINGDING.TTF as font ** TrueType Font: Family=Wingdings Name=Wingdings style=0 fileName=C:\WINDOWS\Fonts\WINGDING.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Symbol, Font Symbol rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\SYMBOL.TTF as font ** TrueType Font: Family=Symbol Name=Symbol style=0 fileName=C:\WINDOWS\Fonts\SYMBOL.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D INFO: Search for font: Dialog Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file ARIALBD.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Arial, Font Arial Bold rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\ARIALBD.TTF as font ** TrueType Font: Family=Arial Name=Arial Bold style=1 fileName=C:\WINDOWS\Fonts\ARIALBD.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file WINGDING.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D INFO: Search for font: Dialog Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file ARIAL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Arial, Font Arial rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\ARIAL.TTF as font ** TrueType Font: Family=Arial Name=Arial style=0 fileName=C:\WINDOWS\Fonts\ARIAL.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file WINGDING.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF
注 - 場合によっては、最後に言及されたフォントが実際には無実である可能性もあります。フォント名が出力されるのは初回使用時であり、その後の使用では表示されません。
この特定のフォントが問題の原因であることを確認するには、そのフォントをシステムから一時的に削除します。この特定のファミリ名に関連付けられたファイル名は、上の出力から容易にわかります。
もう 1 つの確認方法は、Font2DTest ツール (demo/jfc/Font2DTest) を使って疑わしいフォントをテストすることです。特定のフォントのサイズ、スタイル、およびラスター化モードを指定できます。Font2DTest で特定のフォントを表示させる途中で JDK がクラッシュした場合、そのフォントが、問題の原因となったフォントである可能性が非常に高くなります。
あるフォントが JDK のクラッシュを引き起こしたことがわかった場合、この問題を、特定のフォントとオペレーティングシステムを含めて、bugs.sun.com で報告することが非常に重要です。バグ報告の詳細については、第 8 章「バグレポートの提出」を参照してください。
Java は独自のフォントラスタライザを備えているため、Java アプリケーションとネイティブアプリケーションとでテキストの外観に何らかの小さな違いがあることが予想できます。
こうした相違点のもっとも典型的な原因の 1 つは、アンチエイリアス設定が違う可能性があることです。特に、Swing アプリケーションは、Linux デスクトップのフォントアンチエイリアス設定を無視する場合があります。
この動作には、考えられる理由がいくつかあります。
リモート X11 経由の場合、パフォーマンス上の理由により、アンチエイリアスがデフォルトでは有効になりません。アンチエイリアスを強制する方法については、Java 2D FAQ の、フォントやテキストの質問に関するセクションを参照してください。
埋め込みビットマップを使用する CJK フォントは、サブピクセルテキストの代わりにビットマップを使ってレンダリングされる可能性があります。
未サポートデスクトップの一部のバリアントでは、フォントのスムージング設定が適切に報告されません。たとえば、KDE は未サポートですが基本的には動作するはずです。しかし、何らかの問題により、JDK が設定を取得できないようです。
構成が予想どおりのものであることを確認する最良の方法は、Font2DTest を実行し、ネイティブアプリケーションで使用されているフォントを明示的に選択し、ほかのパラメータを必要に応じて設定することです。Font2DTest ツールのサンプル画面を次に示します。
ヒント:ユーザー独自の文字列を入力するには、「Text to use」というラベルの付いたドロップダウンメニューで「user text」を選択します。
Java 言語のフォントのサイズは常に 72 dpi で表現されます。ネイティブ OS では別の画面 dpi を使用できるため、調整が必要になります。対応する Java フォントサイズを計算するには、Toolkit.getScreenResolution() を 72 で割り、その結果にネイティブフォントのサイズを掛けます。
Windows Look & Feel や (Solaris OS および Linux 用の) GTK Look & Feel など、すべての「ネイティブ」Swing Look & Feel で、Swing コンポーネントによってこの調整が自動的に実行されますが、Font2DTest を実行する場合、そのテキスト表示領域では常に 72 dpi が使用されます。
Windows 以外のオペレーティングシステムでの一般的な推奨事項は、Type1 フォントの代わりに TrueType フォントを使用することです。フォントのタイプを確認するもっとも簡単な方法は、ファイル拡張子を調べることです。拡張子 pfa と pfb は Type1 フォントを示し、ttf、ttc、および tte は TrueType フォントを表します。
テキストの境界が予想していたものと異なることに気づいた場合、境界の適切な計算方法を使用していることを確認してください。たとえば、FontMetrics から得られる高さは特定のテキストに固有のものではなく、stringWidth は論理的な有効幅を示し、「幅」と同じものではありません。詳細については、Java 2D FAQ の、フォントやテキストの質問に関するセクションを参照してください。
このセクションでは、Java 2D 印刷で発生する可能性のあるいくつかの問題を説明し、その原因と解決方法を提示します。
Java 2D FAQ の印刷の質問に関するセクションも参照してください。
原因: JRE が使用する Windows プリンタドライバに問題がある可能性があります。
解決方法: 使用中のプリンタの Windows プリンタドライバをアップグレードします。
原因: 一部のジョブでは、プリンタへのスプールが正しく行われません。
解決方法: プリンタドライバのプロパティーで、「高度な印刷のオプション」を無効にします。
原因: アプリケーションが、切断されているプリンタも含め、すべてのプリンタを JRE にプローブさせている可能性があります。
解決方法: 切断されているか到達不可能なネットワークプリンタを探し、それらをプリンタのリストから削除します。
原因: 原因は次のいずれかです。
lpc ユーティリティーが /usr/sbin ディレクトリにありません。
lpstat ユーティリティーが /usr/sbin ディレクトリにありません。
解決方法: lpc や lpstat を、上で言及した標準の場所にインストールします。