From 6adc810eaaf067e25ac88b023a20543577a2d5fd Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 5 Jun 2012 19:40:16 +0100 Subject: [PATCH 1/8] Stop accidentally reading 4 Int16s instead of 2 in SIZE section of Terragen file when loaded from a stream. Fixes a bug introduced 2 weeks ago in 67ebe80 Thanks to Plugh for pointing this out. --- .../Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs index 71c71e6cd5..7a0db2664c 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs @@ -178,8 +178,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders // int sztmp = bs.ReadInt16() + 1; // fileWidth = sztmp; // fileHeight = sztmp; - bs.ReadInt16(); - bs.ReadInt16(); bs.ReadInt16(); bs.ReadInt16(); break; From abf94627f6752c81eec6ebe0412bdcb725a66037 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 6 Jun 2012 02:45:36 +0100 Subject: [PATCH 2/8] Ensure closure of bitmap and memory stream with using() statements in WorldViewModule. If this has any effect then it will only be to the map images returned via requests to the /worldview simulator HTTP path (not enabled by default) --- .../World/WorldView/WorldViewModule.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs index 48c242db51..1aee39a5b9 100644 --- a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs +++ b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs @@ -113,14 +113,15 @@ namespace OpenSim.Region.OptionalModules.World.WorldView if (!m_Enabled) return new Byte[0]; - Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, - height, usetex); + using (Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, height, usetex)) + { + using (MemoryStream str = new MemoryStream()) + { + bmp.Save(str, ImageFormat.Jpeg); - MemoryStream str = new MemoryStream(); - - bmp.Save(str, ImageFormat.Jpeg); - - return str.ToArray(); + return str.ToArray(); + } + } } } } From 3b250211803ee5a39888c6c5bd05b60d253a22b6 Mon Sep 17 00:00:00 2001 From: SignpostMarv Date: Tue, 5 Jun 2012 15:11:45 +0100 Subject: [PATCH 3/8] enabling all corners of a sim to be set in one call --- .../World/Estate/EstateManagementCommands.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs index d2bbea34b8..3b84d5728f 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs @@ -73,7 +73,7 @@ namespace OpenSim.Region.CoreModules.World.Estate "set terrain heights [] []", "Sets the terrain texture heights on corner # to /, if or are specified, it will only " + "set it on regions with a matching coordinate. Specify -1 in or to wildcard" + - " that coordinate. Corner # SW = 0, NW = 1, SE = 2, NE = 3.", + " that coordinate. Corner # SW = 0, NW = 1, SE = 2, NE = 3, all corners = -1.", consoleSetTerrainHeights); m_module.Scene.AddCommand( @@ -143,6 +143,16 @@ namespace OpenSim.Region.CoreModules.World.Estate switch (corner) { + case -1: + m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation1NW = lowValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation2NW = highValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation1SE = lowValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation2SE = highValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation1NE = lowValue; + m_module.Scene.RegionInfo.RegionSettings.Elevation2NE = highValue; + break; case 0: m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue; m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue; From 2b0de66216ca57cf2eac52e777bb362023f8f30a Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 6 Jun 2012 04:11:16 +0100 Subject: [PATCH 4/8] Actively dispose of Bitmaps in Warp3D image module and world map module once we've finished with them. This might help with memory leakage issues though I suspect it won't. --- .../World/Warp3DMap/MapImageModule.cs | 76 ++-- .../World/Warp3DMap/TerrainSplat.cs | 380 +++++++++--------- .../World/WorldMap/WorldMapModule.cs | 7 +- 3 files changed, 248 insertions(+), 215 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs index 4f4e296b27..3538b4673a 100644 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs @@ -204,7 +204,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap Bitmap bitmap = renderer.Scene.getImage(); if (m_useAntiAliasing) - bitmap = ImageUtils.ResizeImage(bitmap, viewport.Width, viewport.Height); + { + using (Bitmap origBitmap = bitmap) + bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height); + } return bitmap; } @@ -318,8 +321,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap uint globalX, globalY; Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); - Bitmap image = TerrainSplat.Splat(heightmap, textureIDs, startHeights, heightRanges, new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain); - warp_Texture texture = new warp_Texture(image); + warp_Texture texture; + + using ( + Bitmap image + = TerrainSplat.Splat( + heightmap, textureIDs, startHeights, heightRanges, + new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain)) + { + texture = new warp_Texture(image); + } + warp_Material material = new warp_Material(texture); material.setReflectivity(50); renderer.Scene.addMaterial("TerrainColor", material); @@ -546,42 +558,46 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap { try { - Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream); - width = bitmap.Width; - height = bitmap.Height; + int pixelBytes; - BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); - int pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; - - // Sum up the individual channels - unsafe + using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream)) { - if (pixelBytes == 4) + width = bitmap.Width; + height = bitmap.Height; + + BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); + pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; + + // Sum up the individual channels + unsafe { - for (int y = 0; y < height; y++) + if (pixelBytes == 4) { - byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); - - for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) { - b += row[x * pixelBytes + 0]; - g += row[x * pixelBytes + 1]; - r += row[x * pixelBytes + 2]; - a += row[x * pixelBytes + 3]; + byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); + + for (int x = 0; x < width; x++) + { + b += row[x * pixelBytes + 0]; + g += row[x * pixelBytes + 1]; + r += row[x * pixelBytes + 2]; + a += row[x * pixelBytes + 3]; + } } } - } - else - { - for (int y = 0; y < height; y++) + else { - byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); - - for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) { - b += row[x * pixelBytes + 0]; - g += row[x * pixelBytes + 1]; - r += row[x * pixelBytes + 2]; + byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); + + for (int x = 0; x < width; x++) + { + b += row[x * pixelBytes + 0]; + g += row[x * pixelBytes + 1]; + r += row[x * pixelBytes + 2]; + } } } } diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs index 7bf675daff..91252f7396 100644 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs @@ -84,218 +84,234 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap Debug.Assert(heightRanges.Length == 4); Bitmap[] detailTexture = new Bitmap[4]; + Bitmap output = null; + BitmapData outputData = null; - if (textureTerrain) + try { - // Swap empty terrain textureIDs with default IDs - for (int i = 0; i < textureIDs.Length; i++) + if (textureTerrain) { - if (textureIDs[i] == UUID.Zero) - textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; - } - - #region Texture Fetching - - if (assetService != null) - { - for (int i = 0; i < 4; i++) + // Swap empty terrain textureIDs with default IDs + for (int i = 0; i < textureIDs.Length; i++) { - AssetBase asset; - UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); - - // Try to fetch a cached copy of the decoded/resized version of this texture - asset = assetService.GetCached(cacheID.ToString()); - if (asset != null) + if (textureIDs[i] == UUID.Zero) + textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; + } + + #region Texture Fetching + + if (assetService != null) + { + for (int i = 0; i < 4; i++) { - try - { - using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data)) - detailTexture[i] = (Bitmap)Image.FromStream(stream); - } - catch (Exception ex) - { - m_log.Warn("Failed to decode cached terrain texture " + cacheID + - " (textureID: " + textureIDs[i] + "): " + ex.Message); - } - } - - if (detailTexture[i] == null) - { - // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG - asset = assetService.Get(textureIDs[i].ToString()); + AssetBase asset; + UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); + + // Try to fetch a cached copy of the decoded/resized version of this texture + asset = assetService.GetCached(cacheID.ToString()); if (asset != null) { - try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); } + try + { + using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data)) + detailTexture[i] = (Bitmap)Image.FromStream(stream); + } catch (Exception ex) { - m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message); + m_log.Warn("Failed to decode cached terrain texture " + cacheID + + " (textureID: " + textureIDs[i] + "): " + ex.Message); } } - - if (detailTexture[i] != null) + + if (detailTexture[i] == null) { - Bitmap bitmap = detailTexture[i]; - - // Make sure this texture is the correct size, otherwise resize - if (bitmap.Width != 256 || bitmap.Height != 256) - bitmap = ImageUtils.ResizeImage(bitmap, 256, 256); - - // Save the decoded and resized texture to the cache - byte[] data; - using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) + // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG + asset = assetService.Get(textureIDs[i].ToString()); + if (asset != null) { - bitmap.Save(stream, ImageFormat.Png); - data = stream.ToArray(); + try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); } + catch (Exception ex) + { + m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message); + } } - - // Cache a PNG copy of this terrain texture - AssetBase newAsset = new AssetBase + + if (detailTexture[i] != null) { - Data = data, - Description = "PNG", - Flags = AssetFlags.Collectable, - FullID = cacheID, - ID = cacheID.ToString(), - Local = true, - Name = String.Empty, - Temporary = true, - Type = (sbyte)AssetType.Unknown - }; - newAsset.Metadata.ContentType = "image/png"; - assetService.Store(newAsset); + Bitmap bitmap = detailTexture[i]; + + // Make sure this texture is the correct size, otherwise resize + if (bitmap.Width != 256 || bitmap.Height != 256) + { + using (Bitmap origBitmap = bitmap) + { + bitmap = ImageUtils.ResizeImage(origBitmap, 256, 256); + } + } + + // Save the decoded and resized texture to the cache + byte[] data; + using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) + { + bitmap.Save(stream, ImageFormat.Png); + data = stream.ToArray(); + } + + // Cache a PNG copy of this terrain texture + AssetBase newAsset = new AssetBase + { + Data = data, + Description = "PNG", + Flags = AssetFlags.Collectable, + FullID = cacheID, + ID = cacheID.ToString(), + Local = true, + Name = String.Empty, + Temporary = true, + Type = (sbyte)AssetType.Unknown + }; + newAsset.Metadata.ContentType = "image/png"; + assetService.Store(newAsset); + } } } } + + #endregion Texture Fetching } - - #endregion Texture Fetching - } - - // Fill in any missing textures with a solid color - for (int i = 0; i < 4; i++) - { - if (detailTexture[i] == null) + + // Fill in any missing textures with a solid color + for (int i = 0; i < 4; i++) { - // Create a solid color texture for this layer - detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb); - using (Graphics gfx = Graphics.FromImage(detailTexture[i])) + if (detailTexture[i] == null) { - using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i])) - gfx.FillRectangle(brush, 0, 0, 256, 256); + // Create a solid color texture for this layer + detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb); + using (Graphics gfx = Graphics.FromImage(detailTexture[i])) + { + using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i])) + gfx.FillRectangle(brush, 0, 0, 256, 256); + } } } - } - - #region Layer Map - - float[] layermap = new float[256 * 256]; - - for (int y = 0; y < 256; y++) - { - for (int x = 0; x < 256; x++) - { - float height = heightmap[y * 256 + x]; - - float pctX = (float)x / 255f; - float pctY = (float)y / 255f; - - // Use bilinear interpolation between the four corners of start height and - // height range to select the current values at this position - float startHeight = ImageUtils.Bilinear( - startHeights[0], - startHeights[2], - startHeights[1], - startHeights[3], - pctX, pctY); - startHeight = Utils.Clamp(startHeight, 0f, 255f); - - float heightRange = ImageUtils.Bilinear( - heightRanges[0], - heightRanges[2], - heightRanges[1], - heightRanges[3], - pctX, pctY); - heightRange = Utils.Clamp(heightRange, 0f, 255f); - - // Generate two frequencies of perlin noise based on our global position - // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting - Vector3 vec = new Vector3 - ( - ((float)regionPosition.X + x) * 0.20319f, - ((float)regionPosition.Y + y) * 0.20319f, - height * 0.25f - ); - - float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f; - float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f; - float noise = (lowFreq + highFreq) * 2f; - - // Combine the current height, generated noise, start height, and height range parameters, then scale all of it - float layer = ((height + noise - startHeight) / heightRange) * 4f; - if (Single.IsNaN(layer)) layer = 0f; - layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f); - } - } - - #endregion Layer Map - - #region Texture Compositing - - Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb); - BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); - - unsafe - { - // Get handles to all of the texture data arrays - BitmapData[] datas = new BitmapData[] - { - detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat), - detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat), - detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat), - detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat) - }; - - int[] comps = new int[] - { - (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, - (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, - (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, - (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3 - }; - + + #region Layer Map + + float[] layermap = new float[256 * 256]; + for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { - float layer = layermap[y * 256 + x]; - - // Select two textures - int l0 = (int)Math.Floor(layer); - int l1 = Math.Min(l0 + 1, 3); - - byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0]; - byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1]; - byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3; - - float aB = *(ptrA + 0); - float aG = *(ptrA + 1); - float aR = *(ptrA + 2); - - float bB = *(ptrB + 0); - float bG = *(ptrB + 1); - float bR = *(ptrB + 2); - - float layerDiff = layer - l0; - - // Interpolate between the two selected textures - *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB)); - *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG)); - *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR)); + float height = heightmap[y * 256 + x]; + + float pctX = (float)x / 255f; + float pctY = (float)y / 255f; + + // Use bilinear interpolation between the four corners of start height and + // height range to select the current values at this position + float startHeight = ImageUtils.Bilinear( + startHeights[0], + startHeights[2], + startHeights[1], + startHeights[3], + pctX, pctY); + startHeight = Utils.Clamp(startHeight, 0f, 255f); + + float heightRange = ImageUtils.Bilinear( + heightRanges[0], + heightRanges[2], + heightRanges[1], + heightRanges[3], + pctX, pctY); + heightRange = Utils.Clamp(heightRange, 0f, 255f); + + // Generate two frequencies of perlin noise based on our global position + // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting + Vector3 vec = new Vector3 + ( + ((float)regionPosition.X + x) * 0.20319f, + ((float)regionPosition.Y + y) * 0.20319f, + height * 0.25f + ); + + float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f; + float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f; + float noise = (lowFreq + highFreq) * 2f; + + // Combine the current height, generated noise, start height, and height range parameters, then scale all of it + float layer = ((height + noise - startHeight) / heightRange) * 4f; + if (Single.IsNaN(layer)) layer = 0f; + layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f); } } - + + #endregion Layer Map + + #region Texture Compositing + + output = new Bitmap(256, 256, PixelFormat.Format24bppRgb); + outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); + + unsafe + { + // Get handles to all of the texture data arrays + BitmapData[] datas = new BitmapData[] + { + detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat), + detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat), + detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat), + detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat) + }; + + int[] comps = new int[] + { + (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, + (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, + (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, + (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3 + }; + + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + float layer = layermap[y * 256 + x]; + + // Select two textures + int l0 = (int)Math.Floor(layer); + int l1 = Math.Min(l0 + 1, 3); + + byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0]; + byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1]; + byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3; + + float aB = *(ptrA + 0); + float aG = *(ptrA + 1); + float aR = *(ptrA + 2); + + float bB = *(ptrB + 0); + float bG = *(ptrB + 1); + float bR = *(ptrB + 2); + + float layerDiff = layer - l0; + + // Interpolate between the two selected textures + *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB)); + *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG)); + *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR)); + } + } + + for (int i = 0; i < 4; i++) + detailTexture[i].UnlockBits(datas[i]); + } + } + finally + { for (int i = 0; i < 4; i++) - detailTexture[i].UnlockBits(datas[i]); + if (detailTexture[i] != null) + detailTexture[i].Dispose(); } output.UnlockBits(outputData); diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 2335bea2a4..c1c6b49705 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -1343,14 +1343,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap if (terrain == null) return; + m_log.DebugFormat("[WORLDMAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName); + byte[] data = terrain.WriteJpeg2000Image(); if (data == null) return; byte[] overlay = GenerateOverlay(); - m_log.Debug("[WORLDMAP]: STORING MAPTILE IMAGE"); - UUID terrainImageID = UUID.Random(); UUID parcelImageID = UUID.Zero; @@ -1365,7 +1365,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap asset.Flags = AssetFlags.Maptile; // Store the new one - m_log.DebugFormat("[WORLDMAP]: Storing map tile {0}", asset.ID); + m_log.DebugFormat("[WORLDMAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName); + m_scene.AssetService.Store(asset); if (overlay != null) From 53c25a47780b5b1167f8e574dc8e9b935a0d5ddd Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 6 Jun 2012 04:15:00 +0100 Subject: [PATCH 5/8] Rename MapImageModule for Warp3D to Warp3DImageModule to match its class name and make it easier to distinguish between map image modules. --- .../World/Warp3DMap/{MapImageModule.cs => Warp3DImageModule.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename OpenSim/Region/CoreModules/World/Warp3DMap/{MapImageModule.cs => Warp3DImageModule.cs} (100%) diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs similarity index 100% rename from OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs rename to OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs From 514dd85199331d8246c3a0dfc9cf782f61f48ef6 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 6 Jun 2012 04:18:38 +0100 Subject: [PATCH 6/8] minor: Change log messages on Warp3DImageModule to show they are from this module --- .../World/Warp3DMap/Warp3DImageModule.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs index 3538b4673a..9002a9f330 100644 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs @@ -54,8 +54,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap private static readonly UUID TEXTURE_METADATA_MAGIC = new UUID("802dc0e0-f080-4931-8b57-d1be8611c4f3"); private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216); - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Scene m_scene; private IRendering m_primMesher; @@ -88,11 +87,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap if (renderers.Count > 0) { m_primMesher = RenderingLoader.LoadRenderer(renderers[0]); - m_log.Debug("[MAPTILE]: Loaded prim mesher " + m_primMesher.ToString()); + m_log.DebugFormat("[WARP 3D IMAGE MODULE]: Loaded prim mesher {0}", m_primMesher); } else { - m_log.Debug("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled"); + m_log.Debug("[WARP 3D IMAGE MODULE]: No prim mesher loaded, prim rendering will be disabled"); } m_scene.RegisterModuleInterface(this); @@ -150,7 +149,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap } catch { - m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); + m_log.Warn("[WARP 3D IMAGE MODULE]: Failed to load StartupConfig"); } m_colors.Clear(); @@ -222,7 +221,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap catch (Exception e) { // JPEG2000 encoder failed - m_log.Error("[MAPTILE]: Failed generating terrain map: " + e); + m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e); } return null; @@ -619,7 +618,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap } catch (Exception ex) { - m_log.WarnFormat("[MAPTILE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}", textureID, j2kData.Length, ex.Message); + m_log.WarnFormat( + "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}", + textureID, j2kData.Length, ex.Message); + width = 0; height = 0; return new Color4(0.5f, 0.5f, 0.5f, 1.0f); From 98b46d48fefc7801b7c9e496000e9329f4266e5e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 7 Jun 2012 02:44:13 +0100 Subject: [PATCH 7/8] Allow the thread watchdog to accept an alarm method that is invoked if the timeout is breached. This alarm can then invoke this to log extra information. This is used in LLUDPServer to show which client was being processed when incoming and outgoing udp watchdog alarms are triggered. --- .../HttpServer/PollServiceRequestManager.cs | 2 + OpenSim/Framework/Watchdog.cs | 40 ++++++----- OpenSim/Region/Application/OpenSim.cs | 10 ++- .../ClientStack/Linden/UDP/LLUDPServer.cs | 69 +++++++++++++++++-- 4 files changed, 96 insertions(+), 25 deletions(-) diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs index 0062d4ef15..f96fd1fa20 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -66,6 +66,7 @@ namespace OpenSim.Framework.Servers.HttpServer ThreadPriority.Normal, false, true, + null, int.MaxValue); } @@ -75,6 +76,7 @@ namespace OpenSim.Framework.Servers.HttpServer ThreadPriority.Normal, false, true, + null, 1000 * 60 * 10); } diff --git a/OpenSim/Framework/Watchdog.cs b/OpenSim/Framework/Watchdog.cs index e93e50e2ca..7552cd15b6 100644 --- a/OpenSim/Framework/Watchdog.cs +++ b/OpenSim/Framework/Watchdog.cs @@ -42,7 +42,7 @@ namespace OpenSim.Framework const double WATCHDOG_INTERVAL_MS = 2500.0d; /// Maximum timeout in milliseconds before a thread is considered dead - const int WATCHDOG_TIMEOUT_MS = 5000; + public const int WATCHDOG_TIMEOUT_MS = 5000; [System.Diagnostics.DebuggerDisplay("{Thread.Name}")] public class ThreadWatchdogInfo @@ -58,7 +58,7 @@ namespace OpenSim.Framework public int FirstTick { get; private set; } /// - /// First time this heartbeat update was invoked + /// Last time this heartbeat update was invoked /// public int LastTick { get; set; } @@ -77,6 +77,11 @@ namespace OpenSim.Framework /// public bool AlarmIfTimeout { get; set; } + /// + /// Method execute if alarm goes off. If null then no alarm method is fired. + /// + public Func AlarmMethod { get; set; } + public ThreadWatchdogInfo(Thread thread, int timeout) { Thread = thread; @@ -87,16 +92,10 @@ namespace OpenSim.Framework } /// - /// This event is called whenever a tracked thread is stopped or - /// has not called UpdateThread() in time - /// - /// The thread that has been identified as dead - /// The last time this thread called UpdateThread() - public delegate void WatchdogTimeout(Thread thread, int lastTick); - - /// This event is called whenever a tracked thread is - /// stopped or has not called UpdateThread() in time - public static event WatchdogTimeout OnWatchdogTimeout; + /// This event is called whenever a tracked thread is + /// stopped or has not called UpdateThread() in time< + /// /summary> + public static event Action OnWatchdogTimeout; private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static Dictionary m_threads; @@ -123,7 +122,7 @@ namespace OpenSim.Framework public static Thread StartThread( ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout) { - return StartThread(start, name, priority, isBackground, alarmIfTimeout, WATCHDOG_TIMEOUT_MS); + return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, WATCHDOG_TIMEOUT_MS); } /// @@ -135,17 +134,24 @@ namespace OpenSim.Framework /// True to run this thread as a background /// thread, otherwise false /// Trigger an alarm function is we have timed out + /// + /// Alarm method to call if alarmIfTimeout is true and there is a timeout. + /// Normally, this will just return some useful debugging information. + /// /// Number of milliseconds to wait until we issue a warning about timeout. /// The newly created Thread object public static Thread StartThread( - ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, int timeout) + ThreadStart start, string name, ThreadPriority priority, bool isBackground, + bool alarmIfTimeout, Func alarmMethod, int timeout) { Thread thread = new Thread(start); thread.Name = name; thread.Priority = priority; thread.IsBackground = isBackground; - ThreadWatchdogInfo twi = new ThreadWatchdogInfo(thread, timeout) { AlarmIfTimeout = alarmIfTimeout }; + ThreadWatchdogInfo twi + = new ThreadWatchdogInfo(thread, timeout) + { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod }; m_log.DebugFormat( "[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); @@ -258,7 +264,7 @@ namespace OpenSim.Framework /// private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { - WatchdogTimeout callback = OnWatchdogTimeout; + Action callback = OnWatchdogTimeout; if (callback != null) { @@ -296,7 +302,7 @@ namespace OpenSim.Framework if (callbackInfos != null) foreach (ThreadWatchdogInfo callbackInfo in callbackInfos) - callback(callbackInfo.Thread, callbackInfo.LastTick); + callback(callbackInfo); } m_watchdogTimer.Start(); diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 76ac82797b..caba236a9c 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -438,12 +438,16 @@ namespace OpenSim } } - private void WatchdogTimeoutHandler(System.Threading.Thread thread, int lastTick) + private void WatchdogTimeoutHandler(Watchdog.ThreadWatchdogInfo twi) { int now = Environment.TickCount & Int32.MaxValue; - m_log.ErrorFormat("[WATCHDOG]: Timeout detected for thread \"{0}\". ThreadState={1}. Last tick was {2}ms ago", - thread.Name, thread.ThreadState, now - lastTick); + m_log.ErrorFormat( + "[WATCHDOG]: Timeout detected for thread \"{0}\". ThreadState={1}. Last tick was {2}ms ago. {3}", + twi.Thread.Name, + twi.Thread.ThreadState, + now - twi.LastTick, + twi.AlarmMethod != null ? string.Format("Data: {0}", twi.AlarmMethod()) : ""); } #region Console Commands diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 32ba590718..e37adb86b8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -163,6 +163,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_malformedCount = 0; // Guard against a spamming attack + /// + /// Record current outgoing client for monitoring purposes. + /// + private IClientAPI m_currentOutgoingClient; + + /// + /// Recording current incoming client for monitoring purposes. + /// + private IClientAPI m_currentIncomingClient; + public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) : base(listenIP, (int)port) { @@ -244,19 +254,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); - m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); + m_log.InfoFormat( + "[LLUDPSERVER]: Starting the LLUDP server in {0} mode", + m_asyncPacketHandling ? "asynchronous" : "synchronous"); base.Start(m_recvBufferSize, m_asyncPacketHandling); // Start the packet processing threads Watchdog.StartThread( - IncomingPacketHandler, "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); + IncomingPacketHandler, + string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), + ThreadPriority.Normal, + false, + true, + GetWatchdogIncomingAlarmData, + Watchdog.WATCHDOG_TIMEOUT_MS); + Watchdog.StartThread( - OutgoingPacketHandler, "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); + OutgoingPacketHandler, + string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName), + ThreadPriority.Normal, + false, + true, + GetWatchdogOutgoingAlarmData, + Watchdog.WATCHDOG_TIMEOUT_MS); m_elapsedMSSinceLastStatReport = Environment.TickCount; } + /// + /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. + /// + /// + private string GetWatchdogIncomingAlarmData() + { + return string.Format( + "Client is {0}", + m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none"); + } + + /// + /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. + /// + /// + private string GetWatchdogOutgoingAlarmData() + { + return string.Format( + "Client is {0}", + m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none"); + } + public new void Stop() { m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); @@ -1173,6 +1220,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // client. m_packetSent will be set to true if a packet is sent m_scene.ForEachClient(clientPacketHandler); + m_currentOutgoingClient = null; + // If nothing was sent, sleep for the minimum amount of time before a // token bucket could get more tokens if (!m_packetSent) @@ -1191,6 +1240,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void ClientOutgoingPacketHandler(IClientAPI client) { + m_currentOutgoingClient = client; + try { if (client is LLClientView) @@ -1216,8 +1267,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } catch (Exception ex) { - m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + - " threw an exception: " + ex.Message, ex); + m_log.Error( + string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex); } } @@ -1243,6 +1294,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { nticks++; watch1.Start(); + m_currentOutgoingClient = client; + try { if (client is LLClientView) @@ -1344,6 +1397,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Make sure this client is still alive if (m_scene.TryGetClient(udpClient.AgentID, out client)) { + m_currentIncomingClient = client; + try { // Process this packet @@ -1361,6 +1416,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); m_log.Error(e.Message, e); } + finally + { + m_currentIncomingClient = null; + } } else { From 7550b97e65fa873b42624648747cec3e12a8e689 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 7 Jun 2012 04:00:29 +0100 Subject: [PATCH 8/8] Log warning if we try to remove a UDP client that has already been removed. --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index e37adb86b8..09bb52c046 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1114,6 +1114,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.IsLoggingOut = true; client.Close(); } + else + { + m_log.WarnFormat( + "[LLUDPSERVER]: Tried to remove client with id {0} but not such client in {1}", + udpClient.AgentID, m_scene.RegionInfo.RegionName); + } } private void IncomingPacketHandler()