From a5979aea46456520886c7c5354cbb74d0db854d6 Mon Sep 17 00:00:00 2001 From: "Paul C. Scharf" Date: Mon, 5 Aug 2024 21:35:43 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=9A=A7=20Make=20renderable=20construc?= =?UTF-8?q?tion=20more=20flexible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Rendering/BufferExtensions.cs | 30 ++++ .../Core/Rendering/IFlushableBuffer.cs | 6 + .../Core/Rendering/IndexBuffer.cs | 59 ++++++++ .../Core/Rendering/Renderable.Builder.cs | 143 ++++++++++++++++++ .../Core/Rendering/Renderable.ForBatched.cs | 47 +++--- .../Core/Rendering/Renderable.ForVertices.cs | 75 --------- .../Renderable.ForVerticesAndIndices.cs | 117 -------------- .../Core/Rendering/VertexBuffer.cs | 51 +++++++ .../MeshBuilders/IndexedMeshBuilder.cs | 5 +- .../PostProcessing/PostProcessor.cs | 2 +- 10 files changed, 321 insertions(+), 214 deletions(-) create mode 100644 Bearded.Graphics/Core/Rendering/BufferExtensions.cs create mode 100644 Bearded.Graphics/Core/Rendering/IFlushableBuffer.cs create mode 100644 Bearded.Graphics/Core/Rendering/IndexBuffer.cs create mode 100644 Bearded.Graphics/Core/Rendering/Renderable.Builder.cs delete mode 100644 Bearded.Graphics/Core/Rendering/Renderable.ForVertices.cs delete mode 100644 Bearded.Graphics/Core/Rendering/Renderable.ForVerticesAndIndices.cs create mode 100644 Bearded.Graphics/Core/Rendering/VertexBuffer.cs diff --git a/Bearded.Graphics/Core/Rendering/BufferExtensions.cs b/Bearded.Graphics/Core/Rendering/BufferExtensions.cs new file mode 100644 index 0000000..ee12664 --- /dev/null +++ b/Bearded.Graphics/Core/Rendering/BufferExtensions.cs @@ -0,0 +1,30 @@ +using Bearded.Graphics.Vertices; + +namespace Bearded.Graphics.Rendering; + +public static class BufferExtensions +{ + public static IVertexBuffer AsVertexBuffer(this Buffer buffer) + where TVertex : struct, IVertexData + { + return VertexBuffer.From(buffer); + } + + public static IVertexBuffer AsVertexBuffer(this BufferStream buffer) + where TVertex : struct, IVertexData + { + return VertexBuffer.From(buffer); + } + + public static IIndexBuffer AsIndexBuffer(this Buffer buffer) + where TIndex : struct + { + return IndexBuffer.From(buffer); + } + + public static IIndexBuffer AsIndexBuffer(this BufferStream buffer) + where TIndex : struct + { + return IndexBuffer.From(buffer); + } +} diff --git a/Bearded.Graphics/Core/Rendering/IFlushableBuffer.cs b/Bearded.Graphics/Core/Rendering/IFlushableBuffer.cs new file mode 100644 index 0000000..9fa080b --- /dev/null +++ b/Bearded.Graphics/Core/Rendering/IFlushableBuffer.cs @@ -0,0 +1,6 @@ +namespace Bearded.Graphics.Rendering; + +public interface IFlushableBuffer +{ + void FlushIfNeeded(); +} diff --git a/Bearded.Graphics/Core/Rendering/IndexBuffer.cs b/Bearded.Graphics/Core/Rendering/IndexBuffer.cs new file mode 100644 index 0000000..f00dc2c --- /dev/null +++ b/Bearded.Graphics/Core/Rendering/IndexBuffer.cs @@ -0,0 +1,59 @@ +using System; +using OpenTK.Graphics.OpenGL; + +namespace Bearded.Graphics.Rendering; + +public interface IIndexBuffer +{ + int Count { get; } + DrawElementsType ElementType { get; } + void ConfigureBoundVertexArray(); +} + +public static class IndexBuffer +{ + public static IIndexBuffer From(Buffer buffer) + where TIndex : struct + { + return new Static(buffer); + } + + public static IIndexBuffer From(BufferStream stream) + where TIndex : struct + { + return new Streaming(stream); + } + + private sealed class Static(Buffer buffer) : IIndexBuffer + where TIndex : struct + { + public int Count => buffer.Count; + + public DrawElementsType ElementType { get; } = elementType(); + + public void ConfigureBoundVertexArray() => buffer.Bind(BufferTarget.ElementArrayBuffer); + } + + private sealed class Streaming(BufferStream stream) : IIndexBuffer, IFlushableBuffer + where TIndex : struct + { + public int Count => stream.Count; + + public DrawElementsType ElementType { get; } = elementType(); + + public void ConfigureBoundVertexArray() => stream.Buffer.Bind(BufferTarget.ElementArrayBuffer); + + public void FlushIfNeeded() => stream.FlushIfDirty(); + } + + private static DrawElementsType elementType() + { + return default(TIndex) switch + { + byte => DrawElementsType.UnsignedByte, + ushort => DrawElementsType.UnsignedShort, + uint => DrawElementsType.UnsignedInt, + _ => throw new NotSupportedException("Index type must be one of [byte, ushort, uint].") + }; + } +} diff --git a/Bearded.Graphics/Core/Rendering/Renderable.Builder.cs b/Bearded.Graphics/Core/Rendering/Renderable.Builder.cs new file mode 100644 index 0000000..e587489 --- /dev/null +++ b/Bearded.Graphics/Core/Rendering/Renderable.Builder.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bearded.Graphics.Shading; +using OpenTK.Graphics.OpenGL; + +namespace Bearded.Graphics.Rendering; + +public static partial class Renderable +{ + public static IRenderable Build(PrimitiveType primitiveType, Action configure) + { + var builder = new Builder(primitiveType); + configure(builder); + return builder.Build(); + } + + public sealed class Builder(PrimitiveType primitiveType) + { + private readonly List vertexBuffers = []; + private IIndexBuffer? indexBuffer; + private Func? instanceCount; + + public Builder With(IVertexBuffer buffer) + { + vertexBuffers.Add(buffer); + return this; + } + + public Builder With(params IVertexBuffer[] buffers) + { + vertexBuffers.AddRange(buffers); + return this; + } + + public Builder With(ReadOnlySpan buffers) + { + vertexBuffers.AddRange(buffers); + return this; + } + + public Builder With(IEnumerable buffers) + { + vertexBuffers.AddRange(buffers); + return this; + } + + public Builder With(IIndexBuffer buffer) + { + indexBuffer = buffer; + return this; + } + + public Builder InstancedWith(Func getInstanceCount) + { + instanceCount = getInstanceCount; + return this; + } + + public IRenderable Build() + { + if (vertexBuffers.Count == 0) + throw new InvalidOperationException("Renderable must have at least one vertex buffer."); + + return build(primitiveType, [..vertexBuffers], indexBuffer, instanceCount); + } + + private static IRenderable build( + PrimitiveType type, + ImmutableArray vertices, + IIndexBuffer? indices, + Func? instanceCount) + { + var flushables = listFlushableBuffers(vertices, indices); + + Action draw = (indices, instanceCount) switch + { + (null, null) => () => GL.DrawArrays(type, 0, vertices[0].Count), + (null, not null) => () => GL.DrawArraysInstanced(type, 0, vertices[0].Count, instanceCount()), + (not null, null) => () => GL.DrawElements(type, indices.Count, indices.ElementType, 0), + (not null, not null) => () => GL.DrawElementsInstanced(type, indices.Count, indices.ElementType, 0, instanceCount()), + }; + + return new Implementation(configure, flushables.IsDefaultOrEmpty ? draw : flushAndDraw); + + void configure(ShaderProgram program) + { + foreach (var buffer in vertices) + { + buffer.ConfigureBoundVertexArray(program); + } + + indices?.ConfigureBoundVertexArray(); + } + + void flushAndDraw() + { + foreach (var flushable in flushables) + { + flushable.FlushIfNeeded(); + } + + draw(); + } + + } + + private static ImmutableArray listFlushableBuffers( + ImmutableArray vertices, IIndexBuffer? indices) + { + var flushableCount = vertices.Count(b => b is IFlushableBuffer); + flushableCount += indices is IFlushableBuffer ? 1 : 0; + + if (flushableCount == 0) + return ImmutableArray.Empty; + + var builder = ImmutableArray.CreateBuilder(flushableCount); + + foreach (var buffer in vertices) + { + if (buffer is IFlushableBuffer flushable) + { + builder.Add(flushable); + } + } + if (indices is IFlushableBuffer indexFlushable) + { + builder.Add(indexFlushable); + } + + return builder.MoveToImmutable(); + } + } + + private sealed class Implementation(Action configureBoundVertexArray, Action render) : IRenderable + { + public DrawCall MakeDrawCallFor(ShaderProgram program) + { + return DrawCall.With(() => configureBoundVertexArray(program), render); + } + } +} diff --git a/Bearded.Graphics/Core/Rendering/Renderable.ForBatched.cs b/Bearded.Graphics/Core/Rendering/Renderable.ForBatched.cs index c59faed..bf822c5 100644 --- a/Bearded.Graphics/Core/Rendering/Renderable.ForBatched.cs +++ b/Bearded.Graphics/Core/Rendering/Renderable.ForBatched.cs @@ -11,21 +11,24 @@ public static partial class Renderable public static IBatchedRenderable ForBatchedVertices(Batcher> batcher, PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched>(batcher, primitiveType, ForVertices); + return new WithBatched>(batcher, buffer => Build(primitiveType, b => b.With(buffer.AsVertexBuffer()))); } public static IBatchedRenderable ForBatchedVertices(Batcher> batcher, PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched>(batcher, primitiveType, ForVertices); + return new WithBatched>(batcher, stream => Build(primitiveType, b => b.With(stream.AsVertexBuffer()))); } public static IBatchedRenderable ForBatchedVerticesAndIndices( Batcher<(Buffer, Buffer)> batcher, PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched<(Buffer, Buffer)>(batcher, primitiveType, - (buffers, pt) => ForVerticesAndIndices(buffers.Item1, buffers.Item2, pt)); + return new WithBatched<(Buffer Vertices, Buffer Indices)>(batcher, + buffers => Build(primitiveType, b => b + .With(buffers.Vertices.AsVertexBuffer()) + .With(buffers.Indices.AsIndexBuffer()) + )); } public static IBatchedRenderable ForBatchedVerticesAndIndices( @@ -33,11 +36,13 @@ public static IBatchedRenderable ForBatchedVerticesAndIndices( PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched(batcher, primitiveType, - (batch, pt) => + return new WithBatched(batcher, + batch => { var (vb, ib) = bufferSelector(batch); - return ForVerticesAndIndices(vb, ib, pt); + return Build(primitiveType, b => b + .With(vb.AsVertexBuffer()) + .With(ib.AsIndexBuffer())); }); } @@ -45,8 +50,11 @@ public static IBatchedRenderable ForBatchedVerticesAndIndices( Batcher<(BufferStream, BufferStream)> batcher, PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched<(BufferStream, BufferStream)>(batcher, primitiveType, - (buffers, pt) => ForVerticesAndIndices(buffers.Item1, buffers.Item2, pt)); + return new WithBatched<(BufferStream, BufferStream)>(batcher, + buffers => Build(primitiveType, b => b + .With(buffers.Item1.AsVertexBuffer()) + .With(buffers.Item2.AsIndexBuffer()) + )); } public static IBatchedRenderable ForBatchedVerticesAndIndices( @@ -54,33 +62,32 @@ public static IBatchedRenderable ForBatchedVerticesAndIndices( PrimitiveType primitiveType) where TV : struct, IVertexData { - return new WithBatched(batcher, primitiveType, - (batch, pt) => + return new WithBatched(batcher, + batch => { var (vb, ib) = bufferSelector(batch); - return ForVerticesAndIndices(vb, ib, pt); + return Build(primitiveType, b => b + .With(vb.AsVertexBuffer()) + .With(ib.AsIndexBuffer()) + ); }); } private sealed class WithBatched : IBatchedRenderable { private readonly Batcher batcher; - private readonly PrimitiveType primitiveType; - private readonly Func createRenderable; + private readonly Func createRenderable; - private readonly Dictionary.Batch, IRenderable> renderables - = new Dictionary.Batch, IRenderable>(); + private readonly Dictionary.Batch, IRenderable> renderables = new(); public event Action? BatchActivated; public event Action? BatchDeactivated; public WithBatched( Batcher batcher, - PrimitiveType primitiveType, - Func createRenderable) + Func createRenderable) { this.batcher = batcher; - this.primitiveType = primitiveType; this.createRenderable = createRenderable; batcher.BatchActivated += onBatchActivated; @@ -103,7 +110,7 @@ private IRenderable getOrCreateRenderableFor(Batcher.Batch batch) { if (!renderables.TryGetValue(batch, out var renderable)) { - renderable = createRenderable(batch.Data, primitiveType); + renderable = createRenderable(batch.Data); renderables.Add(batch, renderable); } diff --git a/Bearded.Graphics/Core/Rendering/Renderable.ForVertices.cs b/Bearded.Graphics/Core/Rendering/Renderable.ForVertices.cs deleted file mode 100644 index c81ed83..0000000 --- a/Bearded.Graphics/Core/Rendering/Renderable.ForVertices.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Bearded.Graphics.Shading; -using Bearded.Graphics.Vertices; -using OpenTK.Graphics.OpenGL; - -namespace Bearded.Graphics.Rendering -{ - public static partial class Renderable - { - public static IRenderable ForVertices(Buffer vertexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVertices(vertexBuffer, primitiveType); - } - - public static IRenderable ForVertices(BufferStream vertexBufferStream, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesStreaming(vertexBufferStream, primitiveType); - } - - private sealed class WithVerticesStreaming : WithVertices - where TVertex : struct, IVertexData - { - private readonly BufferStream vertexBufferStream; - - public WithVerticesStreaming(BufferStream vertexBufferStream, PrimitiveType primitiveType) - : base(vertexBufferStream.Buffer, primitiveType) - { - this.vertexBufferStream = vertexBufferStream; - } - - protected override void Render() - { - vertexBufferStream.FlushIfDirty(); - base.Render(); - } - } - - private class WithVertices : BaseRenderable - where TVertex : struct, IVertexData - { - private readonly Buffer vertexBuffer; - private readonly PrimitiveType primitiveType; - - public WithVertices(Buffer vertexBuffer, PrimitiveType primitiveType) - { - this.vertexBuffer = vertexBuffer; - this.primitiveType = primitiveType; - } - - protected override void ConfigureBoundVertexArray(ShaderProgram program) - { - using var _ = vertexBuffer.Bind(BufferTarget.ArrayBuffer); - VertexData.SetAttributes(program); - } - - protected override void Render() - { - GL.DrawArrays(primitiveType, 0, vertexBuffer.Count); - } - } - - private abstract class BaseRenderable : IRenderable - { - public DrawCall MakeDrawCallFor(ShaderProgram program) - { - return DrawCall.With(() => ConfigureBoundVertexArray(program), Render); - } - - protected abstract void ConfigureBoundVertexArray(ShaderProgram program); - - protected abstract void Render(); - } - } -} diff --git a/Bearded.Graphics/Core/Rendering/Renderable.ForVerticesAndIndices.cs b/Bearded.Graphics/Core/Rendering/Renderable.ForVerticesAndIndices.cs deleted file mode 100644 index ddc4d4d..0000000 --- a/Bearded.Graphics/Core/Rendering/Renderable.ForVerticesAndIndices.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using Bearded.Graphics.Shading; -using Bearded.Graphics.Vertices; -using OpenTK.Graphics.OpenGL; - -namespace Bearded.Graphics.Rendering -{ - public static partial class Renderable - { - public static IRenderable ForVerticesAndIndices( - Buffer vertexBuffer, Buffer indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndices(vertexBuffer, indexBuffer, primitiveType); - } - - public static IRenderable ForVerticesAndIndices( - Buffer vertexBuffer, Buffer indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndices(vertexBuffer, indexBuffer, primitiveType); - } - - public static IRenderable ForVerticesAndIndices( - Buffer vertexBuffer, Buffer indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndices(vertexBuffer, indexBuffer, primitiveType); - } - - public static IRenderable ForVerticesAndIndices( - BufferStream vertexBuffer, BufferStream indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndicesStreaming(vertexBuffer, indexBuffer, primitiveType); - } - - public static IRenderable ForVerticesAndIndices( - BufferStream vertexBuffer, BufferStream indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndicesStreaming(vertexBuffer, indexBuffer, primitiveType); - } - - public static IRenderable ForVerticesAndIndices( - BufferStream vertexBuffer, BufferStream indexBuffer, PrimitiveType primitiveType) - where T : struct, IVertexData - { - return new WithVerticesAndIndicesStreaming(vertexBuffer, indexBuffer, primitiveType); - } - - private sealed class WithVerticesAndIndicesStreaming : WithVerticesAndIndices - where TVertex : struct, IVertexData - where TIndex : struct - { - private readonly BufferStream vertexBufferStream; - private readonly BufferStream indexBufferStream; - - public WithVerticesAndIndicesStreaming(BufferStream vertexBufferStream, - BufferStream indexBufferStream, - PrimitiveType primitiveType) - : base(vertexBufferStream.Buffer, indexBufferStream.Buffer, primitiveType) - { - this.vertexBufferStream = vertexBufferStream; - this.indexBufferStream = indexBufferStream; - } - - protected override void Render() - { - vertexBufferStream.FlushIfDirty(); - indexBufferStream.FlushIfDirty(); - base.Render(); - } - } - - private class WithVerticesAndIndices : BaseRenderable - where TVertex : struct, IVertexData - where TIndex : struct - { - private static readonly DrawElementsType drawElementsType = - default(TIndex) switch - { - byte _ => DrawElementsType.UnsignedByte, - ushort _ => DrawElementsType.UnsignedShort, - uint _ => DrawElementsType.UnsignedInt, - _ => throw new NotSupportedException() - }; - - private readonly Buffer vertexBuffer; - private readonly Buffer indexBuffer; - private readonly PrimitiveType primitiveType; - - public WithVerticesAndIndices(Buffer vertexBuffer, Buffer indexBuffer, - PrimitiveType primitiveType) - { - this.vertexBuffer = vertexBuffer; - this.indexBuffer = indexBuffer; - this.primitiveType = primitiveType; - } - - protected override void ConfigureBoundVertexArray(ShaderProgram program) - { - using (vertexBuffer.Bind(BufferTarget.ArrayBuffer)) - { - VertexData.SetAttributes(program); - } - - indexBuffer.Bind(BufferTarget.ElementArrayBuffer); - } - - protected override void Render() - { - GL.DrawElements(primitiveType, indexBuffer.Count, drawElementsType, 0); - } - } - } -} diff --git a/Bearded.Graphics/Core/Rendering/VertexBuffer.cs b/Bearded.Graphics/Core/Rendering/VertexBuffer.cs new file mode 100644 index 0000000..083ce92 --- /dev/null +++ b/Bearded.Graphics/Core/Rendering/VertexBuffer.cs @@ -0,0 +1,51 @@ +using Bearded.Graphics.Shading; +using Bearded.Graphics.Vertices; + +namespace Bearded.Graphics.Rendering; + +public interface IVertexBuffer +{ + int Count { get; } + void ConfigureBoundVertexArray(ShaderProgram program); +} + +public static class VertexBuffer +{ + public static IVertexBuffer From(Buffer buffer) + where TVertex : struct, IVertexData + { + return new Static(buffer); + } + + public static IVertexBuffer From(BufferStream stream) + where TVertex : struct, IVertexData + { + return new Streaming(stream); + } + + private sealed class Static(Buffer buffer) : IVertexBuffer + where TVertex : struct, IVertexData + { + public int Count => buffer.Count; + + public void ConfigureBoundVertexArray(ShaderProgram program) + { + using var _ = buffer.Bind(); + VertexData.SetAttributes(program); + } + } + + private sealed class Streaming(BufferStream stream) : IVertexBuffer, IFlushableBuffer + where TVertex : struct, IVertexData + { + public int Count => stream.Count; + + public void ConfigureBoundVertexArray(ShaderProgram program) + { + using var _ = stream.Buffer.Bind(); + VertexData.SetAttributes(program); + } + + public void FlushIfNeeded() => stream.FlushIfDirty(); + } +} diff --git a/Bearded.Graphics/MeshBuilders/IndexedMeshBuilder.cs b/Bearded.Graphics/MeshBuilders/IndexedMeshBuilder.cs index 7900a4f..9817650 100644 --- a/Bearded.Graphics/MeshBuilders/IndexedMeshBuilder.cs +++ b/Bearded.Graphics/MeshBuilders/IndexedMeshBuilder.cs @@ -21,7 +21,10 @@ public IndexedMeshBuilder(PrimitiveType primitiveType) public IRenderable ToRenderable() { - return Renderable.ForVerticesAndIndices(Vertices, Indices, primitiveType); + return Renderable.Build(primitiveType, b => b + .With(Vertices.AsVertexBuffer()) + .With(Indices.AsIndexBuffer()) + ); } public void Add( diff --git a/Bearded.Graphics/PostProcessing/PostProcessor.cs b/Bearded.Graphics/PostProcessing/PostProcessor.cs index 67c53f6..e49eda0 100644 --- a/Bearded.Graphics/PostProcessing/PostProcessor.cs +++ b/Bearded.Graphics/PostProcessing/PostProcessor.cs @@ -53,7 +53,7 @@ private static (IRenderable, Buffer) makeRenderable() }); } - var renderable = Renderable.ForVertices(vertices, PrimitiveType.TriangleStrip); + var renderable = Renderable.Build(PrimitiveType.TriangleStrip, b => b.With(vertices.AsVertexBuffer())); return (renderable, vertices); } From ad2624691dbec783abab3259b17a86c6fde4d12c Mon Sep 17 00:00:00 2001 From: "Paul C. Scharf" Date: Mon, 5 Aug 2024 21:36:07 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=94=A2=20Allow=20instanced=20vertex?= =?UTF-8?q?=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Vertices/VertexAttribute.cs | 5 +++- .../Core/Vertices/VertexAttributeTemplate.cs | 5 ++-- Bearded.Graphics/Core/Vertices/VertexData.cs | 29 ++++++++++++------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Bearded.Graphics/Core/Vertices/VertexAttribute.cs b/Bearded.Graphics/Core/Vertices/VertexAttribute.cs index ce2f251..7caff03 100644 --- a/Bearded.Graphics/Core/Vertices/VertexAttribute.cs +++ b/Bearded.Graphics/Core/Vertices/VertexAttribute.cs @@ -10,7 +10,8 @@ public readonly struct VertexAttribute( VertexAttribPointerType type, int stride, int offset, - VertexAttributeFormat format) + VertexAttributeFormat format, + int divisor) { public void SetAttribute(ShaderProgram program) { @@ -37,6 +38,8 @@ public void SetAttribute(ShaderProgram program) default: throw new ArgumentOutOfRangeException(); } + + GL.VertexAttribDivisor(index, divisor); } public override string ToString() => diff --git a/Bearded.Graphics/Core/Vertices/VertexAttributeTemplate.cs b/Bearded.Graphics/Core/Vertices/VertexAttributeTemplate.cs index c704745..d8769c4 100644 --- a/Bearded.Graphics/Core/Vertices/VertexAttributeTemplate.cs +++ b/Bearded.Graphics/Core/Vertices/VertexAttributeTemplate.cs @@ -7,8 +7,9 @@ public readonly struct VertexAttributeTemplate( int size, int bytes, VertexAttribPointerType type, - VertexAttributeFormat format) + VertexAttributeFormat format, + int divisor) { public int Bytes => bytes; - public VertexAttribute ToAttribute(int offset, int stride) => new(name, size, type, stride, offset, format); + public VertexAttribute ToAttribute(int offset, int stride) => new(name, size, type, stride, offset, format, divisor); } diff --git a/Bearded.Graphics/Core/Vertices/VertexData.cs b/Bearded.Graphics/Core/Vertices/VertexData.cs index 511c53d..f638987 100644 --- a/Bearded.Graphics/Core/Vertices/VertexData.cs +++ b/Bearded.Graphics/Core/Vertices/VertexData.cs @@ -30,15 +30,19 @@ public static void SetAttributes(TCollection attributes, ShaderProg } public static ImmutableArray MakeAttributeArray(IEnumerable attributes) - => MakeAttributeArray(attributes.ToList()); + => MakeAttributeArray(attributes.ToArray()); public static ImmutableArray MakeAttributeArray(params VertexAttributeTemplate[] attributes) - => MakeAttributeArray((ICollection)attributes); + => MakeAttributeArray((ReadOnlySpan)attributes); - public static ImmutableArray MakeAttributeArray(ICollection attributes) + public static ImmutableArray MakeAttributeArray(ReadOnlySpan attributes) { - var stride = attributes.Sum(a => a.Bytes); - var array = ImmutableArray.CreateBuilder(attributes.Count); + var stride = 0; + foreach (var template in attributes) + { + stride += template.Bytes; + } + var array = ImmutableArray.CreateBuilder(attributes.Length); var offset = 0; foreach (var template in attributes) { @@ -49,28 +53,31 @@ public static ImmutableArray MakeAttributeArray(ICollection(string name, Format? format = null) - => MakeAttributeTemplate(name, typeof(T), format); + public static VertexAttributeTemplate MakeAttributeTemplate( + string name, Format? format = null, bool instanced = false) + => MakeAttributeTemplate(name, typeof(T), format, instanced); - public static VertexAttributeTemplate MakeAttributeTemplate(string name, Type type, Format? format = null) + public static VertexAttributeTemplate MakeAttributeTemplate( + string name, Type type, Format? format = null, bool instanced = false) { if (!defaultsForType.TryGetValue(type, out var info)) throw new ArgumentException($"Unknown type: {type.Name}"); var bytes = size(info.Type); - return MakeAttributeTemplate(name, info.Type, info.Count, info.Count * bytes, format ?? info.DefaultFormat); + return MakeAttributeTemplate( + name, info.Type, info.Count, info.Count * bytes, format ?? info.DefaultFormat, instanced); } public static VertexAttributeTemplate MakeAttributeTemplate( - string name, PointerType type, int numberOfType, int sizeInBytes, Format format) + string name, PointerType type, int numberOfType, int sizeInBytes, Format format, bool instanced) { if (format == Format.Integer && !isValidIntegerType(type)) throw new ArgumentException("Invalid type for integer vertex attribute. Must be an integer."); if (format == Format.Double && type != PointerType.Double) throw new ArgumentException("Invalid type for 64-bit vertex attribute. Must be Double."); - return new VertexAttributeTemplate(name, numberOfType, sizeInBytes, type, format); + return new VertexAttributeTemplate(name, numberOfType, sizeInBytes, type, format, instanced ? 1 : 0); } private static bool isValidIntegerType(PointerType type) From 8c9f9cb415a6e91c0312932c9982dbe5200ccfaf Mon Sep 17 00:00:00 2001 From: "Paul C. Scharf" Date: Mon, 5 Aug 2024 21:43:02 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20Update=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bearded.Graphics.Examples/01.Basics/GameWindow.cs | 5 ++++- Bearded.Graphics.Examples/02.IndexBuffer/GameWindow.cs | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Bearded.Graphics.Examples/01.Basics/GameWindow.cs b/Bearded.Graphics.Examples/01.Basics/GameWindow.cs index 6aaddcb..5bef1a7 100644 --- a/Bearded.Graphics.Examples/01.Basics/GameWindow.cs +++ b/Bearded.Graphics.Examples/01.Basics/GameWindow.cs @@ -43,7 +43,10 @@ protected override void OnLoad() addTriangle(buffer); // Create a renderable wrapper for our buffer, interpreting it as a triangle list of vertices. - var renderable = Renderable.ForVertices(buffer, PrimitiveType.Triangles); + var renderable = Renderable.Build( + PrimitiveType.Triangles, + b => b.With(buffer.AsVertexBuffer()) + ); // The shader program contains the vertex and fragment shaders. It is assigned to a renderer. shaderProgram = ShaderProgram.FromShaders( diff --git a/Bearded.Graphics.Examples/02.IndexBuffer/GameWindow.cs b/Bearded.Graphics.Examples/02.IndexBuffer/GameWindow.cs index 628b9b4..4675818 100644 --- a/Bearded.Graphics.Examples/02.IndexBuffer/GameWindow.cs +++ b/Bearded.Graphics.Examples/02.IndexBuffer/GameWindow.cs @@ -47,7 +47,12 @@ protected override void OnLoad() // When creating our renderable, the PrimitiveType defines how the indices are interpreted and translated // into geometry. - var renderable = Renderable.ForVerticesAndIndices(vertexBuffer, indexBuffer, PrimitiveType.Triangles); + var renderable = Renderable.Build( + PrimitiveType.Triangles, + b => b + .With(vertexBuffer.AsVertexBuffer()) + .With(indexBuffer.AsIndexBuffer()) + ); shaderProgram = ShaderProgram.FromShaders( ShaderFactory.Vertex.FromFile("geometry.vs"), ShaderFactory.Fragment.FromFile("geometry.fs")); From 7ee3e59fe633cacb7e87060df53a17e2ea37265f Mon Sep 17 00:00:00 2001 From: "Paul C. Scharf" Date: Mon, 5 Aug 2024 22:19:38 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=94=92=20Enforce=20index=20buffer=20t?= =?UTF-8?q?ype?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Rendering/BufferExtensions.cs | 17 ++++++----------- Bearded.Graphics/Core/Rendering/IndexBuffer.cs | 16 ++++++---------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Bearded.Graphics/Core/Rendering/BufferExtensions.cs b/Bearded.Graphics/Core/Rendering/BufferExtensions.cs index ee12664..cfd03bf 100644 --- a/Bearded.Graphics/Core/Rendering/BufferExtensions.cs +++ b/Bearded.Graphics/Core/Rendering/BufferExtensions.cs @@ -16,15 +16,10 @@ public static IVertexBuffer AsVertexBuffer(this BufferStream b return VertexBuffer.From(buffer); } - public static IIndexBuffer AsIndexBuffer(this Buffer buffer) - where TIndex : struct - { - return IndexBuffer.From(buffer); - } - - public static IIndexBuffer AsIndexBuffer(this BufferStream buffer) - where TIndex : struct - { - return IndexBuffer.From(buffer); - } + public static IIndexBuffer AsIndexBuffer(this Buffer buffer) => IndexBuffer.From(buffer); + public static IIndexBuffer AsIndexBuffer(this Buffer buffer) => IndexBuffer.From(buffer); + public static IIndexBuffer AsIndexBuffer(this Buffer buffer) => IndexBuffer.From(buffer); + public static IIndexBuffer AsIndexBuffer(this BufferStream buffer) => IndexBuffer.From(buffer); + public static IIndexBuffer AsIndexBuffer(this BufferStream buffer) => IndexBuffer.From(buffer); + public static IIndexBuffer AsIndexBuffer(this BufferStream buffer) => IndexBuffer.From(buffer); } diff --git a/Bearded.Graphics/Core/Rendering/IndexBuffer.cs b/Bearded.Graphics/Core/Rendering/IndexBuffer.cs index f00dc2c..b57b10d 100644 --- a/Bearded.Graphics/Core/Rendering/IndexBuffer.cs +++ b/Bearded.Graphics/Core/Rendering/IndexBuffer.cs @@ -12,17 +12,13 @@ public interface IIndexBuffer public static class IndexBuffer { - public static IIndexBuffer From(Buffer buffer) - where TIndex : struct - { - return new Static(buffer); - } + public static IIndexBuffer From(Buffer buffer) => new Static(buffer); + public static IIndexBuffer From(Buffer buffer) => new Static(buffer); + public static IIndexBuffer From(Buffer buffer) => new Static(buffer); - public static IIndexBuffer From(BufferStream stream) - where TIndex : struct - { - return new Streaming(stream); - } + public static IIndexBuffer From(BufferStream stream) => new Streaming(stream); + public static IIndexBuffer From(BufferStream stream) => new Streaming(stream); + public static IIndexBuffer From(BufferStream stream) => new Streaming(stream); private sealed class Static(Buffer buffer) : IIndexBuffer where TIndex : struct