OSDN Git Service

Experimental decoded texture caching
authorLatif Khalifa <latifer@streamgrid.net>
Mon, 25 Jul 2011 18:11:14 +0000 (18:11 +0000)
committerLatif Khalifa <latifer@streamgrid.net>
Mon, 25 Jul 2011 18:11:14 +0000 (18:11 +0000)
git-svn-id: https://radegast.googlecode.com/svn/trunk@1008 f7a694da-4d33-11de-9ad6-1127a62b9fcd

Radegast/GUI/Rendering/Rendering.cs
Radegast/GUI/Rendering/RenderingHelpers.cs

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