From 0e4ed378e320447f8b86722e3301a46d0644899f Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 22 Aug 2024 10:04:09 +0200 Subject: [PATCH] [GL] Optimize construction of attribute bindings See: https://github.com/aardvark-platform/aardvark.rendering/pull/104 --- .../Management/PreparedRenderObject.fs | 8 +- .../Management/ResourceManager.fs | 9 +- .../Resources/Pointers.fs | 186 ++++++++++-------- .../Runtime/GeometryPool.fs | 45 +++-- 4 files changed, 142 insertions(+), 106 deletions(-) diff --git a/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs b/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs index 2f24e946..decebbe4 100644 --- a/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs +++ b/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs @@ -988,7 +988,7 @@ module PreparedObjectInfo = resources.Add activation // create all requested vertex-/instance-inputs - let attributeBindings = + let attributeBindings : struct (int * AdaptiveAttribute) list = iface.inputs |> List.choose (fun v -> if v.paramLocation >= 0 then @@ -1040,14 +1040,14 @@ module PreparedObjectInfo = else None ) - |> List.toArray let attributeBuffers = - attributeBindings |> Array.choose (fun (_, attr) -> + attributeBindings |> List.choose (fun struct (_, attr) -> match attr with | AdaptiveAttribute.Buffer b -> Some b.Resource | _ -> None ) + |> List.toArray GL.Check "[Prepare] Buffers" @@ -1076,7 +1076,7 @@ module PreparedObjectInfo = GL.Check "[Prepare] Indirect Buffer" // create the VertexArrayObject - let vibh = x.CreateVertexInputBinding(attributeBindings, index) |> addResource resources + let vibh = x.CreateVertexInputBinding(List.toArray attributeBindings, index) |> addResource resources GL.Check "[Prepare] VAO" let isActive = x.CreateIsActive rj.IsActive |> addResource resources diff --git a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs index f6220712..586e12fa 100644 --- a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs +++ b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs @@ -601,7 +601,7 @@ type ResourceManager private (parent : Option, ctx : Context, r let layers = input |> AVal.mapNonAdaptive (fun l -> l.Slices) x.CreateImageBinding(texture, level, layers, imageType.Properties) - member x.CreateVertexInputBinding(bindings : (int * AdaptiveAttribute)[], index : IndexBinding option) = + member x.CreateVertexInputBinding(bindings : struct (int * AdaptiveAttribute)[], index : IndexBinding option) = vertexInputCache.GetOrCreate( [ bindings :> obj; index :> obj ], fun () -> @@ -611,7 +611,7 @@ 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 struct (i, a) -> struct (i, a.GetValue(t, rt))) let index = match index with @@ -630,6 +630,11 @@ type ResourceManager private (parent : Option, ctx : Context, r } ) + [] + member x.CreateVertexInputBinding(bindings : (int * AdaptiveAttribute)[], index : IndexBinding option) = + let bindings = bindings |> Array.map (fun (i, a) -> struct (i, a)) + x.CreateVertexInputBinding(bindings, index) + member x.CreateUniformBuffer(scope : Ag.Scope, layout : FShade.GLSL.GLSLUniformBuffer, uniforms : IUniformProvider) = uniformBufferManager.CreateUniformBuffer(layout, scope, uniforms) diff --git a/src/Aardvark.Rendering.GL/Resources/Pointers.fs b/src/Aardvark.Rendering.GL/Resources/Pointers.fs index b1a3ef66..db7f4d9d 100644 --- a/src/Aardvark.Rendering.GL/Resources/Pointers.fs +++ b/src/Aardvark.Rendering.GL/Resources/Pointers.fs @@ -54,46 +54,66 @@ 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)[]) = + // Build a table for fast lookup instead of using active patterns multiple times. + let private attributeTable = + let primitives = + [ + struct (typeof, VertexAttribPointerType.HalfFloat) + struct (typeof, VertexAttribPointerType.Float) + struct (typeof, VertexAttribPointerType.Double) + struct (typeof, VertexAttribPointerType.Byte) + struct (typeof, VertexAttribPointerType.Short) + struct (typeof, VertexAttribPointerType.Int) + struct (typeof, VertexAttribPointerType.UnsignedByte) + struct (typeof, VertexAttribPointerType.UnsignedShort) + struct (typeof, VertexAttribPointerType.UnsignedInt) + ] + |> List.map (fun struct (t, a) -> + struct (t, struct (V2i.II, a, t.GetCLRSize())) + ) + + let vectors = + let sizes = [2; 3; 4] + + primitives + |> List.collect (fun struct (t, (_, a, typeSize)) -> + sizes |> List.choose (fun s -> + VectorType.tryGet t s + |> Option.map (fun vt -> + struct (vt, struct (V2i(s, 1), a, typeSize)) + ) + ) + ) + + let colors = + let sizes = [3; 4] + + primitives + |> List.collect (fun struct (t, (_, a, typeSize)) -> + sizes |> List.choose (fun s -> + ColorType.tryGet t s + |> Option.map (fun vt -> + struct (vt, struct (V2i(s, 1), a, typeSize)) + ) + ) + ) + + let matrices = + let sizes = MatrixType.all |> List.map _.Dimension |> List.distinct + + primitives + |> List.collect (fun struct (t, (_, a, typeSize)) -> + sizes |> List.choose (fun s -> + MatrixType.tryGet t s + |> Option.map (fun vt -> + struct (vt, struct (s, a, typeSize)) + ) + ) + ) + + Dictionary.ofListV (primitives @ vectors @ colors @ matrices) + + let bindings (attributes : struct (int * Attribute)[]) = let buffers = System.Collections.Generic.List<_>() let values = System.Collections.Generic.List<_>() @@ -107,40 +127,43 @@ module PointerContextExtensions = | PerVertex -> 0 | PerInstances i -> i - match att.Type with - | MatrixOf(s, AttributePointerType t) -> - let rowSize = vertexAttribPointerTypeSize t * s.X + match Dictionary.tryFindV att.Type attributeTable with + | ValueSome (s, t, typeSize) -> + if s.Y > 1 then + let rowSize = typeSize * s.X - let stride = - if att.Stride = 0 then rowSize * s.Y - else att.Stride + let stride = + if att.Stride = 0 then rowSize * s.Y + else att.Stride - 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 + 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 - | 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 + else + match att.Type with + | ColorOf(d, UInt8) -> + // 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 - // 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 - | 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, s.X, divisor, t, att.Format, att.Stride, att.Offset, buffer.Handle) + buffers.Add ptr | _ -> failf "cannot use %A buffer as vertex or instance attribute buffer" att.Type @@ -148,12 +171,12 @@ module PointerContextExtensions = | Attribute.Value (value, format) -> let typ = value.GetType() - let dim, attributeType = - match typ with - | Attribute (d, t) -> d, t + let struct (dim, attributeType, typeSize) = + match Dictionary.tryFindV typ attributeTable with + | ValueSome (d, a, s) -> struct (d, unbox a, s) | _ -> failf "cannot set value of %A as vertex or instance attribute" typ - let rowSize = uint64 <| vertexAttribTypeSize attributeType * dim.X + let rowSize = uint64 <| typeSize * dim.X // Adjust BGRA layout of C3b and C4b let value : obj = @@ -175,8 +198,7 @@ module PointerContextExtensions = values.Add (NativePtr.read pBinding) ) - - buffers.ToArray(), values.ToArray() + struct (buffers.ToArray(), values.ToArray()) module NativePtr = let setArray (a : 'a[]) (ptr : nativeptr<'a>) = @@ -246,8 +268,8 @@ module PointerContextExtensions = member x.ToStencilMode(mode : StencilMode) = toGLStencilMode mode - member x.CreateVertexInputBinding (index : Option, attributes : (int * Attribute)[]) = - let buffers, values = Attribute.bindings attributes + member x.CreateVertexInputBinding (index : Option, attributes : struct (int * Attribute)[]) = + let struct (buffers, values) = Attribute.bindings attributes let index = match index with | Some i -> i.Handle | _ -> 0 let pBuffers = NativePtr.allocArray buffers @@ -258,9 +280,14 @@ module PointerContextExtensions = NativePtr.write ptr value VertexInputBindingHandle ptr - member x.Update(ptr : VertexInputBindingHandle, index : Option, attributes : (int * Attribute)[]) = + [] + member x.CreateVertexInputBinding (index : Option, attributes : (int * Attribute)[]) = + let attributes = attributes |> Array.map (fun (i, a) -> struct (i, a)) + x.CreateVertexInputBinding(index, attributes) + + member x.Update(ptr : VertexInputBindingHandle, index : Option, attributes : struct (int * Attribute)[]) = let mutable value = NativePtr.read ptr.Pointer - let buffers, values = Attribute.bindings attributes + let struct (buffers, values) = Attribute.bindings attributes let index = match index with | Some i -> i.Handle | _ -> 0 let mutable value = value @@ -294,6 +321,11 @@ module PointerContextExtensions = value.IndexBuffer <- index NativePtr.write ptr.Pointer value + [] + member x.Update(ptr : VertexInputBindingHandle, index : Option, attributes : (int * Attribute)[]) = + let attributes = attributes |> Array.map (fun (i, a) -> struct (i, a)) + x.Update(ptr, index, attributes) + member x.Delete(ptr : VertexInputBindingHandle) = let v = NativePtr.read ptr.Pointer if v.VAO > 0 then diff --git a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs index 50679d25..78ad675a 100644 --- a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs +++ b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs @@ -1636,38 +1636,37 @@ type DrawPool(ctx : Context, alphaToCoverage : bool, bounds : bool, renderBounds let index = typeAndIndex |> Option.map snd db.Flush() - let attributes = + let attributes : struct (int * Attribute) list = pProgramInterface.inputs |> List.map (fun param -> match MapExt.tryFind param.paramSemantic ib.Buffers with - | Some ib -> + | Some ib -> + 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 { - Type = GLSLType.toType param.paramType - Buffer = ib - Frequency = AttributeFrequency.PerInstances 1 - Format = VertexAttributeFormat.Default - Stride = GLSLType.sizeof param.paramType + Type = typ + Buffer = vb + Frequency = AttributeFrequency.PerVertex + Format = format + Stride = Marshal.SizeOf typ 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 { - Type = typ - Buffer = vb - Frequency = AttributeFrequency.PerVertex - Format = format - Stride = Marshal.SizeOf typ - Offset = 0 - } - - | None -> - param.paramLocation, Attribute.Value (V4f.Zero, VertexAttributeFormat.Default) + param.paramLocation, Attribute.Value (V4f.Zero, VertexAttributeFormat.Default) ) - |> List.toArray - let bufferBinding = ctx.CreateVertexInputBinding(index, attributes) + let bufferBinding = ctx.CreateVertexInputBinding(index, List.toArray attributes) let beginMode = let bm = beginMode mode