TrueCraft
TrueCraft copied to clipboard
Client - Ways to reduce memory usage.
- In ChunkHandler.HandleData(), you already know the total uncompressed size of the data. You could use a memory stream with this total size to avoid having to do any resizing operations. (You'll have to manually decompress though, instead of using ZLibStream.UncompressBuffer)
~~2) Why are you using storing the drawable coordinates in ChunkConverter.ProcessChunk()? Can't you just directly call the RenderBlock method?~~
- You probably shouldn't be returning arrays in BlockRenderer.CreateQuad, as this allocates a lot of memory.(CLRProfiler showed ~570 megabytes) Probably a better way would be to pass an index parameter as ref and increment that. I am not sure if this works correctly, but here's one way I came up with:
protected VertexPositionNormalTexture[] CreateUniformCube(Vector3 offset, Vector2[] texture, int indicesOffset, out int[] indices) {
indices = new int[6 * 6];
var vertices = new VertexPositionNormalTexture[4 * 6];
int textureIndex = 0;
int verticesLocIndex = 0, indicesLocIndex = 0;
for (int side = 0; side < 6; side++) {
CreateQuad(side, offset, texture, textureIndex % texture.Length, indicesOffset,
vertices, indices, ref verticesLocIndex, ref indicesLocIndex );
textureIndex += 4;
}
return vertices;
}
static int[] baseIndices = new[] { 0, 1, 3, 1, 2, 3 };
protected static void CreateQuad(int face, Vector3 offset, Vector2[] texture, int textureOffset,
int indicesOffset, VertexPositionNormalTexture[] vertices, int[] indices,
ref int verticesLocIndex, ref int indicesLocIndex ) {
for (int i = 0; i < 6; i++)
indices[indicesLocIndex++] = baseIndices[i] + (face * 4) + indicesOffset;
var unit = CubeMesh[face];
var normal = CubeNormals[face];
for (int i = 0; i < 4; i++) {
vertices[verticesLocIndex++] = new VertexPositionNormalTexture(offset + unit[i], normal, texture[textureOffset + i]);
}
}
-
In the Chunk class, you should consider using DateTime.UtcNow instead of DateTime.Now. Not only should this improve performance, this would also reduce memory usage as DateTime.Now allocates memory for parsing timezone info. (CLRProfiler indicates retrieving DateTime.Now when calling Chunk.GetBlockId from processing chunks ends up allocating 144 megabytes of memory)
-
See comment Part 1 addresses the 1.5 gigabytes allocated in List.AddRange, Part 2 addresses the 1.4 gigabytes in BlockRenderer.RenderBlock
Thanks for all of these suggestions. They all look good and I'll implement them as time permits.
Okay, here's the main savings (in two parts):
Part 1
Replace Lists for vertices and indices in ChunkConverter with a class somewhat like this:
using System;
namespace TrueCraft.UnknownNamespace {
public class FastList<T> {
public T[] buffer;
public int size;
public FastList( int capacity ) {
buffer = new T[capacity];
}
public void Add( T item ) {
EnsureCapacity( size + 1 );
buffer[size++] = item;
}
public void AddRange( T[] collection ) {
int count = collection.Length;
EnsureCapacity( size + count );
Array.Copy( collection, 0, buffer, size, count );
size += count;
}
public void Reset() {
size = 0;
}
private void EnsureCapacity( int minCapacity ) {
if( buffer.Length < minCapacity ) {
int newCapacity = buffer.Length * 2;
if( newCapacity < minCapacity ) newCapacity = minCapacity;
T[] array = new T[newCapacity];
Array.Copy( buffer, 0, array, 0, size );
buffer = array;
}
}
}
}
You'll also have to change the mesh constructor to something along these lines:
public Mesh(GraphicsDevice device, VertexPositionNormalTexture[] vertices, int[] indices,
int verticesUsed, int indicesUsed, bool calculateBounds = true)
{
Empty = verticesUsed == 0 && indicesUsed == 0;
if (!Empty) {
Vertices = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration,
verticesUsed, BufferUsage.WriteOnly);
Vertices.SetData(vertices, 0, verticesUsed);
Indices = new IndexBuffer(device, typeof(int), indicesUsed, BufferUsage.WriteOnly);
Indices.SetData(indices, 0, indicesUsed);
...
}
}
This way, you can reuse arrays when building chunks, and also avoid allocating extra memory in AddRange, unlike the standard List implementation. (Assuming that the standard List implementation is like the ArrayList implementation in CoreFX)
Part 2
This part is relatively simple - Just pass the FastList directly to all BlockRenderer.RenderBlock methods, instead of using intermediate arrays.