- VertexBuffer interface redefined to remove an IntPtr overload for SetData. This removes some unsafe code in TerrainSpriteLayer. This also allows the ThreadedVertexBuffer to use a buffer and post these calls, meaning the SetData call can now be non-blocking. - ThreadedTexture SetData now checks the incoming array size. As the arrays sent here are usually large (megabytes) this allows us to avoid creating temp arrays in the LOH and skip Array.Copy calls on large arrays. This means the call is now blocking more often, but significantly reduces memory churn and GC Gen2 collections.
114 lines
3.0 KiB
C#
114 lines
3.0 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace OpenRA.Platforms.Default
|
|
{
|
|
sealed class VertexBuffer<T> : ThreadAffine, IVertexBuffer<T>
|
|
where T : struct
|
|
{
|
|
static readonly int VertexSize = Marshal.SizeOf(typeof(T));
|
|
uint buffer;
|
|
bool disposed;
|
|
|
|
public VertexBuffer(int size)
|
|
{
|
|
OpenGL.glGenBuffers(1, out buffer);
|
|
OpenGL.CheckGLError();
|
|
Bind();
|
|
|
|
// Generates a buffer with uninitialized memory.
|
|
OpenGL.glBufferData(OpenGL.GL_ARRAY_BUFFER,
|
|
new IntPtr(VertexSize * size),
|
|
IntPtr.Zero,
|
|
OpenGL.GL_DYNAMIC_DRAW);
|
|
OpenGL.CheckGLError();
|
|
|
|
// We need to zero all the memory. Let's generate a smallish array and copy that over the whole buffer.
|
|
var zeroedArrayElementSize = Math.Min(size, 2048);
|
|
var ptr = GCHandle.Alloc(new T[zeroedArrayElementSize], GCHandleType.Pinned);
|
|
try
|
|
{
|
|
for (var offset = 0; offset < size; offset += zeroedArrayElementSize)
|
|
{
|
|
var length = Math.Min(zeroedArrayElementSize, size - offset);
|
|
OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER,
|
|
new IntPtr(VertexSize * offset),
|
|
new IntPtr(VertexSize * length),
|
|
ptr.AddrOfPinnedObject());
|
|
OpenGL.CheckGLError();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ptr.Free();
|
|
}
|
|
}
|
|
|
|
public void SetData(T[] data, int length)
|
|
{
|
|
SetData(data, 0, 0, length);
|
|
}
|
|
|
|
public void SetData(T[] data, int offset, int start, int length)
|
|
{
|
|
Bind();
|
|
|
|
var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER,
|
|
new IntPtr(VertexSize * start),
|
|
new IntPtr(VertexSize * length),
|
|
ptr.AddrOfPinnedObject() + VertexSize * offset);
|
|
}
|
|
finally
|
|
{
|
|
ptr.Free();
|
|
}
|
|
|
|
OpenGL.CheckGLError();
|
|
}
|
|
|
|
public void Bind()
|
|
{
|
|
VerifyThreadAffinity();
|
|
OpenGL.glBindBuffer(OpenGL.GL_ARRAY_BUFFER, buffer);
|
|
OpenGL.CheckGLError();
|
|
OpenGL.glVertexAttribPointer(Shader.VertexPosAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, IntPtr.Zero);
|
|
OpenGL.CheckGLError();
|
|
OpenGL.glVertexAttribPointer(Shader.TexCoordAttributeIndex, 4, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(12));
|
|
OpenGL.CheckGLError();
|
|
OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28));
|
|
OpenGL.CheckGLError();
|
|
OpenGL.glVertexAttribPointer(Shader.TintAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(36));
|
|
OpenGL.CheckGLError();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
void Dispose(bool disposing)
|
|
{
|
|
if (disposed)
|
|
return;
|
|
disposed = true;
|
|
OpenGL.glDeleteBuffers(1, ref buffer);
|
|
OpenGL.CheckGLError();
|
|
}
|
|
}
|
|
}
|