Reduce memory usage of CopyState by around ~43%, fixes #306.

This commit is contained in:
UnknownShadow200 2017-05-26 09:22:46 +10:00
parent 02e694649c
commit f5061dec9d
4 changed files with 140 additions and 97 deletions

View File

@ -110,22 +110,19 @@ namespace MCGalaxy.Commands.Building {
for (ushort zz = minZ; zz <= maxZ; ++zz)
for (ushort xx = minX; xx <= maxX; ++xx)
{
byte b = p.level.GetTile(xx, yy, zz), extB = 0;
if (!p.group.CanModify[b]) { index++; continue; }
if (b == Block.custom_block)
extB = p.level.GetExtTile(xx, yy, zz);
block = p.level.GetExtBlock(xx, yy, zz);
if (!p.group.CanModify[block.BlockID]) { index++; continue; } // TODO: will need to fix this when extblock permissions
if (b != Block.air || cState.PasteAir)
if (block.BlockID != Block.air || cState.PasteAir)
cState.UsedBlocks++;
cState.Blocks[index] = b;
cState.ExtBlocks[index] = extB;
cState.Set(block, index);
index++;
}
if (cState.UsedBlocks > p.group.maxBlocks) {
Player.Message(p, "You tried to copy {0} blocks. You cannot copy more than {1} blocks.",
cState.UsedBlocks, p.group.maxBlocks);
cState.Blocks = null; cState.ExtBlocks = null; cState = null;
cState.Clear(); cState = null;
return false;
}
@ -189,7 +186,7 @@ namespace MCGalaxy.Commands.Building {
using (FileStream fs = File.OpenRead(path))
using (GZipStream gs = new GZipStream(fs, CompressionMode.Decompress))
{
CopyState state = new CopyState(0, 0, 0, 0, 0, 0, null, null);
CopyState state = new CopyState(0, 0, 0, 0, 0, 0);
if (path.CaselessEnds(".cpb")) {
state.LoadFrom(gs);
} else {

View File

@ -22,7 +22,6 @@ using MCGalaxy.Drawing.Ops;
namespace MCGalaxy.Drawing.Brushes {
public sealed class SimplePasteBrush : Brush {
readonly CopyState state;
int index;
public SimplePasteBrush(CopyState state) { this.state = state; }
@ -41,18 +40,12 @@ namespace MCGalaxy.Drawing.Brushes {
int z = (op.Coords.Z - op.Min.Z) % state.Length;
if (z < 0) z += state.Length;
index = state.GetIndex(x, y, z);
ExtBlock block;
block.BlockID = state.Blocks[index];
block.ExtID = state.ExtBlocks[index];
return block;
return state.Get(x, y, z);
}
}
public sealed class PasteBrush : Brush {
readonly CopyState state;
int index;
readonly CopyState state;
public ExtBlock[] Include, Exclude;
public PasteBrush(CopyState state) { this.state = state; }
@ -72,11 +65,7 @@ namespace MCGalaxy.Drawing.Brushes {
int z = (op.Coords.Z - op.Min.Z) % state.Length;
if (z < 0) z += state.Length;
index = state.GetIndex(x, y, z);
ExtBlock block;
block.BlockID = state.Blocks[index];
block.ExtID = state.ExtBlocks[index];
ExtBlock block = state.Get(x, y, z);
if (Exclude != null) {
for (int i = 0; i < Exclude.Length; i++) {
if (block == Exclude[i]) return ExtBlock.Invalid;

View File

@ -23,7 +23,7 @@ namespace MCGalaxy.Drawing {
public sealed class CopyState {
public byte[] Blocks, ExtBlocks;
byte[] raw, isExt;
public int X, Y, Z;
public int OriginX, OriginY, OriginZ;
public int Width, Height, Length;
@ -31,34 +31,32 @@ namespace MCGalaxy.Drawing {
public int UsedBlocks;
public Vec3S32 Offset;
const int identifier = 0x434F5059; // 'COPY'
const int identifierC = 0x434F5043; // 'COPC' (Copy compressed)
internal int OppositeOriginX { get { return OriginX == X ? X + Width - 1 : X; } }
internal int OppositeOriginY { get { return OriginY == Y ? Y + Height - 1 : Y; } }
internal int OppositeOriginZ { get { return OriginZ == Z ? Z + Length - 1 : Z; } }
public int Volume {
get { return Width * Height * Length; }
}
public CopyState(int x, int y, int z, int width, int height, int length,
byte[] blocks, byte[] extBlocks) {
X = x; Y = y; Z = z;
Width = width; Height = height; Length = length;
Blocks = blocks;
ExtBlocks = extBlocks;
UsedBlocks = Volume;
public CopyState(int x, int y, int z, int width, int height, int length) {
Init(x, y, z, width, height, length);
}
public CopyState(int x, int y, int z, int width, int height, int length)
: this(x, y, z, width, height, length, null, null) {
Blocks = new byte[width * height * length];
ExtBlocks = new byte[width * height * length];
void Init(int x, int y, int z, int width, int height, int length) {
X = x; Y = y; Z = z;
Width = width; Height = height; Length = length;
raw = new byte[Volume];
isExt = new byte[(Volume + 7) / 8]; // ceiling divide by 8, as 8 bits fit into 1 byte
UsedBlocks = Volume;
}
public void Clear() {
Blocks = null;
ExtBlocks = null;
raw = null;
isExt = null;
}
public void GetCoords(int index, out ushort x, out ushort y, out ushort z) {
y = (ushort)(index / Width / Length);
index -= y * Width * Length;
@ -71,47 +69,74 @@ namespace MCGalaxy.Drawing {
return (y * Length + z) * Width + x;
}
public void Set(byte type, byte extType, int x, int y, int z) {
Blocks[(y * Length + z) * Width + x] = type;
ExtBlocks[(y * Length + z) * Width + x] = extType;
public ExtBlock Get(int index) {
return ExtBlock.FromRaw(raw[index],
(isExt[index >> 3] & (1 << (index & 0x07))) != 0);
}
public ExtBlock Get(int x, int y, int z) {
int index = (y * Length + z) * Width + x;
return ExtBlock.FromRaw(raw[index],
(isExt[index >> 3] & (1 << (index & 0x07))) != 0);
}
public void Set(ExtBlock block, int index) {
isExt[index >> 3] &= (byte)~(1 << (index & 0x07));
if (block.BlockID == Block.custom_block) {
raw[index] = block.ExtID;
isExt[index >> 3] |= (byte)(1 << (index & 0x07));
} else {
raw[index] = block.BlockID;
}
}
public void Set(ExtBlock block, int x, int y, int z) {
int index = (y * Length + z) * Width + x;
isExt[index >> 3] &= (byte)~(1 << (index & 0x07));
if (block.BlockID == Block.custom_block) {
raw[index] = block.ExtID;
isExt[index >> 3] |= (byte)(1 << (index & 0x07));
} else {
raw[index] = block.BlockID;
}
}
const int identifier1 = 0x434F5059; // version 1, 'COPY' (copy)
const int identifier2 = 0x434F5043; // 'COPC' (copy compressed)
const int identifier3 = 0x434F504F; // 'COPO' (copy optimised)
/// <summary> Saves this copy state to the given stream. </summary>
public void SaveTo(Stream stream) {
BinaryWriter w = new BinaryWriter(stream);
w.Write(identifierC);
w.Write(identifier3);
w.Write(X); w.Write(Y); w.Write(Z);
w.Write(Width); w.Write(Height); w.Write(Length);
byte[] blocks = Blocks.GZip();
w.Write(blocks.Length);
w.Write(blocks);
blocks = ExtBlocks.GZip();
w.Write(blocks.Length);
w.Write(blocks);
byte[] data = raw.GZip();
w.Write(data.Length);
w.Write(data);
data = isExt.GZip();
w.Write(data.Length);
w.Write(data);
w.Write(OriginX); w.Write(OriginY); w.Write(OriginZ);
w.Write((byte)0x0f); // 0ffset
w.Write(Offset.X); w.Write(Offset.Y); w.Write(Offset.Z);
}
/// <summary> Loads this copy state from the given stream. </summary>
public void LoadFrom(Stream stream) {
BinaryReader r = new BinaryReader(stream);
int header = r.ReadInt32();
if (!(header == identifier || header == identifierC))
int identifier = r.ReadInt32();
if (!(identifier == identifier1 || identifier == identifier2 || identifier == identifier3))
throw new InvalidDataException("invalid identifier");
X = r.ReadInt32(); Y = r.ReadInt32(); Z = r.ReadInt32();
Width = r.ReadInt32(); Height = r.ReadInt32(); Length = r.ReadInt32();
if (header == identifier) {
Blocks = r.ReadBytes(Width * Height * Length);
ExtBlocks = r.ReadBytes(Width * Height * Length);
} else {
int uncompressedLen = Width * Height * Length;
int blocksLen = r.ReadInt32();
Blocks = r.ReadBytes(blocksLen).Decompress(uncompressedLen);
blocksLen = r.ReadInt32();
ExtBlocks = r.ReadBytes(blocksLen).Decompress(uncompressedLen);
}
LoadBlocks(r, identifier);
UsedBlocks = Volume;
OriginX = r.ReadInt32(); OriginY = r.ReadInt32(); OriginZ = r.ReadInt32();
@ -119,24 +144,64 @@ namespace MCGalaxy.Drawing {
Offset.X = r.ReadInt32(); Offset.Y = r.ReadInt32(); Offset.Z = r.ReadInt32();
}
void LoadBlocks(BinaryReader r, int identifier) {
byte[] extBlocks;
int dataLen;
switch (identifier) {
case identifier1:
raw = r.ReadBytes(Volume);
extBlocks = r.ReadBytes(Volume);
UnpackExtBlocks(extBlocks);
break;
case identifier2:
dataLen = r.ReadInt32();
raw = r.ReadBytes(dataLen).Decompress(Volume);
dataLen = r.ReadInt32();
extBlocks = r.ReadBytes(dataLen).Decompress(Volume);
UnpackExtBlocks(extBlocks);
break;
case identifier3:
dataLen = r.ReadInt32();
raw = r.ReadBytes(dataLen).Decompress(Volume);
dataLen = r.ReadInt32();
isExt = r.ReadBytes(dataLen).Decompress((Volume + 7) / 8);
break;
}
}
/// <summary> Loads this copy state from the given stream, using the very old format. </summary>
public void LoadFromOld(Stream stream, Stream underlying) {
byte[] raw = new byte[underlying.Length];
underlying.Read(raw, 0, (int)underlying.Length);
raw = raw.Decompress();
if (raw.Length == 0) return;
CalculateBoundsOld(raw);
ExtBlock block = default(ExtBlock);
CalculateBounds(raw);
for (int i = 0; i < raw.Length; i += 7) {
ushort x = BitConverter.ToUInt16(raw, i + 0);
ushort y = BitConverter.ToUInt16(raw, i + 2);
ushort z = BitConverter.ToUInt16(raw, i + 4);
Set(raw[i + 6], 0, x - X, y - Y, z - Z);
block.BlockID = raw[i + 6];
Set(block, x - X, y - Y, z - Z);
}
UsedBlocks = Volume;
OriginX = X; OriginY = Y; OriginZ = Z;
}
void CalculateBoundsOld(byte[] raw) {
void UnpackExtBlocks(byte[] extBlocks) {
isExt = new byte[(Volume + 7) / 8];
for (int i = 0; i < raw.Length; i++) {
if (raw[i] != Block.custom_block) continue;
raw[i] = extBlocks[i];
isExt[i >> 3] |= (byte)(1 << (i & 0x07));
}
}
void CalculateBounds(byte[] raw) {
int minX = int.MaxValue, minY = int.MaxValue, minZ = int.MaxValue;
int maxX = int.MinValue, maxY = int.MinValue, maxZ = int.MinValue;
for (int i = 0; i < raw.Length; i += 7) {
@ -146,19 +211,13 @@ namespace MCGalaxy.Drawing {
minX = Math.Min(x, minX); maxX = Math.Max(x, maxX);
minY = Math.Min(y, minY); maxY = Math.Max(y, maxY);
minZ = Math.Min(z, minZ); maxZ = Math.Max(z, maxZ);
minZ = Math.Min(z, minZ); maxZ = Math.Max(z, maxZ);
}
X = minX; Y = minY; Z = minZ;
Width = maxX - minX + 1;
Height = maxY - minY + 1;
Length = maxZ - minZ + 1;
Blocks = new byte[Width * Height * Length];
ExtBlocks = new byte[Width * Height * Length];
Init(minX, minY, minZ,
(maxX - minX) + 1,
(maxY - minY) + 1,
(maxZ - minZ) + 1);
}
internal int OppositeOriginX { get { return OriginX == X ? X + Width - 1 : X; } }
internal int OppositeOriginY { get { return OriginY == Y ? Y + Height - 1 : Y; } }
internal int OppositeOriginZ { get { return OriginZ == Z ? Z + Length - 1 : Z; } }
}
}

View File

@ -55,11 +55,13 @@ namespace MCGalaxy.Drawing {
}
static CopyState Rotate(CopyState state, CopyState flipped, int[] m) {
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
for (int i = 0; i < blocks.Length; i++) {
int volume = state.Volume;
for (int i = 0; i < volume; i++) {
ushort x, y, z;
state.GetCoords(i, out x, out y, out z);
flipped.Set(blocks[i], extBlocks[i],
ExtBlock block = state.Get(i);
flipped.Set(block,
Rotate(m[0], x, y, z, state),
Rotate(m[1], x, y, z, state),
Rotate(m[2], x, y, z, state));
@ -81,12 +83,12 @@ namespace MCGalaxy.Drawing {
const int posX = 0x100, negX = 0x200, posY = 0x010, negY = 0x020, posZ = 0x001, negZ = 0x002;
static int Rotate(int row, int x, int y, int z, CopyState state) {
switch (row) {
case posX: return x;
case negX: return (state.Width - 1 - x);
case posY: return y;
case negY: return (state.Height - 1 - y);
case posZ: return z;
case negZ: return (state.Length - 1 - z);
case posX: return x;
case negX: return (state.Width - 1 - x);
case posY: return y;
case negY: return (state.Height - 1 - y);
case posZ: return z;
case negZ: return (state.Length - 1 - z);
}
return 0;
}
@ -102,9 +104,8 @@ namespace MCGalaxy.Drawing {
public static void MirrorX(CopyState state) {
int midZ = state.Length / 2, maxZ = state.Length - 1;
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
state.OriginZ = state.OppositeOriginZ;
state.Offset.Z = -state.Offset.Z;
state.Offset.Z = -state.Offset.Z;
for (int y = 0; y < state.Height; y++) {
for (int z = 0; z < midZ; z++) {
@ -112,7 +113,8 @@ namespace MCGalaxy.Drawing {
int start = state.GetIndex(0, y, z);
int end = state.GetIndex(0, y, endZ);
for (int x = 0; x < state.Width; x++) {
Swap(blocks, extBlocks, start, end);
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
state.Set(blockB, start); state.Set(blockA, end);
start++; end++;
}
}
@ -121,7 +123,6 @@ namespace MCGalaxy.Drawing {
public static void MirrorY(CopyState state) {
int midY = state.Height / 2, maxY = state.Height - 1;
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
state.OriginY = state.OppositeOriginY;
state.Offset.Y = -state.Offset.Y;
@ -131,7 +132,8 @@ namespace MCGalaxy.Drawing {
int end = state.GetIndex(0, endY, 0);
for (int z = 0; z < state.Length; z++) {
for (int x = 0; x < state.Width; x++) {
Swap(blocks, extBlocks, start, end);
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
state.Set(blockB, start); state.Set(blockA, end);
start++; end++;
}
}
@ -140,7 +142,6 @@ namespace MCGalaxy.Drawing {
public static void MirrorZ(CopyState state) {
int midX = state.Width / 2, maxX = state.Width - 1;
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
state.OriginX = state.OppositeOriginX;
state.Offset.X = -state.Offset.X;
@ -150,15 +151,12 @@ namespace MCGalaxy.Drawing {
int endX = maxX - x;
int start = state.GetIndex(x, y, z);
int end = state.GetIndex(endX, y, z);
Swap(blocks, extBlocks, start, end);
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
state.Set(blockB, start); state.Set(blockA, end);
}
}
}
}
static void Swap(byte[] blocks, byte[] extBlocks, int a, int b) {
byte tmp = blocks[a]; blocks[a] = blocks[b]; blocks[b] = tmp;
tmp = extBlocks[a]; extBlocks[a] = extBlocks[b]; extBlocks[b] = tmp;
}
}
}