1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using System.Runtime.InteropServices;
14 
15 namespace OpenRA.Platforms.Default
16 {
17 	sealed class VertexBuffer<T> : ThreadAffine, IVertexBuffer<T>
18 			where T : struct
19 	{
20 		static readonly int VertexSize = Marshal.SizeOf(typeof(T));
21 		uint buffer;
22 		bool disposed;
23 
VertexBuffer(int size)24 		public VertexBuffer(int size)
25 		{
26 			OpenGL.glGenBuffers(1, out buffer);
27 			OpenGL.CheckGLError();
28 			Bind();
29 
30 			// Generates a buffer with uninitialized memory.
31 			OpenGL.glBufferData(OpenGL.GL_ARRAY_BUFFER,
32 					new IntPtr(VertexSize * size),
33 					IntPtr.Zero,
34 					OpenGL.GL_DYNAMIC_DRAW);
35 			OpenGL.CheckGLError();
36 
37 			// We need to zero all the memory. Let's generate a smallish array and copy that over the whole buffer.
38 			var zeroedArrayElementSize = Math.Min(size, 2048);
39 			var ptr = GCHandle.Alloc(new T[zeroedArrayElementSize], GCHandleType.Pinned);
40 			try
41 			{
42 				for (var offset = 0; offset < size; offset += zeroedArrayElementSize)
43 				{
44 					var length = Math.Min(zeroedArrayElementSize, size - offset);
45 					OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER,
46 						new IntPtr(VertexSize * offset),
47 						new IntPtr(VertexSize * length),
48 						ptr.AddrOfPinnedObject());
49 					OpenGL.CheckGLError();
50 				}
51 			}
52 			finally
53 			{
54 				ptr.Free();
55 			}
56 		}
57 
SetData(T[] data, int length)58 		public void SetData(T[] data, int length)
59 		{
60 			SetData(data, 0, length);
61 		}
62 
SetData(T[] data, int start, int length)63 		public void SetData(T[] data, int start, int length)
64 		{
65 			Bind();
66 
67 			var ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
68 			try
69 			{
70 				OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER,
71 					new IntPtr(VertexSize * start),
72 					new IntPtr(VertexSize * length),
73 					ptr.AddrOfPinnedObject());
74 			}
75 			finally
76 			{
77 				ptr.Free();
78 			}
79 
80 			OpenGL.CheckGLError();
81 		}
82 
SetData(IntPtr data, int start, int length)83 		public void SetData(IntPtr data, int start, int length)
84 		{
85 			Bind();
86 			OpenGL.glBufferSubData(OpenGL.GL_ARRAY_BUFFER,
87 				new IntPtr(VertexSize * start),
88 				new IntPtr(VertexSize * length),
89 				data);
90 			OpenGL.CheckGLError();
91 		}
92 
Bind()93 		public void Bind()
94 		{
95 			VerifyThreadAffinity();
96 			OpenGL.glBindBuffer(OpenGL.GL_ARRAY_BUFFER, buffer);
97 			OpenGL.CheckGLError();
98 			OpenGL.glVertexAttribPointer(Shader.VertexPosAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, IntPtr.Zero);
99 			OpenGL.CheckGLError();
100 			OpenGL.glVertexAttribPointer(Shader.TexCoordAttributeIndex, 4, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(12));
101 			OpenGL.CheckGLError();
102 			OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28));
103 			OpenGL.CheckGLError();
104 		}
105 
Dispose()106 		public void Dispose()
107 		{
108 			Dispose(true);
109 			GC.SuppressFinalize(this);
110 		}
111 
Dispose(bool disposing)112 		void Dispose(bool disposing)
113 		{
114 			if (disposed)
115 				return;
116 			disposed = true;
117 			OpenGL.glDeleteBuffers(1, ref buffer);
118 			OpenGL.CheckGLError();
119 		}
120 	}
121 }
122