From 1ce085b49f996bb1311dc3b9e6827da897a8a6a7 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 20 Sep 2014 00:50:04 +0200 Subject: [PATCH] Use better references to a single texture and unify more stuff Also encapsulate everything graphic related in the Texture classes and not in the references --- Vocaluxe/Lib/Draw/CDirect3D.cs | 115 +++++++------ Vocaluxe/Lib/Draw/CDrawBase.cs | 225 +++++++++++++++----------- Vocaluxe/Lib/Draw/CDrawBaseWindows.cs | 2 +- Vocaluxe/Lib/Draw/CDrawWinForm.cs | 21 ++- Vocaluxe/Lib/Draw/COpenGL.cs | 111 ++++++------- Vocaluxe/Lib/Draw/CTextureBase.cs | 102 ++++++++++++ Vocaluxe/Vocaluxe.csproj | 1 + VocaluxeLib/Draw/CTextureRef.cs | 85 +--------- 8 files changed, 364 insertions(+), 298 deletions(-) create mode 100644 Vocaluxe/Lib/Draw/CTextureBase.cs diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs index 6484e36c..3f9727f4 100644 --- a/Vocaluxe/Lib/Draw/CDirect3D.cs +++ b/Vocaluxe/Lib/Draw/CDirect3D.cs @@ -42,7 +42,24 @@ namespace Vocaluxe.Lib.Draw } } - class CDirect3D : CDrawBaseWindows, IDraw + class CD3DTexture:CTextureBase,IDisposable + { + public readonly Texture D3DTexture; + public CD3DTexture(Device device, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight) + { + D3DTexture = new Texture(device, W2, H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed); + } + + public void Dispose() + { + if(D3DTexture.Disposed) + throw new ObjectDisposedException(GetType().Name); + D3DTexture.Dispose(); + RefCount = 0; + } + } + + class CDirect3D : CDrawBaseWindows, IDraw { #region private vars private readonly Direct3D _D3D; @@ -168,9 +185,6 @@ namespace Vocaluxe.Lib.Draw } } - #region form events - #endregion form events - #region resize /// /// Resizes the viewport @@ -468,37 +482,33 @@ namespace Vocaluxe.Lib.Draw /// public CTextureRef CopyScreen() { - CTextureRef texture = _GetNewTextureRef(_W, _H); - + var tex = _CreateTexture(new Size(_W, _H)); Surface backbufferSurface = _Device.GetBackBuffer(0, 0); - var tex = new Texture(_Device, texture.W2, texture.H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed); - Surface textureSurface = tex.GetSurfaceLevel(0); + Surface textureSurface = tex.D3DTexture.GetSurfaceLevel(0); Surface.FromSurface(textureSurface, backbufferSurface, Filter.Default, 0, new Rectangle(0, 0, _W, _H), new Rectangle(0, 0, _W, _H)); backbufferSurface.Dispose(); - lock (_Textures) - { - _Textures.Add(texture.ID, tex); - } - return texture; + return _GetTextureReference(_W, _H, tex); } /// /// Copies the current frame into a texture /// - /// The texture in which the frame is copied to - public void CopyScreen(ref CTextureRef texture) + /// The texture in which the frame is copied to + public void CopyScreen(ref CTextureRef textureRef) { - if (!_TextureExists(texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) { - RemoveTexture(ref texture); - texture = CopyScreen(); + RemoveTexture(ref textureRef); + textureRef = CopyScreen(); } else { Surface backbufferSurface = _Device.GetBackBuffer(0, 0); - Surface textureSurface = _Textures[texture.ID].GetSurfaceLevel(0); + Surface textureSurface = texture.D3DTexture.GetSurfaceLevel(0); Surface.FromSurface(textureSurface, backbufferSurface, Filter.Default, 0); + backbufferSurface.Dispose(); } } @@ -566,12 +576,17 @@ namespace Vocaluxe.Lib.Draw #endregion Basic Draw Methods #region Textures - protected override Texture _CreateTexture(CTextureRef texture, byte[] data) + private CD3DTexture _CreateTexture(Size dataSize) + { + return new CD3DTexture(_Device, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height)); + } + + protected override CD3DTexture _CreateTexture(Size dataSize, byte[] data) { //Create a new texture in the managed pool, which does not need to be recreated on a lost device //because a copy of the texture is hold in the Ram - var t = new Texture(_Device, texture.W2, texture.H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed); - _WriteDataToTexture(t, texture.DataSize.Width, data); + CD3DTexture t = _CreateTexture(dataSize); + _WriteDataToTexture(t.D3DTexture, dataSize.Width, data); return t; } @@ -597,14 +612,15 @@ namespace Vocaluxe.Lib.Draw /// /// Updates the data of a texture /// - /// The texture to update + /// The texture to update /// /// /// A byte array containing the new texture's data /// True if succeeded - public override bool UpdateTexture(CTextureRef texture, int w, int h, byte[] data) + public override bool UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data) { - if (!_TextureExists(texture)) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return false; if (texture.DataSize.Width != w || texture.DataSize.Height != h) { @@ -614,10 +630,7 @@ namespace Vocaluxe.Lib.Draw return false; // Texture memory to big texture.DataSize = new Size(w, h); } - lock (_Textures) - { - _WriteDataToTexture(_Textures[texture.ID], w, data); - } + _WriteDataToTexture(texture.D3DTexture, w, data); return true; } @@ -648,17 +661,17 @@ namespace Vocaluxe.Lib.Draw /// /// Draws a texture /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// True if the texture should be mirrored - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false) { if (rect.W < 1 || rect.H < 1) return; - Texture t = _GetTexture(texture); - if (t == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; //Calculate the position @@ -687,18 +700,18 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); - _AddToVertexBuffer(vert, t, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a texture /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// A SRectF struct containing which part of the texture should be drawn /// True if the texture should be mirrored - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) { if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(bounds.H) < 1 || Math.Abs(bounds.W) < 1 || Math.Abs(color.A) < 0.01) @@ -710,8 +723,8 @@ namespace Vocaluxe.Lib.Draw if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y) return; - Texture t = _GetTexture(texture); - if (t == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; //Calculate the position @@ -764,22 +777,21 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); - _AddToVertexBuffer(vert, t, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a texture /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// A Value ranging from 0 to 1 containing the beginning of the texture /// A Value ranging from 0 to 1 containing the ending of the texture - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, float begin, float end) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end) { - if (!_TextureExists(texture)) - return; - if (_Textures[texture.ID] == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; float x1 = 0f + begin * texture.WidthRatio; @@ -807,23 +819,22 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); - _AddToVertexBuffer(vert, _Textures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a reflection of a texture /// - /// The texture of which a reflection should be drawn + /// The texture of which a reflection should be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// A SRectF struct containing which part of the texture should be drawn /// The space between the texture and the reflection /// The height of the reflection - public void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) + public void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, float space, float height) { - if (!_TextureExists(texture)) - return; - if (_Textures[texture.ID] == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon || @@ -890,7 +901,7 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y1), transparent.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), transparent.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); - _AddToVertexBuffer(vert, _Textures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } #endregion drawing @@ -898,7 +909,6 @@ namespace Vocaluxe.Lib.Draw #endregion implementation - #region utility private static Matrix _CalculateRotationMatrix(float rot, float rx1, float rx2, float ry1, float ry2) { Matrix originTranslation = Matrix.Translation(new Vector3(-(float)CSettings.RenderW / 2, (float)CSettings.RenderH / 2, 0)); @@ -921,7 +931,6 @@ namespace Vocaluxe.Lib.Draw } return originTranslation; } - #endregion utility private struct STexturedColoredVertex { diff --git a/Vocaluxe/Lib/Draw/CDrawBase.cs b/Vocaluxe/Lib/Draw/CDrawBase.cs index 45f9f388..3d56f265 100644 --- a/Vocaluxe/Lib/Draw/CDrawBase.cs +++ b/Vocaluxe/Lib/Draw/CDrawBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; @@ -10,23 +9,34 @@ using System.Threading; using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Draw; +using System.Diagnostics; namespace Vocaluxe.Lib.Draw { - struct STextureQueue + abstract class CDrawBase where TTextureType : CTextureBase, IDisposable { - public readonly CTextureRef Texture; - public readonly byte[] Data; - - public STextureQueue(CTextureRef texture, byte[] data) + struct STextureQueue { - Texture = texture; - Data = data; + public readonly int TextureID; + public readonly Size DataSize; + public readonly byte[] Data; + public STextureQueue(int textureID, Size dataSize, byte[] data) + { + TextureID = textureID; + DataSize = dataSize; + Data = data; + } + } + + struct STextureCacheEntry + { + public TTextureType Texture; + /// + /// Size of original image (e.g. of bmp) + /// + public Size OrigSize; } - } - abstract class CDrawBase where TTextureType : class, IDisposable - { protected bool _NonPowerOf2TextureSupported; protected bool _Fullscreen; @@ -38,9 +48,12 @@ namespace Vocaluxe.Lib.Draw private readonly Object _MutexID = new object(); private int _NextID; - protected readonly Dictionary _Textures = new Dictionary(); - protected readonly Dictionary _TextureCache = new Dictionary(); + // Maps texture IDs to textures. Multiple IDs can be mapped to one texture (-->texture.RefCount>1) + private readonly Dictionary _Textures = new Dictionary(); + // Maps texturePaths to textures. Texture may have already been disposed (RefCount<=0) or even null + private readonly Dictionary _TextureCache = new Dictionary(); private readonly Queue _TexturesToLoad = new Queue(); + private int _TextureCount; protected int _H; protected int _W; @@ -72,8 +85,9 @@ namespace Vocaluxe.Lib.Draw //Dispose all textures foreach (TTextureType texture in _Textures.Values) { - if (texture != null) + if (texture != null && --texture.RefCount<=0) texture.Dispose(); + } _Textures.Clear(); _TextureCache.Clear(); @@ -127,7 +141,7 @@ namespace Vocaluxe.Lib.Draw /// /// The value of which the next power of two will be calculated /// The next power of two - private int _CheckForNextPowerOf2(int n) + protected int _CheckForNextPowerOf2(int n) { if (_NonPowerOf2TextureSupported) return n; @@ -140,37 +154,54 @@ namespace Vocaluxe.Lib.Draw /// /// Creates the texture specified by the reference and fills it with the given data /// - /// + /// /// /// - protected abstract TTextureType _CreateTexture(CTextureRef texture, byte[] data); + protected abstract TTextureType _CreateTexture(Size dataSize, byte[] data); /// /// Creates the texture specified by the reference and fills it with the given data
/// Data has to be a pointer to an array of size W*H with 4 Byte values for each pixel (e.g. bmpData.Scan0) ///
- /// + /// /// /// - protected virtual TTextureType _CreateTexture(CTextureRef texture, IntPtr data) + protected virtual TTextureType _CreateTexture(Size dataSize, IntPtr data) { - byte[] dataArray = new byte[4 * texture.DataSize.Width * texture.DataSize.Height]; + byte[] dataArray = new byte[4 * dataSize.Width * dataSize.Height]; Marshal.Copy(data, dataArray, 0, dataArray.Length); - return _CreateTexture(texture, dataArray); + return _CreateTexture(dataSize, dataArray); } - protected CTextureRef _GetNewTextureRef(int origWidth, int origHeight, int dataWidth = 0, int dataHeight = 0) + private void _AddToCache(TTextureType texture, Size origSize, string texturePath) { - Debug.Assert(origWidth > 0 && origHeight > 0); - Debug.Assert(dataWidth > 0 || dataHeight <= 0); - int id; - lock (_MutexID) + if (texture != null && !String.IsNullOrEmpty(texturePath)) { - id = _NextID++; + texture.TexturePath = texturePath; + STextureCacheEntry cacheEntry=new STextureCacheEntry {OrigSize = origSize, Texture = texture}; + lock (_Textures) + { + _TextureCache[texturePath] = cacheEntry; + } } - Size origSize = new Size(origWidth, origHeight); - Size dataSize = (dataHeight > 0) ? new Size(dataWidth, dataHeight) : origSize; - return new CTextureRef(id, origSize, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height)); + } + + private CTextureRef _GetFromCache(string texturePath) + { + lock (_Textures) + { + STextureCacheEntry cacheEntry; + if (_TextureCache.TryGetValue(texturePath, out cacheEntry)) + { + if (cacheEntry.Texture.RefCount <= 0) + _TextureCache.Remove(texturePath); + else + { + return _GetTextureReference(cacheEntry.OrigSize, cacheEntry.Texture); + } + } + } + return null; } /// @@ -180,22 +211,9 @@ namespace Vocaluxe.Lib.Draw /// A STexture object containing the added texture public CTextureRef AddTexture(string texturePath) { - CTextureRef textureRef; - lock (_Textures) - { - WeakReference reference; - if (_TextureCache.TryGetValue(texturePath, out reference)) - { - if (!reference.IsAlive) - _TextureCache.Remove(texturePath); - else - { - textureRef = (CTextureRef)reference.Target; - textureRef.RefCount++; - return textureRef; - } - } - } + CTextureRef textureRef = _GetFromCache(texturePath); + if (textureRef != null) + return textureRef; if (!File.Exists(texturePath)) { @@ -220,16 +238,12 @@ namespace Vocaluxe.Lib.Draw { bmp.Dispose(); } - lock (_Textures) - { - _TextureCache[texturePath] = new WeakReference(textureRef); - } return textureRef; } public CTextureRef AddTexture(Bitmap bmp) { - return AddTexture(bmp, ""); + return AddTexture(bmp, null); } /// @@ -266,18 +280,17 @@ namespace Vocaluxe.Lib.Draw break; } - int w = Math.Min(bmp.Width, maxSize); - int h = Math.Min(bmp.Height, maxSize); - - CTextureRef texture = _GetNewTextureRef(bmp.Width, bmp.Height, w, h); - texture.TexturePath = texturePath; + Size origSize=new Size(bmp.Width, bmp.Height); Bitmap bmp2 = null; + TTextureType texture; try { + // Make sure maxSize is a power of 2 if required + maxSize = _CheckForNextPowerOf2(maxSize); // Do not stretch the texture, only make it smaller - w = Math.Min(bmp.Width, texture.W2); - h = Math.Min(bmp.Height, texture.H2); + int w = Math.Min(bmp.Width, maxSize); + int h = Math.Min(bmp.Height, maxSize); if (bmp.Width != w || bmp.Height != h) { //Create a new Bitmap with the new sizes @@ -290,12 +303,11 @@ namespace Vocaluxe.Lib.Draw g.DrawImage(bmp, new Rectangle(0, 0, bmp2.Width, bmp2.Height)); } bmp = bmp2; - texture.DataSize = new Size(w, h); } //Fill the new Bitmap with the texture data BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - _AddTexture(texture, bmpData.Scan0); + texture=_CreateTexture(new Size(w,h), bmpData.Scan0); bmp.UnlockBits(bmpData); } finally @@ -304,44 +316,64 @@ namespace Vocaluxe.Lib.Draw bmp2.Dispose(); } - return texture; + _AddToCache(texture, origSize, texturePath); + return _GetTextureReference(origSize,texture); } public CTextureRef AddTexture(int w, int h, byte[] data) { - CTextureRef texture = _GetNewTextureRef(w, h); - _AddTexture(texture, data); - return texture; + TTextureType texture = _CreateTexture(new Size(w, h), data); + return _GetTextureReference(w,h, texture); } - private void _AddTexture(CTextureRef texture, byte[] data) + /// + /// Method used to add a texture to the texture list and return a TextureRef to it
+ /// Important: This should be the only function to ever write to _Textures + ///
+ /// + /// + /// + protected CTextureRef _GetTextureReference(Size origSize, TTextureType texture) { - TTextureType t = _CreateTexture(texture, data); + Debug.Assert(origSize.Width>0 && origSize.Height>0); + int id; + lock (_MutexID) + { + id = _NextID++; + } + CTextureRef textureRef = new CTextureRef(id, origSize); + if (texture != null) + texture.RefCount++; + if(texture==null || texture.RefCount==1) + _TextureCount++; lock (_Textures) { - _Textures.Add(texture.ID, t); + _Textures.Add(id, texture); } + return textureRef; } - private void _AddTexture(CTextureRef texture, IntPtr data) + /// + /// Method used to add a texture to the texture list and return a TextureRef to it
+ /// Convenience method + ///
+ /// + /// + /// + /// + protected CTextureRef _GetTextureReference(int origWidth, int origHeight, TTextureType texture) { - TTextureType t = _CreateTexture(texture, data); - lock (_Textures) - { - _Textures.Add(texture.ID, t); - } + return _GetTextureReference(new Size(origWidth, origHeight), texture); } public CTextureRef EnqueueTexture(int w, int h, byte[] data) { - CTextureRef texture = _GetNewTextureRef(w, h); - lock (_Textures) { - _Textures.Add(texture.ID, null); - _TexturesToLoad.Enqueue(new STextureQueue(texture, data)); + CTextureRef textureRef = _GetTextureReference(w, h, null); + _TexturesToLoad.Enqueue(new STextureQueue(textureRef.ID, new Size(w,h),data)); + return textureRef; } - return texture; } /// @@ -371,11 +403,8 @@ namespace Vocaluxe.Lib.Draw /// True if the texture exists protected bool _TextureExists(CTextureRef texture) { - lock (_Textures) - { TTextureType t; - return texture != null && _Textures.TryGetValue(texture.ID, out t) && t != null; - } + return _GetTexture(texture, out t); } protected TTextureType _GetTexture(CTextureRef textureRef) @@ -383,10 +412,19 @@ namespace Vocaluxe.Lib.Draw if (textureRef == null) return null; TTextureType texture; - _Textures.TryGetValue(textureRef.ID, out texture); + lock (_Textures) + { + _Textures.TryGetValue(textureRef.ID, out texture); + } return texture; } + protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture) + { + texture = _GetTexture(textureRef); + return texture != null; + } + /// /// Removes a texture from the Vram /// @@ -397,41 +435,42 @@ namespace Vocaluxe.Lib.Draw return; lock (_Textures) { - if (--texture.RefCount <= 0) - { TTextureType t; if (_Textures.TryGetValue(texture.ID, out t)) { - if (t != null) + if (t != null && --t.RefCount <= 0) + { + if(t.TexturePath!=null) _TextureCache.Remove(t.TexturePath); t.Dispose(); + _TextureCount--; + } _Textures.Remove(texture.ID); } - _TextureCache.Remove(texture.TexturePath); - } texture.ID = -1; } texture = null; } - protected void _CheckQueue() + private void _CheckQueue() { lock (_Textures) { while (_TexturesToLoad.Count > 0) { STextureQueue q = _TexturesToLoad.Dequeue(); - if (!_Textures.ContainsKey(q.Texture.ID)) + if (!_Textures.ContainsKey(q.TextureID)) continue; - TTextureType t = _CreateTexture(q.Texture, q.Data); - _Textures[q.Texture.ID] = t; + TTextureType texture = _CreateTexture(q.DataSize, q.Data); + texture.RefCount = 1; //Currently no more than 1 reference to queued textures is possible + _Textures[q.TextureID] = texture; } } } public int GetTextureCount() { - return _Textures.Count; + return _TextureCount; } #endregion diff --git a/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs index 77c3ac9c..eb3a4b5e 100644 --- a/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs +++ b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs @@ -15,7 +15,7 @@ namespace Vocaluxe.Lib.Draw MessageEventHandler OnMessage { set; } } - abstract class CDrawBaseWindows : CDrawBase where TTextureType : class, IDisposable + abstract class CDrawBaseWindows : CDrawBase where TTextureType : CTextureBase, IDisposable { private struct SClientRect { diff --git a/Vocaluxe/Lib/Draw/CDrawWinForm.cs b/Vocaluxe/Lib/Draw/CDrawWinForm.cs index 14e40292..ee09c54c 100644 --- a/Vocaluxe/Lib/Draw/CDrawWinForm.cs +++ b/Vocaluxe/Lib/Draw/CDrawWinForm.cs @@ -45,6 +45,7 @@ namespace Vocaluxe.Lib.Draw private Rectangle _Bounds; private readonly Dictionary _Textures = new Dictionary(); + private readonly Dictionary _TextureCache = new Dictionary(); private readonly List _Bitmaps = new List(); private readonly Color _ClearColor = Color.DarkBlue; @@ -64,7 +65,6 @@ namespace Vocaluxe.Lib.Draw _G = Graphics.FromImage(_Backbuffer); _G.Clear(Color.DarkBlue); - Paint += _OnPaintEvent; Closing += _OnClosingEvent; KeyDown += _OnKeyDownEvent; KeyPress += _OnKeyPressEvent; @@ -107,8 +107,6 @@ namespace Vocaluxe.Lib.Draw frontBuffer.DrawImage(_Backbuffer, new Rectangle(x, y, w, h), new Rectangle(0, 0, _Backbuffer.Width, _Backbuffer.Height), GraphicsUnit.Pixel); } - private void _OnPaintEvent(object sender, PaintEventArgs e) {} - #region FullScreenStuff private void _ToggleFullScreen() { @@ -293,7 +291,7 @@ namespace Vocaluxe.Lib.Draw public void CopyScreen(ref CTextureRef texture) { - if (!_TextureExists(texture) || texture.W2 != GetScreenWidth() || texture.H2 != GetScreenHeight()) + if (!_TextureExists(texture) || texture.OrigSize.Width != GetScreenWidth() || texture.OrigSize.Height != GetScreenHeight()) { RemoveTexture(ref texture); texture = CopyScreen(); @@ -332,23 +330,22 @@ namespace Vocaluxe.Lib.Draw private CTextureRef _GetNewTexture(int w, int h) { - return new CTextureRef(_Bitmaps.Count - 1, new Size(w, h), new Size(w, h)); + return new CTextureRef(_Bitmaps.Count - 1, new Size(w, h)); } public CTextureRef AddTexture(string texturePath) { if (File.Exists(texturePath)) { - foreach (CTextureRef tex in _Textures.Values) - { - if (tex.TexturePath == texturePath) - return tex; - } + CTextureRef texture; + if (_TextureCache.TryGetValue(texturePath, out texture)) + return texture; using (var bmp = new Bitmap(texturePath)) { - CTextureRef texture = AddTexture(bmp); - texture.TexturePath = texturePath; + texture = AddTexture(bmp); + _TextureCache.Add(texturePath,texture); } + return texture; } return null; diff --git a/Vocaluxe/Lib/Draw/COpenGL.cs b/Vocaluxe/Lib/Draw/COpenGL.cs index fa854c0b..29e5b82b 100644 --- a/Vocaluxe/Lib/Draw/COpenGL.cs +++ b/Vocaluxe/Lib/Draw/COpenGL.cs @@ -60,19 +60,19 @@ namespace Vocaluxe.Lib.Draw } } - class CGLTexture : IDisposable + class COGLTexture : CTextureBase,IDisposable { public int PBO; //The texture "name" according to the specs public readonly int Name; - public CGLTexture(int name) + private bool _IsDisposed; + + public COGLTexture( int name, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight) { Name = name; } - private bool _IsDisposed; - public void Dispose() { if (_IsDisposed) @@ -81,10 +81,11 @@ namespace Vocaluxe.Lib.Draw GL.DeleteTexture(Name); if (PBO > 0) GL.DeleteBuffers(1, ref PBO); + RefCount = 0; } } - class COpenGL : CDrawBaseWindows, IDraw + class COpenGL : CDrawBaseWindows, IDraw { #region private vars private readonly GLControl _Control; @@ -277,39 +278,34 @@ namespace Vocaluxe.Lib.Draw public CTextureRef CopyScreen() { - CTextureRef texture = _GetNewTextureRef(_W, _H); - - CGLTexture t = new CGLTexture(GL.GenTexture()); - GL.BindTexture(TextureTarget.Texture2D, t.Name); + //TODO: Check if _W,_H needs to be used or not + Size size = new Size(GetScreenWidth(), GetScreenHeight()); + COGLTexture texture = _CreateTexture(size); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); - GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, _X, _Y, _W, _H); + GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, size.Width,size.Height);//TODO: Use _X,_Y and _W,_H? GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.BindTexture(TextureTarget.Texture2D, 0); - // Add to Texture List - lock (_Textures) - { - _Textures[texture.ID] = t; - } - - return texture; + return _GetTextureReference(size, texture); } - public void CopyScreen(ref CTextureRef texture) + public void CopyScreen(ref CTextureRef textureRef) { + COGLTexture texture; //Check for actual texture sizes as it may be downsized compared to OrigSize - if (!_TextureExists(texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) + if (!_GetTexture(textureRef, out texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) { - RemoveTexture(ref texture); - texture = CopyScreen(); + RemoveTexture(ref textureRef); + textureRef = CopyScreen(); } else { - GL.BindTexture(TextureTarget.Texture2D, _Textures[texture.ID].Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, GetScreenWidth(), GetScreenHeight()); @@ -419,11 +415,16 @@ namespace Vocaluxe.Lib.Draw #region Textures #region adding - protected override CGLTexture _CreateTexture(CTextureRef texture, IntPtr data) + private COGLTexture _CreateTexture(Size dataSize) { - CGLTexture t = new CGLTexture(GL.GenTexture()); + return new COGLTexture(GL.GenTexture(), dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height)); + } - GL.BindTexture(TextureTarget.Texture2D, t.Name); + protected override COGLTexture _CreateTexture(Size dataSize, IntPtr data) + { + COGLTexture texture = _CreateTexture(dataSize); + + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); @@ -436,19 +437,19 @@ namespace Vocaluxe.Lib.Draw GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); GL.BindTexture(TextureTarget.Texture2D, 0); - return t; + return texture; } - protected override CGLTexture _CreateTexture(CTextureRef texture, byte[] data) + protected override COGLTexture _CreateTexture(Size dataSize, byte[] data) { - CGLTexture t = new CGLTexture(GL.GenTexture()); + COGLTexture texture = _CreateTexture(dataSize); if (_UsePBO) { try { - GL.GenBuffers(1, out t.PBO); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, t.PBO); + GL.GenBuffers(1, out texture.PBO); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, texture.PBO); GL.BufferData(BufferTarget.PixelUnpackBuffer, (IntPtr)data.Length, IntPtr.Zero, BufferUsageHint.StreamDraw); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); } @@ -459,10 +460,10 @@ namespace Vocaluxe.Lib.Draw } } - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.OrigSize.Width, texture.OrigSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data); + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); @@ -471,15 +472,15 @@ namespace Vocaluxe.Lib.Draw GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); GL.BindTexture(TextureTarget.Texture2D, 0); - return t; + return texture; } #endregion adding #region updating - public override bool UpdateTexture(CTextureRef texture, int w, int h, byte[] data) + public override bool UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data) { - CGLTexture t = _GetTexture(texture); - if (t == null) + COGLTexture texture; + if (_GetTexture(textureRef,out texture)) return false; if (texture.DataSize.Width != w || texture.DataSize.Height != h) { @@ -492,14 +493,14 @@ namespace Vocaluxe.Lib.Draw if (_UsePBO) { - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, t.PBO); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, texture.PBO); IntPtr buffer = GL.MapBuffer(BufferTarget.PixelUnpackBuffer, BufferAccess.WriteOnly); Marshal.Copy(data, 0, buffer, data.Length); GL.UnmapBuffer(BufferTarget.PixelUnpackBuffer); - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, w, h, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); @@ -511,7 +512,7 @@ namespace Vocaluxe.Lib.Draw return true; } - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, w, h, PixelFormat.Bgra, PixelType.UnsignedByte, data); @@ -538,16 +539,16 @@ namespace Vocaluxe.Lib.Draw DrawTexture(texture, rect, texture.Color); } - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false) { - CGLTexture t = _GetTexture(texture); - if (t == null) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(color.A) < 0.01) return; - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); float x1 = 0; float x2 = texture.WidthRatio; @@ -601,10 +602,10 @@ namespace Vocaluxe.Lib.Draw GL.BindTexture(TextureTarget.Texture2D, 0); } - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) { - CGLTexture t = _GetTexture(texture); - if (t == null) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(bounds.H) < 1 || Math.Abs(bounds.W) < 1 || @@ -617,7 +618,7 @@ namespace Vocaluxe.Lib.Draw if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y) return; - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio; float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio; @@ -696,13 +697,13 @@ namespace Vocaluxe.Lib.Draw GL.BindTexture(TextureTarget.Texture2D, 0); } - public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, float begin, float end) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end) { - CGLTexture t = _GetTexture(texture); - if (t == null) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.Enable(EnableCap.Blend); GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha); @@ -729,10 +730,10 @@ namespace Vocaluxe.Lib.Draw GL.BindTexture(TextureTarget.Texture2D, 0); } - public void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) + public void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, float space, float height) { - CGLTexture t = _GetTexture(texture); - if (t == null) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon || @@ -748,7 +749,7 @@ namespace Vocaluxe.Lib.Draw if (height > bounds.H) height = bounds.H; - GL.BindTexture(TextureTarget.Texture2D, t.Name); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio; float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio; diff --git a/Vocaluxe/Lib/Draw/CTextureBase.cs b/Vocaluxe/Lib/Draw/CTextureBase.cs new file mode 100644 index 00000000..2d79345e --- /dev/null +++ b/Vocaluxe/Lib/Draw/CTextureBase.cs @@ -0,0 +1,102 @@ +using System.Drawing; + +namespace Vocaluxe.Lib.Draw +{ + abstract class CTextureBase + { + public int RefCount; + public string TexturePath; + + private Size _DataSize; + /// + /// Size of data used (mostly equal to OrigSize but due to resizing this might change) + /// + public Size DataSize + { + get { return _DataSize; } + set + { + _DataSize = value; + _CalcRatios(); + } + } + + private int _W2, _H2; + /// + /// Internal texture width (on device), a power of 2 if necessary + /// + public int W2 + { + get { return _W2; } + set + { + _W2 = value; + _CalcRatios(); + } + } + /// + /// Internal texture height (on device), a power of 2 if necessary + /// + public int H2 + { + get { return _H2; } + set + { + _H2 = value; + _CalcRatios(); + } + } + + /// + /// Internal use. Specifies which part of texture memory is actually used + /// + public float WidthRatio { get; private set; } + /// + /// Internal use. Specifies which part of texture memory is actually used + /// + public float HeightRatio { get; private set; } + + /// + /// Creates a new texture reference + /// + /// Size of the data used + /// Width in video memory + /// Height in video memory + public CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0) + { + _DataSize = dataSize; + _W2 = (texWidth > 0) ? texWidth : dataSize.Width; + _H2 = (texHeight > 0) ? texHeight : dataSize.Height; + + _CalcRatios(); + } + + public bool IsValid() + { + return RefCount > 0; + } + + private void _CalcRatios() + { + // OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel + if (_DataSize.Width == _W2) + WidthRatio = 1f; + else + { + int mod = 0; + if (_DataSize.Width > 1) + mod = -1; + WidthRatio = (float)(_DataSize.Width + mod) / _W2; + } + if (_DataSize.Height == _H2) + HeightRatio = 1f; + else + { + int mod = 0; + if (_DataSize.Height > 1) + mod = -1; + HeightRatio = (float)(_DataSize.Height + mod) / _H2; + } + } + } +} diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index aed2508e..5a54ddfb 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -226,6 +226,7 @@ + diff --git a/VocaluxeLib/Draw/CTextureRef.cs b/VocaluxeLib/Draw/CTextureRef.cs index b05b6607..3bc6526a 100644 --- a/VocaluxeLib/Draw/CTextureRef.cs +++ b/VocaluxeLib/Draw/CTextureRef.cs @@ -25,9 +25,6 @@ namespace VocaluxeLib.Draw public class CTextureRef { public int ID; - public int RefCount = 1; - - public string TexturePath = ""; /// /// Size of original image (e.g. of bmp) @@ -39,20 +36,6 @@ namespace VocaluxeLib.Draw get { return (float)OrigSize.Width / OrigSize.Height; } } - private Size _DataSize; - /// - /// Size of data used (mostly equal to OrigSize but due to resizing this might change) - /// - public Size DataSize - { - get { return _DataSize; } - set - { - _DataSize = value; - _CalcRatios(); - } - } - /// /// Current size when drawn /// @@ -60,60 +43,17 @@ namespace VocaluxeLib.Draw public SColorF Color = new SColorF(1f, 1f, 1f, 1f); - private int _W2, _H2; - /// - /// Internal texture width (on device), a power of 2 if necessary - /// - public int W2 - { - get { return _W2; } - set - { - _W2 = value; - _CalcRatios(); - } - } - /// - /// Internal texture height (on device), a power of 2 if necessary - /// - public int H2 - { - get { return _H2; } - set - { - _H2 = value; - _CalcRatios(); - } - } - - /// - /// Internal use. Specifies which part of texture memory is actually used - /// - public float WidthRatio { get; private set; } - /// - /// Internal use. Specifies which part of texture memory is actually used - /// - public float HeightRatio { get; private set; } - /// /// Creates a new texture reference /// /// ID of the texture /// Original size (Bitmap size) - /// Size of the data used - /// Width in video memory - /// Height in video memory - public CTextureRef(int id, Size origSize, Size dataSize, int texWidth = 0, int texHeight = 0) + public CTextureRef(int id, Size origSize) { ID = id; - _DataSize = dataSize; OrigSize = origSize; - _W2 = (texWidth > 0) ? texWidth : dataSize.Width; - _H2 = (texHeight > 0) ? texHeight : dataSize.Height; Rect = new SRectF(0f, 0f, origSize.Width, origSize.Height, 0f); - - _CalcRatios(); } ~CTextureRef() @@ -125,28 +65,5 @@ namespace VocaluxeLib.Draw CBase.Drawing.RemoveTexture(ref tmp); } } - - private void _CalcRatios() - { - // OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel - if (_DataSize.Width == _W2) - WidthRatio = 1f; - else - { - int mod = 0; - if (_DataSize.Width > 1) - mod = -1; - WidthRatio = (float)(_DataSize.Width + mod) / _W2; - } - if (_DataSize.Height == _H2) - HeightRatio = 1f; - else - { - int mod = 0; - if (_DataSize.Height > 1) - mod = -1; - HeightRatio = (float)(_DataSize.Height + mod) / _H2; - } - } } } \ No newline at end of file