From 8f02fd926e14dfad7f5eb77a67a6701f449511e0 Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Thu, 6 Sep 2012 22:12:05 +0100
Subject: If reusing dynamic textures, do not reuse small data length textures
that fall below current viewer discard level 2 thresholds.
Viewer LL 3.3.4 and before sometimes fail to properly redisplay dynamic textures that have a small data length compared to pixel size when pulled from cache.
This appears to happen when the data length is smaller than the estimate discard level 2 size the viewer uses when making this GetTexture request.
This commit works around this by always regenerating dynamic textures that fall below this threshold rather than reusing them if ReuseDynamicTextures = true
This can be controlled by the [Textures] ReuseDynamicLowDataTextures config setting which defaults to false.
---
.../DynamicTexture/DynamicTextureModule.cs | 59 ++++++++++++++++++----
.../Scripting/LoadImageURL/LoadImageURLModule.cs | 41 +++++++++------
.../VectorRender/Tests/VectorRenderModuleTests.cs | 42 +++++++++++++++
.../Scripting/VectorRender/VectorRenderModule.cs | 24 ++++-----
4 files changed, 125 insertions(+), 41 deletions(-)
(limited to 'OpenSim/Region/CoreModules/Scripting')
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index e09f1a9..1f340df 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
{
-// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int ALL_SIDES = -1;
@@ -54,6 +54,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
///
public bool ReuseTextures { get; set; }
+ ///
+ /// If false, then textures which have a low data size are not reused when ReuseTextures = true.
+ ///
+ ///
+ /// LL viewers 3.3.4 and before appear to not fully render textures pulled from the viewer cache if those
+ /// textures have a relatively high pixel surface but a small data size. Typically, this appears to happen
+ /// if the data size is smaller than the viewer's discard level 2 size estimate. So if this is setting is
+ /// false, textures smaller than the calculation in IsSizeReuseable are always regenerated rather than reused
+ /// to work around this problem.
+ public bool ReuseLowDataTextures { get; set; }
+
private Dictionary RegisteredScenes = new Dictionary();
private Dictionary RenderPlugins =
@@ -83,18 +94,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
///
/// Called by code which actually renders the dynamic texture to supply texture data.
///
- ///
- ///
- /// True if the data generated can be reused for subsequent identical requests
- public void ReturnData(UUID id, byte[] data, bool isReuseable)
+ ///
+ ///
+ public void ReturnData(UUID updaterId, IDynamicTexture texture)
{
DynamicTextureUpdater updater = null;
lock (Updaters)
{
- if (Updaters.ContainsKey(id))
+ if (Updaters.ContainsKey(updaterId))
{
- updater = Updaters[id];
+ updater = Updaters[updaterId];
}
}
@@ -103,11 +113,16 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
if (RegisteredScenes.ContainsKey(updater.SimUUID))
{
Scene scene = RegisteredScenes[updater.SimUUID];
- UUID newTextureID = updater.DataReceived(data, scene);
+ UUID newTextureID = updater.DataReceived(texture.Data, scene);
- if (ReuseTextures && isReuseable && !updater.BlendWithOldTexture)
+ if (ReuseTextures
+ && !updater.BlendWithOldTexture
+ && texture.IsReuseable
+ && (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
+ {
m_reuseableDynamicTextures.Store(
- GenerateReusableTextureKey(updater.BodyData, updater.Params), newTextureID);
+ GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
+ }
}
}
@@ -123,6 +138,27 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
}
}
+ ///
+ /// Determines whether the texture is reuseable based on its data size.
+ ///
+ ///
+ /// This is a workaround for a viewer bug where very small data size textures relative to their pixel size
+ /// are not redisplayed properly when pulled from cache. The calculation here is based on the typical discard
+ /// level of 2, a 'rate' of 0.125 and 4 components (which makes for a factor of 0.5).
+ ///
+ ///
+ private bool IsDataSizeReuseable(IDynamicTexture texture)
+ {
+// Console.WriteLine("{0} {1}", texture.Size.Width, texture.Size.Height);
+ int discardLevel2DataThreshold = (int)Math.Ceiling((texture.Size.Width >> 2) * (texture.Size.Height >> 2) * 0.5);
+
+// m_log.DebugFormat(
+// "[DYNAMIC TEXTURE MODULE]: Discard level 2 threshold {0}, texture data length {1}",
+// discardLevel2DataThreshold, texture.Data.Length);
+
+ return discardLevel2DataThreshold < texture.Data.Length;
+ }
+
public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
string extraParams, int updateTimer)
{
@@ -293,7 +329,10 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
IConfig texturesConfig = config.Configs["Textures"];
if (texturesConfig != null)
+ {
ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
+ ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
+ }
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
{
diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
index 2b3a0f2..45e6527 100644
--- a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
@@ -32,6 +32,7 @@ using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
+using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -73,12 +74,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
// return false;
// }
- public byte[] ConvertUrl(string url, string extraParams)
+ public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
- public byte[] ConvertData(string bodyData, string extraParams)
+ public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
return null;
}
@@ -171,11 +172,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
private void HttpRequestReturn(IAsyncResult result)
{
-
RequestState state = (RequestState) result.AsyncState;
WebRequest request = (WebRequest) state.Request;
Stream stream = null;
byte[] imageJ2000 = new byte[0];
+ Size newSize = new Size(0, 0);
try
{
@@ -188,37 +189,43 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
try
{
Bitmap image = new Bitmap(stream);
- Size newsize;
// TODO: make this a bit less hard coded
if ((image.Height < 64) && (image.Width < 64))
{
- newsize = new Size(32, 32);
+ newSize.Width = 32;
+ newSize.Height = 32;
}
else if ((image.Height < 128) && (image.Width < 128))
{
- newsize = new Size(64, 64);
+ newSize.Width = 64;
+ newSize.Height = 64;
}
else if ((image.Height < 256) && (image.Width < 256))
{
- newsize = new Size(128, 128);
+ newSize.Width = 128;
+ newSize.Height = 128;
}
else if ((image.Height < 512 && image.Width < 512))
{
- newsize = new Size(256, 256);
+ newSize.Width = 256;
+ newSize.Height = 256;
}
else if ((image.Height < 1024 && image.Width < 1024))
{
- newsize = new Size(512, 512);
+ newSize.Width = 512;
+ newSize.Height = 512;
}
else
{
- newsize = new Size(1024, 1024);
+ newSize.Width = 1024;
+ newSize.Height = 1024;
}
- Bitmap resize = new Bitmap(image, newsize);
-
- imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
+ using (Bitmap resize = new Bitmap(image, newSize))
+ {
+ imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
+ }
}
catch (Exception)
{
@@ -233,7 +240,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
catch (WebException)
{
-
}
finally
{
@@ -243,10 +249,13 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
}
- m_log.DebugFormat("[LOADIMAGEURLMODULE] Returning {0} bytes of image data for request {1}",
+ m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}",
imageJ2000.Length, state.RequestID);
- m_textureManager.ReturnData(state.RequestID, imageJ2000, false);
+ m_textureManager.ReturnData(
+ state.RequestID,
+ new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
+ request.RequestUri, null, imageJ2000, newSize, false));
}
#region Nested type: RequestState
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
index b50c0bd..41baccc 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
@@ -57,6 +57,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
m_dtm = new DynamicTextureModule();
m_dtm.ReuseTextures = reuseTextures;
+// m_dtm.ReuseLowDataTextures = reuseTextures;
m_vrm = new VectorRenderModule();
@@ -201,6 +202,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
public void TestRepeatSameDrawReusingTexture()
{
TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
@@ -228,6 +230,46 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
Assert.That(firstDynamicTextureID, Is.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
+ ///
+ /// Test a low data dynamically generated texture such that it is treated as a low data texture that causes
+ /// problems for current viewers.
+ ///
+ ///
+ /// As we do not set DynamicTextureModule.ReuseLowDataTextures = true in this test, it should not reuse the
+ /// texture
+ ///
+ [Test]
+ public void TestRepeatSameDrawLowDataTexture()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "1024",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "1024",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
[Test]
public void TestRepeatSameDrawDifferentExtraParamsReusingTexture()
{
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
index 0e7051e..d82551e 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
@@ -35,6 +35,7 @@ using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
+using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -85,20 +86,14 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
// return lines.Any((str, r) => str.StartsWith("Image"));
// }
- public byte[] ConvertUrl(string url, string extraParams)
+ public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
- public byte[] ConvertData(string bodyData, string extraParams)
+ public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
- bool reuseable;
- return Draw(bodyData, extraParams, out reuseable);
- }
-
- private byte[] ConvertData(string bodyData, string extraParams, out bool reuseable)
- {
- return Draw(bodyData, extraParams, out reuseable);
+ return Draw(bodyData, extraParams);
}
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
@@ -109,10 +104,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
{
// XXX: This isn't actually being done asynchronously!
- bool reuseable;
- byte[] data = ConvertData(bodyData, extraParams, out reuseable);
-
- m_textureManager.ReturnData(id, data, reuseable);
+ m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
return true;
}
@@ -191,7 +183,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
#endregion
- private byte[] Draw(string data, string extraParams, out bool reuseable)
+ private IDynamicTexture Draw(string data, string extraParams)
{
// We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
// we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
@@ -334,6 +326,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
Bitmap bitmap = null;
Graphics graph = null;
+ bool reuseable = false;
try
{
@@ -396,7 +389,8 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
e.Message, e.StackTrace);
}
- return imageJ2000;
+ return new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
+ data, extraParams, imageJ2000, new Size(width, height), reuseable);
}
finally
{
--
cgit v1.1