From: Latif Khalifa Date: Mon, 25 Jul 2011 18:11:14 +0000 (+0000) Subject: Experimental decoded texture caching X-Git-Tag: 2.8~363 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e3a128f22bf19050eb977f6d84e55779f768d1e4;p=radegast%2Fradegast.git Experimental decoded texture caching git-svn-id: https://radegast.googlecode.com/svn/trunk@1008 f7a694da-4d33-11de-9ad6-1127a62b9fcd --- diff --git a/Radegast/GUI/Rendering/Rendering.cs b/Radegast/GUI/Rendering/Rendering.cs index 991665b..34b39c9 100644 --- a/Radegast/GUI/Rendering/Rendering.cs +++ b/Radegast/GUI/Rendering/Rendering.cs @@ -117,7 +117,6 @@ namespace Radegast.Rendering Camera Camera; Dictionary TexturesPtrMap = new Dictionary(); - RadegastInstance instance; MeshmerizerR renderer; OpenTK.Graphics.GraphicsMode GLMode = null; AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false); @@ -145,6 +144,7 @@ namespace Radegast.Rendering OpenTK.Vector4 specularColor; float drawDistance = 48f; float drawDistanceSquared = 48f * 48f; + bool useDecodedImageCache = true; GridClient Client; RadegastInstance Instance; @@ -160,7 +160,7 @@ namespace Radegast.Rendering this.Instance = instance; this.Client = instance.Client; - + UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"]; cbAA.CheckedChanged += cbAA_CheckedChanged; @@ -642,7 +642,7 @@ namespace Radegast.Rendering if (msToSleep < 10) msToSleep = 10; Thread.Sleep(msToSleep); } - + Render(false); glControl.SwapBuffers(); @@ -891,10 +891,14 @@ namespace Radegast.Rendering // Already have this one loaded if (item.Data.TextureInfo.TexturePointer != 0) continue; - if (item.TextureData != null) + byte[] imageBytes = null; + if (item.TGAData != null) + { + imageBytes = item.TGAData; + } + else if (item.TextureData != null) { ManagedImage mi; - Image img; if (!OpenJPEG.DecodeToImage(item.TextureData, out mi)) continue; bool hasAlpha = false; @@ -928,21 +932,36 @@ namespace Radegast.Rendering } } - using (MemoryStream byteData = new MemoryStream(mi.ExportTGA())) + item.Data.TextureInfo.HasAlpha = hasAlpha; + item.Data.TextureInfo.FullAlpha = fullAlpha; + item.Data.TextureInfo.IsMask = isMask; + + imageBytes = mi.ExportTGA(); + if (useDecodedImageCache) + { + RHelp.SaveCachedImage(imageBytes, item.TeFace.TextureID, hasAlpha, fullAlpha, isMask); + } + } + + if (imageBytes != null) + { + Image img; + + using (MemoryStream byteData = new MemoryStream(imageBytes)) { img = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(byteData); } Bitmap bitmap = (Bitmap)img; - item.Data.TextureInfo.HasAlpha = hasAlpha; - item.Data.TextureInfo.FullAlpha = fullAlpha; - item.Data.TextureInfo.IsMask = isMask; bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); - item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, hasAlpha); + item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, item.Data.TextureInfo.HasAlpha); bitmap.Dispose(); - item.TextureData = null; } + + item.TextureData = null; + item.TGAData = null; + imageBytes = null; } context.Dispose(); window.Dispose(); @@ -2409,20 +2428,26 @@ namespace Radegast.Rendering else { TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo; - - if (item.TextureData == null) + if (item.TextureData == null && item.TGAData == null) { - ThreadPool.QueueUserWorkItem(sync => + if (useDecodedImageCache && RHelp.LoadCachedImage(item.TeFace.TextureID, out item.TGAData, out item.Data.TextureInfo.HasAlpha, out item.Data.TextureInfo.FullAlpha, out item.Data.TextureInfo.IsMask)) { - Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) => + PendingTextures.Enqueue(item); + } + else + { + ThreadPool.QueueUserWorkItem(sync => { - if (state == TextureRequestState.Finished) + Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) => { - item.TextureData = asset.AssetData; - PendingTextures.Enqueue(item); - } + if (state == TextureRequestState.Finished) + { + item.TextureData = asset.AssetData; + PendingTextures.Enqueue(item); + } + }); }); - }); + } } else { @@ -2461,15 +2486,15 @@ namespace Radegast.Rendering // Need to adjust UV for spheres as they are sort of half-prim if (prim.PrimData.ProfileCurve == ProfileCurve.HalfCircle) { - teFace = new Primitive.TextureEntryFace(teFace); + teFace = (Primitive.TextureEntryFace)teFace.Clone(); teFace.RepeatV *= 2; teFace.OffsetV += 0.5f; } - // Sculpt UV map seems to come out vertically flipped from the PrimMesher. Fix it - if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero) + // Sculpt UV vertically flipped compared to prims. Flip back + if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && prim.Sculpt.Type != SculptType.Mesh) { - teFace = new Primitive.TextureEntryFace(teFace); + teFace = (Primitive.TextureEntryFace)teFace.Clone(); teFace.RepeatV *= -1; } @@ -2602,27 +2627,40 @@ namespace Radegast.Rendering try { gotImage.Reset(); - instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) => - { - if (state == TextureRequestState.Finished) + bool hasAlpha, fullAlpha, isMask; + byte[] tgaData; + if (useDecodedImageCache && RHelp.LoadCachedImage(textureID, out tgaData, out hasAlpha, out fullAlpha, out isMask)) + { + img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData)); + } + else + { + instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) => { - ManagedImage mi; - OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi); - - if (removeAlpha) + if (state == TextureRequestState.Finished) { - if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0) + ManagedImage mi; + OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi); + + if (removeAlpha) + { + if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0) + { + mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha); + } + } + tgaData = mi.ExportTGA(); + img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData)); + if (useDecodedImageCache) { - mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha); + RHelp.SaveCachedImage(tgaData, textureID, (mi.Channels & ManagedImage.ImageChannels.Alpha) != 0, false, false); } } - - img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA())); + gotImage.Set(); } - gotImage.Set(); - } - ); - gotImage.WaitOne(30 * 1000, false); + ); + gotImage.WaitOne(30 * 1000, false); + } if (img != null) { texture = img; diff --git a/Radegast/GUI/Rendering/RenderingHelpers.cs b/Radegast/GUI/Rendering/RenderingHelpers.cs index e41bd4e..61cab97 100644 --- a/Radegast/GUI/Rendering/RenderingHelpers.cs +++ b/Radegast/GUI/Rendering/RenderingHelpers.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Linq; using System.Text; using System.IO; +using System.IO.Compression; using System.Xml; using System.Threading; using OpenTK.Graphics.OpenGL; @@ -273,6 +274,7 @@ namespace Radegast.Rendering public Primitive Prim; public Primitive.TextureEntryFace TeFace; public byte[] TextureData = null; + public byte[] TGAData = null; } public enum RenderPass @@ -478,6 +480,112 @@ namespace Radegast.Rendering { return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W); } + + #region Cached image save and load + public static readonly string RAD_IMG_MAGIC = "radegast_img"; + + public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask) + { + tgaData = null; + hasAlpha = fullAlpha = isMask = false; + + try + { + string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID)); + //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID)); + + using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + byte[] header = new byte[36]; + int i = 0; + f.Read(header, 0, header.Length); + + // check if the file is starting with magic string + if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length)) + return false; + i += RAD_IMG_MAGIC.Length; + + if (header[i++] != 1) // check version + return false; + + hasAlpha = header[i++] == 1; + fullAlpha = header[i++] == 1; + isMask = header[i++] == 1; + + int uncompressedSize = Utils.BytesToInt(header, i); + i += 4; + + textureID = new UUID(header, i); + i += 16; + + tgaData = new byte[uncompressedSize]; + using (var compressed = new DeflateStream(f, CompressionMode.Decompress)) + { + int read = 0; + while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ; + } + } + + return true; + } + catch (FileNotFoundException) { } + catch (Exception ex) + { + Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message)); + } + return false; + } + + public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask) + { + try + { + string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID)); + //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID)); + + using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None)) + { + int i = 0; + // magic header + f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length); + i += RAD_IMG_MAGIC.Length; + + // version + f.WriteByte((byte)1); + i++; + + // texture info + f.WriteByte(hasAlpha ? (byte)1 : (byte)0); + f.WriteByte(fullAlpha ? (byte)1 : (byte)0); + f.WriteByte(isMask ? (byte)1 : (byte)0); + i += 3; + + // texture size + byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length); + f.Write(uncompressedSize, 0, uncompressedSize.Length); + i += uncompressedSize.Length; + + // texture id + byte[] id = new byte[16]; + textureID.ToBytes(id, 0); + f.Write(id, 0, 16); + i += 16; + + // compressed texture data + using (var compressed = new DeflateStream(f, CompressionMode.Compress)) + { + compressed.Write(tgaData, 0, tgaData.Length); + } + } + return true; + } + catch (Exception ex) + { + Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message)); + return false; + } + } + #endregion Cached image save and load } /// @@ -2470,4 +2578,4 @@ namespace Radegast.Rendering return Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0]) * Math.PI / 180f), (float)(float.Parse(rotparts[1]) * Math.PI / 180f), (float)(float.Parse(rotparts[2]) * Math.PI / 180f)); } } -} +} \ No newline at end of file