From cc9aecb42e36c6765354b1766f35451fe2e0949b Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 17 Aug 2024 14:19:42 +0200 Subject: [PATCH] [GL] improved construction of VertexAttributeBindings --- .../Management/ResourceManager.fs | 6 +- .../Resources/Pointers.fs | 190 ++++++++++-------- .../Runtime/GeometryPool.fs | 10 +- .../Aardvark.Rendering.Tests.fsproj | 1 + .../Tests/Other/AttributeBindingsBench.fs | 44 ++++ 5 files changed, 160 insertions(+), 91 deletions(-) create mode 100644 src/Tests/Aardvark.Rendering.Tests/Tests/Other/AttributeBindingsBench.fs diff --git a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs index f62207128..7d101f0be 100644 --- a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs +++ b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs @@ -611,12 +611,12 @@ type ResourceManager private (parent : Option, ctx : Context, r member x.Create (t : AdaptiveToken, rt : RenderToken, old : Option) = let attributes = - bindings |> Array.map (fun (i, a) -> i, a.GetValue(t, rt)) + bindings |> Array.map (fun (i, a) -> struct(i, a.GetValue(t, rt))) let index = match index with - | Some i -> i.Buffer.Handle.GetValue(t, rt) |> Some - | _ -> None + | Some i -> i.Buffer.Handle.GetValue(t, rt) |> ValueSome + | _ -> ValueNone match old with | Some old -> diff --git a/src/Aardvark.Rendering.GL/Resources/Pointers.fs b/src/Aardvark.Rendering.GL/Resources/Pointers.fs index 3ebf74fc5..6b4ac5c91 100644 --- a/src/Aardvark.Rendering.GL/Resources/Pointers.fs +++ b/src/Aardvark.Rendering.GL/Resources/Pointers.fs @@ -54,107 +54,131 @@ module PointerContextExtensions = module Attribute = - let private (|AttributePointerType|_|) (t : Type) = - match t with - | Float32 -> Some VertexAttribPointerType.Float - | Float64 -> Some VertexAttribPointerType.Double - | SByte -> Some VertexAttribPointerType.Byte - | Int16 -> Some VertexAttribPointerType.Short - | Int32 -> Some VertexAttribPointerType.Int - | Byte -> Some VertexAttribPointerType.UnsignedByte - | UInt16 -> Some VertexAttribPointerType.UnsignedShort - | UInt32 -> Some VertexAttribPointerType.UnsignedInt - | _ -> None - - let private (|AttributePointer|_|) (t : Type) = - match t with - | AttributePointerType t -> Some (V2i.II, t) - | ColorOf (d, AttributePointerType t) - | VectorOf (d, AttributePointerType t) -> Some (V2i(d, 1), t) - | MatrixOf (d, AttributePointerType t) -> Some (d, t) - | _ -> None - - let private (|Attribute|_|) = - (|AttributePointer|_|) >> Option.map (fun (d, t) -> d, unbox t) - - let private vertexAttribTypeSize = - LookupTable.lookupTable [ - VertexAttribType.Byte, sizeof - VertexAttribType.UnsignedByte, sizeof - VertexAttribType.Short, sizeof - VertexAttribType.UnsignedShort, sizeof - VertexAttribType.Int, sizeof - VertexAttribType.UnsignedInt, sizeof - VertexAttribType.HalfFloat, sizeof - VertexAttribType.Float, sizeof - VertexAttribType.Double, sizeof - ] - - let inline private vertexAttribPointerTypeSize (t : VertexAttribPointerType) = - t |> unbox |> vertexAttribTypeSize - - let bindings (attributes : (int * Attribute)[]) = + let private attributeTypeTable = // (dimension, VertexAttribPointerType, elementSize, isByteColor) + Dictionary.ofArrayV [| + typeof, struct(V2i(2,2), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(3,2), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(3,3), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(4,3), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(4,4), VertexAttribPointerType.Int, sizeof, false) + + typeof, struct(V2i(2,2), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(3,2), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(3,3), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(4,3), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(4,4), VertexAttribPointerType.Float, sizeof, false) + + typeof, struct(V2i(2,2), VertexAttribPointerType.Double, sizeof, false) + typeof, struct(V2i(3,2), VertexAttribPointerType.Double, sizeof, false) + typeof, struct(V2i(3,3), VertexAttribPointerType.Double, sizeof, false) + typeof, struct(V2i(4,3), VertexAttribPointerType.Double, sizeof, false) + typeof, struct(V2i(4,4), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(3, 1), VertexAttribPointerType.UnsignedShort, sizeof, true) + typeof, struct(V2i(3, 1), VertexAttribPointerType.UnsignedShort, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(4, 1), VertexAttribPointerType.UnsignedByte, sizeof, true) + typeof, struct(V2i(4, 1), VertexAttribPointerType.UnsignedShort, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(2, 1), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(2, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(2, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(2, 1), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(3, 1), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(3, 1), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(4, 1), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(4, 1), VertexAttribPointerType.Double, sizeof, false) + + typeof, struct(V2i(1, 1), VertexAttribPointerType.Byte, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.Short, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.Int, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.UnsignedByte, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.UnsignedShort, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.UnsignedInt, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.HalfFloat, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.Float, sizeof, false) + typeof, struct(V2i(1, 1), VertexAttribPointerType.Double, sizeof, false) + |] + + + let bindings (attributes : struct(int * Attribute)[]) = let buffers = System.Collections.Generic.List<_>() let values = System.Collections.Generic.List<_>() for (index, att) in attributes do match att with | Attribute.Buffer att -> - let buffer = att.Buffer - let divisor = - match att.Frequency with - | PerVertex -> 0 - | PerInstances i -> i + match Dictionary.tryFindV att.Type attributeTypeTable with + | ValueSome struct(d, t, s, isByteColor) -> + + let buffer = att.Buffer - match att.Type with - | MatrixOf(s, AttributePointerType t) -> - let rowSize = vertexAttribPointerTypeSize t * s.X + let divisor = + match att.Frequency with + | PerVertex -> 0 + | PerInstances i -> i - let stride = - if att.Stride = 0 then rowSize * s.Y - else att.Stride + if d.Y = 1 then - for r in 0 .. s.Y - 1 do - let ptr = VertexBufferBinding(uint32 (index + r), s.X, divisor, t, att.Format, stride, att.Offset + r * rowSize, buffer.Handle) - buffers.Add ptr + if isByteColor then - | ColorOf(d, Byte) -> - // C3b does not seem to work :/ - if d <> 4 then - failf "cannot use %A for vertex or instance attribute buffers. Try any other color type instead." att.Type + if d.X = 3 then + failwith "cannot use C3b for vertex or instance attribute buffers. Try any other color type instead." - // Only works if normalized = true, i.e. only can be used for floating point attributes - if att.Format <> VertexAttributeFormat.Normalized then - failf "%A vertex or instance attribute buffers can only be used for normalized floating point attributes." att.Type + // Only works if normalized = true, i.e. only can be used for floating point attributes + if att.Format <> VertexAttributeFormat.Normalized then + failf "%A vertex or instance attribute buffers can only be used for normalized floating point attributes." att.Type - // See: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml - let ptr = - VertexBufferBinding( - uint32 index, int All.Bgra, divisor, - VertexAttribPointerType.UnsignedByte, VertexAttributeFormat.Normalized, - att.Stride, att.Offset, buffer.Handle - ) + // See: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml + let ptr = + VertexBufferBinding( + uint32 index, int All.Bgra, divisor, + VertexAttribPointerType.UnsignedByte, VertexAttributeFormat.Normalized, + att.Stride, att.Offset, buffer.Handle + ) - buffers.Add ptr + buffers.Add ptr + else - | AttributePointer (d, t) when d.Y = 1 -> - let ptr = VertexBufferBinding(uint32 index, d.X, divisor, t, att.Format, att.Stride, att.Offset, buffer.Handle) - buffers.Add ptr + let ptr = VertexBufferBinding(uint32 index, d.X, divisor, t, att.Format, att.Stride, att.Offset, buffer.Handle) + buffers.Add ptr + + else + let rowSize = s * d.X + + let stride = + if att.Stride = 0 then rowSize * d.Y + else att.Stride + + for r in 0 .. d.Y - 1 do + let ptr = VertexBufferBinding(uint32 (index + r), d.X, divisor, t, att.Format, stride, att.Offset + r * rowSize, buffer.Handle) + buffers.Add ptr | _ -> + failf "cannot use %A buffer as vertex or instance attribute buffer" att.Type | Attribute.Value (value, format) -> let typ = value.GetType() - let dim, attributeType = - match typ with - | Attribute (d, t) -> d, t + let dim, attributeType, rowSize = + match Dictionary.tryFindV typ attributeTypeTable with + | ValueSome struct(d, t, s, _) -> d, t, d.X * s | _ -> failf "cannot set value of %A as vertex or instance attribute" typ - let rowSize = uint64 <| vertexAttribTypeSize attributeType * dim.X - // Adjust BGRA layout of C3b and C4b let value : obj = match value with @@ -167,9 +191,9 @@ module PointerContextExtensions = for r = 0 to dim.Y - 1 do let pBinding = NativePtr.stackalloc 1 - NativePtr.write pBinding <| VertexValueBinding(uint32 (index + r), attributeType, format) + NativePtr.write pBinding <| VertexValueBinding(uint32 (index + r), attributeType |> unbox, format) - Buffer.MemoryCopy(pSrc, NativePtr.toNativeInt pBinding, 32UL, rowSize) + Buffer.MemoryCopy(pSrc, NativePtr.toNativeInt pBinding, 32UL, uint64 rowSize) pSrc <- pSrc + nativeint rowSize values.Add (NativePtr.read pBinding) @@ -246,9 +270,9 @@ module PointerContextExtensions = member x.ToStencilMode(mode : StencilMode) = toGLStencilMode mode - member x.CreateVertexInputBinding (index : Option, attributes : (int * Attribute)[]) = + member x.CreateVertexInputBinding (index : ValueOption, attributes : struct(int * Attribute)[]) = let buffers, values = Attribute.bindings attributes - let index = match index with | Some i -> i.Handle | _ -> 0 + let index = match index with | ValueSome i -> i.Handle | _ -> 0 let pBuffers = NativePtr.allocArray buffers let pValues = NativePtr.allocArray values @@ -258,10 +282,10 @@ module PointerContextExtensions = NativePtr.write ptr value VertexInputBindingHandle ptr - member x.Update(ptr : VertexInputBindingHandle, index : Option, attributes : (int * Attribute)[]) = + member x.Update(ptr : VertexInputBindingHandle, index : ValueOption, attributes : struct(int * Attribute)[]) = let mutable value = NativePtr.read ptr.Pointer let buffers, values = Attribute.bindings attributes - let index = match index with | Some i -> i.Handle | _ -> 0 + let index = match index with | ValueSome i -> i.Handle | _ -> 0 let mutable value = value diff --git a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs index e7eadcbac..a46951ab1 100644 --- a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs +++ b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs @@ -1633,34 +1633,34 @@ type DrawPool(ctx : Context, alphaToCoverage : bool, bounds : bool, renderBounds let calls = Dict.toList indirects |> List.map (fun ((mode, ib, vb, textures, typeAndIndex), db) -> let indexType = typeAndIndex |> Option.map fst - let index = typeAndIndex |> Option.map snd + let index = typeAndIndex |> Option.map snd |> Option.toValueOption db.Flush() let attributes = pProgramInterface.inputs |> List.map (fun param -> match MapExt.tryFind param.paramSemantic ib.Buffers with | Some ib -> - param.paramLocation, Attribute.Buffer { + struct(param.paramLocation, Attribute.Buffer { Type = GLSLType.toType param.paramType Buffer = ib Frequency = AttributeFrequency.PerInstances 1 Format = VertexAttributeFormat.Default Stride = GLSLType.sizeof param.paramType Offset = 0 - } + }) | None -> match MapExt.tryFind param.paramSemantic vb.Buffers with | Some (vb, typ) -> let format = if typ = typeof then VertexAttributeFormat.Normalized else VertexAttributeFormat.Default - param.paramLocation, Attribute.Buffer { + struct(param.paramLocation, Attribute.Buffer { Type = typ Buffer = vb Frequency = AttributeFrequency.PerVertex Format = format Stride = Marshal.SizeOf typ Offset = 0 - } + }) | None -> param.paramLocation, Attribute.Value (V4f.Zero, VertexAttributeFormat.Default) diff --git a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj index 95cb0ed92..9d75dedcc 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj +++ b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj @@ -71,6 +71,7 @@ + diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/AttributeBindingsBench.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/AttributeBindingsBench.fs new file mode 100644 index 000000000..362d66965 --- /dev/null +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/AttributeBindingsBench.fs @@ -0,0 +1,44 @@ +namespace Aardvark.Rendering.Tests + +open Aardvark.Base +open Aardvark.Rendering.GL +open BenchmarkDotNet.Attributes +open System.Reflection + +//BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4651/22H2/2022Update) +//AMD Ryzen 9 7900, 1 CPU, 24 logical and 12 physical cores +//.NET SDK 8.0.400 +// [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI DEBUG + +//Job=InProcess Toolchain=InProcessEmitToolchain + +//| Method | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | +//|--------------------- |-----------:|---------:|---------:|------:|-------:|----------:|------------:| +//| AttributeBindings | 4,959.4 ns | 27.96 ns | 24.78 ns | 1.00 | 1.1597 | 19 KB | 1.00 | +//| AttributeBindingsNew | 132.7 ns | 0.35 ns | 0.32 ns | 0.03 | 0.0880 | 1.44 KB | 0.08 | + +[] [] [] +type AttributeBindingsBench() = + + let attributes = + [| + struct(0, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerInstances 1; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + struct(1, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerInstances 1; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + struct(2, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerInstances 1; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + struct(6, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerInstances 1; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + struct(10, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerVertex; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + struct(11, Attribute.Buffer { Type = typeof; Frequency = AttributeFrequency.PerVertex; Format = VertexAttributeFormat.Default; Stride = 0; Offset = 0; Buffer = new Buffer(Unchecked.defaultof<_>, 0n, 0) }) + |] + + let bindingsFun = Assembly.GetAssembly(typeof) + .GetType("Aardvark.Rendering.GL.PointerContextExtensions") + .GetNestedType("Helpers", BindingFlags.Static ||| BindingFlags.NonPublic) + .GetNestedType("Attribute", BindingFlags.Static ||| BindingFlags.NonPublic) + .GetMethod("bindings", BindingFlags.Static ||| BindingFlags.NonPublic) + + [] + member x.AttributeBindings() = + //PointerContextExtensions.Helpers.Attribute.bindings attributes + bindingsFun.Invoke(null, [| attributes |]) + +