OSDN Git Service

#38036 どこでCTextureの解放漏れが発生しているかを容易に識別できるようにするために、CTextureの生成時にラベルを付与できるようにした。(引数最後にラベ...
[dtxmania/dtxmania.git] / FDK / コード / 04.グラフィック / CTexture.cs
index 08bc328..3a8f8ce 100644 (file)
@@ -12,6 +12,16 @@ using Rectangle = System.Drawing.Rectangle;
 
 namespace FDK
 {
+       /// <summary>
+       /// テクスチャを扱うクラス。
+       /// 使用終了時は必ずDispose()してください。Finalize時の自動Disposeはありません。
+       /// Disposeを忘れた場合は、メモリリークに直結します。
+       /// Finalize時にDisposeしない代わりに、Finalize時にテクスチャのDispose漏れを検出し、
+       /// Trace.TraceWarning()でログを出力します。
+       /// see also:
+       /// https://osdn.net/projects/dtxmania/ticket/38036
+       /// https://github.com/sharpdx/SharpDX/pull/192?w=1
+       /// </summary>
        public class CTexture : IDisposable
        {
                #region [ プロパティ ]
@@ -74,6 +84,8 @@ namespace FDK
                        protected set;
                }
                public Vector3 vc拡大縮小倍率;
+               public string filename;
+               public string label;
                #endregion
 
                // コンストラクタ
@@ -84,14 +96,17 @@ namespace FDK
                        this.szテクスチャサイズ = new Size( 0, 0 );
                        this._透明度 = 0xff;
                        this.texture = null;
+                       this.bSharpDXTextureDispose完了済み = true;
                        this.cvPositionColoredVertexies = null;
                        this.b加算合成 = false;
                        this.fZ軸中心回転 = 0f;
                        this.vc拡大縮小倍率 = new Vector3( 1f, 1f, 1f );
                        this.bFlipY = false;
 //                     this._txData = null;
+                       this.filename = "";
+                       this.label = "";
                }
-               
+
                /// <summary>
                /// <para>指定されたビットマップオブジェクトから Managed テクスチャを作成する。</para>
                /// <para>テクスチャのサイズは、BITMAP画像のサイズ以上、かつ、D3D9デバイスで生成可能な最小のサイズに自動的に調節される。
@@ -103,12 +118,13 @@ namespace FDK
                /// <param name="bitmap">作成元のビットマップ。</param>
                /// <param name="format">テクスチャのフォーマット。</param>
                /// <exception cref="CTextureCreateFailedException">テクスチャの作成に失敗しました。</exception>
-               public CTexture( Device device, Bitmap bitmap, Format format )
+               public CTexture( Device device, Bitmap bitmap, Format format, string _label="" )
                        : this()
                {
                        try
                        {
                                this.Format = format;
+                               this.label = _label;
                                this.sz画像サイズ = new Size( bitmap.Width, bitmap.Height );
                                this.szテクスチャサイズ = this.t指定されたサイズを超えない最適なテクスチャサイズを返す( device, this.sz画像サイズ );
                                this.rc全画像 = new Rectangle( 0, 0, this.sz画像サイズ.Width, this.sz画像サイズ.Height );
@@ -119,6 +135,7 @@ namespace FDK
                                        stream.Seek( 0L, SeekOrigin.Begin );
                                        int colorKey = unchecked( (int) 0xFF000000 );
                                        this.texture = Texture.FromStream( device, stream, this.szテクスチャサイズ.Width, this.szテクスチャサイズ.Height, 1, Usage.None, format, poolvar, Filter.Point, Filter.None, colorKey );
+                                       this.bSharpDXTextureDispose完了済み = false;
                                }
                        }
                        catch ( Exception e )
@@ -141,8 +158,8 @@ namespace FDK
                /// <param name="n高さ">テクスチャの高さ(希望値)。</param>
                /// <param name="format">テクスチャのフォーマット。</param>
                /// <exception cref="CTextureCreateFailedException">テクスチャの作成に失敗しました。</exception>
-               public CTexture( Device device, int n幅, int n高さ, Format format )
-                       : this( device, n幅, n高さ, format, Pool.Managed )
+               public CTexture( Device device, int n幅, int n高さ, Format format, string _label = "")
+                       : this( device, n幅, n高さ, format, Pool.Managed, _label)
                {
                }
                
@@ -155,16 +172,16 @@ namespace FDK
                /// <param name="format">テクスチャのフォーマット。</param>
                /// <param name="b黒を透過する">画像の黒(0xFFFFFFFF)を透過させるなら true。</param>
                /// <exception cref="CTextureCreateFailedException">テクスチャの作成に失敗しました。</exception>
-               public CTexture( Device device, string strファイル名, Format format, bool b黒を透過する )
-                       : this( device, strファイル名, format, b黒を透過する, Pool.Managed )
+               public CTexture( Device device, string strファイル名, Format format, bool b黒を透過する, string _label = "")
+                       : this( device, strファイル名, format, b黒を透過する, Pool.Managed, _label)
                {
                }
-               public CTexture( Device device, byte[] txData, Format format, bool b黒を透過する )
-                       : this( device, txData, format, b黒を透過する, Pool.Managed )
+               public CTexture( Device device, byte[] txData, Format format, bool b黒を透過する, string _label = "")
+                       : this( device, txData, format, b黒を透過する, Pool.Managed, _label)
                {
                }
-               public CTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する )
-                       : this( device, bitmap, format, b黒を透過する, Pool.Managed )
+               public CTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する, string _label = "")
+                       : this( device, bitmap, format, b黒を透過する, Pool.Managed, _label)
                {
                }
                
@@ -182,17 +199,18 @@ namespace FDK
                /// <param name="format">テクスチャのフォーマット。</param>
                /// <param name="pool">テクスチャの管理方法。</param>
                /// <exception cref="CTextureCreateFailedException">テクスチャの作成に失敗しました。</exception>
-               public CTexture( Device device, int n幅, int n高さ, Format format, Pool pool )
-                       : this( device, n幅, n高さ, format, pool, Usage.None, false )
+               public CTexture( Device device, int n幅, int n高さ, Format format, Pool pool, string _label = "")
+                       : this( device, n幅, n高さ, format, pool, Usage.None, false, _label)
                {
                }
                
-               public CTexture( Device device, int n幅, int n高さ, Format format, Pool pool, Usage usage, bool b黒を透過する )
+               public CTexture( Device device, int n幅, int n高さ, Format format, Pool pool, Usage usage, bool b黒を透過する, string _label = "")
                        : this()
                {
                        try
                        {
                                this.Format = format;
+                               this.label = _label;
                                this.sz画像サイズ = new Size( n幅, n高さ );
                                this.szテクスチャサイズ = this.t指定されたサイズを超えない最適なテクスチャサイズを返す( device, this.sz画像サイズ );
                                this.rc全画像 = new Rectangle( 0, 0, this.sz画像サイズ.Width, this.sz画像サイズ.Height );
@@ -213,6 +231,7 @@ namespace FDK
 #endif
                                                // 中で更にメモリ読み込みし直していて無駄なので、Streamを使うのは止めたいところ
                                                this.texture = Texture.FromStream( device, stream, n幅, n高さ, 1, usage, format, pool, Filter.Point, Filter.None, colorKey );
+                                               this.bSharpDXTextureDispose完了済み = false;
                                        }
                                }
                        }
@@ -236,31 +255,33 @@ namespace FDK
                /// <param name="b黒を透過する">画像の黒(0xFFFFFFFF)を透過させるなら true。</param>
                /// <param name="pool">テクスチャの管理方法。</param>
                /// <exception cref="CTextureCreateFailedException">テクスチャの作成に失敗しました。</exception>
-               public CTexture( Device device, string strファイル名, Format format, bool b黒を透過する, Pool pool )
+               public CTexture( Device device, string strファイル名, Format format, bool b黒を透過する, Pool pool, string _label = "")
                        : this()
                {
-                       MakeTexture( device, strファイル名, format, b黒を透過する, pool );
+                       MakeTexture( device, strファイル名, format, b黒を透過する, pool, _label );
                }
-               public void MakeTexture( Device device, string strファイル名, Format format, bool b黒を透過する, Pool pool )
+               public void MakeTexture( Device device, string strファイル名, Format format, bool b黒を透過する, Pool pool, string _label = "")
                {
                        if ( !File.Exists( strファイル名 ) )               // #27122 2012.1.13 from: ImageInformation では FileNotFound 例外は返ってこないので、ここで自分でチェックする。わかりやすいログのために。
                                throw new FileNotFoundException( string.Format( "ファイルが存在しません。\n[{0}]", strファイル名 ) );
 
                        Byte[] _txData = File.ReadAllBytes( strファイル名 );
-                       MakeTexture( device, _txData, format, b黒を透過する, pool );
+                       this.filename = Path.GetFileName( strファイル名 );
+                       MakeTexture( device, _txData, format, b黒を透過する, pool, _label );
                }
 
-               public CTexture( Device device, byte[] txData, Format format, bool b黒を透過する, Pool pool )
+               public CTexture( Device device, byte[] txData, Format format, bool b黒を透過する, Pool pool, string _label = "")
                        : this()
                {
-                       MakeTexture( device, txData, format, b黒を透過する, pool );
+                       MakeTexture( device, txData, format, b黒を透過する, pool, _label );
                }
-               public void MakeTexture( Device device, byte[] txData, Format format, bool b黒を透過する, Pool pool )
+               public void MakeTexture( Device device, byte[] txData, Format format, bool b黒を透過する, Pool pool, string _label = "")
                {
                        try
                        {
                                var information = ImageInformation.FromMemory( txData );
                                this.Format = format;
+                               this.label = _label;
                                this.sz画像サイズ = new Size( information.Width, information.Height );
                                this.rc全画像 = new Rectangle( 0, 0, this.sz画像サイズ.Width, this.sz画像サイズ.Height );
                                int colorKey = ( b黒を透過する ) ? unchecked( (int) 0xFF000000 ) : 0;
@@ -276,6 +297,7 @@ namespace FDK
                                //                              {
                                //Trace.TraceInformation( "CTexture() start: " );
                                this.texture = Texture.FromMemory( device, txData, this.sz画像サイズ.Width, this.sz画像サイズ.Height, 1, Usage.None, format, pool, Filter.Point, Filter.None, colorKey );
+                               this.bSharpDXTextureDispose完了済み = false;
                                //Trace.TraceInformation( "CTexture() end:   " );
                                //                              }
                        }
@@ -287,16 +309,17 @@ namespace FDK
                        }
                }
 
-               public CTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する, Pool pool )
+               public CTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する, Pool pool, string _label = "")
                        : this()
                {
-                       MakeTexture( device, bitmap, format, b黒を透過する, pool );
+                       MakeTexture( device, bitmap, format, b黒を透過する, pool, _label );
                }
-               public void MakeTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する, Pool pool )
+               public void MakeTexture( Device device, Bitmap bitmap, Format format, bool b黒を透過する, Pool pool, string _label = "")
                {
                        try
                        {
                                this.Format = format;
+                               this.label = _label;
                                this.sz画像サイズ = new Size( bitmap.Width, bitmap.Height );
                                this.rc全画像 = new Rectangle( 0, 0, this.sz画像サイズ.Width, this.sz画像サイズ.Height );
                                int colorKey = ( b黒を透過する ) ? unchecked( (int) 0xFF000000 ) : 0;
@@ -353,6 +376,7 @@ namespace FDK
 #endif
                                        texture.UnlockRectangle( 0 );
                                        bitmap.UnlockBits( srcBufData );
+                                       this.bSharpDXTextureDispose完了済み = false;
                                }
                                //Trace.TraceInformation( "CTExture() End: " );
                        }
@@ -587,21 +611,45 @@ namespace FDK
                        device.DrawUserPrimitives( PrimitiveType.TriangleStrip, 2, this.cvPositionColoredVertexies );
                }
 
-               #region [ IDisposable 実装 ]
+
+               #region [ Dispose-Finalize パターン実装 ]
                //-----------------
                public void Dispose()
                {
-                       if( !this.bDispose完了済み )
+                       this.Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+               protected void Dispose(bool disposeManagedObjects)
+               {
+                       if (this.bDispose完了済み)
+                               return;
+
+                       if (disposeManagedObjects)
                        {
-                               // テクスチャの破棄
-                               if( this.texture != null )
+                               // (A) Managed リソースの解放
+                               // テクスチャの破棄 (SharpDXのテクスチャは、SharpDX側で管理されるため、FDKからはmanagedリソースと見做す)
+                               if (this.texture != null)
                                {
                                        this.texture.Dispose();
                                        this.texture = null;
+                                       this.bSharpDXTextureDispose完了済み = true;
                                }
+                       }
+
+                       // (B) Unamanaged リソースの解放
+
 
-                               this.bDispose完了済み = true;
+                       this.bDispose完了済み = true;
+               }
+               ~CTexture()
+               {
+                       // ファイナライザの動作時にtextureのDisposeがされていない場合は、
+                       // CTextureのDispose漏れと見做して警告をログ出力する
+                       if (!this.bSharpDXTextureDispose完了済み)
+                       {
+                               Trace.TraceWarning("CTexture: Dispose漏れを検出しました。(Size=({0}, {1}), filename={2}, label={3})", sz画像サイズ.Width, sz画像サイズ.Height, filename, label );
                        }
+                       this.Dispose(false);
                }
                //-----------------
                #endregion
@@ -612,7 +660,7 @@ namespace FDK
                #region [ private ]
                //-----------------
                protected int _透明度;
-               private bool bDispose完了済み;
+               private bool bDispose完了済み, bSharpDXTextureDispose完了済み;
                protected PositionColoredTexturedVertex[] cvPositionColoredVertexies;
                protected TransformedColoredTexturedVertex[] cvTransformedColoredVertexies;
                protected const Pool poolvar =                                                                                          // 2011.4.25 yyagi