Added buffer and buffer<>, tweaked more documentation

This commit is contained in:
2017-03-09 20:52:11 -05:00
parent bc41ccd65b
commit 9760093f39
9 changed files with 365 additions and 47 deletions

257
Diamond/Buffer.cs Normal file
View File

@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using NLog;
using OpenTK.Graphics.OpenGL4;
namespace Diamond
{
/// <summary>
/// Wrap an OpenGL buffer object
/// </summary>
public class Buffer : GLObject
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
#region Static
private static readonly Dictionary<BufferTarget, Buffer> BoundBuffers;
/// <inheritdoc/>
static Buffer()
{
BoundBuffers = new Dictionary<BufferTarget, Buffer>();
foreach (var value in Enum.GetValues(typeof(BufferTarget)).Cast<BufferTarget>())
BoundBuffers[value] = null;
}
#region ArrayBuffer
/// <summary>
/// The buffer will be used as a source for vertex data, but the connection is only made when
/// glVertexAttribPointer is called. The pointer field of this function is taken as a byte
/// offset from the beginning of whatever buffer is currently bound to this target.
/// </summary>
public static Buffer ArrayBuffer
{
get => BoundBuffers[BufferTarget.ArrayBuffer];
set => Bind(BufferTarget.ArrayBuffer, value);
}
#endregion
#region ElementArrayBuffer
/// <summary>
/// All rendering functions of the form gl*Draw*Elements* will use the pointer field as a byte
/// offset from the beginning of the buffer object bound to this target. The indices used for
/// indexed rendering will be taken from the buffer object. Note that this binding target is
/// part of a Vertex Array Objects state, so a VAO must be bound before binding a buffer here.
/// </summary>
public static Buffer ElementArrayBuffer
{
get => BoundBuffers[BufferTarget.ArrayBuffer];
set => Bind(BufferTarget.ElementArrayBuffer, value);
}
#endregion
#region DrawIndirectBuffer
/// <summary>
/// The buffer bound to this target will be used as the source for the indirect data when performing
/// indirect rendering. This is only available in core OpenGL 4.0 or with ARB_draw_indirect.
/// </summary>
public static Buffer DrawIndirectBuffer
{
get => BoundBuffers[BufferTarget.DrawIndirectBuffer];
set => Bind(BufferTarget.DrawIndirectBuffer, value);
}
#endregion
/// <summary>
/// Bind a buffer to a target. If buffer is null, unbinds the target.
/// </summary>
/// <param name="target">The binding target</param>
/// <param name="buffer">The buffer to bind, or 0 if null</param>
public static void Bind(BufferTarget target, Buffer buffer)
{
GL.BindBuffer(target, buffer?.Id ?? 0);
Logger.Debug("Bound {0} to {1}", (object) buffer ?? "default buffer", target);
BoundBuffers[target] = buffer;
}
#endregion
#region ctor, Delete()
/// <summary>
/// Create a buffer object wrapper
/// </summary>
internal Buffer()
: base(GL.GenBuffer())
{
Logger.Debug("Created {0}", this);
}
/// <inheritdoc />
protected override void Delete()
{
Logger.Debug("Disposing {0}", this);
GL.DeleteBuffer(Id);
}
#endregion
#region Properties
#region Queries
/// <summary>
/// The usage hint for the current data store
/// </summary>
public BufferUsageHint Usage => (BufferUsageHint) Get(BufferParameterName.BufferUsage);
/// <summary>
/// The size of the current data store in bytes
/// </summary>
public int Size => Get(BufferParameterName.BufferSize);
#endregion
#region Stored
#endregion
#endregion
#region Methods
/// <summary>
/// Get a property of this buffer
/// </summary>
/// <param name="param">The property to get</param>
/// <returns>The int value of the property</returns>
public int Get(BufferParameterName param)
{
ArrayBuffer = this;
GL.GetBufferParameter(BufferTarget.ArrayBuffer, param, out int res);
return res;
}
/// <summary>
/// Bind this buffer to a target
/// </summary>
/// <param name="target">The binding target</param>
public void Bind(BufferTarget target) => Bind(target, this);
/// <inheritdoc />
public override string ToString() =>
$"'Buffer {Id}'";
/// <summary>
/// Upload data to this buffer. Deletes the existing data store and creates a new one
/// from the marshalled size of T.
/// </summary>
/// <typeparam name="T">Data element type, used with Marshal.SizeOf to calculate size of data store</typeparam>
/// <param name="data">Values to upload</param>
/// <param name="usage">Usage hint for for this data.</param>
// todo: add usage hint documentation
public void Upload<T>(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw) where T : struct
{
Logger.Debug("Updating {0} data (replacing store)", this);
ArrayBuffer = this;
GL.BufferData(BufferTarget.ArrayBuffer, data.SizeInBytes(), data, usage);
}
/// <summary>
/// Upload the data to this buffer within a range. Does not delete the existing data store.
/// </summary>
/// <param name="data">The data to upload. The range is also applied to this array.</param>
/// <param name="offset">The offset index of data to begin uploading</param>
/// <param name="count">The number of T elements to upload</param>
/// <typeparam name="T">Data element type. Offsets are applied according to the size of this in bytes.</typeparam>
public void Upload<T>(T[] data, int offset, int count) where T : struct
{
Logger.Debug("Updating {0} data range ({1} for {2}] from type {3}",
this, offset, count, typeof(T).Name);
ArrayBuffer = this;
GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr) offset, count, data);
}
#endregion
#region Factory Methods
/// <summary>
/// Creates a buffer, initialized with an array
/// </summary>
/// <typeparam name="T">Data element type.</typeparam>
/// <param name="data">Data used to initialize the buffer</param>
/// <param name="usage">The usage hint for this buffer</param>
/// <returns>The initialized buffer, or null if initialization failed</returns>
// todo: add usage hint documentation
public static Buffer<T> FromData<T>(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw)
where T : struct
{
var buffer = new Buffer<T>();
buffer.Upload(data, usage);
return buffer;
}
#endregion
}
/// <summary>
/// Generic overload for Buffer. Caches properties like data, usage, and size.
/// </summary>
/// <typeparam name="T">Data type used for all operations</typeparam>
// todo this needs to be made as complete as the non-generic version
public sealed class Buffer<T> : Buffer where T : struct
{
/// <summary>
/// Cached usage hint for the buffer's storage
/// </summary>
public new BufferUsageHint Usage { get; private set; }
/// <summary>
/// Size of the buffer data storage in T units.
/// </summary>
public new int Size { get; private set; }
/// <summary>
/// The data in this buffer. Not copied to the buffer until Upload() is called.
/// </summary>
public T[] Data { get; private set; }
/// <summary>
/// Upload data to this buffer. Deletes the existing data store and creates a new one.
/// </summary>
/// <param name="data">Values to upload</param>
/// <param name="usage">Usage hint for for this data.</param>
// todo: add usage hint documentation
public void Upload(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw)
{
base.Upload<T>(data, usage);
Data = data;
Usage = usage;
Size = data.Length;
}
/// <summary>
/// Upload the data to this buffer within a range. Does not delete the existing data store.
/// </summary>
/// <param name="offset">The offset index of data to begin uploading</param>
/// <param name="count">The number of T elements to upload</param>
public void Upload(int offset, int count)
{
base.Upload<T>(Data, offset, count);
}
}
}

View File

@@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Diamond.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -51,6 +52,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Buffer.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="GLObject.cs" />
<Compile Include="Util\SubArray.cs" />
<Compile Include="Shaders\Program.cs" />

26
Diamond/Extensions.cs Normal file
View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Diamond
{
internal static class Extensions
{
private static readonly Dictionary<Type, int> ByteSizes = new Dictionary<Type, int>();
public static int SizeInBytes(this Type type)
{
if (!ByteSizes.ContainsKey(type))
ByteSizes[type] = Marshal.SizeOf(type);
return ByteSizes[type];
}
public static int SizeInBytes<T>(this T[] arr) where T : struct
{
return typeof(T).SizeInBytes() * arr.Length;
}
}
}

View File

@@ -4,6 +4,9 @@ using OpenTK.Graphics;
namespace Diamond
{
/// <summary>
/// A wrapper class for any OpenGL object
/// </summary>
public abstract class GLObject : IDisposable
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

View File

@@ -10,7 +10,7 @@ namespace Diamond.Shaders
/// <summary>
/// Wrap and OpenGL program object
/// </summary>
public class Program : GLObject
public sealed class Program : GLObject
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@@ -19,31 +19,36 @@ namespace Diamond.Shaders
private static Program _current;
/// <summary>
/// The currently active program
/// The currently active program, or null if the default program is active.
/// </summary>
public static Program Current
{
get => _current;
set
{
if (value != null && !value.Linked)
{
Logger.Error("Cannot use {0} because it is not linked", value);
value = null;
set => Use(value);
}
Logger.Debug("Using {0}", (object) value ?? "default program");
GL.UseProgram((_current = value)?.Id ?? 0);
/// <summary>
/// Use this program. If program is null, use the default program.
/// </summary>
public static void Use(Program program)
{
if (program != null && !program.Linked)
{
Logger.Error("Cannot use {0} because it is not linked", program);
program = null;
}
GL.UseProgram(program?.Id ?? 0);
Logger.Debug("Using {0}", (object) program ?? "default program");
_current = program;
}
#endregion
#region Constructor, Delete()
#region ctor, Delete()
/// <summary>
/// Create a program object wrapper
/// </summary>
/// <inheritdoc />
private Program()
: base(GL.CreateProgram())
{
@@ -64,12 +69,12 @@ namespace Diamond.Shaders
#region Queries
/// <summary>
/// The number of active uniforms
/// Gets the number of active uniforms
/// </summary>
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
/// <summary>
/// The number of active attributes
/// Gets the number of active attributes
/// </summary>
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
@@ -78,7 +83,7 @@ namespace Diamond.Shaders
#region Stored
/// <summary>
/// The InfoLog for this program
/// The InfoLog for this program since it was last linked
/// </summary>
public string InfoLog { get; private set; }
@@ -94,7 +99,7 @@ namespace Diamond.Shaders
#region Methods
/// <summary>
/// Get a property of this program
/// Get a property of this program. invokes glGetProgram and returns an int result.
/// </summary>
/// <param name="param">The program property to get</param>
/// <returns>The int value of the program property</returns>
@@ -105,7 +110,8 @@ namespace Diamond.Shaders
}
/// <summary>
/// Try to link this program
/// Try to link this program. If linking fails, the InfoLog is updated, and attribute and uniform caches are reset.
/// If linking is successful, attribute and uniform caches are generated
/// </summary>
public void Link()
{
@@ -148,11 +154,9 @@ namespace Diamond.Shaders
}
/// <summary>
/// Use this program
/// <para></para>
/// Equivalent to <code>Program.Current = value</code>
/// Use this program. Equivalent to Program.Use(this);
/// </summary>
public void Use() => Current = this;
public void Use() => Use(this);
#region Attribute Locations
@@ -163,18 +167,19 @@ namespace Diamond.Shaders
/// Check if this program has an active attribute
/// </summary>
/// <param name="name">The attribute name</param>
/// <returns>Whether the progrma has an active attribute</returns>
/// <returns>Whether the program has an active attribute</returns>
public bool HasAttribute(string name) => _attributes.ContainsKey(name);
/// <summary>
/// Check if this program has an active uniform
/// </summary>
/// <param name="name">The uniform name</param>
/// <returns>Whether the progrma has an active uniform</returns>
/// <returns>Whether the program has an active uniform</returns>
public bool HasUniform(string name) => _uniforms.ContainsKey(name);
/// <summary>
/// Get the location of an attribute by name
/// Get the location of an attribute by name. Throws InvalidOperationException if the attribute
/// is not found. This can be checked with HasAttribute
/// </summary>
/// <param name="name">The attribute name</param>
/// <returns>The location of the attribute</returns>
@@ -186,7 +191,8 @@ namespace Diamond.Shaders
}
/// <summary>
/// Get the location of an uniform by name
/// Get the location of an uniform by name. Throws InvalidOperationException if the uniform
/// is not found. This can be checked with HasUniform
/// </summary>
/// <param name="name">The uniform name</param>
/// <returns>The location of the uniform</returns>
@@ -199,11 +205,21 @@ namespace Diamond.Shaders
#endregion
/// <summary>
/// Attach a shader to this program
/// Attach a shader to this program. If shader is null, does nothing. If shader is not compiled
/// it is still attached but program link will likely fail.
/// </summary>
/// <param name="shader">The shader to attach</param>
public void Attach(Shader shader)
{
if (shader == null)
{
Logger.Error("Cannot attach null shader to {0}", this);
return;
}
if (!shader.Compiled)
Logger.Warn("{0} is not compiled. Program link will likely fail.", shader);
Logger.Debug("Attaching {0} to {1}", shader, this);
GL.AttachShader(Id, shader.Id);
}
@@ -217,14 +233,16 @@ namespace Diamond.Shaders
#region Factory Methods
/// <summary>
/// Create and link a program from precompiled shaders
/// Create and link a program from precompiled shaders. If any shader is null or uncompiled it is still
/// attached, although program link will likely fail.
/// </summary>
/// <param name="shaders">The shaders used in this program</param>
/// <returns>A linked program, or null if initialization failed</returns>
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
/// <summary>
/// Create and link a program from precompiled shaders
/// Create and link a program from precompiled shaders. If any shader is null or uncompiled it is still
/// attached, although program link will likely fail.
/// </summary>
/// <param name="shaders">The shaders used in this program</param>
/// <returns>A linked program, or null if initialization failed</returns>
@@ -239,15 +257,7 @@ namespace Diamond.Shaders
var program = new Program();
foreach (var shader in shaders)
{
if (shader == null)
{
Logger.Error("One or more shaders is null - cannot create program");
program.Dispose();
return null;
}
program.Attach(shader);
}
program.Link();

View File

@@ -12,7 +12,7 @@ namespace Diamond.Shaders
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
#region Constructor, Delete()
#region ctor, Delete()
/// <summary>
/// Create a shader object wrapper

View File

@@ -89,6 +89,7 @@ namespace Diamond.Util
return arr;
}
/// <inheritdoc />
public override string ToString()
{
if (Length == 0)

View File

@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Diamond.Shaders;
using Diamond.Util;
using Newtonsoft.Json.Linq;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using Diamond;
using Buffer = Diamond.Buffer;
namespace hexworld
{
@@ -24,6 +21,7 @@ namespace hexworld
}
private Program _pgm;
private Buffer<float> _buf;
/// <inheritdoc />
protected override void OnUnload(EventArgs e)
@@ -41,7 +39,25 @@ namespace hexworld
_pgm = Program.FromFiles("res/obj.fs.glsl", "res/obj.vs.glsl");
Program.Current = _pgm;
Program.Current = null;
_buf = Buffer.FromData(new float[]
{
-.8f, -.8f,
+.8f, -.8f,
+.0f, +.8f
});
}
/// <inheritdoc />
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Viewport(ClientRectangle);
GL.Clear(ClearBufferMask.ColorBufferBit);
SwapBuffers();
}
}
}

View File

@@ -19,10 +19,12 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>